s6-networking
Software
skarnet.org

The sbearssl library interface

General information

libsbearssl is a support library for the s6-tlsc-io and s6-tlsd-io executables when they're built against the BearSSL backend. Among other things, it offers interfaces to read private keys and certificates from a Unix filesystem, which BearSSL does not provide on its own.

Compiling

Linking

Programming

General concepts

BearSSL provides engines to decode PEM objects and X.509 certificates, and to run a TLS/SSL connection. However, it does not store such objects: it never allocates memory, and does not interact with the filesystem. sbearssl provides functions to address this.

When reading an object into memory, sbearssl stores all the bytes of the object in a stralloc, and the sbearssl_* structures contain indices of bytes in that stralloc. That allows the structures to remain valid even when the stralloc contents get reallocated and move to some other place in the heap. After you have finished adding data to the stralloc and are sure its contents will not move again, you can use the sbearssl_*_to functions to convert sbearssl_* structures to the corresponding br_* structures (native BearSSL), which contain pointers to memory.

Private keys (typically for servers)

BearSSL handles two types of private keys: RSA keys and EC keys (i.e. points on an elliptic curve). sbearssl adds some generic functions to handle keys no matter their type.

int sbearssl_rsa_skey_from (sbearssl_rsa_skey *l, br_rsa_private_key const *k, stralloc *sa)

Converts the RSA private key from BearSSL format (reading from a structure pointed to by k) to sbearssl format (writing to a structure pointed to by l). The data from *k's contents are copied into the stralloc in *sa. The function returns 1 on success and 0 (and sets errno) on failure.

void sbearssl_rsa_skey_to (sbearssl_rsa_skey const *l, br_rsa_private_key *k, char *s)

Converts the RSA private key from sbearssl format (reading from a structure pointed to by l) to BearSSL format (writing to a structure pointed to by k). The indices in l must refer to data stored in the string s.

int sbearssl_ec_skey_from (sbearssl_ec_skey *l, br_ec_private_key const *k, stralloc *sa)

Converts the EC private key from BearSSL format (reading from a structure pointed to by k) to sbearssl format (writing to a structure pointed to by l). The data from *k's contents are copied into the stralloc in *sa. The function returns 1 on success and 0 (and sets errno) on failure.

void sbearssl_ec_skey_to (sbearssl_ec_skey const *l, br_ec_private_key *k, char *s)

Converts the EC private key from sbearssl format (reading from a structure pointed to by l) to BearSSL format (writing to a structure pointed to by k). The indices in l must refer to data stored in the string s.

int sbearssl_skey_from (sbearssl_skey *l, br_skey const *k, stralloc *sa)

Converts the private key from BearSSL format (reading from a structure pointed to by k) to sbearssl format (writing to a structure pointed to by l). The data from *k's contents are copied into the stralloc in *sa. The function returns 1 on success and 0 (and sets errno) on failure.

void sbearssl_skey_to (sbearssl_skey const *l, br_skey *k, char *s)

Converts the private key from sbearssl format (reading from a structure pointed to by l) to BearSSL format (writing to a structure pointed to by k). The indices in l must refer to data stored in the string s.

int sbearssl_skey_readfile (char const *fn, sbearssl_skey *key, stralloc *sa)

Reads a private key from the file named fn and stores it in sbearssl format into the structure in *key, the bytes of the key being added to the stralloc in *sa.

The private key in fn can be either DER-encoded (binary format) or PEM-encoded (text format).

The function returns 0 on success. It returns a negative value in case of a system error, in which case errno identifies the error. It returns a positive value in case of an error returned by a BearSSL decoder, in which case an appropriate message can be obtained with the sbearssl_error_str() function.

Public keys

BearSSL handles two types of public keys: RSA keys and EC keys (i.e. points on an elliptic curve). sbearssl adds some generic functions to handle keys no matter their type.

You normally should not handle public keys directly; you should handle x509 certificate chains instead.

int sbearssl_rsa_pkey_from (sbearssl_rsa_pkey *l, br_rsa_public_key const *k, stralloc *sa)

Converts the RSA public key from BearSSL format (reading from a structure pointed to by k) to sbearssl format (writing to a structure pointed to by l). The data from *k's contents are copied into the stralloc in *sa. The function returns 1 on success and 0 (and sets errno) on failure.

void sbearssl_rsa_pkey_to (sbearssl_rsa_pkey const *l, br_rsa_public_key *k, char *s)

Converts the RSA public key from sbearssl format (reading from a structure pointed to by l) to BearSSL format (writing to a structure pointed to by k). The indices in l must refer to data stored in the string s.

int sbearssl_ec_pkey_from (sbearssl_ec_skey *l, br_ec_public_key const *k, stralloc *sa)

Converts the EC public key from BearSSL format (reading from a structure pointed to by k) to sbearssl format (writing to a structure pointed to by l). The data from *k's contents are copied into the stralloc in *sa. The function returns 1 on success and 0 (and sets errno) on failure.

void sbearssl_ec_pkey_to (sbearssl_ec_pkey const *l, br_ec_public_key *k, char *s)

Converts the EC public key from sbearssl format (reading from a structure pointed to by l) to BearSSL format (writing to a structure pointed to by k). The indices in l must refer to data stored in the string s.

int sbearssl_pkey_from (sbearssl_pkey *l, br_x509_pkey const *k, stralloc *sa)

Converts the public key from BearSSL format (reading from a structure pointed to by k) to sbearssl format (writing to a structure pointed to by l). The data from *k's contents are copied into the stralloc in *sa. The function returns 1 on success and 0 (and sets errno) on failure.

void sbearssl_pkey_to (sbearssl_pkey const *l, br_x509_pkey *k, char *s)

Converts the public key from sbearssl format (reading from a structure pointed to by l) to BearSSL format (writing to a structure pointed to by k). The indices in l must refer to data stored in the string s.

Generic PEM objects

You normally should not have to call these functions directly. Instead, you should use the higher-level functions for private keys, X509 certificate chains and trust anchors, which will perform the PEM decoding for you.

int sbearssl_pem_decode_from_buffer (buffer *b, genalloc *list, stralloc *sa)

Decodes a PEM object, reading from the buffer in *b. The decoded bytes are appended to *sa. list points to a genalloc containing objects of type sbearssl_pemobject. One sbearssl_pemobject is appended to the genalloc per PEM entity decoded from the byte stream read from the buffer.

The function returns 0 on success. It returns a negative value in case of a system error, in which case errno identifies the error. It returns a positive value in case of an error returned by a BearSSL decoder, in which case an appropriate message can be obtained with the sbearssl_error_str() function.

int sbearssl_pem_decode_from_string (char const *s, size_t len, genalloc *list, stralloc *sa)

Decodes a PEM object from the len bytes pointed to by s. The decoded bytes are appended to *sa. list points to a genalloc containing objects of type sbearssl_pemobject. One sbearssl_pemobject is appended to the genalloc per PEM entity found in the bytes in s.

The function returns 0 on success. It returns a negative value in case of a system error, in which case errno identifies the error. It returns a positive value in case of an error returned by a BearSSL decoder, in which case an appropriate message can be obtained with the sbearssl_error_str() function.

X.509 certificates (typically for servers)

int sbearssl_cert_from (sbearssl_cert *l, br_x509_certificate const *k, stralloc *sa)

Converts a certificate from BearSSL format (reading from a structure pointed to by k) to sbearssl format (writing to a structure pointed to by l). The data from *k's contents are copied into the stralloc in *sa. The function returns 1 on success and 0 (and sets errno) on failure.

void sbearssl_cert_to (sbearssl_cert const *l, br_x509_certificate *k, char *s)

Converts a certificate from sbearssl format (reading from a structure pointed to by l) to BearSSL format (writing to a structure pointed to by k). The indices in l must refer to data stored in the string s.

int sbearssl_cert_readfile (char const *fn, genalloc *list, stralloc *sa)

Reads one or more certificates from the file named fn and appends them to the genalloc in *list, which is a dynamically growing list of sbearssl_cert structures. The bytes of the (maybe PEM-decoded, but still DER-encoded) certificate are appended to the stralloc in *sa.

The fn file can be either DER-encoded (binary format) or PEM-encoded (text format). If it is DER-encoded, it must contain exactly one X.509 certificate. If it is PEM-encoded, it may contain a chain of certificates as long as the PEM file fits within the size limits.

fn must not be bigger than SBEARSSL_MAXCERTFILESIZE, which is 8 kB. This function is meant to read individual certificates, not files containing large certificate chains or sets of trust anchors. To do that, use sbearssl_cert_readbigpem() instead.

The function returns 0 on success. It returns a negative value in case of a system error, in which case errno identifies the error. It returns a positive value in case of an error returned by a BearSSL decoder, in which case an appropriate message can be obtained with the sbearssl_error_str() function.

int sbearssl_cert_readbigpem (char const *fn, genalloc *, stralloc *sa)

Reads one or more PEM-encoded certificates from the file named fn and appends them to the genalloc in *list, which is a dynamically growing list of sbearssl_cert structures. The bytes of the PEM-decoded (but still DER-encoded) certificates are appended to the stralloc in *sa.

The function will refuse to read a file that is not valid PEM. Inside the file, It will ignore PEM objects that are not X.509 certificates.

The function returns 0 on success. It returns a negative value in case of a system error, in which case errno identifies the error. It returns a positive value in case of an error returned by a BearSSL decoder, in which case an appropriate message can be obtained with the sbearssl_error_str() function.

Trust anchors (typically for clients)

BearSSL clients do not use X.509-encoded certificates, they use sets of trust anchors, i.e. structures decoded from certificates representing (intermediate or) root CAs.

int sbearssl_ta_from (sbearssl_ta *l, br_x509_trust_anchor const *k, stralloc *sa)

Converts a trust anchor from BearSSL format (reading from a structure pointed to by k) to sbearssl format (writing to a structure pointed to by l). The data from *k's contents are copied into the stralloc in *sa. The function returns 1 on success and 0 (and sets errno) on failure.

void sbearssl_ta_to (sbearssl_ta const *l, br_x509_trust_anchor *k, char *s)

Converts a trust anchor from sbearssl format (reading from a structure pointed to by l) to BearSSL format (writing to a structure pointed to by k). The indices in l must refer to data stored in the string s.

int sbearssl_ta_readfile (char const *fn, genalloc *list, stralloc *sa)

Reads a set of trust anchors from a PEM file named fn which must contain a list of (intermediate or) root CA certificates. The trust anchors are appended to the genalloc in *list, which is a dynamically growing list of sbearssl_ta structures. The contents of the trust anchors are appended to *sa, which is a stralloc used for storage.

The function returns 0 on success. It returns a negative value in case of a system error, in which case errno identifies the error. It returns a positive value in case of an error returned by a BearSSL decoder, in which case an appropriate message can be obtained with the sbearssl_error_str() function.

int sbearssl_ta_readdir (char const *dir, genalloc *list, stralloc *sa)

Reads a set of trust anchors from a directory named dir, which must contain a list of (intermediate or) root CA certificates stored as individual DER- or PEM-encoded files. The trust anchors are appended to the genalloc in *list, which is a dynamically growing list of sbearssl_ta structures. The contents of the trust anchors are appended to *sa, which is a stralloc used for storage.

The function ignores files that do not contain valid DER or PEM objects containing X.509 certificates representing certification authorities.

The function returns 0 on success. It returns a negative value in case of a system error, in which case errno identifies the error. It returns a positive value in case of an error returned by a BearSSL decoder, in which case an appropriate message can be obtained with the sbearssl_error_str() function.

Miscellaneous utilities

You probably shouldn't need to call any of these functions directly, except for the first one.

char const *sbearssl_error_str (int err)

Returns a fixed string containing an error message corresponding to the err code, which must be non-negative. The return value from a few sbearssl functions, if positive, can be interpreted via this function.

int sbearssl_isder (unsigned char const *s, size_t len)

Tests whether the array of len bytes pointed to by s looks like a DER-encoded object. Returns 1 if it does and 0 otherwise.

int sbearssl_tai_from_dayseconds (tai *t, uint32_t days, uint32_t seconds)

Makes a TAI absolute time from the date given in days and seconds as provided in an x509 certificate. Returns 1 on success and 0 on failure (which probably means days/seconds don't encode a valid date).

int sbearssl_dayseconds_from_tai (uint32_t *days, uint32_t *seconds, tai const *t)

Compute suitable days and seconds, for an x509 certificate, from an absolute TAI date. Returns 1 on success and 0 on failure (which probably means the given date doesn't fit the days/seconds format).

int sbearssl_x509_minimal_set_tai (br_x509_minimal_context *ctx, tai const *t)

Sets the validation time for the X.509 context in *ctx to the absolute time contained in *t, which is a tai. Returns 1 if it succeeds, or 0 if it fails - probably because *t does not represent a valid time.

int sbearssl_x509_minimal_set_tain (br_x509_minimal_context *ctx, tain const *a)

Same as the above function, except the time is given as a tain, i.e. a tai plus nanoseconds (which are simply ignored).

void sbearssl_drop (void)

If the process is running as root, then this function drops its privileges (else it does nothing). The gid to drop to is read from the TLS_GID environment variable; the uid to drop to is read from the TLS_UID environment variable. If those variables are not given, then the uid, or gid, or both, are not changed. If they contain something else than numerical uid/gids, the process exits 111 with an error message.

int sbearssl_send_environment (br_ssl_engine_context *ctx, int fd)

Writes a series of null-terminated strings of the form key=value to file descriptor fd; the series is terminated with an additional null character. The strings represent information about the TLS connection represented by context ctx; it is only valid to call this function after the handshake has completed. The exact keys used will change over time, but at least SSL_PROTOCOL=value and SSL_CIPHER=value are transmitted. The function returns 1 if it succeeds and 0 if it fails.

X.509 validation with some information extraction

A sbearssl_x509_small_context structure is an implementation of the br_x509_class type that is a little more powerful than the br_x509_minimal_context implementation provided by BearSSL: running a certificate chain through a sbearssl_x509_small_context validator allows you to get the hash of the End Entity certificate in the chain, as well as the DN it certifies.

void sbearssl_x509_small_init_full (sbearssl_x509_small_context *ctx, br_x509_trust_anchor *btas, size_t n, sbearssl_dn *eedn, uint8_t *eltstatus, char *eehash)

Initializes the validator in *ctx with the list of n trust anchors pointed to by btas. eehash must point to a user-supplied buffer of at least 32 bytes, which will contain the sha256 hash of the EE certificate after validation. eedn must point to a user-supplied sbearssl_dn structure that will contain the DN of the end entity after validation. eltstatus must point to a user-supplied uint8_t, which after validation encodes the status of DN extraction: bit 7 of eltstatus is set if there was an issue during extraction (in which case the contents of *eedn are meaningless) and clear if everything went well, and bits 0 to 5 are set iff the corresponding element of the DN is present, by increasing order C, ST, L, O, OU and CN.

int sbearssl_x509_small_set_tai (sbearssl_x509_small_context *ctx, tai const *t)

int sbearssl_x509_small_set_tain (sbearssl_x509_small_context *ctx, tain const *a)

Like their sbearssl_x509_minimal_set_tai and sbearssl_x509_minimal_set_tain counterparts, but for sbearssl_x509_small_context validators instead of br_x509_minimal_context ones.

Server-side SNI support

A sbearssl_sni_policy_context structure is an implementation of the br_ssl_server_policy_class type that supports Server Name Identification, unlike the default br_ssl_server_policy_rsa_context and br_ssl_server_policy_ec_context implementations provided by BearSSL that only support a single certificate chain.

During preparation, the user fills the structure with as many keypairs as they wish; then at handshake time, depending on the ServerName requested by the client, the relevant keypair is kept and used, and all the other ones are wiped from memory.

void sbearssl_sni_policy_init (sbearssl_sni_policy_context *pol)

Initializes the user-supplied *pol structure.

int sbearssl_sni_policy_add_keypair_file (sbearssl_sni_policy_context *pol, char const *servername, char const *certfile, char const *keyfile)

This function reads a PEM-encoded certificate chain in the file certfile, and a corresponding PEM- or DER-encoded private key in the file keyfile. It registers them with the *pol structure as the keypair to use if the client requests the servername ServerName. Wildcards are supported in servername except on TLDs. If servername is empty, then the keypair is registered as a default, to use when no ServerName matches (or when the client doesn't request SNI).

The function returns 0 on success. It returns a negative value in case of a system error, in which case errno identifies the error. It returns a positive value in case of an error returned by a BearSSL decoder, in which case an appropriate message can be obtained with the sbearssl_error_str() function.

size_t sbearssl_sni_policy_nkeypairs (sbearssl_sni_policy_context const *pol)

Returns the number of keypairs currently registered with *pol.

void sbearssl_sctx_init_full_generic (br_ssl_server_context *sc)

Not directly related to SNI support, but complementary to it. This function is a high-level function missing from BearSSL: it fully initializes a br_ssl_server_context structure, registering all the cipher suites and all the hashes provided by BearSSL with a good degradation order, supporting TLS 1.0 to TLS 1.2, etc. What it doesn't set: the engine buffer, the certificate policy, the optional engine flags, and the optional client certificate validation. If the user wishes to be more conservative with the TLS versions, they can use the br_ssl_engine_set_versions() call on &sc→eng afterwards.

void sbearssl_sctx_set_policy_sni (br_ssl_server_context *sc, sbearssl_sni_policy_context *pol)

Registers *pol as the certificate policy for the *sc server context. This tells BearSSL that the handshake will support SNI and use the keypairs registered with *pol.

Running the TLS/SSL engine (internal function for both clients and servers)

void sbearssl_run (br_ssl_engine_context *ctx, int *fds, tain const *tto, uint32_t options, unsigned int verbosity, sbearssl_handshake_cb_t_ref cb, sbearssl_handshake_cb_context_t *cbarg)

This function runs a full-duplex TLS/SSL engine, reading/writing clear text from/to two file descriptors, and writing/reading ciphertext to/from two other file descriptors, until the connection is closed both ways (either with a SSL close, or with EOF). It does not return.

sbearssl_run will make the process die with an appropriate error message if it encounters an unrecoverable error. If there were no problems and the SSL/TLS connection closed cleanly, the process exits 0.

Initializing and running the engine

The following functions are high-level interfaces and rely on all the functions described above. They're the core of the s6-tlsc-io and s6-tlsd-io programs.

void sbearssl_client_init_and_run (int *fds, tain const *tto, uint32_t preoptions, uint32_t options, unsigned int verbosity, char const *servername, sbearssl_handshake_cb_t_ref cb, unsigned int notif)

This function initializes a TLS context for a client-side connection, then runs the TLS engine via a call to sbearssl_run(). The function does not return. If the context cannot be initialized, the process exits 96 with an appropriate error message.

If the CADIR environment variable is set, then it must contain the path of a directory containing the hashed names of the public certificates identifying the trust anchors. Else, if the CAFILE environment variable is set, then it must contain the path to a PEM file containing all the certificates for the trust anchors. Else, the process exits 100 with an error message.

The arguments are as follows:

void sbearssl_server_init_and_run (int *fds, tain const *tto, uint32_t preoptions, uint32_t options, unsigned int verbosity, sbearssl_handshake_cb_t_ref cb, unsigned int notif)

Same as the previous function, but on the server side. No servername argument is required. The CERTFILE and KEYFILE environment variables are mandatory, they point to the server's certificate and private key. It is only necessary to set CADIR or CAFILE when bit 0 of preoptions is set, in which case client authentication will be requested, and a list of trust anchors (read from either the directory in CADIR or the PEM file in CAFILE) will be used to verify the client certificate.