3 # cython: language_level=3
4 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
5 # Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Lesser General Public License as
9 # published by the Free Software Foundation, version 3 of the License.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """Python ASN.1 DER/BER codec with abstract structures
20 This library allows you to marshal various structures in ASN.1 DER
21 format, unmarshal them in BER/CER/DER ones.
25 >>> Integer().decod(raw) == i
28 There are primitive types, holding single values
29 (:py:class:`pyderasn.BitString`,
30 :py:class:`pyderasn.Boolean`,
31 :py:class:`pyderasn.Enumerated`,
32 :py:class:`pyderasn.GeneralizedTime`,
33 :py:class:`pyderasn.Integer`,
34 :py:class:`pyderasn.Null`,
35 :py:class:`pyderasn.ObjectIdentifier`,
36 :py:class:`pyderasn.OctetString`,
37 :py:class:`pyderasn.UTCTime`,
38 :py:class:`various strings <pyderasn.CommonString>`
39 (:py:class:`pyderasn.BMPString`,
40 :py:class:`pyderasn.GeneralString`,
41 :py:class:`pyderasn.GraphicString`,
42 :py:class:`pyderasn.IA5String`,
43 :py:class:`pyderasn.ISO646String`,
44 :py:class:`pyderasn.NumericString`,
45 :py:class:`pyderasn.PrintableString`,
46 :py:class:`pyderasn.T61String`,
47 :py:class:`pyderasn.TeletexString`,
48 :py:class:`pyderasn.UniversalString`,
49 :py:class:`pyderasn.UTF8String`,
50 :py:class:`pyderasn.VideotexString`,
51 :py:class:`pyderasn.VisibleString`)),
52 constructed types, holding multiple primitive types
53 (:py:class:`pyderasn.Sequence`,
54 :py:class:`pyderasn.SequenceOf`,
55 :py:class:`pyderasn.Set`,
56 :py:class:`pyderasn.SetOf`),
57 and special types like
58 :py:class:`pyderasn.Any` and
59 :py:class:`pyderasn.Choice`.
67 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
68 the default tag used during coding process. You can override it with
69 either ``IMPLICIT`` (using either ``impl`` keyword argument or ``impl``
70 class attribute), or ``EXPLICIT`` one (using either ``expl`` keyword
71 argument or ``expl`` class attribute). Both arguments take raw binary
72 string, containing that tag. You can **not** set implicit and explicit
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
82 EXPLICIT tags always have **constructed** tag. PyDERASN does not
83 explicitly check correctness of schema input here.
87 Implicit tags have **primitive** (``tag_ctxp``) encoding for
92 >>> Integer(impl=tag_ctxp(1))
94 >>> Integer(expl=tag_ctxc(2))
97 Implicit tag is not explicitly shown.
99 Two objects of the same type, but with different implicit/explicit tags
102 You can get object's effective tag (either default or implicited) through
103 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
106 >>> tag_decode(tag_ctxc(123))
108 >>> klass, form, num = tag_decode(tag_ctxc(123))
109 >>> klass == TagClassContext
111 >>> form == TagFormConstructed
114 To determine if object has explicit tag, use ``expled`` boolean property
115 and ``expl_tag`` property, returning explicit tag's value.
120 Many objects in sequences could be ``OPTIONAL`` and could have
121 ``DEFAULT`` value. You can specify that object's property using
122 corresponding keyword arguments.
124 >>> Integer(optional=True, default=123)
125 INTEGER 123 OPTIONAL DEFAULT
127 Those specifications do not play any role in primitive value encoding,
128 but are taken into account when dealing with sequences holding them. For
129 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
132 class Version(Integer):
138 class TBSCertificate(Sequence):
140 ("version", Version(expl=tag_ctxc(0), default="v1")),
143 When default argument is used and value is not specified, then it equals
151 Some objects give ability to set value size constraints. This is either
152 possible integer value, or allowed length of various strings and
153 sequences. Constraints are set in the following way::
158 And values satisfaction is checked as: ``MIN <= X <= MAX``.
160 For simplicity you can also set bounds the following way::
162 bounded_x = X(bounds=(MIN, MAX))
164 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
170 All objects have ``ready`` boolean property, that tells if object is
171 ready to be encoded. If that kind of action is performed on unready
172 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
174 All objects are friendly to ``copy.copy()`` and copied objects can be
177 Also all objects can be safely ``pickle``-d, but pay attention that
178 pickling among different PyDERASN versions is prohibited.
185 Decoding is performed using :py:meth:`pyderasn.Obj.decode` method.
186 ``offset`` optional argument could be used to set initial object's
187 offset in the binary data, for convenience. It returns decoded object
188 and remaining unmarshalled data (tail). Internally all work is done on
189 ``memoryview(data)``, and you can leave returning tail as a memoryview,
190 by specifying ``leavemm=True`` argument.
192 Also note convenient :py:meth:`pyderasn.Obj.decod` method, that
193 immediately checks and raises if there is non-empty tail.
195 When object is decoded, ``decoded`` property is true and you can safely
196 use following properties:
198 * ``offset`` -- position including initial offset where object's tag starts
199 * ``tlen`` -- length of object's tag
200 * ``llen`` -- length of object's length value
201 * ``vlen`` -- length of object's value
202 * ``tlvlen`` -- length of the whole object
204 Pay attention that those values do **not** include anything related to
205 explicit tag. If you want to know information about it, then use:
207 * ``expled`` -- to know if explicit tag is set
208 * ``expl_offset`` (it is lesser than ``offset``)
211 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
212 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
214 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
217 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
224 You can specify so called context keyword argument during
225 :py:meth:`pyderasn.Obj.decode` invocation. It is dictionary containing
226 various options governing decoding process.
228 Currently available context options:
230 * :ref:`allow_default_values <allow_default_values_ctx>`
231 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
232 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
233 * :ref:`bered <bered_ctx>`
234 * :ref:`defines_by_path <defines_by_path_ctx>`
241 All objects have ``pps()`` method, that is a generator of
242 :py:class:`pyderasn.PP` namedtuple, holding various raw information
243 about the object. If ``pps`` is called on sequences, then all underlying
244 ``PP`` will be yielded.
246 You can use :py:func:`pyderasn.pp_console_row` function, converting
247 those ``PP`` to human readable string. Actually exactly it is used for
248 all object ``repr``. But it is easy to write custom formatters.
250 >>> from pyderasn import pprint
251 >>> encoded = Integer(-12345).encode()
252 >>> obj, tail = Integer().decode(encoded)
253 >>> print(pprint(obj))
254 0 [1,1, 2] INTEGER -12345
258 Example certificate::
260 >>> print(pprint(crt))
261 0 [1,3,1604] Certificate SEQUENCE
262 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
263 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
264 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
265 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
266 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
267 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
269 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
270 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
271 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
272 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
273 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
274 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
275 . . . . . . . 13:02:45:53
277 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
278 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
279 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
281 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
282 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
283 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
288 Let's parse that output, human::
290 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
291 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
292 0 1 2 3 4 5 6 7 8 9 10 11
296 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
302 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
308 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
313 Offset of the object, where its DER/BER encoding begins.
314 Pay attention that it does **not** include explicit tag.
316 If explicit tag exists, then this is its length (tag + encoded length).
318 Length of object's tag. For example CHOICE does not have its own tag,
321 Length of encoded length.
323 Length of encoded value.
325 Visual indentation to show the depth of object in the hierarchy.
327 Object's name inside SEQUENCE/CHOICE.
329 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
330 here. "IMPLICIT" is omitted.
332 Object's class name, if set. Omitted if it is just an ordinary simple
333 value (like with ``algorithm`` in example above).
337 Object's value, if set. Can consist of multiple words (like OCTET/BIT
338 STRINGs above). We see ``v3`` value in Version, because it is named.
339 ``rdnSequence`` is the choice of CHOICE type.
341 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
342 default one, specified in the schema.
344 Shows does object contains any kind of BER encoded data (possibly
345 Sequence holding BER-encoded underlying value).
347 Only applicable to BER encoded data. Indefinite length encoding mark.
349 Only applicable to BER encoded data. If object has BER-specific
350 encoding, then ``BER`` will be shown. It does not depend on indefinite
351 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
352 (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime``
361 ASN.1 structures often have ANY and OCTET STRING fields, that are
362 DEFINED BY some previously met ObjectIdentifier. This library provides
363 ability to specify mapping between some OID and field that must be
364 decoded with specific specification.
371 :py:class:`pyderasn.ObjectIdentifier` field inside
372 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
373 necessary for decoding structures. For example, CMS (:rfc:`5652`)
376 class ContentInfo(Sequence):
378 ("contentType", ContentType(defines=((("content",), {
379 id_digestedData: DigestedData(),
380 id_signedData: SignedData(),
382 ("content", Any(expl=tag_ctxc(0))),
385 ``contentType`` field tells that it defines that ``content`` must be
386 decoded with ``SignedData`` specification, if ``contentType`` equals to
387 ``id-signedData``. The same applies to ``DigestedData``. If
388 ``contentType`` contains unknown OID, then no automatic decoding is
391 You can specify multiple fields, that will be autodecoded -- that is why
392 ``defines`` kwarg is a sequence. You can specify defined field
393 relatively or absolutely to current decode path. For example ``defines``
394 for AlgorithmIdentifier of X.509's
395 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
399 id_ecPublicKey: ECParameters(),
400 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
402 (("..", "subjectPublicKey"), {
403 id_rsaEncryption: RSAPublicKey(),
404 id_GostR3410_2001: OctetString(),
408 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
409 autodecode its parameters inside SPKI's algorithm and its public key
412 Following types can be automatically decoded (DEFINED BY):
414 * :py:class:`pyderasn.Any`
415 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
416 * :py:class:`pyderasn.OctetString`
417 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
418 ``Any``/``BitString``/``OctetString``-s
420 When any of those fields is automatically decoded, then ``.defined``
421 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
422 was defined, ``value`` contains corresponding decoded value. For example
423 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
425 .. _defines_by_path_ctx:
427 defines_by_path context option
428 ______________________________
430 Sometimes you either can not or do not want to explicitly set *defines*
431 in the schema. You can dynamically apply those definitions when calling
432 ``.decode()`` method.
434 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
435 value must be sequence of following tuples::
437 (decode_path, defines)
439 where ``decode_path`` is a tuple holding so-called decode path to the
440 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
441 ``defines``, holding exactly the same value as accepted in its
442 :ref:`keyword argument <defines>`.
444 For example, again for CMS, you want to automatically decode
445 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
446 structures it may hold. Also, automatically decode ``controlSequence``
449 content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
452 ((("content",), {id_signedData: SignedData()}),),
457 DecodePathDefBy(id_signedData),
462 id_cct_PKIData: PKIData(),
463 id_cct_PKIResponse: PKIResponse(),
469 DecodePathDefBy(id_signedData),
472 DecodePathDefBy(id_cct_PKIResponse),
478 id_cmc_recipientNonce: RecipientNonce(),
479 id_cmc_senderNonce: SenderNonce(),
480 id_cmc_statusInfoV2: CMCStatusInfoV2(),
481 id_cmc_transactionId: TransactionId(),
486 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
487 First function is useful for path construction when some automatic
488 decoding is already done. ``any`` means literally any value it meet --
489 useful for SEQUENCE/SET OF-s.
496 By default PyDERASN accepts only DER encoded data. It always encodes to
497 DER. But you can optionally enable BER decoding with setting ``bered``
498 :ref:`context <ctx>` argument to True. Indefinite lengths and
499 constructed primitive types should be parsed successfully.
501 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
502 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
503 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``,
504 ``UTCTime``, ``GeneralizedTime`` can contain it.
505 * If object has an indefinite length encoding, then its ``lenindef``
506 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
507 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
509 * If object has an indefinite length encoded explicit tag, then
510 ``expl_lenindef`` is set to True.
511 * If object has either any of BER-related encoding (explicit tag
512 indefinite length, object's indefinite length, BER-encoding) or any
513 underlying component has that kind of encoding, then ``bered``
514 attribute is set to True. For example SignedData CMS can have
515 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
516 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
518 EOC (end-of-contents) token's length is taken in advance in object's
521 .. _allow_expl_oob_ctx:
523 Allow explicit tag out-of-bound
524 -------------------------------
526 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
527 one value, more than one object. If you set ``allow_expl_oob`` context
528 option to True, then no error will be raised and that invalid encoding
529 will be silently further processed. But pay attention that offsets and
530 lengths will be invalid in that case.
534 This option should be used only for skipping some decode errors, just
535 to see the decoded structure somehow.
539 .. autoclass:: pyderasn.Obj
547 .. autoclass:: pyderasn.Boolean
552 .. autoclass:: pyderasn.Integer
553 :members: __init__, named
557 .. autoclass:: pyderasn.BitString
558 :members: __init__, bit_len, named
562 .. autoclass:: pyderasn.OctetString
567 .. autoclass:: pyderasn.Null
572 .. autoclass:: pyderasn.ObjectIdentifier
577 .. autoclass:: pyderasn.Enumerated
581 .. autoclass:: pyderasn.CommonString
585 .. autoclass:: pyderasn.NumericString
589 .. autoclass:: pyderasn.PrintableString
590 :members: __init__, allow_asterisk, allow_ampersand
594 .. autoclass:: pyderasn.UTCTime
595 :members: __init__, todatetime
599 .. autoclass:: pyderasn.GeneralizedTime
600 :members: __init__, todatetime
607 .. autoclass:: pyderasn.Choice
608 :members: __init__, choice, value
612 .. autoclass:: PrimitiveTypes
616 .. autoclass:: pyderasn.Any
624 .. autoclass:: pyderasn.Sequence
629 .. autoclass:: pyderasn.Set
634 .. autoclass:: pyderasn.SequenceOf
639 .. autoclass:: pyderasn.SetOf
645 .. autofunction:: pyderasn.abs_decode_path
646 .. autofunction:: pyderasn.colonize_hex
647 .. autofunction:: pyderasn.hexenc
648 .. autofunction:: pyderasn.hexdec
649 .. autofunction:: pyderasn.tag_encode
650 .. autofunction:: pyderasn.tag_decode
651 .. autofunction:: pyderasn.tag_ctxp
652 .. autofunction:: pyderasn.tag_ctxc
653 .. autoclass:: pyderasn.DecodeError
655 .. autoclass:: pyderasn.NotEnoughData
656 .. autoclass:: pyderasn.ExceedingData
657 .. autoclass:: pyderasn.LenIndefForm
658 .. autoclass:: pyderasn.TagMismatch
659 .. autoclass:: pyderasn.InvalidLength
660 .. autoclass:: pyderasn.InvalidOID
661 .. autoclass:: pyderasn.ObjUnknown
662 .. autoclass:: pyderasn.ObjNotReady
663 .. autoclass:: pyderasn.InvalidValueType
664 .. autoclass:: pyderasn.BoundsError
671 You can decode DER/BER files using command line abilities::
673 $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
675 If there is no schema for your file, then you can try parsing it without,
676 but of course IMPLICIT tags will often make it impossible. But result is
677 good enough for the certificate above::
679 $ python -m pyderasn path/to/file
680 0 [1,3,1604] . >: SEQUENCE OF
681 4 [1,3,1453] . . >: SEQUENCE OF
682 8 [0,0, 5] . . . . >: [0] ANY
683 . . . . . A0:03:02:01:02
684 13 [1,1, 3] . . . . >: INTEGER 61595
685 18 [1,1, 13] . . . . >: SEQUENCE OF
686 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
687 31 [1,1, 0] . . . . . . >: NULL
688 33 [1,3, 274] . . . . >: SEQUENCE OF
689 37 [1,1, 11] . . . . . . >: SET OF
690 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
691 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
692 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
694 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
695 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
696 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
697 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
698 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
699 . . . . . . . . . 61:2E:63:6F:6D:2F
700 1461 [1,1, 13] . . >: SEQUENCE OF
701 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
702 1474 [1,1, 0] . . . . >: NULL
703 1476 [1,2, 129] . . >: BIT STRING 1024 bits
704 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
705 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
711 If you have got dictionaries with ObjectIdentifiers, like example one
712 from ``tests/test_crts.py``::
715 "1.2.840.113549.1.1.1": "id-rsaEncryption",
716 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
718 "2.5.4.10": "id-at-organizationName",
719 "2.5.4.11": "id-at-organizationalUnitName",
722 then you can pass it to pretty printer to see human readable OIDs::
724 $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
726 37 [1,1, 11] . . . . . . >: SET OF
727 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
728 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
729 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
730 50 [1,1, 18] . . . . . . >: SET OF
731 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
732 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
733 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
734 70 [1,1, 18] . . . . . . >: SET OF
735 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
736 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
737 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
743 Each decoded element has so-called decode path: sequence of structure
744 names it is passing during the decode process. Each element has its own
745 unique path inside the whole ASN.1 tree. You can print it out with
746 ``--print-decode-path`` option::
748 $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
749 0 [1,3,1604] Certificate SEQUENCE []
750 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
751 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
752 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
753 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
754 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
755 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
757 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
758 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
759 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
760 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
761 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
762 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
763 . . . . . . . 13:02:45:53
764 46 [1,1, 2] . . . . . . . DEFINED BY 2.5.4.6: CountryName PrintableString ES [tbsCertificate:issuer:rdnSequence:0:0:value:DEFINED BY 2.5.4.6]
767 Now you can print only the specified tree, for example signature algorithm::
769 $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
770 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
771 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
772 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
776 from codecs import getdecoder
777 from codecs import getencoder
778 from collections import namedtuple
779 from collections import OrderedDict
780 from copy import copy
781 from datetime import datetime
782 from datetime import timedelta
783 from math import ceil
784 from string import ascii_letters
785 from string import digits
786 from sys import version_info
787 from unicodedata import category as unicat
789 from six import add_metaclass
790 from six import binary_type
791 from six import byte2int
792 from six import indexbytes
793 from six import int2byte
794 from six import integer_types
795 from six import iterbytes
796 from six import iteritems
797 from six import itervalues
799 from six import string_types
800 from six import text_type
801 from six import unichr as six_unichr
802 from six.moves import xrange as six_xrange
806 from termcolor import colored
807 except ImportError: # pragma: no cover
808 def colored(what, *args, **kwargs):
854 "TagClassApplication",
858 "TagFormConstructed",
869 TagClassUniversal = 0
870 TagClassApplication = 1 << 6
871 TagClassContext = 1 << 7
872 TagClassPrivate = 1 << 6 | 1 << 7
874 TagFormConstructed = 1 << 5
877 TagClassApplication: "APPLICATION ",
878 TagClassPrivate: "PRIVATE ",
879 TagClassUniversal: "UNIV ",
883 LENINDEF = b"\x80" # length indefinite mark
884 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
885 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
886 SET01 = frozenset("01")
887 DECIMALS = frozenset(digits)
892 if not set(value) <= DECIMALS:
893 raise ValueError("non-pure integer")
896 def fractions2float(fractions_raw):
897 pureint(fractions_raw)
898 return float("0." + fractions_raw)
901 ########################################################################
903 ########################################################################
905 class ASN1Error(ValueError):
909 class DecodeError(ASN1Error):
910 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
912 :param str msg: reason of decode failing
913 :param klass: optional exact DecodeError inherited class (like
914 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
915 :py:exc:`InvalidLength`)
916 :param decode_path: tuple of strings. It contains human
917 readable names of the fields through which
918 decoding process has passed
919 :param int offset: binary offset where failure happened
921 super(DecodeError, self).__init__()
924 self.decode_path = decode_path
930 "" if self.klass is None else self.klass.__name__,
932 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
933 if len(self.decode_path) > 0 else ""
935 ("(at %d)" % self.offset) if self.offset > 0 else "",
941 return "%s(%s)" % (self.__class__.__name__, self)
944 class NotEnoughData(DecodeError):
948 class ExceedingData(ASN1Error):
949 def __init__(self, nbytes):
950 super(ExceedingData, self).__init__()
954 return "%d trailing bytes" % self.nbytes
957 return "%s(%s)" % (self.__class__.__name__, self)
960 class LenIndefForm(DecodeError):
964 class TagMismatch(DecodeError):
968 class InvalidLength(DecodeError):
972 class InvalidOID(DecodeError):
976 class ObjUnknown(ASN1Error):
977 def __init__(self, name):
978 super(ObjUnknown, self).__init__()
982 return "object is unknown: %s" % self.name
985 return "%s(%s)" % (self.__class__.__name__, self)
988 class ObjNotReady(ASN1Error):
989 def __init__(self, name):
990 super(ObjNotReady, self).__init__()
994 return "object is not ready: %s" % self.name
997 return "%s(%s)" % (self.__class__.__name__, self)
1000 class InvalidValueType(ASN1Error):
1001 def __init__(self, expected_types):
1002 super(InvalidValueType, self).__init__()
1003 self.expected_types = expected_types
1006 return "invalid value type, expected: %s" % ", ".join(
1007 [repr(t) for t in self.expected_types]
1011 return "%s(%s)" % (self.__class__.__name__, self)
1014 class BoundsError(ASN1Error):
1015 def __init__(self, bound_min, value, bound_max):
1016 super(BoundsError, self).__init__()
1017 self.bound_min = bound_min
1019 self.bound_max = bound_max
1022 return "unsatisfied bounds: %s <= %s <= %s" % (
1029 return "%s(%s)" % (self.__class__.__name__, self)
1032 ########################################################################
1034 ########################################################################
1036 _hexdecoder = getdecoder("hex")
1037 _hexencoder = getencoder("hex")
1041 """Binary data to hexadecimal string convert
1043 return _hexdecoder(data)[0]
1047 """Hexadecimal string to binary data convert
1049 return _hexencoder(data)[0].decode("ascii")
1052 def int_bytes_len(num, byte_len=8):
1055 return int(ceil(float(num.bit_length()) / byte_len))
1058 def zero_ended_encode(num):
1059 octets = bytearray(int_bytes_len(num, 7))
1061 octets[i] = num & 0x7F
1065 octets[i] = 0x80 | (num & 0x7F)
1068 return bytes(octets)
1071 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
1072 """Encode tag to binary form
1074 :param int num: tag's number
1075 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
1076 :py:data:`pyderasn.TagClassContext`,
1077 :py:data:`pyderasn.TagClassApplication`,
1078 :py:data:`pyderasn.TagClassPrivate`)
1079 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
1080 :py:data:`pyderasn.TagFormConstructed`)
1084 return int2byte(klass | form | num)
1085 # [XX|X|11111][1.......][1.......] ... [0.......]
1086 return int2byte(klass | form | 31) + zero_ended_encode(num)
1089 def tag_decode(tag):
1090 """Decode tag from binary form
1094 No validation is performed, assuming that it has already passed.
1096 It returns tuple with three integers, as
1097 :py:func:`pyderasn.tag_encode` accepts.
1099 first_octet = byte2int(tag)
1100 klass = first_octet & 0xC0
1101 form = first_octet & 0x20
1102 if first_octet & 0x1F < 0x1F:
1103 return (klass, form, first_octet & 0x1F)
1105 for octet in iterbytes(tag[1:]):
1108 return (klass, form, num)
1112 """Create CONTEXT PRIMITIVE tag
1114 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
1118 """Create CONTEXT CONSTRUCTED tag
1120 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
1123 def tag_strip(data):
1124 """Take off tag from the data
1126 :returns: (encoded tag, tag length, remaining data)
1129 raise NotEnoughData("no data at all")
1130 if byte2int(data) & 0x1F < 31:
1131 return data[:1], 1, data[1:]
1136 raise DecodeError("unfinished tag")
1137 if indexbytes(data, i) & 0x80 == 0:
1140 return data[:i], i, data[i:]
1146 octets = bytearray(int_bytes_len(l) + 1)
1147 octets[0] = 0x80 | (len(octets) - 1)
1148 for i in six_xrange(len(octets) - 1, 0, -1):
1149 octets[i] = l & 0xFF
1151 return bytes(octets)
1154 def len_decode(data):
1157 :returns: (decoded length, length's length, remaining data)
1158 :raises LenIndefForm: if indefinite form encoding is met
1161 raise NotEnoughData("no data at all")
1162 first_octet = byte2int(data)
1163 if first_octet & 0x80 == 0:
1164 return first_octet, 1, data[1:]
1165 octets_num = first_octet & 0x7F
1166 if octets_num + 1 > len(data):
1167 raise NotEnoughData("encoded length is longer than data")
1169 raise LenIndefForm()
1170 if byte2int(data[1:]) == 0:
1171 raise DecodeError("leading zeros")
1173 for v in iterbytes(data[1:1 + octets_num]):
1176 raise DecodeError("long form instead of short one")
1177 return l, 1 + octets_num, data[1 + octets_num:]
1180 ########################################################################
1182 ########################################################################
1184 class AutoAddSlots(type):
1185 def __new__(cls, name, bases, _dict):
1186 _dict["__slots__"] = _dict.get("__slots__", ())
1187 return type.__new__(cls, name, bases, _dict)
1190 BasicState = namedtuple("BasicState", (
1202 ), **NAMEDTUPLE_KWARGS)
1205 @add_metaclass(AutoAddSlots)
1207 """Common ASN.1 object class
1209 All ASN.1 types are inherited from it. It has metaclass that
1210 automatically adds ``__slots__`` to all inherited classes.
1234 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1235 self._expl = getattr(self, "expl", None) if expl is None else expl
1236 if self.tag != self.tag_default and self._expl is not None:
1237 raise ValueError("implicit and explicit tags can not be set simultaneously")
1238 if default is not None:
1240 self.optional = optional
1241 self.offset, self.llen, self.vlen = _decoded
1243 self.expl_lenindef = False
1244 self.lenindef = False
1245 self.ber_encoded = False
1248 def ready(self): # pragma: no cover
1249 """Is object ready to be encoded?
1251 raise NotImplementedError()
1253 def _assert_ready(self):
1255 raise ObjNotReady(self.__class__.__name__)
1259 """Is either object or any elements inside is BER encoded?
1261 return self.expl_lenindef or self.lenindef or self.ber_encoded
1265 """Is object decoded?
1267 return (self.llen + self.vlen) > 0
1269 def __getstate__(self): # pragma: no cover
1270 """Used for making safe to be mutable pickleable copies
1272 raise NotImplementedError()
1274 def __setstate__(self, state):
1275 if state.version != __version__:
1276 raise ValueError("data is pickled by different PyDERASN version")
1277 self.tag = state.tag
1278 self._expl = state.expl
1279 self.default = state.default
1280 self.optional = state.optional
1281 self.offset = state.offset
1282 self.llen = state.llen
1283 self.vlen = state.vlen
1284 self.expl_lenindef = state.expl_lenindef
1285 self.lenindef = state.lenindef
1286 self.ber_encoded = state.ber_encoded
1290 """See :ref:`decoding`
1292 return len(self.tag)
1296 """See :ref:`decoding`
1298 return self.tlen + self.llen + self.vlen
1300 def __str__(self): # pragma: no cover
1301 return self.__bytes__() if PY2 else self.__unicode__()
1303 def __ne__(self, their):
1304 return not(self == their)
1306 def __gt__(self, their): # pragma: no cover
1307 return not(self < their)
1309 def __le__(self, their): # pragma: no cover
1310 return (self == their) or (self < their)
1312 def __ge__(self, their): # pragma: no cover
1313 return (self == their) or (self > their)
1315 def _encode(self): # pragma: no cover
1316 raise NotImplementedError()
1318 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1319 raise NotImplementedError()
1322 """Encode the structure
1324 :returns: DER representation
1326 raw = self._encode()
1327 if self._expl is None:
1329 return b"".join((self._expl, len_encode(len(raw)), raw))
1331 def hexencode(self):
1332 """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1334 return hexenc(self.encode())
1344 _ctx_immutable=True,
1348 :param data: either binary or memoryview
1349 :param int offset: initial data's offset
1350 :param bool leavemm: do we need to leave memoryview of remaining
1351 data as is, or convert it to bytes otherwise
1352 :param ctx: optional :ref:`context <ctx>` governing decoding process
1353 :param tag_only: decode only the tag, without length and contents
1354 (used only in Choice and Set structures, trying to
1355 determine if tag satisfies the schema)
1356 :param _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1358 :returns: (Obj, remaining data)
1360 .. seealso:: :ref:`decoding`
1364 elif _ctx_immutable:
1366 tlv = memoryview(data)
1367 if self._expl is None:
1368 result = self._decode(
1371 decode_path=decode_path,
1380 t, tlen, lv = tag_strip(tlv)
1381 except DecodeError as err:
1382 raise err.__class__(
1384 klass=self.__class__,
1385 decode_path=decode_path,
1390 klass=self.__class__,
1391 decode_path=decode_path,
1395 l, llen, v = len_decode(lv)
1396 except LenIndefForm as err:
1397 if not ctx.get("bered", False):
1398 raise err.__class__(
1400 klass=self.__class__,
1401 decode_path=decode_path,
1405 offset += tlen + llen
1406 result = self._decode(
1409 decode_path=decode_path,
1413 if tag_only: # pragma: no cover
1416 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1417 if eoc_expected.tobytes() != EOC:
1420 klass=self.__class__,
1421 decode_path=decode_path,
1425 obj.expl_lenindef = True
1426 except DecodeError as err:
1427 raise err.__class__(
1429 klass=self.__class__,
1430 decode_path=decode_path,
1435 raise NotEnoughData(
1436 "encoded length is longer than data",
1437 klass=self.__class__,
1438 decode_path=decode_path,
1441 result = self._decode(
1443 offset=offset + tlen + llen,
1444 decode_path=decode_path,
1448 if tag_only: # pragma: no cover
1451 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1453 "explicit tag out-of-bound, longer than data",
1454 klass=self.__class__,
1455 decode_path=decode_path,
1458 return obj, (tail if leavemm else tail.tobytes())
1460 def decod(self, data, offset=0, decode_path=(), ctx=None):
1461 """Decode the data, check that tail is empty
1463 :raises ExceedingData: if tail is not empty
1465 This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
1466 (decode without tail) that also checks that there is no
1469 obj, tail = self.decode(
1472 decode_path=decode_path,
1477 raise ExceedingData(len(tail))
1480 def hexdecode(self, data, *args, **kwargs):
1481 """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
1483 return self.decode(hexdec(data), *args, **kwargs)
1485 def hexdecod(self, data, *args, **kwargs):
1486 """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
1488 return self.decod(hexdec(data), *args, **kwargs)
1492 """See :ref:`decoding`
1494 return self._expl is not None
1498 """See :ref:`decoding`
1503 def expl_tlen(self):
1504 """See :ref:`decoding`
1506 return len(self._expl)
1509 def expl_llen(self):
1510 """See :ref:`decoding`
1512 if self.expl_lenindef:
1514 return len(len_encode(self.tlvlen))
1517 def expl_offset(self):
1518 """See :ref:`decoding`
1520 return self.offset - self.expl_tlen - self.expl_llen
1523 def expl_vlen(self):
1524 """See :ref:`decoding`
1529 def expl_tlvlen(self):
1530 """See :ref:`decoding`
1532 return self.expl_tlen + self.expl_llen + self.expl_vlen
1535 def fulloffset(self):
1536 """See :ref:`decoding`
1538 return self.expl_offset if self.expled else self.offset
1542 """See :ref:`decoding`
1544 return self.expl_tlvlen if self.expled else self.tlvlen
1546 def pps_lenindef(self, decode_path):
1547 if self.lenindef and not (
1548 getattr(self, "defined", None) is not None and
1549 self.defined[1].lenindef
1552 asn1_type_name="EOC",
1554 decode_path=decode_path,
1556 self.offset + self.tlvlen -
1557 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1565 if self.expl_lenindef:
1567 asn1_type_name="EOC",
1568 obj_name="EXPLICIT",
1569 decode_path=decode_path,
1570 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1579 class DecodePathDefBy(object):
1580 """DEFINED BY representation inside decode path
1582 __slots__ = ("defined_by",)
1584 def __init__(self, defined_by):
1585 self.defined_by = defined_by
1587 def __ne__(self, their):
1588 return not(self == their)
1590 def __eq__(self, their):
1591 if not isinstance(their, self.__class__):
1593 return self.defined_by == their.defined_by
1596 return "DEFINED BY " + str(self.defined_by)
1599 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1602 ########################################################################
1604 ########################################################################
1606 PP = namedtuple("PP", (
1629 ), **NAMEDTUPLE_KWARGS)
1634 asn1_type_name="unknown",
1651 expl_lenindef=False,
1682 def _colourize(what, colour, with_colours, attrs=("bold",)):
1683 return colored(what, colour, attrs=attrs) if with_colours else what
1686 def colonize_hex(hexed):
1687 """Separate hexadecimal string with colons
1689 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1698 with_decode_path=False,
1699 decode_path_len_decrease=0,
1706 " " if pp.expl_offset is None else
1707 ("-%d" % (pp.offset - pp.expl_offset))
1709 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1711 col = _colourize(col, "red", with_colours, ())
1712 col += _colourize("B", "red", with_colours) if pp.bered else " "
1714 col = "[%d,%d,%4d]%s" % (
1718 LENINDEF_PP_CHAR if pp.lenindef else " "
1720 col = _colourize(col, "green", with_colours, ())
1722 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1723 if decode_path_len > 0:
1724 cols.append(" ." * decode_path_len)
1725 ent = pp.decode_path[-1]
1726 if isinstance(ent, DecodePathDefBy):
1727 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1728 value = str(ent.defined_by)
1731 len(oid_maps) > 0 and
1732 ent.defined_by.asn1_type_name ==
1733 ObjectIdentifier.asn1_type_name
1735 for oid_map in oid_maps:
1736 oid_name = oid_map.get(value)
1737 if oid_name is not None:
1738 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1740 if oid_name is None:
1741 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1743 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1744 if pp.expl is not None:
1745 klass, _, num = pp.expl
1746 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1747 cols.append(_colourize(col, "blue", with_colours))
1748 if pp.impl is not None:
1749 klass, _, num = pp.impl
1750 col = "[%s%d]" % (TagClassReprs[klass], num)
1751 cols.append(_colourize(col, "blue", with_colours))
1752 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1753 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1755 cols.append(_colourize("BER", "red", with_colours))
1756 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1757 if pp.value is not None:
1759 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1761 len(oid_maps) > 0 and
1762 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1764 for oid_map in oid_maps:
1765 oid_name = oid_map.get(value)
1766 if oid_name is not None:
1767 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1769 if pp.asn1_type_name == Integer.asn1_type_name:
1770 hex_repr = hex(int(pp.obj._value))[2:].upper()
1771 if len(hex_repr) % 2 != 0:
1772 hex_repr = "0" + hex_repr
1773 cols.append(_colourize(
1774 "(%s)" % colonize_hex(hex_repr),
1779 if pp.blob.__class__ == binary_type:
1780 cols.append(hexenc(pp.blob))
1781 elif pp.blob.__class__ == tuple:
1782 cols.append(", ".join(pp.blob))
1784 cols.append(_colourize("OPTIONAL", "red", with_colours))
1786 cols.append(_colourize("DEFAULT", "red", with_colours))
1787 if with_decode_path:
1788 cols.append(_colourize(
1789 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1793 return " ".join(cols)
1796 def pp_console_blob(pp, decode_path_len_decrease=0):
1797 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1798 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1799 if decode_path_len > 0:
1800 cols.append(" ." * (decode_path_len + 1))
1801 if pp.blob.__class__ == binary_type:
1802 blob = hexenc(pp.blob).upper()
1803 for i in six_xrange(0, len(blob), 32):
1804 chunk = blob[i:i + 32]
1805 yield " ".join(cols + [colonize_hex(chunk)])
1806 elif pp.blob.__class__ == tuple:
1807 yield " ".join(cols + [", ".join(pp.blob)])
1815 with_decode_path=False,
1816 decode_path_only=(),
1818 """Pretty print object
1820 :param Obj obj: object you want to pretty print
1821 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionary.
1822 Its human readable form is printed when OID is met
1823 :param big_blobs: if large binary objects are met (like OctetString
1824 values), do we need to print them too, on separate
1826 :param with_colours: colourize output, if ``termcolor`` library
1828 :param with_decode_path: print decode path
1829 :param decode_path_only: print only that specified decode path
1831 def _pprint_pps(pps):
1833 if hasattr(pp, "_fields"):
1835 decode_path_only != () and
1837 str(p) for p in pp.decode_path[:len(decode_path_only)]
1838 ) != decode_path_only
1842 yield pp_console_row(
1847 with_colours=with_colours,
1848 with_decode_path=with_decode_path,
1849 decode_path_len_decrease=len(decode_path_only),
1851 for row in pp_console_blob(
1853 decode_path_len_decrease=len(decode_path_only),
1857 yield pp_console_row(
1862 with_colours=with_colours,
1863 with_decode_path=with_decode_path,
1864 decode_path_len_decrease=len(decode_path_only),
1867 for row in _pprint_pps(pp):
1869 return "\n".join(_pprint_pps(obj.pps()))
1872 ########################################################################
1873 # ASN.1 primitive types
1874 ########################################################################
1876 BooleanState = namedtuple(
1878 BasicState._fields + ("value",),
1884 """``BOOLEAN`` boolean type
1886 >>> b = Boolean(True)
1888 >>> b == Boolean(True)
1894 tag_default = tag_encode(1)
1895 asn1_type_name = "BOOLEAN"
1907 :param value: set the value. Either boolean type, or
1908 :py:class:`pyderasn.Boolean` object
1909 :param bytes impl: override default tag with ``IMPLICIT`` one
1910 :param bytes expl: override default tag with ``EXPLICIT`` one
1911 :param default: set default value. Type same as in ``value``
1912 :param bool optional: is object ``OPTIONAL`` in sequence
1914 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1915 self._value = None if value is None else self._value_sanitize(value)
1916 if default is not None:
1917 default = self._value_sanitize(default)
1918 self.default = self.__class__(
1924 self._value = default
1926 def _value_sanitize(self, value):
1927 if value.__class__ == bool:
1929 if issubclass(value.__class__, Boolean):
1931 raise InvalidValueType((self.__class__, bool))
1935 return self._value is not None
1937 def __getstate__(self):
1938 return BooleanState(
1953 def __setstate__(self, state):
1954 super(Boolean, self).__setstate__(state)
1955 self._value = state.value
1957 def __nonzero__(self):
1958 self._assert_ready()
1962 self._assert_ready()
1965 def __eq__(self, their):
1966 if their.__class__ == bool:
1967 return self._value == their
1968 if not issubclass(their.__class__, Boolean):
1971 self._value == their._value and
1972 self.tag == their.tag and
1973 self._expl == their._expl
1984 return self.__class__(
1986 impl=self.tag if impl is None else impl,
1987 expl=self._expl if expl is None else expl,
1988 default=self.default if default is None else default,
1989 optional=self.optional if optional is None else optional,
1993 self._assert_ready()
1997 (b"\xFF" if self._value else b"\x00"),
2000 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2002 t, _, lv = tag_strip(tlv)
2003 except DecodeError as err:
2004 raise err.__class__(
2006 klass=self.__class__,
2007 decode_path=decode_path,
2012 klass=self.__class__,
2013 decode_path=decode_path,
2019 l, _, v = len_decode(lv)
2020 except DecodeError as err:
2021 raise err.__class__(
2023 klass=self.__class__,
2024 decode_path=decode_path,
2028 raise InvalidLength(
2029 "Boolean's length must be equal to 1",
2030 klass=self.__class__,
2031 decode_path=decode_path,
2035 raise NotEnoughData(
2036 "encoded length is longer than data",
2037 klass=self.__class__,
2038 decode_path=decode_path,
2041 first_octet = byte2int(v)
2043 if first_octet == 0:
2045 elif first_octet == 0xFF:
2047 elif ctx.get("bered", False):
2052 "unacceptable Boolean value",
2053 klass=self.__class__,
2054 decode_path=decode_path,
2057 obj = self.__class__(
2061 default=self.default,
2062 optional=self.optional,
2063 _decoded=(offset, 1, 1),
2065 obj.ber_encoded = ber_encoded
2069 return pp_console_row(next(self.pps()))
2071 def pps(self, decode_path=()):
2074 asn1_type_name=self.asn1_type_name,
2075 obj_name=self.__class__.__name__,
2076 decode_path=decode_path,
2077 value=str(self._value) if self.ready else None,
2078 optional=self.optional,
2079 default=self == self.default,
2080 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2081 expl=None if self._expl is None else tag_decode(self._expl),
2086 expl_offset=self.expl_offset if self.expled else None,
2087 expl_tlen=self.expl_tlen if self.expled else None,
2088 expl_llen=self.expl_llen if self.expled else None,
2089 expl_vlen=self.expl_vlen if self.expled else None,
2090 expl_lenindef=self.expl_lenindef,
2091 ber_encoded=self.ber_encoded,
2094 for pp in self.pps_lenindef(decode_path):
2098 IntegerState = namedtuple(
2100 BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2106 """``INTEGER`` integer type
2108 >>> b = Integer(-123)
2110 >>> b == Integer(-123)
2115 >>> Integer(2, bounds=(1, 3))
2117 >>> Integer(5, bounds=(1, 3))
2118 Traceback (most recent call last):
2119 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2123 class Version(Integer):
2130 >>> v = Version("v1")
2137 {'v3': 2, 'v1': 0, 'v2': 1}
2139 __slots__ = ("specs", "_bound_min", "_bound_max")
2140 tag_default = tag_encode(2)
2141 asn1_type_name = "INTEGER"
2155 :param value: set the value. Either integer type, named value
2156 (if ``schema`` is specified in the class), or
2157 :py:class:`pyderasn.Integer` object
2158 :param bounds: set ``(MIN, MAX)`` value constraint.
2159 (-inf, +inf) by default
2160 :param bytes impl: override default tag with ``IMPLICIT`` one
2161 :param bytes expl: override default tag with ``EXPLICIT`` one
2162 :param default: set default value. Type same as in ``value``
2163 :param bool optional: is object ``OPTIONAL`` in sequence
2165 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
2167 specs = getattr(self, "schema", {}) if _specs is None else _specs
2168 self.specs = specs if specs.__class__ == dict else dict(specs)
2169 self._bound_min, self._bound_max = getattr(
2172 (float("-inf"), float("+inf")),
2173 ) if bounds is None else bounds
2174 if value is not None:
2175 self._value = self._value_sanitize(value)
2176 if default is not None:
2177 default = self._value_sanitize(default)
2178 self.default = self.__class__(
2184 if self._value is None:
2185 self._value = default
2187 def _value_sanitize(self, value):
2188 if isinstance(value, integer_types):
2190 elif issubclass(value.__class__, Integer):
2191 value = value._value
2192 elif value.__class__ == str:
2193 value = self.specs.get(value)
2195 raise ObjUnknown("integer value: %s" % value)
2197 raise InvalidValueType((self.__class__, int, str))
2198 if not self._bound_min <= value <= self._bound_max:
2199 raise BoundsError(self._bound_min, value, self._bound_max)
2204 return self._value is not None
2206 def __getstate__(self):
2207 return IntegerState(
2225 def __setstate__(self, state):
2226 super(Integer, self).__setstate__(state)
2227 self.specs = state.specs
2228 self._value = state.value
2229 self._bound_min = state.bound_min
2230 self._bound_max = state.bound_max
2233 self._assert_ready()
2234 return int(self._value)
2237 self._assert_ready()
2240 bytes(self._expl or b"") +
2241 str(self._value).encode("ascii"),
2244 def __eq__(self, their):
2245 if isinstance(their, integer_types):
2246 return self._value == their
2247 if not issubclass(their.__class__, Integer):
2250 self._value == their._value and
2251 self.tag == their.tag and
2252 self._expl == their._expl
2255 def __lt__(self, their):
2256 return self._value < their._value
2260 """Return named representation (if exists) of the value
2262 for name, value in iteritems(self.specs):
2263 if value == self._value:
2276 return self.__class__(
2279 (self._bound_min, self._bound_max)
2280 if bounds is None else bounds
2282 impl=self.tag if impl is None else impl,
2283 expl=self._expl if expl is None else expl,
2284 default=self.default if default is None else default,
2285 optional=self.optional if optional is None else optional,
2290 self._assert_ready()
2294 octets = bytearray([0])
2298 octets = bytearray()
2300 octets.append((value & 0xFF) ^ 0xFF)
2302 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2305 octets = bytearray()
2307 octets.append(value & 0xFF)
2309 if octets[-1] & 0x80 > 0:
2312 octets = bytes(octets)
2314 bytes_len = ceil(value.bit_length() / 8) or 1
2317 octets = value.to_bytes(
2322 except OverflowError:
2326 return b"".join((self.tag, len_encode(len(octets)), octets))
2328 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2330 t, _, lv = tag_strip(tlv)
2331 except DecodeError as err:
2332 raise err.__class__(
2334 klass=self.__class__,
2335 decode_path=decode_path,
2340 klass=self.__class__,
2341 decode_path=decode_path,
2347 l, llen, v = len_decode(lv)
2348 except DecodeError as err:
2349 raise err.__class__(
2351 klass=self.__class__,
2352 decode_path=decode_path,
2356 raise NotEnoughData(
2357 "encoded length is longer than data",
2358 klass=self.__class__,
2359 decode_path=decode_path,
2363 raise NotEnoughData(
2365 klass=self.__class__,
2366 decode_path=decode_path,
2369 v, tail = v[:l], v[l:]
2370 first_octet = byte2int(v)
2372 second_octet = byte2int(v[1:])
2374 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2375 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2378 "non normalized integer",
2379 klass=self.__class__,
2380 decode_path=decode_path,
2385 if first_octet & 0x80 > 0:
2386 octets = bytearray()
2387 for octet in bytearray(v):
2388 octets.append(octet ^ 0xFF)
2389 for octet in octets:
2390 value = (value << 8) | octet
2394 for octet in bytearray(v):
2395 value = (value << 8) | octet
2397 value = int.from_bytes(v, byteorder="big", signed=True)
2399 obj = self.__class__(
2401 bounds=(self._bound_min, self._bound_max),
2404 default=self.default,
2405 optional=self.optional,
2407 _decoded=(offset, llen, l),
2409 except BoundsError as err:
2412 klass=self.__class__,
2413 decode_path=decode_path,
2419 return pp_console_row(next(self.pps()))
2421 def pps(self, decode_path=()):
2424 asn1_type_name=self.asn1_type_name,
2425 obj_name=self.__class__.__name__,
2426 decode_path=decode_path,
2427 value=(self.named or str(self._value)) if self.ready else None,
2428 optional=self.optional,
2429 default=self == self.default,
2430 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2431 expl=None if self._expl is None else tag_decode(self._expl),
2436 expl_offset=self.expl_offset if self.expled else None,
2437 expl_tlen=self.expl_tlen if self.expled else None,
2438 expl_llen=self.expl_llen if self.expled else None,
2439 expl_vlen=self.expl_vlen if self.expled else None,
2440 expl_lenindef=self.expl_lenindef,
2443 for pp in self.pps_lenindef(decode_path):
2447 BitStringState = namedtuple(
2449 BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
2454 class BitString(Obj):
2455 """``BIT STRING`` bit string type
2457 >>> BitString(b"hello world")
2458 BIT STRING 88 bits 68656c6c6f20776f726c64
2461 >>> b == b"hello world"
2466 >>> BitString("'0A3B5F291CD'H")
2467 BIT STRING 44 bits 0a3b5f291cd0
2468 >>> b = BitString("'010110000000'B")
2469 BIT STRING 12 bits 5800
2472 >>> b[0], b[1], b[2], b[3]
2473 (False, True, False, True)
2477 [False, True, False, True, True, False, False, False, False, False, False, False]
2481 class KeyUsage(BitString):
2483 ("digitalSignature", 0),
2484 ("nonRepudiation", 1),
2485 ("keyEncipherment", 2),
2488 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2489 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2491 ['nonRepudiation', 'keyEncipherment']
2493 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2497 Pay attention that BIT STRING can be encoded both in primitive
2498 and constructed forms. Decoder always checks constructed form tag
2499 additionally to specified primitive one. If BER decoding is
2500 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2501 of DER restrictions.
2503 __slots__ = ("tag_constructed", "specs", "defined")
2504 tag_default = tag_encode(3)
2505 asn1_type_name = "BIT STRING"
2518 :param value: set the value. Either binary type, tuple of named
2519 values (if ``schema`` is specified in the class),
2520 string in ``'XXX...'B`` form, or
2521 :py:class:`pyderasn.BitString` object
2522 :param bytes impl: override default tag with ``IMPLICIT`` one
2523 :param bytes expl: override default tag with ``EXPLICIT`` one
2524 :param default: set default value. Type same as in ``value``
2525 :param bool optional: is object ``OPTIONAL`` in sequence
2527 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2528 specs = getattr(self, "schema", {}) if _specs is None else _specs
2529 self.specs = specs if specs.__class__ == dict else dict(specs)
2530 self._value = None if value is None else self._value_sanitize(value)
2531 if default is not None:
2532 default = self._value_sanitize(default)
2533 self.default = self.__class__(
2539 self._value = default
2541 tag_klass, _, tag_num = tag_decode(self.tag)
2542 self.tag_constructed = tag_encode(
2544 form=TagFormConstructed,
2548 def _bits2octets(self, bits):
2549 if len(self.specs) > 0:
2550 bits = bits.rstrip("0")
2552 bits += "0" * ((8 - (bit_len % 8)) % 8)
2553 octets = bytearray(len(bits) // 8)
2554 for i in six_xrange(len(octets)):
2555 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2556 return bit_len, bytes(octets)
2558 def _value_sanitize(self, value):
2559 if isinstance(value, (string_types, binary_type)):
2561 isinstance(value, string_types) and
2562 value.startswith("'")
2564 if value.endswith("'B"):
2566 if not frozenset(value) <= SET01:
2567 raise ValueError("B's coding contains unacceptable chars")
2568 return self._bits2octets(value)
2569 if value.endswith("'H"):
2573 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2575 if value.__class__ == binary_type:
2576 return (len(value) * 8, value)
2577 raise InvalidValueType((self.__class__, string_types, binary_type))
2578 if value.__class__ == tuple:
2581 isinstance(value[0], integer_types) and
2582 value[1].__class__ == binary_type
2587 bit = self.specs.get(name)
2589 raise ObjUnknown("BitString value: %s" % name)
2592 return self._bits2octets("")
2593 bits = frozenset(bits)
2594 return self._bits2octets("".join(
2595 ("1" if bit in bits else "0")
2596 for bit in six_xrange(max(bits) + 1)
2598 if issubclass(value.__class__, BitString):
2600 raise InvalidValueType((self.__class__, binary_type, string_types))
2604 return self._value is not None
2606 def __getstate__(self):
2607 return BitStringState(
2621 self.tag_constructed,
2625 def __setstate__(self, state):
2626 super(BitString, self).__setstate__(state)
2627 self.specs = state.specs
2628 self._value = state.value
2629 self.tag_constructed = state.tag_constructed
2630 self.defined = state.defined
2633 self._assert_ready()
2634 for i in six_xrange(self._value[0]):
2639 """Returns number of bits in the string
2641 self._assert_ready()
2642 return self._value[0]
2644 def __bytes__(self):
2645 self._assert_ready()
2646 return self._value[1]
2648 def __eq__(self, their):
2649 if their.__class__ == bytes:
2650 return self._value[1] == their
2651 if not issubclass(their.__class__, BitString):
2654 self._value == their._value and
2655 self.tag == their.tag and
2656 self._expl == their._expl
2661 """Named representation (if exists) of the bits
2663 :returns: [str(name), ...]
2665 return [name for name, bit in iteritems(self.specs) if self[bit]]
2675 return self.__class__(
2677 impl=self.tag if impl is None else impl,
2678 expl=self._expl if expl is None else expl,
2679 default=self.default if default is None else default,
2680 optional=self.optional if optional is None else optional,
2684 def __getitem__(self, key):
2685 if key.__class__ == int:
2686 bit_len, octets = self._value
2690 byte2int(memoryview(octets)[key // 8:]) >>
2693 if isinstance(key, string_types):
2694 value = self.specs.get(key)
2696 raise ObjUnknown("BitString value: %s" % key)
2698 raise InvalidValueType((int, str))
2701 self._assert_ready()
2702 bit_len, octets = self._value
2705 len_encode(len(octets) + 1),
2706 int2byte((8 - bit_len % 8) % 8),
2710 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2712 t, tlen, lv = tag_strip(tlv)
2713 except DecodeError as err:
2714 raise err.__class__(
2716 klass=self.__class__,
2717 decode_path=decode_path,
2721 if tag_only: # pragma: no cover
2724 l, llen, v = len_decode(lv)
2725 except DecodeError as err:
2726 raise err.__class__(
2728 klass=self.__class__,
2729 decode_path=decode_path,
2733 raise NotEnoughData(
2734 "encoded length is longer than data",
2735 klass=self.__class__,
2736 decode_path=decode_path,
2740 raise NotEnoughData(
2742 klass=self.__class__,
2743 decode_path=decode_path,
2746 pad_size = byte2int(v)
2747 if l == 1 and pad_size != 0:
2749 "invalid empty value",
2750 klass=self.__class__,
2751 decode_path=decode_path,
2757 klass=self.__class__,
2758 decode_path=decode_path,
2761 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2764 klass=self.__class__,
2765 decode_path=decode_path,
2768 v, tail = v[:l], v[l:]
2769 obj = self.__class__(
2770 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2773 default=self.default,
2774 optional=self.optional,
2776 _decoded=(offset, llen, l),
2779 if t != self.tag_constructed:
2781 klass=self.__class__,
2782 decode_path=decode_path,
2785 if not ctx.get("bered", False):
2787 "unallowed BER constructed encoding",
2788 klass=self.__class__,
2789 decode_path=decode_path,
2792 if tag_only: # pragma: no cover
2796 l, llen, v = len_decode(lv)
2797 except LenIndefForm:
2798 llen, l, v = 1, 0, lv[1:]
2800 except DecodeError as err:
2801 raise err.__class__(
2803 klass=self.__class__,
2804 decode_path=decode_path,
2808 raise NotEnoughData(
2809 "encoded length is longer than data",
2810 klass=self.__class__,
2811 decode_path=decode_path,
2814 if not lenindef and l == 0:
2815 raise NotEnoughData(
2817 klass=self.__class__,
2818 decode_path=decode_path,
2822 sub_offset = offset + tlen + llen
2826 if v[:EOC_LEN].tobytes() == EOC:
2833 "chunk out of bounds",
2834 klass=self.__class__,
2835 decode_path=decode_path + (str(len(chunks) - 1),),
2836 offset=chunks[-1].offset,
2838 sub_decode_path = decode_path + (str(len(chunks)),)
2840 chunk, v_tail = BitString().decode(
2843 decode_path=sub_decode_path,
2846 _ctx_immutable=False,
2850 "expected BitString encoded chunk",
2851 klass=self.__class__,
2852 decode_path=sub_decode_path,
2855 chunks.append(chunk)
2856 sub_offset += chunk.tlvlen
2857 vlen += chunk.tlvlen
2859 if len(chunks) == 0:
2862 klass=self.__class__,
2863 decode_path=decode_path,
2868 for chunk_i, chunk in enumerate(chunks[:-1]):
2869 if chunk.bit_len % 8 != 0:
2871 "BitString chunk is not multiple of 8 bits",
2872 klass=self.__class__,
2873 decode_path=decode_path + (str(chunk_i),),
2874 offset=chunk.offset,
2876 values.append(bytes(chunk))
2877 bit_len += chunk.bit_len
2878 chunk_last = chunks[-1]
2879 values.append(bytes(chunk_last))
2880 bit_len += chunk_last.bit_len
2881 obj = self.__class__(
2882 value=(bit_len, b"".join(values)),
2885 default=self.default,
2886 optional=self.optional,
2888 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2890 obj.lenindef = lenindef
2891 obj.ber_encoded = True
2892 return obj, (v[EOC_LEN:] if lenindef else v)
2895 return pp_console_row(next(self.pps()))
2897 def pps(self, decode_path=()):
2901 bit_len, blob = self._value
2902 value = "%d bits" % bit_len
2903 if len(self.specs) > 0:
2904 blob = tuple(self.named)
2907 asn1_type_name=self.asn1_type_name,
2908 obj_name=self.__class__.__name__,
2909 decode_path=decode_path,
2912 optional=self.optional,
2913 default=self == self.default,
2914 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2915 expl=None if self._expl is None else tag_decode(self._expl),
2920 expl_offset=self.expl_offset if self.expled else None,
2921 expl_tlen=self.expl_tlen if self.expled else None,
2922 expl_llen=self.expl_llen if self.expled else None,
2923 expl_vlen=self.expl_vlen if self.expled else None,
2924 expl_lenindef=self.expl_lenindef,
2925 lenindef=self.lenindef,
2926 ber_encoded=self.ber_encoded,
2929 defined_by, defined = self.defined or (None, None)
2930 if defined_by is not None:
2932 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2934 for pp in self.pps_lenindef(decode_path):
2938 OctetStringState = namedtuple(
2940 BasicState._fields + (
2951 class OctetString(Obj):
2952 """``OCTET STRING`` binary string type
2954 >>> s = OctetString(b"hello world")
2955 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2956 >>> s == OctetString(b"hello world")
2961 >>> OctetString(b"hello", bounds=(4, 4))
2962 Traceback (most recent call last):
2963 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2964 >>> OctetString(b"hell", bounds=(4, 4))
2965 OCTET STRING 4 bytes 68656c6c
2969 Pay attention that OCTET STRING can be encoded both in primitive
2970 and constructed forms. Decoder always checks constructed form tag
2971 additionally to specified primitive one. If BER decoding is
2972 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2973 of DER restrictions.
2975 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2976 tag_default = tag_encode(4)
2977 asn1_type_name = "OCTET STRING"
2991 :param value: set the value. Either binary type, or
2992 :py:class:`pyderasn.OctetString` object
2993 :param bounds: set ``(MIN, MAX)`` value size constraint.
2994 (-inf, +inf) by default
2995 :param bytes impl: override default tag with ``IMPLICIT`` one
2996 :param bytes expl: override default tag with ``EXPLICIT`` one
2997 :param default: set default value. Type same as in ``value``
2998 :param bool optional: is object ``OPTIONAL`` in sequence
3000 super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
3002 self._bound_min, self._bound_max = getattr(
3006 ) if bounds is None else bounds
3007 if value is not None:
3008 self._value = self._value_sanitize(value)
3009 if default is not None:
3010 default = self._value_sanitize(default)
3011 self.default = self.__class__(
3016 if self._value is None:
3017 self._value = default
3019 tag_klass, _, tag_num = tag_decode(self.tag)
3020 self.tag_constructed = tag_encode(
3022 form=TagFormConstructed,
3026 def _value_sanitize(self, value):
3027 if value.__class__ == binary_type:
3029 elif issubclass(value.__class__, OctetString):
3030 value = value._value
3032 raise InvalidValueType((self.__class__, bytes))
3033 if not self._bound_min <= len(value) <= self._bound_max:
3034 raise BoundsError(self._bound_min, len(value), self._bound_max)
3039 return self._value is not None
3041 def __getstate__(self):
3042 return OctetStringState(
3057 self.tag_constructed,
3061 def __setstate__(self, state):
3062 super(OctetString, self).__setstate__(state)
3063 self._value = state.value
3064 self._bound_min = state.bound_min
3065 self._bound_max = state.bound_max
3066 self.tag_constructed = state.tag_constructed
3067 self.defined = state.defined
3069 def __bytes__(self):
3070 self._assert_ready()
3073 def __eq__(self, their):
3074 if their.__class__ == binary_type:
3075 return self._value == their
3076 if not issubclass(their.__class__, OctetString):
3079 self._value == their._value and
3080 self.tag == their.tag and
3081 self._expl == their._expl
3084 def __lt__(self, their):
3085 return self._value < their._value
3096 return self.__class__(
3099 (self._bound_min, self._bound_max)
3100 if bounds is None else bounds
3102 impl=self.tag if impl is None else impl,
3103 expl=self._expl if expl is None else expl,
3104 default=self.default if default is None else default,
3105 optional=self.optional if optional is None else optional,
3109 self._assert_ready()
3112 len_encode(len(self._value)),
3116 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3118 t, tlen, lv = tag_strip(tlv)
3119 except DecodeError as err:
3120 raise err.__class__(
3122 klass=self.__class__,
3123 decode_path=decode_path,
3130 l, llen, v = len_decode(lv)
3131 except DecodeError as err:
3132 raise err.__class__(
3134 klass=self.__class__,
3135 decode_path=decode_path,
3139 raise NotEnoughData(
3140 "encoded length is longer than data",
3141 klass=self.__class__,
3142 decode_path=decode_path,
3145 v, tail = v[:l], v[l:]
3147 obj = self.__class__(
3149 bounds=(self._bound_min, self._bound_max),
3152 default=self.default,
3153 optional=self.optional,
3154 _decoded=(offset, llen, l),
3157 except DecodeError as err:
3160 klass=self.__class__,
3161 decode_path=decode_path,
3164 except BoundsError as err:
3167 klass=self.__class__,
3168 decode_path=decode_path,
3172 if t != self.tag_constructed:
3174 klass=self.__class__,
3175 decode_path=decode_path,
3178 if not ctx.get("bered", False):
3180 "unallowed BER constructed encoding",
3181 klass=self.__class__,
3182 decode_path=decode_path,
3189 l, llen, v = len_decode(lv)
3190 except LenIndefForm:
3191 llen, l, v = 1, 0, lv[1:]
3193 except DecodeError as err:
3194 raise err.__class__(
3196 klass=self.__class__,
3197 decode_path=decode_path,
3201 raise NotEnoughData(
3202 "encoded length is longer than data",
3203 klass=self.__class__,
3204 decode_path=decode_path,
3208 sub_offset = offset + tlen + llen
3212 if v[:EOC_LEN].tobytes() == EOC:
3219 "chunk out of bounds",
3220 klass=self.__class__,
3221 decode_path=decode_path + (str(len(chunks) - 1),),
3222 offset=chunks[-1].offset,
3224 sub_decode_path = decode_path + (str(len(chunks)),)
3226 chunk, v_tail = OctetString().decode(
3229 decode_path=sub_decode_path,
3232 _ctx_immutable=False,
3236 "expected OctetString encoded chunk",
3237 klass=self.__class__,
3238 decode_path=sub_decode_path,
3241 chunks.append(chunk)
3242 sub_offset += chunk.tlvlen
3243 vlen += chunk.tlvlen
3246 obj = self.__class__(
3247 value=b"".join(bytes(chunk) for chunk in chunks),
3248 bounds=(self._bound_min, self._bound_max),
3251 default=self.default,
3252 optional=self.optional,
3253 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3256 except DecodeError as err:
3259 klass=self.__class__,
3260 decode_path=decode_path,
3263 except BoundsError as err:
3266 klass=self.__class__,
3267 decode_path=decode_path,
3270 obj.lenindef = lenindef
3271 obj.ber_encoded = True
3272 return obj, (v[EOC_LEN:] if lenindef else v)
3275 return pp_console_row(next(self.pps()))
3277 def pps(self, decode_path=()):
3280 asn1_type_name=self.asn1_type_name,
3281 obj_name=self.__class__.__name__,
3282 decode_path=decode_path,
3283 value=("%d bytes" % len(self._value)) if self.ready else None,
3284 blob=self._value if self.ready else None,
3285 optional=self.optional,
3286 default=self == self.default,
3287 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3288 expl=None if self._expl is None else tag_decode(self._expl),
3293 expl_offset=self.expl_offset if self.expled else None,
3294 expl_tlen=self.expl_tlen if self.expled else None,
3295 expl_llen=self.expl_llen if self.expled else None,
3296 expl_vlen=self.expl_vlen if self.expled else None,
3297 expl_lenindef=self.expl_lenindef,
3298 lenindef=self.lenindef,
3299 ber_encoded=self.ber_encoded,
3302 defined_by, defined = self.defined or (None, None)
3303 if defined_by is not None:
3305 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3307 for pp in self.pps_lenindef(decode_path):
3311 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
3315 """``NULL`` null object
3323 tag_default = tag_encode(5)
3324 asn1_type_name = "NULL"
3328 value=None, # unused, but Sequence passes it
3335 :param bytes impl: override default tag with ``IMPLICIT`` one
3336 :param bytes expl: override default tag with ``EXPLICIT`` one
3337 :param bool optional: is object ``OPTIONAL`` in sequence
3339 super(Null, self).__init__(impl, expl, None, optional, _decoded)
3346 def __getstate__(self):
3361 def __eq__(self, their):
3362 if not issubclass(their.__class__, Null):
3365 self.tag == their.tag and
3366 self._expl == their._expl
3376 return self.__class__(
3377 impl=self.tag if impl is None else impl,
3378 expl=self._expl if expl is None else expl,
3379 optional=self.optional if optional is None else optional,
3383 return self.tag + len_encode(0)
3385 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3387 t, _, lv = tag_strip(tlv)
3388 except DecodeError as err:
3389 raise err.__class__(
3391 klass=self.__class__,
3392 decode_path=decode_path,
3397 klass=self.__class__,
3398 decode_path=decode_path,
3401 if tag_only: # pragma: no cover
3404 l, _, v = len_decode(lv)
3405 except DecodeError as err:
3406 raise err.__class__(
3408 klass=self.__class__,
3409 decode_path=decode_path,
3413 raise InvalidLength(
3414 "Null must have zero length",
3415 klass=self.__class__,
3416 decode_path=decode_path,
3419 obj = self.__class__(
3422 optional=self.optional,
3423 _decoded=(offset, 1, 0),
3428 return pp_console_row(next(self.pps()))
3430 def pps(self, decode_path=()):
3433 asn1_type_name=self.asn1_type_name,
3434 obj_name=self.__class__.__name__,
3435 decode_path=decode_path,
3436 optional=self.optional,
3437 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3438 expl=None if self._expl is None else tag_decode(self._expl),
3443 expl_offset=self.expl_offset if self.expled else None,
3444 expl_tlen=self.expl_tlen if self.expled else None,
3445 expl_llen=self.expl_llen if self.expled else None,
3446 expl_vlen=self.expl_vlen if self.expled else None,
3447 expl_lenindef=self.expl_lenindef,
3450 for pp in self.pps_lenindef(decode_path):
3454 ObjectIdentifierState = namedtuple(
3455 "ObjectIdentifierState",
3456 BasicState._fields + ("value", "defines"),
3461 class ObjectIdentifier(Obj):
3462 """``OBJECT IDENTIFIER`` OID type
3464 >>> oid = ObjectIdentifier((1, 2, 3))
3465 OBJECT IDENTIFIER 1.2.3
3466 >>> oid == ObjectIdentifier("1.2.3")
3472 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3473 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3475 >>> str(ObjectIdentifier((3, 1)))
3476 Traceback (most recent call last):
3477 pyderasn.InvalidOID: unacceptable first arc value
3479 __slots__ = ("defines",)
3480 tag_default = tag_encode(6)
3481 asn1_type_name = "OBJECT IDENTIFIER"
3494 :param value: set the value. Either tuples of integers,
3495 string of "."-concatenated integers, or
3496 :py:class:`pyderasn.ObjectIdentifier` object
3497 :param defines: sequence of tuples. Each tuple has two elements.
3498 First one is relative to current one decode
3499 path, aiming to the field defined by that OID.
3500 Read about relative path in
3501 :py:func:`pyderasn.abs_decode_path`. Second
3502 tuple element is ``{OID: pyderasn.Obj()}``
3503 dictionary, mapping between current OID value
3504 and structure applied to defined field.
3505 :ref:`Read about DEFINED BY <definedby>`
3506 :param bytes impl: override default tag with ``IMPLICIT`` one
3507 :param bytes expl: override default tag with ``EXPLICIT`` one
3508 :param default: set default value. Type same as in ``value``
3509 :param bool optional: is object ``OPTIONAL`` in sequence
3511 super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
3513 if value is not None:
3514 self._value = self._value_sanitize(value)
3515 if default is not None:
3516 default = self._value_sanitize(default)
3517 self.default = self.__class__(
3522 if self._value is None:
3523 self._value = default
3524 self.defines = defines
3526 def __add__(self, their):
3527 if their.__class__ == tuple:
3528 return self.__class__(self._value + their)
3529 if isinstance(their, self.__class__):
3530 return self.__class__(self._value + their._value)
3531 raise InvalidValueType((self.__class__, tuple))
3533 def _value_sanitize(self, value):
3534 if issubclass(value.__class__, ObjectIdentifier):
3536 if isinstance(value, string_types):
3538 value = tuple(pureint(arc) for arc in value.split("."))
3540 raise InvalidOID("unacceptable arcs values")
3541 if value.__class__ == tuple:
3543 raise InvalidOID("less than 2 arcs")
3544 first_arc = value[0]
3545 if first_arc in (0, 1):
3546 if not (0 <= value[1] <= 39):
3547 raise InvalidOID("second arc is too wide")
3548 elif first_arc == 2:
3551 raise InvalidOID("unacceptable first arc value")
3552 if not all(arc >= 0 for arc in value):
3553 raise InvalidOID("negative arc value")
3555 raise InvalidValueType((self.__class__, str, tuple))
3559 return self._value is not None
3561 def __getstate__(self):
3562 return ObjectIdentifierState(
3578 def __setstate__(self, state):
3579 super(ObjectIdentifier, self).__setstate__(state)
3580 self._value = state.value
3581 self.defines = state.defines
3584 self._assert_ready()
3585 return iter(self._value)
3588 return ".".join(str(arc) for arc in self._value or ())
3591 self._assert_ready()
3594 bytes(self._expl or b"") +
3595 str(self._value).encode("ascii"),
3598 def __eq__(self, their):
3599 if their.__class__ == tuple:
3600 return self._value == their
3601 if not issubclass(their.__class__, ObjectIdentifier):
3604 self.tag == their.tag and
3605 self._expl == their._expl and
3606 self._value == their._value
3609 def __lt__(self, their):
3610 return self._value < their._value
3621 return self.__class__(
3623 defines=self.defines if defines is None else defines,
3624 impl=self.tag if impl is None else impl,
3625 expl=self._expl if expl is None else expl,
3626 default=self.default if default is None else default,
3627 optional=self.optional if optional is None else optional,
3631 self._assert_ready()
3633 first_value = value[1]
3634 first_arc = value[0]
3637 elif first_arc == 1:
3639 elif first_arc == 2:
3641 else: # pragma: no cover
3642 raise RuntimeError("invalid arc is stored")
3643 octets = [zero_ended_encode(first_value)]
3644 for arc in value[2:]:
3645 octets.append(zero_ended_encode(arc))
3646 v = b"".join(octets)
3647 return b"".join((self.tag, len_encode(len(v)), v))
3649 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3651 t, _, lv = tag_strip(tlv)
3652 except DecodeError as err:
3653 raise err.__class__(
3655 klass=self.__class__,
3656 decode_path=decode_path,
3661 klass=self.__class__,
3662 decode_path=decode_path,
3665 if tag_only: # pragma: no cover
3668 l, llen, v = len_decode(lv)
3669 except DecodeError as err:
3670 raise err.__class__(
3672 klass=self.__class__,
3673 decode_path=decode_path,
3677 raise NotEnoughData(
3678 "encoded length is longer than data",
3679 klass=self.__class__,
3680 decode_path=decode_path,
3684 raise NotEnoughData(
3686 klass=self.__class__,
3687 decode_path=decode_path,
3690 v, tail = v[:l], v[l:]
3697 octet = indexbytes(v, i)
3698 if i == 0 and octet == 0x80:
3699 if ctx.get("bered", False):
3702 raise DecodeError("non normalized arc encoding")
3703 arc = (arc << 7) | (octet & 0x7F)
3704 if octet & 0x80 == 0:
3712 klass=self.__class__,
3713 decode_path=decode_path,
3717 second_arc = arcs[0]
3718 if 0 <= second_arc <= 39:
3720 elif 40 <= second_arc <= 79:
3726 obj = self.__class__(
3727 value=tuple([first_arc, second_arc] + arcs[1:]),
3730 default=self.default,
3731 optional=self.optional,
3732 _decoded=(offset, llen, l),
3735 obj.ber_encoded = True
3739 return pp_console_row(next(self.pps()))
3741 def pps(self, decode_path=()):
3744 asn1_type_name=self.asn1_type_name,
3745 obj_name=self.__class__.__name__,
3746 decode_path=decode_path,
3747 value=str(self) if self.ready else None,
3748 optional=self.optional,
3749 default=self == self.default,
3750 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3751 expl=None if self._expl is None else tag_decode(self._expl),
3756 expl_offset=self.expl_offset if self.expled else None,
3757 expl_tlen=self.expl_tlen if self.expled else None,
3758 expl_llen=self.expl_llen if self.expled else None,
3759 expl_vlen=self.expl_vlen if self.expled else None,
3760 expl_lenindef=self.expl_lenindef,
3761 ber_encoded=self.ber_encoded,
3764 for pp in self.pps_lenindef(decode_path):
3768 class Enumerated(Integer):
3769 """``ENUMERATED`` integer type
3771 This type is identical to :py:class:`pyderasn.Integer`, but requires
3772 schema to be specified and does not accept values missing from it.
3775 tag_default = tag_encode(10)
3776 asn1_type_name = "ENUMERATED"
3787 bounds=None, # dummy argument, workability for Integer.decode
3789 super(Enumerated, self).__init__(
3790 value, bounds, impl, expl, default, optional, _specs, _decoded,
3792 if len(self.specs) == 0:
3793 raise ValueError("schema must be specified")
3795 def _value_sanitize(self, value):
3796 if isinstance(value, self.__class__):
3797 value = value._value
3798 elif isinstance(value, integer_types):
3799 for _value in itervalues(self.specs):
3804 "unknown integer value: %s" % value,
3805 klass=self.__class__,
3807 elif isinstance(value, string_types):
3808 value = self.specs.get(value)
3810 raise ObjUnknown("integer value: %s" % value)
3812 raise InvalidValueType((self.__class__, int, str))
3824 return self.__class__(
3826 impl=self.tag if impl is None else impl,
3827 expl=self._expl if expl is None else expl,
3828 default=self.default if default is None else default,
3829 optional=self.optional if optional is None else optional,
3834 def escape_control_unicode(c):
3835 if unicat(c)[0] == "C":
3836 c = repr(c).lstrip("u").strip("'")
3840 class CommonString(OctetString):
3841 """Common class for all strings
3843 Everything resembles :py:class:`pyderasn.OctetString`, except
3844 ability to deal with unicode text strings.
3846 >>> hexenc("привет мир".encode("utf-8"))
3847 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3848 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3850 >>> s = UTF8String("привет мир")
3851 UTF8String UTF8String привет мир
3853 'привет мир'
3854 >>> hexenc(bytes(s))
3855 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3857 >>> PrintableString("привет мир")
3858 Traceback (most recent call last):
3859 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3861 >>> BMPString("ада", bounds=(2, 2))
3862 Traceback (most recent call last):
3863 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3864 >>> s = BMPString("ад", bounds=(2, 2))
3867 >>> hexenc(bytes(s))
3875 * - :py:class:`pyderasn.UTF8String`
3877 * - :py:class:`pyderasn.NumericString`
3879 * - :py:class:`pyderasn.PrintableString`
3881 * - :py:class:`pyderasn.TeletexString`
3883 * - :py:class:`pyderasn.T61String`
3885 * - :py:class:`pyderasn.VideotexString`
3887 * - :py:class:`pyderasn.IA5String`
3889 * - :py:class:`pyderasn.GraphicString`
3891 * - :py:class:`pyderasn.VisibleString`
3893 * - :py:class:`pyderasn.ISO646String`
3895 * - :py:class:`pyderasn.GeneralString`
3897 * - :py:class:`pyderasn.UniversalString`
3899 * - :py:class:`pyderasn.BMPString`
3904 def _value_sanitize(self, value):
3906 value_decoded = None
3907 if isinstance(value, self.__class__):
3908 value_raw = value._value
3909 elif value.__class__ == text_type:
3910 value_decoded = value
3911 elif value.__class__ == binary_type:
3914 raise InvalidValueType((self.__class__, text_type, binary_type))
3917 value_decoded.encode(self.encoding)
3918 if value_raw is None else value_raw
3921 value_raw.decode(self.encoding)
3922 if value_decoded is None else value_decoded
3924 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3925 raise DecodeError(str(err))
3926 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3934 def __eq__(self, their):
3935 if their.__class__ == binary_type:
3936 return self._value == their
3937 if their.__class__ == text_type:
3938 return self._value == their.encode(self.encoding)
3939 if not isinstance(their, self.__class__):
3942 self._value == their._value and
3943 self.tag == their.tag and
3944 self._expl == their._expl
3947 def __unicode__(self):
3949 return self._value.decode(self.encoding)
3950 return text_type(self._value)
3953 return pp_console_row(next(self.pps(no_unicode=PY2)))
3955 def pps(self, decode_path=(), no_unicode=False):
3959 hexenc(bytes(self)) if no_unicode else
3960 "".join(escape_control_unicode(c) for c in self.__unicode__())
3964 asn1_type_name=self.asn1_type_name,
3965 obj_name=self.__class__.__name__,
3966 decode_path=decode_path,
3968 optional=self.optional,
3969 default=self == self.default,
3970 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3971 expl=None if self._expl is None else tag_decode(self._expl),
3976 expl_offset=self.expl_offset if self.expled else None,
3977 expl_tlen=self.expl_tlen if self.expled else None,
3978 expl_llen=self.expl_llen if self.expled else None,
3979 expl_vlen=self.expl_vlen if self.expled else None,
3980 expl_lenindef=self.expl_lenindef,
3981 ber_encoded=self.ber_encoded,
3984 for pp in self.pps_lenindef(decode_path):
3988 class UTF8String(CommonString):
3990 tag_default = tag_encode(12)
3992 asn1_type_name = "UTF8String"
3995 class AllowableCharsMixin(object):
3997 def allowable_chars(self):
3999 return self._allowable_chars
4000 return frozenset(six_unichr(c) for c in self._allowable_chars)
4003 class NumericString(AllowableCharsMixin, CommonString):
4006 Its value is properly sanitized: only ASCII digits with spaces can
4009 >>> NumericString().allowable_chars
4010 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4013 tag_default = tag_encode(18)
4015 asn1_type_name = "NumericString"
4016 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
4018 def _value_sanitize(self, value):
4019 value = super(NumericString, self)._value_sanitize(value)
4020 if not frozenset(value) <= self._allowable_chars:
4021 raise DecodeError("non-numeric value")
4025 PrintableStringState = namedtuple(
4026 "PrintableStringState",
4027 OctetStringState._fields + ("allowable_chars",),
4032 class PrintableString(AllowableCharsMixin, CommonString):
4035 Its value is properly sanitized: see X.680 41.4 table 10.
4037 >>> PrintableString().allowable_chars
4038 frozenset([' ', "'", ..., 'z'])
4039 >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4040 PrintableString PrintableString foo*bar
4041 >>> obj.allow_asterisk, obj.allow_ampersand
4045 tag_default = tag_encode(19)
4047 asn1_type_name = "PrintableString"
4048 _allowable_chars = frozenset(
4049 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4051 _asterisk = frozenset("*".encode("ascii"))
4052 _ampersand = frozenset("&".encode("ascii"))
4064 allow_asterisk=False,
4065 allow_ampersand=False,
4068 :param allow_asterisk: allow asterisk character
4069 :param allow_ampersand: allow ampersand character
4072 self._allowable_chars |= self._asterisk
4074 self._allowable_chars |= self._ampersand
4075 super(PrintableString, self).__init__(
4076 value, bounds, impl, expl, default, optional, _decoded, ctx,
4080 def allow_asterisk(self):
4081 """Is asterisk character allowed?
4083 return self._asterisk <= self._allowable_chars
4086 def allow_ampersand(self):
4087 """Is ampersand character allowed?
4089 return self._ampersand <= self._allowable_chars
4091 def _value_sanitize(self, value):
4092 value = super(PrintableString, self)._value_sanitize(value)
4093 if not frozenset(value) <= self._allowable_chars:
4094 raise DecodeError("non-printable value")
4097 def __getstate__(self):
4098 return PrintableStringState(
4099 *super(PrintableString, self).__getstate__(),
4100 **{"allowable_chars": self._allowable_chars}
4103 def __setstate__(self, state):
4104 super(PrintableString, self).__setstate__(state)
4105 self._allowable_chars = state.allowable_chars
4116 return self.__class__(
4119 (self._bound_min, self._bound_max)
4120 if bounds is None else bounds
4122 impl=self.tag if impl is None else impl,
4123 expl=self._expl if expl is None else expl,
4124 default=self.default if default is None else default,
4125 optional=self.optional if optional is None else optional,
4126 allow_asterisk=self.allow_asterisk,
4127 allow_ampersand=self.allow_ampersand,
4131 class TeletexString(CommonString):
4133 tag_default = tag_encode(20)
4135 asn1_type_name = "TeletexString"
4138 class T61String(TeletexString):
4140 asn1_type_name = "T61String"
4143 class VideotexString(CommonString):
4145 tag_default = tag_encode(21)
4146 encoding = "iso-8859-1"
4147 asn1_type_name = "VideotexString"
4150 class IA5String(CommonString):
4152 tag_default = tag_encode(22)
4154 asn1_type_name = "IA5"
4157 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4158 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4159 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4162 class VisibleString(CommonString):
4164 tag_default = tag_encode(26)
4166 asn1_type_name = "VisibleString"
4169 UTCTimeState = namedtuple(
4171 OctetStringState._fields + ("ber_raw",),
4176 def str_to_time_fractions(value):
4178 year, v = (v // 10**10), (v % 10**10)
4179 month, v = (v // 10**8), (v % 10**8)
4180 day, v = (v // 10**6), (v % 10**6)
4181 hour, v = (v // 10**4), (v % 10**4)
4182 minute, second = (v // 100), (v % 100)
4183 return year, month, day, hour, minute, second
4186 class UTCTime(VisibleString):
4187 """``UTCTime`` datetime type
4189 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4190 UTCTime UTCTime 2017-09-30T22:07:50
4196 datetime.datetime(2017, 9, 30, 22, 7, 50)
4197 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
4198 datetime.datetime(1957, 9, 30, 22, 7, 50)
4200 If BER encoded value was met, then ``ber_raw`` attribute will hold
4201 its raw representation.
4205 Pay attention that UTCTime can not hold full year, so all years
4206 having < 50 years are treated as 20xx, 19xx otherwise, according
4207 to X.509 recommendation.
4211 No strict validation of UTC offsets are made, but very crude:
4213 * minutes are not exceeding 60
4214 * offset value is not exceeding 14 hours
4216 __slots__ = ("ber_raw",)
4217 tag_default = tag_encode(23)
4219 asn1_type_name = "UTCTime"
4229 bounds=None, # dummy argument, workability for OctetString.decode
4233 :param value: set the value. Either datetime type, or
4234 :py:class:`pyderasn.UTCTime` object
4235 :param bytes impl: override default tag with ``IMPLICIT`` one
4236 :param bytes expl: override default tag with ``EXPLICIT`` one
4237 :param default: set default value. Type same as in ``value``
4238 :param bool optional: is object ``OPTIONAL`` in sequence
4240 super(UTCTime, self).__init__(
4241 None, None, impl, expl, None, optional, _decoded, ctx,
4245 if value is not None:
4246 self._value, self.ber_raw = self._value_sanitize(value, ctx)
4247 self.ber_encoded = self.ber_raw is not None
4248 if default is not None:
4249 default, _ = self._value_sanitize(default)
4250 self.default = self.__class__(
4255 if self._value is None:
4256 self._value = default
4258 self.optional = optional
4260 def _strptime_bered(self, value):
4261 year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
4264 raise ValueError("no timezone")
4265 year += 2000 if year < 50 else 1900
4266 decoded = datetime(year, month, day, hour, minute)
4268 if value[-1] == "Z":
4272 raise ValueError("invalid UTC offset")
4273 if value[-5] == "-":
4275 elif value[-5] == "+":
4278 raise ValueError("invalid UTC offset")
4279 v = pureint(value[-4:])
4280 offset, v = (60 * (v % 100)), v // 100
4282 raise ValueError("invalid UTC offset minutes")
4284 if offset > 14 * 3600:
4285 raise ValueError("too big UTC offset")
4289 return offset, decoded
4291 raise ValueError("invalid UTC offset seconds")
4292 seconds = pureint(value)
4294 raise ValueError("invalid seconds value")
4295 return offset, decoded + timedelta(seconds=seconds)
4297 def _strptime(self, value):
4298 # datetime.strptime's format: %y%m%d%H%M%SZ
4299 if len(value) != LEN_YYMMDDHHMMSSZ:
4300 raise ValueError("invalid UTCTime length")
4301 if value[-1] != "Z":
4302 raise ValueError("non UTC timezone")
4303 year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
4304 year += 2000 if year < 50 else 1900
4305 return datetime(year, month, day, hour, minute, second)
4307 def _dt_sanitize(self, value):
4308 if value.year < 1950 or value.year > 2049:
4309 raise ValueError("UTCTime can hold only 1950-2049 years")
4310 return value.replace(microsecond=0)
4312 def _value_sanitize(self, value, ctx=None):
4313 if value.__class__ == binary_type:
4315 value_decoded = value.decode("ascii")
4316 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4317 raise DecodeError("invalid UTCTime encoding: %r" % err)
4320 return self._strptime(value_decoded), None
4321 except (TypeError, ValueError) as _err:
4323 if (ctx is not None) and ctx.get("bered", False):
4325 offset, _value = self._strptime_bered(value_decoded)
4326 _value = _value - timedelta(seconds=offset)
4327 return self._dt_sanitize(_value), value
4328 except (TypeError, ValueError, OverflowError) as _err:
4331 "invalid %s format: %r" % (self.asn1_type_name, err),
4332 klass=self.__class__,
4334 if isinstance(value, self.__class__):
4335 return value._value, None
4336 if value.__class__ == datetime:
4337 return self._dt_sanitize(value), None
4338 raise InvalidValueType((self.__class__, datetime))
4340 def _pp_value(self):
4342 value = self._value.isoformat()
4343 if self.ber_encoded:
4344 value += " (%s)" % self.ber_raw
4347 def __unicode__(self):
4349 value = self._value.isoformat()
4350 if self.ber_encoded:
4351 value += " (%s)" % self.ber_raw
4353 return text_type(self._pp_value())
4355 def __getstate__(self):
4356 return UTCTimeState(
4357 *super(UTCTime, self).__getstate__(),
4358 **{"ber_raw": self.ber_raw}
4361 def __setstate__(self, state):
4362 super(UTCTime, self).__setstate__(state)
4363 self.ber_raw = state.ber_raw
4365 def __bytes__(self):
4366 self._assert_ready()
4367 return self._encode_time()
4369 def __eq__(self, their):
4370 if their.__class__ == binary_type:
4371 return self._encode_time() == their
4372 if their.__class__ == datetime:
4373 return self.todatetime() == their
4374 if not isinstance(their, self.__class__):
4377 self._value == their._value and
4378 self.tag == their.tag and
4379 self._expl == their._expl
4382 def _encode_time(self):
4383 return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
4386 self._assert_ready()
4387 value = self._encode_time()
4388 return b"".join((self.tag, len_encode(len(value)), value))
4390 def todatetime(self):
4394 return pp_console_row(next(self.pps()))
4396 def pps(self, decode_path=()):
4399 asn1_type_name=self.asn1_type_name,
4400 obj_name=self.__class__.__name__,
4401 decode_path=decode_path,
4402 value=self._pp_value(),
4403 optional=self.optional,
4404 default=self == self.default,
4405 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4406 expl=None if self._expl is None else tag_decode(self._expl),
4411 expl_offset=self.expl_offset if self.expled else None,
4412 expl_tlen=self.expl_tlen if self.expled else None,
4413 expl_llen=self.expl_llen if self.expled else None,
4414 expl_vlen=self.expl_vlen if self.expled else None,
4415 expl_lenindef=self.expl_lenindef,
4416 ber_encoded=self.ber_encoded,
4419 for pp in self.pps_lenindef(decode_path):
4423 class GeneralizedTime(UTCTime):
4424 """``GeneralizedTime`` datetime type
4426 This type is similar to :py:class:`pyderasn.UTCTime`.
4428 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4429 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
4431 '20170930220750.000123Z'
4432 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
4433 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
4437 Only microsecond fractions are supported in DER encoding.
4438 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
4439 higher precision values.
4443 BER encoded data can loss information (accuracy) during decoding
4444 because of float transformations.
4448 Local times (without explicit timezone specification) are treated
4449 as UTC one, no transformations are made.
4453 Zero year is unsupported.
4456 tag_default = tag_encode(24)
4457 asn1_type_name = "GeneralizedTime"
4459 def _dt_sanitize(self, value):
4462 def _strptime_bered(self, value):
4463 if len(value) < 4 + 3 * 2:
4464 raise ValueError("invalid GeneralizedTime")
4465 year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
4466 decoded = datetime(year, month, day, hour)
4467 offset, value = 0, value[10:]
4469 return offset, decoded
4470 if value[-1] == "Z":
4473 for char, sign in (("-", -1), ("+", 1)):
4474 idx = value.rfind(char)
4477 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
4478 v = pureint(offset_raw)
4479 if len(offset_raw) == 4:
4480 offset, v = (60 * (v % 100)), v // 100
4482 raise ValueError("invalid UTC offset minutes")
4483 elif len(offset_raw) == 2:
4486 raise ValueError("invalid UTC offset")
4488 if offset > 14 * 3600:
4489 raise ValueError("too big UTC offset")
4493 return offset, decoded
4494 if value[0] in DECIMAL_SIGNS:
4496 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
4499 raise ValueError("stripped minutes")
4500 decoded += timedelta(seconds=60 * pureint(value[:2]))
4503 return offset, decoded
4504 if value[0] in DECIMAL_SIGNS:
4506 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
4509 raise ValueError("stripped seconds")
4510 decoded += timedelta(seconds=pureint(value[:2]))
4513 return offset, decoded
4514 if value[0] not in DECIMAL_SIGNS:
4515 raise ValueError("invalid format after seconds")
4517 decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
4520 def _strptime(self, value):
4522 if l == LEN_YYYYMMDDHHMMSSZ:
4523 # datetime.strptime's format: %Y%m%d%H%M%SZ
4524 if value[-1] != "Z":
4525 raise ValueError("non UTC timezone")
4526 return datetime(*str_to_time_fractions(value[:-1]))
4527 if l >= LEN_YYYYMMDDHHMMSSDMZ:
4528 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4529 if value[-1] != "Z":
4530 raise ValueError("non UTC timezone")
4531 if value[14] != ".":
4532 raise ValueError("no fractions separator")
4535 raise ValueError("trailing zero")
4538 raise ValueError("only microsecond fractions are supported")
4539 us = pureint(us + ("0" * (6 - us_len)))
4540 year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
4541 return datetime(year, month, day, hour, minute, second, us)
4542 raise ValueError("invalid GeneralizedTime length")
4544 def _encode_time(self):
4546 encoded = value.strftime("%Y%m%d%H%M%S")
4547 if value.microsecond > 0:
4548 encoded += (".%06d" % value.microsecond).rstrip("0")
4549 return (encoded + "Z").encode("ascii")
4552 class GraphicString(CommonString):
4554 tag_default = tag_encode(25)
4555 encoding = "iso-8859-1"
4556 asn1_type_name = "GraphicString"
4559 class ISO646String(VisibleString):
4561 asn1_type_name = "ISO646String"
4564 class GeneralString(CommonString):
4566 tag_default = tag_encode(27)
4567 encoding = "iso-8859-1"
4568 asn1_type_name = "GeneralString"
4571 class UniversalString(CommonString):
4573 tag_default = tag_encode(28)
4574 encoding = "utf-32-be"
4575 asn1_type_name = "UniversalString"
4578 class BMPString(CommonString):
4580 tag_default = tag_encode(30)
4581 encoding = "utf-16-be"
4582 asn1_type_name = "BMPString"
4585 ChoiceState = namedtuple(
4587 BasicState._fields + ("specs", "value",),
4593 """``CHOICE`` special type
4597 class GeneralName(Choice):
4599 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4600 ("dNSName", IA5String(impl=tag_ctxp(2))),
4603 >>> gn = GeneralName()
4605 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4606 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4607 >>> gn["dNSName"] = IA5String("bar.baz")
4608 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4609 >>> gn["rfc822Name"]
4612 [2] IA5String IA5 bar.baz
4615 >>> gn.value == gn["dNSName"]
4618 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4620 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4621 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4623 __slots__ = ("specs",)
4625 asn1_type_name = "CHOICE"
4638 :param value: set the value. Either ``(choice, value)`` tuple, or
4639 :py:class:`pyderasn.Choice` object
4640 :param bytes impl: can not be set, do **not** use it
4641 :param bytes expl: override default tag with ``EXPLICIT`` one
4642 :param default: set default value. Type same as in ``value``
4643 :param bool optional: is object ``OPTIONAL`` in sequence
4645 if impl is not None:
4646 raise ValueError("no implicit tag allowed for CHOICE")
4647 super(Choice, self).__init__(None, expl, default, optional, _decoded)
4649 schema = getattr(self, "schema", ())
4650 if len(schema) == 0:
4651 raise ValueError("schema must be specified")
4653 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
4656 if value is not None:
4657 self._value = self._value_sanitize(value)
4658 if default is not None:
4659 default_value = self._value_sanitize(default)
4660 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4661 default_obj.specs = self.specs
4662 default_obj._value = default_value
4663 self.default = default_obj
4665 self._value = copy(default_obj._value)
4667 def _value_sanitize(self, value):
4668 if (value.__class__ == tuple) and len(value) == 2:
4670 spec = self.specs.get(choice)
4672 raise ObjUnknown(choice)
4673 if not isinstance(obj, spec.__class__):
4674 raise InvalidValueType((spec,))
4675 return (choice, spec(obj))
4676 if isinstance(value, self.__class__):
4678 raise InvalidValueType((self.__class__, tuple))
4682 return self._value is not None and self._value[1].ready
4686 return self.expl_lenindef or (
4687 (self._value is not None) and
4688 self._value[1].bered
4691 def __getstate__(self):
4708 def __setstate__(self, state):
4709 super(Choice, self).__setstate__(state)
4710 self.specs = state.specs
4711 self._value = state.value
4713 def __eq__(self, their):
4714 if (their.__class__ == tuple) and len(their) == 2:
4715 return self._value == their
4716 if not isinstance(their, self.__class__):
4719 self.specs == their.specs and
4720 self._value == their._value
4730 return self.__class__(
4733 expl=self._expl if expl is None else expl,
4734 default=self.default if default is None else default,
4735 optional=self.optional if optional is None else optional,
4740 """Name of the choice
4742 self._assert_ready()
4743 return self._value[0]
4747 """Value of underlying choice
4749 self._assert_ready()
4750 return self._value[1]
4752 def __getitem__(self, key):
4753 if key not in self.specs:
4754 raise ObjUnknown(key)
4755 if self._value is None:
4757 choice, value = self._value
4762 def __setitem__(self, key, value):
4763 spec = self.specs.get(key)
4765 raise ObjUnknown(key)
4766 if not isinstance(value, spec.__class__):
4767 raise InvalidValueType((spec.__class__,))
4768 self._value = (key, spec(value))
4776 return self._value[1].decoded if self.ready else False
4779 self._assert_ready()
4780 return self._value[1].encode()
4782 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4783 for choice, spec in iteritems(self.specs):
4784 sub_decode_path = decode_path + (choice,)
4790 decode_path=sub_decode_path,
4793 _ctx_immutable=False,
4800 klass=self.__class__,
4801 decode_path=decode_path,
4804 if tag_only: # pragma: no cover
4806 value, tail = spec.decode(
4810 decode_path=sub_decode_path,
4812 _ctx_immutable=False,
4814 obj = self.__class__(
4817 default=self.default,
4818 optional=self.optional,
4819 _decoded=(offset, 0, value.fulllen),
4821 obj._value = (choice, value)
4825 value = pp_console_row(next(self.pps()))
4827 value = "%s[%r]" % (value, self.value)
4830 def pps(self, decode_path=()):
4833 asn1_type_name=self.asn1_type_name,
4834 obj_name=self.__class__.__name__,
4835 decode_path=decode_path,
4836 value=self.choice if self.ready else None,
4837 optional=self.optional,
4838 default=self == self.default,
4839 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4840 expl=None if self._expl is None else tag_decode(self._expl),
4845 expl_lenindef=self.expl_lenindef,
4849 yield self.value.pps(decode_path=decode_path + (self.choice,))
4850 for pp in self.pps_lenindef(decode_path):
4854 class PrimitiveTypes(Choice):
4855 """Predefined ``CHOICE`` for all generic primitive types
4857 It could be useful for general decoding of some unspecified values:
4859 >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
4860 OCTET STRING 3 bytes 666f6f
4861 >>> PrimitiveTypes().decod(hexdec("0203123456")).value
4865 schema = tuple((klass.__name__, klass()) for klass in (
4889 AnyState = namedtuple(
4891 BasicState._fields + ("value", "defined"),
4897 """``ANY`` special type
4899 >>> Any(Integer(-123))
4901 >>> a = Any(OctetString(b"hello world").encode())
4902 ANY 040b68656c6c6f20776f726c64
4903 >>> hexenc(bytes(a))
4904 b'0x040x0bhello world'
4906 __slots__ = ("defined",)
4907 tag_default = tag_encode(0)
4908 asn1_type_name = "ANY"
4918 :param value: set the value. Either any kind of pyderasn's
4919 **ready** object, or bytes. Pay attention that
4920 **no** validation is performed is raw binary value
4922 :param bytes expl: override default tag with ``EXPLICIT`` one
4923 :param bool optional: is object ``OPTIONAL`` in sequence
4925 super(Any, self).__init__(None, expl, None, optional, _decoded)
4926 self._value = None if value is None else self._value_sanitize(value)
4929 def _value_sanitize(self, value):
4930 if value.__class__ == binary_type:
4932 if isinstance(value, self.__class__):
4934 if isinstance(value, Obj):
4935 return value.encode()
4936 raise InvalidValueType((self.__class__, Obj, binary_type))
4940 return self._value is not None
4944 if self.expl_lenindef or self.lenindef:
4946 if self.defined is None:
4948 return self.defined[1].bered
4950 def __getstate__(self):
4967 def __setstate__(self, state):
4968 super(Any, self).__setstate__(state)
4969 self._value = state.value
4970 self.defined = state.defined
4972 def __eq__(self, their):
4973 if their.__class__ == binary_type:
4974 return self._value == their
4975 if issubclass(their.__class__, Any):
4976 return self._value == their._value
4985 return self.__class__(
4987 expl=self._expl if expl is None else expl,
4988 optional=self.optional if optional is None else optional,
4991 def __bytes__(self):
4992 self._assert_ready()
5000 self._assert_ready()
5003 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5005 t, tlen, lv = tag_strip(tlv)
5006 except DecodeError as err:
5007 raise err.__class__(
5009 klass=self.__class__,
5010 decode_path=decode_path,
5014 l, llen, v = len_decode(lv)
5015 except LenIndefForm as err:
5016 if not ctx.get("bered", False):
5017 raise err.__class__(
5019 klass=self.__class__,
5020 decode_path=decode_path,
5023 llen, vlen, v = 1, 0, lv[1:]
5024 sub_offset = offset + tlen + llen
5026 while v[:EOC_LEN].tobytes() != EOC:
5027 chunk, v = Any().decode(
5030 decode_path=decode_path + (str(chunk_i),),
5033 _ctx_immutable=False,
5035 vlen += chunk.tlvlen
5036 sub_offset += chunk.tlvlen
5038 tlvlen = tlen + llen + vlen + EOC_LEN
5039 obj = self.__class__(
5040 value=tlv[:tlvlen].tobytes(),
5042 optional=self.optional,
5043 _decoded=(offset, 0, tlvlen),
5046 obj.tag = t.tobytes()
5047 return obj, v[EOC_LEN:]
5048 except DecodeError as err:
5049 raise err.__class__(
5051 klass=self.__class__,
5052 decode_path=decode_path,
5056 raise NotEnoughData(
5057 "encoded length is longer than data",
5058 klass=self.__class__,
5059 decode_path=decode_path,
5062 tlvlen = tlen + llen + l
5063 v, tail = tlv[:tlvlen], v[l:]
5064 obj = self.__class__(
5067 optional=self.optional,
5068 _decoded=(offset, 0, tlvlen),
5070 obj.tag = t.tobytes()
5074 return pp_console_row(next(self.pps()))
5076 def pps(self, decode_path=()):
5079 asn1_type_name=self.asn1_type_name,
5080 obj_name=self.__class__.__name__,
5081 decode_path=decode_path,
5082 blob=self._value if self.ready else None,
5083 optional=self.optional,
5084 default=self == self.default,
5085 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5086 expl=None if self._expl is None else tag_decode(self._expl),
5091 expl_offset=self.expl_offset if self.expled else None,
5092 expl_tlen=self.expl_tlen if self.expled else None,
5093 expl_llen=self.expl_llen if self.expled else None,
5094 expl_vlen=self.expl_vlen if self.expled else None,
5095 expl_lenindef=self.expl_lenindef,
5096 lenindef=self.lenindef,
5099 defined_by, defined = self.defined or (None, None)
5100 if defined_by is not None:
5102 decode_path=decode_path + (DecodePathDefBy(defined_by),)
5104 for pp in self.pps_lenindef(decode_path):
5108 ########################################################################
5109 # ASN.1 constructed types
5110 ########################################################################
5112 def get_def_by_path(defines_by_path, sub_decode_path):
5113 """Get define by decode path
5115 for path, define in defines_by_path:
5116 if len(path) != len(sub_decode_path):
5118 for p1, p2 in zip(path, sub_decode_path):
5119 if (not p1 is any) and (p1 != p2):
5125 def abs_decode_path(decode_path, rel_path):
5126 """Create an absolute decode path from current and relative ones
5128 :param decode_path: current decode path, starting point. Tuple of strings
5129 :param rel_path: relative path to ``decode_path``. Tuple of strings.
5130 If first tuple's element is "/", then treat it as
5131 an absolute path, ignoring ``decode_path`` as
5132 starting point. Also this tuple can contain ".."
5133 elements, stripping the leading element from
5136 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
5137 ("foo", "bar", "baz", "whatever")
5138 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
5140 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
5143 if rel_path[0] == "/":
5145 if rel_path[0] == "..":
5146 return abs_decode_path(decode_path[:-1], rel_path[1:])
5147 return decode_path + rel_path
5150 SequenceState = namedtuple(
5152 BasicState._fields + ("specs", "value",),
5157 class Sequence(Obj):
5158 """``SEQUENCE`` structure type
5160 You have to make specification of sequence::
5162 class Extension(Sequence):
5164 ("extnID", ObjectIdentifier()),
5165 ("critical", Boolean(default=False)),
5166 ("extnValue", OctetString()),
5169 Then, you can work with it as with dictionary.
5171 >>> ext = Extension()
5172 >>> Extension().specs
5174 ('extnID', OBJECT IDENTIFIER),
5175 ('critical', BOOLEAN False OPTIONAL DEFAULT),
5176 ('extnValue', OCTET STRING),
5178 >>> ext["extnID"] = "1.2.3"
5179 Traceback (most recent call last):
5180 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5181 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5183 You can determine if sequence is ready to be encoded:
5188 Traceback (most recent call last):
5189 pyderasn.ObjNotReady: object is not ready: extnValue
5190 >>> ext["extnValue"] = OctetString(b"foobar")
5194 Value you want to assign, must have the same **type** as in
5195 corresponding specification, but it can have different tags,
5196 optional/default attributes -- they will be taken from specification
5199 class TBSCertificate(Sequence):
5201 ("version", Version(expl=tag_ctxc(0), default="v1")),
5204 >>> tbs = TBSCertificate()
5205 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5207 Assign ``None`` to remove value from sequence.
5209 You can set values in Sequence during its initialization:
5211 >>> AlgorithmIdentifier((
5212 ("algorithm", ObjectIdentifier("1.2.3")),
5213 ("parameters", Any(Null()))
5215 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5217 You can determine if value exists/set in the sequence and take its value:
5219 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5222 OBJECT IDENTIFIER 1.2.3
5224 But pay attention that if value has default, then it won't be (not
5225 in) in the sequence (because ``DEFAULT`` must not be encoded in
5226 DER), but you can read its value:
5228 >>> "critical" in ext, ext["critical"]
5229 (False, BOOLEAN False)
5230 >>> ext["critical"] = Boolean(True)
5231 >>> "critical" in ext, ext["critical"]
5232 (True, BOOLEAN True)
5234 All defaulted values are always optional.
5236 .. _allow_default_values_ctx:
5238 DER prohibits default value encoding and will raise an error if
5239 default value is unexpectedly met during decode.
5240 If :ref:`bered <bered_ctx>` context option is set, then no error
5241 will be raised, but ``bered`` attribute set. You can disable strict
5242 defaulted values existence validation by setting
5243 ``"allow_default_values": True`` :ref:`context <ctx>` option.
5245 Two sequences are equal if they have equal specification (schema),
5246 implicit/explicit tagging and the same values.
5248 __slots__ = ("specs",)
5249 tag_default = tag_encode(form=TagFormConstructed, num=16)
5250 asn1_type_name = "SEQUENCE"
5262 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5264 schema = getattr(self, "schema", ())
5266 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5269 if value is not None:
5270 if issubclass(value.__class__, Sequence):
5271 self._value = value._value
5272 elif hasattr(value, "__iter__"):
5273 for seq_key, seq_value in value:
5274 self[seq_key] = seq_value
5276 raise InvalidValueType((Sequence,))
5277 if default is not None:
5278 if not issubclass(default.__class__, Sequence):
5279 raise InvalidValueType((Sequence,))
5280 default_value = default._value
5281 default_obj = self.__class__(impl=self.tag, expl=self._expl)
5282 default_obj.specs = self.specs
5283 default_obj._value = default_value
5284 self.default = default_obj
5286 self._value = copy(default_obj._value)
5290 for name, spec in iteritems(self.specs):
5291 value = self._value.get(name)
5302 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5304 return any(value.bered for value in itervalues(self._value))
5306 def __getstate__(self):
5307 return SequenceState(
5320 {k: copy(v) for k, v in iteritems(self._value)},
5323 def __setstate__(self, state):
5324 super(Sequence, self).__setstate__(state)
5325 self.specs = state.specs
5326 self._value = state.value
5328 def __eq__(self, their):
5329 if not isinstance(their, self.__class__):
5332 self.specs == their.specs and
5333 self.tag == their.tag and
5334 self._expl == their._expl and
5335 self._value == their._value
5346 return self.__class__(
5349 impl=self.tag if impl is None else impl,
5350 expl=self._expl if expl is None else expl,
5351 default=self.default if default is None else default,
5352 optional=self.optional if optional is None else optional,
5355 def __contains__(self, key):
5356 return key in self._value
5358 def __setitem__(self, key, value):
5359 spec = self.specs.get(key)
5361 raise ObjUnknown(key)
5363 self._value.pop(key, None)
5365 if not isinstance(value, spec.__class__):
5366 raise InvalidValueType((spec.__class__,))
5367 value = spec(value=value)
5368 if spec.default is not None and value == spec.default:
5369 self._value.pop(key, None)
5371 self._value[key] = value
5373 def __getitem__(self, key):
5374 value = self._value.get(key)
5375 if value is not None:
5377 spec = self.specs.get(key)
5379 raise ObjUnknown(key)
5380 if spec.default is not None:
5384 def _values_for_encoding(self):
5385 for name, spec in iteritems(self.specs):
5386 value = self._value.get(name)
5390 raise ObjNotReady(name)
5394 v = b"".join(v.encode() for v in self._values_for_encoding())
5395 return b"".join((self.tag, len_encode(len(v)), v))
5397 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5399 t, tlen, lv = tag_strip(tlv)
5400 except DecodeError as err:
5401 raise err.__class__(
5403 klass=self.__class__,
5404 decode_path=decode_path,
5409 klass=self.__class__,
5410 decode_path=decode_path,
5413 if tag_only: # pragma: no cover
5416 ctx_bered = ctx.get("bered", False)
5418 l, llen, v = len_decode(lv)
5419 except LenIndefForm as err:
5421 raise err.__class__(
5423 klass=self.__class__,
5424 decode_path=decode_path,
5427 l, llen, v = 0, 1, lv[1:]
5429 except DecodeError as err:
5430 raise err.__class__(
5432 klass=self.__class__,
5433 decode_path=decode_path,
5437 raise NotEnoughData(
5438 "encoded length is longer than data",
5439 klass=self.__class__,
5440 decode_path=decode_path,
5444 v, tail = v[:l], v[l:]
5446 sub_offset = offset + tlen + llen
5449 ctx_allow_default_values = ctx.get("allow_default_values", False)
5450 for name, spec in iteritems(self.specs):
5451 if spec.optional and (
5452 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5456 sub_decode_path = decode_path + (name,)
5458 value, v_tail = spec.decode(
5462 decode_path=sub_decode_path,
5464 _ctx_immutable=False,
5466 except TagMismatch as err:
5467 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5471 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5472 if defined is not None:
5473 defined_by, defined_spec = defined
5474 if issubclass(value.__class__, SequenceOf):
5475 for i, _value in enumerate(value):
5476 sub_sub_decode_path = sub_decode_path + (
5478 DecodePathDefBy(defined_by),
5480 defined_value, defined_tail = defined_spec.decode(
5481 memoryview(bytes(_value)),
5483 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5484 if value.expled else (value.tlen + value.llen)
5487 decode_path=sub_sub_decode_path,
5489 _ctx_immutable=False,
5491 if len(defined_tail) > 0:
5494 klass=self.__class__,
5495 decode_path=sub_sub_decode_path,
5498 _value.defined = (defined_by, defined_value)
5500 defined_value, defined_tail = defined_spec.decode(
5501 memoryview(bytes(value)),
5503 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5504 if value.expled else (value.tlen + value.llen)
5507 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5509 _ctx_immutable=False,
5511 if len(defined_tail) > 0:
5514 klass=self.__class__,
5515 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5518 value.defined = (defined_by, defined_value)
5520 value_len = value.fulllen
5522 sub_offset += value_len
5524 if spec.default is not None and value == spec.default:
5525 if ctx_bered or ctx_allow_default_values:
5529 "DEFAULT value met",
5530 klass=self.__class__,
5531 decode_path=sub_decode_path,
5534 values[name] = value
5536 spec_defines = getattr(spec, "defines", ())
5537 if len(spec_defines) == 0:
5538 defines_by_path = ctx.get("defines_by_path", ())
5539 if len(defines_by_path) > 0:
5540 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5541 if spec_defines is not None and len(spec_defines) > 0:
5542 for rel_path, schema in spec_defines:
5543 defined = schema.get(value, None)
5544 if defined is not None:
5545 ctx.setdefault("_defines", []).append((
5546 abs_decode_path(sub_decode_path[:-1], rel_path),
5550 if v[:EOC_LEN].tobytes() != EOC:
5553 klass=self.__class__,
5554 decode_path=decode_path,
5562 klass=self.__class__,
5563 decode_path=decode_path,
5566 obj = self.__class__(
5570 default=self.default,
5571 optional=self.optional,
5572 _decoded=(offset, llen, vlen),
5575 obj.lenindef = lenindef
5576 obj.ber_encoded = ber_encoded
5580 value = pp_console_row(next(self.pps()))
5582 for name in self.specs:
5583 _value = self._value.get(name)
5586 cols.append("%s: %s" % (name, repr(_value)))
5587 return "%s[%s]" % (value, "; ".join(cols))
5589 def pps(self, decode_path=()):
5592 asn1_type_name=self.asn1_type_name,
5593 obj_name=self.__class__.__name__,
5594 decode_path=decode_path,
5595 optional=self.optional,
5596 default=self == self.default,
5597 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5598 expl=None if self._expl is None else tag_decode(self._expl),
5603 expl_offset=self.expl_offset if self.expled else None,
5604 expl_tlen=self.expl_tlen if self.expled else None,
5605 expl_llen=self.expl_llen if self.expled else None,
5606 expl_vlen=self.expl_vlen if self.expled else None,
5607 expl_lenindef=self.expl_lenindef,
5608 lenindef=self.lenindef,
5609 ber_encoded=self.ber_encoded,
5612 for name in self.specs:
5613 value = self._value.get(name)
5616 yield value.pps(decode_path=decode_path + (name,))
5617 for pp in self.pps_lenindef(decode_path):
5621 class Set(Sequence):
5622 """``SET`` structure type
5624 Its usage is identical to :py:class:`pyderasn.Sequence`.
5626 .. _allow_unordered_set_ctx:
5628 DER prohibits unordered values encoding and will raise an error
5629 during decode. If :ref:`bered <bered_ctx>` context option is set,
5630 then no error will occur. Also you can disable strict values
5631 ordering check by setting ``"allow_unordered_set": True``
5632 :ref:`context <ctx>` option.
5635 tag_default = tag_encode(form=TagFormConstructed, num=17)
5636 asn1_type_name = "SET"
5639 raws = [v.encode() for v in self._values_for_encoding()]
5642 return b"".join((self.tag, len_encode(len(v)), v))
5644 def _specs_items(self):
5645 return iteritems(self.specs)
5647 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5649 t, tlen, lv = tag_strip(tlv)
5650 except DecodeError as err:
5651 raise err.__class__(
5653 klass=self.__class__,
5654 decode_path=decode_path,
5659 klass=self.__class__,
5660 decode_path=decode_path,
5666 ctx_bered = ctx.get("bered", False)
5668 l, llen, v = len_decode(lv)
5669 except LenIndefForm as err:
5671 raise err.__class__(
5673 klass=self.__class__,
5674 decode_path=decode_path,
5677 l, llen, v = 0, 1, lv[1:]
5679 except DecodeError as err:
5680 raise err.__class__(
5682 klass=self.__class__,
5683 decode_path=decode_path,
5687 raise NotEnoughData(
5688 "encoded length is longer than data",
5689 klass=self.__class__,
5693 v, tail = v[:l], v[l:]
5695 sub_offset = offset + tlen + llen
5698 ctx_allow_default_values = ctx.get("allow_default_values", False)
5699 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5700 value_prev = memoryview(v[:0])
5703 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5705 for name, spec in self._specs_items():
5706 sub_decode_path = decode_path + (name,)
5712 decode_path=sub_decode_path,
5715 _ctx_immutable=False,
5722 klass=self.__class__,
5723 decode_path=decode_path,
5726 value, v_tail = spec.decode(
5730 decode_path=sub_decode_path,
5732 _ctx_immutable=False,
5734 value_len = value.fulllen
5735 if value_prev.tobytes() > v[:value_len].tobytes():
5736 if ctx_bered or ctx_allow_unordered_set:
5740 "unordered " + self.asn1_type_name,
5741 klass=self.__class__,
5742 decode_path=sub_decode_path,
5745 if spec.default is None or value != spec.default:
5747 elif ctx_bered or ctx_allow_default_values:
5751 "DEFAULT value met",
5752 klass=self.__class__,
5753 decode_path=sub_decode_path,
5756 values[name] = value
5757 value_prev = v[:value_len]
5758 sub_offset += value_len
5761 obj = self.__class__(
5765 default=self.default,
5766 optional=self.optional,
5767 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5770 if v[:EOC_LEN].tobytes() != EOC:
5773 klass=self.__class__,
5774 decode_path=decode_path,
5780 for name, spec in iteritems(self.specs):
5781 if name not in values and not spec.optional:
5783 "%s value is not ready" % name,
5784 klass=self.__class__,
5785 decode_path=decode_path,
5788 obj.ber_encoded = ber_encoded
5792 SequenceOfState = namedtuple(
5794 BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
5799 class SequenceOf(Obj):
5800 """``SEQUENCE OF`` sequence type
5802 For that kind of type you must specify the object it will carry on
5803 (bounds are for example here, not required)::
5805 class Ints(SequenceOf):
5810 >>> ints.append(Integer(123))
5811 >>> ints.append(Integer(234))
5813 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5814 >>> [int(i) for i in ints]
5816 >>> ints.append(Integer(345))
5817 Traceback (most recent call last):
5818 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5821 >>> ints[1] = Integer(345)
5823 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5825 Also you can initialize sequence with preinitialized values:
5827 >>> ints = Ints([Integer(123), Integer(234)])
5829 __slots__ = ("spec", "_bound_min", "_bound_max")
5830 tag_default = tag_encode(form=TagFormConstructed, num=16)
5831 asn1_type_name = "SEQUENCE OF"
5844 super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
5846 schema = getattr(self, "schema", None)
5848 raise ValueError("schema must be specified")
5850 self._bound_min, self._bound_max = getattr(
5854 ) if bounds is None else bounds
5856 if value is not None:
5857 self._value = self._value_sanitize(value)
5858 if default is not None:
5859 default_value = self._value_sanitize(default)
5860 default_obj = self.__class__(
5865 default_obj._value = default_value
5866 self.default = default_obj
5868 self._value = copy(default_obj._value)
5870 def _value_sanitize(self, value):
5871 if issubclass(value.__class__, SequenceOf):
5872 value = value._value
5873 elif hasattr(value, "__iter__"):
5876 raise InvalidValueType((self.__class__, iter))
5877 if not self._bound_min <= len(value) <= self._bound_max:
5878 raise BoundsError(self._bound_min, len(value), self._bound_max)
5880 if not isinstance(v, self.spec.__class__):
5881 raise InvalidValueType((self.spec.__class__,))
5886 return all(v.ready for v in self._value)
5890 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5892 return any(v.bered for v in self._value)
5894 def __getstate__(self):
5895 return SequenceOfState(
5908 [copy(v) for v in self._value],
5913 def __setstate__(self, state):
5914 super(SequenceOf, self).__setstate__(state)
5915 self.spec = state.spec
5916 self._value = state.value
5917 self._bound_min = state.bound_min
5918 self._bound_max = state.bound_max
5920 def __eq__(self, their):
5921 if isinstance(their, self.__class__):
5923 self.spec == their.spec and
5924 self.tag == their.tag and
5925 self._expl == their._expl and
5926 self._value == their._value
5928 if hasattr(their, "__iter__"):
5929 return self._value == list(their)
5941 return self.__class__(
5945 (self._bound_min, self._bound_max)
5946 if bounds is None else bounds
5948 impl=self.tag if impl is None else impl,
5949 expl=self._expl if expl is None else expl,
5950 default=self.default if default is None else default,
5951 optional=self.optional if optional is None else optional,
5954 def __contains__(self, key):
5955 return key in self._value
5957 def append(self, value):
5958 if not isinstance(value, self.spec.__class__):
5959 raise InvalidValueType((self.spec.__class__,))
5960 if len(self._value) + 1 > self._bound_max:
5963 len(self._value) + 1,
5966 self._value.append(value)
5969 self._assert_ready()
5970 return iter(self._value)
5973 self._assert_ready()
5974 return len(self._value)
5976 def __setitem__(self, key, value):
5977 if not isinstance(value, self.spec.__class__):
5978 raise InvalidValueType((self.spec.__class__,))
5979 self._value[key] = self.spec(value=value)
5981 def __getitem__(self, key):
5982 return self._value[key]
5984 def _values_for_encoding(self):
5985 return iter(self._value)
5988 v = b"".join(v.encode() for v in self._values_for_encoding())
5989 return b"".join((self.tag, len_encode(len(v)), v))
5991 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5993 t, tlen, lv = tag_strip(tlv)
5994 except DecodeError as err:
5995 raise err.__class__(
5997 klass=self.__class__,
5998 decode_path=decode_path,
6003 klass=self.__class__,
6004 decode_path=decode_path,
6010 ctx_bered = ctx.get("bered", False)
6012 l, llen, v = len_decode(lv)
6013 except LenIndefForm as err:
6015 raise err.__class__(
6017 klass=self.__class__,
6018 decode_path=decode_path,
6021 l, llen, v = 0, 1, lv[1:]
6023 except DecodeError as err:
6024 raise err.__class__(
6026 klass=self.__class__,
6027 decode_path=decode_path,
6031 raise NotEnoughData(
6032 "encoded length is longer than data",
6033 klass=self.__class__,
6034 decode_path=decode_path,
6038 v, tail = v[:l], v[l:]
6040 sub_offset = offset + tlen + llen
6042 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6043 value_prev = memoryview(v[:0])
6047 if lenindef and v[:EOC_LEN].tobytes() == EOC:
6049 sub_decode_path = decode_path + (str(len(_value)),)
6050 value, v_tail = spec.decode(
6054 decode_path=sub_decode_path,
6056 _ctx_immutable=False,
6058 value_len = value.fulllen
6060 if value_prev.tobytes() > v[:value_len].tobytes():
6061 if ctx_bered or ctx_allow_unordered_set:
6065 "unordered " + self.asn1_type_name,
6066 klass=self.__class__,
6067 decode_path=sub_decode_path,
6070 value_prev = v[:value_len]
6071 _value.append(value)
6072 sub_offset += value_len
6076 obj = self.__class__(
6079 bounds=(self._bound_min, self._bound_max),
6082 default=self.default,
6083 optional=self.optional,
6084 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6086 except BoundsError as err:
6089 klass=self.__class__,
6090 decode_path=decode_path,
6094 if v[:EOC_LEN].tobytes() != EOC:
6097 klass=self.__class__,
6098 decode_path=decode_path,
6103 obj.ber_encoded = ber_encoded
6108 pp_console_row(next(self.pps())),
6109 ", ".join(repr(v) for v in self._value),
6112 def pps(self, decode_path=()):
6115 asn1_type_name=self.asn1_type_name,
6116 obj_name=self.__class__.__name__,
6117 decode_path=decode_path,
6118 optional=self.optional,
6119 default=self == self.default,
6120 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6121 expl=None if self._expl is None else tag_decode(self._expl),
6126 expl_offset=self.expl_offset if self.expled else None,
6127 expl_tlen=self.expl_tlen if self.expled else None,
6128 expl_llen=self.expl_llen if self.expled else None,
6129 expl_vlen=self.expl_vlen if self.expled else None,
6130 expl_lenindef=self.expl_lenindef,
6131 lenindef=self.lenindef,
6132 ber_encoded=self.ber_encoded,
6135 for i, value in enumerate(self._value):
6136 yield value.pps(decode_path=decode_path + (str(i),))
6137 for pp in self.pps_lenindef(decode_path):
6141 class SetOf(SequenceOf):
6142 """``SET OF`` sequence type
6144 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6147 tag_default = tag_encode(form=TagFormConstructed, num=17)
6148 asn1_type_name = "SET OF"
6151 raws = [v.encode() for v in self._values_for_encoding()]
6154 return b"".join((self.tag, len_encode(len(v)), v))
6156 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
6157 return super(SetOf, self)._decode(
6163 ordering_check=True,
6167 def obj_by_path(pypath): # pragma: no cover
6168 """Import object specified as string Python path
6170 Modules must be separated from classes/functions with ``:``.
6172 >>> obj_by_path("foo.bar:Baz")
6173 <class 'foo.bar.Baz'>
6174 >>> obj_by_path("foo.bar:Baz.boo")
6175 <classmethod 'foo.bar.Baz.boo'>
6177 mod, objs = pypath.rsplit(":", 1)
6178 from importlib import import_module
6179 obj = import_module(mod)
6180 for obj_name in objs.split("."):
6181 obj = getattr(obj, obj_name)
6185 def generic_decoder(): # pragma: no cover
6186 # All of this below is a big hack with self references
6187 choice = PrimitiveTypes()
6188 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6189 choice.specs["SetOf"] = SetOf(schema=choice)
6190 for i in six_xrange(31):
6191 choice.specs["SequenceOf%d" % i] = SequenceOf(
6195 choice.specs["Any"] = Any()
6197 # Class name equals to type name, to omit it from output
6198 class SEQUENCEOF(SequenceOf):
6206 with_decode_path=False,
6207 decode_path_only=(),
6209 def _pprint_pps(pps):
6211 if hasattr(pp, "_fields"):
6213 decode_path_only != () and
6214 pp.decode_path[:len(decode_path_only)] != decode_path_only
6217 if pp.asn1_type_name == Choice.asn1_type_name:
6219 pp_kwargs = pp._asdict()
6220 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6221 pp = _pp(**pp_kwargs)
6222 yield pp_console_row(
6227 with_colours=with_colours,
6228 with_decode_path=with_decode_path,
6229 decode_path_len_decrease=len(decode_path_only),
6231 for row in pp_console_blob(
6233 decode_path_len_decrease=len(decode_path_only),
6237 for row in _pprint_pps(pp):
6239 return "\n".join(_pprint_pps(obj.pps()))
6240 return SEQUENCEOF(), pprint_any
6243 def main(): # pragma: no cover
6245 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6246 parser.add_argument(
6250 help="Skip that number of bytes from the beginning",
6252 parser.add_argument(
6254 help="Python paths to dictionary with OIDs, comma separated",
6256 parser.add_argument(
6258 help="Python path to schema definition to use",
6260 parser.add_argument(
6261 "--defines-by-path",
6262 help="Python path to decoder's defines_by_path",
6264 parser.add_argument(
6266 action="store_true",
6267 help="Disallow BER encoding",
6269 parser.add_argument(
6270 "--print-decode-path",
6271 action="store_true",
6272 help="Print decode paths",
6274 parser.add_argument(
6275 "--decode-path-only",
6276 help="Print only specified decode path",
6278 parser.add_argument(
6280 action="store_true",
6281 help="Allow explicit tag out-of-bound",
6283 parser.add_argument(
6285 type=argparse.FileType("rb"),
6286 help="Path to DER file you want to decode",
6288 args = parser.parse_args()
6289 args.DERFile.seek(args.skip)
6290 der = memoryview(args.DERFile.read())
6291 args.DERFile.close()
6293 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6294 if args.oids else ()
6297 schema = obj_by_path(args.schema)
6298 from functools import partial
6299 pprinter = partial(pprint, big_blobs=True)
6301 schema, pprinter = generic_decoder()
6303 "bered": not args.nobered,
6304 "allow_expl_oob": args.allow_expl_oob,
6306 if args.defines_by_path is not None:
6307 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6308 obj, tail = schema().decode(der, ctx=ctx)
6309 from os import environ
6313 with_colours=environ.get("NO_COLOR") is None,
6314 with_decode_path=args.print_decode_path,
6316 () if args.decode_path_only is None else
6317 tuple(args.decode_path_only.split(":"))
6321 print("\nTrailing data: %s" % hexenc(tail))
6324 if __name__ == "__main__":