Open-Source Wikis

/

OpenSSL

/

Features

/

Encoders and decoders

openssl/openssl

Encoders and decoders

Active contributors: Tomas Mraz, Pauli, Richard Levitte, Matt Caswell

Purpose

The OSSL_ENCODER and OSSL_DECODER framework converts cryptographic keys (EVP_PKEY) and key parameters between in-memory form and various encoded forms — DER, PEM, JSON-ish text, native-provider OSSL_PARAM representations, and structured wrappers like PKCS#8 and SubjectPublicKeyInfo. It is the 3.0-era replacement for the per-algorithm i2d_*/d2i_* zoo.

The framework is provider-routed: an encoder or decoder is published by a provider and selected by name + property. This is what makes it possible for the FIPS provider to expose a PEM↔EVP_PKEY decoder without statically linking the file-format code.

Why a new framework

Pre-3.0, every key type had its own family:

  • i2d_RSAPrivateKey, d2i_RSAPrivateKey (DER / PKCS#1 RSAPrivateKey)
  • i2d_PrivateKey, d2i_PrivateKey (DER / PKCS#8)
  • PEM_write_bio_RSAPrivateKey, PEM_read_bio_RSAPrivateKey, PEM_write_bio_PrivateKey, PEM_read_bio_PrivateKey
  • algorithm-by-algorithm helpers in crypto/rsa/, crypto/dsa/, crypto/ec/, crypto/dh/, …

Adding a new public-key algorithm meant reproducing this family for each format. The framework breaks the matrix into:

  • Algorithm-aware encoder/decoder implementations in providers, registered by the (input/output structure, type, output type) triple.
  • A driver in libcrypto (crypto/encode_decode/) that chains them together to find a path from caller-input to caller-output.

Directory layout

crypto/encode_decode/
├── encoder_lib.c      28 KB  -- the encoder driver (chain construction, output)
├── encoder_meth.c     21 KB  -- encoder method registration / fetch
├── encoder_pkey.c     13 KB  -- the EVP_PKEY-specific entry points
├── encoder_local.h, encoder_err.c
├── decoder_lib.c      40 KB  -- the decoder driver
├── decoder_meth.c     20 KB  -- decoder method registration / fetch
├── decoder_pkey.c     31 KB  -- the EVP_PKEY-specific entry points
└── decoder_err.c

providers/implementations/encode_decode/
├── encode_key2any.c, encode_key2text.c, encode_key2ms.c, encode_key2blob.c   -- algorithm-agnostic encoder front-ends
├── decode_*.c          -- per-format decoders (DER, PEM, "MSBLOB", PVK, "epki" PKCS#8)
├── encoder_local.h, decoder_local.h
└── ml_kem_codecs.c, ml_dsa_codecs.c, slh_dsa_codecs.c, lms_codecs.c   -- post-quantum algorithm specific bits

Conceptual model

A decoder is a function that turns one encoded form into another. The framework chains decoders together to bridge from input to a target type:

graph LR
    PEM[PEM bytes] --> D1[decoder<br/>PEM -> DER]
    D1 --> DER[DER bytes]
    DER --> D2[decoder<br/>DER -> PKCS8 PrivateKeyInfo]
    D2 --> P8[PKCS#8]
    P8 --> D3[decoder<br/>PKCS8 -> ML-KEM-768 EVP_PKEY]
    D3 --> KEY[EVP_PKEY]

Each decoder publishes:

  • An input structure name (e.g. PEM, DER, PKCS8, SubjectPublicKeyInfo, RSAPrivateKey).
  • An input type (data, key, signature, …).
  • An output keytype for the final stage (RSA, EC, Ed25519, ML-KEM-768, …).

Encoders are the mirror image, with output structure / output type / input keytype.

The driver runs a search to find any acyclic chain whose total cost is minimised and whose first stage matches the caller's input. It does this without requiring callers to enumerate the chain.

Caller surface

/* Decode: read a PEM file containing a private key into an EVP_PKEY. */
EVP_PKEY *pkey = NULL;
const char *pwd = "secret";
OSSL_DECODER_CTX *dctx = OSSL_DECODER_CTX_new_for_pkey(
    &pkey,
    "PEM",          /* input structure */
    NULL,           /* input type, NULL = auto */
    "RSA",          /* desired keytype, or NULL = any */
    EVP_PKEY_PRIVATE_KEY,
    libctx,
    NULL /* propq */ );

OSSL_DECODER_CTX_set_passphrase(dctx, (unsigned char *)pwd, strlen(pwd));
OSSL_DECODER_from_bio(dctx, bio);
OSSL_DECODER_CTX_free(dctx);
/* Encode: write an EVP_PKEY to a BIO as PEM PKCS#8 with a passphrase. */
OSSL_ENCODER_CTX *ectx = OSSL_ENCODER_CTX_new_for_pkey(
    pkey,
    EVP_PKEY_KEY_PARAMETERS | EVP_PKEY_PRIVATE_KEY,
    "PEM",
    "PrivateKeyInfo",
    NULL /* propq */ );

OSSL_ENCODER_CTX_set_passphrase(ectx, (unsigned char *)pwd, strlen(pwd));
OSSL_ENCODER_to_bio(ectx, bio);
OSSL_ENCODER_CTX_free(ectx);

PEM_read_bio_PrivateKey_ex and the surviving i2d_*/d2i_* for keys are now thin wrappers that build an OSSL_ENCODER_CTX / OSSL_DECODER_CTX and run it.

Where the implementations live

All built-in encoders/decoders are under providers/implementations/encode_decode/:

  • encode_key2any.c — encodes any provider-side keydata into a provider-neutral structure (PKCS#8, SubjectPublicKeyInfo, type-specific PKCS#1, etc.) in DER, then optionally wraps in PEM.
  • encode_key2text.c — produces human-readable text dumps.
  • encode_key2ms.c — Microsoft "MSBLOB" key format.
  • encode_key2blob.c — raw key bytes (e.g. for X25519 raw private keys).
  • decode_pem2der.c — strip the PEM armor.
  • decode_der2*.c — DER → PKCS#8 / DER → SubjectPublicKeyInfo / DER → algorithm-specific structure.
  • decode_msblob2*.c, decode_pvk2*.c, decode_epki2pki.c — special-purpose wrappers.
  • Per-algorithm decoders for ML-KEM, ML-DSA, SLH-DSA, LMS in ml_kem_codecs.c etc.

The default and base providers expose all of them; the FIPS provider exposes none (deliberately — FIPS doesn't include PEM/DER plumbing).

Provider authoring

A new encoder is an OSSL_DISPATCH[] exposing:

  • OSSL_FUNC_ENCODER_NEWCTX, _FREECTX, _GETTABLE_PARAMS, _GET_PARAMS, _SETTABLE_CTX_PARAMS, _SET_CTX_PARAMS, _DOES_SELECTION, _ENCODE, _IMPORT_OBJECT, _FREE_OBJECT.

Plus an OSSL_ALGORITHM row registering the algorithm with its name (e.g. RSA), its property string (output=pem,structure=PrivateKeyInfo), and a description.

Decoders mirror this with OSSL_FUNC_DECODER_* and OSSL_FUNC_DECODER_DECODE.

Property keys

The framework uses these property keys (defined in core_names.h):

  • output=<name> — for encoders: which output format the encoder produces (e.g. pem, der, text, msblob, pvk, blob).
  • input=<name> — for decoders: which input format the decoder accepts.
  • structure=<name> — the structural wrapper (e.g. PrivateKeyInfo for PKCS#8, SubjectPublicKeyInfo for SPKI, EncryptedPrivateKeyInfo for encrypted PKCS#8, PrivateKeyInfo, pkcs1, RSAPublicKey, OAEP, epki, type-specific).

Combined with provider=… and fips=yes, this lets callers select e.g. "PEM PKCS#8 from the FIPS provider via the base provider's encoder".

EVP_PKEY interaction

When OSSL_DECODER produces a key it always stores it in an EVP_PKEY in the provider representation (i.e. an EVP_KEYMGMT plus a void *keydata). Likewise OSSL_ENCODER reads from the provider representation. Legacy EVP_PKEY payloads (RSA struct etc.) are upgraded automatically before encoding and downgraded after decoding only if the legacy API path is used.

Integration points

  • PEM_*_PrivateKey, PEM_*_PUBKEY, i2d_PUBKEY, d2i_PrivateKey — all reroute via the encoder/decoder framework.
  • OSSL_STORE uses the decoder framework when reading a file: URI: PEM blocks are detected, fed through decoders, and become OSSL_STORE_INFO items.
  • TLS (SSL_use_certificate_file, SSL_use_PrivateKey_file) uses these to load configuration from disk.
  • CMP / CMS / X.509 consume keys through EVP_PKEY regardless of how it was decoded.

Entry points for modification

  • New algorithm decoder: write a per-keytype decoder under providers/implementations/encode_decode/ (see ml_kem_codecs.c for a recent example), register in the default and base providers' tables.
  • New format (e.g. CBOR for keys): write a decode_<from>2<to>.c and an encode_<from>2<to>.c, register them. The driver finds them automatically.
  • New structure: extend core_names.h with the structure-name macro, register the new encoders/decoders for it.

Documentation

  • doc/man3/OSSL_DECODER.pod, OSSL_DECODER_from_bio.pod, OSSL_DECODER_CTX_new_for_pkey.pod, OSSL_DECODER_CTX_set_passphrase_cb.pod.
  • doc/man3/OSSL_ENCODER.pod, OSSL_ENCODER_to_bio.pod, OSSL_ENCODER_CTX_new_for_pkey.pod.
  • doc/man7/provider-encoder.pod, provider-decoder.pod — provider-side ABI.
  • doc/designs/passing-algorithmidentifier-parameters.md — design notes.

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

Encoders and decoders – OpenSSL wiki | Factory