This section shows an overview of the BearSSL API, to initiate and run SSL clients and servers. The source code contains some sample code in the samples subdirectory; in particular, a basic client is shown in client_basic.c, while a basic server is in server_basic.c.

Context Structures

BearSSL does not perform any dynamic allocation1; the calling application is supposed to perform necessary allocations.

An SSL client will need three such elements:

  • A client context of type br_ssl_client_context. This is a relatively ponderous structure (about 3 to 4 kilobytes).

  • An I/O buffer that is large enough to contain one or two records.

  • A certificate validation context whose job is to provide the server’s public key, based on the certificate chain sent by the server.

An SSL server will similarly need three elements:

  • A server context of type br_ssl_server_context.

  • An I/O buffer.

  • An optional session cache used to remember secret keys established with clients, to support session resumption.

These elements can be allocated anywhere in (writable) memory, e.g. heap, data segment or stack. They must not be moved while in use (they may contain pointers to each other and to themselves). BearSSL does not access writable memory beyond these contexts (and the stack), so any number of concurrently managed SSL connections can run without impacting each other, even in different threads, as long as they use distinct context structures and buffers.

(It is expected that client certificate support, when implemented, will need an extra context element on both client and server.)

Initialisation

Profiles

The simplest way to initialise the context structures is to use a profile. This is an already written function that does everything as appropriate, with decent default settings. Right now, there is only one client profile, called “full”. This works like this:

br_ssl_client_context sc;
br_x509_minimal_context xc;

br_ssl_client_init_full(&sc, &xc, TAs, TAs_NUM);

The client context is sc; the X.509 validation engine uses the xc context structure. TAs points to an array of trust anchors (with TAs_NUM elements); each trust anchor is a structure of type br_x509_trust_anchor and contains an anchor name (an encoded Distinguished Name) and a public key (RSA or EC), along with some flags (namely, whether the anchor is a Certification Authority, or simply a directly trusted server key).

If you want to hardcode the trust anchors in your C source code, then the brssl command-line tool can help you produce the arrays:

brssl ta file1.crt file2.pem

This command will extract certificates from file1.crt and file2.pem, interpret them as trust anchors, and produce the relevant C source code. The file names are unimportant (the extensions like “.crt” have no meaning for brssl); DER and PEM encodings are automatically recognised. When using PEM, several certificates can be stored in the same file; brssl will extract them all (and ignore extra non-certificate objects).

If you prefer your application to be configurable in that respect, and dynamically decode root certificates from files, then BearSSL provides some decoding API (br_pem_decoder_context for PEM, then br_x509_decoder_context for parsing X.509 certificates).

The “full” client profile has the following properties:

  • Supports all “reasonably secure” cipher suites, in a decent order (3DES and non-forward-secure suites are at the bottom of the list).

  • Uses minimal X.509 validation for the server’s certificate. This validation engine checks subject/issuer name matchings, basic constraints, key usage, and (of course) cryptographic signatures. It also rejects certificates with unknown critical extensions. RSA and ECDSA signatures are supported. An important caveat is that the minimal X.509 engine does not support revocation checks.

For the server, a typical profile invocation would be:

br_ssl_server_context sc;

br_ssl_server_init_full_rsa(&sc, CHAIN, CHAIN_LEN, &SKEY);

which uses the “full RSA” profile (server’s key has type RSA). The CHAIN value points to an array of br_x509_certificate structures that encode the certificate chain to send to clients (it contains CHAIN_LEN elements). The SKEY structure is an RSA private key (br_rsa_private_key). The command-line tool (brssl) can help in generating C source code that hardcodes such elements (see brssl chain and brssl skey).

Take care that hardcoding private keys in source code is likely a poor idea, because source code tends to leak to developer systems and version control and backups, and hardcoding a private key often leads to deploying the same private key in thousands of systems, at which point the private key is not really private any more. Alternatively, you could read the private key from a dedicated storage area in your system (say, a “file”), either in a custom format, or using one of the “standard” formats supported by br_skey_decoder_context.

The provided server profile (so far) are:

  • “full_rsa”: full support of all versions and suites, for a RSA server key.

  • “full_ec”: full support of all versions and suites, for an EC server key. Since ECDH cipher suites are made distinct depending on the key type of the certification authority that issued the server’s certificate, this information must be provided by the caller.

  • “minr2g”: minimal support, RSA key exchange, AES/GCM, TLS-1.2.

  • “mine2g”: minimal support, ECDHE_RSA key exchange, AES/GCM, TLS-1.2.

  • “minf2g”: minimal support, ECDHE_ECDSA key exchange, AES/GCM, TLS-1.2.

  • “minu2g”: minimal support, ECDH_RSA key exchange, AES/GCM, TLS-1.2 (server key type is EC, and its certificate was signed with RSA).

  • “minv2g”: minimal support, ECDH_ECDSA key exchange, AES/GCM, TLS-1.2. (server key type is EC, and its certificate was signed with ECDSA).

  • “mine2c”: minimal support, ECDHE_RSA key exchange, ChaCha20+Poly1305, TLS-1.2 (starting from BearSSL 0.2).

  • “minf2c”: minimal support, ECDHE_ECDSA key exchange, ChaCha20+Poly1305, TLS-1.2 (starting from BearSSL 0.2).

A custom profile is any code that does the initialisation of all needed algorithms manually. See the custom_profile.c sample for details.

I/O Buffer

The client and server contexts both contain an inner structure of type br_ssl_engine_context. That field is the first field, and is called eng. Most of the processing of an SSL connection, beyond initialisation, uses only that substructure, and is common between client and server. Functions that work on the common engine substructure have names in br_ssl_engine_*().

The I/O buffer must be large enough to accommodate one or two full records. BearSSL can be configured in “mono” (half-duplex) or “bidi” (full-duplex) modes.

  • Half-duplex mode means that the same buffer is used for both incoming and outgoing records. This saves RAM, but it limits support of applicative protocols that are not half-duplex themselves.

    An example is HTTP. In HTTP, the client sends requests, to which the server responds, but the client is allowed to send several requests in a row. In an SSL context, this means that an incoming record may contain two or more HTTP requests; if the server processes the first request, it cannot begin to answer it because the I/O buffer still contains unread data (the other HTTP requests). Full HTTP support requires, on the server side, full-duplex management.

  • Full-duplex mode means that the I/O buffer is split into two parts, one for incoming records, the other for outgoing data.

The maximum size of an SSL/TLS record is 16384 plaintext bytes. With a MAC (at most 48 extra bytes, for HMAC/SHA-384), padding (at most 256 bytes), explicit IV for CBC (16 bytes) and record header (5 bytes), the maximum per-record overhead is 325 bytes. Thus, the buffer for incoming data should ideally have size 16384 + 325 = 16709 bytes. The macro BR_SSL_BUFSIZE_INPUT evaluates to that value.

For output, BearSSL does not use more padding bytes than necessary; but it also enforces the “1/n-1 split” when using TLS-1.0. The resulting overhead is then at most 85 bytes. The buffer for outgoing data should then have size 16384 + 85 = 16469 bytes. The macro BR_SSL_BUFSIZE_OUTPUT evaluates to that value.

There are two functions that can be used to set the I/O buffers. The br_ssl_engine_set_buffers_bidi() function sets the incoming and outgoing buffers separately:

unsigned char iobuf_in[BR_SSL_BUFSIZE_INPUT];
unsigned char iobuf_out[BR_SSL_BUFSIZE_OUTPUT];

br_ssl_engine_set_buffers_bidi(&sc.eng,
        iobuf_in, sizeof iobuf_in,
        iobuf_out, sizeof iobuf_out);

The other function br_ssl_engine_set_buffer() takes a single buffer, and a flag:

unsigned char iobuf[BR_SSL_BUFSIZE_BIDI];

br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1);

The BR_SSL_BUFSIZE_BIDI macro evaluates to the sum of BR_SSL_BUFSIZE_INPUT and BR_SSL_BUFSIZE_OUTPUT. If the last argument to br_ssl_engine_set_buffer() is non-zero, then the function splits the provided buffer into input and output buffers, for full-duplex processing; if that argument is zero, then half-duplex mode is used. When provided with a single buffer, then the maximum size that BearSSL will use is BR_SSL_BUFSIZE_BIDI (33178 bytes) in full-duplex mode, BR_SSL_BUFSIZE_MONO (16709 bytes) in half-duplex mode.

What if you provide fewer bytes? BearSSL will then adapt. With br_ssl_engine_set_buffer() and full-duplex mode, BearSSL will try to favour the input buffer, so as to be able to support all records sent by the peer. The following rules apply:

  • Before completion of the first handshake, there is no encryption or MAC, and BearSSL can process incoming records of arbitrary length (even if larger than the input buffer size).

  • After completion of the first handshake, BearSSL must be able to store a complete incoming record: it needs to verify the MAC before allowing the application to access the data. Records up to the configured buffer size will be accepted, but larger records will trigger an error (BR_ERR_TOO_LARGE).

  • Outgoing records will never be larger than the outgoing buffer size. Note that SSL/TLS allows for arbitrary fragmentation, so this should not be a problem2.

  • If the incoming buffer size, as configured (or after split) is less than BR_SSL_BUFSIZE_INPUT (16709 bytes), then BearSSL, as a client, will use the Maximum Fragment Length Negotiation extension to try to make the server agree not to use larger records.

    Unfortunately, the extension is asymmetrical, in that it is for the client only; a RAM-constrained server has no standard way to announce that it cannot handle full-sized records. Moreover, while BearSSL, as a server, recognises and honours that extension, not all existing, deployed SSL server implementations do so. Therefore, using smaller buffers in heterogeneous system deployments is a kind of hit-and-miss game. To some extent, an application can force its SSL library to emit short records by making flush calls, but this is fragile.

The minimum size of a buffer is 512 plaintext bytes. Therefore, a fully reduced half-duplex SSL client or server using BearSSL can operate with a buffer as small as 512 + 325 = 837 bytes.

Session Cache

SSL clients and servers can remember “sessions”, and resume them. A resumed session is an abbreviated handshake in which a previously negotiated shared key is reused. Resuming sessions avoids doing the asymmetric crypto again, and uses fewer and shorter messages, so this is a good thing for performance, but it requires the client and the server to remember the session parameters.

On the client side, a session is reused if the same context is used again (see below for context reuse). The “session parameters” can also be extracted with br_ssl_engine_get_session_parameters(), and injected in another context structure with br_ssl_engine_set_session_parameters(). This allows resuming sessions with another context, and, in case of a client that runs several connections to the same server in parallel, sharing the same session for these connections.

On the server side, a cache is set with the following call:

unsigned char store[5000];

br_ssl_session_cache_lru_init(&sc, store, sizeof store);

The store array is where remembered session parameters will be stored. Arbitrary store sizes can be used; with the current implementation, each remembered session requires about 100 bytes, so with a 5000-byte cache array, 50 sessions or so will be remembered. The cache is maintained with an LRU (Least Recently Used) eviction policy.

An object-oriented API, called br_ssl_session_cache_class, is defined, allowing an external session cache implementation to be provided and used with a server context. The br_ssl_server_set_cache() function is used to configure use of that implementation in the server context.

Reset and SNI

Once the context is initialised, an explicit “reset” call MUST be performed. For the client, br_ssl_client_reset() takes three arguments:

  • a pointer to the client context;

  • the name of the target server;

  • a “resume session” flag.

The “resume session” flag, if non-zero, allows the context to try to resume a session, reusing the session parameters remembered from the last connection that used that specific context (see below for context reuse).

The “server name” relates to the two following points:

  • That name will be sent in the ClientHello message, as the SNI extension (Server Name Indication). This is used by the server to select the appropriate certificate (in co-hosting contexts).

  • The name will be matched against the names found in the server’s certificate, in the X.509 minimal validation engine. The engine supports basic wildcard usage (only at the start of the name).

If the server_name parameter is NULL or is an empty string, then both functionalities are deactivated: no name sent as SNI, and no matching done with regards to the received certificate (thus, not using an explicit server name here is usually a bad idea).

The reset function for the server side, br_ssl_server_reset(), has no extra parameter beyond the pointer to the server context.

I/O

Error Status

At any time, an SSL engine is either functioning, or failed. A failed engine does not do anything until explicitly reset.

The current status can be obtained with:

int err;

err = br_ssl_engine_last_error(&sc.eng);

If the engine is functioning, then this returns BR_ERR_OK (which has value 0). Otherwise, this returns a non-zero error code.

The bearssl_ssl.h file defines constant for SSL/TLS errors (their names begin with BR_ERR_). Additional error codes are defined in bearssl_x509.h for error codes related to X.509 certificate processing.

All these error codes fall in the 1..255 range. Error codes can also be in the 256..511 range for received fatal alerts (the peer sent an error message that terminated the connection), and 512..767 range for sent fatal alerts.

Simplified I/O

Simplified I/O is a wrapper around an SSL engine to provide a callback-based API. This – again – uses a caller-allocated context. The client_basic.c and server_basic.c samples use that simplified I/O.

This basically entails:

  • Defining a “read” callback function, that reads 1 or more bytes. It shall block until at least 1 byte is read, or an error or transport closure happens.

  • Defining a “write” callback function, that writes 1 or more bytes. It shall block until at least 1 byte is written, or an error or transport closure happens.

  • Using a br_sslio_context structure initialised with br_sslio_init() over an SSL engine and the two callbacks.

Then the following functions are provided:

  • br_sslio_read(): read some application data bytes (blocks until at least 1 byte is obtained, or an error/closure occurs).

  • br_sslio_read_all(): read some application data bytes (blocks until all requested bytes are obtained, or an error/closure occurs).

  • br_sslio_write(): write some application data bytes (blocks until at least 1 byte is written, or an error/closure occurs).

  • br_sslio_write_all(): write some application data bytes (blocks until all bytes are written, or an error/closure occurs).

  • br_sslio_flush(): flush currently buffered data (if any). Writing is inherently buffered; when br_sslio_write() or br_sslio_write_all() returns, the application data bytes have been injected into the engine, but not necessarily wrapped as records. The br_sslio_flush() calls ensures that all these bytes have been encrypted into records and sent to the “write” callback function.

  • br_sslio_close(): perform the SSL closure protocol. This entails sending a close_notify alert, and receiving a close_notify response.

    Note that a number of deployed SSL implementations do not follow the protocol for closure, and may drop the underlying socket abruptly. As such, errors are often reported by br_sslio_close().

Generic I/O

The generic I/O API presents the engine as a state machine with four possible I/O channels:

  • sendapp: push some application data (plaintext) into the engine.
  • recvapp: obtain some application data (plaintext) from the engine.
  • sendrec: get records from the engine, to be conveyed to the peer.
  • recvrec: inject records freshly obtained from the peer, into the engine.

The “send” name qualifies outgoing data (plaintext that goes into the engine, yielding records that are sent to the peer), while the “recv” name is for incoming data. The exchanges of plaintext use the name “app”, while record data is “rec”.

At any time, the engine may be ready to accept and/or provide data along one or several of these channels, unless it is closed (normal closure or failure). Use br_ssl_engine_current_state() to obtain that status as a combination of flags:

  • BR_SSL_CLOSED: engine is closed. If this flag is set, then this is the only flag in the engine output.

  • BR_SSL_SENDREC: engine is ready for some “sendrec” activity.

  • BR_SSL_RECVREC: engine is ready for some “recvrec” activity.

  • BR_SSL_SENDAPP: engine is ready for some “sendapp” activity.

  • BR_SSL_RECVAPP: engine is ready for some “recvapp” activity.

For each channel, two functions are defined, buf() and ack(). For instance, for the “sendapp” channel, the two functions are:

unsigned char *br_ssl_engine_sendapp_buf(
        const br_ssl_engine_context *cc, size_t *len);
void br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len);

These functions have the following semantics:

  • The buf() function returns a pointer to the buffer where the I/O activity may take place (for “sendapp” and “recvrec”, the buffer where bytes may be written; for “recvapp” and “sendrec”, the buffer from which bytes may be read), and *len is set to the maximum size for that I/O (number of bytes that could be written or read).

    If this channel is not ready, then *len is set to 0 and the function returns NULL.

  • The ack() function is used to inform the engine that len bytes have been written to or read from the buffer returned by buf().

    The len value MUST NOT be 0.

Important rules:

  • The buf() calls do not modify the context; they do not constitute a promise by the caller that the corresponding I/O is about to take place.

  • Any ack() call invalidates what the previous buf() calls reported. In particular, if at any point the engine reports that application data can be written (“sendapp”) and records can be injected (“recvrec”), do not ever try to do both at the same time, notably because the two buffers may overlap.

    If you have application data to send, write it in the buffer and then call the br_ssl_engine_sendapp_ack() function, then use the buf() calls again to see if the engine can still accept some records. Don’t write to both buffers (for “sendapp” and “recvrec”) then only call the two corresponding ack().

As long as you respect these rules, you can handle I/O for any number of concurrent engines. None of these calls is blocking; they always return as promptly as the CPU allows.

The source code for the command-line tool contains a function that uses the generic API, called run_ssl_engine(). That function forwards application data back and forth, between a connected SSL socket, and the standard input/output file descriptors. All descriptors are set in non-blocking mode, and blocking is done with the poll() function (older Unix-like systems might use select() instead).

The code is organised as a loop, with the following steps:

  1. If the engine is in closed/failed state, exit.

  2. Use br_ssl_engine_current_state() to know which channels are open.

  3. Run poll() to wait for I/O on the descriptors corresponding to the open channels.

  4. If there is possible I/O on one channel, do it, and restart the loop (a continue clause in the source code). This restarting corresponds to the fact that the ack() call may change the internal state, including openness of channels.

The first time the “sendapp” channel opens marks the completion of the initial handshake.

Note that write operations are buffered. When the “sendapp” buffer is full, a record is assembled and encrypted, and scheduled for sending; however, if more plaintext data could fit in the next record, then the engine does not assemble the record. The br_ssl_engine_flush() function forces the record completion.

br_ssl_engine_flush() takes a second parameter: if non-zero, then this forces the assembly of an outgoing record even if there is no buffered application data at that point. This results in sending an empty record (that record has its own header and encryption and padding and MAC, but the plaintext contents have length 0). Empty records are nominally compliant to the SSL/TLS standard, but existing implementations do not all support them. Empty records might be used as a kind of “keep-alive” mechanism, to force some activity on the transport medium without actually exchanging application data bytes.

Extra Operations

Flags

Some extra boolean flags can be set to alter the behaviour in some way. The flag-setting functions br_ssl_engine_set_all_flags(), br_ssl_engine_add_flags() and br_ssl_engine_remove_flags() allow changing flag values. By default, no flag is set. Currently defined flags are:

  • BR_OPT_ENFORCE_SERVER_PREFERENCES: if set, then the server will enforce its own prefered order for cipher suite selection; otherwise, it will follow the client preferences. This flag has no effect on the client.

  • BR_OPT_NO_RENEGOTIATION: if set, then renegotiations are forbidden, whether asked programmatically or by the peer. Default behaviour is to allow renegotiation as long as the Secure Renegotiation extension is supported by both client and server.

  • BR_OPT_TOLERATE_NO_CLIENT_AUTH: if set on a server that asks for a client certificate, and the client does not send a certificate, then the handshake will nonetheless keep on and complete. By default, in such a case, the server code aborts the handshake. This flag has no effect on the client. Client certificates are described in more details in the X.509 page.

  • BR_OPT_FAIL_ON_ALPN_MISMATCH: the ALPN extension is used to negotiate an application-level protocol name (the protocol that will be used within the SSL tunnel). If this flag is set, and both client and server are configured to use that extension, and no common protocol can be found, then the handshake is aborted. By default, an ALPN negotiation failure does not prevent the handshake from completing.

Known Key Model

With the “full” client profile, an X.509 validation engine is used. With a custom profile, one can use the br_x509_knownkey_vtable implementation instead, which implements the “known key model”. This is for situations where the client system already knows the server’s public key. In that case, the “known key” engine simply ignores the chain sent by the server, and uses the configured public key instead.

The “known key” engine uses a context (of type br_x509_knownkey_context) initialised with br_x509_knownkey_init_rsa() or br_x509_knownkey_init_ec() (depending on key type: RSA or EC). To set the X.509 engine to use in a client context, call br_ssl_engine_set_x509().

(This is an “engine” call, not a client-specific call, because it will be usable on the server too, when client certificates are implemented.)

Closure

The br_ssl_engine_close() function triggers the closure, as part of the generic API. Once a closure has been initiated, some I/O must still be performed, since a normal SSL closure involves exchanging a pair of alert messages (of type close_notify and level “warning”). The caller should thus keep on running the engine until BR_SSL_CLOSED state is reached.

Many existing SSL/TLS implementations, when run over TCP sockets, simply drop the connection when they wish to close. This makes such closure indistinguishable from forced truncation by active attackers. Ironically, the lack of verified closure protocol was considered to be one of the mortal sins of SSL-2.0, but nowadays it seems to be OK. The underlying reason is that HTTP evolved:

  • HTTP-0.9, which was used in the heyday of SSL-2.0, used to indicate end of data by closing the connection. Each HTTP request required a new connection, which was wasteful. Thus, an attacker could force an undetected truncation of the requested data by sending a fake RST packet to drop the underlying TCP connection. SSL-2.0 would not detect the attacker’s action, but SSL-3.0 and later would, because of the lack of proper close_notify.

  • Modern HTTP (1.0 and later) has explicit content length headers (either Content-Length, or chunked encoding) so any truncation will be detected at the application level. This is how modern SSL libraries involved in HTTPS can get away with not sending the close_notify.

Since BearSSL does not perform I/O itself, you are free, as the caller, to drop the connection whenever you want, and thus be sloppy and not send the close_notify, if that is what you wish to do. Since BearSSL does not do any dynamic allocation, there is no resource to release and no penalty to not completing the SSL connection. However, if you use SSL in a protocol in which the transport medium keeps being used beyond the SSL closure, then you must do the close_notify exchange. BearSSL will do that for you.

Renegotiation

SSL has a feature called “renegotiation” in which client and server engage into a new handshake at any time after the initial handshake. The new handshake can be triggered by the client or by the server, and the messages will be encrypted.

With BearSSL, a renegotiation is triggered with br_ssl_engine_renegotiate(). This works on both clients and servers. Returned value is 1 on success, 0 on error; a “success” means that a new handshake will take place (but it has not begun yet). A failure happens when either the engine is not ready for a renegotiation (e.g. a handshake is already taking place), or the peer does not support the “secure renegotiation” extension.

Renegotiation, as it was initially defined, suffers from a problem, which is that an attacker may do a sort of man-in-the-middle, between a client that believes it is doing a single handshake, and a server that sees a renegotiation. This can be a vulnerability, depending on how the server behaves. Conceptually, the SSL handshake guarantees to the server that all subsequent data, until the next handshake, comes from the same client; the trouble happens when servers begin to assume that this property applies backwards, i.e. a client authenticated after a renegotiated handshake (e.g. with an HTTP cookie) is the same client as the one who sent some data before the renegotiation.

RFC 5746 describes the problem, and its solution: an extra TLS extension that allows clients and servers to unambiguously distinguish between first handshake and renegotiations. BearSSL implements this extension, and refuses to perform renegotiations with a peer who does not implement it.

In practice, there are mostly two contexts where renegotiations can be useful:

  • To support certificate-based client authentication on specific portions of a server: an HTTPS server may not ask for a client certificate upon first handshake, but will trigger a renegotiation after seeing the target path in the HTTP request. Microsoft IIS works that way, because Web browsers tend to have suboptimal user interfaces when a client certificate is requested.

  • To make a “key renewal”, e.g. when using 3DES/CBC encryption over more than a few gigabytes of data, to counter the Sweet32 attack. Arguably, a much better workaround is to use a decent block cipher instead, with 128-bit blocks (the AES comes to mind).

As described above, the BR_OPT_NO_RENEGOTIATION flag can be used to prohibit renegotiations. Note that TLS-1.3 bans renegotiations, and when HTTP/2 is used with TLS-1.2, renegotiations are not permitted either except at the very beginning of the connection.

Context Reuse

At any time, a context structure (client or server) may be reset, with an explicit br_ssl_client_reset() or br_ssl_server_reset() call. This reuses the cipher suite and algorithm configuration, and the configured I/O buffer. The profile function needs not be called again.

Moreover, for the client, resetting the context (instead of initialising a new one) means that the next connection attempt will try to resume the session (if the server supports it). Resuming a session uses a shorter and faster handshake. For a server context, session resumption depends on the configured session cache.

The br_ssl_client_forget_session() function can be used to make a client context “forget” its session.

Memory Wiping

Once a SSL connection has been seen to its completion, the resources it used can be released. It is usually considered a good idea to “wipe” memory so that no trace of secret data (in particular the encryption keys) remains. Whether such wiping actually matters depends on the context, and most wiping occurs more by Tradition than for an actual security reason. Nevertheless, memory wiping can be important, if only to woo auditors when complying to various security standards.

Since BearSSL does not perform any dynamic memory allocation, wiping is simple: the caller can simply use some memset() calls on the contexts and I/O buffers, and be done with it. For the stack, the caller may use a specific stack-wiping function like this:

#include <stddef.h>
#include <stdint.h>

void
wipe_stack(void)
{
        volatile uint32_t tmp[1024];
        size_t u;

        for (u = 0; u < (sizeof tmp) / (sizeof tmp[0]); u ++) {
                tmp[u] = 0;
        }
}

This function bombards the stack with zeros, thus obliterating any trace of secret data that may still linger on it. In this case, this clears 4 kilobytes, which should be enough: BearSSL’s code was designed to use less than 4 kilobytes of stack space at all times.

(TODO: this stack size restriction is important for embedded systems, where RAM is scarce. This implies a limitation on RSA key sizes: BearSSL supports RSA only up to 4096 bits, which “ought to be enough for everyone”. Actual stack usage depends on the architecture and what the C compiler did with the code, and it should be measured. Such measures have not been done yet, so the “less than 4 kilobytes”, while plausible, is still an estimate at this point.)


  1. I am not counting the stack here; but whatever a function allocated on the stack, it is released when the function returns.

  2. Some implementations have trouble with fragmentation, especially with handshake messages, but BearSSL can process all record sizes, including empty records.