Library
Module
Module type
Parameter
Class
Class type
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).
type signature_error = [
| `Bad_signature of Distinguished_name.t * string
| `Bad_encoding of Distinguished_name.t * string * string
| `Hash_not_allowed of
Distinguished_name.t
* [ `MD5 | `SHA1 | `SHA224 | `SHA256 | `SHA384 | `SHA512 ]
| `Unsupported_keytype of Distinguished_name.t * Public_key.t
| `Unsupported_algorithm of Distinguished_name.t * string
| `Msg of string
]
The type of signature verification errors.
val pp_signature_error : signature_error Fmt.t
pp_signature_error ppf sige
pretty-prints the signature error sige
on ppf
.
type ca_error = [
| signature_error
| `CAIssuerSubjectMismatch of Certificate.t
| `CAInvalidVersion of Certificate.t
| `CACertificateExpired of Certificate.t * Ptime.t option
| `CAInvalidExtensions of Certificate.t
]
The polymorphic variant of possible certificate authorities failures.
val valid_ca :
?allowed_hashes:Digestif.hash' list ->
?time:Ptime.t ->
Certificate.t ->
(unit, [> ca_error ]) Stdlib.result
valid_ca ~allowed_hashes ~time certificate
is result
, which is Ok ()
if the given certificate is self-signed with any hash algorithm of hash_allowlist
(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 :
?allowed_hashes:Digestif.hash' list ->
?time:Ptime.t ->
Certificate.t list ->
Certificate.t list
valid_cas ~allowed_hashes ~time certificates
is valid_certificates
, only those certificates which pass the valid_ca
check.
type leaf_validation_error = [
| `LeafCertificateExpired of Certificate.t * Ptime.t option
| `LeafInvalidIP of Certificate.t * Ipaddr.t option
| `LeafInvalidName of Certificate.t * [ `host ] Domain_name.t option
| `LeafInvalidVersion of Certificate.t
| `LeafInvalidExtensions of Certificate.t
]
The polymorphic variant of a leaf certificate validation error.
type chain_validation_error = [
| `IntermediateInvalidExtensions of Certificate.t
| `IntermediateCertificateExpired of Certificate.t * Ptime.t option
| `IntermediateInvalidVersion of Certificate.t
| `ChainIssuerSubjectMismatch of Certificate.t * Certificate.t
| `ChainAuthorityKeyIdSubjectKeyIdMismatch of Certificate.t * Certificate.t
| `ChainInvalidPathlen of Certificate.t * int
| `EmptyCertificateChain
| `NoTrustAnchor of Certificate.t
| `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.
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 :
?ip:Ipaddr.t ->
host:[ `host ] Domain_name.t option ->
time:(unit -> Ptime.t option) ->
?revoked:(issuer:Certificate.t -> cert:Certificate.t -> bool) ->
?allowed_hashes:Digestif.hash' list ->
anchors:Certificate.t list ->
Certificate.t list ->
(Certificate.t, [> chain_error ]) Stdlib.result
verify_chain ~ip ~host ~time ~revoked ~allowed_hashes ~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 allowed_hashes
(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 Certificate.hostnames
. If ip
is specified, the certificate is checked to contain the given ip
, using Certificate.ips
. The returned certificate is the root of the chain, a member of the given list of anchors
.
The polymorphic variant of a fingerprint validation error.
type validation_error = [
| signature_error
| leaf_validation_error
| fingerprint_validation_error
| `EmptyCertificateChain
| `InvalidChain
]
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
.
type r =
((Certificate.t list * Certificate.t) option, validation_error) Stdlib.result
val verify_chain_of_trust :
?ip:Ipaddr.t ->
host:[ `host ] Domain_name.t option ->
time:(unit -> Ptime.t option) ->
?revoked:(issuer:Certificate.t -> cert:Certificate.t -> bool) ->
?allowed_hashes:Digestif.hash' list ->
anchors:Certificate.t list ->
Certificate.t list ->
r
verify_chain_of_trust ~ip ~host ~time ~revoked ~allowed_hashes ~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 Certificate.hostnames
) or ip
if specified (using Certificate.ips
; if some path is valid, using verify_chain
, the result will be Ok
and contain the actual certificate chain and the trust anchor.
val trust_key_fingerprint :
?ip:Ipaddr.t ->
host:[ `host ] Domain_name.t option ->
time:(unit -> Ptime.t option) ->
hash:Digestif.hash' ->
fingerprint:string ->
Certificate.t list ->
r
trust_key_fingerprint ~ip ~host ~time ~hash ~fingerprint certificates
is result
, the first element of certificates
is verified against the given fingerprint
using Public_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 the given host
(using Certificate.hostnames
). If ip
is provided, the certificate is checked to include this IP address (using Certificate.ips
).
val trust_cert_fingerprint :
?ip:Ipaddr.t ->
host:[ `host ] Domain_name.t option ->
time:(unit -> Ptime.t option) ->
hash:Digestif.hash' ->
fingerprint:string ->
Certificate.t list ->
r
trust_cert_fingerprint host ~time ~hash ~fingerprint certificates
is result
, the first element of certificates
is verified to match the given fingerprint
using Certificate.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 the given host
(using Certificate.hostnames
). If ip
is provided, the certificate is checked to include this IP address (using Certificate.ips
). Note that public key pinning has advantages over certificate pinning.