s6-networking: the sbearssl library interface
Description: s6-networking: the sbearssl interface
Keywords: s6-networking net sbearssl library TLS SSL BearSSL libbearssl

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

- Make sure the s6-networking headers, as well as the skalibs headers, and the `bearssl.h` header, are visible in your header search path.
- Use `#include <s6-networking/sbearssl.h>`

## Linking

- Make sure the s6-networking libraries, as well as the skalibs libraries, and the BearSSL libraries, are visible in your library search path.
- Link against `-lsbearssl`, `-lskarnet`, `-lbearssl`, ``cat $sysdeps/socket.lib``, ``cat $sysdeps/spawn.lib``, and ``cat $sysdeps/sysclock.lib``, where `$sysdeps` is your skalibs sysdeps directory.

## 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.

- ctx is a pointer to a fully initialized context, connected to fds[2] and fds[3]. The TLS/SSL handshake does not have to be completed.
- fds is an array of 4 file descriptors, in this order: fd reading clear text, fd writing clear text, fd reading ciphertext, fd writing ciphertext.
- tto is a pointer to a tain containing a relative time (i.e. a timeout) If *tto time elapses with no application data being exchanged, the engine will forcibly close the connection (with the method defined by `options & 1`). You can use `&tain_infinite_relative` as a value for tto if you don't want the engine to ever timeout.
- options is a bitfield.
  - bit 0 tells the engine how to behave when the local application closes the connection (i.e. when the engine reads EOF on fds[0]). If the bit is clear, then the engine will perform as SSL close: it will send a SSL close_notify, and stop processing incoming records, waiting for a peer acknowledgement of the close_notify. If the bit is set, then the engine will not send a close_notify but simply transmit EOF to the peer, while continuing to process incoming records until it gets EOF back. close_notify is secure when handling protocols that are not auto-terminated (such as HTTP 0.9), but it does not permit separate closing of both ways. EOF allows full-duplex until the very end, but is insecure if the application protocol does not know in advance how many bytes it should get. Modern application protocols should all work with EOF.
- verbosity defines the engine's verbosity: the higher the more verbose. This parameter is currently ignored.
- cb is a pointer to a callback function that will be called once after the TLS handshake is performed. This function is called with two arguments: ctx (a pointer to the current bearssl context), and cbarg (a pointer to a caller-provided structure).
- cbarg is a pointer to a structure provided by the caller. It will be passed as a second argument to cb when the TLS handshake has successfully completed.

`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:

- `fds` : an array of 4 file descriptors, that are in this order: the fd reading from the application (cleartext), the fd writing to the application (cleartext), the fd reading from the network, the fd writing to the network.
- `preoptions` : a bitfield.
  - Bit 0: if clear, no client authentication is performed. If set, the `CERTFILE` and `KEYFILE` environment variables are read, they must contain the path to a valid client certificate and private key (else the process exits 96); this certificate is then provided to the server for client authentication.
- `options` : a bitfield, which will be passed to `sbearssl_run()`.
  - Bit 0: if clear, no close_notify is performed and the engine will transmit EOF as received. If set, close_notify will be performed to end the TLS connection.
  - Bit 1: if clear, on reception of an EOF from the peer without a preceding close_notify, the EOF will be transmitted to the local program, and the connection will eventually end normally, with the process exiting 0. If set, if the peer closes the connection without sending a close_notify, the process will exit 98 with a fatal error message.
- verbosity defines the engine's verbosity: the higher the more verbose. This parameter is currently ignored.
- `servername` : the server name used for SNI. If NULL, then no SNI is performed, which may be a security risk.
- `cb` : a pointer to a callback function, that will be passed as is to `sbearssl_run()`.
- `notif` : an unsigned integer that will be embedded into the cbarg structure that will be passed to `sbearssl_run()`. It is used by s6-tlsc-io to identify a valid fd on which to write SSL data information once the handshake has completed.

#### `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.