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``
360 ASN.1 structures often have ANY and OCTET STRING fields, that are
361 DEFINED BY some previously met ObjectIdentifier. This library provides
362 ability to specify mapping between some OID and field that must be
363 decoded with specific specification.
370 :py:class:`pyderasn.ObjectIdentifier` field inside
371 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
372 necessary for decoding structures. For example, CMS (:rfc:`5652`)
375 class ContentInfo(Sequence):
377 ("contentType", ContentType(defines=((("content",), {
378 id_digestedData: DigestedData(),
379 id_signedData: SignedData(),
381 ("content", Any(expl=tag_ctxc(0))),
384 ``contentType`` field tells that it defines that ``content`` must be
385 decoded with ``SignedData`` specification, if ``contentType`` equals to
386 ``id-signedData``. The same applies to ``DigestedData``. If
387 ``contentType`` contains unknown OID, then no automatic decoding is
390 You can specify multiple fields, that will be autodecoded -- that is why
391 ``defines`` kwarg is a sequence. You can specify defined field
392 relatively or absolutely to current decode path. For example ``defines``
393 for AlgorithmIdentifier of X.509's
394 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
398 id_ecPublicKey: ECParameters(),
399 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
401 (("..", "subjectPublicKey"), {
402 id_rsaEncryption: RSAPublicKey(),
403 id_GostR3410_2001: OctetString(),
407 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
408 autodecode its parameters inside SPKI's algorithm and its public key
411 Following types can be automatically decoded (DEFINED BY):
413 * :py:class:`pyderasn.Any`
414 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
415 * :py:class:`pyderasn.OctetString`
416 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
417 ``Any``/``BitString``/``OctetString``-s
419 When any of those fields is automatically decoded, then ``.defined``
420 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
421 was defined, ``value`` contains corresponding decoded value. For example
422 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
424 .. _defines_by_path_ctx:
426 defines_by_path context option
427 ______________________________
429 Sometimes you either can not or do not want to explicitly set *defines*
430 in the schema. You can dynamically apply those definitions when calling
431 ``.decode()`` method.
433 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
434 value must be sequence of following tuples::
436 (decode_path, defines)
438 where ``decode_path`` is a tuple holding so-called decode path to the
439 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
440 ``defines``, holding exactly the same value as accepted in its
441 :ref:`keyword argument <defines>`.
443 For example, again for CMS, you want to automatically decode
444 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
445 structures it may hold. Also, automatically decode ``controlSequence``
448 content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
451 ((("content",), {id_signedData: SignedData()}),),
456 DecodePathDefBy(id_signedData),
461 id_cct_PKIData: PKIData(),
462 id_cct_PKIResponse: PKIResponse(),
468 DecodePathDefBy(id_signedData),
471 DecodePathDefBy(id_cct_PKIResponse),
477 id_cmc_recipientNonce: RecipientNonce(),
478 id_cmc_senderNonce: SenderNonce(),
479 id_cmc_statusInfoV2: CMCStatusInfoV2(),
480 id_cmc_transactionId: TransactionId(),
485 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
486 First function is useful for path construction when some automatic
487 decoding is already done. ``any`` means literally any value it meet --
488 useful for SEQUENCE/SET OF-s.
495 By default PyDERASN accepts only DER encoded data. It always encodes to
496 DER. But you can optionally enable BER decoding with setting ``bered``
497 :ref:`context <ctx>` argument to True. Indefinite lengths and
498 constructed primitive types should be parsed successfully.
500 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
501 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
502 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``,
503 ``UTCTime``, ``GeneralizedTime`` can contain it.
504 * If object has an indefinite length encoding, then its ``lenindef``
505 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
506 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
508 * If object has an indefinite length encoded explicit tag, then
509 ``expl_lenindef`` is set to True.
510 * If object has either any of BER-related encoding (explicit tag
511 indefinite length, object's indefinite length, BER-encoding) or any
512 underlying component has that kind of encoding, then ``bered``
513 attribute is set to True. For example SignedData CMS can have
514 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
515 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
517 EOC (end-of-contents) token's length is taken in advance in object's
520 .. _allow_expl_oob_ctx:
522 Allow explicit tag out-of-bound
523 -------------------------------
525 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
526 one value, more than one object. If you set ``allow_expl_oob`` context
527 option to True, then no error will be raised and that invalid encoding
528 will be silently further processed. But pay attention that offsets and
529 lengths will be invalid in that case.
533 This option should be used only for skipping some decode errors, just
534 to see the decoded structure somehow.
538 .. autoclass:: pyderasn.Obj
546 .. autoclass:: pyderasn.Boolean
551 .. autoclass:: pyderasn.Integer
552 :members: __init__, named
556 .. autoclass:: pyderasn.BitString
557 :members: __init__, bit_len, named
561 .. autoclass:: pyderasn.OctetString
566 .. autoclass:: pyderasn.Null
571 .. autoclass:: pyderasn.ObjectIdentifier
576 .. autoclass:: pyderasn.Enumerated
580 .. autoclass:: pyderasn.CommonString
584 .. autoclass:: pyderasn.NumericString
588 .. autoclass:: pyderasn.PrintableString
589 :members: __init__, allow_asterisk, allow_ampersand
593 .. autoclass:: pyderasn.UTCTime
594 :members: __init__, todatetime
598 .. autoclass:: pyderasn.GeneralizedTime
599 :members: __init__, todatetime
606 .. autoclass:: pyderasn.Choice
607 :members: __init__, choice, value
611 .. autoclass:: PrimitiveTypes
615 .. autoclass:: pyderasn.Any
623 .. autoclass:: pyderasn.Sequence
628 .. autoclass:: pyderasn.Set
633 .. autoclass:: pyderasn.SequenceOf
638 .. autoclass:: pyderasn.SetOf
644 .. autofunction:: pyderasn.abs_decode_path
645 .. autofunction:: pyderasn.colonize_hex
646 .. autofunction:: pyderasn.hexenc
647 .. autofunction:: pyderasn.hexdec
648 .. autofunction:: pyderasn.tag_encode
649 .. autofunction:: pyderasn.tag_decode
650 .. autofunction:: pyderasn.tag_ctxp
651 .. autofunction:: pyderasn.tag_ctxc
652 .. autoclass:: pyderasn.DecodeError
654 .. autoclass:: pyderasn.NotEnoughData
655 .. autoclass:: pyderasn.ExceedingData
656 .. autoclass:: pyderasn.LenIndefForm
657 .. autoclass:: pyderasn.TagMismatch
658 .. autoclass:: pyderasn.InvalidLength
659 .. autoclass:: pyderasn.InvalidOID
660 .. autoclass:: pyderasn.ObjUnknown
661 .. autoclass:: pyderasn.ObjNotReady
662 .. autoclass:: pyderasn.InvalidValueType
663 .. autoclass:: pyderasn.BoundsError
670 You can decode DER/BER files using command line abilities::
672 $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
674 If there is no schema for your file, then you can try parsing it without,
675 but of course IMPLICIT tags will often make it impossible. But result is
676 good enough for the certificate above::
678 $ python -m pyderasn path/to/file
679 0 [1,3,1604] . >: SEQUENCE OF
680 4 [1,3,1453] . . >: SEQUENCE OF
681 8 [0,0, 5] . . . . >: [0] ANY
682 . . . . . A0:03:02:01:02
683 13 [1,1, 3] . . . . >: INTEGER 61595
684 18 [1,1, 13] . . . . >: SEQUENCE OF
685 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
686 31 [1,1, 0] . . . . . . >: NULL
687 33 [1,3, 274] . . . . >: SEQUENCE OF
688 37 [1,1, 11] . . . . . . >: SET OF
689 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
690 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
691 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
693 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
694 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
695 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
696 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
697 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
698 . . . . . . . . . 61:2E:63:6F:6D:2F
699 1461 [1,1, 13] . . >: SEQUENCE OF
700 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
701 1474 [1,1, 0] . . . . >: NULL
702 1476 [1,2, 129] . . >: BIT STRING 1024 bits
703 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
704 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
710 If you have got dictionaries with ObjectIdentifiers, like example one
711 from ``tests/test_crts.py``::
714 "1.2.840.113549.1.1.1": "id-rsaEncryption",
715 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
717 "2.5.4.10": "id-at-organizationName",
718 "2.5.4.11": "id-at-organizationalUnitName",
721 then you can pass it to pretty printer to see human readable OIDs::
723 $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
725 37 [1,1, 11] . . . . . . >: SET OF
726 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
727 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
728 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
729 50 [1,1, 18] . . . . . . >: SET OF
730 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
731 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
732 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
733 70 [1,1, 18] . . . . . . >: SET OF
734 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
735 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
736 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
742 Each decoded element has so-called decode path: sequence of structure
743 names it is passing during the decode process. Each element has its own
744 unique path inside the whole ASN.1 tree. You can print it out with
745 ``--print-decode-path`` option::
747 $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
748 0 [1,3,1604] Certificate SEQUENCE []
749 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
750 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
751 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
752 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
753 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
754 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
756 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
757 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
758 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
759 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
760 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
761 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
762 . . . . . . . 13:02:45:53
763 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]
766 Now you can print only the specified tree, for example signature algorithm::
768 $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
769 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
770 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
771 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
775 from codecs import getdecoder
776 from codecs import getencoder
777 from collections import namedtuple
778 from collections import OrderedDict
779 from copy import copy
780 from datetime import datetime
781 from datetime import timedelta
782 from math import ceil
783 from operator import attrgetter
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", (
1203 ), **NAMEDTUPLE_KWARGS)
1206 @add_metaclass(AutoAddSlots)
1208 """Common ASN.1 object class
1210 All ASN.1 types are inherited from it. It has metaclass that
1211 automatically adds ``__slots__`` to all inherited classes.
1236 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1237 self._expl = getattr(self, "expl", None) if expl is None else expl
1238 if self.tag != self.tag_default and self._expl is not None:
1239 raise ValueError("implicit and explicit tags can not be set simultaneously")
1240 if self.tag is None:
1241 self._tag_order = None
1243 tag_class, _, tag_num = tag_decode(
1244 self.tag if self._expl is None else self._expl
1246 self._tag_order = (tag_class, tag_num)
1247 if default is not None:
1249 self.optional = optional
1250 self.offset, self.llen, self.vlen = _decoded
1252 self.expl_lenindef = False
1253 self.lenindef = False
1254 self.ber_encoded = False
1257 def ready(self): # pragma: no cover
1258 """Is object ready to be encoded?
1260 raise NotImplementedError()
1262 def _assert_ready(self):
1264 raise ObjNotReady(self.__class__.__name__)
1268 """Is either object or any elements inside is BER encoded?
1270 return self.expl_lenindef or self.lenindef or self.ber_encoded
1274 """Is object decoded?
1276 return (self.llen + self.vlen) > 0
1278 def __getstate__(self): # pragma: no cover
1279 """Used for making safe to be mutable pickleable copies
1281 raise NotImplementedError()
1283 def __setstate__(self, state):
1284 if state.version != __version__:
1285 raise ValueError("data is pickled by different PyDERASN version")
1286 self.tag = state.tag
1287 self._tag_order = state.tag_order
1288 self._expl = state.expl
1289 self.default = state.default
1290 self.optional = state.optional
1291 self.offset = state.offset
1292 self.llen = state.llen
1293 self.vlen = state.vlen
1294 self.expl_lenindef = state.expl_lenindef
1295 self.lenindef = state.lenindef
1296 self.ber_encoded = state.ber_encoded
1299 def tag_order(self):
1300 """Tag's (class, number) used for DER/CER sorting
1302 return self._tag_order
1306 """See :ref:`decoding`
1308 return len(self.tag)
1312 """See :ref:`decoding`
1314 return self.tlen + self.llen + self.vlen
1316 def __str__(self): # pragma: no cover
1317 return self.__bytes__() if PY2 else self.__unicode__()
1319 def __ne__(self, their):
1320 return not(self == their)
1322 def __gt__(self, their): # pragma: no cover
1323 return not(self < their)
1325 def __le__(self, their): # pragma: no cover
1326 return (self == their) or (self < their)
1328 def __ge__(self, their): # pragma: no cover
1329 return (self == their) or (self > their)
1331 def _encode(self): # pragma: no cover
1332 raise NotImplementedError()
1334 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1335 raise NotImplementedError()
1338 """Encode the structure
1340 :returns: DER representation
1342 raw = self._encode()
1343 if self._expl is None:
1345 return b"".join((self._expl, len_encode(len(raw)), raw))
1347 def hexencode(self):
1348 """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1350 return hexenc(self.encode())
1360 _ctx_immutable=True,
1364 :param data: either binary or memoryview
1365 :param int offset: initial data's offset
1366 :param bool leavemm: do we need to leave memoryview of remaining
1367 data as is, or convert it to bytes otherwise
1368 :param ctx: optional :ref:`context <ctx>` governing decoding process
1369 :param tag_only: decode only the tag, without length and contents
1370 (used only in Choice and Set structures, trying to
1371 determine if tag satisfies the schema)
1372 :param _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1374 :returns: (Obj, remaining data)
1376 .. seealso:: :ref:`decoding`
1380 elif _ctx_immutable:
1382 tlv = memoryview(data)
1383 if self._expl is None:
1384 result = self._decode(
1387 decode_path=decode_path,
1396 t, tlen, lv = tag_strip(tlv)
1397 except DecodeError as err:
1398 raise err.__class__(
1400 klass=self.__class__,
1401 decode_path=decode_path,
1406 klass=self.__class__,
1407 decode_path=decode_path,
1411 l, llen, v = len_decode(lv)
1412 except LenIndefForm as err:
1413 if not ctx.get("bered", False):
1414 raise err.__class__(
1416 klass=self.__class__,
1417 decode_path=decode_path,
1421 offset += tlen + llen
1422 result = self._decode(
1425 decode_path=decode_path,
1429 if tag_only: # pragma: no cover
1432 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1433 if eoc_expected.tobytes() != EOC:
1436 klass=self.__class__,
1437 decode_path=decode_path,
1441 obj.expl_lenindef = True
1442 except DecodeError as err:
1443 raise err.__class__(
1445 klass=self.__class__,
1446 decode_path=decode_path,
1451 raise NotEnoughData(
1452 "encoded length is longer than data",
1453 klass=self.__class__,
1454 decode_path=decode_path,
1457 result = self._decode(
1459 offset=offset + tlen + llen,
1460 decode_path=decode_path,
1464 if tag_only: # pragma: no cover
1467 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1469 "explicit tag out-of-bound, longer than data",
1470 klass=self.__class__,
1471 decode_path=decode_path,
1474 return obj, (tail if leavemm else tail.tobytes())
1476 def decod(self, data, offset=0, decode_path=(), ctx=None):
1477 """Decode the data, check that tail is empty
1479 :raises ExceedingData: if tail is not empty
1481 This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
1482 (decode without tail) that also checks that there is no
1485 obj, tail = self.decode(
1488 decode_path=decode_path,
1493 raise ExceedingData(len(tail))
1496 def hexdecode(self, data, *args, **kwargs):
1497 """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
1499 return self.decode(hexdec(data), *args, **kwargs)
1501 def hexdecod(self, data, *args, **kwargs):
1502 """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
1504 return self.decod(hexdec(data), *args, **kwargs)
1508 """See :ref:`decoding`
1510 return self._expl is not None
1514 """See :ref:`decoding`
1519 def expl_tlen(self):
1520 """See :ref:`decoding`
1522 return len(self._expl)
1525 def expl_llen(self):
1526 """See :ref:`decoding`
1528 if self.expl_lenindef:
1530 return len(len_encode(self.tlvlen))
1533 def expl_offset(self):
1534 """See :ref:`decoding`
1536 return self.offset - self.expl_tlen - self.expl_llen
1539 def expl_vlen(self):
1540 """See :ref:`decoding`
1545 def expl_tlvlen(self):
1546 """See :ref:`decoding`
1548 return self.expl_tlen + self.expl_llen + self.expl_vlen
1551 def fulloffset(self):
1552 """See :ref:`decoding`
1554 return self.expl_offset if self.expled else self.offset
1558 """See :ref:`decoding`
1560 return self.expl_tlvlen if self.expled else self.tlvlen
1562 def pps_lenindef(self, decode_path):
1563 if self.lenindef and not (
1564 getattr(self, "defined", None) is not None and
1565 self.defined[1].lenindef
1568 asn1_type_name="EOC",
1570 decode_path=decode_path,
1572 self.offset + self.tlvlen -
1573 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1581 if self.expl_lenindef:
1583 asn1_type_name="EOC",
1584 obj_name="EXPLICIT",
1585 decode_path=decode_path,
1586 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1595 class DecodePathDefBy(object):
1596 """DEFINED BY representation inside decode path
1598 __slots__ = ("defined_by",)
1600 def __init__(self, defined_by):
1601 self.defined_by = defined_by
1603 def __ne__(self, their):
1604 return not(self == their)
1606 def __eq__(self, their):
1607 if not isinstance(their, self.__class__):
1609 return self.defined_by == their.defined_by
1612 return "DEFINED BY " + str(self.defined_by)
1615 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1618 ########################################################################
1620 ########################################################################
1622 PP = namedtuple("PP", (
1645 ), **NAMEDTUPLE_KWARGS)
1650 asn1_type_name="unknown",
1667 expl_lenindef=False,
1698 def _colourize(what, colour, with_colours, attrs=("bold",)):
1699 return colored(what, colour, attrs=attrs) if with_colours else what
1702 def colonize_hex(hexed):
1703 """Separate hexadecimal string with colons
1705 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1714 with_decode_path=False,
1715 decode_path_len_decrease=0,
1722 " " if pp.expl_offset is None else
1723 ("-%d" % (pp.offset - pp.expl_offset))
1725 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1727 col = _colourize(col, "red", with_colours, ())
1728 col += _colourize("B", "red", with_colours) if pp.bered else " "
1730 col = "[%d,%d,%4d]%s" % (
1734 LENINDEF_PP_CHAR if pp.lenindef else " "
1736 col = _colourize(col, "green", with_colours, ())
1738 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1739 if decode_path_len > 0:
1740 cols.append(" ." * decode_path_len)
1741 ent = pp.decode_path[-1]
1742 if isinstance(ent, DecodePathDefBy):
1743 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1744 value = str(ent.defined_by)
1747 len(oid_maps) > 0 and
1748 ent.defined_by.asn1_type_name ==
1749 ObjectIdentifier.asn1_type_name
1751 for oid_map in oid_maps:
1752 oid_name = oid_map.get(value)
1753 if oid_name is not None:
1754 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1756 if oid_name is None:
1757 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1759 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1760 if pp.expl is not None:
1761 klass, _, num = pp.expl
1762 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1763 cols.append(_colourize(col, "blue", with_colours))
1764 if pp.impl is not None:
1765 klass, _, num = pp.impl
1766 col = "[%s%d]" % (TagClassReprs[klass], num)
1767 cols.append(_colourize(col, "blue", with_colours))
1768 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1769 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1771 cols.append(_colourize("BER", "red", with_colours))
1772 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1773 if pp.value is not None:
1775 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1777 len(oid_maps) > 0 and
1778 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1780 for oid_map in oid_maps:
1781 oid_name = oid_map.get(value)
1782 if oid_name is not None:
1783 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1785 if pp.asn1_type_name == Integer.asn1_type_name:
1786 hex_repr = hex(int(pp.obj._value))[2:].upper()
1787 if len(hex_repr) % 2 != 0:
1788 hex_repr = "0" + hex_repr
1789 cols.append(_colourize(
1790 "(%s)" % colonize_hex(hex_repr),
1795 if pp.blob.__class__ == binary_type:
1796 cols.append(hexenc(pp.blob))
1797 elif pp.blob.__class__ == tuple:
1798 cols.append(", ".join(pp.blob))
1800 cols.append(_colourize("OPTIONAL", "red", with_colours))
1802 cols.append(_colourize("DEFAULT", "red", with_colours))
1803 if with_decode_path:
1804 cols.append(_colourize(
1805 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1809 return " ".join(cols)
1812 def pp_console_blob(pp, decode_path_len_decrease=0):
1813 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1814 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1815 if decode_path_len > 0:
1816 cols.append(" ." * (decode_path_len + 1))
1817 if pp.blob.__class__ == binary_type:
1818 blob = hexenc(pp.blob).upper()
1819 for i in six_xrange(0, len(blob), 32):
1820 chunk = blob[i:i + 32]
1821 yield " ".join(cols + [colonize_hex(chunk)])
1822 elif pp.blob.__class__ == tuple:
1823 yield " ".join(cols + [", ".join(pp.blob)])
1831 with_decode_path=False,
1832 decode_path_only=(),
1834 """Pretty print object
1836 :param Obj obj: object you want to pretty print
1837 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionary.
1838 Its human readable form is printed when OID is met
1839 :param big_blobs: if large binary objects are met (like OctetString
1840 values), do we need to print them too, on separate
1842 :param with_colours: colourize output, if ``termcolor`` library
1844 :param with_decode_path: print decode path
1845 :param decode_path_only: print only that specified decode path
1847 def _pprint_pps(pps):
1849 if hasattr(pp, "_fields"):
1851 decode_path_only != () and
1853 str(p) for p in pp.decode_path[:len(decode_path_only)]
1854 ) != decode_path_only
1858 yield pp_console_row(
1863 with_colours=with_colours,
1864 with_decode_path=with_decode_path,
1865 decode_path_len_decrease=len(decode_path_only),
1867 for row in pp_console_blob(
1869 decode_path_len_decrease=len(decode_path_only),
1873 yield pp_console_row(
1878 with_colours=with_colours,
1879 with_decode_path=with_decode_path,
1880 decode_path_len_decrease=len(decode_path_only),
1883 for row in _pprint_pps(pp):
1885 return "\n".join(_pprint_pps(obj.pps()))
1888 ########################################################################
1889 # ASN.1 primitive types
1890 ########################################################################
1892 BooleanState = namedtuple(
1894 BasicState._fields + ("value",),
1900 """``BOOLEAN`` boolean type
1902 >>> b = Boolean(True)
1904 >>> b == Boolean(True)
1910 tag_default = tag_encode(1)
1911 asn1_type_name = "BOOLEAN"
1923 :param value: set the value. Either boolean type, or
1924 :py:class:`pyderasn.Boolean` object
1925 :param bytes impl: override default tag with ``IMPLICIT`` one
1926 :param bytes expl: override default tag with ``EXPLICIT`` one
1927 :param default: set default value. Type same as in ``value``
1928 :param bool optional: is object ``OPTIONAL`` in sequence
1930 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1931 self._value = None if value is None else self._value_sanitize(value)
1932 if default is not None:
1933 default = self._value_sanitize(default)
1934 self.default = self.__class__(
1940 self._value = default
1942 def _value_sanitize(self, value):
1943 if value.__class__ == bool:
1945 if issubclass(value.__class__, Boolean):
1947 raise InvalidValueType((self.__class__, bool))
1951 return self._value is not None
1953 def __getstate__(self):
1954 return BooleanState(
1970 def __setstate__(self, state):
1971 super(Boolean, self).__setstate__(state)
1972 self._value = state.value
1974 def __nonzero__(self):
1975 self._assert_ready()
1979 self._assert_ready()
1982 def __eq__(self, their):
1983 if their.__class__ == bool:
1984 return self._value == their
1985 if not issubclass(their.__class__, Boolean):
1988 self._value == their._value and
1989 self.tag == their.tag and
1990 self._expl == their._expl
2001 return self.__class__(
2003 impl=self.tag if impl is None else impl,
2004 expl=self._expl if expl is None else expl,
2005 default=self.default if default is None else default,
2006 optional=self.optional if optional is None else optional,
2010 self._assert_ready()
2014 (b"\xFF" if self._value else b"\x00"),
2017 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2019 t, _, lv = tag_strip(tlv)
2020 except DecodeError as err:
2021 raise err.__class__(
2023 klass=self.__class__,
2024 decode_path=decode_path,
2029 klass=self.__class__,
2030 decode_path=decode_path,
2036 l, _, v = len_decode(lv)
2037 except DecodeError as err:
2038 raise err.__class__(
2040 klass=self.__class__,
2041 decode_path=decode_path,
2045 raise InvalidLength(
2046 "Boolean's length must be equal to 1",
2047 klass=self.__class__,
2048 decode_path=decode_path,
2052 raise NotEnoughData(
2053 "encoded length is longer than data",
2054 klass=self.__class__,
2055 decode_path=decode_path,
2058 first_octet = byte2int(v)
2060 if first_octet == 0:
2062 elif first_octet == 0xFF:
2064 elif ctx.get("bered", False):
2069 "unacceptable Boolean value",
2070 klass=self.__class__,
2071 decode_path=decode_path,
2074 obj = self.__class__(
2078 default=self.default,
2079 optional=self.optional,
2080 _decoded=(offset, 1, 1),
2082 obj.ber_encoded = ber_encoded
2086 return pp_console_row(next(self.pps()))
2088 def pps(self, decode_path=()):
2091 asn1_type_name=self.asn1_type_name,
2092 obj_name=self.__class__.__name__,
2093 decode_path=decode_path,
2094 value=str(self._value) if self.ready else None,
2095 optional=self.optional,
2096 default=self == self.default,
2097 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2098 expl=None if self._expl is None else tag_decode(self._expl),
2103 expl_offset=self.expl_offset if self.expled else None,
2104 expl_tlen=self.expl_tlen if self.expled else None,
2105 expl_llen=self.expl_llen if self.expled else None,
2106 expl_vlen=self.expl_vlen if self.expled else None,
2107 expl_lenindef=self.expl_lenindef,
2108 ber_encoded=self.ber_encoded,
2111 for pp in self.pps_lenindef(decode_path):
2115 IntegerState = namedtuple(
2117 BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2123 """``INTEGER`` integer type
2125 >>> b = Integer(-123)
2127 >>> b == Integer(-123)
2132 >>> Integer(2, bounds=(1, 3))
2134 >>> Integer(5, bounds=(1, 3))
2135 Traceback (most recent call last):
2136 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2140 class Version(Integer):
2147 >>> v = Version("v1")
2154 {'v3': 2, 'v1': 0, 'v2': 1}
2156 __slots__ = ("specs", "_bound_min", "_bound_max")
2157 tag_default = tag_encode(2)
2158 asn1_type_name = "INTEGER"
2172 :param value: set the value. Either integer type, named value
2173 (if ``schema`` is specified in the class), or
2174 :py:class:`pyderasn.Integer` object
2175 :param bounds: set ``(MIN, MAX)`` value constraint.
2176 (-inf, +inf) by default
2177 :param bytes impl: override default tag with ``IMPLICIT`` one
2178 :param bytes expl: override default tag with ``EXPLICIT`` one
2179 :param default: set default value. Type same as in ``value``
2180 :param bool optional: is object ``OPTIONAL`` in sequence
2182 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
2184 specs = getattr(self, "schema", {}) if _specs is None else _specs
2185 self.specs = specs if specs.__class__ == dict else dict(specs)
2186 self._bound_min, self._bound_max = getattr(
2189 (float("-inf"), float("+inf")),
2190 ) if bounds is None else bounds
2191 if value is not None:
2192 self._value = self._value_sanitize(value)
2193 if default is not None:
2194 default = self._value_sanitize(default)
2195 self.default = self.__class__(
2201 if self._value is None:
2202 self._value = default
2204 def _value_sanitize(self, value):
2205 if isinstance(value, integer_types):
2207 elif issubclass(value.__class__, Integer):
2208 value = value._value
2209 elif value.__class__ == str:
2210 value = self.specs.get(value)
2212 raise ObjUnknown("integer value: %s" % value)
2214 raise InvalidValueType((self.__class__, int, str))
2215 if not self._bound_min <= value <= self._bound_max:
2216 raise BoundsError(self._bound_min, value, self._bound_max)
2221 return self._value is not None
2223 def __getstate__(self):
2224 return IntegerState(
2243 def __setstate__(self, state):
2244 super(Integer, self).__setstate__(state)
2245 self.specs = state.specs
2246 self._value = state.value
2247 self._bound_min = state.bound_min
2248 self._bound_max = state.bound_max
2251 self._assert_ready()
2252 return int(self._value)
2255 self._assert_ready()
2258 bytes(self._expl or b"") +
2259 str(self._value).encode("ascii"),
2262 def __eq__(self, their):
2263 if isinstance(their, integer_types):
2264 return self._value == their
2265 if not issubclass(their.__class__, Integer):
2268 self._value == their._value and
2269 self.tag == their.tag and
2270 self._expl == their._expl
2273 def __lt__(self, their):
2274 return self._value < their._value
2278 """Return named representation (if exists) of the value
2280 for name, value in iteritems(self.specs):
2281 if value == self._value:
2294 return self.__class__(
2297 (self._bound_min, self._bound_max)
2298 if bounds is None else bounds
2300 impl=self.tag if impl is None else impl,
2301 expl=self._expl if expl is None else expl,
2302 default=self.default if default is None else default,
2303 optional=self.optional if optional is None else optional,
2308 self._assert_ready()
2312 octets = bytearray([0])
2316 octets = bytearray()
2318 octets.append((value & 0xFF) ^ 0xFF)
2320 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2323 octets = bytearray()
2325 octets.append(value & 0xFF)
2327 if octets[-1] & 0x80 > 0:
2330 octets = bytes(octets)
2332 bytes_len = ceil(value.bit_length() / 8) or 1
2335 octets = value.to_bytes(
2340 except OverflowError:
2344 return b"".join((self.tag, len_encode(len(octets)), octets))
2346 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2348 t, _, lv = tag_strip(tlv)
2349 except DecodeError as err:
2350 raise err.__class__(
2352 klass=self.__class__,
2353 decode_path=decode_path,
2358 klass=self.__class__,
2359 decode_path=decode_path,
2365 l, llen, v = len_decode(lv)
2366 except DecodeError as err:
2367 raise err.__class__(
2369 klass=self.__class__,
2370 decode_path=decode_path,
2374 raise NotEnoughData(
2375 "encoded length is longer than data",
2376 klass=self.__class__,
2377 decode_path=decode_path,
2381 raise NotEnoughData(
2383 klass=self.__class__,
2384 decode_path=decode_path,
2387 v, tail = v[:l], v[l:]
2388 first_octet = byte2int(v)
2390 second_octet = byte2int(v[1:])
2392 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2393 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2396 "non normalized integer",
2397 klass=self.__class__,
2398 decode_path=decode_path,
2403 if first_octet & 0x80 > 0:
2404 octets = bytearray()
2405 for octet in bytearray(v):
2406 octets.append(octet ^ 0xFF)
2407 for octet in octets:
2408 value = (value << 8) | octet
2412 for octet in bytearray(v):
2413 value = (value << 8) | octet
2415 value = int.from_bytes(v, byteorder="big", signed=True)
2417 obj = self.__class__(
2419 bounds=(self._bound_min, self._bound_max),
2422 default=self.default,
2423 optional=self.optional,
2425 _decoded=(offset, llen, l),
2427 except BoundsError as err:
2430 klass=self.__class__,
2431 decode_path=decode_path,
2437 return pp_console_row(next(self.pps()))
2439 def pps(self, decode_path=()):
2442 asn1_type_name=self.asn1_type_name,
2443 obj_name=self.__class__.__name__,
2444 decode_path=decode_path,
2445 value=(self.named or str(self._value)) if self.ready else None,
2446 optional=self.optional,
2447 default=self == self.default,
2448 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2449 expl=None if self._expl is None else tag_decode(self._expl),
2454 expl_offset=self.expl_offset if self.expled else None,
2455 expl_tlen=self.expl_tlen if self.expled else None,
2456 expl_llen=self.expl_llen if self.expled else None,
2457 expl_vlen=self.expl_vlen if self.expled else None,
2458 expl_lenindef=self.expl_lenindef,
2461 for pp in self.pps_lenindef(decode_path):
2465 BitStringState = namedtuple(
2467 BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
2472 class BitString(Obj):
2473 """``BIT STRING`` bit string type
2475 >>> BitString(b"hello world")
2476 BIT STRING 88 bits 68656c6c6f20776f726c64
2479 >>> b == b"hello world"
2484 >>> BitString("'0A3B5F291CD'H")
2485 BIT STRING 44 bits 0a3b5f291cd0
2486 >>> b = BitString("'010110000000'B")
2487 BIT STRING 12 bits 5800
2490 >>> b[0], b[1], b[2], b[3]
2491 (False, True, False, True)
2495 [False, True, False, True, True, False, False, False, False, False, False, False]
2499 class KeyUsage(BitString):
2501 ("digitalSignature", 0),
2502 ("nonRepudiation", 1),
2503 ("keyEncipherment", 2),
2506 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2507 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2509 ['nonRepudiation', 'keyEncipherment']
2511 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2515 Pay attention that BIT STRING can be encoded both in primitive
2516 and constructed forms. Decoder always checks constructed form tag
2517 additionally to specified primitive one. If BER decoding is
2518 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2519 of DER restrictions.
2521 __slots__ = ("tag_constructed", "specs", "defined")
2522 tag_default = tag_encode(3)
2523 asn1_type_name = "BIT STRING"
2536 :param value: set the value. Either binary type, tuple of named
2537 values (if ``schema`` is specified in the class),
2538 string in ``'XXX...'B`` form, or
2539 :py:class:`pyderasn.BitString` object
2540 :param bytes impl: override default tag with ``IMPLICIT`` one
2541 :param bytes expl: override default tag with ``EXPLICIT`` one
2542 :param default: set default value. Type same as in ``value``
2543 :param bool optional: is object ``OPTIONAL`` in sequence
2545 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2546 specs = getattr(self, "schema", {}) if _specs is None else _specs
2547 self.specs = specs if specs.__class__ == dict else dict(specs)
2548 self._value = None if value is None else self._value_sanitize(value)
2549 if default is not None:
2550 default = self._value_sanitize(default)
2551 self.default = self.__class__(
2557 self._value = default
2559 tag_klass, _, tag_num = tag_decode(self.tag)
2560 self.tag_constructed = tag_encode(
2562 form=TagFormConstructed,
2566 def _bits2octets(self, bits):
2567 if len(self.specs) > 0:
2568 bits = bits.rstrip("0")
2570 bits += "0" * ((8 - (bit_len % 8)) % 8)
2571 octets = bytearray(len(bits) // 8)
2572 for i in six_xrange(len(octets)):
2573 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2574 return bit_len, bytes(octets)
2576 def _value_sanitize(self, value):
2577 if isinstance(value, (string_types, binary_type)):
2579 isinstance(value, string_types) and
2580 value.startswith("'")
2582 if value.endswith("'B"):
2584 if not frozenset(value) <= SET01:
2585 raise ValueError("B's coding contains unacceptable chars")
2586 return self._bits2octets(value)
2587 if value.endswith("'H"):
2591 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2593 if value.__class__ == binary_type:
2594 return (len(value) * 8, value)
2595 raise InvalidValueType((self.__class__, string_types, binary_type))
2596 if value.__class__ == tuple:
2599 isinstance(value[0], integer_types) and
2600 value[1].__class__ == binary_type
2605 bit = self.specs.get(name)
2607 raise ObjUnknown("BitString value: %s" % name)
2610 return self._bits2octets("")
2611 bits = frozenset(bits)
2612 return self._bits2octets("".join(
2613 ("1" if bit in bits else "0")
2614 for bit in six_xrange(max(bits) + 1)
2616 if issubclass(value.__class__, BitString):
2618 raise InvalidValueType((self.__class__, binary_type, string_types))
2622 return self._value is not None
2624 def __getstate__(self):
2625 return BitStringState(
2640 self.tag_constructed,
2644 def __setstate__(self, state):
2645 super(BitString, self).__setstate__(state)
2646 self.specs = state.specs
2647 self._value = state.value
2648 self.tag_constructed = state.tag_constructed
2649 self.defined = state.defined
2652 self._assert_ready()
2653 for i in six_xrange(self._value[0]):
2658 """Returns number of bits in the string
2660 self._assert_ready()
2661 return self._value[0]
2663 def __bytes__(self):
2664 self._assert_ready()
2665 return self._value[1]
2667 def __eq__(self, their):
2668 if their.__class__ == bytes:
2669 return self._value[1] == their
2670 if not issubclass(their.__class__, BitString):
2673 self._value == their._value and
2674 self.tag == their.tag and
2675 self._expl == their._expl
2680 """Named representation (if exists) of the bits
2682 :returns: [str(name), ...]
2684 return [name for name, bit in iteritems(self.specs) if self[bit]]
2694 return self.__class__(
2696 impl=self.tag if impl is None else impl,
2697 expl=self._expl if expl is None else expl,
2698 default=self.default if default is None else default,
2699 optional=self.optional if optional is None else optional,
2703 def __getitem__(self, key):
2704 if key.__class__ == int:
2705 bit_len, octets = self._value
2709 byte2int(memoryview(octets)[key // 8:]) >>
2712 if isinstance(key, string_types):
2713 value = self.specs.get(key)
2715 raise ObjUnknown("BitString value: %s" % key)
2717 raise InvalidValueType((int, str))
2720 self._assert_ready()
2721 bit_len, octets = self._value
2724 len_encode(len(octets) + 1),
2725 int2byte((8 - bit_len % 8) % 8),
2729 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2731 t, tlen, lv = tag_strip(tlv)
2732 except DecodeError as err:
2733 raise err.__class__(
2735 klass=self.__class__,
2736 decode_path=decode_path,
2740 if tag_only: # pragma: no cover
2743 l, llen, v = len_decode(lv)
2744 except DecodeError as err:
2745 raise err.__class__(
2747 klass=self.__class__,
2748 decode_path=decode_path,
2752 raise NotEnoughData(
2753 "encoded length is longer than data",
2754 klass=self.__class__,
2755 decode_path=decode_path,
2759 raise NotEnoughData(
2761 klass=self.__class__,
2762 decode_path=decode_path,
2765 pad_size = byte2int(v)
2766 if l == 1 and pad_size != 0:
2768 "invalid empty value",
2769 klass=self.__class__,
2770 decode_path=decode_path,
2776 klass=self.__class__,
2777 decode_path=decode_path,
2780 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2783 klass=self.__class__,
2784 decode_path=decode_path,
2787 v, tail = v[:l], v[l:]
2788 obj = self.__class__(
2789 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2792 default=self.default,
2793 optional=self.optional,
2795 _decoded=(offset, llen, l),
2798 if t != self.tag_constructed:
2800 klass=self.__class__,
2801 decode_path=decode_path,
2804 if not ctx.get("bered", False):
2806 "unallowed BER constructed encoding",
2807 klass=self.__class__,
2808 decode_path=decode_path,
2811 if tag_only: # pragma: no cover
2815 l, llen, v = len_decode(lv)
2816 except LenIndefForm:
2817 llen, l, v = 1, 0, lv[1:]
2819 except DecodeError as err:
2820 raise err.__class__(
2822 klass=self.__class__,
2823 decode_path=decode_path,
2827 raise NotEnoughData(
2828 "encoded length is longer than data",
2829 klass=self.__class__,
2830 decode_path=decode_path,
2833 if not lenindef and l == 0:
2834 raise NotEnoughData(
2836 klass=self.__class__,
2837 decode_path=decode_path,
2841 sub_offset = offset + tlen + llen
2845 if v[:EOC_LEN].tobytes() == EOC:
2852 "chunk out of bounds",
2853 klass=self.__class__,
2854 decode_path=decode_path + (str(len(chunks) - 1),),
2855 offset=chunks[-1].offset,
2857 sub_decode_path = decode_path + (str(len(chunks)),)
2859 chunk, v_tail = BitString().decode(
2862 decode_path=sub_decode_path,
2865 _ctx_immutable=False,
2869 "expected BitString encoded chunk",
2870 klass=self.__class__,
2871 decode_path=sub_decode_path,
2874 chunks.append(chunk)
2875 sub_offset += chunk.tlvlen
2876 vlen += chunk.tlvlen
2878 if len(chunks) == 0:
2881 klass=self.__class__,
2882 decode_path=decode_path,
2887 for chunk_i, chunk in enumerate(chunks[:-1]):
2888 if chunk.bit_len % 8 != 0:
2890 "BitString chunk is not multiple of 8 bits",
2891 klass=self.__class__,
2892 decode_path=decode_path + (str(chunk_i),),
2893 offset=chunk.offset,
2895 values.append(bytes(chunk))
2896 bit_len += chunk.bit_len
2897 chunk_last = chunks[-1]
2898 values.append(bytes(chunk_last))
2899 bit_len += chunk_last.bit_len
2900 obj = self.__class__(
2901 value=(bit_len, b"".join(values)),
2904 default=self.default,
2905 optional=self.optional,
2907 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2909 obj.lenindef = lenindef
2910 obj.ber_encoded = True
2911 return obj, (v[EOC_LEN:] if lenindef else v)
2914 return pp_console_row(next(self.pps()))
2916 def pps(self, decode_path=()):
2920 bit_len, blob = self._value
2921 value = "%d bits" % bit_len
2922 if len(self.specs) > 0:
2923 blob = tuple(self.named)
2926 asn1_type_name=self.asn1_type_name,
2927 obj_name=self.__class__.__name__,
2928 decode_path=decode_path,
2931 optional=self.optional,
2932 default=self == self.default,
2933 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2934 expl=None if self._expl is None else tag_decode(self._expl),
2939 expl_offset=self.expl_offset if self.expled else None,
2940 expl_tlen=self.expl_tlen if self.expled else None,
2941 expl_llen=self.expl_llen if self.expled else None,
2942 expl_vlen=self.expl_vlen if self.expled else None,
2943 expl_lenindef=self.expl_lenindef,
2944 lenindef=self.lenindef,
2945 ber_encoded=self.ber_encoded,
2948 defined_by, defined = self.defined or (None, None)
2949 if defined_by is not None:
2951 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2953 for pp in self.pps_lenindef(decode_path):
2957 OctetStringState = namedtuple(
2959 BasicState._fields + (
2970 class OctetString(Obj):
2971 """``OCTET STRING`` binary string type
2973 >>> s = OctetString(b"hello world")
2974 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2975 >>> s == OctetString(b"hello world")
2980 >>> OctetString(b"hello", bounds=(4, 4))
2981 Traceback (most recent call last):
2982 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2983 >>> OctetString(b"hell", bounds=(4, 4))
2984 OCTET STRING 4 bytes 68656c6c
2988 Pay attention that OCTET STRING can be encoded both in primitive
2989 and constructed forms. Decoder always checks constructed form tag
2990 additionally to specified primitive one. If BER decoding is
2991 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2992 of DER restrictions.
2994 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2995 tag_default = tag_encode(4)
2996 asn1_type_name = "OCTET STRING"
3010 :param value: set the value. Either binary type, or
3011 :py:class:`pyderasn.OctetString` object
3012 :param bounds: set ``(MIN, MAX)`` value size constraint.
3013 (-inf, +inf) by default
3014 :param bytes impl: override default tag with ``IMPLICIT`` one
3015 :param bytes expl: override default tag with ``EXPLICIT`` one
3016 :param default: set default value. Type same as in ``value``
3017 :param bool optional: is object ``OPTIONAL`` in sequence
3019 super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
3021 self._bound_min, self._bound_max = getattr(
3025 ) if bounds is None else bounds
3026 if value is not None:
3027 self._value = self._value_sanitize(value)
3028 if default is not None:
3029 default = self._value_sanitize(default)
3030 self.default = self.__class__(
3035 if self._value is None:
3036 self._value = default
3038 tag_klass, _, tag_num = tag_decode(self.tag)
3039 self.tag_constructed = tag_encode(
3041 form=TagFormConstructed,
3045 def _value_sanitize(self, value):
3046 if value.__class__ == binary_type:
3048 elif issubclass(value.__class__, OctetString):
3049 value = value._value
3051 raise InvalidValueType((self.__class__, bytes))
3052 if not self._bound_min <= len(value) <= self._bound_max:
3053 raise BoundsError(self._bound_min, len(value), self._bound_max)
3058 return self._value is not None
3060 def __getstate__(self):
3061 return OctetStringState(
3077 self.tag_constructed,
3081 def __setstate__(self, state):
3082 super(OctetString, self).__setstate__(state)
3083 self._value = state.value
3084 self._bound_min = state.bound_min
3085 self._bound_max = state.bound_max
3086 self.tag_constructed = state.tag_constructed
3087 self.defined = state.defined
3089 def __bytes__(self):
3090 self._assert_ready()
3093 def __eq__(self, their):
3094 if their.__class__ == binary_type:
3095 return self._value == their
3096 if not issubclass(their.__class__, OctetString):
3099 self._value == their._value and
3100 self.tag == their.tag and
3101 self._expl == their._expl
3104 def __lt__(self, their):
3105 return self._value < their._value
3116 return self.__class__(
3119 (self._bound_min, self._bound_max)
3120 if bounds is None else bounds
3122 impl=self.tag if impl is None else impl,
3123 expl=self._expl if expl is None else expl,
3124 default=self.default if default is None else default,
3125 optional=self.optional if optional is None else optional,
3129 self._assert_ready()
3132 len_encode(len(self._value)),
3136 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3138 t, tlen, lv = tag_strip(tlv)
3139 except DecodeError as err:
3140 raise err.__class__(
3142 klass=self.__class__,
3143 decode_path=decode_path,
3150 l, llen, v = len_decode(lv)
3151 except DecodeError as err:
3152 raise err.__class__(
3154 klass=self.__class__,
3155 decode_path=decode_path,
3159 raise NotEnoughData(
3160 "encoded length is longer than data",
3161 klass=self.__class__,
3162 decode_path=decode_path,
3165 v, tail = v[:l], v[l:]
3167 obj = self.__class__(
3169 bounds=(self._bound_min, self._bound_max),
3172 default=self.default,
3173 optional=self.optional,
3174 _decoded=(offset, llen, l),
3177 except DecodeError as err:
3180 klass=self.__class__,
3181 decode_path=decode_path,
3184 except BoundsError as err:
3187 klass=self.__class__,
3188 decode_path=decode_path,
3192 if t != self.tag_constructed:
3194 klass=self.__class__,
3195 decode_path=decode_path,
3198 if not ctx.get("bered", False):
3200 "unallowed BER constructed encoding",
3201 klass=self.__class__,
3202 decode_path=decode_path,
3209 l, llen, v = len_decode(lv)
3210 except LenIndefForm:
3211 llen, l, v = 1, 0, lv[1:]
3213 except DecodeError as err:
3214 raise err.__class__(
3216 klass=self.__class__,
3217 decode_path=decode_path,
3221 raise NotEnoughData(
3222 "encoded length is longer than data",
3223 klass=self.__class__,
3224 decode_path=decode_path,
3228 sub_offset = offset + tlen + llen
3232 if v[:EOC_LEN].tobytes() == EOC:
3239 "chunk out of bounds",
3240 klass=self.__class__,
3241 decode_path=decode_path + (str(len(chunks) - 1),),
3242 offset=chunks[-1].offset,
3244 sub_decode_path = decode_path + (str(len(chunks)),)
3246 chunk, v_tail = OctetString().decode(
3249 decode_path=sub_decode_path,
3252 _ctx_immutable=False,
3256 "expected OctetString encoded chunk",
3257 klass=self.__class__,
3258 decode_path=sub_decode_path,
3261 chunks.append(chunk)
3262 sub_offset += chunk.tlvlen
3263 vlen += chunk.tlvlen
3266 obj = self.__class__(
3267 value=b"".join(bytes(chunk) for chunk in chunks),
3268 bounds=(self._bound_min, self._bound_max),
3271 default=self.default,
3272 optional=self.optional,
3273 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3276 except DecodeError as err:
3279 klass=self.__class__,
3280 decode_path=decode_path,
3283 except BoundsError as err:
3286 klass=self.__class__,
3287 decode_path=decode_path,
3290 obj.lenindef = lenindef
3291 obj.ber_encoded = True
3292 return obj, (v[EOC_LEN:] if lenindef else v)
3295 return pp_console_row(next(self.pps()))
3297 def pps(self, decode_path=()):
3300 asn1_type_name=self.asn1_type_name,
3301 obj_name=self.__class__.__name__,
3302 decode_path=decode_path,
3303 value=("%d bytes" % len(self._value)) if self.ready else None,
3304 blob=self._value if self.ready else None,
3305 optional=self.optional,
3306 default=self == self.default,
3307 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3308 expl=None if self._expl is None else tag_decode(self._expl),
3313 expl_offset=self.expl_offset if self.expled else None,
3314 expl_tlen=self.expl_tlen if self.expled else None,
3315 expl_llen=self.expl_llen if self.expled else None,
3316 expl_vlen=self.expl_vlen if self.expled else None,
3317 expl_lenindef=self.expl_lenindef,
3318 lenindef=self.lenindef,
3319 ber_encoded=self.ber_encoded,
3322 defined_by, defined = self.defined or (None, None)
3323 if defined_by is not None:
3325 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3327 for pp in self.pps_lenindef(decode_path):
3331 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
3335 """``NULL`` null object
3343 tag_default = tag_encode(5)
3344 asn1_type_name = "NULL"
3348 value=None, # unused, but Sequence passes it
3355 :param bytes impl: override default tag with ``IMPLICIT`` one
3356 :param bytes expl: override default tag with ``EXPLICIT`` one
3357 :param bool optional: is object ``OPTIONAL`` in sequence
3359 super(Null, self).__init__(impl, expl, None, optional, _decoded)
3366 def __getstate__(self):
3382 def __eq__(self, their):
3383 if not issubclass(their.__class__, Null):
3386 self.tag == their.tag and
3387 self._expl == their._expl
3397 return self.__class__(
3398 impl=self.tag if impl is None else impl,
3399 expl=self._expl if expl is None else expl,
3400 optional=self.optional if optional is None else optional,
3404 return self.tag + len_encode(0)
3406 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3408 t, _, lv = tag_strip(tlv)
3409 except DecodeError as err:
3410 raise err.__class__(
3412 klass=self.__class__,
3413 decode_path=decode_path,
3418 klass=self.__class__,
3419 decode_path=decode_path,
3422 if tag_only: # pragma: no cover
3425 l, _, v = len_decode(lv)
3426 except DecodeError as err:
3427 raise err.__class__(
3429 klass=self.__class__,
3430 decode_path=decode_path,
3434 raise InvalidLength(
3435 "Null must have zero length",
3436 klass=self.__class__,
3437 decode_path=decode_path,
3440 obj = self.__class__(
3443 optional=self.optional,
3444 _decoded=(offset, 1, 0),
3449 return pp_console_row(next(self.pps()))
3451 def pps(self, decode_path=()):
3454 asn1_type_name=self.asn1_type_name,
3455 obj_name=self.__class__.__name__,
3456 decode_path=decode_path,
3457 optional=self.optional,
3458 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3459 expl=None if self._expl is None else tag_decode(self._expl),
3464 expl_offset=self.expl_offset if self.expled else None,
3465 expl_tlen=self.expl_tlen if self.expled else None,
3466 expl_llen=self.expl_llen if self.expled else None,
3467 expl_vlen=self.expl_vlen if self.expled else None,
3468 expl_lenindef=self.expl_lenindef,
3471 for pp in self.pps_lenindef(decode_path):
3475 ObjectIdentifierState = namedtuple(
3476 "ObjectIdentifierState",
3477 BasicState._fields + ("value", "defines"),
3482 class ObjectIdentifier(Obj):
3483 """``OBJECT IDENTIFIER`` OID type
3485 >>> oid = ObjectIdentifier((1, 2, 3))
3486 OBJECT IDENTIFIER 1.2.3
3487 >>> oid == ObjectIdentifier("1.2.3")
3493 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3494 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3496 >>> str(ObjectIdentifier((3, 1)))
3497 Traceback (most recent call last):
3498 pyderasn.InvalidOID: unacceptable first arc value
3500 __slots__ = ("defines",)
3501 tag_default = tag_encode(6)
3502 asn1_type_name = "OBJECT IDENTIFIER"
3515 :param value: set the value. Either tuples of integers,
3516 string of "."-concatenated integers, or
3517 :py:class:`pyderasn.ObjectIdentifier` object
3518 :param defines: sequence of tuples. Each tuple has two elements.
3519 First one is relative to current one decode
3520 path, aiming to the field defined by that OID.
3521 Read about relative path in
3522 :py:func:`pyderasn.abs_decode_path`. Second
3523 tuple element is ``{OID: pyderasn.Obj()}``
3524 dictionary, mapping between current OID value
3525 and structure applied to defined field.
3526 :ref:`Read about DEFINED BY <definedby>`
3527 :param bytes impl: override default tag with ``IMPLICIT`` one
3528 :param bytes expl: override default tag with ``EXPLICIT`` one
3529 :param default: set default value. Type same as in ``value``
3530 :param bool optional: is object ``OPTIONAL`` in sequence
3532 super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
3534 if value is not None:
3535 self._value = self._value_sanitize(value)
3536 if default is not None:
3537 default = self._value_sanitize(default)
3538 self.default = self.__class__(
3543 if self._value is None:
3544 self._value = default
3545 self.defines = defines
3547 def __add__(self, their):
3548 if their.__class__ == tuple:
3549 return self.__class__(self._value + their)
3550 if isinstance(their, self.__class__):
3551 return self.__class__(self._value + their._value)
3552 raise InvalidValueType((self.__class__, tuple))
3554 def _value_sanitize(self, value):
3555 if issubclass(value.__class__, ObjectIdentifier):
3557 if isinstance(value, string_types):
3559 value = tuple(pureint(arc) for arc in value.split("."))
3561 raise InvalidOID("unacceptable arcs values")
3562 if value.__class__ == tuple:
3564 raise InvalidOID("less than 2 arcs")
3565 first_arc = value[0]
3566 if first_arc in (0, 1):
3567 if not (0 <= value[1] <= 39):
3568 raise InvalidOID("second arc is too wide")
3569 elif first_arc == 2:
3572 raise InvalidOID("unacceptable first arc value")
3573 if not all(arc >= 0 for arc in value):
3574 raise InvalidOID("negative arc value")
3576 raise InvalidValueType((self.__class__, str, tuple))
3580 return self._value is not None
3582 def __getstate__(self):
3583 return ObjectIdentifierState(
3600 def __setstate__(self, state):
3601 super(ObjectIdentifier, self).__setstate__(state)
3602 self._value = state.value
3603 self.defines = state.defines
3606 self._assert_ready()
3607 return iter(self._value)
3610 return ".".join(str(arc) for arc in self._value or ())
3613 self._assert_ready()
3616 bytes(self._expl or b"") +
3617 str(self._value).encode("ascii"),
3620 def __eq__(self, their):
3621 if their.__class__ == tuple:
3622 return self._value == their
3623 if not issubclass(their.__class__, ObjectIdentifier):
3626 self.tag == their.tag and
3627 self._expl == their._expl and
3628 self._value == their._value
3631 def __lt__(self, their):
3632 return self._value < their._value
3643 return self.__class__(
3645 defines=self.defines if defines is None else defines,
3646 impl=self.tag if impl is None else impl,
3647 expl=self._expl if expl is None else expl,
3648 default=self.default if default is None else default,
3649 optional=self.optional if optional is None else optional,
3653 self._assert_ready()
3655 first_value = value[1]
3656 first_arc = value[0]
3659 elif first_arc == 1:
3661 elif first_arc == 2:
3663 else: # pragma: no cover
3664 raise RuntimeError("invalid arc is stored")
3665 octets = [zero_ended_encode(first_value)]
3666 for arc in value[2:]:
3667 octets.append(zero_ended_encode(arc))
3668 v = b"".join(octets)
3669 return b"".join((self.tag, len_encode(len(v)), v))
3671 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3673 t, _, lv = tag_strip(tlv)
3674 except DecodeError as err:
3675 raise err.__class__(
3677 klass=self.__class__,
3678 decode_path=decode_path,
3683 klass=self.__class__,
3684 decode_path=decode_path,
3687 if tag_only: # pragma: no cover
3690 l, llen, v = len_decode(lv)
3691 except DecodeError as err:
3692 raise err.__class__(
3694 klass=self.__class__,
3695 decode_path=decode_path,
3699 raise NotEnoughData(
3700 "encoded length is longer than data",
3701 klass=self.__class__,
3702 decode_path=decode_path,
3706 raise NotEnoughData(
3708 klass=self.__class__,
3709 decode_path=decode_path,
3712 v, tail = v[:l], v[l:]
3719 octet = indexbytes(v, i)
3720 if i == 0 and octet == 0x80:
3721 if ctx.get("bered", False):
3724 raise DecodeError("non normalized arc encoding")
3725 arc = (arc << 7) | (octet & 0x7F)
3726 if octet & 0x80 == 0:
3734 klass=self.__class__,
3735 decode_path=decode_path,
3739 second_arc = arcs[0]
3740 if 0 <= second_arc <= 39:
3742 elif 40 <= second_arc <= 79:
3748 obj = self.__class__(
3749 value=tuple([first_arc, second_arc] + arcs[1:]),
3752 default=self.default,
3753 optional=self.optional,
3754 _decoded=(offset, llen, l),
3757 obj.ber_encoded = True
3761 return pp_console_row(next(self.pps()))
3763 def pps(self, decode_path=()):
3766 asn1_type_name=self.asn1_type_name,
3767 obj_name=self.__class__.__name__,
3768 decode_path=decode_path,
3769 value=str(self) if self.ready else None,
3770 optional=self.optional,
3771 default=self == self.default,
3772 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3773 expl=None if self._expl is None else tag_decode(self._expl),
3778 expl_offset=self.expl_offset if self.expled else None,
3779 expl_tlen=self.expl_tlen if self.expled else None,
3780 expl_llen=self.expl_llen if self.expled else None,
3781 expl_vlen=self.expl_vlen if self.expled else None,
3782 expl_lenindef=self.expl_lenindef,
3783 ber_encoded=self.ber_encoded,
3786 for pp in self.pps_lenindef(decode_path):
3790 class Enumerated(Integer):
3791 """``ENUMERATED`` integer type
3793 This type is identical to :py:class:`pyderasn.Integer`, but requires
3794 schema to be specified and does not accept values missing from it.
3797 tag_default = tag_encode(10)
3798 asn1_type_name = "ENUMERATED"
3809 bounds=None, # dummy argument, workability for Integer.decode
3811 super(Enumerated, self).__init__(
3812 value, bounds, impl, expl, default, optional, _specs, _decoded,
3814 if len(self.specs) == 0:
3815 raise ValueError("schema must be specified")
3817 def _value_sanitize(self, value):
3818 if isinstance(value, self.__class__):
3819 value = value._value
3820 elif isinstance(value, integer_types):
3821 for _value in itervalues(self.specs):
3826 "unknown integer value: %s" % value,
3827 klass=self.__class__,
3829 elif isinstance(value, string_types):
3830 value = self.specs.get(value)
3832 raise ObjUnknown("integer value: %s" % value)
3834 raise InvalidValueType((self.__class__, int, str))
3846 return self.__class__(
3848 impl=self.tag if impl is None else impl,
3849 expl=self._expl if expl is None else expl,
3850 default=self.default if default is None else default,
3851 optional=self.optional if optional is None else optional,
3856 def escape_control_unicode(c):
3857 if unicat(c)[0] == "C":
3858 c = repr(c).lstrip("u").strip("'")
3862 class CommonString(OctetString):
3863 """Common class for all strings
3865 Everything resembles :py:class:`pyderasn.OctetString`, except
3866 ability to deal with unicode text strings.
3868 >>> hexenc("привет мир".encode("utf-8"))
3869 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3870 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3872 >>> s = UTF8String("привет мир")
3873 UTF8String UTF8String привет мир
3875 'привет мир'
3876 >>> hexenc(bytes(s))
3877 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3879 >>> PrintableString("привет мир")
3880 Traceback (most recent call last):
3881 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3883 >>> BMPString("ада", bounds=(2, 2))
3884 Traceback (most recent call last):
3885 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3886 >>> s = BMPString("ад", bounds=(2, 2))
3889 >>> hexenc(bytes(s))
3897 * - :py:class:`pyderasn.UTF8String`
3899 * - :py:class:`pyderasn.NumericString`
3901 * - :py:class:`pyderasn.PrintableString`
3903 * - :py:class:`pyderasn.TeletexString`
3905 * - :py:class:`pyderasn.T61String`
3907 * - :py:class:`pyderasn.VideotexString`
3909 * - :py:class:`pyderasn.IA5String`
3911 * - :py:class:`pyderasn.GraphicString`
3913 * - :py:class:`pyderasn.VisibleString`
3915 * - :py:class:`pyderasn.ISO646String`
3917 * - :py:class:`pyderasn.GeneralString`
3919 * - :py:class:`pyderasn.UniversalString`
3921 * - :py:class:`pyderasn.BMPString`
3926 def _value_sanitize(self, value):
3928 value_decoded = None
3929 if isinstance(value, self.__class__):
3930 value_raw = value._value
3931 elif value.__class__ == text_type:
3932 value_decoded = value
3933 elif value.__class__ == binary_type:
3936 raise InvalidValueType((self.__class__, text_type, binary_type))
3939 value_decoded.encode(self.encoding)
3940 if value_raw is None else value_raw
3943 value_raw.decode(self.encoding)
3944 if value_decoded is None else value_decoded
3946 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3947 raise DecodeError(str(err))
3948 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3956 def __eq__(self, their):
3957 if their.__class__ == binary_type:
3958 return self._value == their
3959 if their.__class__ == text_type:
3960 return self._value == their.encode(self.encoding)
3961 if not isinstance(their, self.__class__):
3964 self._value == their._value and
3965 self.tag == their.tag and
3966 self._expl == their._expl
3969 def __unicode__(self):
3971 return self._value.decode(self.encoding)
3972 return text_type(self._value)
3975 return pp_console_row(next(self.pps(no_unicode=PY2)))
3977 def pps(self, decode_path=(), no_unicode=False):
3981 hexenc(bytes(self)) if no_unicode else
3982 "".join(escape_control_unicode(c) for c in self.__unicode__())
3986 asn1_type_name=self.asn1_type_name,
3987 obj_name=self.__class__.__name__,
3988 decode_path=decode_path,
3990 optional=self.optional,
3991 default=self == self.default,
3992 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3993 expl=None if self._expl is None else tag_decode(self._expl),
3998 expl_offset=self.expl_offset if self.expled else None,
3999 expl_tlen=self.expl_tlen if self.expled else None,
4000 expl_llen=self.expl_llen if self.expled else None,
4001 expl_vlen=self.expl_vlen if self.expled else None,
4002 expl_lenindef=self.expl_lenindef,
4003 ber_encoded=self.ber_encoded,
4006 for pp in self.pps_lenindef(decode_path):
4010 class UTF8String(CommonString):
4012 tag_default = tag_encode(12)
4014 asn1_type_name = "UTF8String"
4017 class AllowableCharsMixin(object):
4019 def allowable_chars(self):
4021 return self._allowable_chars
4022 return frozenset(six_unichr(c) for c in self._allowable_chars)
4025 class NumericString(AllowableCharsMixin, CommonString):
4028 Its value is properly sanitized: only ASCII digits with spaces can
4031 >>> NumericString().allowable_chars
4032 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4035 tag_default = tag_encode(18)
4037 asn1_type_name = "NumericString"
4038 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
4040 def _value_sanitize(self, value):
4041 value = super(NumericString, self)._value_sanitize(value)
4042 if not frozenset(value) <= self._allowable_chars:
4043 raise DecodeError("non-numeric value")
4047 PrintableStringState = namedtuple(
4048 "PrintableStringState",
4049 OctetStringState._fields + ("allowable_chars",),
4054 class PrintableString(AllowableCharsMixin, CommonString):
4057 Its value is properly sanitized: see X.680 41.4 table 10.
4059 >>> PrintableString().allowable_chars
4060 frozenset([' ', "'", ..., 'z'])
4061 >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4062 PrintableString PrintableString foo*bar
4063 >>> obj.allow_asterisk, obj.allow_ampersand
4067 tag_default = tag_encode(19)
4069 asn1_type_name = "PrintableString"
4070 _allowable_chars = frozenset(
4071 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4073 _asterisk = frozenset("*".encode("ascii"))
4074 _ampersand = frozenset("&".encode("ascii"))
4086 allow_asterisk=False,
4087 allow_ampersand=False,
4090 :param allow_asterisk: allow asterisk character
4091 :param allow_ampersand: allow ampersand character
4094 self._allowable_chars |= self._asterisk
4096 self._allowable_chars |= self._ampersand
4097 super(PrintableString, self).__init__(
4098 value, bounds, impl, expl, default, optional, _decoded, ctx,
4102 def allow_asterisk(self):
4103 """Is asterisk character allowed?
4105 return self._asterisk <= self._allowable_chars
4108 def allow_ampersand(self):
4109 """Is ampersand character allowed?
4111 return self._ampersand <= self._allowable_chars
4113 def _value_sanitize(self, value):
4114 value = super(PrintableString, self)._value_sanitize(value)
4115 if not frozenset(value) <= self._allowable_chars:
4116 raise DecodeError("non-printable value")
4119 def __getstate__(self):
4120 return PrintableStringState(
4121 *super(PrintableString, self).__getstate__(),
4122 **{"allowable_chars": self._allowable_chars}
4125 def __setstate__(self, state):
4126 super(PrintableString, self).__setstate__(state)
4127 self._allowable_chars = state.allowable_chars
4138 return self.__class__(
4141 (self._bound_min, self._bound_max)
4142 if bounds is None else bounds
4144 impl=self.tag if impl is None else impl,
4145 expl=self._expl if expl is None else expl,
4146 default=self.default if default is None else default,
4147 optional=self.optional if optional is None else optional,
4148 allow_asterisk=self.allow_asterisk,
4149 allow_ampersand=self.allow_ampersand,
4153 class TeletexString(CommonString):
4155 tag_default = tag_encode(20)
4157 asn1_type_name = "TeletexString"
4160 class T61String(TeletexString):
4162 asn1_type_name = "T61String"
4165 class VideotexString(CommonString):
4167 tag_default = tag_encode(21)
4168 encoding = "iso-8859-1"
4169 asn1_type_name = "VideotexString"
4172 class IA5String(CommonString):
4174 tag_default = tag_encode(22)
4176 asn1_type_name = "IA5"
4179 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4180 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4181 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4184 class VisibleString(CommonString):
4186 tag_default = tag_encode(26)
4188 asn1_type_name = "VisibleString"
4191 UTCTimeState = namedtuple(
4193 OctetStringState._fields + ("ber_raw",),
4198 def str_to_time_fractions(value):
4200 year, v = (v // 10**10), (v % 10**10)
4201 month, v = (v // 10**8), (v % 10**8)
4202 day, v = (v // 10**6), (v % 10**6)
4203 hour, v = (v // 10**4), (v % 10**4)
4204 minute, second = (v // 100), (v % 100)
4205 return year, month, day, hour, minute, second
4208 class UTCTime(VisibleString):
4209 """``UTCTime`` datetime type
4211 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4212 UTCTime UTCTime 2017-09-30T22:07:50
4218 datetime.datetime(2017, 9, 30, 22, 7, 50)
4219 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
4220 datetime.datetime(1957, 9, 30, 22, 7, 50)
4222 If BER encoded value was met, then ``ber_raw`` attribute will hold
4223 its raw representation.
4227 Pay attention that UTCTime can not hold full year, so all years
4228 having < 50 years are treated as 20xx, 19xx otherwise, according
4229 to X.509 recommendation.
4233 No strict validation of UTC offsets are made, but very crude:
4235 * minutes are not exceeding 60
4236 * offset value is not exceeding 14 hours
4238 __slots__ = ("ber_raw",)
4239 tag_default = tag_encode(23)
4241 asn1_type_name = "UTCTime"
4251 bounds=None, # dummy argument, workability for OctetString.decode
4255 :param value: set the value. Either datetime type, or
4256 :py:class:`pyderasn.UTCTime` object
4257 :param bytes impl: override default tag with ``IMPLICIT`` one
4258 :param bytes expl: override default tag with ``EXPLICIT`` one
4259 :param default: set default value. Type same as in ``value``
4260 :param bool optional: is object ``OPTIONAL`` in sequence
4262 super(UTCTime, self).__init__(
4263 None, None, impl, expl, None, optional, _decoded, ctx,
4267 if value is not None:
4268 self._value, self.ber_raw = self._value_sanitize(value, ctx)
4269 self.ber_encoded = self.ber_raw is not None
4270 if default is not None:
4271 default, _ = self._value_sanitize(default)
4272 self.default = self.__class__(
4277 if self._value is None:
4278 self._value = default
4280 self.optional = optional
4282 def _strptime_bered(self, value):
4283 year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
4286 raise ValueError("no timezone")
4287 year += 2000 if year < 50 else 1900
4288 decoded = datetime(year, month, day, hour, minute)
4290 if value[-1] == "Z":
4294 raise ValueError("invalid UTC offset")
4295 if value[-5] == "-":
4297 elif value[-5] == "+":
4300 raise ValueError("invalid UTC offset")
4301 v = pureint(value[-4:])
4302 offset, v = (60 * (v % 100)), v // 100
4304 raise ValueError("invalid UTC offset minutes")
4306 if offset > 14 * 3600:
4307 raise ValueError("too big UTC offset")
4311 return offset, decoded
4313 raise ValueError("invalid UTC offset seconds")
4314 seconds = pureint(value)
4316 raise ValueError("invalid seconds value")
4317 return offset, decoded + timedelta(seconds=seconds)
4319 def _strptime(self, value):
4320 # datetime.strptime's format: %y%m%d%H%M%SZ
4321 if len(value) != LEN_YYMMDDHHMMSSZ:
4322 raise ValueError("invalid UTCTime length")
4323 if value[-1] != "Z":
4324 raise ValueError("non UTC timezone")
4325 year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
4326 year += 2000 if year < 50 else 1900
4327 return datetime(year, month, day, hour, minute, second)
4329 def _dt_sanitize(self, value):
4330 if value.year < 1950 or value.year > 2049:
4331 raise ValueError("UTCTime can hold only 1950-2049 years")
4332 return value.replace(microsecond=0)
4334 def _value_sanitize(self, value, ctx=None):
4335 if value.__class__ == binary_type:
4337 value_decoded = value.decode("ascii")
4338 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4339 raise DecodeError("invalid UTCTime encoding: %r" % err)
4342 return self._strptime(value_decoded), None
4343 except (TypeError, ValueError) as _err:
4345 if (ctx is not None) and ctx.get("bered", False):
4347 offset, _value = self._strptime_bered(value_decoded)
4348 _value = _value - timedelta(seconds=offset)
4349 return self._dt_sanitize(_value), value
4350 except (TypeError, ValueError, OverflowError) as _err:
4353 "invalid %s format: %r" % (self.asn1_type_name, err),
4354 klass=self.__class__,
4356 if isinstance(value, self.__class__):
4357 return value._value, None
4358 if value.__class__ == datetime:
4359 return self._dt_sanitize(value), None
4360 raise InvalidValueType((self.__class__, datetime))
4362 def _pp_value(self):
4364 value = self._value.isoformat()
4365 if self.ber_encoded:
4366 value += " (%s)" % self.ber_raw
4369 def __unicode__(self):
4371 value = self._value.isoformat()
4372 if self.ber_encoded:
4373 value += " (%s)" % self.ber_raw
4375 return text_type(self._pp_value())
4377 def __getstate__(self):
4378 return UTCTimeState(
4379 *super(UTCTime, self).__getstate__(),
4380 **{"ber_raw": self.ber_raw}
4383 def __setstate__(self, state):
4384 super(UTCTime, self).__setstate__(state)
4385 self.ber_raw = state.ber_raw
4387 def __bytes__(self):
4388 self._assert_ready()
4389 return self._encode_time()
4391 def __eq__(self, their):
4392 if their.__class__ == binary_type:
4393 return self._encode_time() == their
4394 if their.__class__ == datetime:
4395 return self.todatetime() == their
4396 if not isinstance(their, self.__class__):
4399 self._value == their._value and
4400 self.tag == their.tag and
4401 self._expl == their._expl
4404 def _encode_time(self):
4405 return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
4408 self._assert_ready()
4409 value = self._encode_time()
4410 return b"".join((self.tag, len_encode(len(value)), value))
4412 def todatetime(self):
4416 return pp_console_row(next(self.pps()))
4418 def pps(self, decode_path=()):
4421 asn1_type_name=self.asn1_type_name,
4422 obj_name=self.__class__.__name__,
4423 decode_path=decode_path,
4424 value=self._pp_value(),
4425 optional=self.optional,
4426 default=self == self.default,
4427 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4428 expl=None if self._expl is None else tag_decode(self._expl),
4433 expl_offset=self.expl_offset if self.expled else None,
4434 expl_tlen=self.expl_tlen if self.expled else None,
4435 expl_llen=self.expl_llen if self.expled else None,
4436 expl_vlen=self.expl_vlen if self.expled else None,
4437 expl_lenindef=self.expl_lenindef,
4438 ber_encoded=self.ber_encoded,
4441 for pp in self.pps_lenindef(decode_path):
4445 class GeneralizedTime(UTCTime):
4446 """``GeneralizedTime`` datetime type
4448 This type is similar to :py:class:`pyderasn.UTCTime`.
4450 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4451 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
4453 '20170930220750.000123Z'
4454 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
4455 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
4459 Only microsecond fractions are supported in DER encoding.
4460 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
4461 higher precision values.
4465 BER encoded data can loss information (accuracy) during decoding
4466 because of float transformations.
4470 Local times (without explicit timezone specification) are treated
4471 as UTC one, no transformations are made.
4475 Zero year is unsupported.
4478 tag_default = tag_encode(24)
4479 asn1_type_name = "GeneralizedTime"
4481 def _dt_sanitize(self, value):
4484 def _strptime_bered(self, value):
4485 if len(value) < 4 + 3 * 2:
4486 raise ValueError("invalid GeneralizedTime")
4487 year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
4488 decoded = datetime(year, month, day, hour)
4489 offset, value = 0, value[10:]
4491 return offset, decoded
4492 if value[-1] == "Z":
4495 for char, sign in (("-", -1), ("+", 1)):
4496 idx = value.rfind(char)
4499 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
4500 v = pureint(offset_raw)
4501 if len(offset_raw) == 4:
4502 offset, v = (60 * (v % 100)), v // 100
4504 raise ValueError("invalid UTC offset minutes")
4505 elif len(offset_raw) == 2:
4508 raise ValueError("invalid UTC offset")
4510 if offset > 14 * 3600:
4511 raise ValueError("too big UTC offset")
4515 return offset, decoded
4516 if value[0] in DECIMAL_SIGNS:
4518 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
4521 raise ValueError("stripped minutes")
4522 decoded += timedelta(seconds=60 * pureint(value[:2]))
4525 return offset, decoded
4526 if value[0] in DECIMAL_SIGNS:
4528 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
4531 raise ValueError("stripped seconds")
4532 decoded += timedelta(seconds=pureint(value[:2]))
4535 return offset, decoded
4536 if value[0] not in DECIMAL_SIGNS:
4537 raise ValueError("invalid format after seconds")
4539 decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
4542 def _strptime(self, value):
4544 if l == LEN_YYYYMMDDHHMMSSZ:
4545 # datetime.strptime's format: %Y%m%d%H%M%SZ
4546 if value[-1] != "Z":
4547 raise ValueError("non UTC timezone")
4548 return datetime(*str_to_time_fractions(value[:-1]))
4549 if l >= LEN_YYYYMMDDHHMMSSDMZ:
4550 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4551 if value[-1] != "Z":
4552 raise ValueError("non UTC timezone")
4553 if value[14] != ".":
4554 raise ValueError("no fractions separator")
4557 raise ValueError("trailing zero")
4560 raise ValueError("only microsecond fractions are supported")
4561 us = pureint(us + ("0" * (6 - us_len)))
4562 year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
4563 return datetime(year, month, day, hour, minute, second, us)
4564 raise ValueError("invalid GeneralizedTime length")
4566 def _encode_time(self):
4568 encoded = value.strftime("%Y%m%d%H%M%S")
4569 if value.microsecond > 0:
4570 encoded += (".%06d" % value.microsecond).rstrip("0")
4571 return (encoded + "Z").encode("ascii")
4574 class GraphicString(CommonString):
4576 tag_default = tag_encode(25)
4577 encoding = "iso-8859-1"
4578 asn1_type_name = "GraphicString"
4581 class ISO646String(VisibleString):
4583 asn1_type_name = "ISO646String"
4586 class GeneralString(CommonString):
4588 tag_default = tag_encode(27)
4589 encoding = "iso-8859-1"
4590 asn1_type_name = "GeneralString"
4593 class UniversalString(CommonString):
4595 tag_default = tag_encode(28)
4596 encoding = "utf-32-be"
4597 asn1_type_name = "UniversalString"
4600 class BMPString(CommonString):
4602 tag_default = tag_encode(30)
4603 encoding = "utf-16-be"
4604 asn1_type_name = "BMPString"
4607 ChoiceState = namedtuple(
4609 BasicState._fields + ("specs", "value",),
4615 """``CHOICE`` special type
4619 class GeneralName(Choice):
4621 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4622 ("dNSName", IA5String(impl=tag_ctxp(2))),
4625 >>> gn = GeneralName()
4627 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4628 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4629 >>> gn["dNSName"] = IA5String("bar.baz")
4630 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4631 >>> gn["rfc822Name"]
4634 [2] IA5String IA5 bar.baz
4637 >>> gn.value == gn["dNSName"]
4640 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4642 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4643 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4645 __slots__ = ("specs",)
4647 asn1_type_name = "CHOICE"
4660 :param value: set the value. Either ``(choice, value)`` tuple, or
4661 :py:class:`pyderasn.Choice` object
4662 :param bytes impl: can not be set, do **not** use it
4663 :param bytes expl: override default tag with ``EXPLICIT`` one
4664 :param default: set default value. Type same as in ``value``
4665 :param bool optional: is object ``OPTIONAL`` in sequence
4667 if impl is not None:
4668 raise ValueError("no implicit tag allowed for CHOICE")
4669 super(Choice, self).__init__(None, expl, default, optional, _decoded)
4671 schema = getattr(self, "schema", ())
4672 if len(schema) == 0:
4673 raise ValueError("schema must be specified")
4675 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
4678 if value is not None:
4679 self._value = self._value_sanitize(value)
4680 if default is not None:
4681 default_value = self._value_sanitize(default)
4682 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4683 default_obj.specs = self.specs
4684 default_obj._value = default_value
4685 self.default = default_obj
4687 self._value = copy(default_obj._value)
4688 if self._expl is not None:
4689 tag_class, _, tag_num = tag_decode(self._expl)
4690 self._tag_order = (tag_class, tag_num)
4692 def _value_sanitize(self, value):
4693 if (value.__class__ == tuple) and len(value) == 2:
4695 spec = self.specs.get(choice)
4697 raise ObjUnknown(choice)
4698 if not isinstance(obj, spec.__class__):
4699 raise InvalidValueType((spec,))
4700 return (choice, spec(obj))
4701 if isinstance(value, self.__class__):
4703 raise InvalidValueType((self.__class__, tuple))
4707 return self._value is not None and self._value[1].ready
4711 return self.expl_lenindef or (
4712 (self._value is not None) and
4713 self._value[1].bered
4716 def __getstate__(self):
4734 def __setstate__(self, state):
4735 super(Choice, self).__setstate__(state)
4736 self.specs = state.specs
4737 self._value = state.value
4739 def __eq__(self, their):
4740 if (their.__class__ == tuple) and len(their) == 2:
4741 return self._value == their
4742 if not isinstance(their, self.__class__):
4745 self.specs == their.specs and
4746 self._value == their._value
4756 return self.__class__(
4759 expl=self._expl if expl is None else expl,
4760 default=self.default if default is None else default,
4761 optional=self.optional if optional is None else optional,
4766 """Name of the choice
4768 self._assert_ready()
4769 return self._value[0]
4773 """Value of underlying choice
4775 self._assert_ready()
4776 return self._value[1]
4779 def tag_order(self):
4780 self._assert_ready()
4781 return self._value[1].tag_order if self._tag_order is None else self._tag_order
4783 def __getitem__(self, key):
4784 if key not in self.specs:
4785 raise ObjUnknown(key)
4786 if self._value is None:
4788 choice, value = self._value
4793 def __setitem__(self, key, value):
4794 spec = self.specs.get(key)
4796 raise ObjUnknown(key)
4797 if not isinstance(value, spec.__class__):
4798 raise InvalidValueType((spec.__class__,))
4799 self._value = (key, spec(value))
4807 return self._value[1].decoded if self.ready else False
4810 self._assert_ready()
4811 return self._value[1].encode()
4813 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4814 for choice, spec in iteritems(self.specs):
4815 sub_decode_path = decode_path + (choice,)
4821 decode_path=sub_decode_path,
4824 _ctx_immutable=False,
4831 klass=self.__class__,
4832 decode_path=decode_path,
4835 if tag_only: # pragma: no cover
4837 value, tail = spec.decode(
4841 decode_path=sub_decode_path,
4843 _ctx_immutable=False,
4845 obj = self.__class__(
4848 default=self.default,
4849 optional=self.optional,
4850 _decoded=(offset, 0, value.fulllen),
4852 obj._value = (choice, value)
4856 value = pp_console_row(next(self.pps()))
4858 value = "%s[%r]" % (value, self.value)
4861 def pps(self, decode_path=()):
4864 asn1_type_name=self.asn1_type_name,
4865 obj_name=self.__class__.__name__,
4866 decode_path=decode_path,
4867 value=self.choice if self.ready else None,
4868 optional=self.optional,
4869 default=self == self.default,
4870 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4871 expl=None if self._expl is None else tag_decode(self._expl),
4876 expl_lenindef=self.expl_lenindef,
4880 yield self.value.pps(decode_path=decode_path + (self.choice,))
4881 for pp in self.pps_lenindef(decode_path):
4885 class PrimitiveTypes(Choice):
4886 """Predefined ``CHOICE`` for all generic primitive types
4888 It could be useful for general decoding of some unspecified values:
4890 >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
4891 OCTET STRING 3 bytes 666f6f
4892 >>> PrimitiveTypes().decod(hexdec("0203123456")).value
4896 schema = tuple((klass.__name__, klass()) for klass in (
4920 AnyState = namedtuple(
4922 BasicState._fields + ("value", "defined"),
4928 """``ANY`` special type
4930 >>> Any(Integer(-123))
4932 >>> a = Any(OctetString(b"hello world").encode())
4933 ANY 040b68656c6c6f20776f726c64
4934 >>> hexenc(bytes(a))
4935 b'0x040x0bhello world'
4937 __slots__ = ("defined",)
4938 tag_default = tag_encode(0)
4939 asn1_type_name = "ANY"
4949 :param value: set the value. Either any kind of pyderasn's
4950 **ready** object, or bytes. Pay attention that
4951 **no** validation is performed if raw binary value
4952 is valid TLV, except just tag decoding
4953 :param bytes expl: override default tag with ``EXPLICIT`` one
4954 :param bool optional: is object ``OPTIONAL`` in sequence
4956 super(Any, self).__init__(None, expl, None, optional, _decoded)
4960 value = self._value_sanitize(value)
4962 if self._expl is None:
4963 if value.__class__ == binary_type:
4964 tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
4966 tag_class, tag_num = value.tag_order
4968 tag_class, _, tag_num = tag_decode(self._expl)
4969 self._tag_order = (tag_class, tag_num)
4972 def _value_sanitize(self, value):
4973 if value.__class__ == binary_type:
4975 raise ValueError("Any value can not be empty")
4977 if isinstance(value, self.__class__):
4979 if isinstance(value, Obj):
4980 return value.encode()
4981 raise InvalidValueType((self.__class__, Obj, binary_type))
4985 return self._value is not None
4988 def tag_order(self):
4989 self._assert_ready()
4990 return self._tag_order
4994 if self.expl_lenindef or self.lenindef:
4996 if self.defined is None:
4998 return self.defined[1].bered
5000 def __getstate__(self):
5018 def __setstate__(self, state):
5019 super(Any, self).__setstate__(state)
5020 self._value = state.value
5021 self.defined = state.defined
5023 def __eq__(self, their):
5024 if their.__class__ == binary_type:
5025 return self._value == their
5026 if issubclass(their.__class__, Any):
5027 return self._value == their._value
5036 return self.__class__(
5038 expl=self._expl if expl is None else expl,
5039 optional=self.optional if optional is None else optional,
5042 def __bytes__(self):
5043 self._assert_ready()
5051 self._assert_ready()
5054 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5056 t, tlen, lv = tag_strip(tlv)
5057 except DecodeError as err:
5058 raise err.__class__(
5060 klass=self.__class__,
5061 decode_path=decode_path,
5065 l, llen, v = len_decode(lv)
5066 except LenIndefForm as err:
5067 if not ctx.get("bered", False):
5068 raise err.__class__(
5070 klass=self.__class__,
5071 decode_path=decode_path,
5074 llen, vlen, v = 1, 0, lv[1:]
5075 sub_offset = offset + tlen + llen
5077 while v[:EOC_LEN].tobytes() != EOC:
5078 chunk, v = Any().decode(
5081 decode_path=decode_path + (str(chunk_i),),
5084 _ctx_immutable=False,
5086 vlen += chunk.tlvlen
5087 sub_offset += chunk.tlvlen
5089 tlvlen = tlen + llen + vlen + EOC_LEN
5090 obj = self.__class__(
5091 value=tlv[:tlvlen].tobytes(),
5093 optional=self.optional,
5094 _decoded=(offset, 0, tlvlen),
5097 obj.tag = t.tobytes()
5098 return obj, v[EOC_LEN:]
5099 except DecodeError as err:
5100 raise err.__class__(
5102 klass=self.__class__,
5103 decode_path=decode_path,
5107 raise NotEnoughData(
5108 "encoded length is longer than data",
5109 klass=self.__class__,
5110 decode_path=decode_path,
5113 tlvlen = tlen + llen + l
5114 v, tail = tlv[:tlvlen], v[l:]
5115 obj = self.__class__(
5118 optional=self.optional,
5119 _decoded=(offset, 0, tlvlen),
5121 obj.tag = t.tobytes()
5125 return pp_console_row(next(self.pps()))
5127 def pps(self, decode_path=()):
5130 asn1_type_name=self.asn1_type_name,
5131 obj_name=self.__class__.__name__,
5132 decode_path=decode_path,
5133 blob=self._value if self.ready else None,
5134 optional=self.optional,
5135 default=self == self.default,
5136 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5137 expl=None if self._expl is None else tag_decode(self._expl),
5142 expl_offset=self.expl_offset if self.expled else None,
5143 expl_tlen=self.expl_tlen if self.expled else None,
5144 expl_llen=self.expl_llen if self.expled else None,
5145 expl_vlen=self.expl_vlen if self.expled else None,
5146 expl_lenindef=self.expl_lenindef,
5147 lenindef=self.lenindef,
5150 defined_by, defined = self.defined or (None, None)
5151 if defined_by is not None:
5153 decode_path=decode_path + (DecodePathDefBy(defined_by),)
5155 for pp in self.pps_lenindef(decode_path):
5159 ########################################################################
5160 # ASN.1 constructed types
5161 ########################################################################
5163 def get_def_by_path(defines_by_path, sub_decode_path):
5164 """Get define by decode path
5166 for path, define in defines_by_path:
5167 if len(path) != len(sub_decode_path):
5169 for p1, p2 in zip(path, sub_decode_path):
5170 if (not p1 is any) and (p1 != p2):
5176 def abs_decode_path(decode_path, rel_path):
5177 """Create an absolute decode path from current and relative ones
5179 :param decode_path: current decode path, starting point. Tuple of strings
5180 :param rel_path: relative path to ``decode_path``. Tuple of strings.
5181 If first tuple's element is "/", then treat it as
5182 an absolute path, ignoring ``decode_path`` as
5183 starting point. Also this tuple can contain ".."
5184 elements, stripping the leading element from
5187 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
5188 ("foo", "bar", "baz", "whatever")
5189 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
5191 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
5194 if rel_path[0] == "/":
5196 if rel_path[0] == "..":
5197 return abs_decode_path(decode_path[:-1], rel_path[1:])
5198 return decode_path + rel_path
5201 SequenceState = namedtuple(
5203 BasicState._fields + ("specs", "value",),
5208 class Sequence(Obj):
5209 """``SEQUENCE`` structure type
5211 You have to make specification of sequence::
5213 class Extension(Sequence):
5215 ("extnID", ObjectIdentifier()),
5216 ("critical", Boolean(default=False)),
5217 ("extnValue", OctetString()),
5220 Then, you can work with it as with dictionary.
5222 >>> ext = Extension()
5223 >>> Extension().specs
5225 ('extnID', OBJECT IDENTIFIER),
5226 ('critical', BOOLEAN False OPTIONAL DEFAULT),
5227 ('extnValue', OCTET STRING),
5229 >>> ext["extnID"] = "1.2.3"
5230 Traceback (most recent call last):
5231 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5232 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5234 You can determine if sequence is ready to be encoded:
5239 Traceback (most recent call last):
5240 pyderasn.ObjNotReady: object is not ready: extnValue
5241 >>> ext["extnValue"] = OctetString(b"foobar")
5245 Value you want to assign, must have the same **type** as in
5246 corresponding specification, but it can have different tags,
5247 optional/default attributes -- they will be taken from specification
5250 class TBSCertificate(Sequence):
5252 ("version", Version(expl=tag_ctxc(0), default="v1")),
5255 >>> tbs = TBSCertificate()
5256 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5258 Assign ``None`` to remove value from sequence.
5260 You can set values in Sequence during its initialization:
5262 >>> AlgorithmIdentifier((
5263 ("algorithm", ObjectIdentifier("1.2.3")),
5264 ("parameters", Any(Null()))
5266 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5268 You can determine if value exists/set in the sequence and take its value:
5270 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5273 OBJECT IDENTIFIER 1.2.3
5275 But pay attention that if value has default, then it won't be (not
5276 in) in the sequence (because ``DEFAULT`` must not be encoded in
5277 DER), but you can read its value:
5279 >>> "critical" in ext, ext["critical"]
5280 (False, BOOLEAN False)
5281 >>> ext["critical"] = Boolean(True)
5282 >>> "critical" in ext, ext["critical"]
5283 (True, BOOLEAN True)
5285 All defaulted values are always optional.
5287 .. _allow_default_values_ctx:
5289 DER prohibits default value encoding and will raise an error if
5290 default value is unexpectedly met during decode.
5291 If :ref:`bered <bered_ctx>` context option is set, then no error
5292 will be raised, but ``bered`` attribute set. You can disable strict
5293 defaulted values existence validation by setting
5294 ``"allow_default_values": True`` :ref:`context <ctx>` option.
5296 Two sequences are equal if they have equal specification (schema),
5297 implicit/explicit tagging and the same values.
5299 __slots__ = ("specs",)
5300 tag_default = tag_encode(form=TagFormConstructed, num=16)
5301 asn1_type_name = "SEQUENCE"
5313 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5315 schema = getattr(self, "schema", ())
5317 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5320 if value is not None:
5321 if issubclass(value.__class__, Sequence):
5322 self._value = value._value
5323 elif hasattr(value, "__iter__"):
5324 for seq_key, seq_value in value:
5325 self[seq_key] = seq_value
5327 raise InvalidValueType((Sequence,))
5328 if default is not None:
5329 if not issubclass(default.__class__, Sequence):
5330 raise InvalidValueType((Sequence,))
5331 default_value = default._value
5332 default_obj = self.__class__(impl=self.tag, expl=self._expl)
5333 default_obj.specs = self.specs
5334 default_obj._value = default_value
5335 self.default = default_obj
5337 self._value = copy(default_obj._value)
5341 for name, spec in iteritems(self.specs):
5342 value = self._value.get(name)
5353 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5355 return any(value.bered for value in itervalues(self._value))
5357 def __getstate__(self):
5358 return SequenceState(
5372 {k: copy(v) for k, v in iteritems(self._value)},
5375 def __setstate__(self, state):
5376 super(Sequence, self).__setstate__(state)
5377 self.specs = state.specs
5378 self._value = state.value
5380 def __eq__(self, their):
5381 if not isinstance(their, self.__class__):
5384 self.specs == their.specs and
5385 self.tag == their.tag and
5386 self._expl == their._expl and
5387 self._value == their._value
5398 return self.__class__(
5401 impl=self.tag if impl is None else impl,
5402 expl=self._expl if expl is None else expl,
5403 default=self.default if default is None else default,
5404 optional=self.optional if optional is None else optional,
5407 def __contains__(self, key):
5408 return key in self._value
5410 def __setitem__(self, key, value):
5411 spec = self.specs.get(key)
5413 raise ObjUnknown(key)
5415 self._value.pop(key, None)
5417 if not isinstance(value, spec.__class__):
5418 raise InvalidValueType((spec.__class__,))
5419 value = spec(value=value)
5420 if spec.default is not None and value == spec.default:
5421 self._value.pop(key, None)
5423 self._value[key] = value
5425 def __getitem__(self, key):
5426 value = self._value.get(key)
5427 if value is not None:
5429 spec = self.specs.get(key)
5431 raise ObjUnknown(key)
5432 if spec.default is not None:
5436 def _values_for_encoding(self):
5437 for name, spec in iteritems(self.specs):
5438 value = self._value.get(name)
5442 raise ObjNotReady(name)
5446 v = b"".join(v.encode() for v in self._values_for_encoding())
5447 return b"".join((self.tag, len_encode(len(v)), v))
5449 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5451 t, tlen, lv = tag_strip(tlv)
5452 except DecodeError as err:
5453 raise err.__class__(
5455 klass=self.__class__,
5456 decode_path=decode_path,
5461 klass=self.__class__,
5462 decode_path=decode_path,
5465 if tag_only: # pragma: no cover
5468 ctx_bered = ctx.get("bered", False)
5470 l, llen, v = len_decode(lv)
5471 except LenIndefForm as err:
5473 raise err.__class__(
5475 klass=self.__class__,
5476 decode_path=decode_path,
5479 l, llen, v = 0, 1, lv[1:]
5481 except DecodeError as err:
5482 raise err.__class__(
5484 klass=self.__class__,
5485 decode_path=decode_path,
5489 raise NotEnoughData(
5490 "encoded length is longer than data",
5491 klass=self.__class__,
5492 decode_path=decode_path,
5496 v, tail = v[:l], v[l:]
5498 sub_offset = offset + tlen + llen
5501 ctx_allow_default_values = ctx.get("allow_default_values", False)
5502 for name, spec in iteritems(self.specs):
5503 if spec.optional and (
5504 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5508 sub_decode_path = decode_path + (name,)
5510 value, v_tail = spec.decode(
5514 decode_path=sub_decode_path,
5516 _ctx_immutable=False,
5518 except TagMismatch as err:
5519 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5523 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5524 if defined is not None:
5525 defined_by, defined_spec = defined
5526 if issubclass(value.__class__, SequenceOf):
5527 for i, _value in enumerate(value):
5528 sub_sub_decode_path = sub_decode_path + (
5530 DecodePathDefBy(defined_by),
5532 defined_value, defined_tail = defined_spec.decode(
5533 memoryview(bytes(_value)),
5535 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5536 if value.expled else (value.tlen + value.llen)
5539 decode_path=sub_sub_decode_path,
5541 _ctx_immutable=False,
5543 if len(defined_tail) > 0:
5546 klass=self.__class__,
5547 decode_path=sub_sub_decode_path,
5550 _value.defined = (defined_by, defined_value)
5552 defined_value, defined_tail = defined_spec.decode(
5553 memoryview(bytes(value)),
5555 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5556 if value.expled else (value.tlen + value.llen)
5559 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5561 _ctx_immutable=False,
5563 if len(defined_tail) > 0:
5566 klass=self.__class__,
5567 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5570 value.defined = (defined_by, defined_value)
5572 value_len = value.fulllen
5574 sub_offset += value_len
5576 if spec.default is not None and value == spec.default:
5577 if ctx_bered or ctx_allow_default_values:
5581 "DEFAULT value met",
5582 klass=self.__class__,
5583 decode_path=sub_decode_path,
5586 values[name] = value
5588 spec_defines = getattr(spec, "defines", ())
5589 if len(spec_defines) == 0:
5590 defines_by_path = ctx.get("defines_by_path", ())
5591 if len(defines_by_path) > 0:
5592 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5593 if spec_defines is not None and len(spec_defines) > 0:
5594 for rel_path, schema in spec_defines:
5595 defined = schema.get(value, None)
5596 if defined is not None:
5597 ctx.setdefault("_defines", []).append((
5598 abs_decode_path(sub_decode_path[:-1], rel_path),
5602 if v[:EOC_LEN].tobytes() != EOC:
5605 klass=self.__class__,
5606 decode_path=decode_path,
5614 klass=self.__class__,
5615 decode_path=decode_path,
5618 obj = self.__class__(
5622 default=self.default,
5623 optional=self.optional,
5624 _decoded=(offset, llen, vlen),
5627 obj.lenindef = lenindef
5628 obj.ber_encoded = ber_encoded
5632 value = pp_console_row(next(self.pps()))
5634 for name in self.specs:
5635 _value = self._value.get(name)
5638 cols.append("%s: %s" % (name, repr(_value)))
5639 return "%s[%s]" % (value, "; ".join(cols))
5641 def pps(self, decode_path=()):
5644 asn1_type_name=self.asn1_type_name,
5645 obj_name=self.__class__.__name__,
5646 decode_path=decode_path,
5647 optional=self.optional,
5648 default=self == self.default,
5649 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5650 expl=None if self._expl is None else tag_decode(self._expl),
5655 expl_offset=self.expl_offset if self.expled else None,
5656 expl_tlen=self.expl_tlen if self.expled else None,
5657 expl_llen=self.expl_llen if self.expled else None,
5658 expl_vlen=self.expl_vlen if self.expled else None,
5659 expl_lenindef=self.expl_lenindef,
5660 lenindef=self.lenindef,
5661 ber_encoded=self.ber_encoded,
5664 for name in self.specs:
5665 value = self._value.get(name)
5668 yield value.pps(decode_path=decode_path + (name,))
5669 for pp in self.pps_lenindef(decode_path):
5673 class Set(Sequence):
5674 """``SET`` structure type
5676 Its usage is identical to :py:class:`pyderasn.Sequence`.
5678 .. _allow_unordered_set_ctx:
5680 DER prohibits unordered values encoding and will raise an error
5681 during decode. If :ref:`bered <bered_ctx>` context option is set,
5682 then no error will occur. Also you can disable strict values
5683 ordering check by setting ``"allow_unordered_set": True``
5684 :ref:`context <ctx>` option.
5687 tag_default = tag_encode(form=TagFormConstructed, num=17)
5688 asn1_type_name = "SET"
5691 v = b"".join(value.encode() for value in sorted(
5692 self._values_for_encoding(),
5693 key=attrgetter("tag_order"),
5695 return b"".join((self.tag, len_encode(len(v)), v))
5697 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5699 t, tlen, lv = tag_strip(tlv)
5700 except DecodeError as err:
5701 raise err.__class__(
5703 klass=self.__class__,
5704 decode_path=decode_path,
5709 klass=self.__class__,
5710 decode_path=decode_path,
5716 ctx_bered = ctx.get("bered", False)
5718 l, llen, v = len_decode(lv)
5719 except LenIndefForm as err:
5721 raise err.__class__(
5723 klass=self.__class__,
5724 decode_path=decode_path,
5727 l, llen, v = 0, 1, lv[1:]
5729 except DecodeError as err:
5730 raise err.__class__(
5732 klass=self.__class__,
5733 decode_path=decode_path,
5737 raise NotEnoughData(
5738 "encoded length is longer than data",
5739 klass=self.__class__,
5743 v, tail = v[:l], v[l:]
5745 sub_offset = offset + tlen + llen
5748 ctx_allow_default_values = ctx.get("allow_default_values", False)
5749 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5750 tag_order_prev = (0, 0)
5751 _specs_items = copy(self.specs)
5754 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5756 for name, spec in iteritems(_specs_items):
5757 sub_decode_path = decode_path + (name,)
5763 decode_path=sub_decode_path,
5766 _ctx_immutable=False,
5773 klass=self.__class__,
5774 decode_path=decode_path,
5777 value, v_tail = spec.decode(
5781 decode_path=sub_decode_path,
5783 _ctx_immutable=False,
5785 value_tag_order = value.tag_order
5786 value_len = value.fulllen
5787 if tag_order_prev >= value_tag_order:
5788 if ctx_bered or ctx_allow_unordered_set:
5792 "unordered " + self.asn1_type_name,
5793 klass=self.__class__,
5794 decode_path=sub_decode_path,
5797 if spec.default is None or value != spec.default:
5799 elif ctx_bered or ctx_allow_default_values:
5803 "DEFAULT value met",
5804 klass=self.__class__,
5805 decode_path=sub_decode_path,
5808 values[name] = value
5809 del _specs_items[name]
5810 tag_order_prev = value_tag_order
5811 sub_offset += value_len
5815 obj = self.__class__(
5819 default=self.default,
5820 optional=self.optional,
5821 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5824 if v[:EOC_LEN].tobytes() != EOC:
5827 klass=self.__class__,
5828 decode_path=decode_path,
5834 for name, spec in iteritems(self.specs):
5835 if name not in values and not spec.optional:
5837 "%s value is not ready" % name,
5838 klass=self.__class__,
5839 decode_path=decode_path,
5842 obj.ber_encoded = ber_encoded
5846 SequenceOfState = namedtuple(
5848 BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
5853 class SequenceOf(Obj):
5854 """``SEQUENCE OF`` sequence type
5856 For that kind of type you must specify the object it will carry on
5857 (bounds are for example here, not required)::
5859 class Ints(SequenceOf):
5864 >>> ints.append(Integer(123))
5865 >>> ints.append(Integer(234))
5867 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5868 >>> [int(i) for i in ints]
5870 >>> ints.append(Integer(345))
5871 Traceback (most recent call last):
5872 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5875 >>> ints[1] = Integer(345)
5877 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5879 Also you can initialize sequence with preinitialized values:
5881 >>> ints = Ints([Integer(123), Integer(234)])
5883 __slots__ = ("spec", "_bound_min", "_bound_max")
5884 tag_default = tag_encode(form=TagFormConstructed, num=16)
5885 asn1_type_name = "SEQUENCE OF"
5898 super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
5900 schema = getattr(self, "schema", None)
5902 raise ValueError("schema must be specified")
5904 self._bound_min, self._bound_max = getattr(
5908 ) if bounds is None else bounds
5910 if value is not None:
5911 self._value = self._value_sanitize(value)
5912 if default is not None:
5913 default_value = self._value_sanitize(default)
5914 default_obj = self.__class__(
5919 default_obj._value = default_value
5920 self.default = default_obj
5922 self._value = copy(default_obj._value)
5924 def _value_sanitize(self, value):
5925 if issubclass(value.__class__, SequenceOf):
5926 value = value._value
5927 elif hasattr(value, "__iter__"):
5930 raise InvalidValueType((self.__class__, iter))
5931 if not self._bound_min <= len(value) <= self._bound_max:
5932 raise BoundsError(self._bound_min, len(value), self._bound_max)
5934 if not isinstance(v, self.spec.__class__):
5935 raise InvalidValueType((self.spec.__class__,))
5940 return all(v.ready for v in self._value)
5944 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5946 return any(v.bered for v in self._value)
5948 def __getstate__(self):
5949 return SequenceOfState(
5963 [copy(v) for v in self._value],
5968 def __setstate__(self, state):
5969 super(SequenceOf, self).__setstate__(state)
5970 self.spec = state.spec
5971 self._value = state.value
5972 self._bound_min = state.bound_min
5973 self._bound_max = state.bound_max
5975 def __eq__(self, their):
5976 if isinstance(their, self.__class__):
5978 self.spec == their.spec and
5979 self.tag == their.tag and
5980 self._expl == their._expl and
5981 self._value == their._value
5983 if hasattr(their, "__iter__"):
5984 return self._value == list(their)
5996 return self.__class__(
6000 (self._bound_min, self._bound_max)
6001 if bounds is None else bounds
6003 impl=self.tag if impl is None else impl,
6004 expl=self._expl if expl is None else expl,
6005 default=self.default if default is None else default,
6006 optional=self.optional if optional is None else optional,
6009 def __contains__(self, key):
6010 return key in self._value
6012 def append(self, value):
6013 if not isinstance(value, self.spec.__class__):
6014 raise InvalidValueType((self.spec.__class__,))
6015 if len(self._value) + 1 > self._bound_max:
6018 len(self._value) + 1,
6021 self._value.append(value)
6024 self._assert_ready()
6025 return iter(self._value)
6028 self._assert_ready()
6029 return len(self._value)
6031 def __setitem__(self, key, value):
6032 if not isinstance(value, self.spec.__class__):
6033 raise InvalidValueType((self.spec.__class__,))
6034 self._value[key] = self.spec(value=value)
6036 def __getitem__(self, key):
6037 return self._value[key]
6039 def _values_for_encoding(self):
6040 return iter(self._value)
6043 v = b"".join(v.encode() for v in self._values_for_encoding())
6044 return b"".join((self.tag, len_encode(len(v)), v))
6046 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
6048 t, tlen, lv = tag_strip(tlv)
6049 except DecodeError as err:
6050 raise err.__class__(
6052 klass=self.__class__,
6053 decode_path=decode_path,
6058 klass=self.__class__,
6059 decode_path=decode_path,
6065 ctx_bered = ctx.get("bered", False)
6067 l, llen, v = len_decode(lv)
6068 except LenIndefForm as err:
6070 raise err.__class__(
6072 klass=self.__class__,
6073 decode_path=decode_path,
6076 l, llen, v = 0, 1, lv[1:]
6078 except DecodeError as err:
6079 raise err.__class__(
6081 klass=self.__class__,
6082 decode_path=decode_path,
6086 raise NotEnoughData(
6087 "encoded length is longer than data",
6088 klass=self.__class__,
6089 decode_path=decode_path,
6093 v, tail = v[:l], v[l:]
6095 sub_offset = offset + tlen + llen
6097 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6098 value_prev = memoryview(v[:0])
6102 if lenindef and v[:EOC_LEN].tobytes() == EOC:
6104 sub_decode_path = decode_path + (str(len(_value)),)
6105 value, v_tail = spec.decode(
6109 decode_path=sub_decode_path,
6111 _ctx_immutable=False,
6113 value_len = value.fulllen
6115 if value_prev.tobytes() > v[:value_len].tobytes():
6116 if ctx_bered or ctx_allow_unordered_set:
6120 "unordered " + self.asn1_type_name,
6121 klass=self.__class__,
6122 decode_path=sub_decode_path,
6125 value_prev = v[:value_len]
6126 _value.append(value)
6127 sub_offset += value_len
6131 obj = self.__class__(
6134 bounds=(self._bound_min, self._bound_max),
6137 default=self.default,
6138 optional=self.optional,
6139 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6141 except BoundsError as err:
6144 klass=self.__class__,
6145 decode_path=decode_path,
6149 if v[:EOC_LEN].tobytes() != EOC:
6152 klass=self.__class__,
6153 decode_path=decode_path,
6158 obj.ber_encoded = ber_encoded
6163 pp_console_row(next(self.pps())),
6164 ", ".join(repr(v) for v in self._value),
6167 def pps(self, decode_path=()):
6170 asn1_type_name=self.asn1_type_name,
6171 obj_name=self.__class__.__name__,
6172 decode_path=decode_path,
6173 optional=self.optional,
6174 default=self == self.default,
6175 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6176 expl=None if self._expl is None else tag_decode(self._expl),
6181 expl_offset=self.expl_offset if self.expled else None,
6182 expl_tlen=self.expl_tlen if self.expled else None,
6183 expl_llen=self.expl_llen if self.expled else None,
6184 expl_vlen=self.expl_vlen if self.expled else None,
6185 expl_lenindef=self.expl_lenindef,
6186 lenindef=self.lenindef,
6187 ber_encoded=self.ber_encoded,
6190 for i, value in enumerate(self._value):
6191 yield value.pps(decode_path=decode_path + (str(i),))
6192 for pp in self.pps_lenindef(decode_path):
6196 class SetOf(SequenceOf):
6197 """``SET OF`` sequence type
6199 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6202 tag_default = tag_encode(form=TagFormConstructed, num=17)
6203 asn1_type_name = "SET OF"
6206 v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
6207 return b"".join((self.tag, len_encode(len(v)), v))
6209 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
6210 return super(SetOf, self)._decode(
6216 ordering_check=True,
6220 def obj_by_path(pypath): # pragma: no cover
6221 """Import object specified as string Python path
6223 Modules must be separated from classes/functions with ``:``.
6225 >>> obj_by_path("foo.bar:Baz")
6226 <class 'foo.bar.Baz'>
6227 >>> obj_by_path("foo.bar:Baz.boo")
6228 <classmethod 'foo.bar.Baz.boo'>
6230 mod, objs = pypath.rsplit(":", 1)
6231 from importlib import import_module
6232 obj = import_module(mod)
6233 for obj_name in objs.split("."):
6234 obj = getattr(obj, obj_name)
6238 def generic_decoder(): # pragma: no cover
6239 # All of this below is a big hack with self references
6240 choice = PrimitiveTypes()
6241 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6242 choice.specs["SetOf"] = SetOf(schema=choice)
6243 for i in six_xrange(31):
6244 choice.specs["SequenceOf%d" % i] = SequenceOf(
6248 choice.specs["Any"] = Any()
6250 # Class name equals to type name, to omit it from output
6251 class SEQUENCEOF(SequenceOf):
6259 with_decode_path=False,
6260 decode_path_only=(),
6262 def _pprint_pps(pps):
6264 if hasattr(pp, "_fields"):
6266 decode_path_only != () and
6267 pp.decode_path[:len(decode_path_only)] != decode_path_only
6270 if pp.asn1_type_name == Choice.asn1_type_name:
6272 pp_kwargs = pp._asdict()
6273 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6274 pp = _pp(**pp_kwargs)
6275 yield pp_console_row(
6280 with_colours=with_colours,
6281 with_decode_path=with_decode_path,
6282 decode_path_len_decrease=len(decode_path_only),
6284 for row in pp_console_blob(
6286 decode_path_len_decrease=len(decode_path_only),
6290 for row in _pprint_pps(pp):
6292 return "\n".join(_pprint_pps(obj.pps()))
6293 return SEQUENCEOF(), pprint_any
6296 def main(): # pragma: no cover
6298 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6299 parser.add_argument(
6303 help="Skip that number of bytes from the beginning",
6305 parser.add_argument(
6307 help="Python paths to dictionary with OIDs, comma separated",
6309 parser.add_argument(
6311 help="Python path to schema definition to use",
6313 parser.add_argument(
6314 "--defines-by-path",
6315 help="Python path to decoder's defines_by_path",
6317 parser.add_argument(
6319 action="store_true",
6320 help="Disallow BER encoding",
6322 parser.add_argument(
6323 "--print-decode-path",
6324 action="store_true",
6325 help="Print decode paths",
6327 parser.add_argument(
6328 "--decode-path-only",
6329 help="Print only specified decode path",
6331 parser.add_argument(
6333 action="store_true",
6334 help="Allow explicit tag out-of-bound",
6336 parser.add_argument(
6338 type=argparse.FileType("rb"),
6339 help="Path to DER file you want to decode",
6341 args = parser.parse_args()
6342 args.DERFile.seek(args.skip)
6343 der = memoryview(args.DERFile.read())
6344 args.DERFile.close()
6346 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6347 if args.oids else ()
6350 schema = obj_by_path(args.schema)
6351 from functools import partial
6352 pprinter = partial(pprint, big_blobs=True)
6354 schema, pprinter = generic_decoder()
6356 "bered": not args.nobered,
6357 "allow_expl_oob": args.allow_expl_oob,
6359 if args.defines_by_path is not None:
6360 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6361 obj, tail = schema().decode(der, ctx=ctx)
6362 from os import environ
6366 with_colours=environ.get("NO_COLOR") is None,
6367 with_decode_path=args.print_decode_path,
6369 () if args.decode_path_only is None else
6370 tuple(args.decode_path_only.split(":"))
6374 print("\nTrailing data: %s" % hexenc(tail))
6377 if __name__ == "__main__":