crypto::x509
x509: Provides an implementation of a subset of the X.509 RFC 5280.
This module provides a certificate parser, public and private key handling, and tools to verify trust paths of certificate chains.
See cert for how to parse certificates.
The module is work in progress. It is still missing features for certain certificate extensions like name constraints and policy constraints. Certificate creation and support for certification revocation lists is also still missing.
Be advised that Hare's cryptography implementations have not been audited.
Index
Types
type cert;
type dnsname;
type extkeyusage;
type gn;
type gniter;
type keyusage;
type rsapssparams;
type sigparams;
type store;
type store_addfn;
type store_findfn;
type store_finishfn;
type store_nextfn;
type verifyopts;
type chainiter;
type dniter;
type ec_pubkey;
type ia;
type keytype;
type link;
type name;
type pathiter;
type privkey;
type privkey_finishfn;
type privkey_vtable;
type pubkey;
type pubkey_vtable;
type rfc822name;
type signfn;
type slicestore;
type storeiter;
type storevtable;
type utf8reader;
type verifyfn;
type version;
Errors
type badformat;
type badusage;
type critext;
type error;
type expired;
type invalid;
type maxpath;
type nochain;
type notca;
type truncated;
type unknownalgo;
type unsupported;
type badsig;
type weakkey;
Constants
def CERTBUFSZ: size = 4096;
def MAXEXTSZ: size = 32;
def VERIFYBUFSZ: size = size(link) * 32;
def BTSTEPSZ = size(link);
def MAXSNSZ = 20z;
def MAX_SUBJHASHSZ = sha256::SZ;
Functions
fn cert_aki(c: *cert) []u8;
fn cert_checksig(c: *cert, algo: asn1::oid, msg: io::handle, sig: []u8, params: sigparams = void) (void | badsig | io::error);
fn cert_derencode(c: *cert, dest: io::handle) (void | io::error);
fn cert_free(c: *cert) void;
fn cert_issueran(c: *cert) (gniter | void | error);
fn cert_keyusage(c: *cert) (void | u16);
fn cert_pemencode(c: *cert, dest: io::handle) (void | io::error);
fn cert_pubkey(c: *cert, pub: *pubkey) (void | error);
fn cert_ski(c: *cert) []u8;
fn cert_sn(c: *cert) []u8;
fn cert_subjectan(c: *cert) (gniter | void | error);
fn cert_sz(c: *cert) size;
fn cert_verify_hostname(c: *cert, host: name) (void | error);
fn certs_free(certs: []cert) void;
fn dn_next(iter: *dniter) (([]u8, utf8reader) | done | error);
fn dniter_init(it: *dniter, rawdn: []u8) (void | error);
fn gn_close(it: *gniter) (void | error);
fn gn_dns(d: *asn1::decoder) (utf8reader | error);
fn gn_next_dns(it: *gniter) (utf8reader | done | error);
fn gn_next_ip(it: *gniter) (ip::addr | done | error);
fn gn_peek(it: *gniter) (gn | error);
fn gn_rfc822(d: *asn1::decoder) (utf8reader | error);
fn gn_skip(it: *gniter) (void | done | error);
fn gn_trypeek(it: *gniter) (gn | done | error);
fn gn_uri(d: *asn1::decoder) (utf8reader | error);
fn newprivkey() privkey;
fn newpubkey() pubkey;
fn newstore() slicestore;
fn next_chain(v: *chainiter) (pathiter | error | done);
fn parse_cert(src: io::handle, buf: []u8 = []) (cert | nomem | error);
fn parse_certbuf(b: []u8) (cert | error);
fn parse_pemcerts(src: io::handle) ([]cert | nomem | error | io::error);
fn privkey_finish(p: *privkey) void;
fn privkey_load_pem(priv: *privkey, src: io::handle) (void | io::error | invalid | unsupported);
fn pubkey_verify(p: *pubkey, msg: io::handle, algo: asn1::oid, sig: []u8, params: sigparams = void) (size | badsig | io::error);
fn store_add(s: *store, c: cert) (void | nomem | io::error);
fn store_add_file(s: *store, path: str) (void | nomem | fs::error | error);
fn store_add_path(s: *store, path: str) (void | nomem | fs::error | error);
fn store_addfrom_handle(s: *store, src: io::handle) (void | nomem | io::error | error);
fn store_finish(s: *store) void;
fn strerror(e: error) str;
fn strextkeyusage(eku: extkeyusage) str;
fn strgn(gn: u32) str;
fn strkeyusage(ku: keyusage) str;
fn sysstore() (slicestore | nomem);
fn verifier(leaf: *cert, opts: *verifyopts, buf: []u8) (chainiter | error);
fn verify(c: *cert, opts: *verifyopts, buf: []u8) (void | error);
fn _privkey_load_pem(priv: *privkey, src: io::handle) (void | io::error | asn1::error | invalid | unsupported);
fn newdniter() dniter;
fn newslicestore(certs: []cert) slicestore;
fn path_next_cert(p: *pathiter) (*cert | done);
fn privkey_sign(p: *privkey, dest: io::handle, a: asn1::oid, msg: []u8, params: sigparams = void) (size | invalid | unsupported | io::error);
fn strsigalgo(s: asn1::oid) str;
Types
type cert
type cert = struct {
h: memio::stream,
sz: size,
tbscert: []u8,
pubkey: []u8,
sig: []u8,
sigalgo: asn1::oid,
sigparams: sigparams,
version: version,
ca: bool,
errcritext: bool,
maxpathlen: (void | u16),
keyusage: (void | u16),
extkeyusage: u8,
sn: []u8,
validfrom: date::date,
validto: date::date,
issuer: []u8,
subject: []u8,
issueruid: []u8,
subjectuid: []u8,
selfsigned: bool,
owned: bool,
flaws: u32,
err: (void | error),
aki: []u8,
ski: []u8,
sanpos: (void | io::off),
ianpos: (void | io::off),
};
Represents a parsed certificate. For obtaining a cert take a look at parse_pemcerts, parse_cert or parse_certbuf.
type dnsname
type dnsname = str;
DNS name that must be provided as an A-label according to RFC 6125.
type extkeyusage
type extkeyusage = enum u8 {
ANY = 1 << 0, SERVER_AUTH = 1 << 1, CLIENT_AUTH = 1 << 2, CODE_SIGNING = 1 << 3, EMAIL_PROTECTION = 1 << 4, TIME_STAMPING = 1 << 5, OCSP_SIGNING = 1 << 6, };
Extended key usage bit flags.
type gn
type gn = enum u32 {
OTHER = 0,
RFC822 = 1,
DNS = 2,
X400 = 3,
DN = 4,
EDI_PARTY = 5,
URI = 6,
IP = 7,
REGISTERED_ID = 8,
};
General name
type gniter
type gniter = asn1::decoder;
Iterator over general names like Subject Alternative name.
type keyusage
type keyusage = enum u16 {
DIGITAL_SIGNATURE = 1 << 15,
CONTENT_COMMITMENT = 1 << 14,
KEY_ENCIPHERMENT = 1 << 13, DATA_ENCIPHERMENT = 1 << 12,
KEY_AGREEMENT = 1 << 11, KEY_CERT_SIGN = 1 << 10,
CRL_SIGN = 1 << 9,
ENCIPHER_ONLY = 1 << 8,
DECIPHER_ONLY = 1 << 7,
};
Key usage bit flags. Values differ from the RFC, because they reflect how bit flags are stored in cert.
type rsapssparams
type rsapssparams = struct {
hashfn: oid,
maskgenfn: oid,
maskgenhashfn: oid,
saltsz: u16,
};
Parameters for the RSA PSS signature scheme. According to RFC 4055: When signing it is recommended that the parameters except for the 'saltsz' remain fixed fol all usages of a given RSA key pair.
type sigparams
type sigparams = (void | rsapssparams);
Additional signature parameters.
type store
type store = *storevtable;
A store is a pointer to a storevtable which allows implementing user-defined certificate stores. A store contains a collection of certificates and provides means to find and iterate through them. Some implementations may provide the possibility to add additional certificates to the store.
type store_addfn
type store_addfn = fn(s: *store, c: cert) (void | nomem | io::error);
The interface for adding a certificate to a store.
type store_findfn
type store_findfn = fn(s: *store, rawsubject: []u8) (storeiter | io::error);
The interface for finding certificates by providing the raw subject in its DER encoded form. The raw subject is borrowed by the store iterator.
type store_finishfn
type store_finishfn = fn(s: *store) void;
The interface for finishing a store.
type store_nextfn
type store_nextfn = fn(s: *store, it: *storeiter) (*cert | done | io::error);
The interface for receiving the next certificate after the current position the iterator 'it' points to.
type verifyopts
type verifyopts = struct {
trusted: *store,
chain: *store,
date: (void | date::date),
keyusage: u16,
extkeyusage: u8,
};
Contains requirements for the chain to be valid.
type chainiter
Show undocumented member
type chainiter = struct {
leaf: *cert,
opts: *verifyopts,
bt: []link,
sz: size,
};
type dniter
Show undocumented member
type dniter = struct {
h: memio::stream,
d: asn1::decoder,
first: bool,
};
type ec_pubkey
Show undocumented member
type ec_pubkey = struct {
id: oid,
union {
p256: ecdsa::p256pubkey,
p384: ecdsa::p384pubkey,
p521: ecdsa::p521pubkey,
},
};
type ia
Show undocumented member
type ia = enum {
OCSP,
CA_ISSUERS,
};
type keytype
Show undocumented member
type keytype = enum {
RSA,
RSAPSS,
ED25519,
P256,
P384,
P521,
};
type link
Show undocumented member
type link = struct {
cert: *cert,
pathlen: u16,
trusted: bool,
chainit: storeiter,
trustit: storeiter,
extkeyusage: u8,
};
type name
Show undocumented member
type name = (dnsname | rfc822name | ip::addr);
type pathiter
Show undocumented member
type pathiter = struct {
v: *chainiter,
idx: size,
};
type privkey
Show undocumented member
type privkey = struct {
vt: *privkey_vtable,
t: keytype,
union {
rsa: [rsa::PRIVKEYSZ]u8,
ed25519: [ed25519::PRIVKEYSZ]u8,
p256: ecdsa::p256privkey,
p384: ecdsa::p384privkey,
p521: ecdsa::p521privkey,
},
};
type privkey_finishfn
Show undocumented member
type privkey_finishfn = fn(priv: *privkey) void;
type privkey_vtable
Show undocumented member
type privkey_vtable = struct {
sign: *signfn,
finish: *privkey_finishfn,
};
type pubkey
Show undocumented member
type pubkey = struct {
vt: *pubkey_vtable,
union {
rsa: [rsa::PUBKEYSZ]u8,
ed25519: [ed25519::PUBKEYSZ]u8,
ec: ec_pubkey,
},
};
type pubkey_vtable
Show undocumented member
type pubkey_vtable = struct {
verify: *verifyfn,
};
type rfc822name
Show undocumented member
type rfc822name = str;
type signfn
Show undocumented member
type signfn = fn(priv: *privkey, sink: io::handle, msg: []u8, algo: asn1::oid, params: sigparams) (size | invalid | unsupported | io::error);
type slicestore
Show undocumented member
type slicestore = struct {
s: store,
certs: []cert,
};
type storeiter
Show undocumented member
type storeiter = struct {
store: *store,
rawsubject: []u8,
subjecthash: [MAX_SUBJHASHSZ]u8,
idx: size,
subidx: size,
};
type storevtable
Show undocumented member
type storevtable = struct {
find: *store_findfn,
next: *store_nextfn,
add: nullable *store_addfn,
finish: nullable *store_finishfn,
};
type utf8reader
Show undocumented member
type utf8reader = asn1::utf8stream;
type verifyfn
Show undocumented member
type verifyfn = fn(pub: *pubkey, msg: io::handle, algo: asn1::oid, sig: []u8, params: sigparams) (size | badsig | io::error);
type version
Show undocumented member
type version = enum u8 {
V1 = 0,
V2 = 1,
V3 = 2,
};
Errors
type badformat = !str;
Certificate format or encoding wrong.
type badusage
type badusage = !void;
Bad key usage.
type critext
type critext = !void;
Critical extension not supported.
type error
type error = !(...io::error | badsig | expired | badformat | unknownalgo | unsupported | notca | nochain | truncated | critext | maxpath | badusage | invalid | weakkey);
An error which may be returned from an x509 function.
type expired
type expired = !void;
Certificate validity period expired.
type invalid
type invalid = !str;
Invalid input.
type maxpath
type maxpath = !void;
Max trust path length exceeded.
type nochain
type nochain = !void;
No valid chain can be found.
type notca
type notca = !void;
Certificate is not a certificate authority.
type truncated
type truncated = !void;
Certificate not fully readable.
type unknownalgo
type unknownalgo = !void;
Unsupported signature algorithm.
type unsupported
type unsupported = !str;
Unsupported feature.
type badsig
Show undocumented member
type badsig = !str;
type weakkey
Show undocumented member
type weakkey = !void;
Constants
def CERTBUFSZ
def CERTBUFSZ: size = 4096;
The sizes of certificates vary and typically depend on the size of the public key. Certificates with RSA public keys of 4096 bits can reach around 2 kilobyte. With coming post quantum key algorithms in mind, 4096 may be a safer call.
This size should only be used as a guideline, since the certificate sizes may also vary on the use case.
def MAXEXTSZ
def MAXEXTSZ: size = 32;
Maximum number of allowed extensions per certificate.
def VERIFYBUFSZ
def VERIFYBUFSZ: size = size(link) * 32;
Buffer size for verifier or verify that supports chains up to a length of 32.
def BTSTEPSZ
Show undocumented member
def BTSTEPSZ = size(link);
def MAXSNSZ
Show undocumented member
def MAXSNSZ = 20z;
def MAX_SUBJHASHSZ
Show undocumented member
def MAX_SUBJHASHSZ = sha256::SZ;
Functions
fn cert_aki
fn cert_aki(c: *cert) []u8;
Returns the authority key identifier. The value is borrowed from 'c'.
fn cert_checksig
fn cert_checksig(c: *cert, algo: asn1::oid, msg: io::handle, sig: []u8, params: sigparams = void) (void | badsig | io::error);
Verifies if given signature 'sig' on message 'msg' is valid against the certificate 'c'.
This is a low level API and only uses the public key and signature algorithm of 'c', without validating any certificate attributes.
fn cert_derencode
fn cert_derencode(c: *cert, dest: io::handle) (void | io::error);
Writes the DER representation of 'c' to 'dest'.
fn cert_free
fn cert_free(c: *cert) void;
Frees allocated buffer of 'c', if created with parse_cert or parse_certbuf. Has no effect otherwise.
fn cert_issueran
fn cert_issueran(c: *cert) (gniter | void | error);
Returns an iterator over the issuer alternative names, if available. See cert_subjectan for instructions on how to use it.
fn cert_keyusage
fn cert_keyusage(c: *cert) (void | u16);
Returns the keyusage bitmap. See the keyusage type for possible bit values. Returns void, if keyusage is not available.
fn cert_pemencode
fn cert_pemencode(c: *cert, dest: io::handle) (void | io::error);
Writes the cert in the PEM format to 'dest'.
fn cert_pubkey
fn cert_pubkey(c: *cert, pub: *pubkey) (void | error);
Loads the public key of 'c' into 'pub'.
fn cert_ski
fn cert_ski(c: *cert) []u8;
Returns the subject key identifier. The value is borrowed from 'c'.
fn cert_sn
fn cert_sn(c: *cert) []u8;
Serial number borrowed from 'c'.
fn cert_subjectan
fn cert_subjectan(c: *cert) (gniter | void | error);
Returns an iterator over the subject alternative names, if available.
To iterate over a specific name type, use one of the following functions: gn_next_dns, gn_next_ip.
To iterate over all the name use gn_trypeek combined with the previously mentioned functions.
for (let gn => gn_trypeek(iter)?) {
switch (gn) {
case gn::DNS =>
let reader = gn_next_dns(iter)? as utf8reader;
// ... read all of 'reader'
case gn::IP =>
let addr = gn_next_ip(iter)? as ip::addr;
// ...
case =>
// skip ones that aren't relevant
gn_skip(iter)?;
};
};
fn cert_sz
fn cert_sz(c: *cert) size;
Returns the DER encoded size of the cert.
fn cert_verify_hostname
fn cert_verify_hostname(c: *cert, host: name) (void | error);
Verifies if given the certificate 'c' matches 'host'. Does not verify the certificate itself. See verify or verifier for how to find a trust path for 'c'.
If the name is a dnsname, it must be provided as A-label according to RFC 6125. Wildcards are allowed but only on the leftmost part.
fn certs_free
fn certs_free(certs: []cert) void;
Frees the allocated space of 'certs'.
fn dn_next
fn dn_next(iter: *dniter) (([]u8, utf8reader) | done | error);
Returns the next distinguished name as a tuple of the raw oid and a value reader as io::stream. The value must be read before calling dn_next again. The raw oid is borrowed from a static buffer. The reader returns valid UTF-8 multi bytes utf8reader or an error on encoding failures.
fn dniter_init
fn dniter_init(it: *dniter, rawdn: []u8) (void | error);
Initialized an iterator for reading the distinguished names of 'rawdn'. Use dn_next to iterate over the entries.
fn gn_close
fn gn_close(it: *gniter) (void | error);
Closes the iterator. Returns an error, if the iterator is not fully read.
fn gn_dns
fn gn_dns(d: *asn1::decoder) (utf8reader | error);
Reads current name as DNS from an gniter.
fn gn_next_dns
fn gn_next_dns(it: *gniter) (utf8reader | done | error);
Get the next name of type gn::DNS (dNSName).
fn gn_next_ip
fn gn_next_ip(it: *gniter) (ip::addr | done | error);
Get the next name of type gn::IP (ipAddress).
fn gn_peek
fn gn_peek(it: *gniter) (gn | error);
Peeks the type of the next name. Returns an error, if not available.
fn gn_rfc822
fn gn_rfc822(d: *asn1::decoder) (utf8reader | error);
Reads current name as an RFC822 E-Mail from an gniter.
fn gn_skip
fn gn_skip(it: *gniter) (void | done | error);
Skips the next name.
fn gn_trypeek
fn gn_trypeek(it: *gniter) (gn | done | error);
Peeks the type of the next name and returns done, if none available.
fn gn_uri
fn gn_uri(d: *asn1::decoder) (utf8reader | error);
Reads current name as URI from an gniter.
fn newprivkey
fn newprivkey() privkey;
Creates an uninitialized private key. Use privkey_finish to wipe private key data after use.
fn newpubkey
fn newpubkey() pubkey;
Creates an uninitialized public key.
fn newstore
fn newstore() slicestore;
Creates a new variable sized store that allows certificates to be added.
Must be finished with store_finish.
fn next_chain
fn next_chain(v: *chainiter) (pathiter | error | done);
Tries to find the next valid chain. On success a path iterator (pathiter) is returned, that can be used to loop over the trust path found.
fn parse_cert
fn parse_cert(src: io::handle, buf: []u8 = []) (cert | nomem | error);
Reads the entire cert from 'src'. If 'buf' is provided the cert is borrowed from it and the caller is responsible to free the resources after use. If 'buf' is not provided, this function will allocate space in order to store the cert. cert_free must then be called to free the memory after use.
See CERTBUFSZ for a buffer size recommendation.
fn parse_certbuf
fn parse_certbuf(b: []u8) (cert | error);
Parses and takes ownership of a cert stored in 'b'. The cert must be DER encoded. cert_free must be called to free resources after use, if 'b' is an allocated buffer.
fn parse_pemcerts
fn parse_pemcerts(src: io::handle) ([]cert | nomem | error | io::error);
Reads PEM encoded certificates and stores them into allocated buffers. certs_free must be used in order to free the used resources.
fn privkey_finish
fn privkey_finish(p: *privkey) void;
Wipes private key data from memory.
fn privkey_load_pem
fn privkey_load_pem(priv: *privkey, src: io::handle) (void | io::error | invalid | unsupported);
Loads a PEM encoded private key from 'src' into 'priv'.
fn pubkey_verify
fn pubkey_verify(p: *pubkey, msg: io::handle, algo: asn1::oid, sig: []u8, params: sigparams = void) (size | badsig | io::error);
Verifies a signature with given public key 'p'. Returns the number of bytes read from 'msg' on success.
fn store_add
fn store_add(s: *store, c: cert) (void | nomem | io::error);
Adds a certificate to the store. The store will take ownership of the certificate and will invoke cert_free when the store is finished.
fn store_add_file
fn store_add_file(s: *store, path: str) (void | nomem | fs::error | error);
Adds a PEM encoded file stored at 'path'.
fn store_add_path
fn store_add_path(s: *store, path: str) (void | nomem | fs::error | error);
Loads all certificates present at given path into 'store'. If it's a directory, only files of this directory will be added. Sub directories will be ignored.
fn store_addfrom_handle
fn store_addfrom_handle(s: *store, src: io::handle) (void | nomem | io::error | error);
Adds a PEM encoded file copied from 'src'.
fn store_finish
fn store_finish(s: *store) void;
Releases resources used by the store 's'.
fn strerror
fn strerror(e: error) str;
Converts an error into an user-friendly string.
fn strextkeyusage
fn strextkeyusage(eku: extkeyusage) str;
String representation of an external key usage.
fn strgn
fn strgn(gn: u32) str;
String representation of given general name 'gn'.
fn strkeyusage
fn strkeyusage(ku: keyusage) str;
String representation of a key usage.
fn sysstore
fn sysstore() (slicestore | nomem);
Returns the systems trusted certificates. This call will lazy load them once on the first call and returns the same store on each consequential call.
fn verifier
fn verifier(leaf: *cert, opts: *verifyopts, buf: []u8) (chainiter | error);
A certification chain verifier is an iterator that tries to find valid trust paths from the 'leaf' certificate to trusted certificates provided by 'opts' on each next_chain call.
'buf' is required to store the current chain. The maximum chain length depends on its size. You may use VERIFYBUFSZ as a generous default.
fn verify
fn verify(c: *cert, opts: *verifyopts, buf: []u8) (void | error);
Verifies given cert 'c' by finding a valid chain within given verification options 'opts'.
'buf' is required to cache the the chain during the process. The maximum chain length depends on its size. You may use VERIFYBUFSZ as a generous default.
fn _privkey_load_pem
Show undocumented member
fn _privkey_load_pem(priv: *privkey, src: io::handle) (void | io::error | asn1::error | invalid | unsupported);
fn newdniter
Show undocumented member
fn newdniter() dniter;
fn newslicestore
Show undocumented member
fn newslicestore(certs: []cert) slicestore;
fn path_next_cert
Show undocumented member
fn path_next_cert(p: *pathiter) (*cert | done);
fn privkey_sign
Show undocumented member
fn privkey_sign(p: *privkey, dest: io::handle, a: asn1::oid, msg: []u8, params: sigparams = void) (size | invalid | unsupported | io::error);
fn strsigalgo
Show undocumented member
fn strsigalgo(s: asn1::oid) str;