asn1crypto Documentation

repository·master·Indexed 18 days ago

https://github.com/wbond/asn1crypto

A fast, pure Python library for parsing and serializing ASN.1 structures. It features lazy loading and delayed parsing for high performance with large cryptographic structures. The library provides pre-defined modules for standards including X.509, CRL, CSR, OCSP, PKCS#12, PKCS#8, CMS, and TSP, as well as tools for PEM encoding and decoding.

Tokens
7K
Snippets
21
Records
28
Agent score
62%

What's inside asn1crypto

  1. Overview of Universal Types in asn1crypto

    master

    The asn1crypto.core module provides universal type classes that implement BER/DER decoding and DER encoding. These classes allow you to parse, represent, and serialize all standard ASN.1 universal types.

    Common mappings between ASN.1 classes and Python native types include:

    • Boolean -> bool
    • Integer -> int
    • OctetString -> bytes
    • Null -> None
    • ObjectIdentifier -> str (dotted integer format)
    • Sequence -> OrderedDict
    • SequenceOf -> list
    • Set -> OrderedDict
    • SetOf -> list
    • UTCTime/GeneralizedTime -> datetime.datetime
  2. Use SetOf for unordered collections

    master

    The SetOf class is used for ASN.1 SET types, which are unordered collections. It is functionally an exact duplicate of SequenceOf, but whereas SequenceOf is explicitly ordered, SetOf may be in any order. In Python terms, this is analogous to the difference between a list and a set.

    from asn1crypto.core import SetOf, Integer
    
    class Integers(SetOf):
        _child_spec = Integer
  3. Handle dynamic specifications via OID in Sequences

    master

    A common ASN.1 pattern uses an ObjectIdentifier to determine how to interpret a subsequent field (often an Any or OctetString). asn1crypto supports this via two properties on the Sequence class:

    1. _oid_pair: A tuple (oid_field_name, value_field_name). The first element is the name of the field containing the OID, and the second is the name of the field whose type depends on that OID.
    2. _oid_specs: A dict mapping ObjectIdentifier values (keys) to type classes (values). When the value in the oid_field matches a key in _oid_specs, that type class is used to parse the value_field.

    Note: If the value field is an OctetString or OctetBitString that needs sub-parsing, use ParsableOctetString or ParsableOctetBitString instead of Any.

    from asn1crypto.core import Sequence, ObjectIdentifier, Any, OctetString, Integer
    
    class MyId(ObjectIdentifier):
        _map = {
            '1.2.3.4': 'initialization_vector',
            '1.2.3.5': 'iterations',
        }
    
    class MySequence(Sequence):
        _fields = [
            ('type', MyId),
            ('value', Any),
        ]
    
        _oid_pair = ('type', 'value')
        _oid_specs = {
            'initialization_vector': OctetString,
            'iterations': Integer,
        }
  4. Use Explicit and Implicit Tagging in ASN.1 structures

    master

    When defining Sequence, Set, or Choice types, you may need to disambiguate fields that share the same universal type. asn1crypto provides two tagging mechanisms:

    • Implicit Tagging: Changes the tag number of a type to a different value without wrapping it.
    • Explicit Tagging: Wraps the existing type in another tag with the specified tag number.

    Important Rule: A field that is a Choice type must always be explicitly tagged. Using implicit tagging on a Choice alternative would modify the tag of the chosen alternative, breaking the Choice mechanism.

    To apply tagging, pass an optional third element to the field or alternative tuple in the class definition. The tagging value can be an integer tag number or a 2-element tuple containing a string class name and an integer tag.

    from asn1crypto.core import Sequence, Choice, IA5String, UTCTime, ObjectIdentifier
    
    class Person(Choice):
        _alternatives = [
            ('name', IA5String),
            ('email', IA5String, {'implicit': 0}),
        ]
    
    class Record(Sequence):
        _fields = [
            ('id', ObjectIdentifier),
            ('created', UTCTime),
            ('creator', Person, {'explicit': 0, 'optional': True}),
        ]
  5. How asn1crypto achieves high performance

    master

    Unlike other Python ASN.1 libraries (like pyasn1), asn1crypto is designed for speed, especially when dealing with large structures like CRLs. It uses several techniques to minimize computation:

    • Delayed parsing: Byte string values are only parsed when accessed.
    • Lazy loading: Child fields are loaded only when needed.
    • Persistence: The original ASN.1 encoded data is kept until a value is changed.
    • High-level stdlib utilization: Uses Python's standard library efficiently to handle data types.
  6. Run the asn1crypto test suite

    master

    Depending on how you have the package, use one of the following methods to run tests:

    From a Git repository:

    python run.py tests

    To run specific tests using a regular expression (e.g., only OCSP tests):

    python run.py tests ocsp

    From a PyPi source distribution (.tar.gz):

    python setup.py test

    From an installed package: Install the test package first, then run the module:

    pip install asn1crypto_tests
    python -m asn1crypto_tests
  7. Basic usage of universal types: load(), dump(), and native

    master

    All universal types implement a standard set of methods for lifecycle management:

    • .load(data): A class method that accepts a byte string of DER or BER encoded data and returns an instance of the class.
    • .dump(force=False): Returns the serialization of the object into DER encoding. Use force=True if the input was BER but you require strict DER output.
    • .native: A property that returns the data converted into its corresponding Python native type.
    • .copy(): Creates a deep copy of the object, allowing you to modify child fields without affecting the original.
    • .debug(): Prints a tree structure containing header bytes, tags, content bytes, and native values to assist in debugging.
    from asn1crypto.core import Sequence
    
    # Loading and serializing
    parsed = Sequence.load(der_byte_string)
    serialized = parsed.dump(force=True)
    
    # Accessing native Python values
    print(parsed.native)
    
    # Creating a deep copy for modification
    seq2 = parsed.copy()
    seq2[0] = 10
  8. Work with ObjectIdentifier (OID) mapping

    master

    The ObjectIdentifier class represents ASN.1 OIDs. Accessing .native returns a dotted-integer unicode string (e.g., '1.2.3'). You can use the _map property to map these dotted strings to user-friendly descriptions.

    Key attributes and methods:

    • .dotted: Always returns the dotted-integer unicode string.
    • .map(dotted_string): Converts a dotted-integer string to the mapped name.
    • .unmap(name): Converts a mapped name back to the dotted-integer string.
    from asn1crypto.core import ObjectIdentifier
    
    class MyType(ObjectIdentifier):
        _map = {
            '1.8.2.1.23': 'value_name',
            '1.8.2.1.24': 'other_value',
        }
    
    # Will print: "value_name"
    print(MyType('1.8.2.1.23').native)
    
    # Will print: "1.8.2.1.23"
    print(MyType('1.8.2.1.23').dotted)
    
    # Will print: "1.8.2.1.25"
    print(MyType('1.8.2.1.25').native)
    
    # Will print "value_name"
    print(MyType.map('1.8.2.1.23'))
    
    # Will print "1.8.2.1.23"
    print(MyType.unmap('value_name'))
  9. Use Enumerated for restricted integer sets

    master

    The Enumerated class is similar to Integer with a _map, but it is more restrictive: only values defined in the _map property are considered valid. Attempting to access or initialize an Enumerated type with a value not in the map will raise a ValueError.

    from asn1crypto.core import Enumerated
    
    class Version(Enumerated):
        _map = {
            1: 'v1',
            2: 'v2',
        }
    
    # Will print: "v1"
    print(Version(1).native)
    
    # Will raise a ValueError exception
    print(Version(4).native)