Credentials for MoonBit: JWT, JWK and X.509.
moon add moonbitstack/mooncred// 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=At(seconds),
issuer="moonbitstack", audience=["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 |
| jwe | JSON Web Encryption: five-part compact serialisation, key wrapping, GCM and CBC-HMAC | RFC 7516, RFC 7518, RFC 3394 |
| token | A signed string for a cookie or a URL: expiry on verify, a salt per purpose, key rotation | — |
| 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; read the private key that goes with it | RFC 5280, RFC 6125, RFC 7468, RFC 5958, RFC 5915 |
| csr | Certificate signing requests: read one, check its signature, see what it asks for, and write one | RFC 2986, RFC 2985 §5.4 |
| pbe | Encrypted private keys: PBES2 with PBKDF2, and the older PBES1 and PKCS#12 derivations openssl still writes | RFC 8018, RFC 7292 §B.2 |
| pkcs12 | PFX containers: the certificate chain and the key, with a MAC, read and written | RFC 7292, RFC 5652 |
| revoke | Revocation lists and OCSP responses: read one and judge a certificate good, revoked or unknown | RFC 5280 §5, RFC 6960 |
| policy | Certificate policies over a chain: the §6.1 tree, mappings and the three counters | RFC 5280 §6.1 |
| cbor | CBOR, the subset COSE and CWT are written in | RFC 8949 |
| cose | COSE_Sign1, COSE_Mac0, COSE_Encrypt0, and CWT claim sets | RFC 9052, RFC 9053, RFC 8392 |
| 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="moonbitstack", audience=["moonapi"])
// The same thing written as a record update.
let same : @jwt.Policy = { ..@jwt.policy, issuer: Some("moonbitstack"), audience: ["moonapi"] }
// Used as it is, and used with one thing changed for this call.
let a = @jwt.verify(token[:], HS256, key, now=At(seconds), policy=ours)
let b = @jwt.verify(token[:], HS256, key, now=At(seconds), policy=ours, leeway=60)
// Reading a token without asking whether it has expired.
let c = @jwt.verify(token[:], HS256, key, now=Ignored)let verifier = match key.material() {
Okp(x~, ..) => @ed25519.PublicKey::new(x[:])
_ => abort("not an Ed25519 key")
}let policy = @jwt.Policy::new(issuer="https://issuer.example", leeway=30)
let got = @jwt.verify(token[:], HS256, key, now=At(seconds), issuer="https://issuer.example")let plain = @jwt.sign(claims, HS256, key) // typ: "JWT"
let access = @jwt.sign(claims, HS256, key, typ=Some("at+jwt")) // typ: "at+jwt"
let bare = @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[:])
let serves = cert.matches("a.example.com") // subjectAltName names and addresses
let signed = 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 |
let key = @x509.PrivateKey::pem(text) // PRIVATE KEY, EC PRIVATE KEY, RSA PRIVATE KEY
let spki = @x509.Spki::read(@x509.Spki::encode(key.public()))
let pairs = spki == cert.spki // whether the two belong together// A request to be signed, and the same request read back.
let pem = @csr.self_signed_pem(key, subject=@x509.common_name("csr.example.com"))
let request = @csr.Request::pem(pem)
let sound = request.verified() // the signature, over the octets that arrived
let mine = request.asks_for(key.public()) // whether it asks for this key
// A key at rest, and the container that holds it.
let locked = key.encrypt(passphrase~, salt~, iv~, scheme=Aes256Cbc)
let back = @x509.PrivateKey::read_encrypted(locked[:], passphrase~)
let pfx = @pkcs12.build(certs~, key=Some(key.pkcs8()), passphrase~, salt~, iv~, mac_salt~)
let inside = @pkcs12.parse(pfx[:]).contents(passphrase~)
// Revocation, without a network in the library.
let list = @revoke.Crl::pem(text)
let fresh = list.fresh(now) // thisUpdate and nextUpdate, against the caller's clock
let status = list.status(cert) // Good, Revoked(entry), or Unknownlet text = @token.sign(payload, keys~, salt="session", at=Some(now))
let value = @token.verify(text[:], keys~, salt="session", max_age=Some(86400L), now~)let message = @cose.sign1(payload, alg=Es256, signer, kid=Some(kid))
let opened = @cose.verify1(message[:], alg=Es256, verifier)
let claims = @cose.Claims::new(iss="moonbitstack", exp=4102444800L)
let cwt = @cose.signed(claims, alg=Hs256, mac)
let checked = @cose.verified(cwt[:], alg=Hs256, mac, now=At(seconds))let path = @x509.verify(chain[:], roots, now~)
let certs = []
for cert in path {
certs.push(@policy.read(cert.raw))
}
let held = @policy.policies(certs, initial=["2.23.140.1.2.1"]) // empty: not valid for itInstall
Download zipCredentials for MoonBit: JWT, JWK and X.509.