blob: 93506e92bb8e6584fcb4c4877ece26bc37c48cae [file] [log] [blame] [view]
# BoringSSL API Conventions
This document describes conventions for BoringSSL APIs. The [style
guide](./STYLE.md) also includes guidelines, but this document is targeted at
both API consumers and developers. API documentation in BoringSSL may assume
these conventions by default, rather than repeating them for every function.
## Documentation
All supported public APIs are documented in the public header files, found in
`include/openssl`. The API documentation is also available
[online](https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html).
Experimental public APIs are found in `include/openssl/experimental`. Use of
these will likely be incompatible with changes in the near future as they are
finalized.
## Forward declarations
Do not write `typedef struct foo_st FOO` or try otherwise to define BoringSSL's
types. Including `openssl/base.h` (or `openssl/ossl_typ.h` for consumers who
wish to be OpenSSL-compatible) will forward-declare each type without importing
the rest of the library or invasive macros.
## Error-handling
Most functions in BoringSSL may fail, either due to allocation failures or input
errors. Functions which return an `int` typically return one on success and zero
on failure. Functions which return a pointer typically return `NULL` on failure.
However, due to legacy constraints, some functions are more complex. Consult the
API documentation before using a function.
On error, most functions also push errors on the error queue, an `errno`-like
mechanism. See the documentation for
[err.h](https://commondatastorage.googleapis.com/chromium-boringssl-docs/err.h.html)
for more details.
As with `errno`, callers must test the function's return value, not the error
queue to determine whether an operation failed. Some codepaths may not interact
with the error queue, and the error queue may have state from a previous failed
operation. After checking for failure, the caller can then inspect the error
queue in the failure case for details.
As a notable exception, some functions in the SSL/TLS library use a multi-step
process to indicate failure: First, the return value indicates whether the
operation failed. Then, `SSL_get_error` indicates whether the failure was due to
an error (`SSL_ERROR_SSL`) or some recoverable condition (e.g.
`SSL_ERROR_WANT_READ`). In the former case, the caller can use the error queue
for more information.
When ignoring a failed operation, it is recommended to call `ERR_clear_error` to
avoid the state interacting with future operations. Failing to do so should not
affect the actual behavior of any functions, but may result in errors from both
operations being mixed in error logging. We hope to
[improve](https://bugs.chromium.org/p/boringssl/issues/detail?id=38) this
situation in the future.
Where possible, avoid conditioning on specific reason codes and limit usage to
logging. The reason codes are very fine-grained and tend to leak details of the
library's internal structure. Changes in the library often have a side effect of
changing the exact reason code returned.
## Memory allocation
BoringSSL allocates memory via `OPENSSL_malloc`, found in `mem.h`. Use
`OPENSSL_free`, found in the same header file, to release it. BoringSSL
functions will fail gracefully on allocation error, but it is recommended to use
a `malloc` implementation that `abort`s on failure.
## Pointers and slices
Unless otherwise specified, pointer parameters that refer to a single object,
either as an input or output parameter, may not be `NULL`. In this case,
BoringSSL often will not check for `NULL` before dereferencing, so passing
`NULL` may crash or exhibit other undefined behavior. (Sometimes the function
will check for `NULL` anyway, for OpenSSL compatibility, but we still consider
passing `NULL` to be a caller error.)
Pointer parameters may also refer to a contiguous sequence of objects, sometimes
referred to as a *slice*. These will typically be a pair of pointer and length
parameters named like `plaintext` and `plaintext_len`, or `objs` and `num_objs`.
We prefer the former for byte buffers and the latter for sequences of other
types. The documentation will usually refer to both parameters together, e.g.
"`EVP_DigestUpdate` hashes `len` bytes from `data`."
Parameters in C and C++ that use array syntax, such as
`uint8_t out[SHA256_DIGEST_LENGTH]`, are really pointers. In BoringSSL's uses of
this syntax, the pointer must point to the specified number of values.
In other cases, the documentation will describe how the function parameters
determine the slice's length. For example, a slice's length may be measured in
units other than element count, multiple slice parameters may share a length, or
a slice's length may be implicitly determined by other means like RSA key size.
By default, BoringSSL follows C++'s
[slice conventions](https://davidben.net/2024/01/15/empty-slices.html)
for pointers. That is, unless otherwise specified, pointers for non-empty
(non-zero length) slices must be represented by a valid pointer to that many
objects in memory. Pointers for empty (zero length) slices must either be `NULL`
or point within some sequence of objects of a compatible type.
WARNING: The dangling, non-null pointer used by Rust empty slices may *not* be
passed into BoringSSL. Rust FFIs must adjust such pointers to before passing to
BoringSSL. For example, see the `FfiSlice` abstraction in `bssl-crypto`. (We may
relax this if pointer arithmetic rules in C/C++ are adjusted to permit Rust's
pointers. Until then, it is impractical for a C/C++ library to act on such a
slice representation. See
[this document](https://davidben.net/2024/01/15/empty-slices.html) for more
discussion.)
In some cases, OpenSSL compatibility requires that a function will treat `NULL`
slice pointers differently from non-`NULL` pointers. Such behavior will be
described in documentation. For examples, see `EVP_EncryptUpdate`,
`EVP_DigestSignFinal`, and `HMAC_Init_ex`. Callers passing potentially empty
slices into such functions should take care that the `NULL` case is either
unreachable or still has the desired behavior.
If a `const char *` parameter is described as a "NUL-terminated string" or a
"C string", it must point to a sequence of `char` values containing a NUL (zero)
value, which determines the length. Unless otherwise specified, the pointer may
not be `NULL`, matching the C standard library.
For purposes of C and C++'s
[strict aliasing](https://en.cppreference.com/w/c/language/object#Strict_aliasing)
requirements, objects passed by pointers must be accessible as the specified
type. `uint8_t` may be assumed to be the same type as `unsigned char` and thus
may be the pointer type for all object types. BoringSSL does not support
platforms where `uint8_t` is a non-character type. However, there is no
strict aliasing sanitizer, very few C and C++ codebases are valid by strict
aliasing, and BoringSSL itself has some
[known strict aliasing bugs](https://crbug.com/boringssl/444), thus we strongly
recommend consumers build with `-fno-strict-aliasing`.
Pointer parameters additionally have ownership and lifetime requirements,
discussed in the section below.
## Object initialization and cleanup
BoringSSL defines a number of structs for use in its APIs. It is a C library,
so the caller is responsible for ensuring these structs are properly
initialized and released. Consult the documentation for a module for the
proper use of its types. Some general conventions are listed below.
### Heap-allocated types
Some types, such as `RSA`, are heap-allocated. All instances will be allocated
and returned from BoringSSL's APIs. It is an error to instantiate a heap-
allocated type on the stack or embedded within another object.
Heap-allocated types may have functioned named like `RSA_new` which allocates a
fresh blank `RSA`. Other functions may also return newly-allocated instances.
For example, `RSA_parse_public_key` is documented to return a newly-allocated
`RSA` object.
Heap-allocated objects must be released by the corresponding free function,
named like `RSA_free`. Like C's `free` and C++'s `delete`, all free functions
internally check for `NULL`. It is redundant to check for `NULL` before calling.
A heap-allocated type may be reference-counted. In this case, a function named
like `RSA_up_ref` will be available to take an additional reference count. The
free function must be called to decrement the reference count. It will only
release resources when the final reference is released. For OpenSSL
compatibility, these functions return `int`, but callers may assume they always
successfully return one because reference counts use saturating arithmetic.
C++ consumers are recommended to use `bssl::UniquePtr` to manage heap-allocated
objects. `bssl::UniquePtr<T>`, like other types, is forward-declared in
`openssl/base.h`. Code that needs access to the free functions, such as code
which destroys a `bssl::UniquePtr`, must include the corresponding module's
header. (This matches `std::unique_ptr`'s relationship with forward
declarations.) Note, despite the name, `bssl::UniquePtr` is also used with
reference-counted types. It owns a single reference to the object. To take an
additional reference, use the `bssl::UpRef` function, which will return a
separate `bssl::UniquePtr`.
### Stack-allocated types
Other types in BoringSSL are stack-allocated, such as `EVP_MD_CTX`. These
types may be allocated on the stack or embedded within another object.
However, they must still be initialized before use.
Every stack-allocated object in BoringSSL has a *zero state*, analogous to
initializing a pointer to `NULL`. In this state, the object may not be
completely initialized, but it is safe to call cleanup functions. Entering the
zero state cannot fail. (It is usually `memset(0)`.)
The function to enter the zero state is named like `EVP_MD_CTX_init` or
`CBB_zero` and will always return `void`. To release resources associated with
the type, call the cleanup function, named like `EVP_MD_CTX_cleanup`. The
cleanup function must be called on all codepaths, regardless of success or
failure. For example:
uint8_t md[EVP_MAX_MD_SIZE];
unsigned md_len;
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx); /* Enter the zero state. */
int ok = EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL) &&
EVP_DigestUpdate(&ctx, "hello ", 6) &&
EVP_DigestUpdate(&ctx, "world", 5) &&
EVP_DigestFinal_ex(&ctx, md, &md_len);
EVP_MD_CTX_cleanup(&ctx); /* Release |ctx|. */
Note that `EVP_MD_CTX_cleanup` is called whether or not the `EVP_Digest*`
operations succeeded. More complex C functions may use the `goto err` pattern:
int ret = 0;
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
if (!some_other_operation()) {
goto err;
}
uint8_t md[EVP_MAX_MD_SIZE];
unsigned md_len;
if (!EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL) ||
!EVP_DigestUpdate(&ctx, "hello ", 6) ||
!EVP_DigestUpdate(&ctx, "world", 5) ||
!EVP_DigestFinal_ex(&ctx, md, &md_len) {
goto err;
}
ret = 1;
err:
EVP_MD_CTX_cleanup(&ctx);
return ret;
Note that, because `ctx` is set to the zero state before any failures,
`EVP_MD_CTX_cleanup` is safe to call even if the first operation fails before
`EVP_DigestInit_ex`. However, it would be illegal to move the `EVP_MD_CTX_init`
below the `some_other_operation` call.
As a rule of thumb, enter the zero state of stack-allocated structs in the
same place they are declared.
C++ consumers are recommended to use the wrappers named like
`bssl::ScopedEVP_MD_CTX`, defined in the corresponding module's header. These
wrappers are automatically initialized to the zero state and are automatically
cleaned up.
### Data-only types
A few types, such as `SHA_CTX`, are data-only types and do not require cleanup.
These are usually for low-level cryptographic operations. These types may be
used freely without special cleanup conventions.
### Ownership and lifetime
When working with allocated objects, it is important to think about *ownership*
of each object, or what code is responsible for releasing it. This matches the
corresponding notion in higher-level languages like C++ and Rust.
Ownership applies to both uniquely-owned types and reference-counted types. For
the latter, ownership means the code is responsible for releasing one
reference. Note a *reference* in BoringSSL refers to an increment (and eventual
decrement) of an object's reference count, not `T&` in C++. Thus, to "take a
reference" means to increment the reference count and take ownership of
decrementing it.
As BoringSSL's APIs are primarily in C, ownership and lifetime obligations are
not rigorously annotated in the type signatures or checked at compile-time.
Instead, they are described in
[API documentation](https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html).
This section describes some conventions.
Unless otherwise documented, functions do not take ownership of pointer
arguments. The pointer typically must remain valid for the duration of the
function call. The function may internally copy information from the argument or
take a reference, but the caller is free to release its copy or reference at any
point after the call completes.
A function may instead be documented to *take* or *transfer* ownership of a
pointer. The caller must own the object before the function call and, after
transfer, no longer owns it. As a corollary, the caller may no longer reference
the object without a separate guarantee on the lifetime. The function may even
release the object before returning. Callers that wish to independently retain a
transfered object must therefore take a reference or make a copy before
transferring. Callers should also take note of whether the function is
documented to transfer pointers unconditionally or only on success. Unlike C++
and Rust, functions in BoringSSL typically only transfer on success.
Likewise, output pointers may be owning or non-owning. Unless otherwise
documented, functions output non-owning pointers. The caller is not responsible
for releasing the output pointer, but it must not use the pointer beyond its
lifetime. The pointer may be released when the parent object is released or even
sooner on state change in the parent object.
If documented to output a *newly-allocated* object or a *reference* or *copy* of
one, the caller is responsible for releasing the object when it is done.
By convention, functions named `get0` return non-owning pointers. Functions
named `new` or `get1` return owning pointers. Functions named `set0` take
ownership of arguments. Functions named `set1` do not. They typically take a
reference or make a copy internally. These names originally referred to the
effect on a reference count, but the convention applies equally to
non-reference-counted types.
API documentation may also describe more complex obligations. For instance, an
object may borrow a pointer for longer than the duration of a single function
call, in which case the caller must ensure the lifetime extends accordingly.
Memory errors are one of the most common and dangerous bugs in C and C++, so
callers are encouraged to make use of tools such as
[AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) and
higher-level languages.
## Thread safety
BoringSSL is internally aware of the platform threading library and calls into
it as needed. Consult the API documentation for the threading guarantees of
particular objects. In general, stateless reference-counted objects like `RSA`
or `EVP_PKEY` which represent keys may typically be used from multiple threads
simultaneously, provided no thread mutates the key.