openssl/openssl
ASN.1
Active contributors: Tomas Mraz, Pauli, Dr. David von Oheimb, Matt Caswell, Richard Levitte
Purpose
OpenSSL's ASN.1 subsystem encodes and decodes ASN.1 types in DER (and the BER/CER variants where needed). It is the substrate beneath every X.509 certificate, every PKCS structure, every CMS / OCSP / CRL / CMP / TS message in the toolkit. The subsystem provides:
- A type system —
ASN1_INTEGER,ASN1_STRING,ASN1_OBJECT,ASN1_TYPE,ASN1_BIT_STRING,ASN1_SEQUENCE,ASN1_SET,ASN1_TIME, … - A template macro language so domain-specific types (e.g.
X509_NAME_ENTRY) can be declared once and get encode/decode/new/free for free. - A bytestream parser/serializer (
d2i_*andi2d_*family). - A streaming variant for big inputs (
SMIME_ASN1_*,BIO_*integration).
Directory layout
crypto/asn1/
├── asn1_lib.c -- the bytestream encoder/decoder primitives
├── asn1_par.c, asn1_gen.c -- "parse" / "generate" structured pretty-printers
├── tasn_dec.c -- template-driven decoder
├── tasn_enc.c -- template-driven encoder
├── tasn_fre.c -- template-driven free
├── tasn_new.c -- template-driven allocator
├── tasn_typ.c -- the IMPLEMENT_ASN1_TYPE machinery
├── tasn_utl.c -- template helpers
├── tasn_prn.c -- pretty-printer
├── x_*.c -- type-specific encoders (x_int.c, x_bignum.c, x_long.c, x_int64.c, x_pkey.c, x_pubkey.c, x_name.c, x_algor.c, x_attrib.c, x_info.c, x_spki.c, x_val.c, x_sig.c, x_x509.c via crypto/x509/, …)
├── a_*.c -- ASN1_TYPE primitive support (a_int.c, a_bitstr.c, a_object.c, a_strex.c, a_strnid.c, a_print.c, a_d2i_fp.c, a_dup.c, a_gentm.c, a_utctm.c, a_time.c, …)
├── ameth_lib.c -- legacy EVP_PKEY_ASN1_METHOD glue (deprecated)
├── bio_asn1.c, bio_ndef.c -- BIO filters for ASN.1 streaming
├── d2i_param.c, d2i_pr.c, d2i_pu.c, i2d_evp.c, p8_pkey.c, p5_pbev2.c, p5_pbe.c, …
├── nsseq.c -- Netscape sequence (legacy)
├── n_pkey.c -- old Netscape private-key wrapping (deprecated)
└── asn_*.c, charmap.h, evp_asn1.c, asn1_err.c, asn_pack.c, asn_mime.c, asn_moid.cHow templates work
The hallmark of OpenSSL's ASN.1 system is the macro DSL. A schema is declared once with ASN1_SEQUENCE/ASN1_CHOICE/ASN1_SET_OF macros, and a parallel IMPLEMENT_ASN1_FUNCTIONS macro emits encode/decode/new/free.
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)After macro expansion the file gets:
- A static
ASN1_ITEMdescribing the schema. i2d_X509_NAME_ENTRY,d2i_X509_NAME_ENTRY,X509_NAME_ENTRY_new,X509_NAME_ENTRY_freefunctions.
The runtime walks the ASN1_ITEM recursively. The encoder is tasn_enc.c; the decoder tasn_dec.c; allocator tasn_new.c; free tasn_fre.c. The ASN1_TEMPLATE substructure says how each member is encoded — simple, optional, default, sequence-of, set-of, choice, etc.
Schema declarations are spread across the codebase, near the data type they describe:
crypto/x509/x_*.c— X.509 schema (X509,X509_NAME,X509_EXTENSION, …).crypto/cms/cms_asn1.c,cms_pwri.c— CMS schema.crypto/cmp/cmp_asn.c— CMP schema.crypto/crmf/crmf_asn.c— CRMF schema.crypto/ocsp/ocsp_asn.c— OCSP schema.crypto/ts/ts_asn1.c— TS schema.crypto/pkcs7/pk7_asn1.c,crypto/pkcs12/p12_*.c— PKCS family schemas.crypto/ess/ess_asn1.c— ESS extensions.crypto/asn1/p8_pkey.c— PKCS#8 PrivateKeyInfo.
Reading and writing
| Caller intent | Function family |
|---|---|
| Parse a DER blob | d2i_<TYPE>(NULL, &bytes, len) |
| Parse from a BIO | d2i_<TYPE>_bio(bio, NULL) or ASN1_d2i_bio(...) |
| Encode to memory | i2d_<TYPE>(obj, NULL) (returns length); call again with a buffer |
| Encode to a BIO | i2d_<TYPE>_bio(bio, obj) |
| PEM read/write | PEM_read_bio_<TYPE> / PEM_write_bio_<TYPE> (in crypto/pem/) |
For new code, OSSL_DECODER and OSSL_ENCODER (in features/encoders-decoders) are the recommended provider-routed APIs. The i2d_*/d2i_* family is preserved for backward compatibility and is the only path for non-key types like X509, X509_CRL, etc.
Strings
ASN.1 string types map onto a single ASN1_STRING C type with a type discriminator (V_ASN1_PRINTABLESTRING, V_ASN1_UTF8STRING, V_ASN1_BMPSTRING, …). Conversion utilities live in a_strex.c, a_strnid.c, a_print.c. The ASN1_STRING_TABLE machinery knows the per-OID expected string types so e.g. commonName is encoded sensibly.
Time
ASN.1 has two main time types: UTCTime (YYMMDDHHMMSSZ) and GeneralizedTime (YYYYMMDDHHMMSS[.fff]Z). Per X.509 rules, certificates use UTCTime through 2049 and GeneralizedTime thereafter. OpenSSL's ASN1_TIME is a tagged union; conversion helpers in a_time.c, a_gentm.c, a_utctm.c. Common pitfall: comparing ASN1_TIME values requires ASN1_TIME_compare, not direct string comparison.
OIDs
Object Identifiers are stored in ASN1_OBJECT. The big static OID table is in crypto/objects/obj_dat.c (with header obj_dat.h, 7,200 lines, autogenerated from 6,700 lines). Look up by OID string with crypto/objects/objects.txt). The corresponding NID enum and macros are emitted into include/openssl/obj_mac.h (OBJ_txt2nid, by name with OBJ_sn2nid/OBJ_ln2nid.
To add a new OID, edit crypto/objects/objects.txt and run make update.
Error handling
ASN.1 errors push onto the OpenSSL error stack with ASN1err() / ERR_raise(ERR_LIB_ASN1, ...). Common reasons (ASN1_R_*):
ASN1_R_DATA_IS_WRONG,_BAD_TAG,_INVALID_OBJECT_ENCODING,_HEADER_TOO_LONG,_TOO_LONG.ASN1_R_NESTED_ASN1_STRING— defends against deeply nested attacker inputs.ASN1_R_UNKNOWN_FORMAT— the type discriminator didn't match a known string family.
The decoder enforces depth and length limits (ASN1_MAX_STRING_NEST, ASN1_MAX_CONSTRUCTED_NEST) by default to mitigate parsing-time DoS.
Streaming
For multi-megabyte CMS/PKCS#7 signed-data blobs, the _stream family in asn_mime.c and bio_asn1.c does indefinite-length BER encoding/decoding through a chain of BIOs. The classic example: signing a 1 GB tarball into a .p7s without buffering the entire input. The bio_ndef.c filter handles indefinite-length detection.
Integration points
- Every certificate parser in
crypto/x509/calls into the ASN.1 templates. - CMS/PKCS#7/OCSP/CMP/TS bring their own templates but share the same encoder/decoder runtime.
OSSL_ENCODER/OSSL_DECODERinproviders/implementations/encode_decode/ultimately produce the same DER bytes; they are a thin layer on top of the ASN.1 system for keys.
Entry points for modification
- New ASN.1 type: declare with
ASN1_SEQUENCE/IMPLEMENT_ASN1_FUNCTIONSin the appropriatecrypto/<area>/<area>_asn.c. Addi2d_*,d2i_*,*_new,*_freeto a public header (with the new symbols also added toutil/libcrypto.numviamake update). - New OID:
crypto/objects/objects.txt, thenmake update. - Tightening parser limits:
crypto/asn1/asn1_local.handtasn_dec.c.
Documentation
doc/man3/d2i_X509.pod— covers thed2i_*/i2d_*family.doc/man3/ASN1_*.pod— type accessors.doc/man7/x509.pod— overview of the X.509 stack that ASN.1 underpins.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.