Open-Source Wikis

/

OpenSSL

/

Subsystems

/

EVP

openssl/openssl

EVP

Active contributors: Pauli, Tomas Mraz, slontis, Matt Caswell, Neil Horman

Purpose

EVP is the algorithm-agnostic API in libcrypto. Almost every modern caller — applications, libssl, the openssl CLI — uses it. The pattern: pick an algorithm by name and property, get an opaque handle, drive it via init/update/final.

The EVP layer does not contain algorithm logic. It looks up implementations from providers (see core-and-libctx) and dispatches calls through OSSL_DISPATCH function pointers.

Directory layout

crypto/evp/
├── evp_lib.c          36 KB  -- common helpers (size accessors, name lookup, deprecation shims)
├── evp_fetch.c        22 KB  -- *_fetch implementations
├── evp_enc.c          47 KB  -- EVP_CIPHER_CTX driver
├── digest.c           31 KB  -- EVP_MD_CTX driver
├── pmeth_lib.c        43 KB  -- EVP_PKEY_CTX (asym ctx) driver
├── m_sigver.c         19 KB  -- EVP_DigestSign / EVP_DigestVerify
├── signature.c        42 KB  -- EVP_PKEY_sign / EVP_PKEY_verify (one-shot)
├── exchange.c         19 KB  -- EVP_PKEY_derive (key exchange)
├── kem.c              17 KB  -- EVP_PKEY_encapsulate / EVP_PKEY_decapsulate
├── asymcipher.c       17 KB  -- EVP_PKEY_encrypt / EVP_PKEY_decrypt
├── kdf_lib.c, kdf_meth.c     -- EVP_KDF
├── mac_lib.c, mac_meth.c     -- EVP_MAC
├── evp_rand.c         21 KB  -- EVP_RAND
├── keymgmt_lib.c, keymgmt_meth.c, p_lib.c (~68 KB)  -- EVP_KEYMGMT and EVP_PKEY
├── skeymgmt_meth.c           -- EVP_SKEYMGMT (opaque symmetric keys, 3.4+)
├── evp_pkey.c, evp_pkey_type.c, evp_key.c, p5_crpt*.c, p_legacy.c, m_null.c, m_sigver.c
├── ctrl_params_translate.c  111 KB  -- the legacy EVP_PKEY_CTX_ctrl ↔ OSSL_PARAM bridge
├── encode.c           -- base64 helpers (legacy EVP_ENCODE_CTX)
├── enc_b64_avx2.c, enc_b64_scalar.c   -- the SIMD/scalar base64 cores
├── e_*.c                     -- backward-compat wrappers around provider-backed ciphers (e_aes.c, e_chacha20_poly1305.c, …)
├── legacy_*.c                -- backward-compat wrappers for digests
├── ec_ctrl.c, ec_support.c, dh_ctrl.c, dh_support.c, dsa_ctrl.c   -- per-algo controller compatibility
├── pmeth_check.c, pmeth_gn.c -- helpers for params and gen
├── names.c            -- name iteration helpers
├── evp_cnf.c          -- the [evp_properties] config section
├── evp_local.h, evp_err.c
└── pbe_scrypt.c, p_dec.c, p_enc.c, p_open.c, p_seal.c, p_sign.c, p_verify.c   -- legacy convenience helpers

API surface

graph TB
    subgraph Symmetric
        MD[EVP_MD<br/>EVP_MD_CTX]
        CIP[EVP_CIPHER<br/>EVP_CIPHER_CTX]
        MAC[EVP_MAC<br/>EVP_MAC_CTX]
        KDF[EVP_KDF<br/>EVP_KDF_CTX]
        RAND[EVP_RAND<br/>EVP_RAND_CTX]
        SKEY[EVP_SKEY<br/>EVP_SKEYMGMT]
    end
    subgraph Asymmetric
        PKEY[EVP_PKEY<br/>EVP_PKEY_CTX]
        KMGMT[EVP_KEYMGMT]
        SIG[EVP_SIGNATURE]
        KEX[EVP_KEYEXCH]
        ASYM[EVP_ASYM_CIPHER]
        KEM[EVP_KEM]
    end
    subgraph Codecs
        ENC[EVP_ENCODER]
        DEC[EVP_DECODER]
    end

Each box has a matching *_fetch(libctx, name, props) constructor and a *_free destructor.

Lifecycle pattern (digest example)

EVP_MD *md = EVP_MD_fetch(libctx, "SHA2-256", NULL);
EVP_MD_CTX *ctx = EVP_MD_CTX_new();

EVP_DigestInit_ex2(ctx, md, NULL);
EVP_DigestUpdate(ctx, data, len);
unsigned int dl;
unsigned char digest[EVP_MAX_MD_SIZE];
EVP_DigestFinal_ex(ctx, digest, &dl);

EVP_MD_CTX_free(ctx);
EVP_MD_free(md);

The _ex2 form takes OSSL_PARAM arguments. The non-_ex2 forms exist for source compatibility.

EVP_PKEY: a tour

EVP_PKEY is the algorithm-agnostic key handle. It can hold an RSA key, an EC key, an Ed25519 key, an ML-KEM key, or any other public-key thing. Internally it stores either:

  • A legacy representation: RSA *, EC_KEY *, etc. (for backward compatibility with deprecated APIs).
  • A provider representation: an EVP_KEYMGMT * and a provider-side void *keydata.

Modern code should always end up in the provider representation. The conversion happens automatically when needed; see crypto/evp/p_lib.c (evp_pkey_downgrade, evp_pkey_upgrade_to_provider, EVP_PKEY_get0_provider, etc.).

The EVP_PKEY_CTX is a per-operation context (sign, verify, derive, encapsulate, encrypt, …). Operations on it use OSSL_PARAM for parameters; the legacy EVP_PKEY_CTX_ctrl API still works for old callers via the translator at crypto/evp/ctrl_params_translate.c. New code should use OSSL_PARAM directly.

Fetch caching

The first EVP_MD_fetch(libctx, "SHA2-256", "fips=yes") builds the handle from scratch. Subsequent fetches return the same cached EVP_MD (with reference count incremented). The cache lives in the per-operation OSSL_METHOD_STORE inside the libctx.

This means an EVP_MD * is safe to share between threads.

Implicit fetching (and why to avoid it)

Calling EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) with the EVP_sha256() macro returns an EVP_MD * from the default libctx using the default properties. This is convenient but bypasses the libctx isolation that the provider model is designed to give you. Prefer:

EVP_MD *md = EVP_MD_fetch(my_libctx, "SHA2-256", "fips=yes");
EVP_DigestInit_ex2(ctx, md, NULL);

Legacy compatibility shims

The e_*.c, legacy_*.c, m_null.c, *_ctrl.c, dh_support.c, ec_support.c, dsa_ctrl.c, p_*.c, pbe_scrypt.c, p5_crpt*.c files are mostly backward-compatibility helpers:

  • e_aes.c, e_chacha20_poly1305.c, etc. expose the old const EVP_CIPHER *EVP_aes_256_gcm(void) symbol; internally they fetch from the default provider.
  • legacy_md5.c, legacy_sha.c, etc. do the same for digests.
  • dh_ctrl.c, ec_ctrl.c, dsa_ctrl.c translate per-algorithm EVP_PKEY_CTX_ctrl calls into OSSL_PARAM for the new code path.
  • pmeth_*.c, evp_pkey.c wrap the deprecated EVP_PKEY_METHOD stuff.

These exist purely so that pre-3.0 source compiles unchanged. New code never touches them.

ctrl ↔ OSSL_PARAM translator

crypto/evp/ctrl_params_translate.c (111 KB!) is the bridge between the old controller-style API (EVP_PKEY_CTX_ctrl(ctx, type, op, p1, p2)) and the modern parameter API. It contains a per-algorithm switch translating each old controller code into the corresponding OSSL_PARAM. New algorithms should not need additions here.

Integration points

  • Above: every public API in libcrypto's domain libraries (X.509, CMS, OCSP, CMP, …) and the entire TLS/QUIC stack go through EVP for crypto.
  • Below: EVP fetches from OSSL_PROVIDERs. The boundary is the OSSL_DISPATCH function tables.

Entry points for modification

  • Adding a new operation kind (e.g. a brand-new public-key operation): requires extending core_dispatch.h, adding a fetch function in crypto/evp/evp_fetch.c, adding a method store, and writing implementations.
  • Adding a new function to an existing operation: extend the OSSL_FUNC_* ids and update every provider implementation.
  • Touching the EVP_PKEY implementation: crypto/evp/p_lib.c is the canonical home.

Documentation

  • doc/man7/evp.pod — concepts.
  • doc/man3/EVP_DigestInit.pod, EVP_EncryptInit.pod, EVP_PKEY_*.pod — API.
  • doc/man3/EVP_KEYMGMT.pod, EVP_SIGNATURE.pod, EVP_KEYEXCH.pod, EVP_KEM.pod, EVP_ASYM_CIPHER.pod — provider-side primitives.
  • doc/designs/evp_skey.md, doc/designs/evp-cipher-pipeline.md — recent design notes.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

EVP – OpenSSL wiki | Factory