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 collections import namedtuple
1163 from collections import OrderedDict
1164 from copy import copy
1165 from datetime import datetime
1166 from datetime import timedelta
1167 from io import BytesIO
1168 from math import ceil
1169 from operator import attrgetter
1170 from string import ascii_letters
1171 from string import digits
1172 from struct import Struct as struct_Struct
1173 from sys import maxsize as sys_maxsize
1174 from sys import version_info
1175 from unicodedata import category as unicat
1178 from termcolor import colored
1179 except ImportError: # pragma: no cover
1180 def colored(what, *args, **kwargs):
1184 from dateutil.tz import tzutc
1185 except ImportError: # pragma: no cover
1237 "TagClassApplication",
1240 "TagClassUniversal",
1241 "TagFormConstructed",
1252 TagClassUniversal = 0
1253 TagClassApplication = 1 << 6
1254 TagClassContext = 1 << 7
1255 TagClassPrivate = 1 << 6 | 1 << 7
1256 TagFormPrimitive = 0
1257 TagFormConstructed = 1 << 5
1259 TagClassContext: "",
1260 TagClassApplication: "APPLICATION ",
1261 TagClassPrivate: "PRIVATE ",
1262 TagClassUniversal: "UNIV ",
1266 LENINDEF = b"\x80" # length indefinite mark
1267 LENINDEF_PP_CHAR = "∞"
1268 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
1269 SET01 = frozenset("01")
1270 DECIMALS = frozenset(digits)
1271 DECIMAL_SIGNS = ".,"
1272 NEXT_ATTR_NAME = "__next__"
1275 def file_mmaped(fd):
1276 """Make mmap-ed memoryview for reading from file
1278 :param fd: file object
1279 :returns: memoryview over read-only mmap-ing of the whole file
1283 It does not work under Windows.
1286 return memoryview(mmap.mmap(fd.fileno(), length=0, prot=mmap.PROT_READ))
1290 if not set(value) <= DECIMALS:
1291 raise ValueError("non-pure integer")
1295 def fractions2float(fractions_raw):
1296 pureint(fractions_raw)
1297 return float("0." + fractions_raw)
1300 def get_def_by_path(defines_by_path, sub_decode_path):
1301 """Get define by decode path
1303 for path, define in defines_by_path:
1304 if len(path) != len(sub_decode_path):
1306 for p1, p2 in zip(path, sub_decode_path):
1307 if (p1 is not any) and (p1 != p2):
1313 ########################################################################
1315 ########################################################################
1317 class ASN1Error(ValueError):
1321 class DecodeError(ASN1Error):
1322 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
1324 :param str msg: reason of decode failing
1325 :param klass: optional exact DecodeError inherited class (like
1326 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
1327 :py:exc:`InvalidLength`)
1328 :param decode_path: tuple of strings. It contains human
1329 readable names of the fields through which
1330 decoding process has passed
1331 :param int offset: binary offset where failure happened
1336 self.decode_path = decode_path
1337 self.offset = offset
1342 "" if self.klass is None else self.klass.__name__,
1344 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
1345 if len(self.decode_path) > 0 else ""
1347 ("(at %d)" % self.offset) if self.offset > 0 else "",
1353 return "%s(%s)" % (self.__class__.__name__, self)
1356 class NotEnoughData(DecodeError):
1360 class ExceedingData(ASN1Error):
1361 def __init__(self, nbytes):
1363 self.nbytes = nbytes
1366 return "%d trailing bytes" % self.nbytes
1369 return "%s(%s)" % (self.__class__.__name__, self)
1372 class LenIndefForm(DecodeError):
1376 class TagMismatch(DecodeError):
1380 class InvalidLength(DecodeError):
1384 class InvalidOID(DecodeError):
1388 class ObjUnknown(ASN1Error):
1389 def __init__(self, name):
1394 return "object is unknown: %s" % self.name
1397 return "%s(%s)" % (self.__class__.__name__, self)
1400 class ObjNotReady(ASN1Error):
1401 def __init__(self, name):
1406 return "object is not ready: %s" % self.name
1409 return "%s(%s)" % (self.__class__.__name__, self)
1412 class InvalidValueType(ASN1Error):
1413 def __init__(self, expected_types):
1415 self.expected_types = expected_types
1418 return "invalid value type, expected: %s" % ", ".join(
1419 [repr(t) for t in self.expected_types]
1423 return "%s(%s)" % (self.__class__.__name__, self)
1426 class BoundsError(ASN1Error):
1427 def __init__(self, bound_min, value, bound_max):
1429 self.bound_min = bound_min
1431 self.bound_max = bound_max
1434 return "unsatisfied bounds: %s <= %s <= %s" % (
1441 return "%s(%s)" % (self.__class__.__name__, self)
1444 ########################################################################
1446 ########################################################################
1449 """Binary data to hexadecimal string convert
1451 return bytes.fromhex(data)
1455 """Hexadecimal string to binary data convert
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:
4790 def allowable_chars(self):
4791 return frozenset(chr(c) for c in self._allowable_chars)
4793 def _value_sanitize(self, value):
4794 value = super()._value_sanitize(value)
4795 if not frozenset(value) <= self._allowable_chars:
4796 raise DecodeError("non satisfying alphabet value")
4800 NUMERIC_ALLOWABLE_CHARS = frozenset(digits.encode("ascii") + b" ")
4803 class NumericString(AllowableCharsMixin, CommonString):
4806 Its value is properly sanitized: only ASCII digits with spaces can
4809 >>> NumericString().allowable_chars
4810 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4812 __slots__ = ("_allowable_chars",)
4813 tag_default = tag_encode(18)
4815 asn1_type_name = "NumericString"
4817 def __init__(self, *args, **kwargs):
4818 self._allowable_chars = NUMERIC_ALLOWABLE_CHARS
4819 super().__init__(*args, **kwargs)
4822 PrintableStringState = namedtuple(
4823 "PrintableStringState",
4824 OctetStringState._fields + ("allowable_chars",),
4829 PRINTABLE_ALLOWABLE_CHARS = frozenset(
4830 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4834 class PrintableString(AllowableCharsMixin, CommonString):
4837 Its value is properly sanitized: see X.680 41.4 table 10.
4839 >>> PrintableString().allowable_chars
4840 frozenset([' ', "'", ..., 'z'])
4841 >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4842 PrintableString PrintableString foo*bar
4843 >>> obj.allow_asterisk, obj.allow_ampersand
4846 __slots__ = ("_allowable_chars",)
4847 tag_default = tag_encode(19)
4849 asn1_type_name = "PrintableString"
4850 _asterisk = frozenset("*".encode("ascii"))
4851 _ampersand = frozenset("&".encode("ascii"))
4863 allow_asterisk=False,
4864 allow_ampersand=False,
4867 :param allow_asterisk: allow asterisk character
4868 :param allow_ampersand: allow ampersand character
4870 allowable_chars = PRINTABLE_ALLOWABLE_CHARS
4872 allowable_chars |= self._asterisk
4874 allowable_chars |= self._ampersand
4875 self._allowable_chars = allowable_chars
4877 value, bounds, impl, expl, default, optional, _decoded, ctx,
4881 def allow_asterisk(self):
4882 """Is asterisk character allowed?
4884 return self._asterisk <= self._allowable_chars
4887 def allow_ampersand(self):
4888 """Is ampersand character allowed?
4890 return self._ampersand <= self._allowable_chars
4892 def __getstate__(self):
4893 return PrintableStringState(
4894 *super().__getstate__(),
4895 **{"allowable_chars": self._allowable_chars}
4898 def __setstate__(self, state):
4899 super().__setstate__(state)
4900 self._allowable_chars = state.allowable_chars
4911 return self.__class__(
4914 (self._bound_min, self._bound_max)
4915 if bounds is None else bounds
4917 impl=self.tag if impl is None else impl,
4918 expl=self._expl if expl is None else expl,
4919 default=self.default if default is None else default,
4920 optional=self.optional if optional is None else optional,
4921 allow_asterisk=self.allow_asterisk,
4922 allow_ampersand=self.allow_ampersand,
4926 class TeletexString(CommonString):
4928 tag_default = tag_encode(20)
4929 encoding = "iso-8859-1"
4930 asn1_type_name = "TeletexString"
4933 class T61String(TeletexString):
4935 asn1_type_name = "T61String"
4938 class VideotexString(CommonString):
4940 tag_default = tag_encode(21)
4941 encoding = "iso-8859-1"
4942 asn1_type_name = "VideotexString"
4945 IA5_ALLOWABLE_CHARS = frozenset(b"".join(
4946 chr(c).encode("ascii") for c in range(128)
4950 class IA5String(AllowableCharsMixin, CommonString):
4953 Its value is properly sanitized: it is a mix of
4955 * http://www.itscj.ipsj.or.jp/iso-ir/006.pdf (G)
4956 * http://www.itscj.ipsj.or.jp/iso-ir/001.pdf (C0)
4957 * DEL character (0x7F)
4959 It is just 7-bit ASCII.
4961 >>> IA5String().allowable_chars
4962 frozenset(["NUL", ... "DEL"])
4964 __slots__ = ("_allowable_chars",)
4965 tag_default = tag_encode(22)
4967 asn1_type_name = "IA5"
4969 def __init__(self, *args, **kwargs):
4970 self._allowable_chars = IA5_ALLOWABLE_CHARS
4971 super().__init__(*args, **kwargs)
4974 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4975 LEN_LEN_YYMMDDHHMMSSZ = len_encode(LEN_YYMMDDHHMMSSZ)
4976 LEN_YYMMDDHHMMSSZ_WITH_LEN = len(LEN_LEN_YYMMDDHHMMSSZ) + LEN_YYMMDDHHMMSSZ
4977 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4978 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4979 LEN_LEN_YYYYMMDDHHMMSSZ = len_encode(LEN_YYYYMMDDHHMMSSZ)
4982 VISIBLE_ALLOWABLE_CHARS = frozenset(b"".join(
4983 chr(c).encode("ascii") for c in range(ord(" "), ord("~") + 1)
4987 class VisibleString(AllowableCharsMixin, CommonString):
4990 Its value is properly sanitized. ASCII subset from space to tilde is
4991 allowed: http://www.itscj.ipsj.or.jp/iso-ir/006.pdf
4993 >>> VisibleString().allowable_chars
4994 frozenset([" ", ... "~"])
4996 __slots__ = ("_allowable_chars",)
4997 tag_default = tag_encode(26)
4999 asn1_type_name = "VisibleString"
5001 def __init__(self, *args, **kwargs):
5002 self._allowable_chars = VISIBLE_ALLOWABLE_CHARS
5003 super().__init__(*args, **kwargs)
5006 class ISO646String(VisibleString):
5008 asn1_type_name = "ISO646String"
5011 UTCTimeState = namedtuple(
5013 OctetStringState._fields + ("ber_raw",),
5018 def str_to_time_fractions(value):
5020 year, v = (v // 10**10), (v % 10**10)
5021 month, v = (v // 10**8), (v % 10**8)
5022 day, v = (v // 10**6), (v % 10**6)
5023 hour, v = (v // 10**4), (v % 10**4)
5024 minute, second = (v // 100), (v % 100)
5025 return year, month, day, hour, minute, second
5028 class UTCTime(VisibleString):
5029 """``UTCTime`` datetime type
5031 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5032 UTCTime UTCTime 2017-09-30T22:07:50
5038 datetime.datetime(2017, 9, 30, 22, 7, 50)
5039 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
5040 datetime.datetime(1957, 9, 30, 22, 7, 50)
5042 If BER encoded value was met, then ``ber_raw`` attribute will hold
5043 its raw representation.
5047 Only **naive** ``datetime`` objects are supported.
5048 Library assumes that all work is done in UTC.
5052 Pay attention that ``UTCTime`` can not hold full year, so all years
5053 having < 50 years are treated as 20xx, 19xx otherwise, according to
5054 X.509 recommendation. Use ``GeneralizedTime`` instead for
5059 No strict validation of UTC offsets are made (only applicable to
5060 **BER**), but very crude:
5062 * minutes are not exceeding 60
5063 * offset value is not exceeding 14 hours
5065 __slots__ = ("ber_raw",)
5066 tag_default = tag_encode(23)
5068 asn1_type_name = "UTCTime"
5069 evgen_mode_skip_value = False
5079 bounds=None, # dummy argument, workability for OctetString.decode
5083 :param value: set the value. Either datetime type, or
5084 :py:class:`pyderasn.UTCTime` object
5085 :param bytes impl: override default tag with ``IMPLICIT`` one
5086 :param bytes expl: override default tag with ``EXPLICIT`` one
5087 :param default: set default value. Type same as in ``value``
5088 :param bool optional: is object ``OPTIONAL`` in sequence
5090 super().__init__(None, None, impl, expl, None, optional, _decoded, ctx)
5093 if value is not None:
5094 self._value, self.ber_raw = self._value_sanitize(value, ctx)
5095 self.ber_encoded = self.ber_raw is not None
5096 if default is not None:
5097 default, _ = self._value_sanitize(default)
5098 self.default = self.__class__(
5103 if self._value is None:
5104 self._value = default
5106 self.optional = optional
5108 def _strptime_bered(self, value):
5109 year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
5112 raise ValueError("no timezone")
5113 year += 2000 if year < 50 else 1900
5114 decoded = datetime(year, month, day, hour, minute)
5116 if value[-1] == "Z":
5120 raise ValueError("invalid UTC offset")
5121 if value[-5] == "-":
5123 elif value[-5] == "+":
5126 raise ValueError("invalid UTC offset")
5127 v = pureint(value[-4:])
5128 offset, v = (60 * (v % 100)), v // 100
5130 raise ValueError("invalid UTC offset minutes")
5132 if offset > 14 * 3600:
5133 raise ValueError("too big UTC offset")
5137 return offset, decoded
5139 raise ValueError("invalid UTC offset seconds")
5140 seconds = pureint(value)
5142 raise ValueError("invalid seconds value")
5143 return offset, decoded + timedelta(seconds=seconds)
5145 def _strptime(self, value):
5146 # datetime.strptime's format: %y%m%d%H%M%SZ
5147 if len(value) != LEN_YYMMDDHHMMSSZ:
5148 raise ValueError("invalid UTCTime length")
5149 if value[-1] != "Z":
5150 raise ValueError("non UTC timezone")
5151 year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
5152 year += 2000 if year < 50 else 1900
5153 return datetime(year, month, day, hour, minute, second)
5155 def _dt_sanitize(self, value):
5156 if value.year < 1950 or value.year > 2049:
5157 raise ValueError("UTCTime can hold only 1950-2049 years")
5158 return value.replace(microsecond=0)
5160 def _value_sanitize(self, value, ctx=None):
5161 if value.__class__ == bytes:
5163 value_decoded = value.decode("ascii")
5164 except (UnicodeEncodeError, UnicodeDecodeError) as err:
5165 raise DecodeError("invalid UTCTime encoding: %r" % err)
5168 return self._strptime(value_decoded), None
5169 except (TypeError, ValueError) as _err:
5171 if (ctx is not None) and ctx.get("bered", False):
5173 offset, _value = self._strptime_bered(value_decoded)
5174 _value = _value - timedelta(seconds=offset)
5175 return self._dt_sanitize(_value), value
5176 except (TypeError, ValueError, OverflowError) as _err:
5179 "invalid %s format: %r" % (self.asn1_type_name, err),
5180 klass=self.__class__,
5182 if isinstance(value, self.__class__):
5183 return value._value, None
5184 if value.__class__ == datetime:
5185 if value.tzinfo is not None:
5186 raise ValueError("only naive datetime supported")
5187 return self._dt_sanitize(value), None
5188 raise InvalidValueType((self.__class__, datetime))
5190 def _pp_value(self):
5192 value = self._value.isoformat()
5193 if self.ber_encoded:
5194 value += " (%s)" % self.ber_raw
5198 def __unicode__(self):
5200 value = self._value.isoformat()
5201 if self.ber_encoded:
5202 value += " (%s)" % self.ber_raw
5204 return str(self._pp_value())
5206 def __getstate__(self):
5207 return UTCTimeState(*super().__getstate__(), **{"ber_raw": self.ber_raw})
5209 def __setstate__(self, state):
5210 super().__setstate__(state)
5211 self.ber_raw = state.ber_raw
5213 def __bytes__(self):
5214 self._assert_ready()
5215 return self._encode_time()
5217 def __eq__(self, their):
5218 if their.__class__ == bytes:
5219 return self._encode_time() == their
5220 if their.__class__ == datetime:
5221 return self.todatetime() == their
5222 if not isinstance(their, self.__class__):
5225 self._value == their._value and
5226 self.tag == their.tag and
5227 self._expl == their._expl
5230 def _encode_time(self):
5231 return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
5234 self._assert_ready()
5235 return b"".join((self.tag, LEN_LEN_YYMMDDHHMMSSZ, self._encode_time()))
5237 def _encode1st(self, state):
5238 return len(self.tag) + LEN_YYMMDDHHMMSSZ_WITH_LEN, state
5240 def _encode2nd(self, writer, state_iter):
5241 self._assert_ready()
5242 write_full(writer, self._encode())
5244 def _encode_cer(self, writer):
5245 write_full(writer, self._encode())
5247 def todatetime(self):
5250 def totzdatetime(self):
5252 raise NotImplementedError(
5253 "Package python-dateutil is required to use this feature",
5255 return self._value.replace(tzinfo=tzutc())
5258 return pp_console_row(next(self.pps()))
5260 def pps(self, decode_path=()):
5263 asn1_type_name=self.asn1_type_name,
5264 obj_name=self.__class__.__name__,
5265 decode_path=decode_path,
5266 value=self._pp_value(),
5267 optional=self.optional,
5268 default=self == self.default,
5269 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5270 expl=None if self._expl is None else tag_decode(self._expl),
5275 expl_offset=self.expl_offset if self.expled else None,
5276 expl_tlen=self.expl_tlen if self.expled else None,
5277 expl_llen=self.expl_llen if self.expled else None,
5278 expl_vlen=self.expl_vlen if self.expled else None,
5279 expl_lenindef=self.expl_lenindef,
5280 ber_encoded=self.ber_encoded,
5283 for pp in self.pps_lenindef(decode_path):
5287 class GeneralizedTime(UTCTime):
5288 """``GeneralizedTime`` datetime type
5290 This type is similar to :py:class:`pyderasn.UTCTime`.
5292 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5293 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
5295 '20170930220750.000123Z'
5296 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
5297 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
5301 Only **naive** datetime objects are supported.
5302 Library assumes that all work is done in UTC.
5306 Only **microsecond** fractions are supported in DER encoding.
5307 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
5308 higher precision values.
5312 **BER** encoded data can loss information (accuracy) during
5313 decoding because of float transformations.
5317 **Zero** year is unsupported.
5320 tag_default = tag_encode(24)
5321 asn1_type_name = "GeneralizedTime"
5323 def _dt_sanitize(self, value):
5326 def _strptime_bered(self, value):
5327 if len(value) < 4 + 3 * 2:
5328 raise ValueError("invalid GeneralizedTime")
5329 year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
5330 decoded = datetime(year, month, day, hour)
5331 offset, value = 0, value[10:]
5333 return offset, decoded
5334 if value[-1] == "Z":
5337 for char, sign in (("-", -1), ("+", 1)):
5338 idx = value.rfind(char)
5341 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
5342 v = pureint(offset_raw)
5343 if len(offset_raw) == 4:
5344 offset, v = (60 * (v % 100)), v // 100
5346 raise ValueError("invalid UTC offset minutes")
5347 elif len(offset_raw) == 2:
5350 raise ValueError("invalid UTC offset")
5352 if offset > 14 * 3600:
5353 raise ValueError("too big UTC offset")
5357 return offset, decoded
5358 if value[0] in DECIMAL_SIGNS:
5360 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
5363 raise ValueError("stripped minutes")
5364 decoded += timedelta(seconds=60 * pureint(value[:2]))
5367 return offset, decoded
5368 if value[0] in DECIMAL_SIGNS:
5370 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
5373 raise ValueError("stripped seconds")
5374 decoded += timedelta(seconds=pureint(value[:2]))
5377 return offset, decoded
5378 if value[0] not in DECIMAL_SIGNS:
5379 raise ValueError("invalid format after seconds")
5381 decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
5384 def _strptime(self, value):
5386 if l == LEN_YYYYMMDDHHMMSSZ:
5387 # datetime.strptime's format: %Y%m%d%H%M%SZ
5388 if value[-1] != "Z":
5389 raise ValueError("non UTC timezone")
5390 return datetime(*str_to_time_fractions(value[:-1]))
5391 if l >= LEN_YYYYMMDDHHMMSSDMZ:
5392 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
5393 if value[-1] != "Z":
5394 raise ValueError("non UTC timezone")
5395 if value[14] != ".":
5396 raise ValueError("no fractions separator")
5399 raise ValueError("trailing zero")
5402 raise ValueError("only microsecond fractions are supported")
5403 us = pureint(us + ("0" * (6 - us_len)))
5404 year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
5405 return datetime(year, month, day, hour, minute, second, us)
5406 raise ValueError("invalid GeneralizedTime length")
5408 def _encode_time(self):
5410 encoded = value.strftime("%Y%m%d%H%M%S")
5411 if value.microsecond > 0:
5412 encoded += (".%06d" % value.microsecond).rstrip("0")
5413 return (encoded + "Z").encode("ascii")
5416 self._assert_ready()
5418 if value.microsecond > 0:
5419 encoded = self._encode_time()
5420 return b"".join((self.tag, len_encode(len(encoded)), encoded))
5421 return b"".join((self.tag, LEN_LEN_YYYYMMDDHHMMSSZ, self._encode_time()))
5423 def _encode1st(self, state):
5424 self._assert_ready()
5425 vlen = len(self._encode_time())
5426 return len(self.tag) + len_size(vlen) + vlen, state
5428 def _encode2nd(self, writer, state_iter):
5429 write_full(writer, self._encode())
5432 class GraphicString(CommonString):
5434 tag_default = tag_encode(25)
5435 encoding = "iso-8859-1"
5436 asn1_type_name = "GraphicString"
5439 class GeneralString(CommonString):
5441 tag_default = tag_encode(27)
5442 encoding = "iso-8859-1"
5443 asn1_type_name = "GeneralString"
5446 class UniversalString(CommonString):
5448 tag_default = tag_encode(28)
5449 encoding = "utf-32-be"
5450 asn1_type_name = "UniversalString"
5453 class BMPString(CommonString):
5455 tag_default = tag_encode(30)
5456 encoding = "utf-16-be"
5457 asn1_type_name = "BMPString"
5460 ChoiceState = namedtuple(
5462 BasicState._fields + ("specs", "value",),
5468 """``CHOICE`` special type
5472 class GeneralName(Choice):
5474 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
5475 ("dNSName", IA5String(impl=tag_ctxp(2))),
5478 >>> gn = GeneralName()
5480 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
5481 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5482 >>> gn["dNSName"] = IA5String("bar.baz")
5483 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
5484 >>> gn["rfc822Name"]
5487 [2] IA5String IA5 bar.baz
5490 >>> gn.value == gn["dNSName"]
5493 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
5495 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
5496 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5498 __slots__ = ("specs",)
5500 asn1_type_name = "CHOICE"
5513 :param value: set the value. Either ``(choice, value)`` tuple, or
5514 :py:class:`pyderasn.Choice` object
5515 :param bytes impl: can not be set, do **not** use it
5516 :param bytes expl: override default tag with ``EXPLICIT`` one
5517 :param default: set default value. Type same as in ``value``
5518 :param bool optional: is object ``OPTIONAL`` in sequence
5520 if impl is not None:
5521 raise ValueError("no implicit tag allowed for CHOICE")
5522 super().__init__(None, expl, default, optional, _decoded)
5524 schema = getattr(self, "schema", ())
5525 if len(schema) == 0:
5526 raise ValueError("schema must be specified")
5528 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5531 if value is not None:
5532 self._value = self._value_sanitize(value)
5533 if default is not None:
5534 default_value = self._value_sanitize(default)
5535 default_obj = self.__class__(impl=self.tag, expl=self._expl)
5536 default_obj.specs = self.specs
5537 default_obj._value = default_value
5538 self.default = default_obj
5540 self._value = copy(default_obj._value)
5541 if self._expl is not None:
5542 tag_class, _, tag_num = tag_decode(self._expl)
5543 self._tag_order = (tag_class, tag_num)
5545 def _value_sanitize(self, value):
5546 if (value.__class__ == tuple) and len(value) == 2:
5548 spec = self.specs.get(choice)
5550 raise ObjUnknown(choice)
5551 if not isinstance(obj, spec.__class__):
5552 raise InvalidValueType((spec,))
5553 return (choice, spec(obj))
5554 if isinstance(value, self.__class__):
5556 raise InvalidValueType((self.__class__, tuple))
5560 return self._value is not None and self._value[1].ready
5564 return self.expl_lenindef or (
5565 (self._value is not None) and
5566 self._value[1].bered
5569 def __getstate__(self):
5587 def __setstate__(self, state):
5588 super().__setstate__(state)
5589 self.specs = state.specs
5590 self._value = state.value
5592 def __eq__(self, their):
5593 if (their.__class__ == tuple) and len(their) == 2:
5594 return self._value == their
5595 if not isinstance(their, self.__class__):
5598 self.specs == their.specs and
5599 self._value == their._value
5609 return self.__class__(
5612 expl=self._expl if expl is None else expl,
5613 default=self.default if default is None else default,
5614 optional=self.optional if optional is None else optional,
5619 """Name of the choice
5621 self._assert_ready()
5622 return self._value[0]
5626 """Value of underlying choice
5628 self._assert_ready()
5629 return self._value[1]
5632 def tag_order(self):
5633 self._assert_ready()
5634 return self._value[1].tag_order if self._tag_order is None else self._tag_order
5637 def tag_order_cer(self):
5638 return min(v.tag_order_cer for v in self.specs.values())
5640 def __getitem__(self, key):
5641 if key not in self.specs:
5642 raise ObjUnknown(key)
5643 if self._value is None:
5645 choice, value = self._value
5650 def __setitem__(self, key, value):
5651 spec = self.specs.get(key)
5653 raise ObjUnknown(key)
5654 if not isinstance(value, spec.__class__):
5655 raise InvalidValueType((spec.__class__,))
5656 self._value = (key, spec(value))
5664 return self._value[1].decoded if self.ready else False
5667 self._assert_ready()
5668 return self._value[1].encode()
5670 def _encode1st(self, state):
5671 self._assert_ready()
5672 return self._value[1].encode1st(state)
5674 def _encode2nd(self, writer, state_iter):
5675 self._value[1].encode2nd(writer, state_iter)
5677 def _encode_cer(self, writer):
5678 self._assert_ready()
5679 self._value[1].encode_cer(writer)
5681 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5682 for choice, spec in self.specs.items():
5683 sub_decode_path = decode_path + (choice,)
5689 decode_path=sub_decode_path,
5692 _ctx_immutable=False,
5699 klass=self.__class__,
5700 decode_path=decode_path,
5703 if tag_only: # pragma: no cover
5707 for _decode_path, value, tail in spec.decode_evgen(
5711 decode_path=sub_decode_path,
5713 _ctx_immutable=False,
5715 yield _decode_path, value, tail
5717 _, value, tail = next(spec.decode_evgen(
5721 decode_path=sub_decode_path,
5723 _ctx_immutable=False,
5726 obj = self.__class__(
5729 default=self.default,
5730 optional=self.optional,
5731 _decoded=(offset, 0, value.fulllen),
5733 obj._value = (choice, value)
5734 yield decode_path, obj, tail
5737 value = pp_console_row(next(self.pps()))
5739 value = "%s[%r]" % (value, self.value)
5742 def pps(self, decode_path=()):
5745 asn1_type_name=self.asn1_type_name,
5746 obj_name=self.__class__.__name__,
5747 decode_path=decode_path,
5748 value=self.choice if self.ready else None,
5749 optional=self.optional,
5750 default=self == self.default,
5751 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5752 expl=None if self._expl is None else tag_decode(self._expl),
5757 expl_lenindef=self.expl_lenindef,
5761 yield self.value.pps(decode_path=decode_path + (self.choice,))
5762 for pp in self.pps_lenindef(decode_path):
5766 class PrimitiveTypes(Choice):
5767 """Predefined ``CHOICE`` for all generic primitive types
5769 It could be useful for general decoding of some unspecified values:
5771 >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
5772 OCTET STRING 3 bytes 666f6f
5773 >>> PrimitiveTypes().decod(hexdec("0203123456")).value
5777 schema = tuple((klass.__name__, klass()) for klass in (
5801 AnyState = namedtuple(
5803 BasicState._fields + ("value", "defined"),
5809 """``ANY`` special type
5811 >>> Any(Integer(-123))
5812 ANY INTEGER -123 (0X:7B)
5813 >>> a = Any(OctetString(b"hello world").encode())
5814 ANY 040b68656c6c6f20776f726c64
5815 >>> hexenc(bytes(a))
5816 b'0x040x0bhello world'
5818 __slots__ = ("defined",)
5819 tag_default = tag_encode(0)
5820 asn1_type_name = "ANY"
5830 :param value: set the value. Either any kind of pyderasn's
5831 **ready** object, or bytes. Pay attention that
5832 **no** validation is performed if raw binary value
5833 is valid TLV, except just tag decoding
5834 :param bytes expl: override default tag with ``EXPLICIT`` one
5835 :param bool optional: is object ``OPTIONAL`` in sequence
5837 super().__init__(None, expl, None, optional, _decoded)
5841 value = self._value_sanitize(value)
5843 if self._expl is None:
5844 if value.__class__ == bytes:
5845 tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5847 tag_class, tag_num = value.tag_order
5849 tag_class, _, tag_num = tag_decode(self._expl)
5850 self._tag_order = (tag_class, tag_num)
5853 def _value_sanitize(self, value):
5854 if value.__class__ == bytes:
5856 raise ValueError("%s value can not be empty" % self.__class__.__name__)
5858 if isinstance(value, self.__class__):
5860 if not isinstance(value, Obj):
5861 raise InvalidValueType((self.__class__, Obj, bytes))
5866 return self._value is not None
5869 def tag_order(self):
5870 self._assert_ready()
5871 return self._tag_order
5875 if self.expl_lenindef or self.lenindef:
5877 if self.defined is None:
5879 return self.defined[1].bered
5881 def __getstate__(self):
5899 def __setstate__(self, state):
5900 super().__setstate__(state)
5901 self._value = state.value
5902 self.defined = state.defined
5904 def __eq__(self, their):
5905 if their.__class__ == bytes:
5906 if self._value.__class__ == bytes:
5907 return self._value == their
5908 return self._value.encode() == their
5909 if issubclass(their.__class__, Any):
5910 if self.ready and their.ready:
5911 return bytes(self) == bytes(their)
5912 return self.ready == their.ready
5921 return self.__class__(
5923 expl=self._expl if expl is None else expl,
5924 optional=self.optional if optional is None else optional,
5927 def __bytes__(self):
5928 self._assert_ready()
5930 if value.__class__ == bytes:
5932 return self._value.encode()
5939 self._assert_ready()
5941 if value.__class__ == bytes:
5943 return value.encode()
5945 def _encode1st(self, state):
5946 self._assert_ready()
5948 if value.__class__ == bytes:
5949 return len(value), state
5950 return value.encode1st(state)
5952 def _encode2nd(self, writer, state_iter):
5954 if value.__class__ == bytes:
5955 write_full(writer, value)
5957 value.encode2nd(writer, state_iter)
5959 def _encode_cer(self, writer):
5960 self._assert_ready()
5962 if value.__class__ == bytes:
5963 write_full(writer, value)
5965 value.encode_cer(writer)
5967 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5969 t, tlen, lv = tag_strip(tlv)
5970 except DecodeError as err:
5971 raise err.__class__(
5973 klass=self.__class__,
5974 decode_path=decode_path,
5978 l, llen, v = len_decode(lv)
5979 except LenIndefForm as err:
5980 if not ctx.get("bered", False):
5981 raise err.__class__(
5983 klass=self.__class__,
5984 decode_path=decode_path,
5987 llen, vlen, v = 1, 0, lv[1:]
5988 sub_offset = offset + tlen + llen
5990 while v[:EOC_LEN].tobytes() != EOC:
5991 chunk, v = Any().decode(
5994 decode_path=decode_path + (str(chunk_i),),
5997 _ctx_immutable=False,
5999 vlen += chunk.tlvlen
6000 sub_offset += chunk.tlvlen
6002 tlvlen = tlen + llen + vlen + EOC_LEN
6003 obj = self.__class__(
6004 value=None if evgen_mode else tlv[:tlvlen].tobytes(),
6006 optional=self.optional,
6007 _decoded=(offset, 0, tlvlen),
6010 obj.tag = t.tobytes()
6011 yield decode_path, obj, v[EOC_LEN:]
6013 except DecodeError as err:
6014 raise err.__class__(
6016 klass=self.__class__,
6017 decode_path=decode_path,
6021 raise NotEnoughData(
6022 "encoded length is longer than data",
6023 klass=self.__class__,
6024 decode_path=decode_path,
6027 tlvlen = tlen + llen + l
6028 v, tail = tlv[:tlvlen], v[l:]
6029 obj = self.__class__(
6030 value=None if evgen_mode else v.tobytes(),
6032 optional=self.optional,
6033 _decoded=(offset, 0, tlvlen),
6035 obj.tag = t.tobytes()
6036 yield decode_path, obj, tail
6039 return pp_console_row(next(self.pps()))
6041 def pps(self, decode_path=()):
6045 elif value.__class__ == bytes:
6051 asn1_type_name=self.asn1_type_name,
6052 obj_name=self.__class__.__name__,
6053 decode_path=decode_path,
6055 blob=self._value if self._value.__class__ == bytes else None,
6056 optional=self.optional,
6057 default=self == self.default,
6058 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6059 expl=None if self._expl is None else tag_decode(self._expl),
6064 expl_offset=self.expl_offset if self.expled else None,
6065 expl_tlen=self.expl_tlen if self.expled else None,
6066 expl_llen=self.expl_llen if self.expled else None,
6067 expl_vlen=self.expl_vlen if self.expled else None,
6068 expl_lenindef=self.expl_lenindef,
6069 lenindef=self.lenindef,
6072 defined_by, defined = self.defined or (None, None)
6073 if defined_by is not None:
6075 decode_path=decode_path + (DecodePathDefBy(defined_by),)
6077 for pp in self.pps_lenindef(decode_path):
6081 ########################################################################
6082 # ASN.1 constructed types
6083 ########################################################################
6085 def abs_decode_path(decode_path, rel_path):
6086 """Create an absolute decode path from current and relative ones
6088 :param decode_path: current decode path, starting point. Tuple of strings
6089 :param rel_path: relative path to ``decode_path``. Tuple of strings.
6090 If first tuple's element is "/", then treat it as
6091 an absolute path, ignoring ``decode_path`` as
6092 starting point. Also this tuple can contain ".."
6093 elements, stripping the leading element from
6096 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
6097 ("foo", "bar", "baz", "whatever")
6098 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
6100 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
6103 if rel_path[0] == "/":
6105 if rel_path[0] == "..":
6106 return abs_decode_path(decode_path[:-1], rel_path[1:])
6107 return decode_path + rel_path
6110 SequenceState = namedtuple(
6112 BasicState._fields + ("specs", "value",),
6117 class SequenceEncode1stMixin:
6120 def _encode1st(self, state):
6122 idx = len(state) - 1
6124 for v in self._values_for_encoding():
6125 l, _ = v.encode1st(state)
6128 return len(self.tag) + len_size(vlen) + vlen, state
6131 class Sequence(SequenceEncode1stMixin, Obj):
6132 """``SEQUENCE`` structure type
6134 You have to make specification of sequence::
6136 class Extension(Sequence):
6138 ("extnID", ObjectIdentifier()),
6139 ("critical", Boolean(default=False)),
6140 ("extnValue", OctetString()),
6143 Then, you can work with it as with dictionary.
6145 >>> ext = Extension()
6146 >>> Extension().specs
6148 ('extnID', OBJECT IDENTIFIER),
6149 ('critical', BOOLEAN False OPTIONAL DEFAULT),
6150 ('extnValue', OCTET STRING),
6152 >>> ext["extnID"] = "1.2.3"
6153 Traceback (most recent call last):
6154 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
6155 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
6157 You can determine if sequence is ready to be encoded:
6162 Traceback (most recent call last):
6163 pyderasn.ObjNotReady: object is not ready: extnValue
6164 >>> ext["extnValue"] = OctetString(b"foobar")
6168 Value you want to assign, must have the same **type** as in
6169 corresponding specification, but it can have different tags,
6170 optional/default attributes -- they will be taken from specification
6173 class TBSCertificate(Sequence):
6175 ("version", Version(expl=tag_ctxc(0), default="v1")),
6178 >>> tbs = TBSCertificate()
6179 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
6181 Assign ``None`` to remove value from sequence.
6183 You can set values in Sequence during its initialization:
6185 >>> AlgorithmIdentifier((
6186 ("algorithm", ObjectIdentifier("1.2.3")),
6187 ("parameters", Any(Null()))
6189 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
6191 You can determine if value exists/set in the sequence and take its value:
6193 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
6196 OBJECT IDENTIFIER 1.2.3
6198 But pay attention that if value has default, then it won't be (not
6199 in) in the sequence (because ``DEFAULT`` must not be encoded in
6200 DER), but you can read its value:
6202 >>> "critical" in ext, ext["critical"]
6203 (False, BOOLEAN False)
6204 >>> ext["critical"] = Boolean(True)
6205 >>> "critical" in ext, ext["critical"]
6206 (True, BOOLEAN True)
6208 All defaulted values are always optional.
6210 .. _allow_default_values_ctx:
6212 DER prohibits default value encoding and will raise an error if
6213 default value is unexpectedly met during decode.
6214 If :ref:`bered <bered_ctx>` context option is set, then no error
6215 will be raised, but ``bered`` attribute set. You can disable strict
6216 defaulted values existence validation by setting
6217 ``"allow_default_values": True`` :ref:`context <ctx>` option.
6219 All values with DEFAULT specified are decoded atomically in
6220 :ref:`evgen mode <evgen_mode>`. If DEFAULT value is some kind of
6221 SEQUENCE, then it will be yielded as a single element, not
6222 disassembled. That is required for DEFAULT existence check.
6224 Two sequences are equal if they have equal specification (schema),
6225 implicit/explicit tagging and the same values.
6227 __slots__ = ("specs",)
6228 tag_default = tag_encode(form=TagFormConstructed, num=16)
6229 asn1_type_name = "SEQUENCE"
6241 super().__init__(impl, expl, default, optional, _decoded)
6243 schema = getattr(self, "schema", ())
6245 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
6248 if value is not None:
6249 if issubclass(value.__class__, Sequence):
6250 self._value = value._value
6251 elif hasattr(value, "__iter__"):
6252 for seq_key, seq_value in value:
6253 self[seq_key] = seq_value
6255 raise InvalidValueType((Sequence,))
6256 if default is not None:
6257 if not issubclass(default.__class__, Sequence):
6258 raise InvalidValueType((Sequence,))
6259 default_value = default._value
6260 default_obj = self.__class__(impl=self.tag, expl=self._expl)
6261 default_obj.specs = self.specs
6262 default_obj._value = default_value
6263 self.default = default_obj
6265 self._value = copy(default_obj._value)
6269 for name, spec in self.specs.items():
6270 value = self._value.get(name)
6281 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6283 return any(value.bered for value in self._value.values())
6285 def __getstate__(self):
6286 return SequenceState(
6300 {k: copy(v) for k, v in self._value.items()},
6303 def __setstate__(self, state):
6304 super().__setstate__(state)
6305 self.specs = state.specs
6306 self._value = state.value
6308 def __eq__(self, their):
6309 if not isinstance(their, self.__class__):
6312 self.specs == their.specs and
6313 self.tag == their.tag and
6314 self._expl == their._expl and
6315 self._value == their._value
6326 return self.__class__(
6329 impl=self.tag if impl is None else impl,
6330 expl=self._expl if expl is None else expl,
6331 default=self.default if default is None else default,
6332 optional=self.optional if optional is None else optional,
6335 def __contains__(self, key):
6336 return key in self._value
6338 def __setitem__(self, key, value):
6339 spec = self.specs.get(key)
6341 raise ObjUnknown(key)
6343 self._value.pop(key, None)
6345 if not isinstance(value, spec.__class__):
6346 raise InvalidValueType((spec.__class__,))
6347 value = spec(value=value)
6348 if spec.default is not None and value == spec.default:
6349 self._value.pop(key, None)
6351 self._value[key] = value
6353 def __getitem__(self, key):
6354 value = self._value.get(key)
6355 if value is not None:
6357 spec = self.specs.get(key)
6359 raise ObjUnknown(key)
6360 if spec.default is not None:
6364 def _values_for_encoding(self):
6365 for name, spec in self.specs.items():
6366 value = self._value.get(name)
6370 raise ObjNotReady(name)
6374 v = b"".join(v.encode() for v in self._values_for_encoding())
6375 return b"".join((self.tag, len_encode(len(v)), v))
6377 def _encode2nd(self, writer, state_iter):
6378 write_full(writer, self.tag + len_encode(next(state_iter)))
6379 for v in self._values_for_encoding():
6380 v.encode2nd(writer, state_iter)
6382 def _encode_cer(self, writer):
6383 write_full(writer, self.tag + LENINDEF)
6384 for v in self._values_for_encoding():
6385 v.encode_cer(writer)
6386 write_full(writer, EOC)
6388 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6390 t, tlen, lv = tag_strip(tlv)
6391 except DecodeError as err:
6392 raise err.__class__(
6394 klass=self.__class__,
6395 decode_path=decode_path,
6400 klass=self.__class__,
6401 decode_path=decode_path,
6404 if tag_only: # pragma: no cover
6408 ctx_bered = ctx.get("bered", False)
6410 l, llen, v = len_decode(lv)
6411 except LenIndefForm as err:
6413 raise err.__class__(
6415 klass=self.__class__,
6416 decode_path=decode_path,
6419 l, llen, v = 0, 1, lv[1:]
6421 except DecodeError as err:
6422 raise err.__class__(
6424 klass=self.__class__,
6425 decode_path=decode_path,
6429 raise NotEnoughData(
6430 "encoded length is longer than data",
6431 klass=self.__class__,
6432 decode_path=decode_path,
6436 v, tail = v[:l], v[l:]
6438 sub_offset = offset + tlen + llen
6441 ctx_allow_default_values = ctx.get("allow_default_values", False)
6442 for name, spec in self.specs.items():
6443 if spec.optional and (
6444 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
6448 spec_defaulted = spec.default is not None
6449 sub_decode_path = decode_path + (name,)
6451 if evgen_mode and not spec_defaulted:
6452 for _decode_path, value, v_tail in spec.decode_evgen(
6456 decode_path=sub_decode_path,
6458 _ctx_immutable=False,
6460 yield _decode_path, value, v_tail
6462 _, value, v_tail = next(spec.decode_evgen(
6466 decode_path=sub_decode_path,
6468 _ctx_immutable=False,
6471 except TagMismatch as err:
6472 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
6476 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
6477 if not evgen_mode and defined is not None:
6478 defined_by, defined_spec = defined
6479 if issubclass(value.__class__, SequenceOf):
6480 for i, _value in enumerate(value):
6481 sub_sub_decode_path = sub_decode_path + (
6483 DecodePathDefBy(defined_by),
6485 defined_value, defined_tail = defined_spec.decode(
6486 memoryview(bytes(_value)),
6488 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6489 if value.expled else (value.tlen + value.llen)
6492 decode_path=sub_sub_decode_path,
6494 _ctx_immutable=False,
6496 if len(defined_tail) > 0:
6499 klass=self.__class__,
6500 decode_path=sub_sub_decode_path,
6503 _value.defined = (defined_by, defined_value)
6505 defined_value, defined_tail = defined_spec.decode(
6506 memoryview(bytes(value)),
6508 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6509 if value.expled else (value.tlen + value.llen)
6512 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6514 _ctx_immutable=False,
6516 if len(defined_tail) > 0:
6519 klass=self.__class__,
6520 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6523 value.defined = (defined_by, defined_value)
6525 value_len = value.fulllen
6527 sub_offset += value_len
6531 yield sub_decode_path, value, v_tail
6532 if value == spec.default:
6533 if ctx_bered or ctx_allow_default_values:
6537 "DEFAULT value met",
6538 klass=self.__class__,
6539 decode_path=sub_decode_path,
6543 values[name] = value
6544 spec_defines = getattr(spec, "defines", ())
6545 if len(spec_defines) == 0:
6546 defines_by_path = ctx.get("defines_by_path", ())
6547 if len(defines_by_path) > 0:
6548 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
6549 if spec_defines is not None and len(spec_defines) > 0:
6550 for rel_path, schema in spec_defines:
6551 defined = schema.get(value, None)
6552 if defined is not None:
6553 ctx.setdefault("_defines", []).append((
6554 abs_decode_path(sub_decode_path[:-1], rel_path),
6558 if v[:EOC_LEN].tobytes() != EOC:
6561 klass=self.__class__,
6562 decode_path=decode_path,
6570 klass=self.__class__,
6571 decode_path=decode_path,
6574 obj = self.__class__(
6578 default=self.default,
6579 optional=self.optional,
6580 _decoded=(offset, llen, vlen),
6583 obj.lenindef = lenindef
6584 obj.ber_encoded = ber_encoded
6585 yield decode_path, obj, tail
6588 value = pp_console_row(next(self.pps()))
6590 for name in self.specs:
6591 _value = self._value.get(name)
6594 cols.append("%s: %s" % (name, repr(_value)))
6595 return "%s[%s]" % (value, "; ".join(cols))
6597 def pps(self, decode_path=()):
6600 asn1_type_name=self.asn1_type_name,
6601 obj_name=self.__class__.__name__,
6602 decode_path=decode_path,
6603 optional=self.optional,
6604 default=self == self.default,
6605 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6606 expl=None if self._expl is None else tag_decode(self._expl),
6611 expl_offset=self.expl_offset if self.expled else None,
6612 expl_tlen=self.expl_tlen if self.expled else None,
6613 expl_llen=self.expl_llen if self.expled else None,
6614 expl_vlen=self.expl_vlen if self.expled else None,
6615 expl_lenindef=self.expl_lenindef,
6616 lenindef=self.lenindef,
6617 ber_encoded=self.ber_encoded,
6620 for name in self.specs:
6621 value = self._value.get(name)
6624 yield value.pps(decode_path=decode_path + (name,))
6625 for pp in self.pps_lenindef(decode_path):
6629 class Set(Sequence, SequenceEncode1stMixin):
6630 """``SET`` structure type
6632 Its usage is identical to :py:class:`pyderasn.Sequence`.
6634 .. _allow_unordered_set_ctx:
6636 DER prohibits unordered values encoding and will raise an error
6637 during decode. If :ref:`bered <bered_ctx>` context option is set,
6638 then no error will occur. Also you can disable strict values
6639 ordering check by setting ``"allow_unordered_set": True``
6640 :ref:`context <ctx>` option.
6643 tag_default = tag_encode(form=TagFormConstructed, num=17)
6644 asn1_type_name = "SET"
6646 def _values_for_encoding(self):
6647 return sorted(super()._values_for_encoding(), key=attrgetter("tag_order"))
6649 def _encode_cer(self, writer):
6650 write_full(writer, self.tag + LENINDEF)
6652 super()._values_for_encoding(),
6653 key=attrgetter("tag_order_cer"),
6655 v.encode_cer(writer)
6656 write_full(writer, EOC)
6658 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6660 t, tlen, lv = tag_strip(tlv)
6661 except DecodeError as err:
6662 raise err.__class__(
6664 klass=self.__class__,
6665 decode_path=decode_path,
6670 klass=self.__class__,
6671 decode_path=decode_path,
6678 ctx_bered = ctx.get("bered", False)
6680 l, llen, v = len_decode(lv)
6681 except LenIndefForm as err:
6683 raise err.__class__(
6685 klass=self.__class__,
6686 decode_path=decode_path,
6689 l, llen, v = 0, 1, lv[1:]
6691 except DecodeError as err:
6692 raise err.__class__(
6694 klass=self.__class__,
6695 decode_path=decode_path,
6699 raise NotEnoughData(
6700 "encoded length is longer than data",
6701 klass=self.__class__,
6705 v, tail = v[:l], v[l:]
6707 sub_offset = offset + tlen + llen
6710 ctx_allow_default_values = ctx.get("allow_default_values", False)
6711 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6712 tag_order_prev = (0, 0)
6713 _specs_items = copy(self.specs)
6716 if lenindef and v[:EOC_LEN].tobytes() == EOC:
6718 for name, spec in _specs_items.items():
6719 sub_decode_path = decode_path + (name,)
6725 decode_path=sub_decode_path,
6728 _ctx_immutable=False,
6735 klass=self.__class__,
6736 decode_path=decode_path,
6739 spec_defaulted = spec.default is not None
6740 if evgen_mode and not spec_defaulted:
6741 for _decode_path, value, v_tail in spec.decode_evgen(
6745 decode_path=sub_decode_path,
6747 _ctx_immutable=False,
6749 yield _decode_path, value, v_tail
6751 _, value, v_tail = next(spec.decode_evgen(
6755 decode_path=sub_decode_path,
6757 _ctx_immutable=False,
6760 value_tag_order = value.tag_order
6761 value_len = value.fulllen
6762 if tag_order_prev >= value_tag_order:
6763 if ctx_bered or ctx_allow_unordered_set:
6767 "unordered " + self.asn1_type_name,
6768 klass=self.__class__,
6769 decode_path=sub_decode_path,
6774 yield sub_decode_path, value, v_tail
6775 if value != spec.default:
6777 elif ctx_bered or ctx_allow_default_values:
6781 "DEFAULT value met",
6782 klass=self.__class__,
6783 decode_path=sub_decode_path,
6786 values[name] = value
6787 del _specs_items[name]
6788 tag_order_prev = value_tag_order
6789 sub_offset += value_len
6793 obj = self.__class__(
6797 default=self.default,
6798 optional=self.optional,
6799 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6802 if v[:EOC_LEN].tobytes() != EOC:
6805 klass=self.__class__,
6806 decode_path=decode_path,
6811 for name, spec in self.specs.items():
6812 if name not in values and not spec.optional:
6814 "%s value is not ready" % name,
6815 klass=self.__class__,
6816 decode_path=decode_path,
6821 obj.ber_encoded = ber_encoded
6822 yield decode_path, obj, tail
6825 SequenceOfState = namedtuple(
6827 BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
6832 class SequenceOf(SequenceEncode1stMixin, Obj):
6833 """``SEQUENCE OF`` sequence type
6835 For that kind of type you must specify the object it will carry on
6836 (bounds are for example here, not required)::
6838 class Ints(SequenceOf):
6843 >>> ints.append(Integer(123))
6844 >>> ints.append(Integer(234))
6846 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
6847 >>> [int(i) for i in ints]
6849 >>> ints.append(Integer(345))
6850 Traceback (most recent call last):
6851 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
6854 >>> ints[1] = Integer(345)
6856 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
6858 You can initialize sequence with preinitialized values:
6860 >>> ints = Ints([Integer(123), Integer(234)])
6862 Also you can use iterator as a value:
6864 >>> ints = Ints(iter(Integer(i) for i in range(1000000)))
6866 And it won't be iterated until encoding process. Pay attention that
6867 bounds and required schema checks are done only during the encoding
6868 process in that case! After encode was called, then value is zeroed
6869 back to empty list and you have to set it again. That mode is useful
6870 mainly with CER encoding mode, where all objects from the iterable
6871 will be streamed to the buffer, without copying all of them to
6874 __slots__ = ("spec", "_bound_min", "_bound_max")
6875 tag_default = tag_encode(form=TagFormConstructed, num=16)
6876 asn1_type_name = "SEQUENCE OF"
6889 super().__init__(impl, expl, default, optional, _decoded)
6891 schema = getattr(self, "schema", None)
6893 raise ValueError("schema must be specified")
6895 self._bound_min, self._bound_max = getattr(
6899 ) if bounds is None else bounds
6901 if value is not None:
6902 self._value = self._value_sanitize(value)
6903 if default is not None:
6904 default_value = self._value_sanitize(default)
6905 default_obj = self.__class__(
6910 default_obj._value = default_value
6911 self.default = default_obj
6913 self._value = copy(default_obj._value)
6915 def _value_sanitize(self, value):
6917 if issubclass(value.__class__, SequenceOf):
6918 value = value._value
6919 elif hasattr(value, NEXT_ATTR_NAME):
6921 elif hasattr(value, "__iter__"):
6924 raise InvalidValueType((self.__class__, iter, "iterator"))
6926 if not self._bound_min <= len(value) <= self._bound_max:
6927 raise BoundsError(self._bound_min, len(value), self._bound_max)
6928 class_expected = self.spec.__class__
6930 if not isinstance(v, class_expected):
6931 raise InvalidValueType((class_expected,))
6936 if hasattr(self._value, NEXT_ATTR_NAME):
6938 if self._bound_min > 0 and len(self._value) == 0:
6940 return all(v.ready for v in self._value)
6944 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6946 return any(v.bered for v in self._value)
6948 def __getstate__(self):
6949 if hasattr(self._value, NEXT_ATTR_NAME):
6950 raise ValueError("can not pickle SequenceOf with iterator")
6951 return SequenceOfState(
6965 [copy(v) for v in self._value],
6970 def __setstate__(self, state):
6971 super().__setstate__(state)
6972 self.spec = state.spec
6973 self._value = state.value
6974 self._bound_min = state.bound_min
6975 self._bound_max = state.bound_max
6977 def __eq__(self, their):
6978 if isinstance(their, self.__class__):
6980 self.spec == their.spec and
6981 self.tag == their.tag and
6982 self._expl == their._expl and
6983 self._value == their._value
6985 if hasattr(their, "__iter__"):
6986 return self._value == list(their)
6998 return self.__class__(
7002 (self._bound_min, self._bound_max)
7003 if bounds is None else bounds
7005 impl=self.tag if impl is None else impl,
7006 expl=self._expl if expl is None else expl,
7007 default=self.default if default is None else default,
7008 optional=self.optional if optional is None else optional,
7011 def __contains__(self, key):
7012 return key in self._value
7014 def append(self, value):
7015 if not isinstance(value, self.spec.__class__):
7016 raise InvalidValueType((self.spec.__class__,))
7017 if len(self._value) + 1 > self._bound_max:
7020 len(self._value) + 1,
7023 self._value.append(value)
7026 return iter(self._value)
7029 return len(self._value)
7031 def __setitem__(self, key, value):
7032 if not isinstance(value, self.spec.__class__):
7033 raise InvalidValueType((self.spec.__class__,))
7034 self._value[key] = self.spec(value=value)
7036 def __getitem__(self, key):
7037 return self._value[key]
7039 def _values_for_encoding(self):
7040 return iter(self._value)
7043 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7046 values_append = values.append
7047 class_expected = self.spec.__class__
7048 values_for_encoding = self._values_for_encoding()
7050 for v in values_for_encoding:
7051 if not isinstance(v, class_expected):
7052 raise InvalidValueType((class_expected,))
7053 values_append(v.encode())
7054 if not self._bound_min <= len(values) <= self._bound_max:
7055 raise BoundsError(self._bound_min, len(values), self._bound_max)
7056 value = b"".join(values)
7058 value = b"".join(v.encode() for v in self._values_for_encoding())
7059 return b"".join((self.tag, len_encode(len(value)), value))
7061 def _encode1st(self, state):
7062 state = super()._encode1st(state)
7063 if hasattr(self._value, NEXT_ATTR_NAME):
7067 def _encode2nd(self, writer, state_iter):
7068 write_full(writer, self.tag + len_encode(next(state_iter)))
7069 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7072 class_expected = self.spec.__class__
7073 values_for_encoding = self._values_for_encoding()
7075 for v in values_for_encoding:
7076 if not isinstance(v, class_expected):
7077 raise InvalidValueType((class_expected,))
7078 v.encode2nd(writer, state_iter)
7080 if not self._bound_min <= values_count <= self._bound_max:
7081 raise BoundsError(self._bound_min, values_count, self._bound_max)
7083 for v in self._values_for_encoding():
7084 v.encode2nd(writer, state_iter)
7086 def _encode_cer(self, writer):
7087 write_full(writer, self.tag + LENINDEF)
7088 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7090 class_expected = self.spec.__class__
7092 values_for_encoding = self._values_for_encoding()
7094 for v in values_for_encoding:
7095 if not isinstance(v, class_expected):
7096 raise InvalidValueType((class_expected,))
7097 v.encode_cer(writer)
7099 if not self._bound_min <= values_count <= self._bound_max:
7100 raise BoundsError(self._bound_min, values_count, self._bound_max)
7102 for v in self._values_for_encoding():
7103 v.encode_cer(writer)
7104 write_full(writer, EOC)
7114 ordering_check=False,
7117 t, tlen, lv = tag_strip(tlv)
7118 except DecodeError as err:
7119 raise err.__class__(
7121 klass=self.__class__,
7122 decode_path=decode_path,
7127 klass=self.__class__,
7128 decode_path=decode_path,
7135 ctx_bered = ctx.get("bered", False)
7137 l, llen, v = len_decode(lv)
7138 except LenIndefForm as err:
7140 raise err.__class__(
7142 klass=self.__class__,
7143 decode_path=decode_path,
7146 l, llen, v = 0, 1, lv[1:]
7148 except DecodeError as err:
7149 raise err.__class__(
7151 klass=self.__class__,
7152 decode_path=decode_path,
7156 raise NotEnoughData(
7157 "encoded length is longer than data",
7158 klass=self.__class__,
7159 decode_path=decode_path,
7163 v, tail = v[:l], v[l:]
7165 sub_offset = offset + tlen + llen
7168 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
7169 value_prev = memoryview(v[:0])
7173 if lenindef and v[:EOC_LEN].tobytes() == EOC:
7175 sub_decode_path = decode_path + (str(_value_count),)
7177 for _decode_path, value, v_tail in spec.decode_evgen(
7181 decode_path=sub_decode_path,
7183 _ctx_immutable=False,
7185 yield _decode_path, value, v_tail
7187 _, value, v_tail = next(spec.decode_evgen(
7191 decode_path=sub_decode_path,
7193 _ctx_immutable=False,
7196 value_len = value.fulllen
7198 if value_prev.tobytes() > v[:value_len].tobytes():
7199 if ctx_bered or ctx_allow_unordered_set:
7203 "unordered " + self.asn1_type_name,
7204 klass=self.__class__,
7205 decode_path=sub_decode_path,
7208 value_prev = v[:value_len]
7211 _value.append(value)
7212 sub_offset += value_len
7215 if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
7217 msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
7218 klass=self.__class__,
7219 decode_path=decode_path,
7223 obj = self.__class__(
7224 value=None if evgen_mode else _value,
7226 bounds=(self._bound_min, self._bound_max),
7229 default=self.default,
7230 optional=self.optional,
7231 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
7233 except BoundsError as err:
7236 klass=self.__class__,
7237 decode_path=decode_path,
7241 if v[:EOC_LEN].tobytes() != EOC:
7244 klass=self.__class__,
7245 decode_path=decode_path,
7250 obj.ber_encoded = ber_encoded
7251 yield decode_path, obj, tail
7255 pp_console_row(next(self.pps())),
7256 ", ".join(repr(v) for v in self._value),
7259 def pps(self, decode_path=()):
7262 asn1_type_name=self.asn1_type_name,
7263 obj_name=self.__class__.__name__,
7264 decode_path=decode_path,
7265 optional=self.optional,
7266 default=self == self.default,
7267 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
7268 expl=None if self._expl is None else tag_decode(self._expl),
7273 expl_offset=self.expl_offset if self.expled else None,
7274 expl_tlen=self.expl_tlen if self.expled else None,
7275 expl_llen=self.expl_llen if self.expled else None,
7276 expl_vlen=self.expl_vlen if self.expled else None,
7277 expl_lenindef=self.expl_lenindef,
7278 lenindef=self.lenindef,
7279 ber_encoded=self.ber_encoded,
7282 for i, value in enumerate(self._value):
7283 yield value.pps(decode_path=decode_path + (str(i),))
7284 for pp in self.pps_lenindef(decode_path):
7288 class SetOf(SequenceOf):
7289 """``SET OF`` sequence type
7291 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
7294 tag_default = tag_encode(form=TagFormConstructed, num=17)
7295 asn1_type_name = "SET OF"
7297 def _value_sanitize(self, value):
7298 value = super()._value_sanitize(value)
7299 if hasattr(value, NEXT_ATTR_NAME):
7301 "SetOf does not support iterator values, as no sense in them"
7306 v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
7307 return b"".join((self.tag, len_encode(len(v)), v))
7309 def _encode2nd(self, writer, state_iter):
7310 write_full(writer, self.tag + len_encode(next(state_iter)))
7312 for v in self._values_for_encoding():
7314 v.encode2nd(buf.write, state_iter)
7315 values.append(buf.getvalue())
7318 write_full(writer, v)
7320 def _encode_cer(self, writer):
7321 write_full(writer, self.tag + LENINDEF)
7322 for v in sorted(encode_cer(v) for v in self._values_for_encoding()):
7323 write_full(writer, v)
7324 write_full(writer, EOC)
7326 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
7327 return super()._decode(
7334 ordering_check=True,
7338 def obj_by_path(pypath): # pragma: no cover
7339 """Import object specified as string Python path
7341 Modules must be separated from classes/functions with ``:``.
7343 >>> obj_by_path("foo.bar:Baz")
7344 <class 'foo.bar.Baz'>
7345 >>> obj_by_path("foo.bar:Baz.boo")
7346 <classmethod 'foo.bar.Baz.boo'>
7348 mod, objs = pypath.rsplit(":", 1)
7349 from importlib import import_module
7350 obj = import_module(mod)
7351 for obj_name in objs.split("."):
7352 obj = getattr(obj, obj_name)
7356 def generic_decoder(): # pragma: no cover
7357 # All of this below is a big hack with self references
7358 choice = PrimitiveTypes()
7359 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
7360 choice.specs["SetOf"] = SetOf(schema=choice)
7362 choice.specs["SequenceOf%d" % i] = SequenceOf(
7366 choice.specs["Any"] = Any()
7368 # Class name equals to type name, to omit it from output
7369 class SEQUENCEOF(SequenceOf):
7377 with_decode_path=False,
7378 decode_path_only=(),
7381 def _pprint_pps(pps):
7383 if hasattr(pp, "_fields"):
7385 decode_path_only != () and
7386 pp.decode_path[:len(decode_path_only)] != decode_path_only
7389 if pp.asn1_type_name == Choice.asn1_type_name:
7391 pp_kwargs = pp._asdict()
7392 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
7393 pp = _pp(**pp_kwargs)
7394 yield pp_console_row(
7399 with_colours=with_colours,
7400 with_decode_path=with_decode_path,
7401 decode_path_len_decrease=len(decode_path_only),
7403 for row in pp_console_blob(
7405 decode_path_len_decrease=len(decode_path_only),
7409 for row in _pprint_pps(pp):
7411 return "\n".join(_pprint_pps(obj.pps(decode_path)))
7412 return SEQUENCEOF(), pprint_any
7415 def ascii_visualize(ba):
7416 """Output only ASCII printable characters, like in hexdump -C
7418 Example output for given binary string (right part)::
7420 92 2b 39 20 65 91 e6 8e 95 93 1a 58 df 02 78 ea |.+9 e......X..x.|
7423 return "".join((chr(b) if 0x20 <= b <= 0x7E else ".") for b in ba)
7427 """Generate ``hexdump -C`` like output
7431 00000000 30 80 30 80 a0 80 02 01 02 00 00 02 14 54 a5 18 |0.0..........T..|
7432 00000010 69 ef 8b 3f 15 fd ea ad bd 47 e0 94 81 6b 06 6a |i..?.....G...k.j|
7434 Result of that function is a generator of lines, where each line is
7439 ["00000010 ", " 69", " ef", " 8b", " 3f", " 15", " fd", " ea", " ad ",
7440 " bd", " 47", " e0", " 94", " 81", " 6b", " 06", " 6a ",
7441 " |i..?.....G...k.j|"]
7445 hexed = hexenc(raw).upper()
7446 addr, cols = 0, ["%08x " % 0]
7447 for i in range(0, len(hexed), 2):
7448 if i != 0 and i // 2 % 8 == 0:
7450 if i != 0 and i // 2 % 16 == 0:
7451 cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:addr + 16])))
7454 cols = ["%08x " % addr]
7455 cols.append(" " + hexed[i:i + 2])
7457 cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:])))
7461 def browse(raw, obj, oid_maps=()):
7462 """Interactive browser
7464 :param bytes raw: binary data you decoded
7465 :param obj: decoded :py:class:`pyderasn.Obj`
7466 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
7467 Its human readable form is printed when OID is met
7469 .. note:: `urwid <http://urwid.org/>`__ dependency required
7471 This browser is an interactive terminal application for browsing
7472 structures of your decoded ASN.1 objects. You can quit it with **q**
7473 key. It consists of three windows:
7476 View of ASN.1 elements hierarchy. You can navigate it using **Up**,
7477 **Down**, **PageUp**, **PageDown**, **Home**, **End** keys.
7478 **Left** key goes to constructed element above. **Plus**/**Minus**
7479 keys collapse/uncollapse constructed elements. **Space** toggles it
7481 window with various information about element. You can scroll it
7482 with **h**/**l** (down, up) (**H**/**L** for triple speed) keys
7484 window with raw data hexdump and highlighted current element's
7485 contents. It automatically focuses on element's data. You can
7486 scroll it with **j**/**k** (down, up) (**J**/**K** for triple
7487 speed) keys. If element has explicit tag, then it also will be
7488 highlighted with different colour
7490 Window's header contains current decode path and progress bars with
7491 position in *info* and *hexdump* windows.
7493 If you press **d**, then current element will be saved in the
7494 current directory under its decode path name (adding ".0", ".1", etc
7495 suffix if such file already exists). **D** will save it with explicit tag.
7497 You can also invoke it with ``--browse`` command line argument.
7499 from copy import deepcopy
7500 from os.path import exists as path_exists
7503 class TW(urwid.TreeWidget):
7504 def __init__(self, state, *args, **kwargs):
7506 self.scrolled = {"info": False, "hexdump": False}
7507 super().__init__(*args, **kwargs)
7510 pp = self.get_node().get_value()
7511 constructed = len(pp) > 1
7512 return (pp if hasattr(pp, "_fields") else pp[0]), constructed
7514 def _state_update(self):
7515 pp, _ = self._get_pp()
7516 self.state["decode_path"].set_text(
7517 ":".join(str(p) for p in pp.decode_path)
7519 lines = deepcopy(self.state["hexed"])
7521 def attr_set(i, attr):
7522 line = lines[i // 16]
7523 idx = 1 + (i - 16 * (i // 16))
7524 line[idx] = (attr, line[idx])
7526 if pp.expl_offset is not None:
7529 pp.expl_offset + pp.expl_tlen + pp.expl_llen,
7531 attr_set(i, "select-expl")
7532 for i in range(pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen):
7533 attr_set(i, "select-value")
7534 self.state["hexdump"]._set_body([urwid.Text(line) for line in lines])
7535 self.state["hexdump"].set_focus(pp.offset // 16)
7536 self.state["hexdump"].set_focus_valign("middle")
7537 self.state["hexdump_bar"].set_completion(
7538 (100 * pp.offset // 16) //
7539 len(self.state["hexdump"]._body.positions())
7543 [("header", "Name: "), pp.obj_name],
7544 [("header", "Type: "), pp.asn1_type_name],
7545 [("header", "Offset: "), "%d (0x%x)" % (pp.offset, pp.offset)],
7546 [("header", "[TLV]len: "), "%d/%d/%d" % (
7547 pp.tlen, pp.llen, pp.vlen,
7549 [("header", "TLVlen: "), "%d" % sum((
7550 pp.tlen, pp.llen, pp.vlen,
7552 [("header", "Slice: "), "[%d:%d]" % (
7553 pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen,
7557 lines.append([("warning", "LENINDEF")])
7559 lines.append([("warning", "BER encoded")])
7561 lines.append([("warning", "BERed")])
7562 if pp.expl is not None:
7563 lines.append([("header", "EXPLICIT")])
7564 klass, _, num = pp.expl
7565 lines.append([" Tag: %s%d" % (TagClassReprs[klass], num)])
7566 if pp.expl_offset is not None:
7567 lines.append([" Offset: %d" % pp.expl_offset])
7568 lines.append([" [TLV]len: %d/%d/%d" % (
7569 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7571 lines.append([" TLVlen: %d" % sum((
7572 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7574 lines.append([" Slice: [%d:%d]" % (
7576 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen,
7578 if pp.impl is not None:
7579 klass, _, num = pp.impl
7581 ("header", "IMPLICIT: "), "%s%d" % (TagClassReprs[klass], num),
7584 lines.append(["OPTIONAL"])
7586 lines.append(["DEFAULT"])
7587 if len(pp.decode_path) > 0:
7588 ent = pp.decode_path[-1]
7589 if isinstance(ent, DecodePathDefBy):
7591 value = str(ent.defined_by)
7592 oid_name = find_oid_name(
7593 ent.defined_by.asn1_type_name, oid_maps, value,
7595 lines.append([("header", "DEFINED BY: "), "%s" % (
7596 value if oid_name is None
7597 else "%s (%s)" % (oid_name, value)
7600 if pp.value is not None:
7601 lines.append([("header", "Value: "), pp.value])
7603 len(oid_maps) > 0 and
7604 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
7606 for oid_map in oid_maps:
7607 oid_name = oid_map.get(pp.value)
7608 if oid_name is not None:
7609 lines.append([("header", "Human: "), oid_name])
7611 if pp.asn1_type_name == Integer.asn1_type_name:
7613 ("header", "Decimal: "), "%d" % int(pp.obj),
7616 ("header", "Hexadecimal: "), colonize_hex(pp.obj.tohex()),
7618 if pp.blob.__class__ == bytes:
7619 blob = hexenc(pp.blob).upper()
7620 for i in range(0, len(blob), 32):
7621 lines.append([colonize_hex(blob[i:i + 32])])
7622 elif pp.blob.__class__ == tuple:
7623 lines.append([", ".join(pp.blob)])
7624 self.state["info"]._set_body([urwid.Text(line) for line in lines])
7625 self.state["info_bar"].set_completion(0)
7627 def selectable(self):
7628 if self.state["widget_current"] != self:
7629 self.state["widget_current"] = self
7630 self.scrolled["info"] = False
7631 self.scrolled["hexdump"] = False
7632 self._state_update()
7633 return super().selectable()
7635 def _get_display_text_without_offset(self):
7636 pp, constructed = self._get_pp()
7637 style = "constructed" if constructed else ""
7638 if len(pp.decode_path) == 0:
7639 return (style, pp.obj_name)
7640 if pp.asn1_type_name == "EOC":
7641 return ("eoc", "EOC")
7642 ent = pp.decode_path[-1]
7643 if isinstance(ent, DecodePathDefBy):
7644 value = str(ent.defined_by)
7645 oid_name = find_oid_name(
7646 ent.defined_by.asn1_type_name, oid_maps, value,
7648 return ("defby", "DEFBY:" + (
7649 value if oid_name is None else oid_name
7653 def get_display_text(self):
7654 pp, _ = self._get_pp()
7655 style, ent = self._get_display_text_without_offset()
7656 return [(style, ent), " [%d]" % pp.offset]
7658 def _scroll(self, what, step):
7659 self.state[what]._invalidate()
7660 pos = self.state[what].focus_position
7661 if not self.scrolled[what]:
7662 self.scrolled[what] = True
7664 pos = max(0, pos + step)
7665 pos = min(pos, len(self.state[what]._body.positions()) - 1)
7666 self.state[what].set_focus(pos)
7667 self.state[what].set_focus_valign("top")
7668 self.state[what + "_bar"].set_completion(
7669 (100 * pos) // len(self.state[what]._body.positions())
7672 def keypress(self, size, key):
7674 raise urwid.ExitMainLoop()
7677 self.expanded = not self.expanded
7678 self.update_expanded_icon()
7681 hexdump_steps = {"j": 1, "k": -1, "J": 5, "K": -5}
7682 if key in hexdump_steps:
7683 self._scroll("hexdump", hexdump_steps[key])
7686 info_steps = {"h": 1, "l": -1, "H": 5, "L": -5}
7687 if key in info_steps:
7688 self._scroll("info", info_steps[key])
7691 if key in ("d", "D"):
7692 pp, _ = self._get_pp()
7693 dp = ":".join(str(p) for p in pp.decode_path)
7694 dp = dp.replace(" ", "_")
7697 if key == "d" or pp.expl_offset is None:
7698 data = self.state["raw"][pp.offset:(
7699 pp.offset + pp.tlen + pp.llen + pp.vlen
7702 data = self.state["raw"][pp.expl_offset:(
7703 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen
7707 def duplicate_path(dp, ctr):
7710 return "%s.%d" % (dp, ctr)
7713 if not path_exists(duplicate_path(dp, ctr)):
7716 dp = duplicate_path(dp, ctr)
7717 with open(dp, "wb") as fd:
7719 self.state["decode_path"].set_text(
7720 ("warning", "Saved to: " + dp)
7723 return super().keypress(size, key)
7725 class PN(urwid.ParentNode):
7726 def __init__(self, state, value, *args, **kwargs):
7728 if not hasattr(value, "_fields"):
7730 super().__init__(value, *args, **kwargs)
7732 def load_widget(self):
7733 return TW(self.state, self)
7735 def load_child_keys(self):
7736 value = self.get_value()
7737 if hasattr(value, "_fields"):
7739 return range(len(value[1:]))
7741 def load_child_node(self, key):
7744 self.get_value()[key + 1],
7747 depth=self.get_depth() + 1,
7750 class LabeledPG(urwid.ProgressBar):
7751 def __init__(self, label, *args, **kwargs):
7753 super().__init__(*args, **kwargs)
7756 return "%s: %s" % (self.label, super().get_text())
7758 WinHexdump = urwid.ListBox([urwid.Text("")])
7759 WinInfo = urwid.ListBox([urwid.Text("")])
7760 WinDecodePath = urwid.Text("", "center")
7761 WinInfoBar = LabeledPG("info", "pg-normal", "pg-complete")
7762 WinHexdumpBar = LabeledPG("hexdump", "pg-normal", "pg-complete")
7763 WinTree = urwid.TreeListBox(urwid.TreeWalker(PN(
7766 "hexed": list(hexdump(raw)),
7767 "widget_current": None,
7769 "info_bar": WinInfoBar,
7770 "hexdump": WinHexdump,
7771 "hexdump_bar": WinHexdumpBar,
7772 "decode_path": WinDecodePath,
7776 help_text = " ".join((
7778 "space:(un)collapse",
7779 "(pg)up/down/home/end:nav",
7780 "jkJK:hexdump hlHL:info",
7786 ("weight", 1, WinTree),
7787 ("weight", 2, urwid.Pile([
7788 urwid.LineBox(WinInfo),
7789 urwid.LineBox(WinHexdump),
7792 header=urwid.Columns([
7793 ("weight", 2, urwid.AttrWrap(WinDecodePath, "header")),
7794 ("weight", 1, WinInfoBar),
7795 ("weight", 1, WinHexdumpBar),
7797 footer=urwid.AttrWrap(urwid.Text(help_text), "help")
7800 ("header", "bold", ""),
7801 ("constructed", "bold", ""),
7802 ("help", "light magenta", ""),
7803 ("warning", "light red", ""),
7804 ("defby", "light red", ""),
7805 ("eoc", "dark red", ""),
7806 ("select-value", "light green", ""),
7807 ("select-expl", "light red", ""),
7808 ("pg-normal", "", "light blue"),
7809 ("pg-complete", "black", "yellow"),
7814 def main(): # pragma: no cover
7816 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/CER/DER decoder")
7817 parser.add_argument(
7821 help="Skip that number of bytes from the beginning",
7823 parser.add_argument(
7825 help="Python paths to dictionary with OIDs, comma separated",
7827 parser.add_argument(
7829 help="Python path to schema definition to use",
7831 parser.add_argument(
7832 "--defines-by-path",
7833 help="Python path to decoder's defines_by_path",
7835 parser.add_argument(
7837 action="store_true",
7838 help="Disallow BER encoding",
7840 parser.add_argument(
7841 "--print-decode-path",
7842 action="store_true",
7843 help="Print decode paths",
7845 parser.add_argument(
7846 "--decode-path-only",
7847 help="Print only specified decode path",
7849 parser.add_argument(
7851 action="store_true",
7852 help="Allow explicit tag out-of-bound",
7854 parser.add_argument(
7856 action="store_true",
7857 help="Turn on event generation mode",
7859 parser.add_argument(
7861 action="store_true",
7862 help="Start ASN.1 browser",
7864 parser.add_argument(
7866 type=argparse.FileType("rb"),
7867 help="Path to BER/CER/DER file you want to decode",
7869 args = parser.parse_args()
7871 raw = file_mmaped(args.RAWFile)[args.skip:]
7873 args.RAWFile.seek(args.skip)
7874 raw = memoryview(args.RAWFile.read())
7875 args.RAWFile.close()
7877 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
7878 if args.oids else ()
7880 from functools import partial
7882 schema = obj_by_path(args.schema)
7883 pprinter = partial(pprint, big_blobs=True)
7885 schema, pprinter = generic_decoder()
7887 "bered": not args.nobered,
7888 "allow_expl_oob": args.allow_expl_oob,
7890 if args.defines_by_path is not None:
7891 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
7893 obj, _ = schema().decode(raw, ctx=ctx)
7894 browse(raw, obj, oid_maps)
7895 from sys import exit as sys_exit
7897 from os import environ
7901 with_colours=environ.get("NO_COLOR") is None,
7902 with_decode_path=args.print_decode_path,
7904 () if args.decode_path_only is None else
7905 tuple(args.decode_path_only.split(":"))
7909 for decode_path, obj, tail in schema().decode_evgen(raw, ctx=ctx):
7910 print(pprinter(obj, decode_path=decode_path))
7912 obj, tail = schema().decode(raw, ctx=ctx)
7913 print(pprinter(obj))
7915 print("\nTrailing data: %s" % hexenc(tail))
7918 if __name__ == "__main__":
7919 from pyderasn import *