pyasn1
pyasn1 is a pure-Python implementation of ASN.1 types and BER/DER/CER codecs (X.208). It lets developers define ASN.1 schemas as Python classes, then encode/decode wire-format bytes for network protocols and file formats such as X.509 certificates, PKCS structures, SNMP, and LDAP. Current version is 0.6.3 (released 2026), which adds a nesting depth limit to prevent stack overflow (CVE-2026-30922) and fixes an OverflowError from oversized BER length fields. The project is maintained by Christian Heimes and Simon Pichugin under the github.com/pyasn1 organisation following ownership transfer at v0.5.0; releases are irregular but active, with multiple releases per year addressing security CVEs and Python version support.
Warnings
- breaking decode() always returns a 2-tuple (asn1Object, remainingSubstrate). Ignoring the second element or trying to use the result directly as the decoded value is the single most common runtime bug.
- breaking Python 2 and Python < 3.8 support dropped in v0.6.0. The PyPI package now requires Python >=3.8.
- breaking SequenceOf/SetOf instances are no longer auto-initialised as value objects on instantiation (changed in 0.4.x). Code that tested `if mySeqOf:` or iterated an empty SequenceOf immediately after construction will silently get wrong results or raise errors.
- breaking The substrateFun callback signature changed in v0.5.0 from non-streaming (v0.4 style) to streaming. The v0.5.1 patch restores transparent compatibility for decoder.decode(), but custom substrateFun callbacks passed to low-level streaming decoders must use the new streaming signature.
- gotcha Without asn1Spec=, the decoder falls back to generic BER decoding and returns untyped Any objects. IMPLICIT tags cannot be decoded without a spec, so fields will silently be missing or mis-typed.
- gotcha Pre-built ASN.1 modules (X.509 RFC 5280, PKCS, SNMP MIBs, etc.) were moved to the separate pyasn1-modules package long ago and are no longer included in pyasn1 itself.
- gotcha pyasn1 types mimic Python built-ins (Integer ≈ int, OctetString ≈ bytes) but comparisons, hashing, and arithmetic can differ subtly. Passing a raw Python int where an Integer() instance is expected often works, but not always — especially inside Sequence field assignment with constraints.
Install
-
pip install pyasn1 -
pip install pyasn1 pyasn1-modules
Imports
- Integer, OctetString, ObjectIdentifier
from pyasn1.type.univ import Integer, OctetString, ObjectIdentifier
- Sequence, SequenceOf, Set, SetOf, Choice
from pyasn1.type.univ import Sequence, SequenceOf, Set, SetOf, Choice
- NamedType, OptionalNamedType, DefaultedNamedType, NamedTypes
from pyasn1.type.namedtype import NamedType, OptionalNamedType, DefaultedNamedType, NamedTypes
- Tag, tagClassContext, tagFormatSimple
from pyasn1.type.tag import Tag, tagClassContext, tagFormatSimple
- encode (DER)
from pyasn1.codec.der.encoder import encode
- decode (DER)
from pyasn1.codec.der.decoder import decode
- encode/decode (BER)
from pyasn1.codec.ber.encoder import encode from pyasn1.codec.ber.decoder import decode
- encode/decode (native Python dicts)
from pyasn1.codec.native.encoder import encode from pyasn1.codec.native.decoder import decode
- PyAsn1Error
from pyasn1.error import PyAsn1Error
Quickstart
from pyasn1.type.univ import Sequence, Integer
from pyasn1.type.namedtype import NamedType, OptionalNamedType, DefaultedNamedType, NamedTypes
from pyasn1.type.tag import Tag, tagClassContext, tagFormatSimple
from pyasn1.codec.der.encoder import encode
from pyasn1.codec.der.decoder import decode
from pyasn1.error import PyAsn1Error
# 1. Define ASN.1 schema as a Python class
class Record(Sequence):
componentType = NamedTypes(
NamedType('id', Integer()),
OptionalNamedType(
'room',
Integer().subtype(implicitTag=Tag(tagClassContext, tagFormatSimple, 0))
),
DefaultedNamedType(
'house',
Integer(0).subtype(implicitTag=Tag(tagClassContext, tagFormatSimple, 1))
),
)
# 2. Populate the schema object
record = Record()
record['id'] = 123
record['room'] = 321
# 3. DER-encode to bytes
substrate = encode(record)
print('DER bytes:', substrate.hex()) # e.g. 30070201 7b800201 41
# 4. Decode back — ALWAYS unpack the 2-tuple (value, remainder)
try:
received, remainder = decode(substrate, asn1Spec=Record())
except PyAsn1Error as exc:
raise SystemExit(f'Decode failed: {exc}')
assert remainder == b'', 'Unexpected trailing bytes'
assert received['id'] == record['id']
print('Round-trip OK:', received.prettyPrint())