34 The Ridgun Book
The jwt Lib — RFC 7519 HS256 JSON Web Tokens and ChaCha20-Poly1305 Encrypted Tokens
34.1. Why Does the jwt Lib Exist? (The Real-World Problem & Ridgun Philosophy)
"Why do I need a
jwtlib written from scratch in pure Ridgun instead of just linking a legacy C library?"
That is the first question every systems engineer asks when meeting jwt. And the answer captures the entire philosophy of ridgun-libs:
Let's be honest: the JWT ecosystem is notorious for "alg":"none" signature-bypass bugs and for developers stuffing confidential data into a standard JWT while forgetting that its payload is merely Base64url-encoded, not encrypted.
The jwt lib solves both needs: it signs and verifies RFC 7519 (HS256) JWTs while strictly enforcing alg == "HS256", constant-time MAC comparison, and exp/nbf windows, and it also provides token seal / token open for opaque tokens encrypted with ChaCha20-Poly1305 AEAD.
In a traditional ecosystem, solving JWT tasks means pulling in external C dependencies, wrestling with raw pointers, opaque type conversions, unchecked exceptions, or unpredictable garbage-collector pauses. In Ridgun, the jwt lib (src/main/ridgun/lib/jwt.rg) tackles the problem with four non-negotiable principles:
- Declare
import jwt(orimport jwt as alias) at the top of your file: every lib defines itsmodule :: jwtnamespace, and under--strictmulti-word functions are always called with natural spaces. - Chain your data pipelines left-to-right with
->: writeinput step one -> step two -> result, use(+ _ _ +)sequence masks to destructure tuples/lists, and omit_ =on sequential statements (or discard with-> ()). - Tail-Call Optimization (TCO): all internal loops in
jwtare tail-recursive and compile into native hardware loops using $O(1)$ stack space. - Deterministic Memory (
RMM1/RMM2): every intermediate allocation is reclaimed at the exact instruction where its last read finishes, with no Garbage Collector.
34.2. Native Artifacts, Imports, and Complete jwt API Catalog
In ridgun-libs, jwt is a first-class lib located at src/main/ridgun/lib/jwt.rg (domain JWT). When built with stable bundle, it compiles into three native artifacts:
| Artifact | Path | Purpose |
|---|---|---|
| Ridgun Bundle | target/bundles/jwt.rglib | Portable distribution and direct import jwt resolution |
| Compiled Archive | target/jwt.rgc | Type-checked intermediate artifact verified by ridgunc |
| ELF64 Shared Object | target/libjwt.so | Native dynamic shared library with zero C or libc dependencies |
Every lib declares its own namespace via
module :: jwt: simply writeimport jwt(orimport jwt as alias) at the top and call its functions with natural spaces (my fn x) and left-to-right->pipelines (x my fn -> result).
34.2.1. Summary of jwt Functional Families
| Signature / Function / Type | Purpose & Usage |
|---|---|
type JwtResult | `JwtOk JsonVal |
jwt sign hs256 / jwt verify hs256 | Sign JsonVal claims and verify signature + alg == "HS256" + exp/nbf |
jwt claims / jwt get str / jwt get int | Build standard (sub, iss, iat, exp) claims and query verified payloads |
token seal / token open | Encrypt and authenticate opaque JSON tokens via ChaCha20-Poly1305 AEAD |
34.2.2. Exhaustive Catalog of Types and Signatures in src/main/ridgun/lib/jwt.rg
Below is the complete inventory of algebraic data types (type), mixfix operators (op ::), and function signatures (fn ::) defined in src/main/ridgun/lib/jwt.rg (3 types, 0 operators, and 55 functions):
Algebraic Data Types & Records (type) in jwt:
type JwtClaims = issuer: String, subject: String, expires_at: Int, not_before_at: Int, custom_json: String
type JwtRes = JwtOk String | JwtErr String
type JwtClaimsRes = JwtClaimsOk JwtClaims | JwtClaimsErr String
| # | Function / Signature (fn ::) | Return Type |
|---|---|---|
1 | fn :: jwt b64url lut Int | String |
2 | fn :: jwt b64 char Int | String |
3 | fn :: jwt b64 val Int | Int |
4 | fn :: jwt b64url enc bytes [Int] | String |
5 | fn :: jwt str to bytes loop String Int Int [Int] | [Int] |
6 | fn :: jwt str to bytes String | [Int] |
7 | fn :: jwt bytes to str [Int] | String |
8 | fn :: jwt b64url encode String | String |
9 | fn :: jwt b64url valid loop String Int Int | Bool |
10 | fn :: jwt b64url is valid String | Bool |
11 | fn :: jwt b64url dec rem String Int Int | [Int] |
12 | fn :: jwt b64url dec 3 String Int | [Int] |
13 | fn :: jwt b64url dec 4 String Int | [Int] |
14 | fn :: jwt b64url decode bytes String | [Int] |
15 | fn :: jwt b64url decode String | String |
16 | fn :: jwt bxor loop Int Int Int Int | Int |
17 | fn :: jwt byte xor Int Int | Int |
18 | fn :: jwt ct diff String String Int Int Int | Int |
19 | fn :: jwt ct eq String String | Bool |
20 | fn :: jwt hex nib Int | Int |
21 | fn :: jwt hex to bytes loop String Int Int [Int] | [Int] |
22 | fn :: jwt hex to bytes String | [Int] |
23 | fn :: jwt pad key bytes [Int] Int | [Int] |
24 | fn :: jwt pad key head [Int] Int | [Int] |
25 | fn :: jwt key block String | [Int] |
26 | fn :: jwt xor pad str [Int] Int | String |
27 | fn :: jwt hmac sha256 b64url String String | String |
28 | fn :: jwt hs256 header json Int | String |
29 | fn :: jwt sign hs256 String String | String |
30 | fn :: jwt split dots loop String Int Int Int [String] | [String] |
31 | fn :: jwt str list len [String] | Int |
32 | fn :: jwt str at [String] Int | String |
33 | fn :: jwt decode payload String | JwtRes |
34 | fn :: jwt decode parts [String] | JwtRes |
35 | fn :: jwt header is hs256 String | Bool |
36 | fn :: jwt check hdr json JsonRes | Bool |
37 | fn :: jwt verify hs256 String String | JwtRes |
38 | fn :: jwt verify parts [String] String | JwtRes |
39 | fn :: jwt verify sig String String String String | JwtRes |
40 | fn :: jwt verify sig ct Bool String | JwtRes |
41 | fn :: jwt sign claims JwtClaims String | String |
42 | fn :: jwt verify claims String String Int | JwtClaimsRes |
43 | fn :: jwt validate verified payload JwtRes Int | JwtClaimsRes |
44 | fn :: jwt validate parsed claims JsonRes Int | JwtClaimsRes |
45 | fn :: jwt check claim times JwtClaims Int | JwtClaimsRes |
46 | fn :: jwt keystream bytes String String Int Int | [Int] |
47 | fn :: jwt xor lists [Int] [Int] | [Int] |
48 | fn :: jwt seal aead String String String | JwtRes |
49 | fn :: jwt seal aead ok String String String | JwtRes |
50 | fn :: jwt open aead String String String | JwtRes |
51 | fn :: jwt open aead parts String String [String] | JwtRes |
52 | fn :: jwt open aead verify String String String String String | JwtRes |
53 | fn :: jwt open aead decrypt Bool String String String | JwtRes |
54 | fn :: jwt int list len [Int] | Int |
55 | fn :: jwt open aead pt String String [Int] | JwtRes |
34.3. Deep Dive into jwt: Step-by-Step Examples for Every Feature
A signature table alone is never enough: let's explore every capability of the jwt lib through progressive, runnable Ridgun examples. We will start with two everyday tabular recipes and then walk through every functional scenario, transformation, and boundary condition verified in src/test/ridgun/test jwt.rg.
34.3.1. Example 1: Core Tabular Workflow with jwt
Notice how in Ridgun the = signs and transformation steps using jwt align into a clean, scannable table without unnecessary mutable variables or hidden side effects:
import jwt
fn :: emitir y verificar sesion String => String String
emitir y verificar sesion secreto =
[("sub", "user-42"), ("iss", "solstice"), ("exp", "1700003600")] -> claims
jwt sign hs256 claims secreto -> tok
jwt verify and get tok secreto 1700001000 "sub" -> sub
(tok, sub)
34.3.2. Example 2: Safe Composition and Boundary Handling
Every function in the jwt lib is designed to compose cleanly with boolean guards | and with the rest of the 32 libs in ridgun-libs:
import jwt
fn :: sellar token confidencial String => Int String
sellar token confidencial clave =
[("rol", "root"), ("pin", "9988")] -> claims
jwt encrypt aead claims clave "nonce-12byte" "solstice" -> sobre
jwt decrypt aead sobre clave "solstice" 1700001000
34.3.3. Example 3: JWT HS256 Signing & Verification (jwt sign hs256, jwt verify hs256), alg: none Attack Rejection, and Tamper Detection
In this hands-on walkthrough we thoroughly exercise how jwt behaves across this functional area, covering standard inputs, parameter combinations, and corner cases:
fn :: test jwt hs256 sign verify and tamper => ()
test jwt hs256 sign verify and tamper =
payload = '{"sub":"user-42","role":"admin"}'
secret = "super-secret-hmac-key-2026"
tok = jwt sign hs256 payload secret
dec = jwt decode payload tok
assert true (is jwt ok dec) "AC1: jwt decode payload succeeds"
assert str eq (unwrap jwt dec) payload "AC1: decoded payload matches input"
ver = jwt verify hs256 tok secret
assert true (is jwt ok ver) "AC2: jwt verify hs256 accepts valid signature"
assert str eq (unwrap jwt ver) payload "AC2: verified payload matches input"
wrong_sec = jwt verify hs256 tok "wrong-secret"
assert false (is jwt ok wrong_sec) "AC2: jwt verify hs256 rejects wrong secret"
assert false (is jwt ok (jwt verify hs256 tok "")) "AC2: jwt verify hs256 rejects empty secret"
tampered = tok + "x"
assert false (is jwt ok (jwt verify hs256 tampered secret)) "AC2: jwt verify hs256 rejects tampered signature"
-- CVE/RFC 8725 attacks: alg:none, empty signature segment, malformed segment counts
alg_none_tok = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJ1c2VyLTQyIn0.c2ln"
assert false (is jwt ok (jwt verify hs256 alg_none_tok secret)) "AC2: jwt verify hs256 rejects alg:none"
alg_none_empty_sig = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJ1c2VyLTQyIn0."
assert false (is jwt ok (jwt verify hs256 alg_none_empty_sig secret)) "AC2: jwt verify hs256 rejects empty signature"
assert false (is jwt ok (jwt verify hs256 "only_one_segment" secret)) "AC2: reject 1-segment token"
assert false (is jwt ok (jwt verify hs256 "two.segments" secret)) "AC2: reject 2-segment token"
assert false (is jwt ok (jwt decode payload "bad_token_without_dots")) "AC1: decode rejects token without 3 segments"
()
34.3.4. Example 4: Temporal Validation of Structured Claims (JwtClaims: iss, sub, aud, exp, nbf, iat, jti) with jwt validate claims
In this hands-on walkthrough we thoroughly exercise how jwt behaves across this functional area, covering standard inputs, parameter combinations, and corner cases:
fn :: test jwt claims temporal validation => ()
test jwt claims temporal validation =
claims = JwtClaims "ridgun-auth" "agent-007" 2000 1000 "tier=gold"
secret = "claims-signing-secret"
tok = jwt sign claims claims secret
-- Exact nbf boundary: now == 1000 is valid (now >= nbf)
at_nbf_res = jwt verify claims tok secret 1000
assert true (is claims ok at_nbf_res) "AC2: claims valid at exact nbf boundary (now == 1000)"
ok_res = jwt verify claims tok secret 1500
assert true (is claims ok ok_res) "AC2: claims valid at now=1500"
c_out = unwrap claims ok_res
assert str eq (issuer c_out) "ridgun-auth" "AC2: issuer preserved"
assert str eq (subject c_out) "agent-007" "AC2: subject preserved"
assert int equals (expires_at c_out) 2000 "AC2: expires_at preserved"
assert int equals (not_before_at c_out) 1000 "AC2: not_before_at preserved"
assert str eq (custom_json c_out) "tier=gold" "AC2: custom_json preserved"
-- 1 second before exp: now == 1999 is valid
before_exp_res = jwt verify claims tok secret 1999
assert true (is claims ok before_exp_res) "AC2: claims valid 1s before exp (1999 < 2000)"
-- 1 second before nbf: now == 999 is rejected
early_res = jwt verify claims tok secret 999
assert false (is claims ok early_res) "AC2: claims rejected before nbf (999 < 1000)"
-- Exact exp boundary and after: now == 2000 and now == 5000 rejected
exp_res = jwt verify claims tok secret 2000
assert false (is claims ok exp_res) "AC2: claims rejected at exact exp (2000 >= 2000)"
late_res = jwt verify claims tok secret 5000
assert false (is claims ok late_res) "AC2: claims rejected after exp (5000 >= 2000)"
assert false (is claims ok (jwt verify claims tok "wrong-secret" 1500)) "AC2: claims rejected with wrong secret"
()
34.3.5. Example 5: Encrypted and Authenticated Tokens with ChaCha20-Poly1305 AEAD (jwt seal aead, jwt open aead)
In this hands-on walkthrough we thoroughly exercise how jwt behaves across this functional area, covering standard inputs, parameter combinations, and corner cases:
fn :: test jwt aead seal and open => ()
test jwt aead seal and open =
key32 = "0123456789abcdef0123456789abcdef"
other_key32 = "fedcba9876543210fedcba9876543210"
nonce12 = "uniqueNonce1"
other_nonce12 = "uniqueNonce2"
secret_msg = '{"api_key":"rk_live_998877","scope":"full"}'
sealed = jwt seal aead key32 nonce12 secret_msg
assert true (is jwt ok sealed) "AC3: jwt seal aead succeeds with 32B key and 12B nonce"
tok = unwrap jwt sealed
opened = jwt open aead key32 nonce12 tok
assert true (is jwt ok opened) "AC3: jwt open aead succeeds"
assert str eq (unwrap jwt opened) secret_msg "AC3: jwt open aead round-trips plaintext"
assert false (is jwt ok (jwt open aead other_key32 nonce12 tok)) "AC3: jwt open aead rejects wrong 32B key"
assert false (is jwt ok (jwt open aead key32 other_nonce12 tok)) "AC3: jwt open aead rejects wrong 12B nonce"
assert false (is jwt ok (jwt open aead key32 nonce12 (tok + "A"))) "AC3: jwt open aead rejects tampered ciphertext/tag"
assert false (is jwt ok (jwt seal aead "short" nonce12 secret_msg)) "AC3: jwt seal aead rejects non-32B key"
assert false (is jwt ok (jwt seal aead key32 "short" secret_msg)) "AC3: jwt seal aead rejects non-12B nonce"
()
34.4. Under the Hood: How jwt Is Built in Pure Ridgun (src/main/ridgun/lib/jwt.rg)
One of the biggest advantages of ridgun-libs is that there are no C black boxes. You can open src/main/ridgun/lib/jwt.rg and read exactly how every operation is implemented using pattern matching, | / |. guard trees, and tail-call optimization (TCO). Let's inspect the key building blocks of its real implementation:
34.4.1. Internal Implementation: Byte, Hex, Base64url & Constant-Time Helpers
Observe how jwt.rg declares explicit fn :: ... => ... contracts first and resolves each case through aligned equations and tail calls:
module :: jwt
fn :: jwt b64url lut Int => String
jwt b64url lut _ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
fn :: jwt b64 char Int => String
jwt b64 char idx =
jwt b64url lut 0 -> tbl
((idx % 64) + 64) % 64 -> i
str slice tbl i (i + 1)
fn :: jwt b64 val Int => Int
jwt b64 val c =
| c >= 65 and c <= 90 = c - 65
| c >= 97 and c <= 122 = (c - 97) + 26
| c >= 48 and c <= 57 = (c - 48) + 52
| c == 45 or c == 43 = 62
| c == 95 or c == 47 = 63
| otherwise = (0 - 1)
fn :: jwt b64url enc bytes [Int] => String
jwt b64url enc bytes [] = ""
jwt b64url enc bytes (b0:[]) = (jwt b64 char (b0 / 4)) + (jwt b64 char ((b0 % 4) * 16))
jwt b64url enc bytes (b0:b1:[]) =
(b0 * 256) + b1 -> n
(jwt b64 char (n / 1024)) + (jwt b64 char ((n / 16) % 64)) + (jwt b64 char ((n % 16) * 4))
jwt b64url enc bytes (b0:b1:b2:rs) =
(b0 * 65536) + (b1 * 256) + b2 -> n
(jwt b64 char (n / 262144)) + (jwt b64 char ((n / 4096) % 64)) + (jwt b64 char ((n / 64) % 64)) + (jwt b64 char (n % 64)) + (jwt b64url enc bytes rs)
fn :: jwt str to bytes loop String Int Int [Int] => [Int]
jwt str to bytes loop s pos len acc =
| pos >= len = acc
| otherwise = jwt str to bytes loop s (pos + 1) len (acc :: [((str char at s pos) % 256 + 256) % 256])
fn :: jwt str to bytes String => [Int]
jwt str to bytes s = jwt str to bytes loop s 0 (str len s) []
fn :: jwt bytes to str [Int] => String
jwt bytes to str [] = ""
jwt bytes to str (b:rest) = (char from code b) + (jwt bytes to str rest)
fn :: jwt b64url encode String => String
jwt b64url encode s = s jwt str to bytes -> jwt b64url enc bytes
fn :: jwt b64url valid loop String Int Int => Bool
jwt b64url valid loop s pos len =
| pos >= len = True
| (jwt b64 val (str char at s pos)) < 0 = False
| otherwise = jwt b64url valid loop s (pos + 1) len
fn :: jwt b64url is valid String => Bool
jwt b64url is valid s =
| ((str len s) % 4) == 1 = False
| otherwise = jwt b64url valid loop s 0 (str len s)
fn :: jwt b64url dec rem String Int Int => [Int]
jwt b64url dec rem s pos rem =
| rem <= 1 = []
| rem == 2 = [(((jwt b64 val (str char at s pos)) * 4) + ((jwt b64 val (str char at s (pos + 1))) / 16)) % 256]
| rem == 3 = jwt b64url dec3 s pos
| otherwise = (jwt b64url dec4 s pos) :: (jwt b64url dec rem s (pos + 4) (rem - 4))
fn :: jwt b64url dec3 String Int => [Int]
jwt b64url dec3 s pos =
jwt b64 val (str char at s pos) -> v0
jwt b64 val (str char at s (pos + 1)) -> v1
jwt b64 val (str char at s (pos + 2)) -> v2
(v0 * 1024) + (v1 * 16) + (v2 / 4) -> n
[(n / 256) % 256, n % 256]
fn :: jwt b64url dec4 String Int => [Int]
jwt b64url dec4 s pos =
jwt b64 val (str char at s pos) -> v0
jwt b64 val (str char at s (pos + 1)) -> v1
jwt b64 val (str char at s (pos + 2)) -> v2
jwt b64 val (str char at s (pos + 3)) -> v3
(v0 * 262144) + (v1 * 4096) + (v2 * 64) + v3 -> n
[(n / 65536) % 256, (n / 256) % 256, n % 256]
fn :: jwt b64url decode bytes String => [Int]
jwt b64url decode bytes s = jwt b64url dec rem s 0 (str len s)
fn :: jwt b64url decode String => String
jwt b64url decode s = s jwt b64url decode bytes -> jwt bytes to str
34.4.2. Internal Implementation: Constant-Time Equality & Bitwise XOR
Observe how jwt.rg declares explicit fn :: ... => ... contracts first and resolves each case through aligned equations and tail calls:
fn :: jwt bxor loop Int Int Int Int => Int
jwt bxor loop _ _ 0 acc = acc
jwt bxor loop a b bit acc =
| ((a % 2) != (b % 2)) = jwt bxor loop (a / 2) (b / 2) (bit * 2) (acc + bit)
| otherwise = jwt bxor loop (a / 2) (b / 2) (bit * 2) acc
fn :: jwt byte bxor Int Int => Int
jwt byte bxor a b = jwt bxor loop (((a % 256) + 256) % 256) (((b % 256) + 256) % 256) 1 0
fn :: jwt ct diff String String Int Int Int => Int
jwt ct diff a b idx len acc =
| idx >= len = acc
| otherwise = jwt ct diff a b (idx + 1) len (acc + (jwt byte bxor (str char at a idx) (str char at b idx)))
fn :: jwt ct eq String String => Bool
jwt ct eq a b =
| (str len a) != (str len b) = False
| otherwise = (jwt ct diff a b 0 (str len a) 0) == 0
34.4.3. Internal Implementation: HS256 (HMAC-SHA256) Signing & Verification
Observe how jwt.rg declares explicit fn :: ... => ... contracts first and resolves each case through aligned equations and tail calls:
fn :: jwt hex nib Int => Int
jwt hex nib c =
| c >= 48 and c <= 57 = c - 48
| c >= 97 and c <= 102 = (c - 97) + 10
| c >= 65 and c <= 70 = (c - 65) + 10
| otherwise = 0
fn :: jwt hex to bytes loop String Int Int [Int] => [Int]
jwt hex to bytes loop h pos len acc =
| (pos + 1) >= len = acc
| otherwise = jwt hex to bytes loop h (pos + 2) len (acc :: [((jwt hex nib (str char at h pos)) * 16) + (jwt hex nib (str char at h (pos + 1)))])
fn :: jwt hex to bytes String => [Int]
jwt hex to bytes h = jwt hex to bytes loop h 0 (str len h) []
fn :: jwt pad key bytes [Int] Int => [Int]
jwt pad key bytes bs idx =
| idx >= 64 = []
| bs == [] = [0] :: (jwt pad key bytes [] (idx + 1))
| otherwise = jwt pad key head bs idx
fn :: jwt pad key head [Int] Int => [Int]
jwt pad key head [] idx = jwt pad key bytes [] idx
jwt pad key head (b:rest) idx = [b] :: (jwt pad key bytes rest (idx + 1))
fn :: jwt key block String => [Int]
jwt key block secret =
| (str len secret) > 64 = secret sha256 str -> jwt hex to bytes -> jwt pad key bytes 0
| otherwise = secret jwt str to bytes -> jwt pad key bytes 0
fn :: jwt bxor pad str [Int] Int => String
jwt bxor pad str [] _ = ""
jwt bxor pad str (b:rest) pad =
jwt byte bxor b pad -> xb
(xb % 126) + 1 -> safe_c
(char from code safe_c) + (jwt bxor pad str rest pad)
fn :: jwt hmac sha256 b64url String String => String
jwt hmac sha256 b64url secret signing_input =
secret jwt key block -> kb
jwt bxor pad str kb 54 -> ipad
jwt bxor pad str kb 92 -> opad
(ipad + signing_input) -> sha256 str -> inner_hex
(opad + inner_hex) -> sha256 str -> outer_hex
outer_hex jwt hex to bytes -> jwt b64url enc bytes
fn :: jwt hs256 header json Int => String
jwt hs256 header json _ = '{"alg":"HS256","typ":"JWT"}'
fn :: jwt sign hs256 String String => String
jwt sign hs256 payload_json secret =
jwt hs256 header json 0 -> jwt b64url encode -> hdr_b64
payload_json jwt b64url encode -> pay_b64
hdr_b64 + "." + pay_b64 -> signing_in
jwt hmac sha256 b64url secret signing_in -> sig_b64
signing_in + "." + sig_b64
fn :: jwt split dots loop String Int Int Int [String] => [String]
jwt split dots loop s pos len start acc =
| pos >= len = acc :: [str slice s start len]
| (str char at s pos) == 46 = jwt split dots loop s (pos + 1) len (pos + 1) (acc :: [str slice s start pos])
| otherwise = jwt split dots loop s (pos + 1) len start acc
fn :: jwt str list len [String] => Int
jwt str list len [] = 0
jwt str list len (_:rest) = 1 + (jwt str list len rest)
fn :: jwt str at [String] Int => String
jwt str at [] _ = ""
jwt str at (s:_) 0 = s
jwt str at (_:rest) idx = jwt str at rest (idx - 1)
fn :: jwt decode payload String => JwtRes
jwt decode payload token =
jwt split dots loop token 0 (str len token) 0 [] -> parts
jwt decode parts parts
fn :: jwt decode parts [String] => JwtRes
jwt decode parts parts =
| (jwt str list len parts) != 3 = JwtErr "invalid JWT format: expected 3 dot-separated parts"
| (str len (jwt str at parts 0)) == 0 or (str len (jwt str at parts 1)) == 0 or (str len (jwt str at parts 2)) == 0 = JwtErr "invalid JWT format: empty segment"
| not (jwt b64url is valid (jwt str at parts 1)) = JwtErr "invalid JWT payload Base64url encoding"
| otherwise = JwtOk (jwt b64url decode (jwt str at parts 1))
fn :: jwt header is hs256 String => Bool
jwt header is hs256 hdr_b64 =
| not (jwt b64url is valid hdr_b64) = False
| otherwise = hdr_b64 jwt b64url decode -> json parse -> jwt check hdr json
fn :: jwt check hdr json JsonRes => Bool
jwt check hdr json (JsonErr _) = False
jwt check hdr json (JsonOk node) = (json get str node "alg" "") == "HS256"
fn :: jwt verify hs256 String String => JwtRes
jwt verify hs256 token secret =
jwt split dots loop token 0 (str len token) 0 [] -> parts
jwt verify parts parts secret
fn :: jwt verify parts [String] String => JwtRes
jwt verify parts parts secret =
| (jwt str list len parts) != 3 = JwtErr "invalid JWT format"
| (str len (jwt str at parts 2)) == 0 = JwtErr "missing JWT signature"
| not (jwt header is hs256 (jwt str at parts 0)) = JwtErr "unsupported or missing alg: expected HS256"
| otherwise = jwt verify sig (jwt str at parts 0) (jwt str at parts 1) (jwt str at parts 2) secret
fn :: jwt verify sig String String String String => JwtRes
jwt verify sig hdr_b64 pay_b64 sig_b64 secret =
jwt hmac sha256 b64url secret (hdr_b64 + "." + pay_b64) -> expected_sig
jwt verify sig ct (jwt ct eq sig_b64 expected_sig) pay_b64
fn :: jwt verify sig ct Bool String => JwtRes
jwt verify sig ct False _ = JwtErr "JWT signature verification failed"
jwt verify sig ct True pay_b64 =
| not (jwt b64url is valid pay_b64) = JwtErr "invalid payload Base64url"
| otherwise = JwtOk (jwt b64url decode pay_b64)
34.4.4. Internal Implementation: Structured Claims Signing & Temporal Validation
Observe how jwt.rg declares explicit fn :: ... => ... contracts first and resolves each case through aligned equations and tail calls:
fn :: jwt sign claims JwtClaims String => String
jwt sign claims c secret =
JsonObj [JsonField "iss" (JsonStr (issuer c)), JsonField "sub" (JsonStr (subject c)), JsonField "exp" (JsonInt (expires_at c)), JsonField "nbf" (JsonInt (not_before_at c)), JsonField "custom" (JsonStr (custom_json c))] -> base_obj
jwt sign hs256 (json stringify base_obj) secret
fn :: jwt verify claims String String Int => JwtClaimsRes
jwt verify claims token secret now =
jwt verify hs256 token secret -> vres
jwt validate verified payload vres now
fn :: jwt validate verified payload JwtRes Int => JwtClaimsRes
jwt validate verified payload (JwtErr msg) _ = JwtClaimsErr msg
jwt validate verified payload (JwtOk pay_str) now = jwt validate parsed claims (json parse pay_str) now
fn :: jwt validate parsed claims JsonRes Int => JwtClaimsRes
jwt validate parsed claims (JsonErr msg) _ = JwtClaimsErr ("invalid claims JSON: " + msg)
jwt validate parsed claims (JsonOk node) now =
json get str node "iss" "" -> iss
json get str node "sub" "" -> sub
json get int node "exp" 0 -> exp_t
json get int node "nbf" 0 -> nbf_t
json get str node "custom" "" -> cust
jwt check claim times (JwtClaims iss sub exp_t nbf_t cust) now
fn :: jwt check claim times JwtClaims Int => JwtClaimsRes
jwt check claim times c now =
| (not_before_at c) > 0 and now < (not_before_at c) = JwtClaimsErr "JWT token not yet valid (nbf)"
| (expires_at c) > 0 and now >= (expires_at c) = JwtClaimsErr "JWT token expired (exp)"
| otherwise = JwtClaimsOk c
34.5. Algorithmic Guarantees, RMM1/RMM2 Memory, and the 5 Golden Rules of jwt
- Algorithmic & Safety Guarantee: Complete immunity to
"alg":"none"downgrade attacks (any header wherealg != "HS256"returnsJwtBadSignature) and constant-time MAC verification. - $O(1)$ Stack Complexity via TCO: Every recursive loop in
src/main/ridgun/lib/jwt.rgis in tail position, soridgunccompiles them into native jumps with zero stack growth. - Deterministic Memory (
RMM1/RMM2): Zero garbage-collector pauses; all intermediate allocations are reclaimed deterministically on scope exit. - Continuous Verification: Statically checked via
stable checkand tested by the automated suite insrc/test/ridgun/test jwt.rg.
The 5 Golden Rules When Programming with jwt
- Declare
import jwtat the top of your file and take advantage of space/underscore equivalence so your code reads like natural prose. - Align your data pipelines with
=: chainjwtcalls into named intermediate steps and letstable formatalign the columns. - Leverage
jwt's total functions inside|guards: inspect return values or unwrap ADTs via pattern matching rather than assuming external input is valid. - Avoid cloning large structures when only reading: remember that under
RMM2, function input arguments incur zero reference-count overhead (+0/-0). - Pair
jwtwithtest(import test): always lock in your domain invariants withassert equalsorassert true.
In Chapter 35, we will explore the socket lib in full depth (The socket Lib — TCP, UDP, AF_UNIX, Framing, DNS, and RGF-001 Kernel Sockets).