openssl/openssl
Patterns and conventions
A handful of patterns recur throughout the codebase. Reading them now will save you time later.
Coding style
- C99, no compiler-specific extensions in portable code.
- Formatting: WebKit base via
clang-format, configured in.clang-format. The pre-commit hook (.pre-commit-config.yaml) enforces it. - Spelling:
codespellruns in CI (.codespellrc). - Tabs are not used; indentation is four spaces.
if (ptr == NULL)is preferred overif (!ptr). Likewiseif (n != 0)overif (n).- Public symbols live in
include/openssl/; internal ones ininclude/internal/orinclude/crypto/.
The official policy: https://openssl-library.org/policies/technical/coding-style/.
Initialization and cleanup
OpenSSL favors T *T_new(...) / T_free(T *) pairs. The free function must accept NULL. The *_new() returns NULL on failure and pushes onto the error stack:
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
/* error already on the stack */
goto err;
}
/* ...use ctx... */
EVP_MD_CTX_free(ctx);The classic int ret = 0; … err: cleanup; return ret; pattern is universal.
Refcounting
Many structures (X509, EVP_PKEY, SSL_SESSION, OSSL_PROVIDER, …) are reference-counted via CRYPTO_UP_REF / CRYPTO_DOWN_REF (see include/internal/refcount.h). The convention:
X509 *cert = X509_new();
X509_up_ref(cert); /* now refcount = 2 */
some_api_that_takes_ownership(cert);
X509_free(cert); /* releases your reference; the API still has its own */*_dup returns a deep copy when one is needed.
_ex variants and library contexts
Anything new that allocates or fetches should accept an explicit OSSL_LIB_CTX *libctx and a property query string. The convention is the trailing _ex:
EVP_PKEY_CTX *EVP_PKEY_CTX_new_from_name(OSSL_LIB_CTX *libctx,
const char *name,
const char *propquery);
RSA *RSA_new_ex(OSSL_LIB_CTX *libctx, const char *propq);NULL for libctx means the default context. For new code, plumb a libctx through your call graph rather than relying on the default. See subsystems/core-and-libctx.
OSSL_PARAM as the universal arg-pack
Wherever you would otherwise need 11 setters, OpenSSL passes an OSSL_PARAM array. Build with OSSL_PARAM_construct_* and a static array, or with OSSL_PARAM_BLD for dynamic shapes:
OSSL_PARAM params[] = {
OSSL_PARAM_construct_utf8_string("group", "secp256r1", 0),
OSSL_PARAM_construct_int("use_cofactor_dh", &one),
OSSL_PARAM_END
};
EVP_PKEY_keygen_init(ctx);
EVP_PKEY_CTX_set_params(ctx, params);Documented in include/openssl/params.h, include/openssl/param_build.h, crypto/params.c, crypto/param_build.c. See doc/man3/OSSL_PARAM*.pod.
Error reporting
ERR_raise(ERR_LIB_X509, X509_R_BAD_DATE);
ERR_raise_data(ERR_LIB_X509, X509_R_BAD_DATE, "value=%s", str);Each library has its own ERR_LIB_* constant and per-library reason codes. Add new ones to the relevant *err.h and let make update regenerate the dispatch tables. crypto/err/README.md walks through the workflow.
ASN.1 templating
Encoder/decoders for ASN.1 types are declared with macros in *.h.in and implemented with parallel macros in *.c:
ASN1_SEQUENCE(X509_NAME_ENTRY) = {
ASN1_SIMPLE(X509_NAME_ENTRY, object, ASN1_OBJECT),
ASN1_SIMPLE(X509_NAME_ENTRY, value, ASN1_PRINTABLE)
} ASN1_SEQUENCE_END(X509_NAME_ENTRY)
IMPLEMENT_ASN1_FUNCTIONS(X509_NAME_ENTRY)The implementations live in crypto/asn1/. Generic encode/decode helpers like i2d_X509_NAME_ENTRY, d2i_X509_NAME_ENTRY, X509_NAME_ENTRY_new, X509_NAME_ENTRY_free are emitted automatically. See doc/man3/ASN1_TYPE_get.pod, doc/man3/d2i_X509.pod.
For new code, prefer using OSSL_ENCODER_to_data() / OSSL_DECODER_from_data() (provider-backed); the legacy i2d_* and d2i_* family is kept for backward compat. See features/encoders-decoders.
BIO instead of FILE *
OpenSSL functions almost universally take a BIO * rather than a FILE *. The same call works for files, sockets, in-memory buffers, base64-encoding wrappers, and SSL connections:
BIO *out = BIO_new_fp(stdout, BIO_NOCLOSE);
PEM_write_bio_X509(out, cert);
BIO_free_all(out);crypto/bio/ defines the abstraction; BIO_method_st is the vtable; new BIO types implement bread, bwrite, ctrl, etc. See doc/man3/BIO_*.pod.
Build files (build.info)
Each directory has a build.info listing what to compile. Example from crypto/asn1/build.info:
LIBS=../../libcrypto
SOURCE[../../libcrypto]= a_strex.c a_int.c ...When you add a .c file, edit the matching build.info. The top-level Configure reads them all and produces a single Makefile. See reference/build-system.
Documentation policy
Every new public function gets a POD page in the appropriate doc/man[1357]/ directory. The policy:
- Section 1: command-line tools (
openssl-req.pod, etc.). - Section 3: API (one .pod per primary function plus
=itemblocks for related ones). - Section 5: configuration files (
config.pod,fips_config.pod). - Section 7: concepts and protocols (
crypto.pod,provider.pod,openssl-quic.pod,ossl-guide-*.pod).
Run make doc-nits to catch missing sections.
Symbol versioning
OpenSSL uses linker version maps. Public symbols listed in util/libcrypto.num and util/libssl.num get an ordinal that fixes the symbol's identity for ABI purposes; they are baked into linker scripts (libcrypto.ld, libssl.ld) by util/mkdef.pl. Adding a new public symbol means:
- Declare it in a public header.
- Implement it.
- Run
make updateto add it tolibcrypto.num/libssl.num. - Commit the generated changes.
OPENSSL_NO_* macros gate features in/out at compile time; they are emitted into include/openssl/opensslconf.h.in from Configurations/.
Pluggable record layer (since 3.2)
The TLS record layer is a pluggable interface, with the default TLS implementation in ssl/record/methods/. New record methods (e.g. the QUIC record layer) declare a OSSL_RECORD_METHOD and register it. See ssl/record/record.h and doc/designs/quic-design/.
Threading
OpenSSL is thread-safe per OSSL_LIB_CTX. The actual threading primitives are in crypto/threads_*.c (one per platform: pthread, Windows, none). Locks (CRYPTO_RWLOCK), once-init (CRYPTO_THREAD_run_once), and read-copy-update (RCU) are exposed via crypto.h. See subsystems/threading.
Memory
Use OPENSSL_malloc/OPENSSL_free (debug-instrumented, sanitizer-friendly) for general allocations and OPENSSL_secure_malloc/OPENSSL_secure_free for keys and other sensitive material. The secure heap is mlock'd and zeroed on free. See crypto/mem.c, crypto/mem_sec.c. See subsystems/memory.
What not to do
- Don't add new uses of
EVP_PKEY_CTX_ctrlorEVP_PKEY_CTX_ctrl_str. UseOSSL_PARAMinstead. (The legacy controls still work —crypto/evp/ctrl_params_translate.cis the bridge.) - Don't add new uses of
ENGINE_*. Write a provider. - Don't expose new internal-only symbols in
include/openssl/. Useinclude/internal/orinclude/crypto/. - Don't bypass the EVP layer to call low-level functions like
RSA_public_encrypt. They are deprecated. - Don't
printffrom the library. UseBIO_*or the trace facility.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.