3 # cython: language_level=3
4 # pylint: disable=line-too-long,superfluous-parens,protected-access,too-many-lines
5 # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements
6 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures
7 # Copyright (C) 2017-2021 Sergey Matveev <stargrave@stargrave.org>
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU Lesser General Public License as
11 # published by the Free Software Foundation, version 3 of the License.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Python ASN.1 DER/CER/BER codec with abstract structures
22 This library allows you to marshal various structures in ASN.1 DER/CER
23 format, unmarshal BER/CER/DER ones.
27 >>> Integer().decod(raw) == i
30 There are primitive types, holding single values
31 (:py:class:`pyderasn.BitString`,
32 :py:class:`pyderasn.Boolean`,
33 :py:class:`pyderasn.Enumerated`,
34 :py:class:`pyderasn.GeneralizedTime`,
35 :py:class:`pyderasn.Integer`,
36 :py:class:`pyderasn.Null`,
37 :py:class:`pyderasn.ObjectIdentifier`,
38 :py:class:`pyderasn.OctetString`,
39 :py:class:`pyderasn.UTCTime`,
40 :py:class:`various strings <pyderasn.CommonString>`
41 (:py:class:`pyderasn.BMPString`,
42 :py:class:`pyderasn.GeneralString`,
43 :py:class:`pyderasn.GraphicString`,
44 :py:class:`pyderasn.IA5String`,
45 :py:class:`pyderasn.ISO646String`,
46 :py:class:`pyderasn.NumericString`,
47 :py:class:`pyderasn.PrintableString`,
48 :py:class:`pyderasn.T61String`,
49 :py:class:`pyderasn.TeletexString`,
50 :py:class:`pyderasn.UniversalString`,
51 :py:class:`pyderasn.UTF8String`,
52 :py:class:`pyderasn.VideotexString`,
53 :py:class:`pyderasn.VisibleString`)),
54 constructed types, holding multiple primitive types
55 (:py:class:`pyderasn.Sequence`,
56 :py:class:`pyderasn.SequenceOf`,
57 :py:class:`pyderasn.Set`,
58 :py:class:`pyderasn.SetOf`),
59 and special types like
60 :py:class:`pyderasn.Any` and
61 :py:class:`pyderasn.Choice`.
69 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
70 the default tag used during coding process. You can override it with
71 either ``IMPLICIT`` (using either ``impl`` keyword argument or ``impl``
72 class attribute), or ``EXPLICIT`` one (using either ``expl`` keyword
73 argument or ``expl`` class attribute). Both arguments take raw binary
74 string, containing that tag. You can **not** set implicit and explicit
77 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
78 functions, allowing you to easily create ``CONTEXT``
79 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
84 EXPLICIT tags always have **constructed** tag. PyDERASN does not
85 explicitly check correctness of schema input here.
89 Implicit tags have **primitive** (``tag_ctxp``) encoding for
94 >>> Integer(impl=tag_ctxp(1))
96 >>> Integer(expl=tag_ctxc(2))
99 Implicit tag is not explicitly shown.
101 Two objects of the same type, but with different implicit/explicit tags
104 You can get object's effective tag (either default or implicited) through
105 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
108 >>> tag_decode(tag_ctxc(123))
110 >>> klass, form, num = tag_decode(tag_ctxc(123))
111 >>> klass == TagClassContext
113 >>> form == TagFormConstructed
116 To determine if object has explicit tag, use ``expled`` boolean property
117 and ``expl_tag`` property, returning explicit tag's value.
122 Many objects in sequences could be ``OPTIONAL`` and could have
123 ``DEFAULT`` value. You can specify that object's property using
124 corresponding keyword arguments.
126 >>> Integer(optional=True, default=123)
127 INTEGER 123 OPTIONAL DEFAULT
129 Those specifications do not play any role in primitive value encoding,
130 but are taken into account when dealing with sequences holding them. For
131 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
134 class Version(Integer):
140 class TBSCertificate(Sequence):
142 ("version", Version(expl=tag_ctxc(0), default="v1")),
145 When default argument is used and value is not specified, then it equals
153 Some objects give ability to set value size constraints. This is either
154 possible integer value, or allowed length of various strings and
155 sequences. Constraints are set in the following way::
160 And values satisfaction is checked as: ``MIN <= X <= MAX``.
162 For simplicity you can also set bounds the following way::
164 bounded_x = X(bounds=(MIN, MAX))
166 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
172 All objects have ``ready`` boolean property, that tells if object is
173 ready to be encoded. If that kind of action is performed on unready
174 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
176 All objects are friendly to ``copy.copy()`` and copied objects can be
179 Also all objects can be safely ``pickle``-d, but pay attention that
180 pickling among different PyDERASN versions is prohibited.
187 Decoding is performed using :py:meth:`pyderasn.Obj.decode` method.
188 ``offset`` optional argument could be used to set initial object's
189 offset in the binary data, for convenience. It returns decoded object
190 and remaining unmarshalled data (tail). Internally all work is done on
191 ``memoryview(data)``, and you can leave returning tail as a memoryview,
192 by specifying ``leavemm=True`` argument.
194 Also note convenient :py:meth:`pyderasn.Obj.decod` method, that
195 immediately checks and raises if there is non-empty tail.
197 When object is decoded, ``decoded`` property is true and you can safely
198 use following properties:
200 * ``offset`` -- position including initial offset where object's tag starts
201 * ``tlen`` -- length of object's tag
202 * ``llen`` -- length of object's length value
203 * ``vlen`` -- length of object's value
204 * ``tlvlen`` -- length of the whole object
206 Pay attention that those values do **not** include anything related to
207 explicit tag. If you want to know information about it, then use:
209 * ``expled`` -- to know if explicit tag is set
210 * ``expl_offset`` (it is lesser than ``offset``)
213 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
214 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
216 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
219 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
226 You can specify so called context keyword argument during
227 :py:meth:`pyderasn.Obj.decode` invocation. It is dictionary containing
228 various options governing decoding process.
230 Currently available context options:
232 * :ref:`allow_default_values <allow_default_values_ctx>`
233 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
234 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
235 * :ref:`bered <bered_ctx>`
236 * :ref:`defines_by_path <defines_by_path_ctx>`
237 * :ref:`evgen_mode_upto <evgen_mode_upto_ctx>`
244 All objects have ``pps()`` method, that is a generator of
245 :py:class:`pyderasn.PP` namedtuple, holding various raw information
246 about the object. If ``pps`` is called on sequences, then all underlying
247 ``PP`` will be yielded.
249 You can use :py:func:`pyderasn.pp_console_row` function, converting
250 those ``PP`` to human readable string. Actually exactly it is used for
251 all object ``repr``. But it is easy to write custom formatters.
253 >>> from pyderasn import pprint
254 >>> encoded = Integer(-12345).encode()
255 >>> obj, tail = Integer().decode(encoded)
256 >>> print(pprint(obj))
257 0 [1,1, 2] INTEGER -12345
261 Example certificate::
263 >>> print(pprint(crt))
264 0 [1,3,1604] Certificate SEQUENCE
265 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
266 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
267 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
268 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
269 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
270 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
272 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
273 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
274 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
275 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
276 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
277 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
278 . . . . . . . 13:02:45:53
280 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
281 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
282 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
284 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
285 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
286 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
291 Let's parse that output, human::
293 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
294 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
295 0 1 2 3 4 5 6 7 8 9 10 11
299 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
305 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
311 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
316 Offset of the object, where its DER/BER encoding begins.
317 Pay attention that it does **not** include explicit tag.
319 If explicit tag exists, then this is its length (tag + encoded length).
321 Length of object's tag. For example CHOICE does not have its own tag,
324 Length of encoded length.
326 Length of encoded value.
328 Visual indentation to show the depth of object in the hierarchy.
330 Object's name inside SEQUENCE/CHOICE.
332 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
333 here. "IMPLICIT" is omitted.
335 Object's class name, if set. Omitted if it is just an ordinary simple
336 value (like with ``algorithm`` in example above).
340 Object's value, if set. Can consist of multiple words (like OCTET/BIT
341 STRINGs above). We see ``v3`` value in Version, because it is named.
342 ``rdnSequence`` is the choice of CHOICE type.
344 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
345 default one, specified in the schema.
347 Shows does object contains any kind of BER encoded data (possibly
348 Sequence holding BER-encoded underlying value).
350 Only applicable to BER encoded data. Indefinite length encoding mark.
352 Only applicable to BER encoded data. If object has BER-specific
353 encoding, then ``BER`` will be shown. It does not depend on indefinite
354 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
355 (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime``
358 Also it could be helpful to add quick ASN.1 pprinting command in your
359 pdb's configuration file::
361 alias pp1 import pyderasn ;; print(pyderasn.pprint(%1, oid_maps=(locals().get("OID_STR_TO_NAME", {}),)))
368 ASN.1 structures often have ANY and OCTET STRING fields, that are
369 DEFINED BY some previously met ObjectIdentifier. This library provides
370 ability to specify mapping between some OID and field that must be
371 decoded with specific specification.
378 :py:class:`pyderasn.ObjectIdentifier` field inside
379 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
380 necessary for decoding structures. For example, CMS (:rfc:`5652`)
383 class ContentInfo(Sequence):
385 ("contentType", ContentType(defines=((("content",), {
386 id_digestedData: DigestedData(),
387 id_signedData: SignedData(),
389 ("content", Any(expl=tag_ctxc(0))),
392 ``contentType`` field tells that it defines that ``content`` must be
393 decoded with ``SignedData`` specification, if ``contentType`` equals to
394 ``id-signedData``. The same applies to ``DigestedData``. If
395 ``contentType`` contains unknown OID, then no automatic decoding is
398 You can specify multiple fields, that will be autodecoded -- that is why
399 ``defines`` kwarg is a sequence. You can specify defined field
400 relatively or absolutely to current decode path. For example ``defines``
401 for AlgorithmIdentifier of X.509's
402 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
406 id_ecPublicKey: ECParameters(),
407 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
409 (("..", "subjectPublicKey"), {
410 id_rsaEncryption: RSAPublicKey(),
411 id_GostR3410_2001: OctetString(),
415 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
416 autodecode its parameters inside SPKI's algorithm and its public key
419 Following types can be automatically decoded (DEFINED BY):
421 * :py:class:`pyderasn.Any`
422 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
423 * :py:class:`pyderasn.OctetString`
424 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
425 ``Any``/``BitString``/``OctetString``-s
427 When any of those fields is automatically decoded, then ``.defined``
428 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
429 was defined, ``value`` contains corresponding decoded value. For example
430 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
432 .. _defines_by_path_ctx:
434 defines_by_path context option
435 ______________________________
437 Sometimes you either can not or do not want to explicitly set *defines*
438 in the schema. You can dynamically apply those definitions when calling
439 :py:meth:`pyderasn.Obj.decode` method.
441 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
442 value must be sequence of following tuples::
444 (decode_path, defines)
446 where ``decode_path`` is a tuple holding so-called decode path to the
447 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
448 ``defines``, holding exactly the same value as accepted in its
449 :ref:`keyword argument <defines>`.
451 For example, again for CMS, you want to automatically decode
452 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
453 structures it may hold. Also, automatically decode ``controlSequence``
456 content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
459 ((("content",), {id_signedData: SignedData()}),),
464 DecodePathDefBy(id_signedData),
469 id_cct_PKIData: PKIData(),
470 id_cct_PKIResponse: PKIResponse(),
476 DecodePathDefBy(id_signedData),
479 DecodePathDefBy(id_cct_PKIResponse),
485 id_cmc_recipientNonce: RecipientNonce(),
486 id_cmc_senderNonce: SenderNonce(),
487 id_cmc_statusInfoV2: CMCStatusInfoV2(),
488 id_cmc_transactionId: TransactionId(),
493 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
494 First function is useful for path construction when some automatic
495 decoding is already done. ``any`` means literally any value it meet --
496 useful for SEQUENCE/SET OF-s.
503 By default PyDERASN accepts only DER encoded data. By default it encodes
504 to DER. But you can optionally enable BER decoding with setting
505 ``bered`` :ref:`context <ctx>` argument to True. Indefinite lengths and
506 constructed primitive types should be parsed successfully.
508 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
509 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
510 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``,
511 ``UTCTime``, ``GeneralizedTime`` can contain it.
512 * If object has an indefinite length encoding, then its ``lenindef``
513 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
514 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
516 * If object has an indefinite length encoded explicit tag, then
517 ``expl_lenindef`` is set to True.
518 * If object has either any of BER-related encoding (explicit tag
519 indefinite length, object's indefinite length, BER-encoding) or any
520 underlying component has that kind of encoding, then ``bered``
521 attribute is set to True. For example SignedData CMS can have
522 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
523 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
525 EOC (end-of-contents) token's length is taken in advance in object's
528 .. _allow_expl_oob_ctx:
530 Allow explicit tag out-of-bound
531 -------------------------------
533 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
534 one value, more than one object. If you set ``allow_expl_oob`` context
535 option to True, then no error will be raised and that invalid encoding
536 will be silently further processed. But pay attention that offsets and
537 lengths will be invalid in that case.
541 This option should be used only for skipping some decode errors, just
542 to see the decoded structure somehow.
546 Streaming and dealing with huge structures
547 ------------------------------------------
554 ASN.1 structures can be huge, they can hold millions of objects inside
555 (for example Certificate Revocation Lists (CRL), holding revocation
556 state for every previously issued X.509 certificate). CACert.org's 8 MiB
557 CRL file takes more than half a gigabyte of memory to hold the decoded
560 If you just simply want to check the signature over the ``tbsCertList``,
561 you can create specialized schema with that field represented as
562 OctetString for example::
564 class TBSCertListFast(Sequence):
567 ("revokedCertificates", OctetString(
568 impl=SequenceOf.tag_default,
574 This allows you to quickly decode a few fields and check the signature
575 over the ``tbsCertList`` bytes.
577 But how can you get all certificate's serial number from it, after you
578 trust that CRL after signature validation? You can use so called
579 ``evgen`` (event generation) mode, to catch the events/facts of some
580 successful object decoding. Let's use command line capabilities::
582 $ python -m pyderasn --schema tests.test_crl:CertificateList --evgen revoke.crl
583 10 [1,1, 1] . . version: Version INTEGER v2 (01) OPTIONAL
584 15 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
585 26 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
586 13 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
587 34 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.10
588 39 [0,0, 9] . . . . . . value: [UNIV 19] AttributeValue ANY
589 32 [1,1, 14] . . . . . 0: AttributeTypeAndValue SEQUENCE
590 30 [1,1, 16] . . . . 0: RelativeDistinguishedName SET OF
592 188 [1,1, 1] . . . . userCertificate: CertificateSerialNumber INTEGER 17 (11)
593 191 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
594 191 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
595 191 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
596 186 [1,1, 18] . . . 0: RevokedCertificate SEQUENCE
597 208 [1,1, 1] . . . . userCertificate: CertificateSerialNumber INTEGER 20 (14)
598 211 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
599 211 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
600 211 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
601 206 [1,1, 18] . . . 1: RevokedCertificate SEQUENCE
603 9144992 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
604 9144992 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2020-02-08T07:25:06
605 9144985 [1,1, 20] . . . 415755: RevokedCertificate SEQUENCE
606 181 [1,4,9144821] . . revokedCertificates: RevokedCertificates SEQUENCE OF OPTIONAL
607 5 [1,4,9144997] . tbsCertList: TBSCertList SEQUENCE
608 9145009 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
609 9145020 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
610 9145007 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
611 9145022 [1,3, 513] . signatureValue: BIT STRING 4096 bits
612 0 [1,4,9145534] CertificateList SEQUENCE
614 Here we see how decoder works: it decodes SEQUENCE's tag, length, then
615 decodes underlying values. It can not tell if SEQUENCE is decoded, so
616 the event of the upper level SEQUENCE is the last one we see.
617 ``version`` field is just a single INTEGER -- it is decoded and event is
618 fired immediately. Then we see that ``algorithm`` and ``parameters``
619 fields are decoded and only after them the ``signature`` SEQUENCE is
620 fired as a successfully decoded. There are 4 events for each revoked
621 certificate entry in that CRL: ``userCertificate`` serial number,
622 ``utcTime`` of ``revocationDate`` CHOICE, ``RevokedCertificate`` itself
623 as a one of entity in ``revokedCertificates`` SEQUENCE OF.
625 We can do that in our ordinary Python code and understand where we are
626 by looking at deterministically generated decode paths (do not forget
627 about useful ``--print-decode-path`` CLI option). We must use
628 :py:meth:`pyderasn.Obj.decode_evgen` method, instead of ordinary
629 :py:meth:`pyderasn.Obj.decode`. It is generator yielding ``(decode_path,
630 obj, tail)`` tuples::
632 for decode_path, obj, _ in CertificateList().decode_evgen(crl_raw):
634 len(decode_path) == 4 and
635 decode_path[:2] == ("tbsCertList", "revokedCertificates"),
636 decode_path[3] == "userCertificate"
638 print("serial number:", int(obj))
640 Virtually it does not take any memory except at least needed for single
641 object storage. You can easily use that mode to determine required
642 object ``.offset`` and ``.*len`` to be able to decode it separately, or
643 maybe verify signature upon it just by taking bytes by ``.offset`` and
646 .. _evgen_mode_upto_ctx:
651 There is full ability to get any kind of data from the CRL in the
652 example above. However it is not too convenient to get the whole
653 ``RevokedCertificate`` structure, that is pretty lightweight and one may
654 do not want to disassemble it. You can use ``evgen_mode_upto``
655 :ref:`ctx <ctx>` option that semantically equals to
656 :ref:`defines_by_path <defines_by_path_ctx>` -- list of decode paths
657 mapped to any non-None value. If specified decode path is met, then any
658 subsequent objects won't be decoded in evgen mode. That allows us to
659 parse the CRL above with fully assembled ``RevokedCertificate``::
661 for decode_path, obj, _ in CertificateList().decode_evgen(
663 ctx={"evgen_mode_upto": (
664 (("tbsCertList", "revokedCertificates", any), True),
668 len(decode_path) == 3 and
669 decode_path[:2] == ("tbsCertList", "revokedCertificates"),
671 print("serial number:", int(obj["userCertificate"]))
675 SEQUENCE/SET values with DEFAULT specified are automatically decoded
683 POSIX compliant systems have ``mmap`` syscall, giving ability to work
684 the memory mapped file. You can deal with the file like it was an
685 ordinary binary string, allowing you not to load it to the memory first.
686 Also you can use them as an input for OCTET STRING, taking no Python
687 memory for their storage.
689 There is convenient :py:func:`pyderasn.file_mmaped` function that
690 creates read-only memoryview on the file contents::
692 with open("huge", "rb") as fd:
693 raw = file_mmaped(fd)
694 obj = Something.decode(raw)
698 mmap maps the **whole** file. So it plays no role if you seek-ed it
699 before. Take the slice of the resulting memoryview with required
704 If you use ZFS as underlying storage, then pay attention that
705 currently most platforms does not deal good with ZFS ARC and ordinary
706 page cache used for mmaps. It can take twice the necessary size in
707 the memory: both in page cache and ZFS ARC.
712 We can parse any kind of data now, but how can we produce files
713 streamingly, without storing their encoded representation in memory?
714 SEQUENCE by default encodes in memory all its values, joins them in huge
715 binary string, just to know the exact size of SEQUENCE's value for
716 encoding it in TLV. DER requires you to know all exact sizes of the
719 You can use CER encoding mode, that slightly differs from the DER, but
720 does not require exact sizes knowledge, allowing streaming encoding
721 directly to some writer/buffer. Just use
722 :py:meth:`pyderasn.Obj.encode_cer` method, providing the writer where
723 encoded data will flow::
725 with open("result", "wb") as fd:
726 obj.encode_cer(fd.write)
731 obj.encode_cer(buf.write)
733 If you do not want to create in-memory buffer every time, then you can
734 use :py:func:`pyderasn.encode_cer` function::
736 data = encode_cer(obj)
738 Remember that CER is **not valid** DER in most cases, so you **have to**
739 use :ref:`bered <bered_ctx>` :ref:`ctx <ctx>` option during its
740 decoding. Also currently there is **no** validation that provided CER is
741 valid one -- you are sure that it has only valid BER encoding.
745 SET OF values can not be streamingly encoded, because they are
746 required to be sorted byte-by-byte. Big SET OF values still will take
747 much memory. Use neither SET nor SET OF values, as modern ASN.1
750 Do not forget about using :ref:`mmap-ed <mmap>` memoryviews for your
751 OCTET STRINGs! They will be streamingly copied from underlying file to
752 the buffer using 1 KB chunks.
754 Some structures require that some of the elements have to be forcefully
755 DER encoded. For example ``SignedData`` CMS requires you to encode
756 ``SignedAttributes`` and X.509 certificates in DER form, allowing you to
757 encode everything else in BER. You can tell any of the structures to be
758 forcefully encoded in DER during CER encoding, by specifying
759 ``der_forced=True`` attribute::
761 class Certificate(Sequence):
765 class SignedAttributes(SetOf):
767 bounds = (1, float("+inf"))
770 .. _agg_octet_string:
775 In most cases, huge quantity of binary data is stored as OCTET STRING.
776 CER encoding splits it on 1 KB chunks. BER allows splitting on various
777 levels of chunks inclusion::
779 SOME STRING[CONSTRUCTED]
780 OCTET STRING[CONSTRUCTED]
781 OCTET STRING[PRIMITIVE]
783 OCTET STRING[PRIMITIVE]
785 OCTET STRING[PRIMITIVE]
787 OCTET STRING[PRIMITIVE]
789 OCTET STRING[CONSTRUCTED]
790 OCTET STRING[PRIMITIVE]
792 OCTET STRING[PRIMITIVE]
794 OCTET STRING[CONSTRUCTED]
795 OCTET STRING[CONSTRUCTED]
796 OCTET STRING[PRIMITIVE]
799 You can not just take the offset and some ``.vlen`` of the STRING and
800 treat it as the payload. If you decode it without
801 :ref:`evgen mode <evgen_mode>`, then it will be automatically aggregated
802 and ``bytes()`` will give the whole payload contents.
804 You are forced to use :ref:`evgen mode <evgen_mode>` for decoding for
805 small memory footprint. There is convenient
806 :py:func:`pyderasn.agg_octet_string` helper for reconstructing the
807 payload. Let's assume you have got BER/CER encoded ``ContentInfo`` with
808 huge ``SignedData`` and ``EncapsulatedContentInfo``. Let's calculate the
809 SHA512 digest of its ``eContent``::
811 fd = open("data.p7m", "rb")
812 raw = file_mmaped(fd)
813 ctx = {"bered": True}
814 for decode_path, obj, _ in ContentInfo().decode_evgen(raw, ctx=ctx):
815 if decode_path == ("content",):
819 raise ValueError("no content found")
820 hasher_state = sha512()
822 hasher_state.update(data)
824 evgens = SignedData().decode_evgen(
825 raw[content.offset:],
826 offset=content.offset,
829 agg_octet_string(evgens, ("encapContentInfo", "eContent"), raw, hasher)
831 digest = hasher_state.digest()
833 Simply replace ``hasher`` with some writeable file's ``fd.write`` to
834 copy the payload (without BER/CER encoding interleaved overhead) in it.
835 Virtually it won't take memory more than for keeping small structures
836 and 1 KB binary chunks.
840 SEQUENCE OF iterators
841 _____________________
843 You can use iterators as a value in :py:class:`pyderasn.SequenceOf`
844 classes. The only difference with providing the full list of objects, is
845 that type and bounds checking is done during encoding process. Also
846 sequence's value will be emptied after encoding, forcing you to set its
849 This is very useful when you have to create some huge objects, like
850 CRLs, with thousands and millions of entities inside. You can write the
851 generator taking necessary data from the database and giving the
852 ``RevokedCertificate`` objects. Only binary representation of that
853 objects will take memory during DER encoding.
858 There is ability to do 2-pass encoding to DER, writing results directly
859 to specified writer (buffer, file, whatever). It could be 1.5+ times
860 slower than ordinary encoding, but it takes little memory for 1st pass
861 state storing. For example, 1st pass state for CACert.org's CRL with
862 ~416K of certificate entries takes nearly 3.5 MB of memory.
863 ``SignedData`` with several gigabyte ``EncapsulatedContentInfo`` takes
864 nearly 0.5 KB of memory.
866 If you use :ref:`mmap-ed <mmap>` memoryviews, :ref:`SEQUENCE OF
867 iterators <seqof-iterators>` and write directly to opened file, then
868 there is very small memory footprint.
870 1st pass traverses through all the objects of the structure and returns
871 the size of DER encoded structure, together with 1st pass state object.
872 That state contains precalculated lengths for various objects inside the
877 fulllen, state = obj.encode1st()
879 2nd pass takes the writer and 1st pass state. It traverses through all
880 the objects again, but writes their encoded representation to the writer.
884 with open("result", "wb") as fd:
885 obj.encode2nd(fd.write, iter(state))
889 You **MUST NOT** use 1st pass state if anything is changed in the
890 objects. It is intended to be used immediately after 1st pass is
893 If you use :ref:`SEQUENCE OF iterators <seqof-iterators>`, then you
894 have to reinitialize the values after the 1st pass. And you **have to**
895 be sure that the iterator gives exactly the same values as previously.
896 Yes, you have to run your iterator twice -- because this is two pass
899 If you want to encode to the memory, then you can use convenient
900 :py:func:`pyderasn.encode2pass` helper.
906 .. autofunction:: pyderasn.browse
910 .. autoclass:: pyderasn.Obj
918 .. autoclass:: pyderasn.Boolean
923 .. autoclass:: pyderasn.Integer
924 :members: __init__, named, tohex
928 .. autoclass:: pyderasn.BitString
929 :members: __init__, bit_len, named
933 .. autoclass:: pyderasn.OctetString
938 .. autoclass:: pyderasn.Null
943 .. autoclass:: pyderasn.ObjectIdentifier
948 .. autoclass:: pyderasn.Enumerated
952 .. autoclass:: pyderasn.CommonString
956 .. autoclass:: pyderasn.NumericString
960 .. autoclass:: pyderasn.PrintableString
961 :members: __init__, allow_asterisk, allow_ampersand
965 .. autoclass:: pyderasn.IA5String
969 .. autoclass:: pyderasn.VisibleString
973 .. autoclass:: pyderasn.UTCTime
974 :members: __init__, todatetime
978 .. autoclass:: pyderasn.GeneralizedTime
979 :members: __init__, todatetime
986 .. autoclass:: pyderasn.Choice
987 :members: __init__, choice, value
991 .. autoclass:: PrimitiveTypes
995 .. autoclass:: pyderasn.Any
1003 .. autoclass:: pyderasn.Sequence
1008 .. autoclass:: pyderasn.Set
1013 .. autoclass:: pyderasn.SequenceOf
1018 .. autoclass:: pyderasn.SetOf
1024 .. autofunction:: pyderasn.abs_decode_path
1025 .. autofunction:: pyderasn.agg_octet_string
1026 .. autofunction:: pyderasn.ascii_visualize
1027 .. autofunction:: pyderasn.colonize_hex
1028 .. autofunction:: pyderasn.encode2pass
1029 .. autofunction:: pyderasn.encode_cer
1030 .. autofunction:: pyderasn.file_mmaped
1031 .. autofunction:: pyderasn.hexenc
1032 .. autofunction:: pyderasn.hexdec
1033 .. autofunction:: pyderasn.hexdump
1034 .. autofunction:: pyderasn.tag_encode
1035 .. autofunction:: pyderasn.tag_decode
1036 .. autofunction:: pyderasn.tag_ctxp
1037 .. autofunction:: pyderasn.tag_ctxc
1038 .. autoclass:: pyderasn.DecodeError
1040 .. autoclass:: pyderasn.NotEnoughData
1041 .. autoclass:: pyderasn.ExceedingData
1042 .. autoclass:: pyderasn.LenIndefForm
1043 .. autoclass:: pyderasn.TagMismatch
1044 .. autoclass:: pyderasn.InvalidLength
1045 .. autoclass:: pyderasn.InvalidOID
1046 .. autoclass:: pyderasn.ObjUnknown
1047 .. autoclass:: pyderasn.ObjNotReady
1048 .. autoclass:: pyderasn.InvalidValueType
1049 .. autoclass:: pyderasn.BoundsError
1056 You can decode DER/BER files using command line abilities::
1058 $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
1060 If there is no schema for your file, then you can try parsing it without,
1061 but of course IMPLICIT tags will often make it impossible. But result is
1062 good enough for the certificate above::
1064 $ python -m pyderasn path/to/file
1065 0 [1,3,1604] . >: SEQUENCE OF
1066 4 [1,3,1453] . . >: SEQUENCE OF
1067 8 [0,0, 5] . . . . >: [0] ANY
1068 . . . . . A0:03:02:01:02
1069 13 [1,1, 3] . . . . >: INTEGER 61595
1070 18 [1,1, 13] . . . . >: SEQUENCE OF
1071 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1072 31 [1,1, 0] . . . . . . >: NULL
1073 33 [1,3, 274] . . . . >: SEQUENCE OF
1074 37 [1,1, 11] . . . . . . >: SET OF
1075 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
1076 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
1077 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
1079 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
1080 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
1081 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
1082 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
1083 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
1084 . . . . . . . . . 61:2E:63:6F:6D:2F
1085 1461 [1,1, 13] . . >: SEQUENCE OF
1086 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1087 1474 [1,1, 0] . . . . >: NULL
1088 1476 [1,2, 129] . . >: BIT STRING 1024 bits
1089 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
1090 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
1096 If you have got dictionaries with ObjectIdentifiers, like example one
1097 from ``tests/test_crts.py``::
1100 "1.2.840.113549.1.1.1": "id-rsaEncryption",
1101 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
1103 "2.5.4.10": "id-at-organizationName",
1104 "2.5.4.11": "id-at-organizationalUnitName",
1107 then you can pass it to pretty printer to see human readable OIDs::
1109 $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
1111 37 [1,1, 11] . . . . . . >: SET OF
1112 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
1113 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
1114 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
1115 50 [1,1, 18] . . . . . . >: SET OF
1116 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
1117 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
1118 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
1119 70 [1,1, 18] . . . . . . >: SET OF
1120 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
1121 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
1122 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
1128 Each decoded element has so-called decode path: sequence of structure
1129 names it is passing during the decode process. Each element has its own
1130 unique path inside the whole ASN.1 tree. You can print it out with
1131 ``--print-decode-path`` option::
1133 $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
1134 0 [1,3,1604] Certificate SEQUENCE []
1135 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
1136 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
1137 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
1138 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
1139 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
1140 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
1142 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
1143 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
1144 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
1145 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
1146 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
1147 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
1148 . . . . . . . 13:02:45:53
1149 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]
1152 Now you can print only the specified tree, for example signature algorithm::
1154 $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
1155 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
1156 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1157 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
1161 from array import array
1162 from codecs import getdecoder
1163 from codecs import getencoder
1164 from collections import namedtuple
1165 from collections import OrderedDict
1166 from copy import copy
1167 from datetime import datetime
1168 from datetime import timedelta
1169 from io import BytesIO
1170 from math import ceil
1171 from operator import attrgetter
1172 from string import ascii_letters
1173 from string import digits
1174 from struct import Struct as struct_Struct
1175 from sys import maxsize as sys_maxsize
1176 from sys import version_info
1177 from unicodedata import category as unicat
1180 from termcolor import colored
1181 except ImportError: # pragma: no cover
1182 def colored(what, *args, **kwargs):
1233 "TagClassApplication",
1236 "TagClassUniversal",
1237 "TagFormConstructed",
1248 TagClassUniversal = 0
1249 TagClassApplication = 1 << 6
1250 TagClassContext = 1 << 7
1251 TagClassPrivate = 1 << 6 | 1 << 7
1252 TagFormPrimitive = 0
1253 TagFormConstructed = 1 << 5
1255 TagClassContext: "",
1256 TagClassApplication: "APPLICATION ",
1257 TagClassPrivate: "PRIVATE ",
1258 TagClassUniversal: "UNIV ",
1262 LENINDEF = b"\x80" # length indefinite mark
1263 LENINDEF_PP_CHAR = "∞"
1264 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
1265 SET01 = frozenset("01")
1266 DECIMALS = frozenset(digits)
1267 DECIMAL_SIGNS = ".,"
1268 NEXT_ATTR_NAME = "__next__"
1271 def file_mmaped(fd):
1272 """Make mmap-ed memoryview for reading from file
1274 :param fd: file object
1275 :returns: memoryview over read-only mmap-ing of the whole file
1279 It does not work under Windows.
1282 return memoryview(mmap.mmap(fd.fileno(), length=0, prot=mmap.PROT_READ))
1286 if not set(value) <= DECIMALS:
1287 raise ValueError("non-pure integer")
1291 def fractions2float(fractions_raw):
1292 pureint(fractions_raw)
1293 return float("0." + fractions_raw)
1296 def get_def_by_path(defines_by_path, sub_decode_path):
1297 """Get define by decode path
1299 for path, define in defines_by_path:
1300 if len(path) != len(sub_decode_path):
1302 for p1, p2 in zip(path, sub_decode_path):
1303 if (p1 is not any) and (p1 != p2):
1309 ########################################################################
1311 ########################################################################
1313 class ASN1Error(ValueError):
1317 class DecodeError(ASN1Error):
1318 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
1320 :param str msg: reason of decode failing
1321 :param klass: optional exact DecodeError inherited class (like
1322 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
1323 :py:exc:`InvalidLength`)
1324 :param decode_path: tuple of strings. It contains human
1325 readable names of the fields through which
1326 decoding process has passed
1327 :param int offset: binary offset where failure happened
1332 self.decode_path = decode_path
1333 self.offset = offset
1338 "" if self.klass is None else self.klass.__name__,
1340 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
1341 if len(self.decode_path) > 0 else ""
1343 ("(at %d)" % self.offset) if self.offset > 0 else "",
1349 return "%s(%s)" % (self.__class__.__name__, self)
1352 class NotEnoughData(DecodeError):
1356 class ExceedingData(ASN1Error):
1357 def __init__(self, nbytes):
1359 self.nbytes = nbytes
1362 return "%d trailing bytes" % self.nbytes
1365 return "%s(%s)" % (self.__class__.__name__, self)
1368 class LenIndefForm(DecodeError):
1372 class TagMismatch(DecodeError):
1376 class InvalidLength(DecodeError):
1380 class InvalidOID(DecodeError):
1384 class ObjUnknown(ASN1Error):
1385 def __init__(self, name):
1390 return "object is unknown: %s" % self.name
1393 return "%s(%s)" % (self.__class__.__name__, self)
1396 class ObjNotReady(ASN1Error):
1397 def __init__(self, name):
1402 return "object is not ready: %s" % self.name
1405 return "%s(%s)" % (self.__class__.__name__, self)
1408 class InvalidValueType(ASN1Error):
1409 def __init__(self, expected_types):
1411 self.expected_types = expected_types
1414 return "invalid value type, expected: %s" % ", ".join(
1415 [repr(t) for t in self.expected_types]
1419 return "%s(%s)" % (self.__class__.__name__, self)
1422 class BoundsError(ASN1Error):
1423 def __init__(self, bound_min, value, bound_max):
1425 self.bound_min = bound_min
1427 self.bound_max = bound_max
1430 return "unsatisfied bounds: %s <= %s <= %s" % (
1437 return "%s(%s)" % (self.__class__.__name__, self)
1440 ########################################################################
1442 ########################################################################
1444 _hexdecoder = getdecoder("hex")
1445 _hexencoder = getencoder("hex")
1449 """Binary data to hexadecimal string convert
1451 return _hexdecoder(data)[0]
1455 """Hexadecimal string to binary data convert
1457 return _hexencoder(data)[0].decode("ascii")
1460 def int_bytes_len(num, byte_len=8):
1463 return int(ceil(float(num.bit_length()) / byte_len))
1466 def zero_ended_encode(num):
1467 octets = bytearray(int_bytes_len(num, 7))
1469 octets[i] = num & 0x7F
1473 octets[i] = 0x80 | (num & 0x7F)
1476 return bytes(octets)
1479 int2byte = struct_Struct(">B").pack
1482 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
1483 """Encode tag to binary form
1485 :param int num: tag's number
1486 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
1487 :py:data:`pyderasn.TagClassContext`,
1488 :py:data:`pyderasn.TagClassApplication`,
1489 :py:data:`pyderasn.TagClassPrivate`)
1490 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
1491 :py:data:`pyderasn.TagFormConstructed`)
1495 return int2byte(klass | form | num)
1496 # [XX|X|11111][1.......][1.......] ... [0.......]
1497 return int2byte(klass | form | 31) + zero_ended_encode(num)
1500 def tag_decode(tag):
1501 """Decode tag from binary form
1505 No validation is performed, assuming that it has already passed.
1507 It returns tuple with three integers, as
1508 :py:func:`pyderasn.tag_encode` accepts.
1510 first_octet = tag[0]
1511 klass = first_octet & 0xC0
1512 form = first_octet & 0x20
1513 if first_octet & 0x1F < 0x1F:
1514 return (klass, form, first_octet & 0x1F)
1516 for octet in tag[1:]:
1519 return (klass, form, num)
1523 """Create CONTEXT PRIMITIVE tag
1525 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
1529 """Create CONTEXT CONSTRUCTED tag
1531 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
1534 def tag_strip(data):
1535 """Take off tag from the data
1537 :returns: (encoded tag, tag length, remaining data)
1540 raise NotEnoughData("no data at all")
1541 if data[0] & 0x1F < 31:
1542 return data[:1], 1, data[1:]
1547 raise DecodeError("unfinished tag")
1548 if data[i] & 0x80 == 0:
1550 if i == 1 and data[1] < 0x1F:
1551 raise DecodeError("unexpected long form")
1552 if i > 1 and data[1] & 0x7F == 0:
1553 raise DecodeError("leading zero byte in tag value")
1555 return data[:i], i, data[i:]
1561 octets = bytearray(int_bytes_len(l) + 1)
1562 octets[0] = 0x80 | (len(octets) - 1)
1563 for i in range(len(octets) - 1, 0, -1):
1564 octets[i] = l & 0xFF
1566 return bytes(octets)
1569 def len_decode(data):
1572 :returns: (decoded length, length's length, remaining data)
1573 :raises LenIndefForm: if indefinite form encoding is met
1576 raise NotEnoughData("no data at all")
1577 first_octet = data[0]
1578 if first_octet & 0x80 == 0:
1579 return first_octet, 1, data[1:]
1580 octets_num = first_octet & 0x7F
1581 if octets_num + 1 > len(data):
1582 raise NotEnoughData("encoded length is longer than data")
1584 raise LenIndefForm()
1586 raise DecodeError("leading zeros")
1588 for v in data[1:1 + octets_num]:
1591 raise DecodeError("long form instead of short one")
1592 return l, 1 + octets_num, data[1 + octets_num:]
1595 LEN0 = len_encode(0)
1596 LEN1 = len_encode(1)
1597 LEN1K = len_encode(1000)
1601 """How many bytes length field will take
1605 if l < 256: # 1 << 8
1607 if l < 65536: # 1 << 16
1609 if l < 16777216: # 1 << 24
1611 if l < 4294967296: # 1 << 32
1613 if l < 1099511627776: # 1 << 40
1615 if l < 281474976710656: # 1 << 48
1617 if l < 72057594037927936: # 1 << 56
1619 raise OverflowError("too big length")
1622 def write_full(writer, data):
1623 """Fully write provided data
1625 :param writer: must comply with ``io.RawIOBase.write`` behaviour
1627 BytesIO does not guarantee that the whole data will be written at
1628 once. That function write everything provided, raising an error if
1629 ``writer`` returns None.
1631 data = memoryview(data)
1633 while written != len(data):
1634 n = writer(data[written:])
1636 raise ValueError("can not write to buf")
1640 # If it is 64-bit system, then use compact 64-bit array of unsigned
1641 # longs. Use an ordinary list with universal integers otherwise, that
1643 if sys_maxsize > 2 ** 32:
1644 def state_2pass_new():
1647 def state_2pass_new():
1651 ########################################################################
1653 ########################################################################
1655 class AutoAddSlots(type):
1656 def __new__(cls, name, bases, _dict):
1657 _dict["__slots__"] = _dict.get("__slots__", ())
1658 return super().__new__(cls, name, bases, _dict)
1661 BasicState = namedtuple("BasicState", (
1674 ), **NAMEDTUPLE_KWARGS)
1677 class Obj(metaclass=AutoAddSlots):
1678 """Common ASN.1 object class
1680 All ASN.1 types are inherited from it. It has metaclass that
1681 automatically adds ``__slots__`` to all inherited classes.
1706 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1707 self._expl = getattr(self, "expl", None) if expl is None else expl
1708 if self.tag != self.tag_default and self._expl is not None:
1709 raise ValueError("implicit and explicit tags can not be set simultaneously")
1710 if self.tag is None:
1711 self._tag_order = None
1713 tag_class, _, tag_num = tag_decode(
1714 self.tag if self._expl is None else self._expl
1716 self._tag_order = (tag_class, tag_num)
1717 if default is not None:
1719 self.optional = optional
1720 self.offset, self.llen, self.vlen = _decoded
1722 self.expl_lenindef = False
1723 self.lenindef = False
1724 self.ber_encoded = False
1727 def ready(self): # pragma: no cover
1728 """Is object ready to be encoded?
1730 raise NotImplementedError()
1732 def _assert_ready(self):
1734 raise ObjNotReady(self.__class__.__name__)
1738 """Is either object or any elements inside is BER encoded?
1740 return self.expl_lenindef or self.lenindef or self.ber_encoded
1744 """Is object decoded?
1746 return (self.llen + self.vlen) > 0
1748 def __getstate__(self): # pragma: no cover
1749 """Used for making safe to be mutable pickleable copies
1751 raise NotImplementedError()
1753 def __setstate__(self, state):
1754 if state.version != __version__:
1755 raise ValueError("data is pickled by different PyDERASN version")
1756 self.tag = state.tag
1757 self._tag_order = state.tag_order
1758 self._expl = state.expl
1759 self.default = state.default
1760 self.optional = state.optional
1761 self.offset = state.offset
1762 self.llen = state.llen
1763 self.vlen = state.vlen
1764 self.expl_lenindef = state.expl_lenindef
1765 self.lenindef = state.lenindef
1766 self.ber_encoded = state.ber_encoded
1769 def tag_order(self):
1770 """Tag's (class, number) used for DER/CER sorting
1772 return self._tag_order
1775 def tag_order_cer(self):
1776 return self.tag_order
1780 """.. seealso:: :ref:`decoding`
1782 return len(self.tag)
1786 """.. seealso:: :ref:`decoding`
1788 return self.tlen + self.llen + self.vlen
1790 def __str__(self): # pragma: no cover
1791 return self.__unicode__()
1793 def __ne__(self, their):
1794 return not(self == their)
1796 def __gt__(self, their): # pragma: no cover
1797 return not(self < their)
1799 def __le__(self, their): # pragma: no cover
1800 return (self == their) or (self < their)
1802 def __ge__(self, their): # pragma: no cover
1803 return (self == their) or (self > their)
1805 def _encode(self): # pragma: no cover
1806 raise NotImplementedError()
1808 def _encode_cer(self, writer):
1809 write_full(writer, self._encode())
1811 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode): # pragma: no cover
1812 yield NotImplemented
1814 def _encode1st(self, state):
1815 raise NotImplementedError()
1817 def _encode2nd(self, writer, state_iter):
1818 raise NotImplementedError()
1821 """DER encode the structure
1823 :returns: DER representation
1825 raw = self._encode()
1826 if self._expl is None:
1828 return b"".join((self._expl, len_encode(len(raw)), raw))
1830 def encode1st(self, state=None):
1831 """Do the 1st pass of 2-pass encoding
1833 :rtype: (int, array("L"))
1834 :returns: full length of encoded data and precalculated various
1838 state = state_2pass_new()
1839 if self._expl is None:
1840 return self._encode1st(state)
1842 idx = len(state) - 1
1843 vlen, _ = self._encode1st(state)
1845 fulllen = len(self._expl) + len_size(vlen) + vlen
1846 return fulllen, state
1848 def encode2nd(self, writer, state_iter):
1849 """Do the 2nd pass of 2-pass encoding
1851 :param writer: must comply with ``io.RawIOBase.write`` behaviour
1852 :param state_iter: iterator over the 1st pass state (``iter(state)``)
1854 if self._expl is None:
1855 self._encode2nd(writer, state_iter)
1857 write_full(writer, self._expl + len_encode(next(state_iter)))
1858 self._encode2nd(writer, state_iter)
1860 def encode_cer(self, writer):
1861 """CER encode the structure to specified writer
1863 :param writer: must comply with ``io.RawIOBase.write``
1864 behaviour. It takes slice to be written and
1865 returns number of bytes processed. If it returns
1866 None, then exception will be raised
1868 if self._expl is not None:
1869 write_full(writer, self._expl + LENINDEF)
1870 if getattr(self, "der_forced", False):
1871 write_full(writer, self._encode())
1873 self._encode_cer(writer)
1874 if self._expl is not None:
1875 write_full(writer, EOC)
1877 def hexencode(self):
1878 """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1880 return hexenc(self.encode())
1890 _ctx_immutable=True,
1894 :param data: either binary or memoryview
1895 :param int offset: initial data's offset
1896 :param bool leavemm: do we need to leave memoryview of remaining
1897 data as is, or convert it to bytes otherwise
1898 :param decode_path: current decode path (tuples of strings,
1899 possibly with DecodePathDefBy) with will be
1900 the root for all underlying objects
1901 :param ctx: optional :ref:`context <ctx>` governing decoding process
1902 :param bool tag_only: decode only the tag, without length and
1903 contents (used only in Choice and Set
1904 structures, trying to determine if tag satisfies
1906 :param bool _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1908 :returns: (Obj, remaining data)
1910 .. seealso:: :ref:`decoding`
1912 result = next(self.decode_evgen(
1924 _, obj, tail = result
1935 _ctx_immutable=True,
1938 """Decode with evgen mode on
1940 That method is identical to :py:meth:`pyderasn.Obj.decode`, but
1941 it returns the generator producing ``(decode_path, obj, tail)``
1943 .. seealso:: :ref:`evgen mode <evgen_mode>`.
1947 elif _ctx_immutable:
1949 tlv = memoryview(data)
1952 get_def_by_path(ctx.get("evgen_mode_upto", ()), decode_path) is not None
1955 if self._expl is None:
1956 for result in self._decode(
1959 decode_path=decode_path,
1962 evgen_mode=_evgen_mode,
1967 _decode_path, obj, tail = result
1968 if _decode_path is not decode_path:
1972 t, tlen, lv = tag_strip(tlv)
1973 except DecodeError as err:
1974 raise err.__class__(
1976 klass=self.__class__,
1977 decode_path=decode_path,
1982 klass=self.__class__,
1983 decode_path=decode_path,
1987 l, llen, v = len_decode(lv)
1988 except LenIndefForm as err:
1989 if not ctx.get("bered", False):
1990 raise err.__class__(
1992 klass=self.__class__,
1993 decode_path=decode_path,
1997 offset += tlen + llen
1998 for result in self._decode(
2001 decode_path=decode_path,
2004 evgen_mode=_evgen_mode,
2006 if tag_only: # pragma: no cover
2009 _decode_path, obj, tail = result
2010 if _decode_path is not decode_path:
2012 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
2013 if eoc_expected.tobytes() != EOC:
2016 klass=self.__class__,
2017 decode_path=decode_path,
2021 obj.expl_lenindef = True
2022 except DecodeError as err:
2023 raise err.__class__(
2025 klass=self.__class__,
2026 decode_path=decode_path,
2031 raise NotEnoughData(
2032 "encoded length is longer than data",
2033 klass=self.__class__,
2034 decode_path=decode_path,
2037 for result in self._decode(
2039 offset=offset + tlen + llen,
2040 decode_path=decode_path,
2043 evgen_mode=_evgen_mode,
2045 if tag_only: # pragma: no cover
2048 _decode_path, obj, tail = result
2049 if _decode_path is not decode_path:
2051 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
2053 "explicit tag out-of-bound, longer than data",
2054 klass=self.__class__,
2055 decode_path=decode_path,
2058 yield decode_path, obj, (tail if leavemm else tail.tobytes())
2060 def decod(self, data, offset=0, decode_path=(), ctx=None):
2061 """Decode the data, check that tail is empty
2063 :raises ExceedingData: if tail is not empty
2065 This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
2066 (decode without tail) that also checks that there is no
2069 obj, tail = self.decode(
2072 decode_path=decode_path,
2077 raise ExceedingData(len(tail))
2080 def hexdecode(self, data, *args, **kwargs):
2081 """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
2083 return self.decode(hexdec(data), *args, **kwargs)
2085 def hexdecod(self, data, *args, **kwargs):
2086 """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
2088 return self.decod(hexdec(data), *args, **kwargs)
2092 """.. seealso:: :ref:`decoding`
2094 return self._expl is not None
2098 """.. seealso:: :ref:`decoding`
2103 def expl_tlen(self):
2104 """.. seealso:: :ref:`decoding`
2106 return len(self._expl)
2109 def expl_llen(self):
2110 """.. seealso:: :ref:`decoding`
2112 if self.expl_lenindef:
2114 return len(len_encode(self.tlvlen))
2117 def expl_offset(self):
2118 """.. seealso:: :ref:`decoding`
2120 return self.offset - self.expl_tlen - self.expl_llen
2123 def expl_vlen(self):
2124 """.. seealso:: :ref:`decoding`
2129 def expl_tlvlen(self):
2130 """.. seealso:: :ref:`decoding`
2132 return self.expl_tlen + self.expl_llen + self.expl_vlen
2135 def fulloffset(self):
2136 """.. seealso:: :ref:`decoding`
2138 return self.expl_offset if self.expled else self.offset
2142 """.. seealso:: :ref:`decoding`
2144 return self.expl_tlvlen if self.expled else self.tlvlen
2146 def pps_lenindef(self, decode_path):
2147 if self.lenindef and not (
2148 getattr(self, "defined", None) is not None and
2149 self.defined[1].lenindef
2152 asn1_type_name="EOC",
2154 decode_path=decode_path,
2156 self.offset + self.tlvlen -
2157 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
2165 if self.expl_lenindef:
2167 asn1_type_name="EOC",
2168 obj_name="EXPLICIT",
2169 decode_path=decode_path,
2170 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
2179 def encode_cer(obj):
2180 """Encode to CER in memory buffer
2182 :returns bytes: memory buffer contents
2185 obj.encode_cer(buf.write)
2186 return buf.getvalue()
2189 def encode2pass(obj):
2190 """Encode (2-pass mode) to DER in memory buffer
2192 :returns bytes: memory buffer contents
2195 _, state = obj.encode1st()
2196 obj.encode2nd(buf.write, iter(state))
2197 return buf.getvalue()
2200 class DecodePathDefBy:
2201 """DEFINED BY representation inside decode path
2203 __slots__ = ("defined_by",)
2205 def __init__(self, defined_by):
2206 self.defined_by = defined_by
2208 def __ne__(self, their):
2209 return not(self == their)
2211 def __eq__(self, their):
2212 if not isinstance(their, self.__class__):
2214 return self.defined_by == their.defined_by
2217 return "DEFINED BY " + str(self.defined_by)
2220 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
2223 ########################################################################
2225 ########################################################################
2227 PP = namedtuple("PP", (
2250 ), **NAMEDTUPLE_KWARGS)
2255 asn1_type_name="unknown",
2272 expl_lenindef=False,
2303 def _colourize(what, colour, with_colours, attrs=("bold",)):
2304 return colored(what, colour, attrs=attrs) if with_colours else what
2307 def colonize_hex(hexed):
2308 """Separate hexadecimal string with colons
2310 return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
2313 def find_oid_name(asn1_type_name, oid_maps, value):
2314 if len(oid_maps) > 0 and asn1_type_name == ObjectIdentifier.asn1_type_name:
2315 for oid_map in oid_maps:
2316 oid_name = oid_map.get(value)
2317 if oid_name is not None:
2328 with_decode_path=False,
2329 decode_path_len_decrease=0,
2336 " " if pp.expl_offset is None else
2337 ("-%d" % (pp.offset - pp.expl_offset))
2339 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
2341 col = _colourize(col, "red", with_colours, ())
2342 col += _colourize("B", "red", with_colours) if pp.bered else " "
2344 col = "[%d,%d,%4d]%s" % (
2345 pp.tlen, pp.llen, pp.vlen,
2346 LENINDEF_PP_CHAR if pp.lenindef else " "
2348 col = _colourize(col, "green", with_colours, ())
2350 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2351 if decode_path_len > 0:
2352 cols.append(" ." * decode_path_len)
2353 ent = pp.decode_path[-1]
2354 if isinstance(ent, DecodePathDefBy):
2355 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
2356 value = str(ent.defined_by)
2357 oid_name = find_oid_name(ent.defined_by.asn1_type_name, oid_maps, value)
2358 if oid_name is None:
2359 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
2361 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
2363 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
2364 if pp.expl is not None:
2365 klass, _, num = pp.expl
2366 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
2367 cols.append(_colourize(col, "blue", with_colours))
2368 if pp.impl is not None:
2369 klass, _, num = pp.impl
2370 col = "[%s%d]" % (TagClassReprs[klass], num)
2371 cols.append(_colourize(col, "blue", with_colours))
2372 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
2373 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
2375 cols.append(_colourize("BER", "red", with_colours))
2376 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
2377 if pp.value is not None:
2379 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
2380 oid_name = find_oid_name(pp.asn1_type_name, oid_maps, pp.value)
2381 if oid_name is not None:
2382 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
2383 if pp.asn1_type_name == Integer.asn1_type_name:
2384 cols.append(_colourize(
2385 "(%s)" % colonize_hex(pp.obj.tohex()), "green", with_colours,
2388 if pp.blob.__class__ == bytes:
2389 cols.append(hexenc(pp.blob))
2390 elif pp.blob.__class__ == tuple:
2391 cols.append(", ".join(pp.blob))
2393 cols.append(_colourize("OPTIONAL", "red", with_colours))
2395 cols.append(_colourize("DEFAULT", "red", with_colours))
2396 if with_decode_path:
2397 cols.append(_colourize(
2398 "[%s]" % ":".join(str(p) for p in pp.decode_path),
2402 return " ".join(cols)
2405 def pp_console_blob(pp, decode_path_len_decrease=0):
2406 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
2407 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2408 if decode_path_len > 0:
2409 cols.append(" ." * (decode_path_len + 1))
2410 if pp.blob.__class__ == bytes:
2411 blob = hexenc(pp.blob).upper()
2412 for i in range(0, len(blob), 32):
2413 chunk = blob[i:i + 32]
2414 yield " ".join(cols + [colonize_hex(chunk)])
2415 elif pp.blob.__class__ == tuple:
2416 yield " ".join(cols + [", ".join(pp.blob)])
2424 with_decode_path=False,
2425 decode_path_only=(),
2428 """Pretty print object
2430 :param Obj obj: object you want to pretty print
2431 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
2432 Its human readable form is printed when OID is met
2433 :param big_blobs: if large binary objects are met (like OctetString
2434 values), do we need to print them too, on separate
2436 :param with_colours: colourize output, if ``termcolor`` library
2438 :param with_decode_path: print decode path
2439 :param decode_path_only: print only that specified decode path
2441 def _pprint_pps(pps):
2443 if hasattr(pp, "_fields"):
2445 decode_path_only != () and
2447 str(p) for p in pp.decode_path[:len(decode_path_only)]
2448 ) != decode_path_only
2452 yield pp_console_row(
2457 with_colours=with_colours,
2458 with_decode_path=with_decode_path,
2459 decode_path_len_decrease=len(decode_path_only),
2461 for row in pp_console_blob(
2463 decode_path_len_decrease=len(decode_path_only),
2467 yield pp_console_row(
2472 with_colours=with_colours,
2473 with_decode_path=with_decode_path,
2474 decode_path_len_decrease=len(decode_path_only),
2477 for row in _pprint_pps(pp):
2479 return "\n".join(_pprint_pps(obj.pps(decode_path)))
2482 ########################################################################
2483 # ASN.1 primitive types
2484 ########################################################################
2486 BooleanState = namedtuple(
2488 BasicState._fields + ("value",),
2494 """``BOOLEAN`` boolean type
2496 >>> b = Boolean(True)
2498 >>> b == Boolean(True)
2504 tag_default = tag_encode(1)
2505 asn1_type_name = "BOOLEAN"
2517 :param value: set the value. Either boolean type, or
2518 :py:class:`pyderasn.Boolean` object
2519 :param bytes impl: override default tag with ``IMPLICIT`` one
2520 :param bytes expl: override default tag with ``EXPLICIT`` one
2521 :param default: set default value. Type same as in ``value``
2522 :param bool optional: is object ``OPTIONAL`` in sequence
2524 super().__init__(impl, expl, default, optional, _decoded)
2525 self._value = None if value is None else self._value_sanitize(value)
2526 if default is not None:
2527 default = self._value_sanitize(default)
2528 self.default = self.__class__(
2534 self._value = default
2536 def _value_sanitize(self, value):
2537 if value.__class__ == bool:
2539 if issubclass(value.__class__, Boolean):
2541 raise InvalidValueType((self.__class__, bool))
2545 return self._value is not None
2547 def __getstate__(self):
2548 return BooleanState(
2564 def __setstate__(self, state):
2565 super().__setstate__(state)
2566 self._value = state.value
2568 def __nonzero__(self):
2569 self._assert_ready()
2573 self._assert_ready()
2576 def __eq__(self, their):
2577 if their.__class__ == bool:
2578 return self._value == their
2579 if not issubclass(their.__class__, Boolean):
2582 self._value == their._value and
2583 self.tag == their.tag and
2584 self._expl == their._expl
2595 return self.__class__(
2597 impl=self.tag if impl is None else impl,
2598 expl=self._expl if expl is None else expl,
2599 default=self.default if default is None else default,
2600 optional=self.optional if optional is None else optional,
2604 self._assert_ready()
2605 return b"".join((self.tag, LEN1, (b"\xFF" if self._value else b"\x00")))
2607 def _encode1st(self, state):
2608 return len(self.tag) + 2, state
2610 def _encode2nd(self, writer, state_iter):
2611 self._assert_ready()
2612 write_full(writer, self._encode())
2614 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2616 t, _, lv = tag_strip(tlv)
2617 except DecodeError as err:
2618 raise err.__class__(
2620 klass=self.__class__,
2621 decode_path=decode_path,
2626 klass=self.__class__,
2627 decode_path=decode_path,
2634 l, _, v = len_decode(lv)
2635 except DecodeError as err:
2636 raise err.__class__(
2638 klass=self.__class__,
2639 decode_path=decode_path,
2643 raise InvalidLength(
2644 "Boolean's length must be equal to 1",
2645 klass=self.__class__,
2646 decode_path=decode_path,
2650 raise NotEnoughData(
2651 "encoded length is longer than data",
2652 klass=self.__class__,
2653 decode_path=decode_path,
2658 if first_octet == 0:
2660 elif first_octet == 0xFF:
2662 elif ctx.get("bered", False):
2667 "unacceptable Boolean value",
2668 klass=self.__class__,
2669 decode_path=decode_path,
2672 obj = self.__class__(
2676 default=self.default,
2677 optional=self.optional,
2678 _decoded=(offset, 1, 1),
2680 obj.ber_encoded = ber_encoded
2681 yield decode_path, obj, v[1:]
2684 return pp_console_row(next(self.pps()))
2686 def pps(self, decode_path=()):
2689 asn1_type_name=self.asn1_type_name,
2690 obj_name=self.__class__.__name__,
2691 decode_path=decode_path,
2692 value=str(self._value) if self.ready else None,
2693 optional=self.optional,
2694 default=self == self.default,
2695 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2696 expl=None if self._expl is None else tag_decode(self._expl),
2701 expl_offset=self.expl_offset if self.expled else None,
2702 expl_tlen=self.expl_tlen if self.expled else None,
2703 expl_llen=self.expl_llen if self.expled else None,
2704 expl_vlen=self.expl_vlen if self.expled else None,
2705 expl_lenindef=self.expl_lenindef,
2706 ber_encoded=self.ber_encoded,
2709 for pp in self.pps_lenindef(decode_path):
2713 IntegerState = namedtuple(
2715 BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2721 """``INTEGER`` integer type
2723 >>> b = Integer(-123)
2725 >>> b == Integer(-123)
2730 >>> Integer(2, bounds=(1, 3))
2732 >>> Integer(5, bounds=(1, 3))
2733 Traceback (most recent call last):
2734 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2738 class Version(Integer):
2745 >>> v = Version("v1")
2752 {'v3': 2, 'v1': 0, 'v2': 1}
2754 __slots__ = ("specs", "_bound_min", "_bound_max")
2755 tag_default = tag_encode(2)
2756 asn1_type_name = "INTEGER"
2770 :param value: set the value. Either integer type, named value
2771 (if ``schema`` is specified in the class), or
2772 :py:class:`pyderasn.Integer` object
2773 :param bounds: set ``(MIN, MAX)`` value constraint.
2774 (-inf, +inf) by default
2775 :param bytes impl: override default tag with ``IMPLICIT`` one
2776 :param bytes expl: override default tag with ``EXPLICIT`` one
2777 :param default: set default value. Type same as in ``value``
2778 :param bool optional: is object ``OPTIONAL`` in sequence
2780 super().__init__(impl, expl, default, optional, _decoded)
2782 specs = getattr(self, "schema", {}) if _specs is None else _specs
2783 self.specs = specs if specs.__class__ == dict else dict(specs)
2784 self._bound_min, self._bound_max = getattr(
2787 (float("-inf"), float("+inf")),
2788 ) if bounds is None else bounds
2789 if value is not None:
2790 self._value = self._value_sanitize(value)
2791 if default is not None:
2792 default = self._value_sanitize(default)
2793 self.default = self.__class__(
2799 if self._value is None:
2800 self._value = default
2802 def _value_sanitize(self, value):
2803 if isinstance(value, int):
2805 elif issubclass(value.__class__, Integer):
2806 value = value._value
2807 elif value.__class__ == str:
2808 value = self.specs.get(value)
2810 raise ObjUnknown("integer value: %s" % value)
2812 raise InvalidValueType((self.__class__, int, str))
2813 if not self._bound_min <= value <= self._bound_max:
2814 raise BoundsError(self._bound_min, value, self._bound_max)
2819 return self._value is not None
2821 def __getstate__(self):
2822 return IntegerState(
2841 def __setstate__(self, state):
2842 super().__setstate__(state)
2843 self.specs = state.specs
2844 self._value = state.value
2845 self._bound_min = state.bound_min
2846 self._bound_max = state.bound_max
2849 self._assert_ready()
2850 return int(self._value)
2853 """Hexadecimal representation
2855 Use :py:func:`pyderasn.colonize_hex` for colonizing it.
2857 hex_repr = hex(int(self))[2:].upper()
2858 if len(hex_repr) % 2 != 0:
2859 hex_repr = "0" + hex_repr
2863 self._assert_ready()
2864 return hash(b"".join((
2866 bytes(self._expl or b""),
2867 str(self._value).encode("ascii"),
2870 def __eq__(self, their):
2871 if isinstance(their, int):
2872 return self._value == their
2873 if not issubclass(their.__class__, Integer):
2876 self._value == their._value and
2877 self.tag == their.tag and
2878 self._expl == their._expl
2881 def __lt__(self, their):
2882 return self._value < their._value
2886 """Return named representation (if exists) of the value
2888 for name, value in self.specs.items():
2889 if value == self._value:
2902 return self.__class__(
2905 (self._bound_min, self._bound_max)
2906 if bounds is None else bounds
2908 impl=self.tag if impl is None else impl,
2909 expl=self._expl if expl is None else expl,
2910 default=self.default if default is None else default,
2911 optional=self.optional if optional is None else optional,
2915 def _encode_payload(self):
2916 self._assert_ready()
2918 bytes_len = ceil(value.bit_length() / 8) or 1
2921 octets = value.to_bytes(bytes_len, byteorder="big", signed=True)
2922 except OverflowError:
2929 octets = self._encode_payload()
2930 return b"".join((self.tag, len_encode(len(octets)), octets))
2932 def _encode1st(self, state):
2933 l = len(self._encode_payload())
2934 return len(self.tag) + len_size(l) + l, state
2936 def _encode2nd(self, writer, state_iter):
2937 write_full(writer, self._encode())
2939 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2941 t, _, lv = tag_strip(tlv)
2942 except DecodeError as err:
2943 raise err.__class__(
2945 klass=self.__class__,
2946 decode_path=decode_path,
2951 klass=self.__class__,
2952 decode_path=decode_path,
2959 l, llen, v = len_decode(lv)
2960 except DecodeError as err:
2961 raise err.__class__(
2963 klass=self.__class__,
2964 decode_path=decode_path,
2968 raise NotEnoughData(
2969 "encoded length is longer than data",
2970 klass=self.__class__,
2971 decode_path=decode_path,
2975 raise NotEnoughData(
2977 klass=self.__class__,
2978 decode_path=decode_path,
2981 v, tail = v[:l], v[l:]
2986 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2987 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2990 "non normalized integer",
2991 klass=self.__class__,
2992 decode_path=decode_path,
2995 value = int.from_bytes(v, byteorder="big", signed=True)
2997 obj = self.__class__(
2999 bounds=(self._bound_min, self._bound_max),
3002 default=self.default,
3003 optional=self.optional,
3005 _decoded=(offset, llen, l),
3007 except BoundsError as err:
3010 klass=self.__class__,
3011 decode_path=decode_path,
3014 yield decode_path, obj, tail
3017 return pp_console_row(next(self.pps()))
3019 def pps(self, decode_path=()):
3022 asn1_type_name=self.asn1_type_name,
3023 obj_name=self.__class__.__name__,
3024 decode_path=decode_path,
3025 value=(self.named or str(self._value)) if self.ready else None,
3026 optional=self.optional,
3027 default=self == self.default,
3028 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3029 expl=None if self._expl is None else tag_decode(self._expl),
3034 expl_offset=self.expl_offset if self.expled else None,
3035 expl_tlen=self.expl_tlen if self.expled else None,
3036 expl_llen=self.expl_llen if self.expled else None,
3037 expl_vlen=self.expl_vlen if self.expled else None,
3038 expl_lenindef=self.expl_lenindef,
3041 for pp in self.pps_lenindef(decode_path):
3045 BitStringState = namedtuple(
3047 BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
3052 class BitString(Obj):
3053 """``BIT STRING`` bit string type
3055 >>> BitString(b"hello world")
3056 BIT STRING 88 bits 68656c6c6f20776f726c64
3059 >>> b == b"hello world"
3064 >>> BitString("'0A3B5F291CD'H")
3065 BIT STRING 44 bits 0a3b5f291cd0
3066 >>> b = BitString("'010110000000'B")
3067 BIT STRING 12 bits 5800
3070 >>> b[0], b[1], b[2], b[3]
3071 (False, True, False, True)
3075 [False, True, False, True, True, False, False, False, False, False, False, False]
3079 class KeyUsage(BitString):
3081 ("digitalSignature", 0),
3082 ("nonRepudiation", 1),
3083 ("keyEncipherment", 2),
3086 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
3087 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
3089 ['nonRepudiation', 'keyEncipherment']
3091 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
3095 Pay attention that BIT STRING can be encoded both in primitive
3096 and constructed forms. Decoder always checks constructed form tag
3097 additionally to specified primitive one. If BER decoding is
3098 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
3099 of DER restrictions.
3101 __slots__ = ("tag_constructed", "specs", "defined")
3102 tag_default = tag_encode(3)
3103 asn1_type_name = "BIT STRING"
3116 :param value: set the value. Either binary type, tuple of named
3117 values (if ``schema`` is specified in the class),
3118 string in ``'XXX...'B`` form, or
3119 :py:class:`pyderasn.BitString` object
3120 :param bytes impl: override default tag with ``IMPLICIT`` one
3121 :param bytes expl: override default tag with ``EXPLICIT`` one
3122 :param default: set default value. Type same as in ``value``
3123 :param bool optional: is object ``OPTIONAL`` in sequence
3125 super().__init__(impl, expl, default, optional, _decoded)
3126 specs = getattr(self, "schema", {}) if _specs is None else _specs
3127 self.specs = specs if specs.__class__ == dict else dict(specs)
3128 self._value = None if value is None else self._value_sanitize(value)
3129 if default is not None:
3130 default = self._value_sanitize(default)
3131 self.default = self.__class__(
3137 self._value = default
3139 tag_klass, _, tag_num = tag_decode(self.tag)
3140 self.tag_constructed = tag_encode(
3142 form=TagFormConstructed,
3146 def _bits2octets(self, bits):
3147 if len(self.specs) > 0:
3148 bits = bits.rstrip("0")
3150 bits += "0" * ((8 - (bit_len % 8)) % 8)
3151 octets = bytearray(len(bits) // 8)
3152 for i in range(len(octets)):
3153 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
3154 return bit_len, bytes(octets)
3156 def _value_sanitize(self, value):
3157 if isinstance(value, (str, bytes)):
3159 isinstance(value, str) and
3160 value.startswith("'")
3162 if value.endswith("'B"):
3164 if not frozenset(value) <= SET01:
3165 raise ValueError("B's coding contains unacceptable chars")
3166 return self._bits2octets(value)
3167 if value.endswith("'H"):
3171 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
3173 if value.__class__ == bytes:
3174 return (len(value) * 8, value)
3175 raise InvalidValueType((self.__class__, str, bytes))
3176 if value.__class__ == tuple:
3179 isinstance(value[0], int) and
3180 value[1].__class__ == bytes
3185 bit = self.specs.get(name)
3187 raise ObjUnknown("BitString value: %s" % name)
3190 return self._bits2octets("")
3191 bits = frozenset(bits)
3192 return self._bits2octets("".join(
3193 ("1" if bit in bits else "0")
3194 for bit in range(max(bits) + 1)
3196 if issubclass(value.__class__, BitString):
3198 raise InvalidValueType((self.__class__, bytes, str))
3202 return self._value is not None
3204 def __getstate__(self):
3205 return BitStringState(
3220 self.tag_constructed,
3224 def __setstate__(self, state):
3225 super().__setstate__(state)
3226 self.specs = state.specs
3227 self._value = state.value
3228 self.tag_constructed = state.tag_constructed
3229 self.defined = state.defined
3232 self._assert_ready()
3233 for i in range(self._value[0]):
3238 """Returns number of bits in the string
3240 self._assert_ready()
3241 return self._value[0]
3243 def __bytes__(self):
3244 self._assert_ready()
3245 return self._value[1]
3247 def __eq__(self, their):
3248 if their.__class__ == bytes:
3249 return self._value[1] == their
3250 if not issubclass(their.__class__, BitString):
3253 self._value == their._value and
3254 self.tag == their.tag and
3255 self._expl == their._expl
3260 """Named representation (if exists) of the bits
3262 :returns: [str(name), ...]
3264 return [name for name, bit in self.specs.items() if self[bit]]
3274 return self.__class__(
3276 impl=self.tag if impl is None else impl,
3277 expl=self._expl if expl is None else expl,
3278 default=self.default if default is None else default,
3279 optional=self.optional if optional is None else optional,
3283 def __getitem__(self, key):
3284 if key.__class__ == int:
3285 bit_len, octets = self._value
3288 return memoryview(octets)[key // 8] >> (7 - (key % 8)) & 1 == 1
3289 if isinstance(key, str):
3290 value = self.specs.get(key)
3292 raise ObjUnknown("BitString value: %s" % key)
3294 raise InvalidValueType((int, str))
3297 self._assert_ready()
3298 bit_len, octets = self._value
3301 len_encode(len(octets) + 1),
3302 int2byte((8 - bit_len % 8) % 8),
3306 def _encode1st(self, state):
3307 self._assert_ready()
3308 _, octets = self._value
3310 return len(self.tag) + len_size(l) + l, state
3312 def _encode2nd(self, writer, state_iter):
3313 bit_len, octets = self._value
3314 write_full(writer, b"".join((
3316 len_encode(len(octets) + 1),
3317 int2byte((8 - bit_len % 8) % 8),
3319 write_full(writer, octets)
3321 def _encode_cer(self, writer):
3322 bit_len, octets = self._value
3323 if len(octets) + 1 <= 1000:
3324 write_full(writer, self._encode())
3326 write_full(writer, self.tag_constructed)
3327 write_full(writer, LENINDEF)
3328 for offset in range(0, (len(octets) // 999) * 999, 999):
3329 write_full(writer, b"".join((
3330 BitString.tag_default,
3333 octets[offset:offset + 999],
3335 tail = octets[offset + 999:]
3337 tail = int2byte((8 - bit_len % 8) % 8) + tail
3338 write_full(writer, b"".join((
3339 BitString.tag_default,
3340 len_encode(len(tail)),
3343 write_full(writer, EOC)
3345 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3347 t, tlen, lv = tag_strip(tlv)
3348 except DecodeError as err:
3349 raise err.__class__(
3351 klass=self.__class__,
3352 decode_path=decode_path,
3356 if tag_only: # pragma: no cover
3360 l, llen, v = len_decode(lv)
3361 except DecodeError as err:
3362 raise err.__class__(
3364 klass=self.__class__,
3365 decode_path=decode_path,
3369 raise NotEnoughData(
3370 "encoded length is longer than data",
3371 klass=self.__class__,
3372 decode_path=decode_path,
3376 raise NotEnoughData(
3378 klass=self.__class__,
3379 decode_path=decode_path,
3383 if l == 1 and pad_size != 0:
3385 "invalid empty value",
3386 klass=self.__class__,
3387 decode_path=decode_path,
3393 klass=self.__class__,
3394 decode_path=decode_path,
3397 if v[l - 1] & ((1 << pad_size) - 1) != 0:
3400 klass=self.__class__,
3401 decode_path=decode_path,
3404 v, tail = v[:l], v[l:]
3405 bit_len = (len(v) - 1) * 8 - pad_size
3406 obj = self.__class__(
3407 value=None if evgen_mode else (bit_len, v[1:].tobytes()),
3410 default=self.default,
3411 optional=self.optional,
3413 _decoded=(offset, llen, l),
3416 obj._value = (bit_len, None)
3417 yield decode_path, obj, tail
3419 if t != self.tag_constructed:
3421 klass=self.__class__,
3422 decode_path=decode_path,
3425 if not ctx.get("bered", False):
3427 "unallowed BER constructed encoding",
3428 klass=self.__class__,
3429 decode_path=decode_path,
3432 if tag_only: # pragma: no cover
3437 l, llen, v = len_decode(lv)
3438 except LenIndefForm:
3439 llen, l, v = 1, 0, lv[1:]
3441 except DecodeError as err:
3442 raise err.__class__(
3444 klass=self.__class__,
3445 decode_path=decode_path,
3449 raise NotEnoughData(
3450 "encoded length is longer than data",
3451 klass=self.__class__,
3452 decode_path=decode_path,
3455 if not lenindef and l == 0:
3456 raise NotEnoughData(
3458 klass=self.__class__,
3459 decode_path=decode_path,
3463 sub_offset = offset + tlen + llen
3467 if v[:EOC_LEN].tobytes() == EOC:
3474 "chunk out of bounds",
3475 klass=self.__class__,
3476 decode_path=decode_path + (str(len(chunks) - 1),),
3477 offset=chunks[-1].offset,
3479 sub_decode_path = decode_path + (str(len(chunks)),)
3482 for _decode_path, chunk, v_tail in BitString().decode_evgen(
3485 decode_path=sub_decode_path,
3488 _ctx_immutable=False,
3490 yield _decode_path, chunk, v_tail
3492 _, chunk, v_tail = next(BitString().decode_evgen(
3495 decode_path=sub_decode_path,
3498 _ctx_immutable=False,
3503 "expected BitString encoded chunk",
3504 klass=self.__class__,
3505 decode_path=sub_decode_path,
3508 chunks.append(chunk)
3509 sub_offset += chunk.tlvlen
3510 vlen += chunk.tlvlen
3512 if len(chunks) == 0:
3515 klass=self.__class__,
3516 decode_path=decode_path,
3521 for chunk_i, chunk in enumerate(chunks[:-1]):
3522 if chunk.bit_len % 8 != 0:
3524 "BitString chunk is not multiple of 8 bits",
3525 klass=self.__class__,
3526 decode_path=decode_path + (str(chunk_i),),
3527 offset=chunk.offset,
3530 values.append(bytes(chunk))
3531 bit_len += chunk.bit_len
3532 chunk_last = chunks[-1]
3534 values.append(bytes(chunk_last))
3535 bit_len += chunk_last.bit_len
3536 obj = self.__class__(
3537 value=None if evgen_mode else (bit_len, b"".join(values)),
3540 default=self.default,
3541 optional=self.optional,
3543 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3546 obj._value = (bit_len, None)
3547 obj.lenindef = lenindef
3548 obj.ber_encoded = True
3549 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
3552 return pp_console_row(next(self.pps()))
3554 def pps(self, decode_path=()):
3558 bit_len, blob = self._value
3559 value = "%d bits" % bit_len
3560 if len(self.specs) > 0 and blob is not None:
3561 blob = tuple(self.named)
3564 asn1_type_name=self.asn1_type_name,
3565 obj_name=self.__class__.__name__,
3566 decode_path=decode_path,
3569 optional=self.optional,
3570 default=self == self.default,
3571 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3572 expl=None if self._expl is None else tag_decode(self._expl),
3577 expl_offset=self.expl_offset if self.expled else None,
3578 expl_tlen=self.expl_tlen if self.expled else None,
3579 expl_llen=self.expl_llen if self.expled else None,
3580 expl_vlen=self.expl_vlen if self.expled else None,
3581 expl_lenindef=self.expl_lenindef,
3582 lenindef=self.lenindef,
3583 ber_encoded=self.ber_encoded,
3586 defined_by, defined = self.defined or (None, None)
3587 if defined_by is not None:
3589 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3591 for pp in self.pps_lenindef(decode_path):
3595 OctetStringState = namedtuple(
3597 BasicState._fields + (
3608 class OctetString(Obj):
3609 """``OCTET STRING`` binary string type
3611 >>> s = OctetString(b"hello world")
3612 OCTET STRING 11 bytes 68656c6c6f20776f726c64
3613 >>> s == OctetString(b"hello world")
3618 >>> OctetString(b"hello", bounds=(4, 4))
3619 Traceback (most recent call last):
3620 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
3621 >>> OctetString(b"hell", bounds=(4, 4))
3622 OCTET STRING 4 bytes 68656c6c
3624 Memoryviews can be used as a values. If memoryview is made on
3625 mmap-ed file, then it does not take storage inside OctetString
3626 itself. In CER encoding mode it will be streamed to the specified
3627 writer, copying 1 KB chunks.
3629 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
3630 tag_default = tag_encode(4)
3631 asn1_type_name = "OCTET STRING"
3632 evgen_mode_skip_value = True
3646 :param value: set the value. Either binary type, or
3647 :py:class:`pyderasn.OctetString` object
3648 :param bounds: set ``(MIN, MAX)`` value size constraint.
3649 (-inf, +inf) by default
3650 :param bytes impl: override default tag with ``IMPLICIT`` one
3651 :param bytes expl: override default tag with ``EXPLICIT`` one
3652 :param default: set default value. Type same as in ``value``
3653 :param bool optional: is object ``OPTIONAL`` in sequence
3655 super().__init__(impl, expl, default, optional, _decoded)
3657 self._bound_min, self._bound_max = getattr(
3661 ) if bounds is None else bounds
3662 if value is not None:
3663 self._value = self._value_sanitize(value)
3664 if default is not None:
3665 default = self._value_sanitize(default)
3666 self.default = self.__class__(
3671 if self._value is None:
3672 self._value = default
3674 tag_klass, _, tag_num = tag_decode(self.tag)
3675 self.tag_constructed = tag_encode(
3677 form=TagFormConstructed,
3681 def _value_sanitize(self, value):
3682 if value.__class__ == bytes or value.__class__ == memoryview:
3684 elif issubclass(value.__class__, OctetString):
3685 value = value._value
3687 raise InvalidValueType((self.__class__, bytes, memoryview))
3688 if not self._bound_min <= len(value) <= self._bound_max:
3689 raise BoundsError(self._bound_min, len(value), self._bound_max)
3694 return self._value is not None
3696 def __getstate__(self):
3697 return OctetStringState(
3713 self.tag_constructed,
3717 def __setstate__(self, state):
3718 super().__setstate__(state)
3719 self._value = state.value
3720 self._bound_min = state.bound_min
3721 self._bound_max = state.bound_max
3722 self.tag_constructed = state.tag_constructed
3723 self.defined = state.defined
3725 def __bytes__(self):
3726 self._assert_ready()
3727 return bytes(self._value)
3729 def __eq__(self, their):
3730 if their.__class__ == bytes:
3731 return self._value == their
3732 if not issubclass(their.__class__, OctetString):
3735 self._value == their._value and
3736 self.tag == their.tag and
3737 self._expl == their._expl
3740 def __lt__(self, their):
3741 return self._value < their._value
3752 return self.__class__(
3755 (self._bound_min, self._bound_max)
3756 if bounds is None else bounds
3758 impl=self.tag if impl is None else impl,
3759 expl=self._expl if expl is None else expl,
3760 default=self.default if default is None else default,
3761 optional=self.optional if optional is None else optional,
3765 self._assert_ready()
3768 len_encode(len(self._value)),
3772 def _encode1st(self, state):
3773 self._assert_ready()
3774 l = len(self._value)
3775 return len(self.tag) + len_size(l) + l, state
3777 def _encode2nd(self, writer, state_iter):
3779 write_full(writer, self.tag + len_encode(len(value)))
3780 write_full(writer, value)
3782 def _encode_cer(self, writer):
3783 octets = self._value
3784 if len(octets) <= 1000:
3785 write_full(writer, self._encode())
3787 write_full(writer, self.tag_constructed)
3788 write_full(writer, LENINDEF)
3789 for offset in range(0, (len(octets) // 1000) * 1000, 1000):
3790 write_full(writer, b"".join((
3791 OctetString.tag_default,
3793 octets[offset:offset + 1000],
3795 tail = octets[offset + 1000:]
3797 write_full(writer, b"".join((
3798 OctetString.tag_default,
3799 len_encode(len(tail)),
3802 write_full(writer, EOC)
3804 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3806 t, tlen, lv = tag_strip(tlv)
3807 except DecodeError as err:
3808 raise err.__class__(
3810 klass=self.__class__,
3811 decode_path=decode_path,
3819 l, llen, v = len_decode(lv)
3820 except DecodeError as err:
3821 raise err.__class__(
3823 klass=self.__class__,
3824 decode_path=decode_path,
3828 raise NotEnoughData(
3829 "encoded length is longer than data",
3830 klass=self.__class__,
3831 decode_path=decode_path,
3834 v, tail = v[:l], v[l:]
3835 if evgen_mode and not self._bound_min <= len(v) <= self._bound_max:
3837 msg=str(BoundsError(self._bound_min, len(v), self._bound_max)),
3838 klass=self.__class__,
3839 decode_path=decode_path,
3843 obj = self.__class__(
3845 None if (evgen_mode and self.evgen_mode_skip_value)
3848 bounds=(self._bound_min, self._bound_max),
3851 default=self.default,
3852 optional=self.optional,
3853 _decoded=(offset, llen, l),
3856 except DecodeError as err:
3859 klass=self.__class__,
3860 decode_path=decode_path,
3863 except BoundsError as err:
3866 klass=self.__class__,
3867 decode_path=decode_path,
3870 yield decode_path, obj, tail
3872 if t != self.tag_constructed:
3874 klass=self.__class__,
3875 decode_path=decode_path,
3878 if not ctx.get("bered", False):
3880 "unallowed BER constructed encoding",
3881 klass=self.__class__,
3882 decode_path=decode_path,
3890 l, llen, v = len_decode(lv)
3891 except LenIndefForm:
3892 llen, l, v = 1, 0, lv[1:]
3894 except DecodeError as err:
3895 raise err.__class__(
3897 klass=self.__class__,
3898 decode_path=decode_path,
3902 raise NotEnoughData(
3903 "encoded length is longer than data",
3904 klass=self.__class__,
3905 decode_path=decode_path,
3910 sub_offset = offset + tlen + llen
3915 if v[:EOC_LEN].tobytes() == EOC:
3922 "chunk out of bounds",
3923 klass=self.__class__,
3924 decode_path=decode_path + (str(len(chunks) - 1),),
3925 offset=chunks[-1].offset,
3929 sub_decode_path = decode_path + (str(chunks_count),)
3930 for _decode_path, chunk, v_tail in OctetString().decode_evgen(
3933 decode_path=sub_decode_path,
3936 _ctx_immutable=False,
3938 yield _decode_path, chunk, v_tail
3939 if not chunk.ber_encoded:
3940 payload_len += chunk.vlen
3943 sub_decode_path = decode_path + (str(len(chunks)),)
3944 _, chunk, v_tail = next(OctetString().decode_evgen(
3947 decode_path=sub_decode_path,
3950 _ctx_immutable=False,
3953 chunks.append(chunk)
3956 "expected OctetString encoded chunk",
3957 klass=self.__class__,
3958 decode_path=sub_decode_path,
3961 sub_offset += chunk.tlvlen
3962 vlen += chunk.tlvlen
3964 if evgen_mode and not self._bound_min <= payload_len <= self._bound_max:
3966 msg=str(BoundsError(self._bound_min, payload_len, self._bound_max)),
3967 klass=self.__class__,
3968 decode_path=decode_path,
3972 obj = self.__class__(
3974 None if evgen_mode else
3975 b"".join(bytes(chunk) for chunk in chunks)
3977 bounds=(self._bound_min, self._bound_max),
3980 default=self.default,
3981 optional=self.optional,
3982 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3985 except DecodeError as err:
3988 klass=self.__class__,
3989 decode_path=decode_path,
3992 except BoundsError as err:
3995 klass=self.__class__,
3996 decode_path=decode_path,
3999 obj.lenindef = lenindef
4000 obj.ber_encoded = True
4001 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
4004 return pp_console_row(next(self.pps()))
4006 def pps(self, decode_path=()):
4009 asn1_type_name=self.asn1_type_name,
4010 obj_name=self.__class__.__name__,
4011 decode_path=decode_path,
4012 value=("%d bytes" % len(self._value)) if self.ready else None,
4013 blob=self._value if self.ready else None,
4014 optional=self.optional,
4015 default=self == self.default,
4016 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4017 expl=None if self._expl is None else tag_decode(self._expl),
4022 expl_offset=self.expl_offset if self.expled else None,
4023 expl_tlen=self.expl_tlen if self.expled else None,
4024 expl_llen=self.expl_llen if self.expled else None,
4025 expl_vlen=self.expl_vlen if self.expled else None,
4026 expl_lenindef=self.expl_lenindef,
4027 lenindef=self.lenindef,
4028 ber_encoded=self.ber_encoded,
4031 defined_by, defined = self.defined or (None, None)
4032 if defined_by is not None:
4034 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4036 for pp in self.pps_lenindef(decode_path):
4040 def agg_octet_string(evgens, decode_path, raw, writer):
4041 """Aggregate constructed string (OctetString and its derivatives)
4043 :param evgens: iterator of generated events
4044 :param decode_path: points to the string we want to decode
4045 :param raw: slicebable (memoryview, bytearray, etc) with
4046 the data evgens are generated on
4047 :param writer: buffer.write where string is going to be saved
4048 :param writer: where string is going to be saved. Must comply
4049 with ``io.RawIOBase.write`` behaviour
4051 .. seealso:: :ref:`agg_octet_string`
4053 decode_path_len = len(decode_path)
4054 for dp, obj, _ in evgens:
4055 if dp[:decode_path_len] != decode_path:
4057 if not obj.ber_encoded:
4058 write_full(writer, raw[
4059 obj.offset + obj.tlen + obj.llen:
4060 obj.offset + obj.tlen + obj.llen + obj.vlen -
4061 (EOC_LEN if obj.expl_lenindef else 0)
4063 if len(dp) == decode_path_len:
4067 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
4071 """``NULL`` null object
4079 tag_default = tag_encode(5)
4080 asn1_type_name = "NULL"
4084 value=None, # unused, but Sequence passes it
4091 :param bytes impl: override default tag with ``IMPLICIT`` one
4092 :param bytes expl: override default tag with ``EXPLICIT`` one
4093 :param bool optional: is object ``OPTIONAL`` in sequence
4095 super().__init__(impl, expl, None, optional, _decoded)
4102 def __getstate__(self):
4118 def __eq__(self, their):
4119 if not issubclass(their.__class__, Null):
4122 self.tag == their.tag and
4123 self._expl == their._expl
4133 return self.__class__(
4134 impl=self.tag if impl is None else impl,
4135 expl=self._expl if expl is None else expl,
4136 optional=self.optional if optional is None else optional,
4140 return self.tag + LEN0
4142 def _encode1st(self, state):
4143 return len(self.tag) + 1, state
4145 def _encode2nd(self, writer, state_iter):
4146 write_full(writer, self.tag + LEN0)
4148 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4150 t, _, lv = tag_strip(tlv)
4151 except DecodeError as err:
4152 raise err.__class__(
4154 klass=self.__class__,
4155 decode_path=decode_path,
4160 klass=self.__class__,
4161 decode_path=decode_path,
4164 if tag_only: # pragma: no cover
4168 l, _, v = len_decode(lv)
4169 except DecodeError as err:
4170 raise err.__class__(
4172 klass=self.__class__,
4173 decode_path=decode_path,
4177 raise InvalidLength(
4178 "Null must have zero length",
4179 klass=self.__class__,
4180 decode_path=decode_path,
4183 obj = self.__class__(
4186 optional=self.optional,
4187 _decoded=(offset, 1, 0),
4189 yield decode_path, obj, v
4192 return pp_console_row(next(self.pps()))
4194 def pps(self, decode_path=()):
4197 asn1_type_name=self.asn1_type_name,
4198 obj_name=self.__class__.__name__,
4199 decode_path=decode_path,
4200 optional=self.optional,
4201 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4202 expl=None if self._expl is None else tag_decode(self._expl),
4207 expl_offset=self.expl_offset if self.expled else None,
4208 expl_tlen=self.expl_tlen if self.expled else None,
4209 expl_llen=self.expl_llen if self.expled else None,
4210 expl_vlen=self.expl_vlen if self.expled else None,
4211 expl_lenindef=self.expl_lenindef,
4214 for pp in self.pps_lenindef(decode_path):
4218 ObjectIdentifierState = namedtuple(
4219 "ObjectIdentifierState",
4220 BasicState._fields + ("value", "defines"),
4225 class ObjectIdentifier(Obj):
4226 """``OBJECT IDENTIFIER`` OID type
4228 >>> oid = ObjectIdentifier((1, 2, 3))
4229 OBJECT IDENTIFIER 1.2.3
4230 >>> oid == ObjectIdentifier("1.2.3")
4236 >>> oid + (4, 5) + ObjectIdentifier("1.7")
4237 OBJECT IDENTIFIER 1.2.3.4.5.1.7
4239 >>> str(ObjectIdentifier((3, 1)))
4240 Traceback (most recent call last):
4241 pyderasn.InvalidOID: unacceptable first arc value
4243 __slots__ = ("defines",)
4244 tag_default = tag_encode(6)
4245 asn1_type_name = "OBJECT IDENTIFIER"
4258 :param value: set the value. Either tuples of integers,
4259 string of "."-concatenated integers, or
4260 :py:class:`pyderasn.ObjectIdentifier` object
4261 :param defines: sequence of tuples. Each tuple has two elements.
4262 First one is relative to current one decode
4263 path, aiming to the field defined by that OID.
4264 Read about relative path in
4265 :py:func:`pyderasn.abs_decode_path`. Second
4266 tuple element is ``{OID: pyderasn.Obj()}``
4267 dictionary, mapping between current OID value
4268 and structure applied to defined field.
4270 .. seealso:: :ref:`definedby`
4272 :param bytes impl: override default tag with ``IMPLICIT`` one
4273 :param bytes expl: override default tag with ``EXPLICIT`` one
4274 :param default: set default value. Type same as in ``value``
4275 :param bool optional: is object ``OPTIONAL`` in sequence
4277 super().__init__(impl, expl, default, optional, _decoded)
4279 if value is not None:
4280 self._value = self._value_sanitize(value)
4281 if default is not None:
4282 default = self._value_sanitize(default)
4283 self.default = self.__class__(
4288 if self._value is None:
4289 self._value = default
4290 self.defines = defines
4292 def __add__(self, their):
4293 if their.__class__ == tuple:
4294 return self.__class__(self._value + array("L", their))
4295 if isinstance(their, self.__class__):
4296 return self.__class__(self._value + their._value)
4297 raise InvalidValueType((self.__class__, tuple))
4299 def _value_sanitize(self, value):
4300 if issubclass(value.__class__, ObjectIdentifier):
4302 if isinstance(value, str):
4304 value = array("L", (pureint(arc) for arc in value.split(".")))
4306 raise InvalidOID("unacceptable arcs values")
4307 if value.__class__ == tuple:
4309 value = array("L", value)
4310 except OverflowError as err:
4311 raise InvalidOID(repr(err))
4312 if value.__class__ is array:
4314 raise InvalidOID("less than 2 arcs")
4315 first_arc = value[0]
4316 if first_arc in (0, 1):
4317 if not (0 <= value[1] <= 39):
4318 raise InvalidOID("second arc is too wide")
4319 elif first_arc == 2:
4322 raise InvalidOID("unacceptable first arc value")
4323 if not all(arc >= 0 for arc in value):
4324 raise InvalidOID("negative arc value")
4326 raise InvalidValueType((self.__class__, str, tuple))
4330 return self._value is not None
4332 def __getstate__(self):
4333 return ObjectIdentifierState(
4350 def __setstate__(self, state):
4351 super().__setstate__(state)
4352 self._value = state.value
4353 self.defines = state.defines
4356 self._assert_ready()
4357 return iter(self._value)
4360 return ".".join(str(arc) for arc in self._value or ())
4363 self._assert_ready()
4364 return hash(b"".join((
4366 bytes(self._expl or b""),
4367 str(self._value).encode("ascii"),
4370 def __eq__(self, their):
4371 if their.__class__ == tuple:
4372 return self._value == array("L", their)
4373 if not issubclass(their.__class__, ObjectIdentifier):
4376 self.tag == their.tag and
4377 self._expl == their._expl and
4378 self._value == their._value
4381 def __lt__(self, their):
4382 return self._value < their._value
4393 return self.__class__(
4395 defines=self.defines if defines is None else defines,
4396 impl=self.tag if impl is None else impl,
4397 expl=self._expl if expl is None else expl,
4398 default=self.default if default is None else default,
4399 optional=self.optional if optional is None else optional,
4402 def _encode_octets(self):
4403 self._assert_ready()
4405 first_value = value[1]
4406 first_arc = value[0]
4409 elif first_arc == 1:
4411 elif first_arc == 2:
4413 else: # pragma: no cover
4414 raise RuntimeError("invalid arc is stored")
4415 octets = [zero_ended_encode(first_value)]
4416 for arc in value[2:]:
4417 octets.append(zero_ended_encode(arc))
4418 return b"".join(octets)
4421 v = self._encode_octets()
4422 return b"".join((self.tag, len_encode(len(v)), v))
4424 def _encode1st(self, state):
4425 l = len(self._encode_octets())
4426 return len(self.tag) + len_size(l) + l, state
4428 def _encode2nd(self, writer, state_iter):
4429 write_full(writer, self._encode())
4431 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4433 t, _, lv = tag_strip(tlv)
4434 except DecodeError as err:
4435 raise err.__class__(
4437 klass=self.__class__,
4438 decode_path=decode_path,
4443 klass=self.__class__,
4444 decode_path=decode_path,
4447 if tag_only: # pragma: no cover
4451 l, llen, v = len_decode(lv)
4452 except DecodeError as err:
4453 raise err.__class__(
4455 klass=self.__class__,
4456 decode_path=decode_path,
4460 raise NotEnoughData(
4461 "encoded length is longer than data",
4462 klass=self.__class__,
4463 decode_path=decode_path,
4467 raise NotEnoughData(
4469 klass=self.__class__,
4470 decode_path=decode_path,
4473 v, tail = v[:l], v[l:]
4481 if i == 0 and octet == 0x80:
4482 if ctx.get("bered", False):
4486 "non normalized arc encoding",
4487 klass=self.__class__,
4488 decode_path=decode_path,
4491 arc = (arc << 7) | (octet & 0x7F)
4492 if octet & 0x80 == 0:
4495 except OverflowError:
4497 "too huge value for local unsigned long",
4498 klass=self.__class__,
4499 decode_path=decode_path,
4508 klass=self.__class__,
4509 decode_path=decode_path,
4513 second_arc = arcs[0]
4514 if 0 <= second_arc <= 39:
4516 elif 40 <= second_arc <= 79:
4522 obj = self.__class__(
4523 value=array("L", (first_arc, second_arc)) + arcs[1:],
4526 default=self.default,
4527 optional=self.optional,
4528 _decoded=(offset, llen, l),
4531 obj.ber_encoded = True
4532 yield decode_path, obj, tail
4535 return pp_console_row(next(self.pps()))
4537 def pps(self, decode_path=()):
4540 asn1_type_name=self.asn1_type_name,
4541 obj_name=self.__class__.__name__,
4542 decode_path=decode_path,
4543 value=str(self) if self.ready else None,
4544 optional=self.optional,
4545 default=self == self.default,
4546 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4547 expl=None if self._expl is None else tag_decode(self._expl),
4552 expl_offset=self.expl_offset if self.expled else None,
4553 expl_tlen=self.expl_tlen if self.expled else None,
4554 expl_llen=self.expl_llen if self.expled else None,
4555 expl_vlen=self.expl_vlen if self.expled else None,
4556 expl_lenindef=self.expl_lenindef,
4557 ber_encoded=self.ber_encoded,
4560 for pp in self.pps_lenindef(decode_path):
4564 class Enumerated(Integer):
4565 """``ENUMERATED`` integer type
4567 This type is identical to :py:class:`pyderasn.Integer`, but requires
4568 schema to be specified and does not accept values missing from it.
4571 tag_default = tag_encode(10)
4572 asn1_type_name = "ENUMERATED"
4583 bounds=None, # dummy argument, workability for Integer.decode
4586 value, bounds, impl, expl, default, optional, _specs, _decoded,
4588 if len(self.specs) == 0:
4589 raise ValueError("schema must be specified")
4591 def _value_sanitize(self, value):
4592 if isinstance(value, self.__class__):
4593 value = value._value
4594 elif isinstance(value, int):
4595 for _value in self.specs.values():
4600 "unknown integer value: %s" % value,
4601 klass=self.__class__,
4603 elif isinstance(value, str):
4604 value = self.specs.get(value)
4606 raise ObjUnknown("integer value: %s" % value)
4608 raise InvalidValueType((self.__class__, int, str))
4620 return self.__class__(
4622 impl=self.tag if impl is None else impl,
4623 expl=self._expl if expl is None else expl,
4624 default=self.default if default is None else default,
4625 optional=self.optional if optional is None else optional,
4630 def escape_control_unicode(c):
4631 if unicat(c)[0] == "C":
4632 c = repr(c).lstrip("u").strip("'")
4636 class CommonString(OctetString):
4637 """Common class for all strings
4639 Everything resembles :py:class:`pyderasn.OctetString`, except
4640 ability to deal with unicode text strings.
4642 >>> hexenc("привет мир".encode("utf-8"))
4643 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4644 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
4646 >>> s = UTF8String("привет мир")
4647 UTF8String UTF8String привет мир
4649 'привет мир'
4650 >>> hexenc(bytes(s))
4651 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4653 >>> PrintableString("привет мир")
4654 Traceback (most recent call last):
4655 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
4657 >>> BMPString("ада", bounds=(2, 2))
4658 Traceback (most recent call last):
4659 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
4660 >>> s = BMPString("ад", bounds=(2, 2))
4663 >>> hexenc(bytes(s))
4670 - Text Encoding, validation
4671 * - :py:class:`pyderasn.UTF8String`
4673 * - :py:class:`pyderasn.NumericString`
4674 - proper alphabet validation
4675 * - :py:class:`pyderasn.PrintableString`
4676 - proper alphabet validation
4677 * - :py:class:`pyderasn.TeletexString`
4679 * - :py:class:`pyderasn.T61String`
4681 * - :py:class:`pyderasn.VideotexString`
4683 * - :py:class:`pyderasn.IA5String`
4684 - proper alphabet validation
4685 * - :py:class:`pyderasn.GraphicString`
4687 * - :py:class:`pyderasn.VisibleString`, :py:class:`pyderasn.ISO646String`
4688 - proper alphabet validation
4689 * - :py:class:`pyderasn.GeneralString`
4691 * - :py:class:`pyderasn.UniversalString`
4693 * - :py:class:`pyderasn.BMPString`
4698 def _value_sanitize(self, value):
4700 value_decoded = None
4701 if isinstance(value, self.__class__):
4702 value_raw = value._value
4703 elif value.__class__ == str:
4704 value_decoded = value
4705 elif value.__class__ == bytes:
4708 raise InvalidValueType((self.__class__, str, bytes))
4711 value_decoded.encode(self.encoding)
4712 if value_raw is None else value_raw
4715 value_raw.decode(self.encoding)
4716 if value_decoded is None else value_decoded
4718 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4719 raise DecodeError(str(err))
4720 if not self._bound_min <= len(value_decoded) <= self._bound_max:
4728 def __eq__(self, their):
4729 if their.__class__ == bytes:
4730 return self._value == their
4731 if their.__class__ == str:
4732 return self._value == their.encode(self.encoding)
4733 if not isinstance(their, self.__class__):
4736 self._value == their._value and
4737 self.tag == their.tag and
4738 self._expl == their._expl
4741 def __unicode__(self):
4743 return self._value.decode(self.encoding)
4744 return str(self._value)
4747 return pp_console_row(next(self.pps()))
4749 def pps(self, decode_path=()):
4752 value = "".join(escape_control_unicode(c) for c in self.__unicode__())
4755 asn1_type_name=self.asn1_type_name,
4756 obj_name=self.__class__.__name__,
4757 decode_path=decode_path,
4759 optional=self.optional,
4760 default=self == self.default,
4761 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4762 expl=None if self._expl is None else tag_decode(self._expl),
4767 expl_offset=self.expl_offset if self.expled else None,
4768 expl_tlen=self.expl_tlen if self.expled else None,
4769 expl_llen=self.expl_llen if self.expled else None,
4770 expl_vlen=self.expl_vlen if self.expled else None,
4771 expl_lenindef=self.expl_lenindef,
4772 ber_encoded=self.ber_encoded,
4775 for pp in self.pps_lenindef(decode_path):
4779 class UTF8String(CommonString):
4781 tag_default = tag_encode(12)
4783 asn1_type_name = "UTF8String"
4786 class AllowableCharsMixin:
4788 def allowable_chars(self):
4789 return frozenset(chr(c) for c in self._allowable_chars)
4791 def _value_sanitize(self, value):
4792 value = super()._value_sanitize(value)
4793 if not frozenset(value) <= self._allowable_chars:
4794 raise DecodeError("non satisfying alphabet value")
4798 class NumericString(AllowableCharsMixin, CommonString):
4801 Its value is properly sanitized: only ASCII digits with spaces can
4804 >>> NumericString().allowable_chars
4805 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4808 tag_default = tag_encode(18)
4810 asn1_type_name = "NumericString"
4811 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
4814 PrintableStringState = namedtuple(
4815 "PrintableStringState",
4816 OctetStringState._fields + ("allowable_chars",),
4821 class PrintableString(AllowableCharsMixin, CommonString):
4824 Its value is properly sanitized: see X.680 41.4 table 10.
4826 >>> PrintableString().allowable_chars
4827 frozenset([' ', "'", ..., 'z'])
4828 >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4829 PrintableString PrintableString foo*bar
4830 >>> obj.allow_asterisk, obj.allow_ampersand
4834 tag_default = tag_encode(19)
4836 asn1_type_name = "PrintableString"
4837 _allowable_chars = frozenset(
4838 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4840 _asterisk = frozenset("*".encode("ascii"))
4841 _ampersand = frozenset("&".encode("ascii"))
4853 allow_asterisk=False,
4854 allow_ampersand=False,
4857 :param allow_asterisk: allow asterisk character
4858 :param allow_ampersand: allow ampersand character
4861 self._allowable_chars |= self._asterisk
4863 self._allowable_chars |= self._ampersand
4864 super(PrintableString, self).__init__(
4865 value, bounds, impl, expl, default, optional, _decoded, ctx,
4869 def allow_asterisk(self):
4870 """Is asterisk character allowed?
4872 return self._asterisk <= self._allowable_chars
4875 def allow_ampersand(self):
4876 """Is ampersand character allowed?
4878 return self._ampersand <= self._allowable_chars
4880 def __getstate__(self):
4881 return PrintableStringState(
4882 *super().__getstate__(),
4883 **{"allowable_chars": self._allowable_chars}
4886 def __setstate__(self, state):
4887 super().__setstate__(state)
4888 self._allowable_chars = state.allowable_chars
4899 return self.__class__(
4902 (self._bound_min, self._bound_max)
4903 if bounds is None else bounds
4905 impl=self.tag if impl is None else impl,
4906 expl=self._expl if expl is None else expl,
4907 default=self.default if default is None else default,
4908 optional=self.optional if optional is None else optional,
4909 allow_asterisk=self.allow_asterisk,
4910 allow_ampersand=self.allow_ampersand,
4914 class TeletexString(CommonString):
4916 tag_default = tag_encode(20)
4917 encoding = "iso-8859-1"
4918 asn1_type_name = "TeletexString"
4921 class T61String(TeletexString):
4923 asn1_type_name = "T61String"
4926 class VideotexString(CommonString):
4928 tag_default = tag_encode(21)
4929 encoding = "iso-8859-1"
4930 asn1_type_name = "VideotexString"
4933 class IA5String(AllowableCharsMixin, CommonString):
4936 Its value is properly sanitized: it is a mix of
4938 * http://www.itscj.ipsj.or.jp/iso-ir/006.pdf (G)
4939 * http://www.itscj.ipsj.or.jp/iso-ir/001.pdf (C0)
4940 * DEL character (0x7F)
4942 It is just 7-bit ASCII.
4944 >>> IA5String().allowable_chars
4945 frozenset(["NUL", ... "DEL"])
4948 tag_default = tag_encode(22)
4950 asn1_type_name = "IA5"
4951 _allowable_chars = frozenset(b"".join(
4952 chr(c).encode("ascii") for c in range(128)
4956 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4957 LEN_LEN_YYMMDDHHMMSSZ = len_encode(LEN_YYMMDDHHMMSSZ)
4958 LEN_YYMMDDHHMMSSZ_WITH_LEN = len(LEN_LEN_YYMMDDHHMMSSZ) + LEN_YYMMDDHHMMSSZ
4959 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4960 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4961 LEN_LEN_YYYYMMDDHHMMSSZ = len_encode(LEN_YYYYMMDDHHMMSSZ)
4964 class VisibleString(AllowableCharsMixin, CommonString):
4967 Its value is properly sanitized. ASCII subset from space to tilde is
4968 allowed: http://www.itscj.ipsj.or.jp/iso-ir/006.pdf
4970 >>> VisibleString().allowable_chars
4971 frozenset([" ", ... "~"])
4974 tag_default = tag_encode(26)
4976 asn1_type_name = "VisibleString"
4977 _allowable_chars = frozenset(b"".join(
4978 chr(c).encode("ascii") for c in range(ord(" "), ord("~") + 1)
4982 class ISO646String(VisibleString):
4984 asn1_type_name = "ISO646String"
4987 UTCTimeState = namedtuple(
4989 OctetStringState._fields + ("ber_raw",),
4994 def str_to_time_fractions(value):
4996 year, v = (v // 10**10), (v % 10**10)
4997 month, v = (v // 10**8), (v % 10**8)
4998 day, v = (v // 10**6), (v % 10**6)
4999 hour, v = (v // 10**4), (v % 10**4)
5000 minute, second = (v // 100), (v % 100)
5001 return year, month, day, hour, minute, second
5004 class UTCTime(VisibleString):
5005 """``UTCTime`` datetime type
5007 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5008 UTCTime UTCTime 2017-09-30T22:07:50
5014 datetime.datetime(2017, 9, 30, 22, 7, 50)
5015 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
5016 datetime.datetime(1957, 9, 30, 22, 7, 50)
5018 If BER encoded value was met, then ``ber_raw`` attribute will hold
5019 its raw representation.
5023 Only **naive** ``datetime`` objects are supported.
5024 Library assumes that all work is done in UTC.
5028 Pay attention that ``UTCTime`` can not hold full year, so all years
5029 having < 50 years are treated as 20xx, 19xx otherwise, according to
5030 X.509 recommendation. Use ``GeneralizedTime`` instead for
5035 No strict validation of UTC offsets are made (only applicable to
5036 **BER**), but very crude:
5038 * minutes are not exceeding 60
5039 * offset value is not exceeding 14 hours
5041 __slots__ = ("ber_raw",)
5042 tag_default = tag_encode(23)
5044 asn1_type_name = "UTCTime"
5045 evgen_mode_skip_value = False
5055 bounds=None, # dummy argument, workability for OctetString.decode
5059 :param value: set the value. Either datetime type, or
5060 :py:class:`pyderasn.UTCTime` object
5061 :param bytes impl: override default tag with ``IMPLICIT`` one
5062 :param bytes expl: override default tag with ``EXPLICIT`` one
5063 :param default: set default value. Type same as in ``value``
5064 :param bool optional: is object ``OPTIONAL`` in sequence
5066 super().__init__(None, None, impl, expl, None, optional, _decoded, ctx)
5069 if value is not None:
5070 self._value, self.ber_raw = self._value_sanitize(value, ctx)
5071 self.ber_encoded = self.ber_raw is not None
5072 if default is not None:
5073 default, _ = self._value_sanitize(default)
5074 self.default = self.__class__(
5079 if self._value is None:
5080 self._value = default
5082 self.optional = optional
5084 def _strptime_bered(self, value):
5085 year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
5088 raise ValueError("no timezone")
5089 year += 2000 if year < 50 else 1900
5090 decoded = datetime(year, month, day, hour, minute)
5092 if value[-1] == "Z":
5096 raise ValueError("invalid UTC offset")
5097 if value[-5] == "-":
5099 elif value[-5] == "+":
5102 raise ValueError("invalid UTC offset")
5103 v = pureint(value[-4:])
5104 offset, v = (60 * (v % 100)), v // 100
5106 raise ValueError("invalid UTC offset minutes")
5108 if offset > 14 * 3600:
5109 raise ValueError("too big UTC offset")
5113 return offset, decoded
5115 raise ValueError("invalid UTC offset seconds")
5116 seconds = pureint(value)
5118 raise ValueError("invalid seconds value")
5119 return offset, decoded + timedelta(seconds=seconds)
5121 def _strptime(self, value):
5122 # datetime.strptime's format: %y%m%d%H%M%SZ
5123 if len(value) != LEN_YYMMDDHHMMSSZ:
5124 raise ValueError("invalid UTCTime length")
5125 if value[-1] != "Z":
5126 raise ValueError("non UTC timezone")
5127 year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
5128 year += 2000 if year < 50 else 1900
5129 return datetime(year, month, day, hour, minute, second)
5131 def _dt_sanitize(self, value):
5132 if value.year < 1950 or value.year > 2049:
5133 raise ValueError("UTCTime can hold only 1950-2049 years")
5134 return value.replace(microsecond=0)
5136 def _value_sanitize(self, value, ctx=None):
5137 if value.__class__ == bytes:
5139 value_decoded = value.decode("ascii")
5140 except (UnicodeEncodeError, UnicodeDecodeError) as err:
5141 raise DecodeError("invalid UTCTime encoding: %r" % err)
5144 return self._strptime(value_decoded), None
5145 except (TypeError, ValueError) as _err:
5147 if (ctx is not None) and ctx.get("bered", False):
5149 offset, _value = self._strptime_bered(value_decoded)
5150 _value = _value - timedelta(seconds=offset)
5151 return self._dt_sanitize(_value), value
5152 except (TypeError, ValueError, OverflowError) as _err:
5155 "invalid %s format: %r" % (self.asn1_type_name, err),
5156 klass=self.__class__,
5158 if isinstance(value, self.__class__):
5159 return value._value, None
5160 if value.__class__ == datetime:
5161 if value.tzinfo is not None:
5162 raise ValueError("only naive datetime supported")
5163 return self._dt_sanitize(value), None
5164 raise InvalidValueType((self.__class__, datetime))
5166 def _pp_value(self):
5168 value = self._value.isoformat()
5169 if self.ber_encoded:
5170 value += " (%s)" % self.ber_raw
5174 def __unicode__(self):
5176 value = self._value.isoformat()
5177 if self.ber_encoded:
5178 value += " (%s)" % self.ber_raw
5180 return str(self._pp_value())
5182 def __getstate__(self):
5183 return UTCTimeState(*super().__getstate__(), **{"ber_raw": self.ber_raw})
5185 def __setstate__(self, state):
5186 super().__setstate__(state)
5187 self.ber_raw = state.ber_raw
5189 def __bytes__(self):
5190 self._assert_ready()
5191 return self._encode_time()
5193 def __eq__(self, their):
5194 if their.__class__ == bytes:
5195 return self._encode_time() == their
5196 if their.__class__ == datetime:
5197 return self.todatetime() == their
5198 if not isinstance(their, self.__class__):
5201 self._value == their._value and
5202 self.tag == their.tag and
5203 self._expl == their._expl
5206 def _encode_time(self):
5207 return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
5210 self._assert_ready()
5211 return b"".join((self.tag, LEN_LEN_YYMMDDHHMMSSZ, self._encode_time()))
5213 def _encode1st(self, state):
5214 return len(self.tag) + LEN_YYMMDDHHMMSSZ_WITH_LEN, state
5216 def _encode2nd(self, writer, state_iter):
5217 self._assert_ready()
5218 write_full(writer, self._encode())
5220 def _encode_cer(self, writer):
5221 write_full(writer, self._encode())
5223 def todatetime(self):
5227 return pp_console_row(next(self.pps()))
5229 def pps(self, decode_path=()):
5232 asn1_type_name=self.asn1_type_name,
5233 obj_name=self.__class__.__name__,
5234 decode_path=decode_path,
5235 value=self._pp_value(),
5236 optional=self.optional,
5237 default=self == self.default,
5238 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5239 expl=None if self._expl is None else tag_decode(self._expl),
5244 expl_offset=self.expl_offset if self.expled else None,
5245 expl_tlen=self.expl_tlen if self.expled else None,
5246 expl_llen=self.expl_llen if self.expled else None,
5247 expl_vlen=self.expl_vlen if self.expled else None,
5248 expl_lenindef=self.expl_lenindef,
5249 ber_encoded=self.ber_encoded,
5252 for pp in self.pps_lenindef(decode_path):
5256 class GeneralizedTime(UTCTime):
5257 """``GeneralizedTime`` datetime type
5259 This type is similar to :py:class:`pyderasn.UTCTime`.
5261 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5262 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
5264 '20170930220750.000123Z'
5265 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
5266 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
5270 Only **naive** datetime objects are supported.
5271 Library assumes that all work is done in UTC.
5275 Only **microsecond** fractions are supported in DER encoding.
5276 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
5277 higher precision values.
5281 **BER** encoded data can loss information (accuracy) during
5282 decoding because of float transformations.
5286 **Zero** year is unsupported.
5289 tag_default = tag_encode(24)
5290 asn1_type_name = "GeneralizedTime"
5292 def _dt_sanitize(self, value):
5295 def _strptime_bered(self, value):
5296 if len(value) < 4 + 3 * 2:
5297 raise ValueError("invalid GeneralizedTime")
5298 year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
5299 decoded = datetime(year, month, day, hour)
5300 offset, value = 0, value[10:]
5302 return offset, decoded
5303 if value[-1] == "Z":
5306 for char, sign in (("-", -1), ("+", 1)):
5307 idx = value.rfind(char)
5310 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
5311 v = pureint(offset_raw)
5312 if len(offset_raw) == 4:
5313 offset, v = (60 * (v % 100)), v // 100
5315 raise ValueError("invalid UTC offset minutes")
5316 elif len(offset_raw) == 2:
5319 raise ValueError("invalid UTC offset")
5321 if offset > 14 * 3600:
5322 raise ValueError("too big UTC offset")
5326 return offset, decoded
5327 if value[0] in DECIMAL_SIGNS:
5329 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
5332 raise ValueError("stripped minutes")
5333 decoded += timedelta(seconds=60 * pureint(value[:2]))
5336 return offset, decoded
5337 if value[0] in DECIMAL_SIGNS:
5339 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
5342 raise ValueError("stripped seconds")
5343 decoded += timedelta(seconds=pureint(value[:2]))
5346 return offset, decoded
5347 if value[0] not in DECIMAL_SIGNS:
5348 raise ValueError("invalid format after seconds")
5350 decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
5353 def _strptime(self, value):
5355 if l == LEN_YYYYMMDDHHMMSSZ:
5356 # datetime.strptime's format: %Y%m%d%H%M%SZ
5357 if value[-1] != "Z":
5358 raise ValueError("non UTC timezone")
5359 return datetime(*str_to_time_fractions(value[:-1]))
5360 if l >= LEN_YYYYMMDDHHMMSSDMZ:
5361 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
5362 if value[-1] != "Z":
5363 raise ValueError("non UTC timezone")
5364 if value[14] != ".":
5365 raise ValueError("no fractions separator")
5368 raise ValueError("trailing zero")
5371 raise ValueError("only microsecond fractions are supported")
5372 us = pureint(us + ("0" * (6 - us_len)))
5373 year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
5374 return datetime(year, month, day, hour, minute, second, us)
5375 raise ValueError("invalid GeneralizedTime length")
5377 def _encode_time(self):
5379 encoded = value.strftime("%Y%m%d%H%M%S")
5380 if value.microsecond > 0:
5381 encoded += (".%06d" % value.microsecond).rstrip("0")
5382 return (encoded + "Z").encode("ascii")
5385 self._assert_ready()
5387 if value.microsecond > 0:
5388 encoded = self._encode_time()
5389 return b"".join((self.tag, len_encode(len(encoded)), encoded))
5390 return b"".join((self.tag, LEN_LEN_YYYYMMDDHHMMSSZ, self._encode_time()))
5392 def _encode1st(self, state):
5393 self._assert_ready()
5394 vlen = len(self._encode_time())
5395 return len(self.tag) + len_size(vlen) + vlen, state
5397 def _encode2nd(self, writer, state_iter):
5398 write_full(writer, self._encode())
5401 class GraphicString(CommonString):
5403 tag_default = tag_encode(25)
5404 encoding = "iso-8859-1"
5405 asn1_type_name = "GraphicString"
5408 class GeneralString(CommonString):
5410 tag_default = tag_encode(27)
5411 encoding = "iso-8859-1"
5412 asn1_type_name = "GeneralString"
5415 class UniversalString(CommonString):
5417 tag_default = tag_encode(28)
5418 encoding = "utf-32-be"
5419 asn1_type_name = "UniversalString"
5422 class BMPString(CommonString):
5424 tag_default = tag_encode(30)
5425 encoding = "utf-16-be"
5426 asn1_type_name = "BMPString"
5429 ChoiceState = namedtuple(
5431 BasicState._fields + ("specs", "value",),
5437 """``CHOICE`` special type
5441 class GeneralName(Choice):
5443 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
5444 ("dNSName", IA5String(impl=tag_ctxp(2))),
5447 >>> gn = GeneralName()
5449 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
5450 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5451 >>> gn["dNSName"] = IA5String("bar.baz")
5452 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
5453 >>> gn["rfc822Name"]
5456 [2] IA5String IA5 bar.baz
5459 >>> gn.value == gn["dNSName"]
5462 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
5464 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
5465 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5467 __slots__ = ("specs",)
5469 asn1_type_name = "CHOICE"
5482 :param value: set the value. Either ``(choice, value)`` tuple, or
5483 :py:class:`pyderasn.Choice` object
5484 :param bytes impl: can not be set, do **not** use it
5485 :param bytes expl: override default tag with ``EXPLICIT`` one
5486 :param default: set default value. Type same as in ``value``
5487 :param bool optional: is object ``OPTIONAL`` in sequence
5489 if impl is not None:
5490 raise ValueError("no implicit tag allowed for CHOICE")
5491 super().__init__(None, expl, default, optional, _decoded)
5493 schema = getattr(self, "schema", ())
5494 if len(schema) == 0:
5495 raise ValueError("schema must be specified")
5497 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5500 if value is not None:
5501 self._value = self._value_sanitize(value)
5502 if default is not None:
5503 default_value = self._value_sanitize(default)
5504 default_obj = self.__class__(impl=self.tag, expl=self._expl)
5505 default_obj.specs = self.specs
5506 default_obj._value = default_value
5507 self.default = default_obj
5509 self._value = copy(default_obj._value)
5510 if self._expl is not None:
5511 tag_class, _, tag_num = tag_decode(self._expl)
5512 self._tag_order = (tag_class, tag_num)
5514 def _value_sanitize(self, value):
5515 if (value.__class__ == tuple) and len(value) == 2:
5517 spec = self.specs.get(choice)
5519 raise ObjUnknown(choice)
5520 if not isinstance(obj, spec.__class__):
5521 raise InvalidValueType((spec,))
5522 return (choice, spec(obj))
5523 if isinstance(value, self.__class__):
5525 raise InvalidValueType((self.__class__, tuple))
5529 return self._value is not None and self._value[1].ready
5533 return self.expl_lenindef or (
5534 (self._value is not None) and
5535 self._value[1].bered
5538 def __getstate__(self):
5556 def __setstate__(self, state):
5557 super().__setstate__(state)
5558 self.specs = state.specs
5559 self._value = state.value
5561 def __eq__(self, their):
5562 if (their.__class__ == tuple) and len(their) == 2:
5563 return self._value == their
5564 if not isinstance(their, self.__class__):
5567 self.specs == their.specs and
5568 self._value == their._value
5578 return self.__class__(
5581 expl=self._expl if expl is None else expl,
5582 default=self.default if default is None else default,
5583 optional=self.optional if optional is None else optional,
5588 """Name of the choice
5590 self._assert_ready()
5591 return self._value[0]
5595 """Value of underlying choice
5597 self._assert_ready()
5598 return self._value[1]
5601 def tag_order(self):
5602 self._assert_ready()
5603 return self._value[1].tag_order if self._tag_order is None else self._tag_order
5606 def tag_order_cer(self):
5607 return min(v.tag_order_cer for v in self.specs.values())
5609 def __getitem__(self, key):
5610 if key not in self.specs:
5611 raise ObjUnknown(key)
5612 if self._value is None:
5614 choice, value = self._value
5619 def __setitem__(self, key, value):
5620 spec = self.specs.get(key)
5622 raise ObjUnknown(key)
5623 if not isinstance(value, spec.__class__):
5624 raise InvalidValueType((spec.__class__,))
5625 self._value = (key, spec(value))
5633 return self._value[1].decoded if self.ready else False
5636 self._assert_ready()
5637 return self._value[1].encode()
5639 def _encode1st(self, state):
5640 self._assert_ready()
5641 return self._value[1].encode1st(state)
5643 def _encode2nd(self, writer, state_iter):
5644 self._value[1].encode2nd(writer, state_iter)
5646 def _encode_cer(self, writer):
5647 self._assert_ready()
5648 self._value[1].encode_cer(writer)
5650 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5651 for choice, spec in self.specs.items():
5652 sub_decode_path = decode_path + (choice,)
5658 decode_path=sub_decode_path,
5661 _ctx_immutable=False,
5668 klass=self.__class__,
5669 decode_path=decode_path,
5672 if tag_only: # pragma: no cover
5676 for _decode_path, value, tail in spec.decode_evgen(
5680 decode_path=sub_decode_path,
5682 _ctx_immutable=False,
5684 yield _decode_path, value, tail
5686 _, value, tail = next(spec.decode_evgen(
5690 decode_path=sub_decode_path,
5692 _ctx_immutable=False,
5695 obj = self.__class__(
5698 default=self.default,
5699 optional=self.optional,
5700 _decoded=(offset, 0, value.fulllen),
5702 obj._value = (choice, value)
5703 yield decode_path, obj, tail
5706 value = pp_console_row(next(self.pps()))
5708 value = "%s[%r]" % (value, self.value)
5711 def pps(self, decode_path=()):
5714 asn1_type_name=self.asn1_type_name,
5715 obj_name=self.__class__.__name__,
5716 decode_path=decode_path,
5717 value=self.choice if self.ready else None,
5718 optional=self.optional,
5719 default=self == self.default,
5720 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5721 expl=None if self._expl is None else tag_decode(self._expl),
5726 expl_lenindef=self.expl_lenindef,
5730 yield self.value.pps(decode_path=decode_path + (self.choice,))
5731 for pp in self.pps_lenindef(decode_path):
5735 class PrimitiveTypes(Choice):
5736 """Predefined ``CHOICE`` for all generic primitive types
5738 It could be useful for general decoding of some unspecified values:
5740 >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
5741 OCTET STRING 3 bytes 666f6f
5742 >>> PrimitiveTypes().decod(hexdec("0203123456")).value
5746 schema = tuple((klass.__name__, klass()) for klass in (
5770 AnyState = namedtuple(
5772 BasicState._fields + ("value", "defined"),
5778 """``ANY`` special type
5780 >>> Any(Integer(-123))
5781 ANY INTEGER -123 (0X:7B)
5782 >>> a = Any(OctetString(b"hello world").encode())
5783 ANY 040b68656c6c6f20776f726c64
5784 >>> hexenc(bytes(a))
5785 b'0x040x0bhello world'
5787 __slots__ = ("defined",)
5788 tag_default = tag_encode(0)
5789 asn1_type_name = "ANY"
5799 :param value: set the value. Either any kind of pyderasn's
5800 **ready** object, or bytes. Pay attention that
5801 **no** validation is performed if raw binary value
5802 is valid TLV, except just tag decoding
5803 :param bytes expl: override default tag with ``EXPLICIT`` one
5804 :param bool optional: is object ``OPTIONAL`` in sequence
5806 super().__init__(None, expl, None, optional, _decoded)
5810 value = self._value_sanitize(value)
5812 if self._expl is None:
5813 if value.__class__ == bytes:
5814 tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5816 tag_class, tag_num = value.tag_order
5818 tag_class, _, tag_num = tag_decode(self._expl)
5819 self._tag_order = (tag_class, tag_num)
5822 def _value_sanitize(self, value):
5823 if value.__class__ == bytes:
5825 raise ValueError("%s value can not be empty" % self.__class__.__name__)
5827 if isinstance(value, self.__class__):
5829 if not isinstance(value, Obj):
5830 raise InvalidValueType((self.__class__, Obj, bytes))
5835 return self._value is not None
5838 def tag_order(self):
5839 self._assert_ready()
5840 return self._tag_order
5844 if self.expl_lenindef or self.lenindef:
5846 if self.defined is None:
5848 return self.defined[1].bered
5850 def __getstate__(self):
5868 def __setstate__(self, state):
5869 super().__setstate__(state)
5870 self._value = state.value
5871 self.defined = state.defined
5873 def __eq__(self, their):
5874 if their.__class__ == bytes:
5875 if self._value.__class__ == bytes:
5876 return self._value == their
5877 return self._value.encode() == their
5878 if issubclass(their.__class__, Any):
5879 if self.ready and their.ready:
5880 return bytes(self) == bytes(their)
5881 return self.ready == their.ready
5890 return self.__class__(
5892 expl=self._expl if expl is None else expl,
5893 optional=self.optional if optional is None else optional,
5896 def __bytes__(self):
5897 self._assert_ready()
5899 if value.__class__ == bytes:
5901 return self._value.encode()
5908 self._assert_ready()
5910 if value.__class__ == bytes:
5912 return value.encode()
5914 def _encode1st(self, state):
5915 self._assert_ready()
5917 if value.__class__ == bytes:
5918 return len(value), state
5919 return value.encode1st(state)
5921 def _encode2nd(self, writer, state_iter):
5923 if value.__class__ == bytes:
5924 write_full(writer, value)
5926 value.encode2nd(writer, state_iter)
5928 def _encode_cer(self, writer):
5929 self._assert_ready()
5931 if value.__class__ == bytes:
5932 write_full(writer, value)
5934 value.encode_cer(writer)
5936 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5938 t, tlen, lv = tag_strip(tlv)
5939 except DecodeError as err:
5940 raise err.__class__(
5942 klass=self.__class__,
5943 decode_path=decode_path,
5947 l, llen, v = len_decode(lv)
5948 except LenIndefForm as err:
5949 if not ctx.get("bered", False):
5950 raise err.__class__(
5952 klass=self.__class__,
5953 decode_path=decode_path,
5956 llen, vlen, v = 1, 0, lv[1:]
5957 sub_offset = offset + tlen + llen
5959 while v[:EOC_LEN].tobytes() != EOC:
5960 chunk, v = Any().decode(
5963 decode_path=decode_path + (str(chunk_i),),
5966 _ctx_immutable=False,
5968 vlen += chunk.tlvlen
5969 sub_offset += chunk.tlvlen
5971 tlvlen = tlen + llen + vlen + EOC_LEN
5972 obj = self.__class__(
5973 value=None if evgen_mode else tlv[:tlvlen].tobytes(),
5975 optional=self.optional,
5976 _decoded=(offset, 0, tlvlen),
5979 obj.tag = t.tobytes()
5980 yield decode_path, obj, v[EOC_LEN:]
5982 except DecodeError as err:
5983 raise err.__class__(
5985 klass=self.__class__,
5986 decode_path=decode_path,
5990 raise NotEnoughData(
5991 "encoded length is longer than data",
5992 klass=self.__class__,
5993 decode_path=decode_path,
5996 tlvlen = tlen + llen + l
5997 v, tail = tlv[:tlvlen], v[l:]
5998 obj = self.__class__(
5999 value=None if evgen_mode else v.tobytes(),
6001 optional=self.optional,
6002 _decoded=(offset, 0, tlvlen),
6004 obj.tag = t.tobytes()
6005 yield decode_path, obj, tail
6008 return pp_console_row(next(self.pps()))
6010 def pps(self, decode_path=()):
6014 elif value.__class__ == bytes:
6020 asn1_type_name=self.asn1_type_name,
6021 obj_name=self.__class__.__name__,
6022 decode_path=decode_path,
6024 blob=self._value if self._value.__class__ == bytes else None,
6025 optional=self.optional,
6026 default=self == self.default,
6027 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6028 expl=None if self._expl is None else tag_decode(self._expl),
6033 expl_offset=self.expl_offset if self.expled else None,
6034 expl_tlen=self.expl_tlen if self.expled else None,
6035 expl_llen=self.expl_llen if self.expled else None,
6036 expl_vlen=self.expl_vlen if self.expled else None,
6037 expl_lenindef=self.expl_lenindef,
6038 lenindef=self.lenindef,
6041 defined_by, defined = self.defined or (None, None)
6042 if defined_by is not None:
6044 decode_path=decode_path + (DecodePathDefBy(defined_by),)
6046 for pp in self.pps_lenindef(decode_path):
6050 ########################################################################
6051 # ASN.1 constructed types
6052 ########################################################################
6054 def abs_decode_path(decode_path, rel_path):
6055 """Create an absolute decode path from current and relative ones
6057 :param decode_path: current decode path, starting point. Tuple of strings
6058 :param rel_path: relative path to ``decode_path``. Tuple of strings.
6059 If first tuple's element is "/", then treat it as
6060 an absolute path, ignoring ``decode_path`` as
6061 starting point. Also this tuple can contain ".."
6062 elements, stripping the leading element from
6065 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
6066 ("foo", "bar", "baz", "whatever")
6067 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
6069 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
6072 if rel_path[0] == "/":
6074 if rel_path[0] == "..":
6075 return abs_decode_path(decode_path[:-1], rel_path[1:])
6076 return decode_path + rel_path
6079 SequenceState = namedtuple(
6081 BasicState._fields + ("specs", "value",),
6086 class SequenceEncode1stMixing:
6087 def _encode1st(self, state):
6089 idx = len(state) - 1
6091 for v in self._values_for_encoding():
6092 l, _ = v.encode1st(state)
6095 return len(self.tag) + len_size(vlen) + vlen, state
6098 class Sequence(SequenceEncode1stMixing, Obj):
6099 """``SEQUENCE`` structure type
6101 You have to make specification of sequence::
6103 class Extension(Sequence):
6105 ("extnID", ObjectIdentifier()),
6106 ("critical", Boolean(default=False)),
6107 ("extnValue", OctetString()),
6110 Then, you can work with it as with dictionary.
6112 >>> ext = Extension()
6113 >>> Extension().specs
6115 ('extnID', OBJECT IDENTIFIER),
6116 ('critical', BOOLEAN False OPTIONAL DEFAULT),
6117 ('extnValue', OCTET STRING),
6119 >>> ext["extnID"] = "1.2.3"
6120 Traceback (most recent call last):
6121 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
6122 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
6124 You can determine if sequence is ready to be encoded:
6129 Traceback (most recent call last):
6130 pyderasn.ObjNotReady: object is not ready: extnValue
6131 >>> ext["extnValue"] = OctetString(b"foobar")
6135 Value you want to assign, must have the same **type** as in
6136 corresponding specification, but it can have different tags,
6137 optional/default attributes -- they will be taken from specification
6140 class TBSCertificate(Sequence):
6142 ("version", Version(expl=tag_ctxc(0), default="v1")),
6145 >>> tbs = TBSCertificate()
6146 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
6148 Assign ``None`` to remove value from sequence.
6150 You can set values in Sequence during its initialization:
6152 >>> AlgorithmIdentifier((
6153 ("algorithm", ObjectIdentifier("1.2.3")),
6154 ("parameters", Any(Null()))
6156 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
6158 You can determine if value exists/set in the sequence and take its value:
6160 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
6163 OBJECT IDENTIFIER 1.2.3
6165 But pay attention that if value has default, then it won't be (not
6166 in) in the sequence (because ``DEFAULT`` must not be encoded in
6167 DER), but you can read its value:
6169 >>> "critical" in ext, ext["critical"]
6170 (False, BOOLEAN False)
6171 >>> ext["critical"] = Boolean(True)
6172 >>> "critical" in ext, ext["critical"]
6173 (True, BOOLEAN True)
6175 All defaulted values are always optional.
6177 .. _allow_default_values_ctx:
6179 DER prohibits default value encoding and will raise an error if
6180 default value is unexpectedly met during decode.
6181 If :ref:`bered <bered_ctx>` context option is set, then no error
6182 will be raised, but ``bered`` attribute set. You can disable strict
6183 defaulted values existence validation by setting
6184 ``"allow_default_values": True`` :ref:`context <ctx>` option.
6186 All values with DEFAULT specified are decoded atomically in
6187 :ref:`evgen mode <evgen_mode>`. If DEFAULT value is some kind of
6188 SEQUENCE, then it will be yielded as a single element, not
6189 disassembled. That is required for DEFAULT existence check.
6191 Two sequences are equal if they have equal specification (schema),
6192 implicit/explicit tagging and the same values.
6194 __slots__ = ("specs",)
6195 tag_default = tag_encode(form=TagFormConstructed, num=16)
6196 asn1_type_name = "SEQUENCE"
6208 super().__init__(impl, expl, default, optional, _decoded)
6210 schema = getattr(self, "schema", ())
6212 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
6215 if value is not None:
6216 if issubclass(value.__class__, Sequence):
6217 self._value = value._value
6218 elif hasattr(value, "__iter__"):
6219 for seq_key, seq_value in value:
6220 self[seq_key] = seq_value
6222 raise InvalidValueType((Sequence,))
6223 if default is not None:
6224 if not issubclass(default.__class__, Sequence):
6225 raise InvalidValueType((Sequence,))
6226 default_value = default._value
6227 default_obj = self.__class__(impl=self.tag, expl=self._expl)
6228 default_obj.specs = self.specs
6229 default_obj._value = default_value
6230 self.default = default_obj
6232 self._value = copy(default_obj._value)
6236 for name, spec in self.specs.items():
6237 value = self._value.get(name)
6248 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6250 return any(value.bered for value in self._value.values())
6252 def __getstate__(self):
6253 return SequenceState(
6267 {k: copy(v) for k, v in self._value.items()},
6270 def __setstate__(self, state):
6271 super().__setstate__(state)
6272 self.specs = state.specs
6273 self._value = state.value
6275 def __eq__(self, their):
6276 if not isinstance(their, self.__class__):
6279 self.specs == their.specs and
6280 self.tag == their.tag and
6281 self._expl == their._expl and
6282 self._value == their._value
6293 return self.__class__(
6296 impl=self.tag if impl is None else impl,
6297 expl=self._expl if expl is None else expl,
6298 default=self.default if default is None else default,
6299 optional=self.optional if optional is None else optional,
6302 def __contains__(self, key):
6303 return key in self._value
6305 def __setitem__(self, key, value):
6306 spec = self.specs.get(key)
6308 raise ObjUnknown(key)
6310 self._value.pop(key, None)
6312 if not isinstance(value, spec.__class__):
6313 raise InvalidValueType((spec.__class__,))
6314 value = spec(value=value)
6315 if spec.default is not None and value == spec.default:
6316 self._value.pop(key, None)
6318 self._value[key] = value
6320 def __getitem__(self, key):
6321 value = self._value.get(key)
6322 if value is not None:
6324 spec = self.specs.get(key)
6326 raise ObjUnknown(key)
6327 if spec.default is not None:
6331 def _values_for_encoding(self):
6332 for name, spec in self.specs.items():
6333 value = self._value.get(name)
6337 raise ObjNotReady(name)
6341 v = b"".join(v.encode() for v in self._values_for_encoding())
6342 return b"".join((self.tag, len_encode(len(v)), v))
6344 def _encode2nd(self, writer, state_iter):
6345 write_full(writer, self.tag + len_encode(next(state_iter)))
6346 for v in self._values_for_encoding():
6347 v.encode2nd(writer, state_iter)
6349 def _encode_cer(self, writer):
6350 write_full(writer, self.tag + LENINDEF)
6351 for v in self._values_for_encoding():
6352 v.encode_cer(writer)
6353 write_full(writer, EOC)
6355 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6357 t, tlen, lv = tag_strip(tlv)
6358 except DecodeError as err:
6359 raise err.__class__(
6361 klass=self.__class__,
6362 decode_path=decode_path,
6367 klass=self.__class__,
6368 decode_path=decode_path,
6371 if tag_only: # pragma: no cover
6375 ctx_bered = ctx.get("bered", False)
6377 l, llen, v = len_decode(lv)
6378 except LenIndefForm as err:
6380 raise err.__class__(
6382 klass=self.__class__,
6383 decode_path=decode_path,
6386 l, llen, v = 0, 1, lv[1:]
6388 except DecodeError as err:
6389 raise err.__class__(
6391 klass=self.__class__,
6392 decode_path=decode_path,
6396 raise NotEnoughData(
6397 "encoded length is longer than data",
6398 klass=self.__class__,
6399 decode_path=decode_path,
6403 v, tail = v[:l], v[l:]
6405 sub_offset = offset + tlen + llen
6408 ctx_allow_default_values = ctx.get("allow_default_values", False)
6409 for name, spec in self.specs.items():
6410 if spec.optional and (
6411 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
6415 spec_defaulted = spec.default is not None
6416 sub_decode_path = decode_path + (name,)
6418 if evgen_mode and not spec_defaulted:
6419 for _decode_path, value, v_tail in spec.decode_evgen(
6423 decode_path=sub_decode_path,
6425 _ctx_immutable=False,
6427 yield _decode_path, value, v_tail
6429 _, value, v_tail = next(spec.decode_evgen(
6433 decode_path=sub_decode_path,
6435 _ctx_immutable=False,
6438 except TagMismatch as err:
6439 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
6443 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
6444 if not evgen_mode and defined is not None:
6445 defined_by, defined_spec = defined
6446 if issubclass(value.__class__, SequenceOf):
6447 for i, _value in enumerate(value):
6448 sub_sub_decode_path = sub_decode_path + (
6450 DecodePathDefBy(defined_by),
6452 defined_value, defined_tail = defined_spec.decode(
6453 memoryview(bytes(_value)),
6455 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6456 if value.expled else (value.tlen + value.llen)
6459 decode_path=sub_sub_decode_path,
6461 _ctx_immutable=False,
6463 if len(defined_tail) > 0:
6466 klass=self.__class__,
6467 decode_path=sub_sub_decode_path,
6470 _value.defined = (defined_by, defined_value)
6472 defined_value, defined_tail = defined_spec.decode(
6473 memoryview(bytes(value)),
6475 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6476 if value.expled else (value.tlen + value.llen)
6479 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6481 _ctx_immutable=False,
6483 if len(defined_tail) > 0:
6486 klass=self.__class__,
6487 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6490 value.defined = (defined_by, defined_value)
6492 value_len = value.fulllen
6494 sub_offset += value_len
6498 yield sub_decode_path, value, v_tail
6499 if value == spec.default:
6500 if ctx_bered or ctx_allow_default_values:
6504 "DEFAULT value met",
6505 klass=self.__class__,
6506 decode_path=sub_decode_path,
6510 values[name] = value
6511 spec_defines = getattr(spec, "defines", ())
6512 if len(spec_defines) == 0:
6513 defines_by_path = ctx.get("defines_by_path", ())
6514 if len(defines_by_path) > 0:
6515 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
6516 if spec_defines is not None and len(spec_defines) > 0:
6517 for rel_path, schema in spec_defines:
6518 defined = schema.get(value, None)
6519 if defined is not None:
6520 ctx.setdefault("_defines", []).append((
6521 abs_decode_path(sub_decode_path[:-1], rel_path),
6525 if v[:EOC_LEN].tobytes() != EOC:
6528 klass=self.__class__,
6529 decode_path=decode_path,
6537 klass=self.__class__,
6538 decode_path=decode_path,
6541 obj = self.__class__(
6545 default=self.default,
6546 optional=self.optional,
6547 _decoded=(offset, llen, vlen),
6550 obj.lenindef = lenindef
6551 obj.ber_encoded = ber_encoded
6552 yield decode_path, obj, tail
6555 value = pp_console_row(next(self.pps()))
6557 for name in self.specs:
6558 _value = self._value.get(name)
6561 cols.append("%s: %s" % (name, repr(_value)))
6562 return "%s[%s]" % (value, "; ".join(cols))
6564 def pps(self, decode_path=()):
6567 asn1_type_name=self.asn1_type_name,
6568 obj_name=self.__class__.__name__,
6569 decode_path=decode_path,
6570 optional=self.optional,
6571 default=self == self.default,
6572 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6573 expl=None if self._expl is None else tag_decode(self._expl),
6578 expl_offset=self.expl_offset if self.expled else None,
6579 expl_tlen=self.expl_tlen if self.expled else None,
6580 expl_llen=self.expl_llen if self.expled else None,
6581 expl_vlen=self.expl_vlen if self.expled else None,
6582 expl_lenindef=self.expl_lenindef,
6583 lenindef=self.lenindef,
6584 ber_encoded=self.ber_encoded,
6587 for name in self.specs:
6588 value = self._value.get(name)
6591 yield value.pps(decode_path=decode_path + (name,))
6592 for pp in self.pps_lenindef(decode_path):
6596 class Set(Sequence, SequenceEncode1stMixing):
6597 """``SET`` structure type
6599 Its usage is identical to :py:class:`pyderasn.Sequence`.
6601 .. _allow_unordered_set_ctx:
6603 DER prohibits unordered values encoding and will raise an error
6604 during decode. If :ref:`bered <bered_ctx>` context option is set,
6605 then no error will occur. Also you can disable strict values
6606 ordering check by setting ``"allow_unordered_set": True``
6607 :ref:`context <ctx>` option.
6610 tag_default = tag_encode(form=TagFormConstructed, num=17)
6611 asn1_type_name = "SET"
6613 def _values_for_encoding(self):
6614 return sorted(super()._values_for_encoding(), key=attrgetter("tag_order"))
6616 def _encode_cer(self, writer):
6617 write_full(writer, self.tag + LENINDEF)
6619 super()._values_for_encoding(),
6620 key=attrgetter("tag_order_cer"),
6622 v.encode_cer(writer)
6623 write_full(writer, EOC)
6625 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6627 t, tlen, lv = tag_strip(tlv)
6628 except DecodeError as err:
6629 raise err.__class__(
6631 klass=self.__class__,
6632 decode_path=decode_path,
6637 klass=self.__class__,
6638 decode_path=decode_path,
6645 ctx_bered = ctx.get("bered", False)
6647 l, llen, v = len_decode(lv)
6648 except LenIndefForm as err:
6650 raise err.__class__(
6652 klass=self.__class__,
6653 decode_path=decode_path,
6656 l, llen, v = 0, 1, lv[1:]
6658 except DecodeError as err:
6659 raise err.__class__(
6661 klass=self.__class__,
6662 decode_path=decode_path,
6666 raise NotEnoughData(
6667 "encoded length is longer than data",
6668 klass=self.__class__,
6672 v, tail = v[:l], v[l:]
6674 sub_offset = offset + tlen + llen
6677 ctx_allow_default_values = ctx.get("allow_default_values", False)
6678 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6679 tag_order_prev = (0, 0)
6680 _specs_items = copy(self.specs)
6683 if lenindef and v[:EOC_LEN].tobytes() == EOC:
6685 for name, spec in _specs_items.items():
6686 sub_decode_path = decode_path + (name,)
6692 decode_path=sub_decode_path,
6695 _ctx_immutable=False,
6702 klass=self.__class__,
6703 decode_path=decode_path,
6706 spec_defaulted = spec.default is not None
6707 if evgen_mode and not spec_defaulted:
6708 for _decode_path, value, v_tail in spec.decode_evgen(
6712 decode_path=sub_decode_path,
6714 _ctx_immutable=False,
6716 yield _decode_path, value, v_tail
6718 _, value, v_tail = next(spec.decode_evgen(
6722 decode_path=sub_decode_path,
6724 _ctx_immutable=False,
6727 value_tag_order = value.tag_order
6728 value_len = value.fulllen
6729 if tag_order_prev >= value_tag_order:
6730 if ctx_bered or ctx_allow_unordered_set:
6734 "unordered " + self.asn1_type_name,
6735 klass=self.__class__,
6736 decode_path=sub_decode_path,
6741 yield sub_decode_path, value, v_tail
6742 if value != spec.default:
6744 elif ctx_bered or ctx_allow_default_values:
6748 "DEFAULT value met",
6749 klass=self.__class__,
6750 decode_path=sub_decode_path,
6753 values[name] = value
6754 del _specs_items[name]
6755 tag_order_prev = value_tag_order
6756 sub_offset += value_len
6760 obj = self.__class__(
6764 default=self.default,
6765 optional=self.optional,
6766 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6769 if v[:EOC_LEN].tobytes() != EOC:
6772 klass=self.__class__,
6773 decode_path=decode_path,
6778 for name, spec in self.specs.items():
6779 if name not in values and not spec.optional:
6781 "%s value is not ready" % name,
6782 klass=self.__class__,
6783 decode_path=decode_path,
6788 obj.ber_encoded = ber_encoded
6789 yield decode_path, obj, tail
6792 SequenceOfState = namedtuple(
6794 BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
6799 class SequenceOf(SequenceEncode1stMixing, Obj):
6800 """``SEQUENCE OF`` sequence type
6802 For that kind of type you must specify the object it will carry on
6803 (bounds are for example here, not required)::
6805 class Ints(SequenceOf):
6810 >>> ints.append(Integer(123))
6811 >>> ints.append(Integer(234))
6813 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
6814 >>> [int(i) for i in ints]
6816 >>> ints.append(Integer(345))
6817 Traceback (most recent call last):
6818 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
6821 >>> ints[1] = Integer(345)
6823 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
6825 You can initialize sequence with preinitialized values:
6827 >>> ints = Ints([Integer(123), Integer(234)])
6829 Also you can use iterator as a value:
6831 >>> ints = Ints(iter(Integer(i) for i in range(1000000)))
6833 And it won't be iterated until encoding process. Pay attention that
6834 bounds and required schema checks are done only during the encoding
6835 process in that case! After encode was called, then value is zeroed
6836 back to empty list and you have to set it again. That mode is useful
6837 mainly with CER encoding mode, where all objects from the iterable
6838 will be streamed to the buffer, without copying all of them to
6841 __slots__ = ("spec", "_bound_min", "_bound_max")
6842 tag_default = tag_encode(form=TagFormConstructed, num=16)
6843 asn1_type_name = "SEQUENCE OF"
6856 super().__init__(impl, expl, default, optional, _decoded)
6858 schema = getattr(self, "schema", None)
6860 raise ValueError("schema must be specified")
6862 self._bound_min, self._bound_max = getattr(
6866 ) if bounds is None else bounds
6868 if value is not None:
6869 self._value = self._value_sanitize(value)
6870 if default is not None:
6871 default_value = self._value_sanitize(default)
6872 default_obj = self.__class__(
6877 default_obj._value = default_value
6878 self.default = default_obj
6880 self._value = copy(default_obj._value)
6882 def _value_sanitize(self, value):
6884 if issubclass(value.__class__, SequenceOf):
6885 value = value._value
6886 elif hasattr(value, NEXT_ATTR_NAME):
6888 elif hasattr(value, "__iter__"):
6891 raise InvalidValueType((self.__class__, iter, "iterator"))
6893 if not self._bound_min <= len(value) <= self._bound_max:
6894 raise BoundsError(self._bound_min, len(value), self._bound_max)
6895 class_expected = self.spec.__class__
6897 if not isinstance(v, class_expected):
6898 raise InvalidValueType((class_expected,))
6903 if hasattr(self._value, NEXT_ATTR_NAME):
6905 if self._bound_min > 0 and len(self._value) == 0:
6907 return all(v.ready for v in self._value)
6911 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6913 return any(v.bered for v in self._value)
6915 def __getstate__(self):
6916 if hasattr(self._value, NEXT_ATTR_NAME):
6917 raise ValueError("can not pickle SequenceOf with iterator")
6918 return SequenceOfState(
6932 [copy(v) for v in self._value],
6937 def __setstate__(self, state):
6938 super().__setstate__(state)
6939 self.spec = state.spec
6940 self._value = state.value
6941 self._bound_min = state.bound_min
6942 self._bound_max = state.bound_max
6944 def __eq__(self, their):
6945 if isinstance(their, self.__class__):
6947 self.spec == their.spec and
6948 self.tag == their.tag and
6949 self._expl == their._expl and
6950 self._value == their._value
6952 if hasattr(their, "__iter__"):
6953 return self._value == list(their)
6965 return self.__class__(
6969 (self._bound_min, self._bound_max)
6970 if bounds is None else bounds
6972 impl=self.tag if impl is None else impl,
6973 expl=self._expl if expl is None else expl,
6974 default=self.default if default is None else default,
6975 optional=self.optional if optional is None else optional,
6978 def __contains__(self, key):
6979 return key in self._value
6981 def append(self, value):
6982 if not isinstance(value, self.spec.__class__):
6983 raise InvalidValueType((self.spec.__class__,))
6984 if len(self._value) + 1 > self._bound_max:
6987 len(self._value) + 1,
6990 self._value.append(value)
6993 return iter(self._value)
6996 return len(self._value)
6998 def __setitem__(self, key, value):
6999 if not isinstance(value, self.spec.__class__):
7000 raise InvalidValueType((self.spec.__class__,))
7001 self._value[key] = self.spec(value=value)
7003 def __getitem__(self, key):
7004 return self._value[key]
7006 def _values_for_encoding(self):
7007 return iter(self._value)
7010 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7013 values_append = values.append
7014 class_expected = self.spec.__class__
7015 values_for_encoding = self._values_for_encoding()
7017 for v in values_for_encoding:
7018 if not isinstance(v, class_expected):
7019 raise InvalidValueType((class_expected,))
7020 values_append(v.encode())
7021 if not self._bound_min <= len(values) <= self._bound_max:
7022 raise BoundsError(self._bound_min, len(values), self._bound_max)
7023 value = b"".join(values)
7025 value = b"".join(v.encode() for v in self._values_for_encoding())
7026 return b"".join((self.tag, len_encode(len(value)), value))
7028 def _encode1st(self, state):
7029 state = super()._encode1st(state)
7030 if hasattr(self._value, NEXT_ATTR_NAME):
7034 def _encode2nd(self, writer, state_iter):
7035 write_full(writer, self.tag + len_encode(next(state_iter)))
7036 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7039 class_expected = self.spec.__class__
7040 values_for_encoding = self._values_for_encoding()
7042 for v in values_for_encoding:
7043 if not isinstance(v, class_expected):
7044 raise InvalidValueType((class_expected,))
7045 v.encode2nd(writer, state_iter)
7047 if not self._bound_min <= values_count <= self._bound_max:
7048 raise BoundsError(self._bound_min, values_count, self._bound_max)
7050 for v in self._values_for_encoding():
7051 v.encode2nd(writer, state_iter)
7053 def _encode_cer(self, writer):
7054 write_full(writer, self.tag + LENINDEF)
7055 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7057 class_expected = self.spec.__class__
7059 values_for_encoding = self._values_for_encoding()
7061 for v in values_for_encoding:
7062 if not isinstance(v, class_expected):
7063 raise InvalidValueType((class_expected,))
7064 v.encode_cer(writer)
7066 if not self._bound_min <= values_count <= self._bound_max:
7067 raise BoundsError(self._bound_min, values_count, self._bound_max)
7069 for v in self._values_for_encoding():
7070 v.encode_cer(writer)
7071 write_full(writer, EOC)
7081 ordering_check=False,
7084 t, tlen, lv = tag_strip(tlv)
7085 except DecodeError as err:
7086 raise err.__class__(
7088 klass=self.__class__,
7089 decode_path=decode_path,
7094 klass=self.__class__,
7095 decode_path=decode_path,
7102 ctx_bered = ctx.get("bered", False)
7104 l, llen, v = len_decode(lv)
7105 except LenIndefForm as err:
7107 raise err.__class__(
7109 klass=self.__class__,
7110 decode_path=decode_path,
7113 l, llen, v = 0, 1, lv[1:]
7115 except DecodeError as err:
7116 raise err.__class__(
7118 klass=self.__class__,
7119 decode_path=decode_path,
7123 raise NotEnoughData(
7124 "encoded length is longer than data",
7125 klass=self.__class__,
7126 decode_path=decode_path,
7130 v, tail = v[:l], v[l:]
7132 sub_offset = offset + tlen + llen
7135 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
7136 value_prev = memoryview(v[:0])
7140 if lenindef and v[:EOC_LEN].tobytes() == EOC:
7142 sub_decode_path = decode_path + (str(_value_count),)
7144 for _decode_path, value, v_tail in spec.decode_evgen(
7148 decode_path=sub_decode_path,
7150 _ctx_immutable=False,
7152 yield _decode_path, value, v_tail
7154 _, value, v_tail = next(spec.decode_evgen(
7158 decode_path=sub_decode_path,
7160 _ctx_immutable=False,
7163 value_len = value.fulllen
7165 if value_prev.tobytes() > v[:value_len].tobytes():
7166 if ctx_bered or ctx_allow_unordered_set:
7170 "unordered " + self.asn1_type_name,
7171 klass=self.__class__,
7172 decode_path=sub_decode_path,
7175 value_prev = v[:value_len]
7178 _value.append(value)
7179 sub_offset += value_len
7182 if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
7184 msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
7185 klass=self.__class__,
7186 decode_path=decode_path,
7190 obj = self.__class__(
7191 value=None if evgen_mode else _value,
7193 bounds=(self._bound_min, self._bound_max),
7196 default=self.default,
7197 optional=self.optional,
7198 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
7200 except BoundsError as err:
7203 klass=self.__class__,
7204 decode_path=decode_path,
7208 if v[:EOC_LEN].tobytes() != EOC:
7211 klass=self.__class__,
7212 decode_path=decode_path,
7217 obj.ber_encoded = ber_encoded
7218 yield decode_path, obj, tail
7222 pp_console_row(next(self.pps())),
7223 ", ".join(repr(v) for v in self._value),
7226 def pps(self, decode_path=()):
7229 asn1_type_name=self.asn1_type_name,
7230 obj_name=self.__class__.__name__,
7231 decode_path=decode_path,
7232 optional=self.optional,
7233 default=self == self.default,
7234 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
7235 expl=None if self._expl is None else tag_decode(self._expl),
7240 expl_offset=self.expl_offset if self.expled else None,
7241 expl_tlen=self.expl_tlen if self.expled else None,
7242 expl_llen=self.expl_llen if self.expled else None,
7243 expl_vlen=self.expl_vlen if self.expled else None,
7244 expl_lenindef=self.expl_lenindef,
7245 lenindef=self.lenindef,
7246 ber_encoded=self.ber_encoded,
7249 for i, value in enumerate(self._value):
7250 yield value.pps(decode_path=decode_path + (str(i),))
7251 for pp in self.pps_lenindef(decode_path):
7255 class SetOf(SequenceOf):
7256 """``SET OF`` sequence type
7258 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
7261 tag_default = tag_encode(form=TagFormConstructed, num=17)
7262 asn1_type_name = "SET OF"
7264 def _value_sanitize(self, value):
7265 value = super()._value_sanitize(value)
7266 if hasattr(value, NEXT_ATTR_NAME):
7268 "SetOf does not support iterator values, as no sense in them"
7273 v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
7274 return b"".join((self.tag, len_encode(len(v)), v))
7276 def _encode2nd(self, writer, state_iter):
7277 write_full(writer, self.tag + len_encode(next(state_iter)))
7279 for v in self._values_for_encoding():
7281 v.encode2nd(buf.write, state_iter)
7282 values.append(buf.getvalue())
7285 write_full(writer, v)
7287 def _encode_cer(self, writer):
7288 write_full(writer, self.tag + LENINDEF)
7289 for v in sorted(encode_cer(v) for v in self._values_for_encoding()):
7290 write_full(writer, v)
7291 write_full(writer, EOC)
7293 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
7294 return super()._decode(
7301 ordering_check=True,
7305 def obj_by_path(pypath): # pragma: no cover
7306 """Import object specified as string Python path
7308 Modules must be separated from classes/functions with ``:``.
7310 >>> obj_by_path("foo.bar:Baz")
7311 <class 'foo.bar.Baz'>
7312 >>> obj_by_path("foo.bar:Baz.boo")
7313 <classmethod 'foo.bar.Baz.boo'>
7315 mod, objs = pypath.rsplit(":", 1)
7316 from importlib import import_module
7317 obj = import_module(mod)
7318 for obj_name in objs.split("."):
7319 obj = getattr(obj, obj_name)
7323 def generic_decoder(): # pragma: no cover
7324 # All of this below is a big hack with self references
7325 choice = PrimitiveTypes()
7326 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
7327 choice.specs["SetOf"] = SetOf(schema=choice)
7329 choice.specs["SequenceOf%d" % i] = SequenceOf(
7333 choice.specs["Any"] = Any()
7335 # Class name equals to type name, to omit it from output
7336 class SEQUENCEOF(SequenceOf):
7344 with_decode_path=False,
7345 decode_path_only=(),
7348 def _pprint_pps(pps):
7350 if hasattr(pp, "_fields"):
7352 decode_path_only != () and
7353 pp.decode_path[:len(decode_path_only)] != decode_path_only
7356 if pp.asn1_type_name == Choice.asn1_type_name:
7358 pp_kwargs = pp._asdict()
7359 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
7360 pp = _pp(**pp_kwargs)
7361 yield pp_console_row(
7366 with_colours=with_colours,
7367 with_decode_path=with_decode_path,
7368 decode_path_len_decrease=len(decode_path_only),
7370 for row in pp_console_blob(
7372 decode_path_len_decrease=len(decode_path_only),
7376 for row in _pprint_pps(pp):
7378 return "\n".join(_pprint_pps(obj.pps(decode_path)))
7379 return SEQUENCEOF(), pprint_any
7382 def ascii_visualize(ba):
7383 """Output only ASCII printable characters, like in hexdump -C
7385 Example output for given binary string (right part)::
7387 92 2b 39 20 65 91 e6 8e 95 93 1a 58 df 02 78 ea |.+9 e......X..x.|
7390 return "".join((chr(b) if 0x20 <= b <= 0x7E else ".") for b in ba)
7394 """Generate ``hexdump -C`` like output
7398 00000000 30 80 30 80 a0 80 02 01 02 00 00 02 14 54 a5 18 |0.0..........T..|
7399 00000010 69 ef 8b 3f 15 fd ea ad bd 47 e0 94 81 6b 06 6a |i..?.....G...k.j|
7401 Result of that function is a generator of lines, where each line is
7406 ["00000010 ", " 69", " ef", " 8b", " 3f", " 15", " fd", " ea", " ad ",
7407 " bd", " 47", " e0", " 94", " 81", " 6b", " 06", " 6a ",
7408 " |i..?.....G...k.j|"]
7412 hexed = hexenc(raw).upper()
7413 addr, cols = 0, ["%08x " % 0]
7414 for i in range(0, len(hexed), 2):
7415 if i != 0 and i // 2 % 8 == 0:
7417 if i != 0 and i // 2 % 16 == 0:
7418 cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:addr + 16])))
7421 cols = ["%08x " % addr]
7422 cols.append(" " + hexed[i:i + 2])
7424 cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:])))
7428 def browse(raw, obj, oid_maps=()):
7429 """Interactive browser
7431 :param bytes raw: binary data you decoded
7432 :param obj: decoded :py:class:`pyderasn.Obj`
7433 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
7434 Its human readable form is printed when OID is met
7436 .. note:: `urwid <http://urwid.org/>`__ dependency required
7438 This browser is an interactive terminal application for browsing
7439 structures of your decoded ASN.1 objects. You can quit it with **q**
7440 key. It consists of three windows:
7443 View of ASN.1 elements hierarchy. You can navigate it using **Up**,
7444 **Down**, **PageUp**, **PageDown**, **Home**, **End** keys.
7445 **Left** key goes to constructed element above. **Plus**/**Minus**
7446 keys collapse/uncollapse constructed elements. **Space** toggles it
7448 window with various information about element. You can scroll it
7449 with **h**/**l** (down, up) (**H**/**L** for triple speed) keys
7451 window with raw data hexdump and highlighted current element's
7452 contents. It automatically focuses on element's data. You can
7453 scroll it with **j**/**k** (down, up) (**J**/**K** for triple
7454 speed) keys. If element has explicit tag, then it also will be
7455 highlighted with different colour
7457 Window's header contains current decode path and progress bars with
7458 position in *info* and *hexdump* windows.
7460 If you press **d**, then current element will be saved in the
7461 current directory under its decode path name (adding ".0", ".1", etc
7462 suffix if such file already exists). **D** will save it with explicit tag.
7464 You can also invoke it with ``--browse`` command line argument.
7466 from copy import deepcopy
7467 from os.path import exists as path_exists
7470 class TW(urwid.TreeWidget):
7471 def __init__(self, state, *args, **kwargs):
7473 self.scrolled = {"info": False, "hexdump": False}
7474 super().__init__(*args, **kwargs)
7477 pp = self.get_node().get_value()
7478 constructed = len(pp) > 1
7479 return (pp if hasattr(pp, "_fields") else pp[0]), constructed
7481 def _state_update(self):
7482 pp, _ = self._get_pp()
7483 self.state["decode_path"].set_text(
7484 ":".join(str(p) for p in pp.decode_path)
7486 lines = deepcopy(self.state["hexed"])
7488 def attr_set(i, attr):
7489 line = lines[i // 16]
7490 idx = 1 + (i - 16 * (i // 16))
7491 line[idx] = (attr, line[idx])
7493 if pp.expl_offset is not None:
7496 pp.expl_offset + pp.expl_tlen + pp.expl_llen,
7498 attr_set(i, "select-expl")
7499 for i in range(pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen):
7500 attr_set(i, "select-value")
7501 self.state["hexdump"]._set_body([urwid.Text(line) for line in lines])
7502 self.state["hexdump"].set_focus(pp.offset // 16)
7503 self.state["hexdump"].set_focus_valign("middle")
7504 self.state["hexdump_bar"].set_completion(
7505 (100 * pp.offset // 16) //
7506 len(self.state["hexdump"]._body.positions())
7510 [("header", "Name: "), pp.obj_name],
7511 [("header", "Type: "), pp.asn1_type_name],
7512 [("header", "Offset: "), "%d (0x%x)" % (pp.offset, pp.offset)],
7513 [("header", "[TLV]len: "), "%d/%d/%d" % (
7514 pp.tlen, pp.llen, pp.vlen,
7516 [("header", "TLVlen: "), "%d" % sum((
7517 pp.tlen, pp.llen, pp.vlen,
7519 [("header", "Slice: "), "[%d:%d]" % (
7520 pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen,
7524 lines.append([("warning", "LENINDEF")])
7526 lines.append([("warning", "BER encoded")])
7528 lines.append([("warning", "BERed")])
7529 if pp.expl is not None:
7530 lines.append([("header", "EXPLICIT")])
7531 klass, _, num = pp.expl
7532 lines.append([" Tag: %s%d" % (TagClassReprs[klass], num)])
7533 if pp.expl_offset is not None:
7534 lines.append([" Offset: %d" % pp.expl_offset])
7535 lines.append([" [TLV]len: %d/%d/%d" % (
7536 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7538 lines.append([" TLVlen: %d" % sum((
7539 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7541 lines.append([" Slice: [%d:%d]" % (
7543 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen,
7545 if pp.impl is not None:
7546 klass, _, num = pp.impl
7548 ("header", "IMPLICIT: "), "%s%d" % (TagClassReprs[klass], num),
7551 lines.append(["OPTIONAL"])
7553 lines.append(["DEFAULT"])
7554 if len(pp.decode_path) > 0:
7555 ent = pp.decode_path[-1]
7556 if isinstance(ent, DecodePathDefBy):
7558 value = str(ent.defined_by)
7559 oid_name = find_oid_name(
7560 ent.defined_by.asn1_type_name, oid_maps, value,
7562 lines.append([("header", "DEFINED BY: "), "%s" % (
7563 value if oid_name is None
7564 else "%s (%s)" % (oid_name, value)
7567 if pp.value is not None:
7568 lines.append([("header", "Value: "), pp.value])
7570 len(oid_maps) > 0 and
7571 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
7573 for oid_map in oid_maps:
7574 oid_name = oid_map.get(pp.value)
7575 if oid_name is not None:
7576 lines.append([("header", "Human: "), oid_name])
7578 if pp.asn1_type_name == Integer.asn1_type_name:
7580 ("header", "Decimal: "), "%d" % int(pp.obj),
7583 ("header", "Hexadecimal: "), colonize_hex(pp.obj.tohex()),
7585 if pp.blob.__class__ == bytes:
7586 blob = hexenc(pp.blob).upper()
7587 for i in range(0, len(blob), 32):
7588 lines.append([colonize_hex(blob[i:i + 32])])
7589 elif pp.blob.__class__ == tuple:
7590 lines.append([", ".join(pp.blob)])
7591 self.state["info"]._set_body([urwid.Text(line) for line in lines])
7592 self.state["info_bar"].set_completion(0)
7594 def selectable(self):
7595 if self.state["widget_current"] != self:
7596 self.state["widget_current"] = self
7597 self.scrolled["info"] = False
7598 self.scrolled["hexdump"] = False
7599 self._state_update()
7600 return super().selectable()
7602 def _get_display_text_without_offset(self):
7603 pp, constructed = self._get_pp()
7604 style = "constructed" if constructed else ""
7605 if len(pp.decode_path) == 0:
7606 return (style, pp.obj_name)
7607 if pp.asn1_type_name == "EOC":
7608 return ("eoc", "EOC")
7609 ent = pp.decode_path[-1]
7610 if isinstance(ent, DecodePathDefBy):
7611 value = str(ent.defined_by)
7612 oid_name = find_oid_name(
7613 ent.defined_by.asn1_type_name, oid_maps, value,
7615 return ("defby", "DEFBY:" + (
7616 value if oid_name is None else oid_name
7620 def get_display_text(self):
7621 pp, _ = self._get_pp()
7622 style, ent = self._get_display_text_without_offset()
7623 return [(style, ent), " [%d]" % pp.offset]
7625 def _scroll(self, what, step):
7626 self.state[what]._invalidate()
7627 pos = self.state[what].focus_position
7628 if not self.scrolled[what]:
7629 self.scrolled[what] = True
7631 pos = max(0, pos + step)
7632 pos = min(pos, len(self.state[what]._body.positions()) - 1)
7633 self.state[what].set_focus(pos)
7634 self.state[what].set_focus_valign("top")
7635 self.state[what + "_bar"].set_completion(
7636 (100 * pos) // len(self.state[what]._body.positions())
7639 def keypress(self, size, key):
7641 raise urwid.ExitMainLoop()
7644 self.expanded = not self.expanded
7645 self.update_expanded_icon()
7648 hexdump_steps = {"j": 1, "k": -1, "J": 5, "K": -5}
7649 if key in hexdump_steps:
7650 self._scroll("hexdump", hexdump_steps[key])
7653 info_steps = {"h": 1, "l": -1, "H": 5, "L": -5}
7654 if key in info_steps:
7655 self._scroll("info", info_steps[key])
7658 if key in ("d", "D"):
7659 pp, _ = self._get_pp()
7660 dp = ":".join(str(p) for p in pp.decode_path)
7661 dp = dp.replace(" ", "_")
7664 if key == "d" or pp.expl_offset is None:
7665 data = self.state["raw"][pp.offset:(
7666 pp.offset + pp.tlen + pp.llen + pp.vlen
7669 data = self.state["raw"][pp.expl_offset:(
7670 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen
7674 def duplicate_path(dp, ctr):
7677 return "%s.%d" % (dp, ctr)
7680 if not path_exists(duplicate_path(dp, ctr)):
7683 dp = duplicate_path(dp, ctr)
7684 with open(dp, "wb") as fd:
7686 self.state["decode_path"].set_text(
7687 ("warning", "Saved to: " + dp)
7690 return super().keypress(size, key)
7692 class PN(urwid.ParentNode):
7693 def __init__(self, state, value, *args, **kwargs):
7695 if not hasattr(value, "_fields"):
7697 super().__init__(value, *args, **kwargs)
7699 def load_widget(self):
7700 return TW(self.state, self)
7702 def load_child_keys(self):
7703 value = self.get_value()
7704 if hasattr(value, "_fields"):
7706 return range(len(value[1:]))
7708 def load_child_node(self, key):
7711 self.get_value()[key + 1],
7714 depth=self.get_depth() + 1,
7717 class LabeledPG(urwid.ProgressBar):
7718 def __init__(self, label, *args, **kwargs):
7720 super().__init__(*args, **kwargs)
7723 return "%s: %s" % (self.label, super().get_text())
7725 WinHexdump = urwid.ListBox([urwid.Text("")])
7726 WinInfo = urwid.ListBox([urwid.Text("")])
7727 WinDecodePath = urwid.Text("", "center")
7728 WinInfoBar = LabeledPG("info", "pg-normal", "pg-complete")
7729 WinHexdumpBar = LabeledPG("hexdump", "pg-normal", "pg-complete")
7730 WinTree = urwid.TreeListBox(urwid.TreeWalker(PN(
7733 "hexed": list(hexdump(raw)),
7734 "widget_current": None,
7736 "info_bar": WinInfoBar,
7737 "hexdump": WinHexdump,
7738 "hexdump_bar": WinHexdumpBar,
7739 "decode_path": WinDecodePath,
7743 help_text = " ".join((
7745 "space:(un)collapse",
7746 "(pg)up/down/home/end:nav",
7747 "jkJK:hexdump hlHL:info",
7753 ("weight", 1, WinTree),
7754 ("weight", 2, urwid.Pile([
7755 urwid.LineBox(WinInfo),
7756 urwid.LineBox(WinHexdump),
7759 header=urwid.Columns([
7760 ("weight", 2, urwid.AttrWrap(WinDecodePath, "header")),
7761 ("weight", 1, WinInfoBar),
7762 ("weight", 1, WinHexdumpBar),
7764 footer=urwid.AttrWrap(urwid.Text(help_text), "help")
7767 ("header", "bold", ""),
7768 ("constructed", "bold", ""),
7769 ("help", "light magenta", ""),
7770 ("warning", "light red", ""),
7771 ("defby", "light red", ""),
7772 ("eoc", "dark red", ""),
7773 ("select-value", "light green", ""),
7774 ("select-expl", "light red", ""),
7775 ("pg-normal", "", "light blue"),
7776 ("pg-complete", "black", "yellow"),
7781 def main(): # pragma: no cover
7783 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/CER/DER decoder")
7784 parser.add_argument(
7788 help="Skip that number of bytes from the beginning",
7790 parser.add_argument(
7792 help="Python paths to dictionary with OIDs, comma separated",
7794 parser.add_argument(
7796 help="Python path to schema definition to use",
7798 parser.add_argument(
7799 "--defines-by-path",
7800 help="Python path to decoder's defines_by_path",
7802 parser.add_argument(
7804 action="store_true",
7805 help="Disallow BER encoding",
7807 parser.add_argument(
7808 "--print-decode-path",
7809 action="store_true",
7810 help="Print decode paths",
7812 parser.add_argument(
7813 "--decode-path-only",
7814 help="Print only specified decode path",
7816 parser.add_argument(
7818 action="store_true",
7819 help="Allow explicit tag out-of-bound",
7821 parser.add_argument(
7823 action="store_true",
7824 help="Turn on event generation mode",
7826 parser.add_argument(
7828 action="store_true",
7829 help="Start ASN.1 browser",
7831 parser.add_argument(
7833 type=argparse.FileType("rb"),
7834 help="Path to BER/CER/DER file you want to decode",
7836 args = parser.parse_args()
7838 raw = file_mmaped(args.RAWFile)[args.skip:]
7840 args.RAWFile.seek(args.skip)
7841 raw = memoryview(args.RAWFile.read())
7842 args.RAWFile.close()
7844 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
7845 if args.oids else ()
7847 from functools import partial
7849 schema = obj_by_path(args.schema)
7850 pprinter = partial(pprint, big_blobs=True)
7852 schema, pprinter = generic_decoder()
7854 "bered": not args.nobered,
7855 "allow_expl_oob": args.allow_expl_oob,
7857 if args.defines_by_path is not None:
7858 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
7860 obj, _ = schema().decode(raw, ctx=ctx)
7861 browse(raw, obj, oid_maps)
7862 from sys import exit as sys_exit
7864 from os import environ
7868 with_colours=environ.get("NO_COLOR") is None,
7869 with_decode_path=args.print_decode_path,
7871 () if args.decode_path_only is None else
7872 tuple(args.decode_path_only.split(":"))
7876 for decode_path, obj, tail in schema().decode_evgen(raw, ctx=ctx):
7877 print(pprinter(obj, decode_path=decode_path))
7879 obj, tail = schema().decode(raw, ctx=ctx)
7880 print(pprinter(obj))
7882 print("\nTrailing data: %s" % hexenc(tail))
7885 if __name__ == "__main__":
7886 from pyderasn import *