Certificates in SSL/TLS

Chain Validation

X.509 certificates are a public-key distribution method. The client needs to know the public key of the server in order to perform the asymmetric cryptography involved in the handshake; the server shows its certificate to the client, and that certificate contains the server’s public key. The client will trust that information, i.e. accept to use that public key as the genuine server’s public key, after a process called validation.

Certificate validation involves a lot of checks detailed in all their mind-boggling details in RFC 5280. The gist of the process is the following:

  • Each certificate is signed. The signature shall be verified against the public key of a Certification Authority, an entity that we trust for signing certificates (i.e. the CA is supposed to check the match between the identity and the public key in each certificate that it signs).

  • CA public keys are also obtained through certificates, which must be validated, so the process is recursive. It stops on a trust anchor, which is a public key that the validator already knows and trusts. Trust anchors are often called “root certificates” and typically come preinstalled in the operating system or Web browser; similarly, non-root certificates are often called “intermediate CA”. Validation is thus applied on a certificate chain that goes from the trust anchor down to the certificate which is the one we really are interested in, and called the end-entity (EE) because it is at the end of the chain. Chains are also known as “paths”.

  • Each certificate contains two Distinguished Names (names in a complicated format known as “X.500”). The subjectDN is the identity of the certificate owner; the issuerDN is the identity of the CA that issued (signed) that certificate. In the chain, the subjectDN of each certificate should be equal to the issuerDN of the next certificate in the chain. The trust anchor, nominally, has no issuer, but in many cases trust anchors are stored in the format of a certificate, usually self-issued (its subjectDN and issuerDN are identical) and self-signed (the signature on the certificate matches the public key in the certificate itself). Self-signed certificates are traditional; that self-signature does not provide any security feature, but it must be there because the X.509 certificate format mandates a non-optional field for the signature.

  • Each certificate has a pair of validity dates; the certificate is valid only as long as the validation time (normally, the current time) lies between these two dates (internally called notBefore and notAfter, respectively). This means that certificates may expire, and also that certificate validation requires knowledge of the current date and time (at least approximately).

  • Each certificate may contain a large number of extensions, with an open-ended format, that may impact the validation process in arbitrary ways. Each extension can be marked as “critical” or “not critical”: a critical extension is one that must not be ignored (thus, a system that does not recognise an extension can ignore it if it is non-critical, but it must reject the certificate if the extension is critical).

Within the extensions that can be present in a certificate, one is called “Key Usage”; if present, then it specifies the allowed usages for that public key, all others being forbidden. The defined usages are:

  • digitalSignature
  • nonRepudiation
  • keyEncipherment
  • dataEncipherment
  • keyAgreement
  • keyCertSign
  • cRLSign
  • encipherOnly
  • decipherOnly

The precise meaning of these values is subject to debate; for instance, when the SSL client generates a random pre-master secret and encrypts it with the server’s RSA public key, is that “keyEncipherment” (because the pre-master secret is a key and is being encrypted), “dataEncipherment” (because the pre-master secret is also a piece of data), or “keyAgreement” (since this is done in order to have a transient shared secret between client and server)? In order to ensure interoperability, BearSSL considers that:

  • if either (or both) of digitalSignature or nonRepudiation is set, then the key is valid for verifying signatures (but not for signatures on certificates by CA);

  • if keyEncipherment, dataEncipherment and/or keyAgreement is set, then the key is valid for key exchange (encryption in the case of RSA);

  • if keyCertSign is set, then the key is accepted for validating signatures on certificates.

If the Key Usage extension is not present, all usages are deemed allowed.

Chains in SSL/TLS

In full X.509 theory, a system that wishes to perform certificate validation (called a “relying party” in X.509 terminology) should perform path building, a process consisting in gathering certificates from various sources (locally known, provided within the protocol, automatically downloaded…) and assembling them into chains until one of them can be validated. There is no limit on the complexity of such a process; indeed, any validation engine that indulges in generic path building can usually be made to run and loop for arbitrary amounts of time (this is why such implementations often enforce a strict time limit).

Fortunately, SSL specifies that each server should provide its certificate as part of an already built and valid chain. A client is then allowed to validate just that chain, in due order. Each client is free to perform generic path building if that’s its thing; some SSL clients do (e.g. Schannel, the SSL implementation in Windows) while some others do not (typical applications using OpenSSL).

In that already assembled chain, the root certificate (if the trust anchor is represented as a certificate) may or may not be included. Sending the root is useless since the receiving party must already have it, and won’t learn anything new if it is sent; however, SSL allows it, and some SSL implementations include the root certificate in their chains. This is mostly harmless.

In all of the above, I used the X.509 terminology in which the chain is ordered from trust anchor to end-entity. In SSL, the chain is actually sent in reverse order, the end-entity coming first.

Certificates and Cipher Suites

There are interactions between certificates and cipher suites:

  • TLS_RSA_* cipher suites use RSA key exchange, in which the client encrypts a random value (the “pre-master secret”) with the server’s public key, obtained from its certificate. That public key must be of type RSA and amenable to key exchange or encryption.

  • TLS_ECDH_* cipher suites use Elliptic Curve Diffie-Hellman, which is a key agreement protocol. That public key must be of type EC, and amenable to key exchange or encryption. There are two sub-types, that depend on the type of the key of the issuing CA (i.e. not the server’s public key itself, but the public key of the CA that issued the server’s certificate): TLS_ECDH_RSA_* (issuing CA has a RSA key) and TLS_ECDH_ECDSA_* (issuing CA has an EC key).

  • TLS_ECDHE_RSA cipher suites use “ephemeral ECDH”: the ECDH key pairs are transient; the server’s public key (the one in the certificate) has type RSA, and the server uses the corresponding private key to sign its ephemeral ECDH public key.

  • TLS_ECDHE_ECDSA cipher suites are like TLS_ECDHE_RSA but with an ECDSA signature instead of RSA, so the server’s public key (in its certificate) has type EC and is allowed for signatures.

Nominally, an SSL server or client does not need to decode or process in any way its own certificate: when a server sends its certificate to the client, it is to convince the client, not the server. From the server’s point of view, this is just an opaque blob that might help the client; the server does not need to trust its own certificate. BearSSL follows that point of view and thus allows a server not to include any certificate validation code; the server knows its private key and this is enough. However, the TLS_ECDH_* cipher suites depend also on the type of key used by the issuing CA, and that information can be retrieved by decoding the certificate itself (at least the signature part). In BearSSL, that information is meant to be provided by the calling application (the br_ssl_server_set_single_ec() function has a parameter called cert_issuer_key_type) but the calling application may certainly used the X.509 decoding functions of BearSSL to obtain it dynamically.

Client Certificates

In basic SSL usage, the server has a certificate and the corresponding private key, and sends that certificate to the client. The client validates the server’s certificate chain, extract the public key from the server’s certificate, and uses it in some way in the handshake. This means that the client authenticates the server: through the certificate, the client gains some guarantee that whatever it will send through the SSL connection will be readable only by the intended server. However, from the point of view of the server, the client is anonymous.

In many usage contexts, client authentication is performed afterwards, within the SSL tunnel (e.g. the client sends a username and password). However, SSL also allows for client certificates. In that model, the server requests a certificate from the client (with a CertificateRequest message). The client is free not to send a certificate, in which case it remains anonymous and the server decides whether to keep on with the connection or not. But if the client has a private key and a corresponding certificate, then it may send that certificate (again, in a chain) and use the private key to convince the server that it really controls the private key that matches the public key in the certificate.

Usually, the client-side private key operation is a signature, thus the client certificate must be amenable to signatures (PKCS#1 signatures for RSA keys, ECDSA signatures for EC keys). This signature is computed over a hash of all previous handshake messages, and sent as a CertificateVerify message (it is after the ClientKeyExchange message, and this is important for security). However, there is another mode known as static ECDH which can be applied in the following conditions:

  • The server certificate contains an EC public key.

  • The client certificate also contains an EC public key, in the same curve.

  • The cipher suite is one of the TLS_ECDH_* suites (not TLS_ECDHE_*).

In the SSL handshake, when this mode is selected, the ClientKeyExchange will be empty, and there will be no CertificateVerify message. Nevertheless, client and server are mutually authenticated, and the shared secret (the “pre-master secret”) they come up with is obtained with Diffie-Hellman using their respective ECDH key pairs.

Mathematically speaking, one might envision a semi-static ECDH key exchange, in which the key exchange would use TLS_ECDHE_*, and the client would use its own static private key against the ephemeral ECDH key pair sent by the server. However, RFC 4492 explicitly forbids that:

The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because the use of a long-term ECDH client key would jeopardize the forward secrecy property of these algorithms.

The reasoning here is that if the client uses its permanent private key as part of the key exchange mechanism, then the compromise of that private key would allow decrypting past sessions recorded by the attacker. Whether this reason really makes sense is debatable, because the need for forward secrecy depends on how much ulterior private key compromises are likely and matter in the context at hand; it is not necessarily the case that a client private key compromise and a server private key compromise are as likely or as important as each other. Nevertheless, so it is specified.

BearSSL supports all modes (RSA signatures, ECDSA signatures, static ECDH). Static ECDH is not forward secure (as explained above), but it is also the lightest mode, for CPU usage, total size of exchanged messages, and code footprint.

Chain Validation in BearSSL

Chain Validation API

BearSSL defines a chain validation API, which is used by both client and server (to validate the server’s and the client’s certificate chains, respectively). It uses BearSSL’s OOP concept:

  • A chain validation engine has a context structure that maintains the running state for the current validation.

  • The first field of that context structure is a pointer to a vtable that includes pointers to the methods (functions) for the engine.

The type of that vtable is defined in the bearssl_x509.h header. The methods are:

void (*start_chain)(const br_x509_class **ctx,
        const char *server_name);

void (*start_cert)(const br_x509_class **ctx, uint32_t length);

void (*append)(const br_x509_class **ctx,
        const unsigned char *buf, size_t len);

void (*end_cert)(const br_x509_class **ctx);

unsigned (*end_chain)(const br_x509_class **ctx);

const br_x509_pkey *(*get_pkey)(
        const br_x509_class *const *ctx, unsigned *usages);

start_chain() is called first, when a chain is about to be received and validated. The server_name parameter may be non-NULL, normally on the client side (i.e. when validating the server’s certificate): this is the “intended server name” (in an HTTPS context, the server name part in the target URL) and the validation engine shall make sure that the provided name appears at the proper emplacement in the server’s certificate (the EE certificate). The “proper emplacement” is defined in RFC 2818 as the Subject Alt Name (SAN) extension, with names of type dNSName; if there is no SAN extension at all, then the Common Name (CN) part of the subjectDN field is used. The name(s) in the certificate may further contain “wildcards” (the * character).

start_cert() is called at the start of each new certificate in the chain; the length (in bytes) of that certificate is provided as parameter. Note that the length of a certificate is encoded over three bytes in the SSL protocol, so it may potentially be as big as 16 megabytes, though “normal” certificates rarely exceed one or two kilobytes in length.

The certificate itself (supposedly DER-encoded) is injected as one or several append() calls, each with a chunk of certificate data. Chunks may be arbitrarily small (but never less than 1 byte) since they depend in part on encoding details on the wire. The total length of all chunks matches the length provided to start_cert() exactly. Implementations need not necessarily keep track of how many bytes were provided, since the end_cert() method is called when the certificate is finished.

When all certificates have been injected, the end_chain() method is called. At that point, the engine shall return either 0 (if the chain could be validated), or a non-zero error code.

The get_pkey() method is called after the chain validation, when end_chain() returned 0. The caller thus asks for the public key which was successfully validated. It is expected that the returned pointer (pointer to br_x509_pkey) points to some emplacement allocated in the validation engine context structure. The caller will use and/or copy the parts that it needs. There will be no explicit “deallocation” call, so if the public key structure is dynamically allocated, then it is up to the validation engine to release it afterwards. Moreover, get_pkey() may be called several times, and it should return the same pointer every time.

This API was meant to allow plugging any arbitrary validation engine, while not requiring heavy buffering. The certificates are provided chunk by chunk, in the order they are received, which is the SSL order (EE first). Chains should normally link to some sort of root certificate; as per SSL rules, the root certificate itself (traditionally self-signed) may or may not be sent.

BearSSL provides two implementations of that API. Either may be used both on the client and server sides.

The “Known Key” Engine

The “known key” engine is a simple implementation that does not actually validate the certificates. It is meant for specific situations where the peer’s public key is already known (e.g. some sort of SSH model in which clients remember the server’s public key); the implementation will then simply ignore the received certificate, will always succeed, and will return the configured public key1.

In BearSSL, this engine’s context structure has type br_x509_knownkey_context and is initialised with br_x509_knownkey_init_rsa() (for a known key of type RSA) or br_x509_knownkey_init_ec() (for a known key of type EC). In either case, the “allowed usages” (signatures and/or key exchange) must be specified; BearSSL will thus use that information to decide which kind of cipher suite or authentication mechanism is appropriate.

Since the “known key” engine does not actually look at the received certificates, it can perfectly tolerate pure junk data or even an empty chain (an empty chain is a chain containing no certificate at all). Note, though, that in the case of client certificates, sending an empty chain is the signal (in the protocol) for a client refusing or unable to honour a server’s request for a client certificate; therefore, if you use the “known key” model for client authentication, then you must take care that the client won’t try to send an empty chain in that case. Instead, the client could send a chain of one empty certificate, which won’t be much larger on the wire.

Of course, when interoperability is important, clients and servers should send actual DER-encoded X.509 certificates, or other types of certificates, e.g. OpenPGP messages, as specified in RFC 6091, if supported in your case (BearSSL does not include any support for OpenPGP messages, but such a support could be added within the validation API described here).

The “Minimal” Engine

The “minimal” engine is the implementation that uses the br_x509_minimal_context structure. It performs basic chain validation, with the following behaviour:

  • The “minimal” engine validates only the chain as received; there is no path building.

  • The engine uses a configurable set of trust anchors. Each trust anchor is the combination of a name (a DER-encoded X.500 name) and a public key. There are two types of trust anchors:

    • CA trust anchors are used to verify signatures on certificates. If validation encounters a certificate whose issuerDN matches a trust anchor name, and the signature on the certificate is verifiable against the anchor’s public key, then validation succeeds and subsequent certificates are ignored. Thus, trust anchors can be root CA but also intermediate CA.

    • Non-CA trust anchors implement direct trust: if the EE certificate matches a non-CA trust anchor (subjectDN identical to the anchor name, and same public key), then validation succeeds. The EE certificate itself is still fully decoded to allow for name extraction and matching, and key usage gathering.

  • Subject / issuer DN are matched with binary equality only. This was allowed in a previous version of X.509 (RFC 3280); current version (RFC 5280) mandates some relatively heavy DN normalisation (including lots of Unicode case folding) that BearSSL does not implement. In practice, though, existing CA ensure identical names, so that specific constraint is not a problem.

    Actual DN matching is down with hashing (to avoid buffering names that can be arbitrarily long). The hash function to use is provided as parameter to the br_x509_minimal_init() function.

  • Expiry dates (notAfter and notBefore) are checked for each certificate (except those after the trust anchor). If BearSSL was compiled for a platform on which current date and time can be easily obtained, then this will be done automatically. Otherwise, the date and time must be provided externally with a br_x509_minimal_set_time() call.

    • That function expects the date and time as a pair of 32-bit integers; one counts the number of days since January 1st, 0 AD, while the other is the number of seconds since midnight. This is made so to avoid the Year 2038 problem.

    • The count of days uses a proleptic Gregorian calendar, i.e. the virtual extension of the Gregorian rules, currently in force in almost all countries, to dates prior to the actual adoption of the calendar rules on October 15th, 15822.

    • The count of seconds is between 0 and 86400. The value 86400 (corresponding to the 61st second of the last minute of the day) is technically possible in case of a leap second. In general, leap seconds can be safely ignored in everything that involves certificate, since they operate with much coarser dates (certificates are typically valid for years so such accuracy is not needed).

  • X.509 certificate extensions are processed:

    • The Basic Constraints extension is honoured, not only for the “CA” flag (all certificates except the EE must have a Basic Constraints extension with that flag set to TRUE), but also for the maximum path length constraint3.

    • The Key Usage extension is honoured. For the EE certificate, the relevant flags are used to decide which cipher suites or authentication mechanisms can be used with that public key; for CA certificates, the keyCertSign bit is required if the Key Usage extension is present.

    • The “minimal” engine does not handle revocation. Therefore, it ignores the CRL Distribution Points extension and the Freshest CRL extension.

    • Some informative extensions (Authority Key Identifier, Subject Key Identifier, Issuer Alt Name, Subject Directory Attributes, Authority Info Access and Subject Info Access) are also ignored.

    • The Subject Alt Name extension is used, for extracting names, especially for server name matching. As for wildcards, BearSSL supports a single “*” character, standing for the complete first name element of the name; in other words, only a starting “*.” sequence will be considered to be a wildcard.

    • For all other extensions, they are ignored if non-critical, or imply validation failure if marked critical.

  • Public keys of type RSA and EC are extracted.

    • RSA keys must be no longer than 4096 bits. Furthermore, the total size of the encoded modulus and encoded public exponent must not exceed 520 bytes, so a 4096-bit RSA key must have a public exponent that fits on at most 8 bytes4.

    • EC keys must use a “named curve” which must be one of the curves supported by BearSSL (currently only three, the NIST curves P-256, P-384 and P-521; other curves, especially Curve25519 and Curve448, will be added in due course).

  • Signatures are verified, subject to the algorithms that are configured in the engine. Hash functions from the SHA family (SHA-1 to SHA-512) can be used for hashing; MD5 is explicitly disallowed. Support for any hash function can be disabled by simply not configuring its implementation in the validation engine (I expect some people to do that for SHA-1). RSA signatures (PKCS#1 v1.5) and ECDSA are supported (again, if the corresponding implementations are provided).

As an illustration to the trust anchor management: let’s suppose that a SSL server sends a chain consisting in three certificates C0, C1 and C2 (in that order). The SSL client, using the “minimal” engine, will apply the following process:

  1. If C0’s subjectDN matches a non-CA trust anchor name, and the public key in C0 is identical to the key of that anchor, then success (this is direct trust).

  2. If C0’s issuerDN matches a CA trust anchor name, and the signature on C0 can be verified with the anchor’s public key, then success.

  3. If C0’s issuerDN does not match C1’s subjectDN, or the signature on C0 cannot be verified with the public key in C1, then failure.

  4. If C1’s issuerDN matches a CA trust anchor name, and the signature on C1 can be verified with the anchor’s public key, then success.

  5. If C1’s issuerDN does not match C2’s subjectDN, or the signature on C1 cannot be verified with the public key in C2, then failure.

  6. If C2’s issuerDN matches a CA trust anchor name, and the signature on C2 can be verified with the anchor’s public key, then success.

  7. Failure.

(In these steps I omitted processing of expiry dates, certificate extensions, and name checks.)

If, for instance, the client uses C2 as trust anchor, then the process will succeed at step 4; the certificate C2 as transmitted will be simply ignored and the server might have omitted it. In all generality, the validation walks the chain, verifying signatures and names, until it can hook up on one of the trust anchors.

The “minimal” engine is initialised with br_x509_minimal_init(). That function expects as parameters a pointer to the context to initialise, a hash function implementation for DN comparisons (any hash function may be used, but of course a collision-resistant one is preferred), and a list of trust anchors. The list of trust anchors may be arbitrarily long, but if you provide thousands of them, then some slowdown may be expected, since each name lookup will entail re-hashing all these names (and your trust model is weird, too).

Hash function implementations (for signature support) are set with br_x509_miminal_set_hash(). Signature verification implementations are set with br_x509_minimal_set_rsa() and br_x509_minimal_set_ecdsa(), for RSA and ECDSA, respectively.

An additional minimal size for RSA keys can be configured with br_x509_minimal_set_minrsa(). The minimal size is expressed in bytes; RSA keys smaller than that size will be rejected. The default minimal size is 128 bytes, thereby refusing to use RSA keys of less than 1017 bits. Generally speaking, issuing CA are supposed to enforce proper key sizes, and such a test should not be needed, but existing practice by slightly sloppy CA, and conformance issues, may force the use of that minimal RSA key size constraint.

Additional name extraction can be configured with br_x509_minimal_set_name_elements() (see below).

Name Extraction

Validating a certificate is all fine, in that it allows a “relying party” to gain some confidence in the binding of a public key with an identity. It is still necessary to extract that identity and check it. For instance, certificate validation by a client may inform it that the server’s certificate is genuine, i.e. is a real certificate truly signed by a trusted CA; however, the client also needs to make sure that this genuine certificate was issued to the expected server, not to some other server.

Identity checks are automatic in the case of the server’s certificate: the SSL client engine provides the intended server name to the start_chain() method, and the “minimal” validation engine will enforce RFC 2818 rules (the name must match one of the dNSName instances in the Subject Alt Name extension, or one of the Common Name elements in the subjectDN if there is no SAN extension). For the client certificate (sent upon request by the server, and validated by the server), there is no really standard specification of which name elements shall be used in the client certificate. Therefore, a generic name extraction API is provided by BearSSL.

The API consists in an array of br_name_element structures that are provided by the caller, with a call to br_x509_minimal_set_name_elements(). Each br_name_element structure identifies a specific name element, and points to a destination buffer in which the name will be written if encountered. The following notes apply:

  • Names can be elements of the subjectDN or in the SAN extension.

  • Extracted names must use one of the following ASN.1 types:

    • NumericString, PrintableString, IA5String and TeletexString are decoded with ISO-8859-1 (“latin-1”) conventions. There is no validation that the decoded characters are really part of the set of characters allowed by that specific ASN.1 type.

    • UTF8String is decoded with UTF-8 conventions. Invalid UTF-8 encodings (including a non-minimal length) are rejected. A leading BOM (U+FEFF code point), if present, is skipped.

    • BMPString is decoded with UTF-16 conventions (big-endian by default, but a leading BOM is properly detected and may be used to switch to little-endian). Surrogate pairs are assembled; lone surrogates imply rejection.

    • If any of the encodings produces a code point of value 0, or a noncharacter5, then the string as a whole is rejected.

  • Extracted names are converted to UTF-8 and written in the relevant buffer with a terminating zero. If the buffer is too small to receive the name, then the name is rejected.

  • There is an internal limitation (that might be removed in a future version), by which names longer than 255 bytes (when encoded in UTF-8, but without the terminating zero) are rejected, even if the destination buffer would allow it.

  • Each br_name_element identifies a specific name, with its oid field:

    • For name elements in the subject DN, the oid field shall point to an encoded OID, consisting in a leading length byte, followed by the DER-encoded OID value (no tag or length). E.g. the OID 2.5.4.3, for id-at-commonName, shall be specified as an array of four bytes, with values 03 55 04 03 (the leading byte of value 3 specifies that it is followed by exactly 3 bytes).

    • For SAN elements other than otherName, the oid field shall point to a two-byte array, first one being 0, and second one being the tag value. This is supported for rfc822Name (with 00 01), dNSName (with 00 02), and uniformResourceIdentifier (with 00 06).

    • For SAN elements of type otherName, the oid field shall point to an array that begins with two bytes of value 0, followed by an encoded OID as described above (with the leading length byte). In practice, this is mostly used for Microsoft’s UPN (a “User Principal Name” with a syntax reminiscent of email addresses), with the encoded string 00 00 0A 2B 06 01 04 01 82 37 14 02 03.

  • When the EE certificate is received, the status field of all provided name elements is set to zero. Then, as each name is received, if its type matches the oid element of one of the still-unused structures, then the name is extracted, and the status is set to 1 on success, or -1 on error on that name. It is possible to configure several br_name_element structures with the same oid, so as to gather several names of the same type, if the certificate does contains several such names: the matching names will fill the structures in the order they are encountered.

In practice, name extraction is expected to be used by servers when requesting client certificates; they will probably target the subject DN name elements “Common Name” (for display purposes), “UID” (for actual account names), and some “Organization” and “Organizational Unit” elements (for systems where these values are used for user scoping). SAN elements rfc822Name (for email addresses) and otherName (for Microsoft UPN) should also be typical targets.

Certificate Choosing

All of the above relates to how certificates are processed when they are received. API are also defined for the sending.

The SSL server chooses its certificate chain with an implementation of br_ssl_server_policy_class (see the header file). This is (yet again) an OOP context with vtable system. The choose() method is invoked when the ClientHello message has been received; it selects the cipher suite to use, and the certificate chain to send back. Depending on what cipher suite was chosen, one of do_keyx() or do_sign() will be called to use the server’s private key.

The choose() method receives a list of cipher suites that corresponds to the list sent by the client, trimmed to remove cipher suites that are not supported by the server, and possibly sorted by server preferences (if that specific flag was set). This API was meant to allow arbitrary implementations to be plugged in, especially external providers of private key cryptography (think: HSM, smart cards…). It also allows the server to select its certificate among an arbitrary number of certificates, based on, for instance, client support for specific key types, or intended server names (clients can send an intended server name as part of the ClientHello message, in the Server Name Indication extension).

BearSSL comes with basic implementations of that type, that simply return a single certificate chain, and use a software private key (RSA or EC, with BearSSL’s code). The context structures for these simple implementations are included in br_ssl_server_context, so there is no need for extra allocation by the caller if they are used. The chain and private key are provided with one of the br_ssl_server_init_*() methods. Note that in the case of EC keys, the key type of the CA that issued the server’s certificate should be provided, in order to support TLS_ECDH_* cipher suites.

An arbitrary, externally provided implementation of the br_ssl_server_policy_class type can be set with the br_ssl_server_set_policy() function.

The SSL client uses a similar API for the client certificate, relating to the br_ssl_client_certificate_class vtable type. This type has some extra methods for receiving the list of anchor names sent by the server. Indeed, the CertificateRequest message sent by the server may include a possibly long list of “anchor names” (DER-encoded X.500 names) that are supposed to identify the trust anchors that the server will use to validate the client certificate. The idea is that a client may use these names to choose the certificate to send.

The start_name_list(), start_name(), append_name(), end_name() and end_name_list() methods are invoked in due order to receive the names, one by one, and chunk by chunk within each name, in about the same way as certificate chain processing in the validation API.

There again, some simple implementations of the type are provided, with context structures already included in the SSL client context. The functions br_ssl_client_set_single_rsa() and br_ssl_client_set_single_ec() are used to configure a single RSA-based or EC-based certificate chain and private key, that will use BearSSL’s implementations for cryptographic operations. Alternatively, an externally provided implementation can be set with br_ssl_client_set_client_certificate().

Client certificates are requested by the server if the br_ssl_server_set_trust_anchor_names() function is called. That function receives the anchor names that should be sent to the client. In practice, most of the time, that list of names matches the list of names of anchors that will really be used to validate the client; in order to make the configuration task easier for the server application, the br_ssl_server_set_trust_anchor_names_alt() function may be called instead, to use a list of trust anchors as the source for the anchor names.

It shall be noted that since SSL supports renegotiations initiated by both the client and the server, the validation engines and certificate choosing contexts may be reused at any time, as long as the connection is alive. Therefore, provided arrays of trust anchors (and trust anchor names) shall remain valid at all times. If (and only if) renegotiations are disabled (with the BR_OPT_NO_RENEGOTIATION flag), then the structures used by validation may be released when the handshake is done. As of version 0.2 of BearSSL, end of the initial handshake can be detected by the engine accepting application data (br_ssl_engine_sendapp_buf() returns a non-NULL pointer).


  1. “Success” here means that the public key is given to the SSL engine; that engine will still perform all necessary cryptographic operations with that key; for instance, with a TLS_ECDHE_* cipher suite, a SSL client will verify the server’s signature computed over the ServerKeyExchange message.

  2. Or later in some other countries, e.g. Great Britain switched to it in 1752, while Russia waited for 1918.

  3. The “minimal” engine does not skip self-issued certificates from the path length count, so it may reject some paths which are technically valid, but not the other way round. In other words, it “fails safe”. Self-issued certificates are certificates with identical subject and issuer names, and might conceptually be used in some key renewal mechanisms, but in practice they are extremely rare.

  4. Almost all RSA keys in existence use the 17-bit value 65537 as public exponent, for reasons that have a lot more to do with tradition and mythology than security. Some widespread SSL/X.509 implementations cannot deal with public exponents larger than 32 bits, so 8 bytes ought to be enough. If the modulus is smaller than 4096 bits, the public exponent may be larger than 64 bits.

  5. Noncharacters are code points in the U+FDD0 to U+FEDF range, code points whose value ends with FFFE or FFFF when truncated to 16 bits, code points beyond U+10FFFD, and surrogates in the U+D800 to U+DFFF range.