The reference, by package.
Every public type, function and value of mooncred 0.6.7, read from each package's interface and the comments above its definitions. The guide shows them at work.
Every public type, function and value of mooncred 0.6.7, read from each package's interface and the comments above its definitions. The guide shows them at work.
JSON Web Signature, RFC 7515 · JSON Web Algorithms, RFC 7518 · JSON Web Token, RFC 7519 · EdDSA for JOSE, RFC 8037
Only the compact serialisation, which is the one tokens are written in. Signing and verification go through mooncrypt/spec's contracts, never through a particular algorithm, so a program that only issues HS256 tokens does not link an elliptic curve.
Refused Alg Clock Keyed OnClash Policy Wins policy claims_unverified header mac sign verify
pub(all) suberror Refused {
Shape(String)
Algorithm(want~ : String, got~ : String)
Signature
Expired(at~ : Double)
TooEarly(at~ : Double)
Issuer(want~ : String, got~ : String)
Audience(want~ : Array[String])
Subject(want~ : String, got~ : String)
IssuedAt(at~ : Double)
Type(want~ : String, got~ : String)
Missing(claim~ : String)
} derive(Eq, @debug.Debug)
Why a token was not accepted.
Each reason is separate so a caller can map it to the right answer — an expired token deserves a different response from a forged one — and so a log says which check failed rather than that one did.
pub(all) enum Alg {
HS256
HS384
HS512
RS256
RS384
RS512
PS256
PS384
PS512
ES256
ES384
ES512
EdDSA
} derive(Eq, @debug.Debug)
A JOSE signature algorithm (RFC 7518 §3.1), and the one RFC 8037 adds.
none is not here and never will be. It is the algorithm that made "verified" mean "not verified" in a dozen libraries, and a type that cannot express it cannot be talked into accepting it.
pub(all) enum Clock {
At(Int64)
Ignored
} derive(Eq, @debug.Debug)
When the time claims are measured against.
A library has no clock — reading one is a side effect and there is no reason for a token verifier to have that power — so the instant comes in from the caller. Ignored is the way to read a token without asking whether it has expired: a debugging tool, an audit of tokens already collected, a migration that has to read what was issued last year. It is spelled out at the call site because the alternative is a verifier that quietly stopped checking.
type Keyed
A keyed MAC, standing in as both signer and verifier.
HMAC is symmetric: the same key that signs verifies. What it is not is a signature — anyone who can check an HS256 token can mint one — so a token verified this way says only that someone holding the shared secret issued it.
pub(all) enum OnClash[T] {
Ignore
Panic
Handle((T, T) -> T)
}
What to do when a setting is given twice.
Handle receives the base and the merged result and returns whichever it wants, so a caller can audit, refuse, or merge some other way entirely.
There is no Warn: a library has nowhere to warn to. It arrives when moonlog does.
pub(all) struct Policy {
leeway : Int64
expiry : Bool
not_before : Bool
issued_at : Bool
issuer : String?
audience : Array[String]
subject : String?
typ : String?
required : Array[String]
} derive(Eq, @debug.Debug)
What a token has to satisfy beyond its signature.
Every field is a check that is either off or told what to expect, and every default is what the mainstream libraries do: exp and nbf are checked, iat is not, and iss, aud, sub and the header's typ are looked at only when the caller says what to expect. Nothing is enforced implicitly and nothing is skipped implicitly.
One of these is assembled once for a deployment and passed to every verification, which is what jsonwebtoken's Validation, .NET's TokenValidationParameters and jose's options object are for.
pub(all) enum Wins {
Base
Extra
} derive(Eq, @debug.Debug)
Which side wins when a setting is given twice.
Every function here that takes both a base and something layered over it says which one wins, and lets a caller say otherwise. The defaults differ by function and each one says why: verify lets the override win, because a per-call argument is more specific than a policy assembled once; sign lets the base win, because the header it computes describes the signature it is about to make.
pub let policy : Policy
The policy a verification uses when the caller does not say otherwise.
pub fn claims_unverified(StringView) -> Map[String, Json] raise Refused
The claims of a token that has not been verified.
Whoever sent the token wrote these. Reading them before verify has returned is reading an attacker's input; it is here because a gateway sometimes must route on iss before it knows which key to fetch, and for no other reason.
pub fn header(StringView) -> Map[String, Json] raise Refused
The header of a token that has not been verified.
This is for one thing: reading kid to choose which key to verify with. Nothing else in here has been checked by anybody.
pub fn mac(() -> &@spec.Mac) -> Keyed
Take a way of making a keyed MAC and use it as a JWT key.
It takes a factory rather than a MAC because one state cannot authenticate two messages, and a key outlives the token it was used on.
pub fn sign(Map[String, Json], Alg, &@spec.Signer, typ? : String?, extra? : Map[String, Json], wins? : Wins, clash? : OnClash[Map[String, Json]]) -> String
Sign a claims set as a compact JWT.
extra goes into the header beside alg and typ — kid is what it is usually for, so a verifier can pick the right key out of a set before it verifies anything.
typ is "JWT", which is what every library writes and what RFC 7519 §5.1 recommends. None leaves it out; another string puts that in — RFC 9068's access tokens want "at+jwt".
**extra cannot quietly rewrite alg or typ.** The header describes the signature this function is about to make; a token whose header says none while the bytes say HS256 is not a configuration choice, it is a mistake, so wins starts at Base and clash at Panic. A caller who means it can say wins=Extra, clash=Ignore.
The key is whatever can sign; this package never chooses an algorithm's implementation, so a program that issues only HS256 tokens links only HMAC.
pub fn verify(StringView, Alg, &@spec.Verifier, now~ : Clock, policy? : Policy, wins? : Wins, clash? : OnClash[Policy], leeway? : Int64, expiry? : Bool, issuer? : String, audience? : Array[String]) -> Map[String, Json] raise Refused
Verify a compact JWT and return its claims.
**The algorithm is the caller's, not the token's.** A verifier that reads alg out of the header and then trusts it is how an RS256 public key gets used as an HS256 shared secret, and how none gets accepted; here the header must match what the caller already decided, or the token is refused.
The signature is checked before any claim is read, so a forged token is never inspected for its contents.
Configuration has three layers, each overriding the one before: @jwt.policy < the policy given here < the named arguments given here. wins turns the last two around; clash decides whether being given a setting twice is silent, fatal, or yours to arbitrate.
now is not configuration but data: the instant the time claims are measured against, or Ignored to read a token without asking whether it has expired — a debugging tool, an audit of tokens already collected, a migration that has to read what was issued last year.
pub fn Alg::name(Self) -> String
The name this algorithm goes by in a header.
pub fn Alg::of(StringView) -> Self raise Refused
Read an algorithm by name.
pub fn Keyed::sign(Self, BytesView) -> Bytes
@spec.Signer's sign, callable on the value directly.
pub fn Keyed::verify(Self, BytesView, BytesView) -> Bool
@spec.Verifier's verify, callable on the value directly.
pub fn Policy::new(leeway? : Int64, expiry? : Bool, not_before? : Bool, issued_at? : Bool, issuer? : String, audience? : Array[String], subject? : String, typ? : String, required? : Array[String]) -> Self
Build a policy by naming the parts that differ from the default.
The same thing can be written { ..@jwt.policy, expiry: false }; this form exists because a named argument reads better when several parts differ. issuer, subject and typ can be set here but not cleared: the preset leaves all three unset, so there is nothing to clear. Starting from a base of one's own, { ..mine, issuer: None } is how a check is dropped.
JSON Web Key, RFC 7517 · Key parameters by type, RFC 7518 §6 · Octet key pairs, RFC 8037 §2
Reading and writing the format, and choosing a key out of a set. Turning the material into something that can sign belongs to the program, which knows which algorithms it wants linked.
pub(all) suberror Refused {
Shape(String)
Missing(field~ : String)
NotBase64(field~ : String)
Unknown(kty~ : String)
} derive(Eq, @debug.Debug)
Why a JWK could not be read.
type Jwk
One key, with the metadata a chooser needs.
pub(all) enum Material {
Oct(key~ : Bytes)
Rsa(modulus~ : Bytes, exponent~ : Bytes, secret~ : Bytes?)
Ec(curve~ : String, x~ : Bytes, y~ : Bytes, secret~ : Bytes?)
Okp(curve~ : String, x~ : Bytes, secret~ : Bytes?)
} derive(Eq, @debug.Debug)
The key material a JWK carries, by key type (RFC 7518 §6).
Bytes, not keys: turning these into something that can sign would mean choosing an implementation for every algorithm, and then a program that reads one JWK would link all of them. The two lines that build a key from this material belong to the program, which knows which algorithms it wants.
type Set
A JWK Set (RFC 7517 §5) — what a jwks_uri serves.
pub fn Jwk::alg(Self) -> String?
alg, the one algorithm this key is for, when it says.
pub fn Jwk::id(Self) -> String?
kid, the name a verifier picks this key out of a set by.
pub fn Jwk::is_private(Self) -> Bool
Whether the key carries a private part.
A key set published at a well-known URL must answer false for every key in it, which is worth asserting in a test rather than discovering in a log.
pub fn Jwk::json(Self, secret? : Bool) -> Json
Write the key back out.
The private part is left out unless secret says otherwise, because the common reason to write a JWK is to publish it and the common accident is to publish the whole thing.
pub fn Jwk::material(Self) -> Material
The key material itself.
pub fn Jwk::ops(Self) -> Array[String]
key_ops, empty when the key does not say.
pub fn Jwk::parse(StringView) -> Self raise Refused
Read one key from its JSON text.
pub fn Jwk::read(Json) -> Self raise Refused
Read one key from a parsed document.
pub fn Jwk::usage(Self) -> String?
use: "sig" or "enc".
pub fn Set::by_id(Self, StringView) -> Jwk?
The key with this kid.
pub fn Set::keys(Self) -> Array[Jwk]
Every key that was understood.
pub fn Set::parse(StringView, skip? : Bool) -> Self raise Refused
Read a set from its JSON text.
pub fn Set::pick(Self, alg~ : String, id? : String, usage? : String?, first? : Bool) -> Jwk?
The key to verify a token with: the one named by kid if the token names one, otherwise the only candidate for this algorithm.
A key that says nothing about its algorithm is a candidate for any; one that names a different algorithm, or is marked for encryption, is not. When several remain, none is returned — picking one arbitrarily is how a rotated key silently keeps verifying — and first is how a caller asks for the other behaviour.
usage is the use a candidate must carry when it carries one; "sig" is the default because this is a signature library. None accepts any.
pub fn Set::read(Json, skip? : Bool) -> Self raise Refused
Read a set from a parsed document.
A key the reader does not understand is skipped rather than fatal: RFC 7517 §5.1 says so, and a set that grows a post-quantum key must not stop every verifier that has not heard of it. skip turns that off for a caller who would rather hear about it.
JSON Web Encryption, RFC 7516 · JSON Web Algorithms, RFC 7518 · AES Key Wrap, RFC 3394
The compact serialisation only, which is the one that travels in a header or a URL: five base64url parts — the header, the wrapped key, the IV, the ciphertext and the tag.
Refused Alg Enc Identity Key decrypt encrypt unwrap wrap
pub(all) suberror Refused {
Malformed(String)
Unsupported(String)
Key
} derive(Eq, @debug.Debug)
Why a token could not be sealed or opened.
pub(all) enum Alg {
Dir
A128Kw
A192Kw
A256Kw
RsaOaep256
} derive(Eq, @debug.Debug)
The key-management algorithm: how the content-encryption key reaches the recipient.
pub(all) enum Enc {
A128Gcm
A192Gcm
A256Gcm
A128CbcHs256
A192CbcHs384
A256CbcHs512
} derive(Eq, @debug.Debug)
The content-encryption algorithm: authenticated encryption over the payload.
pub(all) enum Identity {
Shared(Bytes)
Rsa(@rsa.PrivateKey)
}
What a token is opened with.
pub(all) enum Key {
Shared(Bytes)
Rsa(@rsa.PublicKey)
}
What a token is sealed to.
pub fn decrypt(StringView, key~ : Identity, alg~ : Alg, enc~ : Enc) -> Bytes raise Refused
Open a token, checking that the header names the algorithms the caller expects.
The expected alg and enc are arguments and not read from the header on the caller's behalf for the reason JWT's none exists: a recipient that believes whatever the token says about itself can be talked into the weakest algorithm it supports. A token that names another algorithm is Malformed rather than opened under the caller's.
pub fn encrypt(BytesView, key~ : Key, alg~ : Alg, enc~ : Enc, cek? : Bytes?, iv~ : BytesView, seed? : BytesView) -> String raise Refused
Seal a payload under a key, into the compact serialisation: five base64url parts, the header first, so the recipient knows what to do before it does anything.
cek is the content-encryption key and iv the nonce; both are the caller's, for the reason this library has no generator. Under dir the CEK is the shared key and cek must be None; under any other algorithm it is required and must be cek_size octets. seed is what RSA-OAEP-256 needs to make its padding: the algorithm is randomised, and the randomness has to come from somewhere that is not here.
pub fn unwrap(BytesView, BytesView) -> Bytes raise Refused
The inverse: recover the key, or Malformed when the octets are not a wrapped key under this one. The integrity check is the initial value coming back out of the last round — that is the whole reason the algorithm mixes a counter into it.
pub fn wrap(BytesView, BytesView) -> Bytes raise Refused
Wrap a content-encryption key under a key-encryption key. The plaintext must be a whole number of eight-octet blocks and at least sixteen octets, which is what every CEK in RFC 7518 is.
Signed tokens: HMAC over the caller's own octets, in a string a URL or a cookie can carry.
The shape is itsdangerous': a base64url payload, a base64url timestamp, and a base64url tag, separated by dots. What keeps two uses of one key apart is the salt, which is hashed into the tag and never travels with it, so a token signed for one purpose cannot be replayed against another even though the key is the same.
pub(all) suberror Refused {
Malformed(String)
Signature
Expired
} derive(Eq, @debug.Debug)
Why a token was not accepted.
pub(all) struct Keys {
current : Bytes
previous : Array[Bytes]
}
The keys a token may be verified against: the one signing now, and the ones retired but still honoured, newest first. Rotating is a push, and every token signed by the old key keeps working until the call that drops it.
pub fn sign(BytesView, keys~ : Keys, salt? : String, at? : Int64?) -> String
Sign payload and return the token: payload.timestamp.tag, each part base64url without padding, so it survives a URL, a cookie and a header unchanged.
salt is what makes the signature specific to a purpose. It is not sent with the token; it is hashed into the tag, so a token issued by the password-reset path cannot be presented to the session path even when both use one key.
pub fn verify(StringView, keys~ : Keys, salt? : String, max_age? : Int64?, now~ : Int64) -> Bytes raise Refused
The payload of a token, if the tag is one this key set could have made and the timestamp is inside max_age seconds of now.
A token with no timestamp has no age, so max_age does not apply to it: the format allows one and the caller that signs without at is saying the token does not expire. Pass at for anything that should.
pub fn Keys::new(BytesView) -> Self
A key set with one key in it.
pub fn Keys::rotate(Self, BytesView) -> Self
The same set with current retired and next signing from now on. A token signed a moment before the call still verifies; one signed a moment after verifies under the new key.
X.509 v3 certificates and certification paths, RFC 5280 · Service identity, RFC 6125 · PEM, RFC 7468 · Private keys, RFC 5958, RFC 5915, RFC 8017 §A.1.2
Read a certificate down to its extensions, check its signature under RSA, ECDSA or Ed25519, match it against a host name or address, and validate a chain to a set of trust anchors. Writes the self-signed P-256 certificate a TLS server needs, and reads its private key.
Refused Attr Cert Constraints Exts GeneralName Key Name Policy PrivateKey Roots Spki Usage Validity eku_any eku_client_auth eku_server_auth oid_cn policy alg_ec_key alg_ecdsa_sha256 algorithm common_name dns_match exts name parse pem pem_wrap pems self_signed signature_ok spki_of tbs_of validity_of verify
pub(all) suberror Refused {
Malformed(at~ : Int32, String)
Expired(at~ : Int32)
Premature(at~ : Int32)
Issuer
Authority(at~ : Int32)
Signature(at~ : Int32)
Depth(limit~ : Int32)
Budget(limit~ : Int32)
Host(String)
Purpose(at~ : Int32)
Critical(at~ : Int32, oid~ : String)
Constraint(at~ : Int32, name~ : String)
Algorithm(at~ : Int32, oid~ : String)
Weak(at~ : Int32)
Comparisons(limit~ : Int32)
} derive(Eq, @debug.Debug)
Why a chain was refused. at is the position in the chain as given — 0 the leaf, then the intermediates in the order they arrived — of the certificate the refusal is about.
pub(all) struct Attr {
oid : String
value : String
} derive(Eq, @debug.Debug)
One AttributeTypeAndValue: the attribute's OID in dotted-decimal form, and its value as text.
pub(all) struct Cert {
raw : BytesView
tbs : BytesView
version : Int32
serial : @bigint.BigInt
alg : String
params : Bytes?
issuer : Name
validity : Validity
subject : Name
spki : Spki
exts : Exts
sig : @asn1.Bits
}
A parsed certificate. tbs is the exact encoding the signature covers and raw the whole certificate, both as views into the input: a signature is checked over the bytes that arrived, never over a re-encoding of what they were understood to mean. params is the encoding of the signature algorithm's parameters, which RSASSA-PSS needs.
pub(all) struct Constraints {
permitted : Array[GeneralName]
excluded : Array[GeneralName]
} derive(Eq, @debug.Debug)
A NameConstraints (RFC 5280 §4.2.1.10): the subtrees every name below the CA must fall in, and the ones none may.
pub(all) struct Exts {
san : Array[GeneralName]
ca : Bool
path_len : Int32?
key_usage : @asn1.Bits?
eku : Array[String]?
ski : Bytes?
aki : Bytes?
constraints : Constraints?
unknown_critical : Array[String]
} derive(Eq, @debug.Debug)
The extensions a certificate is judged on (RFC 5280 §4.2). key_usage bits are numbered from the top of the first octet — 0 digitalSignature, 5 keyCertSign. eku is None when the extension is absent, which means any purpose, as against a listed set that does not include the one being asked for. ski and aki are the key identifiers path building uses to try the likeliest issuer first. unknown_critical names the critical extensions this parser does not understand: §4.2 makes such a certificate unusable, and a caller can only obey that rule if it is told rather than left to assume the skip was safe.
pub(all) enum GeneralName {
Dns(String)
Email(String)
Uri(String)
Ip(Bytes)
Dir(Name)
Named(Int32, Bytes)
} derive(Eq, @debug.Debug)
A GeneralName (RFC 5280 §4.2.1.6). The forms that name a peer are decoded; the rest keep their field number and contents, so a caller can see what was there instead of being told the extension was empty. In a name constraint an Ip is an address followed by its mask, eight or thirty-two octets; in a subjectAltName it is the bare address.
pub(all) enum Key {
Rsa(@rsa.PublicKey)
Ec(@ecdsa.PublicKey)
Ed25519(@ed25519.PublicKey)
}
A public key as a signature can be checked against. An RSA or EC key is held with the digest its own algorithm defaults to; a certificate's signature algorithm decides the digest it is actually checked with, so the key is re-read for each signature rather than fixed here.
pub(all) struct Name {
rdns : Array[Array[Attr]]
raw : Bytes
} derive(Eq, @debug.Debug)
A distinguished name as it is encoded — a sequence of relative distinguished names, each a set of attributes — together with the encoding itself. Path building matches an issuer to its subject by those octets, as webpki and Go do: two readings of one name must not decide whether a certificate chains.
pub(all) struct Policy {
usage : Usage
depth : Int32
budget : Int32
comparisons : Int32
bits : Int32
} derive(Eq, @debug.Debug)
What a validation asks for and how far it may go. depth bounds the intermediates on the path, budget the signature checks spent finding it, comparisons the name-constraint comparisons, and bits the smallest RSA modulus a signature is accepted under.
pub(all) enum PrivateKey {
Rsa(@rsa.PrivateKey)
Ec(@ecdsa.PrivateKey)
Ed25519(@ed25519.PrivateKey)
}
A private key as a server signs with: what a PRIVATE KEY, EC PRIVATE KEY or RSA PRIVATE KEY file holds.
pub struct Roots {
Trust anchors. An anchor is a name and a key: its own validity and signature are never checked, which is what lets a long-lived root carry a SHA-1 self-signature. Its name constraints, when it has them, bind everything below it.
pub(all) struct Spki {
alg : String
params : String?
key : @asn1.Bits
} derive(Eq, @debug.Debug)
A SubjectPublicKeyInfo: the algorithm OID, the parameter OID when the algorithm takes one (for an EC key, the named curve), and the key bits. Absent and NULL parameters both read as None, which is what every algorithm reachable from here means by either.
pub(all) enum Usage {
Server
Client
Any
Other(String)
} derive(Eq, @debug.Debug)
The key purpose the leaf must allow (RFC 5280 §4.2.1.12). Any asks for none and checks nothing; Other names a purpose by OID.
pub(all) struct Validity {
not_before : Int64
not_after : Int64
} derive(Eq, @debug.Debug)
A validity window, in seconds since the Unix epoch.
pub let eku_any : String
anyExtendedKeyUsage (RFC 5280 §4.2.1.12).
pub let eku_client_auth : String
id-kp-clientAuth.
pub let eku_server_auth : String
id-kp-serverAuth: the purpose a TLS server certificate needs (RFC 5280 §4.2.1.12).
pub let oid_cn : String
commonName (X.520 §6.2.2), the attribute a name is usually read from.
pub let policy : Policy
A server certificate; webpki's limits — six intermediates, a hundred signature checks, a quarter of a million name-constraint comparisons; RSA keys of 2048 bits and up (CA/Browser Forum Baseline Requirements §6.1.5).
pub fn alg_ec_key() -> Bytes
The id-ecPublicKey with the prime256v1 (P-256) curve AlgorithmIdentifier (RFC 5480).
pub fn alg_ecdsa_sha256() -> Bytes
The ecdsa-with-SHA256 AlgorithmIdentifier (RFC 5758): a SEQUENCE of just the OID, ECDSA taking no parameters.
pub fn algorithm(BytesView) -> (String, Bytes?) raise @asn1.Refused
An AlgorithmIdentifier from its DER: the OID in dotted-decimal form and the parameters as they were encoded, or None when the identifier has none. A certificate, a request, a revocation list and a response all carry one, and signature_ok takes exactly this pair.
pub fn common_name(String) -> Bytes
A Name with a single commonName attribute: RDNSequence → RelativeDistinguishedName (SET) → AttributeTypeAndValue (SEQUENCE of the commonName OID 2.5.4.3 and the UTF8String value).
pub fn dns_match(String, String) -> Bool
Whether the dNSName pattern names host (RFC 6125 §6.4.3): label by label, ASCII case insensitive. A first label of exactly * stands for one whole label and needs two more after it, so *.a.example covers b.a.example and neither a.example nor c.b.a.example, and *.com covers nothing. A * anywhere else, or fused to the rest of its label as in b*.a.example, matches nothing — a partial wildcard is a claim on a name the issuer never checked. A pattern or host that is not a well-formed DNS name matches nothing.
pub fn exts(BytesView) -> Exts raise @asn1.Refused
The extensions a certificate carries, from the Extensions SEQUENCE on its own — the same encoding arrives in a certificate request's extensionRequest attribute and in a revocation list, so it is read here rather than at each of the three callers.
pub fn name(BytesView) -> Name raise @asn1.Refused
A name from the DER of its Name SEQUENCE, for the callers outside a certificate: a certificate request's subject, a revocation list's issuer, a responder's name.
pub fn parse(BytesView) -> Cert raise @asn1.Refused
Parse a DER certificate (RFC 5280 §4.1). Raises on anything that is not exactly one well-formed certificate: a truncated or over-long field, a payload glued to the end, a version that does not admit the fields present, or an outer signature algorithm that disagrees with the one the signature actually covers.
pub fn pem(String) -> Bytes raise @asn1.Refused
The DER inside a PEM envelope (RFC 7468).
Everything outside the -----BEGIN ...----- and -----END ...----- lines is dropped and the rest is base64-decoded. The label is not checked: what the octets are is the caller's question, and a CERTIFICATE and a PUBLIC KEY reach different readers from here.
Whitespace inside the body is ignored, which is what RFC 7468 §2 asks of a parser and what makes a CRLF file read the same as an LF one.
pub fn pem_wrap(BytesView, label? : String) -> String
The PEM envelope around DER (RFC 7468 §2): the label, the base64 body broken at sixty-four columns, and the closing line. What this writes, openssl reads — the line length and the trailing newline are the parts a strict reader notices.
pub fn pems(String, label? : String) -> Array[Bytes] raise @asn1.Refused
The DER of every PEM envelope in text with the label label, in order — a CA bundle such as ca-certificates.crt or curl's cacert.pem is a run of CERTIFICATE envelopes with prose between them. Envelopes with another label are passed over.
pub fn self_signed(@ecdsa.PrivateKey, Bytes, String, String, String) -> Bytes
A self-signed X.509 certificate: build the TBSCertificate for key's public key, sign its DER with ES256, DER-encode the r/s signature (RFC 5280 requires the ECDSA-Sig-Value SEQUENCE, not the raw concatenation), and wrap TBS + algorithm + signature in the outer Certificate SEQUENCE.
pub fn signature_ok(BytesView, alg~ : String, params~ : Bytes?, sig~ : @asn1.Bits, key~ : Spki) -> Bool raise @asn1.Refused
Check a signature made over tbs against key. The algorithm is named by its OID and the parameters of its AlgorithmIdentifier, exactly as a certificate, a certificate request, a revocation list or a response carries them.
RSA PKCS#1 v1.5 and PSS with SHA-256, SHA-384 or SHA-512, ECDSA with the same three digests on any of the four curves, and Ed25519. An algorithm outside that set raises Unsupported rather than returning false, so a caller cannot read "not checked" as "checked and bad". A key of the wrong kind for the algorithm returns false: no signature by that algorithm verifies under it.
pub fn spki_of(@ecdsa.PublicKey) -> Bytes
A SubjectPublicKeyInfo for a P-256 key: the ecPublicKey/prime256v1 algorithm and the uncompressed point as a BIT STRING.
pub fn tbs_of(Bytes, String, String, String, @ecdsa.PublicKey) -> Bytes
The TBSCertificate (RFC 5280 §4.1.2): version v3 [0] EXPLICIT INTEGER 2, the serial number, the signature algorithm, the issuer, the validity, the subject, and the SubjectPublicKeyInfo. Self-signed, so issuer and subject are the same commonName. Extensions are omitted (a valid, minimal profile).
pub fn validity_of(String, String) -> Bytes
A Validity: notBefore and notAfter as UTCTime YYMMDDHHMMSSZ.
pub fn verify(ArrayView[Bytes], Roots, now~ : Int64, host? : String, policy? : Policy) -> Array[Cert] raise Refused
Validate a chain and return the path found: the leaf, the intermediates it runs through, and the anchor it ends on.
chain is the leaf followed by the intermediates the peer sent, in any order; ones the path does not need are ignored, which is what a TLS server's habit of sending a cross-signed extra asks for. now is the time to judge validity at, in seconds since the Unix epoch. host, when given, is a DNS name or an IP literal the leaf must name; without it names are not compared.
The rules, and whose they are: - Names come from subjectAltName only, never the commonName (RFC 6125, Go since 1.15, webpki). - Every issuer must be a CA, have keyCertSign when it lists key usages, and respect its pathLenConstraint. A leaf that is a CA is refused, as webpki does. - Validity is checked on every certificate but the anchor. - The leaf must allow policy.usage; an issuer that lists key purposes must allow it too (Go and the Mozilla root programme chain the restriction). - An unknown critical extension anywhere on the path refuses it. - Name constraints bind dNSName, iPAddress, rfc822Name, directoryName and the host of a URI; a constraint of another form, over a name of that form, refuses rather than guesses. - Several candidate issuers with the issuer's name are each tried, preferring the one whose subjectKeyIdentifier matches the authorityKeyIdentifier; every signature check spends from policy.budget.
When no path works, the refusal is the first reason a candidate path failed, or Issuer when there was no candidate at all.
pub fn Cert::matches(Self, String) -> Bool
Whether host is one of the certificate's identities: a DNS name against the subjectAltName dNSName entries, an IPv4 or IPv6 literal against the iPAddress entries. The two never cross — reading an address as a name is how a certificate for a name comes to be trusted for an address. A certificate with no subjectAltName matches nothing: the commonName stopped being an identity with RFC 6125, and Go and webpki no longer fall back to it.
pub fn Cert::signed_by(Self, Spki) -> Bool raise @asn1.Refused
Check the certificate's signature under issuer — its issuer's key, or its own if it is self-signed.
pub fn Cert::valid_at(Self, Int64) -> Bool
Whether now, in seconds since the Unix epoch, falls inside the certificate's validity window. Both ends are inclusive: RFC 5280 §4.1.2.5 gives them as the first and last instant the certificate is valid, not as an open interval.
pub fn Exts::empty() -> Self
An Exts with nothing in it — the starting point for a caller assembling the extensions a certificate or a request should ask for.
pub fn Exts::encode(Self) -> Bytes raise @asn1.Refused
The Extensions SEQUENCE for these extensions (RFC 5280 §4.2), in the order this package writes them. An extension it reads but does not write — a name constraint, or an unknown critical one whose value was dropped on the way in — is Unsupported rather than silently left out, because leaving it out changes what the certificate means.
pub fn Name::cn(Self) -> String?
The name's commonName, if it has one.
pub fn Name::get(Self, String) -> String?
The value of the first attribute with this OID, in encoding order.
pub fn Policy::new(usage? : Usage, depth? : Int32, budget? : Int32, comparisons? : Int32, bits? : Int32) -> Self
A policy with any field changed from the preset.
pub fn PrivateKey::encrypt(Self, passphrase~ : StringView, salt~ : BytesView, iv~ : BytesView, scheme? : @pbe.Scheme, rounds? : Int32) -> Bytes raise @pbe.Refused
Encrypt a key under a passphrase, as an EncryptedPrivateKeyInfo (RFC 5958 §2).
salt and iv are the caller's, for the reason this library has no generator: a salt must be fresh for every file ever written, and only the caller knows where freshness comes from.
pub fn PrivateKey::encrypt_pem(Self, passphrase~ : StringView, salt~ : BytesView, iv~ : BytesView, scheme? : @pbe.Scheme, rounds? : Int32) -> String raise @pbe.Refused
The same as an ENCRYPTED PRIVATE KEY file — the text openssl pkcs8 -topk8 writes and openssl pkcs8 -in reads.
pub fn PrivateKey::pem(String) -> Self raise @asn1.Refused
Read the first private key in a PEM file, under any of the labels openssl writes: PRIVATE KEY, EC PRIVATE KEY (after which an EC PARAMETERS block is passed over) or RSA PRIVATE KEY. An encrypted key, in either the PKCS#8 or the legacy Proc-Type form, is Unsupported here: pem_encrypted is the reader that has a passphrase to work with.
pub fn PrivateKey::pem_encrypted(String, passphrase~ : StringView) -> Self raise @pbe.Refused
The same from the PEM text around it. A file that holds a key this package cannot decrypt is Passphrase when the octets do not open, which is what a wrong passphrase looks like.
pub fn PrivateKey::pkcs8(Self) -> Bytes raise @asn1.Refused
The PKCS#8 PrivateKeyInfo for a key in hand (RFC 5958): the version, the algorithm, and the SEC1 or CurvePrivateKey octets inside an OCTET STRING.
An RSA key cannot be rebuilt from @rsa.PrivateKey: the type keeps its primes to itself and offers no way to read them back, so a key that arrived as PKCS#1 or PKCS#8 cannot be turned into the other without the octets it arrived in. The caller who has those octets — the file they read the key from — encrypts them directly.
pub fn PrivateKey::pkcs8_pem(Self) -> String raise @asn1.Refused
The plainkey PKCS#8 file this one would have been before a passphrase was put on it, for the keys where that can be rebuilt at all.
pub fn PrivateKey::public(Self) -> Key
The public half: what the certificate for this key carries, so key.public() == spki.key() tells whether a certificate and a key belong together.
pub fn PrivateKey::read(BytesView) -> Self raise @asn1.Refused
Read a private key from DER in any of the three forms in use: PKCS#8 (RFC 5958), SEC1's ECPrivateKey (RFC 5915), or PKCS#1's RSAPrivateKey (RFC 8017 §A.1.2). The form is told by the second element of the outer SEQUENCE, as OpenSSL tells it.
An RSA key whose primes do not multiply to its modulus, an EC key whose scalar is out of range or whose embedded public key is not its own, and an Ed25519 seed of the wrong length are all Malformed. An algorithm that does not sign (X25519) or a multi-prime RSA key is Unsupported.
pub fn PrivateKey::read_encrypted(BytesView, passphrase~ : StringView) -> Self raise @pbe.Refused
Read an encrypted PKCS#8 key — an ENCRYPTED PRIVATE KEY file — with the passphrase that protects it. PBES2 with PBKDF2 and AES or triple DES, and the PBES1 schemes a PKCS#12 file or an older openssl pkcs8 -v1 writes, all open here.
pub fn Roots::add(Self, BytesView) -> Unit raise @asn1.Refused
Add one anchor. The same certificate added twice is kept once.
pub fn Roots::iter(Self) -> Iter[Cert]
The anchors, in the order they were added.
pub fn Roots::length(Self) -> Int32
How many anchors there are.
pub fn Roots::new(certs? : ArrayView[Bytes]) -> Self raise @asn1.Refused
Anchors from DER certificates.
pub fn Roots::pem(String) -> Self raise @asn1.Refused
Anchors from a PEM bundle such as /etc/ssl/certs/ca-certificates.crt or curl's cacert.pem.
pub fn Spki::encode(Key) -> Bytes
The SubjectPublicKeyInfo for a public key: the algorithm, the parameters that algorithm takes — NULL for RSA, the named curve for EC, none for Ed25519 — and the key as a BIT STRING. This is what a certificate, a request or a key file carries, built from a key in hand.
pub fn Spki::key(Self) -> Key raise @asn1.Refused
The public key a SubjectPublicKeyInfo carries: RSA, ECDSA on P-256, P-384, P-521 or secp256k1, or Ed25519. Another algorithm raises Unsupported.
pub fn Spki::p256(Self) -> @ecdsa.PublicKey raise @asn1.Refused
The P-256 public key a SubjectPublicKeyInfo carries. Refuses any other curve.
pub fn Spki::read(BytesView) -> Self raise @asn1.Refused
Read a bare SubjectPublicKeyInfo.
A certificate carries one in Cert::spki; this is for the ones that travel on their own — a PUBLIC KEY PEM file, or the key MySQL's caching_sha2_password hands a client mid-handshake.
pub fn Spki::rsa(Self, digest? : @rsa.Digest, scheme? : @rsa.Scheme) -> @rsa.PublicKey raise @asn1.Refused
The RSA public key a SubjectPublicKeyInfo carries.
The key bits are an RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER } (RFC 8017 §A.1.1). Refuses any algorithm but rsaEncryption.
digest and scheme are the signature parameters the key is built with. They do not enter encryption at all — @rsa.PublicKey::encrypt takes its own — so a caller that only encrypts can leave them alone.
PKCS#10 certification requests, RFC 2986 · Attributes, RFC 2985
Read the request a client sends before it has a certificate, check the signature that proves it holds the key it asks to have certified, and write one for a key in hand.
Request parse self_signed self_signed_pem
pub(all) struct Request {
raw : BytesView
tbs : BytesView
subject : @x509.Name
spki : @x509.Spki
exts : @x509.Exts
alg : String
params : Bytes?
sig : @asn1.Bits
}
A parsed certification request. tbs is the exact CertificationRequestInfo the signature covers, kept as a view into the input for the same reason a certificate keeps its own: a signature is checked over the octets that arrived, never over a re-encoding of them.
pub fn parse(BytesView) -> Request raise @asn1.Refused
Read a DER certification request.
Anything that is not exactly one well-formed request is Malformed: a version other than v1, a field past the attributes, an attribute that repeats, or an extensionRequest that does not carry exactly one Extensions. The signature is not checked here — verified does that, and keeping the two apart is what lets a caller read a request it cannot yet verify.
pub fn self_signed(@x509.PrivateKey, subject~ : Bytes, exts? : @x509.Exts?) -> Bytes raise @asn1.Refused
Write a certification request for a key in hand (RFC 2986 §4).
subject is the DER of a Name — @x509.common_name for the usual case — and exts the extensions to ask for, which travel in an extensionRequest attribute. The request is signed by key, so the CA that reads it can tell the requester holds the private half.
pub fn self_signed_pem(@x509.PrivateKey, subject~ : Bytes, exts? : @x509.Exts?) -> String raise @asn1.Refused
The same request inside the PEM envelope openssl req writes.
pub fn Request::asks_for(Self, @x509.Key) -> Bool
Whether the request asks for the certificate of this key: the key inside the request and this one are the same. A CA compares before it issues, so a request and the key it was made from cannot be swapped for another pair.
pub fn Request::der(Self) -> BytesView
The DER this request arrived as.
pub fn Request::pem(String) -> Self raise @asn1.Refused
Read the first certification request in a PEM file. openssl req writes the label CERTIFICATE REQUEST; the older NEW CERTIFICATE REQUEST is read too, because files with it are still in the wild.
pub fn Request::to_pem(Self) -> String
The PEM envelope around this request, in the label openssl req writes.
pub fn Request::verified(Self) -> Bool raise @asn1.Refused
Whether the request's signature verifies under the public key it carries. A request is always self-signed — that is what separates it from a certificate — so the key to check against is the one inside it, and an algorithm this package does not implement raises Unsupported rather than returning false.
Encrypted private keys, PKCS#8 · PBES2, PBKDF2 and PBES1, RFC 8018 · PKCS#12 password-based encryption, RFC 7292 §B
Reading and writing an EncryptedPrivateKeyInfo: the PBES2 schemes built on PBKDF2, the older PBES1 schemes PKCS#12 files still carry, and the three-key triple DES and RC2 the legacy ones are keyed with.
Refused Encrypted Kdf Prf Scheme decrypt encrypt pkcs12_key pkcs12_key_with
pub(all) suberror Refused {
Malformed(String)
Unsupported(String)
Passphrase
} derive(Eq, @debug.Debug)
Why an encrypted key could not be read or written.
pub(all) struct Encrypted {
raw : Bytes
alg : String
kdf : Kdf
cipher : String?
iv : Bytes?
icv : Int32
body : Bytes
} derive(Eq, @debug.Debug)
A parsed EncryptedPrivateKeyInfo. body is the ciphertext; everything else is what is needed to turn it back into a PrivateKeyInfo.
pub(all) enum Kdf {
Pbkdf2(salt~ : Bytes, rounds~ : Int32, prf~ : String, length~ : Int32?)
Pkcs12(salt~ : Bytes, rounds~ : Int32)
Pbkdf1(salt~ : Bytes, rounds~ : Int32, md5~ : Bool)
} derive(Eq, @debug.Debug)
The key derivation a file names, with what it needs to run.
pub(all) enum Prf {
Sha1
Sha224
Sha256
Sha384
Sha512
}
The pseudorandom function PBKDF2 is keyed with (RFC 8018 §5.2).
pub(all) enum Scheme {
Aes128Cbc
Aes192Cbc
Aes256Cbc
Aes128Gcm
Aes192Gcm
Aes256Gcm
TripleDesCbc
LegacyTripleDes
LegacyRc2
}
How to write an encrypted key. The first six are PBES2 with PBKDF2; the last two are the PBES1 schemes a PKCS#12 file needs when it has to be readable by a client from before 2010.
pub fn decrypt(BytesView, BytesView, passphrase~ : StringView) -> Bytes raise Refused
Decrypt ciphertext under an AlgorithmIdentifier that arrived on its own, outside an EncryptedPrivateKeyInfo. A PKCS#12 EncryptedData carries its scheme this way, and so does a CMS EnvelopedData; wrapping the two fields back into the file they would have been is what lets one reader serve them all.
pub fn encrypt(BytesView, passphrase~ : StringView, salt~ : BytesView, iv~ : BytesView, scheme? : Scheme, rounds? : Int32) -> Bytes raise Refused
Encrypt a PrivateKeyInfo under a passphrase.
salt and iv are the caller's: a salt must be new for every key ever written, and this library has no generator to draw one from. Sixteen octets of salt is what PBKDF2 is usually given; the IV must be the cipher's block size — sixteen for AES, eight for triple DES, and twelve for a GCM nonce.
pub fn pkcs12_key(StringView, BytesView, id~ : Int32, rounds~ : Int32, len~ : Int32) -> Bytes raise Refused
The same derivation with SHA-1, which is what PBES1 under the PKCS#12 OIDs is defined with.
pub fn pkcs12_key_with(prf~ : Prf, StringView, BytesView, id~ : Int32, rounds~ : Int32, len~ : Int32) -> Bytes raise Refused
The PKCS#12 key derivation (RFC 7292 §B.2). id says what the key is for: 1 an encryption key, 2 an IV, 3 a MAC key.
The hash is a parameter here because a PKCS#12 file's MAC names its own digest — OpenSSL 3 writes SHA-256 where the 1999 files had SHA-1 — and the width the derivation mixes in is that hash's block size, not its output length.
pub fn Encrypted::algorithm(Self) -> Bytes raise Refused
The encryptionAlgorithm field of this file, as DER — the AlgorithmIdentifier a container needs when it stores these octets somewhere other than an EncryptedPrivateKeyInfo, as a PKCS#12 EncryptedData does.
pub fn Encrypted::decrypt(Self, passphrase~ : StringView) -> Bytes raise Refused
Recover the PrivateKeyInfo these octets hold.
pub fn Encrypted::name(Self) -> String
What the file calls itself, for a log line or an error message.
pub fn Encrypted::parse(BytesView) -> Self raise Refused
Read an EncryptedPrivateKeyInfo.
PKCS#12 personal information exchange, RFC 7292 · PKCS#7 content types, RFC 5652 §3
Read and write the container a key and its certificates travel in: the AuthenticatedSafe, the SafeBags inside it, both ways of encrypting a bag, and the digest that ties the whole file to the passphrase. The file's MAC is checked before anything is decrypted, because a container that does not authenticate should not be asked to decrypt.
Refused Contents Mac Pfx build parse
pub(all) suberror Refused {
Malformed(String)
Unsupported(String)
Passphrase
}
Why a container could not be read or written: the octets are not a PFX, a scheme or a bag type is not one this package implements, or the passphrase is not the file's.
pub(all) struct Contents {
certs : Array[Bytes]
key : Bytes?
}
What a container held once it was opened: the certificates in the order the bags carried them, and the private key as a PKCS#8 PrivateKeyInfo.
pub(all) struct Mac {
alg : String
digest : Bytes
salt : Bytes
iterations : Int32
}
The MAC a PFX carries (RFC 7292 §4): the digest algorithm, the digest, the salt it was stretched with, and the round count. OpenSSL 3 writes SHA-256 where the 1999 files had SHA-1.
pub(all) struct Pfx {
raw : BytesView
version : Int32
auth_safe : BytesView
mac : Mac?
}
A parsed PFX. auth_safe is the AuthenticatedSafe octets the MAC covers, kept as a view for the reason every signature in this library is checked over what arrived: re-encoding before verifying would check a file that nobody sent.
pub fn build(certs~ : ArrayView[Bytes], key~ : Bytes?, passphrase~ : StringView, salt~ : BytesView, iv~ : BytesView, mac_salt~ : BytesView, scheme? : @pbe.Scheme, rounds? : Int32, mac_rounds? : Int32) -> Bytes raise Refused
Write a container holding certs and, when there is one, key — a PKCS#8 PrivateKeyInfo.
The certificates go into an EncryptedData under scheme and the key into a shrouded key bag under the same one, which is the shape OpenSSL writes and the shape every client since 1999 knows how to open. salt and iv are the caller's for the reason this library has no generator; the same pair encrypts both bags, so they must be fresh for every file, and the MAC has a salt of its own because it is the one value a reader can check without a plaintext.
pub fn parse(BytesView) -> Pfx raise Refused
Parse a PFX. Only the envelope is read here: the bags need the passphrase, so contents is the call that opens them, and verify is the one that says whether the file was edited.
pub fn Pfx::contents(Self, passphrase~ : StringView) -> Contents raise Refused
Open the container: the certificates every bag carried, and the private key with its shrouding removed. The MAC is not checked here — verify is — because a caller may legitimately want to look inside a file it cannot authenticate, and a library that refuses to say what is in a file until it likes the file is a library that cannot be used to find out why.
pub fn Pfx::has_mac(Self) -> Bool
Whether the file carries a MAC at all. A PFX without one is not tampered with by definition — there is nothing to check it against — but it is also not authenticated, and a caller that needs that distinction can only make it if it is told.
pub fn Pfx::verify(Self, passphrase~ : StringView) -> Bool raise Refused
Whether the MAC this file carries is the one its passphrase produces over its AuthenticatedSafe.
False for a file with no MAC: nothing was verified, which is not the same as verified good. The comparison is over the whole digest and does not stop at the first octet that differs, so the answer leaks nothing about how close a guess was.
Certificate revocation, RFC 5280 §5 · Online Certificate Status Protocol, RFC 6960
Both answer the same question — is this certificate still good — and neither answers it about the network. Fetching a list or a response is the caller's business; reading one, checking that the issuer signed it, and deciding what it says about a certificate is this package's.
Basic Crl Entry Ocsp Responder Revoked Single Status
pub(all) struct Basic {
tbs : BytesView
responder : Responder
produced_at : Int64
responses : Array[Single]
alg : String
params : Bytes?
sig : @asn1.Bits
}
The signed part of a response (RFC 6960 §4.2.1): who answered, when, and what it said.
pub(all) struct Crl {
raw : BytesView
tbs : BytesView
issuer : @x509.Name
this_update : Int64
next_update : Int64?
entries : Array[Entry]
alg : String
params : Bytes?
sig : @asn1.Bits
}
A parsed CertificateList (RFC 5280 §5.1). tbs is the exact TBSCertList the signature covers.
pub(all) struct Entry {
serial : @bigint.BigInt
revoked : Revoked
}
One line of a revocation list.
pub(all) struct Ocsp {
raw : BytesView
status : Int32
basic : Basic?
}
A whole OCSPResponse (RFC 6960 §4.2.1): the transport-level status, and the signed answer when that status is successful.
pub(all) enum Responder {
ByName(@x509.Name)
ByKey(Bytes)
} derive(Eq, @debug.Debug)
How the responder names itself (RFC 6960 §4.2.2.3): by its distinguished name, or by a hash of its public key.
pub(all) struct Revoked {
at : Int64
reason : Int32?
} derive(Eq, @debug.Debug)
A withdrawal: the time it took effect and the reason the issuer gave.
pub(all) struct Single {
hash : String
issuer_name_hash : Bytes
issuer_key_hash : Bytes
serial : @bigint.BigInt
status : Status
this_update : Int64
next_update : Int64?
}
One answer inside a response: which certificate it is about, what the answer is, and the window it is good for.
pub(all) enum Status {
Good
Revoked(Revoked)
Unknown
} derive(Eq, @debug.Debug)
What a revocation source says about a certificate.
pub fn Basic::fresh(Self, Int64) -> Bool
Whether every answer in the response is current at now.
pub fn Basic::signed_by(Self, @x509.Spki) -> Bool raise @asn1.Refused
Whether the response was signed by issuer. A responder may sign with a key of its own — the certs field exists for that — in which case the caller passes the responder's key instead.
pub fn Crl::fresh(Self, Int64) -> Bool
Whether the list is current at now, in seconds since the Unix epoch: thisUpdate has passed and nextUpdate has not. A list with no nextUpdate never expires by its own terms, which RFC 5280 §5.1.2.5 allows and which a caller has to weigh for itself.
pub fn Crl::parse(BytesView) -> Self raise @asn1.Refused
Read a DER CertificateList.
The version is optional and the entry list is optional, so an empty list — a CA that has withdrawn nothing — is a list with no entries rather than a malformed one. Anything that is not exactly one well-formed list is Malformed.
pub fn Crl::pem(String) -> Self raise @asn1.Refused
Read the first revocation list in a PEM file, under the label openssl ca -gencrl writes.
pub fn Crl::signed_by(Self, @x509.Spki) -> Bool raise @asn1.Refused
Whether the list was signed by issuer — the key of the CA whose certificates it speaks for. Until this is true, nothing else the list says means anything.
pub fn Crl::status(Self, @x509.Cert) -> Status
What the list says about this certificate.
Unknown when the list belongs to another issuer: a list only speaks for the certificates its issuer signed, and serials are the issuer's own numbering. Freshness and the signature are the caller's to check — signed_by and fresh are why they can.
pub fn Ocsp::parse(BytesView) -> Self raise @asn1.Refused
Parse a DER OCSPResponse.
A response whose status is not successful carries no answer and is read as such rather than refused: tryLater and unauthorized are things a responder says, and a caller that treats them as a parse failure cannot retry the one it should.
pub fn Ocsp::status(Self, @x509.Cert, @x509.Cert) -> Status raise @asn1.Refused
What the response says about this certificate, given the issuer that signed it.
The CertID is what makes the answer about this certificate: the hash of the issuer's name, the hash of its public key and the serial all have to match, so a response about another certificate under another CA is Unknown rather than an answer that was taken on faith.
Certificate policies, RFC 5280 §6.1
Additive: path validation in x509 is untouched, and a caller that never asks for a policy never links this.
Refused Cert any_policy policies read
pub(all) suberror Refused {
Malformed(String)
} derive(Eq, @debug.Debug)
Why a certificate's policies could not be read.
pub(all) struct Cert {
policies : Array[String]?
mappings : Array[(String, String)]
require_explicit : Int32?
inhibit_mapping : Int32?
inhibit_any : Int32?
} derive(Eq, @debug.Debug)
The policy extensions of one certificate, as they bear on the tree.
pub let any_policy : String
anyPolicy (RFC 5280 §4.2.1.4): the identifier that stands for every policy.
pub fn policies(Array[Cert], initial~ : Array[String], any? : Bool) -> Array[String]
The policies that survive the chain, in the order they were first named.
An empty result is the answer §6.1 gives a chain that is not valid for the initial policy set. initial is the set the relying party is willing to accept; empty means any policy, which is what a caller with no opinion means.
pub fn read(BytesView) -> Cert raise Refused
Read the four policy extensions out of a certificate.
pub fn Cert::empty() -> Self
A certificate that constrains nothing, which is what a certificate with none of these extensions is.
COSE, RFC 9052 · COSE algorithms, RFC 9053 · CWT, RFC 8392
Three structures — COSE_Sign1, COSE_Mac0, COSE_Encrypt0 — and the claim set CWT signs.
Refused Alg Claims Clock Keyed Message decrypt0 encrypt0 mac mac0 macd parse sign1 signed uncovered verified verify1 verify_mac0
pub(all) suberror Refused {
Malformed(String)
Unsupported(String)
Signature
Key
Expired(at~ : Int64)
TooEarly(at~ : Int64)
} derive(Eq, @debug.Debug)
Why a message could not be made or opened.
pub(all) enum Alg {
Es256
EdDsa
Rs256
Hs256
A128Gcm
A192Gcm
A256Gcm
} derive(Eq, @debug.Debug)
The algorithm, by its COSE identifier (RFC 9053).
One enumeration covers signing, MAC and encryption because a COSE header names all three in the same alg label; which of them a value means is decided by the structure it appears in, and a value that cannot be that structure's is refused.
pub(all) struct Claims {
iss : String?
sub : String?
aud : String?
exp : Int64?
nbf : Int64?
iat : Int64?
cti : Bytes?
} derive(Eq, @debug.Debug)
The registered claims of RFC 8392 §3.1.
pub(all) enum Clock {
At(Int64)
Ignored
} derive(Eq, @debug.Debug)
When the caller says it is.
The clock is an argument rather than something this library reads, for the reason none of these packages calls the system: a verifier that consults its own clock cannot be tested at the moment a token expires, and one that cannot be tested that way is one nobody checks.
pub struct Keyed {
A keyed MAC, standing in as both signer and verifier, the way jwt.mac does: it takes a factory because one MAC state cannot authenticate two messages and a key outlives the message it was used on.
pub(all) struct Message {
head : Bytes
payload : Bytes
tail : Bytes
} derive(Eq, @debug.Debug)
A parsed COSE message: the head header as it arrived, the payload, and whatever else the structure carries.
pub fn decrypt0(BytesView, alg~ : Alg, key~ : BytesView, external? : BytesView) -> Bytes raise Refused
Open a COSE_Encrypt0: the nonce comes from the head header, which is authenticated, so a message whose header was changed does not open.
pub fn encrypt0(BytesView, alg~ : Alg, key~ : BytesView, iv~ : BytesView, kid? : BytesView?, external? : BytesView) -> Bytes raise Refused
Encrypt a payload into a COSE_Encrypt0, under a key the caller supplies.
The nonce is not generated here for the reason no other package generates one: entropy is the caller's to find, and a library that quietly reuses a nonce under a long-lived key is worse than one that asks.
pub fn mac(() -> &@spec.Mac) -> Keyed
A MAC factory as a Keyed, which makes and checks COSE_Mac0 tags.
pub fn mac0(BytesView, alg~ : Alg, &@spec.Signer, kid? : BytesView?, external? : BytesView) -> Bytes raise Refused
MAC a payload into a COSE_Mac0.
pub fn macd(Claims, alg~ : Alg, &@spec.Signer, kid? : BytesView?, external? : BytesView) -> Bytes raise Refused
MAC a claim set into a COSE_Mac0, and verify one back.
pub fn parse(BytesView, tag~ : UInt64) -> Message raise Refused
Read one of the three single-recipient structures, checking its tag and its shape.
tag is the number the caller expects, so a COSE_Mac0 handed to a verifier of signatures is refused rather than verified under the wrong rule.
pub fn sign1(BytesView, alg~ : Alg, &@spec.Signer, kid? : BytesView?, external? : BytesView) -> Bytes raise Refused
Sign a payload into a COSE_Sign1, wrapped in its tag.
alg is not consulted to choose a key — the signer knows its own — but it is written into the head header and it must be the algorithm the signer actually produces, or the message names something it is not. external is the additional data of RFC 9052 §4.4, which a protocol uses to bind a message to its context; the empty byte string is the usual choice.
pub fn signed(Claims, alg~ : Alg, &@spec.Signer, kid? : BytesView?, external? : BytesView) -> Bytes raise Refused
Sign a claim set into a COSE_Sign1.
pub fn uncovered(BytesView, alg~ : Alg, &@spec.Verifier, now~ : Clock, leeway? : Int64, external? : BytesView) -> Claims raise Refused
Verify a COSE_Mac0 made by macd and read the claim set out of it, judging its times as verified does.
pub fn verified(BytesView, alg~ : Alg, &@spec.Verifier, now~ : Clock, leeway? : Int64, external? : BytesView) -> Claims raise Refused
Verify a COSE_Sign1 that carries a claim set, and judge its times.
now has no default: exp and nbf are checked against it with leeway seconds of slack either side, and now=Ignored skips the check, which a caller has to write out. The sixty-second leeway is python-cwt's.
pub fn verify1(BytesView, alg~ : Alg, &@spec.Verifier, external? : BytesView) -> Bytes raise Refused
Check a COSE_Sign1 and hand back its payload.
pub fn verify_mac0(BytesView, alg~ : Alg, &@spec.Verifier, external? : BytesView) -> Bytes raise Refused
Check a COSE_Mac0 and hand back its payload.
pub fn Alg::name(Self) -> String
The algorithm's name as JOSE spells it, for messages and logs; on the wire COSE writes the integer identifier instead.
pub fn Claims::check(Self, now~ : Clock, leeway? : Int64) -> Unit raise Refused
Whether the claim set is valid at now, which is the only judgement a verifier can make without knowing what the token is for.
The leeway is the seconds of slack allowed either side, for the reason a signer's clock and a verifier's rarely agree exactly: with a leeway of sixty, a token that expired half a minute ago still passes.
pub fn Claims::new(iss? : String, sub? : String, aud? : String, exp? : Int64, nbf? : Int64, iat? : Int64, cti? : Bytes) -> Self
A claim set from the claims given; the others stay absent, not empty.
pub fn Claims::of(@cbor.Value) -> Self raise Refused
Read a claim set back, keeping the seven claims and dropping the rest.
pub fn Claims::value(Self) -> @cbor.Value
The claim set as the CBOR map it is signed as: only the claims that are set, in the order the numbers put them in, so that two encodings of the same claims are the same octets.
pub fn Keyed::sign(Self, BytesView) -> Bytes
@spec.Signer's sign, callable on the value directly.
pub fn Keyed::verify(Self, BytesView, BytesView) -> Bool
@spec.Verifier's verify, callable on the value directly.
What COSE and CWT are written in, and nothing more: the subset those two use, with definite lengths only.
pub(all) suberror Refused {
Malformed(String)
Unsupported(String)
} derive(Eq, @debug.Debug)
Why some octets are not a CBOR item.
pub(all) enum Value {
Int(Int64)
Bytes(Bytes)
Text(String)
Array(Array[Value])
Map(Array[(Value, Value)])
Tag(UInt64, Value)
Bool(Bool)
Null
Undefined
Simple(Byte)
} derive(Eq, @debug.Debug)
A CBOR item.
Int covers both major types 0 and 1, so an integer reads as the number it is rather than as a sign and a magnitude the caller has to combine. That costs the top half of the unsigned range, which is refused on the way in: nothing in a credential or a token is a 2^63-byte count.
pub fn decode(BytesView) -> Value raise Refused
Read one item, and insist that nothing follows it.
pub fn encode(Value) -> Bytes
The octets of an item: definite lengths, and the shortest head that holds the argument.
pub fn Value::bytes(Self) -> Bytes?
The octets a value holds, if it holds any.
pub fn Value::get(Self, Self) -> Self?
Look a key up in a map, which is a sequence of pairs rather than a lookup: the first pair with that key wins, as it would in an object read in order.
pub fn Value::int(Self) -> Int64?
The integer a key holds, if it holds one.
The Mozilla CA certificate store, from curl's extract
Its own package so a program that brings its own anchors never links the two hundred kilobytes of the bundle.
pub let count : Int32
How many certificates the bundle holds.
pub let version : String
The date of the Mozilla store this bundle was extracted from.
pub fn mozilla() -> @x509.Roots
The Mozilla CA store as trust anchors: the roots Firefox trusts for TLS servers, as curl extracts them. Each call parses the bundle afresh, so the set returned is the caller's to add to; build it once and keep it.