package x509

  1. Overview
  2. Docs

Chain Validation.

A chain of pairwise signed X.509 certificates is sent to the endpoint, which use these to authenticate the other endpoint. Usually a set of trust anchors is configured on the endpoint, and the chain needs to be rooted in one of the trust anchors. In reality, chains may be incomplete or reversed, and there can be multiple paths from the leaf certificate to a trust anchor.

RFC 5280 specifies a path validation algorithm for authenticating chains, but this does not handle multiple possible paths. RFC 4158 describes possible path building strategies.

This module provides path building, chain of trust verification, trust anchor (certificate authority) validation, and validation via a fingerprint list (for a trust on first use implementation).

Certificate Authorities

type ca_error = [
  1. | `CAIssuerSubjectMismatch of Certificate.t
  2. | `CAInvalidVersion of Certificate.t
  3. | `CAInvalidSelfSignature of Certificate.t
  4. | `CACertificateExpired of Certificate.t * Ptime.t option
  5. | `CAInvalidExtensions of Certificate.t
]

The polymorphic variant of possible certificate authorities failures.

val pp_ca_error : ca_error Fmt.t

pp_ca_error ppf ca_error pretty-prints the CA error ca_error.

val valid_ca : ?hash_whitelist:Mirage_crypto.Hash.hash list -> ?time:Ptime.t -> Certificate.t -> (unit, ca_error) Rresult.result

valid_ca ~hash_whitelist ~time certificate is result, which is Ok () if the given certificate is self-signed with any hash algorithm of hash_whitelist (defaults to any hash), it is valid at time, its extensions are not present (if X.509 version 1 certificate), or are appropriate for a CA (BasicConstraints is present and true, KeyUsage extension contains keyCertSign).

val valid_cas : ?hash_whitelist:Mirage_crypto.Hash.hash list -> ?time:Ptime.t -> Certificate.t list -> Certificate.t list

valid_cas ~hash_whitelist ~time certificates is valid_certificates, only those certificates which pass the valid_ca check.

Chain of trust verification

type leaf_validation_error = [
  1. | `LeafCertificateExpired of Certificate.t * Ptime.t option
  2. | `LeafInvalidName of Certificate.t * [ `host ] Domain_name.t option
  3. | `LeafInvalidVersion of Certificate.t
  4. | `LeafInvalidExtensions of Certificate.t
]

The polymorphic variant of a leaf certificate validation error.

type chain_validation_error = [
  1. | `IntermediateInvalidExtensions of Certificate.t
  2. | `IntermediateCertificateExpired of Certificate.t * Ptime.t option
  3. | `IntermediateInvalidVersion of Certificate.t
  4. | `ChainIssuerSubjectMismatch of Certificate.t * Certificate.t
  5. | `ChainAuthorityKeyIdSubjectKeyIdMismatch of Certificate.t * Certificate.t
  6. | `ChainInvalidSignature of Certificate.t * Certificate.t
  7. | `ChainInvalidPathlen of Certificate.t * int
  8. | `EmptyCertificateChain
  9. | `NoTrustAnchor of Certificate.t
  10. | `Revoked of Certificate.t
]

The polymorphic variant of a chain validation error.

val build_paths : Certificate.t -> Certificate.t list -> Certificate.t list list

build_paths server rest is paths, which are all possible certificate paths starting with server. These chains (C1..Cn) fulfill the predicate that each certificate Cn is issued by the next one in the chain (C(n+1)): the issuer of Cn matches the subject of C(n+1). This is as described in RFC 4158.

type chain_error = [
  1. | `Leaf of leaf_validation_error
  2. | `Chain of chain_validation_error
]

The polymorphic variant of a chain validation error: either the leaf certificate is problematic, or the chain itself.

val pp_chain_error : chain_error Fmt.t

pp_chain_error ppf chain_error pretty-prints the chain_error.

val verify_chain : host:[ `host ] Domain_name.t option -> time:(unit -> Ptime.t option) -> ?revoked:(issuer:Certificate.t -> cert:Certificate.t -> bool) -> ?hash_whitelist:Mirage_crypto.Hash.hash list -> anchors:Certificate.t list -> Certificate.t list -> (Certificate.t, chain_error) Rresult.result

verify_chain ~host ~time ~revoked ~hash_whitelist ~anchors chain is result, either Ok and the trust anchor used to verify the chain, or Error and the chain error. RFC 5280 describes the implemented path validation algorithm: The validity period of the given certificates is checked against the time. The signature algorithm must be present in hash_whitelist (defaults to SHA-2). The X509v3 extensions of the chain are checked, then a chain of trust from anchors to the server certificate is validated. The path length constraints are checked. The server certificate is checked to contain the given host, using hostnames. The returned certificate is the root of the chain, a member of the given list of anchors.

type fingerprint_validation_error = [
  1. | `ServerNameNotPresent of Certificate.t * [ `host ] Domain_name.t
  2. | `NameNotInList of Certificate.t
  3. | `InvalidFingerprint of Certificate.t * Cstruct.t * Cstruct.t
]

The polymorphic variant of a fingerprint validation error.

type validation_error = [
  1. | `EmptyCertificateChain
  2. | `InvalidChain
  3. | `Leaf of leaf_validation_error
  4. | `Fingerprint of fingerprint_validation_error
]

The polymorphic variant of validation errors.

val pp_validation_error : validation_error Fmt.t

pp_validation_error ppf validation_error pretty-prints the validation_error.

val verify_chain_of_trust : host:[ `host ] Domain_name.t option -> time:(unit -> Ptime.t option) -> ?revoked:(issuer:Certificate.t -> cert:Certificate.t -> bool) -> ?hash_whitelist:Mirage_crypto.Hash.hash list -> anchors:Certificate.t list -> Certificate.t list -> r

verify_chain_of_trust host ~time ~revoked ~hash_whitelist ~anchors certificates is result. First, all possible paths are constructed using the build_paths function, the first certificate of the chain is verified to be a valid leaf certificate (no BasicConstraints extension) and contains the given host (using hostnames); if some path is valid, using verify_chain, the result will be Ok and contain the actual certificate chain and the trust anchor.

Fingerprint verification

val trust_key_fingerprint : host:[ `host ] Domain_name.t option -> time:(unit -> Ptime.t option) -> hash:Mirage_crypto.Hash.hash -> fingerprints:([ `host ] Domain_name.t * Cstruct.t) list -> Certificate.t list -> r

trust_key_fingerprint host ~time ~hash ~fingerprints certificates is result, the first element of certificates is verified against the given fingerprints list using key_fingerprint. If time is provided, the certificate has to be valid at the given timestamp. If host is provided, the certificate is checked for this name. The hostname of the fingerprint list must match the name in the certificate, using hostnames.

val trust_cert_fingerprint : host:[ `host ] Domain_name.t option -> time:(unit -> Ptime.t option) -> hash:Mirage_crypto.Hash.hash -> fingerprints:([ `host ] Domain_name.t * Cstruct.t) list -> Certificate.t list -> r

trust_cert_fingerprint host ~time ~hash ~fingerprints certificates is result, the first element of certificates is verified to match the given fingerprints list using fingerprint. If time is provided, the certificate is checked to be valid in at the given timestamp. If host is provided, the certificate is checked for this name. The hostname of the fingerprint list must match the name in the certificate, using hostnames. Note that public key pinning has advantages over certificate pinning.

OCaml

Innovation. Community. Security.