3 # cython: language_level=3
4 # pylint: disable=line-too-long,superfluous-parens,protected-access,too-many-lines
5 # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements
6 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures
7 # Copyright (C) 2017-2021 Sergey Matveev <stargrave@stargrave.org>
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU Lesser General Public License as
11 # published by the Free Software Foundation, version 3 of the License.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Python ASN.1 DER/CER/BER codec with abstract structures
22 This library allows you to marshal various structures in ASN.1 DER/CER
23 format, unmarshal BER/CER/DER ones.
27 >>> Integer().decod(raw) == i
30 There are primitive types, holding single values
31 (:py:class:`pyderasn.BitString`,
32 :py:class:`pyderasn.Boolean`,
33 :py:class:`pyderasn.Enumerated`,
34 :py:class:`pyderasn.GeneralizedTime`,
35 :py:class:`pyderasn.Integer`,
36 :py:class:`pyderasn.Null`,
37 :py:class:`pyderasn.ObjectIdentifier`,
38 :py:class:`pyderasn.OctetString`,
39 :py:class:`pyderasn.UTCTime`,
40 :py:class:`various strings <pyderasn.CommonString>`
41 (:py:class:`pyderasn.BMPString`,
42 :py:class:`pyderasn.GeneralString`,
43 :py:class:`pyderasn.GraphicString`,
44 :py:class:`pyderasn.IA5String`,
45 :py:class:`pyderasn.ISO646String`,
46 :py:class:`pyderasn.NumericString`,
47 :py:class:`pyderasn.PrintableString`,
48 :py:class:`pyderasn.T61String`,
49 :py:class:`pyderasn.TeletexString`,
50 :py:class:`pyderasn.UniversalString`,
51 :py:class:`pyderasn.UTF8String`,
52 :py:class:`pyderasn.VideotexString`,
53 :py:class:`pyderasn.VisibleString`)),
54 constructed types, holding multiple primitive types
55 (:py:class:`pyderasn.Sequence`,
56 :py:class:`pyderasn.SequenceOf`,
57 :py:class:`pyderasn.Set`,
58 :py:class:`pyderasn.SetOf`),
59 and special types like
60 :py:class:`pyderasn.Any` and
61 :py:class:`pyderasn.Choice`.
69 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
70 the default tag used during coding process. You can override it with
71 either ``IMPLICIT`` (using either ``impl`` keyword argument or ``impl``
72 class attribute), or ``EXPLICIT`` one (using either ``expl`` keyword
73 argument or ``expl`` class attribute). Both arguments take raw binary
74 string, containing that tag. You can **not** set implicit and explicit
77 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
78 functions, allowing you to easily create ``CONTEXT``
79 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
84 EXPLICIT tags always have **constructed** tag. PyDERASN does not
85 explicitly check correctness of schema input here.
89 Implicit tags have **primitive** (``tag_ctxp``) encoding for
94 >>> Integer(impl=tag_ctxp(1))
96 >>> Integer(expl=tag_ctxc(2))
99 Implicit tag is not explicitly shown.
101 Two objects of the same type, but with different implicit/explicit tags
104 You can get object's effective tag (either default or implicited) through
105 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
108 >>> tag_decode(tag_ctxc(123))
110 >>> klass, form, num = tag_decode(tag_ctxc(123))
111 >>> klass == TagClassContext
113 >>> form == TagFormConstructed
116 To determine if object has explicit tag, use ``expled`` boolean property
117 and ``expl_tag`` property, returning explicit tag's value.
122 Many objects in sequences could be ``OPTIONAL`` and could have
123 ``DEFAULT`` value. You can specify that object's property using
124 corresponding keyword arguments.
126 >>> Integer(optional=True, default=123)
127 INTEGER 123 OPTIONAL DEFAULT
129 Those specifications do not play any role in primitive value encoding,
130 but are taken into account when dealing with sequences holding them. For
131 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
134 class Version(Integer):
140 class TBSCertificate(Sequence):
142 ("version", Version(expl=tag_ctxc(0), default="v1")),
145 When default argument is used and value is not specified, then it equals
153 Some objects give ability to set value size constraints. This is either
154 possible integer value, or allowed length of various strings and
155 sequences. Constraints are set in the following way::
160 And values satisfaction is checked as: ``MIN <= X <= MAX``.
162 For simplicity you can also set bounds the following way::
164 bounded_x = X(bounds=(MIN, MAX))
166 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
172 All objects have ``ready`` boolean property, that tells if object is
173 ready to be encoded. If that kind of action is performed on unready
174 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
176 All objects are friendly to ``copy.copy()`` and copied objects can be
179 Also all objects can be safely ``pickle``-d, but pay attention that
180 pickling among different PyDERASN versions is prohibited.
187 Decoding is performed using :py:meth:`pyderasn.Obj.decode` method.
188 ``offset`` optional argument could be used to set initial object's
189 offset in the binary data, for convenience. It returns decoded object
190 and remaining unmarshalled data (tail). Internally all work is done on
191 ``memoryview(data)``, and you can leave returning tail as a memoryview,
192 by specifying ``leavemm=True`` argument.
194 Also note convenient :py:meth:`pyderasn.Obj.decod` method, that
195 immediately checks and raises if there is non-empty tail.
197 When object is decoded, ``decoded`` property is true and you can safely
198 use following properties:
200 * ``offset`` -- position including initial offset where object's tag starts
201 * ``tlen`` -- length of object's tag
202 * ``llen`` -- length of object's length value
203 * ``vlen`` -- length of object's value
204 * ``tlvlen`` -- length of the whole object
206 Pay attention that those values do **not** include anything related to
207 explicit tag. If you want to know information about it, then use:
209 * ``expled`` -- to know if explicit tag is set
210 * ``expl_offset`` (it is lesser than ``offset``)
213 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
214 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
216 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
219 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
226 You can specify so called context keyword argument during
227 :py:meth:`pyderasn.Obj.decode` invocation. It is dictionary containing
228 various options governing decoding process.
230 Currently available context options:
232 * :ref:`allow_default_values <allow_default_values_ctx>`
233 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
234 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
235 * :ref:`bered <bered_ctx>`
236 * :ref:`defines_by_path <defines_by_path_ctx>`
237 * :ref:`evgen_mode_upto <evgen_mode_upto_ctx>`
244 All objects have ``pps()`` method, that is a generator of
245 :py:class:`pyderasn.PP` namedtuple, holding various raw information
246 about the object. If ``pps`` is called on sequences, then all underlying
247 ``PP`` will be yielded.
249 You can use :py:func:`pyderasn.pp_console_row` function, converting
250 those ``PP`` to human readable string. Actually exactly it is used for
251 all object ``repr``. But it is easy to write custom formatters.
253 >>> from pyderasn import pprint
254 >>> encoded = Integer(-12345).encode()
255 >>> obj, tail = Integer().decode(encoded)
256 >>> print(pprint(obj))
257 0 [1,1, 2] INTEGER -12345
261 Example certificate::
263 >>> print(pprint(crt))
264 0 [1,3,1604] Certificate SEQUENCE
265 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
266 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
267 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
268 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
269 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
270 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
272 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
273 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
274 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
275 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
276 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
277 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
278 . . . . . . . 13:02:45:53
280 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
281 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
282 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
284 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
285 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
286 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
291 Let's parse that output, human::
293 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
294 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
295 0 1 2 3 4 5 6 7 8 9 10 11
299 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
305 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
311 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
316 Offset of the object, where its DER/BER encoding begins.
317 Pay attention that it does **not** include explicit tag.
319 If explicit tag exists, then this is its length (tag + encoded length).
321 Length of object's tag. For example CHOICE does not have its own tag,
324 Length of encoded length.
326 Length of encoded value.
328 Visual indentation to show the depth of object in the hierarchy.
330 Object's name inside SEQUENCE/CHOICE.
332 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
333 here. "IMPLICIT" is omitted.
335 Object's class name, if set. Omitted if it is just an ordinary simple
336 value (like with ``algorithm`` in example above).
340 Object's value, if set. Can consist of multiple words (like OCTET/BIT
341 STRINGs above). We see ``v3`` value in Version, because it is named.
342 ``rdnSequence`` is the choice of CHOICE type.
344 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
345 default one, specified in the schema.
347 Shows does object contains any kind of BER encoded data (possibly
348 Sequence holding BER-encoded underlying value).
350 Only applicable to BER encoded data. Indefinite length encoding mark.
352 Only applicable to BER encoded data. If object has BER-specific
353 encoding, then ``BER`` will be shown. It does not depend on indefinite
354 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
355 (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime``
358 Also it could be helpful to add quick ASN.1 pprinting command in your
359 pdb's configuration file::
361 alias pp1 import pyderasn ;; print(pyderasn.pprint(%1, oid_maps=(locals().get("OID_STR_TO_NAME", {}),)))
368 ASN.1 structures often have ANY and OCTET STRING fields, that are
369 DEFINED BY some previously met ObjectIdentifier. This library provides
370 ability to specify mapping between some OID and field that must be
371 decoded with specific specification.
378 :py:class:`pyderasn.ObjectIdentifier` field inside
379 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
380 necessary for decoding structures. For example, CMS (:rfc:`5652`)
383 class ContentInfo(Sequence):
385 ("contentType", ContentType(defines=((("content",), {
386 id_digestedData: DigestedData(),
387 id_signedData: SignedData(),
389 ("content", Any(expl=tag_ctxc(0))),
392 ``contentType`` field tells that it defines that ``content`` must be
393 decoded with ``SignedData`` specification, if ``contentType`` equals to
394 ``id-signedData``. The same applies to ``DigestedData``. If
395 ``contentType`` contains unknown OID, then no automatic decoding is
398 You can specify multiple fields, that will be autodecoded -- that is why
399 ``defines`` kwarg is a sequence. You can specify defined field
400 relatively or absolutely to current decode path. For example ``defines``
401 for AlgorithmIdentifier of X.509's
402 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
406 id_ecPublicKey: ECParameters(),
407 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
409 (("..", "subjectPublicKey"), {
410 id_rsaEncryption: RSAPublicKey(),
411 id_GostR3410_2001: OctetString(),
415 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
416 autodecode its parameters inside SPKI's algorithm and its public key
419 Following types can be automatically decoded (DEFINED BY):
421 * :py:class:`pyderasn.Any`
422 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
423 * :py:class:`pyderasn.OctetString`
424 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
425 ``Any``/``BitString``/``OctetString``-s
427 When any of those fields is automatically decoded, then ``.defined``
428 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
429 was defined, ``value`` contains corresponding decoded value. For example
430 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
432 .. _defines_by_path_ctx:
434 defines_by_path context option
435 ______________________________
437 Sometimes you either can not or do not want to explicitly set *defines*
438 in the schema. You can dynamically apply those definitions when calling
439 :py:meth:`pyderasn.Obj.decode` method.
441 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
442 value must be sequence of following tuples::
444 (decode_path, defines)
446 where ``decode_path`` is a tuple holding so-called decode path to the
447 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
448 ``defines``, holding exactly the same value as accepted in its
449 :ref:`keyword argument <defines>`.
451 For example, again for CMS, you want to automatically decode
452 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
453 structures it may hold. Also, automatically decode ``controlSequence``
456 content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
459 ((("content",), {id_signedData: SignedData()}),),
464 DecodePathDefBy(id_signedData),
469 id_cct_PKIData: PKIData(),
470 id_cct_PKIResponse: PKIResponse(),
476 DecodePathDefBy(id_signedData),
479 DecodePathDefBy(id_cct_PKIResponse),
485 id_cmc_recipientNonce: RecipientNonce(),
486 id_cmc_senderNonce: SenderNonce(),
487 id_cmc_statusInfoV2: CMCStatusInfoV2(),
488 id_cmc_transactionId: TransactionId(),
493 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
494 First function is useful for path construction when some automatic
495 decoding is already done. ``any`` means literally any value it meet --
496 useful for SEQUENCE/SET OF-s.
503 By default PyDERASN accepts only DER encoded data. By default it encodes
504 to DER. But you can optionally enable BER decoding with setting
505 ``bered`` :ref:`context <ctx>` argument to True. Indefinite lengths and
506 constructed primitive types should be parsed successfully.
508 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
509 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
510 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``,
511 ``UTCTime``, ``GeneralizedTime`` can contain it.
512 * If object has an indefinite length encoding, then its ``lenindef``
513 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
514 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
516 * If object has an indefinite length encoded explicit tag, then
517 ``expl_lenindef`` is set to True.
518 * If object has either any of BER-related encoding (explicit tag
519 indefinite length, object's indefinite length, BER-encoding) or any
520 underlying component has that kind of encoding, then ``bered``
521 attribute is set to True. For example SignedData CMS can have
522 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
523 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
525 EOC (end-of-contents) token's length is taken in advance in object's
528 .. _allow_expl_oob_ctx:
530 Allow explicit tag out-of-bound
531 -------------------------------
533 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
534 one value, more than one object. If you set ``allow_expl_oob`` context
535 option to True, then no error will be raised and that invalid encoding
536 will be silently further processed. But pay attention that offsets and
537 lengths will be invalid in that case.
541 This option should be used only for skipping some decode errors, just
542 to see the decoded structure somehow.
546 Streaming and dealing with huge structures
547 ------------------------------------------
554 ASN.1 structures can be huge, they can hold millions of objects inside
555 (for example Certificate Revocation Lists (CRL), holding revocation
556 state for every previously issued X.509 certificate). CACert.org's 8 MiB
557 CRL file takes more than half a gigabyte of memory to hold the decoded
560 If you just simply want to check the signature over the ``tbsCertList``,
561 you can create specialized schema with that field represented as
562 OctetString for example::
564 class TBSCertListFast(Sequence):
567 ("revokedCertificates", OctetString(
568 impl=SequenceOf.tag_default,
574 This allows you to quickly decode a few fields and check the signature
575 over the ``tbsCertList`` bytes.
577 But how can you get all certificate's serial number from it, after you
578 trust that CRL after signature validation? You can use so called
579 ``evgen`` (event generation) mode, to catch the events/facts of some
580 successful object decoding. Let's use command line capabilities::
582 $ python -m pyderasn --schema tests.test_crl:CertificateList --evgen revoke.crl
583 10 [1,1, 1] . . version: Version INTEGER v2 (01) OPTIONAL
584 15 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
585 26 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
586 13 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
587 34 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.10
588 39 [0,0, 9] . . . . . . value: [UNIV 19] AttributeValue ANY
589 32 [1,1, 14] . . . . . 0: AttributeTypeAndValue SEQUENCE
590 30 [1,1, 16] . . . . 0: RelativeDistinguishedName SET OF
592 188 [1,1, 1] . . . . userCertificate: CertificateSerialNumber INTEGER 17 (11)
593 191 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
594 191 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
595 191 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
596 186 [1,1, 18] . . . 0: RevokedCertificate SEQUENCE
597 208 [1,1, 1] . . . . userCertificate: CertificateSerialNumber INTEGER 20 (14)
598 211 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
599 211 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
600 211 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
601 206 [1,1, 18] . . . 1: RevokedCertificate SEQUENCE
603 9144992 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
604 9144992 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2020-02-08T07:25:06
605 9144985 [1,1, 20] . . . 415755: RevokedCertificate SEQUENCE
606 181 [1,4,9144821] . . revokedCertificates: RevokedCertificates SEQUENCE OF OPTIONAL
607 5 [1,4,9144997] . tbsCertList: TBSCertList SEQUENCE
608 9145009 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
609 9145020 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
610 9145007 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
611 9145022 [1,3, 513] . signatureValue: BIT STRING 4096 bits
612 0 [1,4,9145534] CertificateList SEQUENCE
614 Here we see how decoder works: it decodes SEQUENCE's tag, length, then
615 decodes underlying values. It can not tell if SEQUENCE is decoded, so
616 the event of the upper level SEQUENCE is the last one we see.
617 ``version`` field is just a single INTEGER -- it is decoded and event is
618 fired immediately. Then we see that ``algorithm`` and ``parameters``
619 fields are decoded and only after them the ``signature`` SEQUENCE is
620 fired as a successfully decoded. There are 4 events for each revoked
621 certificate entry in that CRL: ``userCertificate`` serial number,
622 ``utcTime`` of ``revocationDate`` CHOICE, ``RevokedCertificate`` itself
623 as a one of entity in ``revokedCertificates`` SEQUENCE OF.
625 We can do that in our ordinary Python code and understand where we are
626 by looking at deterministically generated decode paths (do not forget
627 about useful ``--print-decode-path`` CLI option). We must use
628 :py:meth:`pyderasn.Obj.decode_evgen` method, instead of ordinary
629 :py:meth:`pyderasn.Obj.decode`. It is generator yielding ``(decode_path,
630 obj, tail)`` tuples::
632 for decode_path, obj, _ in CertificateList().decode_evgen(crl_raw):
634 len(decode_path) == 4 and
635 decode_path[:2] == ("tbsCertList", "revokedCertificates"),
636 decode_path[3] == "userCertificate"
638 print("serial number:", int(obj))
640 Virtually it does not take any memory except at least needed for single
641 object storage. You can easily use that mode to determine required
642 object ``.offset`` and ``.*len`` to be able to decode it separately, or
643 maybe verify signature upon it just by taking bytes by ``.offset`` and
646 .. _evgen_mode_upto_ctx:
651 There is full ability to get any kind of data from the CRL in the
652 example above. However it is not too convenient to get the whole
653 ``RevokedCertificate`` structure, that is pretty lightweight and one may
654 do not want to disassemble it. You can use ``evgen_mode_upto``
655 :ref:`ctx <ctx>` option that semantically equals to
656 :ref:`defines_by_path <defines_by_path_ctx>` -- list of decode paths
657 mapped to any non-None value. If specified decode path is met, then any
658 subsequent objects won't be decoded in evgen mode. That allows us to
659 parse the CRL above with fully assembled ``RevokedCertificate``::
661 for decode_path, obj, _ in CertificateList().decode_evgen(
663 ctx={"evgen_mode_upto": (
664 (("tbsCertList", "revokedCertificates", any), True),
668 len(decode_path) == 3 and
669 decode_path[:2] == ("tbsCertList", "revokedCertificates"),
671 print("serial number:", int(obj["userCertificate"]))
675 SEQUENCE/SET values with DEFAULT specified are automatically decoded
683 POSIX compliant systems have ``mmap`` syscall, giving ability to work
684 the memory mapped file. You can deal with the file like it was an
685 ordinary binary string, allowing you not to load it to the memory first.
686 Also you can use them as an input for OCTET STRING, taking no Python
687 memory for their storage.
689 There is convenient :py:func:`pyderasn.file_mmaped` function that
690 creates read-only memoryview on the file contents::
692 with open("huge", "rb") as fd:
693 raw = file_mmaped(fd)
694 obj = Something.decode(raw)
698 mmap maps the **whole** file. So it plays no role if you seek-ed it
699 before. Take the slice of the resulting memoryview with required
704 If you use ZFS as underlying storage, then pay attention that
705 currently most platforms does not deal good with ZFS ARC and ordinary
706 page cache used for mmaps. It can take twice the necessary size in
707 the memory: both in page cache and ZFS ARC.
712 We can parse any kind of data now, but how can we produce files
713 streamingly, without storing their encoded representation in memory?
714 SEQUENCE by default encodes in memory all its values, joins them in huge
715 binary string, just to know the exact size of SEQUENCE's value for
716 encoding it in TLV. DER requires you to know all exact sizes of the
719 You can use CER encoding mode, that slightly differs from the DER, but
720 does not require exact sizes knowledge, allowing streaming encoding
721 directly to some writer/buffer. Just use
722 :py:meth:`pyderasn.Obj.encode_cer` method, providing the writer where
723 encoded data will flow::
725 with open("result", "wb") as fd:
726 obj.encode_cer(fd.write)
731 obj.encode_cer(buf.write)
733 If you do not want to create in-memory buffer every time, then you can
734 use :py:func:`pyderasn.encode_cer` function::
736 data = encode_cer(obj)
738 Remember that CER is **not valid** DER in most cases, so you **have to**
739 use :ref:`bered <bered_ctx>` :ref:`ctx <ctx>` option during its
740 decoding. Also currently there is **no** validation that provided CER is
741 valid one -- you are sure that it has only valid BER encoding.
745 SET OF values can not be streamingly encoded, because they are
746 required to be sorted byte-by-byte. Big SET OF values still will take
747 much memory. Use neither SET nor SET OF values, as modern ASN.1
750 Do not forget about using :ref:`mmap-ed <mmap>` memoryviews for your
751 OCTET STRINGs! They will be streamingly copied from underlying file to
752 the buffer using 1 KB chunks.
754 Some structures require that some of the elements have to be forcefully
755 DER encoded. For example ``SignedData`` CMS requires you to encode
756 ``SignedAttributes`` and X.509 certificates in DER form, allowing you to
757 encode everything else in BER. You can tell any of the structures to be
758 forcefully encoded in DER during CER encoding, by specifying
759 ``der_forced=True`` attribute::
761 class Certificate(Sequence):
765 class SignedAttributes(SetOf):
767 bounds = (1, float("+inf"))
770 .. _agg_octet_string:
775 In most cases, huge quantity of binary data is stored as OCTET STRING.
776 CER encoding splits it on 1 KB chunks. BER allows splitting on various
777 levels of chunks inclusion::
779 SOME STRING[CONSTRUCTED]
780 OCTET STRING[CONSTRUCTED]
781 OCTET STRING[PRIMITIVE]
783 OCTET STRING[PRIMITIVE]
785 OCTET STRING[PRIMITIVE]
787 OCTET STRING[PRIMITIVE]
789 OCTET STRING[CONSTRUCTED]
790 OCTET STRING[PRIMITIVE]
792 OCTET STRING[PRIMITIVE]
794 OCTET STRING[CONSTRUCTED]
795 OCTET STRING[CONSTRUCTED]
796 OCTET STRING[PRIMITIVE]
799 You can not just take the offset and some ``.vlen`` of the STRING and
800 treat it as the payload. If you decode it without
801 :ref:`evgen mode <evgen_mode>`, then it will be automatically aggregated
802 and ``bytes()`` will give the whole payload contents.
804 You are forced to use :ref:`evgen mode <evgen_mode>` for decoding for
805 small memory footprint. There is convenient
806 :py:func:`pyderasn.agg_octet_string` helper for reconstructing the
807 payload. Let's assume you have got BER/CER encoded ``ContentInfo`` with
808 huge ``SignedData`` and ``EncapsulatedContentInfo``. Let's calculate the
809 SHA512 digest of its ``eContent``::
811 fd = open("data.p7m", "rb")
812 raw = file_mmaped(fd)
813 ctx = {"bered": True}
814 for decode_path, obj, _ in ContentInfo().decode_evgen(raw, ctx=ctx):
815 if decode_path == ("content",):
819 raise ValueError("no content found")
820 hasher_state = sha512()
822 hasher_state.update(data)
824 evgens = SignedData().decode_evgen(
825 raw[content.offset:],
826 offset=content.offset,
829 agg_octet_string(evgens, ("encapContentInfo", "eContent"), raw, hasher)
831 digest = hasher_state.digest()
833 Simply replace ``hasher`` with some writeable file's ``fd.write`` to
834 copy the payload (without BER/CER encoding interleaved overhead) in it.
835 Virtually it won't take memory more than for keeping small structures
836 and 1 KB binary chunks.
840 SEQUENCE OF iterators
841 _____________________
843 You can use iterators as a value in :py:class:`pyderasn.SequenceOf`
844 classes. The only difference with providing the full list of objects, is
845 that type and bounds checking is done during encoding process. Also
846 sequence's value will be emptied after encoding, forcing you to set its
849 This is very useful when you have to create some huge objects, like
850 CRLs, with thousands and millions of entities inside. You can write the
851 generator taking necessary data from the database and giving the
852 ``RevokedCertificate`` objects. Only binary representation of that
853 objects will take memory during DER encoding.
858 There is ability to do 2-pass encoding to DER, writing results directly
859 to specified writer (buffer, file, whatever). It could be 1.5+ times
860 slower than ordinary encoding, but it takes little memory for 1st pass
861 state storing. For example, 1st pass state for CACert.org's CRL with
862 ~416K of certificate entries takes nearly 3.5 MB of memory.
863 ``SignedData`` with several gigabyte ``EncapsulatedContentInfo`` takes
864 nearly 0.5 KB of memory.
866 If you use :ref:`mmap-ed <mmap>` memoryviews, :ref:`SEQUENCE OF
867 iterators <seqof-iterators>` and write directly to opened file, then
868 there is very small memory footprint.
870 1st pass traverses through all the objects of the structure and returns
871 the size of DER encoded structure, together with 1st pass state object.
872 That state contains precalculated lengths for various objects inside the
877 fulllen, state = obj.encode1st()
879 2nd pass takes the writer and 1st pass state. It traverses through all
880 the objects again, but writes their encoded representation to the writer.
884 with open("result", "wb") as fd:
885 obj.encode2nd(fd.write, iter(state))
889 You **MUST NOT** use 1st pass state if anything is changed in the
890 objects. It is intended to be used immediately after 1st pass is
893 If you use :ref:`SEQUENCE OF iterators <seqof-iterators>`, then you
894 have to reinitialize the values after the 1st pass. And you **have to**
895 be sure that the iterator gives exactly the same values as previously.
896 Yes, you have to run your iterator twice -- because this is two pass
899 If you want to encode to the memory, then you can use convenient
900 :py:func:`pyderasn.encode2pass` helper.
906 .. autofunction:: pyderasn.browse
910 .. autoclass:: pyderasn.Obj
918 .. autoclass:: pyderasn.Boolean
923 .. autoclass:: pyderasn.Integer
924 :members: __init__, named, tohex
928 .. autoclass:: pyderasn.BitString
929 :members: __init__, bit_len, named
933 .. autoclass:: pyderasn.OctetString
938 .. autoclass:: pyderasn.Null
943 .. autoclass:: pyderasn.ObjectIdentifier
948 .. autoclass:: pyderasn.Enumerated
952 .. autoclass:: pyderasn.CommonString
956 .. autoclass:: pyderasn.NumericString
960 .. autoclass:: pyderasn.PrintableString
961 :members: __init__, allow_asterisk, allow_ampersand
965 .. autoclass:: pyderasn.IA5String
969 .. autoclass:: pyderasn.VisibleString
973 .. autoclass:: pyderasn.UTCTime
974 :members: __init__, todatetime
978 .. autoclass:: pyderasn.GeneralizedTime
979 :members: __init__, todatetime
986 .. autoclass:: pyderasn.Choice
987 :members: __init__, choice, value
991 .. autoclass:: PrimitiveTypes
995 .. autoclass:: pyderasn.Any
1003 .. autoclass:: pyderasn.Sequence
1008 .. autoclass:: pyderasn.Set
1013 .. autoclass:: pyderasn.SequenceOf
1018 .. autoclass:: pyderasn.SetOf
1024 .. autofunction:: pyderasn.abs_decode_path
1025 .. autofunction:: pyderasn.agg_octet_string
1026 .. autofunction:: pyderasn.ascii_visualize
1027 .. autofunction:: pyderasn.colonize_hex
1028 .. autofunction:: pyderasn.encode2pass
1029 .. autofunction:: pyderasn.encode_cer
1030 .. autofunction:: pyderasn.file_mmaped
1031 .. autofunction:: pyderasn.hexenc
1032 .. autofunction:: pyderasn.hexdec
1033 .. autofunction:: pyderasn.hexdump
1034 .. autofunction:: pyderasn.tag_encode
1035 .. autofunction:: pyderasn.tag_decode
1036 .. autofunction:: pyderasn.tag_ctxp
1037 .. autofunction:: pyderasn.tag_ctxc
1038 .. autoclass:: pyderasn.DecodeError
1040 .. autoclass:: pyderasn.NotEnoughData
1041 .. autoclass:: pyderasn.ExceedingData
1042 .. autoclass:: pyderasn.LenIndefForm
1043 .. autoclass:: pyderasn.TagMismatch
1044 .. autoclass:: pyderasn.InvalidLength
1045 .. autoclass:: pyderasn.InvalidOID
1046 .. autoclass:: pyderasn.ObjUnknown
1047 .. autoclass:: pyderasn.ObjNotReady
1048 .. autoclass:: pyderasn.InvalidValueType
1049 .. autoclass:: pyderasn.BoundsError
1056 You can decode DER/BER files using command line abilities::
1058 $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
1060 If there is no schema for your file, then you can try parsing it without,
1061 but of course IMPLICIT tags will often make it impossible. But result is
1062 good enough for the certificate above::
1064 $ python -m pyderasn path/to/file
1065 0 [1,3,1604] . >: SEQUENCE OF
1066 4 [1,3,1453] . . >: SEQUENCE OF
1067 8 [0,0, 5] . . . . >: [0] ANY
1068 . . . . . A0:03:02:01:02
1069 13 [1,1, 3] . . . . >: INTEGER 61595
1070 18 [1,1, 13] . . . . >: SEQUENCE OF
1071 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1072 31 [1,1, 0] . . . . . . >: NULL
1073 33 [1,3, 274] . . . . >: SEQUENCE OF
1074 37 [1,1, 11] . . . . . . >: SET OF
1075 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
1076 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
1077 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
1079 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
1080 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
1081 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
1082 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
1083 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
1084 . . . . . . . . . 61:2E:63:6F:6D:2F
1085 1461 [1,1, 13] . . >: SEQUENCE OF
1086 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1087 1474 [1,1, 0] . . . . >: NULL
1088 1476 [1,2, 129] . . >: BIT STRING 1024 bits
1089 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
1090 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
1096 If you have got dictionaries with ObjectIdentifiers, like example one
1097 from ``tests/test_crts.py``::
1100 "1.2.840.113549.1.1.1": "id-rsaEncryption",
1101 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
1103 "2.5.4.10": "id-at-organizationName",
1104 "2.5.4.11": "id-at-organizationalUnitName",
1107 then you can pass it to pretty printer to see human readable OIDs::
1109 $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
1111 37 [1,1, 11] . . . . . . >: SET OF
1112 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
1113 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
1114 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
1115 50 [1,1, 18] . . . . . . >: SET OF
1116 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
1117 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
1118 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
1119 70 [1,1, 18] . . . . . . >: SET OF
1120 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
1121 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
1122 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
1128 Each decoded element has so-called decode path: sequence of structure
1129 names it is passing during the decode process. Each element has its own
1130 unique path inside the whole ASN.1 tree. You can print it out with
1131 ``--print-decode-path`` option::
1133 $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
1134 0 [1,3,1604] Certificate SEQUENCE []
1135 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
1136 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
1137 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
1138 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
1139 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
1140 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
1142 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
1143 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
1144 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
1145 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
1146 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
1147 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
1148 . . . . . . . 13:02:45:53
1149 46 [1,1, 2] . . . . . . . DEFINED BY 2.5.4.6: CountryName PrintableString ES [tbsCertificate:issuer:rdnSequence:0:0:value:DEFINED BY 2.5.4.6]
1152 Now you can print only the specified tree, for example signature algorithm::
1154 $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
1155 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
1156 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1157 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
1161 from array import array
1162 from collections import namedtuple
1163 from collections import OrderedDict
1164 from copy import copy
1165 from datetime import datetime
1166 from datetime import timedelta
1167 from io import BytesIO
1168 from math import ceil
1169 from operator import attrgetter
1170 from string import ascii_letters
1171 from string import digits
1172 from struct import Struct as struct_Struct
1173 from sys import maxsize as sys_maxsize
1174 from sys import version_info
1175 from unicodedata import category as unicat
1178 from termcolor import colored
1179 except ImportError: # pragma: no cover
1180 def colored(what, *args, **kwargs):
1184 from dateutil.tz import UTC as tzUTC
1185 except ImportError: # pragma: no cover
1237 "TagClassApplication",
1240 "TagClassUniversal",
1241 "TagFormConstructed",
1252 TagClassUniversal = 0
1253 TagClassApplication = 1 << 6
1254 TagClassContext = 1 << 7
1255 TagClassPrivate = 1 << 6 | 1 << 7
1256 TagFormPrimitive = 0
1257 TagFormConstructed = 1 << 5
1259 TagClassContext: "",
1260 TagClassApplication: "APPLICATION ",
1261 TagClassPrivate: "PRIVATE ",
1262 TagClassUniversal: "UNIV ",
1266 LENINDEF = b"\x80" # length indefinite mark
1267 LENINDEF_PP_CHAR = "∞"
1268 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
1269 SET01 = frozenset("01")
1270 DECIMALS = frozenset(digits)
1271 DECIMAL_SIGNS = ".,"
1272 NEXT_ATTR_NAME = "__next__"
1275 def file_mmaped(fd):
1276 """Make mmap-ed memoryview for reading from file
1278 :param fd: file object
1279 :returns: memoryview over read-only mmap-ing of the whole file
1283 It does not work under Windows.
1286 return memoryview(mmap.mmap(fd.fileno(), length=0, prot=mmap.PROT_READ))
1290 if not set(value) <= DECIMALS:
1291 raise ValueError("non-pure integer")
1295 def fractions2float(fractions_raw):
1296 pureint(fractions_raw)
1297 return float("0." + fractions_raw)
1300 def get_def_by_path(defines_by_path, sub_decode_path):
1301 """Get define by decode path
1303 for path, define in defines_by_path:
1304 if len(path) != len(sub_decode_path):
1306 for p1, p2 in zip(path, sub_decode_path):
1307 if (p1 is not any) and (p1 != p2):
1313 ########################################################################
1315 ########################################################################
1317 class ASN1Error(ValueError):
1321 class DecodeError(ASN1Error):
1322 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
1324 :param str msg: reason of decode failing
1325 :param klass: optional exact DecodeError inherited class (like
1326 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
1327 :py:exc:`InvalidLength`)
1328 :param decode_path: tuple of strings. It contains human
1329 readable names of the fields through which
1330 decoding process has passed
1331 :param int offset: binary offset where failure happened
1336 self.decode_path = decode_path
1337 self.offset = offset
1342 "" if self.klass is None else self.klass.__name__,
1344 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
1345 if len(self.decode_path) > 0 else ""
1347 ("(at %d)" % self.offset) if self.offset > 0 else "",
1353 return "%s(%s)" % (self.__class__.__name__, self)
1356 class NotEnoughData(DecodeError):
1360 class ExceedingData(ASN1Error):
1361 def __init__(self, nbytes):
1363 self.nbytes = nbytes
1366 return "%d trailing bytes" % self.nbytes
1369 return "%s(%s)" % (self.__class__.__name__, self)
1372 class LenIndefForm(DecodeError):
1376 class TagMismatch(DecodeError):
1380 class InvalidLength(DecodeError):
1384 class InvalidOID(DecodeError):
1388 class ObjUnknown(ASN1Error):
1389 def __init__(self, name):
1394 return "object is unknown: %s" % self.name
1397 return "%s(%s)" % (self.__class__.__name__, self)
1400 class ObjNotReady(ASN1Error):
1401 def __init__(self, name):
1406 return "object is not ready: %s" % self.name
1409 return "%s(%s)" % (self.__class__.__name__, self)
1412 class InvalidValueType(ASN1Error):
1413 def __init__(self, expected_types):
1415 self.expected_types = expected_types
1418 return "invalid value type, expected: %s" % ", ".join(
1419 [repr(t) for t in self.expected_types]
1423 return "%s(%s)" % (self.__class__.__name__, self)
1426 class BoundsError(ASN1Error):
1427 def __init__(self, bound_min, value, bound_max):
1429 self.bound_min = bound_min
1431 self.bound_max = bound_max
1434 return "unsatisfied bounds: %s <= %s <= %s" % (
1441 return "%s(%s)" % (self.__class__.__name__, self)
1444 ########################################################################
1446 ########################################################################
1449 """Binary data to hexadecimal string convert
1451 return bytes.fromhex(data)
1455 """Hexadecimal string to binary data convert
1460 def int_bytes_len(num, byte_len=8):
1463 return int(ceil(float(num.bit_length()) / byte_len))
1466 def zero_ended_encode(num):
1467 octets = bytearray(int_bytes_len(num, 7))
1469 octets[i] = num & 0x7F
1473 octets[i] = 0x80 | (num & 0x7F)
1476 return bytes(octets)
1479 int2byte = struct_Struct(">B").pack
1482 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
1483 """Encode tag to binary form
1485 :param int num: tag's number
1486 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
1487 :py:data:`pyderasn.TagClassContext`,
1488 :py:data:`pyderasn.TagClassApplication`,
1489 :py:data:`pyderasn.TagClassPrivate`)
1490 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
1491 :py:data:`pyderasn.TagFormConstructed`)
1495 return int2byte(klass | form | num)
1496 # [XX|X|11111][1.......][1.......] ... [0.......]
1497 return int2byte(klass | form | 31) + zero_ended_encode(num)
1500 def tag_decode(tag):
1501 """Decode tag from binary form
1505 No validation is performed, assuming that it has already passed.
1507 It returns tuple with three integers, as
1508 :py:func:`pyderasn.tag_encode` accepts.
1510 first_octet = tag[0]
1511 klass = first_octet & 0xC0
1512 form = first_octet & 0x20
1513 if first_octet & 0x1F < 0x1F:
1514 return (klass, form, first_octet & 0x1F)
1516 for octet in tag[1:]:
1519 return (klass, form, num)
1523 """Create CONTEXT PRIMITIVE tag
1525 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
1529 """Create CONTEXT CONSTRUCTED tag
1531 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
1534 def tag_strip(data):
1535 """Take off tag from the data
1537 :returns: (encoded tag, tag length, remaining data)
1540 raise NotEnoughData("no data at all")
1541 if data[0] & 0x1F < 31:
1542 return data[:1], 1, data[1:]
1547 raise DecodeError("unfinished tag")
1548 if data[i] & 0x80 == 0:
1550 if i == 1 and data[1] < 0x1F:
1551 raise DecodeError("unexpected long form")
1552 if i > 1 and data[1] & 0x7F == 0:
1553 raise DecodeError("leading zero byte in tag value")
1555 return data[:i], i, data[i:]
1561 octets = bytearray(int_bytes_len(l) + 1)
1562 octets[0] = 0x80 | (len(octets) - 1)
1563 for i in range(len(octets) - 1, 0, -1):
1564 octets[i] = l & 0xFF
1566 return bytes(octets)
1569 def len_decode(data):
1572 :returns: (decoded length, length's length, remaining data)
1573 :raises LenIndefForm: if indefinite form encoding is met
1576 raise NotEnoughData("no data at all")
1577 first_octet = data[0]
1578 if first_octet & 0x80 == 0:
1579 return first_octet, 1, data[1:]
1580 octets_num = first_octet & 0x7F
1581 if octets_num + 1 > len(data):
1582 raise NotEnoughData("encoded length is longer than data")
1584 raise LenIndefForm()
1586 raise DecodeError("leading zeros")
1588 for v in data[1:1 + octets_num]:
1591 raise DecodeError("long form instead of short one")
1592 return l, 1 + octets_num, data[1 + octets_num:]
1595 LEN0 = len_encode(0)
1596 LEN1 = len_encode(1)
1597 LEN1K = len_encode(1000)
1601 """How many bytes length field will take
1605 if l < 256: # 1 << 8
1607 if l < 65536: # 1 << 16
1609 if l < 16777216: # 1 << 24
1611 if l < 4294967296: # 1 << 32
1613 if l < 1099511627776: # 1 << 40
1615 if l < 281474976710656: # 1 << 48
1617 if l < 72057594037927936: # 1 << 56
1619 raise OverflowError("too big length")
1622 def write_full(writer, data):
1623 """Fully write provided data
1625 :param writer: must comply with ``io.RawIOBase.write`` behaviour
1627 BytesIO does not guarantee that the whole data will be written at
1628 once. That function write everything provided, raising an error if
1629 ``writer`` returns None.
1631 data = memoryview(data)
1633 while written != len(data):
1634 n = writer(data[written:])
1636 raise ValueError("can not write to buf")
1640 # If it is 64-bit system, then use compact 64-bit array of unsigned
1641 # longs. Use an ordinary list with universal integers otherwise, that
1643 if sys_maxsize > 2 ** 32:
1644 def state_2pass_new():
1647 def state_2pass_new():
1651 ########################################################################
1653 ########################################################################
1655 class AutoAddSlots(type):
1656 def __new__(cls, name, bases, _dict):
1657 _dict["__slots__"] = _dict.get("__slots__", ())
1658 return super().__new__(cls, name, bases, _dict)
1661 BasicState = namedtuple("BasicState", (
1674 ), **NAMEDTUPLE_KWARGS)
1677 class Obj(metaclass=AutoAddSlots):
1678 """Common ASN.1 object class
1680 All ASN.1 types are inherited from it. It has metaclass that
1681 automatically adds ``__slots__`` to all inherited classes.
1706 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1707 self._expl = getattr(self, "expl", None) if expl is None else expl
1708 if self.tag != self.tag_default and self._expl is not None:
1709 raise ValueError("implicit and explicit tags can not be set simultaneously")
1710 if self.tag is None:
1711 self._tag_order = None
1713 tag_class, _, tag_num = tag_decode(
1714 self.tag if self._expl is None else self._expl
1716 self._tag_order = (tag_class, tag_num)
1717 if default is not None:
1719 self.optional = optional
1720 self.offset, self.llen, self.vlen = _decoded
1722 self.expl_lenindef = False
1723 self.lenindef = False
1724 self.ber_encoded = False
1727 def ready(self): # pragma: no cover
1728 """Is object ready to be encoded?
1730 raise NotImplementedError()
1732 def _assert_ready(self):
1734 raise ObjNotReady(self.__class__.__name__)
1738 """Is either object or any elements inside is BER encoded?
1740 return self.expl_lenindef or self.lenindef or self.ber_encoded
1744 """Is object decoded?
1746 return (self.llen + self.vlen) > 0
1748 def __getstate__(self): # pragma: no cover
1749 """Used for making safe to be mutable pickleable copies
1751 raise NotImplementedError()
1753 def __setstate__(self, state):
1754 if state.version != __version__:
1755 raise ValueError("data is pickled by different PyDERASN version")
1756 self.tag = state.tag
1757 self._tag_order = state.tag_order
1758 self._expl = state.expl
1759 self.default = state.default
1760 self.optional = state.optional
1761 self.offset = state.offset
1762 self.llen = state.llen
1763 self.vlen = state.vlen
1764 self.expl_lenindef = state.expl_lenindef
1765 self.lenindef = state.lenindef
1766 self.ber_encoded = state.ber_encoded
1769 def tag_order(self):
1770 """Tag's (class, number) used for DER/CER sorting
1772 return self._tag_order
1775 def tag_order_cer(self):
1776 return self.tag_order
1780 """.. seealso:: :ref:`decoding`
1782 return len(self.tag)
1786 """.. seealso:: :ref:`decoding`
1788 return self.tlen + self.llen + self.vlen
1790 def __str__(self): # pragma: no cover
1791 return self.__unicode__()
1793 def __ne__(self, their):
1794 return not(self == their)
1796 def __gt__(self, their): # pragma: no cover
1797 return not(self < their)
1799 def __le__(self, their): # pragma: no cover
1800 return (self == their) or (self < their)
1802 def __ge__(self, their): # pragma: no cover
1803 return (self == their) or (self > their)
1805 def _encode(self): # pragma: no cover
1806 raise NotImplementedError()
1808 def _encode_cer(self, writer):
1809 write_full(writer, self._encode())
1811 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode): # pragma: no cover
1812 yield NotImplemented
1814 def _encode1st(self, state):
1815 raise NotImplementedError()
1817 def _encode2nd(self, writer, state_iter):
1818 raise NotImplementedError()
1821 """DER encode the structure
1823 :returns: DER representation
1825 raw = self._encode()
1826 if self._expl is None:
1828 return b"".join((self._expl, len_encode(len(raw)), raw))
1830 def encode1st(self, state=None):
1831 """Do the 1st pass of 2-pass encoding
1833 :rtype: (int, array("L"))
1834 :returns: full length of encoded data and precalculated various
1838 state = state_2pass_new()
1839 if self._expl is None:
1840 return self._encode1st(state)
1842 idx = len(state) - 1
1843 vlen, _ = self._encode1st(state)
1845 fulllen = len(self._expl) + len_size(vlen) + vlen
1846 return fulllen, state
1848 def encode2nd(self, writer, state_iter):
1849 """Do the 2nd pass of 2-pass encoding
1851 :param writer: must comply with ``io.RawIOBase.write`` behaviour
1852 :param state_iter: iterator over the 1st pass state (``iter(state)``)
1854 if self._expl is None:
1855 self._encode2nd(writer, state_iter)
1857 write_full(writer, self._expl + len_encode(next(state_iter)))
1858 self._encode2nd(writer, state_iter)
1860 def encode_cer(self, writer):
1861 """CER encode the structure to specified writer
1863 :param writer: must comply with ``io.RawIOBase.write``
1864 behaviour. It takes slice to be written and
1865 returns number of bytes processed. If it returns
1866 None, then exception will be raised
1868 if self._expl is not None:
1869 write_full(writer, self._expl + LENINDEF)
1870 if getattr(self, "der_forced", False):
1871 write_full(writer, self._encode())
1873 self._encode_cer(writer)
1874 if self._expl is not None:
1875 write_full(writer, EOC)
1877 def hexencode(self):
1878 """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1880 return hexenc(self.encode())
1890 _ctx_immutable=True,
1894 :param data: either binary or memoryview
1895 :param int offset: initial data's offset
1896 :param bool leavemm: do we need to leave memoryview of remaining
1897 data as is, or convert it to bytes otherwise
1898 :param decode_path: current decode path (tuples of strings,
1899 possibly with DecodePathDefBy) with will be
1900 the root for all underlying objects
1901 :param ctx: optional :ref:`context <ctx>` governing decoding process
1902 :param bool tag_only: decode only the tag, without length and
1903 contents (used only in Choice and Set
1904 structures, trying to determine if tag satisfies
1906 :param bool _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1908 :returns: (Obj, remaining data)
1910 .. seealso:: :ref:`decoding`
1912 result = next(self.decode_evgen(
1924 _, obj, tail = result
1935 _ctx_immutable=True,
1938 """Decode with evgen mode on
1940 That method is identical to :py:meth:`pyderasn.Obj.decode`, but
1941 it returns the generator producing ``(decode_path, obj, tail)``
1943 .. seealso:: :ref:`evgen mode <evgen_mode>`.
1947 elif _ctx_immutable:
1949 tlv = memoryview(data)
1952 get_def_by_path(ctx.get("evgen_mode_upto", ()), decode_path) is not None
1955 if self._expl is None:
1956 for result in self._decode(
1959 decode_path=decode_path,
1962 evgen_mode=_evgen_mode,
1967 _decode_path, obj, tail = result
1968 if _decode_path is not decode_path:
1972 t, tlen, lv = tag_strip(tlv)
1973 except DecodeError as err:
1974 raise err.__class__(
1976 klass=self.__class__,
1977 decode_path=decode_path,
1982 klass=self.__class__,
1983 decode_path=decode_path,
1987 l, llen, v = len_decode(lv)
1988 except LenIndefForm as err:
1989 if not ctx.get("bered", False):
1990 raise err.__class__(
1992 klass=self.__class__,
1993 decode_path=decode_path,
1997 offset += tlen + llen
1998 for result in self._decode(
2001 decode_path=decode_path,
2004 evgen_mode=_evgen_mode,
2006 if tag_only: # pragma: no cover
2009 _decode_path, obj, tail = result
2010 if _decode_path is not decode_path:
2012 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
2013 if eoc_expected.tobytes() != EOC:
2016 klass=self.__class__,
2017 decode_path=decode_path,
2021 obj.expl_lenindef = True
2022 except DecodeError as err:
2023 raise err.__class__(
2025 klass=self.__class__,
2026 decode_path=decode_path,
2031 raise NotEnoughData(
2032 "encoded length is longer than data",
2033 klass=self.__class__,
2034 decode_path=decode_path,
2037 for result in self._decode(
2039 offset=offset + tlen + llen,
2040 decode_path=decode_path,
2043 evgen_mode=_evgen_mode,
2045 if tag_only: # pragma: no cover
2048 _decode_path, obj, tail = result
2049 if _decode_path is not decode_path:
2051 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
2053 "explicit tag out-of-bound, longer than data",
2054 klass=self.__class__,
2055 decode_path=decode_path,
2058 yield decode_path, obj, (tail if leavemm else tail.tobytes())
2060 def decod(self, data, offset=0, decode_path=(), ctx=None):
2061 """Decode the data, check that tail is empty
2063 :raises ExceedingData: if tail is not empty
2065 This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
2066 (decode without tail) that also checks that there is no
2069 obj, tail = self.decode(
2072 decode_path=decode_path,
2077 raise ExceedingData(len(tail))
2080 def hexdecode(self, data, *args, **kwargs):
2081 """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
2083 return self.decode(hexdec(data), *args, **kwargs)
2085 def hexdecod(self, data, *args, **kwargs):
2086 """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
2088 return self.decod(hexdec(data), *args, **kwargs)
2092 """.. seealso:: :ref:`decoding`
2094 return self._expl is not None
2098 """.. seealso:: :ref:`decoding`
2103 def expl_tlen(self):
2104 """.. seealso:: :ref:`decoding`
2106 return len(self._expl)
2109 def expl_llen(self):
2110 """.. seealso:: :ref:`decoding`
2112 if self.expl_lenindef:
2114 return len(len_encode(self.tlvlen))
2117 def expl_offset(self):
2118 """.. seealso:: :ref:`decoding`
2120 return self.offset - self.expl_tlen - self.expl_llen
2123 def expl_vlen(self):
2124 """.. seealso:: :ref:`decoding`
2129 def expl_tlvlen(self):
2130 """.. seealso:: :ref:`decoding`
2132 return self.expl_tlen + self.expl_llen + self.expl_vlen
2135 def fulloffset(self):
2136 """.. seealso:: :ref:`decoding`
2138 return self.expl_offset if self.expled else self.offset
2142 """.. seealso:: :ref:`decoding`
2144 return self.expl_tlvlen if self.expled else self.tlvlen
2146 def pps_lenindef(self, decode_path):
2147 if self.lenindef and not (
2148 getattr(self, "defined", None) is not None and
2149 self.defined[1].lenindef
2152 asn1_type_name="EOC",
2154 decode_path=decode_path,
2156 self.offset + self.tlvlen -
2157 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
2165 if self.expl_lenindef:
2167 asn1_type_name="EOC",
2168 obj_name="EXPLICIT",
2169 decode_path=decode_path,
2170 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
2179 def encode_cer(obj):
2180 """Encode to CER in memory buffer
2182 :returns bytes: memory buffer contents
2185 obj.encode_cer(buf.write)
2186 return buf.getvalue()
2189 def encode2pass(obj):
2190 """Encode (2-pass mode) to DER in memory buffer
2192 :returns bytes: memory buffer contents
2195 _, state = obj.encode1st()
2196 obj.encode2nd(buf.write, iter(state))
2197 return buf.getvalue()
2200 class DecodePathDefBy:
2201 """DEFINED BY representation inside decode path
2203 __slots__ = ("defined_by",)
2205 def __init__(self, defined_by):
2206 self.defined_by = defined_by
2208 def __ne__(self, their):
2209 return not(self == their)
2211 def __eq__(self, their):
2212 if not isinstance(their, self.__class__):
2214 return self.defined_by == their.defined_by
2217 return "DEFINED BY " + str(self.defined_by)
2220 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
2223 ########################################################################
2225 ########################################################################
2227 PP = namedtuple("PP", (
2250 ), **NAMEDTUPLE_KWARGS)
2255 asn1_type_name="unknown",
2272 expl_lenindef=False,
2303 def _colourize(what, colour, with_colours, attrs=("bold",)):
2304 return colored(what, colour, attrs=attrs) if with_colours else what
2307 def colonize_hex(hexed):
2308 """Separate hexadecimal string with colons
2310 return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
2313 def find_oid_name(asn1_type_name, oid_maps, value):
2314 if len(oid_maps) > 0 and asn1_type_name == ObjectIdentifier.asn1_type_name:
2315 for oid_map in oid_maps:
2316 oid_name = oid_map.get(value)
2317 if oid_name is not None:
2328 with_decode_path=False,
2329 decode_path_len_decrease=0,
2336 " " if pp.expl_offset is None else
2337 ("-%d" % (pp.offset - pp.expl_offset))
2339 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
2341 col = _colourize(col, "red", with_colours, ())
2342 col += _colourize("B", "red", with_colours) if pp.bered else " "
2344 col = "[%d,%d,%4d]%s" % (
2345 pp.tlen, pp.llen, pp.vlen,
2346 LENINDEF_PP_CHAR if pp.lenindef else " "
2348 col = _colourize(col, "green", with_colours, ())
2350 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2351 if decode_path_len > 0:
2352 cols.append(" ." * decode_path_len)
2353 ent = pp.decode_path[-1]
2354 if isinstance(ent, DecodePathDefBy):
2355 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
2356 value = str(ent.defined_by)
2357 oid_name = find_oid_name(ent.defined_by.asn1_type_name, oid_maps, value)
2358 if oid_name is None:
2359 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
2361 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
2363 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
2364 if pp.expl is not None:
2365 klass, _, num = pp.expl
2366 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
2367 cols.append(_colourize(col, "blue", with_colours))
2368 if pp.impl is not None:
2369 klass, _, num = pp.impl
2370 col = "[%s%d]" % (TagClassReprs[klass], num)
2371 cols.append(_colourize(col, "blue", with_colours))
2372 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
2373 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
2375 cols.append(_colourize("BER", "red", with_colours))
2376 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
2377 if pp.value is not None:
2379 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
2380 oid_name = find_oid_name(pp.asn1_type_name, oid_maps, pp.value)
2381 if oid_name is not None:
2382 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
2383 if pp.asn1_type_name == Integer.asn1_type_name:
2384 cols.append(_colourize(
2385 "(%s)" % colonize_hex(pp.obj.tohex()), "green", with_colours,
2388 if pp.blob.__class__ == bytes:
2389 cols.append(hexenc(pp.blob))
2390 elif pp.blob.__class__ == tuple:
2391 cols.append(", ".join(pp.blob))
2393 cols.append(_colourize("OPTIONAL", "red", with_colours))
2395 cols.append(_colourize("DEFAULT", "red", with_colours))
2396 if with_decode_path:
2397 cols.append(_colourize(
2398 "[%s]" % ":".join(str(p) for p in pp.decode_path),
2402 return " ".join(cols)
2405 def pp_console_blob(pp, decode_path_len_decrease=0):
2406 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
2407 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2408 if decode_path_len > 0:
2409 cols.append(" ." * (decode_path_len + 1))
2410 if pp.blob.__class__ == bytes:
2411 blob = hexenc(pp.blob).upper()
2412 for i in range(0, len(blob), 32):
2413 chunk = blob[i:i + 32]
2414 yield " ".join(cols + [colonize_hex(chunk)])
2415 elif pp.blob.__class__ == tuple:
2416 yield " ".join(cols + [", ".join(pp.blob)])
2424 with_decode_path=False,
2425 decode_path_only=(),
2428 """Pretty print object
2430 :param Obj obj: object you want to pretty print
2431 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
2432 Its human readable form is printed when OID is met
2433 :param big_blobs: if large binary objects are met (like OctetString
2434 values), do we need to print them too, on separate
2436 :param with_colours: colourize output, if ``termcolor`` library
2438 :param with_decode_path: print decode path
2439 :param decode_path_only: print only that specified decode path
2441 def _pprint_pps(pps):
2443 if hasattr(pp, "_fields"):
2445 decode_path_only != () and
2447 str(p) for p in pp.decode_path[:len(decode_path_only)]
2448 ) != decode_path_only
2452 yield pp_console_row(
2457 with_colours=with_colours,
2458 with_decode_path=with_decode_path,
2459 decode_path_len_decrease=len(decode_path_only),
2461 for row in pp_console_blob(
2463 decode_path_len_decrease=len(decode_path_only),
2467 yield pp_console_row(
2472 with_colours=with_colours,
2473 with_decode_path=with_decode_path,
2474 decode_path_len_decrease=len(decode_path_only),
2477 for row in _pprint_pps(pp):
2479 return "\n".join(_pprint_pps(obj.pps(decode_path)))
2482 ########################################################################
2483 # ASN.1 primitive types
2484 ########################################################################
2486 BooleanState = namedtuple(
2488 BasicState._fields + ("value",),
2494 """``BOOLEAN`` boolean type
2496 >>> b = Boolean(True)
2498 >>> b == Boolean(True)
2504 tag_default = tag_encode(1)
2505 asn1_type_name = "BOOLEAN"
2517 :param value: set the value. Either boolean type, or
2518 :py:class:`pyderasn.Boolean` object
2519 :param bytes impl: override default tag with ``IMPLICIT`` one
2520 :param bytes expl: override default tag with ``EXPLICIT`` one
2521 :param default: set default value. Type same as in ``value``
2522 :param bool optional: is object ``OPTIONAL`` in sequence
2524 super().__init__(impl, expl, default, optional, _decoded)
2525 self._value = None if value is None else self._value_sanitize(value)
2526 if default is not None:
2527 default = self._value_sanitize(default)
2528 self.default = self.__class__(
2534 self._value = default
2536 def _value_sanitize(self, value):
2537 if value.__class__ == bool:
2539 if issubclass(value.__class__, Boolean):
2541 raise InvalidValueType((self.__class__, bool))
2545 return self._value is not None
2547 def __getstate__(self):
2548 return BooleanState(
2564 def __setstate__(self, state):
2565 super().__setstate__(state)
2566 self._value = state.value
2568 def __nonzero__(self):
2569 self._assert_ready()
2573 self._assert_ready()
2576 def __eq__(self, their):
2577 if their.__class__ == bool:
2578 return self._value == their
2579 if not issubclass(their.__class__, Boolean):
2582 self._value == their._value and
2583 self.tag == their.tag and
2584 self._expl == their._expl
2595 return self.__class__(
2597 impl=self.tag if impl is None else impl,
2598 expl=self._expl if expl is None else expl,
2599 default=self.default if default is None else default,
2600 optional=self.optional if optional is None else optional,
2604 self._assert_ready()
2605 return b"".join((self.tag, LEN1, (b"\xFF" if self._value else b"\x00")))
2607 def _encode1st(self, state):
2608 return len(self.tag) + 2, state
2610 def _encode2nd(self, writer, state_iter):
2611 self._assert_ready()
2612 write_full(writer, self._encode())
2614 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2616 t, _, lv = tag_strip(tlv)
2617 except DecodeError as err:
2618 raise err.__class__(
2620 klass=self.__class__,
2621 decode_path=decode_path,
2626 klass=self.__class__,
2627 decode_path=decode_path,
2634 l, _, v = len_decode(lv)
2635 except DecodeError as err:
2636 raise err.__class__(
2638 klass=self.__class__,
2639 decode_path=decode_path,
2643 raise InvalidLength(
2644 "Boolean's length must be equal to 1",
2645 klass=self.__class__,
2646 decode_path=decode_path,
2650 raise NotEnoughData(
2651 "encoded length is longer than data",
2652 klass=self.__class__,
2653 decode_path=decode_path,
2658 if first_octet == 0:
2660 elif first_octet == 0xFF:
2662 elif ctx.get("bered", False):
2667 "unacceptable Boolean value",
2668 klass=self.__class__,
2669 decode_path=decode_path,
2672 obj = self.__class__(
2676 default=self.default,
2677 optional=self.optional,
2678 _decoded=(offset, 1, 1),
2680 obj.ber_encoded = ber_encoded
2681 yield decode_path, obj, v[1:]
2684 return pp_console_row(next(self.pps()))
2686 def pps(self, decode_path=()):
2689 asn1_type_name=self.asn1_type_name,
2690 obj_name=self.__class__.__name__,
2691 decode_path=decode_path,
2692 value=str(self._value) if self.ready else None,
2693 optional=self.optional,
2694 default=self == self.default,
2695 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2696 expl=None if self._expl is None else tag_decode(self._expl),
2701 expl_offset=self.expl_offset if self.expled else None,
2702 expl_tlen=self.expl_tlen if self.expled else None,
2703 expl_llen=self.expl_llen if self.expled else None,
2704 expl_vlen=self.expl_vlen if self.expled else None,
2705 expl_lenindef=self.expl_lenindef,
2706 ber_encoded=self.ber_encoded,
2709 for pp in self.pps_lenindef(decode_path):
2713 IntegerState = namedtuple(
2715 BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2721 """``INTEGER`` integer type
2723 >>> b = Integer(-123)
2725 >>> b == Integer(-123)
2730 >>> Integer(2, bounds=(1, 3))
2732 >>> Integer(5, bounds=(1, 3))
2733 Traceback (most recent call last):
2734 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2738 class Version(Integer):
2745 >>> v = Version("v1")
2752 {'v3': 2, 'v1': 0, 'v2': 1}
2754 __slots__ = ("specs", "_bound_min", "_bound_max")
2755 tag_default = tag_encode(2)
2756 asn1_type_name = "INTEGER"
2770 :param value: set the value. Either integer type, named value
2771 (if ``schema`` is specified in the class), or
2772 :py:class:`pyderasn.Integer` object
2773 :param bounds: set ``(MIN, MAX)`` value constraint.
2774 (-inf, +inf) by default
2775 :param bytes impl: override default tag with ``IMPLICIT`` one
2776 :param bytes expl: override default tag with ``EXPLICIT`` one
2777 :param default: set default value. Type same as in ``value``
2778 :param bool optional: is object ``OPTIONAL`` in sequence
2780 super().__init__(impl, expl, default, optional, _decoded)
2782 specs = getattr(self, "schema", {}) if _specs is None else _specs
2783 self.specs = specs if specs.__class__ == dict else dict(specs)
2784 self._bound_min, self._bound_max = getattr(
2787 (float("-inf"), float("+inf")),
2788 ) if bounds is None else bounds
2789 if value is not None:
2790 self._value = self._value_sanitize(value)
2791 if default is not None:
2792 default = self._value_sanitize(default)
2793 self.default = self.__class__(
2799 if self._value is None:
2800 self._value = default
2802 def _value_sanitize(self, value):
2803 if isinstance(value, int):
2805 elif issubclass(value.__class__, Integer):
2806 value = value._value
2807 elif value.__class__ == str:
2808 value = self.specs.get(value)
2810 raise ObjUnknown("integer value: %s" % value)
2812 raise InvalidValueType((self.__class__, int, str))
2813 if not self._bound_min <= value <= self._bound_max:
2814 raise BoundsError(self._bound_min, value, self._bound_max)
2819 return self._value is not None
2821 def __getstate__(self):
2822 return IntegerState(
2841 def __setstate__(self, state):
2842 super().__setstate__(state)
2843 self.specs = state.specs
2844 self._value = state.value
2845 self._bound_min = state.bound_min
2846 self._bound_max = state.bound_max
2849 self._assert_ready()
2850 return int(self._value)
2853 """Hexadecimal representation
2855 Use :py:func:`pyderasn.colonize_hex` for colonizing it.
2857 hex_repr = hex(int(self))[2:].upper()
2858 if len(hex_repr) % 2 != 0:
2859 hex_repr = "0" + hex_repr
2863 self._assert_ready()
2864 return hash(b"".join((
2866 bytes(self._expl or b""),
2867 str(self._value).encode("ascii"),
2870 def __eq__(self, their):
2871 if isinstance(their, int):
2872 return self._value == their
2873 if not issubclass(their.__class__, Integer):
2876 self._value == their._value and
2877 self.tag == their.tag and
2878 self._expl == their._expl
2881 def __lt__(self, their):
2882 return self._value < their._value
2886 """Return named representation (if exists) of the value
2888 for name, value in self.specs.items():
2889 if value == self._value:
2902 return self.__class__(
2905 (self._bound_min, self._bound_max)
2906 if bounds is None else bounds
2908 impl=self.tag if impl is None else impl,
2909 expl=self._expl if expl is None else expl,
2910 default=self.default if default is None else default,
2911 optional=self.optional if optional is None else optional,
2915 def _encode_payload(self):
2916 self._assert_ready()
2918 bytes_len = ceil(value.bit_length() / 8) or 1
2921 octets = value.to_bytes(bytes_len, byteorder="big", signed=True)
2922 except OverflowError:
2929 octets = self._encode_payload()
2930 return b"".join((self.tag, len_encode(len(octets)), octets))
2932 def _encode1st(self, state):
2933 l = len(self._encode_payload())
2934 return len(self.tag) + len_size(l) + l, state
2936 def _encode2nd(self, writer, state_iter):
2937 write_full(writer, self._encode())
2939 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2941 t, _, lv = tag_strip(tlv)
2942 except DecodeError as err:
2943 raise err.__class__(
2945 klass=self.__class__,
2946 decode_path=decode_path,
2951 klass=self.__class__,
2952 decode_path=decode_path,
2959 l, llen, v = len_decode(lv)
2960 except DecodeError as err:
2961 raise err.__class__(
2963 klass=self.__class__,
2964 decode_path=decode_path,
2968 raise NotEnoughData(
2969 "encoded length is longer than data",
2970 klass=self.__class__,
2971 decode_path=decode_path,
2975 raise NotEnoughData(
2977 klass=self.__class__,
2978 decode_path=decode_path,
2981 v, tail = v[:l], v[l:]
2986 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2987 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2990 "non normalized integer",
2991 klass=self.__class__,
2992 decode_path=decode_path,
2995 value = int.from_bytes(v, byteorder="big", signed=True)
2997 obj = self.__class__(
2999 bounds=(self._bound_min, self._bound_max),
3002 default=self.default,
3003 optional=self.optional,
3005 _decoded=(offset, llen, l),
3007 except BoundsError as err:
3010 klass=self.__class__,
3011 decode_path=decode_path,
3014 yield decode_path, obj, tail
3017 return pp_console_row(next(self.pps()))
3019 def pps(self, decode_path=()):
3022 asn1_type_name=self.asn1_type_name,
3023 obj_name=self.__class__.__name__,
3024 decode_path=decode_path,
3025 value=(self.named or str(self._value)) if self.ready else None,
3026 optional=self.optional,
3027 default=self == self.default,
3028 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3029 expl=None if self._expl is None else tag_decode(self._expl),
3034 expl_offset=self.expl_offset if self.expled else None,
3035 expl_tlen=self.expl_tlen if self.expled else None,
3036 expl_llen=self.expl_llen if self.expled else None,
3037 expl_vlen=self.expl_vlen if self.expled else None,
3038 expl_lenindef=self.expl_lenindef,
3041 for pp in self.pps_lenindef(decode_path):
3045 BitStringState = namedtuple(
3047 BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
3052 class BitString(Obj):
3053 """``BIT STRING`` bit string type
3055 >>> BitString(b"hello world")
3056 BIT STRING 88 bits 68656c6c6f20776f726c64
3059 >>> b == b"hello world"
3064 >>> BitString("'0A3B5F291CD'H")
3065 BIT STRING 44 bits 0a3b5f291cd0
3066 >>> b = BitString("'010110000000'B")
3067 BIT STRING 12 bits 5800
3070 >>> b[0], b[1], b[2], b[3]
3071 (False, True, False, True)
3075 [False, True, False, True, True, False, False, False, False, False, False, False]
3079 class KeyUsage(BitString):
3081 ("digitalSignature", 0),
3082 ("nonRepudiation", 1),
3083 ("keyEncipherment", 2),
3086 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
3087 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
3089 ['nonRepudiation', 'keyEncipherment']
3091 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
3095 Pay attention that BIT STRING can be encoded both in primitive
3096 and constructed forms. Decoder always checks constructed form tag
3097 additionally to specified primitive one. If BER decoding is
3098 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
3099 of DER restrictions.
3101 __slots__ = ("tag_constructed", "specs", "defined")
3102 tag_default = tag_encode(3)
3103 asn1_type_name = "BIT STRING"
3116 :param value: set the value. Either binary type, tuple of named
3117 values (if ``schema`` is specified in the class),
3118 string in ``'XXX...'B`` form, or
3119 :py:class:`pyderasn.BitString` object
3120 :param bytes impl: override default tag with ``IMPLICIT`` one
3121 :param bytes expl: override default tag with ``EXPLICIT`` one
3122 :param default: set default value. Type same as in ``value``
3123 :param bool optional: is object ``OPTIONAL`` in sequence
3125 super().__init__(impl, expl, default, optional, _decoded)
3126 specs = getattr(self, "schema", {}) if _specs is None else _specs
3127 self.specs = specs if specs.__class__ == dict else dict(specs)
3128 self._value = None if value is None else self._value_sanitize(value)
3129 if default is not None:
3130 default = self._value_sanitize(default)
3131 self.default = self.__class__(
3137 self._value = default
3139 tag_klass, _, tag_num = tag_decode(self.tag)
3140 self.tag_constructed = tag_encode(
3142 form=TagFormConstructed,
3146 def _bits2octets(self, bits):
3147 if len(self.specs) > 0:
3148 bits = bits.rstrip("0")
3150 bits += "0" * ((8 - (bit_len % 8)) % 8)
3151 octets = bytearray(len(bits) // 8)
3152 for i in range(len(octets)):
3153 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
3154 return bit_len, bytes(octets)
3156 def _value_sanitize(self, value):
3157 if isinstance(value, (str, bytes)):
3159 isinstance(value, str) and
3160 value.startswith("'")
3162 if value.endswith("'B"):
3164 if not frozenset(value) <= SET01:
3165 raise ValueError("B's coding contains unacceptable chars")
3166 return self._bits2octets(value)
3167 if value.endswith("'H"):
3171 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
3173 if value.__class__ == bytes:
3174 return (len(value) * 8, value)
3175 raise InvalidValueType((self.__class__, str, bytes))
3176 if value.__class__ == tuple:
3179 isinstance(value[0], int) and
3180 value[1].__class__ == bytes
3185 bit = self.specs.get(name)
3187 raise ObjUnknown("BitString value: %s" % name)
3190 return self._bits2octets("")
3191 bits = frozenset(bits)
3192 return self._bits2octets("".join(
3193 ("1" if bit in bits else "0")
3194 for bit in range(max(bits) + 1)
3196 if issubclass(value.__class__, BitString):
3198 raise InvalidValueType((self.__class__, bytes, str))
3202 return self._value is not None
3204 def __getstate__(self):
3205 return BitStringState(
3220 self.tag_constructed,
3224 def __setstate__(self, state):
3225 super().__setstate__(state)
3226 self.specs = state.specs
3227 self._value = state.value
3228 self.tag_constructed = state.tag_constructed
3229 self.defined = state.defined
3232 self._assert_ready()
3233 for i in range(self._value[0]):
3238 """Returns number of bits in the string
3240 self._assert_ready()
3241 return self._value[0]
3243 def __bytes__(self):
3244 self._assert_ready()
3245 return self._value[1]
3247 def __eq__(self, their):
3248 if their.__class__ == bytes:
3249 return self._value[1] == their
3250 if not issubclass(their.__class__, BitString):
3253 self._value == their._value and
3254 self.tag == their.tag and
3255 self._expl == their._expl
3260 """Named representation (if exists) of the bits
3262 :returns: [str(name), ...]
3264 return [name for name, bit in self.specs.items() if self[bit]]
3274 return self.__class__(
3276 impl=self.tag if impl is None else impl,
3277 expl=self._expl if expl is None else expl,
3278 default=self.default if default is None else default,
3279 optional=self.optional if optional is None else optional,
3283 def __getitem__(self, key):
3284 if key.__class__ == int:
3285 bit_len, octets = self._value
3288 return memoryview(octets)[key // 8] >> (7 - (key % 8)) & 1 == 1
3289 if isinstance(key, str):
3290 value = self.specs.get(key)
3292 raise ObjUnknown("BitString value: %s" % key)
3294 raise InvalidValueType((int, str))
3297 self._assert_ready()
3298 bit_len, octets = self._value
3301 len_encode(len(octets) + 1),
3302 int2byte((8 - bit_len % 8) % 8),
3306 def _encode1st(self, state):
3307 self._assert_ready()
3308 _, octets = self._value
3310 return len(self.tag) + len_size(l) + l, state
3312 def _encode2nd(self, writer, state_iter):
3313 bit_len, octets = self._value
3314 write_full(writer, b"".join((
3316 len_encode(len(octets) + 1),
3317 int2byte((8 - bit_len % 8) % 8),
3319 write_full(writer, octets)
3321 def _encode_cer(self, writer):
3322 bit_len, octets = self._value
3323 if len(octets) + 1 <= 1000:
3324 write_full(writer, self._encode())
3326 write_full(writer, self.tag_constructed)
3327 write_full(writer, LENINDEF)
3328 for offset in range(0, (len(octets) // 999) * 999, 999):
3329 write_full(writer, b"".join((
3330 BitString.tag_default,
3333 octets[offset:offset + 999],
3335 tail = octets[offset + 999:]
3337 tail = int2byte((8 - bit_len % 8) % 8) + tail
3338 write_full(writer, b"".join((
3339 BitString.tag_default,
3340 len_encode(len(tail)),
3343 write_full(writer, EOC)
3345 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3347 t, tlen, lv = tag_strip(tlv)
3348 except DecodeError as err:
3349 raise err.__class__(
3351 klass=self.__class__,
3352 decode_path=decode_path,
3356 if tag_only: # pragma: no cover
3360 l, llen, v = len_decode(lv)
3361 except DecodeError as err:
3362 raise err.__class__(
3364 klass=self.__class__,
3365 decode_path=decode_path,
3369 raise NotEnoughData(
3370 "encoded length is longer than data",
3371 klass=self.__class__,
3372 decode_path=decode_path,
3376 raise NotEnoughData(
3378 klass=self.__class__,
3379 decode_path=decode_path,
3383 if l == 1 and pad_size != 0:
3385 "invalid empty value",
3386 klass=self.__class__,
3387 decode_path=decode_path,
3393 klass=self.__class__,
3394 decode_path=decode_path,
3397 if v[l - 1] & ((1 << pad_size) - 1) != 0:
3400 klass=self.__class__,
3401 decode_path=decode_path,
3404 v, tail = v[:l], v[l:]
3405 bit_len = (len(v) - 1) * 8 - pad_size
3406 obj = self.__class__(
3407 value=None if evgen_mode else (bit_len, v[1:].tobytes()),
3410 default=self.default,
3411 optional=self.optional,
3413 _decoded=(offset, llen, l),
3416 obj._value = (bit_len, None)
3417 yield decode_path, obj, tail
3419 if t != self.tag_constructed:
3421 klass=self.__class__,
3422 decode_path=decode_path,
3425 if not ctx.get("bered", False):
3427 "unallowed BER constructed encoding",
3428 klass=self.__class__,
3429 decode_path=decode_path,
3432 if tag_only: # pragma: no cover
3437 l, llen, v = len_decode(lv)
3438 except LenIndefForm:
3439 llen, l, v = 1, 0, lv[1:]
3441 except DecodeError as err:
3442 raise err.__class__(
3444 klass=self.__class__,
3445 decode_path=decode_path,
3449 raise NotEnoughData(
3450 "encoded length is longer than data",
3451 klass=self.__class__,
3452 decode_path=decode_path,
3455 if not lenindef and l == 0:
3456 raise NotEnoughData(
3458 klass=self.__class__,
3459 decode_path=decode_path,
3463 sub_offset = offset + tlen + llen
3467 if v[:EOC_LEN].tobytes() == EOC:
3474 "chunk out of bounds",
3475 klass=self.__class__,
3476 decode_path=decode_path + (str(len(chunks) - 1),),
3477 offset=chunks[-1].offset,
3479 sub_decode_path = decode_path + (str(len(chunks)),)
3482 for _decode_path, chunk, v_tail in BitString().decode_evgen(
3485 decode_path=sub_decode_path,
3488 _ctx_immutable=False,
3490 yield _decode_path, chunk, v_tail
3492 _, chunk, v_tail = next(BitString().decode_evgen(
3495 decode_path=sub_decode_path,
3498 _ctx_immutable=False,
3503 "expected BitString encoded chunk",
3504 klass=self.__class__,
3505 decode_path=sub_decode_path,
3508 chunks.append(chunk)
3509 sub_offset += chunk.tlvlen
3510 vlen += chunk.tlvlen
3512 if len(chunks) == 0:
3515 klass=self.__class__,
3516 decode_path=decode_path,
3521 for chunk_i, chunk in enumerate(chunks[:-1]):
3522 if chunk.bit_len % 8 != 0:
3524 "BitString chunk is not multiple of 8 bits",
3525 klass=self.__class__,
3526 decode_path=decode_path + (str(chunk_i),),
3527 offset=chunk.offset,
3530 values.append(bytes(chunk))
3531 bit_len += chunk.bit_len
3532 chunk_last = chunks[-1]
3534 values.append(bytes(chunk_last))
3535 bit_len += chunk_last.bit_len
3536 obj = self.__class__(
3537 value=None if evgen_mode else (bit_len, b"".join(values)),
3540 default=self.default,
3541 optional=self.optional,
3543 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3546 obj._value = (bit_len, None)
3547 obj.lenindef = lenindef
3548 obj.ber_encoded = True
3549 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
3552 return pp_console_row(next(self.pps()))
3554 def pps(self, decode_path=()):
3558 bit_len, blob = self._value
3559 value = "%d bits" % bit_len
3560 if len(self.specs) > 0 and blob is not None:
3561 blob = tuple(self.named)
3564 asn1_type_name=self.asn1_type_name,
3565 obj_name=self.__class__.__name__,
3566 decode_path=decode_path,
3569 optional=self.optional,
3570 default=self == self.default,
3571 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3572 expl=None if self._expl is None else tag_decode(self._expl),
3577 expl_offset=self.expl_offset if self.expled else None,
3578 expl_tlen=self.expl_tlen if self.expled else None,
3579 expl_llen=self.expl_llen if self.expled else None,
3580 expl_vlen=self.expl_vlen if self.expled else None,
3581 expl_lenindef=self.expl_lenindef,
3582 lenindef=self.lenindef,
3583 ber_encoded=self.ber_encoded,
3586 defined_by, defined = self.defined or (None, None)
3587 if defined_by is not None:
3589 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3591 for pp in self.pps_lenindef(decode_path):
3595 OctetStringState = namedtuple(
3597 BasicState._fields + (
3608 class OctetString(Obj):
3609 """``OCTET STRING`` binary string type
3611 >>> s = OctetString(b"hello world")
3612 OCTET STRING 11 bytes 68656c6c6f20776f726c64
3613 >>> s == OctetString(b"hello world")
3618 >>> OctetString(b"hello", bounds=(4, 4))
3619 Traceback (most recent call last):
3620 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
3621 >>> OctetString(b"hell", bounds=(4, 4))
3622 OCTET STRING 4 bytes 68656c6c
3624 Memoryviews can be used as a values. If memoryview is made on
3625 mmap-ed file, then it does not take storage inside OctetString
3626 itself. In CER encoding mode it will be streamed to the specified
3627 writer, copying 1 KB chunks.
3629 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
3630 tag_default = tag_encode(4)
3631 asn1_type_name = "OCTET STRING"
3632 evgen_mode_skip_value = True
3646 :param value: set the value. Either binary type, or
3647 :py:class:`pyderasn.OctetString` object
3648 :param bounds: set ``(MIN, MAX)`` value size constraint.
3649 (-inf, +inf) by default
3650 :param bytes impl: override default tag with ``IMPLICIT`` one
3651 :param bytes expl: override default tag with ``EXPLICIT`` one
3652 :param default: set default value. Type same as in ``value``
3653 :param bool optional: is object ``OPTIONAL`` in sequence
3655 super().__init__(impl, expl, default, optional, _decoded)
3657 self._bound_min, self._bound_max = getattr(
3661 ) if bounds is None else bounds
3662 if value is not None:
3663 self._value = self._value_sanitize(value)
3664 if default is not None:
3665 default = self._value_sanitize(default)
3666 self.default = self.__class__(
3671 if self._value is None:
3672 self._value = default
3674 tag_klass, _, tag_num = tag_decode(self.tag)
3675 self.tag_constructed = tag_encode(
3677 form=TagFormConstructed,
3681 def _value_sanitize(self, value):
3682 if value.__class__ == bytes or value.__class__ == memoryview:
3684 elif issubclass(value.__class__, OctetString):
3685 value = value._value
3687 raise InvalidValueType((self.__class__, bytes, memoryview))
3688 if not self._bound_min <= len(value) <= self._bound_max:
3689 raise BoundsError(self._bound_min, len(value), self._bound_max)
3694 return self._value is not None
3696 def __getstate__(self):
3697 return OctetStringState(
3713 self.tag_constructed,
3717 def __setstate__(self, state):
3718 super().__setstate__(state)
3719 self._value = state.value
3720 self._bound_min = state.bound_min
3721 self._bound_max = state.bound_max
3722 self.tag_constructed = state.tag_constructed
3723 self.defined = state.defined
3725 def __bytes__(self):
3726 self._assert_ready()
3727 return bytes(self._value)
3729 def __eq__(self, their):
3730 if their.__class__ == bytes:
3731 return self._value == their
3732 if not issubclass(their.__class__, OctetString):
3735 self._value == their._value and
3736 self.tag == their.tag and
3737 self._expl == their._expl
3740 def __lt__(self, their):
3741 return self._value < their._value
3752 return self.__class__(
3755 (self._bound_min, self._bound_max)
3756 if bounds is None else bounds
3758 impl=self.tag if impl is None else impl,
3759 expl=self._expl if expl is None else expl,
3760 default=self.default if default is None else default,
3761 optional=self.optional if optional is None else optional,
3765 self._assert_ready()
3768 len_encode(len(self._value)),
3772 def _encode1st(self, state):
3773 self._assert_ready()
3774 l = len(self._value)
3775 return len(self.tag) + len_size(l) + l, state
3777 def _encode2nd(self, writer, state_iter):
3779 write_full(writer, self.tag + len_encode(len(value)))
3780 write_full(writer, value)
3782 def _encode_cer(self, writer):
3783 octets = self._value
3784 if len(octets) <= 1000:
3785 write_full(writer, self._encode())
3787 write_full(writer, self.tag_constructed)
3788 write_full(writer, LENINDEF)
3789 for offset in range(0, (len(octets) // 1000) * 1000, 1000):
3790 write_full(writer, b"".join((
3791 OctetString.tag_default,
3793 octets[offset:offset + 1000],
3795 tail = octets[offset + 1000:]
3797 write_full(writer, b"".join((
3798 OctetString.tag_default,
3799 len_encode(len(tail)),
3802 write_full(writer, EOC)
3804 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3806 t, tlen, lv = tag_strip(tlv)
3807 except DecodeError as err:
3808 raise err.__class__(
3810 klass=self.__class__,
3811 decode_path=decode_path,
3819 l, llen, v = len_decode(lv)
3820 except DecodeError as err:
3821 raise err.__class__(
3823 klass=self.__class__,
3824 decode_path=decode_path,
3828 raise NotEnoughData(
3829 "encoded length is longer than data",
3830 klass=self.__class__,
3831 decode_path=decode_path,
3834 v, tail = v[:l], v[l:]
3835 if evgen_mode and not self._bound_min <= len(v) <= self._bound_max:
3837 msg=str(BoundsError(self._bound_min, len(v), self._bound_max)),
3838 klass=self.__class__,
3839 decode_path=decode_path,
3843 obj = self.__class__(
3845 None if (evgen_mode and self.evgen_mode_skip_value)
3848 bounds=(self._bound_min, self._bound_max),
3851 default=self.default,
3852 optional=self.optional,
3853 _decoded=(offset, llen, l),
3856 except DecodeError as err:
3859 klass=self.__class__,
3860 decode_path=decode_path,
3863 except BoundsError as err:
3866 klass=self.__class__,
3867 decode_path=decode_path,
3870 yield decode_path, obj, tail
3872 if t != self.tag_constructed:
3874 klass=self.__class__,
3875 decode_path=decode_path,
3878 if not ctx.get("bered", False):
3880 "unallowed BER constructed encoding",
3881 klass=self.__class__,
3882 decode_path=decode_path,
3890 l, llen, v = len_decode(lv)
3891 except LenIndefForm:
3892 llen, l, v = 1, 0, lv[1:]
3894 except DecodeError as err:
3895 raise err.__class__(
3897 klass=self.__class__,
3898 decode_path=decode_path,
3902 raise NotEnoughData(
3903 "encoded length is longer than data",
3904 klass=self.__class__,
3905 decode_path=decode_path,
3910 sub_offset = offset + tlen + llen
3915 if v[:EOC_LEN].tobytes() == EOC:
3922 "chunk out of bounds",
3923 klass=self.__class__,
3924 decode_path=decode_path + (str(len(chunks) - 1),),
3925 offset=chunks[-1].offset,
3929 sub_decode_path = decode_path + (str(chunks_count),)
3930 for _decode_path, chunk, v_tail in OctetString().decode_evgen(
3933 decode_path=sub_decode_path,
3936 _ctx_immutable=False,
3938 yield _decode_path, chunk, v_tail
3939 if not chunk.ber_encoded:
3940 payload_len += chunk.vlen
3943 sub_decode_path = decode_path + (str(len(chunks)),)
3944 _, chunk, v_tail = next(OctetString().decode_evgen(
3947 decode_path=sub_decode_path,
3950 _ctx_immutable=False,
3953 chunks.append(chunk)
3956 "expected OctetString encoded chunk",
3957 klass=self.__class__,
3958 decode_path=sub_decode_path,
3961 sub_offset += chunk.tlvlen
3962 vlen += chunk.tlvlen
3964 if evgen_mode and not self._bound_min <= payload_len <= self._bound_max:
3966 msg=str(BoundsError(self._bound_min, payload_len, self._bound_max)),
3967 klass=self.__class__,
3968 decode_path=decode_path,
3972 obj = self.__class__(
3974 None if evgen_mode else
3975 b"".join(bytes(chunk) for chunk in chunks)
3977 bounds=(self._bound_min, self._bound_max),
3980 default=self.default,
3981 optional=self.optional,
3982 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3985 except DecodeError as err:
3988 klass=self.__class__,
3989 decode_path=decode_path,
3992 except BoundsError as err:
3995 klass=self.__class__,
3996 decode_path=decode_path,
3999 obj.lenindef = lenindef
4000 obj.ber_encoded = True
4001 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
4004 return pp_console_row(next(self.pps()))
4006 def pps(self, decode_path=()):
4009 asn1_type_name=self.asn1_type_name,
4010 obj_name=self.__class__.__name__,
4011 decode_path=decode_path,
4012 value=("%d bytes" % len(self._value)) if self.ready else None,
4013 blob=self._value if self.ready else None,
4014 optional=self.optional,
4015 default=self == self.default,
4016 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4017 expl=None if self._expl is None else tag_decode(self._expl),
4022 expl_offset=self.expl_offset if self.expled else None,
4023 expl_tlen=self.expl_tlen if self.expled else None,
4024 expl_llen=self.expl_llen if self.expled else None,
4025 expl_vlen=self.expl_vlen if self.expled else None,
4026 expl_lenindef=self.expl_lenindef,
4027 lenindef=self.lenindef,
4028 ber_encoded=self.ber_encoded,
4031 defined_by, defined = self.defined or (None, None)
4032 if defined_by is not None:
4034 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4036 for pp in self.pps_lenindef(decode_path):
4040 def agg_octet_string(evgens, decode_path, raw, writer):
4041 """Aggregate constructed string (OctetString and its derivatives)
4043 :param evgens: iterator of generated events
4044 :param decode_path: points to the string we want to decode
4045 :param raw: slicebable (memoryview, bytearray, etc) with
4046 the data evgens are generated on
4047 :param writer: buffer.write where string is going to be saved
4048 :param writer: where string is going to be saved. Must comply
4049 with ``io.RawIOBase.write`` behaviour
4051 .. seealso:: :ref:`agg_octet_string`
4053 decode_path_len = len(decode_path)
4054 for dp, obj, _ in evgens:
4055 if dp[:decode_path_len] != decode_path:
4057 if not obj.ber_encoded:
4058 write_full(writer, raw[
4059 obj.offset + obj.tlen + obj.llen:
4060 obj.offset + obj.tlen + obj.llen + obj.vlen -
4061 (EOC_LEN if obj.expl_lenindef else 0)
4063 if len(dp) == decode_path_len:
4067 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
4071 """``NULL`` null object
4079 tag_default = tag_encode(5)
4080 asn1_type_name = "NULL"
4084 value=None, # unused, but Sequence passes it
4091 :param bytes impl: override default tag with ``IMPLICIT`` one
4092 :param bytes expl: override default tag with ``EXPLICIT`` one
4093 :param bool optional: is object ``OPTIONAL`` in sequence
4095 super().__init__(impl, expl, None, optional, _decoded)
4102 def __getstate__(self):
4118 def __eq__(self, their):
4119 if not issubclass(their.__class__, Null):
4122 self.tag == their.tag and
4123 self._expl == their._expl
4133 return self.__class__(
4134 impl=self.tag if impl is None else impl,
4135 expl=self._expl if expl is None else expl,
4136 optional=self.optional if optional is None else optional,
4140 return self.tag + LEN0
4142 def _encode1st(self, state):
4143 return len(self.tag) + 1, state
4145 def _encode2nd(self, writer, state_iter):
4146 write_full(writer, self.tag + LEN0)
4148 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4150 t, _, lv = tag_strip(tlv)
4151 except DecodeError as err:
4152 raise err.__class__(
4154 klass=self.__class__,
4155 decode_path=decode_path,
4160 klass=self.__class__,
4161 decode_path=decode_path,
4164 if tag_only: # pragma: no cover
4168 l, _, v = len_decode(lv)
4169 except DecodeError as err:
4170 raise err.__class__(
4172 klass=self.__class__,
4173 decode_path=decode_path,
4177 raise InvalidLength(
4178 "Null must have zero length",
4179 klass=self.__class__,
4180 decode_path=decode_path,
4183 obj = self.__class__(
4186 optional=self.optional,
4187 _decoded=(offset, 1, 0),
4189 yield decode_path, obj, v
4192 return pp_console_row(next(self.pps()))
4194 def pps(self, decode_path=()):
4197 asn1_type_name=self.asn1_type_name,
4198 obj_name=self.__class__.__name__,
4199 decode_path=decode_path,
4200 optional=self.optional,
4201 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4202 expl=None if self._expl is None else tag_decode(self._expl),
4207 expl_offset=self.expl_offset if self.expled else None,
4208 expl_tlen=self.expl_tlen if self.expled else None,
4209 expl_llen=self.expl_llen if self.expled else None,
4210 expl_vlen=self.expl_vlen if self.expled else None,
4211 expl_lenindef=self.expl_lenindef,
4214 for pp in self.pps_lenindef(decode_path):
4218 ObjectIdentifierState = namedtuple(
4219 "ObjectIdentifierState",
4220 BasicState._fields + ("value", "defines"),
4225 class ObjectIdentifier(Obj):
4226 """``OBJECT IDENTIFIER`` OID type
4228 >>> oid = ObjectIdentifier((1, 2, 3))
4229 OBJECT IDENTIFIER 1.2.3
4230 >>> oid == ObjectIdentifier("1.2.3")
4236 >>> oid + (4, 5) + ObjectIdentifier("1.7")
4237 OBJECT IDENTIFIER 1.2.3.4.5.1.7
4239 >>> str(ObjectIdentifier((3, 1)))
4240 Traceback (most recent call last):
4241 pyderasn.InvalidOID: unacceptable first arc value
4243 __slots__ = ("defines",)
4244 tag_default = tag_encode(6)
4245 asn1_type_name = "OBJECT IDENTIFIER"
4258 :param value: set the value. Either tuples of integers,
4259 string of "."-concatenated integers, or
4260 :py:class:`pyderasn.ObjectIdentifier` object
4261 :param defines: sequence of tuples. Each tuple has two elements.
4262 First one is relative to current one decode
4263 path, aiming to the field defined by that OID.
4264 Read about relative path in
4265 :py:func:`pyderasn.abs_decode_path`. Second
4266 tuple element is ``{OID: pyderasn.Obj()}``
4267 dictionary, mapping between current OID value
4268 and structure applied to defined field.
4270 .. seealso:: :ref:`definedby`
4272 :param bytes impl: override default tag with ``IMPLICIT`` one
4273 :param bytes expl: override default tag with ``EXPLICIT`` one
4274 :param default: set default value. Type same as in ``value``
4275 :param bool optional: is object ``OPTIONAL`` in sequence
4277 super().__init__(impl, expl, default, optional, _decoded)
4279 if value is not None:
4280 self._value = self._value_sanitize(value)
4281 if default is not None:
4282 default = self._value_sanitize(default)
4283 self.default = self.__class__(
4288 if self._value is None:
4289 self._value = default
4290 self.defines = defines
4292 def __add__(self, their):
4293 if their.__class__ == tuple:
4294 return self.__class__(self._value + array("L", their))
4295 if isinstance(their, self.__class__):
4296 return self.__class__(self._value + their._value)
4297 raise InvalidValueType((self.__class__, tuple))
4299 def _value_sanitize(self, value):
4300 if issubclass(value.__class__, ObjectIdentifier):
4302 if isinstance(value, str):
4304 value = array("L", (pureint(arc) for arc in value.split(".")))
4306 raise InvalidOID("unacceptable arcs values")
4307 if value.__class__ == tuple:
4309 value = array("L", value)
4310 except OverflowError as err:
4311 raise InvalidOID(repr(err))
4312 if value.__class__ is array:
4314 raise InvalidOID("less than 2 arcs")
4315 first_arc = value[0]
4316 if first_arc in (0, 1):
4317 if not (0 <= value[1] <= 39):
4318 raise InvalidOID("second arc is too wide")
4319 elif first_arc == 2:
4322 raise InvalidOID("unacceptable first arc value")
4323 if not all(arc >= 0 for arc in value):
4324 raise InvalidOID("negative arc value")
4326 raise InvalidValueType((self.__class__, str, tuple))
4330 return self._value is not None
4332 def __getstate__(self):
4333 return ObjectIdentifierState(
4350 def __setstate__(self, state):
4351 super().__setstate__(state)
4352 self._value = state.value
4353 self.defines = state.defines
4356 self._assert_ready()
4357 return iter(self._value)
4360 return ".".join(str(arc) for arc in self._value or ())
4363 self._assert_ready()
4364 return hash(b"".join((
4366 bytes(self._expl or b""),
4367 str(self._value).encode("ascii"),
4370 def __eq__(self, their):
4371 if their.__class__ == tuple:
4372 return self._value == array("L", their)
4373 if not issubclass(their.__class__, ObjectIdentifier):
4376 self.tag == their.tag and
4377 self._expl == their._expl and
4378 self._value == their._value
4381 def __lt__(self, their):
4382 return self._value < their._value
4393 return self.__class__(
4395 defines=self.defines if defines is None else defines,
4396 impl=self.tag if impl is None else impl,
4397 expl=self._expl if expl is None else expl,
4398 default=self.default if default is None else default,
4399 optional=self.optional if optional is None else optional,
4402 def _encode_octets(self):
4403 self._assert_ready()
4405 first_value = value[1]
4406 first_arc = value[0]
4409 elif first_arc == 1:
4411 elif first_arc == 2:
4413 else: # pragma: no cover
4414 raise RuntimeError("invalid arc is stored")
4415 octets = [zero_ended_encode(first_value)]
4416 for arc in value[2:]:
4417 octets.append(zero_ended_encode(arc))
4418 return b"".join(octets)
4421 v = self._encode_octets()
4422 return b"".join((self.tag, len_encode(len(v)), v))
4424 def _encode1st(self, state):
4425 l = len(self._encode_octets())
4426 return len(self.tag) + len_size(l) + l, state
4428 def _encode2nd(self, writer, state_iter):
4429 write_full(writer, self._encode())
4431 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4433 t, _, lv = tag_strip(tlv)
4434 except DecodeError as err:
4435 raise err.__class__(
4437 klass=self.__class__,
4438 decode_path=decode_path,
4443 klass=self.__class__,
4444 decode_path=decode_path,
4447 if tag_only: # pragma: no cover
4451 l, llen, v = len_decode(lv)
4452 except DecodeError as err:
4453 raise err.__class__(
4455 klass=self.__class__,
4456 decode_path=decode_path,
4460 raise NotEnoughData(
4461 "encoded length is longer than data",
4462 klass=self.__class__,
4463 decode_path=decode_path,
4467 raise NotEnoughData(
4469 klass=self.__class__,
4470 decode_path=decode_path,
4473 v, tail = v[:l], v[l:]
4481 if i == 0 and octet == 0x80:
4482 if ctx.get("bered", False):
4486 "non normalized arc encoding",
4487 klass=self.__class__,
4488 decode_path=decode_path,
4491 arc = (arc << 7) | (octet & 0x7F)
4492 if octet & 0x80 == 0:
4495 except OverflowError:
4497 "too huge value for local unsigned long",
4498 klass=self.__class__,
4499 decode_path=decode_path,
4508 klass=self.__class__,
4509 decode_path=decode_path,
4513 second_arc = arcs[0]
4514 if 0 <= second_arc <= 39:
4516 elif 40 <= second_arc <= 79:
4522 obj = self.__class__(
4523 value=array("L", (first_arc, second_arc)) + arcs[1:],
4526 default=self.default,
4527 optional=self.optional,
4528 _decoded=(offset, llen, l),
4531 obj.ber_encoded = True
4532 yield decode_path, obj, tail
4535 return pp_console_row(next(self.pps()))
4537 def pps(self, decode_path=()):
4540 asn1_type_name=self.asn1_type_name,
4541 obj_name=self.__class__.__name__,
4542 decode_path=decode_path,
4543 value=str(self) if self.ready else None,
4544 optional=self.optional,
4545 default=self == self.default,
4546 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4547 expl=None if self._expl is None else tag_decode(self._expl),
4552 expl_offset=self.expl_offset if self.expled else None,
4553 expl_tlen=self.expl_tlen if self.expled else None,
4554 expl_llen=self.expl_llen if self.expled else None,
4555 expl_vlen=self.expl_vlen if self.expled else None,
4556 expl_lenindef=self.expl_lenindef,
4557 ber_encoded=self.ber_encoded,
4560 for pp in self.pps_lenindef(decode_path):
4564 class Enumerated(Integer):
4565 """``ENUMERATED`` integer type
4567 This type is identical to :py:class:`pyderasn.Integer`, but requires
4568 schema to be specified and does not accept values missing from it.
4571 tag_default = tag_encode(10)
4572 asn1_type_name = "ENUMERATED"
4583 bounds=None, # dummy argument, workability for Integer.decode
4586 value, bounds, impl, expl, default, optional, _specs, _decoded,
4588 if len(self.specs) == 0:
4589 raise ValueError("schema must be specified")
4591 def _value_sanitize(self, value):
4592 if isinstance(value, self.__class__):
4593 value = value._value
4594 elif isinstance(value, int):
4595 for _value in self.specs.values():
4600 "unknown integer value: %s" % value,
4601 klass=self.__class__,
4603 elif isinstance(value, str):
4604 value = self.specs.get(value)
4606 raise ObjUnknown("integer value: %s" % value)
4608 raise InvalidValueType((self.__class__, int, str))
4620 return self.__class__(
4622 impl=self.tag if impl is None else impl,
4623 expl=self._expl if expl is None else expl,
4624 default=self.default if default is None else default,
4625 optional=self.optional if optional is None else optional,
4630 def escape_control_unicode(c):
4631 if unicat(c)[0] == "C":
4632 c = repr(c).lstrip("u").strip("'")
4636 class CommonString(OctetString):
4637 """Common class for all strings
4639 Everything resembles :py:class:`pyderasn.OctetString`, except
4640 ability to deal with unicode text strings.
4642 >>> hexenc("привет мир".encode("utf-8"))
4643 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4644 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
4646 >>> s = UTF8String("привет мир")
4647 UTF8String UTF8String привет мир
4649 'привет мир'
4650 >>> hexenc(bytes(s))
4651 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4653 >>> PrintableString("привет мир")
4654 Traceback (most recent call last):
4655 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
4657 >>> BMPString("ада", bounds=(2, 2))
4658 Traceback (most recent call last):
4659 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
4660 >>> s = BMPString("ад", bounds=(2, 2))
4663 >>> hexenc(bytes(s))
4670 - Text Encoding, validation
4671 * - :py:class:`pyderasn.UTF8String`
4673 * - :py:class:`pyderasn.NumericString`
4674 - proper alphabet validation
4675 * - :py:class:`pyderasn.PrintableString`
4676 - proper alphabet validation
4677 * - :py:class:`pyderasn.TeletexString`
4679 * - :py:class:`pyderasn.T61String`
4681 * - :py:class:`pyderasn.VideotexString`
4683 * - :py:class:`pyderasn.IA5String`
4684 - proper alphabet validation
4685 * - :py:class:`pyderasn.GraphicString`
4687 * - :py:class:`pyderasn.VisibleString`, :py:class:`pyderasn.ISO646String`
4688 - proper alphabet validation
4689 * - :py:class:`pyderasn.GeneralString`
4691 * - :py:class:`pyderasn.UniversalString`
4693 * - :py:class:`pyderasn.BMPString`
4698 def _value_sanitize(self, value):
4700 value_decoded = None
4701 if isinstance(value, self.__class__):
4702 value_raw = value._value
4703 elif value.__class__ == str:
4704 value_decoded = value
4705 elif value.__class__ == bytes:
4708 raise InvalidValueType((self.__class__, str, bytes))
4711 value_decoded.encode(self.encoding)
4712 if value_raw is None else value_raw
4715 value_raw.decode(self.encoding)
4716 if value_decoded is None else value_decoded
4718 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4719 raise DecodeError(str(err))
4720 if not self._bound_min <= len(value_decoded) <= self._bound_max:
4728 def __eq__(self, their):
4729 if their.__class__ == bytes:
4730 return self._value == their
4731 if their.__class__ == str:
4732 return self._value == their.encode(self.encoding)
4733 if not isinstance(their, self.__class__):
4736 self._value == their._value and
4737 self.tag == their.tag and
4738 self._expl == their._expl
4741 def __unicode__(self):
4743 return self._value.decode(self.encoding)
4744 return str(self._value)
4747 return pp_console_row(next(self.pps()))
4749 def pps(self, decode_path=()):
4752 value = "".join(escape_control_unicode(c) for c in self.__unicode__())
4755 asn1_type_name=self.asn1_type_name,
4756 obj_name=self.__class__.__name__,
4757 decode_path=decode_path,
4759 optional=self.optional,
4760 default=self == self.default,
4761 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4762 expl=None if self._expl is None else tag_decode(self._expl),
4767 expl_offset=self.expl_offset if self.expled else None,
4768 expl_tlen=self.expl_tlen if self.expled else None,
4769 expl_llen=self.expl_llen if self.expled else None,
4770 expl_vlen=self.expl_vlen if self.expled else None,
4771 expl_lenindef=self.expl_lenindef,
4772 ber_encoded=self.ber_encoded,
4775 for pp in self.pps_lenindef(decode_path):
4779 class UTF8String(CommonString):
4781 tag_default = tag_encode(12)
4783 asn1_type_name = "UTF8String"
4786 class AllowableCharsMixin:
4790 def allowable_chars(self):
4791 return frozenset(chr(c) for c in self._allowable_chars)
4793 def _value_sanitize(self, value):
4794 value = super()._value_sanitize(value)
4795 if not frozenset(value) <= self._allowable_chars:
4796 raise DecodeError("non satisfying alphabet value")
4800 NUMERIC_ALLOWABLE_CHARS = frozenset(digits.encode("ascii") + b" ")
4803 class NumericString(AllowableCharsMixin, CommonString):
4806 Its value is properly sanitized: only ASCII digits with spaces can
4809 >>> NumericString().allowable_chars
4810 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4812 __slots__ = ("_allowable_chars",)
4813 tag_default = tag_encode(18)
4815 asn1_type_name = "NumericString"
4817 def __init__(self, *args, **kwargs):
4818 self._allowable_chars = NUMERIC_ALLOWABLE_CHARS
4819 super().__init__(*args, **kwargs)
4822 PrintableStringState = namedtuple(
4823 "PrintableStringState",
4824 OctetStringState._fields + ("allowable_chars",),
4829 PRINTABLE_ALLOWABLE_CHARS = frozenset(
4830 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4834 class PrintableString(AllowableCharsMixin, CommonString):
4837 Its value is properly sanitized: see X.680 41.4 table 10.
4839 >>> PrintableString().allowable_chars
4840 frozenset([' ', "'", ..., 'z'])
4841 >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4842 PrintableString PrintableString foo*bar
4843 >>> obj.allow_asterisk, obj.allow_ampersand
4846 __slots__ = ("_allowable_chars",)
4847 tag_default = tag_encode(19)
4849 asn1_type_name = "PrintableString"
4850 _asterisk = frozenset("*".encode("ascii"))
4851 _ampersand = frozenset("&".encode("ascii"))
4863 allow_asterisk=False,
4864 allow_ampersand=False,
4867 :param allow_asterisk: allow asterisk character
4868 :param allow_ampersand: allow ampersand character
4870 allowable_chars = PRINTABLE_ALLOWABLE_CHARS
4872 allowable_chars |= self._asterisk
4874 allowable_chars |= self._ampersand
4875 self._allowable_chars = allowable_chars
4877 value, bounds, impl, expl, default, optional, _decoded, ctx,
4881 def allow_asterisk(self):
4882 """Is asterisk character allowed?
4884 return self._asterisk <= self._allowable_chars
4887 def allow_ampersand(self):
4888 """Is ampersand character allowed?
4890 return self._ampersand <= self._allowable_chars
4892 def __getstate__(self):
4893 return PrintableStringState(
4894 *super().__getstate__(),
4895 **{"allowable_chars": self._allowable_chars}
4898 def __setstate__(self, state):
4899 super().__setstate__(state)
4900 self._allowable_chars = state.allowable_chars
4911 return self.__class__(
4914 (self._bound_min, self._bound_max)
4915 if bounds is None else bounds
4917 impl=self.tag if impl is None else impl,
4918 expl=self._expl if expl is None else expl,
4919 default=self.default if default is None else default,
4920 optional=self.optional if optional is None else optional,
4921 allow_asterisk=self.allow_asterisk,
4922 allow_ampersand=self.allow_ampersand,
4926 class TeletexString(CommonString):
4928 tag_default = tag_encode(20)
4929 encoding = "iso-8859-1"
4930 asn1_type_name = "TeletexString"
4933 class T61String(TeletexString):
4935 asn1_type_name = "T61String"
4938 class VideotexString(CommonString):
4940 tag_default = tag_encode(21)
4941 encoding = "iso-8859-1"
4942 asn1_type_name = "VideotexString"
4945 IA5_ALLOWABLE_CHARS = frozenset(b"".join(
4946 chr(c).encode("ascii") for c in range(128)
4950 class IA5String(AllowableCharsMixin, CommonString):
4953 Its value is properly sanitized: it is a mix of
4955 * http://www.itscj.ipsj.or.jp/iso-ir/006.pdf (G)
4956 * http://www.itscj.ipsj.or.jp/iso-ir/001.pdf (C0)
4957 * DEL character (0x7F)
4959 It is just 7-bit ASCII.
4961 >>> IA5String().allowable_chars
4962 frozenset(["NUL", ... "DEL"])
4964 __slots__ = ("_allowable_chars",)
4965 tag_default = tag_encode(22)
4967 asn1_type_name = "IA5"
4969 def __init__(self, *args, **kwargs):
4970 self._allowable_chars = IA5_ALLOWABLE_CHARS
4971 super().__init__(*args, **kwargs)
4974 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4975 LEN_LEN_YYMMDDHHMMSSZ = len_encode(LEN_YYMMDDHHMMSSZ)
4976 LEN_YYMMDDHHMMSSZ_WITH_LEN = len(LEN_LEN_YYMMDDHHMMSSZ) + LEN_YYMMDDHHMMSSZ
4977 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4978 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4979 LEN_LEN_YYYYMMDDHHMMSSZ = len_encode(LEN_YYYYMMDDHHMMSSZ)
4982 VISIBLE_ALLOWABLE_CHARS = frozenset(b"".join(
4983 chr(c).encode("ascii") for c in range(ord(" "), ord("~") + 1)
4987 class VisibleString(AllowableCharsMixin, CommonString):
4990 Its value is properly sanitized. ASCII subset from space to tilde is
4991 allowed: http://www.itscj.ipsj.or.jp/iso-ir/006.pdf
4993 >>> VisibleString().allowable_chars
4994 frozenset([" ", ... "~"])
4996 __slots__ = ("_allowable_chars",)
4997 tag_default = tag_encode(26)
4999 asn1_type_name = "VisibleString"
5001 def __init__(self, *args, **kwargs):
5002 self._allowable_chars = VISIBLE_ALLOWABLE_CHARS
5003 super().__init__(*args, **kwargs)
5006 class ISO646String(VisibleString):
5008 asn1_type_name = "ISO646String"
5011 UTCTimeState = namedtuple(
5013 OctetStringState._fields + ("ber_raw",),
5018 def str_to_time_fractions(value):
5020 year, v = (v // 10**10), (v % 10**10)
5021 month, v = (v // 10**8), (v % 10**8)
5022 day, v = (v // 10**6), (v % 10**6)
5023 hour, v = (v // 10**4), (v % 10**4)
5024 minute, second = (v // 100), (v % 100)
5025 return year, month, day, hour, minute, second
5028 class UTCTime(VisibleString):
5029 """``UTCTime`` datetime type
5031 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5032 UTCTime UTCTime 2017-09-30T22:07:50
5038 datetime.datetime(2017, 9, 30, 22, 7, 50)
5039 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
5040 datetime.datetime(1957, 9, 30, 22, 7, 50)
5042 If BER encoded value was met, then ``ber_raw`` attribute will hold
5043 its raw representation.
5047 Only **naive** ``datetime`` objects are supported.
5048 Library assumes that all work is done in UTC.
5052 Pay attention that ``UTCTime`` can not hold full year, so all years
5053 having < 50 years are treated as 20xx, 19xx otherwise, according to
5054 X.509 recommendation. Use ``GeneralizedTime`` instead for
5059 No strict validation of UTC offsets are made (only applicable to
5060 **BER**), but very crude:
5062 * minutes are not exceeding 60
5063 * offset value is not exceeding 14 hours
5065 __slots__ = ("ber_raw",)
5066 tag_default = tag_encode(23)
5068 asn1_type_name = "UTCTime"
5069 evgen_mode_skip_value = False
5079 bounds=None, # dummy argument, workability for OctetString.decode
5083 :param value: set the value. Either datetime type, or
5084 :py:class:`pyderasn.UTCTime` object
5085 :param bytes impl: override default tag with ``IMPLICIT`` one
5086 :param bytes expl: override default tag with ``EXPLICIT`` one
5087 :param default: set default value. Type same as in ``value``
5088 :param bool optional: is object ``OPTIONAL`` in sequence
5090 super().__init__(None, None, impl, expl, None, optional, _decoded, ctx)
5093 if value is not None:
5094 self._value, self.ber_raw = self._value_sanitize(value, ctx)
5095 self.ber_encoded = self.ber_raw is not None
5096 if default is not None:
5097 default, _ = self._value_sanitize(default)
5098 self.default = self.__class__(
5103 if self._value is None:
5104 self._value = default
5106 self.optional = optional
5108 def _strptime_bered(self, value):
5109 year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
5112 raise ValueError("no timezone")
5113 year += 2000 if year < 50 else 1900
5114 decoded = datetime(year, month, day, hour, minute)
5116 if value[-1] == "Z":
5120 raise ValueError("invalid UTC offset")
5121 if value[-5] == "-":
5123 elif value[-5] == "+":
5126 raise ValueError("invalid UTC offset")
5127 v = pureint(value[-4:])
5128 offset, v = (60 * (v % 100)), v // 100
5130 raise ValueError("invalid UTC offset minutes")
5132 if offset > 14 * 3600:
5133 raise ValueError("too big UTC offset")
5137 return offset, decoded
5139 raise ValueError("invalid UTC offset seconds")
5140 seconds = pureint(value)
5142 raise ValueError("invalid seconds value")
5143 return offset, decoded + timedelta(seconds=seconds)
5145 def _strptime(self, value):
5146 # datetime.strptime's format: %y%m%d%H%M%SZ
5147 if len(value) != LEN_YYMMDDHHMMSSZ:
5148 raise ValueError("invalid UTCTime length")
5149 if value[-1] != "Z":
5150 raise ValueError("non UTC timezone")
5151 year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
5152 year += 2000 if year < 50 else 1900
5153 return datetime(year, month, day, hour, minute, second)
5155 def _dt_sanitize(self, value):
5156 if value.year < 1950 or value.year > 2049:
5157 raise ValueError("UTCTime can hold only 1950-2049 years")
5158 return value.replace(microsecond=0)
5160 def _value_sanitize(self, value, ctx=None):
5161 if value.__class__ == bytes:
5163 value_decoded = value.decode("ascii")
5164 except (UnicodeEncodeError, UnicodeDecodeError) as err:
5165 raise DecodeError("invalid UTCTime encoding: %r" % err)
5168 return self._strptime(value_decoded), None
5169 except (TypeError, ValueError) as _err:
5171 if (ctx is not None) and ctx.get("bered", False):
5173 offset, _value = self._strptime_bered(value_decoded)
5174 _value = _value - timedelta(seconds=offset)
5175 return self._dt_sanitize(_value), value
5176 except (TypeError, ValueError, OverflowError) as _err:
5179 "invalid %s format: %r" % (self.asn1_type_name, err),
5180 klass=self.__class__,
5182 if isinstance(value, self.__class__):
5183 return value._value, None
5184 if value.__class__ == datetime:
5185 if value.tzinfo is not None:
5186 raise ValueError("only naive datetime supported")
5187 return self._dt_sanitize(value), None
5188 raise InvalidValueType((self.__class__, datetime))
5190 def _pp_value(self):
5192 value = self._value.isoformat()
5193 if self.ber_encoded:
5194 value += " (%s)" % self.ber_raw
5198 def __unicode__(self):
5200 value = self._value.isoformat()
5201 if self.ber_encoded:
5202 value += " (%s)" % self.ber_raw
5204 return str(self._pp_value())
5206 def __getstate__(self):
5207 return UTCTimeState(*super().__getstate__(), **{"ber_raw": self.ber_raw})
5209 def __setstate__(self, state):
5210 super().__setstate__(state)
5211 self.ber_raw = state.ber_raw
5213 def __bytes__(self):
5214 self._assert_ready()
5215 return self._encode_time()
5217 def __eq__(self, their):
5218 if their.__class__ == bytes:
5219 return self._encode_time() == their
5220 if their.__class__ == datetime:
5221 return self.todatetime() == their
5222 if not isinstance(their, self.__class__):
5225 self._value == their._value and
5226 self.tag == their.tag and
5227 self._expl == their._expl
5230 def _encode_time(self):
5231 return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
5234 self._assert_ready()
5235 return b"".join((self.tag, LEN_LEN_YYMMDDHHMMSSZ, self._encode_time()))
5237 def _encode1st(self, state):
5238 return len(self.tag) + LEN_YYMMDDHHMMSSZ_WITH_LEN, state
5240 def _encode2nd(self, writer, state_iter):
5241 self._assert_ready()
5242 write_full(writer, self._encode())
5244 def _encode_cer(self, writer):
5245 write_full(writer, self._encode())
5247 def todatetime(self):
5250 def totzdatetime(self):
5252 return self._value.replace(tzinfo=tzUTC)
5253 except TypeError as err:
5254 raise NotImplementedError("Missing dateutil.tz") from err
5257 return pp_console_row(next(self.pps()))
5259 def pps(self, decode_path=()):
5262 asn1_type_name=self.asn1_type_name,
5263 obj_name=self.__class__.__name__,
5264 decode_path=decode_path,
5265 value=self._pp_value(),
5266 optional=self.optional,
5267 default=self == self.default,
5268 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5269 expl=None if self._expl is None else tag_decode(self._expl),
5274 expl_offset=self.expl_offset if self.expled else None,
5275 expl_tlen=self.expl_tlen if self.expled else None,
5276 expl_llen=self.expl_llen if self.expled else None,
5277 expl_vlen=self.expl_vlen if self.expled else None,
5278 expl_lenindef=self.expl_lenindef,
5279 ber_encoded=self.ber_encoded,
5282 for pp in self.pps_lenindef(decode_path):
5286 class GeneralizedTime(UTCTime):
5287 """``GeneralizedTime`` datetime type
5289 This type is similar to :py:class:`pyderasn.UTCTime`.
5291 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5292 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
5294 '20170930220750.000123Z'
5295 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
5296 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
5300 Only **naive** datetime objects are supported.
5301 Library assumes that all work is done in UTC.
5305 Only **microsecond** fractions are supported in DER encoding.
5306 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
5307 higher precision values.
5311 **BER** encoded data can loss information (accuracy) during
5312 decoding because of float transformations.
5316 **Zero** year is unsupported.
5319 tag_default = tag_encode(24)
5320 asn1_type_name = "GeneralizedTime"
5322 def _dt_sanitize(self, value):
5325 def _strptime_bered(self, value):
5326 if len(value) < 4 + 3 * 2:
5327 raise ValueError("invalid GeneralizedTime")
5328 year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
5329 decoded = datetime(year, month, day, hour)
5330 offset, value = 0, value[10:]
5332 return offset, decoded
5333 if value[-1] == "Z":
5336 for char, sign in (("-", -1), ("+", 1)):
5337 idx = value.rfind(char)
5340 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
5341 v = pureint(offset_raw)
5342 if len(offset_raw) == 4:
5343 offset, v = (60 * (v % 100)), v // 100
5345 raise ValueError("invalid UTC offset minutes")
5346 elif len(offset_raw) == 2:
5349 raise ValueError("invalid UTC offset")
5351 if offset > 14 * 3600:
5352 raise ValueError("too big UTC offset")
5356 return offset, decoded
5357 if value[0] in DECIMAL_SIGNS:
5359 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
5362 raise ValueError("stripped minutes")
5363 decoded += timedelta(seconds=60 * pureint(value[:2]))
5366 return offset, decoded
5367 if value[0] in DECIMAL_SIGNS:
5369 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
5372 raise ValueError("stripped seconds")
5373 decoded += timedelta(seconds=pureint(value[:2]))
5376 return offset, decoded
5377 if value[0] not in DECIMAL_SIGNS:
5378 raise ValueError("invalid format after seconds")
5380 decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
5383 def _strptime(self, value):
5385 if l == LEN_YYYYMMDDHHMMSSZ:
5386 # datetime.strptime's format: %Y%m%d%H%M%SZ
5387 if value[-1] != "Z":
5388 raise ValueError("non UTC timezone")
5389 return datetime(*str_to_time_fractions(value[:-1]))
5390 if l >= LEN_YYYYMMDDHHMMSSDMZ:
5391 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
5392 if value[-1] != "Z":
5393 raise ValueError("non UTC timezone")
5394 if value[14] != ".":
5395 raise ValueError("no fractions separator")
5398 raise ValueError("trailing zero")
5401 raise ValueError("only microsecond fractions are supported")
5402 us = pureint(us + ("0" * (6 - us_len)))
5403 year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
5404 return datetime(year, month, day, hour, minute, second, us)
5405 raise ValueError("invalid GeneralizedTime length")
5407 def _encode_time(self):
5409 encoded = value.strftime("%Y%m%d%H%M%S")
5410 if value.microsecond > 0:
5411 encoded += (".%06d" % value.microsecond).rstrip("0")
5412 return (encoded + "Z").encode("ascii")
5415 self._assert_ready()
5417 if value.microsecond > 0:
5418 encoded = self._encode_time()
5419 return b"".join((self.tag, len_encode(len(encoded)), encoded))
5420 return b"".join((self.tag, LEN_LEN_YYYYMMDDHHMMSSZ, self._encode_time()))
5422 def _encode1st(self, state):
5423 self._assert_ready()
5424 vlen = len(self._encode_time())
5425 return len(self.tag) + len_size(vlen) + vlen, state
5427 def _encode2nd(self, writer, state_iter):
5428 write_full(writer, self._encode())
5431 class GraphicString(CommonString):
5433 tag_default = tag_encode(25)
5434 encoding = "iso-8859-1"
5435 asn1_type_name = "GraphicString"
5438 class GeneralString(CommonString):
5440 tag_default = tag_encode(27)
5441 encoding = "iso-8859-1"
5442 asn1_type_name = "GeneralString"
5445 class UniversalString(CommonString):
5447 tag_default = tag_encode(28)
5448 encoding = "utf-32-be"
5449 asn1_type_name = "UniversalString"
5452 class BMPString(CommonString):
5454 tag_default = tag_encode(30)
5455 encoding = "utf-16-be"
5456 asn1_type_name = "BMPString"
5459 ChoiceState = namedtuple(
5461 BasicState._fields + ("specs", "value",),
5467 """``CHOICE`` special type
5471 class GeneralName(Choice):
5473 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
5474 ("dNSName", IA5String(impl=tag_ctxp(2))),
5477 >>> gn = GeneralName()
5479 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
5480 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5481 >>> gn["dNSName"] = IA5String("bar.baz")
5482 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
5483 >>> gn["rfc822Name"]
5486 [2] IA5String IA5 bar.baz
5489 >>> gn.value == gn["dNSName"]
5492 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
5494 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
5495 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5497 __slots__ = ("specs",)
5499 asn1_type_name = "CHOICE"
5512 :param value: set the value. Either ``(choice, value)`` tuple, or
5513 :py:class:`pyderasn.Choice` object
5514 :param bytes impl: can not be set, do **not** use it
5515 :param bytes expl: override default tag with ``EXPLICIT`` one
5516 :param default: set default value. Type same as in ``value``
5517 :param bool optional: is object ``OPTIONAL`` in sequence
5519 if impl is not None:
5520 raise ValueError("no implicit tag allowed for CHOICE")
5521 super().__init__(None, expl, default, optional, _decoded)
5523 schema = getattr(self, "schema", ())
5524 if len(schema) == 0:
5525 raise ValueError("schema must be specified")
5527 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5530 if value is not None:
5531 self._value = self._value_sanitize(value)
5532 if default is not None:
5533 default_value = self._value_sanitize(default)
5534 default_obj = self.__class__(impl=self.tag, expl=self._expl)
5535 default_obj.specs = self.specs
5536 default_obj._value = default_value
5537 self.default = default_obj
5539 self._value = copy(default_obj._value)
5540 if self._expl is not None:
5541 tag_class, _, tag_num = tag_decode(self._expl)
5542 self._tag_order = (tag_class, tag_num)
5544 def _value_sanitize(self, value):
5545 if (value.__class__ == tuple) and len(value) == 2:
5547 spec = self.specs.get(choice)
5549 raise ObjUnknown(choice)
5550 if not isinstance(obj, spec.__class__):
5551 raise InvalidValueType((spec,))
5552 return (choice, spec(obj))
5553 if isinstance(value, self.__class__):
5555 raise InvalidValueType((self.__class__, tuple))
5559 return self._value is not None and self._value[1].ready
5563 return self.expl_lenindef or (
5564 (self._value is not None) and
5565 self._value[1].bered
5568 def __getstate__(self):
5586 def __setstate__(self, state):
5587 super().__setstate__(state)
5588 self.specs = state.specs
5589 self._value = state.value
5591 def __eq__(self, their):
5592 if (their.__class__ == tuple) and len(their) == 2:
5593 return self._value == their
5594 if not isinstance(their, self.__class__):
5597 self.specs == their.specs and
5598 self._value == their._value
5608 return self.__class__(
5611 expl=self._expl if expl is None else expl,
5612 default=self.default if default is None else default,
5613 optional=self.optional if optional is None else optional,
5618 """Name of the choice
5620 self._assert_ready()
5621 return self._value[0]
5625 """Value of underlying choice
5627 self._assert_ready()
5628 return self._value[1]
5631 def tag_order(self):
5632 self._assert_ready()
5633 return self._value[1].tag_order if self._tag_order is None else self._tag_order
5636 def tag_order_cer(self):
5637 return min(v.tag_order_cer for v in self.specs.values())
5639 def __getitem__(self, key):
5640 if key not in self.specs:
5641 raise ObjUnknown(key)
5642 if self._value is None:
5644 choice, value = self._value
5649 def __setitem__(self, key, value):
5650 spec = self.specs.get(key)
5652 raise ObjUnknown(key)
5653 if not isinstance(value, spec.__class__):
5654 raise InvalidValueType((spec.__class__,))
5655 self._value = (key, spec(value))
5663 return self._value[1].decoded if self.ready else False
5666 self._assert_ready()
5667 return self._value[1].encode()
5669 def _encode1st(self, state):
5670 self._assert_ready()
5671 return self._value[1].encode1st(state)
5673 def _encode2nd(self, writer, state_iter):
5674 self._value[1].encode2nd(writer, state_iter)
5676 def _encode_cer(self, writer):
5677 self._assert_ready()
5678 self._value[1].encode_cer(writer)
5680 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5681 for choice, spec in self.specs.items():
5682 sub_decode_path = decode_path + (choice,)
5688 decode_path=sub_decode_path,
5691 _ctx_immutable=False,
5698 klass=self.__class__,
5699 decode_path=decode_path,
5702 if tag_only: # pragma: no cover
5706 for _decode_path, value, tail in spec.decode_evgen(
5710 decode_path=sub_decode_path,
5712 _ctx_immutable=False,
5714 yield _decode_path, value, tail
5716 _, value, tail = next(spec.decode_evgen(
5720 decode_path=sub_decode_path,
5722 _ctx_immutable=False,
5725 obj = self.__class__(
5728 default=self.default,
5729 optional=self.optional,
5730 _decoded=(offset, 0, value.fulllen),
5732 obj._value = (choice, value)
5733 yield decode_path, obj, tail
5736 value = pp_console_row(next(self.pps()))
5738 value = "%s[%r]" % (value, self.value)
5741 def pps(self, decode_path=()):
5744 asn1_type_name=self.asn1_type_name,
5745 obj_name=self.__class__.__name__,
5746 decode_path=decode_path,
5747 value=self.choice if self.ready else None,
5748 optional=self.optional,
5749 default=self == self.default,
5750 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5751 expl=None if self._expl is None else tag_decode(self._expl),
5756 expl_lenindef=self.expl_lenindef,
5760 yield self.value.pps(decode_path=decode_path + (self.choice,))
5761 for pp in self.pps_lenindef(decode_path):
5765 class PrimitiveTypes(Choice):
5766 """Predefined ``CHOICE`` for all generic primitive types
5768 It could be useful for general decoding of some unspecified values:
5770 >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
5771 OCTET STRING 3 bytes 666f6f
5772 >>> PrimitiveTypes().decod(hexdec("0203123456")).value
5776 schema = tuple((klass.__name__, klass()) for klass in (
5800 AnyState = namedtuple(
5802 BasicState._fields + ("value", "defined"),
5808 """``ANY`` special type
5810 >>> Any(Integer(-123))
5811 ANY INTEGER -123 (0X:7B)
5812 >>> a = Any(OctetString(b"hello world").encode())
5813 ANY 040b68656c6c6f20776f726c64
5814 >>> hexenc(bytes(a))
5815 b'0x040x0bhello world'
5817 __slots__ = ("defined",)
5818 tag_default = tag_encode(0)
5819 asn1_type_name = "ANY"
5829 :param value: set the value. Either any kind of pyderasn's
5830 **ready** object, or bytes. Pay attention that
5831 **no** validation is performed if raw binary value
5832 is valid TLV, except just tag decoding
5833 :param bytes expl: override default tag with ``EXPLICIT`` one
5834 :param bool optional: is object ``OPTIONAL`` in sequence
5836 super().__init__(None, expl, None, optional, _decoded)
5840 value = self._value_sanitize(value)
5842 if self._expl is None:
5843 if value.__class__ == bytes:
5844 tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5846 tag_class, tag_num = value.tag_order
5848 tag_class, _, tag_num = tag_decode(self._expl)
5849 self._tag_order = (tag_class, tag_num)
5852 def _value_sanitize(self, value):
5853 if value.__class__ == bytes:
5855 raise ValueError("%s value can not be empty" % self.__class__.__name__)
5857 if isinstance(value, self.__class__):
5859 if not isinstance(value, Obj):
5860 raise InvalidValueType((self.__class__, Obj, bytes))
5865 return self._value is not None
5868 def tag_order(self):
5869 self._assert_ready()
5870 return self._tag_order
5874 if self.expl_lenindef or self.lenindef:
5876 if self.defined is None:
5878 return self.defined[1].bered
5880 def __getstate__(self):
5898 def __setstate__(self, state):
5899 super().__setstate__(state)
5900 self._value = state.value
5901 self.defined = state.defined
5903 def __eq__(self, their):
5904 if their.__class__ == bytes:
5905 if self._value.__class__ == bytes:
5906 return self._value == their
5907 return self._value.encode() == their
5908 if issubclass(their.__class__, Any):
5909 if self.ready and their.ready:
5910 return bytes(self) == bytes(their)
5911 return self.ready == their.ready
5920 return self.__class__(
5922 expl=self._expl if expl is None else expl,
5923 optional=self.optional if optional is None else optional,
5926 def __bytes__(self):
5927 self._assert_ready()
5929 if value.__class__ == bytes:
5931 return self._value.encode()
5938 self._assert_ready()
5940 if value.__class__ == bytes:
5942 return value.encode()
5944 def _encode1st(self, state):
5945 self._assert_ready()
5947 if value.__class__ == bytes:
5948 return len(value), state
5949 return value.encode1st(state)
5951 def _encode2nd(self, writer, state_iter):
5953 if value.__class__ == bytes:
5954 write_full(writer, value)
5956 value.encode2nd(writer, state_iter)
5958 def _encode_cer(self, writer):
5959 self._assert_ready()
5961 if value.__class__ == bytes:
5962 write_full(writer, value)
5964 value.encode_cer(writer)
5966 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5968 t, tlen, lv = tag_strip(tlv)
5969 except DecodeError as err:
5970 raise err.__class__(
5972 klass=self.__class__,
5973 decode_path=decode_path,
5977 l, llen, v = len_decode(lv)
5978 except LenIndefForm as err:
5979 if not ctx.get("bered", False):
5980 raise err.__class__(
5982 klass=self.__class__,
5983 decode_path=decode_path,
5986 llen, vlen, v = 1, 0, lv[1:]
5987 sub_offset = offset + tlen + llen
5989 while v[:EOC_LEN].tobytes() != EOC:
5990 chunk, v = Any().decode(
5993 decode_path=decode_path + (str(chunk_i),),
5996 _ctx_immutable=False,
5998 vlen += chunk.tlvlen
5999 sub_offset += chunk.tlvlen
6001 tlvlen = tlen + llen + vlen + EOC_LEN
6002 obj = self.__class__(
6003 value=None if evgen_mode else tlv[:tlvlen].tobytes(),
6005 optional=self.optional,
6006 _decoded=(offset, 0, tlvlen),
6009 obj.tag = t.tobytes()
6010 yield decode_path, obj, v[EOC_LEN:]
6012 except DecodeError as err:
6013 raise err.__class__(
6015 klass=self.__class__,
6016 decode_path=decode_path,
6020 raise NotEnoughData(
6021 "encoded length is longer than data",
6022 klass=self.__class__,
6023 decode_path=decode_path,
6026 tlvlen = tlen + llen + l
6027 v, tail = tlv[:tlvlen], v[l:]
6028 obj = self.__class__(
6029 value=None if evgen_mode else v.tobytes(),
6031 optional=self.optional,
6032 _decoded=(offset, 0, tlvlen),
6034 obj.tag = t.tobytes()
6035 yield decode_path, obj, tail
6038 return pp_console_row(next(self.pps()))
6040 def pps(self, decode_path=()):
6044 elif value.__class__ == bytes:
6050 asn1_type_name=self.asn1_type_name,
6051 obj_name=self.__class__.__name__,
6052 decode_path=decode_path,
6054 blob=self._value if self._value.__class__ == bytes else None,
6055 optional=self.optional,
6056 default=self == self.default,
6057 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6058 expl=None if self._expl is None else tag_decode(self._expl),
6063 expl_offset=self.expl_offset if self.expled else None,
6064 expl_tlen=self.expl_tlen if self.expled else None,
6065 expl_llen=self.expl_llen if self.expled else None,
6066 expl_vlen=self.expl_vlen if self.expled else None,
6067 expl_lenindef=self.expl_lenindef,
6068 lenindef=self.lenindef,
6071 defined_by, defined = self.defined or (None, None)
6072 if defined_by is not None:
6074 decode_path=decode_path + (DecodePathDefBy(defined_by),)
6076 for pp in self.pps_lenindef(decode_path):
6080 ########################################################################
6081 # ASN.1 constructed types
6082 ########################################################################
6084 def abs_decode_path(decode_path, rel_path):
6085 """Create an absolute decode path from current and relative ones
6087 :param decode_path: current decode path, starting point. Tuple of strings
6088 :param rel_path: relative path to ``decode_path``. Tuple of strings.
6089 If first tuple's element is "/", then treat it as
6090 an absolute path, ignoring ``decode_path`` as
6091 starting point. Also this tuple can contain ".."
6092 elements, stripping the leading element from
6095 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
6096 ("foo", "bar", "baz", "whatever")
6097 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
6099 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
6102 if rel_path[0] == "/":
6104 if rel_path[0] == "..":
6105 return abs_decode_path(decode_path[:-1], rel_path[1:])
6106 return decode_path + rel_path
6109 SequenceState = namedtuple(
6111 BasicState._fields + ("specs", "value",),
6116 class SequenceEncode1stMixin:
6119 def _encode1st(self, state):
6121 idx = len(state) - 1
6123 for v in self._values_for_encoding():
6124 l, _ = v.encode1st(state)
6127 return len(self.tag) + len_size(vlen) + vlen, state
6130 class Sequence(SequenceEncode1stMixin, Obj):
6131 """``SEQUENCE`` structure type
6133 You have to make specification of sequence::
6135 class Extension(Sequence):
6137 ("extnID", ObjectIdentifier()),
6138 ("critical", Boolean(default=False)),
6139 ("extnValue", OctetString()),
6142 Then, you can work with it as with dictionary.
6144 >>> ext = Extension()
6145 >>> Extension().specs
6147 ('extnID', OBJECT IDENTIFIER),
6148 ('critical', BOOLEAN False OPTIONAL DEFAULT),
6149 ('extnValue', OCTET STRING),
6151 >>> ext["extnID"] = "1.2.3"
6152 Traceback (most recent call last):
6153 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
6154 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
6156 You can determine if sequence is ready to be encoded:
6161 Traceback (most recent call last):
6162 pyderasn.ObjNotReady: object is not ready: extnValue
6163 >>> ext["extnValue"] = OctetString(b"foobar")
6167 Value you want to assign, must have the same **type** as in
6168 corresponding specification, but it can have different tags,
6169 optional/default attributes -- they will be taken from specification
6172 class TBSCertificate(Sequence):
6174 ("version", Version(expl=tag_ctxc(0), default="v1")),
6177 >>> tbs = TBSCertificate()
6178 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
6180 Assign ``None`` to remove value from sequence.
6182 You can set values in Sequence during its initialization:
6184 >>> AlgorithmIdentifier((
6185 ("algorithm", ObjectIdentifier("1.2.3")),
6186 ("parameters", Any(Null()))
6188 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
6190 You can determine if value exists/set in the sequence and take its value:
6192 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
6195 OBJECT IDENTIFIER 1.2.3
6197 But pay attention that if value has default, then it won't be (not
6198 in) in the sequence (because ``DEFAULT`` must not be encoded in
6199 DER), but you can read its value:
6201 >>> "critical" in ext, ext["critical"]
6202 (False, BOOLEAN False)
6203 >>> ext["critical"] = Boolean(True)
6204 >>> "critical" in ext, ext["critical"]
6205 (True, BOOLEAN True)
6207 All defaulted values are always optional.
6209 .. _allow_default_values_ctx:
6211 DER prohibits default value encoding and will raise an error if
6212 default value is unexpectedly met during decode.
6213 If :ref:`bered <bered_ctx>` context option is set, then no error
6214 will be raised, but ``bered`` attribute set. You can disable strict
6215 defaulted values existence validation by setting
6216 ``"allow_default_values": True`` :ref:`context <ctx>` option.
6218 All values with DEFAULT specified are decoded atomically in
6219 :ref:`evgen mode <evgen_mode>`. If DEFAULT value is some kind of
6220 SEQUENCE, then it will be yielded as a single element, not
6221 disassembled. That is required for DEFAULT existence check.
6223 Two sequences are equal if they have equal specification (schema),
6224 implicit/explicit tagging and the same values.
6226 __slots__ = ("specs",)
6227 tag_default = tag_encode(form=TagFormConstructed, num=16)
6228 asn1_type_name = "SEQUENCE"
6240 super().__init__(impl, expl, default, optional, _decoded)
6242 schema = getattr(self, "schema", ())
6244 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
6247 if value is not None:
6248 if issubclass(value.__class__, Sequence):
6249 self._value = value._value
6250 elif hasattr(value, "__iter__"):
6251 for seq_key, seq_value in value:
6252 self[seq_key] = seq_value
6254 raise InvalidValueType((Sequence,))
6255 if default is not None:
6256 if not issubclass(default.__class__, Sequence):
6257 raise InvalidValueType((Sequence,))
6258 default_value = default._value
6259 default_obj = self.__class__(impl=self.tag, expl=self._expl)
6260 default_obj.specs = self.specs
6261 default_obj._value = default_value
6262 self.default = default_obj
6264 self._value = copy(default_obj._value)
6268 for name, spec in self.specs.items():
6269 value = self._value.get(name)
6280 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6282 return any(value.bered for value in self._value.values())
6284 def __getstate__(self):
6285 return SequenceState(
6299 {k: copy(v) for k, v in self._value.items()},
6302 def __setstate__(self, state):
6303 super().__setstate__(state)
6304 self.specs = state.specs
6305 self._value = state.value
6307 def __eq__(self, their):
6308 if not isinstance(their, self.__class__):
6311 self.specs == their.specs and
6312 self.tag == their.tag and
6313 self._expl == their._expl and
6314 self._value == their._value
6325 return self.__class__(
6328 impl=self.tag if impl is None else impl,
6329 expl=self._expl if expl is None else expl,
6330 default=self.default if default is None else default,
6331 optional=self.optional if optional is None else optional,
6334 def __contains__(self, key):
6335 return key in self._value
6337 def __setitem__(self, key, value):
6338 spec = self.specs.get(key)
6340 raise ObjUnknown(key)
6342 self._value.pop(key, None)
6344 if not isinstance(value, spec.__class__):
6345 raise InvalidValueType((spec.__class__,))
6346 value = spec(value=value)
6347 if spec.default is not None and value == spec.default:
6348 self._value.pop(key, None)
6350 self._value[key] = value
6352 def __getitem__(self, key):
6353 value = self._value.get(key)
6354 if value is not None:
6356 spec = self.specs.get(key)
6358 raise ObjUnknown(key)
6359 if spec.default is not None:
6363 def _values_for_encoding(self):
6364 for name, spec in self.specs.items():
6365 value = self._value.get(name)
6369 raise ObjNotReady(name)
6373 v = b"".join(v.encode() for v in self._values_for_encoding())
6374 return b"".join((self.tag, len_encode(len(v)), v))
6376 def _encode2nd(self, writer, state_iter):
6377 write_full(writer, self.tag + len_encode(next(state_iter)))
6378 for v in self._values_for_encoding():
6379 v.encode2nd(writer, state_iter)
6381 def _encode_cer(self, writer):
6382 write_full(writer, self.tag + LENINDEF)
6383 for v in self._values_for_encoding():
6384 v.encode_cer(writer)
6385 write_full(writer, EOC)
6387 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6389 t, tlen, lv = tag_strip(tlv)
6390 except DecodeError as err:
6391 raise err.__class__(
6393 klass=self.__class__,
6394 decode_path=decode_path,
6399 klass=self.__class__,
6400 decode_path=decode_path,
6403 if tag_only: # pragma: no cover
6407 ctx_bered = ctx.get("bered", False)
6409 l, llen, v = len_decode(lv)
6410 except LenIndefForm as err:
6412 raise err.__class__(
6414 klass=self.__class__,
6415 decode_path=decode_path,
6418 l, llen, v = 0, 1, lv[1:]
6420 except DecodeError as err:
6421 raise err.__class__(
6423 klass=self.__class__,
6424 decode_path=decode_path,
6428 raise NotEnoughData(
6429 "encoded length is longer than data",
6430 klass=self.__class__,
6431 decode_path=decode_path,
6435 v, tail = v[:l], v[l:]
6437 sub_offset = offset + tlen + llen
6440 ctx_allow_default_values = ctx.get("allow_default_values", False)
6441 for name, spec in self.specs.items():
6442 if spec.optional and (
6443 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
6447 spec_defaulted = spec.default is not None
6448 sub_decode_path = decode_path + (name,)
6450 if evgen_mode and not spec_defaulted:
6451 for _decode_path, value, v_tail in spec.decode_evgen(
6455 decode_path=sub_decode_path,
6457 _ctx_immutable=False,
6459 yield _decode_path, value, v_tail
6461 _, value, v_tail = next(spec.decode_evgen(
6465 decode_path=sub_decode_path,
6467 _ctx_immutable=False,
6470 except TagMismatch as err:
6471 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
6475 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
6476 if not evgen_mode and defined is not None:
6477 defined_by, defined_spec = defined
6478 if issubclass(value.__class__, SequenceOf):
6479 for i, _value in enumerate(value):
6480 sub_sub_decode_path = sub_decode_path + (
6482 DecodePathDefBy(defined_by),
6484 defined_value, defined_tail = defined_spec.decode(
6485 memoryview(bytes(_value)),
6487 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6488 if value.expled else (value.tlen + value.llen)
6491 decode_path=sub_sub_decode_path,
6493 _ctx_immutable=False,
6495 if len(defined_tail) > 0:
6498 klass=self.__class__,
6499 decode_path=sub_sub_decode_path,
6502 _value.defined = (defined_by, defined_value)
6504 defined_value, defined_tail = defined_spec.decode(
6505 memoryview(bytes(value)),
6507 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6508 if value.expled else (value.tlen + value.llen)
6511 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6513 _ctx_immutable=False,
6515 if len(defined_tail) > 0:
6518 klass=self.__class__,
6519 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6522 value.defined = (defined_by, defined_value)
6524 value_len = value.fulllen
6526 sub_offset += value_len
6530 yield sub_decode_path, value, v_tail
6531 if value == spec.default:
6532 if ctx_bered or ctx_allow_default_values:
6536 "DEFAULT value met",
6537 klass=self.__class__,
6538 decode_path=sub_decode_path,
6542 values[name] = value
6543 spec_defines = getattr(spec, "defines", ())
6544 if len(spec_defines) == 0:
6545 defines_by_path = ctx.get("defines_by_path", ())
6546 if len(defines_by_path) > 0:
6547 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
6548 if spec_defines is not None and len(spec_defines) > 0:
6549 for rel_path, schema in spec_defines:
6550 defined = schema.get(value, None)
6551 if defined is not None:
6552 ctx.setdefault("_defines", []).append((
6553 abs_decode_path(sub_decode_path[:-1], rel_path),
6557 if v[:EOC_LEN].tobytes() != EOC:
6560 klass=self.__class__,
6561 decode_path=decode_path,
6569 klass=self.__class__,
6570 decode_path=decode_path,
6573 obj = self.__class__(
6577 default=self.default,
6578 optional=self.optional,
6579 _decoded=(offset, llen, vlen),
6582 obj.lenindef = lenindef
6583 obj.ber_encoded = ber_encoded
6584 yield decode_path, obj, tail
6587 value = pp_console_row(next(self.pps()))
6589 for name in self.specs:
6590 _value = self._value.get(name)
6593 cols.append("%s: %s" % (name, repr(_value)))
6594 return "%s[%s]" % (value, "; ".join(cols))
6596 def pps(self, decode_path=()):
6599 asn1_type_name=self.asn1_type_name,
6600 obj_name=self.__class__.__name__,
6601 decode_path=decode_path,
6602 optional=self.optional,
6603 default=self == self.default,
6604 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6605 expl=None if self._expl is None else tag_decode(self._expl),
6610 expl_offset=self.expl_offset if self.expled else None,
6611 expl_tlen=self.expl_tlen if self.expled else None,
6612 expl_llen=self.expl_llen if self.expled else None,
6613 expl_vlen=self.expl_vlen if self.expled else None,
6614 expl_lenindef=self.expl_lenindef,
6615 lenindef=self.lenindef,
6616 ber_encoded=self.ber_encoded,
6619 for name in self.specs:
6620 value = self._value.get(name)
6623 yield value.pps(decode_path=decode_path + (name,))
6624 for pp in self.pps_lenindef(decode_path):
6628 class Set(Sequence, SequenceEncode1stMixin):
6629 """``SET`` structure type
6631 Its usage is identical to :py:class:`pyderasn.Sequence`.
6633 .. _allow_unordered_set_ctx:
6635 DER prohibits unordered values encoding and will raise an error
6636 during decode. If :ref:`bered <bered_ctx>` context option is set,
6637 then no error will occur. Also you can disable strict values
6638 ordering check by setting ``"allow_unordered_set": True``
6639 :ref:`context <ctx>` option.
6642 tag_default = tag_encode(form=TagFormConstructed, num=17)
6643 asn1_type_name = "SET"
6645 def _values_for_encoding(self):
6646 return sorted(super()._values_for_encoding(), key=attrgetter("tag_order"))
6648 def _encode_cer(self, writer):
6649 write_full(writer, self.tag + LENINDEF)
6651 super()._values_for_encoding(),
6652 key=attrgetter("tag_order_cer"),
6654 v.encode_cer(writer)
6655 write_full(writer, EOC)
6657 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6659 t, tlen, lv = tag_strip(tlv)
6660 except DecodeError as err:
6661 raise err.__class__(
6663 klass=self.__class__,
6664 decode_path=decode_path,
6669 klass=self.__class__,
6670 decode_path=decode_path,
6677 ctx_bered = ctx.get("bered", False)
6679 l, llen, v = len_decode(lv)
6680 except LenIndefForm as err:
6682 raise err.__class__(
6684 klass=self.__class__,
6685 decode_path=decode_path,
6688 l, llen, v = 0, 1, lv[1:]
6690 except DecodeError as err:
6691 raise err.__class__(
6693 klass=self.__class__,
6694 decode_path=decode_path,
6698 raise NotEnoughData(
6699 "encoded length is longer than data",
6700 klass=self.__class__,
6704 v, tail = v[:l], v[l:]
6706 sub_offset = offset + tlen + llen
6709 ctx_allow_default_values = ctx.get("allow_default_values", False)
6710 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6711 tag_order_prev = (0, 0)
6712 _specs_items = copy(self.specs)
6715 if lenindef and v[:EOC_LEN].tobytes() == EOC:
6717 for name, spec in _specs_items.items():
6718 sub_decode_path = decode_path + (name,)
6724 decode_path=sub_decode_path,
6727 _ctx_immutable=False,
6734 klass=self.__class__,
6735 decode_path=decode_path,
6738 spec_defaulted = spec.default is not None
6739 if evgen_mode and not spec_defaulted:
6740 for _decode_path, value, v_tail in spec.decode_evgen(
6744 decode_path=sub_decode_path,
6746 _ctx_immutable=False,
6748 yield _decode_path, value, v_tail
6750 _, value, v_tail = next(spec.decode_evgen(
6754 decode_path=sub_decode_path,
6756 _ctx_immutable=False,
6759 value_tag_order = value.tag_order
6760 value_len = value.fulllen
6761 if tag_order_prev >= value_tag_order:
6762 if ctx_bered or ctx_allow_unordered_set:
6766 "unordered " + self.asn1_type_name,
6767 klass=self.__class__,
6768 decode_path=sub_decode_path,
6773 yield sub_decode_path, value, v_tail
6774 if value != spec.default:
6776 elif ctx_bered or ctx_allow_default_values:
6780 "DEFAULT value met",
6781 klass=self.__class__,
6782 decode_path=sub_decode_path,
6785 values[name] = value
6786 del _specs_items[name]
6787 tag_order_prev = value_tag_order
6788 sub_offset += value_len
6792 obj = self.__class__(
6796 default=self.default,
6797 optional=self.optional,
6798 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6801 if v[:EOC_LEN].tobytes() != EOC:
6804 klass=self.__class__,
6805 decode_path=decode_path,
6810 for name, spec in self.specs.items():
6811 if name not in values and not spec.optional:
6813 "%s value is not ready" % name,
6814 klass=self.__class__,
6815 decode_path=decode_path,
6820 obj.ber_encoded = ber_encoded
6821 yield decode_path, obj, tail
6824 SequenceOfState = namedtuple(
6826 BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
6831 class SequenceOf(SequenceEncode1stMixin, Obj):
6832 """``SEQUENCE OF`` sequence type
6834 For that kind of type you must specify the object it will carry on
6835 (bounds are for example here, not required)::
6837 class Ints(SequenceOf):
6842 >>> ints.append(Integer(123))
6843 >>> ints.append(Integer(234))
6845 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
6846 >>> [int(i) for i in ints]
6848 >>> ints.append(Integer(345))
6849 Traceback (most recent call last):
6850 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
6853 >>> ints[1] = Integer(345)
6855 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
6857 You can initialize sequence with preinitialized values:
6859 >>> ints = Ints([Integer(123), Integer(234)])
6861 Also you can use iterator as a value:
6863 >>> ints = Ints(iter(Integer(i) for i in range(1000000)))
6865 And it won't be iterated until encoding process. Pay attention that
6866 bounds and required schema checks are done only during the encoding
6867 process in that case! After encode was called, then value is zeroed
6868 back to empty list and you have to set it again. That mode is useful
6869 mainly with CER encoding mode, where all objects from the iterable
6870 will be streamed to the buffer, without copying all of them to
6873 __slots__ = ("spec", "_bound_min", "_bound_max")
6874 tag_default = tag_encode(form=TagFormConstructed, num=16)
6875 asn1_type_name = "SEQUENCE OF"
6888 super().__init__(impl, expl, default, optional, _decoded)
6890 schema = getattr(self, "schema", None)
6892 raise ValueError("schema must be specified")
6894 self._bound_min, self._bound_max = getattr(
6898 ) if bounds is None else bounds
6900 if value is not None:
6901 self._value = self._value_sanitize(value)
6902 if default is not None:
6903 default_value = self._value_sanitize(default)
6904 default_obj = self.__class__(
6909 default_obj._value = default_value
6910 self.default = default_obj
6912 self._value = copy(default_obj._value)
6914 def _value_sanitize(self, value):
6916 if issubclass(value.__class__, SequenceOf):
6917 value = value._value
6918 elif hasattr(value, NEXT_ATTR_NAME):
6920 elif hasattr(value, "__iter__"):
6923 raise InvalidValueType((self.__class__, iter, "iterator"))
6925 if not self._bound_min <= len(value) <= self._bound_max:
6926 raise BoundsError(self._bound_min, len(value), self._bound_max)
6927 class_expected = self.spec.__class__
6929 if not isinstance(v, class_expected):
6930 raise InvalidValueType((class_expected,))
6935 if hasattr(self._value, NEXT_ATTR_NAME):
6937 if self._bound_min > 0 and len(self._value) == 0:
6939 return all(v.ready for v in self._value)
6943 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6945 return any(v.bered for v in self._value)
6947 def __getstate__(self):
6948 if hasattr(self._value, NEXT_ATTR_NAME):
6949 raise ValueError("can not pickle SequenceOf with iterator")
6950 return SequenceOfState(
6964 [copy(v) for v in self._value],
6969 def __setstate__(self, state):
6970 super().__setstate__(state)
6971 self.spec = state.spec
6972 self._value = state.value
6973 self._bound_min = state.bound_min
6974 self._bound_max = state.bound_max
6976 def __eq__(self, their):
6977 if isinstance(their, self.__class__):
6979 self.spec == their.spec and
6980 self.tag == their.tag and
6981 self._expl == their._expl and
6982 self._value == their._value
6984 if hasattr(their, "__iter__"):
6985 return self._value == list(their)
6997 return self.__class__(
7001 (self._bound_min, self._bound_max)
7002 if bounds is None else bounds
7004 impl=self.tag if impl is None else impl,
7005 expl=self._expl if expl is None else expl,
7006 default=self.default if default is None else default,
7007 optional=self.optional if optional is None else optional,
7010 def __contains__(self, key):
7011 return key in self._value
7013 def append(self, value):
7014 if not isinstance(value, self.spec.__class__):
7015 raise InvalidValueType((self.spec.__class__,))
7016 if len(self._value) + 1 > self._bound_max:
7019 len(self._value) + 1,
7022 self._value.append(value)
7025 return iter(self._value)
7028 return len(self._value)
7030 def __setitem__(self, key, value):
7031 if not isinstance(value, self.spec.__class__):
7032 raise InvalidValueType((self.spec.__class__,))
7033 self._value[key] = self.spec(value=value)
7035 def __getitem__(self, key):
7036 return self._value[key]
7038 def _values_for_encoding(self):
7039 return iter(self._value)
7042 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7045 values_append = values.append
7046 class_expected = self.spec.__class__
7047 values_for_encoding = self._values_for_encoding()
7049 for v in values_for_encoding:
7050 if not isinstance(v, class_expected):
7051 raise InvalidValueType((class_expected,))
7052 values_append(v.encode())
7053 if not self._bound_min <= len(values) <= self._bound_max:
7054 raise BoundsError(self._bound_min, len(values), self._bound_max)
7055 value = b"".join(values)
7057 value = b"".join(v.encode() for v in self._values_for_encoding())
7058 return b"".join((self.tag, len_encode(len(value)), value))
7060 def _encode1st(self, state):
7061 state = super()._encode1st(state)
7062 if hasattr(self._value, NEXT_ATTR_NAME):
7066 def _encode2nd(self, writer, state_iter):
7067 write_full(writer, self.tag + len_encode(next(state_iter)))
7068 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7071 class_expected = self.spec.__class__
7072 values_for_encoding = self._values_for_encoding()
7074 for v in values_for_encoding:
7075 if not isinstance(v, class_expected):
7076 raise InvalidValueType((class_expected,))
7077 v.encode2nd(writer, state_iter)
7079 if not self._bound_min <= values_count <= self._bound_max:
7080 raise BoundsError(self._bound_min, values_count, self._bound_max)
7082 for v in self._values_for_encoding():
7083 v.encode2nd(writer, state_iter)
7085 def _encode_cer(self, writer):
7086 write_full(writer, self.tag + LENINDEF)
7087 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7089 class_expected = self.spec.__class__
7091 values_for_encoding = self._values_for_encoding()
7093 for v in values_for_encoding:
7094 if not isinstance(v, class_expected):
7095 raise InvalidValueType((class_expected,))
7096 v.encode_cer(writer)
7098 if not self._bound_min <= values_count <= self._bound_max:
7099 raise BoundsError(self._bound_min, values_count, self._bound_max)
7101 for v in self._values_for_encoding():
7102 v.encode_cer(writer)
7103 write_full(writer, EOC)
7113 ordering_check=False,
7116 t, tlen, lv = tag_strip(tlv)
7117 except DecodeError as err:
7118 raise err.__class__(
7120 klass=self.__class__,
7121 decode_path=decode_path,
7126 klass=self.__class__,
7127 decode_path=decode_path,
7134 ctx_bered = ctx.get("bered", False)
7136 l, llen, v = len_decode(lv)
7137 except LenIndefForm as err:
7139 raise err.__class__(
7141 klass=self.__class__,
7142 decode_path=decode_path,
7145 l, llen, v = 0, 1, lv[1:]
7147 except DecodeError as err:
7148 raise err.__class__(
7150 klass=self.__class__,
7151 decode_path=decode_path,
7155 raise NotEnoughData(
7156 "encoded length is longer than data",
7157 klass=self.__class__,
7158 decode_path=decode_path,
7162 v, tail = v[:l], v[l:]
7164 sub_offset = offset + tlen + llen
7167 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
7168 value_prev = memoryview(v[:0])
7172 if lenindef and v[:EOC_LEN].tobytes() == EOC:
7174 sub_decode_path = decode_path + (str(_value_count),)
7176 for _decode_path, value, v_tail in spec.decode_evgen(
7180 decode_path=sub_decode_path,
7182 _ctx_immutable=False,
7184 yield _decode_path, value, v_tail
7186 _, value, v_tail = next(spec.decode_evgen(
7190 decode_path=sub_decode_path,
7192 _ctx_immutable=False,
7195 value_len = value.fulllen
7197 if value_prev.tobytes() > v[:value_len].tobytes():
7198 if ctx_bered or ctx_allow_unordered_set:
7202 "unordered " + self.asn1_type_name,
7203 klass=self.__class__,
7204 decode_path=sub_decode_path,
7207 value_prev = v[:value_len]
7210 _value.append(value)
7211 sub_offset += value_len
7214 if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
7216 msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
7217 klass=self.__class__,
7218 decode_path=decode_path,
7222 obj = self.__class__(
7223 value=None if evgen_mode else _value,
7225 bounds=(self._bound_min, self._bound_max),
7228 default=self.default,
7229 optional=self.optional,
7230 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
7232 except BoundsError as err:
7235 klass=self.__class__,
7236 decode_path=decode_path,
7240 if v[:EOC_LEN].tobytes() != EOC:
7243 klass=self.__class__,
7244 decode_path=decode_path,
7249 obj.ber_encoded = ber_encoded
7250 yield decode_path, obj, tail
7254 pp_console_row(next(self.pps())),
7255 ", ".join(repr(v) for v in self._value),
7258 def pps(self, decode_path=()):
7261 asn1_type_name=self.asn1_type_name,
7262 obj_name=self.__class__.__name__,
7263 decode_path=decode_path,
7264 optional=self.optional,
7265 default=self == self.default,
7266 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
7267 expl=None if self._expl is None else tag_decode(self._expl),
7272 expl_offset=self.expl_offset if self.expled else None,
7273 expl_tlen=self.expl_tlen if self.expled else None,
7274 expl_llen=self.expl_llen if self.expled else None,
7275 expl_vlen=self.expl_vlen if self.expled else None,
7276 expl_lenindef=self.expl_lenindef,
7277 lenindef=self.lenindef,
7278 ber_encoded=self.ber_encoded,
7281 for i, value in enumerate(self._value):
7282 yield value.pps(decode_path=decode_path + (str(i),))
7283 for pp in self.pps_lenindef(decode_path):
7287 class SetOf(SequenceOf):
7288 """``SET OF`` sequence type
7290 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
7293 tag_default = tag_encode(form=TagFormConstructed, num=17)
7294 asn1_type_name = "SET OF"
7296 def _value_sanitize(self, value):
7297 value = super()._value_sanitize(value)
7298 if hasattr(value, NEXT_ATTR_NAME):
7300 "SetOf does not support iterator values, as no sense in them"
7305 v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
7306 return b"".join((self.tag, len_encode(len(v)), v))
7308 def _encode2nd(self, writer, state_iter):
7309 write_full(writer, self.tag + len_encode(next(state_iter)))
7311 for v in self._values_for_encoding():
7313 v.encode2nd(buf.write, state_iter)
7314 values.append(buf.getvalue())
7317 write_full(writer, v)
7319 def _encode_cer(self, writer):
7320 write_full(writer, self.tag + LENINDEF)
7321 for v in sorted(encode_cer(v) for v in self._values_for_encoding()):
7322 write_full(writer, v)
7323 write_full(writer, EOC)
7325 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
7326 return super()._decode(
7333 ordering_check=True,
7337 def obj_by_path(pypath): # pragma: no cover
7338 """Import object specified as string Python path
7340 Modules must be separated from classes/functions with ``:``.
7342 >>> obj_by_path("foo.bar:Baz")
7343 <class 'foo.bar.Baz'>
7344 >>> obj_by_path("foo.bar:Baz.boo")
7345 <classmethod 'foo.bar.Baz.boo'>
7347 mod, objs = pypath.rsplit(":", 1)
7348 from importlib import import_module
7349 obj = import_module(mod)
7350 for obj_name in objs.split("."):
7351 obj = getattr(obj, obj_name)
7355 def generic_decoder(): # pragma: no cover
7356 # All of this below is a big hack with self references
7357 choice = PrimitiveTypes()
7358 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
7359 choice.specs["SetOf"] = SetOf(schema=choice)
7361 choice.specs["SequenceOf%d" % i] = SequenceOf(
7365 choice.specs["Any"] = Any()
7367 # Class name equals to type name, to omit it from output
7368 class SEQUENCEOF(SequenceOf):
7376 with_decode_path=False,
7377 decode_path_only=(),
7380 def _pprint_pps(pps):
7382 if hasattr(pp, "_fields"):
7384 decode_path_only != () and
7385 pp.decode_path[:len(decode_path_only)] != decode_path_only
7388 if pp.asn1_type_name == Choice.asn1_type_name:
7390 pp_kwargs = pp._asdict()
7391 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
7392 pp = _pp(**pp_kwargs)
7393 yield pp_console_row(
7398 with_colours=with_colours,
7399 with_decode_path=with_decode_path,
7400 decode_path_len_decrease=len(decode_path_only),
7402 for row in pp_console_blob(
7404 decode_path_len_decrease=len(decode_path_only),
7408 for row in _pprint_pps(pp):
7410 return "\n".join(_pprint_pps(obj.pps(decode_path)))
7411 return SEQUENCEOF(), pprint_any
7414 def ascii_visualize(ba):
7415 """Output only ASCII printable characters, like in hexdump -C
7417 Example output for given binary string (right part)::
7419 92 2b 39 20 65 91 e6 8e 95 93 1a 58 df 02 78 ea |.+9 e......X..x.|
7422 return "".join((chr(b) if 0x20 <= b <= 0x7E else ".") for b in ba)
7426 """Generate ``hexdump -C`` like output
7430 00000000 30 80 30 80 a0 80 02 01 02 00 00 02 14 54 a5 18 |0.0..........T..|
7431 00000010 69 ef 8b 3f 15 fd ea ad bd 47 e0 94 81 6b 06 6a |i..?.....G...k.j|
7433 Result of that function is a generator of lines, where each line is
7438 ["00000010 ", " 69", " ef", " 8b", " 3f", " 15", " fd", " ea", " ad ",
7439 " bd", " 47", " e0", " 94", " 81", " 6b", " 06", " 6a ",
7440 " |i..?.....G...k.j|"]
7444 hexed = hexenc(raw).upper()
7445 addr, cols = 0, ["%08x " % 0]
7446 for i in range(0, len(hexed), 2):
7447 if i != 0 and i // 2 % 8 == 0:
7449 if i != 0 and i // 2 % 16 == 0:
7450 cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:addr + 16])))
7453 cols = ["%08x " % addr]
7454 cols.append(" " + hexed[i:i + 2])
7456 cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:])))
7460 def browse(raw, obj, oid_maps=()):
7461 """Interactive browser
7463 :param bytes raw: binary data you decoded
7464 :param obj: decoded :py:class:`pyderasn.Obj`
7465 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
7466 Its human readable form is printed when OID is met
7468 .. note:: `urwid <http://urwid.org/>`__ dependency required
7470 This browser is an interactive terminal application for browsing
7471 structures of your decoded ASN.1 objects. You can quit it with **q**
7472 key. It consists of three windows:
7475 View of ASN.1 elements hierarchy. You can navigate it using **Up**,
7476 **Down**, **PageUp**, **PageDown**, **Home**, **End** keys.
7477 **Left** key goes to constructed element above. **Plus**/**Minus**
7478 keys collapse/uncollapse constructed elements. **Space** toggles it
7480 window with various information about element. You can scroll it
7481 with **h**/**l** (down, up) (**H**/**L** for triple speed) keys
7483 window with raw data hexdump and highlighted current element's
7484 contents. It automatically focuses on element's data. You can
7485 scroll it with **j**/**k** (down, up) (**J**/**K** for triple
7486 speed) keys. If element has explicit tag, then it also will be
7487 highlighted with different colour
7489 Window's header contains current decode path and progress bars with
7490 position in *info* and *hexdump* windows.
7492 If you press **d**, then current element will be saved in the
7493 current directory under its decode path name (adding ".0", ".1", etc
7494 suffix if such file already exists). **D** will save it with explicit tag.
7496 You can also invoke it with ``--browse`` command line argument.
7498 from copy import deepcopy
7499 from os.path import exists as path_exists
7502 class TW(urwid.TreeWidget):
7503 def __init__(self, state, *args, **kwargs):
7505 self.scrolled = {"info": False, "hexdump": False}
7506 super().__init__(*args, **kwargs)
7509 pp = self.get_node().get_value()
7510 constructed = len(pp) > 1
7511 return (pp if hasattr(pp, "_fields") else pp[0]), constructed
7513 def _state_update(self):
7514 pp, _ = self._get_pp()
7515 self.state["decode_path"].set_text(
7516 ":".join(str(p) for p in pp.decode_path)
7518 lines = deepcopy(self.state["hexed"])
7520 def attr_set(i, attr):
7521 line = lines[i // 16]
7522 idx = 1 + (i - 16 * (i // 16))
7523 line[idx] = (attr, line[idx])
7525 if pp.expl_offset is not None:
7528 pp.expl_offset + pp.expl_tlen + pp.expl_llen,
7530 attr_set(i, "select-expl")
7531 for i in range(pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen):
7532 attr_set(i, "select-value")
7533 self.state["hexdump"]._set_body([urwid.Text(line) for line in lines])
7534 self.state["hexdump"].set_focus(pp.offset // 16)
7535 self.state["hexdump"].set_focus_valign("middle")
7536 self.state["hexdump_bar"].set_completion(
7537 (100 * pp.offset // 16) //
7538 len(self.state["hexdump"]._body.positions())
7542 [("header", "Name: "), pp.obj_name],
7543 [("header", "Type: "), pp.asn1_type_name],
7544 [("header", "Offset: "), "%d (0x%x)" % (pp.offset, pp.offset)],
7545 [("header", "[TLV]len: "), "%d/%d/%d" % (
7546 pp.tlen, pp.llen, pp.vlen,
7548 [("header", "TLVlen: "), "%d" % sum((
7549 pp.tlen, pp.llen, pp.vlen,
7551 [("header", "Slice: "), "[%d:%d]" % (
7552 pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen,
7556 lines.append([("warning", "LENINDEF")])
7558 lines.append([("warning", "BER encoded")])
7560 lines.append([("warning", "BERed")])
7561 if pp.expl is not None:
7562 lines.append([("header", "EXPLICIT")])
7563 klass, _, num = pp.expl
7564 lines.append([" Tag: %s%d" % (TagClassReprs[klass], num)])
7565 if pp.expl_offset is not None:
7566 lines.append([" Offset: %d" % pp.expl_offset])
7567 lines.append([" [TLV]len: %d/%d/%d" % (
7568 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7570 lines.append([" TLVlen: %d" % sum((
7571 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7573 lines.append([" Slice: [%d:%d]" % (
7575 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen,
7577 if pp.impl is not None:
7578 klass, _, num = pp.impl
7580 ("header", "IMPLICIT: "), "%s%d" % (TagClassReprs[klass], num),
7583 lines.append(["OPTIONAL"])
7585 lines.append(["DEFAULT"])
7586 if len(pp.decode_path) > 0:
7587 ent = pp.decode_path[-1]
7588 if isinstance(ent, DecodePathDefBy):
7590 value = str(ent.defined_by)
7591 oid_name = find_oid_name(
7592 ent.defined_by.asn1_type_name, oid_maps, value,
7594 lines.append([("header", "DEFINED BY: "), "%s" % (
7595 value if oid_name is None
7596 else "%s (%s)" % (oid_name, value)
7599 if pp.value is not None:
7600 lines.append([("header", "Value: "), pp.value])
7602 len(oid_maps) > 0 and
7603 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
7605 for oid_map in oid_maps:
7606 oid_name = oid_map.get(pp.value)
7607 if oid_name is not None:
7608 lines.append([("header", "Human: "), oid_name])
7610 if pp.asn1_type_name == Integer.asn1_type_name:
7612 ("header", "Decimal: "), "%d" % int(pp.obj),
7615 ("header", "Hexadecimal: "), colonize_hex(pp.obj.tohex()),
7617 if pp.blob.__class__ == bytes:
7618 blob = hexenc(pp.blob).upper()
7619 for i in range(0, len(blob), 32):
7620 lines.append([colonize_hex(blob[i:i + 32])])
7621 elif pp.blob.__class__ == tuple:
7622 lines.append([", ".join(pp.blob)])
7623 self.state["info"]._set_body([urwid.Text(line) for line in lines])
7624 self.state["info_bar"].set_completion(0)
7626 def selectable(self):
7627 if self.state["widget_current"] != self:
7628 self.state["widget_current"] = self
7629 self.scrolled["info"] = False
7630 self.scrolled["hexdump"] = False
7631 self._state_update()
7632 return super().selectable()
7634 def _get_display_text_without_offset(self):
7635 pp, constructed = self._get_pp()
7636 style = "constructed" if constructed else ""
7637 if len(pp.decode_path) == 0:
7638 return (style, pp.obj_name)
7639 if pp.asn1_type_name == "EOC":
7640 return ("eoc", "EOC")
7641 ent = pp.decode_path[-1]
7642 if isinstance(ent, DecodePathDefBy):
7643 value = str(ent.defined_by)
7644 oid_name = find_oid_name(
7645 ent.defined_by.asn1_type_name, oid_maps, value,
7647 return ("defby", "DEFBY:" + (
7648 value if oid_name is None else oid_name
7652 def get_display_text(self):
7653 pp, _ = self._get_pp()
7654 style, ent = self._get_display_text_without_offset()
7655 return [(style, ent), " [%d]" % pp.offset]
7657 def _scroll(self, what, step):
7658 self.state[what]._invalidate()
7659 pos = self.state[what].focus_position
7660 if not self.scrolled[what]:
7661 self.scrolled[what] = True
7663 pos = max(0, pos + step)
7664 pos = min(pos, len(self.state[what]._body.positions()) - 1)
7665 self.state[what].set_focus(pos)
7666 self.state[what].set_focus_valign("top")
7667 self.state[what + "_bar"].set_completion(
7668 (100 * pos) // len(self.state[what]._body.positions())
7671 def keypress(self, size, key):
7673 raise urwid.ExitMainLoop()
7676 self.expanded = not self.expanded
7677 self.update_expanded_icon()
7680 hexdump_steps = {"j": 1, "k": -1, "J": 5, "K": -5}
7681 if key in hexdump_steps:
7682 self._scroll("hexdump", hexdump_steps[key])
7685 info_steps = {"h": 1, "l": -1, "H": 5, "L": -5}
7686 if key in info_steps:
7687 self._scroll("info", info_steps[key])
7690 if key in ("d", "D"):
7691 pp, _ = self._get_pp()
7692 dp = ":".join(str(p) for p in pp.decode_path)
7693 dp = dp.replace(" ", "_")
7696 if key == "d" or pp.expl_offset is None:
7697 data = self.state["raw"][pp.offset:(
7698 pp.offset + pp.tlen + pp.llen + pp.vlen
7701 data = self.state["raw"][pp.expl_offset:(
7702 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen
7706 def duplicate_path(dp, ctr):
7709 return "%s.%d" % (dp, ctr)
7712 if not path_exists(duplicate_path(dp, ctr)):
7715 dp = duplicate_path(dp, ctr)
7716 with open(dp, "wb") as fd:
7718 self.state["decode_path"].set_text(
7719 ("warning", "Saved to: " + dp)
7722 return super().keypress(size, key)
7724 class PN(urwid.ParentNode):
7725 def __init__(self, state, value, *args, **kwargs):
7727 if not hasattr(value, "_fields"):
7729 super().__init__(value, *args, **kwargs)
7731 def load_widget(self):
7732 return TW(self.state, self)
7734 def load_child_keys(self):
7735 value = self.get_value()
7736 if hasattr(value, "_fields"):
7738 return range(len(value[1:]))
7740 def load_child_node(self, key):
7743 self.get_value()[key + 1],
7746 depth=self.get_depth() + 1,
7749 class LabeledPG(urwid.ProgressBar):
7750 def __init__(self, label, *args, **kwargs):
7752 super().__init__(*args, **kwargs)
7755 return "%s: %s" % (self.label, super().get_text())
7757 WinHexdump = urwid.ListBox([urwid.Text("")])
7758 WinInfo = urwid.ListBox([urwid.Text("")])
7759 WinDecodePath = urwid.Text("", "center")
7760 WinInfoBar = LabeledPG("info", "pg-normal", "pg-complete")
7761 WinHexdumpBar = LabeledPG("hexdump", "pg-normal", "pg-complete")
7762 WinTree = urwid.TreeListBox(urwid.TreeWalker(PN(
7765 "hexed": list(hexdump(raw)),
7766 "widget_current": None,
7768 "info_bar": WinInfoBar,
7769 "hexdump": WinHexdump,
7770 "hexdump_bar": WinHexdumpBar,
7771 "decode_path": WinDecodePath,
7775 help_text = " ".join((
7777 "space:(un)collapse",
7778 "(pg)up/down/home/end:nav",
7779 "jkJK:hexdump hlHL:info",
7785 ("weight", 1, WinTree),
7786 ("weight", 2, urwid.Pile([
7787 urwid.LineBox(WinInfo),
7788 urwid.LineBox(WinHexdump),
7791 header=urwid.Columns([
7792 ("weight", 2, urwid.AttrWrap(WinDecodePath, "header")),
7793 ("weight", 1, WinInfoBar),
7794 ("weight", 1, WinHexdumpBar),
7796 footer=urwid.AttrWrap(urwid.Text(help_text), "help")
7799 ("header", "bold", ""),
7800 ("constructed", "bold", ""),
7801 ("help", "light magenta", ""),
7802 ("warning", "light red", ""),
7803 ("defby", "light red", ""),
7804 ("eoc", "dark red", ""),
7805 ("select-value", "light green", ""),
7806 ("select-expl", "light red", ""),
7807 ("pg-normal", "", "light blue"),
7808 ("pg-complete", "black", "yellow"),
7813 def main(): # pragma: no cover
7815 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/CER/DER decoder")
7816 parser.add_argument(
7820 help="Skip that number of bytes from the beginning",
7822 parser.add_argument(
7824 help="Python paths to dictionary with OIDs, comma separated",
7826 parser.add_argument(
7828 help="Python path to schema definition to use",
7830 parser.add_argument(
7831 "--defines-by-path",
7832 help="Python path to decoder's defines_by_path",
7834 parser.add_argument(
7836 action="store_true",
7837 help="Disallow BER encoding",
7839 parser.add_argument(
7840 "--print-decode-path",
7841 action="store_true",
7842 help="Print decode paths",
7844 parser.add_argument(
7845 "--decode-path-only",
7846 help="Print only specified decode path",
7848 parser.add_argument(
7850 action="store_true",
7851 help="Allow explicit tag out-of-bound",
7853 parser.add_argument(
7855 action="store_true",
7856 help="Turn on event generation mode",
7858 parser.add_argument(
7860 action="store_true",
7861 help="Start ASN.1 browser",
7863 parser.add_argument(
7865 type=argparse.FileType("rb"),
7866 help="Path to BER/CER/DER file you want to decode",
7868 args = parser.parse_args()
7870 raw = file_mmaped(args.RAWFile)[args.skip:]
7872 args.RAWFile.seek(args.skip)
7873 raw = memoryview(args.RAWFile.read())
7874 args.RAWFile.close()
7876 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
7877 if args.oids else ()
7879 from functools import partial
7881 schema = obj_by_path(args.schema)
7882 pprinter = partial(pprint, big_blobs=True)
7884 schema, pprinter = generic_decoder()
7886 "bered": not args.nobered,
7887 "allow_expl_oob": args.allow_expl_oob,
7889 if args.defines_by_path is not None:
7890 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
7892 obj, _ = schema().decode(raw, ctx=ctx)
7893 browse(raw, obj, oid_maps)
7894 from sys import exit as sys_exit
7896 from os import environ
7900 with_colours=environ.get("NO_COLOR") is None,
7901 with_decode_path=args.print_decode_path,
7903 () if args.decode_path_only is None else
7904 tuple(args.decode_path_only.split(":"))
7908 for decode_path, obj, tail in schema().decode_evgen(raw, ctx=ctx):
7909 print(pprinter(obj, decode_path=decode_path))
7911 obj, tail = schema().decode(raw, ctx=ctx)
7912 print(pprinter(obj))
7914 print("\nTrailing data: %s" % hexenc(tail))
7917 if __name__ == "__main__":
7918 from pyderasn import *