mooncred — credential formats for MoonBit: JSON Web Tokens, JWS and JWK with the JOSE algorithm set, and X.509 certificates with chain validation to the Mozilla roots, built on mooncrypt's algorithms.
// The key is whatever signs. Here, HMAC over SHA-256.
let key = @jwt.mac(fn() {
@hmac.Hmac::new(secret[:], fn() { @sha2.Hasher::new() })
})
let token = @jwt.sign(claims, HS256, key)
// The algorithm is the caller's, not the token's.
let got = @jwt.verify(token[:], HS256, key, now=now_seconds,
issuer=Some("moonbitstack"), audience=Some("moonapi"))| Package | What | Specification |
|---|---|---|
| jwt | JWS compact serialisation, all thirteen JOSE algorithms | RFC 7515, 7518, 7519, 8037 |
| jwk | JSON Web Key and key sets: reading, writing, choosing | RFC 7517, RFC 7518 §6 |
| x509 | X.509 v3 certificates: read one down to its extensions, check its signature under RSA, ECDSA or Ed25519, match it against a host, validate a chain to a set of trust anchors; build a self-signed P-256 one | RFC 5280, RFC 6125, RFC 7468 |
| roots | The Mozilla CA store as trust anchors, from curl's extract | — |
@jwt.policy < the policy you pass < the arguments you pass// A policy for the deployment, assembled once.
let ours = @jwt.Policy::new(issuer=Some("moonbitstack"), audience=["moonapi"])
// The same thing written as a record update.
let ours : @jwt.Policy = { ..@jwt.policy, issuer: Some("moonbitstack") }
// Used as it is, and used with one thing changed for this call.
@jwt.verify(token[:], HS256, key, now=At(seconds), policy=ours)
@jwt.verify(token[:], HS256, key, now=At(seconds), policy=ours, leeway=60L)
// Reading a token without asking whether it has expired.
@jwt.verify(token[:], HS256, key, now=Ignored)match key.material() {
Okp(x~, ..) => @ed25519.PublicKey::new(x[:])
_ => …
}@jwt.Policy::new(issuer="https://issuer.example", leeway=30)
@jwt.verify(token[:], HS256, key, now=At(seconds), issuer="https://issuer.example")@jwt.sign(claims, HS256, key) // typ: "JWT"
@jwt.sign(claims, HS256, key, typ=Some("at+jwt")) // typ: "at+jwt"
@jwt.sign(claims, HS256, key, typ=None) // no typ header// Validate what a server sent, against the Mozilla roots.
let roots = @roots.mozilla() // build once, keep
let path = @x509.verify(chain[:], roots, now~, host="example.com")
// path: the leaf, the intermediates it runs through, the anchor
// Or one certificate on its own.
let cert = @x509.parse(der[:])
cert.matches("a.example.com") // subjectAltName names and addresses
cert.signed_by(issuer.spki) // raises for an algorithm it does not check| Rule | From |
|---|---|
| Names come from subjectAltName only, never the commonName; an IP literal is held to the iPAddress entries | RFC 6125, Go, webpki |
| Every issuer is a CA, has keyCertSign when it lists key usages, respects its pathLenConstraint; a CA is not a leaf | RFC 5280 §4.2.1.9, webpki |
| Validity on every certificate but the anchor | RFC 5280 §6.1.3 |
| The leaf allows the purpose asked for; an issuer that lists purposes allows it too | RFC 5280 §4.2.1.12, Go |
| An unknown critical extension refuses the chain | RFC 5280 §4.2 |
| Name constraints over dNSName, iPAddress, rfc822Name, directoryName and URI hosts | RFC 5280 §4.2.1.10 |
| RSA PKCS#1 and PSS, ECDSA on four curves, Ed25519; no SHA-1 | webpki, Go |
| At most six intermediates and a hundred signature checks | webpki |
moon add moonbitstack/mooncredInstall
Download zipmooncred — credential formats for MoonBit: JSON Web Tokens, JWS and JWK with the JOSE algorithm set, and X.509 certificates with chain validation to the Mozilla roots, built on mooncrypt's algorithms.