Library reference¶
Contents
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().decod(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 either impl
keyword argument or impl
class attribute), or EXPLICIT
one (using either expl
keyword
argument or expl
class attribute). 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 are friendly to copy.copy()
and copied objects can be
safely mutated.
Also all objects can be safely pickle
-d, but pay attention that
pickling among different PyDERASN versions is prohibited.
Decoding¶
Decoding is performed using pyderasn.Obj.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.
Also note convenient pyderasn.Obj.decod()
method, that
immediately checks and raises if there is non-empty tail.
When object is decoded, decoded
property is true and you can safely
use following properties:
offset
– position including initial offset where object’s tag startstlen
– length of object’s tagllen
– length of object’s length valuevlen
– length of object’s valuetlvlen
– 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 setexpl_offset
(it is lesser thanoffset
)expl_tlen
,expl_llen
expl_vlen
(that actually equals to ordinarytlvlen
)fulloffset
– it equals toexpl_offset
if explicit tag is set,offset
otherwisefulllen
– it equals toexpl_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
pyderasn.Obj.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
Example certificate:
>>> print(pprint(crt))
0 [1,3,1604] Certificate SEQUENCE
4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
. . . . 05:00
33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
. . . . . . . 13:02:45:53
[...]
1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
. . . 05:00
1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
. . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
. . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
[...]
Trailing data: 0a
Let’s parse that output, human:
10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
0 1 2 3 4 5 6 7 8 9 10 11
20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
^ ^ ^ ^ ^ ^ ^ ^
0 2 3 4 5 6 9 10
33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
^ ^ ^ ^ ^ ^ ^ ^ ^
0 2 3 4 5 6 8 9 10
52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
^ ^ ^ ^ ^
12 13 14 9 10
0: | Offset of the object, where its DER/BER encoding begins. Pay attention that it does not include explicit tag. |
---|---|
1: | If explicit tag exists, then this is its length (tag + encoded length). |
2: | Length of object’s tag. For example CHOICE does not have its own tag, so it is zero. |
3: | Length of encoded length. |
4: | Length of encoded value. |
5: | Visual indentation to show the depth of object in the hierarchy. |
6: | Object’s name inside SEQUENCE/CHOICE. |
7: | If either IMPLICIT or EXPLICIT tag is set, then it will be shown here. “IMPLICIT” is omitted. |
8: | Object’s class name, if set. Omitted if it is just an ordinary simple
value (like with algorithm in example above). |
9: | Object’s ASN.1 type. |
10: | Object’s value, if set. Can consist of multiple words (like OCTET/BIT
STRINGs above). We see v3 value in Version, because it is named.
rdnSequence is the choice of CHOICE type. |
11: | Possible other flags like OPTIONAL and DEFAULT, if value equals to the default one, specified in the schema. |
12: | Shows does object contains any kind of BER encoded data (possibly Sequence holding BER-encoded underlying value). |
13: | Only applicable to BER encoded data. Indefinite length encoding mark. |
14: | Only applicable to BER encoded data. If object has BER-specific
encoding, then BER will be shown. It does not depend on indefinite
length encoding. EOC , BOOLEAN , BIT STRING , OCTET STRING
(and its derivatives), SET , SET OF , UTCTime , GeneralizedTime
could be BERed. |
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):
pyderasn.Any
pyderasn.BitString
(that is multiple of 8 bits)pyderasn.OctetString
pyderasn.SequenceOf
/pyderasn.SetOf
Any
/BitString
/OctetString
-s
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 = ContentInfo().decod(data, ctx={"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. OnlyBOOLEAN
,BIT STRING
,OCTET STRING
,OBJECT IDENTIFIER
,SEQUENCE
,SET
,SET OF
,UTCTime
,GeneralizedTime
can contain it. - If object has an indefinite length encoding, then its
lenindef
attribute is set to True. OnlyBIT 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 haveContentInfo:content:signerInfos:*
bered
value set to True, butContentInfo: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.
Base Obj¶
-
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.-
bered
¶ Is either object or any elements inside is BER encoded?
-
decod
(data, offset=0, decode_path=(), ctx=None)¶ Decode the data, check that tail is empty
Raises: ExceedingData – if tail is not empty This is just a wrapper over
pyderasn.Obj.decode()
(decode without tail) that also checks that there is no trailing data left.
-
decode
(data, offset=0, leavemm=False, decode_path=(), ctx=None, tag_only=False, _ctx_immutable=True)¶ Decode the data
Parameters: - data – either binary or memoryview
- offset (int) – initial data’s offset
- leavemm (bool) – do we need to leave memoryview of remaining data as is, or convert it to bytes otherwise
- ctx – optional context governing decoding process
- tag_only – decode only the tag, without length and contents (used only in Choice and Set structures, trying to determine if tag satisfies the scheme)
- _ctx_immutable – do we need to
copy.copy()
ctx
before using it?
Returns: (Obj, remaining data)
See also
-
decoded
¶ Is object decoded?
-
encode
()¶ Encode the structure
Returns: DER representation
-
hexdecod
(data, *args, **kwargs)¶ Do
pyderasn.Obj.decod()
with hexadecimal decoded data
-
hexdecode
(data, *args, **kwargs)¶ Do
pyderasn.Obj.decode()
with hexadecimal decoded data
-
hexencode
()¶ Do hexadecimal encoded
pyderasn.Obj.encode()
-
ready
¶ Is object ready to be encoded?
-
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
- value – set the value. Either boolean type, or
-
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), orpyderasn.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
- value – set the value. Either integer type, named value
(if
-
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, orpyderasn.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
- value – set the value. Either binary type, tuple of named
values (if
-
OctetString¶
-
class
pyderasn.
OctetString
(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), ctx=None)¶ 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), ctx=None)¶ 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
- value – set the value. Either binary type, or
-
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
- impl (bytes) – override default tag with
-
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
- value – set the value. Either tuples of integers,
string of “.”-concatenated integers, or
-
Enumerated¶
-
class
pyderasn.
Enumerated
(value=None, impl=None, expl=None, default=None, optional=False, _specs=None, _decoded=(0, 0, 0), bounds=None)¶ ENUMERATED
integer typeThis 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), ctx=None)¶ 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), ctx=None)¶ Numeric string
Its value is properly sanitized: only ASCII digits with spaces can be stored.
>>> NumericString().allowable_chars frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
PrintableString¶
-
class
pyderasn.
PrintableString
(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), ctx=None, allow_asterisk=False, allow_ampersand=False)¶ Printable string
Its value is properly sanitized: see X.680 41.4 table 10.
>>> PrintableString().allowable_chars frozenset([' ', "'", ..., 'z']) >>> obj = PrintableString("foo*bar", allow_asterisk=True) PrintableString PrintableString foo*bar >>> obj.allow_asterisk, obj.allow_ampersand (True, False)
-
__init__
(value=None, bounds=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), ctx=None, allow_asterisk=False, allow_ampersand=False)¶ Parameters: - allow_asterisk – allow asterisk character
- allow_ampersand – allow ampersand character
-
UTCTime¶
-
class
pyderasn.
UTCTime
(value=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), bounds=None, ctx=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)
If BER encoded value was met, then
ber_raw
attribute will hold its raw representation.Warning
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 recommendation.
Warning
No strict validation of UTC offsets are made, but very crude:
- minutes are not exceeding 60
- offset value is not exceeding 14 hours
-
__init__
(value=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), bounds=None, ctx=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
- value – set the value. Either datetime type, or
GeneralizedTime¶
-
class
pyderasn.
GeneralizedTime
(value=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0), bounds=None, ctx=None)¶ GeneralizedTime
datetime typeThis 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
Warning
Only microsecond fractions are supported in DER encoding.
pyderasn.DecodeError
will be raised during decoding of higher precision values.Warning
BER encoded data can loss information (accuracy) during decoding because of float transformations.
Warning
Local times (without explicit timezone specification) are treated as UTC one, no transformations are made.
Warning
Zero year is unsupported.
Special types¶
Choice¶
-
class
pyderasn.
Choice
(value=None, schema=None, impl=None, expl=None, default=None, optional=False, _decoded=(0, 0, 0))¶ CHOICE
special typeclass 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, orpyderasn.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
- value – set the value. Either
-
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 typesIt could be useful for general decoding of some unspecified values:
>>> PrimitiveTypes().decod(hexdec("0403666f6f")).value OCTET STRING 3 bytes 666f6f >>> PrimitiveTypes().decod(hexdec("0203123456")).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 typeYou 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 typeIts 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 typeFor 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 typeIts 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, ignoringdecode_path
as starting point. Also this tuple can contain “..” elements, stripping the leading element fromdecode_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.
colonize_hex
(hexed)¶ Separate hexadecimal string with colons
-
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.
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.
ExceedingData
(nbytes)¶
-
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)¶