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-2020 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/BER codec with abstract structures
22 This library allows you to marshal various structures in ASN.1 DER
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``
363 ASN.1 structures often have ANY and OCTET STRING fields, that are
364 DEFINED BY some previously met ObjectIdentifier. This library provides
365 ability to specify mapping between some OID and field that must be
366 decoded with specific specification.
373 :py:class:`pyderasn.ObjectIdentifier` field inside
374 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
375 necessary for decoding structures. For example, CMS (:rfc:`5652`)
378 class ContentInfo(Sequence):
380 ("contentType", ContentType(defines=((("content",), {
381 id_digestedData: DigestedData(),
382 id_signedData: SignedData(),
384 ("content", Any(expl=tag_ctxc(0))),
387 ``contentType`` field tells that it defines that ``content`` must be
388 decoded with ``SignedData`` specification, if ``contentType`` equals to
389 ``id-signedData``. The same applies to ``DigestedData``. If
390 ``contentType`` contains unknown OID, then no automatic decoding is
393 You can specify multiple fields, that will be autodecoded -- that is why
394 ``defines`` kwarg is a sequence. You can specify defined field
395 relatively or absolutely to current decode path. For example ``defines``
396 for AlgorithmIdentifier of X.509's
397 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
401 id_ecPublicKey: ECParameters(),
402 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
404 (("..", "subjectPublicKey"), {
405 id_rsaEncryption: RSAPublicKey(),
406 id_GostR3410_2001: OctetString(),
410 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
411 autodecode its parameters inside SPKI's algorithm and its public key
414 Following types can be automatically decoded (DEFINED BY):
416 * :py:class:`pyderasn.Any`
417 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
418 * :py:class:`pyderasn.OctetString`
419 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
420 ``Any``/``BitString``/``OctetString``-s
422 When any of those fields is automatically decoded, then ``.defined``
423 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
424 was defined, ``value`` contains corresponding decoded value. For example
425 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
427 .. _defines_by_path_ctx:
429 defines_by_path context option
430 ______________________________
432 Sometimes you either can not or do not want to explicitly set *defines*
433 in the schema. You can dynamically apply those definitions when calling
434 :py:meth:`pyderasn.Obj.decode` method.
436 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
437 value must be sequence of following tuples::
439 (decode_path, defines)
441 where ``decode_path`` is a tuple holding so-called decode path to the
442 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
443 ``defines``, holding exactly the same value as accepted in its
444 :ref:`keyword argument <defines>`.
446 For example, again for CMS, you want to automatically decode
447 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
448 structures it may hold. Also, automatically decode ``controlSequence``
451 content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
454 ((("content",), {id_signedData: SignedData()}),),
459 DecodePathDefBy(id_signedData),
464 id_cct_PKIData: PKIData(),
465 id_cct_PKIResponse: PKIResponse(),
471 DecodePathDefBy(id_signedData),
474 DecodePathDefBy(id_cct_PKIResponse),
480 id_cmc_recipientNonce: RecipientNonce(),
481 id_cmc_senderNonce: SenderNonce(),
482 id_cmc_statusInfoV2: CMCStatusInfoV2(),
483 id_cmc_transactionId: TransactionId(),
488 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
489 First function is useful for path construction when some automatic
490 decoding is already done. ``any`` means literally any value it meet --
491 useful for SEQUENCE/SET OF-s.
498 By default PyDERASN accepts only DER encoded data. By default it encodes
499 to DER. But you can optionally enable BER decoding with setting
500 ``bered`` :ref:`context <ctx>` argument to True. Indefinite lengths and
501 constructed primitive types should be parsed successfully.
503 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
504 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
505 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``,
506 ``UTCTime``, ``GeneralizedTime`` can contain it.
507 * If object has an indefinite length encoding, then its ``lenindef``
508 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
509 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
511 * If object has an indefinite length encoded explicit tag, then
512 ``expl_lenindef`` is set to True.
513 * If object has either any of BER-related encoding (explicit tag
514 indefinite length, object's indefinite length, BER-encoding) or any
515 underlying component has that kind of encoding, then ``bered``
516 attribute is set to True. For example SignedData CMS can have
517 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
518 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
520 EOC (end-of-contents) token's length is taken in advance in object's
523 .. _allow_expl_oob_ctx:
525 Allow explicit tag out-of-bound
526 -------------------------------
528 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
529 one value, more than one object. If you set ``allow_expl_oob`` context
530 option to True, then no error will be raised and that invalid encoding
531 will be silently further processed. But pay attention that offsets and
532 lengths will be invalid in that case.
536 This option should be used only for skipping some decode errors, just
537 to see the decoded structure somehow.
541 Streaming and dealing with huge structures
542 ------------------------------------------
549 ASN.1 structures can be huge, they can hold millions of objects inside
550 (for example Certificate Revocation Lists (CRL), holding revocation
551 state for every previously issued X.509 certificate). CACert.org's 8 MiB
552 CRL file takes more than half a gigabyte of memory to hold the decoded
555 If you just simply want to check the signature over the ``tbsCertList``,
556 you can create specialized schema with that field represented as
557 OctetString for example::
559 class TBSCertListFast(Sequence):
562 ("revokedCertificates", OctetString(
563 impl=SequenceOf.tag_default,
569 This allows you to quickly decode a few fields and check the signature
570 over the ``tbsCertList`` bytes.
572 But how can you get all certificate's serial number from it, after you
573 trust that CRL after signature validation? You can use so called
574 ``evgen`` (event generation) mode, to catch the events/facts of some
575 successful object decoding. Let's use command line capabilities::
577 $ python -m pyderasn --schema tests.test_crl:CertificateList --evgen revoke.crl
578 10 [1,1, 1] . . version: Version INTEGER v2 (01) OPTIONAL
579 15 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
580 26 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
581 13 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
582 34 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.10
583 39 [0,0, 9] . . . . . . value: [UNIV 19] AttributeValue ANY
584 32 [1,1, 14] . . . . . 0: AttributeTypeAndValue SEQUENCE
585 30 [1,1, 16] . . . . 0: RelativeDistinguishedName SET OF
587 188 [1,1, 1] . . . . userCertificate: CertificateSerialNumber INTEGER 17 (11)
588 191 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
589 191 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
590 191 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
591 186 [1,1, 18] . . . 0: RevokedCertificate SEQUENCE
592 208 [1,1, 1] . . . . userCertificate: CertificateSerialNumber INTEGER 20 (14)
593 211 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
594 211 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
595 211 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
596 206 [1,1, 18] . . . 1: RevokedCertificate SEQUENCE
598 9144992 [0,0, 15] . . . . revocationDate: Time CHOICE utcTime
599 9144992 [1,1, 13] . . . . . utcTime: UTCTime UTCTime 2020-02-08T07:25:06
600 9144985 [1,1, 20] . . . 415755: RevokedCertificate SEQUENCE
601 181 [1,4,9144821] . . revokedCertificates: RevokedCertificates SEQUENCE OF OPTIONAL
602 5 [1,4,9144997] . tbsCertList: TBSCertList SEQUENCE
603 9145009 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
604 9145020 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
605 9145007 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
606 9145022 [1,3, 513] . signatureValue: BIT STRING 4096 bits
607 0 [1,4,9145534] CertificateList SEQUENCE
609 Here we see how decoder works: it decodes SEQUENCE's tag, length, then
610 decodes underlying values. It can not tell if SEQUENCE is decoded, so
611 the event of the upper level SEQUENCE is the last one we see.
612 ``version`` field is just a single INTEGER -- it is decoded and event is
613 fired immediately. Then we see that ``algorithm`` and ``parameters``
614 fields are decoded and only after them the ``signature`` SEQUENCE is
615 fired as a successfully decoded. There are 4 events for each revoked
616 certificate entry in that CRL: ``userCertificate`` serial number,
617 ``utcTime`` of ``revocationDate`` CHOICE, ``RevokedCertificate`` itself
618 as a one of entity in ``revokedCertificates`` SEQUENCE OF.
620 We can do that in our ordinary Python code and understand where we are
621 by looking at deterministically generated decode paths (do not forget
622 about useful ``--print-decode-path`` CLI option). We must use
623 :py:meth:`pyderasn.Obj.decode_evgen` method, instead of ordinary
624 :py:meth:`pyderasn.Obj.decode`. It is generator yielding ``(decode_path,
625 obj, tail)`` tuples::
627 for decode_path, obj, _ in CertificateList().decode_evgen(crl_raw):
629 len(decode_path) == 4 and
630 decode_path[:2] == ("tbsCertList", "revokedCertificates"),
631 decode_path[3] == "userCertificate"
633 print("serial number:", int(obj))
635 Virtually it does not take any memory except at least needed for single
636 object storage. You can easily use that mode to determine required
637 object ``.offset`` and ``.*len`` to be able to decode it separately, or
638 maybe verify signature upon it just by taking bytes by ``.offset`` and
641 .. _evgen_mode_upto_ctx:
646 There is full ability to get any kind of data from the CRL in the
647 example above. However it is not too convenient to get the whole
648 ``RevokedCertificate`` structure, that is pretty lightweight and one may
649 do not want to disassemble it. You can use ``evgen_mode_upto``
650 :ref:`ctx <ctx>` option that semantically equals to
651 :ref:`defines_by_path <defines_by_path_ctx>` -- list of decode paths
652 mapped to any non-None value. If specified decode path is met, then any
653 subsequent objects won't be decoded in evgen mode. That allows us to
654 parse the CRL above with fully assembled ``RevokedCertificate``::
656 for decode_path, obj, _ in CertificateList().decode_evgen(
658 ctx={"evgen_mode_upto": (
659 (("tbsCertList", "revokedCertificates", any), True),
663 len(decode_path) == 3 and
664 decode_path[:2] == ("tbsCertList", "revokedCertificates"),
666 print("serial number:", int(obj["userCertificate"]))
670 SEQUENCE/SET values with DEFAULT specified are automatically decoded
678 POSIX compliant systems have ``mmap`` syscall, giving ability to work
679 the memory mapped file. You can deal with the file like it was an
680 ordinary binary string, allowing you not to load it to the memory first.
681 Also you can use them as an input for OCTET STRING, taking no Python
682 memory for their storage.
684 There is convenient :py:func:`pyderasn.file_mmaped` function that
685 creates read-only memoryview on the file contents::
687 with open("huge", "rb") as fd:
688 raw = file_mmaped(fd)
689 obj = Something.decode(raw)
693 mmap-ed files in Python2.7 does not implement buffer protocol, so
694 memoryview won't work on them.
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 opener = io.open if PY2 else open
726 with opener("result", "wb") as fd:
727 obj.encode_cer(fd.write)
732 obj.encode_cer(buf.write)
734 If you do not want to create in-memory buffer every time, then you can
735 use :py:func:`pyderasn.encode_cer` function::
737 data = encode_cer(obj)
739 Remember that CER is **not valid** DER in most cases, so you **have to**
740 use :ref:`bered <bered_ctx>` :ref:`ctx <ctx>` option during its
741 decoding. Also currently there is **no** validation that provided CER is
742 valid one -- you are sure that it has only valid BER encoding.
746 SET OF values can not be streamingly encoded, because they are
747 required to be sorted byte-by-byte. Big SET OF values still will take
748 much memory. Use neither SET nor SET OF values, as modern ASN.1
751 Do not forget about using :ref:`mmap-ed <mmap>` memoryviews for your
752 OCTET STRINGs! They will be streamingly copied from underlying file to
753 the buffer using 1 KB chunks.
755 Some structures require that some of the elements have to be forcefully
756 DER encoded. For example ``SignedData`` CMS requires you to encode
757 ``SignedAttributes`` and X.509 certificates in DER form, allowing you to
758 encode everything else in BER. You can tell any of the structures to be
759 forcefully encoded in DER during CER encoding, by specifying
760 ``der_forced=True`` attribute::
762 class Certificate(Sequence):
766 class SignedAttributes(SetOf):
771 .. _agg_octet_string:
776 In most cases, huge quantity of binary data is stored as OCTET STRING.
777 CER encoding splits it on 1 KB chunks. BER allows splitting on various
778 levels of chunks inclusion::
780 SOME STRING[CONSTRUCTED]
781 OCTET STRING[CONSTRUCTED]
782 OCTET STRING[PRIMITIVE]
784 OCTET STRING[PRIMITIVE]
786 OCTET STRING[PRIMITIVE]
788 OCTET STRING[PRIMITIVE]
790 OCTET STRING[CONSTRUCTED]
791 OCTET STRING[PRIMITIVE]
793 OCTET STRING[PRIMITIVE]
795 OCTET STRING[CONSTRUCTED]
796 OCTET STRING[CONSTRUCTED]
797 OCTET STRING[PRIMITIVE]
800 You can not just take the offset and some ``.vlen`` of the STRING and
801 treat it as the payload. If you decode it without
802 :ref:`evgen mode <evgen_mode>`, then it will be automatically aggregated
803 and ``bytes()`` will give the whole payload contents.
805 You are forced to use :ref:`evgen mode <evgen_mode>` for decoding for
806 small memory footprint. There is convenient
807 :py:func:`pyderasn.agg_octet_string` helper for reconstructing the
808 payload. Let's assume you have got BER/CER encoded ``ContentInfo`` with
809 huge ``SignedData`` and ``EncapsulatedContentInfo``. Let's calculate the
810 SHA512 digest of its ``eContent``::
812 fd = open("data.p7m", "rb")
813 raw = file_mmaped(fd)
814 ctx = {"bered": True}
815 for decode_path, obj, _ in ContentInfo().decode_evgen(raw, ctx=ctx):
816 if decode_path == ("content",):
820 raise ValueError("no content found")
821 hasher_state = sha512()
823 hasher_state.update(data)
825 evgens = SignedData().decode_evgen(
826 raw[content.offset:],
827 offset=content.offset,
830 agg_octet_string(evgens, ("encapContentInfo", "eContent"), raw, hasher)
832 digest = hasher_state.digest()
834 Simply replace ``hasher`` with some writeable file's ``fd.write`` to
835 copy the payload (without BER/CER encoding interleaved overhead) in it.
836 Virtually it won't take memory more than for keeping small structures
837 and 1 KB binary chunks.
841 SEQUENCE OF iterators
842 _____________________
844 You can use iterators as a value in :py:class:`pyderasn.SequenceOf`
845 classes. The only difference with providing the full list of objects, is
846 that type and bounds checking is done during encoding process. Also
847 sequence's value will be emptied after encoding, forcing you to set its
850 This is very useful when you have to create some huge objects, like
851 CRLs, with thousands and millions of entities inside. You can write the
852 generator taking necessary data from the database and giving the
853 ``RevokedCertificate`` objects. Only binary representation of that
854 objects will take memory during DER encoding.
859 There is ability to do 2-pass encoding to DER, writing results directly
860 to specified writer (buffer, file, whatever). It could be 1.5+ times
861 slower than ordinary encoding, but it takes little memory for 1st pass
862 state storing. For example, 1st pass state for CACert.org's CRL with
863 ~416K of certificate entries takes nearly 3.5 MB of memory.
864 ``SignedData`` with several gigabyte ``EncapsulatedContentInfo`` takes
865 nearly 0.5 KB of memory.
867 If you use :ref:`mmap-ed <mmap>` memoryviews, :ref:`SEQUENCE OF
868 iterators <seqof-iterators>` and write directly to opened file, then
869 there is very small memory footprint.
871 1st pass traverses through all the objects of the structure and returns
872 the size of DER encoded structure, together with 1st pass state object.
873 That state contains precalculated lengths for various objects inside the
878 fulllen, state = obj.encode1st()
880 2nd pass takes the writer and 1st pass state. It traverses through all
881 the objects again, but writes their encoded representation to the writer.
885 opener = io.open if PY2 else open
886 with opener("result", "wb") as fd:
887 obj.encode2nd(fd.write, iter(state))
891 You **MUST NOT** use 1st pass state if anything is changed in the
892 objects. It is intended to be used immediately after 1st pass is
895 If you use :ref:`SEQUENCE OF iterators <seqof-iterators>`, then you
896 have to reinitialize the values after the 1st pass. And you **have to**
897 be sure that the iterator gives exactly the same values as previously.
898 Yes, you have to run your iterator twice -- because this is two pass
901 If you want to encode to the memory, then you can use convenient
902 :py:func:`pyderasn.encode2pass` helper.
908 .. autofunction:: pyderasn.browse
912 .. autoclass:: pyderasn.Obj
920 .. autoclass:: pyderasn.Boolean
925 .. autoclass:: pyderasn.Integer
926 :members: __init__, named, tohex
930 .. autoclass:: pyderasn.BitString
931 :members: __init__, bit_len, named
935 .. autoclass:: pyderasn.OctetString
940 .. autoclass:: pyderasn.Null
945 .. autoclass:: pyderasn.ObjectIdentifier
950 .. autoclass:: pyderasn.Enumerated
954 .. autoclass:: pyderasn.CommonString
958 .. autoclass:: pyderasn.NumericString
962 .. autoclass:: pyderasn.PrintableString
963 :members: __init__, allow_asterisk, allow_ampersand
967 .. autoclass:: pyderasn.UTCTime
968 :members: __init__, todatetime
972 .. autoclass:: pyderasn.GeneralizedTime
973 :members: __init__, todatetime
980 .. autoclass:: pyderasn.Choice
981 :members: __init__, choice, value
985 .. autoclass:: PrimitiveTypes
989 .. autoclass:: pyderasn.Any
997 .. autoclass:: pyderasn.Sequence
1002 .. autoclass:: pyderasn.Set
1007 .. autoclass:: pyderasn.SequenceOf
1012 .. autoclass:: pyderasn.SetOf
1018 .. autofunction:: pyderasn.abs_decode_path
1019 .. autofunction:: pyderasn.agg_octet_string
1020 .. autofunction:: pyderasn.ascii_visualize
1021 .. autofunction:: pyderasn.colonize_hex
1022 .. autofunction:: pyderasn.encode2pass
1023 .. autofunction:: pyderasn.encode_cer
1024 .. autofunction:: pyderasn.file_mmaped
1025 .. autofunction:: pyderasn.hexenc
1026 .. autofunction:: pyderasn.hexdec
1027 .. autofunction:: pyderasn.hexdump
1028 .. autofunction:: pyderasn.tag_encode
1029 .. autofunction:: pyderasn.tag_decode
1030 .. autofunction:: pyderasn.tag_ctxp
1031 .. autofunction:: pyderasn.tag_ctxc
1032 .. autoclass:: pyderasn.DecodeError
1034 .. autoclass:: pyderasn.NotEnoughData
1035 .. autoclass:: pyderasn.ExceedingData
1036 .. autoclass:: pyderasn.LenIndefForm
1037 .. autoclass:: pyderasn.TagMismatch
1038 .. autoclass:: pyderasn.InvalidLength
1039 .. autoclass:: pyderasn.InvalidOID
1040 .. autoclass:: pyderasn.ObjUnknown
1041 .. autoclass:: pyderasn.ObjNotReady
1042 .. autoclass:: pyderasn.InvalidValueType
1043 .. autoclass:: pyderasn.BoundsError
1050 You can decode DER/BER files using command line abilities::
1052 $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
1054 If there is no schema for your file, then you can try parsing it without,
1055 but of course IMPLICIT tags will often make it impossible. But result is
1056 good enough for the certificate above::
1058 $ python -m pyderasn path/to/file
1059 0 [1,3,1604] . >: SEQUENCE OF
1060 4 [1,3,1453] . . >: SEQUENCE OF
1061 8 [0,0, 5] . . . . >: [0] ANY
1062 . . . . . A0:03:02:01:02
1063 13 [1,1, 3] . . . . >: INTEGER 61595
1064 18 [1,1, 13] . . . . >: SEQUENCE OF
1065 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1066 31 [1,1, 0] . . . . . . >: NULL
1067 33 [1,3, 274] . . . . >: SEQUENCE OF
1068 37 [1,1, 11] . . . . . . >: SET OF
1069 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
1070 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
1071 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
1073 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
1074 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
1075 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
1076 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
1077 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
1078 . . . . . . . . . 61:2E:63:6F:6D:2F
1079 1461 [1,1, 13] . . >: SEQUENCE OF
1080 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1081 1474 [1,1, 0] . . . . >: NULL
1082 1476 [1,2, 129] . . >: BIT STRING 1024 bits
1083 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
1084 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
1090 If you have got dictionaries with ObjectIdentifiers, like example one
1091 from ``tests/test_crts.py``::
1094 "1.2.840.113549.1.1.1": "id-rsaEncryption",
1095 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
1097 "2.5.4.10": "id-at-organizationName",
1098 "2.5.4.11": "id-at-organizationalUnitName",
1101 then you can pass it to pretty printer to see human readable OIDs::
1103 $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
1105 37 [1,1, 11] . . . . . . >: SET OF
1106 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
1107 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
1108 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
1109 50 [1,1, 18] . . . . . . >: SET OF
1110 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
1111 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
1112 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
1113 70 [1,1, 18] . . . . . . >: SET OF
1114 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
1115 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
1116 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
1122 Each decoded element has so-called decode path: sequence of structure
1123 names it is passing during the decode process. Each element has its own
1124 unique path inside the whole ASN.1 tree. You can print it out with
1125 ``--print-decode-path`` option::
1127 $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
1128 0 [1,3,1604] Certificate SEQUENCE []
1129 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
1130 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
1131 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
1132 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
1133 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
1134 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
1136 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
1137 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
1138 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
1139 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
1140 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
1141 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
1142 . . . . . . . 13:02:45:53
1143 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]
1146 Now you can print only the specified tree, for example signature algorithm::
1148 $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
1149 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
1150 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1151 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
1155 from array import array
1156 from codecs import getdecoder
1157 from codecs import getencoder
1158 from collections import namedtuple
1159 from collections import OrderedDict
1160 from copy import copy
1161 from datetime import datetime
1162 from datetime import timedelta
1163 from io import BytesIO
1164 from math import ceil
1165 from mmap import mmap
1166 from mmap import PROT_READ
1167 from operator import attrgetter
1168 from string import ascii_letters
1169 from string import digits
1170 from sys import maxsize as sys_maxsize
1171 from sys import version_info
1172 from unicodedata import category as unicat
1174 from six import add_metaclass
1175 from six import binary_type
1176 from six import byte2int
1177 from six import indexbytes
1178 from six import int2byte
1179 from six import integer_types
1180 from six import iterbytes
1181 from six import iteritems
1182 from six import itervalues
1184 from six import string_types
1185 from six import text_type
1186 from six import unichr as six_unichr
1187 from six.moves import xrange as six_xrange
1191 from termcolor import colored
1192 except ImportError: # pragma: no cover
1193 def colored(what, *args, **kwargs):
1244 "TagClassApplication",
1247 "TagClassUniversal",
1248 "TagFormConstructed",
1259 TagClassUniversal = 0
1260 TagClassApplication = 1 << 6
1261 TagClassContext = 1 << 7
1262 TagClassPrivate = 1 << 6 | 1 << 7
1263 TagFormPrimitive = 0
1264 TagFormConstructed = 1 << 5
1266 TagClassContext: "",
1267 TagClassApplication: "APPLICATION ",
1268 TagClassPrivate: "PRIVATE ",
1269 TagClassUniversal: "UNIV ",
1273 LENINDEF = b"\x80" # length indefinite mark
1274 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
1275 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
1276 SET01 = frozenset("01")
1277 DECIMALS = frozenset(digits)
1278 DECIMAL_SIGNS = ".,"
1279 NEXT_ATTR_NAME = "next" if PY2 else "__next__"
1282 def file_mmaped(fd):
1283 """Make mmap-ed memoryview for reading from file
1285 :param fd: file object
1286 :returns: memoryview over read-only mmap-ing of the whole file
1288 return memoryview(mmap(fd.fileno(), 0, prot=PROT_READ))
1291 if not set(value) <= DECIMALS:
1292 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 (not p1 is 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
1333 super(DecodeError, self).__init__()
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):
1362 super(ExceedingData, self).__init__()
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):
1390 super(ObjUnknown, self).__init__()
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):
1402 super(ObjNotReady, self).__init__()
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):
1414 super(InvalidValueType, self).__init__()
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):
1428 super(BoundsError, self).__init__()
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 ########################################################################
1448 _hexdecoder = getdecoder("hex")
1449 _hexencoder = getencoder("hex")
1453 """Binary data to hexadecimal string convert
1455 return _hexdecoder(data)[0]
1459 """Hexadecimal string to binary data convert
1461 return _hexencoder(data)[0].decode("ascii")
1464 def int_bytes_len(num, byte_len=8):
1467 return int(ceil(float(num.bit_length()) / byte_len))
1470 def zero_ended_encode(num):
1471 octets = bytearray(int_bytes_len(num, 7))
1473 octets[i] = num & 0x7F
1477 octets[i] = 0x80 | (num & 0x7F)
1480 return bytes(octets)
1483 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
1484 """Encode tag to binary form
1486 :param int num: tag's number
1487 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
1488 :py:data:`pyderasn.TagClassContext`,
1489 :py:data:`pyderasn.TagClassApplication`,
1490 :py:data:`pyderasn.TagClassPrivate`)
1491 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
1492 :py:data:`pyderasn.TagFormConstructed`)
1496 return int2byte(klass | form | num)
1497 # [XX|X|11111][1.......][1.......] ... [0.......]
1498 return int2byte(klass | form | 31) + zero_ended_encode(num)
1501 def tag_decode(tag):
1502 """Decode tag from binary form
1506 No validation is performed, assuming that it has already passed.
1508 It returns tuple with three integers, as
1509 :py:func:`pyderasn.tag_encode` accepts.
1511 first_octet = byte2int(tag)
1512 klass = first_octet & 0xC0
1513 form = first_octet & 0x20
1514 if first_octet & 0x1F < 0x1F:
1515 return (klass, form, first_octet & 0x1F)
1517 for octet in iterbytes(tag[1:]):
1520 return (klass, form, num)
1524 """Create CONTEXT PRIMITIVE tag
1526 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
1530 """Create CONTEXT CONSTRUCTED tag
1532 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
1535 def tag_strip(data):
1536 """Take off tag from the data
1538 :returns: (encoded tag, tag length, remaining data)
1541 raise NotEnoughData("no data at all")
1542 if byte2int(data) & 0x1F < 31:
1543 return data[:1], 1, data[1:]
1548 raise DecodeError("unfinished tag")
1549 if indexbytes(data, i) & 0x80 == 0:
1552 return data[:i], i, data[i:]
1558 octets = bytearray(int_bytes_len(l) + 1)
1559 octets[0] = 0x80 | (len(octets) - 1)
1560 for i in six_xrange(len(octets) - 1, 0, -1):
1561 octets[i] = l & 0xFF
1563 return bytes(octets)
1566 def len_decode(data):
1569 :returns: (decoded length, length's length, remaining data)
1570 :raises LenIndefForm: if indefinite form encoding is met
1573 raise NotEnoughData("no data at all")
1574 first_octet = byte2int(data)
1575 if first_octet & 0x80 == 0:
1576 return first_octet, 1, data[1:]
1577 octets_num = first_octet & 0x7F
1578 if octets_num + 1 > len(data):
1579 raise NotEnoughData("encoded length is longer than data")
1581 raise LenIndefForm()
1582 if byte2int(data[1:]) == 0:
1583 raise DecodeError("leading zeros")
1585 for v in iterbytes(data[1:1 + octets_num]):
1588 raise DecodeError("long form instead of short one")
1589 return l, 1 + octets_num, data[1 + octets_num:]
1592 LEN0 = len_encode(0)
1593 LEN1 = len_encode(1)
1594 LEN1K = len_encode(1000)
1598 """How many bytes length field will take
1602 if l < 256: # 1 << 8
1604 if l < 65536: # 1 << 16
1606 if l < 16777216: # 1 << 24
1608 if l < 4294967296: # 1 << 32
1610 if l < 1099511627776: # 1 << 40
1612 if l < 281474976710656: # 1 << 48
1614 if l < 72057594037927936: # 1 << 56
1616 raise OverflowError("too big length")
1619 def write_full(writer, data):
1620 """Fully write provided data
1622 :param writer: must comply with ``io.RawIOBase.write`` behaviour
1624 BytesIO does not guarantee that the whole data will be written at
1625 once. That function write everything provided, raising an error if
1626 ``writer`` returns None.
1628 data = memoryview(data)
1630 while written != len(data):
1631 n = writer(data[written:])
1633 raise ValueError("can not write to buf")
1637 # If it is 64-bit system, then use compact 64-bit array of unsigned
1638 # longs. Use an ordinary list with universal integers otherwise, that
1640 if sys_maxsize > 2 ** 32:
1641 def state_2pass_new():
1644 def state_2pass_new():
1648 ########################################################################
1650 ########################################################################
1652 class AutoAddSlots(type):
1653 def __new__(cls, name, bases, _dict):
1654 _dict["__slots__"] = _dict.get("__slots__", ())
1655 return type.__new__(cls, name, bases, _dict)
1658 BasicState = namedtuple("BasicState", (
1671 ), **NAMEDTUPLE_KWARGS)
1674 @add_metaclass(AutoAddSlots)
1676 """Common ASN.1 object class
1678 All ASN.1 types are inherited from it. It has metaclass that
1679 automatically adds ``__slots__`` to all inherited classes.
1704 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1705 self._expl = getattr(self, "expl", None) if expl is None else expl
1706 if self.tag != self.tag_default and self._expl is not None:
1707 raise ValueError("implicit and explicit tags can not be set simultaneously")
1708 if self.tag is None:
1709 self._tag_order = None
1711 tag_class, _, tag_num = tag_decode(
1712 self.tag if self._expl is None else self._expl
1714 self._tag_order = (tag_class, tag_num)
1715 if default is not None:
1717 self.optional = optional
1718 self.offset, self.llen, self.vlen = _decoded
1720 self.expl_lenindef = False
1721 self.lenindef = False
1722 self.ber_encoded = False
1725 def ready(self): # pragma: no cover
1726 """Is object ready to be encoded?
1728 raise NotImplementedError()
1730 def _assert_ready(self):
1732 raise ObjNotReady(self.__class__.__name__)
1736 """Is either object or any elements inside is BER encoded?
1738 return self.expl_lenindef or self.lenindef or self.ber_encoded
1742 """Is object decoded?
1744 return (self.llen + self.vlen) > 0
1746 def __getstate__(self): # pragma: no cover
1747 """Used for making safe to be mutable pickleable copies
1749 raise NotImplementedError()
1751 def __setstate__(self, state):
1752 if state.version != __version__:
1753 raise ValueError("data is pickled by different PyDERASN version")
1754 self.tag = state.tag
1755 self._tag_order = state.tag_order
1756 self._expl = state.expl
1757 self.default = state.default
1758 self.optional = state.optional
1759 self.offset = state.offset
1760 self.llen = state.llen
1761 self.vlen = state.vlen
1762 self.expl_lenindef = state.expl_lenindef
1763 self.lenindef = state.lenindef
1764 self.ber_encoded = state.ber_encoded
1767 def tag_order(self):
1768 """Tag's (class, number) used for DER/CER sorting
1770 return self._tag_order
1773 def tag_order_cer(self):
1774 return self.tag_order
1778 """.. seealso:: :ref:`decoding`
1780 return len(self.tag)
1784 """.. seealso:: :ref:`decoding`
1786 return self.tlen + self.llen + self.vlen
1788 def __str__(self): # pragma: no cover
1789 return self.__bytes__() if PY2 else self.__unicode__()
1791 def __ne__(self, their):
1792 return not(self == their)
1794 def __gt__(self, their): # pragma: no cover
1795 return not(self < their)
1797 def __le__(self, their): # pragma: no cover
1798 return (self == their) or (self < their)
1800 def __ge__(self, their): # pragma: no cover
1801 return (self == their) or (self > their)
1803 def _encode(self): # pragma: no cover
1804 raise NotImplementedError()
1806 def _encode_cer(self, writer):
1807 write_full(writer, self._encode())
1809 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode): # pragma: no cover
1810 yield NotImplemented
1812 def _encode1st(self, state):
1813 raise NotImplementedError()
1815 def _encode2nd(self, writer, state_iter):
1816 raise NotImplementedError()
1819 """DER encode the structure
1821 :returns: DER representation
1823 raw = self._encode()
1824 if self._expl is None:
1826 return b"".join((self._expl, len_encode(len(raw)), raw))
1828 def encode1st(self, state=None):
1829 """Do the 1st pass of 2-pass encoding
1831 :rtype: (int, array("L"))
1832 :returns: full length of encoded data and precalculated various
1836 state = state_2pass_new()
1837 if self._expl is None:
1838 return self._encode1st(state)
1840 idx = len(state) - 1
1841 vlen, _ = self._encode1st(state)
1843 fulllen = len(self._expl) + len_size(vlen) + vlen
1844 return fulllen, state
1846 def encode2nd(self, writer, state_iter):
1847 """Do the 2nd pass of 2-pass encoding
1849 :param writer: must comply with ``io.RawIOBase.write`` behaviour
1850 :param state_iter: iterator over the 1st pass state (``iter(state)``)
1852 if self._expl is None:
1853 self._encode2nd(writer, state_iter)
1855 write_full(writer, self._expl + len_encode(next(state_iter)))
1856 self._encode2nd(writer, state_iter)
1858 def encode_cer(self, writer):
1859 """CER encode the structure to specified writer
1861 :param writer: must comply with ``io.RawIOBase.write``
1862 behaviour. It takes slice to be written and
1863 returns number of bytes processed. If it returns
1864 None, then exception will be raised
1866 if self._expl is not None:
1867 write_full(writer, self._expl + LENINDEF)
1868 if getattr(self, "der_forced", False):
1869 write_full(writer, self._encode())
1871 self._encode_cer(writer)
1872 if self._expl is not None:
1873 write_full(writer, EOC)
1875 def hexencode(self):
1876 """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1878 return hexenc(self.encode())
1888 _ctx_immutable=True,
1892 :param data: either binary or memoryview
1893 :param int offset: initial data's offset
1894 :param bool leavemm: do we need to leave memoryview of remaining
1895 data as is, or convert it to bytes otherwise
1896 :param decode_path: current decode path (tuples of strings,
1897 possibly with DecodePathDefBy) with will be
1898 the root for all underlying objects
1899 :param ctx: optional :ref:`context <ctx>` governing decoding process
1900 :param bool tag_only: decode only the tag, without length and
1901 contents (used only in Choice and Set
1902 structures, trying to determine if tag satisfies
1904 :param bool _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1906 :returns: (Obj, remaining data)
1908 .. seealso:: :ref:`decoding`
1910 result = next(self.decode_evgen(
1922 _, obj, tail = result
1933 _ctx_immutable=True,
1936 """Decode with evgen mode on
1938 That method is identical to :py:meth:`pyderasn.Obj.decode`, but
1939 it returns the generator producing ``(decode_path, obj, tail)``
1941 .. seealso:: :ref:`evgen mode <evgen_mode>`.
1945 elif _ctx_immutable:
1947 tlv = memoryview(data)
1950 get_def_by_path(ctx.get("evgen_mode_upto", ()), decode_path) is not None
1953 if self._expl is None:
1954 for result in self._decode(
1957 decode_path=decode_path,
1960 evgen_mode=_evgen_mode,
1965 _decode_path, obj, tail = result
1966 if not _decode_path is decode_path:
1970 t, tlen, lv = tag_strip(tlv)
1971 except DecodeError as err:
1972 raise err.__class__(
1974 klass=self.__class__,
1975 decode_path=decode_path,
1980 klass=self.__class__,
1981 decode_path=decode_path,
1985 l, llen, v = len_decode(lv)
1986 except LenIndefForm as err:
1987 if not ctx.get("bered", False):
1988 raise err.__class__(
1990 klass=self.__class__,
1991 decode_path=decode_path,
1995 offset += tlen + llen
1996 for result in self._decode(
1999 decode_path=decode_path,
2002 evgen_mode=_evgen_mode,
2004 if tag_only: # pragma: no cover
2007 _decode_path, obj, tail = result
2008 if not _decode_path is decode_path:
2010 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
2011 if eoc_expected.tobytes() != EOC:
2014 klass=self.__class__,
2015 decode_path=decode_path,
2019 obj.expl_lenindef = True
2020 except DecodeError as err:
2021 raise err.__class__(
2023 klass=self.__class__,
2024 decode_path=decode_path,
2029 raise NotEnoughData(
2030 "encoded length is longer than data",
2031 klass=self.__class__,
2032 decode_path=decode_path,
2035 for result in self._decode(
2037 offset=offset + tlen + llen,
2038 decode_path=decode_path,
2041 evgen_mode=_evgen_mode,
2043 if tag_only: # pragma: no cover
2046 _decode_path, obj, tail = result
2047 if not _decode_path is decode_path:
2049 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
2051 "explicit tag out-of-bound, longer than data",
2052 klass=self.__class__,
2053 decode_path=decode_path,
2056 yield decode_path, obj, (tail if leavemm else tail.tobytes())
2058 def decod(self, data, offset=0, decode_path=(), ctx=None):
2059 """Decode the data, check that tail is empty
2061 :raises ExceedingData: if tail is not empty
2063 This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
2064 (decode without tail) that also checks that there is no
2067 obj, tail = self.decode(
2070 decode_path=decode_path,
2075 raise ExceedingData(len(tail))
2078 def hexdecode(self, data, *args, **kwargs):
2079 """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
2081 return self.decode(hexdec(data), *args, **kwargs)
2083 def hexdecod(self, data, *args, **kwargs):
2084 """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
2086 return self.decod(hexdec(data), *args, **kwargs)
2090 """.. seealso:: :ref:`decoding`
2092 return self._expl is not None
2096 """.. seealso:: :ref:`decoding`
2101 def expl_tlen(self):
2102 """.. seealso:: :ref:`decoding`
2104 return len(self._expl)
2107 def expl_llen(self):
2108 """.. seealso:: :ref:`decoding`
2110 if self.expl_lenindef:
2112 return len(len_encode(self.tlvlen))
2115 def expl_offset(self):
2116 """.. seealso:: :ref:`decoding`
2118 return self.offset - self.expl_tlen - self.expl_llen
2121 def expl_vlen(self):
2122 """.. seealso:: :ref:`decoding`
2127 def expl_tlvlen(self):
2128 """.. seealso:: :ref:`decoding`
2130 return self.expl_tlen + self.expl_llen + self.expl_vlen
2133 def fulloffset(self):
2134 """.. seealso:: :ref:`decoding`
2136 return self.expl_offset if self.expled else self.offset
2140 """.. seealso:: :ref:`decoding`
2142 return self.expl_tlvlen if self.expled else self.tlvlen
2144 def pps_lenindef(self, decode_path):
2145 if self.lenindef and not (
2146 getattr(self, "defined", None) is not None and
2147 self.defined[1].lenindef
2150 asn1_type_name="EOC",
2152 decode_path=decode_path,
2154 self.offset + self.tlvlen -
2155 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
2163 if self.expl_lenindef:
2165 asn1_type_name="EOC",
2166 obj_name="EXPLICIT",
2167 decode_path=decode_path,
2168 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
2177 def encode_cer(obj):
2178 """Encode to CER in memory buffer
2180 :returns bytes: memory buffer contents
2183 obj.encode_cer(buf.write)
2184 return buf.getvalue()
2187 def encode2pass(obj):
2188 """Encode (2-pass mode) to DER in memory buffer
2190 :returns bytes: memory buffer contents
2193 _, state = obj.encode1st()
2194 obj.encode2nd(buf.write, iter(state))
2195 return buf.getvalue()
2198 class DecodePathDefBy(object):
2199 """DEFINED BY representation inside decode path
2201 __slots__ = ("defined_by",)
2203 def __init__(self, defined_by):
2204 self.defined_by = defined_by
2206 def __ne__(self, their):
2207 return not(self == their)
2209 def __eq__(self, their):
2210 if not isinstance(their, self.__class__):
2212 return self.defined_by == their.defined_by
2215 return "DEFINED BY " + str(self.defined_by)
2218 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
2221 ########################################################################
2223 ########################################################################
2225 PP = namedtuple("PP", (
2248 ), **NAMEDTUPLE_KWARGS)
2253 asn1_type_name="unknown",
2270 expl_lenindef=False,
2301 def _colourize(what, colour, with_colours, attrs=("bold",)):
2302 return colored(what, colour, attrs=attrs) if with_colours else what
2305 def colonize_hex(hexed):
2306 """Separate hexadecimal string with colons
2308 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
2311 def find_oid_name(asn1_type_name, oid_maps, value):
2312 if len(oid_maps) > 0 and asn1_type_name == ObjectIdentifier.asn1_type_name:
2313 for oid_map in oid_maps:
2314 oid_name = oid_map.get(value)
2315 if oid_name is not None:
2326 with_decode_path=False,
2327 decode_path_len_decrease=0,
2334 " " if pp.expl_offset is None else
2335 ("-%d" % (pp.offset - pp.expl_offset))
2337 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
2339 col = _colourize(col, "red", with_colours, ())
2340 col += _colourize("B", "red", with_colours) if pp.bered else " "
2342 col = "[%d,%d,%4d]%s" % (
2343 pp.tlen, pp.llen, pp.vlen,
2344 LENINDEF_PP_CHAR if pp.lenindef else " "
2346 col = _colourize(col, "green", with_colours, ())
2348 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2349 if decode_path_len > 0:
2350 cols.append(" ." * decode_path_len)
2351 ent = pp.decode_path[-1]
2352 if isinstance(ent, DecodePathDefBy):
2353 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
2354 value = str(ent.defined_by)
2355 oid_name = find_oid_name(ent.defined_by.asn1_type_name, oid_maps, value)
2356 if oid_name is None:
2357 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
2359 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
2361 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
2362 if pp.expl is not None:
2363 klass, _, num = pp.expl
2364 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
2365 cols.append(_colourize(col, "blue", with_colours))
2366 if pp.impl is not None:
2367 klass, _, num = pp.impl
2368 col = "[%s%d]" % (TagClassReprs[klass], num)
2369 cols.append(_colourize(col, "blue", with_colours))
2370 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
2371 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
2373 cols.append(_colourize("BER", "red", with_colours))
2374 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
2375 if pp.value is not None:
2377 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
2378 oid_name = find_oid_name(pp.asn1_type_name, oid_maps, pp.value)
2379 if oid_name is not None:
2380 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
2381 if pp.asn1_type_name == Integer.asn1_type_name:
2382 cols.append(_colourize(
2383 "(%s)" % colonize_hex(pp.obj.tohex()), "green", with_colours,
2386 if pp.blob.__class__ == binary_type:
2387 cols.append(hexenc(pp.blob))
2388 elif pp.blob.__class__ == tuple:
2389 cols.append(", ".join(pp.blob))
2391 cols.append(_colourize("OPTIONAL", "red", with_colours))
2393 cols.append(_colourize("DEFAULT", "red", with_colours))
2394 if with_decode_path:
2395 cols.append(_colourize(
2396 "[%s]" % ":".join(str(p) for p in pp.decode_path),
2400 return " ".join(cols)
2403 def pp_console_blob(pp, decode_path_len_decrease=0):
2404 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
2405 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2406 if decode_path_len > 0:
2407 cols.append(" ." * (decode_path_len + 1))
2408 if pp.blob.__class__ == binary_type:
2409 blob = hexenc(pp.blob).upper()
2410 for i in six_xrange(0, len(blob), 32):
2411 chunk = blob[i:i + 32]
2412 yield " ".join(cols + [colonize_hex(chunk)])
2413 elif pp.blob.__class__ == tuple:
2414 yield " ".join(cols + [", ".join(pp.blob)])
2422 with_decode_path=False,
2423 decode_path_only=(),
2426 """Pretty print object
2428 :param Obj obj: object you want to pretty print
2429 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
2430 Its human readable form is printed when OID is met
2431 :param big_blobs: if large binary objects are met (like OctetString
2432 values), do we need to print them too, on separate
2434 :param with_colours: colourize output, if ``termcolor`` library
2436 :param with_decode_path: print decode path
2437 :param decode_path_only: print only that specified decode path
2439 def _pprint_pps(pps):
2441 if hasattr(pp, "_fields"):
2443 decode_path_only != () and
2445 str(p) for p in pp.decode_path[:len(decode_path_only)]
2446 ) != decode_path_only
2450 yield pp_console_row(
2455 with_colours=with_colours,
2456 with_decode_path=with_decode_path,
2457 decode_path_len_decrease=len(decode_path_only),
2459 for row in pp_console_blob(
2461 decode_path_len_decrease=len(decode_path_only),
2465 yield pp_console_row(
2470 with_colours=with_colours,
2471 with_decode_path=with_decode_path,
2472 decode_path_len_decrease=len(decode_path_only),
2475 for row in _pprint_pps(pp):
2477 return "\n".join(_pprint_pps(obj.pps(decode_path)))
2480 ########################################################################
2481 # ASN.1 primitive types
2482 ########################################################################
2484 BooleanState = namedtuple(
2486 BasicState._fields + ("value",),
2492 """``BOOLEAN`` boolean type
2494 >>> b = Boolean(True)
2496 >>> b == Boolean(True)
2502 tag_default = tag_encode(1)
2503 asn1_type_name = "BOOLEAN"
2515 :param value: set the value. Either boolean type, or
2516 :py:class:`pyderasn.Boolean` object
2517 :param bytes impl: override default tag with ``IMPLICIT`` one
2518 :param bytes expl: override default tag with ``EXPLICIT`` one
2519 :param default: set default value. Type same as in ``value``
2520 :param bool optional: is object ``OPTIONAL`` in sequence
2522 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
2523 self._value = None if value is None else self._value_sanitize(value)
2524 if default is not None:
2525 default = self._value_sanitize(default)
2526 self.default = self.__class__(
2532 self._value = default
2534 def _value_sanitize(self, value):
2535 if value.__class__ == bool:
2537 if issubclass(value.__class__, Boolean):
2539 raise InvalidValueType((self.__class__, bool))
2543 return self._value is not None
2545 def __getstate__(self):
2546 return BooleanState(
2562 def __setstate__(self, state):
2563 super(Boolean, self).__setstate__(state)
2564 self._value = state.value
2566 def __nonzero__(self):
2567 self._assert_ready()
2571 self._assert_ready()
2574 def __eq__(self, their):
2575 if their.__class__ == bool:
2576 return self._value == their
2577 if not issubclass(their.__class__, Boolean):
2580 self._value == their._value and
2581 self.tag == their.tag and
2582 self._expl == their._expl
2593 return self.__class__(
2595 impl=self.tag if impl is None else impl,
2596 expl=self._expl if expl is None else expl,
2597 default=self.default if default is None else default,
2598 optional=self.optional if optional is None else optional,
2602 self._assert_ready()
2603 return b"".join((self.tag, LEN1, (b"\xFF" if self._value else b"\x00")))
2605 def _encode1st(self, state):
2606 return len(self.tag) + 2, state
2608 def _encode2nd(self, writer, state_iter):
2609 self._assert_ready()
2610 write_full(writer, self._encode())
2612 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2614 t, _, lv = tag_strip(tlv)
2615 except DecodeError as err:
2616 raise err.__class__(
2618 klass=self.__class__,
2619 decode_path=decode_path,
2624 klass=self.__class__,
2625 decode_path=decode_path,
2632 l, _, v = len_decode(lv)
2633 except DecodeError as err:
2634 raise err.__class__(
2636 klass=self.__class__,
2637 decode_path=decode_path,
2641 raise InvalidLength(
2642 "Boolean's length must be equal to 1",
2643 klass=self.__class__,
2644 decode_path=decode_path,
2648 raise NotEnoughData(
2649 "encoded length is longer than data",
2650 klass=self.__class__,
2651 decode_path=decode_path,
2654 first_octet = byte2int(v)
2656 if first_octet == 0:
2658 elif first_octet == 0xFF:
2660 elif ctx.get("bered", False):
2665 "unacceptable Boolean value",
2666 klass=self.__class__,
2667 decode_path=decode_path,
2670 obj = self.__class__(
2674 default=self.default,
2675 optional=self.optional,
2676 _decoded=(offset, 1, 1),
2678 obj.ber_encoded = ber_encoded
2679 yield decode_path, obj, v[1:]
2682 return pp_console_row(next(self.pps()))
2684 def pps(self, decode_path=()):
2687 asn1_type_name=self.asn1_type_name,
2688 obj_name=self.__class__.__name__,
2689 decode_path=decode_path,
2690 value=str(self._value) if self.ready else None,
2691 optional=self.optional,
2692 default=self == self.default,
2693 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2694 expl=None if self._expl is None else tag_decode(self._expl),
2699 expl_offset=self.expl_offset if self.expled else None,
2700 expl_tlen=self.expl_tlen if self.expled else None,
2701 expl_llen=self.expl_llen if self.expled else None,
2702 expl_vlen=self.expl_vlen if self.expled else None,
2703 expl_lenindef=self.expl_lenindef,
2704 ber_encoded=self.ber_encoded,
2707 for pp in self.pps_lenindef(decode_path):
2711 IntegerState = namedtuple(
2713 BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2719 """``INTEGER`` integer type
2721 >>> b = Integer(-123)
2723 >>> b == Integer(-123)
2728 >>> Integer(2, bounds=(1, 3))
2730 >>> Integer(5, bounds=(1, 3))
2731 Traceback (most recent call last):
2732 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2736 class Version(Integer):
2743 >>> v = Version("v1")
2750 {'v3': 2, 'v1': 0, 'v2': 1}
2752 __slots__ = ("specs", "_bound_min", "_bound_max")
2753 tag_default = tag_encode(2)
2754 asn1_type_name = "INTEGER"
2768 :param value: set the value. Either integer type, named value
2769 (if ``schema`` is specified in the class), or
2770 :py:class:`pyderasn.Integer` object
2771 :param bounds: set ``(MIN, MAX)`` value constraint.
2772 (-inf, +inf) by default
2773 :param bytes impl: override default tag with ``IMPLICIT`` one
2774 :param bytes expl: override default tag with ``EXPLICIT`` one
2775 :param default: set default value. Type same as in ``value``
2776 :param bool optional: is object ``OPTIONAL`` in sequence
2778 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
2780 specs = getattr(self, "schema", {}) if _specs is None else _specs
2781 self.specs = specs if specs.__class__ == dict else dict(specs)
2782 self._bound_min, self._bound_max = getattr(
2785 (float("-inf"), float("+inf")),
2786 ) if bounds is None else bounds
2787 if value is not None:
2788 self._value = self._value_sanitize(value)
2789 if default is not None:
2790 default = self._value_sanitize(default)
2791 self.default = self.__class__(
2797 if self._value is None:
2798 self._value = default
2800 def _value_sanitize(self, value):
2801 if isinstance(value, integer_types):
2803 elif issubclass(value.__class__, Integer):
2804 value = value._value
2805 elif value.__class__ == str:
2806 value = self.specs.get(value)
2808 raise ObjUnknown("integer value: %s" % value)
2810 raise InvalidValueType((self.__class__, int, str))
2811 if not self._bound_min <= value <= self._bound_max:
2812 raise BoundsError(self._bound_min, value, self._bound_max)
2817 return self._value is not None
2819 def __getstate__(self):
2820 return IntegerState(
2839 def __setstate__(self, state):
2840 super(Integer, self).__setstate__(state)
2841 self.specs = state.specs
2842 self._value = state.value
2843 self._bound_min = state.bound_min
2844 self._bound_max = state.bound_max
2847 self._assert_ready()
2848 return int(self._value)
2851 """Hexadecimal representation
2853 Use :py:func:`pyderasn.colonize_hex` for colonizing it.
2855 hex_repr = hex(int(self))[2:].upper()
2856 if len(hex_repr) % 2 != 0:
2857 hex_repr = "0" + hex_repr
2861 self._assert_ready()
2862 return hash(b"".join((
2864 bytes(self._expl or b""),
2865 str(self._value).encode("ascii"),
2868 def __eq__(self, their):
2869 if isinstance(their, integer_types):
2870 return self._value == their
2871 if not issubclass(their.__class__, Integer):
2874 self._value == their._value and
2875 self.tag == their.tag and
2876 self._expl == their._expl
2879 def __lt__(self, their):
2880 return self._value < their._value
2884 """Return named representation (if exists) of the value
2886 for name, value in iteritems(self.specs):
2887 if value == self._value:
2900 return self.__class__(
2903 (self._bound_min, self._bound_max)
2904 if bounds is None else bounds
2906 impl=self.tag if impl is None else impl,
2907 expl=self._expl if expl is None else expl,
2908 default=self.default if default is None else default,
2909 optional=self.optional if optional is None else optional,
2913 def _encode_payload(self):
2914 self._assert_ready()
2918 octets = bytearray([0])
2922 octets = bytearray()
2924 octets.append((value & 0xFF) ^ 0xFF)
2926 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2929 octets = bytearray()
2931 octets.append(value & 0xFF)
2933 if octets[-1] & 0x80 > 0:
2936 octets = bytes(octets)
2938 bytes_len = ceil(value.bit_length() / 8) or 1
2941 octets = value.to_bytes(
2946 except OverflowError:
2953 octets = self._encode_payload()
2954 return b"".join((self.tag, len_encode(len(octets)), octets))
2956 def _encode1st(self, state):
2957 l = len(self._encode_payload())
2958 return len(self.tag) + len_size(l) + l, state
2960 def _encode2nd(self, writer, state_iter):
2961 write_full(writer, self._encode())
2963 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2965 t, _, lv = tag_strip(tlv)
2966 except DecodeError as err:
2967 raise err.__class__(
2969 klass=self.__class__,
2970 decode_path=decode_path,
2975 klass=self.__class__,
2976 decode_path=decode_path,
2983 l, llen, v = len_decode(lv)
2984 except DecodeError as err:
2985 raise err.__class__(
2987 klass=self.__class__,
2988 decode_path=decode_path,
2992 raise NotEnoughData(
2993 "encoded length is longer than data",
2994 klass=self.__class__,
2995 decode_path=decode_path,
2999 raise NotEnoughData(
3001 klass=self.__class__,
3002 decode_path=decode_path,
3005 v, tail = v[:l], v[l:]
3006 first_octet = byte2int(v)
3008 second_octet = byte2int(v[1:])
3010 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
3011 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
3014 "non normalized integer",
3015 klass=self.__class__,
3016 decode_path=decode_path,
3021 if first_octet & 0x80 > 0:
3022 octets = bytearray()
3023 for octet in bytearray(v):
3024 octets.append(octet ^ 0xFF)
3025 for octet in octets:
3026 value = (value << 8) | octet
3030 for octet in bytearray(v):
3031 value = (value << 8) | octet
3033 value = int.from_bytes(v, byteorder="big", signed=True)
3035 obj = self.__class__(
3037 bounds=(self._bound_min, self._bound_max),
3040 default=self.default,
3041 optional=self.optional,
3043 _decoded=(offset, llen, l),
3045 except BoundsError as err:
3048 klass=self.__class__,
3049 decode_path=decode_path,
3052 yield decode_path, obj, tail
3055 return pp_console_row(next(self.pps()))
3057 def pps(self, decode_path=()):
3060 asn1_type_name=self.asn1_type_name,
3061 obj_name=self.__class__.__name__,
3062 decode_path=decode_path,
3063 value=(self.named or str(self._value)) if self.ready else None,
3064 optional=self.optional,
3065 default=self == self.default,
3066 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3067 expl=None if self._expl is None else tag_decode(self._expl),
3072 expl_offset=self.expl_offset if self.expled else None,
3073 expl_tlen=self.expl_tlen if self.expled else None,
3074 expl_llen=self.expl_llen if self.expled else None,
3075 expl_vlen=self.expl_vlen if self.expled else None,
3076 expl_lenindef=self.expl_lenindef,
3079 for pp in self.pps_lenindef(decode_path):
3083 BitStringState = namedtuple(
3085 BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
3090 class BitString(Obj):
3091 """``BIT STRING`` bit string type
3093 >>> BitString(b"hello world")
3094 BIT STRING 88 bits 68656c6c6f20776f726c64
3097 >>> b == b"hello world"
3102 >>> BitString("'0A3B5F291CD'H")
3103 BIT STRING 44 bits 0a3b5f291cd0
3104 >>> b = BitString("'010110000000'B")
3105 BIT STRING 12 bits 5800
3108 >>> b[0], b[1], b[2], b[3]
3109 (False, True, False, True)
3113 [False, True, False, True, True, False, False, False, False, False, False, False]
3117 class KeyUsage(BitString):
3119 ("digitalSignature", 0),
3120 ("nonRepudiation", 1),
3121 ("keyEncipherment", 2),
3124 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
3125 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
3127 ['nonRepudiation', 'keyEncipherment']
3129 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
3133 Pay attention that BIT STRING can be encoded both in primitive
3134 and constructed forms. Decoder always checks constructed form tag
3135 additionally to specified primitive one. If BER decoding is
3136 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
3137 of DER restrictions.
3139 __slots__ = ("tag_constructed", "specs", "defined")
3140 tag_default = tag_encode(3)
3141 asn1_type_name = "BIT STRING"
3154 :param value: set the value. Either binary type, tuple of named
3155 values (if ``schema`` is specified in the class),
3156 string in ``'XXX...'B`` form, or
3157 :py:class:`pyderasn.BitString` object
3158 :param bytes impl: override default tag with ``IMPLICIT`` one
3159 :param bytes expl: override default tag with ``EXPLICIT`` one
3160 :param default: set default value. Type same as in ``value``
3161 :param bool optional: is object ``OPTIONAL`` in sequence
3163 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
3164 specs = getattr(self, "schema", {}) if _specs is None else _specs
3165 self.specs = specs if specs.__class__ == dict else dict(specs)
3166 self._value = None if value is None else self._value_sanitize(value)
3167 if default is not None:
3168 default = self._value_sanitize(default)
3169 self.default = self.__class__(
3175 self._value = default
3177 tag_klass, _, tag_num = tag_decode(self.tag)
3178 self.tag_constructed = tag_encode(
3180 form=TagFormConstructed,
3184 def _bits2octets(self, bits):
3185 if len(self.specs) > 0:
3186 bits = bits.rstrip("0")
3188 bits += "0" * ((8 - (bit_len % 8)) % 8)
3189 octets = bytearray(len(bits) // 8)
3190 for i in six_xrange(len(octets)):
3191 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
3192 return bit_len, bytes(octets)
3194 def _value_sanitize(self, value):
3195 if isinstance(value, (string_types, binary_type)):
3197 isinstance(value, string_types) and
3198 value.startswith("'")
3200 if value.endswith("'B"):
3202 if not frozenset(value) <= SET01:
3203 raise ValueError("B's coding contains unacceptable chars")
3204 return self._bits2octets(value)
3205 if value.endswith("'H"):
3209 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
3211 if value.__class__ == binary_type:
3212 return (len(value) * 8, value)
3213 raise InvalidValueType((self.__class__, string_types, binary_type))
3214 if value.__class__ == tuple:
3217 isinstance(value[0], integer_types) and
3218 value[1].__class__ == binary_type
3223 bit = self.specs.get(name)
3225 raise ObjUnknown("BitString value: %s" % name)
3228 return self._bits2octets("")
3229 bits = frozenset(bits)
3230 return self._bits2octets("".join(
3231 ("1" if bit in bits else "0")
3232 for bit in six_xrange(max(bits) + 1)
3234 if issubclass(value.__class__, BitString):
3236 raise InvalidValueType((self.__class__, binary_type, string_types))
3240 return self._value is not None
3242 def __getstate__(self):
3243 return BitStringState(
3258 self.tag_constructed,
3262 def __setstate__(self, state):
3263 super(BitString, self).__setstate__(state)
3264 self.specs = state.specs
3265 self._value = state.value
3266 self.tag_constructed = state.tag_constructed
3267 self.defined = state.defined
3270 self._assert_ready()
3271 for i in six_xrange(self._value[0]):
3276 """Returns number of bits in the string
3278 self._assert_ready()
3279 return self._value[0]
3281 def __bytes__(self):
3282 self._assert_ready()
3283 return self._value[1]
3285 def __eq__(self, their):
3286 if their.__class__ == bytes:
3287 return self._value[1] == their
3288 if not issubclass(their.__class__, BitString):
3291 self._value == their._value and
3292 self.tag == their.tag and
3293 self._expl == their._expl
3298 """Named representation (if exists) of the bits
3300 :returns: [str(name), ...]
3302 return [name for name, bit in iteritems(self.specs) if self[bit]]
3312 return self.__class__(
3314 impl=self.tag if impl is None else impl,
3315 expl=self._expl if expl is None else expl,
3316 default=self.default if default is None else default,
3317 optional=self.optional if optional is None else optional,
3321 def __getitem__(self, key):
3322 if key.__class__ == int:
3323 bit_len, octets = self._value
3327 byte2int(memoryview(octets)[key // 8:]) >>
3330 if isinstance(key, string_types):
3331 value = self.specs.get(key)
3333 raise ObjUnknown("BitString value: %s" % key)
3335 raise InvalidValueType((int, str))
3338 self._assert_ready()
3339 bit_len, octets = self._value
3342 len_encode(len(octets) + 1),
3343 int2byte((8 - bit_len % 8) % 8),
3347 def _encode1st(self, state):
3348 self._assert_ready()
3349 _, octets = self._value
3351 return len(self.tag) + len_size(l) + l, state
3353 def _encode2nd(self, writer, state_iter):
3354 bit_len, octets = self._value
3355 write_full(writer, b"".join((
3357 len_encode(len(octets) + 1),
3358 int2byte((8 - bit_len % 8) % 8),
3360 write_full(writer, octets)
3362 def _encode_cer(self, writer):
3363 bit_len, octets = self._value
3364 if len(octets) + 1 <= 1000:
3365 write_full(writer, self._encode())
3367 write_full(writer, self.tag_constructed)
3368 write_full(writer, LENINDEF)
3369 for offset in six_xrange(0, (len(octets) // 999) * 999, 999):
3370 write_full(writer, b"".join((
3371 BitString.tag_default,
3374 octets[offset:offset + 999],
3376 tail = octets[offset+999:]
3378 tail = int2byte((8 - bit_len % 8) % 8) + tail
3379 write_full(writer, b"".join((
3380 BitString.tag_default,
3381 len_encode(len(tail)),
3384 write_full(writer, EOC)
3386 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3388 t, tlen, lv = tag_strip(tlv)
3389 except DecodeError as err:
3390 raise err.__class__(
3392 klass=self.__class__,
3393 decode_path=decode_path,
3397 if tag_only: # pragma: no cover
3401 l, llen, v = len_decode(lv)
3402 except DecodeError as err:
3403 raise err.__class__(
3405 klass=self.__class__,
3406 decode_path=decode_path,
3410 raise NotEnoughData(
3411 "encoded length is longer than data",
3412 klass=self.__class__,
3413 decode_path=decode_path,
3417 raise NotEnoughData(
3419 klass=self.__class__,
3420 decode_path=decode_path,
3423 pad_size = byte2int(v)
3424 if l == 1 and pad_size != 0:
3426 "invalid empty value",
3427 klass=self.__class__,
3428 decode_path=decode_path,
3434 klass=self.__class__,
3435 decode_path=decode_path,
3438 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
3441 klass=self.__class__,
3442 decode_path=decode_path,
3445 v, tail = v[:l], v[l:]
3446 bit_len = (len(v) - 1) * 8 - pad_size
3447 obj = self.__class__(
3448 value=None if evgen_mode else (bit_len, v[1:].tobytes()),
3451 default=self.default,
3452 optional=self.optional,
3454 _decoded=(offset, llen, l),
3457 obj._value = (bit_len, None)
3458 yield decode_path, obj, tail
3460 if t != self.tag_constructed:
3462 klass=self.__class__,
3463 decode_path=decode_path,
3466 if not ctx.get("bered", False):
3468 "unallowed BER constructed encoding",
3469 klass=self.__class__,
3470 decode_path=decode_path,
3473 if tag_only: # pragma: no cover
3478 l, llen, v = len_decode(lv)
3479 except LenIndefForm:
3480 llen, l, v = 1, 0, lv[1:]
3482 except DecodeError as err:
3483 raise err.__class__(
3485 klass=self.__class__,
3486 decode_path=decode_path,
3490 raise NotEnoughData(
3491 "encoded length is longer than data",
3492 klass=self.__class__,
3493 decode_path=decode_path,
3496 if not lenindef and l == 0:
3497 raise NotEnoughData(
3499 klass=self.__class__,
3500 decode_path=decode_path,
3504 sub_offset = offset + tlen + llen
3508 if v[:EOC_LEN].tobytes() == EOC:
3515 "chunk out of bounds",
3516 klass=self.__class__,
3517 decode_path=decode_path + (str(len(chunks) - 1),),
3518 offset=chunks[-1].offset,
3520 sub_decode_path = decode_path + (str(len(chunks)),)
3523 for _decode_path, chunk, v_tail in BitString().decode_evgen(
3526 decode_path=sub_decode_path,
3529 _ctx_immutable=False,
3531 yield _decode_path, chunk, v_tail
3533 _, chunk, v_tail = next(BitString().decode_evgen(
3536 decode_path=sub_decode_path,
3539 _ctx_immutable=False,
3544 "expected BitString encoded chunk",
3545 klass=self.__class__,
3546 decode_path=sub_decode_path,
3549 chunks.append(chunk)
3550 sub_offset += chunk.tlvlen
3551 vlen += chunk.tlvlen
3553 if len(chunks) == 0:
3556 klass=self.__class__,
3557 decode_path=decode_path,
3562 for chunk_i, chunk in enumerate(chunks[:-1]):
3563 if chunk.bit_len % 8 != 0:
3565 "BitString chunk is not multiple of 8 bits",
3566 klass=self.__class__,
3567 decode_path=decode_path + (str(chunk_i),),
3568 offset=chunk.offset,
3571 values.append(bytes(chunk))
3572 bit_len += chunk.bit_len
3573 chunk_last = chunks[-1]
3575 values.append(bytes(chunk_last))
3576 bit_len += chunk_last.bit_len
3577 obj = self.__class__(
3578 value=None if evgen_mode else (bit_len, b"".join(values)),
3581 default=self.default,
3582 optional=self.optional,
3584 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3587 obj._value = (bit_len, None)
3588 obj.lenindef = lenindef
3589 obj.ber_encoded = True
3590 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
3593 return pp_console_row(next(self.pps()))
3595 def pps(self, decode_path=()):
3599 bit_len, blob = self._value
3600 value = "%d bits" % bit_len
3601 if len(self.specs) > 0 and blob is not None:
3602 blob = tuple(self.named)
3605 asn1_type_name=self.asn1_type_name,
3606 obj_name=self.__class__.__name__,
3607 decode_path=decode_path,
3610 optional=self.optional,
3611 default=self == self.default,
3612 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3613 expl=None if self._expl is None else tag_decode(self._expl),
3618 expl_offset=self.expl_offset if self.expled else None,
3619 expl_tlen=self.expl_tlen if self.expled else None,
3620 expl_llen=self.expl_llen if self.expled else None,
3621 expl_vlen=self.expl_vlen if self.expled else None,
3622 expl_lenindef=self.expl_lenindef,
3623 lenindef=self.lenindef,
3624 ber_encoded=self.ber_encoded,
3627 defined_by, defined = self.defined or (None, None)
3628 if defined_by is not None:
3630 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3632 for pp in self.pps_lenindef(decode_path):
3636 OctetStringState = namedtuple(
3638 BasicState._fields + (
3649 class OctetString(Obj):
3650 """``OCTET STRING`` binary string type
3652 >>> s = OctetString(b"hello world")
3653 OCTET STRING 11 bytes 68656c6c6f20776f726c64
3654 >>> s == OctetString(b"hello world")
3659 >>> OctetString(b"hello", bounds=(4, 4))
3660 Traceback (most recent call last):
3661 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
3662 >>> OctetString(b"hell", bounds=(4, 4))
3663 OCTET STRING 4 bytes 68656c6c
3665 Memoryviews can be used as a values. If memoryview is made on
3666 mmap-ed file, then it does not take storage inside OctetString
3667 itself. In CER encoding mode it will be streamed to the specified
3668 writer, copying 1 KB chunks.
3670 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
3671 tag_default = tag_encode(4)
3672 asn1_type_name = "OCTET STRING"
3673 evgen_mode_skip_value = True
3687 :param value: set the value. Either binary type, or
3688 :py:class:`pyderasn.OctetString` object
3689 :param bounds: set ``(MIN, MAX)`` value size constraint.
3690 (-inf, +inf) by default
3691 :param bytes impl: override default tag with ``IMPLICIT`` one
3692 :param bytes expl: override default tag with ``EXPLICIT`` one
3693 :param default: set default value. Type same as in ``value``
3694 :param bool optional: is object ``OPTIONAL`` in sequence
3696 super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
3698 self._bound_min, self._bound_max = getattr(
3702 ) if bounds is None else bounds
3703 if value is not None:
3704 self._value = self._value_sanitize(value)
3705 if default is not None:
3706 default = self._value_sanitize(default)
3707 self.default = self.__class__(
3712 if self._value is None:
3713 self._value = default
3715 tag_klass, _, tag_num = tag_decode(self.tag)
3716 self.tag_constructed = tag_encode(
3718 form=TagFormConstructed,
3722 def _value_sanitize(self, value):
3723 if value.__class__ == binary_type or value.__class__ == memoryview:
3725 elif issubclass(value.__class__, OctetString):
3726 value = value._value
3728 raise InvalidValueType((self.__class__, bytes, memoryview))
3729 if not self._bound_min <= len(value) <= self._bound_max:
3730 raise BoundsError(self._bound_min, len(value), self._bound_max)
3735 return self._value is not None
3737 def __getstate__(self):
3738 return OctetStringState(
3754 self.tag_constructed,
3758 def __setstate__(self, state):
3759 super(OctetString, self).__setstate__(state)
3760 self._value = state.value
3761 self._bound_min = state.bound_min
3762 self._bound_max = state.bound_max
3763 self.tag_constructed = state.tag_constructed
3764 self.defined = state.defined
3766 def __bytes__(self):
3767 self._assert_ready()
3768 return bytes(self._value)
3770 def __eq__(self, their):
3771 if their.__class__ == binary_type:
3772 return self._value == their
3773 if not issubclass(their.__class__, OctetString):
3776 self._value == their._value and
3777 self.tag == their.tag and
3778 self._expl == their._expl
3781 def __lt__(self, their):
3782 return self._value < their._value
3793 return self.__class__(
3796 (self._bound_min, self._bound_max)
3797 if bounds is None else bounds
3799 impl=self.tag if impl is None else impl,
3800 expl=self._expl if expl is None else expl,
3801 default=self.default if default is None else default,
3802 optional=self.optional if optional is None else optional,
3806 self._assert_ready()
3809 len_encode(len(self._value)),
3813 def _encode1st(self, state):
3814 self._assert_ready()
3815 l = len(self._value)
3816 return len(self.tag) + len_size(l) + l, state
3818 def _encode2nd(self, writer, state_iter):
3820 write_full(writer, self.tag + len_encode(len(value)))
3821 write_full(writer, value)
3823 def _encode_cer(self, writer):
3824 octets = self._value
3825 if len(octets) <= 1000:
3826 write_full(writer, self._encode())
3828 write_full(writer, self.tag_constructed)
3829 write_full(writer, LENINDEF)
3830 for offset in six_xrange(0, (len(octets) // 1000) * 1000, 1000):
3831 write_full(writer, b"".join((
3832 OctetString.tag_default,
3834 octets[offset:offset + 1000],
3836 tail = octets[offset+1000:]
3838 write_full(writer, b"".join((
3839 OctetString.tag_default,
3840 len_encode(len(tail)),
3843 write_full(writer, EOC)
3845 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3847 t, tlen, lv = tag_strip(tlv)
3848 except DecodeError as err:
3849 raise err.__class__(
3851 klass=self.__class__,
3852 decode_path=decode_path,
3860 l, llen, v = len_decode(lv)
3861 except DecodeError as err:
3862 raise err.__class__(
3864 klass=self.__class__,
3865 decode_path=decode_path,
3869 raise NotEnoughData(
3870 "encoded length is longer than data",
3871 klass=self.__class__,
3872 decode_path=decode_path,
3875 v, tail = v[:l], v[l:]
3876 if evgen_mode and not self._bound_min <= len(v) <= self._bound_max:
3878 msg=str(BoundsError(self._bound_min, len(v), self._bound_max)),
3879 klass=self.__class__,
3880 decode_path=decode_path,
3884 obj = self.__class__(
3886 None if (evgen_mode and self.evgen_mode_skip_value)
3889 bounds=(self._bound_min, self._bound_max),
3892 default=self.default,
3893 optional=self.optional,
3894 _decoded=(offset, llen, l),
3897 except DecodeError as err:
3900 klass=self.__class__,
3901 decode_path=decode_path,
3904 except BoundsError as err:
3907 klass=self.__class__,
3908 decode_path=decode_path,
3911 yield decode_path, obj, tail
3913 if t != self.tag_constructed:
3915 klass=self.__class__,
3916 decode_path=decode_path,
3919 if not ctx.get("bered", False):
3921 "unallowed BER constructed encoding",
3922 klass=self.__class__,
3923 decode_path=decode_path,
3931 l, llen, v = len_decode(lv)
3932 except LenIndefForm:
3933 llen, l, v = 1, 0, lv[1:]
3935 except DecodeError as err:
3936 raise err.__class__(
3938 klass=self.__class__,
3939 decode_path=decode_path,
3943 raise NotEnoughData(
3944 "encoded length is longer than data",
3945 klass=self.__class__,
3946 decode_path=decode_path,
3951 sub_offset = offset + tlen + llen
3956 if v[:EOC_LEN].tobytes() == EOC:
3963 "chunk out of bounds",
3964 klass=self.__class__,
3965 decode_path=decode_path + (str(len(chunks) - 1),),
3966 offset=chunks[-1].offset,
3970 sub_decode_path = decode_path + (str(chunks_count),)
3971 for _decode_path, chunk, v_tail in OctetString().decode_evgen(
3974 decode_path=sub_decode_path,
3977 _ctx_immutable=False,
3979 yield _decode_path, chunk, v_tail
3980 if not chunk.ber_encoded:
3981 payload_len += chunk.vlen
3984 sub_decode_path = decode_path + (str(len(chunks)),)
3985 _, chunk, v_tail = next(OctetString().decode_evgen(
3988 decode_path=sub_decode_path,
3991 _ctx_immutable=False,
3994 chunks.append(chunk)
3997 "expected OctetString encoded chunk",
3998 klass=self.__class__,
3999 decode_path=sub_decode_path,
4002 sub_offset += chunk.tlvlen
4003 vlen += chunk.tlvlen
4005 if evgen_mode and not self._bound_min <= payload_len <= self._bound_max:
4007 msg=str(BoundsError(self._bound_min, payload_len, self._bound_max)),
4008 klass=self.__class__,
4009 decode_path=decode_path,
4013 obj = self.__class__(
4015 None if evgen_mode else
4016 b"".join(bytes(chunk) for chunk in chunks)
4018 bounds=(self._bound_min, self._bound_max),
4021 default=self.default,
4022 optional=self.optional,
4023 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4026 except DecodeError as err:
4029 klass=self.__class__,
4030 decode_path=decode_path,
4033 except BoundsError as err:
4036 klass=self.__class__,
4037 decode_path=decode_path,
4040 obj.lenindef = lenindef
4041 obj.ber_encoded = True
4042 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
4045 return pp_console_row(next(self.pps()))
4047 def pps(self, decode_path=()):
4050 asn1_type_name=self.asn1_type_name,
4051 obj_name=self.__class__.__name__,
4052 decode_path=decode_path,
4053 value=("%d bytes" % len(self._value)) if self.ready else None,
4054 blob=self._value if self.ready else None,
4055 optional=self.optional,
4056 default=self == self.default,
4057 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4058 expl=None if self._expl is None else tag_decode(self._expl),
4063 expl_offset=self.expl_offset if self.expled else None,
4064 expl_tlen=self.expl_tlen if self.expled else None,
4065 expl_llen=self.expl_llen if self.expled else None,
4066 expl_vlen=self.expl_vlen if self.expled else None,
4067 expl_lenindef=self.expl_lenindef,
4068 lenindef=self.lenindef,
4069 ber_encoded=self.ber_encoded,
4072 defined_by, defined = self.defined or (None, None)
4073 if defined_by is not None:
4075 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4077 for pp in self.pps_lenindef(decode_path):
4081 def agg_octet_string(evgens, decode_path, raw, writer):
4082 """Aggregate constructed string (OctetString and its derivatives)
4084 :param evgens: iterator of generated events
4085 :param decode_path: points to the string we want to decode
4086 :param raw: slicebable (memoryview, bytearray, etc) with
4087 the data evgens are generated on
4088 :param writer: buffer.write where string is going to be saved
4089 :param writer: where string is going to be saved. Must comply
4090 with ``io.RawIOBase.write`` behaviour
4092 .. seealso:: :ref:`agg_octet_string`
4094 decode_path_len = len(decode_path)
4095 for dp, obj, _ in evgens:
4096 if dp[:decode_path_len] != decode_path:
4098 if not obj.ber_encoded:
4099 write_full(writer, raw[
4100 obj.offset + obj.tlen + obj.llen:
4101 obj.offset + obj.tlen + obj.llen + obj.vlen -
4102 (EOC_LEN if obj.expl_lenindef else 0)
4104 if len(dp) == decode_path_len:
4108 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
4112 """``NULL`` null object
4120 tag_default = tag_encode(5)
4121 asn1_type_name = "NULL"
4125 value=None, # unused, but Sequence passes it
4132 :param bytes impl: override default tag with ``IMPLICIT`` one
4133 :param bytes expl: override default tag with ``EXPLICIT`` one
4134 :param bool optional: is object ``OPTIONAL`` in sequence
4136 super(Null, self).__init__(impl, expl, None, optional, _decoded)
4143 def __getstate__(self):
4159 def __eq__(self, their):
4160 if not issubclass(their.__class__, Null):
4163 self.tag == their.tag and
4164 self._expl == their._expl
4174 return self.__class__(
4175 impl=self.tag if impl is None else impl,
4176 expl=self._expl if expl is None else expl,
4177 optional=self.optional if optional is None else optional,
4181 return self.tag + LEN0
4183 def _encode1st(self, state):
4184 return len(self.tag) + 1, state
4186 def _encode2nd(self, writer, state_iter):
4187 write_full(writer, self.tag + LEN0)
4189 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4191 t, _, lv = tag_strip(tlv)
4192 except DecodeError as err:
4193 raise err.__class__(
4195 klass=self.__class__,
4196 decode_path=decode_path,
4201 klass=self.__class__,
4202 decode_path=decode_path,
4205 if tag_only: # pragma: no cover
4209 l, _, v = len_decode(lv)
4210 except DecodeError as err:
4211 raise err.__class__(
4213 klass=self.__class__,
4214 decode_path=decode_path,
4218 raise InvalidLength(
4219 "Null must have zero length",
4220 klass=self.__class__,
4221 decode_path=decode_path,
4224 obj = self.__class__(
4227 optional=self.optional,
4228 _decoded=(offset, 1, 0),
4230 yield decode_path, obj, v
4233 return pp_console_row(next(self.pps()))
4235 def pps(self, decode_path=()):
4238 asn1_type_name=self.asn1_type_name,
4239 obj_name=self.__class__.__name__,
4240 decode_path=decode_path,
4241 optional=self.optional,
4242 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4243 expl=None if self._expl is None else tag_decode(self._expl),
4248 expl_offset=self.expl_offset if self.expled else None,
4249 expl_tlen=self.expl_tlen if self.expled else None,
4250 expl_llen=self.expl_llen if self.expled else None,
4251 expl_vlen=self.expl_vlen if self.expled else None,
4252 expl_lenindef=self.expl_lenindef,
4255 for pp in self.pps_lenindef(decode_path):
4259 ObjectIdentifierState = namedtuple(
4260 "ObjectIdentifierState",
4261 BasicState._fields + ("value", "defines"),
4266 class ObjectIdentifier(Obj):
4267 """``OBJECT IDENTIFIER`` OID type
4269 >>> oid = ObjectIdentifier((1, 2, 3))
4270 OBJECT IDENTIFIER 1.2.3
4271 >>> oid == ObjectIdentifier("1.2.3")
4277 >>> oid + (4, 5) + ObjectIdentifier("1.7")
4278 OBJECT IDENTIFIER 1.2.3.4.5.1.7
4280 >>> str(ObjectIdentifier((3, 1)))
4281 Traceback (most recent call last):
4282 pyderasn.InvalidOID: unacceptable first arc value
4284 __slots__ = ("defines",)
4285 tag_default = tag_encode(6)
4286 asn1_type_name = "OBJECT IDENTIFIER"
4299 :param value: set the value. Either tuples of integers,
4300 string of "."-concatenated integers, or
4301 :py:class:`pyderasn.ObjectIdentifier` object
4302 :param defines: sequence of tuples. Each tuple has two elements.
4303 First one is relative to current one decode
4304 path, aiming to the field defined by that OID.
4305 Read about relative path in
4306 :py:func:`pyderasn.abs_decode_path`. Second
4307 tuple element is ``{OID: pyderasn.Obj()}``
4308 dictionary, mapping between current OID value
4309 and structure applied to defined field.
4311 .. seealso:: :ref:`definedby`
4313 :param bytes impl: override default tag with ``IMPLICIT`` one
4314 :param bytes expl: override default tag with ``EXPLICIT`` one
4315 :param default: set default value. Type same as in ``value``
4316 :param bool optional: is object ``OPTIONAL`` in sequence
4318 super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
4320 if value is not None:
4321 self._value = self._value_sanitize(value)
4322 if default is not None:
4323 default = self._value_sanitize(default)
4324 self.default = self.__class__(
4329 if self._value is None:
4330 self._value = default
4331 self.defines = defines
4333 def __add__(self, their):
4334 if their.__class__ == tuple:
4335 return self.__class__(self._value + array("L", their))
4336 if isinstance(their, self.__class__):
4337 return self.__class__(self._value + their._value)
4338 raise InvalidValueType((self.__class__, tuple))
4340 def _value_sanitize(self, value):
4341 if issubclass(value.__class__, ObjectIdentifier):
4343 if isinstance(value, string_types):
4345 value = array("L", (pureint(arc) for arc in value.split(".")))
4347 raise InvalidOID("unacceptable arcs values")
4348 if value.__class__ == tuple:
4350 value = array("L", value)
4351 except OverflowError as err:
4352 raise InvalidOID(repr(err))
4353 if value.__class__ is array:
4355 raise InvalidOID("less than 2 arcs")
4356 first_arc = value[0]
4357 if first_arc in (0, 1):
4358 if not (0 <= value[1] <= 39):
4359 raise InvalidOID("second arc is too wide")
4360 elif first_arc == 2:
4363 raise InvalidOID("unacceptable first arc value")
4364 if not all(arc >= 0 for arc in value):
4365 raise InvalidOID("negative arc value")
4367 raise InvalidValueType((self.__class__, str, tuple))
4371 return self._value is not None
4373 def __getstate__(self):
4374 return ObjectIdentifierState(
4391 def __setstate__(self, state):
4392 super(ObjectIdentifier, self).__setstate__(state)
4393 self._value = state.value
4394 self.defines = state.defines
4397 self._assert_ready()
4398 return iter(self._value)
4401 return ".".join(str(arc) for arc in self._value or ())
4404 self._assert_ready()
4405 return hash(b"".join((
4407 bytes(self._expl or b""),
4408 str(self._value).encode("ascii"),
4411 def __eq__(self, their):
4412 if their.__class__ == tuple:
4413 return self._value == array("L", their)
4414 if not issubclass(their.__class__, ObjectIdentifier):
4417 self.tag == their.tag and
4418 self._expl == their._expl and
4419 self._value == their._value
4422 def __lt__(self, their):
4423 return self._value < their._value
4434 return self.__class__(
4436 defines=self.defines if defines is None else defines,
4437 impl=self.tag if impl is None else impl,
4438 expl=self._expl if expl is None else expl,
4439 default=self.default if default is None else default,
4440 optional=self.optional if optional is None else optional,
4443 def _encode_octets(self):
4444 self._assert_ready()
4446 first_value = value[1]
4447 first_arc = value[0]
4450 elif first_arc == 1:
4452 elif first_arc == 2:
4454 else: # pragma: no cover
4455 raise RuntimeError("invalid arc is stored")
4456 octets = [zero_ended_encode(first_value)]
4457 for arc in value[2:]:
4458 octets.append(zero_ended_encode(arc))
4459 return b"".join(octets)
4462 v = self._encode_octets()
4463 return b"".join((self.tag, len_encode(len(v)), v))
4465 def _encode1st(self, state):
4466 l = len(self._encode_octets())
4467 return len(self.tag) + len_size(l) + l, state
4469 def _encode2nd(self, writer, state_iter):
4470 write_full(writer, self._encode())
4472 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4474 t, _, lv = tag_strip(tlv)
4475 except DecodeError as err:
4476 raise err.__class__(
4478 klass=self.__class__,
4479 decode_path=decode_path,
4484 klass=self.__class__,
4485 decode_path=decode_path,
4488 if tag_only: # pragma: no cover
4492 l, llen, v = len_decode(lv)
4493 except DecodeError as err:
4494 raise err.__class__(
4496 klass=self.__class__,
4497 decode_path=decode_path,
4501 raise NotEnoughData(
4502 "encoded length is longer than data",
4503 klass=self.__class__,
4504 decode_path=decode_path,
4508 raise NotEnoughData(
4510 klass=self.__class__,
4511 decode_path=decode_path,
4514 v, tail = v[:l], v[l:]
4521 octet = indexbytes(v, i)
4522 if i == 0 and octet == 0x80:
4523 if ctx.get("bered", False):
4527 "non normalized arc encoding",
4528 klass=self.__class__,
4529 decode_path=decode_path,
4532 arc = (arc << 7) | (octet & 0x7F)
4533 if octet & 0x80 == 0:
4536 except OverflowError:
4538 "too huge value for local unsigned long",
4539 klass=self.__class__,
4540 decode_path=decode_path,
4549 klass=self.__class__,
4550 decode_path=decode_path,
4554 second_arc = arcs[0]
4555 if 0 <= second_arc <= 39:
4557 elif 40 <= second_arc <= 79:
4563 obj = self.__class__(
4564 value=array("L", (first_arc, second_arc)) + arcs[1:],
4567 default=self.default,
4568 optional=self.optional,
4569 _decoded=(offset, llen, l),
4572 obj.ber_encoded = True
4573 yield decode_path, obj, tail
4576 return pp_console_row(next(self.pps()))
4578 def pps(self, decode_path=()):
4581 asn1_type_name=self.asn1_type_name,
4582 obj_name=self.__class__.__name__,
4583 decode_path=decode_path,
4584 value=str(self) if self.ready else None,
4585 optional=self.optional,
4586 default=self == self.default,
4587 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4588 expl=None if self._expl is None else tag_decode(self._expl),
4593 expl_offset=self.expl_offset if self.expled else None,
4594 expl_tlen=self.expl_tlen if self.expled else None,
4595 expl_llen=self.expl_llen if self.expled else None,
4596 expl_vlen=self.expl_vlen if self.expled else None,
4597 expl_lenindef=self.expl_lenindef,
4598 ber_encoded=self.ber_encoded,
4601 for pp in self.pps_lenindef(decode_path):
4605 class Enumerated(Integer):
4606 """``ENUMERATED`` integer type
4608 This type is identical to :py:class:`pyderasn.Integer`, but requires
4609 schema to be specified and does not accept values missing from it.
4612 tag_default = tag_encode(10)
4613 asn1_type_name = "ENUMERATED"
4624 bounds=None, # dummy argument, workability for Integer.decode
4626 super(Enumerated, self).__init__(
4627 value, bounds, impl, expl, default, optional, _specs, _decoded,
4629 if len(self.specs) == 0:
4630 raise ValueError("schema must be specified")
4632 def _value_sanitize(self, value):
4633 if isinstance(value, self.__class__):
4634 value = value._value
4635 elif isinstance(value, integer_types):
4636 for _value in itervalues(self.specs):
4641 "unknown integer value: %s" % value,
4642 klass=self.__class__,
4644 elif isinstance(value, string_types):
4645 value = self.specs.get(value)
4647 raise ObjUnknown("integer value: %s" % value)
4649 raise InvalidValueType((self.__class__, int, str))
4661 return self.__class__(
4663 impl=self.tag if impl is None else impl,
4664 expl=self._expl if expl is None else expl,
4665 default=self.default if default is None else default,
4666 optional=self.optional if optional is None else optional,
4671 def escape_control_unicode(c):
4672 if unicat(c)[0] == "C":
4673 c = repr(c).lstrip("u").strip("'")
4677 class CommonString(OctetString):
4678 """Common class for all strings
4680 Everything resembles :py:class:`pyderasn.OctetString`, except
4681 ability to deal with unicode text strings.
4683 >>> hexenc("привет мир".encode("utf-8"))
4684 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4685 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
4687 >>> s = UTF8String("привет мир")
4688 UTF8String UTF8String привет мир
4690 'привет мир'
4691 >>> hexenc(bytes(s))
4692 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4694 >>> PrintableString("привет мир")
4695 Traceback (most recent call last):
4696 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
4698 >>> BMPString("ада", bounds=(2, 2))
4699 Traceback (most recent call last):
4700 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
4701 >>> s = BMPString("ад", bounds=(2, 2))
4704 >>> hexenc(bytes(s))
4712 * - :py:class:`pyderasn.UTF8String`
4714 * - :py:class:`pyderasn.NumericString`
4716 * - :py:class:`pyderasn.PrintableString`
4718 * - :py:class:`pyderasn.TeletexString`
4720 * - :py:class:`pyderasn.T61String`
4722 * - :py:class:`pyderasn.VideotexString`
4724 * - :py:class:`pyderasn.IA5String`
4726 * - :py:class:`pyderasn.GraphicString`
4728 * - :py:class:`pyderasn.VisibleString`
4730 * - :py:class:`pyderasn.ISO646String`
4732 * - :py:class:`pyderasn.GeneralString`
4734 * - :py:class:`pyderasn.UniversalString`
4736 * - :py:class:`pyderasn.BMPString`
4741 def _value_sanitize(self, value):
4743 value_decoded = None
4744 if isinstance(value, self.__class__):
4745 value_raw = value._value
4746 elif value.__class__ == text_type:
4747 value_decoded = value
4748 elif value.__class__ == binary_type:
4751 raise InvalidValueType((self.__class__, text_type, binary_type))
4754 value_decoded.encode(self.encoding)
4755 if value_raw is None else value_raw
4758 value_raw.decode(self.encoding)
4759 if value_decoded is None else value_decoded
4761 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4762 raise DecodeError(str(err))
4763 if not self._bound_min <= len(value_decoded) <= self._bound_max:
4771 def __eq__(self, their):
4772 if their.__class__ == binary_type:
4773 return self._value == their
4774 if their.__class__ == text_type:
4775 return self._value == their.encode(self.encoding)
4776 if not isinstance(their, self.__class__):
4779 self._value == their._value and
4780 self.tag == their.tag and
4781 self._expl == their._expl
4784 def __unicode__(self):
4786 return self._value.decode(self.encoding)
4787 return text_type(self._value)
4790 return pp_console_row(next(self.pps(no_unicode=PY2)))
4792 def pps(self, decode_path=(), no_unicode=False):
4796 hexenc(bytes(self)) if no_unicode else
4797 "".join(escape_control_unicode(c) for c in self.__unicode__())
4801 asn1_type_name=self.asn1_type_name,
4802 obj_name=self.__class__.__name__,
4803 decode_path=decode_path,
4805 optional=self.optional,
4806 default=self == self.default,
4807 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4808 expl=None if self._expl is None else tag_decode(self._expl),
4813 expl_offset=self.expl_offset if self.expled else None,
4814 expl_tlen=self.expl_tlen if self.expled else None,
4815 expl_llen=self.expl_llen if self.expled else None,
4816 expl_vlen=self.expl_vlen if self.expled else None,
4817 expl_lenindef=self.expl_lenindef,
4818 ber_encoded=self.ber_encoded,
4821 for pp in self.pps_lenindef(decode_path):
4825 class UTF8String(CommonString):
4827 tag_default = tag_encode(12)
4829 asn1_type_name = "UTF8String"
4832 class AllowableCharsMixin(object):
4834 def allowable_chars(self):
4836 return self._allowable_chars
4837 return frozenset(six_unichr(c) for c in self._allowable_chars)
4840 class NumericString(AllowableCharsMixin, CommonString):
4843 Its value is properly sanitized: only ASCII digits with spaces can
4846 >>> NumericString().allowable_chars
4847 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4850 tag_default = tag_encode(18)
4852 asn1_type_name = "NumericString"
4853 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
4855 def _value_sanitize(self, value):
4856 value = super(NumericString, self)._value_sanitize(value)
4857 if not frozenset(value) <= self._allowable_chars:
4858 raise DecodeError("non-numeric value")
4862 PrintableStringState = namedtuple(
4863 "PrintableStringState",
4864 OctetStringState._fields + ("allowable_chars",),
4869 class PrintableString(AllowableCharsMixin, CommonString):
4872 Its value is properly sanitized: see X.680 41.4 table 10.
4874 >>> PrintableString().allowable_chars
4875 frozenset([' ', "'", ..., 'z'])
4876 >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4877 PrintableString PrintableString foo*bar
4878 >>> obj.allow_asterisk, obj.allow_ampersand
4882 tag_default = tag_encode(19)
4884 asn1_type_name = "PrintableString"
4885 _allowable_chars = frozenset(
4886 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4888 _asterisk = frozenset("*".encode("ascii"))
4889 _ampersand = frozenset("&".encode("ascii"))
4901 allow_asterisk=False,
4902 allow_ampersand=False,
4905 :param allow_asterisk: allow asterisk character
4906 :param allow_ampersand: allow ampersand character
4909 self._allowable_chars |= self._asterisk
4911 self._allowable_chars |= self._ampersand
4912 super(PrintableString, self).__init__(
4913 value, bounds, impl, expl, default, optional, _decoded, ctx,
4917 def allow_asterisk(self):
4918 """Is asterisk character allowed?
4920 return self._asterisk <= self._allowable_chars
4923 def allow_ampersand(self):
4924 """Is ampersand character allowed?
4926 return self._ampersand <= self._allowable_chars
4928 def _value_sanitize(self, value):
4929 value = super(PrintableString, self)._value_sanitize(value)
4930 if not frozenset(value) <= self._allowable_chars:
4931 raise DecodeError("non-printable value")
4934 def __getstate__(self):
4935 return PrintableStringState(
4936 *super(PrintableString, self).__getstate__(),
4937 **{"allowable_chars": self._allowable_chars}
4940 def __setstate__(self, state):
4941 super(PrintableString, self).__setstate__(state)
4942 self._allowable_chars = state.allowable_chars
4953 return self.__class__(
4956 (self._bound_min, self._bound_max)
4957 if bounds is None else bounds
4959 impl=self.tag if impl is None else impl,
4960 expl=self._expl if expl is None else expl,
4961 default=self.default if default is None else default,
4962 optional=self.optional if optional is None else optional,
4963 allow_asterisk=self.allow_asterisk,
4964 allow_ampersand=self.allow_ampersand,
4968 class TeletexString(CommonString):
4970 tag_default = tag_encode(20)
4972 asn1_type_name = "TeletexString"
4975 class T61String(TeletexString):
4977 asn1_type_name = "T61String"
4980 class VideotexString(CommonString):
4982 tag_default = tag_encode(21)
4983 encoding = "iso-8859-1"
4984 asn1_type_name = "VideotexString"
4987 class IA5String(CommonString):
4989 tag_default = tag_encode(22)
4991 asn1_type_name = "IA5"
4994 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4995 LEN_LEN_YYMMDDHHMMSSZ = len_encode(LEN_YYMMDDHHMMSSZ)
4996 LEN_YYMMDDHHMMSSZ_WITH_LEN = len(LEN_LEN_YYMMDDHHMMSSZ) + LEN_YYMMDDHHMMSSZ
4997 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4998 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4999 LEN_LEN_YYYYMMDDHHMMSSZ = len_encode(LEN_YYYYMMDDHHMMSSZ)
5002 class VisibleString(CommonString):
5004 tag_default = tag_encode(26)
5006 asn1_type_name = "VisibleString"
5009 UTCTimeState = namedtuple(
5011 OctetStringState._fields + ("ber_raw",),
5016 def str_to_time_fractions(value):
5018 year, v = (v // 10**10), (v % 10**10)
5019 month, v = (v // 10**8), (v % 10**8)
5020 day, v = (v // 10**6), (v % 10**6)
5021 hour, v = (v // 10**4), (v % 10**4)
5022 minute, second = (v // 100), (v % 100)
5023 return year, month, day, hour, minute, second
5026 class UTCTime(VisibleString):
5027 """``UTCTime`` datetime type
5029 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5030 UTCTime UTCTime 2017-09-30T22:07:50
5036 datetime.datetime(2017, 9, 30, 22, 7, 50)
5037 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
5038 datetime.datetime(1957, 9, 30, 22, 7, 50)
5040 If BER encoded value was met, then ``ber_raw`` attribute will hold
5041 its raw representation.
5045 Pay attention that UTCTime can not hold full year, so all years
5046 having < 50 years are treated as 20xx, 19xx otherwise, according
5047 to X.509 recommendation.
5051 No strict validation of UTC offsets are made, but very crude:
5053 * minutes are not exceeding 60
5054 * offset value is not exceeding 14 hours
5056 __slots__ = ("ber_raw",)
5057 tag_default = tag_encode(23)
5059 asn1_type_name = "UTCTime"
5060 evgen_mode_skip_value = False
5070 bounds=None, # dummy argument, workability for OctetString.decode
5074 :param value: set the value. Either datetime type, or
5075 :py:class:`pyderasn.UTCTime` object
5076 :param bytes impl: override default tag with ``IMPLICIT`` one
5077 :param bytes expl: override default tag with ``EXPLICIT`` one
5078 :param default: set default value. Type same as in ``value``
5079 :param bool optional: is object ``OPTIONAL`` in sequence
5081 super(UTCTime, self).__init__(
5082 None, None, impl, expl, None, optional, _decoded, ctx,
5086 if value is not None:
5087 self._value, self.ber_raw = self._value_sanitize(value, ctx)
5088 self.ber_encoded = self.ber_raw is not None
5089 if default is not None:
5090 default, _ = self._value_sanitize(default)
5091 self.default = self.__class__(
5096 if self._value is None:
5097 self._value = default
5099 self.optional = optional
5101 def _strptime_bered(self, value):
5102 year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
5105 raise ValueError("no timezone")
5106 year += 2000 if year < 50 else 1900
5107 decoded = datetime(year, month, day, hour, minute)
5109 if value[-1] == "Z":
5113 raise ValueError("invalid UTC offset")
5114 if value[-5] == "-":
5116 elif value[-5] == "+":
5119 raise ValueError("invalid UTC offset")
5120 v = pureint(value[-4:])
5121 offset, v = (60 * (v % 100)), v // 100
5123 raise ValueError("invalid UTC offset minutes")
5125 if offset > 14 * 3600:
5126 raise ValueError("too big UTC offset")
5130 return offset, decoded
5132 raise ValueError("invalid UTC offset seconds")
5133 seconds = pureint(value)
5135 raise ValueError("invalid seconds value")
5136 return offset, decoded + timedelta(seconds=seconds)
5138 def _strptime(self, value):
5139 # datetime.strptime's format: %y%m%d%H%M%SZ
5140 if len(value) != LEN_YYMMDDHHMMSSZ:
5141 raise ValueError("invalid UTCTime length")
5142 if value[-1] != "Z":
5143 raise ValueError("non UTC timezone")
5144 year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
5145 year += 2000 if year < 50 else 1900
5146 return datetime(year, month, day, hour, minute, second)
5148 def _dt_sanitize(self, value):
5149 if value.year < 1950 or value.year > 2049:
5150 raise ValueError("UTCTime can hold only 1950-2049 years")
5151 return value.replace(microsecond=0)
5153 def _value_sanitize(self, value, ctx=None):
5154 if value.__class__ == binary_type:
5156 value_decoded = value.decode("ascii")
5157 except (UnicodeEncodeError, UnicodeDecodeError) as err:
5158 raise DecodeError("invalid UTCTime encoding: %r" % err)
5161 return self._strptime(value_decoded), None
5162 except (TypeError, ValueError) as _err:
5164 if (ctx is not None) and ctx.get("bered", False):
5166 offset, _value = self._strptime_bered(value_decoded)
5167 _value = _value - timedelta(seconds=offset)
5168 return self._dt_sanitize(_value), value
5169 except (TypeError, ValueError, OverflowError) as _err:
5172 "invalid %s format: %r" % (self.asn1_type_name, err),
5173 klass=self.__class__,
5175 if isinstance(value, self.__class__):
5176 return value._value, None
5177 if value.__class__ == datetime:
5178 return self._dt_sanitize(value), None
5179 raise InvalidValueType((self.__class__, datetime))
5181 def _pp_value(self):
5183 value = self._value.isoformat()
5184 if self.ber_encoded:
5185 value += " (%s)" % self.ber_raw
5189 def __unicode__(self):
5191 value = self._value.isoformat()
5192 if self.ber_encoded:
5193 value += " (%s)" % self.ber_raw
5195 return text_type(self._pp_value())
5197 def __getstate__(self):
5198 return UTCTimeState(
5199 *super(UTCTime, self).__getstate__(),
5200 **{"ber_raw": self.ber_raw}
5203 def __setstate__(self, state):
5204 super(UTCTime, self).__setstate__(state)
5205 self.ber_raw = state.ber_raw
5207 def __bytes__(self):
5208 self._assert_ready()
5209 return self._encode_time()
5211 def __eq__(self, their):
5212 if their.__class__ == binary_type:
5213 return self._encode_time() == their
5214 if their.__class__ == datetime:
5215 return self.todatetime() == their
5216 if not isinstance(their, self.__class__):
5219 self._value == their._value and
5220 self.tag == their.tag and
5221 self._expl == their._expl
5224 def _encode_time(self):
5225 return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
5228 self._assert_ready()
5229 return b"".join((self.tag, LEN_LEN_YYMMDDHHMMSSZ, self._encode_time()))
5231 def _encode1st(self, state):
5232 return len(self.tag) + LEN_YYMMDDHHMMSSZ_WITH_LEN, state
5234 def _encode2nd(self, writer, state_iter):
5235 self._assert_ready()
5236 write_full(writer, self._encode())
5238 def _encode_cer(self, writer):
5239 write_full(writer, self._encode())
5241 def todatetime(self):
5245 return pp_console_row(next(self.pps()))
5247 def pps(self, decode_path=()):
5250 asn1_type_name=self.asn1_type_name,
5251 obj_name=self.__class__.__name__,
5252 decode_path=decode_path,
5253 value=self._pp_value(),
5254 optional=self.optional,
5255 default=self == self.default,
5256 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5257 expl=None if self._expl is None else tag_decode(self._expl),
5262 expl_offset=self.expl_offset if self.expled else None,
5263 expl_tlen=self.expl_tlen if self.expled else None,
5264 expl_llen=self.expl_llen if self.expled else None,
5265 expl_vlen=self.expl_vlen if self.expled else None,
5266 expl_lenindef=self.expl_lenindef,
5267 ber_encoded=self.ber_encoded,
5270 for pp in self.pps_lenindef(decode_path):
5274 class GeneralizedTime(UTCTime):
5275 """``GeneralizedTime`` datetime type
5277 This type is similar to :py:class:`pyderasn.UTCTime`.
5279 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5280 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
5282 '20170930220750.000123Z'
5283 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
5284 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
5288 Only microsecond fractions are supported in DER encoding.
5289 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
5290 higher precision values.
5294 BER encoded data can loss information (accuracy) during decoding
5295 because of float transformations.
5299 Local times (without explicit timezone specification) are treated
5300 as UTC one, no transformations are made.
5304 Zero year is unsupported.
5307 tag_default = tag_encode(24)
5308 asn1_type_name = "GeneralizedTime"
5310 def _dt_sanitize(self, value):
5313 def _strptime_bered(self, value):
5314 if len(value) < 4 + 3 * 2:
5315 raise ValueError("invalid GeneralizedTime")
5316 year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
5317 decoded = datetime(year, month, day, hour)
5318 offset, value = 0, value[10:]
5320 return offset, decoded
5321 if value[-1] == "Z":
5324 for char, sign in (("-", -1), ("+", 1)):
5325 idx = value.rfind(char)
5328 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
5329 v = pureint(offset_raw)
5330 if len(offset_raw) == 4:
5331 offset, v = (60 * (v % 100)), v // 100
5333 raise ValueError("invalid UTC offset minutes")
5334 elif len(offset_raw) == 2:
5337 raise ValueError("invalid UTC offset")
5339 if offset > 14 * 3600:
5340 raise ValueError("too big UTC offset")
5344 return offset, decoded
5345 if value[0] in DECIMAL_SIGNS:
5347 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
5350 raise ValueError("stripped minutes")
5351 decoded += timedelta(seconds=60 * pureint(value[:2]))
5354 return offset, decoded
5355 if value[0] in DECIMAL_SIGNS:
5357 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
5360 raise ValueError("stripped seconds")
5361 decoded += timedelta(seconds=pureint(value[:2]))
5364 return offset, decoded
5365 if value[0] not in DECIMAL_SIGNS:
5366 raise ValueError("invalid format after seconds")
5368 decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
5371 def _strptime(self, value):
5373 if l == LEN_YYYYMMDDHHMMSSZ:
5374 # datetime.strptime's format: %Y%m%d%H%M%SZ
5375 if value[-1] != "Z":
5376 raise ValueError("non UTC timezone")
5377 return datetime(*str_to_time_fractions(value[:-1]))
5378 if l >= LEN_YYYYMMDDHHMMSSDMZ:
5379 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
5380 if value[-1] != "Z":
5381 raise ValueError("non UTC timezone")
5382 if value[14] != ".":
5383 raise ValueError("no fractions separator")
5386 raise ValueError("trailing zero")
5389 raise ValueError("only microsecond fractions are supported")
5390 us = pureint(us + ("0" * (6 - us_len)))
5391 year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
5392 return datetime(year, month, day, hour, minute, second, us)
5393 raise ValueError("invalid GeneralizedTime length")
5395 def _encode_time(self):
5397 encoded = value.strftime("%Y%m%d%H%M%S")
5398 if value.microsecond > 0:
5399 encoded += (".%06d" % value.microsecond).rstrip("0")
5400 return (encoded + "Z").encode("ascii")
5403 self._assert_ready()
5405 if value.microsecond > 0:
5406 encoded = self._encode_time()
5407 return b"".join((self.tag, len_encode(len(encoded)), encoded))
5408 return b"".join((self.tag, LEN_LEN_YYYYMMDDHHMMSSZ, self._encode_time()))
5410 def _encode1st(self, state):
5411 self._assert_ready()
5412 vlen = len(self._encode_time())
5413 return len(self.tag) + len_size(vlen) + vlen, state
5415 def _encode2nd(self, writer, state_iter):
5416 write_full(writer, self._encode())
5419 class GraphicString(CommonString):
5421 tag_default = tag_encode(25)
5422 encoding = "iso-8859-1"
5423 asn1_type_name = "GraphicString"
5426 class ISO646String(VisibleString):
5428 asn1_type_name = "ISO646String"
5431 class GeneralString(CommonString):
5433 tag_default = tag_encode(27)
5434 encoding = "iso-8859-1"
5435 asn1_type_name = "GeneralString"
5438 class UniversalString(CommonString):
5440 tag_default = tag_encode(28)
5441 encoding = "utf-32-be"
5442 asn1_type_name = "UniversalString"
5445 class BMPString(CommonString):
5447 tag_default = tag_encode(30)
5448 encoding = "utf-16-be"
5449 asn1_type_name = "BMPString"
5452 ChoiceState = namedtuple(
5454 BasicState._fields + ("specs", "value",),
5460 """``CHOICE`` special type
5464 class GeneralName(Choice):
5466 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
5467 ("dNSName", IA5String(impl=tag_ctxp(2))),
5470 >>> gn = GeneralName()
5472 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
5473 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5474 >>> gn["dNSName"] = IA5String("bar.baz")
5475 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
5476 >>> gn["rfc822Name"]
5479 [2] IA5String IA5 bar.baz
5482 >>> gn.value == gn["dNSName"]
5485 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
5487 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
5488 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5490 __slots__ = ("specs",)
5492 asn1_type_name = "CHOICE"
5505 :param value: set the value. Either ``(choice, value)`` tuple, or
5506 :py:class:`pyderasn.Choice` object
5507 :param bytes impl: can not be set, do **not** use it
5508 :param bytes expl: override default tag with ``EXPLICIT`` one
5509 :param default: set default value. Type same as in ``value``
5510 :param bool optional: is object ``OPTIONAL`` in sequence
5512 if impl is not None:
5513 raise ValueError("no implicit tag allowed for CHOICE")
5514 super(Choice, self).__init__(None, expl, default, optional, _decoded)
5516 schema = getattr(self, "schema", ())
5517 if len(schema) == 0:
5518 raise ValueError("schema must be specified")
5520 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5523 if value is not None:
5524 self._value = self._value_sanitize(value)
5525 if default is not None:
5526 default_value = self._value_sanitize(default)
5527 default_obj = self.__class__(impl=self.tag, expl=self._expl)
5528 default_obj.specs = self.specs
5529 default_obj._value = default_value
5530 self.default = default_obj
5532 self._value = copy(default_obj._value)
5533 if self._expl is not None:
5534 tag_class, _, tag_num = tag_decode(self._expl)
5535 self._tag_order = (tag_class, tag_num)
5537 def _value_sanitize(self, value):
5538 if (value.__class__ == tuple) and len(value) == 2:
5540 spec = self.specs.get(choice)
5542 raise ObjUnknown(choice)
5543 if not isinstance(obj, spec.__class__):
5544 raise InvalidValueType((spec,))
5545 return (choice, spec(obj))
5546 if isinstance(value, self.__class__):
5548 raise InvalidValueType((self.__class__, tuple))
5552 return self._value is not None and self._value[1].ready
5556 return self.expl_lenindef or (
5557 (self._value is not None) and
5558 self._value[1].bered
5561 def __getstate__(self):
5579 def __setstate__(self, state):
5580 super(Choice, self).__setstate__(state)
5581 self.specs = state.specs
5582 self._value = state.value
5584 def __eq__(self, their):
5585 if (their.__class__ == tuple) and len(their) == 2:
5586 return self._value == their
5587 if not isinstance(their, self.__class__):
5590 self.specs == their.specs and
5591 self._value == their._value
5601 return self.__class__(
5604 expl=self._expl if expl is None else expl,
5605 default=self.default if default is None else default,
5606 optional=self.optional if optional is None else optional,
5611 """Name of the choice
5613 self._assert_ready()
5614 return self._value[0]
5618 """Value of underlying choice
5620 self._assert_ready()
5621 return self._value[1]
5624 def tag_order(self):
5625 self._assert_ready()
5626 return self._value[1].tag_order if self._tag_order is None else self._tag_order
5629 def tag_order_cer(self):
5630 return min(v.tag_order_cer for v in itervalues(self.specs))
5632 def __getitem__(self, key):
5633 if key not in self.specs:
5634 raise ObjUnknown(key)
5635 if self._value is None:
5637 choice, value = self._value
5642 def __setitem__(self, key, value):
5643 spec = self.specs.get(key)
5645 raise ObjUnknown(key)
5646 if not isinstance(value, spec.__class__):
5647 raise InvalidValueType((spec.__class__,))
5648 self._value = (key, spec(value))
5656 return self._value[1].decoded if self.ready else False
5659 self._assert_ready()
5660 return self._value[1].encode()
5662 def _encode1st(self, state):
5663 self._assert_ready()
5664 return self._value[1].encode1st(state)
5666 def _encode2nd(self, writer, state_iter):
5667 self._value[1].encode2nd(writer, state_iter)
5669 def _encode_cer(self, writer):
5670 self._assert_ready()
5671 self._value[1].encode_cer(writer)
5673 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5674 for choice, spec in iteritems(self.specs):
5675 sub_decode_path = decode_path + (choice,)
5681 decode_path=sub_decode_path,
5684 _ctx_immutable=False,
5691 klass=self.__class__,
5692 decode_path=decode_path,
5695 if tag_only: # pragma: no cover
5699 for _decode_path, value, tail in spec.decode_evgen(
5703 decode_path=sub_decode_path,
5705 _ctx_immutable=False,
5707 yield _decode_path, value, tail
5709 _, value, tail = next(spec.decode_evgen(
5713 decode_path=sub_decode_path,
5715 _ctx_immutable=False,
5718 obj = self.__class__(
5721 default=self.default,
5722 optional=self.optional,
5723 _decoded=(offset, 0, value.fulllen),
5725 obj._value = (choice, value)
5726 yield decode_path, obj, tail
5729 value = pp_console_row(next(self.pps()))
5731 value = "%s[%r]" % (value, self.value)
5734 def pps(self, decode_path=()):
5737 asn1_type_name=self.asn1_type_name,
5738 obj_name=self.__class__.__name__,
5739 decode_path=decode_path,
5740 value=self.choice if self.ready else None,
5741 optional=self.optional,
5742 default=self == self.default,
5743 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5744 expl=None if self._expl is None else tag_decode(self._expl),
5749 expl_lenindef=self.expl_lenindef,
5753 yield self.value.pps(decode_path=decode_path + (self.choice,))
5754 for pp in self.pps_lenindef(decode_path):
5758 class PrimitiveTypes(Choice):
5759 """Predefined ``CHOICE`` for all generic primitive types
5761 It could be useful for general decoding of some unspecified values:
5763 >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
5764 OCTET STRING 3 bytes 666f6f
5765 >>> PrimitiveTypes().decod(hexdec("0203123456")).value
5769 schema = tuple((klass.__name__, klass()) for klass in (
5793 AnyState = namedtuple(
5795 BasicState._fields + ("value", "defined"),
5801 """``ANY`` special type
5803 >>> Any(Integer(-123))
5804 ANY INTEGER -123 (0X:7B)
5805 >>> a = Any(OctetString(b"hello world").encode())
5806 ANY 040b68656c6c6f20776f726c64
5807 >>> hexenc(bytes(a))
5808 b'0x040x0bhello world'
5810 __slots__ = ("defined",)
5811 tag_default = tag_encode(0)
5812 asn1_type_name = "ANY"
5822 :param value: set the value. Either any kind of pyderasn's
5823 **ready** object, or bytes. Pay attention that
5824 **no** validation is performed if raw binary value
5825 is valid TLV, except just tag decoding
5826 :param bytes expl: override default tag with ``EXPLICIT`` one
5827 :param bool optional: is object ``OPTIONAL`` in sequence
5829 super(Any, self).__init__(None, expl, None, optional, _decoded)
5833 value = self._value_sanitize(value)
5835 if self._expl is None:
5836 if value.__class__ == binary_type:
5837 tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5839 tag_class, tag_num = value.tag_order
5841 tag_class, _, tag_num = tag_decode(self._expl)
5842 self._tag_order = (tag_class, tag_num)
5845 def _value_sanitize(self, value):
5846 if value.__class__ == binary_type:
5848 raise ValueError("Any value can not be empty")
5850 if isinstance(value, self.__class__):
5852 if not isinstance(value, Obj):
5853 raise InvalidValueType((self.__class__, Obj, binary_type))
5858 return self._value is not None
5861 def tag_order(self):
5862 self._assert_ready()
5863 return self._tag_order
5867 if self.expl_lenindef or self.lenindef:
5869 if self.defined is None:
5871 return self.defined[1].bered
5873 def __getstate__(self):
5891 def __setstate__(self, state):
5892 super(Any, self).__setstate__(state)
5893 self._value = state.value
5894 self.defined = state.defined
5896 def __eq__(self, their):
5897 if their.__class__ == binary_type:
5898 if self._value.__class__ == binary_type:
5899 return self._value == their
5900 return self._value.encode() == their
5901 if issubclass(their.__class__, Any):
5902 if self.ready and their.ready:
5903 return bytes(self) == bytes(their)
5904 return self.ready == their.ready
5913 return self.__class__(
5915 expl=self._expl if expl is None else expl,
5916 optional=self.optional if optional is None else optional,
5919 def __bytes__(self):
5920 self._assert_ready()
5922 if value.__class__ == binary_type:
5924 return self._value.encode()
5931 self._assert_ready()
5933 if value.__class__ == binary_type:
5935 return value.encode()
5937 def _encode1st(self, state):
5938 self._assert_ready()
5940 if value.__class__ == binary_type:
5941 return len(value), state
5942 return value.encode1st(state)
5944 def _encode2nd(self, writer, state_iter):
5946 if value.__class__ == binary_type:
5947 write_full(writer, value)
5949 value.encode2nd(writer, state_iter)
5951 def _encode_cer(self, writer):
5952 self._assert_ready()
5954 if value.__class__ == binary_type:
5955 write_full(writer, value)
5957 value.encode_cer(writer)
5959 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5961 t, tlen, lv = tag_strip(tlv)
5962 except DecodeError as err:
5963 raise err.__class__(
5965 klass=self.__class__,
5966 decode_path=decode_path,
5970 l, llen, v = len_decode(lv)
5971 except LenIndefForm as err:
5972 if not ctx.get("bered", False):
5973 raise err.__class__(
5975 klass=self.__class__,
5976 decode_path=decode_path,
5979 llen, vlen, v = 1, 0, lv[1:]
5980 sub_offset = offset + tlen + llen
5982 while v[:EOC_LEN].tobytes() != EOC:
5983 chunk, v = Any().decode(
5986 decode_path=decode_path + (str(chunk_i),),
5989 _ctx_immutable=False,
5991 vlen += chunk.tlvlen
5992 sub_offset += chunk.tlvlen
5994 tlvlen = tlen + llen + vlen + EOC_LEN
5995 obj = self.__class__(
5996 value=None if evgen_mode else tlv[:tlvlen].tobytes(),
5998 optional=self.optional,
5999 _decoded=(offset, 0, tlvlen),
6002 obj.tag = t.tobytes()
6003 yield decode_path, obj, v[EOC_LEN:]
6005 except DecodeError as err:
6006 raise err.__class__(
6008 klass=self.__class__,
6009 decode_path=decode_path,
6013 raise NotEnoughData(
6014 "encoded length is longer than data",
6015 klass=self.__class__,
6016 decode_path=decode_path,
6019 tlvlen = tlen + llen + l
6020 v, tail = tlv[:tlvlen], v[l:]
6021 obj = self.__class__(
6022 value=None if evgen_mode else v.tobytes(),
6024 optional=self.optional,
6025 _decoded=(offset, 0, tlvlen),
6027 obj.tag = t.tobytes()
6028 yield decode_path, obj, tail
6031 return pp_console_row(next(self.pps()))
6033 def pps(self, decode_path=()):
6037 elif value.__class__ == binary_type:
6043 asn1_type_name=self.asn1_type_name,
6044 obj_name=self.__class__.__name__,
6045 decode_path=decode_path,
6047 blob=self._value if self._value.__class__ == binary_type else None,
6048 optional=self.optional,
6049 default=self == self.default,
6050 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6051 expl=None if self._expl is None else tag_decode(self._expl),
6056 expl_offset=self.expl_offset if self.expled else None,
6057 expl_tlen=self.expl_tlen if self.expled else None,
6058 expl_llen=self.expl_llen if self.expled else None,
6059 expl_vlen=self.expl_vlen if self.expled else None,
6060 expl_lenindef=self.expl_lenindef,
6061 lenindef=self.lenindef,
6064 defined_by, defined = self.defined or (None, None)
6065 if defined_by is not None:
6067 decode_path=decode_path + (DecodePathDefBy(defined_by),)
6069 for pp in self.pps_lenindef(decode_path):
6073 ########################################################################
6074 # ASN.1 constructed types
6075 ########################################################################
6077 def abs_decode_path(decode_path, rel_path):
6078 """Create an absolute decode path from current and relative ones
6080 :param decode_path: current decode path, starting point. Tuple of strings
6081 :param rel_path: relative path to ``decode_path``. Tuple of strings.
6082 If first tuple's element is "/", then treat it as
6083 an absolute path, ignoring ``decode_path`` as
6084 starting point. Also this tuple can contain ".."
6085 elements, stripping the leading element from
6088 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
6089 ("foo", "bar", "baz", "whatever")
6090 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
6092 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
6095 if rel_path[0] == "/":
6097 if rel_path[0] == "..":
6098 return abs_decode_path(decode_path[:-1], rel_path[1:])
6099 return decode_path + rel_path
6102 SequenceState = namedtuple(
6104 BasicState._fields + ("specs", "value",),
6109 class SequenceEncode1stMixing(object):
6110 def _encode1st(self, state):
6112 idx = len(state) - 1
6114 for v in self._values_for_encoding():
6115 l, _ = v.encode1st(state)
6118 return len(self.tag) + len_size(vlen) + vlen, state
6121 class Sequence(SequenceEncode1stMixing, Obj):
6122 """``SEQUENCE`` structure type
6124 You have to make specification of sequence::
6126 class Extension(Sequence):
6128 ("extnID", ObjectIdentifier()),
6129 ("critical", Boolean(default=False)),
6130 ("extnValue", OctetString()),
6133 Then, you can work with it as with dictionary.
6135 >>> ext = Extension()
6136 >>> Extension().specs
6138 ('extnID', OBJECT IDENTIFIER),
6139 ('critical', BOOLEAN False OPTIONAL DEFAULT),
6140 ('extnValue', OCTET STRING),
6142 >>> ext["extnID"] = "1.2.3"
6143 Traceback (most recent call last):
6144 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
6145 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
6147 You can determine if sequence is ready to be encoded:
6152 Traceback (most recent call last):
6153 pyderasn.ObjNotReady: object is not ready: extnValue
6154 >>> ext["extnValue"] = OctetString(b"foobar")
6158 Value you want to assign, must have the same **type** as in
6159 corresponding specification, but it can have different tags,
6160 optional/default attributes -- they will be taken from specification
6163 class TBSCertificate(Sequence):
6165 ("version", Version(expl=tag_ctxc(0), default="v1")),
6168 >>> tbs = TBSCertificate()
6169 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
6171 Assign ``None`` to remove value from sequence.
6173 You can set values in Sequence during its initialization:
6175 >>> AlgorithmIdentifier((
6176 ("algorithm", ObjectIdentifier("1.2.3")),
6177 ("parameters", Any(Null()))
6179 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
6181 You can determine if value exists/set in the sequence and take its value:
6183 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
6186 OBJECT IDENTIFIER 1.2.3
6188 But pay attention that if value has default, then it won't be (not
6189 in) in the sequence (because ``DEFAULT`` must not be encoded in
6190 DER), but you can read its value:
6192 >>> "critical" in ext, ext["critical"]
6193 (False, BOOLEAN False)
6194 >>> ext["critical"] = Boolean(True)
6195 >>> "critical" in ext, ext["critical"]
6196 (True, BOOLEAN True)
6198 All defaulted values are always optional.
6200 .. _allow_default_values_ctx:
6202 DER prohibits default value encoding and will raise an error if
6203 default value is unexpectedly met during decode.
6204 If :ref:`bered <bered_ctx>` context option is set, then no error
6205 will be raised, but ``bered`` attribute set. You can disable strict
6206 defaulted values existence validation by setting
6207 ``"allow_default_values": True`` :ref:`context <ctx>` option.
6209 All values with DEFAULT specified are decoded atomically in
6210 :ref:`evgen mode <evgen_mode>`. If DEFAULT value is some kind of
6211 SEQUENCE, then it will be yielded as a single element, not
6212 disassembled. That is required for DEFAULT existence check.
6214 Two sequences are equal if they have equal specification (schema),
6215 implicit/explicit tagging and the same values.
6217 __slots__ = ("specs",)
6218 tag_default = tag_encode(form=TagFormConstructed, num=16)
6219 asn1_type_name = "SEQUENCE"
6231 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
6233 schema = getattr(self, "schema", ())
6235 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
6238 if value is not None:
6239 if issubclass(value.__class__, Sequence):
6240 self._value = value._value
6241 elif hasattr(value, "__iter__"):
6242 for seq_key, seq_value in value:
6243 self[seq_key] = seq_value
6245 raise InvalidValueType((Sequence,))
6246 if default is not None:
6247 if not issubclass(default.__class__, Sequence):
6248 raise InvalidValueType((Sequence,))
6249 default_value = default._value
6250 default_obj = self.__class__(impl=self.tag, expl=self._expl)
6251 default_obj.specs = self.specs
6252 default_obj._value = default_value
6253 self.default = default_obj
6255 self._value = copy(default_obj._value)
6259 for name, spec in iteritems(self.specs):
6260 value = self._value.get(name)
6271 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6273 return any(value.bered for value in itervalues(self._value))
6275 def __getstate__(self):
6276 return SequenceState(
6290 {k: copy(v) for k, v in iteritems(self._value)},
6293 def __setstate__(self, state):
6294 super(Sequence, self).__setstate__(state)
6295 self.specs = state.specs
6296 self._value = state.value
6298 def __eq__(self, their):
6299 if not isinstance(their, self.__class__):
6302 self.specs == their.specs and
6303 self.tag == their.tag and
6304 self._expl == their._expl and
6305 self._value == their._value
6316 return self.__class__(
6319 impl=self.tag if impl is None else impl,
6320 expl=self._expl if expl is None else expl,
6321 default=self.default if default is None else default,
6322 optional=self.optional if optional is None else optional,
6325 def __contains__(self, key):
6326 return key in self._value
6328 def __setitem__(self, key, value):
6329 spec = self.specs.get(key)
6331 raise ObjUnknown(key)
6333 self._value.pop(key, None)
6335 if not isinstance(value, spec.__class__):
6336 raise InvalidValueType((spec.__class__,))
6337 value = spec(value=value)
6338 if spec.default is not None and value == spec.default:
6339 self._value.pop(key, None)
6341 self._value[key] = value
6343 def __getitem__(self, key):
6344 value = self._value.get(key)
6345 if value is not None:
6347 spec = self.specs.get(key)
6349 raise ObjUnknown(key)
6350 if spec.default is not None:
6354 def _values_for_encoding(self):
6355 for name, spec in iteritems(self.specs):
6356 value = self._value.get(name)
6360 raise ObjNotReady(name)
6364 v = b"".join(v.encode() for v in self._values_for_encoding())
6365 return b"".join((self.tag, len_encode(len(v)), v))
6367 def _encode2nd(self, writer, state_iter):
6368 write_full(writer, self.tag + len_encode(next(state_iter)))
6369 for v in self._values_for_encoding():
6370 v.encode2nd(writer, state_iter)
6372 def _encode_cer(self, writer):
6373 write_full(writer, self.tag + LENINDEF)
6374 for v in self._values_for_encoding():
6375 v.encode_cer(writer)
6376 write_full(writer, EOC)
6378 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6380 t, tlen, lv = tag_strip(tlv)
6381 except DecodeError as err:
6382 raise err.__class__(
6384 klass=self.__class__,
6385 decode_path=decode_path,
6390 klass=self.__class__,
6391 decode_path=decode_path,
6394 if tag_only: # pragma: no cover
6398 ctx_bered = ctx.get("bered", False)
6400 l, llen, v = len_decode(lv)
6401 except LenIndefForm as err:
6403 raise err.__class__(
6405 klass=self.__class__,
6406 decode_path=decode_path,
6409 l, llen, v = 0, 1, lv[1:]
6411 except DecodeError as err:
6412 raise err.__class__(
6414 klass=self.__class__,
6415 decode_path=decode_path,
6419 raise NotEnoughData(
6420 "encoded length is longer than data",
6421 klass=self.__class__,
6422 decode_path=decode_path,
6426 v, tail = v[:l], v[l:]
6428 sub_offset = offset + tlen + llen
6431 ctx_allow_default_values = ctx.get("allow_default_values", False)
6432 for name, spec in iteritems(self.specs):
6433 if spec.optional and (
6434 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
6438 spec_defaulted = spec.default is not None
6439 sub_decode_path = decode_path + (name,)
6441 if evgen_mode and not spec_defaulted:
6442 for _decode_path, value, v_tail in spec.decode_evgen(
6446 decode_path=sub_decode_path,
6448 _ctx_immutable=False,
6450 yield _decode_path, value, v_tail
6452 _, value, v_tail = next(spec.decode_evgen(
6456 decode_path=sub_decode_path,
6458 _ctx_immutable=False,
6461 except TagMismatch as err:
6462 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
6466 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
6467 if not evgen_mode and defined is not None:
6468 defined_by, defined_spec = defined
6469 if issubclass(value.__class__, SequenceOf):
6470 for i, _value in enumerate(value):
6471 sub_sub_decode_path = sub_decode_path + (
6473 DecodePathDefBy(defined_by),
6475 defined_value, defined_tail = defined_spec.decode(
6476 memoryview(bytes(_value)),
6478 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6479 if value.expled else (value.tlen + value.llen)
6482 decode_path=sub_sub_decode_path,
6484 _ctx_immutable=False,
6486 if len(defined_tail) > 0:
6489 klass=self.__class__,
6490 decode_path=sub_sub_decode_path,
6493 _value.defined = (defined_by, defined_value)
6495 defined_value, defined_tail = defined_spec.decode(
6496 memoryview(bytes(value)),
6498 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6499 if value.expled else (value.tlen + value.llen)
6502 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6504 _ctx_immutable=False,
6506 if len(defined_tail) > 0:
6509 klass=self.__class__,
6510 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6513 value.defined = (defined_by, defined_value)
6515 value_len = value.fulllen
6517 sub_offset += value_len
6521 yield sub_decode_path, value, v_tail
6522 if value == spec.default:
6523 if ctx_bered or ctx_allow_default_values:
6527 "DEFAULT value met",
6528 klass=self.__class__,
6529 decode_path=sub_decode_path,
6533 values[name] = value
6534 spec_defines = getattr(spec, "defines", ())
6535 if len(spec_defines) == 0:
6536 defines_by_path = ctx.get("defines_by_path", ())
6537 if len(defines_by_path) > 0:
6538 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
6539 if spec_defines is not None and len(spec_defines) > 0:
6540 for rel_path, schema in spec_defines:
6541 defined = schema.get(value, None)
6542 if defined is not None:
6543 ctx.setdefault("_defines", []).append((
6544 abs_decode_path(sub_decode_path[:-1], rel_path),
6548 if v[:EOC_LEN].tobytes() != EOC:
6551 klass=self.__class__,
6552 decode_path=decode_path,
6560 klass=self.__class__,
6561 decode_path=decode_path,
6564 obj = self.__class__(
6568 default=self.default,
6569 optional=self.optional,
6570 _decoded=(offset, llen, vlen),
6573 obj.lenindef = lenindef
6574 obj.ber_encoded = ber_encoded
6575 yield decode_path, obj, tail
6578 value = pp_console_row(next(self.pps()))
6580 for name in self.specs:
6581 _value = self._value.get(name)
6584 cols.append("%s: %s" % (name, repr(_value)))
6585 return "%s[%s]" % (value, "; ".join(cols))
6587 def pps(self, decode_path=()):
6590 asn1_type_name=self.asn1_type_name,
6591 obj_name=self.__class__.__name__,
6592 decode_path=decode_path,
6593 optional=self.optional,
6594 default=self == self.default,
6595 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6596 expl=None if self._expl is None else tag_decode(self._expl),
6601 expl_offset=self.expl_offset if self.expled else None,
6602 expl_tlen=self.expl_tlen if self.expled else None,
6603 expl_llen=self.expl_llen if self.expled else None,
6604 expl_vlen=self.expl_vlen if self.expled else None,
6605 expl_lenindef=self.expl_lenindef,
6606 lenindef=self.lenindef,
6607 ber_encoded=self.ber_encoded,
6610 for name in self.specs:
6611 value = self._value.get(name)
6614 yield value.pps(decode_path=decode_path + (name,))
6615 for pp in self.pps_lenindef(decode_path):
6619 class Set(Sequence, SequenceEncode1stMixing):
6620 """``SET`` structure type
6622 Its usage is identical to :py:class:`pyderasn.Sequence`.
6624 .. _allow_unordered_set_ctx:
6626 DER prohibits unordered values encoding and will raise an error
6627 during decode. If :ref:`bered <bered_ctx>` context option is set,
6628 then no error will occur. Also you can disable strict values
6629 ordering check by setting ``"allow_unordered_set": True``
6630 :ref:`context <ctx>` option.
6633 tag_default = tag_encode(form=TagFormConstructed, num=17)
6634 asn1_type_name = "SET"
6636 def _values_for_encoding(self):
6638 super(Set, self)._values_for_encoding(),
6639 key=attrgetter("tag_order"),
6642 def _encode_cer(self, writer):
6643 write_full(writer, self.tag + LENINDEF)
6645 super(Set, self)._values_for_encoding(),
6646 key=attrgetter("tag_order_cer"),
6648 v.encode_cer(writer)
6649 write_full(writer, EOC)
6651 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6653 t, tlen, lv = tag_strip(tlv)
6654 except DecodeError as err:
6655 raise err.__class__(
6657 klass=self.__class__,
6658 decode_path=decode_path,
6663 klass=self.__class__,
6664 decode_path=decode_path,
6671 ctx_bered = ctx.get("bered", False)
6673 l, llen, v = len_decode(lv)
6674 except LenIndefForm as err:
6676 raise err.__class__(
6678 klass=self.__class__,
6679 decode_path=decode_path,
6682 l, llen, v = 0, 1, lv[1:]
6684 except DecodeError as err:
6685 raise err.__class__(
6687 klass=self.__class__,
6688 decode_path=decode_path,
6692 raise NotEnoughData(
6693 "encoded length is longer than data",
6694 klass=self.__class__,
6698 v, tail = v[:l], v[l:]
6700 sub_offset = offset + tlen + llen
6703 ctx_allow_default_values = ctx.get("allow_default_values", False)
6704 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6705 tag_order_prev = (0, 0)
6706 _specs_items = copy(self.specs)
6709 if lenindef and v[:EOC_LEN].tobytes() == EOC:
6711 for name, spec in iteritems(_specs_items):
6712 sub_decode_path = decode_path + (name,)
6718 decode_path=sub_decode_path,
6721 _ctx_immutable=False,
6728 klass=self.__class__,
6729 decode_path=decode_path,
6732 spec_defaulted = spec.default is not None
6733 if evgen_mode and not spec_defaulted:
6734 for _decode_path, value, v_tail in spec.decode_evgen(
6738 decode_path=sub_decode_path,
6740 _ctx_immutable=False,
6742 yield _decode_path, value, v_tail
6744 _, value, v_tail = next(spec.decode_evgen(
6748 decode_path=sub_decode_path,
6750 _ctx_immutable=False,
6753 value_tag_order = value.tag_order
6754 value_len = value.fulllen
6755 if tag_order_prev >= value_tag_order:
6756 if ctx_bered or ctx_allow_unordered_set:
6760 "unordered " + self.asn1_type_name,
6761 klass=self.__class__,
6762 decode_path=sub_decode_path,
6767 yield sub_decode_path, value, v_tail
6768 if value != spec.default:
6770 elif ctx_bered or ctx_allow_default_values:
6774 "DEFAULT value met",
6775 klass=self.__class__,
6776 decode_path=sub_decode_path,
6779 values[name] = value
6780 del _specs_items[name]
6781 tag_order_prev = value_tag_order
6782 sub_offset += value_len
6786 obj = self.__class__(
6790 default=self.default,
6791 optional=self.optional,
6792 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6795 if v[:EOC_LEN].tobytes() != EOC:
6798 klass=self.__class__,
6799 decode_path=decode_path,
6804 for name, spec in iteritems(self.specs):
6805 if name not in values and not spec.optional:
6807 "%s value is not ready" % name,
6808 klass=self.__class__,
6809 decode_path=decode_path,
6814 obj.ber_encoded = ber_encoded
6815 yield decode_path, obj, tail
6818 SequenceOfState = namedtuple(
6820 BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
6825 class SequenceOf(SequenceEncode1stMixing, Obj):
6826 """``SEQUENCE OF`` sequence type
6828 For that kind of type you must specify the object it will carry on
6829 (bounds are for example here, not required)::
6831 class Ints(SequenceOf):
6836 >>> ints.append(Integer(123))
6837 >>> ints.append(Integer(234))
6839 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
6840 >>> [int(i) for i in ints]
6842 >>> ints.append(Integer(345))
6843 Traceback (most recent call last):
6844 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
6847 >>> ints[1] = Integer(345)
6849 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
6851 You can initialize sequence with preinitialized values:
6853 >>> ints = Ints([Integer(123), Integer(234)])
6855 Also you can use iterator as a value:
6857 >>> ints = Ints(iter(Integer(i) for i in range(1000000)))
6859 And it won't be iterated until encoding process. Pay attention that
6860 bounds and required schema checks are done only during the encoding
6861 process in that case! After encode was called, then value is zeroed
6862 back to empty list and you have to set it again. That mode is useful
6863 mainly with CER encoding mode, where all objects from the iterable
6864 will be streamed to the buffer, without copying all of them to
6867 __slots__ = ("spec", "_bound_min", "_bound_max")
6868 tag_default = tag_encode(form=TagFormConstructed, num=16)
6869 asn1_type_name = "SEQUENCE OF"
6882 super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
6884 schema = getattr(self, "schema", None)
6886 raise ValueError("schema must be specified")
6888 self._bound_min, self._bound_max = getattr(
6892 ) if bounds is None else bounds
6894 if value is not None:
6895 self._value = self._value_sanitize(value)
6896 if default is not None:
6897 default_value = self._value_sanitize(default)
6898 default_obj = self.__class__(
6903 default_obj._value = default_value
6904 self.default = default_obj
6906 self._value = copy(default_obj._value)
6908 def _value_sanitize(self, value):
6910 if issubclass(value.__class__, SequenceOf):
6911 value = value._value
6912 elif hasattr(value, NEXT_ATTR_NAME):
6914 elif hasattr(value, "__iter__"):
6917 raise InvalidValueType((self.__class__, iter, "iterator"))
6919 if not self._bound_min <= len(value) <= self._bound_max:
6920 raise BoundsError(self._bound_min, len(value), self._bound_max)
6921 class_expected = self.spec.__class__
6923 if not isinstance(v, class_expected):
6924 raise InvalidValueType((class_expected,))
6929 if hasattr(self._value, NEXT_ATTR_NAME):
6931 if self._bound_min > 0 and len(self._value) == 0:
6933 return all(v.ready for v in self._value)
6937 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6939 return any(v.bered for v in self._value)
6941 def __getstate__(self):
6942 if hasattr(self._value, NEXT_ATTR_NAME):
6943 raise ValueError("can not pickle SequenceOf with iterator")
6944 return SequenceOfState(
6958 [copy(v) for v in self._value],
6963 def __setstate__(self, state):
6964 super(SequenceOf, self).__setstate__(state)
6965 self.spec = state.spec
6966 self._value = state.value
6967 self._bound_min = state.bound_min
6968 self._bound_max = state.bound_max
6970 def __eq__(self, their):
6971 if isinstance(their, self.__class__):
6973 self.spec == their.spec and
6974 self.tag == their.tag and
6975 self._expl == their._expl and
6976 self._value == their._value
6978 if hasattr(their, "__iter__"):
6979 return self._value == list(their)
6991 return self.__class__(
6995 (self._bound_min, self._bound_max)
6996 if bounds is None else bounds
6998 impl=self.tag if impl is None else impl,
6999 expl=self._expl if expl is None else expl,
7000 default=self.default if default is None else default,
7001 optional=self.optional if optional is None else optional,
7004 def __contains__(self, key):
7005 return key in self._value
7007 def append(self, value):
7008 if not isinstance(value, self.spec.__class__):
7009 raise InvalidValueType((self.spec.__class__,))
7010 if len(self._value) + 1 > self._bound_max:
7013 len(self._value) + 1,
7016 self._value.append(value)
7019 return iter(self._value)
7022 return len(self._value)
7024 def __setitem__(self, key, value):
7025 if not isinstance(value, self.spec.__class__):
7026 raise InvalidValueType((self.spec.__class__,))
7027 self._value[key] = self.spec(value=value)
7029 def __getitem__(self, key):
7030 return self._value[key]
7032 def _values_for_encoding(self):
7033 return iter(self._value)
7036 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7039 values_append = values.append
7040 class_expected = self.spec.__class__
7041 values_for_encoding = self._values_for_encoding()
7043 for v in values_for_encoding:
7044 if not isinstance(v, class_expected):
7045 raise InvalidValueType((class_expected,))
7046 values_append(v.encode())
7047 if not self._bound_min <= len(values) <= self._bound_max:
7048 raise BoundsError(self._bound_min, len(values), self._bound_max)
7049 value = b"".join(values)
7051 value = b"".join(v.encode() for v in self._values_for_encoding())
7052 return b"".join((self.tag, len_encode(len(value)), value))
7054 def _encode1st(self, state):
7055 state = super(SequenceOf, self)._encode1st(state)
7056 if hasattr(self._value, NEXT_ATTR_NAME):
7060 def _encode2nd(self, writer, state_iter):
7061 write_full(writer, self.tag + len_encode(next(state_iter)))
7062 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7065 class_expected = self.spec.__class__
7066 values_for_encoding = self._values_for_encoding()
7068 for v in values_for_encoding:
7069 if not isinstance(v, class_expected):
7070 raise InvalidValueType((class_expected,))
7071 v.encode2nd(writer, state_iter)
7073 if not self._bound_min <= values_count <= self._bound_max:
7074 raise BoundsError(self._bound_min, values_count, self._bound_max)
7076 for v in self._values_for_encoding():
7077 v.encode2nd(writer, state_iter)
7079 def _encode_cer(self, writer):
7080 write_full(writer, self.tag + LENINDEF)
7081 iterator = hasattr(self._value, NEXT_ATTR_NAME)
7083 class_expected = self.spec.__class__
7085 values_for_encoding = self._values_for_encoding()
7087 for v in values_for_encoding:
7088 if not isinstance(v, class_expected):
7089 raise InvalidValueType((class_expected,))
7090 v.encode_cer(writer)
7092 if not self._bound_min <= values_count <= self._bound_max:
7093 raise BoundsError(self._bound_min, values_count, self._bound_max)
7095 for v in self._values_for_encoding():
7096 v.encode_cer(writer)
7097 write_full(writer, EOC)
7107 ordering_check=False,
7110 t, tlen, lv = tag_strip(tlv)
7111 except DecodeError as err:
7112 raise err.__class__(
7114 klass=self.__class__,
7115 decode_path=decode_path,
7120 klass=self.__class__,
7121 decode_path=decode_path,
7128 ctx_bered = ctx.get("bered", False)
7130 l, llen, v = len_decode(lv)
7131 except LenIndefForm as err:
7133 raise err.__class__(
7135 klass=self.__class__,
7136 decode_path=decode_path,
7139 l, llen, v = 0, 1, lv[1:]
7141 except DecodeError as err:
7142 raise err.__class__(
7144 klass=self.__class__,
7145 decode_path=decode_path,
7149 raise NotEnoughData(
7150 "encoded length is longer than data",
7151 klass=self.__class__,
7152 decode_path=decode_path,
7156 v, tail = v[:l], v[l:]
7158 sub_offset = offset + tlen + llen
7161 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
7162 value_prev = memoryview(v[:0])
7166 if lenindef and v[:EOC_LEN].tobytes() == EOC:
7168 sub_decode_path = decode_path + (str(_value_count),)
7170 for _decode_path, value, v_tail in spec.decode_evgen(
7174 decode_path=sub_decode_path,
7176 _ctx_immutable=False,
7178 yield _decode_path, value, v_tail
7180 _, value, v_tail = next(spec.decode_evgen(
7184 decode_path=sub_decode_path,
7186 _ctx_immutable=False,
7189 value_len = value.fulllen
7191 if value_prev.tobytes() > v[:value_len].tobytes():
7192 if ctx_bered or ctx_allow_unordered_set:
7196 "unordered " + self.asn1_type_name,
7197 klass=self.__class__,
7198 decode_path=sub_decode_path,
7201 value_prev = v[:value_len]
7204 _value.append(value)
7205 sub_offset += value_len
7208 if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
7210 msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
7211 klass=self.__class__,
7212 decode_path=decode_path,
7216 obj = self.__class__(
7217 value=None if evgen_mode else _value,
7219 bounds=(self._bound_min, self._bound_max),
7222 default=self.default,
7223 optional=self.optional,
7224 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
7226 except BoundsError as err:
7229 klass=self.__class__,
7230 decode_path=decode_path,
7234 if v[:EOC_LEN].tobytes() != EOC:
7237 klass=self.__class__,
7238 decode_path=decode_path,
7243 obj.ber_encoded = ber_encoded
7244 yield decode_path, obj, tail
7248 pp_console_row(next(self.pps())),
7249 ", ".join(repr(v) for v in self._value),
7252 def pps(self, decode_path=()):
7255 asn1_type_name=self.asn1_type_name,
7256 obj_name=self.__class__.__name__,
7257 decode_path=decode_path,
7258 optional=self.optional,
7259 default=self == self.default,
7260 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
7261 expl=None if self._expl is None else tag_decode(self._expl),
7266 expl_offset=self.expl_offset if self.expled else None,
7267 expl_tlen=self.expl_tlen if self.expled else None,
7268 expl_llen=self.expl_llen if self.expled else None,
7269 expl_vlen=self.expl_vlen if self.expled else None,
7270 expl_lenindef=self.expl_lenindef,
7271 lenindef=self.lenindef,
7272 ber_encoded=self.ber_encoded,
7275 for i, value in enumerate(self._value):
7276 yield value.pps(decode_path=decode_path + (str(i),))
7277 for pp in self.pps_lenindef(decode_path):
7281 class SetOf(SequenceOf):
7282 """``SET OF`` sequence type
7284 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
7287 tag_default = tag_encode(form=TagFormConstructed, num=17)
7288 asn1_type_name = "SET OF"
7290 def _value_sanitize(self, value):
7291 value = super(SetOf, self)._value_sanitize(value)
7292 if hasattr(value, NEXT_ATTR_NAME):
7294 "SetOf does not support iterator values, as no sense in them"
7299 v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
7300 return b"".join((self.tag, len_encode(len(v)), v))
7302 def _encode2nd(self, writer, state_iter):
7303 write_full(writer, self.tag + len_encode(next(state_iter)))
7305 for v in self._values_for_encoding():
7307 v.encode2nd(buf.write, state_iter)
7308 values.append(buf.getvalue())
7311 write_full(writer, v)
7313 def _encode_cer(self, writer):
7314 write_full(writer, self.tag + LENINDEF)
7315 for v in sorted(encode_cer(v) for v in self._values_for_encoding()):
7316 write_full(writer, v)
7317 write_full(writer, EOC)
7319 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
7320 return super(SetOf, self)._decode(
7327 ordering_check=True,
7331 def obj_by_path(pypath): # pragma: no cover
7332 """Import object specified as string Python path
7334 Modules must be separated from classes/functions with ``:``.
7336 >>> obj_by_path("foo.bar:Baz")
7337 <class 'foo.bar.Baz'>
7338 >>> obj_by_path("foo.bar:Baz.boo")
7339 <classmethod 'foo.bar.Baz.boo'>
7341 mod, objs = pypath.rsplit(":", 1)
7342 from importlib import import_module
7343 obj = import_module(mod)
7344 for obj_name in objs.split("."):
7345 obj = getattr(obj, obj_name)
7349 def generic_decoder(): # pragma: no cover
7350 # All of this below is a big hack with self references
7351 choice = PrimitiveTypes()
7352 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
7353 choice.specs["SetOf"] = SetOf(schema=choice)
7354 for i in six_xrange(31):
7355 choice.specs["SequenceOf%d" % i] = SequenceOf(
7359 choice.specs["Any"] = Any()
7361 # Class name equals to type name, to omit it from output
7362 class SEQUENCEOF(SequenceOf):
7370 with_decode_path=False,
7371 decode_path_only=(),
7374 def _pprint_pps(pps):
7376 if hasattr(pp, "_fields"):
7378 decode_path_only != () and
7379 pp.decode_path[:len(decode_path_only)] != decode_path_only
7382 if pp.asn1_type_name == Choice.asn1_type_name:
7384 pp_kwargs = pp._asdict()
7385 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
7386 pp = _pp(**pp_kwargs)
7387 yield pp_console_row(
7392 with_colours=with_colours,
7393 with_decode_path=with_decode_path,
7394 decode_path_len_decrease=len(decode_path_only),
7396 for row in pp_console_blob(
7398 decode_path_len_decrease=len(decode_path_only),
7402 for row in _pprint_pps(pp):
7404 return "\n".join(_pprint_pps(obj.pps(decode_path)))
7405 return SEQUENCEOF(), pprint_any
7408 def ascii_visualize(ba):
7409 """Output only ASCII printable characters, like in hexdump -C
7411 Example output for given binary string (right part)::
7413 92 2b 39 20 65 91 e6 8e 95 93 1a 58 df 02 78 ea |.+9 e......X..x.|
7416 return "".join((chr(b) if 0x20 <= b <= 0x7E else ".") for b in ba)
7420 """Generate ``hexdump -C`` like output
7424 00000000 30 80 30 80 a0 80 02 01 02 00 00 02 14 54 a5 18 |0.0..........T..|
7425 00000010 69 ef 8b 3f 15 fd ea ad bd 47 e0 94 81 6b 06 6a |i..?.....G...k.j|
7427 Result of that function is a generator of lines, where each line is
7432 ["00000010 ", " 69", " ef", " 8b", " 3f", " 15", " fd", " ea", " ad ",
7433 " bd", " 47", " e0", " 94", " 81", " 6b", " 06", " 6a ",
7434 " |i..?.....G...k.j|"]
7438 hexed = hexenc(raw).upper()
7439 addr, cols = 0, ["%08x " % 0]
7440 for i in six_xrange(0, len(hexed), 2):
7441 if i != 0 and i // 2 % 8 == 0:
7443 if i != 0 and i // 2 % 16 == 0:
7444 cols.append(" |%s|" % ascii_visualize(bytearray(raw[addr:addr + 16])))
7447 cols = ["%08x " % addr]
7448 cols.append(" " + hexed[i:i + 2])
7450 cols.append(" |%s|" % ascii_visualize(bytearray(raw[addr:])))
7454 def browse(raw, obj, oid_maps=()):
7455 """Interactive browser
7457 :param bytes raw: binary data you decoded
7458 :param obj: decoded :py:class:`pyderasn.Obj`
7459 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
7460 Its human readable form is printed when OID is met
7462 .. note:: `urwid <http://urwid.org/>`__ dependency required
7464 This browser is an interactive terminal application for browsing
7465 structures of your decoded ASN.1 objects. You can quit it with **q**
7466 key. It consists of three windows:
7469 View of ASN.1 elements hierarchy. You can navigate it using **Up**,
7470 **Down**, **PageUp**, **PageDown**, **Home**, **End** keys.
7471 **Left** key goes to constructed element above. **Plus**/**Minus**
7472 keys collapse/uncollapse constructed elements. **Space** toggles it
7474 window with various information about element. You can scroll it
7475 with **h**/**l** (down, up) (**H**/**L** for triple speed) keys
7477 window with raw data hexdump and highlighted current element's
7478 contents. It automatically focuses on element's data. You can
7479 scroll it with **j**/**k** (down, up) (**J**/**K** for triple
7480 speed) keys. If element has explicit tag, then it also will be
7481 highlighted with different colour
7483 Window's header contains current decode path and progress bars with
7484 position in *info* and *hexdump* windows.
7486 If you press **d**, then current element will be saved in the
7487 current directory under its decode path name (adding ".0", ".1", etc
7488 suffix if such file already exists). **D** will save it with explicit tag.
7490 from copy import deepcopy
7491 from os.path import exists as path_exists
7494 class TW(urwid.TreeWidget):
7495 def __init__(self, state, *args, **kwargs):
7497 self.scrolled = {"info": False, "hexdump": False}
7498 super(TW, self).__init__(*args, **kwargs)
7501 pp = self.get_node().get_value()
7502 constructed = len(pp) > 1
7503 return (pp if hasattr(pp, "_fields") else pp[0]), constructed
7505 def _state_update(self):
7506 pp, _ = self._get_pp()
7507 self.state["decode_path"].set_text(
7508 ":".join(str(p) for p in pp.decode_path)
7510 lines = deepcopy(self.state["hexed"])
7512 def attr_set(i, attr):
7513 line = lines[i // 16]
7514 idx = 1 + (i - 16 * (i // 16))
7515 line[idx] = (attr, line[idx])
7517 if pp.expl_offset is not None:
7518 for i in six_xrange(
7520 pp.expl_offset + pp.expl_tlen + pp.expl_llen,
7522 attr_set(i, "select-expl")
7523 for i in six_xrange(pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen):
7524 attr_set(i, "select-value")
7525 self.state["hexdump"]._set_body([urwid.Text(line) for line in lines])
7526 self.state["hexdump"].set_focus(pp.offset // 16)
7527 self.state["hexdump"].set_focus_valign("middle")
7528 self.state["hexdump_bar"].set_completion(
7529 (100 * pp.offset // 16) //
7530 len(self.state["hexdump"]._body.positions())
7534 [("header", "Name: "), pp.obj_name],
7535 [("header", "Type: "), pp.asn1_type_name],
7536 [("header", "Offset: "), "%d (0x%x)" % (pp.offset, pp.offset)],
7537 [("header", "[TLV]len: "), "%d/%d/%d" % (
7538 pp.tlen, pp.llen, pp.vlen,
7540 [("header", "Slice: "), "[%d:%d]" % (
7541 pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen,
7545 lines.append([("warning", "LENINDEF")])
7547 lines.append([("warning", "BER encoded")])
7549 lines.append([("warning", "BERed")])
7550 if pp.expl is not None:
7551 lines.append([("header", "EXPLICIT")])
7552 klass, _, num = pp.expl
7553 lines.append([" Tag: %s%d" % (TagClassReprs[klass], num)])
7554 if pp.expl_offset is not None:
7555 lines.append([" Offset: %d" % pp.expl_offset])
7556 lines.append([" [TLV]len: %d/%d/%d" % (
7557 pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7559 lines.append([" Slice: [%d:%d]" % (
7561 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen,
7563 if pp.impl is not None:
7564 klass, _, num = pp.impl
7566 ("header", "IMPLICIT: "), "%s%d" % (TagClassReprs[klass], num),
7569 lines.append(["OPTIONAL"])
7571 lines.append(["DEFAULT"])
7572 if len(pp.decode_path) > 0:
7573 ent = pp.decode_path[-1]
7574 if isinstance(ent, DecodePathDefBy):
7576 value = str(ent.defined_by)
7577 oid_name = find_oid_name(
7578 ent.defined_by.asn1_type_name, oid_maps, value,
7580 lines.append([("header", "DEFINED BY: "), "%s" % (
7581 value if oid_name is None
7582 else "%s (%s)" % (oid_name, value)
7585 if pp.value is not None:
7586 lines.append([("header", "Value: "), pp.value])
7588 len(oid_maps) > 0 and
7589 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
7591 for oid_map in oid_maps:
7592 oid_name = oid_map.get(pp.value)
7593 if oid_name is not None:
7594 lines.append([("header", "Human: "), oid_name])
7596 if pp.asn1_type_name == Integer.asn1_type_name:
7598 ("header", "Decimal: "), "%d" % int(pp.obj),
7601 ("header", "Hexadecimal: "), colonize_hex(pp.obj.tohex()),
7603 if pp.blob.__class__ == binary_type:
7604 blob = hexenc(pp.blob).upper()
7605 for i in six_xrange(0, len(blob), 32):
7606 lines.append([colonize_hex(blob[i:i + 32])])
7607 elif pp.blob.__class__ == tuple:
7608 lines.append([", ".join(pp.blob)])
7609 self.state["info"]._set_body([urwid.Text(line) for line in lines])
7610 self.state["info_bar"].set_completion(0)
7612 def selectable(self):
7613 if self.state["widget_current"] != self:
7614 self.state["widget_current"] = self
7615 self.scrolled["info"] = False
7616 self.scrolled["hexdump"] = False
7617 self._state_update()
7618 return super(TW, self).selectable()
7620 def get_display_text(self):
7621 pp, constructed = self._get_pp()
7622 style = "constructed" if constructed else ""
7623 if len(pp.decode_path) == 0:
7624 return (style, pp.obj_name)
7625 if pp.asn1_type_name == "EOC":
7626 return ("eoc", "EOC")
7627 ent = pp.decode_path[-1]
7628 if isinstance(ent, DecodePathDefBy):
7629 value = str(ent.defined_by)
7630 oid_name = find_oid_name(
7631 ent.defined_by.asn1_type_name, oid_maps, value,
7633 return ("defby", "DEFBY:" + (
7634 value if oid_name is None else oid_name
7638 def _scroll(self, what, step):
7639 self.state[what]._invalidate()
7640 pos = self.state[what].focus_position
7641 if not self.scrolled[what]:
7642 self.scrolled[what] = True
7644 pos = max(0, pos + step)
7645 pos = min(pos, len(self.state[what]._body.positions()) - 1)
7646 self.state[what].set_focus(pos)
7647 self.state[what].set_focus_valign("top")
7648 self.state[what + "_bar"].set_completion(
7649 (100 * pos) // len(self.state[what]._body.positions())
7652 def keypress(self, size, key):
7654 raise urwid.ExitMainLoop()
7657 self.expanded = not self.expanded
7658 self.update_expanded_icon()
7661 hexdump_steps = {"j": 1, "k": -1, "J": 5, "K": -5}
7662 if key in hexdump_steps:
7663 self._scroll("hexdump", hexdump_steps[key])
7666 info_steps = {"h": 1, "l": -1, "H": 5, "L": -5}
7667 if key in info_steps:
7668 self._scroll("info", info_steps[key])
7671 if key in ("d", "D"):
7672 pp, _ = self._get_pp()
7673 dp = ":".join(str(p) for p in pp.decode_path)
7674 dp = dp.replace(" ", "_")
7677 if key == "d" or pp.expl_offset is None:
7678 data = self.state["raw"][pp.offset:(
7679 pp.offset + pp.tlen + pp.llen + pp.vlen
7682 data = self.state["raw"][pp.expl_offset:(
7683 pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen
7687 def duplicate_path(dp, ctr):
7690 return "%s.%d" % (dp, ctr)
7693 if not path_exists(duplicate_path(dp, ctr)):
7696 dp = duplicate_path(dp, ctr)
7697 with open(dp, "wb") as fd:
7699 self.state["decode_path"].set_text(
7700 ("warning", "Saved to: " + dp)
7703 return super(TW, self).keypress(size, key)
7705 class PN(urwid.ParentNode):
7706 def __init__(self, state, value, *args, **kwargs):
7708 if not hasattr(value, "_fields"):
7710 super(PN, self).__init__(value, *args, **kwargs)
7712 def load_widget(self):
7713 return TW(self.state, self)
7715 def load_child_keys(self):
7716 value = self.get_value()
7717 if hasattr(value, "_fields"):
7719 return range(len(value[1:]))
7721 def load_child_node(self, key):
7724 self.get_value()[key + 1],
7727 depth=self.get_depth() + 1,
7730 class LabeledPG(urwid.ProgressBar):
7731 def __init__(self, label, *args, **kwargs):
7733 super(LabeledPG, self).__init__(*args, **kwargs)
7736 return "%s: %s" % (self.label, super(LabeledPG, self).get_text())
7738 WinHexdump = urwid.ListBox([urwid.Text("")])
7739 WinInfo = urwid.ListBox([urwid.Text("")])
7740 WinDecodePath = urwid.Text("", "center")
7741 WinInfoBar = LabeledPG("info", "pg-normal", "pg-complete")
7742 WinHexdumpBar = LabeledPG("hexdump", "pg-normal", "pg-complete")
7743 WinTree = urwid.TreeListBox(urwid.TreeWalker(PN(
7746 "hexed": list(hexdump(raw)),
7747 "widget_current": None,
7749 "info_bar": WinInfoBar,
7750 "hexdump": WinHexdump,
7751 "hexdump_bar": WinHexdumpBar,
7752 "decode_path": WinDecodePath,
7756 help_text = " ".join((
7758 "space:(un)collapse",
7759 "(pg)up/down/home/end:nav",
7760 "jkJK:hexdump hlHL:info",
7766 ("weight", 1, WinTree),
7767 ("weight", 2, urwid.Pile([
7768 urwid.LineBox(WinInfo),
7769 urwid.LineBox(WinHexdump),
7772 header=urwid.Columns([
7773 ("weight", 2, urwid.AttrWrap(WinDecodePath, "header")),
7774 ("weight", 1, WinInfoBar),
7775 ("weight", 1, WinHexdumpBar),
7777 footer=urwid.AttrWrap(urwid.Text(help_text), "help")
7780 ("header", "bold", ""),
7781 ("constructed", "bold", ""),
7782 ("help", "light magenta", ""),
7783 ("warning", "light red", ""),
7784 ("defby", "light red", ""),
7785 ("eoc", "dark red", ""),
7786 ("select-value", "light green", ""),
7787 ("select-expl", "light red", ""),
7788 ("pg-normal", "", "light blue"),
7789 ("pg-complete", "black", "yellow"),
7794 def main(): # pragma: no cover
7796 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/CER/DER decoder")
7797 parser.add_argument(
7801 help="Skip that number of bytes from the beginning",
7803 parser.add_argument(
7805 help="Python paths to dictionary with OIDs, comma separated",
7807 parser.add_argument(
7809 help="Python path to schema definition to use",
7811 parser.add_argument(
7812 "--defines-by-path",
7813 help="Python path to decoder's defines_by_path",
7815 parser.add_argument(
7817 action="store_true",
7818 help="Disallow BER encoding",
7820 parser.add_argument(
7821 "--print-decode-path",
7822 action="store_true",
7823 help="Print decode paths",
7825 parser.add_argument(
7826 "--decode-path-only",
7827 help="Print only specified decode path",
7829 parser.add_argument(
7831 action="store_true",
7832 help="Allow explicit tag out-of-bound",
7834 parser.add_argument(
7836 action="store_true",
7837 help="Turn on event generation mode",
7839 parser.add_argument(
7841 action="store_true",
7842 help="Start ASN.1 browser",
7844 parser.add_argument(
7846 type=argparse.FileType("rb"),
7847 help="Path to BER/CER/DER file you want to decode",
7849 args = parser.parse_args()
7851 args.RAWFile.seek(args.skip)
7852 raw = memoryview(args.RAWFile.read())
7853 args.RAWFile.close()
7855 raw = file_mmaped(args.RAWFile)[args.skip:]
7857 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
7858 if args.oids else ()
7860 from functools import partial
7862 schema = obj_by_path(args.schema)
7863 pprinter = partial(pprint, big_blobs=True)
7865 schema, pprinter = generic_decoder()
7867 "bered": not args.nobered,
7868 "allow_expl_oob": args.allow_expl_oob,
7870 if args.defines_by_path is not None:
7871 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
7873 obj, _ = schema().decode(raw, ctx=ctx)
7874 browse(raw, obj, oid_maps)
7875 from sys import exit as sys_exit
7877 from os import environ
7881 with_colours=environ.get("NO_COLOR") is None,
7882 with_decode_path=args.print_decode_path,
7884 () if args.decode_path_only is None else
7885 tuple(args.decode_path_only.split(":"))
7889 for decode_path, obj, tail in schema().decode_evgen(raw, ctx=ctx):
7890 print(pprinter(obj, decode_path=decode_path))
7892 obj, tail = schema().decode(raw, ctx=ctx)
7893 print(pprinter(obj))
7895 print("\nTrailing data: %s" % hexenc(tail))
7898 if __name__ == "__main__":
7899 from pyderasn import *