// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
/// Every check is made at one moment, 2026-10-29 00:00 UTC, the day after the
/// openssl fixtures were written, so the transcript is the same on every run.
let now : Int64 = 1793232000L
///|
fn sign_one() -> (String, @jwt.Keyed) raise {
// @@listing sign
let secret = b"a shared secret nobody else has"
let key = @jwt.mac(fn() {
@hmac.Hmac::new(secret[:], fn() { @sha2.Hasher::new() })
})
let claims : Map[String, Json] = {
"sub": "heke1228",
"iss": "moonbitstack",
"aud": "moonapi",
"exp": 1793235600,
}
let token = @jwt.sign(claims, HS256, key)
println(token)
let got = @jwt.verify(
token[:],
HS256,
key,
now=At(now),
issuer="moonbitstack",
audience=["moonapi"],
)
if got.get("sub") is Some(String(sub)) {
println("subject: \{sub}")
}
// @@end
(token, key)
}
///|
fn refuse(token : String, key : @jwt.Keyed) -> Unit raise {
// @@listing refuse
// The same bytes, offered as another algorithm.
try @jwt.verify(token[:], HS384, key, now=At(now)) |> ignore catch {
Algorithm(want~, got~) => println("refused: \{got} is not \{want}")
_ => println("refused")
}
// Two hours on, the token has lapsed.
try @jwt.verify(token[:], HS256, key, now=At(now + 7200)) |> ignore catch {
Expired(at~) => println("refused: expired at \{at.to_int64()}")
_ => println("refused")
}
// Read anyway, with the clock switched off on purpose.
let old = @jwt.verify(token[:], HS256, key, now=Ignored)
println("read with no clock: \{old.length()} claims")
// @@end
}
///|
fn settle(token : String, key : @jwt.Keyed) -> Unit raise {
// @@listing policy
// Assembled once for a deployment.
let ours = @jwt.Policy::new(
issuer="moonbitstack",
audience=["moonapi"],
leeway=30,
)
// The same record, written as an update of the preset.
let same : @jwt.Policy = {
..@jwt.policy,
issuer: Some("moonbitstack"),
audience: ["moonapi"],
leeway: 30,
}
println("same policy: \{ours == same}")
// A hundred seconds past `exp`: outside the policy's leeway.
let late : @jwt.Clock = At(1793235700L)
// This one call allows two minutes; the argument beats the record.
let got = @jwt.verify(token[:], HS256, key, now=late, policy=ours, leeway=120)
println("accepted with leeway 120: \{got.length()} claims")
// `wins=Base` turns that around, and the record's 30 s stands.
try
@jwt.verify(
token[:],
HS256,
key,
now=late,
policy=ours,
leeway=120,
wins=Base,
)
|> ignore
catch {
Expired(..) => println("refused with wins=Base: the policy decides")
_ => println("refused")
}
// @@end
}
///|
fn keys_from_json() -> Unit raise {
// @@listing jwk
let set = @jwk.Set::parse(
(
#|{"keys":[{"kty":"OKP","crv":"Ed25519","kid":"2026-09","alg":"EdDSA",
#|"d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
#|"x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}]}
),
)
guard set.pick(alg="EdDSA", id="2026-09") is Some(jwk) else { return }
guard jwk.material() is Okp(x~, secret=Some(seed), ..) else { return }
let signer = @ed25519.PrivateKey::new(seed[:])
let token = @jwt.sign({ "sub": "heke1228" }, EdDSA, signer, extra={
"kid": "2026-09",
})
// A verifier reads `kid` first, fetches that key, then verifies.
guard @jwt.header(token[:]).get("kid") is Some(String(kid)) else { return }
guard set.by_id(kid) is Some(found) else { return }
guard found.material() is Okp(x=public, ..) else { return }
println("kid \{kid}, same key: \{public == x}")
let got = @jwt.verify(
token[:],
EdDSA,
@ed25519.PublicKey::new(public[:]),
now=At(now),
)
println("EdDSA verified: \{got.length()} claim")
// @@end
}
///|
fn signed_strings() -> Unit raise {
// @@listing token
let keys = @token.Keys::new(b"the key that signs session cookies")
let cookie = @token.sign(b"user=heke1228", keys~, salt="session", at=now)
println(cookie)
let value = @token.verify(
cookie[:],
keys~,
salt="session",
max_age=3600L,
now~,
)
println("value: \{value.length()} bytes")
// A cookie signed for one purpose does not pass for another.
try @token.verify(cookie[:], keys~, salt="reset", now~) |> ignore catch {
Signature => println("refused under salt \"reset\"")
_ => println("refused")
}
// A new key signs from now on; cookies under the old one still read.
let next = keys.rotate(b"next quarter's session key")
let kept = @token.verify(cookie[:], keys=next, salt="session", now~)
println("after rotation: \{kept.length()} bytes")
// @@end
}
///|
fn chains() -> Unit raise {
// @@listing chain
let anchors = @x509.Roots::pem(authority)
let chain = [@x509.pem(good)]
let path = @x509.verify(chain[:], anchors, now~, host="leaf.example.com")
for cert in path {
println(cert.subject.cn().unwrap_or("?"))
}
try
@x509.verify(chain[:], anchors, now~, host="other.example.com") |> ignore
catch {
Host(name) => println("refused: not issued for \{name}")
_ => println("refused")
}
// @@end
}
///|
fn read_one() -> Unit raise {
// @@listing read
let cert = @x509.parse(@x509.pem(good))
println("subject \{cert.subject.cn().unwrap_or("?")}")
println("issuer \{cert.issuer.cn().unwrap_or("?")}")
for name in cert.exts.san {
if name is Dns(host) {
println("dns \{host}")
}
}
println("serves leaf.example.com: \{cert.matches("leaf.example.com")}")
println("valid now: \{cert.valid_at(now)}")
let ca = @x509.parse(@x509.pem(authority))
println("signed by the CA: \{cert.signed_by(ca.spki)}")
// @@end
}
///|
fn issue() -> (@ecdsa.PrivateKey, Bytes) raise {
// @@listing issue
// RFC 6979's P-256 test key; a real one comes from a random source.
let key = @ecdsa.PrivateKey::new(
@base16.decode(
"C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721",
),
)
// The instants are seconds since the Unix epoch, the same unit a certificate's validity is
// read back in, and the extensions are what a client compares and obeys.
let der = @x509.self_signed(
key=Ec(key),
cn="moonbitstack.example",
serial=b"\x01",
not_before=1793232000L,
not_after=1824768000L,
exts={ ..@x509.Exts::empty(), san: [Dns("moonbitstack.example")], },
)
let cert = @x509.parse(der)
println(
"self-signed \{cert.subject.cn().unwrap_or("?")}: \{cert.signed_by(cert.spki)}",
)
println("names its own host: \{cert.matches("moonbitstack.example")}")
// What a CA would ask for instead: a request, signed by the same key.
let pem = @csr.self_signed_pem(
Ec(key),
subject=@x509.common_name("moonbitstack.example"),
)
let request = @csr.Request::pem(pem)
println("request verified: \{request.verified()}")
println("asks for this key: \{request.asks_for(Ec(key.public()))}")
// The same call with a post-quantum key. The identifier in the certificate and the signature
// over it both follow the key, so nothing else changes.
let seed = @base16.decode(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
)
let pq : @x509.PrivateKey = MlDsa(
@mldsa.PrivateKey::from_seed(kind=D65, seed),
)
let pq_cert = @x509.parse(
@x509.self_signed(
key=pq,
cn="pqc.example",
serial=b"\x02",
not_before=1793232000L,
not_after=1824768000L,
),
)
println(
"ML-DSA certificate: \{pq_cert.alg}, \{pq_cert.sig.bytes.length()} octets of signature",
)
// @@end
(key, der)
}
///|
fn at_rest(key : @ecdsa.PrivateKey) -> Unit raise {
// @@listing keys
// Salt and IV are arguments: fresh random bytes in production.
let pem = @x509.PrivateKey::Ec(key).encrypt_pem(
passphrase="correct horse",
salt=b"0123456789abcdef",
iv=b"fedcba9876543210",
)
println(pem.split("\n").next().unwrap_or(""))
let back = @x509.PrivateKey::pem_encrypted(pem, passphrase="correct horse")
println(
"same key: \{back.public() is Ec(public) && public.bytes() == key.public().bytes()}",
)
try
@x509.PrivateKey::pem_encrypted(pem, passphrase="battery staple") |> ignore
catch {
_ => println("wrong passphrase: refused")
}
// @@end
}
///|
fn container(key : @ecdsa.PrivateKey, der : Bytes) -> Unit raise {
// @@listing pkcs12
let pfx = @pkcs12.build(
certs=[der],
key=Some(@x509.PrivateKey::Ec(key).pkcs8()),
passphrase="correct horse",
salt=b"0123456789abcdef",
iv=b"fedcba9876543210",
mac_salt=b"mac salt",
)
let read = @pkcs12.parse(pfx)
println("MAC checks out: \{read.verify(passphrase="correct horse")}")
let inside = read.contents(passphrase="correct horse")
println(
"\{inside.certs.length()} certificate, key inside: \{inside.key is Some(_)}",
)
// @@end
}
///|
fn revocation() -> Unit raise {
// @@listing revoke
let list = @revoke.Crl::pem(crl)
let ca = @x509.parse(@x509.pem(authority))
println("list signed by the CA: \{list.signed_by(ca.spki)}")
println("list still fresh: \{list.fresh(now)}")
for name in ["good", "revoked"] {
let cert = @x509.parse(
@x509.pem(if name == "good" { good } else { revoked }),
)
match list.status(cert) {
Good => println("\{name}: good")
Revoked(_) => println("\{name}: revoked")
Unknown => println("\{name}: unknown")
}
}
// @@end
}
///|
fn encrypted() -> Unit raise {
// @@listing jwe
let shared = b"a 32-byte key for A256GCM, here."
let jwe = @jwe.encrypt(
b"card ending 4242",
key=Shared(shared),
alg=Dir,
enc=A256Gcm,
iv=b"twelve bytes",
)
println(jwe)
let opened = @jwe.decrypt(jwe[:], key=Shared(shared), alg=Dir, enc=A256Gcm)
println("opened: \{opened == b"card ending 4242"}")
// @@end
}
///|
fn cwt() -> Unit raise {
// @@listing cose
// RFC 8032's first Ed25519 test key.
let signer = @ed25519.PrivateKey::new(
@base16.decode(
"9D61B19DEFFD5A60BA844AF492EC2CC44449C5697B326919703BAC031CAE7F60",
),
)
let claims = @cose.Claims::new(
iss="moonbitstack",
sub="heke1228",
exp=1793235600L,
)
let token = @cose.signed(claims, alg=EdDSA, signer)
println("a CWT of \{token.length()} bytes")
let back = @cose.verified(token, alg=EdDSA, signer.public(), now=At(now))
println("issuer: \{back.iss.unwrap_or("?")}")
// @@end
}
///|
fn anchors() -> Unit {
// @@listing roots
let mozilla = @roots.mozilla()
println("\{mozilla.length()} anchors, Mozilla store of \{@roots.version}")
// @@end
}
///|
#coverage.skip
fn main raise {
println("@@sign")
let (token, key) = sign_one()
println("@@refuse")
refuse(token, key)
println("@@policy")
settle(token, key)
println("@@jwk")
keys_from_json()
println("@@token")
signed_strings()
println("@@chain")
chains()
println("@@read")
read_one()
println("@@issue")
let (ec, der) = issue()
println("@@keys")
at_rest(ec)
println("@@pkcs12")
container(ec, der)
println("@@revoke")
revocation()
println("@@jwe")
encrypted()
println("@@cose")
cwt()
println("@@roots")
anchors()
}