Sign in

    moonbitstack/mooncred/x509 does not have a README file

    Refused

    pub(all) suberror Refused {
    Malformed(at~ : Int, String)
    Expired(at~ : Int)
    Premature(at~ : Int)
    Issuer
    Authority(at~ : Int)
    Signature(at~ : Int)
    Depth(limit~ : Int)
    Budget(limit~ : Int)
    Host(String)
    Purpose(at~ : Int)
    Critical(at~ : Int, oid~ : String)
    Constraint(at~ : Int, name~ : String)
    Algorithm(at~ : Int, oid~ : String)
    } derive(Eq,
    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.

    Refused::equal

    fn Refused::equal(Refused, Refused) -> Bool

    Refused::not_equal

    fn Refused::not_equal(x : Refused, y : Refused) -> Bool

    Refused::to_repr

    Attr

    pub(all) struct Attr {
    oid : String
    value : String
    } derive(Eq,
    Debug
    )

    One AttributeTypeAndValue: the attribute's OID in dotted-decimal form, and its value as text.

    Attr::equal

    fn Attr::equal(Attr, Attr) -> Bool

    Attr::not_equal

    fn Attr::not_equal(x : Attr, y : Attr) -> Bool

    Attr::to_repr

    Cert

    pub(all) struct Cert {
    raw : BytesView
    tbs : BytesView
    version : Int
    serial :
    BigInt

    alg : String
    params : Bytes?
    issuer : Name
    validity : Validity
    subject : Name
    spki : Spki
    exts : Exts
    sig :
    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.

    Cert::matches

    fn Cert::matches(self : Cert, host : 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.

    Cert::signed_by

    fn Cert::signed_by(self : Cert, issuer : Spki) -> Bool raise
    Refused

    Check the certificate's signature under issuer — its issuer's key, or its own if it is self-signed. 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.

    Cert::valid_at

    fn Cert::valid_at(self : Cert, now : 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.

    Constraints

    pub(all) struct Constraints {
    permitted : Array[GeneralName]
    excluded : Array[GeneralName]
    } derive(Eq,
    Debug
    )

    A NameConstraints (RFC 5280 §4.2.1.10): the subtrees every name below the CA must fall in, and the ones none may.

    Constraints::equal

    fn Constraints::equal(Constraints, Constraints) -> Bool

    Constraints::not_equal

    fn Constraints::not_equal(x : Constraints, y : Constraints) -> Bool

    Exts

    pub(all) struct Exts {
    san : Array[GeneralName]
    ca : Bool
    path_len : Int?
    key_usage :
    Bits
    ?
    eku : Array[String]?
    ski : Bytes?
    aki : Bytes?
    constraints : Constraints?
    unknown_critical : Array[String]
    } derive(Eq,
    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.

    Exts::equal

    fn Exts::equal(Exts, Exts) -> Bool

    Exts::not_equal

    fn Exts::not_equal(x : Exts, y : Exts) -> Bool

    Exts::to_repr

    GeneralName

    pub(all) enum GeneralName {
    Dns(String)
    Email(String)
    Uri(String)
    Ip(Bytes)
    Dir(Name)
    Named(Int, Bytes)
    } derive(Eq,
    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.

    GeneralName::equal

    fn GeneralName::equal(GeneralName, GeneralName) -> Bool

    GeneralName::not_equal

    fn GeneralName::not_equal(x : GeneralName, y : GeneralName) -> Bool

    Key

    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.

    Name

    pub(all) struct Name {
    rdns : Array[Array[Attr]]
    raw : Bytes
    } derive(Eq,
    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.

    Name::cn

    fn Name::cn(self : Name) -> String?

    The name's commonName, if it has one.

    Name::equal

    fn Name::equal(Name, Name) -> Bool

    Name::get

    fn Name::get(self : Name, oid : String) -> String?

    The value of the first attribute with this OID, in encoding order.

    Name::not_equal

    fn Name::not_equal(x : Name, y : Name) -> Bool

    Name::to_repr

    Policy

    pub(all) struct Policy {
    usage : Usage
    depth : Int
    budget : Int
    } derive(Eq,
    Debug
    )

    What a validation asks for and how far it may go. depth bounds the intermediates on the path and budget the signature checks spent finding it; the presets are webpki's.

    Policy::equal

    fn Policy::equal(Policy, Policy) -> Bool

    Policy::new

    fn Policy::new(usage? : Usage, depth? : Int, budget? : Int) -> Policy

    A policy with any field changed from the preset.

    Policy::not_equal

    fn Policy::not_equal(x : Policy, y : Policy) -> Bool

    Policy::to_repr

    Roots

    pub struct Roots {
    // private fields
    }

    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.

    Roots::add

    fn Roots::add(self : Roots, der : BytesView) -> Unit raise
    Refused

    Add one anchor. The same certificate added twice is kept once.

    Roots::iter

    fn Roots::iter(self : Roots) -> Iter[Cert]

    The anchors, in the order they were added.

    Roots::length

    fn Roots::length(self : Roots) -> Int

    How many anchors there are.

    Roots::new

    Anchors from DER certificates.

    Roots::pem

    fn Roots::pem(bundle : String) -> Roots raise
    Refused

    Anchors from a PEM bundle such as /etc/ssl/certs/ca-certificates.crt or curl's cacert.pem.

    Spki

    pub(all) struct Spki {
    alg : String
    params : String?
    key :
    Bits

    } derive(Eq,
    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.

    Spki::equal

    fn Spki::equal(Spki, Spki) -> Bool

    Spki::key

    The public key a SubjectPublicKeyInfo carries: RSA, ECDSA on P-256, P-384, P-521 or secp256k1, or Ed25519. Another algorithm raises Unsupported.

    Spki::not_equal

    fn Spki::not_equal(x : Spki, y : Spki) -> Bool

    Spki::p256

    The P-256 public key a SubjectPublicKeyInfo carries. Refuses any other curve.

    Spki::read

    fn Spki::read(der : BytesView) -> Spki raise
    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.

    Spki::rsa

    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.

    Spki::to_repr

    Usage

    pub(all) enum Usage {
    Server
    Client
    Any
    Other(String)
    } derive(Eq,
    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.

    Usage::equal

    fn Usage::equal(Usage, Usage) -> Bool

    Usage::not_equal

    fn Usage::not_equal(x : Usage, y : Usage) -> Bool

    Usage::to_repr

    Validity

    pub(all) struct Validity {
    not_before : Int64
    not_after : Int64
    } derive(Eq,
    Debug
    )

    A validity window, in seconds since the Unix epoch.

    Validity::equal

    fn Validity::equal(Validity, Validity) -> Bool

    Validity::not_equal

    fn Validity::not_equal(x : Validity, y : Validity) -> Bool

    Validity::to_repr

    alg_ec_key

    fn alg_ec_key() -> Bytes

    The id-ecPublicKey with the prime256v1 (P-256) curve AlgorithmIdentifier (RFC 5480).

    alg_ecdsa_sha256

    fn alg_ecdsa_sha256() -> Bytes

    The ecdsa-with-SHA256 AlgorithmIdentifier (RFC 5758): a SEQUENCE of just the OID, ECDSA taking no parameters.

    common_name

    fn common_name(cn : 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).

    dns_match

    fn dns_match(pattern : String, host : 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, so *.a.example covers b.a.example and neither a.example nor c.b.a.example. 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.

    eku_any

    let eku_any : String

    anyExtendedKeyUsage (RFC 5280 §4.2.1.12).

    eku_client_auth

    let eku_client_auth : String

    id-kp-clientAuth.

    eku_server_auth

    let eku_server_auth : String

    id-kp-serverAuth: the purpose a TLS server certificate needs (RFC 5280 §4.2.1.12).

    oid_cn

    let oid_cn : String

    commonName (X.520 §6.2.2), the attribute a name is usually read from.

    parse

    fn parse(der : BytesView) -> Cert raise
    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.

    pem

    fn pem(text : String) -> Bytes raise
    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.

    pems

    fn pems(text : String, label? : String) -> Array[Bytes] raise
    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.

    policy

    let policy : Policy

    A server certificate, at most six intermediates, at most a hundred signature checks.

    self_signed

    fn self_signed(key :
    PrivateKey
    , serial : Bytes, cn : String, not_before : String, not_after : 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.

    spki_of

    A SubjectPublicKeyInfo for a P-256 key: the ecPublicKey/prime256v1 algorithm and the uncompressed point as a BIT STRING.

    tbs_of

    fn tbs_of(serial : Bytes, cn : String, not_before : String, not_after : String, pub_key :
    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).

    validity_of

    fn validity_of(not_before : String, not_after : String) -> Bytes

    A Validity: notBefore and notAfter as UTCTime YYMMDDHHMMSSZ.

    verify

    fn verify(chain : ArrayView[Bytes], roots : 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.