Library reference

Python ASN.1 DER/BER codec with abstract structures

This library allows you to marshal various structures in ASN.1 DER format, unmarshal them in BER/CER/DER ones.

>>> i = Integer(123)
>>> raw = i.encode()
>>> Integer().decode(raw) == i
True

There are primitive types, holding single values (pyderasn.BitString, pyderasn.Boolean, pyderasn.Enumerated, pyderasn.GeneralizedTime, pyderasn.Integer, pyderasn.Null, pyderasn.ObjectIdentifier, pyderasn.OctetString, pyderasn.UTCTime, various strings (pyderasn.BMPString, pyderasn.GeneralString, pyderasn.GraphicString, pyderasn.IA5String, pyderasn.ISO646String, pyderasn.NumericString, pyderasn.PrintableString, pyderasn.T61String, pyderasn.TeletexString, pyderasn.UniversalString, pyderasn.UTF8String, pyderasn.VideotexString, pyderasn.VisibleString)), constructed types, holding multiple primitive types (pyderasn.Sequence, pyderasn.SequenceOf, pyderasn.Set, pyderasn.SetOf), and special types like pyderasn.Any and pyderasn.Choice.

Common for most types

Tags

Most types in ASN.1 has specific tag for them. Obj.tag_default is the default tag used during coding process. You can override it with either IMPLICIT (using impl keyword argument), or EXPLICIT one (using expl keyword argument). Both arguments take raw binary string, containing that tag. You can not set implicit and explicit tags simultaneously.

There are pyderasn.tag_ctxp() and pyderasn.tag_ctxc() functions, allowing you to easily create CONTEXT PRIMITIVE/CONSTRUCTED tags, by specifying only the required tag number. Pay attention that explicit tags always have constructed tag (tag_ctxc), but implicit tags for primitive types are primitive (tag_ctxp).

>>> Integer(impl=tag_ctxp(1))
[1] INTEGER
>>> Integer(expl=tag_ctxc(2))
[2] EXPLICIT INTEGER

Implicit tag is not explicitly shown.

Two objects of the same type, but with different implicit/explicit tags are not equal.

You can get object’s effective tag (either default or implicited) through tag property. You can decode it using pyderasn.tag_decode() function:

>>> tag_decode(tag_ctxc(123))
(128, 32, 123)
>>> klass, form, num = tag_decode(tag_ctxc(123))
>>> klass == TagClassContext
True
>>> form == TagFormConstructed
True

To determine if object has explicit tag, use expled boolean property and expl_tag property, returning explicit tag’s value.

Default/optional

Many objects in sequences could be OPTIONAL and could have DEFAULT value. You can specify that object’s property using corresponding keyword arguments.

>>> Integer(optional=True, default=123)
INTEGER 123 OPTIONAL DEFAULT

Those specifications do not play any role in primitive value encoding, but are taken into account when dealing with sequences holding them. For example TBSCertificate sequence holds defaulted, explicitly tagged version field:

class Version(Integer):
    schema = (
        ("v1", 0),
        ("v2", 1),
        ("v3", 2),
    )
class TBSCertificate(Sequence):
    schema = (
        ("version", Version(expl=tag_ctxc(0), default="v1")),
    [...]

When default argument is used and value is not specified, then it equals to default one.

Size constraints

Some objects give ability to set value size constraints. This is either possible integer value, or allowed length of various strings and sequences. Constraints are set in the following way:

class X(...):
    bounds = (MIN, MAX)

And values satisfaction is checked as: MIN <= X <= MAX.

For simplicity you can also set bounds the following way:

bounded_x = X(bounds=(MIN, MAX))

If bounds are not satisfied, then pyderasn.BoundsError is raised.

Common methods

All objects have ready boolean property, that tells if object is ready to be encoded. If that kind of action is performed on unready object, then pyderasn.ObjNotReady exception will be raised.

All objects have copy() method, that returns their copy, that can be safely mutated.

Decoding

Decoding is performed using decode() method. offset optional argument could be used to set initial object’s offset in the binary data, for convenience. It returns decoded object and remaining unmarshalled data (tail). Internally all work is done on memoryview(data), and you can leave returning tail as a memoryview, by specifying leavemm=True argument.

When object is decoded, decoded property is true and you can safely use following properties:

  • offset – position including initial offset where object’s tag starts
  • tlen – length of object’s tag
  • llen – length of object’s length value
  • vlen – length of object’s value
  • tlvlen – length of the whole object

Pay attention that those values do not include anything related to explicit tag. If you want to know information about it, then use:

  • expled – to know if explicit tag is set
  • expl_offset (it is lesser than offset)
  • expl_tlen,
  • expl_llen
  • expl_vlen (that actually equals to ordinary tlvlen)
  • fulloffset – it equals to expl_offset if explicit tag is set, offset otherwise
  • fulllen – it equals to expl_len if explicit tag is set, tlvlen otherwise

When error occurs, pyderasn.DecodeError is raised.

Context

You can specify so called context keyword argument during decode() invocation. It is dictionary containing various options governing decoding process.

Currently available context options:

Pretty printing

All objects have pps() method, that is a generator of pyderasn.PP namedtuple, holding various raw information about the object. If pps is called on sequences, then all underlying PP will be yielded.

You can use pyderasn.pp_console_row() function, converting those PP to human readable string. Actually exactly it is used for all object repr. But it is easy to write custom formatters.

>>> from pyderasn import pprint
>>> encoded = Integer(-12345).encode()
>>> obj, tail = Integer().decode(encoded)
>>> print(pprint(obj))
    0   [1,1,   2] INTEGER -12345

DEFINED BY

ASN.1 structures often have ANY and OCTET STRING fields, that are DEFINED BY some previously met ObjectIdentifier. This library provides ability to specify mapping between some OID and field that must be decoded with specific specification.

defines kwarg

pyderasn.ObjectIdentifier field inside pyderasn.Sequence can hold mapping between OIDs and necessary for decoding structures. For example, CMS (RFC 5652) container:

class ContentInfo(Sequence):
    schema = (
        ("contentType", ContentType(defines=((("content",), {
            id_digestedData: DigestedData(),
            id_signedData: SignedData(),
        }),))),
        ("content", Any(expl=tag_ctxc(0))),
    )

contentType field tells that it defines that content must be decoded with SignedData specification, if contentType equals to id-signedData. The same applies to DigestedData. If contentType contains unknown OID, then no automatic decoding is done.

You can specify multiple fields, that will be autodecoded – that is why defines kwarg is a sequence. You can specify defined field relatively or absolutely to current decode path. For example defines for AlgorithmIdentifier of X.509’s tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm:

(
    (("parameters",), {
        id_ecPublicKey: ECParameters(),
        id_GostR3410_2001: GostR34102001PublicKeyParameters(),
    }),
    (("..", "subjectPublicKey"), {
        id_rsaEncryption: RSAPublicKey(),
        id_GostR3410_2001: OctetString(),
    }),
),

tells that if certificate’s SPKI algorithm is GOST R 34.10-2001, then autodecode its parameters inside SPKI’s algorithm and its public key itself.

Following types can be automatically decoded (DEFINED BY):

When any of those fields is automatically decoded, then .defined attribute contains (OID, value) tuple. OID tells by which OID it was defined, value contains corresponding decoded value. For example above, content_info["content"].defined == (id_signedData, signed_data).

defines_by_path context option

Sometimes you either can not or do not want to explicitly set defines in the scheme. You can dynamically apply those definitions when calling .decode() method.

Specify defines_by_path key in the decode context. Its value must be sequence of following tuples:

(decode_path, defines)

where decode_path is a tuple holding so-called decode path to the exact pyderasn.ObjectIdentifier field you want to apply defines, holding exactly the same value as accepted in its keyword argument.

For example, again for CMS, you want to automatically decode SignedData and CMC’s (RFC 5272) PKIData and PKIResponse structures it may hold. Also, automatically decode controlSequence of PKIResponse:

content_info, tail = ContentInfo().decode(data, defines_by_path=(
    (
        ("contentType",),
        ((("content",), {id_signedData: SignedData()}),),
    ),
    (
        (
            "content",
            DecodePathDefBy(id_signedData),
            "encapContentInfo",
            "eContentType",
        ),
        ((("eContent",), {
            id_cct_PKIData: PKIData(),
            id_cct_PKIResponse: PKIResponse(),
        })),
    ),
    (
        (
            "content",
            DecodePathDefBy(id_signedData),
            "encapContentInfo",
            "eContent",
            DecodePathDefBy(id_cct_PKIResponse),
            "controlSequence",
            any,
            "attrType",
        ),
        ((("attrValues",), {
            id_cmc_recipientNonce: RecipientNonce(),
            id_cmc_senderNonce: SenderNonce(),
            id_cmc_statusInfoV2: CMCStatusInfoV2(),
            id_cmc_transactionId: TransactionId(),
        })),
    ),
))

Pay attention for pyderasn.DecodePathDefBy and any. First function is useful for path construction when some automatic decoding is already done. any means literally any value it meet – useful for SEQUENCE/SET OF-s.

BER encoding

By default PyDERASN accepts only DER encoded data. It always encodes to DER. But you can optionally enable BER decoding with setting bered context argument to True. Indefinite lengths and constructed primitive types should be parsed successfully.

  • If object is encoded in BER form (not the DER one), then ber_encoded attribute is set to True. Only BOOLEAN, BIT STRING, OCTET STRING, SEQUENCE, SET, SET OF can contain it.
  • If object has an indefinite length encoding, then its lenindef attribute is set to True. Only BIT STRING, OCTET STRING, SEQUENCE, SET, SEQUENCE OF, SET OF, ANY can contain it.
  • If object has an indefinite length encoded explicit tag, then expl_lenindef is set to True.
  • If object has either any of BER-related encoding (explicit tag indefinite length, object’s indefinite length, BER-encoding) or any underlying component has that kind of encoding, then bered attribute is set to True. For example SignedData CMS can have ContentInfo:content:signerInfos:* bered value set to True, but ContentInfo:content:signerInfos:*:signedAttrs won’t.

EOC (end-of-contents) token’s length is taken in advance in object’s value length.

Allow explicit tag out-of-bound

Invalid BER encoding could contain EXPLICIT tag containing more than one value, more than one object. If you set allow_expl_oob context option to True, then no error will be raised and that invalid encoding will be silently further processed. But pay attention that offsets and lengths will be invalid in that case.

Warning

This option should be used only for skipping some decode errors, just to see the decoded structure somehow.

Primitive types

Boolean

class pyderasn.Boolean(value=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

BOOLEAN boolean type

>>> b = Boolean(True)
BOOLEAN True
>>> b == Boolean(True)
True
>>> bool(b)
True
__init__(value=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))
Parameters:
  • value – set the value. Either boolean type, or pyderasn.Boolean object
  • impl (bytes) – override default tag with IMPLICIT one
  • expl (bytes) – override default tag with EXPLICIT one
  • default – set default value. Type same as in value
  • optional (bool) – is object OPTIONAL in sequence

Integer

class pyderasn.Integer(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _specs=None, _decoded=(0, 0, 0))

INTEGER integer type

>>> b = Integer(-123)
INTEGER -123
>>> b == Integer(-123)
True
>>> int(b)
-123
>>> Integer(2, bounds=(1, 3))
INTEGER 2
>>> Integer(5, bounds=(1, 3))
Traceback (most recent call last):
pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
class Version(Integer):
    schema = (
        ("v1", 0),
        ("v2", 1),
        ("v3", 2),
    )
>>> v = Version("v1")
Version INTEGER v1
>>> int(v)
0
>>> v.named
'v1'
>>> v.specs
{'v3': 2, 'v1': 0, 'v2': 1}
__init__(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _specs=None, _decoded=(0, 0, 0))
Parameters:
  • value – set the value. Either integer type, named value (if schema is specified in the class), or pyderasn.Integer object
  • bounds – set (MIN, MAX) value constraint. (-inf, +inf) by default
  • impl (bytes) – override default tag with IMPLICIT one
  • expl (bytes) – override default tag with EXPLICIT one
  • default – set default value. Type same as in value
  • optional (bool) – is object OPTIONAL in sequence

BitString

class pyderasn.BitString(value=None, impl=None, expl=None, default=None, optional=False, _specs=None, _decoded=(0, 0, 0))

BIT STRING bit string type

>>> BitString(b"hello world")
BIT STRING 88 bits 68656c6c6f20776f726c64
>>> bytes(b)
b'hello world'
>>> b == b"hello world"
True
>>> b.bit_len
88
>>> BitString("'0A3B5F291CD'H")
BIT STRING 44 bits 0a3b5f291cd0
>>> b = BitString("'010110000000'B")
BIT STRING 12 bits 5800
>>> b.bit_len
12
>>> b[0], b[1], b[2], b[3]
(False, True, False, True)
>>> b[1000]
False
>>> [v for v in b]
[False, True, False, True, True, False, False, False, False, False, False, False]
class KeyUsage(BitString):
    schema = (
        ("digitalSignature", 0),
        ("nonRepudiation", 1),
        ("keyEncipherment", 2),
    )
>>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
>>> b.named
['nonRepudiation', 'keyEncipherment']
>>> b.specs
{'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}

Note

Pay attention that BIT STRING can be encoded both in primitive and constructed forms. Decoder always checks constructed form tag additionally to specified primitive one. If BER decoding is not enabled, then decoder will fail, because of DER restrictions.

__init__(value=None, impl=None, expl=None, default=None, optional=False, _specs=None, _decoded=(0, 0, 0))
Parameters:
  • value – set the value. Either binary type, tuple of named values (if schema is specified in the class), string in 'XXX...'B form, or pyderasn.BitString object
  • impl (bytes) – override default tag with IMPLICIT one
  • expl (bytes) – override default tag with EXPLICIT one
  • default – set default value. Type same as in value
  • optional (bool) – is object OPTIONAL in sequence

OctetString

class pyderasn.OctetString(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

OCTET STRING binary string type

>>> s = OctetString(b"hello world")
OCTET STRING 11 bytes 68656c6c6f20776f726c64
>>> s == OctetString(b"hello world")
True
>>> bytes(s)
b'hello world'
>>> OctetString(b"hello", bounds=(4, 4))
Traceback (most recent call last):
pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
>>> OctetString(b"hell", bounds=(4, 4))
OCTET STRING 4 bytes 68656c6c

Note

Pay attention that OCTET STRING can be encoded both in primitive and constructed forms. Decoder always checks constructed form tag additionally to specified primitive one. If BER decoding is not enabled, then decoder will fail, because of DER restrictions.

__init__(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))
Parameters:
  • value – set the value. Either binary type, or pyderasn.OctetString object
  • bounds – set (MIN, MAX) value size constraint. (-inf, +inf) by default
  • impl (bytes) – override default tag with IMPLICIT one
  • expl (bytes) – override default tag with EXPLICIT one
  • default – set default value. Type same as in value
  • optional (bool) – is object OPTIONAL in sequence

Null

class pyderasn.Null(value=None, impl=None, expl=None, optional=False, _decoded=(0, 0, 0))

NULL null object

>>> n = Null()
NULL
>>> n.ready
True
__init__(value=None, impl=None, expl=None, optional=False, _decoded=(0, 0, 0))
Parameters:
  • impl (bytes) – override default tag with IMPLICIT one
  • expl (bytes) – override default tag with EXPLICIT one
  • optional (bool) – is object OPTIONAL in sequence

ObjectIdentifier

class pyderasn.ObjectIdentifier(value=None, defines=(), impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

OBJECT IDENTIFIER OID type

>>> oid = ObjectIdentifier((1, 2, 3))
OBJECT IDENTIFIER 1.2.3
>>> oid == ObjectIdentifier("1.2.3")
True
>>> tuple(oid)
(1, 2, 3)
>>> str(oid)
'1.2.3'
>>> oid + (4, 5) + ObjectIdentifier("1.7")
OBJECT IDENTIFIER 1.2.3.4.5.1.7
>>> str(ObjectIdentifier((3, 1)))
Traceback (most recent call last):
pyderasn.InvalidOID: unacceptable first arc value
__init__(value=None, defines=(), impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))
Parameters:
  • value – set the value. Either tuples of integers, string of “.”-concatenated integers, or pyderasn.ObjectIdentifier object
  • defines – sequence of tuples. Each tuple has two elements. First one is relative to current one decode path, aiming to the field defined by that OID. Read about relative path in pyderasn.abs_decode_path(). Second tuple element is {OID: pyderasn.Obj()} dictionary, mapping between current OID value and structure applied to defined field. Read about DEFINED BY
  • impl (bytes) – override default tag with IMPLICIT one
  • expl (bytes) – override default tag with EXPLICIT one
  • default – set default value. Type same as in value
  • optional (bool) – is object OPTIONAL in sequence

Enumerated

class pyderasn.Enumerated(value=None, impl=None, expl=None, default=None, optional=False, _specs=None, _decoded=(0, 0, 0), bounds=None)

ENUMERATED integer type

This type is identical to pyderasn.Integer, but requires schema to be specified and does not accept values missing from it.

CommonString

class pyderasn.CommonString(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

Common class for all strings

Everything resembles pyderasn.OctetString, except ability to deal with unicode text strings.

>>> hexenc("привет мир".encode("utf-8"))
'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
>>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
True
>>> s = UTF8String("привет мир")
UTF8String UTF8String привет мир
>>> str(s)
'привет мир'
>>> hexenc(bytes(s))
'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
>>> PrintableString("привет мир")
Traceback (most recent call last):
pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
>>> BMPString("ада", bounds=(2, 2))
Traceback (most recent call last):
pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
>>> s = BMPString("ад", bounds=(2, 2))
>>> s.encoding
'utf-16-be'
>>> hexenc(bytes(s))
'04300434'
Class Text Encoding
pyderasn.UTF8String utf-8
pyderasn.NumericString ascii
pyderasn.PrintableString ascii
pyderasn.TeletexString ascii
pyderasn.T61String ascii
pyderasn.VideotexString iso-8859-1
pyderasn.IA5String ascii
pyderasn.GraphicString iso-8859-1
pyderasn.VisibleString ascii
pyderasn.ISO646String ascii
pyderasn.GeneralString iso-8859-1
pyderasn.UniversalString utf-32-be
pyderasn.BMPString utf-16-be

NumericString

class pyderasn.NumericString(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

Numeric string

Its value is properly sanitized: only ASCII digits with spaces can be stored.

>>> NumericString().allowable_chars
set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])

UTCTime

class pyderasn.UTCTime(value=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), bounds=None)

UTCTime datetime type

>>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
UTCTime UTCTime 2017-09-30T22:07:50
>>> str(t)
'170930220750Z'
>>> bytes(t)
b'170930220750Z'
>>> t.todatetime()
datetime.datetime(2017, 9, 30, 22, 7, 50)
>>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
datetime.datetime(1957, 9, 30, 22, 7, 50)
__init__(value=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), bounds=None)
Parameters:
  • value – set the value. Either datetime type, or pyderasn.UTCTime object
  • impl (bytes) – override default tag with IMPLICIT one
  • expl (bytes) – override default tag with EXPLICIT one
  • default – set default value. Type same as in value
  • optional (bool) – is object OPTIONAL in sequence
todatetime()

Convert to datetime

Returns:datetime

Pay attention that UTCTime can not hold full year, so all years having < 50 years are treated as 20xx, 19xx otherwise, according to X.509 recomendation.

GeneralizedTime

class pyderasn.GeneralizedTime(value=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), bounds=None)

GeneralizedTime datetime type

This type is similar to pyderasn.UTCTime.

>>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
>>> str(t)
'20170930220750.000123Z'
>>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
GeneralizedTime GeneralizedTime 2057-09-30T22:07:50

Special types

Choice

class pyderasn.Choice(value=None, schema=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

CHOICE special type

class GeneralName(Choice):
    schema = (
        ("rfc822Name", IA5String(impl=tag_ctxp(1))),
        ("dNSName", IA5String(impl=tag_ctxp(2))),
    )
>>> gn = GeneralName()
GeneralName CHOICE
>>> gn["rfc822Name"] = IA5String("foo@bar.baz")
GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
>>> gn["dNSName"] = IA5String("bar.baz")
GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
>>> gn["rfc822Name"]
None
>>> gn["dNSName"]
[2] IA5String IA5 bar.baz
>>> gn.choice
'dNSName'
>>> gn.value == gn["dNSName"]
True
>>> gn.specs
OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
>>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
__init__(value=None, schema=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))
Parameters:
  • value – set the value. Either (choice, value) tuple, or pyderasn.Choice object
  • impl (bytes) – can not be set, do not use it
  • expl (bytes) – override default tag with EXPLICIT one
  • default – set default value. Type same as in value
  • optional (bool) – is object OPTIONAL in sequence

PrimitiveTypes

class pyderasn.PrimitiveTypes(value=None, schema=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

Predefined CHOICE for all generic primitive types

It could be useful for general decoding of some unspecified values:

>>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
OCTET STRING 3 bytes 666f6f
>>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
INTEGER 1193046

Any

class pyderasn.Any(value=None, expl=None, optional=False, _decoded=(0, 0, 0))

ANY special type

>>> Any(Integer(-123))
ANY 020185
>>> a = Any(OctetString(b"hello world").encode())
ANY 040b68656c6c6f20776f726c64
>>> hexenc(bytes(a))
b'0x040x0bhello world'
__init__(value=None, expl=None, optional=False, _decoded=(0, 0, 0))
Parameters:
  • value – set the value. Either any kind of pyderasn’s ready object, or bytes. Pay attention that no validation is performed is raw binary value is valid TLV
  • expl (bytes) – override default tag with EXPLICIT one
  • optional (bool) – is object OPTIONAL in sequence

Constructed types

Sequence

class pyderasn.Sequence(value=None, schema=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

SEQUENCE structure type

You have to make specification of sequence:

class Extension(Sequence):
    schema = (
        ("extnID", ObjectIdentifier()),
        ("critical", Boolean(default=False)),
        ("extnValue", OctetString()),
    )

Then, you can work with it as with dictionary.

>>> ext = Extension()
>>> Extension().specs
OrderedDict([
    ('extnID', OBJECT IDENTIFIER),
    ('critical', BOOLEAN False OPTIONAL DEFAULT),
    ('extnValue', OCTET STRING),
])
>>> ext["extnID"] = "1.2.3"
Traceback (most recent call last):
pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
>>> ext["extnID"] = ObjectIdentifier("1.2.3")

You can determine if sequence is ready to be encoded:

>>> ext.ready
False
>>> ext.encode()
Traceback (most recent call last):
pyderasn.ObjNotReady: object is not ready: extnValue
>>> ext["extnValue"] = OctetString(b"foobar")
>>> ext.ready
True

Value you want to assign, must have the same type as in corresponding specification, but it can have different tags, optional/default attributes – they will be taken from specification automatically:

class TBSCertificate(Sequence):
    schema = (
        ("version", Version(expl=tag_ctxc(0), default="v1")),
    [...]
>>> tbs = TBSCertificate()
>>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``

Assign None to remove value from sequence.

You can set values in Sequence during its initialization:

>>> AlgorithmIdentifier((
    ("algorithm", ObjectIdentifier("1.2.3")),
    ("parameters", Any(Null()))
))
AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]

You can determine if value exists/set in the sequence and take its value:

>>> "extnID" in ext, "extnValue" in ext, "critical" in ext
(True, True, False)
>>> ext["extnID"]
OBJECT IDENTIFIER 1.2.3

But pay attention that if value has default, then it won’t be (not in) in the sequence (because DEFAULT must not be encoded in DER), but you can read its value:

>>> "critical" in ext, ext["critical"]
(False, BOOLEAN False)
>>> ext["critical"] = Boolean(True)
>>> "critical" in ext, ext["critical"]
(True, BOOLEAN True)

All defaulted values are always optional.

DER prohibits default value encoding and will raise an error if default value is unexpectedly met during decode. If bered context option is set, then no error will be raised, but bered attribute set. You can disable strict defaulted values existence validation by setting "allow_default_values": True context option.

Two sequences are equal if they have equal specification (schema), implicit/explicit tagging and the same values.

Set

class pyderasn.Set(value=None, schema=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

SET structure type

Its usage is identical to pyderasn.Sequence.

DER prohibits unordered values encoding and will raise an error during decode. If If bered context option is set, then no error will occure. Also you can disable strict values ordering check by setting "allow_unordered_set": True context option.

SequenceOf

class pyderasn.SequenceOf(value=None, schema=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

SEQUENCE OF sequence type

For that kind of type you must specify the object it will carry on (bounds are for example here, not required):

class Ints(SequenceOf):
    schema = Integer()
    bounds = (0, 2)
>>> ints = Ints()
>>> ints.append(Integer(123))
>>> ints.append(Integer(234))
>>> ints
Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
>>> [int(i) for i in ints]
[123, 234]
>>> ints.append(Integer(345))
Traceback (most recent call last):
pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
>>> ints[1]
INTEGER 234
>>> ints[1] = Integer(345)
>>> ints
Ints SEQUENCE OF[INTEGER 123, INTEGER 345]

Also you can initialize sequence with preinitialized values:

>>> ints = Ints([Integer(123), Integer(234)])

SetOf

class pyderasn.SetOf(value=None, schema=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

SET OF sequence type

Its usage is identical to pyderasn.SequenceOf.

Various

pyderasn.abs_decode_path(decode_path, rel_path)

Create an absolute decode path from current and relative ones

Parameters:
  • decode_path – current decode path, starting point. Tuple of strings
  • rel_path – relative path to decode_path. Tuple of strings. If first tuple’s element is “/”, then treat it as an absolute path, ignoring decode_path as starting point. Also this tuple can contain “..” elements, stripping the leading element from decode_path
>>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
("foo", "bar", "baz", "whatever")
>>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
("foo", "whatever")
>>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
("baz", "whatever")
pyderasn.hexenc(data)

Hexadecimal string to binary data convert

pyderasn.hexdec(data)

Binary data to hexadecimal string convert

pyderasn.tag_encode(num, klass=0, form=0)

Encode tag to binary form

Parameters:
  • num (int) – tag’s number
  • klass (int) – tag’s class (pyderasn.TagClassUniversal, pyderasn.TagClassContext, pyderasn.TagClassApplication, pyderasn.TagClassPrivate)
  • form (int) – tag’s form (pyderasn.TagFormPrimitive, pyderasn.TagFormConstructed)
pyderasn.tag_decode(tag)

Decode tag from binary form

Warning

No validation is performed, assuming that it has already passed.

It returns tuple with three integers, as pyderasn.tag_encode() accepts.

pyderasn.tag_ctxp(num)

Create CONTEXT PRIMITIVE tag

pyderasn.tag_ctxc(num)

Create CONTEXT CONSTRUCTED tag

class pyderasn.Obj(impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))

Common ASN.1 object class

All ASN.1 types are inherited from it. It has metaclass that automatically adds __slots__ to all inherited classes.

class pyderasn.DecodeError(msg='', klass=None, decode_path=(), offset=0)
__init__(msg='', klass=None, decode_path=(), offset=0)
Parameters:
  • msg (str) – reason of decode failing
  • klass – optional exact DecodeError inherited class (like NotEnoughData, TagMismatch, InvalidLength)
  • decode_path – tuple of strings. It contains human readable names of the fields through which decoding process has passed
  • offset (int) – binary offset where failure happened
class pyderasn.NotEnoughData(msg='', klass=None, decode_path=(), offset=0)
class pyderasn.LenIndefForm(msg='', klass=None, decode_path=(), offset=0)
class pyderasn.TagMismatch(msg='', klass=None, decode_path=(), offset=0)
class pyderasn.InvalidLength(msg='', klass=None, decode_path=(), offset=0)
class pyderasn.InvalidOID(msg='', klass=None, decode_path=(), offset=0)
class pyderasn.ObjUnknown(name)
class pyderasn.ObjNotReady(name)
class pyderasn.InvalidValueType(expected_types)
class pyderasn.BoundsError(bound_min, value, bound_max)