3 # cython: language_level=3
4 # pylint: disable=line-too-long,superfluous-parens,protected-access,too-many-lines
5 # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements
6 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures
7 # Copyright (C) 2017-2021 Sergey Matveev <stargrave@stargrave.org>
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU Lesser General Public License as
11 # published by the Free Software Foundation, version 3 of the License.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Python ASN.1 DER/CER/BER codec with abstract structures
22 This library allows you to marshal various structures in ASN.1 DER/CER
23 format, unmarshal BER/CER/DER ones.
27 >>> Integer().decod(raw) == i
30 There are primitive types, holding single values
31 (:py:class:`pyderasn.BitString`,
32 :py:class:`pyderasn.Boolean`,
33 :py:class:`pyderasn.Enumerated`,
34 :py:class:`pyderasn.GeneralizedTime`,
35 :py:class:`pyderasn.Integer`,
36 :py:class:`pyderasn.Null`,
37 :py:class:`pyderasn.ObjectIdentifier`,
38 :py:class:`pyderasn.OctetString`,
39 :py:class:`pyderasn.UTCTime`,
40 :py:class:`various strings <pyderasn.CommonString>`
41 (:py:class:`pyderasn.BMPString`,
42 :py:class:`pyderasn.GeneralString`,
43 :py:class:`pyderasn.GraphicString`,
44 :py:class:`pyderasn.IA5String`,
45 :py:class:`pyderasn.ISO646String`,
46 :py:class:`pyderasn.NumericString`,
47 :py:class:`pyderasn.PrintableString`,
48 :py:class:`pyderasn.T61String`,
49 :py:class:`pyderasn.TeletexString`,
50 :py:class:`pyderasn.UniversalString`,
51 :py:class:`pyderasn.UTF8String`,
52 :py:class:`pyderasn.VideotexString`,
53 :py:class:`pyderasn.VisibleString`)),
54 constructed types, holding multiple primitive types
55 (:py:class:`pyderasn.Sequence`,
56 :py:class:`pyderasn.SequenceOf`,
57 :py:class:`pyderasn.Set`,
58 :py:class:`pyderasn.SetOf`),
59 and special types like
60 :py:class:`pyderasn.Any` and
61 :py:class:`pyderasn.Choice`.
69 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
70 the default tag used during coding process. You can override it with
71 either ``IMPLICIT`` (using either ``impl`` keyword argument or ``impl``
72 class attribute), or ``EXPLICIT`` one (using either ``expl`` keyword
73 argument or ``expl`` class attribute). Both arguments take raw binary
74 string, containing that tag. You can **not** set implicit and explicit
77 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
78 functions, allowing you to easily create ``CONTEXT``
79 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
84 EXPLICIT tags always have **constructed** tag. PyDERASN does not
85 explicitly check correctness of schema input here.
89 Implicit tags have **primitive** (``tag_ctxp``) encoding for
94 >>> Integer(impl=tag_ctxp(1))
96 >>> Integer(expl=tag_ctxc(2))
99 Implicit tag is not explicitly shown.
101 Two objects of the same type, but with different implicit/explicit tags
104 You can get object's effective tag (either default or implicited) through
105 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
108 >>> tag_decode(tag_ctxc(123))
110 >>> klass, form, num = tag_decode(tag_ctxc(123))
111 >>> klass == TagClassContext
113 >>> form == TagFormConstructed
116 To determine if object has explicit tag, use ``expled`` boolean property
117 and ``expl_tag`` property, returning explicit tag's value.
122 Many objects in sequences could be ``OPTIONAL`` and could have
123 ``DEFAULT`` value. You can specify that object's property using
124 corresponding keyword arguments.
126 >>> Integer(optional=True, default=123)
127 INTEGER 123 OPTIONAL DEFAULT
129 Those specifications do not play any role in primitive value encoding,
130 but are taken into account when dealing with sequences holding them. For
131 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
134 class Version(Integer):
140 class TBSCertificate(Sequence):
142 ("version", Version(expl=tag_ctxc(0), default="v1")),
145 When default argument is used and value is not specified, then it equals
153 Some objects give ability to set value size constraints. This is either
154 possible integer value, or allowed length of various strings and
155 sequences. Constraints are set in the following way::
160 And values satisfaction is checked as: ``MIN <= X <= MAX``.
162 For simplicity you can also set bounds the following way::
164 bounded_x = X(bounds=(MIN, MAX))
166 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
172 All objects have ``ready`` boolean property, that tells if object is
173 ready to be encoded. If that kind of action is performed on unready
174 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
176 All objects are friendly to ``copy.copy()`` and copied objects can be
179 Also all objects can be safely ``pickle``-d, but pay attention that
180 pickling among different PyDERASN versions is prohibited.
187 Decoding is performed using :py:meth:`pyderasn.Obj.decode` method.
188 ``offset`` optional argument could be used to set initial object's
189 offset in the binary data, for convenience. It returns decoded object
190 and remaining unmarshalled data (tail). Internally all work is done on
191 ``memoryview(data)``, and you can leave returning tail as a memoryview,
192 by specifying ``leavemm=True`` argument.
194 Also note convenient :py:meth:`pyderasn.Obj.decod` method, that
195 immediately checks and raises if there is non-empty tail.
197 When object is decoded, ``decoded`` property is true and you can safely
198 use following properties:
200 * ``offset`` -- position including initial offset where object's tag starts
201 * ``tlen`` -- length of object's tag
202 * ``llen`` -- length of object's length value
203 * ``vlen`` -- length of object's value
204 * ``tlvlen`` -- length of the whole object
206 Pay attention that those values do **not** include anything related to
207 explicit tag. If you want to know information about it, then use:
209 * ``expled`` -- to know if explicit tag is set
210 * ``expl_offset`` (it is lesser than ``offset``)
213 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
214 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
216 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
219 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
226 You can specify so called context keyword argument during
227 :py:meth:`pyderasn.Obj.decode` invocation. It is dictionary containing
228 various options governing decoding process.
230 Currently available context options:
232 * :ref:`allow_default_values <allow_default_values_ctx>`
233 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
234 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
235 * :ref:`bered <bered_ctx>`
236 * :ref:`defines_by_path <defines_by_path_ctx>`
237 * :ref:`evgen_mode_upto <evgen_mode_upto_ctx>`
244 All objects have ``pps()`` method, that is a generator of
245 :py:class:`pyderasn.PP` namedtuple, holding various raw information
246 about the object. If ``pps`` is called on sequences, then all underlying
247 ``PP`` will be yielded.
249 You can use :py:func:`pyderasn.pp_console_row` function, converting
250 those ``PP`` to human readable string. Actually exactly it is used for
251 all object ``repr``. But it is easy to write custom formatters.
253 >>> from pyderasn import pprint
254 >>> encoded = Integer(-12345).encode()
255 >>> obj, tail = Integer().decode(encoded)
256 >>> print(pprint(obj))
257 0 [1,1, 2] INTEGER -12345
261 Example certificate::
263 >>> print(pprint(crt))
264 0 [1,3,1604] Certificate SEQUENCE
265 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
266 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
267 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
268 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
269 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
270 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
272 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
273 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
274 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
275 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
276 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
277 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
278 . . . . . . . 13:02:45:53
280 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
281 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
282 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
284 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
285 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
286 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
291 Let's parse that output, human::
293 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
294 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
295 0 1 2 3 4 5 6 7 8 9 10 11
299 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
305 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
311 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
316 Offset of the object, where its DER/BER encoding begins.
317 Pay attention that it does **not** include explicit tag.
319 If explicit tag exists, then this is its length (tag + encoded length).
321 Length of object's tag. For example CHOICE does not have its own tag,
324 Length of encoded length.
326 Length of encoded value.
328 Visual indentation to show the depth of object in the hierarchy.
330 Object's name inside SEQUENCE/CHOICE.
332 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
333 here. "IMPLICIT" is omitted.
335 Object's class name, if set. Omitted if it is just an ordinary simple
336 value (like with ``algorithm`` in example above).
340 Object's value, if set. Can consist of multiple words (like OCTET/BIT
341 STRINGs above). We see ``v3`` value in Version, because it is named.
342 ``rdnSequence`` is the choice of CHOICE type.
344 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
345 default one, specified in the schema.
347 Shows does object contains any kind of BER encoded data (possibly
348 Sequence holding BER-encoded underlying value).
350 Only applicable to BER encoded data. Indefinite length encoding mark.
352 Only applicable to BER encoded data. If object has BER-specific
353 encoding, then ``BER`` will be shown. It does not depend on indefinite
354 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
355 (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime``
358 Also it could be helpful to add quick ASN.1 pprinting command in your
359 pdb's configuration file::
361 alias pp1 import pyderasn ;; print(pyderasn.pprint(%1, oid_maps=(locals().get("OID_STR_TO_NAME", {}),)))
368 ASN.1 structures often have ANY and OCTET STRING fields, that are
369 DEFINED BY some previously met ObjectIdentifier. This library provides
370 ability to specify mapping between some OID and field that must be
371 decoded with specific specification.
378 :py:class:`pyderasn.ObjectIdentifier` field inside
379 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
380 necessary for decoding structures. For example, CMS (:rfc:`5652`)
383 class ContentInfo(Sequence):
385 ("contentType", ContentType(defines=((("content",), {
386 id_digestedData: DigestedData(),
387 id_signedData: SignedData(),
389 ("content", Any(expl=tag_ctxc(0))),
392 ``contentType`` field tells that it defines that ``content`` must be
393 decoded with ``SignedData`` specification, if ``contentType`` equals to
394 ``id-signedData``. The same applies to ``DigestedData``. If
395 ``contentType`` contains unknown OID, then no automatic decoding is
398 You can specify multiple fields, that will be autodecoded -- that is why
399 ``defines`` kwarg is a sequence. You can specify defined field
400 relatively or absolutely to current decode path. For example ``defines``
401 for AlgorithmIdentifier of X.509's
402 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
406 id_ecPublicKey: ECParameters(),
407 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
409 (("..", "subjectPublicKey"), {
410 id_rsaEncryption: RSAPublicKey(),
411 id_GostR3410_2001: OctetString(),
415 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
416 autodecode its parameters inside SPKI's algorithm and its public key
419 Following types can be automatically decoded (DEFINED BY):
421 * :py:class:`pyderasn.Any`
422 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
423 * :py:class:`pyderasn.OctetString`
424 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
425 ``Any``/``BitString``/``OctetString``-s
427 When any of those fields is automatically decoded, then ``.defined``
428 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
429 was defined, ``value`` contains corresponding decoded value. For example
430 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
432 .. _defines_by_path_ctx:
434 defines_by_path context option
435 ______________________________
437 Sometimes you either can not or do not want to explicitly set *defines*
438 in the schema. You can dynamically apply those definitions when calling
439 :py:meth:`pyderasn.Obj.decode` method.
441 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
442 value must be sequence of following tuples::
444 (decode_path, defines)
446 where ``decode_path`` is a tuple holding so-called decode path to the
447 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
448 ``defines``, holding exactly the same value as accepted in its
449 :ref:`keyword argument <defines>`.
451 For example, again for CMS, you want to automatically decode
452 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
453 structures it may hold. Also, automatically decode ``controlSequence``
456 content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
459 ((("content",), {id_signedData: SignedData()}),),
464 DecodePathDefBy(id_signedData),
469 id_cct_PKIData: PKIData(),
470 id_cct_PKIResponse: PKIResponse(),
476 DecodePathDefBy(id_signedData),
479 DecodePathDefBy(id_cct_PKIResponse),
485 id_cmc_recipientNonce: RecipientNonce(),
486 id_cmc_senderNonce: SenderNonce(),
487 id_cmc_statusInfoV2: CMCStatusInfoV2(),
488 id_cmc_transactionId: TransactionId(),
493 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
494 First function is useful for path construction when some automatic
495 decoding is already done. ``any`` means literally any value it meet --
496 useful for SEQUENCE/SET OF-s.
503 By default PyDERASN accepts only DER encoded data. By default it encodes
504 to DER. But you can optionally enable BER decoding with setting
505 ``bered`` :ref:`context <ctx>` argument to True. Indefinite lengths and
506 constructed primitive types should be parsed successfully.
508 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
509 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
510 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``,
511 ``UTCTime``, ``GeneralizedTime`` can contain it.
512 * If object has an indefinite length encoding, then its ``lenindef``
513 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
514 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
516 * If object has an indefinite length encoded explicit tag, then
517 ``expl_lenindef`` is set to True.
518 * If object has either any of BER-related encoding (explicit tag
519 indefinite length, object's indefinite length, BER-encoding) or any
520 underlying component has that kind of encoding, then ``bered``
521 attribute is set to True. For example SignedData CMS can have
522 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
523 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
525 EOC (end-of-contents) token's length is taken in advance in object's
528 .. _allow_expl_oob_ctx:
530 Allow explicit tag out-of-bound
531 -------------------------------
533 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
534 one value, more than one object. If you set ``allow_expl_oob`` context
535 option to True, then no error will be raised and that invalid encoding
536 will be silently further processed. But pay attention that offsets and
537 lengths will be invalid in that case.
541 This option should be used only for skipping some decode errors, just
542 to see the decoded structure somehow.
546 Streaming and dealing with huge structures
547 ------------------------------------------
554 ASN.1 structures can be huge, they can hold millions of objects inside
555 (for example Certificate Revocation Lists (CRL), holding revocation
556 state for every previously issued X.509 certificate). CACert.org's 8 MiB
557 CRL file takes more than half a gigabyte of memory to hold the decoded
560 If you just simply want to check the signature over the ``tbsCertList``,
561 you can create specialized schema with that field represented as
562 OctetString for example::
564 class TBSCertListFast(Sequence):
567 ("revokedCertificates", OctetString(
568 impl=SequenceOf.tag_default,
574 This allows you to quickly decode a few fields and check the signature
575 over the ``tbsCertList`` bytes.
577 But how can you get all certificate's serial number from it, after you
578 trust that CRL after signature validation? You can use so called
579 ``evgen`` (event generation) mode, to catch the events/facts of some
580 successful object decoding. Let's use command line capabilities::
582 $ python -m pyderasn --schema tests.test_crl:CertificateList --evgen revoke.crl
583 10 [1,1, 1] . . version: Version INTEGER v2 (01) OPTIONAL
584 15 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
585 26 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
586 13 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
587 34 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.10
588 39 [0,0, 9] . . . . . . value: [UNIV 19] AttributeValue ANY
589 32 [1,1, 14] . . . . . 0: AttributeTypeAndValue SEQUENCE
590 30 [1,1, 16] . . . . 0: RelativeDistinguishedName SET OF
592 188 [1,1, 1] . . . . userCertificate: CertificateSerialNumber INTEGER 17 (11)
593 191 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
594 191 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
595 191 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
596 186 [1,1, 18] . . . 0: RevokedCertificate SEQUENCE
597 208 [1,1, 1] . . . . userCertificate: CertificateSerialNumber INTEGER 20 (14)
598 211 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
599 211 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
600 211 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
601 206 [1,1, 18] . . . 1: RevokedCertificate SEQUENCE
603 9144992 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
604 9144992 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2020-02-08T07:25:06
605 9144985 [1,1, 20] . . . 415755: RevokedCertificate SEQUENCE
606 181 [1,4,9144821] . . revokedCertificates: RevokedCertificates SEQUENCE OF OPTIONAL
607 5 [1,4,9144997] . tbsCertList: TBSCertList SEQUENCE
608 9145009 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
609 9145020 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
610 9145007 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
611 9145022 [1,3, 513] . signatureValue: BIT STRING 4096 bits
612 0 [1,4,9145534] CertificateList SEQUENCE
614 Here we see how decoder works: it decodes SEQUENCE's tag, length, then
615 decodes underlying values. It can not tell if SEQUENCE is decoded, so
616 the event of the upper level SEQUENCE is the last one we see.
617 ``version`` field is just a single INTEGER -- it is decoded and event is
618 fired immediately. Then we see that ``algorithm`` and ``parameters``
619 fields are decoded and only after them the ``signature`` SEQUENCE is
620 fired as a successfully decoded. There are 4 events for each revoked
621 certificate entry in that CRL: ``userCertificate`` serial number,
622 ``utcTime`` of ``revocationDate`` CHOICE, ``RevokedCertificate`` itself
623 as a one of entity in ``revokedCertificates`` SEQUENCE OF.
625 We can do that in our ordinary Python code and understand where we are
626 by looking at deterministically generated decode paths (do not forget
627 about useful ``--print-decode-path`` CLI option). We must use
628 :py:meth:`pyderasn.Obj.decode_evgen` method, instead of ordinary
629 :py:meth:`pyderasn.Obj.decode`. It is generator yielding ``(decode_path,
630 obj, tail)`` tuples::
632 for decode_path, obj, _ in CertificateList().decode_evgen(crl_raw):
634 len(decode_path) == 4 and
635 decode_path[:2] == ("tbsCertList", "revokedCertificates"),
636 decode_path[3] == "userCertificate"
638 print("serial number:", int(obj))
640 Virtually it does not take any memory except at least needed for single
641 object storage. You can easily use that mode to determine required
642 object ``.offset`` and ``.*len`` to be able to decode it separately, or
643 maybe verify signature upon it just by taking bytes by ``.offset`` and
646 .. _evgen_mode_upto_ctx:
651 There is full ability to get any kind of data from the CRL in the
652 example above. However it is not too convenient to get the whole
653 ``RevokedCertificate`` structure, that is pretty lightweight and one may
654 do not want to disassemble it. You can use ``evgen_mode_upto``
655 :ref:`ctx <ctx>` option that semantically equals to
656 :ref:`defines_by_path <defines_by_path_ctx>` -- list of decode paths
657 mapped to any non-None value. If specified decode path is met, then any
658 subsequent objects won't be decoded in evgen mode. That allows us to
659 parse the CRL above with fully assembled ``RevokedCertificate``::
661 for decode_path, obj, _ in CertificateList().decode_evgen(
663 ctx={"evgen_mode_upto": (
664 (("tbsCertList", "revokedCertificates", any), True),
668 len(decode_path) == 3 and
669 decode_path[:2] == ("tbsCertList", "revokedCertificates"),
671 print("serial number:", int(obj["userCertificate"]))
675 SEQUENCE/SET values with DEFAULT specified are automatically decoded
683 POSIX compliant systems have ``mmap`` syscall, giving ability to work
684 the memory mapped file. You can deal with the file like it was an
685 ordinary binary string, allowing you not to load it to the memory first.
686 Also you can use them as an input for OCTET STRING, taking no Python
687 memory for their storage.
689 There is convenient :py:func:`pyderasn.file_mmaped` function that
690 creates read-only memoryview on the file contents::
692 with open("huge", "rb") as fd:
693 raw = file_mmaped(fd)
694 obj = Something.decode(raw)
698 mmap maps the **whole** file. So it plays no role if you seek-ed it
699 before. Take the slice of the resulting memoryview with required
704 If you use ZFS as underlying storage, then pay attention that
705 currently most platforms does not deal good with ZFS ARC and ordinary
706 page cache used for mmaps. It can take twice the necessary size in
707 the memory: both in page cache and ZFS ARC.
712 We can parse any kind of data now, but how can we produce files
713 streamingly, without storing their encoded representation in memory?
714 SEQUENCE by default encodes in memory all its values, joins them in huge
715 binary string, just to know the exact size of SEQUENCE's value for
716 encoding it in TLV. DER requires you to know all exact sizes of the
719 You can use CER encoding mode, that slightly differs from the DER, but
720 does not require exact sizes knowledge, allowing streaming encoding
721 directly to some writer/buffer. Just use
722 :py:meth:`pyderasn.Obj.encode_cer` method, providing the writer where
723 encoded data will flow::
725 with open("result", "wb") as fd:
726 obj.encode_cer(fd.write)
731 obj.encode_cer(buf.write)
733 If you do not want to create in-memory buffer every time, then you can
734 use :py:func:`pyderasn.encode_cer` function::
736 data = encode_cer(obj)
738 Remember that CER is **not valid** DER in most cases, so you **have to**
739 use :ref:`bered <bered_ctx>` :ref:`ctx <ctx>` option during its
740 decoding. Also currently there is **no** validation that provided CER is
741 valid one -- you are sure that it has only valid BER encoding.
745 SET OF values can not be streamingly encoded, because they are
746 required to be sorted byte-by-byte. Big SET OF values still will take
747 much memory. Use neither SET nor SET OF values, as modern ASN.1
750 Do not forget about using :ref:`mmap-ed <mmap>` memoryviews for your
751 OCTET STRINGs! They will be streamingly copied from underlying file to
752 the buffer using 1 KB chunks.
754 Some structures require that some of the elements have to be forcefully
755 DER encoded. For example ``SignedData`` CMS requires you to encode
756 ``SignedAttributes`` and X.509 certificates in DER form, allowing you to
757 encode everything else in BER. You can tell any of the structures to be
758 forcefully encoded in DER during CER encoding, by specifying
759 ``der_forced=True`` attribute::
761 class Certificate(Sequence):
765 class SignedAttributes(SetOf):
767 bounds = (1, float("+inf"))
770 .. _agg_octet_string:
775 In most cases, huge quantity of binary data is stored as OCTET STRING.
776 CER encoding splits it on 1 KB chunks. BER allows splitting on various
777 levels of chunks inclusion::
779 SOME STRING[CONSTRUCTED]
780 OCTET STRING[CONSTRUCTED]
781 OCTET STRING[PRIMITIVE]
783 OCTET STRING[PRIMITIVE]
785 OCTET STRING[PRIMITIVE]
787 OCTET STRING[PRIMITIVE]
789 OCTET STRING[CONSTRUCTED]
790 OCTET STRING[PRIMITIVE]
792 OCTET STRING[PRIMITIVE]
794 OCTET STRING[CONSTRUCTED]
795 OCTET STRING[CONSTRUCTED]
796 OCTET STRING[PRIMITIVE]
799 You can not just take the offset and some ``.vlen`` of the STRING and
800 treat it as the payload. If you decode it without
801 :ref:`evgen mode <evgen_mode>`, then it will be automatically aggregated
802 and ``bytes()`` will give the whole payload contents.
804 You are forced to use :ref:`evgen mode <evgen_mode>` for decoding for
805 small memory footprint. There is convenient
806 :py:func:`pyderasn.agg_octet_string` helper for reconstructing the
807 payload. Let's assume you have got BER/CER encoded ``ContentInfo`` with
808 huge ``SignedData`` and ``EncapsulatedContentInfo``. Let's calculate the
809 SHA512 digest of its ``eContent``::
811 fd = open("data.p7m", "rb")
812 raw = file_mmaped(fd)
813 ctx = {"bered": True}
814 for decode_path, obj, _ in ContentInfo().decode_evgen(raw, ctx=ctx):
815 if decode_path == ("content",):
819 raise ValueError("no content found")
820 hasher_state = sha512()
822 hasher_state.update(data)
824 evgens = SignedData().decode_evgen(
825 raw[content.offset:],
826 offset=content.offset,
829 agg_octet_string(evgens, ("encapContentInfo", "eContent"), raw, hasher)
831 digest = hasher_state.digest()
833 Simply replace ``hasher`` with some writeable file's ``fd.write`` to
834 copy the payload (without BER/CER encoding interleaved overhead) in it.
835 Virtually it won't take memory more than for keeping small structures
836 and 1 KB binary chunks.
840 SEQUENCE OF iterators
841 _____________________
843 You can use iterators as a value in :py:class:`pyderasn.SequenceOf`
844 classes. The only difference with providing the full list of objects, is
845 that type and bounds checking is done during encoding process. Also
846 sequence's value will be emptied after encoding, forcing you to set its
849 This is very useful when you have to create some huge objects, like
850 CRLs, with thousands and millions of entities inside. You can write the
851 generator taking necessary data from the database and giving the
852 ``RevokedCertificate`` objects. Only binary representation of that
853 objects will take memory during DER encoding.
858 There is ability to do 2-pass encoding to DER, writing results directly
859 to specified writer (buffer, file, whatever). It could be 1.5+ times
860 slower than ordinary encoding, but it takes little memory for 1st pass
861 state storing. For example, 1st pass state for CACert.org's CRL with
862 ~416K of certificate entries takes nearly 3.5 MB of memory.
863 ``SignedData`` with several gigabyte ``EncapsulatedContentInfo`` takes
864 nearly 0.5 KB of memory.
866 If you use :ref:`mmap-ed <mmap>` memoryviews, :ref:`SEQUENCE OF
867 iterators <seqof-iterators>` and write directly to opened file, then
868 there is very small memory footprint.
870 1st pass traverses through all the objects of the structure and returns
871 the size of DER encoded structure, together with 1st pass state object.
872 That state contains precalculated lengths for various objects inside the
877 fulllen, state = obj.encode1st()
879 2nd pass takes the writer and 1st pass state. It traverses through all
880 the objects again, but writes their encoded representation to the writer.
884 with open("result", "wb") as fd:
885 obj.encode2nd(fd.write, iter(state))
889 You **MUST NOT** use 1st pass state if anything is changed in the
890 objects. It is intended to be used immediately after 1st pass is
893 If you use :ref:`SEQUENCE OF iterators <seqof-iterators>`, then you
894 have to reinitialize the values after the 1st pass. And you **have to**
895 be sure that the iterator gives exactly the same values as previously.
896 Yes, you have to run your iterator twice -- because this is two pass
899 If you want to encode to the memory, then you can use convenient
900 :py:func:`pyderasn.encode2pass` helper.
906 .. autofunction:: pyderasn.browse
910 .. autoclass:: pyderasn.Obj
918 .. autoclass:: pyderasn.Boolean
923 .. autoclass:: pyderasn.Integer
924 :members: __init__, named, tohex
928 .. autoclass:: pyderasn.BitString
929 :members: __init__, bit_len, named
933 .. autoclass:: pyderasn.OctetString
938 .. autoclass:: pyderasn.Null
943 .. autoclass:: pyderasn.ObjectIdentifier
948 .. autoclass:: pyderasn.Enumerated
952 .. autoclass:: pyderasn.CommonString
956 .. autoclass:: pyderasn.NumericString
960 .. autoclass:: pyderasn.PrintableString
961 :members: __init__, allow_asterisk, allow_ampersand
965 .. autoclass:: pyderasn.IA5String
969 .. autoclass:: pyderasn.VisibleString
973 .. autoclass:: pyderasn.UTCTime
974 :members: __init__, todatetime
978 .. autoclass:: pyderasn.GeneralizedTime
979 :members: __init__, todatetime
986 .. autoclass:: pyderasn.Choice
987 :members: __init__, choice, value
991 .. autoclass:: PrimitiveTypes
995 .. autoclass:: pyderasn.Any
1003 .. autoclass:: pyderasn.Sequence
1008 .. autoclass:: pyderasn.Set
1013 .. autoclass:: pyderasn.SequenceOf
1018 .. autoclass:: pyderasn.SetOf
1024 .. autofunction:: pyderasn.abs_decode_path
1025 .. autofunction:: pyderasn.agg_octet_string
1026 .. autofunction:: pyderasn.ascii_visualize
1027 .. autofunction:: pyderasn.colonize_hex
1028 .. autofunction:: pyderasn.encode2pass
1029 .. autofunction:: pyderasn.encode_cer
1030 .. autofunction:: pyderasn.file_mmaped
1031 .. autofunction:: pyderasn.hexenc
1032 .. autofunction:: pyderasn.hexdec
1033 .. autofunction:: pyderasn.hexdump
1034 .. autofunction:: pyderasn.tag_encode
1035 .. autofunction:: pyderasn.tag_decode
1036 .. autofunction:: pyderasn.tag_ctxp
1037 .. autofunction:: pyderasn.tag_ctxc
1038 .. autoclass:: pyderasn.DecodeError
1040 .. autoclass:: pyderasn.NotEnoughData
1041 .. autoclass:: pyderasn.ExceedingData
1042 .. autoclass:: pyderasn.LenIndefForm
1043 .. autoclass:: pyderasn.TagMismatch
1044 .. autoclass:: pyderasn.InvalidLength
1045 .. autoclass:: pyderasn.InvalidOID
1046 .. autoclass:: pyderasn.ObjUnknown
1047 .. autoclass:: pyderasn.ObjNotReady
1048 .. autoclass:: pyderasn.InvalidValueType
1049 .. autoclass:: pyderasn.BoundsError
1056 You can decode DER/BER files using command line abilities::
1058 $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
1060 If there is no schema for your file, then you can try parsing it without,
1061 but of course IMPLICIT tags will often make it impossible. But result is
1062 good enough for the certificate above::
1064 $ python -m pyderasn path/to/file
1065 0 [1,3,1604] . >: SEQUENCE OF
1066 4 [1,3,1453] . . >: SEQUENCE OF
1067 8 [0,0, 5] . . . . >: [0] ANY
1068 . . . . . A0:03:02:01:02
1069 13 [1,1, 3] . . . . >: INTEGER 61595
1070 18 [1,1, 13] . . . . >: SEQUENCE OF
1071 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1072 31 [1,1, 0] . . . . . . >: NULL
1073 33 [1,3, 274] . . . . >: SEQUENCE OF
1074 37 [1,1, 11] . . . . . . >: SET OF
1075 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
1076 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
1077 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
1079 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
1080 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
1081 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
1082 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
1083 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
1084 . . . . . . . . . 61:2E:63:6F:6D:2F
1085 1461 [1,1, 13] . . >: SEQUENCE OF
1086 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1087 1474 [1,1, 0] . . . . >: NULL
1088 1476 [1,2, 129] . . >: BIT STRING 1024 bits
1089 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
1090 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
1096 If you have got dictionaries with ObjectIdentifiers, like example one
1097 from ``tests/test_crts.py``::
1100 "1.2.840.113549.1.1.1": "id-rsaEncryption",
1101 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
1103 "2.5.4.10": "id-at-organizationName",
1104 "2.5.4.11": "id-at-organizationalUnitName",
1107 then you can pass it to pretty printer to see human readable OIDs::
1109 $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
1111 37 [1,1, 11] . . . . . . >: SET OF
1112 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
1113 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
1114 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
1115 50 [1,1, 18] . . . . . . >: SET OF
1116 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
1117 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
1118 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
1119 70 [1,1, 18] . . . . . . >: SET OF
1120 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
1121 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
1122 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
1128 Each decoded element has so-called decode path: sequence of structure
1129 names it is passing during the decode process. Each element has its own
1130 unique path inside the whole ASN.1 tree. You can print it out with
1131 ``--print-decode-path`` option::
1133 $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
1134 0 [1,3,1604] Certificate SEQUENCE []
1135 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
1136 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
1137 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
1138 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
1139 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
1140 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
1142 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
1143 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
1144 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
1145 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
1146 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
1147 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
1148 . . . . . . . 13:02:45:53
1149 46 [1,1, 2] . . . . . . . DEFINED BY 2.5.4.6: CountryName PrintableString ES [tbsCertificate:issuer:rdnSequence:0:0:value:DEFINED BY 2.5.4.6]
1152 Now you can print only the specified tree, for example signature algorithm::
1154 $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
1155 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
1156 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1157 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
1161 from array import array
1162 from codecs import getdecoder
1163 from codecs import getencoder
1164 from collections import namedtuple
1165 from collections import OrderedDict
1166 from copy import copy
1167 from datetime import datetime
1168 from datetime import timedelta
1169 from io import BytesIO
1170 from math import ceil
1171 from operator import attrgetter
1172 from string import ascii_letters
1173 from string import digits
1174 from struct import Struct as struct_Struct
1175 from sys import maxsize as sys_maxsize
1176 from sys import version_info
1177 from unicodedata import category as unicat
1180 from termcolor import colored
1181 except ImportError: # pragma: no cover
1182 def colored(what, *args, **kwargs):
1233 "TagClassApplication",
1236 "TagClassUniversal",
1237 "TagFormConstructed",
1248 TagClassUniversal = 0
1249 TagClassApplication = 1 << 6
1250 TagClassContext = 1 << 7
1251 TagClassPrivate = 1 << 6 | 1 << 7
1252 TagFormPrimitive = 0
1253 TagFormConstructed = 1 << 5
1255 TagClassContext: "",
1256 TagClassApplication: "APPLICATION ",
1257 TagClassPrivate: "PRIVATE ",
1258 TagClassUniversal: "UNIV ",
1262 LENINDEF = b"\x80" # length indefinite mark
1263 LENINDEF_PP_CHAR = "∞"
1264 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
1265 SET01 = frozenset("01")
1266 DECIMALS = frozenset(digits)
1267 DECIMAL_SIGNS = ".,"
1268 NEXT_ATTR_NAME = "__next__"
1271 def file_mmaped(fd):
1272 """Make mmap-ed memoryview for reading from file
1274 :param fd: file object
1275 :returns: memoryview over read-only mmap-ing of the whole file
1279 It does not work under Windows.
1282 return memoryview(mmap.mmap(fd.fileno(), length=0, prot=mmap.PROT_READ))
1286 if not set(value) <= DECIMALS:
1287 raise ValueError("non-pure integer")
1291 def fractions2float(fractions_raw):
1292 pureint(fractions_raw)
1293 return float("0." + fractions_raw)
1296 def get_def_by_path(defines_by_path, sub_decode_path):
1297 """Get define by decode path
1299 for path, define in defines_by_path:
1300 if len(path) != len(sub_decode_path):
1302 for p1, p2 in zip(path, sub_decode_path):
1303 if (p1 is not any) and (p1 != p2):
1309 ########################################################################
1311 ########################################################################
1313 class ASN1Error(ValueError):
1317 class DecodeError(ASN1Error):
1318 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
1320 :param str msg: reason of decode failing
1321 :param klass: optional exact DecodeError inherited class (like
1322 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
1323 :py:exc:`InvalidLength`)
1324 :param decode_path: tuple of strings. It contains human
1325 readable names of the fields through which
1326 decoding process has passed
1327 :param int offset: binary offset where failure happened
1332 self.decode_path = decode_path
1333 self.offset = offset
1338 "" if self.klass is None else self.klass.__name__,
1340 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
1341 if len(self.decode_path) > 0 else ""
1343 ("(at %d)" % self.offset) if self.offset > 0 else "",
1349 return "%s(%s)" % (self.__class__.__name__, self)
1352 class NotEnoughData(DecodeError):
1356 class ExceedingData(ASN1Error):
1357 def __init__(self, nbytes):
1359 self.nbytes = nbytes
1362 return "%d trailing bytes" % self.nbytes
1365 return "%s(%s)" % (self.__class__.__name__, self)
1368 class LenIndefForm(DecodeError):
1372 class TagMismatch(DecodeError):
1376 class InvalidLength(DecodeError):
1380 class InvalidOID(DecodeError):
1384 class ObjUnknown(ASN1Error):
1385 def __init__(self, name):
1390 return "object is unknown: %s" % self.name
1393 return "%s(%s)" % (self.__class__.__name__, self)
1396 class ObjNotReady(ASN1Error):
1397 def __init__(self, name):
1402 return "object is not ready: %s" % self.name
1405 return "%s(%s)" % (self.__class__.__name__, self)
1408 class InvalidValueType(ASN1Error):
1409 def __init__(self, expected_types):
1411 self.expected_types = expected_types
1414 return "invalid value type, expected: %s" % ", ".join(
1415 [repr(t) for t in self.expected_types]
1419 return "%s(%s)" % (self.__class__.__name__, self)
1422 class BoundsError(ASN1Error):
1423 def __init__(self, bound_min, value, bound_max):
1425 self.bound_min = bound_min
1427 self.bound_max = bound_max
1430 return "unsatisfied bounds: %s <= %s <= %s" % (
1437 return "%s(%s)" % (self.__class__.__name__, self)
1440 ########################################################################
1442 ########################################################################
1444 _hexdecoder = getdecoder("hex")
1445 _hexencoder = getencoder("hex")
1449 """Binary data to hexadecimal string convert
1451 return _hexdecoder(data)[0]
1455 """Hexadecimal string to binary data convert
1457 return _hexencoder(data)[0].decode("ascii")
1460 def int_bytes_len(num, byte_len=8):
1463 return int(ceil(float(num.bit_length()) / byte_len))
1466 def zero_ended_encode(num):
1467 octets = bytearray(int_bytes_len(num, 7))
1469 octets[i] = num & 0x7F
1473 octets[i] = 0x80 | (num & 0x7F)
1476 return bytes(octets)
1479 int2byte = struct_Struct(">B").pack
1482 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
1483 """Encode tag to binary form
1485 :param int num: tag's number
1486 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
1487 :py:data:`pyderasn.TagClassContext`,
1488 :py:data:`pyderasn.TagClassApplication`,
1489 :py:data:`pyderasn.TagClassPrivate`)
1490 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
1491 :py:data:`pyderasn.TagFormConstructed`)
1495 return int2byte(klass | form | num)
1496 # [XX|X|11111][1.......][1.......] ... [0.......]
1497 return int2byte(klass | form | 31) + zero_ended_encode(num)
1500 def tag_decode(tag):
1501 """Decode tag from binary form
1505 No validation is performed, assuming that it has already passed.
1507 It returns tuple with three integers, as
1508 :py:func:`pyderasn.tag_encode` accepts.
1510 first_octet = tag[0]
1511 klass = first_octet & 0xC0
1512 form = first_octet & 0x20
1513 if first_octet & 0x1F < 0x1F:
1514 return (klass, form, first_octet & 0x1F)
1516 for octet in tag[1:]:
1519 return (klass, form, num)
1523 """Create CONTEXT PRIMITIVE tag
1525 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
1529 """Create CONTEXT CONSTRUCTED tag
1531 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
1534 def tag_strip(data):
1535 """Take off tag from the data
1537 :returns: (encoded tag, tag length, remaining data)
1540 raise NotEnoughData("no data at all")
1541 if data[0] & 0x1F < 31:
1542 return data[:1], 1, data[1:]
1547 raise DecodeError("unfinished tag")
1548 if data[i] & 0x80 == 0:
1550 if i == 1 and data[1] < 0x1F:
1551 raise DecodeError("unexpected long form")
1552 if i > 1 and data[1] & 0x7F == 0:
1553 raise DecodeError("leading zero byte in tag value")
1555 return data[:i], i, data[i:]
1561 octets = bytearray(int_bytes_len(l) + 1)
1562 octets[0] = 0x80 | (len(octets) - 1)
1563 for i in range(len(octets) - 1, 0, -1):
1564 octets[i] = l & 0xFF
1566 return bytes(octets)
1569 def len_decode(data):
1572 :returns: (decoded length, length's length, remaining data)
1573 :raises LenIndefForm: if indefinite form encoding is met
1576 raise NotEnoughData("no data at all")
1577 first_octet = data[0]
1578 if first_octet & 0x80 == 0:
1579 return first_octet, 1, data[1:]
1580 octets_num = first_octet & 0x7F
1581 if octets_num + 1 > len(data):
1582 raise NotEnoughData("encoded length is longer than data")
1584 raise LenIndefForm()
1586 raise DecodeError("leading zeros")
1588 for v in data[1:1 + octets_num]:
1591 raise DecodeError("long form instead of short one")
1592 return l, 1 + octets_num, data[1 + octets_num:]
1595 LEN0 = len_encode(0)
1596 LEN1 = len_encode(1)
1597 LEN1K = len_encode(1000)
1601 """How many bytes length field will take
1605 if l < 256: # 1 << 8
1607 if l < 65536: # 1 << 16
1609 if l < 16777216: # 1 << 24
1611 if l < 4294967296: # 1 << 32
1613 if l < 1099511627776: # 1 << 40
1615 if l < 281474976710656: # 1 << 48
1617 if l < 72057594037927936: # 1 << 56
1619 raise OverflowError("too big length")
1622 def write_full(writer, data):
1623 """Fully write provided data
1625 :param writer: must comply with ``io.RawIOBase.write`` behaviour
1627 BytesIO does not guarantee that the whole data will be written at
1628 once. That function write everything provided, raising an error if
1629 ``writer`` returns None.
1631 data = memoryview(data)
1633 while written != len(data):
1634 n = writer(data[written:])
1636 raise ValueError("can not write to buf")
1640 # If it is 64-bit system, then use compact 64-bit array of unsigned
1641 # longs. Use an ordinary list with universal integers otherwise, that
1643 if sys_maxsize > 2 ** 32:
1644 def state_2pass_new():
1647 def state_2pass_new():
1651 ########################################################################
1653 ########################################################################
1655 class AutoAddSlots(type):
1656 def __new__(cls, name, bases, _dict):
1657 _dict["__slots__"] = _dict.get("__slots__", ())
1658 return super().__new__(cls, name, bases, _dict)
1661 BasicState = namedtuple("BasicState", (
1674 ), **NAMEDTUPLE_KWARGS)
1677 class Obj(metaclass=AutoAddSlots):
1678 """Common ASN.1 object class
1680 All ASN.1 types are inherited from it. It has metaclass that
1681 automatically adds ``__slots__`` to all inherited classes.
1706 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1707 self._expl = getattr(self, "expl", None) if expl is None else expl
1708 if self.tag != self.tag_default and self._expl is not None:
1709 raise ValueError("implicit and explicit tags can not be set simultaneously")
1710 if self.tag is None:
1711 self._tag_order = None
1713 tag_class, _, tag_num = tag_decode(
1714 self.tag if self._expl is None else self._expl
1716 self._tag_order = (tag_class, tag_num)
1717 if default is not None:
1719 self.optional = optional
1720 self.offset, self.llen, self.vlen = _decoded
1722 self.expl_lenindef = False
1723 self.lenindef = False
1724 self.ber_encoded = False
1727 def ready(self): # pragma: no cover
1728 """Is object ready to be encoded?
1730 raise NotImplementedError()
1732 def _assert_ready(self):
1734 raise ObjNotReady(self.__class__.__name__)
1738 """Is either object or any elements inside is BER encoded?
1740 return self.expl_lenindef or self.lenindef or self.ber_encoded
1744 """Is object decoded?
1746 return (self.llen + self.vlen) > 0
1748 def __getstate__(self): # pragma: no cover
1749 """Used for making safe to be mutable pickleable copies
1751 raise NotImplementedError()
1753 def __setstate__(self, state):
1754 if state.version != __version__:
1755 raise ValueError("data is pickled by different PyDERASN version")
1756 self.tag = state.tag
1757 self._tag_order = state.tag_order
1758 self._expl = state.expl
1759 self.default = state.default
1760 self.optional = state.optional
1761 self.offset = state.offset
1762 self.llen = state.llen
1763 self.vlen = state.vlen
1764 self.expl_lenindef = state.expl_lenindef
1765 self.lenindef = state.lenindef
1766 self.ber_encoded = state.ber_encoded
1769 def tag_order(self):
1770 """Tag's (class, number) used for DER/CER sorting
1772 return self._tag_order
1775 def tag_order_cer(self):
1776 return self.tag_order
1780 """.. seealso:: :ref:`decoding`
1782 return len(self.tag)
1786 """.. seealso:: :ref:`decoding`
1788 return self.tlen + self.llen + self.vlen
1790 def __str__(self): # pragma: no cover
1791 return self.__unicode__()
1793 def __ne__(self, their):
1794 return not(self == their)
1796 def __gt__(self, their): # pragma: no cover
1797 return not(self < their)
1799 def __le__(self, their): # pragma: no cover
1800 return (self == their) or (self < their)
1802 def __ge__(self, their): # pragma: no cover
1803 return (self == their) or (self > their)
1805 def _encode(self): # pragma: no cover
1806 raise NotImplementedError()
1808 def _encode_cer(self, writer):
1809 write_full(writer, self._encode())
1811 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode): # pragma: no cover
1812 yield NotImplemented
1814 def _encode1st(self, state):
1815 raise NotImplementedError()
1817 def _encode2nd(self, writer, state_iter):
1818 raise NotImplementedError()
1821 """DER encode the structure
1823 :returns: DER representation
1825 raw = self._encode()
1826 if self._expl is None:
1828 return b"".join((self._expl, len_encode(len(raw)), raw))
1830 def encode1st(self, state=None):
1831 """Do the 1st pass of 2-pass encoding
1833 :rtype: (int, array("L"))
1834 :returns: full length of encoded data and precalculated various
1838 state = state_2pass_new()
1839 if self._expl is None:
1840 return self._encode1st(state)
1842 idx = len(state) - 1
1843 vlen, _ = self._encode1st(state)
1845 fulllen = len(self._expl) + len_size(vlen) + vlen
1846 return fulllen, state
1848 def encode2nd(self, writer, state_iter):
1849 """Do the 2nd pass of 2-pass encoding
1851 :param writer: must comply with ``io.RawIOBase.write`` behaviour
1852 :param state_iter: iterator over the 1st pass state (``iter(state)``)
1854 if self._expl is None:
1855 self._encode2nd(writer, state_iter)
1857 write_full(writer, self._expl + len_encode(next(state_iter)))
1858 self._encode2nd(writer, state_iter)
1860 def encode_cer(self, writer):
1861 """CER encode the structure to specified writer
1863 :param writer: must comply with ``io.RawIOBase.write``
1864 behaviour. It takes slice to be written and
1865 returns number of bytes processed. If it returns
1866 None, then exception will be raised
1868 if self._expl is not None:
1869 write_full(writer, self._expl + LENINDEF)
1870 if getattr(self, "der_forced", False):
1871 write_full(writer, self._encode())
1873 self._encode_cer(writer)
1874 if self._expl is not None:
1875 write_full(writer, EOC)
1877 def hexencode(self):
1878 """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1880 return hexenc(self.encode())
1890 _ctx_immutable=True,
1894 :param data: either binary or memoryview
1895 :param int offset: initial data's offset
1896 :param bool leavemm: do we need to leave memoryview of remaining
1897 data as is, or convert it to bytes otherwise
1898 :param decode_path: current decode path (tuples of strings,
1899 possibly with DecodePathDefBy) with will be
1900 the root for all underlying objects
1901 :param ctx: optional :ref:`context <ctx>` governing decoding process
1902 :param bool tag_only: decode only the tag, without length and
1903 contents (used only in Choice and Set
1904 structures, trying to determine if tag satisfies
1906 :param bool _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1908 :returns: (Obj, remaining data)
1910 .. seealso:: :ref:`decoding`
1912 result = next(self.decode_evgen(
1924 _, obj, tail = result
1935 _ctx_immutable=True,
1938 """Decode with evgen mode on
1940 That method is identical to :py:meth:`pyderasn.Obj.decode`, but
1941 it returns the generator producing ``(decode_path, obj, tail)``
1943 .. seealso:: :ref:`evgen mode <evgen_mode>`.
1947 elif _ctx_immutable:
1949 tlv = memoryview(data)
1952 get_def_by_path(ctx.get("evgen_mode_upto", ()), decode_path) is not None
1955 if self._expl is None:
1956 for result in self._decode(
1959 decode_path=decode_path,
1962 evgen_mode=_evgen_mode,
1967 _decode_path, obj, tail = result
1968 if _decode_path is not decode_path:
1972 t, tlen, lv = tag_strip(tlv)
1973 except DecodeError as err:
1974 raise err.__class__(
1976 klass=self.__class__,
1977 decode_path=decode_path,
1982 klass=self.__class__,
1983 decode_path=decode_path,
1987 l, llen, v = len_decode(lv)
1988 except LenIndefForm as err:
1989 if not ctx.get("bered", False):
1990 raise err.__class__(
1992 klass=self.__class__,
1993 decode_path=decode_path,
1997 offset += tlen + llen
1998 for result in self._decode(
2001 decode_path=decode_path,
2004 evgen_mode=_evgen_mode,
2006 if tag_only: # pragma: no cover
2009 _decode_path, obj, tail = result
2010 if _decode_path is not decode_path:
2012 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
2013 if eoc_expected.tobytes() != EOC:
2016 klass=self.__class__,
2017 decode_path=decode_path,
2021 obj.expl_lenindef = True
2022 except DecodeError as err:
2023 raise err.__class__(
2025 klass=self.__class__,
2026 decode_path=decode_path,
2031 raise NotEnoughData(
2032 "encoded length is longer than data",
2033 klass=self.__class__,
2034 decode_path=decode_path,
2037 for result in self._decode(
2039 offset=offset + tlen + llen,
2040 decode_path=decode_path,
2043 evgen_mode=_evgen_mode,
2045 if tag_only: # pragma: no cover
2048 _decode_path, obj, tail = result
2049 if _decode_path is not decode_path:
2051 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
2053 "explicit tag out-of-bound, longer than data",
2054 klass=self.__class__,
2055 decode_path=decode_path,
2058 yield decode_path, obj, (tail if leavemm else tail.tobytes())
2060 def decod(self, data, offset=0, decode_path=(), ctx=None):
2061 """Decode the data, check that tail is empty
2063 :raises ExceedingData: if tail is not empty
2065 This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
2066 (decode without tail) that also checks that there is no
2069 obj, tail = self.decode(
2072 decode_path=decode_path,
2077 raise ExceedingData(len(tail))
2080 def hexdecode(self, data, *args, **kwargs):
2081 """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
2083 return self.decode(hexdec(data), *args, **kwargs)
2085 def hexdecod(self, data, *args, **kwargs):
2086 """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
2088 return self.decod(hexdec(data), *args, **kwargs)
2092 """.. seealso:: :ref:`decoding`
2094 return self._expl is not None
2098 """.. seealso:: :ref:`decoding`
2103 def expl_tlen(self):
2104 """.. seealso:: :ref:`decoding`
2106 return len(self._expl)
2109 def expl_llen(self):
2110 """.. seealso:: :ref:`decoding`
2112 if self.expl_lenindef:
2114 return len(len_encode(self.tlvlen))
2117 def expl_offset(self):
2118 """.. seealso:: :ref:`decoding`
2120 return self.offset - self.expl_tlen - self.expl_llen
2123 def expl_vlen(self):
2124 """.. seealso:: :ref:`decoding`
2129 def expl_tlvlen(self):
2130 """.. seealso:: :ref:`decoding`
2132 return self.expl_tlen + self.expl_llen + self.expl_vlen
2135 def fulloffset(self):
2136 """.. seealso:: :ref:`decoding`
2138 return self.expl_offset if self.expled else self.offset
2142 """.. seealso:: :ref:`decoding`
2144 return self.expl_tlvlen if self.expled else self.tlvlen
2146 def pps_lenindef(self, decode_path):
2147 if self.lenindef and not (
2148 getattr(self, "defined", None) is not None and
2149 self.defined[1].lenindef
2152 asn1_type_name="EOC",
2154 decode_path=decode_path,
2156 self.offset + self.tlvlen -
2157 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
2165 if self.expl_lenindef:
2167 asn1_type_name="EOC",
2168 obj_name="EXPLICIT",
2169 decode_path=decode_path,
2170 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
2179 def encode_cer(obj):
2180 """Encode to CER in memory buffer
2182 :returns bytes: memory buffer contents
2185 obj.encode_cer(buf.write)
2186 return buf.getvalue()
2189 def encode2pass(obj):
2190 """Encode (2-pass mode) to DER in memory buffer
2192 :returns bytes: memory buffer contents
2195 _, state = obj.encode1st()
2196 obj.encode2nd(buf.write, iter(state))
2197 return buf.getvalue()
2200 class DecodePathDefBy:
2201 """DEFINED BY representation inside decode path
2203 __slots__ = ("defined_by",)
2205 def __init__(self, defined_by):
2206 self.defined_by = defined_by
2208 def __ne__(self, their):
2209 return not(self == their)
2211 def __eq__(self, their):
2212 if not isinstance(their, self.__class__):
2214 return self.defined_by == their.defined_by
2217 return "DEFINED BY " + str(self.defined_by)
2220 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
2223 ########################################################################
2225 ########################################################################
2227 PP = namedtuple("PP", (
2250 ), **NAMEDTUPLE_KWARGS)
2255 asn1_type_name="unknown",
2272 expl_lenindef=False,
2303 def _colourize(what, colour, with_colours, attrs=("bold",)):
2304 return colored(what, colour, attrs=attrs) if with_colours else what
2307 def colonize_hex(hexed):
2308 """Separate hexadecimal string with colons
2310 return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
2313 def find_oid_name(asn1_type_name, oid_maps, value):
2314 if len(oid_maps) > 0 and asn1_type_name == ObjectIdentifier.asn1_type_name:
2315 for oid_map in oid_maps:
2316 oid_name = oid_map.get(value)
2317 if oid_name is not None:
2328 with_decode_path=False,
2329 decode_path_len_decrease=0,
2336 " " if pp.expl_offset is None else
2337 ("-%d" % (pp.offset - pp.expl_offset))
2339 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
2341 col = _colourize(col, "red", with_colours, ())
2342 col += _colourize("B", "red", with_colours) if pp.bered else " "
2344 col = "[%d,%d,%4d]%s" % (
2345 pp.tlen, pp.llen, pp.vlen,
2346 LENINDEF_PP_CHAR if pp.lenindef else " "
2348 col = _colourize(col, "green", with_colours, ())
2350 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2351 if decode_path_len > 0:
2352 cols.append(" ." * decode_path_len)
2353 ent = pp.decode_path[-1]
2354 if isinstance(ent, DecodePathDefBy):
2355 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
2356 value = str(ent.defined_by)
2357 oid_name = find_oid_name(ent.defined_by.asn1_type_name, oid_maps, value)
2358 if oid_name is None:
2359 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
2361 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
2363 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
2364 if pp.expl is not None:
2365 klass, _, num = pp.expl
2366 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
2367 cols.append(_colourize(col, "blue", with_colours))
2368 if pp.impl is not None:
2369 klass, _, num = pp.impl
2370 col = "[%s%d]" % (TagClassReprs[klass], num)
2371 cols.append(_colourize(col, "blue", with_colours))
2372 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
2373 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
2375 cols.append(_colourize("BER", "red", with_colours))
2376 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
2377 if pp.value is not None:
2379 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
2380 oid_name = find_oid_name(pp.asn1_type_name, oid_maps, pp.value)
2381 if oid_name is not None:
2382 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
2383 if pp.asn1_type_name == Integer.asn1_type_name:
2384 cols.append(_colourize(
2385 "(%s)" % colonize_hex(pp.obj.tohex()), "green", with_colours,
2388 if pp.blob.__class__ == bytes:
2389 cols.append(hexenc(pp.blob))
2390 elif pp.blob.__class__ == tuple:
2391 cols.append(", ".join(pp.blob))
2393 cols.append(_colourize("OPTIONAL", "red", with_colours))
2395 cols.append(_colourize("DEFAULT", "red", with_colours))
2396 if with_decode_path:
2397 cols.append(_colourize(
2398 "[%s]" % ":".join(str(p) for p in pp.decode_path),
2402 return " ".join(cols)
2405 def pp_console_blob(pp, decode_path_len_decrease=0):
2406 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
2407 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2408 if decode_path_len > 0:
2409 cols.append(" ." * (decode_path_len + 1))
2410 if pp.blob.__class__ == bytes:
2411 blob = hexenc(pp.blob).upper()
2412 for i in range(0, len(blob), 32):
2413 chunk = blob[i:i + 32]
2414 yield " ".join(cols + [colonize_hex(chunk)])
2415 elif pp.blob.__class__ == tuple:
2416 yield " ".join(cols + [", ".join(pp.blob)])
2424 with_decode_path=False,
2425 decode_path_only=(),
2428 """Pretty print object
2430 :param Obj obj: object you want to pretty print
2431 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
2432 Its human readable form is printed when OID is met
2433 :param big_blobs: if large binary objects are met (like OctetString
2434 values), do we need to print them too, on separate
2436 :param with_colours: colourize output, if ``termcolor`` library
2438 :param with_decode_path: print decode path
2439 :param decode_path_only: print only that specified decode path
2441 def _pprint_pps(pps):
2443 if hasattr(pp, "_fields"):
2445 decode_path_only != () and
2447 str(p) for p in pp.decode_path[:len(decode_path_only)]
2448 ) != decode_path_only
2452 yield pp_console_row(
2457 with_colours=with_colours,
2458 with_decode_path=with_decode_path,
2459 decode_path_len_decrease=len(decode_path_only),
2461 for row in pp_console_blob(
2463 decode_path_len_decrease=len(decode_path_only),
2467 yield pp_console_row(
2472 with_colours=with_colours,
2473 with_decode_path=with_decode_path,
2474 decode_path_len_decrease=len(decode_path_only),
2477 for row in _pprint_pps(pp):
2479 return "\n".join(_pprint_pps(obj.pps(decode_path)))
2482 ########################################################################
2483 # ASN.1 primitive types
2484 ########################################################################
2486 BooleanState = namedtuple(
2488 BasicState._fields + ("value",),
2494 """``BOOLEAN`` boolean type
2496 >>> b = Boolean(True)
2498 >>> b == Boolean(True)
2504 tag_default = tag_encode(1)
2505 asn1_type_name = "BOOLEAN"
2517 :param value: set the value. Either boolean type, or
2518 :py:class:`pyderasn.Boolean` object
2519 :param bytes impl: override default tag with ``IMPLICIT`` one
2520 :param bytes expl: override default tag with ``EXPLICIT`` one
2521 :param default: set default value. Type same as in ``value``
2522 :param bool optional: is object ``OPTIONAL`` in sequence
2524 super().__init__(impl, expl, default, optional, _decoded)
2525 self._value = None if value is None else self._value_sanitize(value)
2526 if default is not None:
2527 default = self._value_sanitize(default)
2528 self.default = self.__class__(
2534 self._value = default
2536 def _value_sanitize(self, value):
2537 if value.__class__ == bool:
2539 if issubclass(value.__class__, Boolean):
2541 raise InvalidValueType((self.__class__, bool))
2545 return self._value is not None
2547 def __getstate__(self):
2548 return BooleanState(
2564 def __setstate__(self, state):
2565 super().__setstate__(state)
2566 self._value = state.value
2568 def __nonzero__(self):
2569 self._assert_ready()
2573 self._assert_ready()
2576 def __eq__(self, their):
2577 if their.__class__ == bool:
2578 return self._value == their
2579 if not issubclass(their.__class__, Boolean):
2582 self._value == their._value and
2583 self.tag == their.tag and
2584 self._expl == their._expl
2595 return self.__class__(
2597 impl=self.tag if impl is None else impl,
2598 expl=self._expl if expl is None else expl,
2599 default=self.default if default is None else default,
2600 optional=self.optional if optional is None else optional,
2604 self._assert_ready()
2605 return b"".join((self.tag, LEN1, (b"\xFF" if self._value else b"\x00")))
2607 def _encode1st(self, state):
2608 return len(self.tag) + 2, state
2610 def _encode2nd(self, writer, state_iter):
2611 self._assert_ready()
2612 write_full(writer, self._encode())
2614 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2616 t, _, lv = tag_strip(tlv)
2617 except DecodeError as err:
2618 raise err.__class__(
2620 klass=self.__class__,
2621 decode_path=decode_path,
2626 klass=self.__class__,
2627 decode_path=decode_path,
2634 l, _, v = len_decode(lv)
2635 except DecodeError as err:
2636 raise err.__class__(
2638 klass=self.__class__,
2639 decode_path=decode_path,
2643 raise InvalidLength(
2644 "Boolean's length must be equal to 1",
2645 klass=self.__class__,
2646 decode_path=decode_path,
2650 raise NotEnoughData(
2651 "encoded length is longer than data",
2652 klass=self.__class__,
2653 decode_path=decode_path,
2658 if first_octet == 0:
2660 elif first_octet == 0xFF:
2662 elif ctx.get("bered", False):
2667 "unacceptable Boolean value",
2668 klass=self.__class__,
2669 decode_path=decode_path,
2672 obj = self.__class__(
2676 default=self.default,
2677 optional=self.optional,
2678 _decoded=(offset, 1, 1),
2680 obj.ber_encoded = ber_encoded
2681 yield decode_path, obj, v[1:]
2684 return pp_console_row(next(self.pps()))
2686 def pps(self, decode_path=()):
2689 asn1_type_name=self.asn1_type_name,
2690 obj_name=self.__class__.__name__,
2691 decode_path=decode_path,
2692 value=str(self._value) if self.ready else None,
2693 optional=self.optional,
2694 default=self == self.default,
2695 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2696 expl=None if self._expl is None else tag_decode(self._expl),
2701 expl_offset=self.expl_offset if self.expled else None,
2702 expl_tlen=self.expl_tlen if self.expled else None,
2703 expl_llen=self.expl_llen if self.expled else None,
2704 expl_vlen=self.expl_vlen if self.expled else None,
2705 expl_lenindef=self.expl_lenindef,
2706 ber_encoded=self.ber_encoded,
2709 for pp in self.pps_lenindef(decode_path):
2713 IntegerState = namedtuple(
2715 BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2721 """``INTEGER`` integer type
2723 >>> b = Integer(-123)
2725 >>> b == Integer(-123)
2730 >>> Integer(2, bounds=(1, 3))
2732 >>> Integer(5, bounds=(1, 3))
2733 Traceback (most recent call last):
2734 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2738 class Version(Integer):
2745 >>> v = Version("v1")
2752 {'v3': 2, 'v1': 0, 'v2': 1}
2754 __slots__ = ("specs", "_bound_min", "_bound_max")
2755 tag_default = tag_encode(2)
2756 asn1_type_name = "INTEGER"
2770 :param value: set the value. Either integer type, named value
2771 (if ``schema`` is specified in the class), or
2772 :py:class:`pyderasn.Integer` object
2773 :param bounds: set ``(MIN, MAX)`` value constraint.
2774 (-inf, +inf) by default
2775 :param bytes impl: override default tag with ``IMPLICIT`` one
2776 :param bytes expl: override default tag with ``EXPLICIT`` one
2777 :param default: set default value. Type same as in ``value``
2778 :param bool optional: is object ``OPTIONAL`` in sequence
2780 super().__init__(impl, expl, default, optional, _decoded)
2782 specs = getattr(self, "schema", {}) if _specs is None else _specs
2783 self.specs = specs if specs.__class__ == dict else dict(specs)
2784 self._bound_min, self._bound_max = getattr(
2787 (float("-inf"), float("+inf")),
2788 ) if bounds is None else bounds
2789 if value is not None:
2790 self._value = self._value_sanitize(value)
2791 if default is not None:
2792 default = self._value_sanitize(default)
2793 self.default = self.__class__(
2799 if self._value is None:
2800 self._value = default
2802 def _value_sanitize(self, value):
2803 if isinstance(value, int):
2805 elif issubclass(value.__class__, Integer):
2806 value = value._value
2807 elif value.__class__ == str:
2808 value = self.specs.get(value)
2810 raise ObjUnknown("integer value: %s" % value)
2812 raise InvalidValueType((self.__class__, int, str))
2813 if not self._bound_min <= value <= self._bound_max:
2814 raise BoundsError(self._bound_min, value, self._bound_max)
2819 return self._value is not None
2821 def __getstate__(self):
2822 return IntegerState(
2841 def __setstate__(self, state):
2842 super().__setstate__(state)
2843 self.specs = state.specs
2844 self._value = state.value
2845 self._bound_min = state.bound_min
2846 self._bound_max = state.bound_max
2849 self._assert_ready()
2850 return int(self._value)
2853 """Hexadecimal representation
2855 Use :py:func:`pyderasn.colonize_hex` for colonizing it.
2857 hex_repr = hex(int(self))[2:].upper()
2858 if len(hex_repr) % 2 != 0:
2859 hex_repr = "0" + hex_repr
2863 self._assert_ready()
2864 return hash(b"".join((
2866 bytes(self._expl or b""),
2867 str(self._value).encode("ascii"),
2870 def __eq__(self, their):
2871 if isinstance(their, int):
2872 return self._value == their
2873 if not issubclass(their.__class__, Integer):
2876 self._value == their._value and
2877 self.tag == their.tag and
2878 self._expl == their._expl
2881 def __lt__(self, their):
2882 return self._value < their._value
2886 """Return named representation (if exists) of the value
2888 for name, value in self.specs.items():
2889 if value == self._value:
2902 return self.__class__(
2905 (self._bound_min, self._bound_max)
2906 if bounds is None else bounds
2908 impl=self.tag if impl is None else impl,
2909 expl=self._expl if expl is None else expl,
2910 default=self.default if default is None else default,
2911 optional=self.optional if optional is None else optional,
2915 def _encode_payload(self):
2916 self._assert_ready()
2918 bytes_len = ceil(value.bit_length() / 8) or 1
2921 octets = value.to_bytes(bytes_len, byteorder="big", signed=True)
2922 except OverflowError:
2929 octets = self._encode_payload()
2930 return b"".join((self.tag, len_encode(len(octets)), octets))
2932 def _encode1st(self, state):
2933 l = len(self._encode_payload())
2934 return len(self.tag) + len_size(l) + l, state
2936 def _encode2nd(self, writer, state_iter):
2937 write_full(writer, self._encode())
2939 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2941 t, _, lv = tag_strip(tlv)
2942 except DecodeError as err:
2943 raise err.__class__(
2945 klass=self.__class__,
2946 decode_path=decode_path,
2951 klass=self.__class__,
2952 decode_path=decode_path,
2959 l, llen, v = len_decode(lv)
2960 except DecodeError as err:
2961 raise err.__class__(
2963 klass=self.__class__,
2964 decode_path=decode_path,
2968 raise NotEnoughData(
2969 "encoded length is longer than data",
2970 klass=self.__class__,
2971 decode_path=decode_path,
2975 raise NotEnoughData(
2977 klass=self.__class__,
2978 decode_path=decode_path,
2981 v, tail = v[:l], v[l:]
2986 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2987 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2990 "non normalized integer",
2991 klass=self.__class__,
2992 decode_path=decode_path,
2995 value = int.from_bytes(v, byteorder="big", signed=True)
2997 obj = self.__class__(
2999 bounds=(self._bound_min, self._bound_max),
3002 default=self.default,
3003 optional=self.optional,
3005 _decoded=(offset, llen, l),
3007 except BoundsError as err:
3010 klass=self.__class__,
3011 decode_path=decode_path,
3014 yield decode_path, obj, tail
3017 return pp_console_row(next(self.pps()))
3019 def pps(self, decode_path=()):
3022 asn1_type_name=self.asn1_type_name,
3023 obj_name=self.__class__.__name__,
3024 decode_path=decode_path,
3025 value=(self.named or str(self._value)) if self.ready else None,
3026 optional=self.optional,
3027 default=self == self.default,
3028 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3029 expl=None if self._expl is None else tag_decode(self._expl),
3034 expl_offset=self.expl_offset if self.expled else None,
3035 expl_tlen=self.expl_tlen if self.expled else None,
3036 expl_llen=self.expl_llen if self.expled else None,
3037 expl_vlen=self.expl_vlen if self.expled else None,
3038 expl_lenindef=self.expl_lenindef,
3041 for pp in self.pps_lenindef(decode_path):
3045 BitStringState = namedtuple(
3047 BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
3052 class BitString(Obj):
3053 """``BIT STRING`` bit string type
3055 >>> BitString(b"hello world")
3056 BIT STRING 88 bits 68656c6c6f20776f726c64
3059 >>> b == b"hello world"
3064 >>> BitString("'0A3B5F291CD'H")
3065 BIT STRING 44 bits 0a3b5f291cd0
3066 >>> b = BitString("'010110000000'B")
3067 BIT STRING 12 bits 5800
3070 >>> b[0], b[1], b[2], b[3]
3071 (False, True, False, True)
3075 [False, True, False, True, True, False, False, False, False, False, False, False]
3079 class KeyUsage(BitString):
3081 ("digitalSignature", 0),
3082 ("nonRepudiation", 1),
3083 ("keyEncipherment", 2),
3086 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
3087 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
3089 ['nonRepudiation', 'keyEncipherment']
3091 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
3095 Pay attention that BIT STRING can be encoded both in primitive
3096 and constructed forms. Decoder always checks constructed form tag
3097 additionally to specified primitive one. If BER decoding is
3098 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
3099 of DER restrictions.
3101 __slots__ = ("tag_constructed", "specs", "defined")
3102 tag_default = tag_encode(3)
3103 asn1_type_name = "BIT STRING"
3116 :param value: set the value. Either binary type, tuple of named
3117 values (if ``schema`` is specified in the class),
3118 string in ``'XXX...'B`` form, or
3119 :py:class:`pyderasn.BitString` object
3120 :param bytes impl: override default tag with ``IMPLICIT`` one
3121 :param bytes expl: override default tag with ``EXPLICIT`` one
3122 :param default: set default value. Type same as in ``value``
3123 :param bool optional: is object ``OPTIONAL`` in sequence
3125 super().__init__(impl, expl, default, optional, _decoded)
3126 specs = getattr(self, "schema", {}) if _specs is None else _specs
3127 self.specs = specs if specs.__class__ == dict else dict(specs)
3128 self._value = None if value is None else self._value_sanitize(value)
3129 if default is not None:
3130 default = self._value_sanitize(default)
3131 self.default = self.__class__(
3137 self._value = default
3139 tag_klass, _, tag_num = tag_decode(self.tag)
3140 self.tag_constructed = tag_encode(
3142 form=TagFormConstructed,
3146 def _bits2octets(self, bits):
3147 if len(self.specs) > 0:
3148 bits = bits.rstrip("0")
3150 bits += "0" * ((8 - (bit_len % 8)) % 8)
3151 octets = bytearray(len(bits) // 8)
3152 for i in range(len(octets)):
3153 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
3154 return bit_len, bytes(octets)
3156 def _value_sanitize(self, value):
3157 if isinstance(value, (str, bytes)):
3159 isinstance(value, str) and
3160 value.startswith("'")
3162 if value.endswith("'B"):
3164 if not frozenset(value) <= SET01:
3165 raise ValueError("B's coding contains unacceptable chars")
3166 return self._bits2octets(value)
3167 if value.endswith("'H"):
3171 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
3173 if value.__class__ == bytes:
3174 return (len(value) * 8, value)
3175 raise InvalidValueType((self.__class__, str, bytes))
3176 if value.__class__ == tuple:
3179 isinstance(value[0], int) and
3180 value[1].__class__ == bytes
3185 bit = self.specs.get(name)
3187 raise ObjUnknown("BitString value: %s" % name)
3190 return self._bits2octets("")
3191 bits = frozenset(bits)
3192 return self._bits2octets("".join(
3193 ("1" if bit in bits else "0")
3194 for bit in range(max(bits) + 1)
3196 if issubclass(value.__class__, BitString):
3198 raise InvalidValueType((self.__class__, bytes, str))
3202 return self._value is not None
3204 def __getstate__(self):
3205 return BitStringState(
3220 self.tag_constructed,
3224 def __setstate__(self, state):
3225 super().__setstate__(state)
3226 self.specs = state.specs
3227 self._value = state.value
3228 self.tag_constructed = state.tag_constructed
3229 self.defined = state.defined
3232 self._assert_ready()
3233 for i in range(self._value[0]):
3238 """Returns number of bits in the string
3240 self._assert_ready()
3241 return self._value[0]
3243 def __bytes__(self):
3244 self._assert_ready()
3245 return self._value[1]
3247 def __eq__(self, their):
3248 if their.__class__ == bytes:
3249 return self._value[1] == their
3250 if not issubclass(their.__class__, BitString):
3253 self._value == their._value and
3254 self.tag == their.tag and
3255 self._expl == their._expl
3260 """Named representation (if exists) of the bits
3262 :returns: [str(name), ...]
3264 return [name for name, bit in self.specs.items() if self[bit]]
3274 return self.__class__(
3276 impl=self.tag if impl is None else impl,
3277 expl=self._expl if expl is None else expl,
3278 default=self.default if default is None else default,
3279 optional=self.optional if optional is None else optional,
3283 def __getitem__(self, key):
3284 if key.__class__ == int:
3285 bit_len, octets = self._value
3288 return memoryview(octets)[key // 8] >> (7 - (key % 8)) & 1 == 1
3289 if isinstance(key, str):
3290 value = self.specs.get(key)
3292 raise ObjUnknown("BitString value: %s" % key)
3294 raise InvalidValueType((int, str))
3297 self._assert_ready()
3298 bit_len, octets = self._value
3301 len_encode(len(octets) + 1),
3302 int2byte((8 - bit_len % 8) % 8),
3306 def _encode1st(self, state):
3307 self._assert_ready()
3308 _, octets = self._value
3310 return len(self.tag) + len_size(l) + l, state
3312 def _encode2nd(self, writer, state_iter):
3313 bit_len, octets = self._value
3314 write_full(writer, b"".join((
3316 len_encode(len(octets) + 1),
3317 int2byte((8 - bit_len % 8) % 8),
3319 write_full(writer, octets)
3321 def _encode_cer(self, writer):
3322 bit_len, octets = self._value
3323 if len(octets) + 1 <= 1000:
3324 write_full(writer, self._encode())
3326 write_full(writer, self.tag_constructed)
3327 write_full(writer, LENINDEF)
3328 for offset in range(0, (len(octets) // 999) * 999, 999):
3329 write_full(writer, b"".join((
3330 BitString.tag_default,
3333 octets[offset:offset + 999],
3335 tail = octets[offset + 999:]
3337 tail = int2byte((8 - bit_len % 8) % 8) + tail
3338 write_full(writer, b"".join((
3339 BitString.tag_default,
3340 len_encode(len(tail)),
3343 write_full(writer, EOC)
3345 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3347 t, tlen, lv = tag_strip(tlv)
3348 except DecodeError as err:
3349 raise err.__class__(
3351 klass=self.__class__,
3352 decode_path=decode_path,
3356 if tag_only: # pragma: no cover
3360 l, llen, v = len_decode(lv)
3361 except DecodeError as err:
3362 raise err.__class__(
3364 klass=self.__class__,
3365 decode_path=decode_path,
3369 raise NotEnoughData(
3370 "encoded length is longer than data",
3371 klass=self.__class__,
3372 decode_path=decode_path,
3376 raise NotEnoughData(
3378 klass=self.__class__,
3379 decode_path=decode_path,
3383 if l == 1 and pad_size != 0:
3385 "invalid empty value",
3386 klass=self.__class__,
3387 decode_path=decode_path,
3393 klass=self.__class__,
3394 decode_path=decode_path,
3397 if v[l - 1] & ((1 << pad_size) - 1) != 0:
3400 klass=self.__class__,
3401 decode_path=decode_path,
3404 v, tail = v[:l], v[l:]
3405 bit_len = (len(v) - 1) * 8 - pad_size
3406 obj = self.__class__(
3407 value=None if evgen_mode else (bit_len, v[1:].tobytes()),
3410 default=self.default,
3411 optional=self.optional,
3413 _decoded=(offset, llen, l),
3416 obj._value = (bit_len, None)
3417 yield decode_path, obj, tail
3419 if t != self.tag_constructed:
3421 klass=self.__class__,
3422 decode_path=decode_path,
3425 if not ctx.get("bered", False):
3427 "unallowed BER constructed encoding",
3428 klass=self.__class__,
3429 decode_path=decode_path,
3432 if tag_only: # pragma: no cover
3437 l, llen, v = len_decode(lv)
3438 except LenIndefForm:
3439 llen, l, v = 1, 0, lv[1:]
3441 except DecodeError as err:
3442 raise err.__class__(
3444 klass=self.__class__,
3445 decode_path=decode_path,
3449 raise NotEnoughData(
3450 "encoded length is longer than data",
3451 klass=self.__class__,
3452 decode_path=decode_path,
3455 if not lenindef and l == 0:
3456 raise NotEnoughData(
3458 klass=self.__class__,
3459 decode_path=decode_path,
3463 sub_offset = offset + tlen + llen
3467 if v[:EOC_LEN].tobytes() == EOC:
3474 "chunk out of bounds",
3475 klass=self.__class__,
3476 decode_path=decode_path + (str(len(chunks) - 1),),
3477 offset=chunks[-1].offset,
3479 sub_decode_path = decode_path + (str(len(chunks)),)
3482 for _decode_path, chunk, v_tail in BitString().decode_evgen(
3485 decode_path=sub_decode_path,
3488 _ctx_immutable=False,
3490 yield _decode_path, chunk, v_tail
3492 _, chunk, v_tail = next(BitString().decode_evgen(
3495 decode_path=sub_decode_path,
3498 _ctx_immutable=False,
3503 "expected BitString encoded chunk",
3504 klass=self.__class__,
3505 decode_path=sub_decode_path,
3508 chunks.append(chunk)
3509 sub_offset += chunk.tlvlen
3510 vlen += chunk.tlvlen
3512 if len(chunks) == 0:
3515 klass=self.__class__,
3516 decode_path=decode_path,
3521 for chunk_i, chunk in enumerate(chunks[:-1]):
3522 if chunk.bit_len % 8 != 0:
3524 "BitString chunk is not multiple of 8 bits",
3525 klass=self.__class__,
3526 decode_path=decode_path + (str(chunk_i),),
3527 offset=chunk.offset,
3530 values.append(bytes(chunk))
3531 bit_len += chunk.bit_len
3532 chunk_last = chunks[-1]
3534 values.append(bytes(chunk_last))
3535 bit_len += chunk_last.bit_len
3536 obj = self.__class__(
3537 value=None if evgen_mode else (bit_len, b"".join(values)),
3540 default=self.default,
3541 optional=self.optional,
3543 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3546 obj._value = (bit_len, None)
3547 obj.lenindef = lenindef
3548 obj.ber_encoded = True
3549 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
3552 return pp_console_row(next(self.pps()))
3554 def pps(self, decode_path=()):
3558 bit_len, blob = self._value
3559 value = "%d bits" % bit_len
3560 if len(self.specs) > 0 and blob is not None:
3561 blob = tuple(self.named)
3564 asn1_type_name=self.asn1_type_name,
3565 obj_name=self.__class__.__name__,
3566 decode_path=decode_path,
3569 optional=self.optional,
3570 default=self == self.default,
3571 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3572 expl=None if self._expl is None else tag_decode(self._expl),
3577 expl_offset=self.expl_offset if self.expled else None,
3578 expl_tlen=self.expl_tlen if self.expled else None,
3579 expl_llen=self.expl_llen if self.expled else None,
3580 expl_vlen=self.expl_vlen if self.expled else None,
3581 expl_lenindef=self.expl_lenindef,
3582 lenindef=self.lenindef,
3583 ber_encoded=self.ber_encoded,
3586 defined_by, defined = self.defined or (None, None)
3587 if defined_by is not None:
3589 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3591 for pp in self.pps_lenindef(decode_path):
3595 OctetStringState = namedtuple(
3597 BasicState._fields + (
3608 class OctetString(Obj):
3609 """``OCTET STRING`` binary string type
3611 >>> s = OctetString(b"hello world")
3612 OCTET STRING 11 bytes 68656c6c6f20776f726c64
3613 >>> s == OctetString(b"hello world")
3618 >>> OctetString(b"hello", bounds=(4, 4))
3619 Traceback (most recent call last):
3620 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
3621 >>> OctetString(b"hell", bounds=(4, 4))
3622 OCTET STRING 4 bytes 68656c6c
3624 Memoryviews can be used as a values. If memoryview is made on
3625 mmap-ed file, then it does not take storage inside OctetString
3626 itself. In CER encoding mode it will be streamed to the specified
3627 writer, copying 1 KB chunks.
3629 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
3630 tag_default = tag_encode(4)
3631 asn1_type_name = "OCTET STRING"
3632 evgen_mode_skip_value = True
3646 :param value: set the value. Either binary type, or
3647 :py:class:`pyderasn.OctetString` object
3648 :param bounds: set ``(MIN, MAX)`` value size constraint.
3649 (-inf, +inf) by default
3650 :param bytes impl: override default tag with ``IMPLICIT`` one
3651 :param bytes expl: override default tag with ``EXPLICIT`` one
3652 :param default: set default value. Type same as in ``value``
3653 :param bool optional: is object ``OPTIONAL`` in sequence
3655 super().__init__(impl, expl, default, optional, _decoded)
3657 self._bound_min, self._bound_max = getattr(
3661 ) if bounds is None else bounds
3662 if value is not None:
3663 self._value = self._value_sanitize(value)
3664 if default is not None:
3665 default = self._value_sanitize(default)
3666 self.default = self.__class__(
3671 if self._value is None:
3672 self._value = default
3674 tag_klass, _, tag_num = tag_decode(self.tag)
3675 self.tag_constructed = tag_encode(
3677 form=TagFormConstructed,
3681 def _value_sanitize(self, value):
3682 if value.__class__ == bytes or value.__class__ == memoryview:
3684 elif issubclass(value.__class__, OctetString):
3685 value = value._value
3687 raise InvalidValueType((self.__class__, bytes, memoryview))
3688 if not self._bound_min <= len(value) <= self._bound_max:
3689 raise BoundsError(self._bound_min, len(value), self._bound_max)
3694 return self._value is not None
3696 def __getstate__(self):
3697 return OctetStringState(
3713 self.tag_constructed,
3717 def __setstate__(self, state):
3718 super().__setstate__(state)
3719 self._value = state.value
3720 self._bound_min = state.bound_min
3721 self._bound_max = state.bound_max
3722 self.tag_constructed = state.tag_constructed
3723 self.defined = state.defined
3725 def __bytes__(self):
3726 self._assert_ready()
3727 return bytes(self._value)
3729 def __eq__(self, their):
3730 if their.__class__ == bytes:
3731 return self._value == their
3732 if not issubclass(their.__class__, OctetString):
3735 self._value == their._value and
3736 self.tag == their.tag and
3737 self._expl == their._expl
3740 def __lt__(self, their):
3741 return self._value < their._value
3752 return self.__class__(
3755 (self._bound_min, self._bound_max)
3756 if bounds is None else bounds
3758 impl=self.tag if impl is None else impl,
3759 expl=self._expl if expl is None else expl,
3760 default=self.default if default is None else default,
3761 optional=self.optional if optional is None else optional,
3765 self._assert_ready()
3768 len_encode(len(self._value)),
3772 def _encode1st(self, state):
3773 self._assert_ready()
3774 l = len(self._value)
3775 return len(self.tag) + len_size(l) + l, state
3777 def _encode2nd(self, writer, state_iter):
3779 write_full(writer, self.tag + len_encode(len(value)))
3780 write_full(writer, value)
3782 def _encode_cer(self, writer):
3783 octets = self._value
3784 if len(octets) <= 1000:
3785 write_full(writer, self._encode())
3787 write_full(writer, self.tag_constructed)
3788 write_full(writer, LENINDEF)
3789 for offset in range(0, (len(octets) // 1000) * 1000, 1000):
3790 write_full(writer, b"".join((
3791 OctetString.tag_default,
3793 octets[offset:offset + 1000],
3795 tail = octets[offset + 1000:]
3797 write_full(writer, b"".join((
3798 OctetString.tag_default,
3799 len_encode(len(tail)),
3802 write_full(writer, EOC)
3804 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3806 t, tlen, lv = tag_strip(tlv)
3807 except DecodeError as err:
3808 raise err.__class__(
3810 klass=self.__class__,
3811 decode_path=decode_path,
3819 l, llen, v = len_decode(lv)
3820 except DecodeError as err:
3821 raise err.__class__(
3823 klass=self.__class__,
3824 decode_path=decode_path,
3828 raise NotEnoughData(
3829 "encoded length is longer than data",
3830 klass=self.__class__,
3831 decode_path=decode_path,
3834 v, tail = v[:l], v[l:]
3835 if evgen_mode and not self._bound_min <= len(v) <= self._bound_max:
3837 msg=str(BoundsError(self._bound_min, len(v), self._bound_max)),
3838 klass=self.__class__,
3839 decode_path=decode_path,
3843 obj = self.__class__(
3845 None if (evgen_mode and self.evgen_mode_skip_value)
3848 bounds=(self._bound_min, self._bound_max),
3851 default=self.default,
3852 optional=self.optional,
3853 _decoded=(offset, llen, l),
3856 except DecodeError as err:
3859 klass=self.__class__,
3860 decode_path=decode_path,
3863 except BoundsError as err:
3866 klass=self.__class__,
3867 decode_path=decode_path,
3870 yield decode_path, obj, tail
3872 if t != self.tag_constructed:
3874 klass=self.__class__,
3875 decode_path=decode_path,
3878 if not ctx.get("bered", False):
3880 "unallowed BER constructed encoding",
3881 klass=self.__class__,
3882 decode_path=decode_path,
3890 l, llen, v = len_decode(lv)
3891 except LenIndefForm:
3892 llen, l, v = 1, 0, lv[1:]
3894 except DecodeError as err:
3895 raise err.__class__(
3897 klass=self.__class__,
3898 decode_path=decode_path,
3902 raise NotEnoughData(
3903 "encoded length is longer than data",
3904 klass=self.__class__,
3905 decode_path=decode_path,
3910 sub_offset = offset + tlen + llen
3915 if v[:EOC_LEN].tobytes() == EOC:
3922 "chunk out of bounds",
3923 klass=self.__class__,
3924 decode_path=decode_path + (str(len(chunks) - 1),),
3925 offset=chunks[-1].offset,
3929 sub_decode_path = decode_path + (str(chunks_count),)
3930 for _decode_path, chunk, v_tail in OctetString().decode_evgen(
3933 decode_path=sub_decode_path,
3936 _ctx_immutable=False,
3938 yield _decode_path, chunk, v_tail
3939 if not chunk.ber_encoded:
3940 payload_len += chunk.vlen
3943 sub_decode_path = decode_path + (str(len(chunks)),)
3944 _, chunk, v_tail = next(OctetString().decode_evgen(
3947 decode_path=sub_decode_path,
3950 _ctx_immutable=False,
3953 chunks.append(chunk)
3956 "expected OctetString encoded chunk",
3957 klass=self.__class__,
3958 decode_path=sub_decode_path,
3961 sub_offset += chunk.tlvlen
3962 vlen += chunk.tlvlen
3964 if evgen_mode and not self._bound_min <= payload_len <= self._bound_max:
3966 msg=str(BoundsError(self._bound_min, payload_len, self._bound_max)),
3967 klass=self.__class__,
3968 decode_path=decode_path,
3972 obj = self.__class__(
3974 None if evgen_mode else
3975 b"".join(bytes(chunk) for chunk in chunks)
3977 bounds=(self._bound_min, self._bound_max),
3980 default=self.default,
3981 optional=self.optional,
3982 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3985 except DecodeError as err:
3988 klass=self.__class__,
3989 decode_path=decode_path,
3992 except BoundsError as err:
3995 klass=self.__class__,
3996 decode_path=decode_path,
3999 obj.lenindef = lenindef
4000 obj.ber_encoded = True
4001 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
4004 return pp_console_row(next(self.pps()))
4006 def pps(self, decode_path=()):
4009 asn1_type_name=self.asn1_type_name,
4010 obj_name=self.__class__.__name__,
4011 decode_path=decode_path,
4012 value=("%d bytes" % len(self._value)) if self.ready else None,
4013 blob=self._value if self.ready else None,
4014 optional=self.optional,
4015 default=self == self.default,
4016 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4017 expl=None if self._expl is None else tag_decode(self._expl),
4022 expl_offset=self.expl_offset if self.expled else None,
4023 expl_tlen=self.expl_tlen if self.expled else None,
4024 expl_llen=self.expl_llen if self.expled else None,
4025 expl_vlen=self.expl_vlen if self.expled else None,
4026 expl_lenindef=self.expl_lenindef,
4027 lenindef=self.lenindef,
4028 ber_encoded=self.ber_encoded,
4031 defined_by, defined = self.defined or (None, None)
4032 if defined_by is not None:
4034 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4036 for pp in self.pps_lenindef(decode_path):
4040 def agg_octet_string(evgens, decode_path, raw, writer):
4041 """Aggregate constructed string (OctetString and its derivatives)
4043 :param evgens: iterator of generated events
4044 :param decode_path: points to the string we want to decode
4045 :param raw: slicebable (memoryview, bytearray, etc) with
4046 the data evgens are generated on
4047 :param writer: buffer.write where string is going to be saved
4048 :param writer: where string is going to be saved. Must comply
4049 with ``io.RawIOBase.write`` behaviour
4051 .. seealso:: :ref:`agg_octet_string`
4053 decode_path_len = len(decode_path)
4054 for dp, obj, _ in evgens:
4055 if dp[:decode_path_len] != decode_path:
4057 if not obj.ber_encoded:
4058 write_full(writer, raw[
4059 obj.offset + obj.tlen + obj.llen:
4060 obj.offset + obj.tlen + obj.llen + obj.vlen -
4061 (EOC_LEN if obj.expl_lenindef else 0)
4063 if len(dp) == decode_path_len:
4067 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
4071 """``NULL`` null object
4079 tag_default = tag_encode(5)
4080 asn1_type_name = "NULL"
4084 value=None, # unused, but Sequence passes it
4091 :param bytes impl: override default tag with ``IMPLICIT`` one
4092 :param bytes expl: override default tag with ``EXPLICIT`` one
4093 :param bool optional: is object ``OPTIONAL`` in sequence
4095 super().__init__(impl, expl, None, optional, _decoded)
4102 def __getstate__(self):
4118 def __eq__(self, their):
4119 if not issubclass(their.__class__, Null):
4122 self.tag == their.tag and
4123 self._expl == their._expl
4133 return self.__class__(
4134 impl=self.tag if impl is None else impl,
4135 expl=self._expl if expl is None else expl,
4136 optional=self.optional if optional is None else optional,
4140 return self.tag + LEN0
4142 def _encode1st(self, state):
4143 return len(self.tag) + 1, state
4145 def _encode2nd(self, writer, state_iter):
4146 write_full(writer, self.tag + LEN0)
4148 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4150 t, _, lv = tag_strip(tlv)
4151 except DecodeError as err:
4152 raise err.__class__(
4154 klass=self.__class__,
4155 decode_path=decode_path,
4160 klass=self.__class__,
4161 decode_path=decode_path,
4164 if tag_only: # pragma: no cover
4168 l, _, v = len_decode(lv)
4169 except DecodeError as err:
4170 raise err.__class__(
4172 klass=self.__class__,
4173 decode_path=decode_path,
4177 raise InvalidLength(
4178 "Null must have zero length",
4179 klass=self.__class__,
4180 decode_path=decode_path,
4183 obj = self.__class__(
4186 optional=self.optional,
4187 _decoded=(offset, 1, 0),
4189 yield decode_path, obj, v
4192 return pp_console_row(next(self.pps()))
4194 def pps(self, decode_path=()):
4197 asn1_type_name=self.asn1_type_name,
4198 obj_name=self.__class__.__name__,
4199 decode_path=decode_path,
4200 optional=self.optional,
4201 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4202 expl=None if self._expl is None else tag_decode(self._expl),
4207 expl_offset=self.expl_offset if self.expled else None,
4208 expl_tlen=self.expl_tlen if self.expled else None,
4209 expl_llen=self.expl_llen if self.expled else None,
4210 expl_vlen=self.expl_vlen if self.expled else None,
4211 expl_lenindef=self.expl_lenindef,
4214 for pp in self.pps_lenindef(decode_path):
4218 ObjectIdentifierState = namedtuple(
4219 "ObjectIdentifierState",
4220 BasicState._fields + ("value", "defines"),
4225 class ObjectIdentifier(Obj):
4226 """``OBJECT IDENTIFIER`` OID type
4228 >>> oid = ObjectIdentifier((1, 2, 3))
4229 OBJECT IDENTIFIER 1.2.3
4230 >>> oid == ObjectIdentifier("1.2.3")
4236 >>> oid + (4, 5) + ObjectIdentifier("1.7")
4237 OBJECT IDENTIFIER 1.2.3.4.5.1.7
4239 >>> str(ObjectIdentifier((3, 1)))
4240 Traceback (most recent call last):
4241 pyderasn.InvalidOID: unacceptable first arc value
4243 __slots__ = ("defines",)
4244 tag_default = tag_encode(6)
4245 asn1_type_name = "OBJECT IDENTIFIER"
4258 :param value: set the value. Either tuples of integers,
4259 string of "."-concatenated integers, or
4260 :py:class:`pyderasn.ObjectIdentifier` object
4261 :param defines: sequence of tuples. Each tuple has two elements.
4262 First one is relative to current one decode
4263 path, aiming to the field defined by that OID.
4264 Read about relative path in
4265 :py:func:`pyderasn.abs_decode_path`. Second
4266 tuple element is ``{OID: pyderasn.Obj()}``
4267 dictionary, mapping between current OID value
4268 and structure applied to defined field.
4270 .. seealso:: :ref:`definedby`
4272 :param bytes impl: override default tag with ``IMPLICIT`` one
4273 :param bytes expl: override default tag with ``EXPLICIT`` one
4274 :param default: set default value. Type same as in ``value``
4275 :param bool optional: is object ``OPTIONAL`` in sequence
4277 super().__init__(impl, expl, default, optional, _decoded)
4279 if value is not None:
4280 self._value = self._value_sanitize(value)
4281 if default is not None:
4282 default = self._value_sanitize(default)
4283 self.default = self.__class__(
4288 if self._value is None:
4289 self._value = default
4290 self.defines = defines
4292 def __add__(self, their):
4293 if their.__class__ == tuple:
4294 return self.__class__(self._value + array("L", their))
4295 if isinstance(their, self.__class__):
4296 return self.__class__(self._value + their._value)
4297 raise InvalidValueType((self.__class__, tuple))
4299 def _value_sanitize(self, value):
4300 if issubclass(value.__class__, ObjectIdentifier):
4302 if isinstance(value, str):
4304 value = array("L", (pureint(arc) for arc in value.split(".")))
4306 raise InvalidOID("unacceptable arcs values")
4307 if value.__class__ == tuple:
4309 value = array("L", value)
4310 except OverflowError as err:
4311 raise InvalidOID(repr(err))
4312 if value.__class__ is array:
4314 raise InvalidOID("less than 2 arcs")
4315 first_arc = value[0]
4316 if first_arc in (0, 1):
4317 if not (0 <= value[1] <= 39):
4318 raise InvalidOID("second arc is too wide")
4319 elif first_arc == 2:
4322 raise InvalidOID("unacceptable first arc value")
4323 if not all(arc >= 0 for arc in value):
4324 raise InvalidOID("negative arc value")
4326 raise InvalidValueType((self.__class__, str, tuple))
4330 return self._value is not None
4332 def __getstate__(self):
4333 return ObjectIdentifierState(
4350 def __setstate__(self, state):
4351 super().__setstate__(state)
4352 self._value = state.value
4353 self.defines = state.defines
4356 self._assert_ready()
4357 return iter(self._value)
4360 return ".".join(str(arc) for arc in self._value or ())
4363 self._assert_ready()
4364 return hash(b"".join((
4366 bytes(self._expl or b""),
4367 str(self._value).encode("ascii"),
4370 def __eq__(self, their):
4371 if their.__class__ == tuple:
4372 return self._value == array("L", their)
4373 if not issubclass(their.__class__, ObjectIdentifier):
4376 self.tag == their.tag and
4377 self._expl == their._expl and
4378 self._value == their._value
4381 def __lt__(self, their):
4382 return self._value < their._value
4393 return self.__class__(
4395 defines=self.defines if defines is None else defines,
4396 impl=self.tag if impl is None else impl,
4397 expl=self._expl if expl is None else expl,
4398 default=self.default if default is None else default,
4399 optional=self.optional if optional is None else optional,
4402 def _encode_octets(self):
4403 self._assert_ready()
4405 first_value = value[1]
4406 first_arc = value[0]
4409 elif first_arc == 1:
4411 elif first_arc == 2:
4413 else: # pragma: no cover
4414 raise RuntimeError("invalid arc is stored")
4415 octets = [zero_ended_encode(first_value)]
4416 for arc in value[2:]:
4417 octets.append(zero_ended_encode(arc))
4418 return b"".join(octets)
4421 v = self._encode_octets()
4422 return b"".join((self.tag, len_encode(len(v)), v))
4424 def _encode1st(self, state):
4425 l = len(self._encode_octets())
4426 return len(self.tag) + len_size(l) + l, state
4428 def _encode2nd(self, writer, state_iter):
4429 write_full(writer, self._encode())
4431 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4433 t, _, lv = tag_strip(tlv)
4434 except DecodeError as err:
4435 raise err.__class__(
4437 klass=self.__class__,
4438 decode_path=decode_path,
4443 klass=self.__class__,
4444 decode_path=decode_path,
4447 if tag_only: # pragma: no cover
4451 l, llen, v = len_decode(lv)
4452 except DecodeError as err:
4453 raise err.__class__(
4455 klass=self.__class__,
4456 decode_path=decode_path,
4460 raise NotEnoughData(
4461 "encoded length is longer than data",
4462 klass=self.__class__,
4463 decode_path=decode_path,
4467 raise NotEnoughData(
4469 klass=self.__class__,
4470 decode_path=decode_path,
4473 v, tail = v[:l], v[l:]
4481 if i == 0 and octet == 0x80:
4482 if ctx.get("bered", False):
4486 "non normalized arc encoding",
4487 klass=self.__class__,
4488 decode_path=decode_path,
4491 arc = (arc << 7) | (octet & 0x7F)
4492 if octet & 0x80 == 0:
4495 except OverflowError:
4497 "too huge value for local unsigned long",
4498 klass=self.__class__,
4499 decode_path=decode_path,
4508 klass=self.__class__,
4509 decode_path=decode_path,
4513 second_arc = arcs[0]
4514 if 0 <= second_arc <= 39:
4516 elif 40 <= second_arc <= 79:
4522 obj = self.__class__(
4523 value=array("L", (first_arc, second_arc)) + arcs[1:],
4526 default=self.default,
4527 optional=self.optional,
4528 _decoded=(offset, llen, l),
4531 obj.ber_encoded = True
4532 yield decode_path, obj, tail
4535 return pp_console_row(next(self.pps()))
4537 def pps(self, decode_path=()):
4540 asn1_type_name=self.asn1_type_name,
4541 obj_name=self.__class__.__name__,
4542 decode_path=decode_path,
4543 value=str(self) if self.ready else None,
4544 optional=self.optional,
4545 default=self == self.default,
4546 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4547 expl=None if self._expl is None else tag_decode(self._expl),
4552 expl_offset=self.expl_offset if self.expled else None,
4553 expl_tlen=self.expl_tlen if self.expled else None,
4554 expl_llen=self.expl_llen if self.expled else None,
4555 expl_vlen=self.expl_vlen if self.expled else None,
4556 expl_lenindef=self.expl_lenindef,
4557 ber_encoded=self.ber_encoded,
4560 for pp in self.pps_lenindef(decode_path):
4564 class Enumerated(Integer):
4565 """``ENUMERATED`` integer type
4567 This type is identical to :py:class:`pyderasn.Integer`, but requires
4568 schema to be specified and does not accept values missing from it.
4571 tag_default = tag_encode(10)
4572 asn1_type_name = "ENUMERATED"
4583 bounds=None, # dummy argument, workability for Integer.decode
4586 value, bounds, impl, expl, default, optional, _specs, _decoded,
4588 if len(self.specs) == 0:
4589 raise ValueError("schema must be specified")
4591 def _value_sanitize(self, value):
4592 if isinstance(value, self.__class__):
4593 value = value._value
4594 elif isinstance(value, int):
4595 for _value in self.specs.values():
4600 "unknown integer value: %s" % value,
4601 klass=self.__class__,
4603 elif isinstance(value, str):
4604 value = self.specs.get(value)
4606 raise ObjUnknown("integer value: %s" % value)
4608 raise InvalidValueType((self.__class__, int, str))
4620 return self.__class__(
4622 impl=self.tag if impl is None else impl,
4623 expl=self._expl if expl is None else expl,
4624 default=self.default if default is None else default,
4625 optional=self.optional if optional is None else optional,
4630 def escape_control_unicode(c):
4631 if unicat(c)[0] == "C":
4632 c = repr(c).lstrip("u").strip("'")
4636 class CommonString(OctetString):
4637 """Common class for all strings
4639 Everything resembles :py:class:`pyderasn.OctetString`, except
4640 ability to deal with unicode text strings.
4642 >>> hexenc("привет мир".encode("utf-8"))
4643 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4644 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
4646 >>> s = UTF8String("привет мир")
4647 UTF8String UTF8String привет мир
4649 'привет мир'
4650 >>> hexenc(bytes(s))
4651 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4653 >>> PrintableString("привет мир")
4654 Traceback (most recent call last):
4655 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
4657 >>> BMPString("ада", bounds=(2, 2))
4658 Traceback (most recent call last):
4659 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
4660 >>> s = BMPString("ад", bounds=(2, 2))
4663 >>> hexenc(bytes(s))
4670 - Text Encoding, validation
4671 * - :py:class:`pyderasn.UTF8String`
4673 * - :py:class:`pyderasn.NumericString`
4674 - proper alphabet validation
4675 * - :py:class:`pyderasn.PrintableString`
4676 - proper alphabet validation
4677 * - :py:class:`pyderasn.TeletexString`
4679 * - :py:class:`pyderasn.T61String`
4681 * - :py:class:`pyderasn.VideotexString`
4683 * - :py:class:`pyderasn.IA5String`
4684 - proper alphabet validation
4685 * - :py:class:`pyderasn.GraphicString`
4687 * - :py:class:`pyderasn.VisibleString`, :py:class:`pyderasn.ISO646String`
4688 - proper alphabet validation
4689 * - :py:class:`pyderasn.GeneralString`
4691 * - :py:class:`pyderasn.UniversalString`
4693 * - :py:class:`pyderasn.BMPString`
4698 def _value_sanitize(self, value):
4700 value_decoded = None
4701 if isinstance(value, self.__class__):
4702 value_raw = value._value
4703 elif value.__class__ == str:
4704 value_decoded = value
4705 elif value.__class__ == bytes:
4708 raise InvalidValueType((self.__class__, str, bytes))
4711 value_decoded.encode(self.encoding)
4712 if value_raw is None else value_raw
4715 value_raw.decode(self.encoding)
4716 if value_decoded is None else value_decoded
4718 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4719 raise DecodeError(str(err))
4720 if not self._bound_min <= len(value_decoded) <= self._bound_max:
4728 def __eq__(self, their):
4729 if their.__class__ == bytes:
4730 return self._value == their
4731 if their.__class__ == str:
4732 return self._value == their.encode(self.encoding)
4733 if not isinstance(their, self.__class__):
4736 self._value == their._value and
4737 self.tag == their.tag and
4738 self._expl == their._expl
4741 def __unicode__(self):
4743 return self._value.decode(self.encoding)
4744 return str(self._value)
4747 return pp_console_row(next(self.pps()))
4749 def pps(self, decode_path=()):
4752 value = "".join(escape_control_unicode(c) for c in self.__unicode__())
4755 asn1_type_name=self.asn1_type_name,
4756 obj_name=self.__class__.__name__,
4757 decode_path=decode_path,
4759 optional=self.optional,
4760 default=self == self.default,
4761 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4762 expl=None if self._expl is None else tag_decode(self._expl),
4767 expl_offset=self.expl_offset if self.expled else None,
4768 expl_tlen=self.expl_tlen if self.expled else None,
4769 expl_llen=self.expl_llen if self.expled else None,
4770 expl_vlen=self.expl_vlen if self.expled else None,
4771 expl_lenindef=self.expl_lenindef,
4772 ber_encoded=self.ber_encoded,
4775 for pp in self.pps_lenindef(decode_path):
4779 class UTF8String(CommonString):
4781 tag_default = tag_encode(12)
4783 asn1_type_name = "UTF8String"
4786 class AllowableCharsMixin:
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):
5251 return pp_console_row(next(self.pps()))
5253 def pps(self, decode_path=()):
5256 asn1_type_name=self.asn1_type_name,
5257 obj_name=self.__class__.__name__,
5258 decode_path=decode_path,
5259 value=self._pp_value(),
5260 optional=self.optional,
5261 default=self == self.default,
5262 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5263 expl=None if self._expl is None else tag_decode(self._expl),
5268 expl_offset=self.expl_offset if self.expled else None,
5269 expl_tlen=self.expl_tlen if self.expled else None,
5270 expl_llen=self.expl_llen if self.expled else None,
5271 expl_vlen=self.expl_vlen if self.expled else None,
5272 expl_lenindef=self.expl_lenindef,
5273 ber_encoded=self.ber_encoded,
5276 for pp in self.pps_lenindef(decode_path):
5280 class GeneralizedTime(UTCTime):
5281 """``GeneralizedTime`` datetime type
5283 This type is similar to :py:class:`pyderasn.UTCTime`.
5285 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5286 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
5288 '20170930220750.000123Z'
5289 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
5290 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
5294 Only **naive** datetime objects are supported.
5295 Library assumes that all work is done in UTC.
5299 Only **microsecond** fractions are supported in DER encoding.
5300 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
5301 higher precision values.
5305 **BER** encoded data can loss information (accuracy) during
5306 decoding because of float transformations.
5310 **Zero** year is unsupported.
5313 tag_default = tag_encode(24)
5314 asn1_type_name = "GeneralizedTime"
5316 def _dt_sanitize(self, value):
5319 def _strptime_bered(self, value):
5320 if len(value) < 4 + 3 * 2:
5321 raise ValueError("invalid GeneralizedTime")
5322 year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
5323 decoded = datetime(year, month, day, hour)
5324 offset, value = 0, value[10:]
5326 return offset, decoded
5327 if value[-1] == "Z":
5330 for char, sign in (("-", -1), ("+", 1)):
5331 idx = value.rfind(char)
5334 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
5335 v = pureint(offset_raw)
5336 if len(offset_raw) == 4:
5337 offset, v = (60 * (v % 100)), v // 100
5339 raise ValueError("invalid UTC offset minutes")
5340 elif len(offset_raw) == 2:
5343 raise ValueError("invalid UTC offset")
5345 if offset > 14 * 3600:
5346 raise ValueError("too big UTC offset")
5350 return offset, decoded
5351 if value[0] in DECIMAL_SIGNS:
5353 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
5356 raise ValueError("stripped minutes")
5357 decoded += timedelta(seconds=60 * pureint(value[:2]))
5360 return offset, decoded
5361 if value[0] in DECIMAL_SIGNS:
5363 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
5366 raise ValueError("stripped seconds")
5367 decoded += timedelta(seconds=pureint(value[:2]))
5370 return offset, decoded
5371 if value[0] not in DECIMAL_SIGNS:
5372 raise ValueError("invalid format after seconds")
5374 decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
5377 def _strptime(self, value):
5379 if l == LEN_YYYYMMDDHHMMSSZ:
5380 # datetime.strptime's format: %Y%m%d%H%M%SZ
5381 if value[-1] != "Z":
5382 raise ValueError("non UTC timezone")
5383 return datetime(*str_to_time_fractions(value[:-1]))
5384 if l >= LEN_YYYYMMDDHHMMSSDMZ:
5385 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
5386 if value[-1] != "Z":
5387 raise ValueError("non UTC timezone")
5388 if value[14] != ".":
5389 raise ValueError("no fractions separator")
5392 raise ValueError("trailing zero")
5395 raise ValueError("only microsecond fractions are supported")
5396 us = pureint(us + ("0" * (6 - us_len)))
5397 year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
5398 return datetime(year, month, day, hour, minute, second, us)
5399 raise ValueError("invalid GeneralizedTime length")
5401 def _encode_time(self):
5403 encoded = value.strftime("%Y%m%d%H%M%S")
5404 if value.microsecond > 0:
5405 encoded += (".%06d" % value.microsecond).rstrip("0")
5406 return (encoded + "Z").encode("ascii")
5409 self._assert_ready()
5411 if value.microsecond > 0:
5412 encoded = self._encode_time()
5413 return b"".join((self.tag, len_encode(len(encoded)), encoded))
5414 return b"".join((self.tag, LEN_LEN_YYYYMMDDHHMMSSZ, self._encode_time()))
5416 def _encode1st(self, state):
5417 self._assert_ready()
5418 vlen = len(self._encode_time())
5419 return len(self.tag) + len_size(vlen) + vlen, state
5421 def _encode2nd(self, writer, state_iter):
5422 write_full(writer, self._encode())
5425 class GraphicString(CommonString):
5427 tag_default = tag_encode(25)
5428 encoding = "iso-8859-1"
5429 asn1_type_name = "GraphicString"
5432 class GeneralString(CommonString):
5434 tag_default = tag_encode(27)
5435 encoding = "iso-8859-1"
5436 asn1_type_name = "GeneralString"
5439 class UniversalString(CommonString):
5441 tag_default = tag_encode(28)
5442 encoding = "utf-32-be"
5443 asn1_type_name = "UniversalString"
5446 class BMPString(CommonString):
5448 tag_default = tag_encode(30)
5449 encoding = "utf-16-be"
5450 asn1_type_name = "BMPString"
5453 ChoiceState = namedtuple(
5455 BasicState._fields + ("specs", "value",),
5461 """``CHOICE`` special type
5465 class GeneralName(Choice):
5467 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
5468 ("dNSName", IA5String(impl=tag_ctxp(2))),
5471 >>> gn = GeneralName()
5473 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
5474 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5475 >>> gn["dNSName"] = IA5String("bar.baz")
5476 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
5477 >>> gn["rfc822Name"]
5480 [2] IA5String IA5 bar.baz
5483 >>> gn.value == gn["dNSName"]
5486 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
5488 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
5489 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5491 __slots__ = ("specs",)
5493 asn1_type_name = "CHOICE"
5506 :param value: set the value. Either ``(choice, value)`` tuple, or
5507 :py:class:`pyderasn.Choice` object
5508 :param bytes impl: can not be set, do **not** use it
5509 :param bytes expl: override default tag with ``EXPLICIT`` one
5510 :param default: set default value. Type same as in ``value``
5511 :param bool optional: is object ``OPTIONAL`` in sequence
5513 if impl is not None:
5514 raise ValueError("no implicit tag allowed for CHOICE")
5515 super().__init__(None, expl, default, optional, _decoded)
5517 schema = getattr(self, "schema", ())
5518 if len(schema) == 0:
5519 raise ValueError("schema must be specified")
5521 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5524 if value is not None:
5525 self._value = self._value_sanitize(value)
5526 if default is not None:
5527 default_value = self._value_sanitize(default)
5528 default_obj = self.__class__(impl=self.tag, expl=self._expl)
5529 default_obj.specs = self.specs
5530 default_obj._value = default_value
5531 self.default = default_obj
5533 self._value = copy(default_obj._value)
5534 if self._expl is not None:
5535 tag_class, _, tag_num = tag_decode(self._expl)
5536 self._tag_order = (tag_class, tag_num)
5538 def _value_sanitize(self, value):
5539 if (value.__class__ == tuple) and len(value) == 2:
5541 spec = self.specs.get(choice)
5543 raise ObjUnknown(choice)
5544 if not isinstance(obj, spec.__class__):
5545 raise InvalidValueType((spec,))
5546 return (choice, spec(obj))
5547 if isinstance(value, self.__class__):
5549 raise InvalidValueType((self.__class__, tuple))
5553 return self._value is not None and self._value[1].ready
5557 return self.expl_lenindef or (
5558 (self._value is not None) and
5559 self._value[1].bered
5562 def __getstate__(self):
5580 def __setstate__(self, state):
5581 super().__setstate__(state)
5582 self.specs = state.specs
5583 self._value = state.value
5585 def __eq__(self, their):
5586 if (their.__class__ == tuple) and len(their) == 2:
5587 return self._value == their
5588 if not isinstance(their, self.__class__):
5591 self.specs == their.specs and
5592 self._value == their._value
5602 return self.__class__(
5605 expl=self._expl if expl is None else expl,
5606 default=self.default if default is None else default,
5607 optional=self.optional if optional is None else optional,
5612 """Name of the choice
5614 self._assert_ready()
5615 return self._value[0]
5619 """Value of underlying choice
5621 self._assert_ready()
5622 return self._value[1]
5625 def tag_order(self):
5626 self._assert_ready()
5627 return self._value[1].tag_order if self._tag_order is None else self._tag_order
5630 def tag_order_cer(self):
5631 return min(v.tag_order_cer for v in self.specs.values())
5633 def __getitem__(self, key):
5634 if key not in self.specs:
5635 raise ObjUnknown(key)
5636 if self._value is None:
5638 choice, value = self._value
5643 def __setitem__(self, key, value):
5644 spec = self.specs.get(key)
5646 raise ObjUnknown(key)
5647 if not isinstance(value, spec.__class__):
5648 raise InvalidValueType((spec.__class__,))
5649 self._value = (key, spec(value))
5657 return self._value[1].decoded if self.ready else False
5660 self._assert_ready()
5661 return self._value[1].encode()
5663 def _encode1st(self, state):
5664 self._assert_ready()
5665 return self._value[1].encode1st(state)
5667 def _encode2nd(self, writer, state_iter):
5668 self._value[1].encode2nd(writer, state_iter)
5670 def _encode_cer(self, writer):
5671 self._assert_ready()
5672 self._value[1].encode_cer(writer)
5674 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5675 for choice, spec in self.specs.items():
5676 sub_decode_path = decode_path + (choice,)
5682 decode_path=sub_decode_path,
5685 _ctx_immutable=False,
5692 klass=self.__class__,
5693 decode_path=decode_path,
5696 if tag_only: # pragma: no cover
5700 for _decode_path, value, tail in spec.decode_evgen(
5704 decode_path=sub_decode_path,
5706 _ctx_immutable=False,
5708 yield _decode_path, value, tail
5710 _, value, tail = next(spec.decode_evgen(
5714 decode_path=sub_decode_path,
5716 _ctx_immutable=False,
5719 obj = self.__class__(
5722 default=self.default,
5723 optional=self.optional,
5724 _decoded=(offset, 0, value.fulllen),
5726 obj._value = (choice, value)
5727 yield decode_path, obj, tail
5730 value = pp_console_row(next(self.pps()))
5732 value = "%s[%r]" % (value, self.value)
5735 def pps(self, decode_path=()):
5738 asn1_type_name=self.asn1_type_name,
5739 obj_name=self.__class__.__name__,
5740 decode_path=decode_path,
5741 value=self.choice if self.ready else None,
5742 optional=self.optional,
5743 default=self == self.default,
5744 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5745 expl=None if self._expl is None else tag_decode(self._expl),
5750 expl_lenindef=self.expl_lenindef,
5754 yield self.value.pps(decode_path=decode_path + (self.choice,))
5755 for pp in self.pps_lenindef(decode_path):
5759 class PrimitiveTypes(Choice):
5760 """Predefined ``CHOICE`` for all generic primitive types
5762 It could be useful for general decoding of some unspecified values:
5764 >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
5765 OCTET STRING 3 bytes 666f6f
5766 >>> PrimitiveTypes().decod(hexdec("0203123456")).value
5770 schema = tuple((klass.__name__, klass()) for klass in (
5794 AnyState = namedtuple(
5796 BasicState._fields + ("value", "defined"),
5802 """``ANY`` special type
5804 >>> Any(Integer(-123))
5805 ANY INTEGER -123 (0X:7B)
5806 >>> a = Any(OctetString(b"hello world").encode())
5807 ANY 040b68656c6c6f20776f726c64
5808 >>> hexenc(bytes(a))
5809 b'0x040x0bhello world'
5811 __slots__ = ("defined",)
5812 tag_default = tag_encode(0)
5813 asn1_type_name = "ANY"
5823 :param value: set the value. Either any kind of pyderasn's
5824 **ready** object, or bytes. Pay attention that
5825 **no** validation is performed if raw binary value
5826 is valid TLV, except just tag decoding
5827 :param bytes expl: override default tag with ``EXPLICIT`` one
5828 :param bool optional: is object ``OPTIONAL`` in sequence
5830 super().__init__(None, expl, None, optional, _decoded)
5834 value = self._value_sanitize(value)
5836 if self._expl is None:
5837 if value.__class__ == bytes:
5838 tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5840 tag_class, tag_num = value.tag_order
5842 tag_class, _, tag_num = tag_decode(self._expl)
5843 self._tag_order = (tag_class, tag_num)
5846 def _value_sanitize(self, value):
5847 if value.__class__ == bytes:
5849 raise ValueError("%s value can not be empty" % self.__class__.__name__)
5851 if isinstance(value, self.__class__):
5853 if not isinstance(value, Obj):
5854 raise InvalidValueType((self.__class__, Obj, bytes))
5859 return self._value is not None
5862 def tag_order(self):
5863 self._assert_ready()
5864 return self._tag_order
5868 if self.expl_lenindef or self.lenindef:
5870 if self.defined is None:
5872 return self.defined[1].bered
5874 def __getstate__(self):
5892 def __setstate__(self, state):
5893 super().__setstate__(state)
5894 self._value = state.value
5895 self.defined = state.defined
5897 def __eq__(self, their):
5898 if their.__class__ == bytes:
5899 if self._value.__class__ == bytes:
5900 return self._value == their
5901 return self._value.encode() == their
5902 if issubclass(their.__class__, Any):
5903 if self.ready and their.ready:
5904 return bytes(self) == bytes(their)
5905 return self.ready == their.ready
5914 return self.__class__(
5916 expl=self._expl if expl is None else expl,
5917 optional=self.optional if optional is None else optional,
5920 def __bytes__(self):
5921 self._assert_ready()
5923 if value.__class__ == bytes:
5925 return self._value.encode()
5932 self._assert_ready()
5934 if value.__class__ == bytes:
5936 return value.encode()
5938 def _encode1st(self, state):
5939 self._assert_ready()
5941 if value.__class__ == bytes:
5942 return len(value), state
5943 return value.encode1st(state)
5945 def _encode2nd(self, writer, state_iter):
5947 if value.__class__ == bytes:
5948 write_full(writer, value)
5950 value.encode2nd(writer, state_iter)
5952 def _encode_cer(self, writer):
5953 self._assert_ready()
5955 if value.__class__ == bytes:
5956 write_full(writer, value)
5958 value.encode_cer(writer)
5960 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5962 t, tlen, lv = tag_strip(tlv)
5963 except DecodeError as err:
5964 raise err.__class__(
5966 klass=self.__class__,
5967 decode_path=decode_path,
5971 l, llen, v = len_decode(lv)
5972 except LenIndefForm as err:
5973 if not ctx.get("bered", False):
5974 raise err.__class__(
5976 klass=self.__class__,
5977 decode_path=decode_path,
5980 llen, vlen, v = 1, 0, lv[1:]
5981 sub_offset = offset + tlen + llen
5983 while v[:EOC_LEN].tobytes() != EOC:
5984 chunk, v = Any().decode(
5987 decode_path=decode_path + (str(chunk_i),),
5990 _ctx_immutable=False,
5992 vlen += chunk.tlvlen
5993 sub_offset += chunk.tlvlen
5995 tlvlen = tlen + llen + vlen + EOC_LEN
5996 obj = self.__class__(
5997 value=None if evgen_mode else tlv[:tlvlen].tobytes(),
5999 optional=self.optional,
6000 _decoded=(offset, 0, tlvlen),
6003 obj.tag = t.tobytes()
6004 yield decode_path, obj, v[EOC_LEN:]
6006 except DecodeError as err:
6007 raise err.__class__(
6009 klass=self.__class__,
6010 decode_path=decode_path,
6014 raise NotEnoughData(
6015 "encoded length is longer than data",
6016 klass=self.__class__,
6017 decode_path=decode_path,
6020 tlvlen = tlen + llen + l
6021 v, tail = tlv[:tlvlen], v[l:]
6022 obj = self.__class__(
6023 value=None if evgen_mode else v.tobytes(),
6025 optional=self.optional,
6026 _decoded=(offset, 0, tlvlen),
6028 obj.tag = t.tobytes()
6029 yield decode_path, obj, tail
6032 return pp_console_row(next(self.pps()))
6034 def pps(self, decode_path=()):
6038 elif value.__class__ == bytes:
6044 asn1_type_name=self.asn1_type_name,
6045 obj_name=self.__class__.__name__,
6046 decode_path=decode_path,
6048 blob=self._value if self._value.__class__ == bytes else None,
6049 optional=self.optional,
6050 default=self == self.default,
6051 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6052 expl=None if self._expl is None else tag_decode(self._expl),
6057 expl_offset=self.expl_offset if self.expled else None,
6058 expl_tlen=self.expl_tlen if self.expled else None,
6059 expl_llen=self.expl_llen if self.expled else None,
6060 expl_vlen=self.expl_vlen if self.expled else None,
6061 expl_lenindef=self.expl_lenindef,
6062 lenindef=self.lenindef,
6065 defined_by, defined = self.defined or (None, None)
6066 if defined_by is not None:
6068 decode_path=decode_path + (DecodePathDefBy(defined_by),)
6070 for pp in self.pps_lenindef(decode_path):
6074 ########################################################################
6075 # ASN.1 constructed types
6076 ########################################################################
6078 def abs_decode_path(decode_path, rel_path):
6079 """Create an absolute decode path from current and relative ones
6081 :param decode_path: current decode path, starting point. Tuple of strings
6082 :param rel_path: relative path to ``decode_path``. Tuple of strings.
6083 If first tuple's element is "/", then treat it as
6084 an absolute path, ignoring ``decode_path`` as
6085 starting point. Also this tuple can contain ".."
6086 elements, stripping the leading element from
6089 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
6090 ("foo", "bar", "baz", "whatever")
6091 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
6093 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
6096 if rel_path[0] == "/":
6098 if rel_path[0] == "..":
6099 return abs_decode_path(decode_path[:-1], rel_path[1:])
6100 return decode_path + rel_path
6103 SequenceState = namedtuple(
6105 BasicState._fields + ("specs", "value",),
6110 class SequenceEncode1stMixin:
6113 def _encode1st(self, state):
6115 idx = len(state) - 1
6117 for v in self._values_for_encoding():
6118 l, _ = v.encode1st(state)
6121 return len(self.tag) + len_size(vlen) + vlen, state
6124 class Sequence(SequenceEncode1stMixin, Obj):
6125 """``SEQUENCE`` structure type
6127 You have to make specification of sequence::
6129 class Extension(Sequence):
6131 ("extnID", ObjectIdentifier()),
6132 ("critical", Boolean(default=False)),
6133 ("extnValue", OctetString()),
6136 Then, you can work with it as with dictionary.
6138 >>> ext = Extension()
6139 >>> Extension().specs
6141 ('extnID', OBJECT IDENTIFIER),
6142 ('critical', BOOLEAN False OPTIONAL DEFAULT),
6143 ('extnValue', OCTET STRING),
6145 >>> ext["extnID"] = "1.2.3"
6146 Traceback (most recent call last):
6147 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
6148 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
6150 You can determine if sequence is ready to be encoded:
6155 Traceback (most recent call last):
6156 pyderasn.ObjNotReady: object is not ready: extnValue
6157 >>> ext["extnValue"] = OctetString(b"foobar")
6161 Value you want to assign, must have the same **type** as in
6162 corresponding specification, but it can have different tags,
6163 optional/default attributes -- they will be taken from specification
6166 class TBSCertificate(Sequence):
6168 ("version", Version(expl=tag_ctxc(0), default="v1")),
6171 >>> tbs = TBSCertificate()
6172 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
6174 Assign ``None`` to remove value from sequence.
6176 You can set values in Sequence during its initialization:
6178 >>> AlgorithmIdentifier((
6179 ("algorithm", ObjectIdentifier("1.2.3")),
6180 ("parameters", Any(Null()))
6182 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
6184 You can determine if value exists/set in the sequence and take its value:
6186 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
6189 OBJECT IDENTIFIER 1.2.3
6191 But pay attention that if value has default, then it won't be (not
6192 in) in the sequence (because ``DEFAULT`` must not be encoded in
6193 DER), but you can read its value:
6195 >>> "critical" in ext, ext["critical"]
6196 (False, BOOLEAN False)
6197 >>> ext["critical"] = Boolean(True)
6198 >>> "critical" in ext, ext["critical"]
6199 (True, BOOLEAN True)
6201 All defaulted values are always optional.
6203 .. _allow_default_values_ctx:
6205 DER prohibits default value encoding and will raise an error if
6206 default value is unexpectedly met during decode.
6207 If :ref:`bered <bered_ctx>` context option is set, then no error
6208 will be raised, but ``bered`` attribute set. You can disable strict
6209 defaulted values existence validation by setting
6210 ``"allow_default_values": True`` :ref:`context <ctx>` option.
6212 All values with DEFAULT specified are decoded atomically in
6213 :ref:`evgen mode <evgen_mode>`. If DEFAULT value is some kind of
6214 SEQUENCE, then it will be yielded as a single element, not
6215 disassembled. That is required for DEFAULT existence check.
6217 Two sequences are equal if they have equal specification (schema),
6218 implicit/explicit tagging and the same values.
6220 __slots__ = ("specs",)
6221 tag_default = tag_encode(form=TagFormConstructed, num=16)
6222 asn1_type_name = "SEQUENCE"
6234 super().__init__(impl, expl, default, optional, _decoded)
6236 schema = getattr(self, "schema", ())
6238 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
6241 if value is not None:
6242 if issubclass(value.__class__, Sequence):
6243 self._value = value._value
6244 elif hasattr(value, "__iter__"):
6245 for seq_key, seq_value in value:
6246 self[seq_key] = seq_value
6248 raise InvalidValueType((Sequence,))
6249 if default is not None:
6250 if not issubclass(default.__class__, Sequence):
6251 raise InvalidValueType((Sequence,))
6252 default_value = default._value
6253 default_obj = self.__class__(impl=self.tag, expl=self._expl)
6254 default_obj.specs = self.specs
6255 default_obj._value = default_value
6256 self.default = default_obj
6258 self._value = copy(default_obj._value)
6262 for name, spec in self.specs.items():
6263 value = self._value.get(name)
6274 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6276 return any(value.bered for value in self._value.values())
6278 def __getstate__(self):
6279 return SequenceState(
6293 {k: copy(v) for k, v in self._value.items()},
6296 def __setstate__(self, state):
6297 super().__setstate__(state)
6298 self.specs = state.specs
6299 self._value = state.value
6301 def __eq__(self, their):
6302 if not isinstance(their, self.__class__):
6305 self.specs == their.specs and
6306 self.tag == their.tag and
6307 self._expl == their._expl and
6308 self._value == their._value
6319 return self.__class__(
6322 impl=self.tag if impl is None else impl,
6323 expl=self._expl if expl is None else expl,
6324 default=self.default if default is None else default,
6325 optional=self.optional if optional is None else optional,
6328 def __contains__(self, key):
6329 return key in self._value
6331 def __setitem__(self, key, value):
6332 spec = self.specs.get(key)
6334 raise ObjUnknown(key)
6336 self._value.pop(key, None)
6338 if not isinstance(value, spec.__class__):
6339 raise InvalidValueType((spec.__class__,))
6340 value = spec(value=value)
6341 if spec.default is not None and value == spec.default:
6342 self._value.pop(key, None)
6344 self._value[key] = value
6346 def __getitem__(self, key):
6347 value = self._value.get(key)
6348 if value is not None:
6350 spec = self.specs.get(key)
6352 raise ObjUnknown(key)
6353 if spec.default is not None:
6357 def _values_for_encoding(self):
6358 for name, spec in self.specs.items():
6359 value = self._value.get(name)
6363 raise ObjNotReady(name)
6367 v = b"".join(v.encode() for v in self._values_for_encoding())
6368 return b"".join((self.tag, len_encode(len(v)), v))
6370 def _encode2nd(self, writer, state_iter):
6371 write_full(writer, self.tag + len_encode(next(state_iter)))
6372 for v in self._values_for_encoding():
6373 v.encode2nd(writer, state_iter)
6375 def _encode_cer(self, writer):
6376 write_full(writer, self.tag + LENINDEF)
6377 for v in self._values_for_encoding():
6378 v.encode_cer(writer)
6379 write_full(writer, EOC)
6381 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6383 t, tlen, lv = tag_strip(tlv)
6384 except DecodeError as err:
6385 raise err.__class__(
6387 klass=self.__class__,
6388 decode_path=decode_path,
6393 klass=self.__class__,
6394 decode_path=decode_path,
6397 if tag_only: # pragma: no cover
6401 ctx_bered = ctx.get("bered", False)
6403 l, llen, v = len_decode(lv)
6404 except LenIndefForm as err:
6406 raise err.__class__(
6408 klass=self.__class__,
6409 decode_path=decode_path,
6412 l, llen, v = 0, 1, lv[1:]
6414 except DecodeError as err:
6415 raise err.__class__(
6417 klass=self.__class__,
6418 decode_path=decode_path,
6422 raise NotEnoughData(
6423 "encoded length is longer than data",
6424 klass=self.__class__,
6425 decode_path=decode_path,
6429 v, tail = v[:l], v[l:]
6431 sub_offset = offset + tlen + llen
6434 ctx_allow_default_values = ctx.get("allow_default_values", False)
6435 for name, spec in self.specs.items():
6436 if spec.optional and (
6437 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
6441 spec_defaulted = spec.default is not None
6442 sub_decode_path = decode_path + (name,)
6444 if evgen_mode and not spec_defaulted:
6445 for _decode_path, value, v_tail in spec.decode_evgen(
6449 decode_path=sub_decode_path,
6451 _ctx_immutable=False,
6453 yield _decode_path, value, v_tail
6455 _, value, v_tail = next(spec.decode_evgen(
6459 decode_path=sub_decode_path,
6461 _ctx_immutable=False,
6464 except TagMismatch as err:
6465 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
6469 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
6470 if not evgen_mode and defined is not None:
6471 defined_by, defined_spec = defined
6472 if issubclass(value.__class__, SequenceOf):
6473 for i, _value in enumerate(value):
6474 sub_sub_decode_path = sub_decode_path + (
6476 DecodePathDefBy(defined_by),
6478 defined_value, defined_tail = defined_spec.decode(
6479 memoryview(bytes(_value)),
6481 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6482 if value.expled else (value.tlen + value.llen)
6485 decode_path=sub_sub_decode_path,
6487 _ctx_immutable=False,
6489 if len(defined_tail) > 0:
6492 klass=self.__class__,
6493 decode_path=sub_sub_decode_path,
6496 _value.defined = (defined_by, defined_value)
6498 defined_value, defined_tail = defined_spec.decode(
6499 memoryview(bytes(value)),
6501 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6502 if value.expled else (value.tlen + value.llen)
6505 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6507 _ctx_immutable=False,
6509 if len(defined_tail) > 0:
6512 klass=self.__class__,
6513 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6516 value.defined = (defined_by, defined_value)
6518 value_len = value.fulllen
6520 sub_offset += value_len
6524 yield sub_decode_path, value, v_tail
6525 if value == spec.default:
6526 if ctx_bered or ctx_allow_default_values:
6530 "DEFAULT value met",
6531 klass=self.__class__,
6532 decode_path=sub_decode_path,
6536 values[name] = value
6537 spec_defines = getattr(spec, "defines", ())
6538 if len(spec_defines) == 0:
6539 defines_by_path = ctx.get("defines_by_path", ())
6540 if len(defines_by_path) > 0:
6541 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
6542 if spec_defines is not None and len(spec_defines) > 0:
6543 for rel_path, schema in spec_defines:
6544 defined = schema.get(value, None)
6545 if defined is not None:
6546 ctx.setdefault("_defines", []).append((
6547 abs_decode_path(sub_decode_path[:-1], rel_path),
6551 if v[:EOC_LEN].tobytes() != EOC:
6554 klass=self.__class__,
6555 decode_path=decode_path,
6563 klass=self.__class__,
6564 decode_path=decode_path,
6567 obj = self.__class__(
6571 default=self.default,
6572 optional=self.optional,
6573 _decoded=(offset, llen, vlen),
6576 obj.lenindef = lenindef
6577 obj.ber_encoded = ber_encoded
6578 yield decode_path, obj, tail
6581 value = pp_console_row(next(self.pps()))
6583 for name in self.specs:
6584 _value = self._value.get(name)
6587 cols.append("%s: %s" % (name, repr(_value)))
6588 return "%s[%s]" % (value, "; ".join(cols))
6590 def pps(self, decode_path=()):
6593 asn1_type_name=self.asn1_type_name,
6594 obj_name=self.__class__.__name__,
6595 decode_path=decode_path,
6596 optional=self.optional,
6597 default=self == self.default,
6598 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6599 expl=None if self._expl is None else tag_decode(self._expl),
6604 expl_offset=self.expl_offset if self.expled else None,
6605 expl_tlen=self.expl_tlen if self.expled else None,
6606 expl_llen=self.expl_llen if self.expled else None,
6607 expl_vlen=self.expl_vlen if self.expled else None,
6608 expl_lenindef=self.expl_lenindef,
6609 lenindef=self.lenindef,
6610 ber_encoded=self.ber_encoded,
6613 for name in self.specs:
6614 value = self._value.get(name)
6617 yield value.pps(decode_path=decode_path + (name,))
6618 for pp in self.pps_lenindef(decode_path):
6622 class Set(Sequence, SequenceEncode1stMixin):
6623 """``SET`` structure type
6625 Its usage is identical to :py:class:`pyderasn.Sequence`.
6627 .. _allow_unordered_set_ctx:
6629 DER prohibits unordered values encoding and will raise an error
6630 during decode. If :ref:`bered <bered_ctx>` context option is set,
6631 then no error will occur. Also you can disable strict values
6632 ordering check by setting ``"allow_unordered_set": True``
6633 :ref:`context <ctx>` option.
6636 tag_default = tag_encode(form=TagFormConstructed, num=17)
6637 asn1_type_name = "SET"
6639 def _values_for_encoding(self):
6640 return sorted(super()._values_for_encoding(), key=attrgetter("tag_order"))
6642 def _encode_cer(self, writer):
6643 write_full(writer, self.tag + LENINDEF)
6645 super()._values_for_encoding(),
6646 key=attrgetter("tag_order_cer"),
6648 v.encode_cer(writer)
6649 write_full(writer, EOC)
6651 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6653 t, tlen, lv = tag_strip(tlv)
6654 except DecodeError as err:
6655 raise err.__class__(
6657 klass=self.__class__,
6658 decode_path=decode_path,
6663 klass=self.__class__,
6664 decode_path=decode_path,
6671 ctx_bered = ctx.get("bered", False)
6673 l, llen, v = len_decode(lv)
6674 except LenIndefForm as err:
6676 raise err.__class__(
6678 klass=self.__class__,
6679 decode_path=decode_path,
6682 l, llen, v = 0, 1, lv[1:]
6684 except DecodeError as err:
6685 raise err.__class__(
6687 klass=self.__class__,
6688 decode_path=decode_path,
6692 raise NotEnoughData(
6693 "encoded length is longer than data",
6694 klass=self.__class__,
6698 v, tail = v[:l], v[l:]
6700 sub_offset = offset + tlen + llen
6703 ctx_allow_default_values = ctx.get("allow_default_values", False)
6704 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6705 tag_order_prev = (0, 0)
6706 _specs_items = copy(self.specs)
6709 if lenindef and v[:EOC_LEN].tobytes() == EOC:
6711 for name, spec in _specs_items.items():
6712 sub_decode_path = decode_path + (name,)
6718 decode_path=sub_decode_path,
6721 _ctx_immutable=False,
6728 klass=self.__class__,
6729 decode_path=decode_path,
6732 spec_defaulted = spec.default is not None
6733 if evgen_mode and not spec_defaulted:
6734 for _decode_path, value, v_tail in spec.decode_evgen(
6738 decode_path=sub_decode_path,
6740 _ctx_immutable=False,
6742 yield _decode_path, value, v_tail
6744 _, value, v_tail = next(spec.decode_evgen(
6748 decode_path=sub_decode_path,
6750 _ctx_immutable=False,
6753 value_tag_order = value.tag_order
6754 value_len = value.fulllen
6755 if tag_order_prev >= value_tag_order:
6756 if ctx_bered or ctx_allow_unordered_set:
6760 "unordered " + self.asn1_type_name,
6761 klass=self.__class__,
6762 decode_path=sub_decode_path,
6767 yield sub_decode_path, value, v_tail
6768 if value != spec.default:
6770 elif ctx_bered or ctx_allow_default_values:
6774 "DEFAULT value met",
6775 klass=self.__class__,
6776 decode_path=sub_decode_path,
6779 values[name] = value
6780 del _specs_items[name]
6781 tag_order_prev = value_tag_order
6782 sub_offset += value_len
6786 obj = self.__class__(
6790 default=self.default,
6791 optional=self.optional,
6792 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6795 if v[:EOC_LEN].tobytes() != EOC:
6798 klass=self.__class__,
6799 decode_path=decode_path,
6804 for name, spec in self.specs.items():
6805 if name not in values and not spec.optional:
6807 "%s value is not ready" % name,
6808 klass=self.__class__,
6809 decode_path=decode_path,
6814 obj.ber_encoded = ber_encoded
6815 yield decode_path, obj, tail
6818 SequenceOfState = namedtuple(
6820 BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
6825 class SequenceOf(SequenceEncode1stMixin, Obj):
6826 """``SEQUENCE OF`` sequence type
6828 For that kind of type you must specify the object it will carry on
6829 (bounds are for example here, not required)::
6831 class Ints(SequenceOf):
6836 >>> ints.append(Integer(123))
6837 >>> ints.append(Integer(234))
6839 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
6840 >>> [int(i) for i in ints]
6842 >>> ints.append(Integer(345))
6843 Traceback (most recent call last):
6844 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
6847 >>> ints[1] = Integer(345)
6849 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
6851 You can initialize sequence with preinitialized values:
6853 >>> ints = Ints([Integer(123), Integer(234)])
6855 Also you can use iterator as a value:
6857 >>> ints = Ints(iter(Integer(i) for i in range(1000000)))
6859 And it won't be iterated until encoding process. Pay attention that
6860 bounds and required schema checks are done only during the encoding
6861 process in that case! After encode was called, then value is zeroed
6862 back to empty list and you have to set it again. That mode is useful
6863 mainly with CER encoding mode, where all objects from the iterable
6864 will be streamed to the buffer, without copying all of them to
6867 __slots__ = ("spec", "_bound_min", "_bound_max")
6868 tag_default = tag_encode(form=TagFormConstructed, num=16)
6869 asn1_type_name = "SEQUENCE OF"
6882 super().__init__(impl, expl, default, optional, _decoded)
6884 schema = getattr(self, "schema", None)
6886 raise ValueError("schema must be specified")
6888 self._bound_min, self._bound_max = getattr(
6892 ) if bounds is None else bounds
6894 if value is not None:
6895 self._value = self._value_sanitize(value)
6896 if default is not None:
6897 default_value = self._value_sanitize(default)
6898 default_obj = self.__class__(
6903 default_obj._value = default_value
6904 self.default = default_obj
6906 self._value = copy(default_obj._value)
6908 def _value_sanitize(self, value):
6910 if issubclass(value.__class__, SequenceOf):
6911 value = value._value
6912 elif hasattr(value, NEXT_ATTR_NAME):
6914 elif hasattr(value, "__iter__"):
6917 raise InvalidValueType((self.__class__, iter, "iterator"))
6919 if not self._bound_min <= len(value) <= self._bound_max:
6920 raise BoundsError(self._bound_min, len(value), self._bound_max)
6921 class_expected = self.spec.__class__
6923 if not isinstance(v, class_expected):
6924 raise InvalidValueType((class_expected,))
6929 if hasattr(self._value, NEXT_ATTR_NAME):
6931 if self._bound_min > 0 and len(self._value) == 0:
6933 return all(v.ready for v in self._value)
6937 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6939 return any(v.bered for v in self._value)
6941 def __getstate__(self):
6942 if hasattr(self._value, NEXT_ATTR_NAME):
6943 raise ValueError("can not pickle SequenceOf with iterator")
6944 return SequenceOfState(
6958 [copy(v) for v in self._value],
6963 def __setstate__(self, state):
6964 super().__setstate__(state)
6965 self.spec = state.spec
6966 self._value = state.value
6967 self._bound_min = state.bound_min
6968 self._bound_max = state.bound_max
6970 def __eq__(self, their):
6971 if isinstance(their, self.__class__):
6973 self.spec == their.spec and
6974 self.tag == their.tag and
6975 self._expl == their._expl and
6976 self._value == their._value
6978 if hasattr(their, "__iter__"):
6979 return self._value == list(their)
6991 return self.__class__(
6995 (self._bound_min, self._bound_max)
6996 if bounds is None else bounds
6998 impl=self.tag if impl is None else impl,
6999 expl=self._expl if expl is None else expl,
7000 default=self.default if default is None else default,
7001 optional=self.optional if optional is None else optional,
7004 def __contains__(self, key):
7005 return key in self._value
7007 def append(self, value):
7008 if not isinstance(value, self.spec.__class__):
7009 raise InvalidValueType((self.spec.__class__,))
7010 if len(self._value) + 1 > self._bound_max:
7013 len(self._value) + 1,
7016 self._value.append(value)
7019 return iter(self._value)
7022 return len(self._value)
7024 def __setitem__(self, key, value):
7025 if not isinstance(value, self.spec.__class__):
7026 raise InvalidValueType((self.spec.__class__,))
7027 self._value[key] = self.spec(value=value)
7029 def __getitem__(self, key):
7030 return self._value[key]
7032 def _values_for_encoding(self):
7033 return iter(self._value)
7036 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7039 values_append = values.append
7040 class_expected = self.spec.__class__
7041 values_for_encoding = self._values_for_encoding()
7043 for v in values_for_encoding:
7044 if not isinstance(v, class_expected):
7045 raise InvalidValueType((class_expected,))
7046 values_append(v.encode())
7047 if not self._bound_min <= len(values) <= self._bound_max:
7048 raise BoundsError(self._bound_min, len(values), self._bound_max)
7049 value = b"".join(values)
7051 value = b"".join(v.encode() for v in self._values_for_encoding())
7052 return b"".join((self.tag, len_encode(len(value)), value))
7054 def _encode1st(self, state):
7055 state = super()._encode1st(state)
7056 if hasattr(self._value, NEXT_ATTR_NAME):
7060 def _encode2nd(self, writer, state_iter):
7061 write_full(writer, self.tag + len_encode(next(state_iter)))
7062 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7065 class_expected = self.spec.__class__
7066 values_for_encoding = self._values_for_encoding()
7068 for v in values_for_encoding:
7069 if not isinstance(v, class_expected):
7070 raise InvalidValueType((class_expected,))
7071 v.encode2nd(writer, state_iter)
7073 if not self._bound_min <= values_count <= self._bound_max:
7074 raise BoundsError(self._bound_min, values_count, self._bound_max)
7076 for v in self._values_for_encoding():
7077 v.encode2nd(writer, state_iter)
7079 def _encode_cer(self, writer):
7080 write_full(writer, self.tag + LENINDEF)
7081 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7083 class_expected = self.spec.__class__
7085 values_for_encoding = self._values_for_encoding()
7087 for v in values_for_encoding:
7088 if not isinstance(v, class_expected):
7089 raise InvalidValueType((class_expected,))
7090 v.encode_cer(writer)
7092 if not self._bound_min <= values_count <= self._bound_max:
7093 raise BoundsError(self._bound_min, values_count, self._bound_max)
7095 for v in self._values_for_encoding():
7096 v.encode_cer(writer)
7097 write_full(writer, EOC)
7107 ordering_check=False,
7110 t, tlen, lv = tag_strip(tlv)
7111 except DecodeError as err:
7112 raise err.__class__(
7114 klass=self.__class__,
7115 decode_path=decode_path,
7120 klass=self.__class__,
7121 decode_path=decode_path,
7128 ctx_bered = ctx.get("bered", False)
7130 l, llen, v = len_decode(lv)
7131 except LenIndefForm as err:
7133 raise err.__class__(
7135 klass=self.__class__,
7136 decode_path=decode_path,
7139 l, llen, v = 0, 1, lv[1:]
7141 except DecodeError as err:
7142 raise err.__class__(
7144 klass=self.__class__,
7145 decode_path=decode_path,
7149 raise NotEnoughData(
7150 "encoded length is longer than data",
7151 klass=self.__class__,
7152 decode_path=decode_path,
7156 v, tail = v[:l], v[l:]
7158 sub_offset = offset + tlen + llen
7161 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
7162 value_prev = memoryview(v[:0])
7166 if lenindef and v[:EOC_LEN].tobytes() == EOC:
7168 sub_decode_path = decode_path + (str(_value_count),)
7170 for _decode_path, value, v_tail in spec.decode_evgen(
7174 decode_path=sub_decode_path,
7176 _ctx_immutable=False,
7178 yield _decode_path, value, v_tail
7180 _, value, v_tail = next(spec.decode_evgen(
7184 decode_path=sub_decode_path,
7186 _ctx_immutable=False,
7189 value_len = value.fulllen
7191 if value_prev.tobytes() > v[:value_len].tobytes():
7192 if ctx_bered or ctx_allow_unordered_set:
7196 "unordered " + self.asn1_type_name,
7197 klass=self.__class__,
7198 decode_path=sub_decode_path,
7201 value_prev = v[:value_len]
7204 _value.append(value)
7205 sub_offset += value_len
7208 if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
7210 msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
7211 klass=self.__class__,
7212 decode_path=decode_path,
7216 obj = self.__class__(
7217 value=None if evgen_mode else _value,
7219 bounds=(self._bound_min, self._bound_max),
7222 default=self.default,
7223 optional=self.optional,
7224 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
7226 except BoundsError as err:
7229 klass=self.__class__,
7230 decode_path=decode_path,
7234 if v[:EOC_LEN].tobytes() != EOC:
7237 klass=self.__class__,
7238 decode_path=decode_path,
7243 obj.ber_encoded = ber_encoded
7244 yield decode_path, obj, tail
7248 pp_console_row(next(self.pps())),
7249 ", ".join(repr(v) for v in self._value),
7252 def pps(self, decode_path=()):
7255 asn1_type_name=self.asn1_type_name,
7256 obj_name=self.__class__.__name__,
7257 decode_path=decode_path,
7258 optional=self.optional,
7259 default=self == self.default,
7260 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
7261 expl=None if self._expl is None else tag_decode(self._expl),
7266 expl_offset=self.expl_offset if self.expled else None,
7267 expl_tlen=self.expl_tlen if self.expled else None,
7268 expl_llen=self.expl_llen if self.expled else None,
7269 expl_vlen=self.expl_vlen if self.expled else None,
7270 expl_lenindef=self.expl_lenindef,
7271 lenindef=self.lenindef,
7272 ber_encoded=self.ber_encoded,
7275 for i, value in enumerate(self._value):
7276 yield value.pps(decode_path=decode_path + (str(i),))
7277 for pp in self.pps_lenindef(decode_path):
7281 class SetOf(SequenceOf):
7282 """``SET OF`` sequence type
7284 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
7287 tag_default = tag_encode(form=TagFormConstructed, num=17)
7288 asn1_type_name = "SET OF"
7290 def _value_sanitize(self, value):
7291 value = super()._value_sanitize(value)
7292 if hasattr(value, NEXT_ATTR_NAME):
7294 "SetOf does not support iterator values, as no sense in them"
7299 v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
7300 return b"".join((self.tag, len_encode(len(v)), v))
7302 def _encode2nd(self, writer, state_iter):
7303 write_full(writer, self.tag + len_encode(next(state_iter)))
7305 for v in self._values_for_encoding():
7307 v.encode2nd(buf.write, state_iter)
7308 values.append(buf.getvalue())
7311 write_full(writer, v)
7313 def _encode_cer(self, writer):
7314 write_full(writer, self.tag + LENINDEF)
7315 for v in sorted(encode_cer(v) for v in self._values_for_encoding()):
7316 write_full(writer, v)
7317 write_full(writer, EOC)
7319 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
7320 return super()._decode(
7327 ordering_check=True,
7331 def obj_by_path(pypath): # pragma: no cover
7332 """Import object specified as string Python path
7334 Modules must be separated from classes/functions with ``:``.
7336 >>> obj_by_path("foo.bar:Baz")
7337 <class 'foo.bar.Baz'>
7338 >>> obj_by_path("foo.bar:Baz.boo")
7339 <classmethod 'foo.bar.Baz.boo'>
7341 mod, objs = pypath.rsplit(":", 1)
7342 from importlib import import_module
7343 obj = import_module(mod)
7344 for obj_name in objs.split("."):
7345 obj = getattr(obj, obj_name)
7349 def generic_decoder(): # pragma: no cover
7350 # All of this below is a big hack with self references
7351 choice = PrimitiveTypes()
7352 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
7353 choice.specs["SetOf"] = SetOf(schema=choice)
7355 choice.specs["SequenceOf%d" % i] = SequenceOf(
7359 choice.specs["Any"] = Any()
7361 # Class name equals to type name, to omit it from output
7362 class SEQUENCEOF(SequenceOf):
7370 with_decode_path=False,
7371 decode_path_only=(),
7374 def _pprint_pps(pps):
7376 if hasattr(pp, "_fields"):
7378 decode_path_only != () and
7379 pp.decode_path[:len(decode_path_only)] != decode_path_only
7382 if pp.asn1_type_name == Choice.asn1_type_name:
7384 pp_kwargs = pp._asdict()
7385 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
7386 pp = _pp(**pp_kwargs)
7387 yield pp_console_row(
7392 with_colours=with_colours,
7393 with_decode_path=with_decode_path,
7394 decode_path_len_decrease=len(decode_path_only),
7396 for row in pp_console_blob(
7398 decode_path_len_decrease=len(decode_path_only),
7402 for row in _pprint_pps(pp):
7404 return "\n".join(_pprint_pps(obj.pps(decode_path)))
7405 return SEQUENCEOF(), pprint_any
7408 def ascii_visualize(ba):
7409 """Output only ASCII printable characters, like in hexdump -C
7411 Example output for given binary string (right part)::
7413 92 2b 39 20 65 91 e6 8e 95 93 1a 58 df 02 78 ea |.+9 e......X..x.|
7416 return "".join((chr(b) if 0x20 <= b <= 0x7E else ".") for b in ba)
7420 """Generate ``hexdump -C`` like output
7424 00000000 30 80 30 80 a0 80 02 01 02 00 00 02 14 54 a5 18 |0.0..........T..|
7425 00000010 69 ef 8b 3f 15 fd ea ad bd 47 e0 94 81 6b 06 6a |i..?.....G...k.j|
7427 Result of that function is a generator of lines, where each line is
7432 ["00000010 ", " 69", " ef", " 8b", " 3f", " 15", " fd", " ea", " ad ",
7433 " bd", " 47", " e0", " 94", " 81", " 6b", " 06", " 6a ",
7434 " |i..?.....G...k.j|"]
7438 hexed = hexenc(raw).upper()
7439 addr, cols = 0, ["%08x " % 0]
7440 for i in range(0, len(hexed), 2):
7441 if i != 0 and i // 2 % 8 == 0:
7443 if i != 0 and i // 2 % 16 == 0:
7444 cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:addr + 16])))
7447 cols = ["%08x " % addr]
7448 cols.append(" " + hexed[i:i + 2])
7450 cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:])))
7454 def browse(raw, obj, oid_maps=()):
7455 """Interactive browser
7457 :param bytes raw: binary data you decoded
7458 :param obj: decoded :py:class:`pyderasn.Obj`
7459 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
7460 Its human readable form is printed when OID is met
7462 .. note:: `urwid <http://urwid.org/>`__ dependency required
7464 This browser is an interactive terminal application for browsing
7465 structures of your decoded ASN.1 objects. You can quit it with **q**
7466 key. It consists of three windows:
7469 View of ASN.1 elements hierarchy. You can navigate it using **Up**,
7470 **Down**, **PageUp**, **PageDown**, **Home**, **End** keys.
7471 **Left** key goes to constructed element above. **Plus**/**Minus**
7472 keys collapse/uncollapse constructed elements. **Space** toggles it
7474 window with various information about element. You can scroll it
7475 with **h**/**l** (down, up) (**H**/**L** for triple speed) keys
7477 window with raw data hexdump and highlighted current element's
7478 contents. It automatically focuses on element's data. You can
7479 scroll it with **j**/**k** (down, up) (**J**/**K** for triple
7480 speed) keys. If element has explicit tag, then it also will be
7481 highlighted with different colour
7483 Window's header contains current decode path and progress bars with
7484 position in *info* and *hexdump* windows.
7486 If you press **d**, then current element will be saved in the
7487 current directory under its decode path name (adding ".0", ".1", etc
7488 suffix if such file already exists). **D** will save it with explicit tag.
7490 You can also invoke it with ``--browse`` command line argument.
7492 from copy import deepcopy
7493 from os.path import exists as path_exists
7496 class TW(urwid.TreeWidget):
7497 def __init__(self, state, *args, **kwargs):
7499 self.scrolled = {"info": False, "hexdump": False}
7500 super().__init__(*args, **kwargs)
7503 pp = self.get_node().get_value()
7504 constructed = len(pp) > 1
7505 return (pp if hasattr(pp, "_fields") else pp[0]), constructed
7507 def _state_update(self):
7508 pp, _ = self._get_pp()
7509 self.state["decode_path"].set_text(
7510 ":".join(str(p) for p in pp.decode_path)
7512 lines = deepcopy(self.state["hexed"])
7514 def attr_set(i, attr):
7515 line = lines[i // 16]
7516 idx = 1 + (i - 16 * (i // 16))
7517 line[idx] = (attr, line[idx])
7519 if pp.expl_offset is not None:
7522 pp.expl_offset + pp.expl_tlen + pp.expl_llen,
7524 attr_set(i, "select-expl")
7525 for i in range(pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen):
7526 attr_set(i, "select-value")
7527 self.state["hexdump"]._set_body([urwid.Text(line) for line in lines])
7528 self.state["hexdump"].set_focus(pp.offset // 16)
7529 self.state["hexdump"].set_focus_valign("middle")
7530 self.state["hexdump_bar"].set_completion(
7531 (100 * pp.offset // 16) //
7532 len(self.state["hexdump"]._body.positions())
7536 [("header", "Name: "), pp.obj_name],
7537 [("header", "Type: "), pp.asn1_type_name],
7538 [("header", "Offset: "), "%d (0x%x)" % (pp.offset, pp.offset)],
7539 [("header", "[TLV]len: "), "%d/%d/%d" % (
7540 pp.tlen, pp.llen, pp.vlen,
7542 [("header", "TLVlen: "), "%d" % sum((
7543 pp.tlen, pp.llen, pp.vlen,
7545 [("header", "Slice: "), "[%d:%d]" % (
7546 pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen,
7550 lines.append([("warning", "LENINDEF")])
7552 lines.append([("warning", "BER encoded")])
7554 lines.append([("warning", "BERed")])
7555 if pp.expl is not None:
7556 lines.append([("header", "EXPLICIT")])
7557 klass, _, num = pp.expl
7558 lines.append([" Tag: %s%d" % (TagClassReprs[klass], num)])
7559 if pp.expl_offset is not None:
7560 lines.append([" Offset: %d" % pp.expl_offset])
7561 lines.append([" [TLV]len: %d/%d/%d" % (
7562 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7564 lines.append([" TLVlen: %d" % sum((
7565 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7567 lines.append([" Slice: [%d:%d]" % (
7569 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen,
7571 if pp.impl is not None:
7572 klass, _, num = pp.impl
7574 ("header", "IMPLICIT: "), "%s%d" % (TagClassReprs[klass], num),
7577 lines.append(["OPTIONAL"])
7579 lines.append(["DEFAULT"])
7580 if len(pp.decode_path) > 0:
7581 ent = pp.decode_path[-1]
7582 if isinstance(ent, DecodePathDefBy):
7584 value = str(ent.defined_by)
7585 oid_name = find_oid_name(
7586 ent.defined_by.asn1_type_name, oid_maps, value,
7588 lines.append([("header", "DEFINED BY: "), "%s" % (
7589 value if oid_name is None
7590 else "%s (%s)" % (oid_name, value)
7593 if pp.value is not None:
7594 lines.append([("header", "Value: "), pp.value])
7596 len(oid_maps) > 0 and
7597 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
7599 for oid_map in oid_maps:
7600 oid_name = oid_map.get(pp.value)
7601 if oid_name is not None:
7602 lines.append([("header", "Human: "), oid_name])
7604 if pp.asn1_type_name == Integer.asn1_type_name:
7606 ("header", "Decimal: "), "%d" % int(pp.obj),
7609 ("header", "Hexadecimal: "), colonize_hex(pp.obj.tohex()),
7611 if pp.blob.__class__ == bytes:
7612 blob = hexenc(pp.blob).upper()
7613 for i in range(0, len(blob), 32):
7614 lines.append([colonize_hex(blob[i:i + 32])])
7615 elif pp.blob.__class__ == tuple:
7616 lines.append([", ".join(pp.blob)])
7617 self.state["info"]._set_body([urwid.Text(line) for line in lines])
7618 self.state["info_bar"].set_completion(0)
7620 def selectable(self):
7621 if self.state["widget_current"] != self:
7622 self.state["widget_current"] = self
7623 self.scrolled["info"] = False
7624 self.scrolled["hexdump"] = False
7625 self._state_update()
7626 return super().selectable()
7628 def _get_display_text_without_offset(self):
7629 pp, constructed = self._get_pp()
7630 style = "constructed" if constructed else ""
7631 if len(pp.decode_path) == 0:
7632 return (style, pp.obj_name)
7633 if pp.asn1_type_name == "EOC":
7634 return ("eoc", "EOC")
7635 ent = pp.decode_path[-1]
7636 if isinstance(ent, DecodePathDefBy):
7637 value = str(ent.defined_by)
7638 oid_name = find_oid_name(
7639 ent.defined_by.asn1_type_name, oid_maps, value,
7641 return ("defby", "DEFBY:" + (
7642 value if oid_name is None else oid_name
7646 def get_display_text(self):
7647 pp, _ = self._get_pp()
7648 style, ent = self._get_display_text_without_offset()
7649 return [(style, ent), " [%d]" % pp.offset]
7651 def _scroll(self, what, step):
7652 self.state[what]._invalidate()
7653 pos = self.state[what].focus_position
7654 if not self.scrolled[what]:
7655 self.scrolled[what] = True
7657 pos = max(0, pos + step)
7658 pos = min(pos, len(self.state[what]._body.positions()) - 1)
7659 self.state[what].set_focus(pos)
7660 self.state[what].set_focus_valign("top")
7661 self.state[what + "_bar"].set_completion(
7662 (100 * pos) // len(self.state[what]._body.positions())
7665 def keypress(self, size, key):
7667 raise urwid.ExitMainLoop()
7670 self.expanded = not self.expanded
7671 self.update_expanded_icon()
7674 hexdump_steps = {"j": 1, "k": -1, "J": 5, "K": -5}
7675 if key in hexdump_steps:
7676 self._scroll("hexdump", hexdump_steps[key])
7679 info_steps = {"h": 1, "l": -1, "H": 5, "L": -5}
7680 if key in info_steps:
7681 self._scroll("info", info_steps[key])
7684 if key in ("d", "D"):
7685 pp, _ = self._get_pp()
7686 dp = ":".join(str(p) for p in pp.decode_path)
7687 dp = dp.replace(" ", "_")
7690 if key == "d" or pp.expl_offset is None:
7691 data = self.state["raw"][pp.offset:(
7692 pp.offset + pp.tlen + pp.llen + pp.vlen
7695 data = self.state["raw"][pp.expl_offset:(
7696 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen
7700 def duplicate_path(dp, ctr):
7703 return "%s.%d" % (dp, ctr)
7706 if not path_exists(duplicate_path(dp, ctr)):
7709 dp = duplicate_path(dp, ctr)
7710 with open(dp, "wb") as fd:
7712 self.state["decode_path"].set_text(
7713 ("warning", "Saved to: " + dp)
7716 return super().keypress(size, key)
7718 class PN(urwid.ParentNode):
7719 def __init__(self, state, value, *args, **kwargs):
7721 if not hasattr(value, "_fields"):
7723 super().__init__(value, *args, **kwargs)
7725 def load_widget(self):
7726 return TW(self.state, self)
7728 def load_child_keys(self):
7729 value = self.get_value()
7730 if hasattr(value, "_fields"):
7732 return range(len(value[1:]))
7734 def load_child_node(self, key):
7737 self.get_value()[key + 1],
7740 depth=self.get_depth() + 1,
7743 class LabeledPG(urwid.ProgressBar):
7744 def __init__(self, label, *args, **kwargs):
7746 super().__init__(*args, **kwargs)
7749 return "%s: %s" % (self.label, super().get_text())
7751 WinHexdump = urwid.ListBox([urwid.Text("")])
7752 WinInfo = urwid.ListBox([urwid.Text("")])
7753 WinDecodePath = urwid.Text("", "center")
7754 WinInfoBar = LabeledPG("info", "pg-normal", "pg-complete")
7755 WinHexdumpBar = LabeledPG("hexdump", "pg-normal", "pg-complete")
7756 WinTree = urwid.TreeListBox(urwid.TreeWalker(PN(
7759 "hexed": list(hexdump(raw)),
7760 "widget_current": None,
7762 "info_bar": WinInfoBar,
7763 "hexdump": WinHexdump,
7764 "hexdump_bar": WinHexdumpBar,
7765 "decode_path": WinDecodePath,
7769 help_text = " ".join((
7771 "space:(un)collapse",
7772 "(pg)up/down/home/end:nav",
7773 "jkJK:hexdump hlHL:info",
7779 ("weight", 1, WinTree),
7780 ("weight", 2, urwid.Pile([
7781 urwid.LineBox(WinInfo),
7782 urwid.LineBox(WinHexdump),
7785 header=urwid.Columns([
7786 ("weight", 2, urwid.AttrWrap(WinDecodePath, "header")),
7787 ("weight", 1, WinInfoBar),
7788 ("weight", 1, WinHexdumpBar),
7790 footer=urwid.AttrWrap(urwid.Text(help_text), "help")
7793 ("header", "bold", ""),
7794 ("constructed", "bold", ""),
7795 ("help", "light magenta", ""),
7796 ("warning", "light red", ""),
7797 ("defby", "light red", ""),
7798 ("eoc", "dark red", ""),
7799 ("select-value", "light green", ""),
7800 ("select-expl", "light red", ""),
7801 ("pg-normal", "", "light blue"),
7802 ("pg-complete", "black", "yellow"),
7807 def main(): # pragma: no cover
7809 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/CER/DER decoder")
7810 parser.add_argument(
7814 help="Skip that number of bytes from the beginning",
7816 parser.add_argument(
7818 help="Python paths to dictionary with OIDs, comma separated",
7820 parser.add_argument(
7822 help="Python path to schema definition to use",
7824 parser.add_argument(
7825 "--defines-by-path",
7826 help="Python path to decoder's defines_by_path",
7828 parser.add_argument(
7830 action="store_true",
7831 help="Disallow BER encoding",
7833 parser.add_argument(
7834 "--print-decode-path",
7835 action="store_true",
7836 help="Print decode paths",
7838 parser.add_argument(
7839 "--decode-path-only",
7840 help="Print only specified decode path",
7842 parser.add_argument(
7844 action="store_true",
7845 help="Allow explicit tag out-of-bound",
7847 parser.add_argument(
7849 action="store_true",
7850 help="Turn on event generation mode",
7852 parser.add_argument(
7854 action="store_true",
7855 help="Start ASN.1 browser",
7857 parser.add_argument(
7859 type=argparse.FileType("rb"),
7860 help="Path to BER/CER/DER file you want to decode",
7862 args = parser.parse_args()
7864 raw = file_mmaped(args.RAWFile)[args.skip:]
7866 args.RAWFile.seek(args.skip)
7867 raw = memoryview(args.RAWFile.read())
7868 args.RAWFile.close()
7870 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
7871 if args.oids else ()
7873 from functools import partial
7875 schema = obj_by_path(args.schema)
7876 pprinter = partial(pprint, big_blobs=True)
7878 schema, pprinter = generic_decoder()
7880 "bered": not args.nobered,
7881 "allow_expl_oob": args.allow_expl_oob,
7883 if args.defines_by_path is not None:
7884 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
7886 obj, _ = schema().decode(raw, ctx=ctx)
7887 browse(raw, obj, oid_maps)
7888 from sys import exit as sys_exit
7890 from os import environ
7894 with_colours=environ.get("NO_COLOR") is None,
7895 with_decode_path=args.print_decode_path,
7897 () if args.decode_path_only is None else
7898 tuple(args.decode_path_only.split(":"))
7902 for decode_path, obj, tail in schema().decode_evgen(raw, ctx=ctx):
7903 print(pprinter(obj, decode_path=decode_path))
7905 obj, tail = schema().decode(raw, ctx=ctx)
7906 print(pprinter(obj))
7908 print("\nTrailing data: %s" % hexenc(tail))
7911 if __name__ == "__main__":
7912 from pyderasn import *