-
Notifications
You must be signed in to change notification settings - Fork 776
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
We're starting to get quite a lot of these ALL-CAPS.md documents. There's been enough questions around how to properly use types like EVP_MD_CTX that we probably should write down some of these common rules. Change-Id: I125f4e82efb168a071b54ff76c5af34c42ff4800 Reviewed-on: https://boringssl-review.googlesource.com/9115 Reviewed-by: Adam Langley <[email protected]>
- Loading branch information
Showing
2 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
# 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. | ||
|
||
|
||
## 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). | ||
|
||
Some headers lack documention comments. These are functions and structures from | ||
OpenSSL's legacy ASN.1, X.509, and PEM implementation. If possible, avoid using | ||
them. These are left largely unmodified from upstream and are retained only for | ||
compatibilty with existing OpenSSL consumers. | ||
|
||
|
||
## 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. | ||
|
||
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 specific and may change over time. | ||
|
||
|
||
## 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. | ||
|
||
|
||
## 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`. Consumers are not required 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 `std:unique_ptr` with a custom deallocator | ||
to manage heap-allocated objects. | ||
|
||
|
||
### 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 implement a type which enters the zero state in | ||
its constructor and calls the cleanup function in the destructor. For example: | ||
|
||
class ScopedEVP_MD_CTX { | ||
public: | ||
ScopedEVP_MD_CTX() { | ||
EVP_MD_CTX_init(&ctx_); | ||
} | ||
|
||
~ScopedEVP_MD_CTX() { | ||
EVP_MD_CTX_cleanup(&ctx_); | ||
} | ||
|
||
EVP_MD_CTX *get() { return &ctx_; } | ||
|
||
ScopedEVP_MD_CTX(const EVP_MD_CTX &) = delete; | ||
EVP_MD_CTX &operator=(const EVP_MD_CTX &) = delete; | ||
|
||
private: | ||
EVP_MD_CTX ctx_; | ||
}; | ||
|
||
|
||
### 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. | ||
|
||
|
||
## 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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters