3 # cython: language_level=3
4 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
5 # Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Lesser General Public License as
9 # published by the Free Software Foundation, version 3 of the License.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """Python ASN.1 DER/BER codec with abstract structures
20 This library allows you to marshal various structures in ASN.1 DER
21 format, unmarshal them in BER/CER/DER ones.
25 >>> Integer().decod(raw) == i
28 There are primitive types, holding single values
29 (:py:class:`pyderasn.BitString`,
30 :py:class:`pyderasn.Boolean`,
31 :py:class:`pyderasn.Enumerated`,
32 :py:class:`pyderasn.GeneralizedTime`,
33 :py:class:`pyderasn.Integer`,
34 :py:class:`pyderasn.Null`,
35 :py:class:`pyderasn.ObjectIdentifier`,
36 :py:class:`pyderasn.OctetString`,
37 :py:class:`pyderasn.UTCTime`,
38 :py:class:`various strings <pyderasn.CommonString>`
39 (:py:class:`pyderasn.BMPString`,
40 :py:class:`pyderasn.GeneralString`,
41 :py:class:`pyderasn.GraphicString`,
42 :py:class:`pyderasn.IA5String`,
43 :py:class:`pyderasn.ISO646String`,
44 :py:class:`pyderasn.NumericString`,
45 :py:class:`pyderasn.PrintableString`,
46 :py:class:`pyderasn.T61String`,
47 :py:class:`pyderasn.TeletexString`,
48 :py:class:`pyderasn.UniversalString`,
49 :py:class:`pyderasn.UTF8String`,
50 :py:class:`pyderasn.VideotexString`,
51 :py:class:`pyderasn.VisibleString`)),
52 constructed types, holding multiple primitive types
53 (:py:class:`pyderasn.Sequence`,
54 :py:class:`pyderasn.SequenceOf`,
55 :py:class:`pyderasn.Set`,
56 :py:class:`pyderasn.SetOf`),
57 and special types like
58 :py:class:`pyderasn.Any` and
59 :py:class:`pyderasn.Choice`.
67 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
68 the default tag used during coding process. You can override it with
69 either ``IMPLICIT`` (using either ``impl`` keyword argument or ``impl``
70 class attribute), or ``EXPLICIT`` one (using either ``expl`` keyword
71 argument or ``expl`` class attribute). Both arguments take raw binary
72 string, containing that tag. You can **not** set implicit and explicit
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
82 EXPLICIT tags always have **constructed** tag. PyDERASN does not
83 explicitly check correctness of schema input here.
87 Implicit tags have **primitive** (``tag_ctxp``) encoding for
92 >>> Integer(impl=tag_ctxp(1))
94 >>> Integer(expl=tag_ctxc(2))
97 Implicit tag is not explicitly shown.
99 Two objects of the same type, but with different implicit/explicit tags
102 You can get object's effective tag (either default or implicited) through
103 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
106 >>> tag_decode(tag_ctxc(123))
108 >>> klass, form, num = tag_decode(tag_ctxc(123))
109 >>> klass == TagClassContext
111 >>> form == TagFormConstructed
114 To determine if object has explicit tag, use ``expled`` boolean property
115 and ``expl_tag`` property, returning explicit tag's value.
120 Many objects in sequences could be ``OPTIONAL`` and could have
121 ``DEFAULT`` value. You can specify that object's property using
122 corresponding keyword arguments.
124 >>> Integer(optional=True, default=123)
125 INTEGER 123 OPTIONAL DEFAULT
127 Those specifications do not play any role in primitive value encoding,
128 but are taken into account when dealing with sequences holding them. For
129 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
132 class Version(Integer):
138 class TBSCertificate(Sequence):
140 ("version", Version(expl=tag_ctxc(0), default="v1")),
143 When default argument is used and value is not specified, then it equals
151 Some objects give ability to set value size constraints. This is either
152 possible integer value, or allowed length of various strings and
153 sequences. Constraints are set in the following way::
158 And values satisfaction is checked as: ``MIN <= X <= MAX``.
160 For simplicity you can also set bounds the following way::
162 bounded_x = X(bounds=(MIN, MAX))
164 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
170 All objects have ``ready`` boolean property, that tells if object is
171 ready to be encoded. If that kind of action is performed on unready
172 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
174 All objects are friendly to ``copy.copy()`` and copied objects can be
177 Also all objects can be safely ``pickle``-d, but pay attention that
178 pickling among different PyDERASN versions is prohibited.
185 Decoding is performed using :py:meth:`pyderasn.Obj.decode` method.
186 ``offset`` optional argument could be used to set initial object's
187 offset in the binary data, for convenience. It returns decoded object
188 and remaining unmarshalled data (tail). Internally all work is done on
189 ``memoryview(data)``, and you can leave returning tail as a memoryview,
190 by specifying ``leavemm=True`` argument.
192 Also note convenient :py:meth:`pyderasn.Obj.decod` method, that
193 immediately checks and raises if there is non-empty tail.
195 When object is decoded, ``decoded`` property is true and you can safely
196 use following properties:
198 * ``offset`` -- position including initial offset where object's tag starts
199 * ``tlen`` -- length of object's tag
200 * ``llen`` -- length of object's length value
201 * ``vlen`` -- length of object's value
202 * ``tlvlen`` -- length of the whole object
204 Pay attention that those values do **not** include anything related to
205 explicit tag. If you want to know information about it, then use:
207 * ``expled`` -- to know if explicit tag is set
208 * ``expl_offset`` (it is lesser than ``offset``)
211 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
212 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
214 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
217 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
224 You can specify so called context keyword argument during
225 :py:meth:`pyderasn.Obj.decode` invocation. It is dictionary containing
226 various options governing decoding process.
228 Currently available context options:
230 * :ref:`allow_default_values <allow_default_values_ctx>`
231 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
232 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
233 * :ref:`bered <bered_ctx>`
234 * :ref:`defines_by_path <defines_by_path_ctx>`
241 All objects have ``pps()`` method, that is a generator of
242 :py:class:`pyderasn.PP` namedtuple, holding various raw information
243 about the object. If ``pps`` is called on sequences, then all underlying
244 ``PP`` will be yielded.
246 You can use :py:func:`pyderasn.pp_console_row` function, converting
247 those ``PP`` to human readable string. Actually exactly it is used for
248 all object ``repr``. But it is easy to write custom formatters.
250 >>> from pyderasn import pprint
251 >>> encoded = Integer(-12345).encode()
252 >>> obj, tail = Integer().decode(encoded)
253 >>> print(pprint(obj))
254 0 [1,1, 2] INTEGER -12345
258 Example certificate::
260 >>> print(pprint(crt))
261 0 [1,3,1604] Certificate SEQUENCE
262 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
263 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
264 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
265 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
266 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
267 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
269 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
270 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
271 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
272 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
273 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
274 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
275 . . . . . . . 13:02:45:53
277 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
278 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
279 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
281 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
282 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
283 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
288 Let's parse that output, human::
290 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
291 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
292 0 1 2 3 4 5 6 7 8 9 10 11
296 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
302 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
308 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
313 Offset of the object, where its DER/BER encoding begins.
314 Pay attention that it does **not** include explicit tag.
316 If explicit tag exists, then this is its length (tag + encoded length).
318 Length of object's tag. For example CHOICE does not have its own tag,
321 Length of encoded length.
323 Length of encoded value.
325 Visual indentation to show the depth of object in the hierarchy.
327 Object's name inside SEQUENCE/CHOICE.
329 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
330 here. "IMPLICIT" is omitted.
332 Object's class name, if set. Omitted if it is just an ordinary simple
333 value (like with ``algorithm`` in example above).
337 Object's value, if set. Can consist of multiple words (like OCTET/BIT
338 STRINGs above). We see ``v3`` value in Version, because it is named.
339 ``rdnSequence`` is the choice of CHOICE type.
341 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
342 default one, specified in the schema.
344 Shows does object contains any kind of BER encoded data (possibly
345 Sequence holding BER-encoded underlying value).
347 Only applicable to BER encoded data. Indefinite length encoding mark.
349 Only applicable to BER encoded data. If object has BER-specific
350 encoding, then ``BER`` will be shown. It does not depend on indefinite
351 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
352 (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime``
360 ASN.1 structures often have ANY and OCTET STRING fields, that are
361 DEFINED BY some previously met ObjectIdentifier. This library provides
362 ability to specify mapping between some OID and field that must be
363 decoded with specific specification.
370 :py:class:`pyderasn.ObjectIdentifier` field inside
371 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
372 necessary for decoding structures. For example, CMS (:rfc:`5652`)
375 class ContentInfo(Sequence):
377 ("contentType", ContentType(defines=((("content",), {
378 id_digestedData: DigestedData(),
379 id_signedData: SignedData(),
381 ("content", Any(expl=tag_ctxc(0))),
384 ``contentType`` field tells that it defines that ``content`` must be
385 decoded with ``SignedData`` specification, if ``contentType`` equals to
386 ``id-signedData``. The same applies to ``DigestedData``. If
387 ``contentType`` contains unknown OID, then no automatic decoding is
390 You can specify multiple fields, that will be autodecoded -- that is why
391 ``defines`` kwarg is a sequence. You can specify defined field
392 relatively or absolutely to current decode path. For example ``defines``
393 for AlgorithmIdentifier of X.509's
394 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
398 id_ecPublicKey: ECParameters(),
399 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
401 (("..", "subjectPublicKey"), {
402 id_rsaEncryption: RSAPublicKey(),
403 id_GostR3410_2001: OctetString(),
407 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
408 autodecode its parameters inside SPKI's algorithm and its public key
411 Following types can be automatically decoded (DEFINED BY):
413 * :py:class:`pyderasn.Any`
414 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
415 * :py:class:`pyderasn.OctetString`
416 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
417 ``Any``/``BitString``/``OctetString``-s
419 When any of those fields is automatically decoded, then ``.defined``
420 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
421 was defined, ``value`` contains corresponding decoded value. For example
422 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
424 .. _defines_by_path_ctx:
426 defines_by_path context option
427 ______________________________
429 Sometimes you either can not or do not want to explicitly set *defines*
430 in the schema. You can dynamically apply those definitions when calling
431 ``.decode()`` method.
433 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
434 value must be sequence of following tuples::
436 (decode_path, defines)
438 where ``decode_path`` is a tuple holding so-called decode path to the
439 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
440 ``defines``, holding exactly the same value as accepted in its
441 :ref:`keyword argument <defines>`.
443 For example, again for CMS, you want to automatically decode
444 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
445 structures it may hold. Also, automatically decode ``controlSequence``
448 content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
451 ((("content",), {id_signedData: SignedData()}),),
456 DecodePathDefBy(id_signedData),
461 id_cct_PKIData: PKIData(),
462 id_cct_PKIResponse: PKIResponse(),
468 DecodePathDefBy(id_signedData),
471 DecodePathDefBy(id_cct_PKIResponse),
477 id_cmc_recipientNonce: RecipientNonce(),
478 id_cmc_senderNonce: SenderNonce(),
479 id_cmc_statusInfoV2: CMCStatusInfoV2(),
480 id_cmc_transactionId: TransactionId(),
485 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
486 First function is useful for path construction when some automatic
487 decoding is already done. ``any`` means literally any value it meet --
488 useful for SEQUENCE/SET OF-s.
495 By default PyDERASN accepts only DER encoded data. It always encodes to
496 DER. But you can optionally enable BER decoding with setting ``bered``
497 :ref:`context <ctx>` argument to True. Indefinite lengths and
498 constructed primitive types should be parsed successfully.
500 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
501 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
502 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``,
503 ``UTCTime``, ``GeneralizedTime`` can contain it.
504 * If object has an indefinite length encoding, then its ``lenindef``
505 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
506 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
508 * If object has an indefinite length encoded explicit tag, then
509 ``expl_lenindef`` is set to True.
510 * If object has either any of BER-related encoding (explicit tag
511 indefinite length, object's indefinite length, BER-encoding) or any
512 underlying component has that kind of encoding, then ``bered``
513 attribute is set to True. For example SignedData CMS can have
514 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
515 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
517 EOC (end-of-contents) token's length is taken in advance in object's
520 .. _allow_expl_oob_ctx:
522 Allow explicit tag out-of-bound
523 -------------------------------
525 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
526 one value, more than one object. If you set ``allow_expl_oob`` context
527 option to True, then no error will be raised and that invalid encoding
528 will be silently further processed. But pay attention that offsets and
529 lengths will be invalid in that case.
533 This option should be used only for skipping some decode errors, just
534 to see the decoded structure somehow.
538 .. autoclass:: pyderasn.Obj
546 .. autoclass:: pyderasn.Boolean
551 .. autoclass:: pyderasn.Integer
552 :members: __init__, named
556 .. autoclass:: pyderasn.BitString
557 :members: __init__, bit_len, named
561 .. autoclass:: pyderasn.OctetString
566 .. autoclass:: pyderasn.Null
571 .. autoclass:: pyderasn.ObjectIdentifier
576 .. autoclass:: pyderasn.Enumerated
580 .. autoclass:: pyderasn.CommonString
584 .. autoclass:: pyderasn.NumericString
588 .. autoclass:: pyderasn.PrintableString
589 :members: __init__, allow_asterisk, allow_ampersand
593 .. autoclass:: pyderasn.UTCTime
594 :members: __init__, todatetime
598 .. autoclass:: pyderasn.GeneralizedTime
599 :members: __init__, todatetime
606 .. autoclass:: pyderasn.Choice
607 :members: __init__, choice, value
611 .. autoclass:: PrimitiveTypes
615 .. autoclass:: pyderasn.Any
623 .. autoclass:: pyderasn.Sequence
628 .. autoclass:: pyderasn.Set
633 .. autoclass:: pyderasn.SequenceOf
638 .. autoclass:: pyderasn.SetOf
644 .. autofunction:: pyderasn.abs_decode_path
645 .. autofunction:: pyderasn.colonize_hex
646 .. autofunction:: pyderasn.hexenc
647 .. autofunction:: pyderasn.hexdec
648 .. autofunction:: pyderasn.tag_encode
649 .. autofunction:: pyderasn.tag_decode
650 .. autofunction:: pyderasn.tag_ctxp
651 .. autofunction:: pyderasn.tag_ctxc
652 .. autoclass:: pyderasn.DecodeError
654 .. autoclass:: pyderasn.NotEnoughData
655 .. autoclass:: pyderasn.ExceedingData
656 .. autoclass:: pyderasn.LenIndefForm
657 .. autoclass:: pyderasn.TagMismatch
658 .. autoclass:: pyderasn.InvalidLength
659 .. autoclass:: pyderasn.InvalidOID
660 .. autoclass:: pyderasn.ObjUnknown
661 .. autoclass:: pyderasn.ObjNotReady
662 .. autoclass:: pyderasn.InvalidValueType
663 .. autoclass:: pyderasn.BoundsError
670 You can decode DER/BER files using command line abilities::
672 $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
674 If there is no schema for your file, then you can try parsing it without,
675 but of course IMPLICIT tags will often make it impossible. But result is
676 good enough for the certificate above::
678 $ python -m pyderasn path/to/file
679 0 [1,3,1604] . >: SEQUENCE OF
680 4 [1,3,1453] . . >: SEQUENCE OF
681 8 [0,0, 5] . . . . >: [0] ANY
682 . . . . . A0:03:02:01:02
683 13 [1,1, 3] . . . . >: INTEGER 61595
684 18 [1,1, 13] . . . . >: SEQUENCE OF
685 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
686 31 [1,1, 0] . . . . . . >: NULL
687 33 [1,3, 274] . . . . >: SEQUENCE OF
688 37 [1,1, 11] . . . . . . >: SET OF
689 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
690 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
691 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
693 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
694 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
695 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
696 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
697 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
698 . . . . . . . . . 61:2E:63:6F:6D:2F
699 1461 [1,1, 13] . . >: SEQUENCE OF
700 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
701 1474 [1,1, 0] . . . . >: NULL
702 1476 [1,2, 129] . . >: BIT STRING 1024 bits
703 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
704 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
710 If you have got dictionaries with ObjectIdentifiers, like example one
711 from ``tests/test_crts.py``::
714 "1.2.840.113549.1.1.1": "id-rsaEncryption",
715 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
717 "2.5.4.10": "id-at-organizationName",
718 "2.5.4.11": "id-at-organizationalUnitName",
721 then you can pass it to pretty printer to see human readable OIDs::
723 $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
725 37 [1,1, 11] . . . . . . >: SET OF
726 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
727 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
728 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
729 50 [1,1, 18] . . . . . . >: SET OF
730 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
731 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
732 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
733 70 [1,1, 18] . . . . . . >: SET OF
734 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
735 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
736 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
742 Each decoded element has so-called decode path: sequence of structure
743 names it is passing during the decode process. Each element has its own
744 unique path inside the whole ASN.1 tree. You can print it out with
745 ``--print-decode-path`` option::
747 $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
748 0 [1,3,1604] Certificate SEQUENCE []
749 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
750 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
751 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
752 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
753 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
754 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
756 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
757 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
758 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
759 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
760 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
761 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
762 . . . . . . . 13:02:45:53
763 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]
766 Now you can print only the specified tree, for example signature algorithm::
768 $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
769 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
770 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
771 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
775 from codecs import getdecoder
776 from codecs import getencoder
777 from collections import namedtuple
778 from collections import OrderedDict
779 from copy import copy
780 from datetime import datetime
781 from datetime import timedelta
782 from math import ceil
783 from operator import attrgetter
784 from string import ascii_letters
785 from string import digits
786 from sys import version_info
787 from unicodedata import category as unicat
789 from six import add_metaclass
790 from six import binary_type
791 from six import byte2int
792 from six import indexbytes
793 from six import int2byte
794 from six import integer_types
795 from six import iterbytes
796 from six import iteritems
797 from six import itervalues
799 from six import string_types
800 from six import text_type
801 from six import unichr as six_unichr
802 from six.moves import xrange as six_xrange
806 from termcolor import colored
807 except ImportError: # pragma: no cover
808 def colored(what, *args, **kwargs):
854 "TagClassApplication",
858 "TagFormConstructed",
869 TagClassUniversal = 0
870 TagClassApplication = 1 << 6
871 TagClassContext = 1 << 7
872 TagClassPrivate = 1 << 6 | 1 << 7
874 TagFormConstructed = 1 << 5
877 TagClassApplication: "APPLICATION ",
878 TagClassPrivate: "PRIVATE ",
879 TagClassUniversal: "UNIV ",
883 LENINDEF = b"\x80" # length indefinite mark
884 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
885 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
886 SET01 = frozenset("01")
887 DECIMALS = frozenset(digits)
892 if not set(value) <= DECIMALS:
893 raise ValueError("non-pure integer")
896 def fractions2float(fractions_raw):
897 pureint(fractions_raw)
898 return float("0." + fractions_raw)
901 def get_def_by_path(defines_by_path, sub_decode_path):
902 """Get define by decode path
904 for path, define in defines_by_path:
905 if len(path) != len(sub_decode_path):
907 for p1, p2 in zip(path, sub_decode_path):
908 if (not p1 is any) and (p1 != p2):
914 ########################################################################
916 ########################################################################
918 class ASN1Error(ValueError):
922 class DecodeError(ASN1Error):
923 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
925 :param str msg: reason of decode failing
926 :param klass: optional exact DecodeError inherited class (like
927 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
928 :py:exc:`InvalidLength`)
929 :param decode_path: tuple of strings. It contains human
930 readable names of the fields through which
931 decoding process has passed
932 :param int offset: binary offset where failure happened
934 super(DecodeError, self).__init__()
937 self.decode_path = decode_path
943 "" if self.klass is None else self.klass.__name__,
945 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
946 if len(self.decode_path) > 0 else ""
948 ("(at %d)" % self.offset) if self.offset > 0 else "",
954 return "%s(%s)" % (self.__class__.__name__, self)
957 class NotEnoughData(DecodeError):
961 class ExceedingData(ASN1Error):
962 def __init__(self, nbytes):
963 super(ExceedingData, self).__init__()
967 return "%d trailing bytes" % self.nbytes
970 return "%s(%s)" % (self.__class__.__name__, self)
973 class LenIndefForm(DecodeError):
977 class TagMismatch(DecodeError):
981 class InvalidLength(DecodeError):
985 class InvalidOID(DecodeError):
989 class ObjUnknown(ASN1Error):
990 def __init__(self, name):
991 super(ObjUnknown, self).__init__()
995 return "object is unknown: %s" % self.name
998 return "%s(%s)" % (self.__class__.__name__, self)
1001 class ObjNotReady(ASN1Error):
1002 def __init__(self, name):
1003 super(ObjNotReady, self).__init__()
1007 return "object is not ready: %s" % self.name
1010 return "%s(%s)" % (self.__class__.__name__, self)
1013 class InvalidValueType(ASN1Error):
1014 def __init__(self, expected_types):
1015 super(InvalidValueType, self).__init__()
1016 self.expected_types = expected_types
1019 return "invalid value type, expected: %s" % ", ".join(
1020 [repr(t) for t in self.expected_types]
1024 return "%s(%s)" % (self.__class__.__name__, self)
1027 class BoundsError(ASN1Error):
1028 def __init__(self, bound_min, value, bound_max):
1029 super(BoundsError, self).__init__()
1030 self.bound_min = bound_min
1032 self.bound_max = bound_max
1035 return "unsatisfied bounds: %s <= %s <= %s" % (
1042 return "%s(%s)" % (self.__class__.__name__, self)
1045 ########################################################################
1047 ########################################################################
1049 _hexdecoder = getdecoder("hex")
1050 _hexencoder = getencoder("hex")
1054 """Binary data to hexadecimal string convert
1056 return _hexdecoder(data)[0]
1060 """Hexadecimal string to binary data convert
1062 return _hexencoder(data)[0].decode("ascii")
1065 def int_bytes_len(num, byte_len=8):
1068 return int(ceil(float(num.bit_length()) / byte_len))
1071 def zero_ended_encode(num):
1072 octets = bytearray(int_bytes_len(num, 7))
1074 octets[i] = num & 0x7F
1078 octets[i] = 0x80 | (num & 0x7F)
1081 return bytes(octets)
1084 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
1085 """Encode tag to binary form
1087 :param int num: tag's number
1088 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
1089 :py:data:`pyderasn.TagClassContext`,
1090 :py:data:`pyderasn.TagClassApplication`,
1091 :py:data:`pyderasn.TagClassPrivate`)
1092 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
1093 :py:data:`pyderasn.TagFormConstructed`)
1097 return int2byte(klass | form | num)
1098 # [XX|X|11111][1.......][1.......] ... [0.......]
1099 return int2byte(klass | form | 31) + zero_ended_encode(num)
1102 def tag_decode(tag):
1103 """Decode tag from binary form
1107 No validation is performed, assuming that it has already passed.
1109 It returns tuple with three integers, as
1110 :py:func:`pyderasn.tag_encode` accepts.
1112 first_octet = byte2int(tag)
1113 klass = first_octet & 0xC0
1114 form = first_octet & 0x20
1115 if first_octet & 0x1F < 0x1F:
1116 return (klass, form, first_octet & 0x1F)
1118 for octet in iterbytes(tag[1:]):
1121 return (klass, form, num)
1125 """Create CONTEXT PRIMITIVE tag
1127 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
1131 """Create CONTEXT CONSTRUCTED tag
1133 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
1136 def tag_strip(data):
1137 """Take off tag from the data
1139 :returns: (encoded tag, tag length, remaining data)
1142 raise NotEnoughData("no data at all")
1143 if byte2int(data) & 0x1F < 31:
1144 return data[:1], 1, data[1:]
1149 raise DecodeError("unfinished tag")
1150 if indexbytes(data, i) & 0x80 == 0:
1153 return data[:i], i, data[i:]
1159 octets = bytearray(int_bytes_len(l) + 1)
1160 octets[0] = 0x80 | (len(octets) - 1)
1161 for i in six_xrange(len(octets) - 1, 0, -1):
1162 octets[i] = l & 0xFF
1164 return bytes(octets)
1167 def len_decode(data):
1170 :returns: (decoded length, length's length, remaining data)
1171 :raises LenIndefForm: if indefinite form encoding is met
1174 raise NotEnoughData("no data at all")
1175 first_octet = byte2int(data)
1176 if first_octet & 0x80 == 0:
1177 return first_octet, 1, data[1:]
1178 octets_num = first_octet & 0x7F
1179 if octets_num + 1 > len(data):
1180 raise NotEnoughData("encoded length is longer than data")
1182 raise LenIndefForm()
1183 if byte2int(data[1:]) == 0:
1184 raise DecodeError("leading zeros")
1186 for v in iterbytes(data[1:1 + octets_num]):
1189 raise DecodeError("long form instead of short one")
1190 return l, 1 + octets_num, data[1 + octets_num:]
1193 ########################################################################
1195 ########################################################################
1197 class AutoAddSlots(type):
1198 def __new__(cls, name, bases, _dict):
1199 _dict["__slots__"] = _dict.get("__slots__", ())
1200 return type.__new__(cls, name, bases, _dict)
1203 BasicState = namedtuple("BasicState", (
1216 ), **NAMEDTUPLE_KWARGS)
1219 @add_metaclass(AutoAddSlots)
1221 """Common ASN.1 object class
1223 All ASN.1 types are inherited from it. It has metaclass that
1224 automatically adds ``__slots__`` to all inherited classes.
1249 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1250 self._expl = getattr(self, "expl", None) if expl is None else expl
1251 if self.tag != self.tag_default and self._expl is not None:
1252 raise ValueError("implicit and explicit tags can not be set simultaneously")
1253 if self.tag is None:
1254 self._tag_order = None
1256 tag_class, _, tag_num = tag_decode(
1257 self.tag if self._expl is None else self._expl
1259 self._tag_order = (tag_class, tag_num)
1260 if default is not None:
1262 self.optional = optional
1263 self.offset, self.llen, self.vlen = _decoded
1265 self.expl_lenindef = False
1266 self.lenindef = False
1267 self.ber_encoded = False
1270 def ready(self): # pragma: no cover
1271 """Is object ready to be encoded?
1273 raise NotImplementedError()
1275 def _assert_ready(self):
1277 raise ObjNotReady(self.__class__.__name__)
1281 """Is either object or any elements inside is BER encoded?
1283 return self.expl_lenindef or self.lenindef or self.ber_encoded
1287 """Is object decoded?
1289 return (self.llen + self.vlen) > 0
1291 def __getstate__(self): # pragma: no cover
1292 """Used for making safe to be mutable pickleable copies
1294 raise NotImplementedError()
1296 def __setstate__(self, state):
1297 if state.version != __version__:
1298 raise ValueError("data is pickled by different PyDERASN version")
1299 self.tag = state.tag
1300 self._tag_order = state.tag_order
1301 self._expl = state.expl
1302 self.default = state.default
1303 self.optional = state.optional
1304 self.offset = state.offset
1305 self.llen = state.llen
1306 self.vlen = state.vlen
1307 self.expl_lenindef = state.expl_lenindef
1308 self.lenindef = state.lenindef
1309 self.ber_encoded = state.ber_encoded
1312 def tag_order(self):
1313 """Tag's (class, number) used for DER/CER sorting
1315 return self._tag_order
1319 """See :ref:`decoding`
1321 return len(self.tag)
1325 """See :ref:`decoding`
1327 return self.tlen + self.llen + self.vlen
1329 def __str__(self): # pragma: no cover
1330 return self.__bytes__() if PY2 else self.__unicode__()
1332 def __ne__(self, their):
1333 return not(self == their)
1335 def __gt__(self, their): # pragma: no cover
1336 return not(self < their)
1338 def __le__(self, their): # pragma: no cover
1339 return (self == their) or (self < their)
1341 def __ge__(self, their): # pragma: no cover
1342 return (self == their) or (self > their)
1344 def _encode(self): # pragma: no cover
1345 raise NotImplementedError()
1347 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode): # pragma: no cover
1348 yield NotImplemented
1351 """Encode the structure
1353 :returns: DER representation
1355 raw = self._encode()
1356 if self._expl is None:
1358 return b"".join((self._expl, len_encode(len(raw)), raw))
1360 def hexencode(self):
1361 """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1363 return hexenc(self.encode())
1373 _ctx_immutable=True,
1375 result = next(self.decode_evgen(
1387 _, obj, tail = result
1398 _ctx_immutable=True,
1403 :param data: either binary or memoryview
1404 :param int offset: initial data's offset
1405 :param bool leavemm: do we need to leave memoryview of remaining
1406 data as is, or convert it to bytes otherwise
1407 :param ctx: optional :ref:`context <ctx>` governing decoding process
1408 :param tag_only: decode only the tag, without length and contents
1409 (used only in Choice and Set structures, trying to
1410 determine if tag satisfies the schema)
1411 :param _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1413 :returns: (Obj, remaining data)
1415 .. seealso:: :ref:`decoding`
1419 elif _ctx_immutable:
1421 tlv = memoryview(data)
1424 get_def_by_path(ctx.get("evgen_mode_upto", ()), decode_path) is not None
1427 if self._expl is None:
1428 for result in self._decode(
1431 decode_path=decode_path,
1434 evgen_mode=_evgen_mode,
1439 _decode_path, obj, tail = result
1440 if not _decode_path is decode_path:
1444 t, tlen, lv = tag_strip(tlv)
1445 except DecodeError as err:
1446 raise err.__class__(
1448 klass=self.__class__,
1449 decode_path=decode_path,
1454 klass=self.__class__,
1455 decode_path=decode_path,
1459 l, llen, v = len_decode(lv)
1460 except LenIndefForm as err:
1461 if not ctx.get("bered", False):
1462 raise err.__class__(
1464 klass=self.__class__,
1465 decode_path=decode_path,
1469 offset += tlen + llen
1470 for result in self._decode(
1473 decode_path=decode_path,
1476 evgen_mode=_evgen_mode,
1478 if tag_only: # pragma: no cover
1481 _decode_path, obj, tail = result
1482 if not _decode_path is decode_path:
1484 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1485 if eoc_expected.tobytes() != EOC:
1488 klass=self.__class__,
1489 decode_path=decode_path,
1493 obj.expl_lenindef = True
1494 except DecodeError as err:
1495 raise err.__class__(
1497 klass=self.__class__,
1498 decode_path=decode_path,
1503 raise NotEnoughData(
1504 "encoded length is longer than data",
1505 klass=self.__class__,
1506 decode_path=decode_path,
1509 for result in self._decode(
1511 offset=offset + tlen + llen,
1512 decode_path=decode_path,
1515 evgen_mode=_evgen_mode,
1517 if tag_only: # pragma: no cover
1520 _decode_path, obj, tail = result
1521 if not _decode_path is decode_path:
1523 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1525 "explicit tag out-of-bound, longer than data",
1526 klass=self.__class__,
1527 decode_path=decode_path,
1530 yield decode_path, obj, (tail if leavemm else tail.tobytes())
1532 def decod(self, data, offset=0, decode_path=(), ctx=None):
1533 """Decode the data, check that tail is empty
1535 :raises ExceedingData: if tail is not empty
1537 This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
1538 (decode without tail) that also checks that there is no
1541 obj, tail = self.decode(
1544 decode_path=decode_path,
1549 raise ExceedingData(len(tail))
1552 def hexdecode(self, data, *args, **kwargs):
1553 """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
1555 return self.decode(hexdec(data), *args, **kwargs)
1557 def hexdecod(self, data, *args, **kwargs):
1558 """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
1560 return self.decod(hexdec(data), *args, **kwargs)
1564 """See :ref:`decoding`
1566 return self._expl is not None
1570 """See :ref:`decoding`
1575 def expl_tlen(self):
1576 """See :ref:`decoding`
1578 return len(self._expl)
1581 def expl_llen(self):
1582 """See :ref:`decoding`
1584 if self.expl_lenindef:
1586 return len(len_encode(self.tlvlen))
1589 def expl_offset(self):
1590 """See :ref:`decoding`
1592 return self.offset - self.expl_tlen - self.expl_llen
1595 def expl_vlen(self):
1596 """See :ref:`decoding`
1601 def expl_tlvlen(self):
1602 """See :ref:`decoding`
1604 return self.expl_tlen + self.expl_llen + self.expl_vlen
1607 def fulloffset(self):
1608 """See :ref:`decoding`
1610 return self.expl_offset if self.expled else self.offset
1614 """See :ref:`decoding`
1616 return self.expl_tlvlen if self.expled else self.tlvlen
1618 def pps_lenindef(self, decode_path):
1619 if self.lenindef and not (
1620 getattr(self, "defined", None) is not None and
1621 self.defined[1].lenindef
1624 asn1_type_name="EOC",
1626 decode_path=decode_path,
1628 self.offset + self.tlvlen -
1629 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1637 if self.expl_lenindef:
1639 asn1_type_name="EOC",
1640 obj_name="EXPLICIT",
1641 decode_path=decode_path,
1642 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1651 class DecodePathDefBy(object):
1652 """DEFINED BY representation inside decode path
1654 __slots__ = ("defined_by",)
1656 def __init__(self, defined_by):
1657 self.defined_by = defined_by
1659 def __ne__(self, their):
1660 return not(self == their)
1662 def __eq__(self, their):
1663 if not isinstance(their, self.__class__):
1665 return self.defined_by == their.defined_by
1668 return "DEFINED BY " + str(self.defined_by)
1671 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1674 ########################################################################
1676 ########################################################################
1678 PP = namedtuple("PP", (
1701 ), **NAMEDTUPLE_KWARGS)
1706 asn1_type_name="unknown",
1723 expl_lenindef=False,
1754 def _colourize(what, colour, with_colours, attrs=("bold",)):
1755 return colored(what, colour, attrs=attrs) if with_colours else what
1758 def colonize_hex(hexed):
1759 """Separate hexadecimal string with colons
1761 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1770 with_decode_path=False,
1771 decode_path_len_decrease=0,
1778 " " if pp.expl_offset is None else
1779 ("-%d" % (pp.offset - pp.expl_offset))
1781 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1783 col = _colourize(col, "red", with_colours, ())
1784 col += _colourize("B", "red", with_colours) if pp.bered else " "
1786 col = "[%d,%d,%4d]%s" % (
1790 LENINDEF_PP_CHAR if pp.lenindef else " "
1792 col = _colourize(col, "green", with_colours, ())
1794 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1795 if decode_path_len > 0:
1796 cols.append(" ." * decode_path_len)
1797 ent = pp.decode_path[-1]
1798 if isinstance(ent, DecodePathDefBy):
1799 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1800 value = str(ent.defined_by)
1803 len(oid_maps) > 0 and
1804 ent.defined_by.asn1_type_name ==
1805 ObjectIdentifier.asn1_type_name
1807 for oid_map in oid_maps:
1808 oid_name = oid_map.get(value)
1809 if oid_name is not None:
1810 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1812 if oid_name is None:
1813 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1815 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1816 if pp.expl is not None:
1817 klass, _, num = pp.expl
1818 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1819 cols.append(_colourize(col, "blue", with_colours))
1820 if pp.impl is not None:
1821 klass, _, num = pp.impl
1822 col = "[%s%d]" % (TagClassReprs[klass], num)
1823 cols.append(_colourize(col, "blue", with_colours))
1824 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1825 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1827 cols.append(_colourize("BER", "red", with_colours))
1828 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1829 if pp.value is not None:
1831 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1833 len(oid_maps) > 0 and
1834 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1836 for oid_map in oid_maps:
1837 oid_name = oid_map.get(value)
1838 if oid_name is not None:
1839 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1841 if pp.asn1_type_name == Integer.asn1_type_name:
1842 hex_repr = hex(int(pp.obj._value))[2:].upper()
1843 if len(hex_repr) % 2 != 0:
1844 hex_repr = "0" + hex_repr
1845 cols.append(_colourize(
1846 "(%s)" % colonize_hex(hex_repr),
1851 if pp.blob.__class__ == binary_type:
1852 cols.append(hexenc(pp.blob))
1853 elif pp.blob.__class__ == tuple:
1854 cols.append(", ".join(pp.blob))
1856 cols.append(_colourize("OPTIONAL", "red", with_colours))
1858 cols.append(_colourize("DEFAULT", "red", with_colours))
1859 if with_decode_path:
1860 cols.append(_colourize(
1861 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1865 return " ".join(cols)
1868 def pp_console_blob(pp, decode_path_len_decrease=0):
1869 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1870 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1871 if decode_path_len > 0:
1872 cols.append(" ." * (decode_path_len + 1))
1873 if pp.blob.__class__ == binary_type:
1874 blob = hexenc(pp.blob).upper()
1875 for i in six_xrange(0, len(blob), 32):
1876 chunk = blob[i:i + 32]
1877 yield " ".join(cols + [colonize_hex(chunk)])
1878 elif pp.blob.__class__ == tuple:
1879 yield " ".join(cols + [", ".join(pp.blob)])
1887 with_decode_path=False,
1888 decode_path_only=(),
1890 """Pretty print object
1892 :param Obj obj: object you want to pretty print
1893 :param oid_maps: list of ``str(OID) <-> human readable string`` dictionary.
1894 Its human readable form is printed when OID is met
1895 :param big_blobs: if large binary objects are met (like OctetString
1896 values), do we need to print them too, on separate
1898 :param with_colours: colourize output, if ``termcolor`` library
1900 :param with_decode_path: print decode path
1901 :param decode_path_only: print only that specified decode path
1903 def _pprint_pps(pps):
1905 if hasattr(pp, "_fields"):
1907 decode_path_only != () and
1909 str(p) for p in pp.decode_path[:len(decode_path_only)]
1910 ) != decode_path_only
1914 yield pp_console_row(
1919 with_colours=with_colours,
1920 with_decode_path=with_decode_path,
1921 decode_path_len_decrease=len(decode_path_only),
1923 for row in pp_console_blob(
1925 decode_path_len_decrease=len(decode_path_only),
1929 yield pp_console_row(
1934 with_colours=with_colours,
1935 with_decode_path=with_decode_path,
1936 decode_path_len_decrease=len(decode_path_only),
1939 for row in _pprint_pps(pp):
1941 return "\n".join(_pprint_pps(obj.pps()))
1944 ########################################################################
1945 # ASN.1 primitive types
1946 ########################################################################
1948 BooleanState = namedtuple(
1950 BasicState._fields + ("value",),
1956 """``BOOLEAN`` boolean type
1958 >>> b = Boolean(True)
1960 >>> b == Boolean(True)
1966 tag_default = tag_encode(1)
1967 asn1_type_name = "BOOLEAN"
1979 :param value: set the value. Either boolean type, or
1980 :py:class:`pyderasn.Boolean` object
1981 :param bytes impl: override default tag with ``IMPLICIT`` one
1982 :param bytes expl: override default tag with ``EXPLICIT`` one
1983 :param default: set default value. Type same as in ``value``
1984 :param bool optional: is object ``OPTIONAL`` in sequence
1986 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1987 self._value = None if value is None else self._value_sanitize(value)
1988 if default is not None:
1989 default = self._value_sanitize(default)
1990 self.default = self.__class__(
1996 self._value = default
1998 def _value_sanitize(self, value):
1999 if value.__class__ == bool:
2001 if issubclass(value.__class__, Boolean):
2003 raise InvalidValueType((self.__class__, bool))
2007 return self._value is not None
2009 def __getstate__(self):
2010 return BooleanState(
2026 def __setstate__(self, state):
2027 super(Boolean, self).__setstate__(state)
2028 self._value = state.value
2030 def __nonzero__(self):
2031 self._assert_ready()
2035 self._assert_ready()
2038 def __eq__(self, their):
2039 if their.__class__ == bool:
2040 return self._value == their
2041 if not issubclass(their.__class__, Boolean):
2044 self._value == their._value and
2045 self.tag == their.tag and
2046 self._expl == their._expl
2057 return self.__class__(
2059 impl=self.tag if impl is None else impl,
2060 expl=self._expl if expl is None else expl,
2061 default=self.default if default is None else default,
2062 optional=self.optional if optional is None else optional,
2066 self._assert_ready()
2070 (b"\xFF" if self._value else b"\x00"),
2073 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2075 t, _, lv = tag_strip(tlv)
2076 except DecodeError as err:
2077 raise err.__class__(
2079 klass=self.__class__,
2080 decode_path=decode_path,
2085 klass=self.__class__,
2086 decode_path=decode_path,
2093 l, _, v = len_decode(lv)
2094 except DecodeError as err:
2095 raise err.__class__(
2097 klass=self.__class__,
2098 decode_path=decode_path,
2102 raise InvalidLength(
2103 "Boolean's length must be equal to 1",
2104 klass=self.__class__,
2105 decode_path=decode_path,
2109 raise NotEnoughData(
2110 "encoded length is longer than data",
2111 klass=self.__class__,
2112 decode_path=decode_path,
2115 first_octet = byte2int(v)
2117 if first_octet == 0:
2119 elif first_octet == 0xFF:
2121 elif ctx.get("bered", False):
2126 "unacceptable Boolean value",
2127 klass=self.__class__,
2128 decode_path=decode_path,
2131 obj = self.__class__(
2135 default=self.default,
2136 optional=self.optional,
2137 _decoded=(offset, 1, 1),
2139 obj.ber_encoded = ber_encoded
2140 yield decode_path, obj, v[1:]
2143 return pp_console_row(next(self.pps()))
2145 def pps(self, decode_path=()):
2148 asn1_type_name=self.asn1_type_name,
2149 obj_name=self.__class__.__name__,
2150 decode_path=decode_path,
2151 value=str(self._value) if self.ready else None,
2152 optional=self.optional,
2153 default=self == self.default,
2154 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2155 expl=None if self._expl is None else tag_decode(self._expl),
2160 expl_offset=self.expl_offset if self.expled else None,
2161 expl_tlen=self.expl_tlen if self.expled else None,
2162 expl_llen=self.expl_llen if self.expled else None,
2163 expl_vlen=self.expl_vlen if self.expled else None,
2164 expl_lenindef=self.expl_lenindef,
2165 ber_encoded=self.ber_encoded,
2168 for pp in self.pps_lenindef(decode_path):
2172 IntegerState = namedtuple(
2174 BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2180 """``INTEGER`` integer type
2182 >>> b = Integer(-123)
2184 >>> b == Integer(-123)
2189 >>> Integer(2, bounds=(1, 3))
2191 >>> Integer(5, bounds=(1, 3))
2192 Traceback (most recent call last):
2193 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2197 class Version(Integer):
2204 >>> v = Version("v1")
2211 {'v3': 2, 'v1': 0, 'v2': 1}
2213 __slots__ = ("specs", "_bound_min", "_bound_max")
2214 tag_default = tag_encode(2)
2215 asn1_type_name = "INTEGER"
2229 :param value: set the value. Either integer type, named value
2230 (if ``schema`` is specified in the class), or
2231 :py:class:`pyderasn.Integer` object
2232 :param bounds: set ``(MIN, MAX)`` value constraint.
2233 (-inf, +inf) by default
2234 :param bytes impl: override default tag with ``IMPLICIT`` one
2235 :param bytes expl: override default tag with ``EXPLICIT`` one
2236 :param default: set default value. Type same as in ``value``
2237 :param bool optional: is object ``OPTIONAL`` in sequence
2239 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
2241 specs = getattr(self, "schema", {}) if _specs is None else _specs
2242 self.specs = specs if specs.__class__ == dict else dict(specs)
2243 self._bound_min, self._bound_max = getattr(
2246 (float("-inf"), float("+inf")),
2247 ) if bounds is None else bounds
2248 if value is not None:
2249 self._value = self._value_sanitize(value)
2250 if default is not None:
2251 default = self._value_sanitize(default)
2252 self.default = self.__class__(
2258 if self._value is None:
2259 self._value = default
2261 def _value_sanitize(self, value):
2262 if isinstance(value, integer_types):
2264 elif issubclass(value.__class__, Integer):
2265 value = value._value
2266 elif value.__class__ == str:
2267 value = self.specs.get(value)
2269 raise ObjUnknown("integer value: %s" % value)
2271 raise InvalidValueType((self.__class__, int, str))
2272 if not self._bound_min <= value <= self._bound_max:
2273 raise BoundsError(self._bound_min, value, self._bound_max)
2278 return self._value is not None
2280 def __getstate__(self):
2281 return IntegerState(
2300 def __setstate__(self, state):
2301 super(Integer, self).__setstate__(state)
2302 self.specs = state.specs
2303 self._value = state.value
2304 self._bound_min = state.bound_min
2305 self._bound_max = state.bound_max
2308 self._assert_ready()
2309 return int(self._value)
2312 self._assert_ready()
2315 bytes(self._expl or b"") +
2316 str(self._value).encode("ascii"),
2319 def __eq__(self, their):
2320 if isinstance(their, integer_types):
2321 return self._value == their
2322 if not issubclass(their.__class__, Integer):
2325 self._value == their._value and
2326 self.tag == their.tag and
2327 self._expl == their._expl
2330 def __lt__(self, their):
2331 return self._value < their._value
2335 """Return named representation (if exists) of the value
2337 for name, value in iteritems(self.specs):
2338 if value == self._value:
2351 return self.__class__(
2354 (self._bound_min, self._bound_max)
2355 if bounds is None else bounds
2357 impl=self.tag if impl is None else impl,
2358 expl=self._expl if expl is None else expl,
2359 default=self.default if default is None else default,
2360 optional=self.optional if optional is None else optional,
2365 self._assert_ready()
2369 octets = bytearray([0])
2373 octets = bytearray()
2375 octets.append((value & 0xFF) ^ 0xFF)
2377 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2380 octets = bytearray()
2382 octets.append(value & 0xFF)
2384 if octets[-1] & 0x80 > 0:
2387 octets = bytes(octets)
2389 bytes_len = ceil(value.bit_length() / 8) or 1
2392 octets = value.to_bytes(
2397 except OverflowError:
2401 return b"".join((self.tag, len_encode(len(octets)), octets))
2403 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2405 t, _, lv = tag_strip(tlv)
2406 except DecodeError as err:
2407 raise err.__class__(
2409 klass=self.__class__,
2410 decode_path=decode_path,
2415 klass=self.__class__,
2416 decode_path=decode_path,
2423 l, llen, v = len_decode(lv)
2424 except DecodeError as err:
2425 raise err.__class__(
2427 klass=self.__class__,
2428 decode_path=decode_path,
2432 raise NotEnoughData(
2433 "encoded length is longer than data",
2434 klass=self.__class__,
2435 decode_path=decode_path,
2439 raise NotEnoughData(
2441 klass=self.__class__,
2442 decode_path=decode_path,
2445 v, tail = v[:l], v[l:]
2446 first_octet = byte2int(v)
2448 second_octet = byte2int(v[1:])
2450 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2451 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2454 "non normalized integer",
2455 klass=self.__class__,
2456 decode_path=decode_path,
2461 if first_octet & 0x80 > 0:
2462 octets = bytearray()
2463 for octet in bytearray(v):
2464 octets.append(octet ^ 0xFF)
2465 for octet in octets:
2466 value = (value << 8) | octet
2470 for octet in bytearray(v):
2471 value = (value << 8) | octet
2473 value = int.from_bytes(v, byteorder="big", signed=True)
2475 obj = self.__class__(
2477 bounds=(self._bound_min, self._bound_max),
2480 default=self.default,
2481 optional=self.optional,
2483 _decoded=(offset, llen, l),
2485 except BoundsError as err:
2488 klass=self.__class__,
2489 decode_path=decode_path,
2492 yield decode_path, obj, tail
2495 return pp_console_row(next(self.pps()))
2497 def pps(self, decode_path=()):
2500 asn1_type_name=self.asn1_type_name,
2501 obj_name=self.__class__.__name__,
2502 decode_path=decode_path,
2503 value=(self.named or str(self._value)) if self.ready else None,
2504 optional=self.optional,
2505 default=self == self.default,
2506 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2507 expl=None if self._expl is None else tag_decode(self._expl),
2512 expl_offset=self.expl_offset if self.expled else None,
2513 expl_tlen=self.expl_tlen if self.expled else None,
2514 expl_llen=self.expl_llen if self.expled else None,
2515 expl_vlen=self.expl_vlen if self.expled else None,
2516 expl_lenindef=self.expl_lenindef,
2519 for pp in self.pps_lenindef(decode_path):
2523 BitStringState = namedtuple(
2525 BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
2530 class BitString(Obj):
2531 """``BIT STRING`` bit string type
2533 >>> BitString(b"hello world")
2534 BIT STRING 88 bits 68656c6c6f20776f726c64
2537 >>> b == b"hello world"
2542 >>> BitString("'0A3B5F291CD'H")
2543 BIT STRING 44 bits 0a3b5f291cd0
2544 >>> b = BitString("'010110000000'B")
2545 BIT STRING 12 bits 5800
2548 >>> b[0], b[1], b[2], b[3]
2549 (False, True, False, True)
2553 [False, True, False, True, True, False, False, False, False, False, False, False]
2557 class KeyUsage(BitString):
2559 ("digitalSignature", 0),
2560 ("nonRepudiation", 1),
2561 ("keyEncipherment", 2),
2564 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2565 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2567 ['nonRepudiation', 'keyEncipherment']
2569 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2573 Pay attention that BIT STRING can be encoded both in primitive
2574 and constructed forms. Decoder always checks constructed form tag
2575 additionally to specified primitive one. If BER decoding is
2576 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2577 of DER restrictions.
2579 __slots__ = ("tag_constructed", "specs", "defined")
2580 tag_default = tag_encode(3)
2581 asn1_type_name = "BIT STRING"
2594 :param value: set the value. Either binary type, tuple of named
2595 values (if ``schema`` is specified in the class),
2596 string in ``'XXX...'B`` form, or
2597 :py:class:`pyderasn.BitString` object
2598 :param bytes impl: override default tag with ``IMPLICIT`` one
2599 :param bytes expl: override default tag with ``EXPLICIT`` one
2600 :param default: set default value. Type same as in ``value``
2601 :param bool optional: is object ``OPTIONAL`` in sequence
2603 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2604 specs = getattr(self, "schema", {}) if _specs is None else _specs
2605 self.specs = specs if specs.__class__ == dict else dict(specs)
2606 self._value = None if value is None else self._value_sanitize(value)
2607 if default is not None:
2608 default = self._value_sanitize(default)
2609 self.default = self.__class__(
2615 self._value = default
2617 tag_klass, _, tag_num = tag_decode(self.tag)
2618 self.tag_constructed = tag_encode(
2620 form=TagFormConstructed,
2624 def _bits2octets(self, bits):
2625 if len(self.specs) > 0:
2626 bits = bits.rstrip("0")
2628 bits += "0" * ((8 - (bit_len % 8)) % 8)
2629 octets = bytearray(len(bits) // 8)
2630 for i in six_xrange(len(octets)):
2631 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2632 return bit_len, bytes(octets)
2634 def _value_sanitize(self, value):
2635 if isinstance(value, (string_types, binary_type)):
2637 isinstance(value, string_types) and
2638 value.startswith("'")
2640 if value.endswith("'B"):
2642 if not frozenset(value) <= SET01:
2643 raise ValueError("B's coding contains unacceptable chars")
2644 return self._bits2octets(value)
2645 if value.endswith("'H"):
2649 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2651 if value.__class__ == binary_type:
2652 return (len(value) * 8, value)
2653 raise InvalidValueType((self.__class__, string_types, binary_type))
2654 if value.__class__ == tuple:
2657 isinstance(value[0], integer_types) and
2658 value[1].__class__ == binary_type
2663 bit = self.specs.get(name)
2665 raise ObjUnknown("BitString value: %s" % name)
2668 return self._bits2octets("")
2669 bits = frozenset(bits)
2670 return self._bits2octets("".join(
2671 ("1" if bit in bits else "0")
2672 for bit in six_xrange(max(bits) + 1)
2674 if issubclass(value.__class__, BitString):
2676 raise InvalidValueType((self.__class__, binary_type, string_types))
2680 return self._value is not None
2682 def __getstate__(self):
2683 return BitStringState(
2698 self.tag_constructed,
2702 def __setstate__(self, state):
2703 super(BitString, self).__setstate__(state)
2704 self.specs = state.specs
2705 self._value = state.value
2706 self.tag_constructed = state.tag_constructed
2707 self.defined = state.defined
2710 self._assert_ready()
2711 for i in six_xrange(self._value[0]):
2716 """Returns number of bits in the string
2718 self._assert_ready()
2719 return self._value[0]
2721 def __bytes__(self):
2722 self._assert_ready()
2723 return self._value[1]
2725 def __eq__(self, their):
2726 if their.__class__ == bytes:
2727 return self._value[1] == their
2728 if not issubclass(their.__class__, BitString):
2731 self._value == their._value and
2732 self.tag == their.tag and
2733 self._expl == their._expl
2738 """Named representation (if exists) of the bits
2740 :returns: [str(name), ...]
2742 return [name for name, bit in iteritems(self.specs) if self[bit]]
2752 return self.__class__(
2754 impl=self.tag if impl is None else impl,
2755 expl=self._expl if expl is None else expl,
2756 default=self.default if default is None else default,
2757 optional=self.optional if optional is None else optional,
2761 def __getitem__(self, key):
2762 if key.__class__ == int:
2763 bit_len, octets = self._value
2767 byte2int(memoryview(octets)[key // 8:]) >>
2770 if isinstance(key, string_types):
2771 value = self.specs.get(key)
2773 raise ObjUnknown("BitString value: %s" % key)
2775 raise InvalidValueType((int, str))
2778 self._assert_ready()
2779 bit_len, octets = self._value
2782 len_encode(len(octets) + 1),
2783 int2byte((8 - bit_len % 8) % 8),
2787 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2789 t, tlen, lv = tag_strip(tlv)
2790 except DecodeError as err:
2791 raise err.__class__(
2793 klass=self.__class__,
2794 decode_path=decode_path,
2798 if tag_only: # pragma: no cover
2802 l, llen, v = len_decode(lv)
2803 except DecodeError as err:
2804 raise err.__class__(
2806 klass=self.__class__,
2807 decode_path=decode_path,
2811 raise NotEnoughData(
2812 "encoded length is longer than data",
2813 klass=self.__class__,
2814 decode_path=decode_path,
2818 raise NotEnoughData(
2820 klass=self.__class__,
2821 decode_path=decode_path,
2824 pad_size = byte2int(v)
2825 if l == 1 and pad_size != 0:
2827 "invalid empty value",
2828 klass=self.__class__,
2829 decode_path=decode_path,
2835 klass=self.__class__,
2836 decode_path=decode_path,
2839 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2842 klass=self.__class__,
2843 decode_path=decode_path,
2846 v, tail = v[:l], v[l:]
2847 bit_len = (len(v) - 1) * 8 - pad_size
2848 obj = self.__class__(
2849 value=None if evgen_mode else (bit_len, v[1:].tobytes()),
2852 default=self.default,
2853 optional=self.optional,
2855 _decoded=(offset, llen, l),
2858 obj._value = (bit_len, None)
2859 yield decode_path, obj, tail
2861 if t != self.tag_constructed:
2863 klass=self.__class__,
2864 decode_path=decode_path,
2867 if not ctx.get("bered", False):
2869 "unallowed BER constructed encoding",
2870 klass=self.__class__,
2871 decode_path=decode_path,
2874 if tag_only: # pragma: no cover
2879 l, llen, v = len_decode(lv)
2880 except LenIndefForm:
2881 llen, l, v = 1, 0, lv[1:]
2883 except DecodeError as err:
2884 raise err.__class__(
2886 klass=self.__class__,
2887 decode_path=decode_path,
2891 raise NotEnoughData(
2892 "encoded length is longer than data",
2893 klass=self.__class__,
2894 decode_path=decode_path,
2897 if not lenindef and l == 0:
2898 raise NotEnoughData(
2900 klass=self.__class__,
2901 decode_path=decode_path,
2905 sub_offset = offset + tlen + llen
2909 if v[:EOC_LEN].tobytes() == EOC:
2916 "chunk out of bounds",
2917 klass=self.__class__,
2918 decode_path=decode_path + (str(len(chunks) - 1),),
2919 offset=chunks[-1].offset,
2921 sub_decode_path = decode_path + (str(len(chunks)),)
2924 for _decode_path, chunk, v_tail in BitString().decode_evgen(
2927 decode_path=sub_decode_path,
2930 _ctx_immutable=False,
2932 yield _decode_path, chunk, v_tail
2934 _, chunk, v_tail = next(BitString().decode_evgen(
2937 decode_path=sub_decode_path,
2940 _ctx_immutable=False,
2945 "expected BitString encoded chunk",
2946 klass=self.__class__,
2947 decode_path=sub_decode_path,
2950 chunks.append(chunk)
2951 sub_offset += chunk.tlvlen
2952 vlen += chunk.tlvlen
2954 if len(chunks) == 0:
2957 klass=self.__class__,
2958 decode_path=decode_path,
2963 for chunk_i, chunk in enumerate(chunks[:-1]):
2964 if chunk.bit_len % 8 != 0:
2966 "BitString chunk is not multiple of 8 bits",
2967 klass=self.__class__,
2968 decode_path=decode_path + (str(chunk_i),),
2969 offset=chunk.offset,
2972 values.append(bytes(chunk))
2973 bit_len += chunk.bit_len
2974 chunk_last = chunks[-1]
2976 values.append(bytes(chunk_last))
2977 bit_len += chunk_last.bit_len
2978 obj = self.__class__(
2979 value=None if evgen_mode else (bit_len, b"".join(values)),
2982 default=self.default,
2983 optional=self.optional,
2985 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2988 obj._value = (bit_len, None)
2989 obj.lenindef = lenindef
2990 obj.ber_encoded = True
2991 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
2994 return pp_console_row(next(self.pps()))
2996 def pps(self, decode_path=()):
3000 bit_len, blob = self._value
3001 value = "%d bits" % bit_len
3002 if len(self.specs) > 0 and blob is not None:
3003 blob = tuple(self.named)
3006 asn1_type_name=self.asn1_type_name,
3007 obj_name=self.__class__.__name__,
3008 decode_path=decode_path,
3011 optional=self.optional,
3012 default=self == self.default,
3013 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3014 expl=None if self._expl is None else tag_decode(self._expl),
3019 expl_offset=self.expl_offset if self.expled else None,
3020 expl_tlen=self.expl_tlen if self.expled else None,
3021 expl_llen=self.expl_llen if self.expled else None,
3022 expl_vlen=self.expl_vlen if self.expled else None,
3023 expl_lenindef=self.expl_lenindef,
3024 lenindef=self.lenindef,
3025 ber_encoded=self.ber_encoded,
3028 defined_by, defined = self.defined or (None, None)
3029 if defined_by is not None:
3031 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3033 for pp in self.pps_lenindef(decode_path):
3037 OctetStringState = namedtuple(
3039 BasicState._fields + (
3050 class OctetString(Obj):
3051 """``OCTET STRING`` binary string type
3053 >>> s = OctetString(b"hello world")
3054 OCTET STRING 11 bytes 68656c6c6f20776f726c64
3055 >>> s == OctetString(b"hello world")
3060 >>> OctetString(b"hello", bounds=(4, 4))
3061 Traceback (most recent call last):
3062 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
3063 >>> OctetString(b"hell", bounds=(4, 4))
3064 OCTET STRING 4 bytes 68656c6c
3068 Pay attention that OCTET STRING can be encoded both in primitive
3069 and constructed forms. Decoder always checks constructed form tag
3070 additionally to specified primitive one. If BER decoding is
3071 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
3072 of DER restrictions.
3074 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
3075 tag_default = tag_encode(4)
3076 asn1_type_name = "OCTET STRING"
3077 evgen_mode_skip_value = True
3091 :param value: set the value. Either binary type, or
3092 :py:class:`pyderasn.OctetString` object
3093 :param bounds: set ``(MIN, MAX)`` value size constraint.
3094 (-inf, +inf) by default
3095 :param bytes impl: override default tag with ``IMPLICIT`` one
3096 :param bytes expl: override default tag with ``EXPLICIT`` one
3097 :param default: set default value. Type same as in ``value``
3098 :param bool optional: is object ``OPTIONAL`` in sequence
3100 super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
3102 self._bound_min, self._bound_max = getattr(
3106 ) if bounds is None else bounds
3107 if value is not None:
3108 self._value = self._value_sanitize(value)
3109 if default is not None:
3110 default = self._value_sanitize(default)
3111 self.default = self.__class__(
3116 if self._value is None:
3117 self._value = default
3119 tag_klass, _, tag_num = tag_decode(self.tag)
3120 self.tag_constructed = tag_encode(
3122 form=TagFormConstructed,
3126 def _value_sanitize(self, value):
3127 if value.__class__ == binary_type:
3129 elif issubclass(value.__class__, OctetString):
3130 value = value._value
3132 raise InvalidValueType((self.__class__, bytes))
3133 if not self._bound_min <= len(value) <= self._bound_max:
3134 raise BoundsError(self._bound_min, len(value), self._bound_max)
3139 return self._value is not None
3141 def __getstate__(self):
3142 return OctetStringState(
3158 self.tag_constructed,
3162 def __setstate__(self, state):
3163 super(OctetString, self).__setstate__(state)
3164 self._value = state.value
3165 self._bound_min = state.bound_min
3166 self._bound_max = state.bound_max
3167 self.tag_constructed = state.tag_constructed
3168 self.defined = state.defined
3170 def __bytes__(self):
3171 self._assert_ready()
3174 def __eq__(self, their):
3175 if their.__class__ == binary_type:
3176 return self._value == their
3177 if not issubclass(their.__class__, OctetString):
3180 self._value == their._value and
3181 self.tag == their.tag and
3182 self._expl == their._expl
3185 def __lt__(self, their):
3186 return self._value < their._value
3197 return self.__class__(
3200 (self._bound_min, self._bound_max)
3201 if bounds is None else bounds
3203 impl=self.tag if impl is None else impl,
3204 expl=self._expl if expl is None else expl,
3205 default=self.default if default is None else default,
3206 optional=self.optional if optional is None else optional,
3210 self._assert_ready()
3213 len_encode(len(self._value)),
3217 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3219 t, tlen, lv = tag_strip(tlv)
3220 except DecodeError as err:
3221 raise err.__class__(
3223 klass=self.__class__,
3224 decode_path=decode_path,
3232 l, llen, v = len_decode(lv)
3233 except DecodeError as err:
3234 raise err.__class__(
3236 klass=self.__class__,
3237 decode_path=decode_path,
3241 raise NotEnoughData(
3242 "encoded length is longer than data",
3243 klass=self.__class__,
3244 decode_path=decode_path,
3247 v, tail = v[:l], v[l:]
3248 if evgen_mode and not self._bound_min <= len(v) <= self._bound_max:
3250 msg=str(BoundsError(self._bound_min, len(v), self._bound_max)),
3251 klass=self.__class__,
3252 decode_path=decode_path,
3256 obj = self.__class__(
3258 None if (evgen_mode and self.evgen_mode_skip_value)
3261 bounds=(self._bound_min, self._bound_max),
3264 default=self.default,
3265 optional=self.optional,
3266 _decoded=(offset, llen, l),
3269 except DecodeError as err:
3272 klass=self.__class__,
3273 decode_path=decode_path,
3276 except BoundsError as err:
3279 klass=self.__class__,
3280 decode_path=decode_path,
3283 yield decode_path, obj, tail
3285 if t != self.tag_constructed:
3287 klass=self.__class__,
3288 decode_path=decode_path,
3291 if not ctx.get("bered", False):
3293 "unallowed BER constructed encoding",
3294 klass=self.__class__,
3295 decode_path=decode_path,
3303 l, llen, v = len_decode(lv)
3304 except LenIndefForm:
3305 llen, l, v = 1, 0, lv[1:]
3307 except DecodeError as err:
3308 raise err.__class__(
3310 klass=self.__class__,
3311 decode_path=decode_path,
3315 raise NotEnoughData(
3316 "encoded length is longer than data",
3317 klass=self.__class__,
3318 decode_path=decode_path,
3323 sub_offset = offset + tlen + llen
3328 if v[:EOC_LEN].tobytes() == EOC:
3335 "chunk out of bounds",
3336 klass=self.__class__,
3337 decode_path=decode_path + (str(len(chunks) - 1),),
3338 offset=chunks[-1].offset,
3342 sub_decode_path = decode_path + (str(chunks_count),)
3343 for _decode_path, chunk, v_tail in OctetString().decode_evgen(
3346 decode_path=sub_decode_path,
3349 _ctx_immutable=False,
3351 yield _decode_path, chunk, v_tail
3352 if not chunk.ber_encoded:
3353 payload_len += chunk.vlen
3356 sub_decode_path = decode_path + (str(len(chunks)),)
3357 _, chunk, v_tail = next(OctetString().decode_evgen(
3360 decode_path=sub_decode_path,
3363 _ctx_immutable=False,
3366 chunks.append(chunk)
3369 "expected OctetString encoded chunk",
3370 klass=self.__class__,
3371 decode_path=sub_decode_path,
3374 sub_offset += chunk.tlvlen
3375 vlen += chunk.tlvlen
3377 if evgen_mode and not self._bound_min <= payload_len <= self._bound_max:
3379 msg=str(BoundsError(self._bound_min, payload_len, self._bound_max)),
3380 klass=self.__class__,
3381 decode_path=decode_path,
3385 obj = self.__class__(
3387 None if evgen_mode else
3388 b"".join(bytes(chunk) for chunk in chunks)
3390 bounds=(self._bound_min, self._bound_max),
3393 default=self.default,
3394 optional=self.optional,
3395 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3398 except DecodeError as err:
3401 klass=self.__class__,
3402 decode_path=decode_path,
3405 except BoundsError as err:
3408 klass=self.__class__,
3409 decode_path=decode_path,
3412 obj.lenindef = lenindef
3413 obj.ber_encoded = True
3414 yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
3417 return pp_console_row(next(self.pps()))
3419 def pps(self, decode_path=()):
3422 asn1_type_name=self.asn1_type_name,
3423 obj_name=self.__class__.__name__,
3424 decode_path=decode_path,
3425 value=("%d bytes" % len(self._value)) if self.ready else None,
3426 blob=self._value if self.ready else None,
3427 optional=self.optional,
3428 default=self == self.default,
3429 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3430 expl=None if self._expl is None else tag_decode(self._expl),
3435 expl_offset=self.expl_offset if self.expled else None,
3436 expl_tlen=self.expl_tlen if self.expled else None,
3437 expl_llen=self.expl_llen if self.expled else None,
3438 expl_vlen=self.expl_vlen if self.expled else None,
3439 expl_lenindef=self.expl_lenindef,
3440 lenindef=self.lenindef,
3441 ber_encoded=self.ber_encoded,
3444 defined_by, defined = self.defined or (None, None)
3445 if defined_by is not None:
3447 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3449 for pp in self.pps_lenindef(decode_path):
3453 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
3457 """``NULL`` null object
3465 tag_default = tag_encode(5)
3466 asn1_type_name = "NULL"
3470 value=None, # unused, but Sequence passes it
3477 :param bytes impl: override default tag with ``IMPLICIT`` one
3478 :param bytes expl: override default tag with ``EXPLICIT`` one
3479 :param bool optional: is object ``OPTIONAL`` in sequence
3481 super(Null, self).__init__(impl, expl, None, optional, _decoded)
3488 def __getstate__(self):
3504 def __eq__(self, their):
3505 if not issubclass(their.__class__, Null):
3508 self.tag == their.tag and
3509 self._expl == their._expl
3519 return self.__class__(
3520 impl=self.tag if impl is None else impl,
3521 expl=self._expl if expl is None else expl,
3522 optional=self.optional if optional is None else optional,
3526 return self.tag + len_encode(0)
3528 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3530 t, _, lv = tag_strip(tlv)
3531 except DecodeError as err:
3532 raise err.__class__(
3534 klass=self.__class__,
3535 decode_path=decode_path,
3540 klass=self.__class__,
3541 decode_path=decode_path,
3544 if tag_only: # pragma: no cover
3548 l, _, v = len_decode(lv)
3549 except DecodeError as err:
3550 raise err.__class__(
3552 klass=self.__class__,
3553 decode_path=decode_path,
3557 raise InvalidLength(
3558 "Null must have zero length",
3559 klass=self.__class__,
3560 decode_path=decode_path,
3563 obj = self.__class__(
3566 optional=self.optional,
3567 _decoded=(offset, 1, 0),
3569 yield decode_path, obj, v
3572 return pp_console_row(next(self.pps()))
3574 def pps(self, decode_path=()):
3577 asn1_type_name=self.asn1_type_name,
3578 obj_name=self.__class__.__name__,
3579 decode_path=decode_path,
3580 optional=self.optional,
3581 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3582 expl=None if self._expl is None else tag_decode(self._expl),
3587 expl_offset=self.expl_offset if self.expled else None,
3588 expl_tlen=self.expl_tlen if self.expled else None,
3589 expl_llen=self.expl_llen if self.expled else None,
3590 expl_vlen=self.expl_vlen if self.expled else None,
3591 expl_lenindef=self.expl_lenindef,
3594 for pp in self.pps_lenindef(decode_path):
3598 ObjectIdentifierState = namedtuple(
3599 "ObjectIdentifierState",
3600 BasicState._fields + ("value", "defines"),
3605 class ObjectIdentifier(Obj):
3606 """``OBJECT IDENTIFIER`` OID type
3608 >>> oid = ObjectIdentifier((1, 2, 3))
3609 OBJECT IDENTIFIER 1.2.3
3610 >>> oid == ObjectIdentifier("1.2.3")
3616 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3617 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3619 >>> str(ObjectIdentifier((3, 1)))
3620 Traceback (most recent call last):
3621 pyderasn.InvalidOID: unacceptable first arc value
3623 __slots__ = ("defines",)
3624 tag_default = tag_encode(6)
3625 asn1_type_name = "OBJECT IDENTIFIER"
3638 :param value: set the value. Either tuples of integers,
3639 string of "."-concatenated integers, or
3640 :py:class:`pyderasn.ObjectIdentifier` object
3641 :param defines: sequence of tuples. Each tuple has two elements.
3642 First one is relative to current one decode
3643 path, aiming to the field defined by that OID.
3644 Read about relative path in
3645 :py:func:`pyderasn.abs_decode_path`. Second
3646 tuple element is ``{OID: pyderasn.Obj()}``
3647 dictionary, mapping between current OID value
3648 and structure applied to defined field.
3649 :ref:`Read about DEFINED BY <definedby>`
3650 :param bytes impl: override default tag with ``IMPLICIT`` one
3651 :param bytes expl: override default tag with ``EXPLICIT`` one
3652 :param default: set default value. Type same as in ``value``
3653 :param bool optional: is object ``OPTIONAL`` in sequence
3655 super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
3657 if value is not None:
3658 self._value = self._value_sanitize(value)
3659 if default is not None:
3660 default = self._value_sanitize(default)
3661 self.default = self.__class__(
3666 if self._value is None:
3667 self._value = default
3668 self.defines = defines
3670 def __add__(self, their):
3671 if their.__class__ == tuple:
3672 return self.__class__(self._value + their)
3673 if isinstance(their, self.__class__):
3674 return self.__class__(self._value + their._value)
3675 raise InvalidValueType((self.__class__, tuple))
3677 def _value_sanitize(self, value):
3678 if issubclass(value.__class__, ObjectIdentifier):
3680 if isinstance(value, string_types):
3682 value = tuple(pureint(arc) for arc in value.split("."))
3684 raise InvalidOID("unacceptable arcs values")
3685 if value.__class__ == tuple:
3687 raise InvalidOID("less than 2 arcs")
3688 first_arc = value[0]
3689 if first_arc in (0, 1):
3690 if not (0 <= value[1] <= 39):
3691 raise InvalidOID("second arc is too wide")
3692 elif first_arc == 2:
3695 raise InvalidOID("unacceptable first arc value")
3696 if not all(arc >= 0 for arc in value):
3697 raise InvalidOID("negative arc value")
3699 raise InvalidValueType((self.__class__, str, tuple))
3703 return self._value is not None
3705 def __getstate__(self):
3706 return ObjectIdentifierState(
3723 def __setstate__(self, state):
3724 super(ObjectIdentifier, self).__setstate__(state)
3725 self._value = state.value
3726 self.defines = state.defines
3729 self._assert_ready()
3730 return iter(self._value)
3733 return ".".join(str(arc) for arc in self._value or ())
3736 self._assert_ready()
3739 bytes(self._expl or b"") +
3740 str(self._value).encode("ascii"),
3743 def __eq__(self, their):
3744 if their.__class__ == tuple:
3745 return self._value == their
3746 if not issubclass(their.__class__, ObjectIdentifier):
3749 self.tag == their.tag and
3750 self._expl == their._expl and
3751 self._value == their._value
3754 def __lt__(self, their):
3755 return self._value < their._value
3766 return self.__class__(
3768 defines=self.defines if defines is None else defines,
3769 impl=self.tag if impl is None else impl,
3770 expl=self._expl if expl is None else expl,
3771 default=self.default if default is None else default,
3772 optional=self.optional if optional is None else optional,
3776 self._assert_ready()
3778 first_value = value[1]
3779 first_arc = value[0]
3782 elif first_arc == 1:
3784 elif first_arc == 2:
3786 else: # pragma: no cover
3787 raise RuntimeError("invalid arc is stored")
3788 octets = [zero_ended_encode(first_value)]
3789 for arc in value[2:]:
3790 octets.append(zero_ended_encode(arc))
3791 v = b"".join(octets)
3792 return b"".join((self.tag, len_encode(len(v)), v))
3794 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3796 t, _, lv = tag_strip(tlv)
3797 except DecodeError as err:
3798 raise err.__class__(
3800 klass=self.__class__,
3801 decode_path=decode_path,
3806 klass=self.__class__,
3807 decode_path=decode_path,
3810 if tag_only: # pragma: no cover
3814 l, llen, v = len_decode(lv)
3815 except DecodeError as err:
3816 raise err.__class__(
3818 klass=self.__class__,
3819 decode_path=decode_path,
3823 raise NotEnoughData(
3824 "encoded length is longer than data",
3825 klass=self.__class__,
3826 decode_path=decode_path,
3830 raise NotEnoughData(
3832 klass=self.__class__,
3833 decode_path=decode_path,
3836 v, tail = v[:l], v[l:]
3843 octet = indexbytes(v, i)
3844 if i == 0 and octet == 0x80:
3845 if ctx.get("bered", False):
3848 raise DecodeError("non normalized arc encoding")
3849 arc = (arc << 7) | (octet & 0x7F)
3850 if octet & 0x80 == 0:
3858 klass=self.__class__,
3859 decode_path=decode_path,
3863 second_arc = arcs[0]
3864 if 0 <= second_arc <= 39:
3866 elif 40 <= second_arc <= 79:
3872 obj = self.__class__(
3873 value=tuple([first_arc, second_arc] + arcs[1:]),
3876 default=self.default,
3877 optional=self.optional,
3878 _decoded=(offset, llen, l),
3881 obj.ber_encoded = True
3882 yield decode_path, obj, tail
3885 return pp_console_row(next(self.pps()))
3887 def pps(self, decode_path=()):
3890 asn1_type_name=self.asn1_type_name,
3891 obj_name=self.__class__.__name__,
3892 decode_path=decode_path,
3893 value=str(self) if self.ready else None,
3894 optional=self.optional,
3895 default=self == self.default,
3896 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3897 expl=None if self._expl is None else tag_decode(self._expl),
3902 expl_offset=self.expl_offset if self.expled else None,
3903 expl_tlen=self.expl_tlen if self.expled else None,
3904 expl_llen=self.expl_llen if self.expled else None,
3905 expl_vlen=self.expl_vlen if self.expled else None,
3906 expl_lenindef=self.expl_lenindef,
3907 ber_encoded=self.ber_encoded,
3910 for pp in self.pps_lenindef(decode_path):
3914 class Enumerated(Integer):
3915 """``ENUMERATED`` integer type
3917 This type is identical to :py:class:`pyderasn.Integer`, but requires
3918 schema to be specified and does not accept values missing from it.
3921 tag_default = tag_encode(10)
3922 asn1_type_name = "ENUMERATED"
3933 bounds=None, # dummy argument, workability for Integer.decode
3935 super(Enumerated, self).__init__(
3936 value, bounds, impl, expl, default, optional, _specs, _decoded,
3938 if len(self.specs) == 0:
3939 raise ValueError("schema must be specified")
3941 def _value_sanitize(self, value):
3942 if isinstance(value, self.__class__):
3943 value = value._value
3944 elif isinstance(value, integer_types):
3945 for _value in itervalues(self.specs):
3950 "unknown integer value: %s" % value,
3951 klass=self.__class__,
3953 elif isinstance(value, string_types):
3954 value = self.specs.get(value)
3956 raise ObjUnknown("integer value: %s" % value)
3958 raise InvalidValueType((self.__class__, int, str))
3970 return self.__class__(
3972 impl=self.tag if impl is None else impl,
3973 expl=self._expl if expl is None else expl,
3974 default=self.default if default is None else default,
3975 optional=self.optional if optional is None else optional,
3980 def escape_control_unicode(c):
3981 if unicat(c)[0] == "C":
3982 c = repr(c).lstrip("u").strip("'")
3986 class CommonString(OctetString):
3987 """Common class for all strings
3989 Everything resembles :py:class:`pyderasn.OctetString`, except
3990 ability to deal with unicode text strings.
3992 >>> hexenc("привет мир".encode("utf-8"))
3993 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3994 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3996 >>> s = UTF8String("привет мир")
3997 UTF8String UTF8String привет мир
3999 'привет мир'
4000 >>> hexenc(bytes(s))
4001 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4003 >>> PrintableString("привет мир")
4004 Traceback (most recent call last):
4005 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
4007 >>> BMPString("ада", bounds=(2, 2))
4008 Traceback (most recent call last):
4009 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
4010 >>> s = BMPString("ад", bounds=(2, 2))
4013 >>> hexenc(bytes(s))
4021 * - :py:class:`pyderasn.UTF8String`
4023 * - :py:class:`pyderasn.NumericString`
4025 * - :py:class:`pyderasn.PrintableString`
4027 * - :py:class:`pyderasn.TeletexString`
4029 * - :py:class:`pyderasn.T61String`
4031 * - :py:class:`pyderasn.VideotexString`
4033 * - :py:class:`pyderasn.IA5String`
4035 * - :py:class:`pyderasn.GraphicString`
4037 * - :py:class:`pyderasn.VisibleString`
4039 * - :py:class:`pyderasn.ISO646String`
4041 * - :py:class:`pyderasn.GeneralString`
4043 * - :py:class:`pyderasn.UniversalString`
4045 * - :py:class:`pyderasn.BMPString`
4050 def _value_sanitize(self, value):
4052 value_decoded = None
4053 if isinstance(value, self.__class__):
4054 value_raw = value._value
4055 elif value.__class__ == text_type:
4056 value_decoded = value
4057 elif value.__class__ == binary_type:
4060 raise InvalidValueType((self.__class__, text_type, binary_type))
4063 value_decoded.encode(self.encoding)
4064 if value_raw is None else value_raw
4067 value_raw.decode(self.encoding)
4068 if value_decoded is None else value_decoded
4070 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4071 raise DecodeError(str(err))
4072 if not self._bound_min <= len(value_decoded) <= self._bound_max:
4080 def __eq__(self, their):
4081 if their.__class__ == binary_type:
4082 return self._value == their
4083 if their.__class__ == text_type:
4084 return self._value == their.encode(self.encoding)
4085 if not isinstance(their, self.__class__):
4088 self._value == their._value and
4089 self.tag == their.tag and
4090 self._expl == their._expl
4093 def __unicode__(self):
4095 return self._value.decode(self.encoding)
4096 return text_type(self._value)
4099 return pp_console_row(next(self.pps(no_unicode=PY2)))
4101 def pps(self, decode_path=(), no_unicode=False):
4105 hexenc(bytes(self)) if no_unicode else
4106 "".join(escape_control_unicode(c) for c in self.__unicode__())
4110 asn1_type_name=self.asn1_type_name,
4111 obj_name=self.__class__.__name__,
4112 decode_path=decode_path,
4114 optional=self.optional,
4115 default=self == self.default,
4116 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4117 expl=None if self._expl is None else tag_decode(self._expl),
4122 expl_offset=self.expl_offset if self.expled else None,
4123 expl_tlen=self.expl_tlen if self.expled else None,
4124 expl_llen=self.expl_llen if self.expled else None,
4125 expl_vlen=self.expl_vlen if self.expled else None,
4126 expl_lenindef=self.expl_lenindef,
4127 ber_encoded=self.ber_encoded,
4130 for pp in self.pps_lenindef(decode_path):
4134 class UTF8String(CommonString):
4136 tag_default = tag_encode(12)
4138 asn1_type_name = "UTF8String"
4141 class AllowableCharsMixin(object):
4143 def allowable_chars(self):
4145 return self._allowable_chars
4146 return frozenset(six_unichr(c) for c in self._allowable_chars)
4149 class NumericString(AllowableCharsMixin, CommonString):
4152 Its value is properly sanitized: only ASCII digits with spaces can
4155 >>> NumericString().allowable_chars
4156 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4159 tag_default = tag_encode(18)
4161 asn1_type_name = "NumericString"
4162 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
4164 def _value_sanitize(self, value):
4165 value = super(NumericString, self)._value_sanitize(value)
4166 if not frozenset(value) <= self._allowable_chars:
4167 raise DecodeError("non-numeric value")
4171 PrintableStringState = namedtuple(
4172 "PrintableStringState",
4173 OctetStringState._fields + ("allowable_chars",),
4178 class PrintableString(AllowableCharsMixin, CommonString):
4181 Its value is properly sanitized: see X.680 41.4 table 10.
4183 >>> PrintableString().allowable_chars
4184 frozenset([' ', "'", ..., 'z'])
4185 >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4186 PrintableString PrintableString foo*bar
4187 >>> obj.allow_asterisk, obj.allow_ampersand
4191 tag_default = tag_encode(19)
4193 asn1_type_name = "PrintableString"
4194 _allowable_chars = frozenset(
4195 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4197 _asterisk = frozenset("*".encode("ascii"))
4198 _ampersand = frozenset("&".encode("ascii"))
4210 allow_asterisk=False,
4211 allow_ampersand=False,
4214 :param allow_asterisk: allow asterisk character
4215 :param allow_ampersand: allow ampersand character
4218 self._allowable_chars |= self._asterisk
4220 self._allowable_chars |= self._ampersand
4221 super(PrintableString, self).__init__(
4222 value, bounds, impl, expl, default, optional, _decoded, ctx,
4226 def allow_asterisk(self):
4227 """Is asterisk character allowed?
4229 return self._asterisk <= self._allowable_chars
4232 def allow_ampersand(self):
4233 """Is ampersand character allowed?
4235 return self._ampersand <= self._allowable_chars
4237 def _value_sanitize(self, value):
4238 value = super(PrintableString, self)._value_sanitize(value)
4239 if not frozenset(value) <= self._allowable_chars:
4240 raise DecodeError("non-printable value")
4243 def __getstate__(self):
4244 return PrintableStringState(
4245 *super(PrintableString, self).__getstate__(),
4246 **{"allowable_chars": self._allowable_chars}
4249 def __setstate__(self, state):
4250 super(PrintableString, self).__setstate__(state)
4251 self._allowable_chars = state.allowable_chars
4262 return self.__class__(
4265 (self._bound_min, self._bound_max)
4266 if bounds is None else bounds
4268 impl=self.tag if impl is None else impl,
4269 expl=self._expl if expl is None else expl,
4270 default=self.default if default is None else default,
4271 optional=self.optional if optional is None else optional,
4272 allow_asterisk=self.allow_asterisk,
4273 allow_ampersand=self.allow_ampersand,
4277 class TeletexString(CommonString):
4279 tag_default = tag_encode(20)
4281 asn1_type_name = "TeletexString"
4284 class T61String(TeletexString):
4286 asn1_type_name = "T61String"
4289 class VideotexString(CommonString):
4291 tag_default = tag_encode(21)
4292 encoding = "iso-8859-1"
4293 asn1_type_name = "VideotexString"
4296 class IA5String(CommonString):
4298 tag_default = tag_encode(22)
4300 asn1_type_name = "IA5"
4303 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4304 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4305 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4308 class VisibleString(CommonString):
4310 tag_default = tag_encode(26)
4312 asn1_type_name = "VisibleString"
4315 UTCTimeState = namedtuple(
4317 OctetStringState._fields + ("ber_raw",),
4322 def str_to_time_fractions(value):
4324 year, v = (v // 10**10), (v % 10**10)
4325 month, v = (v // 10**8), (v % 10**8)
4326 day, v = (v // 10**6), (v % 10**6)
4327 hour, v = (v // 10**4), (v % 10**4)
4328 minute, second = (v // 100), (v % 100)
4329 return year, month, day, hour, minute, second
4332 class UTCTime(VisibleString):
4333 """``UTCTime`` datetime type
4335 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4336 UTCTime UTCTime 2017-09-30T22:07:50
4342 datetime.datetime(2017, 9, 30, 22, 7, 50)
4343 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
4344 datetime.datetime(1957, 9, 30, 22, 7, 50)
4346 If BER encoded value was met, then ``ber_raw`` attribute will hold
4347 its raw representation.
4351 Pay attention that UTCTime can not hold full year, so all years
4352 having < 50 years are treated as 20xx, 19xx otherwise, according
4353 to X.509 recommendation.
4357 No strict validation of UTC offsets are made, but very crude:
4359 * minutes are not exceeding 60
4360 * offset value is not exceeding 14 hours
4362 __slots__ = ("ber_raw",)
4363 tag_default = tag_encode(23)
4365 asn1_type_name = "UTCTime"
4366 evgen_mode_skip_value = False
4376 bounds=None, # dummy argument, workability for OctetString.decode
4380 :param value: set the value. Either datetime type, or
4381 :py:class:`pyderasn.UTCTime` object
4382 :param bytes impl: override default tag with ``IMPLICIT`` one
4383 :param bytes expl: override default tag with ``EXPLICIT`` one
4384 :param default: set default value. Type same as in ``value``
4385 :param bool optional: is object ``OPTIONAL`` in sequence
4387 super(UTCTime, self).__init__(
4388 None, None, impl, expl, None, optional, _decoded, ctx,
4392 if value is not None:
4393 self._value, self.ber_raw = self._value_sanitize(value, ctx)
4394 self.ber_encoded = self.ber_raw is not None
4395 if default is not None:
4396 default, _ = self._value_sanitize(default)
4397 self.default = self.__class__(
4402 if self._value is None:
4403 self._value = default
4405 self.optional = optional
4407 def _strptime_bered(self, value):
4408 year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
4411 raise ValueError("no timezone")
4412 year += 2000 if year < 50 else 1900
4413 decoded = datetime(year, month, day, hour, minute)
4415 if value[-1] == "Z":
4419 raise ValueError("invalid UTC offset")
4420 if value[-5] == "-":
4422 elif value[-5] == "+":
4425 raise ValueError("invalid UTC offset")
4426 v = pureint(value[-4:])
4427 offset, v = (60 * (v % 100)), v // 100
4429 raise ValueError("invalid UTC offset minutes")
4431 if offset > 14 * 3600:
4432 raise ValueError("too big UTC offset")
4436 return offset, decoded
4438 raise ValueError("invalid UTC offset seconds")
4439 seconds = pureint(value)
4441 raise ValueError("invalid seconds value")
4442 return offset, decoded + timedelta(seconds=seconds)
4444 def _strptime(self, value):
4445 # datetime.strptime's format: %y%m%d%H%M%SZ
4446 if len(value) != LEN_YYMMDDHHMMSSZ:
4447 raise ValueError("invalid UTCTime length")
4448 if value[-1] != "Z":
4449 raise ValueError("non UTC timezone")
4450 year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
4451 year += 2000 if year < 50 else 1900
4452 return datetime(year, month, day, hour, minute, second)
4454 def _dt_sanitize(self, value):
4455 if value.year < 1950 or value.year > 2049:
4456 raise ValueError("UTCTime can hold only 1950-2049 years")
4457 return value.replace(microsecond=0)
4459 def _value_sanitize(self, value, ctx=None):
4460 if value.__class__ == binary_type:
4462 value_decoded = value.decode("ascii")
4463 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4464 raise DecodeError("invalid UTCTime encoding: %r" % err)
4467 return self._strptime(value_decoded), None
4468 except (TypeError, ValueError) as _err:
4470 if (ctx is not None) and ctx.get("bered", False):
4472 offset, _value = self._strptime_bered(value_decoded)
4473 _value = _value - timedelta(seconds=offset)
4474 return self._dt_sanitize(_value), value
4475 except (TypeError, ValueError, OverflowError) as _err:
4478 "invalid %s format: %r" % (self.asn1_type_name, err),
4479 klass=self.__class__,
4481 if isinstance(value, self.__class__):
4482 return value._value, None
4483 if value.__class__ == datetime:
4484 return self._dt_sanitize(value), None
4485 raise InvalidValueType((self.__class__, datetime))
4487 def _pp_value(self):
4489 value = self._value.isoformat()
4490 if self.ber_encoded:
4491 value += " (%s)" % self.ber_raw
4494 def __unicode__(self):
4496 value = self._value.isoformat()
4497 if self.ber_encoded:
4498 value += " (%s)" % self.ber_raw
4500 return text_type(self._pp_value())
4502 def __getstate__(self):
4503 return UTCTimeState(
4504 *super(UTCTime, self).__getstate__(),
4505 **{"ber_raw": self.ber_raw}
4508 def __setstate__(self, state):
4509 super(UTCTime, self).__setstate__(state)
4510 self.ber_raw = state.ber_raw
4512 def __bytes__(self):
4513 self._assert_ready()
4514 return self._encode_time()
4516 def __eq__(self, their):
4517 if their.__class__ == binary_type:
4518 return self._encode_time() == their
4519 if their.__class__ == datetime:
4520 return self.todatetime() == their
4521 if not isinstance(their, self.__class__):
4524 self._value == their._value and
4525 self.tag == their.tag and
4526 self._expl == their._expl
4529 def _encode_time(self):
4530 return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
4533 self._assert_ready()
4534 value = self._encode_time()
4535 return b"".join((self.tag, len_encode(len(value)), value))
4537 def todatetime(self):
4541 return pp_console_row(next(self.pps()))
4543 def pps(self, decode_path=()):
4546 asn1_type_name=self.asn1_type_name,
4547 obj_name=self.__class__.__name__,
4548 decode_path=decode_path,
4549 value=self._pp_value(),
4550 optional=self.optional,
4551 default=self == self.default,
4552 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4553 expl=None if self._expl is None else tag_decode(self._expl),
4558 expl_offset=self.expl_offset if self.expled else None,
4559 expl_tlen=self.expl_tlen if self.expled else None,
4560 expl_llen=self.expl_llen if self.expled else None,
4561 expl_vlen=self.expl_vlen if self.expled else None,
4562 expl_lenindef=self.expl_lenindef,
4563 ber_encoded=self.ber_encoded,
4566 for pp in self.pps_lenindef(decode_path):
4570 class GeneralizedTime(UTCTime):
4571 """``GeneralizedTime`` datetime type
4573 This type is similar to :py:class:`pyderasn.UTCTime`.
4575 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4576 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
4578 '20170930220750.000123Z'
4579 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
4580 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
4584 Only microsecond fractions are supported in DER encoding.
4585 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
4586 higher precision values.
4590 BER encoded data can loss information (accuracy) during decoding
4591 because of float transformations.
4595 Local times (without explicit timezone specification) are treated
4596 as UTC one, no transformations are made.
4600 Zero year is unsupported.
4603 tag_default = tag_encode(24)
4604 asn1_type_name = "GeneralizedTime"
4606 def _dt_sanitize(self, value):
4609 def _strptime_bered(self, value):
4610 if len(value) < 4 + 3 * 2:
4611 raise ValueError("invalid GeneralizedTime")
4612 year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
4613 decoded = datetime(year, month, day, hour)
4614 offset, value = 0, value[10:]
4616 return offset, decoded
4617 if value[-1] == "Z":
4620 for char, sign in (("-", -1), ("+", 1)):
4621 idx = value.rfind(char)
4624 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
4625 v = pureint(offset_raw)
4626 if len(offset_raw) == 4:
4627 offset, v = (60 * (v % 100)), v // 100
4629 raise ValueError("invalid UTC offset minutes")
4630 elif len(offset_raw) == 2:
4633 raise ValueError("invalid UTC offset")
4635 if offset > 14 * 3600:
4636 raise ValueError("too big UTC offset")
4640 return offset, decoded
4641 if value[0] in DECIMAL_SIGNS:
4643 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
4646 raise ValueError("stripped minutes")
4647 decoded += timedelta(seconds=60 * pureint(value[:2]))
4650 return offset, decoded
4651 if value[0] in DECIMAL_SIGNS:
4653 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
4656 raise ValueError("stripped seconds")
4657 decoded += timedelta(seconds=pureint(value[:2]))
4660 return offset, decoded
4661 if value[0] not in DECIMAL_SIGNS:
4662 raise ValueError("invalid format after seconds")
4664 decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
4667 def _strptime(self, value):
4669 if l == LEN_YYYYMMDDHHMMSSZ:
4670 # datetime.strptime's format: %Y%m%d%H%M%SZ
4671 if value[-1] != "Z":
4672 raise ValueError("non UTC timezone")
4673 return datetime(*str_to_time_fractions(value[:-1]))
4674 if l >= LEN_YYYYMMDDHHMMSSDMZ:
4675 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4676 if value[-1] != "Z":
4677 raise ValueError("non UTC timezone")
4678 if value[14] != ".":
4679 raise ValueError("no fractions separator")
4682 raise ValueError("trailing zero")
4685 raise ValueError("only microsecond fractions are supported")
4686 us = pureint(us + ("0" * (6 - us_len)))
4687 year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
4688 return datetime(year, month, day, hour, minute, second, us)
4689 raise ValueError("invalid GeneralizedTime length")
4691 def _encode_time(self):
4693 encoded = value.strftime("%Y%m%d%H%M%S")
4694 if value.microsecond > 0:
4695 encoded += (".%06d" % value.microsecond).rstrip("0")
4696 return (encoded + "Z").encode("ascii")
4699 class GraphicString(CommonString):
4701 tag_default = tag_encode(25)
4702 encoding = "iso-8859-1"
4703 asn1_type_name = "GraphicString"
4706 class ISO646String(VisibleString):
4708 asn1_type_name = "ISO646String"
4711 class GeneralString(CommonString):
4713 tag_default = tag_encode(27)
4714 encoding = "iso-8859-1"
4715 asn1_type_name = "GeneralString"
4718 class UniversalString(CommonString):
4720 tag_default = tag_encode(28)
4721 encoding = "utf-32-be"
4722 asn1_type_name = "UniversalString"
4725 class BMPString(CommonString):
4727 tag_default = tag_encode(30)
4728 encoding = "utf-16-be"
4729 asn1_type_name = "BMPString"
4732 ChoiceState = namedtuple(
4734 BasicState._fields + ("specs", "value",),
4740 """``CHOICE`` special type
4744 class GeneralName(Choice):
4746 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4747 ("dNSName", IA5String(impl=tag_ctxp(2))),
4750 >>> gn = GeneralName()
4752 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4753 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4754 >>> gn["dNSName"] = IA5String("bar.baz")
4755 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4756 >>> gn["rfc822Name"]
4759 [2] IA5String IA5 bar.baz
4762 >>> gn.value == gn["dNSName"]
4765 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4767 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4768 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4770 __slots__ = ("specs",)
4772 asn1_type_name = "CHOICE"
4785 :param value: set the value. Either ``(choice, value)`` tuple, or
4786 :py:class:`pyderasn.Choice` object
4787 :param bytes impl: can not be set, do **not** use it
4788 :param bytes expl: override default tag with ``EXPLICIT`` one
4789 :param default: set default value. Type same as in ``value``
4790 :param bool optional: is object ``OPTIONAL`` in sequence
4792 if impl is not None:
4793 raise ValueError("no implicit tag allowed for CHOICE")
4794 super(Choice, self).__init__(None, expl, default, optional, _decoded)
4796 schema = getattr(self, "schema", ())
4797 if len(schema) == 0:
4798 raise ValueError("schema must be specified")
4800 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
4803 if value is not None:
4804 self._value = self._value_sanitize(value)
4805 if default is not None:
4806 default_value = self._value_sanitize(default)
4807 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4808 default_obj.specs = self.specs
4809 default_obj._value = default_value
4810 self.default = default_obj
4812 self._value = copy(default_obj._value)
4813 if self._expl is not None:
4814 tag_class, _, tag_num = tag_decode(self._expl)
4815 self._tag_order = (tag_class, tag_num)
4817 def _value_sanitize(self, value):
4818 if (value.__class__ == tuple) and len(value) == 2:
4820 spec = self.specs.get(choice)
4822 raise ObjUnknown(choice)
4823 if not isinstance(obj, spec.__class__):
4824 raise InvalidValueType((spec,))
4825 return (choice, spec(obj))
4826 if isinstance(value, self.__class__):
4828 raise InvalidValueType((self.__class__, tuple))
4832 return self._value is not None and self._value[1].ready
4836 return self.expl_lenindef or (
4837 (self._value is not None) and
4838 self._value[1].bered
4841 def __getstate__(self):
4859 def __setstate__(self, state):
4860 super(Choice, self).__setstate__(state)
4861 self.specs = state.specs
4862 self._value = state.value
4864 def __eq__(self, their):
4865 if (their.__class__ == tuple) and len(their) == 2:
4866 return self._value == their
4867 if not isinstance(their, self.__class__):
4870 self.specs == their.specs and
4871 self._value == their._value
4881 return self.__class__(
4884 expl=self._expl if expl is None else expl,
4885 default=self.default if default is None else default,
4886 optional=self.optional if optional is None else optional,
4891 """Name of the choice
4893 self._assert_ready()
4894 return self._value[0]
4898 """Value of underlying choice
4900 self._assert_ready()
4901 return self._value[1]
4904 def tag_order(self):
4905 self._assert_ready()
4906 return self._value[1].tag_order if self._tag_order is None else self._tag_order
4908 def __getitem__(self, key):
4909 if key not in self.specs:
4910 raise ObjUnknown(key)
4911 if self._value is None:
4913 choice, value = self._value
4918 def __setitem__(self, key, value):
4919 spec = self.specs.get(key)
4921 raise ObjUnknown(key)
4922 if not isinstance(value, spec.__class__):
4923 raise InvalidValueType((spec.__class__,))
4924 self._value = (key, spec(value))
4932 return self._value[1].decoded if self.ready else False
4935 self._assert_ready()
4936 return self._value[1].encode()
4938 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4939 for choice, spec in iteritems(self.specs):
4940 sub_decode_path = decode_path + (choice,)
4946 decode_path=sub_decode_path,
4949 _ctx_immutable=False,
4956 klass=self.__class__,
4957 decode_path=decode_path,
4960 if tag_only: # pragma: no cover
4964 for _decode_path, value, tail in spec.decode_evgen(
4968 decode_path=sub_decode_path,
4970 _ctx_immutable=False,
4972 yield _decode_path, value, tail
4974 _, value, tail = next(spec.decode_evgen(
4978 decode_path=sub_decode_path,
4980 _ctx_immutable=False,
4983 obj = self.__class__(
4986 default=self.default,
4987 optional=self.optional,
4988 _decoded=(offset, 0, value.fulllen),
4990 obj._value = (choice, value)
4991 yield decode_path, obj, tail
4994 value = pp_console_row(next(self.pps()))
4996 value = "%s[%r]" % (value, self.value)
4999 def pps(self, decode_path=()):
5002 asn1_type_name=self.asn1_type_name,
5003 obj_name=self.__class__.__name__,
5004 decode_path=decode_path,
5005 value=self.choice if self.ready else None,
5006 optional=self.optional,
5007 default=self == self.default,
5008 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5009 expl=None if self._expl is None else tag_decode(self._expl),
5014 expl_lenindef=self.expl_lenindef,
5018 yield self.value.pps(decode_path=decode_path + (self.choice,))
5019 for pp in self.pps_lenindef(decode_path):
5023 class PrimitiveTypes(Choice):
5024 """Predefined ``CHOICE`` for all generic primitive types
5026 It could be useful for general decoding of some unspecified values:
5028 >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
5029 OCTET STRING 3 bytes 666f6f
5030 >>> PrimitiveTypes().decod(hexdec("0203123456")).value
5034 schema = tuple((klass.__name__, klass()) for klass in (
5058 AnyState = namedtuple(
5060 BasicState._fields + ("value", "defined"),
5066 """``ANY`` special type
5068 >>> Any(Integer(-123))
5070 >>> a = Any(OctetString(b"hello world").encode())
5071 ANY 040b68656c6c6f20776f726c64
5072 >>> hexenc(bytes(a))
5073 b'0x040x0bhello world'
5075 __slots__ = ("defined",)
5076 tag_default = tag_encode(0)
5077 asn1_type_name = "ANY"
5087 :param value: set the value. Either any kind of pyderasn's
5088 **ready** object, or bytes. Pay attention that
5089 **no** validation is performed if raw binary value
5090 is valid TLV, except just tag decoding
5091 :param bytes expl: override default tag with ``EXPLICIT`` one
5092 :param bool optional: is object ``OPTIONAL`` in sequence
5094 super(Any, self).__init__(None, expl, None, optional, _decoded)
5098 value = self._value_sanitize(value)
5100 if self._expl is None:
5101 if value.__class__ == binary_type:
5102 tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5104 tag_class, tag_num = value.tag_order
5106 tag_class, _, tag_num = tag_decode(self._expl)
5107 self._tag_order = (tag_class, tag_num)
5110 def _value_sanitize(self, value):
5111 if value.__class__ == binary_type:
5113 raise ValueError("Any value can not be empty")
5115 if isinstance(value, self.__class__):
5117 if isinstance(value, Obj):
5118 return value.encode()
5119 raise InvalidValueType((self.__class__, Obj, binary_type))
5123 return self._value is not None
5126 def tag_order(self):
5127 self._assert_ready()
5128 return self._tag_order
5132 if self.expl_lenindef or self.lenindef:
5134 if self.defined is None:
5136 return self.defined[1].bered
5138 def __getstate__(self):
5156 def __setstate__(self, state):
5157 super(Any, self).__setstate__(state)
5158 self._value = state.value
5159 self.defined = state.defined
5161 def __eq__(self, their):
5162 if their.__class__ == binary_type:
5163 return self._value == their
5164 if issubclass(their.__class__, Any):
5165 return self._value == their._value
5174 return self.__class__(
5176 expl=self._expl if expl is None else expl,
5177 optional=self.optional if optional is None else optional,
5180 def __bytes__(self):
5181 self._assert_ready()
5189 self._assert_ready()
5192 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5194 t, tlen, lv = tag_strip(tlv)
5195 except DecodeError as err:
5196 raise err.__class__(
5198 klass=self.__class__,
5199 decode_path=decode_path,
5203 l, llen, v = len_decode(lv)
5204 except LenIndefForm as err:
5205 if not ctx.get("bered", False):
5206 raise err.__class__(
5208 klass=self.__class__,
5209 decode_path=decode_path,
5212 llen, vlen, v = 1, 0, lv[1:]
5213 sub_offset = offset + tlen + llen
5215 while v[:EOC_LEN].tobytes() != EOC:
5216 chunk, v = Any().decode(
5219 decode_path=decode_path + (str(chunk_i),),
5222 _ctx_immutable=False,
5224 vlen += chunk.tlvlen
5225 sub_offset += chunk.tlvlen
5227 tlvlen = tlen + llen + vlen + EOC_LEN
5228 obj = self.__class__(
5229 value=None if evgen_mode else tlv[:tlvlen].tobytes(),
5231 optional=self.optional,
5232 _decoded=(offset, 0, tlvlen),
5235 obj.tag = t.tobytes()
5236 yield decode_path, obj, v[EOC_LEN:]
5238 except DecodeError as err:
5239 raise err.__class__(
5241 klass=self.__class__,
5242 decode_path=decode_path,
5246 raise NotEnoughData(
5247 "encoded length is longer than data",
5248 klass=self.__class__,
5249 decode_path=decode_path,
5252 tlvlen = tlen + llen + l
5253 v, tail = tlv[:tlvlen], v[l:]
5254 obj = self.__class__(
5255 value=None if evgen_mode else v.tobytes(),
5257 optional=self.optional,
5258 _decoded=(offset, 0, tlvlen),
5260 obj.tag = t.tobytes()
5261 yield decode_path, obj, tail
5264 return pp_console_row(next(self.pps()))
5266 def pps(self, decode_path=()):
5269 asn1_type_name=self.asn1_type_name,
5270 obj_name=self.__class__.__name__,
5271 decode_path=decode_path,
5272 blob=self._value if self.ready else None,
5273 optional=self.optional,
5274 default=self == self.default,
5275 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5276 expl=None if self._expl is None else tag_decode(self._expl),
5281 expl_offset=self.expl_offset if self.expled else None,
5282 expl_tlen=self.expl_tlen if self.expled else None,
5283 expl_llen=self.expl_llen if self.expled else None,
5284 expl_vlen=self.expl_vlen if self.expled else None,
5285 expl_lenindef=self.expl_lenindef,
5286 lenindef=self.lenindef,
5289 defined_by, defined = self.defined or (None, None)
5290 if defined_by is not None:
5292 decode_path=decode_path + (DecodePathDefBy(defined_by),)
5294 for pp in self.pps_lenindef(decode_path):
5298 ########################################################################
5299 # ASN.1 constructed types
5300 ########################################################################
5302 def get_def_by_path(defines_by_path, sub_decode_path):
5303 """Get define by decode path
5305 for path, define in defines_by_path:
5306 if len(path) != len(sub_decode_path):
5308 for p1, p2 in zip(path, sub_decode_path):
5309 if (not p1 is any) and (p1 != p2):
5315 def abs_decode_path(decode_path, rel_path):
5316 """Create an absolute decode path from current and relative ones
5318 :param decode_path: current decode path, starting point. Tuple of strings
5319 :param rel_path: relative path to ``decode_path``. Tuple of strings.
5320 If first tuple's element is "/", then treat it as
5321 an absolute path, ignoring ``decode_path`` as
5322 starting point. Also this tuple can contain ".."
5323 elements, stripping the leading element from
5326 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
5327 ("foo", "bar", "baz", "whatever")
5328 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
5330 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
5333 if rel_path[0] == "/":
5335 if rel_path[0] == "..":
5336 return abs_decode_path(decode_path[:-1], rel_path[1:])
5337 return decode_path + rel_path
5340 SequenceState = namedtuple(
5342 BasicState._fields + ("specs", "value",),
5347 class Sequence(Obj):
5348 """``SEQUENCE`` structure type
5350 You have to make specification of sequence::
5352 class Extension(Sequence):
5354 ("extnID", ObjectIdentifier()),
5355 ("critical", Boolean(default=False)),
5356 ("extnValue", OctetString()),
5359 Then, you can work with it as with dictionary.
5361 >>> ext = Extension()
5362 >>> Extension().specs
5364 ('extnID', OBJECT IDENTIFIER),
5365 ('critical', BOOLEAN False OPTIONAL DEFAULT),
5366 ('extnValue', OCTET STRING),
5368 >>> ext["extnID"] = "1.2.3"
5369 Traceback (most recent call last):
5370 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5371 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5373 You can determine if sequence is ready to be encoded:
5378 Traceback (most recent call last):
5379 pyderasn.ObjNotReady: object is not ready: extnValue
5380 >>> ext["extnValue"] = OctetString(b"foobar")
5384 Value you want to assign, must have the same **type** as in
5385 corresponding specification, but it can have different tags,
5386 optional/default attributes -- they will be taken from specification
5389 class TBSCertificate(Sequence):
5391 ("version", Version(expl=tag_ctxc(0), default="v1")),
5394 >>> tbs = TBSCertificate()
5395 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5397 Assign ``None`` to remove value from sequence.
5399 You can set values in Sequence during its initialization:
5401 >>> AlgorithmIdentifier((
5402 ("algorithm", ObjectIdentifier("1.2.3")),
5403 ("parameters", Any(Null()))
5405 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5407 You can determine if value exists/set in the sequence and take its value:
5409 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5412 OBJECT IDENTIFIER 1.2.3
5414 But pay attention that if value has default, then it won't be (not
5415 in) in the sequence (because ``DEFAULT`` must not be encoded in
5416 DER), but you can read its value:
5418 >>> "critical" in ext, ext["critical"]
5419 (False, BOOLEAN False)
5420 >>> ext["critical"] = Boolean(True)
5421 >>> "critical" in ext, ext["critical"]
5422 (True, BOOLEAN True)
5424 All defaulted values are always optional.
5426 .. _allow_default_values_ctx:
5428 DER prohibits default value encoding and will raise an error if
5429 default value is unexpectedly met during decode.
5430 If :ref:`bered <bered_ctx>` context option is set, then no error
5431 will be raised, but ``bered`` attribute set. You can disable strict
5432 defaulted values existence validation by setting
5433 ``"allow_default_values": True`` :ref:`context <ctx>` option.
5437 Check for default value existence is not performed in
5438 ``evgen_mode``, because previously decoded values are not stored
5439 in memory, to be able to compare them.
5441 Two sequences are equal if they have equal specification (schema),
5442 implicit/explicit tagging and the same values.
5444 __slots__ = ("specs",)
5445 tag_default = tag_encode(form=TagFormConstructed, num=16)
5446 asn1_type_name = "SEQUENCE"
5458 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5460 schema = getattr(self, "schema", ())
5462 schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5465 if value is not None:
5466 if issubclass(value.__class__, Sequence):
5467 self._value = value._value
5468 elif hasattr(value, "__iter__"):
5469 for seq_key, seq_value in value:
5470 self[seq_key] = seq_value
5472 raise InvalidValueType((Sequence,))
5473 if default is not None:
5474 if not issubclass(default.__class__, Sequence):
5475 raise InvalidValueType((Sequence,))
5476 default_value = default._value
5477 default_obj = self.__class__(impl=self.tag, expl=self._expl)
5478 default_obj.specs = self.specs
5479 default_obj._value = default_value
5480 self.default = default_obj
5482 self._value = copy(default_obj._value)
5486 for name, spec in iteritems(self.specs):
5487 value = self._value.get(name)
5498 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5500 return any(value.bered for value in itervalues(self._value))
5502 def __getstate__(self):
5503 return SequenceState(
5517 {k: copy(v) for k, v in iteritems(self._value)},
5520 def __setstate__(self, state):
5521 super(Sequence, self).__setstate__(state)
5522 self.specs = state.specs
5523 self._value = state.value
5525 def __eq__(self, their):
5526 if not isinstance(their, self.__class__):
5529 self.specs == their.specs and
5530 self.tag == their.tag and
5531 self._expl == their._expl and
5532 self._value == their._value
5543 return self.__class__(
5546 impl=self.tag if impl is None else impl,
5547 expl=self._expl if expl is None else expl,
5548 default=self.default if default is None else default,
5549 optional=self.optional if optional is None else optional,
5552 def __contains__(self, key):
5553 return key in self._value
5555 def __setitem__(self, key, value):
5556 spec = self.specs.get(key)
5558 raise ObjUnknown(key)
5560 self._value.pop(key, None)
5562 if not isinstance(value, spec.__class__):
5563 raise InvalidValueType((spec.__class__,))
5564 value = spec(value=value)
5565 if spec.default is not None and value == spec.default:
5566 self._value.pop(key, None)
5568 self._value[key] = value
5570 def __getitem__(self, key):
5571 value = self._value.get(key)
5572 if value is not None:
5574 spec = self.specs.get(key)
5576 raise ObjUnknown(key)
5577 if spec.default is not None:
5581 def _values_for_encoding(self):
5582 for name, spec in iteritems(self.specs):
5583 value = self._value.get(name)
5587 raise ObjNotReady(name)
5591 v = b"".join(v.encode() for v in self._values_for_encoding())
5592 return b"".join((self.tag, len_encode(len(v)), v))
5594 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5596 t, tlen, lv = tag_strip(tlv)
5597 except DecodeError as err:
5598 raise err.__class__(
5600 klass=self.__class__,
5601 decode_path=decode_path,
5606 klass=self.__class__,
5607 decode_path=decode_path,
5610 if tag_only: # pragma: no cover
5614 ctx_bered = ctx.get("bered", False)
5616 l, llen, v = len_decode(lv)
5617 except LenIndefForm as err:
5619 raise err.__class__(
5621 klass=self.__class__,
5622 decode_path=decode_path,
5625 l, llen, v = 0, 1, lv[1:]
5627 except DecodeError as err:
5628 raise err.__class__(
5630 klass=self.__class__,
5631 decode_path=decode_path,
5635 raise NotEnoughData(
5636 "encoded length is longer than data",
5637 klass=self.__class__,
5638 decode_path=decode_path,
5642 v, tail = v[:l], v[l:]
5644 sub_offset = offset + tlen + llen
5647 ctx_allow_default_values = ctx.get("allow_default_values", False)
5648 for name, spec in iteritems(self.specs):
5649 if spec.optional and (
5650 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5654 sub_decode_path = decode_path + (name,)
5657 for _decode_path, value, v_tail in spec.decode_evgen(
5661 decode_path=sub_decode_path,
5663 _ctx_immutable=False,
5665 yield _decode_path, value, v_tail
5667 _, value, v_tail = next(spec.decode_evgen(
5671 decode_path=sub_decode_path,
5673 _ctx_immutable=False,
5676 except TagMismatch as err:
5677 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5681 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5682 if not evgen_mode and defined is not None:
5683 defined_by, defined_spec = defined
5684 if issubclass(value.__class__, SequenceOf):
5685 for i, _value in enumerate(value):
5686 sub_sub_decode_path = sub_decode_path + (
5688 DecodePathDefBy(defined_by),
5690 defined_value, defined_tail = defined_spec.decode(
5691 memoryview(bytes(_value)),
5693 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5694 if value.expled else (value.tlen + value.llen)
5697 decode_path=sub_sub_decode_path,
5699 _ctx_immutable=False,
5701 if len(defined_tail) > 0:
5704 klass=self.__class__,
5705 decode_path=sub_sub_decode_path,
5708 _value.defined = (defined_by, defined_value)
5710 defined_value, defined_tail = defined_spec.decode(
5711 memoryview(bytes(value)),
5713 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5714 if value.expled else (value.tlen + value.llen)
5717 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5719 _ctx_immutable=False,
5721 if len(defined_tail) > 0:
5724 klass=self.__class__,
5725 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5728 value.defined = (defined_by, defined_value)
5730 value_len = value.fulllen
5732 sub_offset += value_len
5735 if spec.default is not None and value == spec.default:
5736 # This will not work in evgen_mode
5737 if ctx_bered or ctx_allow_default_values:
5741 "DEFAULT value met",
5742 klass=self.__class__,
5743 decode_path=sub_decode_path,
5746 values[name] = value
5747 spec_defines = getattr(spec, "defines", ())
5748 if len(spec_defines) == 0:
5749 defines_by_path = ctx.get("defines_by_path", ())
5750 if len(defines_by_path) > 0:
5751 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5752 if spec_defines is not None and len(spec_defines) > 0:
5753 for rel_path, schema in spec_defines:
5754 defined = schema.get(value, None)
5755 if defined is not None:
5756 ctx.setdefault("_defines", []).append((
5757 abs_decode_path(sub_decode_path[:-1], rel_path),
5761 if v[:EOC_LEN].tobytes() != EOC:
5764 klass=self.__class__,
5765 decode_path=decode_path,
5773 klass=self.__class__,
5774 decode_path=decode_path,
5777 obj = self.__class__(
5781 default=self.default,
5782 optional=self.optional,
5783 _decoded=(offset, llen, vlen),
5786 obj.lenindef = lenindef
5787 obj.ber_encoded = ber_encoded
5788 yield decode_path, obj, tail
5791 value = pp_console_row(next(self.pps()))
5793 for name in self.specs:
5794 _value = self._value.get(name)
5797 cols.append("%s: %s" % (name, repr(_value)))
5798 return "%s[%s]" % (value, "; ".join(cols))
5800 def pps(self, decode_path=()):
5803 asn1_type_name=self.asn1_type_name,
5804 obj_name=self.__class__.__name__,
5805 decode_path=decode_path,
5806 optional=self.optional,
5807 default=self == self.default,
5808 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5809 expl=None if self._expl is None else tag_decode(self._expl),
5814 expl_offset=self.expl_offset if self.expled else None,
5815 expl_tlen=self.expl_tlen if self.expled else None,
5816 expl_llen=self.expl_llen if self.expled else None,
5817 expl_vlen=self.expl_vlen if self.expled else None,
5818 expl_lenindef=self.expl_lenindef,
5819 lenindef=self.lenindef,
5820 ber_encoded=self.ber_encoded,
5823 for name in self.specs:
5824 value = self._value.get(name)
5827 yield value.pps(decode_path=decode_path + (name,))
5828 for pp in self.pps_lenindef(decode_path):
5832 class Set(Sequence):
5833 """``SET`` structure type
5835 Its usage is identical to :py:class:`pyderasn.Sequence`.
5837 .. _allow_unordered_set_ctx:
5839 DER prohibits unordered values encoding and will raise an error
5840 during decode. If :ref:`bered <bered_ctx>` context option is set,
5841 then no error will occur. Also you can disable strict values
5842 ordering check by setting ``"allow_unordered_set": True``
5843 :ref:`context <ctx>` option.
5846 tag_default = tag_encode(form=TagFormConstructed, num=17)
5847 asn1_type_name = "SET"
5850 v = b"".join(value.encode() for value in sorted(
5851 self._values_for_encoding(),
5852 key=attrgetter("tag_order"),
5854 return b"".join((self.tag, len_encode(len(v)), v))
5856 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5858 t, tlen, lv = tag_strip(tlv)
5859 except DecodeError as err:
5860 raise err.__class__(
5862 klass=self.__class__,
5863 decode_path=decode_path,
5868 klass=self.__class__,
5869 decode_path=decode_path,
5876 ctx_bered = ctx.get("bered", False)
5878 l, llen, v = len_decode(lv)
5879 except LenIndefForm as err:
5881 raise err.__class__(
5883 klass=self.__class__,
5884 decode_path=decode_path,
5887 l, llen, v = 0, 1, lv[1:]
5889 except DecodeError as err:
5890 raise err.__class__(
5892 klass=self.__class__,
5893 decode_path=decode_path,
5897 raise NotEnoughData(
5898 "encoded length is longer than data",
5899 klass=self.__class__,
5903 v, tail = v[:l], v[l:]
5905 sub_offset = offset + tlen + llen
5908 ctx_allow_default_values = ctx.get("allow_default_values", False)
5909 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5910 tag_order_prev = (0, 0)
5911 _specs_items = copy(self.specs)
5914 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5916 for name, spec in iteritems(_specs_items):
5917 sub_decode_path = decode_path + (name,)
5923 decode_path=sub_decode_path,
5926 _ctx_immutable=False,
5933 klass=self.__class__,
5934 decode_path=decode_path,
5938 for _decode_path, value, v_tail in spec.decode_evgen(
5942 decode_path=sub_decode_path,
5944 _ctx_immutable=False,
5946 yield _decode_path, value, v_tail
5948 _, value, v_tail = next(spec.decode_evgen(
5952 decode_path=sub_decode_path,
5954 _ctx_immutable=False,
5957 value_tag_order = value.tag_order
5958 value_len = value.fulllen
5959 if tag_order_prev >= value_tag_order:
5960 if ctx_bered or ctx_allow_unordered_set:
5964 "unordered " + self.asn1_type_name,
5965 klass=self.__class__,
5966 decode_path=sub_decode_path,
5969 if spec.default is None or value != spec.default:
5971 elif ctx_bered or ctx_allow_default_values:
5975 "DEFAULT value met",
5976 klass=self.__class__,
5977 decode_path=sub_decode_path,
5980 values[name] = value
5981 del _specs_items[name]
5982 tag_order_prev = value_tag_order
5983 sub_offset += value_len
5987 obj = self.__class__(
5991 default=self.default,
5992 optional=self.optional,
5993 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5996 if v[:EOC_LEN].tobytes() != EOC:
5999 klass=self.__class__,
6000 decode_path=decode_path,
6005 for name, spec in iteritems(self.specs):
6006 if name not in values and not spec.optional:
6008 "%s value is not ready" % name,
6009 klass=self.__class__,
6010 decode_path=decode_path,
6015 obj.ber_encoded = ber_encoded
6016 yield decode_path, obj, tail
6019 SequenceOfState = namedtuple(
6021 BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
6026 class SequenceOf(Obj):
6027 """``SEQUENCE OF`` sequence type
6029 For that kind of type you must specify the object it will carry on
6030 (bounds are for example here, not required)::
6032 class Ints(SequenceOf):
6037 >>> ints.append(Integer(123))
6038 >>> ints.append(Integer(234))
6040 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
6041 >>> [int(i) for i in ints]
6043 >>> ints.append(Integer(345))
6044 Traceback (most recent call last):
6045 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
6048 >>> ints[1] = Integer(345)
6050 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
6052 Also you can initialize sequence with preinitialized values:
6054 >>> ints = Ints([Integer(123), Integer(234)])
6056 __slots__ = ("spec", "_bound_min", "_bound_max")
6057 tag_default = tag_encode(form=TagFormConstructed, num=16)
6058 asn1_type_name = "SEQUENCE OF"
6071 super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
6073 schema = getattr(self, "schema", None)
6075 raise ValueError("schema must be specified")
6077 self._bound_min, self._bound_max = getattr(
6081 ) if bounds is None else bounds
6083 if value is not None:
6084 self._value = self._value_sanitize(value)
6085 if default is not None:
6086 default_value = self._value_sanitize(default)
6087 default_obj = self.__class__(
6092 default_obj._value = default_value
6093 self.default = default_obj
6095 self._value = copy(default_obj._value)
6097 def _value_sanitize(self, value):
6098 if issubclass(value.__class__, SequenceOf):
6099 value = value._value
6100 elif hasattr(value, "__iter__"):
6103 raise InvalidValueType((self.__class__, iter))
6104 if not self._bound_min <= len(value) <= self._bound_max:
6105 raise BoundsError(self._bound_min, len(value), self._bound_max)
6107 if not isinstance(v, self.spec.__class__):
6108 raise InvalidValueType((self.spec.__class__,))
6113 return all(v.ready for v in self._value)
6117 if self.expl_lenindef or self.lenindef or self.ber_encoded:
6119 return any(v.bered for v in self._value)
6121 def __getstate__(self):
6122 return SequenceOfState(
6136 [copy(v) for v in self._value],
6141 def __setstate__(self, state):
6142 super(SequenceOf, self).__setstate__(state)
6143 self.spec = state.spec
6144 self._value = state.value
6145 self._bound_min = state.bound_min
6146 self._bound_max = state.bound_max
6148 def __eq__(self, their):
6149 if isinstance(their, self.__class__):
6151 self.spec == their.spec and
6152 self.tag == their.tag and
6153 self._expl == their._expl and
6154 self._value == their._value
6156 if hasattr(their, "__iter__"):
6157 return self._value == list(their)
6169 return self.__class__(
6173 (self._bound_min, self._bound_max)
6174 if bounds is None else bounds
6176 impl=self.tag if impl is None else impl,
6177 expl=self._expl if expl is None else expl,
6178 default=self.default if default is None else default,
6179 optional=self.optional if optional is None else optional,
6182 def __contains__(self, key):
6183 return key in self._value
6185 def append(self, value):
6186 if not isinstance(value, self.spec.__class__):
6187 raise InvalidValueType((self.spec.__class__,))
6188 if len(self._value) + 1 > self._bound_max:
6191 len(self._value) + 1,
6194 self._value.append(value)
6197 self._assert_ready()
6198 return iter(self._value)
6201 self._assert_ready()
6202 return len(self._value)
6204 def __setitem__(self, key, value):
6205 if not isinstance(value, self.spec.__class__):
6206 raise InvalidValueType((self.spec.__class__,))
6207 self._value[key] = self.spec(value=value)
6209 def __getitem__(self, key):
6210 return self._value[key]
6212 def _values_for_encoding(self):
6213 return iter(self._value)
6216 v = b"".join(v.encode() for v in self._values_for_encoding())
6217 return b"".join((self.tag, len_encode(len(v)), v))
6227 ordering_check=False,
6230 t, tlen, lv = tag_strip(tlv)
6231 except DecodeError as err:
6232 raise err.__class__(
6234 klass=self.__class__,
6235 decode_path=decode_path,
6240 klass=self.__class__,
6241 decode_path=decode_path,
6248 ctx_bered = ctx.get("bered", False)
6250 l, llen, v = len_decode(lv)
6251 except LenIndefForm as err:
6253 raise err.__class__(
6255 klass=self.__class__,
6256 decode_path=decode_path,
6259 l, llen, v = 0, 1, lv[1:]
6261 except DecodeError as err:
6262 raise err.__class__(
6264 klass=self.__class__,
6265 decode_path=decode_path,
6269 raise NotEnoughData(
6270 "encoded length is longer than data",
6271 klass=self.__class__,
6272 decode_path=decode_path,
6276 v, tail = v[:l], v[l:]
6278 sub_offset = offset + tlen + llen
6281 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6282 value_prev = memoryview(v[:0])
6286 if lenindef and v[:EOC_LEN].tobytes() == EOC:
6288 sub_decode_path = decode_path + (str(_value_count),)
6290 for _decode_path, value, v_tail in spec.decode_evgen(
6294 decode_path=sub_decode_path,
6296 _ctx_immutable=False,
6298 yield _decode_path, value, v_tail
6300 _, value, v_tail = next(spec.decode_evgen(
6304 decode_path=sub_decode_path,
6306 _ctx_immutable=False,
6309 value_len = value.fulllen
6311 if value_prev.tobytes() > v[:value_len].tobytes():
6312 if ctx_bered or ctx_allow_unordered_set:
6316 "unordered " + self.asn1_type_name,
6317 klass=self.__class__,
6318 decode_path=sub_decode_path,
6321 value_prev = v[:value_len]
6324 _value.append(value)
6325 sub_offset += value_len
6328 if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
6330 msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
6331 klass=self.__class__,
6332 decode_path=decode_path,
6336 obj = self.__class__(
6337 value=None if evgen_mode else _value,
6339 bounds=(self._bound_min, self._bound_max),
6342 default=self.default,
6343 optional=self.optional,
6344 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6346 except BoundsError as err:
6349 klass=self.__class__,
6350 decode_path=decode_path,
6354 if v[:EOC_LEN].tobytes() != EOC:
6357 klass=self.__class__,
6358 decode_path=decode_path,
6363 obj.ber_encoded = ber_encoded
6364 yield decode_path, obj, tail
6368 pp_console_row(next(self.pps())),
6369 ", ".join(repr(v) for v in self._value),
6372 def pps(self, decode_path=()):
6375 asn1_type_name=self.asn1_type_name,
6376 obj_name=self.__class__.__name__,
6377 decode_path=decode_path,
6378 optional=self.optional,
6379 default=self == self.default,
6380 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6381 expl=None if self._expl is None else tag_decode(self._expl),
6386 expl_offset=self.expl_offset if self.expled else None,
6387 expl_tlen=self.expl_tlen if self.expled else None,
6388 expl_llen=self.expl_llen if self.expled else None,
6389 expl_vlen=self.expl_vlen if self.expled else None,
6390 expl_lenindef=self.expl_lenindef,
6391 lenindef=self.lenindef,
6392 ber_encoded=self.ber_encoded,
6395 for i, value in enumerate(self._value):
6396 yield value.pps(decode_path=decode_path + (str(i),))
6397 for pp in self.pps_lenindef(decode_path):
6401 class SetOf(SequenceOf):
6402 """``SET OF`` sequence type
6404 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6407 tag_default = tag_encode(form=TagFormConstructed, num=17)
6408 asn1_type_name = "SET OF"
6411 v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
6412 return b"".join((self.tag, len_encode(len(v)), v))
6414 def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6415 return super(SetOf, self)._decode(
6422 ordering_check=True,
6426 def obj_by_path(pypath): # pragma: no cover
6427 """Import object specified as string Python path
6429 Modules must be separated from classes/functions with ``:``.
6431 >>> obj_by_path("foo.bar:Baz")
6432 <class 'foo.bar.Baz'>
6433 >>> obj_by_path("foo.bar:Baz.boo")
6434 <classmethod 'foo.bar.Baz.boo'>
6436 mod, objs = pypath.rsplit(":", 1)
6437 from importlib import import_module
6438 obj = import_module(mod)
6439 for obj_name in objs.split("."):
6440 obj = getattr(obj, obj_name)
6444 def generic_decoder(): # pragma: no cover
6445 # All of this below is a big hack with self references
6446 choice = PrimitiveTypes()
6447 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6448 choice.specs["SetOf"] = SetOf(schema=choice)
6449 for i in six_xrange(31):
6450 choice.specs["SequenceOf%d" % i] = SequenceOf(
6454 choice.specs["Any"] = Any()
6456 # Class name equals to type name, to omit it from output
6457 class SEQUENCEOF(SequenceOf):
6465 with_decode_path=False,
6466 decode_path_only=(),
6468 def _pprint_pps(pps):
6470 if hasattr(pp, "_fields"):
6472 decode_path_only != () and
6473 pp.decode_path[:len(decode_path_only)] != decode_path_only
6476 if pp.asn1_type_name == Choice.asn1_type_name:
6478 pp_kwargs = pp._asdict()
6479 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6480 pp = _pp(**pp_kwargs)
6481 yield pp_console_row(
6486 with_colours=with_colours,
6487 with_decode_path=with_decode_path,
6488 decode_path_len_decrease=len(decode_path_only),
6490 for row in pp_console_blob(
6492 decode_path_len_decrease=len(decode_path_only),
6496 for row in _pprint_pps(pp):
6498 return "\n".join(_pprint_pps(obj.pps()))
6499 return SEQUENCEOF(), pprint_any
6502 def main(): # pragma: no cover
6504 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6505 parser.add_argument(
6509 help="Skip that number of bytes from the beginning",
6511 parser.add_argument(
6513 help="Python paths to dictionary with OIDs, comma separated",
6515 parser.add_argument(
6517 help="Python path to schema definition to use",
6519 parser.add_argument(
6520 "--defines-by-path",
6521 help="Python path to decoder's defines_by_path",
6523 parser.add_argument(
6525 action="store_true",
6526 help="Disallow BER encoding",
6528 parser.add_argument(
6529 "--print-decode-path",
6530 action="store_true",
6531 help="Print decode paths",
6533 parser.add_argument(
6534 "--decode-path-only",
6535 help="Print only specified decode path",
6537 parser.add_argument(
6539 action="store_true",
6540 help="Allow explicit tag out-of-bound",
6542 parser.add_argument(
6544 type=argparse.FileType("rb"),
6545 help="Path to DER file you want to decode",
6547 args = parser.parse_args()
6548 args.DERFile.seek(args.skip)
6549 der = memoryview(args.DERFile.read())
6550 args.DERFile.close()
6552 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6553 if args.oids else ()
6556 schema = obj_by_path(args.schema)
6557 from functools import partial
6558 pprinter = partial(pprint, big_blobs=True)
6560 schema, pprinter = generic_decoder()
6562 "bered": not args.nobered,
6563 "allow_expl_oob": args.allow_expl_oob,
6565 if args.defines_by_path is not None:
6566 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6567 obj, tail = schema().decode(der, ctx=ctx)
6568 from os import environ
6572 with_colours=environ.get("NO_COLOR") is None,
6573 with_decode_path=args.print_decode_path,
6575 () if args.decode_path_only is None else
6576 tuple(args.decode_path_only.split(":"))
6580 print("\nTrailing data: %s" % hexenc(tail))
6583 if __name__ == "__main__":