]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Initial support for CER encoding
[pyderasn.git] / pyderasn.py
1 #!/usr/bin/env python
2 # coding: utf-8
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>
6 #
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.
10 #
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.
15 #
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
19
20 This library allows you to marshal various structures in ASN.1 DER
21 format, unmarshal them in BER/CER/DER ones.
22
23     >>> i = Integer(123)
24     >>> raw = i.encode()
25     >>> Integer().decod(raw) == i
26     True
27
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`.
60
61 Common for most types
62 ---------------------
63
64 Tags
65 ____
66
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
73 tags simultaneously.
74
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
78 number.
79
80 .. note::
81
82    EXPLICIT tags always have **constructed** tag. PyDERASN does not
83    explicitly check correctness of schema input here.
84
85 .. note::
86
87    Implicit tags have **primitive** (``tag_ctxp``) encoding for
88    primitive values.
89
90 ::
91
92     >>> Integer(impl=tag_ctxp(1))
93     [1] INTEGER
94     >>> Integer(expl=tag_ctxc(2))
95     [2] EXPLICIT INTEGER
96
97 Implicit tag is not explicitly shown.
98
99 Two objects of the same type, but with different implicit/explicit tags
100 are **not** equal.
101
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`
104 function::
105
106     >>> tag_decode(tag_ctxc(123))
107     (128, 32, 123)
108     >>> klass, form, num = tag_decode(tag_ctxc(123))
109     >>> klass == TagClassContext
110     True
111     >>> form == TagFormConstructed
112     True
113
114 To determine if object has explicit tag, use ``expled`` boolean property
115 and ``expl_tag`` property, returning explicit tag's value.
116
117 Default/optional
118 ________________
119
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.
123
124     >>> Integer(optional=True, default=123)
125     INTEGER 123 OPTIONAL DEFAULT
126
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
130 ``version`` field::
131
132     class Version(Integer):
133         schema = (
134             ("v1", 0),
135             ("v2", 1),
136             ("v3", 2),
137         )
138     class TBSCertificate(Sequence):
139         schema = (
140             ("version", Version(expl=tag_ctxc(0), default="v1")),
141         [...]
142
143 When default argument is used and value is not specified, then it equals
144 to default one.
145
146 .. _bounds:
147
148 Size constraints
149 ________________
150
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::
154
155     class X(...):
156         bounds = (MIN, MAX)
157
158 And values satisfaction is checked as: ``MIN <= X <= MAX``.
159
160 For simplicity you can also set bounds the following way::
161
162     bounded_x = X(bounds=(MIN, MAX))
163
164 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
165 raised.
166
167 Common methods
168 ______________
169
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.
173
174 All objects are friendly to ``copy.copy()`` and copied objects can be
175 safely mutated.
176
177 Also all objects can be safely ``pickle``-d, but pay attention that
178 pickling among different PyDERASN versions is prohibited.
179
180 .. _decoding:
181
182 Decoding
183 --------
184
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.
191
192 Also note convenient :py:meth:`pyderasn.Obj.decod` method, that
193 immediately checks and raises if there is non-empty tail.
194
195 When object is decoded, ``decoded`` property is true and you can safely
196 use following properties:
197
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
203
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:
206
207 * ``expled`` -- to know if explicit tag is set
208 * ``expl_offset`` (it is lesser than ``offset``)
209 * ``expl_tlen``,
210 * ``expl_llen``
211 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
212 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
213   ``offset`` otherwise
214 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
215   ``tlvlen`` otherwise
216
217 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
218
219 .. _ctx:
220
221 Context
222 _______
223
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.
227
228 Currently available context options:
229
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>`
235
236 .. _pprinting:
237
238 Pretty printing
239 ---------------
240
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.
245
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.
249
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
255
256 .. _pprint_example:
257
258 Example certificate::
259
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
268                         . . . . 05:00
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
276     [...]
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
280                         . . . 05:00
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
284      [...]
285
286     Trailing data: 0a
287
288 Let's parse that output, human::
289
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
293
294 ::
295
296        20   [1,1,   9]    . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
297        ^     ^ ^    ^     ^     ^          ^                 ^
298        0     2 3    4     5     6          9                 10
299
300 ::
301
302        33   [0,0, 278]    . . issuer: Name CHOICE rdnSequence
303        ^     ^ ^    ^     ^   ^       ^    ^      ^
304        0     2 3    4     5   6       8    9      10
305
306 ::
307
308        52-2∞ B [1,1,1054]∞  . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
309              ^           ^                                 ^   ^            ^
310             12          13                                14   9            10
311
312 :0:
313  Offset of the object, where its DER/BER encoding begins.
314  Pay attention that it does **not** include explicit tag.
315 :1:
316  If explicit tag exists, then this is its length (tag + encoded length).
317 :2:
318  Length of object's tag. For example CHOICE does not have its own tag,
319  so it is zero.
320 :3:
321  Length of encoded length.
322 :4:
323  Length of encoded value.
324 :5:
325  Visual indentation to show the depth of object in the hierarchy.
326 :6:
327  Object's name inside SEQUENCE/CHOICE.
328 :7:
329  If either IMPLICIT or EXPLICIT tag is set, then it will be shown
330  here. "IMPLICIT" is omitted.
331 :8:
332  Object's class name, if set. Omitted if it is just an ordinary simple
333  value (like with ``algorithm`` in example above).
334 :9:
335  Object's ASN.1 type.
336 :10:
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.
340 :11:
341  Possible other flags like OPTIONAL and DEFAULT, if value equals to the
342  default one, specified in the schema.
343 :12:
344  Shows does object contains any kind of BER encoded data (possibly
345  Sequence holding BER-encoded underlying value).
346 :13:
347  Only applicable to BER encoded data. Indefinite length encoding mark.
348 :14:
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``
353  could be BERed.
354
355 .. _definedby:
356
357 DEFINED BY
358 ----------
359
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.
364
365 .. _defines:
366
367 defines kwarg
368 _____________
369
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`)
373 container::
374
375     class ContentInfo(Sequence):
376         schema = (
377             ("contentType", ContentType(defines=((("content",), {
378                 id_digestedData: DigestedData(),
379                 id_signedData: SignedData(),
380             }),))),
381             ("content", Any(expl=tag_ctxc(0))),
382         )
383
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
388 done.
389
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``::
395
396         (
397             (("parameters",), {
398                 id_ecPublicKey: ECParameters(),
399                 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
400             }),
401             (("..", "subjectPublicKey"), {
402                 id_rsaEncryption: RSAPublicKey(),
403                 id_GostR3410_2001: OctetString(),
404             }),
405         ),
406
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
409 itself.
410
411 Following types can be automatically decoded (DEFINED BY):
412
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
418
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)``.
423
424 .. _defines_by_path_ctx:
425
426 defines_by_path context option
427 ______________________________
428
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.
432
433 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
434 value must be sequence of following tuples::
435
436     (decode_path, defines)
437
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>`.
442
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``
446 of ``PKIResponse``::
447
448     content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
449         (
450             ("contentType",),
451             ((("content",), {id_signedData: SignedData()}),),
452         ),
453         (
454             (
455                 "content",
456                 DecodePathDefBy(id_signedData),
457                 "encapContentInfo",
458                 "eContentType",
459             ),
460             ((("eContent",), {
461                 id_cct_PKIData: PKIData(),
462                 id_cct_PKIResponse: PKIResponse(),
463             })),
464         ),
465         (
466             (
467                 "content",
468                 DecodePathDefBy(id_signedData),
469                 "encapContentInfo",
470                 "eContent",
471                 DecodePathDefBy(id_cct_PKIResponse),
472                 "controlSequence",
473                 any,
474                 "attrType",
475             ),
476             ((("attrValues",), {
477                 id_cmc_recipientNonce: RecipientNonce(),
478                 id_cmc_senderNonce: SenderNonce(),
479                 id_cmc_statusInfoV2: CMCStatusInfoV2(),
480                 id_cmc_transactionId: TransactionId(),
481             })),
482         ),
483     )})
484
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.
489
490 .. _bered_ctx:
491
492 BER encoding
493 ------------
494
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.
499
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
507   contain it.
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.
516
517 EOC (end-of-contents) token's length is taken in advance in object's
518 value length.
519
520 .. _allow_expl_oob_ctx:
521
522 Allow explicit tag out-of-bound
523 -------------------------------
524
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.
530
531 .. warning::
532
533    This option should be used only for skipping some decode errors, just
534    to see the decoded structure somehow.
535
536 Base Obj
537 --------
538 .. autoclass:: pyderasn.Obj
539    :members:
540
541 Primitive types
542 ---------------
543
544 Boolean
545 _______
546 .. autoclass:: pyderasn.Boolean
547    :members: __init__
548
549 Integer
550 _______
551 .. autoclass:: pyderasn.Integer
552    :members: __init__, named
553
554 BitString
555 _________
556 .. autoclass:: pyderasn.BitString
557    :members: __init__, bit_len, named
558
559 OctetString
560 ___________
561 .. autoclass:: pyderasn.OctetString
562    :members: __init__
563
564 Null
565 ____
566 .. autoclass:: pyderasn.Null
567    :members: __init__
568
569 ObjectIdentifier
570 ________________
571 .. autoclass:: pyderasn.ObjectIdentifier
572    :members: __init__
573
574 Enumerated
575 __________
576 .. autoclass:: pyderasn.Enumerated
577
578 CommonString
579 ____________
580 .. autoclass:: pyderasn.CommonString
581
582 NumericString
583 _____________
584 .. autoclass:: pyderasn.NumericString
585
586 PrintableString
587 _______________
588 .. autoclass:: pyderasn.PrintableString
589    :members: __init__, allow_asterisk, allow_ampersand
590
591 UTCTime
592 _______
593 .. autoclass:: pyderasn.UTCTime
594    :members: __init__, todatetime
595
596 GeneralizedTime
597 _______________
598 .. autoclass:: pyderasn.GeneralizedTime
599    :members: __init__, todatetime
600
601 Special types
602 -------------
603
604 Choice
605 ______
606 .. autoclass:: pyderasn.Choice
607    :members: __init__, choice, value
608
609 PrimitiveTypes
610 ______________
611 .. autoclass:: PrimitiveTypes
612
613 Any
614 ___
615 .. autoclass:: pyderasn.Any
616    :members: __init__
617
618 Constructed types
619 -----------------
620
621 Sequence
622 ________
623 .. autoclass:: pyderasn.Sequence
624    :members: __init__
625
626 Set
627 ___
628 .. autoclass:: pyderasn.Set
629    :members: __init__
630
631 SequenceOf
632 __________
633 .. autoclass:: pyderasn.SequenceOf
634    :members: __init__
635
636 SetOf
637 _____
638 .. autoclass:: pyderasn.SetOf
639    :members: __init__
640
641 Various
642 -------
643
644 .. autofunction:: pyderasn.abs_decode_path
645 .. autofunction:: pyderasn.colonize_hex
646 .. autofunction:: pyderasn.encode_cer
647 .. autofunction:: pyderasn.hexenc
648 .. autofunction:: pyderasn.hexdec
649 .. autofunction:: pyderasn.tag_encode
650 .. autofunction:: pyderasn.tag_decode
651 .. autofunction:: pyderasn.tag_ctxp
652 .. autofunction:: pyderasn.tag_ctxc
653 .. autoclass:: pyderasn.DecodeError
654    :members: __init__
655 .. autoclass:: pyderasn.NotEnoughData
656 .. autoclass:: pyderasn.ExceedingData
657 .. autoclass:: pyderasn.LenIndefForm
658 .. autoclass:: pyderasn.TagMismatch
659 .. autoclass:: pyderasn.InvalidLength
660 .. autoclass:: pyderasn.InvalidOID
661 .. autoclass:: pyderasn.ObjUnknown
662 .. autoclass:: pyderasn.ObjNotReady
663 .. autoclass:: pyderasn.InvalidValueType
664 .. autoclass:: pyderasn.BoundsError
665
666 .. _cmdline:
667
668 Command-line usage
669 ------------------
670
671 You can decode DER/BER files using command line abilities::
672
673     $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
674
675 If there is no schema for your file, then you can try parsing it without,
676 but of course IMPLICIT tags will often make it impossible. But result is
677 good enough for the certificate above::
678
679     $ python -m pyderasn path/to/file
680         0   [1,3,1604]  . >: SEQUENCE OF
681         4   [1,3,1453]  . . >: SEQUENCE OF
682         8   [0,0,   5]  . . . . >: [0] ANY
683                         . . . . . A0:03:02:01:02
684        13   [1,1,   3]  . . . . >: INTEGER 61595
685        18   [1,1,  13]  . . . . >: SEQUENCE OF
686        20   [1,1,   9]  . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
687        31   [1,1,   0]  . . . . . . >: NULL
688        33   [1,3, 274]  . . . . >: SEQUENCE OF
689        37   [1,1,  11]  . . . . . . >: SET OF
690        39   [1,1,   9]  . . . . . . . . >: SEQUENCE OF
691        41   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
692        46   [1,1,   2]  . . . . . . . . . . >: PrintableString PrintableString ES
693     [...]
694      1409   [1,1,  50]  . . . . . . >: SEQUENCE OF
695      1411   [1,1,   8]  . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
696      1421   [1,1,  38]  . . . . . . . . >: OCTET STRING 38 bytes
697                         . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
698                         . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
699                         . . . . . . . . . 61:2E:63:6F:6D:2F
700      1461   [1,1,  13]  . . >: SEQUENCE OF
701      1463   [1,1,   9]  . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
702      1474   [1,1,   0]  . . . . >: NULL
703      1476   [1,2, 129]  . . >: BIT STRING 1024 bits
704                         . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
705                         . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
706     [...]
707
708 Human readable OIDs
709 ___________________
710
711 If you have got dictionaries with ObjectIdentifiers, like example one
712 from ``tests/test_crts.py``::
713
714     stroid2name = {
715         "1.2.840.113549.1.1.1": "id-rsaEncryption",
716         "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
717         [...]
718         "2.5.4.10": "id-at-organizationName",
719         "2.5.4.11": "id-at-organizationalUnitName",
720     }
721
722 then you can pass it to pretty printer to see human readable OIDs::
723
724     $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
725     [...]
726        37   [1,1,  11]  . . . . . . >: SET OF
727        39   [1,1,   9]  . . . . . . . . >: SEQUENCE OF
728        41   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
729        46   [1,1,   2]  . . . . . . . . . . >: PrintableString PrintableString ES
730        50   [1,1,  18]  . . . . . . >: SET OF
731        52   [1,1,  16]  . . . . . . . . >: SEQUENCE OF
732        54   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
733        59   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
734        70   [1,1,  18]  . . . . . . >: SET OF
735        72   [1,1,  16]  . . . . . . . . >: SEQUENCE OF
736        74   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
737        79   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
738     [...]
739
740 Decode paths
741 ____________
742
743 Each decoded element has so-called decode path: sequence of structure
744 names it is passing during the decode process. Each element has its own
745 unique path inside the whole ASN.1 tree. You can print it out with
746 ``--print-decode-path`` option::
747
748     $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
749        0    [1,3,1604]  Certificate SEQUENCE []
750        4    [1,3,1453]   . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
751       10-2  [1,1,   1]   . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
752       13    [1,1,   3]   . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
753       18    [1,1,  13]   . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
754       20    [1,1,   9]   . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
755       31    [0,0,   2]   . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
756                          . . . . 05:00
757       33    [0,0, 278]   . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
758       33    [1,3, 274]   . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
759       37    [1,1,  11]   . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
760       39    [1,1,   9]   . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
761       41    [1,1,   3]   . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
762       46    [0,0,   4]   . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
763                          . . . . . . . 13:02:45:53
764       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]
765     [...]
766
767 Now you can print only the specified tree, for example signature algorithm::
768
769     $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
770       18    [1,1,  13]  AlgorithmIdentifier SEQUENCE
771       20    [1,1,   9]   . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
772       31    [0,0,   2]   . parameters: [UNIV 5] ANY OPTIONAL
773                          . . 05:00
774 """
775
776 from codecs import getdecoder
777 from codecs import getencoder
778 from collections import namedtuple
779 from collections import OrderedDict
780 from copy import copy
781 from datetime import datetime
782 from datetime import timedelta
783 from io import BytesIO
784 from math import ceil
785 from operator import attrgetter
786 from string import ascii_letters
787 from string import digits
788 from sys import version_info
789 from unicodedata import category as unicat
790
791 from six import add_metaclass
792 from six import binary_type
793 from six import byte2int
794 from six import indexbytes
795 from six import int2byte
796 from six import integer_types
797 from six import iterbytes
798 from six import iteritems
799 from six import itervalues
800 from six import PY2
801 from six import string_types
802 from six import text_type
803 from six import unichr as six_unichr
804 from six.moves import xrange as six_xrange
805
806
807 try:
808     from termcolor import colored
809 except ImportError:  # pragma: no cover
810     def colored(what, *args, **kwargs):
811         return what
812
813 __version__ = "7.0"
814
815 __all__ = (
816     "Any",
817     "BitString",
818     "BMPString",
819     "Boolean",
820     "BoundsError",
821     "Choice",
822     "DecodeError",
823     "DecodePathDefBy",
824     "encode_cer",
825     "Enumerated",
826     "ExceedingData",
827     "GeneralizedTime",
828     "GeneralString",
829     "GraphicString",
830     "hexdec",
831     "hexenc",
832     "IA5String",
833     "Integer",
834     "InvalidLength",
835     "InvalidOID",
836     "InvalidValueType",
837     "ISO646String",
838     "LenIndefForm",
839     "NotEnoughData",
840     "Null",
841     "NumericString",
842     "obj_by_path",
843     "ObjectIdentifier",
844     "ObjNotReady",
845     "ObjUnknown",
846     "OctetString",
847     "PrimitiveTypes",
848     "PrintableString",
849     "Sequence",
850     "SequenceOf",
851     "Set",
852     "SetOf",
853     "T61String",
854     "tag_ctxc",
855     "tag_ctxp",
856     "tag_decode",
857     "TagClassApplication",
858     "TagClassContext",
859     "TagClassPrivate",
860     "TagClassUniversal",
861     "TagFormConstructed",
862     "TagFormPrimitive",
863     "TagMismatch",
864     "TeletexString",
865     "UniversalString",
866     "UTCTime",
867     "UTF8String",
868     "VideotexString",
869     "VisibleString",
870 )
871
872 TagClassUniversal = 0
873 TagClassApplication = 1 << 6
874 TagClassContext = 1 << 7
875 TagClassPrivate = 1 << 6 | 1 << 7
876 TagFormPrimitive = 0
877 TagFormConstructed = 1 << 5
878 TagClassReprs = {
879     TagClassContext: "",
880     TagClassApplication: "APPLICATION ",
881     TagClassPrivate: "PRIVATE ",
882     TagClassUniversal: "UNIV ",
883 }
884 EOC = b"\x00\x00"
885 EOC_LEN = len(EOC)
886 LENINDEF = b"\x80"  # length indefinite mark
887 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
888 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
889 SET01 = frozenset("01")
890 DECIMALS = frozenset(digits)
891 DECIMAL_SIGNS = ".,"
892
893
894 def pureint(value):
895     if not set(value) <= DECIMALS:
896         raise ValueError("non-pure integer")
897     return int(value)
898
899 def fractions2float(fractions_raw):
900     pureint(fractions_raw)
901     return float("0." + fractions_raw)
902
903
904 def get_def_by_path(defines_by_path, sub_decode_path):
905     """Get define by decode path
906     """
907     for path, define in defines_by_path:
908         if len(path) != len(sub_decode_path):
909             continue
910         for p1, p2 in zip(path, sub_decode_path):
911             if (not p1 is any) and (p1 != p2):
912                 break
913         else:
914             return define
915
916
917 ########################################################################
918 # Errors
919 ########################################################################
920
921 class ASN1Error(ValueError):
922     pass
923
924
925 class DecodeError(ASN1Error):
926     def __init__(self, msg="", klass=None, decode_path=(), offset=0):
927         """
928         :param str msg: reason of decode failing
929         :param klass: optional exact DecodeError inherited class (like
930                       :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
931                       :py:exc:`InvalidLength`)
932         :param decode_path: tuple of strings. It contains human
933                             readable names of the fields through which
934                             decoding process has passed
935         :param int offset: binary offset where failure happened
936         """
937         super(DecodeError, self).__init__()
938         self.msg = msg
939         self.klass = klass
940         self.decode_path = decode_path
941         self.offset = offset
942
943     def __str__(self):
944         return " ".join(
945             c for c in (
946                 "" if self.klass is None else self.klass.__name__,
947                 (
948                     ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
949                     if len(self.decode_path) > 0 else ""
950                 ),
951                 ("(at %d)" % self.offset) if self.offset > 0 else "",
952                 self.msg,
953             ) if c != ""
954         )
955
956     def __repr__(self):
957         return "%s(%s)" % (self.__class__.__name__, self)
958
959
960 class NotEnoughData(DecodeError):
961     pass
962
963
964 class ExceedingData(ASN1Error):
965     def __init__(self, nbytes):
966         super(ExceedingData, self).__init__()
967         self.nbytes = nbytes
968
969     def __str__(self):
970         return "%d trailing bytes" % self.nbytes
971
972     def __repr__(self):
973         return "%s(%s)" % (self.__class__.__name__, self)
974
975
976 class LenIndefForm(DecodeError):
977     pass
978
979
980 class TagMismatch(DecodeError):
981     pass
982
983
984 class InvalidLength(DecodeError):
985     pass
986
987
988 class InvalidOID(DecodeError):
989     pass
990
991
992 class ObjUnknown(ASN1Error):
993     def __init__(self, name):
994         super(ObjUnknown, self).__init__()
995         self.name = name
996
997     def __str__(self):
998         return "object is unknown: %s" % self.name
999
1000     def __repr__(self):
1001         return "%s(%s)" % (self.__class__.__name__, self)
1002
1003
1004 class ObjNotReady(ASN1Error):
1005     def __init__(self, name):
1006         super(ObjNotReady, self).__init__()
1007         self.name = name
1008
1009     def __str__(self):
1010         return "object is not ready: %s" % self.name
1011
1012     def __repr__(self):
1013         return "%s(%s)" % (self.__class__.__name__, self)
1014
1015
1016 class InvalidValueType(ASN1Error):
1017     def __init__(self, expected_types):
1018         super(InvalidValueType, self).__init__()
1019         self.expected_types = expected_types
1020
1021     def __str__(self):
1022         return "invalid value type, expected: %s" % ", ".join(
1023             [repr(t) for t in self.expected_types]
1024         )
1025
1026     def __repr__(self):
1027         return "%s(%s)" % (self.__class__.__name__, self)
1028
1029
1030 class BoundsError(ASN1Error):
1031     def __init__(self, bound_min, value, bound_max):
1032         super(BoundsError, self).__init__()
1033         self.bound_min = bound_min
1034         self.value = value
1035         self.bound_max = bound_max
1036
1037     def __str__(self):
1038         return "unsatisfied bounds: %s <= %s <= %s" % (
1039             self.bound_min,
1040             self.value,
1041             self.bound_max,
1042         )
1043
1044     def __repr__(self):
1045         return "%s(%s)" % (self.__class__.__name__, self)
1046
1047
1048 ########################################################################
1049 # Basic coders
1050 ########################################################################
1051
1052 _hexdecoder = getdecoder("hex")
1053 _hexencoder = getencoder("hex")
1054
1055
1056 def hexdec(data):
1057     """Binary data to hexadecimal string convert
1058     """
1059     return _hexdecoder(data)[0]
1060
1061
1062 def hexenc(data):
1063     """Hexadecimal string to binary data convert
1064     """
1065     return _hexencoder(data)[0].decode("ascii")
1066
1067
1068 def int_bytes_len(num, byte_len=8):
1069     if num == 0:
1070         return 1
1071     return int(ceil(float(num.bit_length()) / byte_len))
1072
1073
1074 def zero_ended_encode(num):
1075     octets = bytearray(int_bytes_len(num, 7))
1076     i = len(octets) - 1
1077     octets[i] = num & 0x7F
1078     num >>= 7
1079     i -= 1
1080     while num > 0:
1081         octets[i] = 0x80 | (num & 0x7F)
1082         num >>= 7
1083         i -= 1
1084     return bytes(octets)
1085
1086
1087 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
1088     """Encode tag to binary form
1089
1090     :param int num: tag's number
1091     :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
1092                       :py:data:`pyderasn.TagClassContext`,
1093                       :py:data:`pyderasn.TagClassApplication`,
1094                       :py:data:`pyderasn.TagClassPrivate`)
1095     :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
1096                      :py:data:`pyderasn.TagFormConstructed`)
1097     """
1098     if num < 31:
1099         # [XX|X|.....]
1100         return int2byte(klass | form | num)
1101     # [XX|X|11111][1.......][1.......] ... [0.......]
1102     return int2byte(klass | form | 31) + zero_ended_encode(num)
1103
1104
1105 def tag_decode(tag):
1106     """Decode tag from binary form
1107
1108     .. warning::
1109
1110        No validation is performed, assuming that it has already passed.
1111
1112     It returns tuple with three integers, as
1113     :py:func:`pyderasn.tag_encode` accepts.
1114     """
1115     first_octet = byte2int(tag)
1116     klass = first_octet & 0xC0
1117     form = first_octet & 0x20
1118     if first_octet & 0x1F < 0x1F:
1119         return (klass, form, first_octet & 0x1F)
1120     num = 0
1121     for octet in iterbytes(tag[1:]):
1122         num <<= 7
1123         num |= octet & 0x7F
1124     return (klass, form, num)
1125
1126
1127 def tag_ctxp(num):
1128     """Create CONTEXT PRIMITIVE tag
1129     """
1130     return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
1131
1132
1133 def tag_ctxc(num):
1134     """Create CONTEXT CONSTRUCTED tag
1135     """
1136     return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
1137
1138
1139 def tag_strip(data):
1140     """Take off tag from the data
1141
1142     :returns: (encoded tag, tag length, remaining data)
1143     """
1144     if len(data) == 0:
1145         raise NotEnoughData("no data at all")
1146     if byte2int(data) & 0x1F < 31:
1147         return data[:1], 1, data[1:]
1148     i = 0
1149     while True:
1150         i += 1
1151         if i == len(data):
1152             raise DecodeError("unfinished tag")
1153         if indexbytes(data, i) & 0x80 == 0:
1154             break
1155     i += 1
1156     return data[:i], i, data[i:]
1157
1158
1159 def len_encode(l):
1160     if l < 0x80:
1161         return int2byte(l)
1162     octets = bytearray(int_bytes_len(l) + 1)
1163     octets[0] = 0x80 | (len(octets) - 1)
1164     for i in six_xrange(len(octets) - 1, 0, -1):
1165         octets[i] = l & 0xFF
1166         l >>= 8
1167     return bytes(octets)
1168
1169
1170 def len_decode(data):
1171     """Decode length
1172
1173     :returns: (decoded length, length's length, remaining data)
1174     :raises LenIndefForm: if indefinite form encoding is met
1175     """
1176     if len(data) == 0:
1177         raise NotEnoughData("no data at all")
1178     first_octet = byte2int(data)
1179     if first_octet & 0x80 == 0:
1180         return first_octet, 1, data[1:]
1181     octets_num = first_octet & 0x7F
1182     if octets_num + 1 > len(data):
1183         raise NotEnoughData("encoded length is longer than data")
1184     if octets_num == 0:
1185         raise LenIndefForm()
1186     if byte2int(data[1:]) == 0:
1187         raise DecodeError("leading zeros")
1188     l = 0
1189     for v in iterbytes(data[1:1 + octets_num]):
1190         l = (l << 8) | v
1191     if l <= 127:
1192         raise DecodeError("long form instead of short one")
1193     return l, 1 + octets_num, data[1 + octets_num:]
1194
1195
1196 LEN1K = len_encode(1000)
1197
1198
1199 def write_full(writer, data):
1200     """Fully write provided data
1201
1202     BytesIO does not guarantee that the whole data will be written at once.
1203     """
1204     data = memoryview(data)
1205     written = 0
1206     while written != len(data):
1207         n = writer(data[written:])
1208         if n is None:
1209             raise ValueError("can not write to buf")
1210         written += n
1211
1212
1213 ########################################################################
1214 # Base class
1215 ########################################################################
1216
1217 class AutoAddSlots(type):
1218     def __new__(cls, name, bases, _dict):
1219         _dict["__slots__"] = _dict.get("__slots__", ())
1220         return type.__new__(cls, name, bases, _dict)
1221
1222
1223 BasicState = namedtuple("BasicState", (
1224     "version",
1225     "tag",
1226     "tag_order",
1227     "expl",
1228     "default",
1229     "optional",
1230     "offset",
1231     "llen",
1232     "vlen",
1233     "expl_lenindef",
1234     "lenindef",
1235     "ber_encoded",
1236 ), **NAMEDTUPLE_KWARGS)
1237
1238
1239 @add_metaclass(AutoAddSlots)
1240 class Obj(object):
1241     """Common ASN.1 object class
1242
1243     All ASN.1 types are inherited from it. It has metaclass that
1244     automatically adds ``__slots__`` to all inherited classes.
1245     """
1246     __slots__ = (
1247         "tag",
1248         "_tag_order",
1249         "_value",
1250         "_expl",
1251         "default",
1252         "optional",
1253         "offset",
1254         "llen",
1255         "vlen",
1256         "expl_lenindef",
1257         "lenindef",
1258         "ber_encoded",
1259     )
1260
1261     def __init__(
1262             self,
1263             impl=None,
1264             expl=None,
1265             default=None,
1266             optional=False,
1267             _decoded=(0, 0, 0),
1268     ):
1269         self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1270         self._expl = getattr(self, "expl", None) if expl is None else expl
1271         if self.tag != self.tag_default and self._expl is not None:
1272             raise ValueError("implicit and explicit tags can not be set simultaneously")
1273         if self.tag is None:
1274             self._tag_order = None
1275         else:
1276             tag_class, _, tag_num = tag_decode(
1277                 self.tag if self._expl is None else self._expl
1278             )
1279             self._tag_order = (tag_class, tag_num)
1280         if default is not None:
1281             optional = True
1282         self.optional = optional
1283         self.offset, self.llen, self.vlen = _decoded
1284         self.default = None
1285         self.expl_lenindef = False
1286         self.lenindef = False
1287         self.ber_encoded = False
1288
1289     @property
1290     def ready(self):  # pragma: no cover
1291         """Is object ready to be encoded?
1292         """
1293         raise NotImplementedError()
1294
1295     def _assert_ready(self):
1296         if not self.ready:
1297             raise ObjNotReady(self.__class__.__name__)
1298
1299     @property
1300     def bered(self):
1301         """Is either object or any elements inside is BER encoded?
1302         """
1303         return self.expl_lenindef or self.lenindef or self.ber_encoded
1304
1305     @property
1306     def decoded(self):
1307         """Is object decoded?
1308         """
1309         return (self.llen + self.vlen) > 0
1310
1311     def __getstate__(self):  # pragma: no cover
1312         """Used for making safe to be mutable pickleable copies
1313         """
1314         raise NotImplementedError()
1315
1316     def __setstate__(self, state):
1317         if state.version != __version__:
1318             raise ValueError("data is pickled by different PyDERASN version")
1319         self.tag = state.tag
1320         self._tag_order = state.tag_order
1321         self._expl = state.expl
1322         self.default = state.default
1323         self.optional = state.optional
1324         self.offset = state.offset
1325         self.llen = state.llen
1326         self.vlen = state.vlen
1327         self.expl_lenindef = state.expl_lenindef
1328         self.lenindef = state.lenindef
1329         self.ber_encoded = state.ber_encoded
1330
1331     @property
1332     def tag_order(self):
1333         """Tag's (class, number) used for DER/CER sorting
1334         """
1335         return self._tag_order
1336
1337     @property
1338     def tag_order_cer(self):
1339         return self.tag_order
1340
1341     @property
1342     def tlen(self):
1343         """See :ref:`decoding`
1344         """
1345         return len(self.tag)
1346
1347     @property
1348     def tlvlen(self):
1349         """See :ref:`decoding`
1350         """
1351         return self.tlen + self.llen + self.vlen
1352
1353     def __str__(self):  # pragma: no cover
1354         return self.__bytes__() if PY2 else self.__unicode__()
1355
1356     def __ne__(self, their):
1357         return not(self == their)
1358
1359     def __gt__(self, their):  # pragma: no cover
1360         return not(self < their)
1361
1362     def __le__(self, their):  # pragma: no cover
1363         return (self == their) or (self < their)
1364
1365     def __ge__(self, their):  # pragma: no cover
1366         return (self == their) or (self > their)
1367
1368     def _encode(self):  # pragma: no cover
1369         raise NotImplementedError()
1370
1371     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):  # pragma: no cover
1372         yield NotImplemented
1373
1374     def encode(self):
1375         """Encode the structure
1376
1377         :returns: DER representation
1378         """
1379         raw = self._encode()
1380         if self._expl is None:
1381             return raw
1382         return b"".join((self._expl, len_encode(len(raw)), raw))
1383
1384     def encode_cer(self, writer):
1385         if self._expl is not None:
1386             write_full(writer, self._expl + LENINDEF)
1387         if getattr(self, "der_forced", False):
1388             write_full(writer, self._encode())
1389         else:
1390             self._encode_cer(writer)
1391         if self._expl is not None:
1392             write_full(writer, EOC)
1393
1394     def _encode_cer(self, writer):
1395         write_full(writer, self._encode())
1396
1397     def hexencode(self):
1398         """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1399         """
1400         return hexenc(self.encode())
1401
1402     def decode(
1403             self,
1404             data,
1405             offset=0,
1406             leavemm=False,
1407             decode_path=(),
1408             ctx=None,
1409             tag_only=False,
1410             _ctx_immutable=True,
1411     ):
1412         result = next(self.decode_evgen(
1413             data,
1414             offset,
1415             leavemm,
1416             decode_path,
1417             ctx,
1418             tag_only,
1419             _ctx_immutable,
1420             _evgen_mode=False,
1421         ))
1422         if result is None:
1423             return None
1424         _, obj, tail = result
1425         return obj, tail
1426
1427     def decode_evgen(
1428             self,
1429             data,
1430             offset=0,
1431             leavemm=False,
1432             decode_path=(),
1433             ctx=None,
1434             tag_only=False,
1435             _ctx_immutable=True,
1436             _evgen_mode=True,
1437     ):
1438         """Decode the data
1439
1440         :param data: either binary or memoryview
1441         :param int offset: initial data's offset
1442         :param bool leavemm: do we need to leave memoryview of remaining
1443                     data as is, or convert it to bytes otherwise
1444         :param ctx: optional :ref:`context <ctx>` governing decoding process
1445         :param tag_only: decode only the tag, without length and contents
1446                          (used only in Choice and Set structures, trying to
1447                          determine if tag satisfies the schema)
1448         :param _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1449                                before using it?
1450         :returns: (Obj, remaining data)
1451
1452         .. seealso:: :ref:`decoding`
1453         """
1454         if ctx is None:
1455             ctx = {}
1456         elif _ctx_immutable:
1457             ctx = copy(ctx)
1458         tlv = memoryview(data)
1459         if (
1460                 _evgen_mode and
1461                 get_def_by_path(ctx.get("evgen_mode_upto", ()), decode_path) is not None
1462         ):
1463             _evgen_mode = False
1464         if self._expl is None:
1465             for result in self._decode(
1466                     tlv,
1467                     offset=offset,
1468                     decode_path=decode_path,
1469                     ctx=ctx,
1470                     tag_only=tag_only,
1471                     evgen_mode=_evgen_mode,
1472             ):
1473                 if tag_only:
1474                     yield None
1475                     return
1476                 _decode_path, obj, tail = result
1477                 if not _decode_path is decode_path:
1478                     yield result
1479         else:
1480             try:
1481                 t, tlen, lv = tag_strip(tlv)
1482             except DecodeError as err:
1483                 raise err.__class__(
1484                     msg=err.msg,
1485                     klass=self.__class__,
1486                     decode_path=decode_path,
1487                     offset=offset,
1488                 )
1489             if t != self._expl:
1490                 raise TagMismatch(
1491                     klass=self.__class__,
1492                     decode_path=decode_path,
1493                     offset=offset,
1494                 )
1495             try:
1496                 l, llen, v = len_decode(lv)
1497             except LenIndefForm as err:
1498                 if not ctx.get("bered", False):
1499                     raise err.__class__(
1500                         msg=err.msg,
1501                         klass=self.__class__,
1502                         decode_path=decode_path,
1503                         offset=offset,
1504                     )
1505                 llen, v = 1, lv[1:]
1506                 offset += tlen + llen
1507                 for result in self._decode(
1508                         v,
1509                         offset=offset,
1510                         decode_path=decode_path,
1511                         ctx=ctx,
1512                         tag_only=tag_only,
1513                         evgen_mode=_evgen_mode,
1514                 ):
1515                     if tag_only:  # pragma: no cover
1516                         yield None
1517                         return
1518                     _decode_path, obj, tail = result
1519                     if not _decode_path is decode_path:
1520                         yield result
1521                 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1522                 if eoc_expected.tobytes() != EOC:
1523                     raise DecodeError(
1524                         "no EOC",
1525                         klass=self.__class__,
1526                         decode_path=decode_path,
1527                         offset=offset,
1528                     )
1529                 obj.vlen += EOC_LEN
1530                 obj.expl_lenindef = True
1531             except DecodeError as err:
1532                 raise err.__class__(
1533                     msg=err.msg,
1534                     klass=self.__class__,
1535                     decode_path=decode_path,
1536                     offset=offset,
1537                 )
1538             else:
1539                 if l > len(v):
1540                     raise NotEnoughData(
1541                         "encoded length is longer than data",
1542                         klass=self.__class__,
1543                         decode_path=decode_path,
1544                         offset=offset,
1545                     )
1546                 for result in self._decode(
1547                         v,
1548                         offset=offset + tlen + llen,
1549                         decode_path=decode_path,
1550                         ctx=ctx,
1551                         tag_only=tag_only,
1552                         evgen_mode=_evgen_mode,
1553                 ):
1554                     if tag_only:  # pragma: no cover
1555                         yield None
1556                         return
1557                     _decode_path, obj, tail = result
1558                     if not _decode_path is decode_path:
1559                         yield result
1560                 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1561                     raise DecodeError(
1562                         "explicit tag out-of-bound, longer than data",
1563                         klass=self.__class__,
1564                         decode_path=decode_path,
1565                         offset=offset,
1566                     )
1567         yield decode_path, obj, (tail if leavemm else tail.tobytes())
1568
1569     def decod(self, data, offset=0, decode_path=(), ctx=None):
1570         """Decode the data, check that tail is empty
1571
1572         :raises ExceedingData: if tail is not empty
1573
1574         This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
1575         (decode without tail) that also checks that there is no
1576         trailing data left.
1577         """
1578         obj, tail = self.decode(
1579             data,
1580             offset=offset,
1581             decode_path=decode_path,
1582             ctx=ctx,
1583             leavemm=True,
1584         )
1585         if len(tail) > 0:
1586             raise ExceedingData(len(tail))
1587         return obj
1588
1589     def hexdecode(self, data, *args, **kwargs):
1590         """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
1591         """
1592         return self.decode(hexdec(data), *args, **kwargs)
1593
1594     def hexdecod(self, data, *args, **kwargs):
1595         """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
1596         """
1597         return self.decod(hexdec(data), *args, **kwargs)
1598
1599     @property
1600     def expled(self):
1601         """See :ref:`decoding`
1602         """
1603         return self._expl is not None
1604
1605     @property
1606     def expl_tag(self):
1607         """See :ref:`decoding`
1608         """
1609         return self._expl
1610
1611     @property
1612     def expl_tlen(self):
1613         """See :ref:`decoding`
1614         """
1615         return len(self._expl)
1616
1617     @property
1618     def expl_llen(self):
1619         """See :ref:`decoding`
1620         """
1621         if self.expl_lenindef:
1622             return 1
1623         return len(len_encode(self.tlvlen))
1624
1625     @property
1626     def expl_offset(self):
1627         """See :ref:`decoding`
1628         """
1629         return self.offset - self.expl_tlen - self.expl_llen
1630
1631     @property
1632     def expl_vlen(self):
1633         """See :ref:`decoding`
1634         """
1635         return self.tlvlen
1636
1637     @property
1638     def expl_tlvlen(self):
1639         """See :ref:`decoding`
1640         """
1641         return self.expl_tlen + self.expl_llen + self.expl_vlen
1642
1643     @property
1644     def fulloffset(self):
1645         """See :ref:`decoding`
1646         """
1647         return self.expl_offset if self.expled else self.offset
1648
1649     @property
1650     def fulllen(self):
1651         """See :ref:`decoding`
1652         """
1653         return self.expl_tlvlen if self.expled else self.tlvlen
1654
1655     def pps_lenindef(self, decode_path):
1656         if self.lenindef and not (
1657                 getattr(self, "defined", None) is not None and
1658                 self.defined[1].lenindef
1659         ):
1660             yield _pp(
1661                 asn1_type_name="EOC",
1662                 obj_name="",
1663                 decode_path=decode_path,
1664                 offset=(
1665                     self.offset + self.tlvlen -
1666                     (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1667                 ),
1668                 tlen=1,
1669                 llen=1,
1670                 vlen=0,
1671                 ber_encoded=True,
1672                 bered=True,
1673             )
1674         if self.expl_lenindef:
1675             yield _pp(
1676                 asn1_type_name="EOC",
1677                 obj_name="EXPLICIT",
1678                 decode_path=decode_path,
1679                 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1680                 tlen=1,
1681                 llen=1,
1682                 vlen=0,
1683                 ber_encoded=True,
1684                 bered=True,
1685             )
1686
1687
1688 def encode_cer(obj):
1689     """Encode to CER in memory
1690     """
1691     buf = BytesIO()
1692     obj.encode_cer(buf.write)
1693     return buf.getvalue()
1694
1695
1696 class DecodePathDefBy(object):
1697     """DEFINED BY representation inside decode path
1698     """
1699     __slots__ = ("defined_by",)
1700
1701     def __init__(self, defined_by):
1702         self.defined_by = defined_by
1703
1704     def __ne__(self, their):
1705         return not(self == their)
1706
1707     def __eq__(self, their):
1708         if not isinstance(their, self.__class__):
1709             return False
1710         return self.defined_by == their.defined_by
1711
1712     def __str__(self):
1713         return "DEFINED BY " + str(self.defined_by)
1714
1715     def __repr__(self):
1716         return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1717
1718
1719 ########################################################################
1720 # Pretty printing
1721 ########################################################################
1722
1723 PP = namedtuple("PP", (
1724     "obj",
1725     "asn1_type_name",
1726     "obj_name",
1727     "decode_path",
1728     "value",
1729     "blob",
1730     "optional",
1731     "default",
1732     "impl",
1733     "expl",
1734     "offset",
1735     "tlen",
1736     "llen",
1737     "vlen",
1738     "expl_offset",
1739     "expl_tlen",
1740     "expl_llen",
1741     "expl_vlen",
1742     "expl_lenindef",
1743     "lenindef",
1744     "ber_encoded",
1745     "bered",
1746 ), **NAMEDTUPLE_KWARGS)
1747
1748
1749 def _pp(
1750         obj=None,
1751         asn1_type_name="unknown",
1752         obj_name="unknown",
1753         decode_path=(),
1754         value=None,
1755         blob=None,
1756         optional=False,
1757         default=False,
1758         impl=None,
1759         expl=None,
1760         offset=0,
1761         tlen=0,
1762         llen=0,
1763         vlen=0,
1764         expl_offset=None,
1765         expl_tlen=None,
1766         expl_llen=None,
1767         expl_vlen=None,
1768         expl_lenindef=False,
1769         lenindef=False,
1770         ber_encoded=False,
1771         bered=False,
1772 ):
1773     return PP(
1774         obj,
1775         asn1_type_name,
1776         obj_name,
1777         decode_path,
1778         value,
1779         blob,
1780         optional,
1781         default,
1782         impl,
1783         expl,
1784         offset,
1785         tlen,
1786         llen,
1787         vlen,
1788         expl_offset,
1789         expl_tlen,
1790         expl_llen,
1791         expl_vlen,
1792         expl_lenindef,
1793         lenindef,
1794         ber_encoded,
1795         bered,
1796     )
1797
1798
1799 def _colourize(what, colour, with_colours, attrs=("bold",)):
1800     return colored(what, colour, attrs=attrs) if with_colours else what
1801
1802
1803 def colonize_hex(hexed):
1804     """Separate hexadecimal string with colons
1805     """
1806     return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1807
1808
1809 def pp_console_row(
1810         pp,
1811         oid_maps=(),
1812         with_offsets=False,
1813         with_blob=True,
1814         with_colours=False,
1815         with_decode_path=False,
1816         decode_path_len_decrease=0,
1817 ):
1818     cols = []
1819     if with_offsets:
1820         col = "%5d%s%s" % (
1821             pp.offset,
1822             (
1823                 "  " if pp.expl_offset is None else
1824                 ("-%d" % (pp.offset - pp.expl_offset))
1825             ),
1826             LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1827         )
1828         col = _colourize(col, "red", with_colours, ())
1829         col += _colourize("B", "red", with_colours) if pp.bered else " "
1830         cols.append(col)
1831         col = "[%d,%d,%4d]%s" % (
1832             pp.tlen,
1833             pp.llen,
1834             pp.vlen,
1835             LENINDEF_PP_CHAR if pp.lenindef else " "
1836         )
1837         col = _colourize(col, "green", with_colours, ())
1838         cols.append(col)
1839     decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1840     if decode_path_len > 0:
1841         cols.append(" ." * decode_path_len)
1842         ent = pp.decode_path[-1]
1843         if isinstance(ent, DecodePathDefBy):
1844             cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1845             value = str(ent.defined_by)
1846             oid_name = None
1847             if (
1848                     len(oid_maps) > 0 and
1849                     ent.defined_by.asn1_type_name ==
1850                     ObjectIdentifier.asn1_type_name
1851             ):
1852                 for oid_map in oid_maps:
1853                     oid_name = oid_map.get(value)
1854                     if oid_name is not None:
1855                         cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1856                         break
1857             if oid_name is None:
1858                 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1859         else:
1860             cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1861     if pp.expl is not None:
1862         klass, _, num = pp.expl
1863         col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1864         cols.append(_colourize(col, "blue", with_colours))
1865     if pp.impl is not None:
1866         klass, _, num = pp.impl
1867         col = "[%s%d]" % (TagClassReprs[klass], num)
1868         cols.append(_colourize(col, "blue", with_colours))
1869     if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1870         cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1871     if pp.ber_encoded:
1872         cols.append(_colourize("BER", "red", with_colours))
1873     cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1874     if pp.value is not None:
1875         value = pp.value
1876         cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1877         if (
1878                 len(oid_maps) > 0 and
1879                 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1880         ):
1881             for oid_map in oid_maps:
1882                 oid_name = oid_map.get(value)
1883                 if oid_name is not None:
1884                     cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1885                     break
1886         if pp.asn1_type_name == Integer.asn1_type_name:
1887             hex_repr = hex(int(pp.obj._value))[2:].upper()
1888             if len(hex_repr) % 2 != 0:
1889                 hex_repr = "0" + hex_repr
1890             cols.append(_colourize(
1891                 "(%s)" % colonize_hex(hex_repr),
1892                 "green",
1893                 with_colours,
1894             ))
1895     if with_blob:
1896         if pp.blob.__class__ == binary_type:
1897             cols.append(hexenc(pp.blob))
1898         elif pp.blob.__class__ == tuple:
1899             cols.append(", ".join(pp.blob))
1900     if pp.optional:
1901         cols.append(_colourize("OPTIONAL", "red", with_colours))
1902     if pp.default:
1903         cols.append(_colourize("DEFAULT", "red", with_colours))
1904     if with_decode_path:
1905         cols.append(_colourize(
1906             "[%s]" % ":".join(str(p) for p in pp.decode_path),
1907             "grey",
1908             with_colours,
1909         ))
1910     return " ".join(cols)
1911
1912
1913 def pp_console_blob(pp, decode_path_len_decrease=0):
1914     cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1915     decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1916     if decode_path_len > 0:
1917         cols.append(" ." * (decode_path_len + 1))
1918     if pp.blob.__class__ == binary_type:
1919         blob = hexenc(pp.blob).upper()
1920         for i in six_xrange(0, len(blob), 32):
1921             chunk = blob[i:i + 32]
1922             yield " ".join(cols + [colonize_hex(chunk)])
1923     elif pp.blob.__class__ == tuple:
1924         yield " ".join(cols + [", ".join(pp.blob)])
1925
1926
1927 def pprint(
1928         obj,
1929         oid_maps=(),
1930         big_blobs=False,
1931         with_colours=False,
1932         with_decode_path=False,
1933         decode_path_only=(),
1934 ):
1935     """Pretty print object
1936
1937     :param Obj obj: object you want to pretty print
1938     :param oid_maps: list of ``str(OID) <-> human readable string`` dictionary.
1939                      Its human readable form is printed when OID is met
1940     :param big_blobs: if large binary objects are met (like OctetString
1941                       values), do we need to print them too, on separate
1942                       lines
1943     :param with_colours: colourize output, if ``termcolor`` library
1944                          is available
1945     :param with_decode_path: print decode path
1946     :param decode_path_only: print only that specified decode path
1947     """
1948     def _pprint_pps(pps):
1949         for pp in pps:
1950             if hasattr(pp, "_fields"):
1951                 if (
1952                         decode_path_only != () and
1953                         tuple(
1954                             str(p) for p in pp.decode_path[:len(decode_path_only)]
1955                         ) != decode_path_only
1956                 ):
1957                     continue
1958                 if big_blobs:
1959                     yield pp_console_row(
1960                         pp,
1961                         oid_maps=oid_maps,
1962                         with_offsets=True,
1963                         with_blob=False,
1964                         with_colours=with_colours,
1965                         with_decode_path=with_decode_path,
1966                         decode_path_len_decrease=len(decode_path_only),
1967                     )
1968                     for row in pp_console_blob(
1969                             pp,
1970                             decode_path_len_decrease=len(decode_path_only),
1971                     ):
1972                         yield row
1973                 else:
1974                     yield pp_console_row(
1975                         pp,
1976                         oid_maps=oid_maps,
1977                         with_offsets=True,
1978                         with_blob=True,
1979                         with_colours=with_colours,
1980                         with_decode_path=with_decode_path,
1981                         decode_path_len_decrease=len(decode_path_only),
1982                     )
1983             else:
1984                 for row in _pprint_pps(pp):
1985                     yield row
1986     return "\n".join(_pprint_pps(obj.pps()))
1987
1988
1989 ########################################################################
1990 # ASN.1 primitive types
1991 ########################################################################
1992
1993 BooleanState = namedtuple(
1994     "BooleanState",
1995     BasicState._fields + ("value",),
1996     **NAMEDTUPLE_KWARGS
1997 )
1998
1999
2000 class Boolean(Obj):
2001     """``BOOLEAN`` boolean type
2002
2003     >>> b = Boolean(True)
2004     BOOLEAN True
2005     >>> b == Boolean(True)
2006     True
2007     >>> bool(b)
2008     True
2009     """
2010     __slots__ = ()
2011     tag_default = tag_encode(1)
2012     asn1_type_name = "BOOLEAN"
2013
2014     def __init__(
2015             self,
2016             value=None,
2017             impl=None,
2018             expl=None,
2019             default=None,
2020             optional=False,
2021             _decoded=(0, 0, 0),
2022     ):
2023         """
2024         :param value: set the value. Either boolean type, or
2025                       :py:class:`pyderasn.Boolean` object
2026         :param bytes impl: override default tag with ``IMPLICIT`` one
2027         :param bytes expl: override default tag with ``EXPLICIT`` one
2028         :param default: set default value. Type same as in ``value``
2029         :param bool optional: is object ``OPTIONAL`` in sequence
2030         """
2031         super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
2032         self._value = None if value is None else self._value_sanitize(value)
2033         if default is not None:
2034             default = self._value_sanitize(default)
2035             self.default = self.__class__(
2036                 value=default,
2037                 impl=self.tag,
2038                 expl=self._expl,
2039             )
2040             if value is None:
2041                 self._value = default
2042
2043     def _value_sanitize(self, value):
2044         if value.__class__ == bool:
2045             return value
2046         if issubclass(value.__class__, Boolean):
2047             return value._value
2048         raise InvalidValueType((self.__class__, bool))
2049
2050     @property
2051     def ready(self):
2052         return self._value is not None
2053
2054     def __getstate__(self):
2055         return BooleanState(
2056             __version__,
2057             self.tag,
2058             self._tag_order,
2059             self._expl,
2060             self.default,
2061             self.optional,
2062             self.offset,
2063             self.llen,
2064             self.vlen,
2065             self.expl_lenindef,
2066             self.lenindef,
2067             self.ber_encoded,
2068             self._value,
2069         )
2070
2071     def __setstate__(self, state):
2072         super(Boolean, self).__setstate__(state)
2073         self._value = state.value
2074
2075     def __nonzero__(self):
2076         self._assert_ready()
2077         return self._value
2078
2079     def __bool__(self):
2080         self._assert_ready()
2081         return self._value
2082
2083     def __eq__(self, their):
2084         if their.__class__ == bool:
2085             return self._value == their
2086         if not issubclass(their.__class__, Boolean):
2087             return False
2088         return (
2089             self._value == their._value and
2090             self.tag == their.tag and
2091             self._expl == their._expl
2092         )
2093
2094     def __call__(
2095             self,
2096             value=None,
2097             impl=None,
2098             expl=None,
2099             default=None,
2100             optional=None,
2101     ):
2102         return self.__class__(
2103             value=value,
2104             impl=self.tag if impl is None else impl,
2105             expl=self._expl if expl is None else expl,
2106             default=self.default if default is None else default,
2107             optional=self.optional if optional is None else optional,
2108         )
2109
2110     def _encode(self):
2111         self._assert_ready()
2112         return b"".join((
2113             self.tag,
2114             len_encode(1),
2115             (b"\xFF" if self._value else b"\x00"),
2116         ))
2117
2118     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2119         try:
2120             t, _, lv = tag_strip(tlv)
2121         except DecodeError as err:
2122             raise err.__class__(
2123                 msg=err.msg,
2124                 klass=self.__class__,
2125                 decode_path=decode_path,
2126                 offset=offset,
2127             )
2128         if t != self.tag:
2129             raise TagMismatch(
2130                 klass=self.__class__,
2131                 decode_path=decode_path,
2132                 offset=offset,
2133             )
2134         if tag_only:
2135             yield None
2136             return
2137         try:
2138             l, _, v = len_decode(lv)
2139         except DecodeError as err:
2140             raise err.__class__(
2141                 msg=err.msg,
2142                 klass=self.__class__,
2143                 decode_path=decode_path,
2144                 offset=offset,
2145             )
2146         if l != 1:
2147             raise InvalidLength(
2148                 "Boolean's length must be equal to 1",
2149                 klass=self.__class__,
2150                 decode_path=decode_path,
2151                 offset=offset,
2152             )
2153         if l > len(v):
2154             raise NotEnoughData(
2155                 "encoded length is longer than data",
2156                 klass=self.__class__,
2157                 decode_path=decode_path,
2158                 offset=offset,
2159             )
2160         first_octet = byte2int(v)
2161         ber_encoded = False
2162         if first_octet == 0:
2163             value = False
2164         elif first_octet == 0xFF:
2165             value = True
2166         elif ctx.get("bered", False):
2167             value = True
2168             ber_encoded = True
2169         else:
2170             raise DecodeError(
2171                 "unacceptable Boolean value",
2172                 klass=self.__class__,
2173                 decode_path=decode_path,
2174                 offset=offset,
2175             )
2176         obj = self.__class__(
2177             value=value,
2178             impl=self.tag,
2179             expl=self._expl,
2180             default=self.default,
2181             optional=self.optional,
2182             _decoded=(offset, 1, 1),
2183         )
2184         obj.ber_encoded = ber_encoded
2185         yield decode_path, obj, v[1:]
2186
2187     def __repr__(self):
2188         return pp_console_row(next(self.pps()))
2189
2190     def pps(self, decode_path=()):
2191         yield _pp(
2192             obj=self,
2193             asn1_type_name=self.asn1_type_name,
2194             obj_name=self.__class__.__name__,
2195             decode_path=decode_path,
2196             value=str(self._value) if self.ready else None,
2197             optional=self.optional,
2198             default=self == self.default,
2199             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2200             expl=None if self._expl is None else tag_decode(self._expl),
2201             offset=self.offset,
2202             tlen=self.tlen,
2203             llen=self.llen,
2204             vlen=self.vlen,
2205             expl_offset=self.expl_offset if self.expled else None,
2206             expl_tlen=self.expl_tlen if self.expled else None,
2207             expl_llen=self.expl_llen if self.expled else None,
2208             expl_vlen=self.expl_vlen if self.expled else None,
2209             expl_lenindef=self.expl_lenindef,
2210             ber_encoded=self.ber_encoded,
2211             bered=self.bered,
2212         )
2213         for pp in self.pps_lenindef(decode_path):
2214             yield pp
2215
2216
2217 IntegerState = namedtuple(
2218     "IntegerState",
2219     BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2220     **NAMEDTUPLE_KWARGS
2221 )
2222
2223
2224 class Integer(Obj):
2225     """``INTEGER`` integer type
2226
2227     >>> b = Integer(-123)
2228     INTEGER -123
2229     >>> b == Integer(-123)
2230     True
2231     >>> int(b)
2232     -123
2233
2234     >>> Integer(2, bounds=(1, 3))
2235     INTEGER 2
2236     >>> Integer(5, bounds=(1, 3))
2237     Traceback (most recent call last):
2238     pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2239
2240     ::
2241
2242         class Version(Integer):
2243             schema = (
2244                 ("v1", 0),
2245                 ("v2", 1),
2246                 ("v3", 2),
2247             )
2248
2249     >>> v = Version("v1")
2250     Version INTEGER v1
2251     >>> int(v)
2252     0
2253     >>> v.named
2254     'v1'
2255     >>> v.specs
2256     {'v3': 2, 'v1': 0, 'v2': 1}
2257     """
2258     __slots__ = ("specs", "_bound_min", "_bound_max")
2259     tag_default = tag_encode(2)
2260     asn1_type_name = "INTEGER"
2261
2262     def __init__(
2263             self,
2264             value=None,
2265             bounds=None,
2266             impl=None,
2267             expl=None,
2268             default=None,
2269             optional=False,
2270             _specs=None,
2271             _decoded=(0, 0, 0),
2272     ):
2273         """
2274         :param value: set the value. Either integer type, named value
2275                       (if ``schema`` is specified in the class), or
2276                       :py:class:`pyderasn.Integer` object
2277         :param bounds: set ``(MIN, MAX)`` value constraint.
2278                        (-inf, +inf) by default
2279         :param bytes impl: override default tag with ``IMPLICIT`` one
2280         :param bytes expl: override default tag with ``EXPLICIT`` one
2281         :param default: set default value. Type same as in ``value``
2282         :param bool optional: is object ``OPTIONAL`` in sequence
2283         """
2284         super(Integer, self).__init__(impl, expl, default, optional, _decoded)
2285         self._value = value
2286         specs = getattr(self, "schema", {}) if _specs is None else _specs
2287         self.specs = specs if specs.__class__ == dict else dict(specs)
2288         self._bound_min, self._bound_max = getattr(
2289             self,
2290             "bounds",
2291             (float("-inf"), float("+inf")),
2292         ) if bounds is None else bounds
2293         if value is not None:
2294             self._value = self._value_sanitize(value)
2295         if default is not None:
2296             default = self._value_sanitize(default)
2297             self.default = self.__class__(
2298                 value=default,
2299                 impl=self.tag,
2300                 expl=self._expl,
2301                 _specs=self.specs,
2302             )
2303             if self._value is None:
2304                 self._value = default
2305
2306     def _value_sanitize(self, value):
2307         if isinstance(value, integer_types):
2308             pass
2309         elif issubclass(value.__class__, Integer):
2310             value = value._value
2311         elif value.__class__ == str:
2312             value = self.specs.get(value)
2313             if value is None:
2314                 raise ObjUnknown("integer value: %s" % value)
2315         else:
2316             raise InvalidValueType((self.__class__, int, str))
2317         if not self._bound_min <= value <= self._bound_max:
2318             raise BoundsError(self._bound_min, value, self._bound_max)
2319         return value
2320
2321     @property
2322     def ready(self):
2323         return self._value is not None
2324
2325     def __getstate__(self):
2326         return IntegerState(
2327             __version__,
2328             self.tag,
2329             self._tag_order,
2330             self._expl,
2331             self.default,
2332             self.optional,
2333             self.offset,
2334             self.llen,
2335             self.vlen,
2336             self.expl_lenindef,
2337             self.lenindef,
2338             self.ber_encoded,
2339             self.specs,
2340             self._value,
2341             self._bound_min,
2342             self._bound_max,
2343         )
2344
2345     def __setstate__(self, state):
2346         super(Integer, self).__setstate__(state)
2347         self.specs = state.specs
2348         self._value = state.value
2349         self._bound_min = state.bound_min
2350         self._bound_max = state.bound_max
2351
2352     def __int__(self):
2353         self._assert_ready()
2354         return int(self._value)
2355
2356     def __hash__(self):
2357         self._assert_ready()
2358         return hash(
2359             self.tag +
2360             bytes(self._expl or b"") +
2361             str(self._value).encode("ascii"),
2362         )
2363
2364     def __eq__(self, their):
2365         if isinstance(their, integer_types):
2366             return self._value == their
2367         if not issubclass(their.__class__, Integer):
2368             return False
2369         return (
2370             self._value == their._value and
2371             self.tag == their.tag and
2372             self._expl == their._expl
2373         )
2374
2375     def __lt__(self, their):
2376         return self._value < their._value
2377
2378     @property
2379     def named(self):
2380         """Return named representation (if exists) of the value
2381         """
2382         for name, value in iteritems(self.specs):
2383             if value == self._value:
2384                 return name
2385         return None
2386
2387     def __call__(
2388             self,
2389             value=None,
2390             bounds=None,
2391             impl=None,
2392             expl=None,
2393             default=None,
2394             optional=None,
2395     ):
2396         return self.__class__(
2397             value=value,
2398             bounds=(
2399                 (self._bound_min, self._bound_max)
2400                 if bounds is None else bounds
2401             ),
2402             impl=self.tag if impl is None else impl,
2403             expl=self._expl if expl is None else expl,
2404             default=self.default if default is None else default,
2405             optional=self.optional if optional is None else optional,
2406             _specs=self.specs,
2407         )
2408
2409     def _encode(self):
2410         self._assert_ready()
2411         value = self._value
2412         if PY2:
2413             if value == 0:
2414                 octets = bytearray([0])
2415             elif value < 0:
2416                 value = -value
2417                 value -= 1
2418                 octets = bytearray()
2419                 while value > 0:
2420                     octets.append((value & 0xFF) ^ 0xFF)
2421                     value >>= 8
2422                 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2423                     octets.append(0xFF)
2424             else:
2425                 octets = bytearray()
2426                 while value > 0:
2427                     octets.append(value & 0xFF)
2428                     value >>= 8
2429                 if octets[-1] & 0x80 > 0:
2430                     octets.append(0x00)
2431             octets.reverse()
2432             octets = bytes(octets)
2433         else:
2434             bytes_len = ceil(value.bit_length() / 8) or 1
2435             while True:
2436                 try:
2437                     octets = value.to_bytes(
2438                         bytes_len,
2439                         byteorder="big",
2440                         signed=True,
2441                     )
2442                 except OverflowError:
2443                     bytes_len += 1
2444                 else:
2445                     break
2446         return b"".join((self.tag, len_encode(len(octets)), octets))
2447
2448     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2449         try:
2450             t, _, lv = tag_strip(tlv)
2451         except DecodeError as err:
2452             raise err.__class__(
2453                 msg=err.msg,
2454                 klass=self.__class__,
2455                 decode_path=decode_path,
2456                 offset=offset,
2457             )
2458         if t != self.tag:
2459             raise TagMismatch(
2460                 klass=self.__class__,
2461                 decode_path=decode_path,
2462                 offset=offset,
2463             )
2464         if tag_only:
2465             yield None
2466             return
2467         try:
2468             l, llen, v = len_decode(lv)
2469         except DecodeError as err:
2470             raise err.__class__(
2471                 msg=err.msg,
2472                 klass=self.__class__,
2473                 decode_path=decode_path,
2474                 offset=offset,
2475             )
2476         if l > len(v):
2477             raise NotEnoughData(
2478                 "encoded length is longer than data",
2479                 klass=self.__class__,
2480                 decode_path=decode_path,
2481                 offset=offset,
2482             )
2483         if l == 0:
2484             raise NotEnoughData(
2485                 "zero length",
2486                 klass=self.__class__,
2487                 decode_path=decode_path,
2488                 offset=offset,
2489             )
2490         v, tail = v[:l], v[l:]
2491         first_octet = byte2int(v)
2492         if l > 1:
2493             second_octet = byte2int(v[1:])
2494             if (
2495                     ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2496                     ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2497             ):
2498                 raise DecodeError(
2499                     "non normalized integer",
2500                     klass=self.__class__,
2501                     decode_path=decode_path,
2502                     offset=offset,
2503                 )
2504         if PY2:
2505             value = 0
2506             if first_octet & 0x80 > 0:
2507                 octets = bytearray()
2508                 for octet in bytearray(v):
2509                     octets.append(octet ^ 0xFF)
2510                 for octet in octets:
2511                     value = (value << 8) | octet
2512                 value += 1
2513                 value = -value
2514             else:
2515                 for octet in bytearray(v):
2516                     value = (value << 8) | octet
2517         else:
2518             value = int.from_bytes(v, byteorder="big", signed=True)
2519         try:
2520             obj = self.__class__(
2521                 value=value,
2522                 bounds=(self._bound_min, self._bound_max),
2523                 impl=self.tag,
2524                 expl=self._expl,
2525                 default=self.default,
2526                 optional=self.optional,
2527                 _specs=self.specs,
2528                 _decoded=(offset, llen, l),
2529             )
2530         except BoundsError as err:
2531             raise DecodeError(
2532                 msg=str(err),
2533                 klass=self.__class__,
2534                 decode_path=decode_path,
2535                 offset=offset,
2536             )
2537         yield decode_path, obj, tail
2538
2539     def __repr__(self):
2540         return pp_console_row(next(self.pps()))
2541
2542     def pps(self, decode_path=()):
2543         yield _pp(
2544             obj=self,
2545             asn1_type_name=self.asn1_type_name,
2546             obj_name=self.__class__.__name__,
2547             decode_path=decode_path,
2548             value=(self.named or str(self._value)) if self.ready else None,
2549             optional=self.optional,
2550             default=self == self.default,
2551             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2552             expl=None if self._expl is None else tag_decode(self._expl),
2553             offset=self.offset,
2554             tlen=self.tlen,
2555             llen=self.llen,
2556             vlen=self.vlen,
2557             expl_offset=self.expl_offset if self.expled else None,
2558             expl_tlen=self.expl_tlen if self.expled else None,
2559             expl_llen=self.expl_llen if self.expled else None,
2560             expl_vlen=self.expl_vlen if self.expled else None,
2561             expl_lenindef=self.expl_lenindef,
2562             bered=self.bered,
2563         )
2564         for pp in self.pps_lenindef(decode_path):
2565             yield pp
2566
2567
2568 BitStringState = namedtuple(
2569     "BitStringState",
2570     BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
2571     **NAMEDTUPLE_KWARGS
2572 )
2573
2574
2575 class BitString(Obj):
2576     """``BIT STRING`` bit string type
2577
2578     >>> BitString(b"hello world")
2579     BIT STRING 88 bits 68656c6c6f20776f726c64
2580     >>> bytes(b)
2581     b'hello world'
2582     >>> b == b"hello world"
2583     True
2584     >>> b.bit_len
2585     88
2586
2587     >>> BitString("'0A3B5F291CD'H")
2588     BIT STRING 44 bits 0a3b5f291cd0
2589     >>> b = BitString("'010110000000'B")
2590     BIT STRING 12 bits 5800
2591     >>> b.bit_len
2592     12
2593     >>> b[0], b[1], b[2], b[3]
2594     (False, True, False, True)
2595     >>> b[1000]
2596     False
2597     >>> [v for v in b]
2598     [False, True, False, True, True, False, False, False, False, False, False, False]
2599
2600     ::
2601
2602         class KeyUsage(BitString):
2603             schema = (
2604                 ("digitalSignature", 0),
2605                 ("nonRepudiation", 1),
2606                 ("keyEncipherment", 2),
2607             )
2608
2609     >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2610     KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2611     >>> b.named
2612     ['nonRepudiation', 'keyEncipherment']
2613     >>> b.specs
2614     {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2615
2616     .. note::
2617
2618        Pay attention that BIT STRING can be encoded both in primitive
2619        and constructed forms. Decoder always checks constructed form tag
2620        additionally to specified primitive one. If BER decoding is
2621        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2622        of DER restrictions.
2623     """
2624     __slots__ = ("tag_constructed", "specs", "defined")
2625     tag_default = tag_encode(3)
2626     asn1_type_name = "BIT STRING"
2627
2628     def __init__(
2629             self,
2630             value=None,
2631             impl=None,
2632             expl=None,
2633             default=None,
2634             optional=False,
2635             _specs=None,
2636             _decoded=(0, 0, 0),
2637     ):
2638         """
2639         :param value: set the value. Either binary type, tuple of named
2640                       values (if ``schema`` is specified in the class),
2641                       string in ``'XXX...'B`` form, or
2642                       :py:class:`pyderasn.BitString` object
2643         :param bytes impl: override default tag with ``IMPLICIT`` one
2644         :param bytes expl: override default tag with ``EXPLICIT`` one
2645         :param default: set default value. Type same as in ``value``
2646         :param bool optional: is object ``OPTIONAL`` in sequence
2647         """
2648         super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2649         specs = getattr(self, "schema", {}) if _specs is None else _specs
2650         self.specs = specs if specs.__class__ == dict else dict(specs)
2651         self._value = None if value is None else self._value_sanitize(value)
2652         if default is not None:
2653             default = self._value_sanitize(default)
2654             self.default = self.__class__(
2655                 value=default,
2656                 impl=self.tag,
2657                 expl=self._expl,
2658             )
2659             if value is None:
2660                 self._value = default
2661         self.defined = None
2662         tag_klass, _, tag_num = tag_decode(self.tag)
2663         self.tag_constructed = tag_encode(
2664             klass=tag_klass,
2665             form=TagFormConstructed,
2666             num=tag_num,
2667         )
2668
2669     def _bits2octets(self, bits):
2670         if len(self.specs) > 0:
2671             bits = bits.rstrip("0")
2672         bit_len = len(bits)
2673         bits += "0" * ((8 - (bit_len % 8)) % 8)
2674         octets = bytearray(len(bits) // 8)
2675         for i in six_xrange(len(octets)):
2676             octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2677         return bit_len, bytes(octets)
2678
2679     def _value_sanitize(self, value):
2680         if isinstance(value, (string_types, binary_type)):
2681             if (
2682                     isinstance(value, string_types) and
2683                     value.startswith("'")
2684             ):
2685                 if value.endswith("'B"):
2686                     value = value[1:-2]
2687                     if not frozenset(value) <= SET01:
2688                         raise ValueError("B's coding contains unacceptable chars")
2689                     return self._bits2octets(value)
2690                 if value.endswith("'H"):
2691                     value = value[1:-2]
2692                     return (
2693                         len(value) * 4,
2694                         hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2695                     )
2696             if value.__class__ == binary_type:
2697                 return (len(value) * 8, value)
2698             raise InvalidValueType((self.__class__, string_types, binary_type))
2699         if value.__class__ == tuple:
2700             if (
2701                     len(value) == 2 and
2702                     isinstance(value[0], integer_types) and
2703                     value[1].__class__ == binary_type
2704             ):
2705                 return value
2706             bits = []
2707             for name in value:
2708                 bit = self.specs.get(name)
2709                 if bit is None:
2710                     raise ObjUnknown("BitString value: %s" % name)
2711                 bits.append(bit)
2712             if len(bits) == 0:
2713                 return self._bits2octets("")
2714             bits = frozenset(bits)
2715             return self._bits2octets("".join(
2716                 ("1" if bit in bits else "0")
2717                 for bit in six_xrange(max(bits) + 1)
2718             ))
2719         if issubclass(value.__class__, BitString):
2720             return value._value
2721         raise InvalidValueType((self.__class__, binary_type, string_types))
2722
2723     @property
2724     def ready(self):
2725         return self._value is not None
2726
2727     def __getstate__(self):
2728         return BitStringState(
2729             __version__,
2730             self.tag,
2731             self._tag_order,
2732             self._expl,
2733             self.default,
2734             self.optional,
2735             self.offset,
2736             self.llen,
2737             self.vlen,
2738             self.expl_lenindef,
2739             self.lenindef,
2740             self.ber_encoded,
2741             self.specs,
2742             self._value,
2743             self.tag_constructed,
2744             self.defined,
2745         )
2746
2747     def __setstate__(self, state):
2748         super(BitString, self).__setstate__(state)
2749         self.specs = state.specs
2750         self._value = state.value
2751         self.tag_constructed = state.tag_constructed
2752         self.defined = state.defined
2753
2754     def __iter__(self):
2755         self._assert_ready()
2756         for i in six_xrange(self._value[0]):
2757             yield self[i]
2758
2759     @property
2760     def bit_len(self):
2761         """Returns number of bits in the string
2762         """
2763         self._assert_ready()
2764         return self._value[0]
2765
2766     def __bytes__(self):
2767         self._assert_ready()
2768         return self._value[1]
2769
2770     def __eq__(self, their):
2771         if their.__class__ == bytes:
2772             return self._value[1] == their
2773         if not issubclass(their.__class__, BitString):
2774             return False
2775         return (
2776             self._value == their._value and
2777             self.tag == their.tag and
2778             self._expl == their._expl
2779         )
2780
2781     @property
2782     def named(self):
2783         """Named representation (if exists) of the bits
2784
2785         :returns: [str(name), ...]
2786         """
2787         return [name for name, bit in iteritems(self.specs) if self[bit]]
2788
2789     def __call__(
2790             self,
2791             value=None,
2792             impl=None,
2793             expl=None,
2794             default=None,
2795             optional=None,
2796     ):
2797         return self.__class__(
2798             value=value,
2799             impl=self.tag if impl is None else impl,
2800             expl=self._expl if expl is None else expl,
2801             default=self.default if default is None else default,
2802             optional=self.optional if optional is None else optional,
2803             _specs=self.specs,
2804         )
2805
2806     def __getitem__(self, key):
2807         if key.__class__ == int:
2808             bit_len, octets = self._value
2809             if key >= bit_len:
2810                 return False
2811             return (
2812                 byte2int(memoryview(octets)[key // 8:]) >>
2813                 (7 - (key % 8))
2814             ) & 1 == 1
2815         if isinstance(key, string_types):
2816             value = self.specs.get(key)
2817             if value is None:
2818                 raise ObjUnknown("BitString value: %s" % key)
2819             return self[value]
2820         raise InvalidValueType((int, str))
2821
2822     def _encode(self):
2823         self._assert_ready()
2824         bit_len, octets = self._value
2825         return b"".join((
2826             self.tag,
2827             len_encode(len(octets) + 1),
2828             int2byte((8 - bit_len % 8) % 8),
2829             octets,
2830         ))
2831
2832     def _encode_cer(self, writer):
2833         bit_len, octets = self._value
2834         if len(octets) + 1 <= 1000:
2835             write_full(writer, self._encode())
2836             return
2837         write_full(writer, self.tag_constructed)
2838         write_full(writer, LENINDEF)
2839         for offset in six_xrange(0, (len(octets) // 999) * 999, 999):
2840             write_full(writer, b"".join((
2841                 BitString.tag_default,
2842                 LEN1K,
2843                 int2byte(0),
2844                 octets[offset:offset + 999],
2845             )))
2846         tail = octets[offset+999:]
2847         if len(tail) > 0:
2848             tail = int2byte((8 - bit_len % 8) % 8) + tail
2849             write_full(writer, b"".join((
2850                 BitString.tag_default,
2851                 len_encode(len(tail)),
2852                 tail,
2853             )))
2854         write_full(writer, EOC)
2855
2856     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2857         try:
2858             t, tlen, lv = tag_strip(tlv)
2859         except DecodeError as err:
2860             raise err.__class__(
2861                 msg=err.msg,
2862                 klass=self.__class__,
2863                 decode_path=decode_path,
2864                 offset=offset,
2865             )
2866         if t == self.tag:
2867             if tag_only:  # pragma: no cover
2868                 yield None
2869                 return
2870             try:
2871                 l, llen, v = len_decode(lv)
2872             except DecodeError as err:
2873                 raise err.__class__(
2874                     msg=err.msg,
2875                     klass=self.__class__,
2876                     decode_path=decode_path,
2877                     offset=offset,
2878                 )
2879             if l > len(v):
2880                 raise NotEnoughData(
2881                     "encoded length is longer than data",
2882                     klass=self.__class__,
2883                     decode_path=decode_path,
2884                     offset=offset,
2885                 )
2886             if l == 0:
2887                 raise NotEnoughData(
2888                     "zero length",
2889                     klass=self.__class__,
2890                     decode_path=decode_path,
2891                     offset=offset,
2892                 )
2893             pad_size = byte2int(v)
2894             if l == 1 and pad_size != 0:
2895                 raise DecodeError(
2896                     "invalid empty value",
2897                     klass=self.__class__,
2898                     decode_path=decode_path,
2899                     offset=offset,
2900                 )
2901             if pad_size > 7:
2902                 raise DecodeError(
2903                     "too big pad",
2904                     klass=self.__class__,
2905                     decode_path=decode_path,
2906                     offset=offset,
2907                 )
2908             if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2909                 raise DecodeError(
2910                     "invalid pad",
2911                     klass=self.__class__,
2912                     decode_path=decode_path,
2913                     offset=offset,
2914                 )
2915             v, tail = v[:l], v[l:]
2916             bit_len = (len(v) - 1) * 8 - pad_size
2917             obj = self.__class__(
2918                 value=None if evgen_mode else (bit_len, v[1:].tobytes()),
2919                 impl=self.tag,
2920                 expl=self._expl,
2921                 default=self.default,
2922                 optional=self.optional,
2923                 _specs=self.specs,
2924                 _decoded=(offset, llen, l),
2925             )
2926             if evgen_mode:
2927                 obj._value = (bit_len, None)
2928             yield decode_path, obj, tail
2929             return
2930         if t != self.tag_constructed:
2931             raise TagMismatch(
2932                 klass=self.__class__,
2933                 decode_path=decode_path,
2934                 offset=offset,
2935             )
2936         if not ctx.get("bered", False):
2937             raise DecodeError(
2938                 "unallowed BER constructed encoding",
2939                 klass=self.__class__,
2940                 decode_path=decode_path,
2941                 offset=offset,
2942             )
2943         if tag_only:  # pragma: no cover
2944             yield None
2945             return
2946         lenindef = False
2947         try:
2948             l, llen, v = len_decode(lv)
2949         except LenIndefForm:
2950             llen, l, v = 1, 0, lv[1:]
2951             lenindef = True
2952         except DecodeError as err:
2953             raise err.__class__(
2954                 msg=err.msg,
2955                 klass=self.__class__,
2956                 decode_path=decode_path,
2957                 offset=offset,
2958             )
2959         if l > len(v):
2960             raise NotEnoughData(
2961                 "encoded length is longer than data",
2962                 klass=self.__class__,
2963                 decode_path=decode_path,
2964                 offset=offset,
2965             )
2966         if not lenindef and l == 0:
2967             raise NotEnoughData(
2968                 "zero length",
2969                 klass=self.__class__,
2970                 decode_path=decode_path,
2971                 offset=offset,
2972             )
2973         chunks = []
2974         sub_offset = offset + tlen + llen
2975         vlen = 0
2976         while True:
2977             if lenindef:
2978                 if v[:EOC_LEN].tobytes() == EOC:
2979                     break
2980             else:
2981                 if vlen == l:
2982                     break
2983                 if vlen > l:
2984                     raise DecodeError(
2985                         "chunk out of bounds",
2986                         klass=self.__class__,
2987                         decode_path=decode_path + (str(len(chunks) - 1),),
2988                         offset=chunks[-1].offset,
2989                     )
2990             sub_decode_path = decode_path + (str(len(chunks)),)
2991             try:
2992                 if evgen_mode:
2993                     for _decode_path, chunk, v_tail in BitString().decode_evgen(
2994                             v,
2995                             offset=sub_offset,
2996                             decode_path=sub_decode_path,
2997                             leavemm=True,
2998                             ctx=ctx,
2999                             _ctx_immutable=False,
3000                     ):
3001                         yield _decode_path, chunk, v_tail
3002                 else:
3003                     _, chunk, v_tail = next(BitString().decode_evgen(
3004                         v,
3005                         offset=sub_offset,
3006                         decode_path=sub_decode_path,
3007                         leavemm=True,
3008                         ctx=ctx,
3009                         _ctx_immutable=False,
3010                         _evgen_mode=False,
3011                     ))
3012             except TagMismatch:
3013                 raise DecodeError(
3014                     "expected BitString encoded chunk",
3015                     klass=self.__class__,
3016                     decode_path=sub_decode_path,
3017                     offset=sub_offset,
3018                 )
3019             chunks.append(chunk)
3020             sub_offset += chunk.tlvlen
3021             vlen += chunk.tlvlen
3022             v = v_tail
3023         if len(chunks) == 0:
3024             raise DecodeError(
3025                 "no chunks",
3026                 klass=self.__class__,
3027                 decode_path=decode_path,
3028                 offset=offset,
3029             )
3030         values = []
3031         bit_len = 0
3032         for chunk_i, chunk in enumerate(chunks[:-1]):
3033             if chunk.bit_len % 8 != 0:
3034                 raise DecodeError(
3035                     "BitString chunk is not multiple of 8 bits",
3036                     klass=self.__class__,
3037                     decode_path=decode_path + (str(chunk_i),),
3038                     offset=chunk.offset,
3039                 )
3040             if not evgen_mode:
3041                 values.append(bytes(chunk))
3042             bit_len += chunk.bit_len
3043         chunk_last = chunks[-1]
3044         if not evgen_mode:
3045             values.append(bytes(chunk_last))
3046         bit_len += chunk_last.bit_len
3047         obj = self.__class__(
3048             value=None if evgen_mode else (bit_len, b"".join(values)),
3049             impl=self.tag,
3050             expl=self._expl,
3051             default=self.default,
3052             optional=self.optional,
3053             _specs=self.specs,
3054             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3055         )
3056         if evgen_mode:
3057             obj._value = (bit_len, None)
3058         obj.lenindef = lenindef
3059         obj.ber_encoded = True
3060         yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
3061
3062     def __repr__(self):
3063         return pp_console_row(next(self.pps()))
3064
3065     def pps(self, decode_path=()):
3066         value = None
3067         blob = None
3068         if self.ready:
3069             bit_len, blob = self._value
3070             value = "%d bits" % bit_len
3071             if len(self.specs) > 0 and blob is not None:
3072                 blob = tuple(self.named)
3073         yield _pp(
3074             obj=self,
3075             asn1_type_name=self.asn1_type_name,
3076             obj_name=self.__class__.__name__,
3077             decode_path=decode_path,
3078             value=value,
3079             blob=blob,
3080             optional=self.optional,
3081             default=self == self.default,
3082             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3083             expl=None if self._expl is None else tag_decode(self._expl),
3084             offset=self.offset,
3085             tlen=self.tlen,
3086             llen=self.llen,
3087             vlen=self.vlen,
3088             expl_offset=self.expl_offset if self.expled else None,
3089             expl_tlen=self.expl_tlen if self.expled else None,
3090             expl_llen=self.expl_llen if self.expled else None,
3091             expl_vlen=self.expl_vlen if self.expled else None,
3092             expl_lenindef=self.expl_lenindef,
3093             lenindef=self.lenindef,
3094             ber_encoded=self.ber_encoded,
3095             bered=self.bered,
3096         )
3097         defined_by, defined = self.defined or (None, None)
3098         if defined_by is not None:
3099             yield defined.pps(
3100                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3101             )
3102         for pp in self.pps_lenindef(decode_path):
3103             yield pp
3104
3105
3106 OctetStringState = namedtuple(
3107     "OctetStringState",
3108     BasicState._fields + (
3109         "value",
3110         "bound_min",
3111         "bound_max",
3112         "tag_constructed",
3113         "defined",
3114     ),
3115     **NAMEDTUPLE_KWARGS
3116 )
3117
3118
3119 class OctetString(Obj):
3120     """``OCTET STRING`` binary string type
3121
3122     >>> s = OctetString(b"hello world")
3123     OCTET STRING 11 bytes 68656c6c6f20776f726c64
3124     >>> s == OctetString(b"hello world")
3125     True
3126     >>> bytes(s)
3127     b'hello world'
3128
3129     >>> OctetString(b"hello", bounds=(4, 4))
3130     Traceback (most recent call last):
3131     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
3132     >>> OctetString(b"hell", bounds=(4, 4))
3133     OCTET STRING 4 bytes 68656c6c
3134
3135     .. note::
3136
3137        Pay attention that OCTET STRING can be encoded both in primitive
3138        and constructed forms. Decoder always checks constructed form tag
3139        additionally to specified primitive one. If BER decoding is
3140        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
3141        of DER restrictions.
3142     """
3143     __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
3144     tag_default = tag_encode(4)
3145     asn1_type_name = "OCTET STRING"
3146     evgen_mode_skip_value = True
3147
3148     def __init__(
3149             self,
3150             value=None,
3151             bounds=None,
3152             impl=None,
3153             expl=None,
3154             default=None,
3155             optional=False,
3156             _decoded=(0, 0, 0),
3157             ctx=None,
3158     ):
3159         """
3160         :param value: set the value. Either binary type, or
3161                       :py:class:`pyderasn.OctetString` object
3162         :param bounds: set ``(MIN, MAX)`` value size constraint.
3163                        (-inf, +inf) by default
3164         :param bytes impl: override default tag with ``IMPLICIT`` one
3165         :param bytes expl: override default tag with ``EXPLICIT`` one
3166         :param default: set default value. Type same as in ``value``
3167         :param bool optional: is object ``OPTIONAL`` in sequence
3168         """
3169         super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
3170         self._value = value
3171         self._bound_min, self._bound_max = getattr(
3172             self,
3173             "bounds",
3174             (0, float("+inf")),
3175         ) if bounds is None else bounds
3176         if value is not None:
3177             self._value = self._value_sanitize(value)
3178         if default is not None:
3179             default = self._value_sanitize(default)
3180             self.default = self.__class__(
3181                 value=default,
3182                 impl=self.tag,
3183                 expl=self._expl,
3184             )
3185             if self._value is None:
3186                 self._value = default
3187         self.defined = None
3188         tag_klass, _, tag_num = tag_decode(self.tag)
3189         self.tag_constructed = tag_encode(
3190             klass=tag_klass,
3191             form=TagFormConstructed,
3192             num=tag_num,
3193         )
3194
3195     def _value_sanitize(self, value):
3196         if value.__class__ == binary_type:
3197             pass
3198         elif issubclass(value.__class__, OctetString):
3199             value = value._value
3200         else:
3201             raise InvalidValueType((self.__class__, bytes))
3202         if not self._bound_min <= len(value) <= self._bound_max:
3203             raise BoundsError(self._bound_min, len(value), self._bound_max)
3204         return value
3205
3206     @property
3207     def ready(self):
3208         return self._value is not None
3209
3210     def __getstate__(self):
3211         return OctetStringState(
3212             __version__,
3213             self.tag,
3214             self._tag_order,
3215             self._expl,
3216             self.default,
3217             self.optional,
3218             self.offset,
3219             self.llen,
3220             self.vlen,
3221             self.expl_lenindef,
3222             self.lenindef,
3223             self.ber_encoded,
3224             self._value,
3225             self._bound_min,
3226             self._bound_max,
3227             self.tag_constructed,
3228             self.defined,
3229         )
3230
3231     def __setstate__(self, state):
3232         super(OctetString, self).__setstate__(state)
3233         self._value = state.value
3234         self._bound_min = state.bound_min
3235         self._bound_max = state.bound_max
3236         self.tag_constructed = state.tag_constructed
3237         self.defined = state.defined
3238
3239     def __bytes__(self):
3240         self._assert_ready()
3241         return self._value
3242
3243     def __eq__(self, their):
3244         if their.__class__ == binary_type:
3245             return self._value == their
3246         if not issubclass(their.__class__, OctetString):
3247             return False
3248         return (
3249             self._value == their._value and
3250             self.tag == their.tag and
3251             self._expl == their._expl
3252         )
3253
3254     def __lt__(self, their):
3255         return self._value < their._value
3256
3257     def __call__(
3258             self,
3259             value=None,
3260             bounds=None,
3261             impl=None,
3262             expl=None,
3263             default=None,
3264             optional=None,
3265     ):
3266         return self.__class__(
3267             value=value,
3268             bounds=(
3269                 (self._bound_min, self._bound_max)
3270                 if bounds is None else bounds
3271             ),
3272             impl=self.tag if impl is None else impl,
3273             expl=self._expl if expl is None else expl,
3274             default=self.default if default is None else default,
3275             optional=self.optional if optional is None else optional,
3276         )
3277
3278     def _encode(self):
3279         self._assert_ready()
3280         return b"".join((
3281             self.tag,
3282             len_encode(len(self._value)),
3283             self._value,
3284         ))
3285
3286     def _encode_cer(self, writer):
3287         octets = self._value
3288         if len(octets) <= 1000:
3289             write_full(writer, self._encode())
3290             return
3291         write_full(writer, self.tag_constructed)
3292         write_full(writer, LENINDEF)
3293         for offset in six_xrange(0, (len(octets) // 1000) * 1000, 1000):
3294             write_full(writer, b"".join((
3295                 OctetString.tag_default,
3296                 LEN1K,
3297                 octets[offset:offset + 1000],
3298             )))
3299         tail = octets[offset+1000:]
3300         if len(tail) > 0:
3301             write_full(writer, b"".join((
3302                 OctetString.tag_default,
3303                 len_encode(len(tail)),
3304                 tail,
3305             )))
3306         write_full(writer, EOC)
3307
3308     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3309         try:
3310             t, tlen, lv = tag_strip(tlv)
3311         except DecodeError as err:
3312             raise err.__class__(
3313                 msg=err.msg,
3314                 klass=self.__class__,
3315                 decode_path=decode_path,
3316                 offset=offset,
3317             )
3318         if t == self.tag:
3319             if tag_only:
3320                 yield None
3321                 return
3322             try:
3323                 l, llen, v = len_decode(lv)
3324             except DecodeError as err:
3325                 raise err.__class__(
3326                     msg=err.msg,
3327                     klass=self.__class__,
3328                     decode_path=decode_path,
3329                     offset=offset,
3330                 )
3331             if l > len(v):
3332                 raise NotEnoughData(
3333                     "encoded length is longer than data",
3334                     klass=self.__class__,
3335                     decode_path=decode_path,
3336                     offset=offset,
3337                 )
3338             v, tail = v[:l], v[l:]
3339             if evgen_mode and not self._bound_min <= len(v) <= self._bound_max:
3340                 raise DecodeError(
3341                     msg=str(BoundsError(self._bound_min, len(v), self._bound_max)),
3342                     klass=self.__class__,
3343                     decode_path=decode_path,
3344                     offset=offset,
3345                 )
3346             try:
3347                 obj = self.__class__(
3348                     value=(
3349                         None if (evgen_mode and self.evgen_mode_skip_value)
3350                         else v.tobytes()
3351                     ),
3352                     bounds=(self._bound_min, self._bound_max),
3353                     impl=self.tag,
3354                     expl=self._expl,
3355                     default=self.default,
3356                     optional=self.optional,
3357                     _decoded=(offset, llen, l),
3358                     ctx=ctx,
3359                 )
3360             except DecodeError as err:
3361                 raise DecodeError(
3362                     msg=err.msg,
3363                     klass=self.__class__,
3364                     decode_path=decode_path,
3365                     offset=offset,
3366                 )
3367             except BoundsError as err:
3368                 raise DecodeError(
3369                     msg=str(err),
3370                     klass=self.__class__,
3371                     decode_path=decode_path,
3372                     offset=offset,
3373                 )
3374             yield decode_path, obj, tail
3375             return
3376         if t != self.tag_constructed:
3377             raise TagMismatch(
3378                 klass=self.__class__,
3379                 decode_path=decode_path,
3380                 offset=offset,
3381             )
3382         if not ctx.get("bered", False):
3383             raise DecodeError(
3384                 "unallowed BER constructed encoding",
3385                 klass=self.__class__,
3386                 decode_path=decode_path,
3387                 offset=offset,
3388             )
3389         if tag_only:
3390             yield None
3391             return
3392         lenindef = False
3393         try:
3394             l, llen, v = len_decode(lv)
3395         except LenIndefForm:
3396             llen, l, v = 1, 0, lv[1:]
3397             lenindef = True
3398         except DecodeError as err:
3399             raise err.__class__(
3400                 msg=err.msg,
3401                 klass=self.__class__,
3402                 decode_path=decode_path,
3403                 offset=offset,
3404             )
3405         if l > len(v):
3406             raise NotEnoughData(
3407                 "encoded length is longer than data",
3408                 klass=self.__class__,
3409                 decode_path=decode_path,
3410                 offset=offset,
3411             )
3412         chunks = []
3413         chunks_count = 0
3414         sub_offset = offset + tlen + llen
3415         vlen = 0
3416         payload_len = 0
3417         while True:
3418             if lenindef:
3419                 if v[:EOC_LEN].tobytes() == EOC:
3420                     break
3421             else:
3422                 if vlen == l:
3423                     break
3424                 if vlen > l:
3425                     raise DecodeError(
3426                         "chunk out of bounds",
3427                         klass=self.__class__,
3428                         decode_path=decode_path + (str(len(chunks) - 1),),
3429                         offset=chunks[-1].offset,
3430                     )
3431             try:
3432                 if evgen_mode:
3433                     sub_decode_path = decode_path + (str(chunks_count),)
3434                     for _decode_path, chunk, v_tail in OctetString().decode_evgen(
3435                             v,
3436                             offset=sub_offset,
3437                             decode_path=sub_decode_path,
3438                             leavemm=True,
3439                             ctx=ctx,
3440                             _ctx_immutable=False,
3441                     ):
3442                         yield _decode_path, chunk, v_tail
3443                         if not chunk.ber_encoded:
3444                             payload_len += chunk.vlen
3445                     chunks_count += 1
3446                 else:
3447                     sub_decode_path = decode_path + (str(len(chunks)),)
3448                     _, chunk, v_tail = next(OctetString().decode_evgen(
3449                         v,
3450                         offset=sub_offset,
3451                         decode_path=sub_decode_path,
3452                         leavemm=True,
3453                         ctx=ctx,
3454                         _ctx_immutable=False,
3455                         _evgen_mode=False,
3456                     ))
3457                     chunks.append(chunk)
3458             except TagMismatch:
3459                 raise DecodeError(
3460                     "expected OctetString encoded chunk",
3461                     klass=self.__class__,
3462                     decode_path=sub_decode_path,
3463                     offset=sub_offset,
3464                 )
3465             sub_offset += chunk.tlvlen
3466             vlen += chunk.tlvlen
3467             v = v_tail
3468         if evgen_mode and not self._bound_min <= payload_len <= self._bound_max:
3469             raise DecodeError(
3470                 msg=str(BoundsError(self._bound_min, payload_len, self._bound_max)),
3471                 klass=self.__class__,
3472                 decode_path=decode_path,
3473                 offset=offset,
3474             )
3475         try:
3476             obj = self.__class__(
3477                 value=(
3478                     None if evgen_mode else
3479                     b"".join(bytes(chunk) for chunk in chunks)
3480                 ),
3481                 bounds=(self._bound_min, self._bound_max),
3482                 impl=self.tag,
3483                 expl=self._expl,
3484                 default=self.default,
3485                 optional=self.optional,
3486                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3487                 ctx=ctx,
3488             )
3489         except DecodeError as err:
3490             raise DecodeError(
3491                 msg=err.msg,
3492                 klass=self.__class__,
3493                 decode_path=decode_path,
3494                 offset=offset,
3495             )
3496         except BoundsError as err:
3497             raise DecodeError(
3498                 msg=str(err),
3499                 klass=self.__class__,
3500                 decode_path=decode_path,
3501                 offset=offset,
3502             )
3503         obj.lenindef = lenindef
3504         obj.ber_encoded = True
3505         yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
3506
3507     def __repr__(self):
3508         return pp_console_row(next(self.pps()))
3509
3510     def pps(self, decode_path=()):
3511         yield _pp(
3512             obj=self,
3513             asn1_type_name=self.asn1_type_name,
3514             obj_name=self.__class__.__name__,
3515             decode_path=decode_path,
3516             value=("%d bytes" % len(self._value)) if self.ready else None,
3517             blob=self._value if self.ready else None,
3518             optional=self.optional,
3519             default=self == self.default,
3520             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3521             expl=None if self._expl is None else tag_decode(self._expl),
3522             offset=self.offset,
3523             tlen=self.tlen,
3524             llen=self.llen,
3525             vlen=self.vlen,
3526             expl_offset=self.expl_offset if self.expled else None,
3527             expl_tlen=self.expl_tlen if self.expled else None,
3528             expl_llen=self.expl_llen if self.expled else None,
3529             expl_vlen=self.expl_vlen if self.expled else None,
3530             expl_lenindef=self.expl_lenindef,
3531             lenindef=self.lenindef,
3532             ber_encoded=self.ber_encoded,
3533             bered=self.bered,
3534         )
3535         defined_by, defined = self.defined or (None, None)
3536         if defined_by is not None:
3537             yield defined.pps(
3538                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3539             )
3540         for pp in self.pps_lenindef(decode_path):
3541             yield pp
3542
3543
3544 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
3545
3546
3547 class Null(Obj):
3548     """``NULL`` null object
3549
3550     >>> n = Null()
3551     NULL
3552     >>> n.ready
3553     True
3554     """
3555     __slots__ = ()
3556     tag_default = tag_encode(5)
3557     asn1_type_name = "NULL"
3558
3559     def __init__(
3560             self,
3561             value=None,  # unused, but Sequence passes it
3562             impl=None,
3563             expl=None,
3564             optional=False,
3565             _decoded=(0, 0, 0),
3566     ):
3567         """
3568         :param bytes impl: override default tag with ``IMPLICIT`` one
3569         :param bytes expl: override default tag with ``EXPLICIT`` one
3570         :param bool optional: is object ``OPTIONAL`` in sequence
3571         """
3572         super(Null, self).__init__(impl, expl, None, optional, _decoded)
3573         self.default = None
3574
3575     @property
3576     def ready(self):
3577         return True
3578
3579     def __getstate__(self):
3580         return NullState(
3581             __version__,
3582             self.tag,
3583             self._tag_order,
3584             self._expl,
3585             self.default,
3586             self.optional,
3587             self.offset,
3588             self.llen,
3589             self.vlen,
3590             self.expl_lenindef,
3591             self.lenindef,
3592             self.ber_encoded,
3593         )
3594
3595     def __eq__(self, their):
3596         if not issubclass(their.__class__, Null):
3597             return False
3598         return (
3599             self.tag == their.tag and
3600             self._expl == their._expl
3601         )
3602
3603     def __call__(
3604             self,
3605             value=None,
3606             impl=None,
3607             expl=None,
3608             optional=None,
3609     ):
3610         return self.__class__(
3611             impl=self.tag if impl is None else impl,
3612             expl=self._expl if expl is None else expl,
3613             optional=self.optional if optional is None else optional,
3614         )
3615
3616     def _encode(self):
3617         return self.tag + len_encode(0)
3618
3619     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3620         try:
3621             t, _, lv = tag_strip(tlv)
3622         except DecodeError as err:
3623             raise err.__class__(
3624                 msg=err.msg,
3625                 klass=self.__class__,
3626                 decode_path=decode_path,
3627                 offset=offset,
3628             )
3629         if t != self.tag:
3630             raise TagMismatch(
3631                 klass=self.__class__,
3632                 decode_path=decode_path,
3633                 offset=offset,
3634             )
3635         if tag_only:  # pragma: no cover
3636             yield None
3637             return
3638         try:
3639             l, _, v = len_decode(lv)
3640         except DecodeError as err:
3641             raise err.__class__(
3642                 msg=err.msg,
3643                 klass=self.__class__,
3644                 decode_path=decode_path,
3645                 offset=offset,
3646             )
3647         if l != 0:
3648             raise InvalidLength(
3649                 "Null must have zero length",
3650                 klass=self.__class__,
3651                 decode_path=decode_path,
3652                 offset=offset,
3653             )
3654         obj = self.__class__(
3655             impl=self.tag,
3656             expl=self._expl,
3657             optional=self.optional,
3658             _decoded=(offset, 1, 0),
3659         )
3660         yield decode_path, obj, v
3661
3662     def __repr__(self):
3663         return pp_console_row(next(self.pps()))
3664
3665     def pps(self, decode_path=()):
3666         yield _pp(
3667             obj=self,
3668             asn1_type_name=self.asn1_type_name,
3669             obj_name=self.__class__.__name__,
3670             decode_path=decode_path,
3671             optional=self.optional,
3672             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3673             expl=None if self._expl is None else tag_decode(self._expl),
3674             offset=self.offset,
3675             tlen=self.tlen,
3676             llen=self.llen,
3677             vlen=self.vlen,
3678             expl_offset=self.expl_offset if self.expled else None,
3679             expl_tlen=self.expl_tlen if self.expled else None,
3680             expl_llen=self.expl_llen if self.expled else None,
3681             expl_vlen=self.expl_vlen if self.expled else None,
3682             expl_lenindef=self.expl_lenindef,
3683             bered=self.bered,
3684         )
3685         for pp in self.pps_lenindef(decode_path):
3686             yield pp
3687
3688
3689 ObjectIdentifierState = namedtuple(
3690     "ObjectIdentifierState",
3691     BasicState._fields + ("value", "defines"),
3692     **NAMEDTUPLE_KWARGS
3693 )
3694
3695
3696 class ObjectIdentifier(Obj):
3697     """``OBJECT IDENTIFIER`` OID type
3698
3699     >>> oid = ObjectIdentifier((1, 2, 3))
3700     OBJECT IDENTIFIER 1.2.3
3701     >>> oid == ObjectIdentifier("1.2.3")
3702     True
3703     >>> tuple(oid)
3704     (1, 2, 3)
3705     >>> str(oid)
3706     '1.2.3'
3707     >>> oid + (4, 5) + ObjectIdentifier("1.7")
3708     OBJECT IDENTIFIER 1.2.3.4.5.1.7
3709
3710     >>> str(ObjectIdentifier((3, 1)))
3711     Traceback (most recent call last):
3712     pyderasn.InvalidOID: unacceptable first arc value
3713     """
3714     __slots__ = ("defines",)
3715     tag_default = tag_encode(6)
3716     asn1_type_name = "OBJECT IDENTIFIER"
3717
3718     def __init__(
3719             self,
3720             value=None,
3721             defines=(),
3722             impl=None,
3723             expl=None,
3724             default=None,
3725             optional=False,
3726             _decoded=(0, 0, 0),
3727     ):
3728         """
3729         :param value: set the value. Either tuples of integers,
3730                       string of "."-concatenated integers, or
3731                       :py:class:`pyderasn.ObjectIdentifier` object
3732         :param defines: sequence of tuples. Each tuple has two elements.
3733                         First one is relative to current one decode
3734                         path, aiming to the field defined by that OID.
3735                         Read about relative path in
3736                         :py:func:`pyderasn.abs_decode_path`. Second
3737                         tuple element is ``{OID: pyderasn.Obj()}``
3738                         dictionary, mapping between current OID value
3739                         and structure applied to defined field.
3740                         :ref:`Read about DEFINED BY <definedby>`
3741         :param bytes impl: override default tag with ``IMPLICIT`` one
3742         :param bytes expl: override default tag with ``EXPLICIT`` one
3743         :param default: set default value. Type same as in ``value``
3744         :param bool optional: is object ``OPTIONAL`` in sequence
3745         """
3746         super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
3747         self._value = value
3748         if value is not None:
3749             self._value = self._value_sanitize(value)
3750         if default is not None:
3751             default = self._value_sanitize(default)
3752             self.default = self.__class__(
3753                 value=default,
3754                 impl=self.tag,
3755                 expl=self._expl,
3756             )
3757             if self._value is None:
3758                 self._value = default
3759         self.defines = defines
3760
3761     def __add__(self, their):
3762         if their.__class__ == tuple:
3763             return self.__class__(self._value + their)
3764         if isinstance(their, self.__class__):
3765             return self.__class__(self._value + their._value)
3766         raise InvalidValueType((self.__class__, tuple))
3767
3768     def _value_sanitize(self, value):
3769         if issubclass(value.__class__, ObjectIdentifier):
3770             return value._value
3771         if isinstance(value, string_types):
3772             try:
3773                 value = tuple(pureint(arc) for arc in value.split("."))
3774             except ValueError:
3775                 raise InvalidOID("unacceptable arcs values")
3776         if value.__class__ == tuple:
3777             if len(value) < 2:
3778                 raise InvalidOID("less than 2 arcs")
3779             first_arc = value[0]
3780             if first_arc in (0, 1):
3781                 if not (0 <= value[1] <= 39):
3782                     raise InvalidOID("second arc is too wide")
3783             elif first_arc == 2:
3784                 pass
3785             else:
3786                 raise InvalidOID("unacceptable first arc value")
3787             if not all(arc >= 0 for arc in value):
3788                 raise InvalidOID("negative arc value")
3789             return value
3790         raise InvalidValueType((self.__class__, str, tuple))
3791
3792     @property
3793     def ready(self):
3794         return self._value is not None
3795
3796     def __getstate__(self):
3797         return ObjectIdentifierState(
3798             __version__,
3799             self.tag,
3800             self._tag_order,
3801             self._expl,
3802             self.default,
3803             self.optional,
3804             self.offset,
3805             self.llen,
3806             self.vlen,
3807             self.expl_lenindef,
3808             self.lenindef,
3809             self.ber_encoded,
3810             self._value,
3811             self.defines,
3812         )
3813
3814     def __setstate__(self, state):
3815         super(ObjectIdentifier, self).__setstate__(state)
3816         self._value = state.value
3817         self.defines = state.defines
3818
3819     def __iter__(self):
3820         self._assert_ready()
3821         return iter(self._value)
3822
3823     def __str__(self):
3824         return ".".join(str(arc) for arc in self._value or ())
3825
3826     def __hash__(self):
3827         self._assert_ready()
3828         return hash(
3829             self.tag +
3830             bytes(self._expl or b"") +
3831             str(self._value).encode("ascii"),
3832         )
3833
3834     def __eq__(self, their):
3835         if their.__class__ == tuple:
3836             return self._value == their
3837         if not issubclass(their.__class__, ObjectIdentifier):
3838             return False
3839         return (
3840             self.tag == their.tag and
3841             self._expl == their._expl and
3842             self._value == their._value
3843         )
3844
3845     def __lt__(self, their):
3846         return self._value < their._value
3847
3848     def __call__(
3849             self,
3850             value=None,
3851             defines=None,
3852             impl=None,
3853             expl=None,
3854             default=None,
3855             optional=None,
3856     ):
3857         return self.__class__(
3858             value=value,
3859             defines=self.defines if defines is None else defines,
3860             impl=self.tag if impl is None else impl,
3861             expl=self._expl if expl is None else expl,
3862             default=self.default if default is None else default,
3863             optional=self.optional if optional is None else optional,
3864         )
3865
3866     def _encode(self):
3867         self._assert_ready()
3868         value = self._value
3869         first_value = value[1]
3870         first_arc = value[0]
3871         if first_arc == 0:
3872             pass
3873         elif first_arc == 1:
3874             first_value += 40
3875         elif first_arc == 2:
3876             first_value += 80
3877         else:  # pragma: no cover
3878             raise RuntimeError("invalid arc is stored")
3879         octets = [zero_ended_encode(first_value)]
3880         for arc in value[2:]:
3881             octets.append(zero_ended_encode(arc))
3882         v = b"".join(octets)
3883         return b"".join((self.tag, len_encode(len(v)), v))
3884
3885     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3886         try:
3887             t, _, lv = tag_strip(tlv)
3888         except DecodeError as err:
3889             raise err.__class__(
3890                 msg=err.msg,
3891                 klass=self.__class__,
3892                 decode_path=decode_path,
3893                 offset=offset,
3894             )
3895         if t != self.tag:
3896             raise TagMismatch(
3897                 klass=self.__class__,
3898                 decode_path=decode_path,
3899                 offset=offset,
3900             )
3901         if tag_only:  # pragma: no cover
3902             yield None
3903             return
3904         try:
3905             l, llen, v = len_decode(lv)
3906         except DecodeError as err:
3907             raise err.__class__(
3908                 msg=err.msg,
3909                 klass=self.__class__,
3910                 decode_path=decode_path,
3911                 offset=offset,
3912             )
3913         if l > len(v):
3914             raise NotEnoughData(
3915                 "encoded length is longer than data",
3916                 klass=self.__class__,
3917                 decode_path=decode_path,
3918                 offset=offset,
3919             )
3920         if l == 0:
3921             raise NotEnoughData(
3922                 "zero length",
3923                 klass=self.__class__,
3924                 decode_path=decode_path,
3925                 offset=offset,
3926             )
3927         v, tail = v[:l], v[l:]
3928         arcs = []
3929         ber_encoded = False
3930         while len(v) > 0:
3931             i = 0
3932             arc = 0
3933             while True:
3934                 octet = indexbytes(v, i)
3935                 if i == 0 and octet == 0x80:
3936                     if ctx.get("bered", False):
3937                         ber_encoded = True
3938                     else:
3939                         raise DecodeError("non normalized arc encoding")
3940                 arc = (arc << 7) | (octet & 0x7F)
3941                 if octet & 0x80 == 0:
3942                     arcs.append(arc)
3943                     v = v[i + 1:]
3944                     break
3945                 i += 1
3946                 if i == len(v):
3947                     raise DecodeError(
3948                         "unfinished OID",
3949                         klass=self.__class__,
3950                         decode_path=decode_path,
3951                         offset=offset,
3952                     )
3953         first_arc = 0
3954         second_arc = arcs[0]
3955         if 0 <= second_arc <= 39:
3956             first_arc = 0
3957         elif 40 <= second_arc <= 79:
3958             first_arc = 1
3959             second_arc -= 40
3960         else:
3961             first_arc = 2
3962             second_arc -= 80
3963         obj = self.__class__(
3964             value=tuple([first_arc, second_arc] + arcs[1:]),
3965             impl=self.tag,
3966             expl=self._expl,
3967             default=self.default,
3968             optional=self.optional,
3969             _decoded=(offset, llen, l),
3970         )
3971         if ber_encoded:
3972             obj.ber_encoded = True
3973         yield decode_path, obj, tail
3974
3975     def __repr__(self):
3976         return pp_console_row(next(self.pps()))
3977
3978     def pps(self, decode_path=()):
3979         yield _pp(
3980             obj=self,
3981             asn1_type_name=self.asn1_type_name,
3982             obj_name=self.__class__.__name__,
3983             decode_path=decode_path,
3984             value=str(self) if self.ready else None,
3985             optional=self.optional,
3986             default=self == self.default,
3987             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3988             expl=None if self._expl is None else tag_decode(self._expl),
3989             offset=self.offset,
3990             tlen=self.tlen,
3991             llen=self.llen,
3992             vlen=self.vlen,
3993             expl_offset=self.expl_offset if self.expled else None,
3994             expl_tlen=self.expl_tlen if self.expled else None,
3995             expl_llen=self.expl_llen if self.expled else None,
3996             expl_vlen=self.expl_vlen if self.expled else None,
3997             expl_lenindef=self.expl_lenindef,
3998             ber_encoded=self.ber_encoded,
3999             bered=self.bered,
4000         )
4001         for pp in self.pps_lenindef(decode_path):
4002             yield pp
4003
4004
4005 class Enumerated(Integer):
4006     """``ENUMERATED`` integer type
4007
4008     This type is identical to :py:class:`pyderasn.Integer`, but requires
4009     schema to be specified and does not accept values missing from it.
4010     """
4011     __slots__ = ()
4012     tag_default = tag_encode(10)
4013     asn1_type_name = "ENUMERATED"
4014
4015     def __init__(
4016             self,
4017             value=None,
4018             impl=None,
4019             expl=None,
4020             default=None,
4021             optional=False,
4022             _specs=None,
4023             _decoded=(0, 0, 0),
4024             bounds=None,  # dummy argument, workability for Integer.decode
4025     ):
4026         super(Enumerated, self).__init__(
4027             value, bounds, impl, expl, default, optional, _specs, _decoded,
4028         )
4029         if len(self.specs) == 0:
4030             raise ValueError("schema must be specified")
4031
4032     def _value_sanitize(self, value):
4033         if isinstance(value, self.__class__):
4034             value = value._value
4035         elif isinstance(value, integer_types):
4036             for _value in itervalues(self.specs):
4037                 if _value == value:
4038                     break
4039             else:
4040                 raise DecodeError(
4041                     "unknown integer value: %s" % value,
4042                     klass=self.__class__,
4043                 )
4044         elif isinstance(value, string_types):
4045             value = self.specs.get(value)
4046             if value is None:
4047                 raise ObjUnknown("integer value: %s" % value)
4048         else:
4049             raise InvalidValueType((self.__class__, int, str))
4050         return value
4051
4052     def __call__(
4053             self,
4054             value=None,
4055             impl=None,
4056             expl=None,
4057             default=None,
4058             optional=None,
4059             _specs=None,
4060     ):
4061         return self.__class__(
4062             value=value,
4063             impl=self.tag if impl is None else impl,
4064             expl=self._expl if expl is None else expl,
4065             default=self.default if default is None else default,
4066             optional=self.optional if optional is None else optional,
4067             _specs=self.specs,
4068         )
4069
4070
4071 def escape_control_unicode(c):
4072     if unicat(c)[0] == "C":
4073         c = repr(c).lstrip("u").strip("'")
4074     return c
4075
4076
4077 class CommonString(OctetString):
4078     """Common class for all strings
4079
4080     Everything resembles :py:class:`pyderasn.OctetString`, except
4081     ability to deal with unicode text strings.
4082
4083     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
4084     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4085     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
4086     True
4087     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
4088     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
4089     >>> str(s)
4090     'привет Ð¼Ð¸Ñ€'
4091     >>> hexenc(bytes(s))
4092     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4093
4094     >>> PrintableString("привет Ð¼Ð¸Ñ€")
4095     Traceback (most recent call last):
4096     pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
4097
4098     >>> BMPString("ада", bounds=(2, 2))
4099     Traceback (most recent call last):
4100     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
4101     >>> s = BMPString("ад", bounds=(2, 2))
4102     >>> s.encoding
4103     'utf-16-be'
4104     >>> hexenc(bytes(s))
4105     '04300434'
4106
4107     .. list-table::
4108        :header-rows: 1
4109
4110        * - Class
4111          - Text Encoding
4112        * - :py:class:`pyderasn.UTF8String`
4113          - utf-8
4114        * - :py:class:`pyderasn.NumericString`
4115          - ascii
4116        * - :py:class:`pyderasn.PrintableString`
4117          - ascii
4118        * - :py:class:`pyderasn.TeletexString`
4119          - ascii
4120        * - :py:class:`pyderasn.T61String`
4121          - ascii
4122        * - :py:class:`pyderasn.VideotexString`
4123          - iso-8859-1
4124        * - :py:class:`pyderasn.IA5String`
4125          - ascii
4126        * - :py:class:`pyderasn.GraphicString`
4127          - iso-8859-1
4128        * - :py:class:`pyderasn.VisibleString`
4129          - ascii
4130        * - :py:class:`pyderasn.ISO646String`
4131          - ascii
4132        * - :py:class:`pyderasn.GeneralString`
4133          - iso-8859-1
4134        * - :py:class:`pyderasn.UniversalString`
4135          - utf-32-be
4136        * - :py:class:`pyderasn.BMPString`
4137          - utf-16-be
4138     """
4139     __slots__ = ()
4140
4141     def _value_sanitize(self, value):
4142         value_raw = None
4143         value_decoded = None
4144         if isinstance(value, self.__class__):
4145             value_raw = value._value
4146         elif value.__class__ == text_type:
4147             value_decoded = value
4148         elif value.__class__ == binary_type:
4149             value_raw = value
4150         else:
4151             raise InvalidValueType((self.__class__, text_type, binary_type))
4152         try:
4153             value_raw = (
4154                 value_decoded.encode(self.encoding)
4155                 if value_raw is None else value_raw
4156             )
4157             value_decoded = (
4158                 value_raw.decode(self.encoding)
4159                 if value_decoded is None else value_decoded
4160             )
4161         except (UnicodeEncodeError, UnicodeDecodeError) as err:
4162             raise DecodeError(str(err))
4163         if not self._bound_min <= len(value_decoded) <= self._bound_max:
4164             raise BoundsError(
4165                 self._bound_min,
4166                 len(value_decoded),
4167                 self._bound_max,
4168             )
4169         return value_raw
4170
4171     def __eq__(self, their):
4172         if their.__class__ == binary_type:
4173             return self._value == their
4174         if their.__class__ == text_type:
4175             return self._value == their.encode(self.encoding)
4176         if not isinstance(their, self.__class__):
4177             return False
4178         return (
4179             self._value == their._value and
4180             self.tag == their.tag and
4181             self._expl == their._expl
4182         )
4183
4184     def __unicode__(self):
4185         if self.ready:
4186             return self._value.decode(self.encoding)
4187         return text_type(self._value)
4188
4189     def __repr__(self):
4190         return pp_console_row(next(self.pps(no_unicode=PY2)))
4191
4192     def pps(self, decode_path=(), no_unicode=False):
4193         value = None
4194         if self.ready:
4195             value = (
4196                 hexenc(bytes(self)) if no_unicode else
4197                 "".join(escape_control_unicode(c) for c in self.__unicode__())
4198             )
4199         yield _pp(
4200             obj=self,
4201             asn1_type_name=self.asn1_type_name,
4202             obj_name=self.__class__.__name__,
4203             decode_path=decode_path,
4204             value=value,
4205             optional=self.optional,
4206             default=self == self.default,
4207             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4208             expl=None if self._expl is None else tag_decode(self._expl),
4209             offset=self.offset,
4210             tlen=self.tlen,
4211             llen=self.llen,
4212             vlen=self.vlen,
4213             expl_offset=self.expl_offset if self.expled else None,
4214             expl_tlen=self.expl_tlen if self.expled else None,
4215             expl_llen=self.expl_llen if self.expled else None,
4216             expl_vlen=self.expl_vlen if self.expled else None,
4217             expl_lenindef=self.expl_lenindef,
4218             ber_encoded=self.ber_encoded,
4219             bered=self.bered,
4220         )
4221         for pp in self.pps_lenindef(decode_path):
4222             yield pp
4223
4224
4225 class UTF8String(CommonString):
4226     __slots__ = ()
4227     tag_default = tag_encode(12)
4228     encoding = "utf-8"
4229     asn1_type_name = "UTF8String"
4230
4231
4232 class AllowableCharsMixin(object):
4233     @property
4234     def allowable_chars(self):
4235         if PY2:
4236             return self._allowable_chars
4237         return frozenset(six_unichr(c) for c in self._allowable_chars)
4238
4239
4240 class NumericString(AllowableCharsMixin, CommonString):
4241     """Numeric string
4242
4243     Its value is properly sanitized: only ASCII digits with spaces can
4244     be stored.
4245
4246     >>> NumericString().allowable_chars
4247     frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4248     """
4249     __slots__ = ()
4250     tag_default = tag_encode(18)
4251     encoding = "ascii"
4252     asn1_type_name = "NumericString"
4253     _allowable_chars = frozenset(digits.encode("ascii") + b" ")
4254
4255     def _value_sanitize(self, value):
4256         value = super(NumericString, self)._value_sanitize(value)
4257         if not frozenset(value) <= self._allowable_chars:
4258             raise DecodeError("non-numeric value")
4259         return value
4260
4261
4262 PrintableStringState = namedtuple(
4263     "PrintableStringState",
4264     OctetStringState._fields + ("allowable_chars",),
4265     **NAMEDTUPLE_KWARGS
4266 )
4267
4268
4269 class PrintableString(AllowableCharsMixin, CommonString):
4270     """Printable string
4271
4272     Its value is properly sanitized: see X.680 41.4 table 10.
4273
4274     >>> PrintableString().allowable_chars
4275     frozenset([' ', "'", ..., 'z'])
4276     >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4277     PrintableString PrintableString foo*bar
4278     >>> obj.allow_asterisk, obj.allow_ampersand
4279     (True, False)
4280     """
4281     __slots__ = ()
4282     tag_default = tag_encode(19)
4283     encoding = "ascii"
4284     asn1_type_name = "PrintableString"
4285     _allowable_chars = frozenset(
4286         (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4287     )
4288     _asterisk = frozenset("*".encode("ascii"))
4289     _ampersand = frozenset("&".encode("ascii"))
4290
4291     def __init__(
4292             self,
4293             value=None,
4294             bounds=None,
4295             impl=None,
4296             expl=None,
4297             default=None,
4298             optional=False,
4299             _decoded=(0, 0, 0),
4300             ctx=None,
4301             allow_asterisk=False,
4302             allow_ampersand=False,
4303     ):
4304         """
4305         :param allow_asterisk: allow asterisk character
4306         :param allow_ampersand: allow ampersand character
4307         """
4308         if allow_asterisk:
4309             self._allowable_chars |= self._asterisk
4310         if allow_ampersand:
4311             self._allowable_chars |= self._ampersand
4312         super(PrintableString, self).__init__(
4313             value, bounds, impl, expl, default, optional, _decoded, ctx,
4314         )
4315
4316     @property
4317     def allow_asterisk(self):
4318         """Is asterisk character allowed?
4319         """
4320         return self._asterisk <= self._allowable_chars
4321
4322     @property
4323     def allow_ampersand(self):
4324         """Is ampersand character allowed?
4325         """
4326         return self._ampersand <= self._allowable_chars
4327
4328     def _value_sanitize(self, value):
4329         value = super(PrintableString, self)._value_sanitize(value)
4330         if not frozenset(value) <= self._allowable_chars:
4331             raise DecodeError("non-printable value")
4332         return value
4333
4334     def __getstate__(self):
4335         return PrintableStringState(
4336             *super(PrintableString, self).__getstate__(),
4337             **{"allowable_chars": self._allowable_chars}
4338         )
4339
4340     def __setstate__(self, state):
4341         super(PrintableString, self).__setstate__(state)
4342         self._allowable_chars = state.allowable_chars
4343
4344     def __call__(
4345             self,
4346             value=None,
4347             bounds=None,
4348             impl=None,
4349             expl=None,
4350             default=None,
4351             optional=None,
4352     ):
4353         return self.__class__(
4354             value=value,
4355             bounds=(
4356                 (self._bound_min, self._bound_max)
4357                 if bounds is None else bounds
4358             ),
4359             impl=self.tag if impl is None else impl,
4360             expl=self._expl if expl is None else expl,
4361             default=self.default if default is None else default,
4362             optional=self.optional if optional is None else optional,
4363             allow_asterisk=self.allow_asterisk,
4364             allow_ampersand=self.allow_ampersand,
4365         )
4366
4367
4368 class TeletexString(CommonString):
4369     __slots__ = ()
4370     tag_default = tag_encode(20)
4371     encoding = "ascii"
4372     asn1_type_name = "TeletexString"
4373
4374
4375 class T61String(TeletexString):
4376     __slots__ = ()
4377     asn1_type_name = "T61String"
4378
4379
4380 class VideotexString(CommonString):
4381     __slots__ = ()
4382     tag_default = tag_encode(21)
4383     encoding = "iso-8859-1"
4384     asn1_type_name = "VideotexString"
4385
4386
4387 class IA5String(CommonString):
4388     __slots__ = ()
4389     tag_default = tag_encode(22)
4390     encoding = "ascii"
4391     asn1_type_name = "IA5"
4392
4393
4394 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4395 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4396 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4397
4398
4399 class VisibleString(CommonString):
4400     __slots__ = ()
4401     tag_default = tag_encode(26)
4402     encoding = "ascii"
4403     asn1_type_name = "VisibleString"
4404
4405
4406 UTCTimeState = namedtuple(
4407     "UTCTimeState",
4408     OctetStringState._fields + ("ber_raw",),
4409     **NAMEDTUPLE_KWARGS
4410 )
4411
4412
4413 def str_to_time_fractions(value):
4414     v = pureint(value)
4415     year, v = (v // 10**10), (v % 10**10)
4416     month, v = (v // 10**8), (v % 10**8)
4417     day, v = (v // 10**6), (v % 10**6)
4418     hour, v = (v // 10**4), (v % 10**4)
4419     minute, second = (v // 100), (v % 100)
4420     return year, month, day, hour, minute, second
4421
4422
4423 class UTCTime(VisibleString):
4424     """``UTCTime`` datetime type
4425
4426     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4427     UTCTime UTCTime 2017-09-30T22:07:50
4428     >>> str(t)
4429     '170930220750Z'
4430     >>> bytes(t)
4431     b'170930220750Z'
4432     >>> t.todatetime()
4433     datetime.datetime(2017, 9, 30, 22, 7, 50)
4434     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
4435     datetime.datetime(1957, 9, 30, 22, 7, 50)
4436
4437     If BER encoded value was met, then ``ber_raw`` attribute will hold
4438     its raw representation.
4439
4440     .. warning::
4441
4442        Pay attention that UTCTime can not hold full year, so all years
4443        having < 50 years are treated as 20xx, 19xx otherwise, according
4444        to X.509 recommendation.
4445
4446     .. warning::
4447
4448        No strict validation of UTC offsets are made, but very crude:
4449
4450        * minutes are not exceeding 60
4451        * offset value is not exceeding 14 hours
4452     """
4453     __slots__ = ("ber_raw",)
4454     tag_default = tag_encode(23)
4455     encoding = "ascii"
4456     asn1_type_name = "UTCTime"
4457     evgen_mode_skip_value = False
4458
4459     def __init__(
4460             self,
4461             value=None,
4462             impl=None,
4463             expl=None,
4464             default=None,
4465             optional=False,
4466             _decoded=(0, 0, 0),
4467             bounds=None,  # dummy argument, workability for OctetString.decode
4468             ctx=None,
4469     ):
4470         """
4471         :param value: set the value. Either datetime type, or
4472                       :py:class:`pyderasn.UTCTime` object
4473         :param bytes impl: override default tag with ``IMPLICIT`` one
4474         :param bytes expl: override default tag with ``EXPLICIT`` one
4475         :param default: set default value. Type same as in ``value``
4476         :param bool optional: is object ``OPTIONAL`` in sequence
4477         """
4478         super(UTCTime, self).__init__(
4479             None, None, impl, expl, None, optional, _decoded, ctx,
4480         )
4481         self._value = value
4482         self.ber_raw = None
4483         if value is not None:
4484             self._value, self.ber_raw = self._value_sanitize(value, ctx)
4485             self.ber_encoded = self.ber_raw is not None
4486         if default is not None:
4487             default, _ = self._value_sanitize(default)
4488             self.default = self.__class__(
4489                 value=default,
4490                 impl=self.tag,
4491                 expl=self._expl,
4492             )
4493             if self._value is None:
4494                 self._value = default
4495             optional = True
4496         self.optional = optional
4497
4498     def _strptime_bered(self, value):
4499         year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
4500         value = value[10:]
4501         if len(value) == 0:
4502             raise ValueError("no timezone")
4503         year += 2000 if year < 50 else 1900
4504         decoded = datetime(year, month, day, hour, minute)
4505         offset = 0
4506         if value[-1] == "Z":
4507             value = value[:-1]
4508         else:
4509             if len(value) < 5:
4510                 raise ValueError("invalid UTC offset")
4511             if value[-5] == "-":
4512                 sign = -1
4513             elif value[-5] == "+":
4514                 sign = 1
4515             else:
4516                 raise ValueError("invalid UTC offset")
4517             v = pureint(value[-4:])
4518             offset, v = (60 * (v % 100)), v // 100
4519             if offset >= 3600:
4520                 raise ValueError("invalid UTC offset minutes")
4521             offset += 3600 * v
4522             if offset > 14 * 3600:
4523                 raise ValueError("too big UTC offset")
4524             offset *= sign
4525             value = value[:-5]
4526         if len(value) == 0:
4527             return offset, decoded
4528         if len(value) != 2:
4529             raise ValueError("invalid UTC offset seconds")
4530         seconds = pureint(value)
4531         if seconds >= 60:
4532             raise ValueError("invalid seconds value")
4533         return offset, decoded + timedelta(seconds=seconds)
4534
4535     def _strptime(self, value):
4536         # datetime.strptime's format: %y%m%d%H%M%SZ
4537         if len(value) != LEN_YYMMDDHHMMSSZ:
4538             raise ValueError("invalid UTCTime length")
4539         if value[-1] != "Z":
4540             raise ValueError("non UTC timezone")
4541         year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
4542         year += 2000 if year < 50 else 1900
4543         return datetime(year, month, day, hour, minute, second)
4544
4545     def _dt_sanitize(self, value):
4546         if value.year < 1950 or value.year > 2049:
4547             raise ValueError("UTCTime can hold only 1950-2049 years")
4548         return value.replace(microsecond=0)
4549
4550     def _value_sanitize(self, value, ctx=None):
4551         if value.__class__ == binary_type:
4552             try:
4553                 value_decoded = value.decode("ascii")
4554             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4555                 raise DecodeError("invalid UTCTime encoding: %r" % err)
4556             err = None
4557             try:
4558                 return self._strptime(value_decoded), None
4559             except (TypeError, ValueError) as _err:
4560                 err = _err
4561                 if (ctx is not None) and ctx.get("bered", False):
4562                     try:
4563                         offset, _value = self._strptime_bered(value_decoded)
4564                         _value = _value - timedelta(seconds=offset)
4565                         return self._dt_sanitize(_value), value
4566                     except (TypeError, ValueError, OverflowError) as _err:
4567                         err = _err
4568             raise DecodeError(
4569                 "invalid %s format: %r" % (self.asn1_type_name, err),
4570                 klass=self.__class__,
4571             )
4572         if isinstance(value, self.__class__):
4573             return value._value, None
4574         if value.__class__ == datetime:
4575             return self._dt_sanitize(value), None
4576         raise InvalidValueType((self.__class__, datetime))
4577
4578     def _pp_value(self):
4579         if self.ready:
4580             value = self._value.isoformat()
4581             if self.ber_encoded:
4582                 value += " (%s)" % self.ber_raw
4583             return value
4584
4585     def __unicode__(self):
4586         if self.ready:
4587             value = self._value.isoformat()
4588             if self.ber_encoded:
4589                 value += " (%s)" % self.ber_raw
4590             return value
4591         return text_type(self._pp_value())
4592
4593     def __getstate__(self):
4594         return UTCTimeState(
4595             *super(UTCTime, self).__getstate__(),
4596             **{"ber_raw": self.ber_raw}
4597         )
4598
4599     def __setstate__(self, state):
4600         super(UTCTime, self).__setstate__(state)
4601         self.ber_raw = state.ber_raw
4602
4603     def __bytes__(self):
4604         self._assert_ready()
4605         return self._encode_time()
4606
4607     def __eq__(self, their):
4608         if their.__class__ == binary_type:
4609             return self._encode_time() == their
4610         if their.__class__ == datetime:
4611             return self.todatetime() == their
4612         if not isinstance(their, self.__class__):
4613             return False
4614         return (
4615             self._value == their._value and
4616             self.tag == their.tag and
4617             self._expl == their._expl
4618         )
4619
4620     def _encode_time(self):
4621         return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
4622
4623     def _encode(self):
4624         self._assert_ready()
4625         value = self._encode_time()
4626         return b"".join((self.tag, len_encode(len(value)), value))
4627
4628     def _encode_cer(self, writer):
4629         write_full(writer, self._encode())
4630
4631     def todatetime(self):
4632         return self._value
4633
4634     def __repr__(self):
4635         return pp_console_row(next(self.pps()))
4636
4637     def pps(self, decode_path=()):
4638         yield _pp(
4639             obj=self,
4640             asn1_type_name=self.asn1_type_name,
4641             obj_name=self.__class__.__name__,
4642             decode_path=decode_path,
4643             value=self._pp_value(),
4644             optional=self.optional,
4645             default=self == self.default,
4646             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4647             expl=None if self._expl is None else tag_decode(self._expl),
4648             offset=self.offset,
4649             tlen=self.tlen,
4650             llen=self.llen,
4651             vlen=self.vlen,
4652             expl_offset=self.expl_offset if self.expled else None,
4653             expl_tlen=self.expl_tlen if self.expled else None,
4654             expl_llen=self.expl_llen if self.expled else None,
4655             expl_vlen=self.expl_vlen if self.expled else None,
4656             expl_lenindef=self.expl_lenindef,
4657             ber_encoded=self.ber_encoded,
4658             bered=self.bered,
4659         )
4660         for pp in self.pps_lenindef(decode_path):
4661             yield pp
4662
4663
4664 class GeneralizedTime(UTCTime):
4665     """``GeneralizedTime`` datetime type
4666
4667     This type is similar to :py:class:`pyderasn.UTCTime`.
4668
4669     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4670     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
4671     >>> str(t)
4672     '20170930220750.000123Z'
4673     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
4674     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
4675
4676     .. warning::
4677
4678        Only microsecond fractions are supported in DER encoding.
4679        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
4680        higher precision values.
4681
4682     .. warning::
4683
4684        BER encoded data can loss information (accuracy) during decoding
4685        because of float transformations.
4686
4687     .. warning::
4688
4689        Local times (without explicit timezone specification) are treated
4690        as UTC one, no transformations are made.
4691
4692     .. warning::
4693
4694        Zero year is unsupported.
4695     """
4696     __slots__ = ()
4697     tag_default = tag_encode(24)
4698     asn1_type_name = "GeneralizedTime"
4699
4700     def _dt_sanitize(self, value):
4701         return value
4702
4703     def _strptime_bered(self, value):
4704         if len(value) < 4 + 3 * 2:
4705             raise ValueError("invalid GeneralizedTime")
4706         year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
4707         decoded = datetime(year, month, day, hour)
4708         offset, value = 0, value[10:]
4709         if len(value) == 0:
4710             return offset, decoded
4711         if value[-1] == "Z":
4712             value = value[:-1]
4713         else:
4714             for char, sign in (("-", -1), ("+", 1)):
4715                 idx = value.rfind(char)
4716                 if idx == -1:
4717                     continue
4718                 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
4719                 v = pureint(offset_raw)
4720                 if len(offset_raw) == 4:
4721                     offset, v = (60 * (v % 100)), v // 100
4722                     if offset >= 3600:
4723                         raise ValueError("invalid UTC offset minutes")
4724                 elif len(offset_raw) == 2:
4725                     pass
4726                 else:
4727                     raise ValueError("invalid UTC offset")
4728                 offset += 3600 * v
4729                 if offset > 14 * 3600:
4730                     raise ValueError("too big UTC offset")
4731                 offset *= sign
4732                 break
4733         if len(value) == 0:
4734             return offset, decoded
4735         if value[0] in DECIMAL_SIGNS:
4736             return offset, (
4737                 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
4738             )
4739         if len(value) < 2:
4740             raise ValueError("stripped minutes")
4741         decoded += timedelta(seconds=60 * pureint(value[:2]))
4742         value = value[2:]
4743         if len(value) == 0:
4744             return offset, decoded
4745         if value[0] in DECIMAL_SIGNS:
4746             return offset, (
4747                 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
4748             )
4749         if len(value) < 2:
4750             raise ValueError("stripped seconds")
4751         decoded += timedelta(seconds=pureint(value[:2]))
4752         value = value[2:]
4753         if len(value) == 0:
4754             return offset, decoded
4755         if value[0] not in DECIMAL_SIGNS:
4756             raise ValueError("invalid format after seconds")
4757         return offset, (
4758             decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
4759         )
4760
4761     def _strptime(self, value):
4762         l = len(value)
4763         if l == LEN_YYYYMMDDHHMMSSZ:
4764             # datetime.strptime's format: %Y%m%d%H%M%SZ
4765             if value[-1] != "Z":
4766                 raise ValueError("non UTC timezone")
4767             return datetime(*str_to_time_fractions(value[:-1]))
4768         if l >= LEN_YYYYMMDDHHMMSSDMZ:
4769             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4770             if value[-1] != "Z":
4771                 raise ValueError("non UTC timezone")
4772             if value[14] != ".":
4773                 raise ValueError("no fractions separator")
4774             us = value[15:-1]
4775             if us[-1] == "0":
4776                 raise ValueError("trailing zero")
4777             us_len = len(us)
4778             if us_len > 6:
4779                 raise ValueError("only microsecond fractions are supported")
4780             us = pureint(us + ("0" * (6 - us_len)))
4781             year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
4782             return datetime(year, month, day, hour, minute, second, us)
4783         raise ValueError("invalid GeneralizedTime length")
4784
4785     def _encode_time(self):
4786         value = self._value
4787         encoded = value.strftime("%Y%m%d%H%M%S")
4788         if value.microsecond > 0:
4789             encoded += (".%06d" % value.microsecond).rstrip("0")
4790         return (encoded + "Z").encode("ascii")
4791
4792
4793 class GraphicString(CommonString):
4794     __slots__ = ()
4795     tag_default = tag_encode(25)
4796     encoding = "iso-8859-1"
4797     asn1_type_name = "GraphicString"
4798
4799
4800 class ISO646String(VisibleString):
4801     __slots__ = ()
4802     asn1_type_name = "ISO646String"
4803
4804
4805 class GeneralString(CommonString):
4806     __slots__ = ()
4807     tag_default = tag_encode(27)
4808     encoding = "iso-8859-1"
4809     asn1_type_name = "GeneralString"
4810
4811
4812 class UniversalString(CommonString):
4813     __slots__ = ()
4814     tag_default = tag_encode(28)
4815     encoding = "utf-32-be"
4816     asn1_type_name = "UniversalString"
4817
4818
4819 class BMPString(CommonString):
4820     __slots__ = ()
4821     tag_default = tag_encode(30)
4822     encoding = "utf-16-be"
4823     asn1_type_name = "BMPString"
4824
4825
4826 ChoiceState = namedtuple(
4827     "ChoiceState",
4828     BasicState._fields + ("specs", "value",),
4829     **NAMEDTUPLE_KWARGS
4830 )
4831
4832
4833 class Choice(Obj):
4834     """``CHOICE`` special type
4835
4836     ::
4837
4838         class GeneralName(Choice):
4839             schema = (
4840                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4841                 ("dNSName", IA5String(impl=tag_ctxp(2))),
4842             )
4843
4844     >>> gn = GeneralName()
4845     GeneralName CHOICE
4846     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4847     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4848     >>> gn["dNSName"] = IA5String("bar.baz")
4849     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4850     >>> gn["rfc822Name"]
4851     None
4852     >>> gn["dNSName"]
4853     [2] IA5String IA5 bar.baz
4854     >>> gn.choice
4855     'dNSName'
4856     >>> gn.value == gn["dNSName"]
4857     True
4858     >>> gn.specs
4859     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4860
4861     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4862     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4863     """
4864     __slots__ = ("specs",)
4865     tag_default = None
4866     asn1_type_name = "CHOICE"
4867
4868     def __init__(
4869             self,
4870             value=None,
4871             schema=None,
4872             impl=None,
4873             expl=None,
4874             default=None,
4875             optional=False,
4876             _decoded=(0, 0, 0),
4877     ):
4878         """
4879         :param value: set the value. Either ``(choice, value)`` tuple, or
4880                       :py:class:`pyderasn.Choice` object
4881         :param bytes impl: can not be set, do **not** use it
4882         :param bytes expl: override default tag with ``EXPLICIT`` one
4883         :param default: set default value. Type same as in ``value``
4884         :param bool optional: is object ``OPTIONAL`` in sequence
4885         """
4886         if impl is not None:
4887             raise ValueError("no implicit tag allowed for CHOICE")
4888         super(Choice, self).__init__(None, expl, default, optional, _decoded)
4889         if schema is None:
4890             schema = getattr(self, "schema", ())
4891         if len(schema) == 0:
4892             raise ValueError("schema must be specified")
4893         self.specs = (
4894             schema if schema.__class__ == OrderedDict else OrderedDict(schema)
4895         )
4896         self._value = None
4897         if value is not None:
4898             self._value = self._value_sanitize(value)
4899         if default is not None:
4900             default_value = self._value_sanitize(default)
4901             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4902             default_obj.specs = self.specs
4903             default_obj._value = default_value
4904             self.default = default_obj
4905             if value is None:
4906                 self._value = copy(default_obj._value)
4907         if self._expl is not None:
4908             tag_class, _, tag_num = tag_decode(self._expl)
4909             self._tag_order = (tag_class, tag_num)
4910
4911     def _value_sanitize(self, value):
4912         if (value.__class__ == tuple) and len(value) == 2:
4913             choice, obj = value
4914             spec = self.specs.get(choice)
4915             if spec is None:
4916                 raise ObjUnknown(choice)
4917             if not isinstance(obj, spec.__class__):
4918                 raise InvalidValueType((spec,))
4919             return (choice, spec(obj))
4920         if isinstance(value, self.__class__):
4921             return value._value
4922         raise InvalidValueType((self.__class__, tuple))
4923
4924     @property
4925     def ready(self):
4926         return self._value is not None and self._value[1].ready
4927
4928     @property
4929     def bered(self):
4930         return self.expl_lenindef or (
4931             (self._value is not None) and
4932             self._value[1].bered
4933         )
4934
4935     def __getstate__(self):
4936         return ChoiceState(
4937             __version__,
4938             self.tag,
4939             self._tag_order,
4940             self._expl,
4941             self.default,
4942             self.optional,
4943             self.offset,
4944             self.llen,
4945             self.vlen,
4946             self.expl_lenindef,
4947             self.lenindef,
4948             self.ber_encoded,
4949             self.specs,
4950             copy(self._value),
4951         )
4952
4953     def __setstate__(self, state):
4954         super(Choice, self).__setstate__(state)
4955         self.specs = state.specs
4956         self._value = state.value
4957
4958     def __eq__(self, their):
4959         if (their.__class__ == tuple) and len(their) == 2:
4960             return self._value == their
4961         if not isinstance(their, self.__class__):
4962             return False
4963         return (
4964             self.specs == their.specs and
4965             self._value == their._value
4966         )
4967
4968     def __call__(
4969             self,
4970             value=None,
4971             expl=None,
4972             default=None,
4973             optional=None,
4974     ):
4975         return self.__class__(
4976             value=value,
4977             schema=self.specs,
4978             expl=self._expl if expl is None else expl,
4979             default=self.default if default is None else default,
4980             optional=self.optional if optional is None else optional,
4981         )
4982
4983     @property
4984     def choice(self):
4985         """Name of the choice
4986         """
4987         self._assert_ready()
4988         return self._value[0]
4989
4990     @property
4991     def value(self):
4992         """Value of underlying choice
4993         """
4994         self._assert_ready()
4995         return self._value[1]
4996
4997     @property
4998     def tag_order(self):
4999         self._assert_ready()
5000         return self._value[1].tag_order if self._tag_order is None else self._tag_order
5001
5002     @property
5003     def tag_order_cer(self):
5004         return min(v.tag_order_cer for v in itervalues(self.specs))
5005
5006     def __getitem__(self, key):
5007         if key not in self.specs:
5008             raise ObjUnknown(key)
5009         if self._value is None:
5010             return None
5011         choice, value = self._value
5012         if choice != key:
5013             return None
5014         return value
5015
5016     def __setitem__(self, key, value):
5017         spec = self.specs.get(key)
5018         if spec is None:
5019             raise ObjUnknown(key)
5020         if not isinstance(value, spec.__class__):
5021             raise InvalidValueType((spec.__class__,))
5022         self._value = (key, spec(value))
5023
5024     @property
5025     def tlen(self):
5026         return 0
5027
5028     @property
5029     def decoded(self):
5030         return self._value[1].decoded if self.ready else False
5031
5032     def _encode(self):
5033         self._assert_ready()
5034         return self._value[1].encode()
5035
5036     def _encode_cer(self, writer):
5037         self._assert_ready()
5038         self._value[1].encode_cer(writer)
5039
5040     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5041         for choice, spec in iteritems(self.specs):
5042             sub_decode_path = decode_path + (choice,)
5043             try:
5044                 spec.decode(
5045                     tlv,
5046                     offset=offset,
5047                     leavemm=True,
5048                     decode_path=sub_decode_path,
5049                     ctx=ctx,
5050                     tag_only=True,
5051                     _ctx_immutable=False,
5052                 )
5053             except TagMismatch:
5054                 continue
5055             break
5056         else:
5057             raise TagMismatch(
5058                 klass=self.__class__,
5059                 decode_path=decode_path,
5060                 offset=offset,
5061             )
5062         if tag_only:  # pragma: no cover
5063             yield None
5064             return
5065         if evgen_mode:
5066             for _decode_path, value, tail in spec.decode_evgen(
5067                     tlv,
5068                     offset=offset,
5069                     leavemm=True,
5070                     decode_path=sub_decode_path,
5071                     ctx=ctx,
5072                     _ctx_immutable=False,
5073             ):
5074                 yield _decode_path, value, tail
5075         else:
5076             _, value, tail = next(spec.decode_evgen(
5077                 tlv,
5078                 offset=offset,
5079                 leavemm=True,
5080                 decode_path=sub_decode_path,
5081                 ctx=ctx,
5082                 _ctx_immutable=False,
5083                 _evgen_mode=False,
5084             ))
5085         obj = self.__class__(
5086             schema=self.specs,
5087             expl=self._expl,
5088             default=self.default,
5089             optional=self.optional,
5090             _decoded=(offset, 0, value.fulllen),
5091         )
5092         obj._value = (choice, value)
5093         yield decode_path, obj, tail
5094
5095     def __repr__(self):
5096         value = pp_console_row(next(self.pps()))
5097         if self.ready:
5098             value = "%s[%r]" % (value, self.value)
5099         return value
5100
5101     def pps(self, decode_path=()):
5102         yield _pp(
5103             obj=self,
5104             asn1_type_name=self.asn1_type_name,
5105             obj_name=self.__class__.__name__,
5106             decode_path=decode_path,
5107             value=self.choice if self.ready else None,
5108             optional=self.optional,
5109             default=self == self.default,
5110             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5111             expl=None if self._expl is None else tag_decode(self._expl),
5112             offset=self.offset,
5113             tlen=self.tlen,
5114             llen=self.llen,
5115             vlen=self.vlen,
5116             expl_lenindef=self.expl_lenindef,
5117             bered=self.bered,
5118         )
5119         if self.ready:
5120             yield self.value.pps(decode_path=decode_path + (self.choice,))
5121         for pp in self.pps_lenindef(decode_path):
5122             yield pp
5123
5124
5125 class PrimitiveTypes(Choice):
5126     """Predefined ``CHOICE`` for all generic primitive types
5127
5128     It could be useful for general decoding of some unspecified values:
5129
5130     >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
5131     OCTET STRING 3 bytes 666f6f
5132     >>> PrimitiveTypes().decod(hexdec("0203123456")).value
5133     INTEGER 1193046
5134     """
5135     __slots__ = ()
5136     schema = tuple((klass.__name__, klass()) for klass in (
5137         Boolean,
5138         Integer,
5139         BitString,
5140         OctetString,
5141         Null,
5142         ObjectIdentifier,
5143         UTF8String,
5144         NumericString,
5145         PrintableString,
5146         TeletexString,
5147         VideotexString,
5148         IA5String,
5149         UTCTime,
5150         GeneralizedTime,
5151         GraphicString,
5152         VisibleString,
5153         ISO646String,
5154         GeneralString,
5155         UniversalString,
5156         BMPString,
5157     ))
5158
5159
5160 AnyState = namedtuple(
5161     "AnyState",
5162     BasicState._fields + ("value", "defined"),
5163     **NAMEDTUPLE_KWARGS
5164 )
5165
5166
5167 class Any(Obj):
5168     """``ANY`` special type
5169
5170     >>> Any(Integer(-123))
5171     ANY INTEGER -123 (0X:7B)
5172     >>> a = Any(OctetString(b"hello world").encode())
5173     ANY 040b68656c6c6f20776f726c64
5174     >>> hexenc(bytes(a))
5175     b'0x040x0bhello world'
5176     """
5177     __slots__ = ("defined",)
5178     tag_default = tag_encode(0)
5179     asn1_type_name = "ANY"
5180
5181     def __init__(
5182             self,
5183             value=None,
5184             expl=None,
5185             optional=False,
5186             _decoded=(0, 0, 0),
5187     ):
5188         """
5189         :param value: set the value. Either any kind of pyderasn's
5190                       **ready** object, or bytes. Pay attention that
5191                       **no** validation is performed if raw binary value
5192                       is valid TLV, except just tag decoding
5193         :param bytes expl: override default tag with ``EXPLICIT`` one
5194         :param bool optional: is object ``OPTIONAL`` in sequence
5195         """
5196         super(Any, self).__init__(None, expl, None, optional, _decoded)
5197         if value is None:
5198             self._value = None
5199         else:
5200             value = self._value_sanitize(value)
5201             self._value = value
5202             if self._expl is None:
5203                 if value.__class__ == binary_type:
5204                     tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5205                 else:
5206                     tag_class, tag_num = value.tag_order
5207             else:
5208                 tag_class, _, tag_num = tag_decode(self._expl)
5209             self._tag_order = (tag_class, tag_num)
5210         self.defined = None
5211
5212     def _value_sanitize(self, value):
5213         if value.__class__ == binary_type:
5214             if len(value) == 0:
5215                 raise ValueError("Any value can not be empty")
5216             return value
5217         if isinstance(value, self.__class__):
5218             return value._value
5219         if not isinstance(value, Obj):
5220             raise InvalidValueType((self.__class__, Obj, binary_type))
5221         return value
5222
5223     @property
5224     def ready(self):
5225         return self._value is not None
5226
5227     @property
5228     def tag_order(self):
5229         self._assert_ready()
5230         return self._tag_order
5231
5232     @property
5233     def bered(self):
5234         if self.expl_lenindef or self.lenindef:
5235             return True
5236         if self.defined is None:
5237             return False
5238         return self.defined[1].bered
5239
5240     def __getstate__(self):
5241         return AnyState(
5242             __version__,
5243             self.tag,
5244             self._tag_order,
5245             self._expl,
5246             None,
5247             self.optional,
5248             self.offset,
5249             self.llen,
5250             self.vlen,
5251             self.expl_lenindef,
5252             self.lenindef,
5253             self.ber_encoded,
5254             self._value,
5255             self.defined,
5256         )
5257
5258     def __setstate__(self, state):
5259         super(Any, self).__setstate__(state)
5260         self._value = state.value
5261         self.defined = state.defined
5262
5263     def __eq__(self, their):
5264         if their.__class__ == binary_type:
5265             if self._value.__class__ == binary_type:
5266                 return self._value == their
5267             return self._value.encode() == their
5268         if issubclass(their.__class__, Any):
5269             if self.ready and their.ready:
5270                 return bytes(self) == bytes(their)
5271             return self.ready == their.ready
5272         return False
5273
5274     def __call__(
5275             self,
5276             value=None,
5277             expl=None,
5278             optional=None,
5279     ):
5280         return self.__class__(
5281             value=value,
5282             expl=self._expl if expl is None else expl,
5283             optional=self.optional if optional is None else optional,
5284         )
5285
5286     def __bytes__(self):
5287         self._assert_ready()
5288         value = self._value
5289         if value.__class__ == binary_type:
5290             return value
5291         return self._value.encode()
5292
5293     @property
5294     def tlen(self):
5295         return 0
5296
5297     def _encode(self):
5298         self._assert_ready()
5299         value = self._value
5300         if value.__class__ == binary_type:
5301             return value
5302         return value.encode()
5303
5304     def _encode_cer(self, writer):
5305         self._assert_ready()
5306         value = self._value
5307         if value.__class__ == binary_type:
5308             write_full(writer, value)
5309         else:
5310             value.encode_cer(writer)
5311
5312     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5313         try:
5314             t, tlen, lv = tag_strip(tlv)
5315         except DecodeError as err:
5316             raise err.__class__(
5317                 msg=err.msg,
5318                 klass=self.__class__,
5319                 decode_path=decode_path,
5320                 offset=offset,
5321             )
5322         try:
5323             l, llen, v = len_decode(lv)
5324         except LenIndefForm as err:
5325             if not ctx.get("bered", False):
5326                 raise err.__class__(
5327                     msg=err.msg,
5328                     klass=self.__class__,
5329                     decode_path=decode_path,
5330                     offset=offset,
5331                 )
5332             llen, vlen, v = 1, 0, lv[1:]
5333             sub_offset = offset + tlen + llen
5334             chunk_i = 0
5335             while v[:EOC_LEN].tobytes() != EOC:
5336                 chunk, v = Any().decode(
5337                     v,
5338                     offset=sub_offset,
5339                     decode_path=decode_path + (str(chunk_i),),
5340                     leavemm=True,
5341                     ctx=ctx,
5342                     _ctx_immutable=False,
5343                 )
5344                 vlen += chunk.tlvlen
5345                 sub_offset += chunk.tlvlen
5346                 chunk_i += 1
5347             tlvlen = tlen + llen + vlen + EOC_LEN
5348             obj = self.__class__(
5349                 value=None if evgen_mode else tlv[:tlvlen].tobytes(),
5350                 expl=self._expl,
5351                 optional=self.optional,
5352                 _decoded=(offset, 0, tlvlen),
5353             )
5354             obj.lenindef = True
5355             obj.tag = t.tobytes()
5356             yield decode_path, obj, v[EOC_LEN:]
5357             return
5358         except DecodeError as err:
5359             raise err.__class__(
5360                 msg=err.msg,
5361                 klass=self.__class__,
5362                 decode_path=decode_path,
5363                 offset=offset,
5364             )
5365         if l > len(v):
5366             raise NotEnoughData(
5367                 "encoded length is longer than data",
5368                 klass=self.__class__,
5369                 decode_path=decode_path,
5370                 offset=offset,
5371             )
5372         tlvlen = tlen + llen + l
5373         v, tail = tlv[:tlvlen], v[l:]
5374         obj = self.__class__(
5375             value=None if evgen_mode else v.tobytes(),
5376             expl=self._expl,
5377             optional=self.optional,
5378             _decoded=(offset, 0, tlvlen),
5379         )
5380         obj.tag = t.tobytes()
5381         yield decode_path, obj, tail
5382
5383     def __repr__(self):
5384         return pp_console_row(next(self.pps()))
5385
5386     def pps(self, decode_path=()):
5387         value = self._value
5388         if value is None:
5389             pass
5390         elif value.__class__ == binary_type:
5391             value = None
5392         else:
5393             value = repr(value)
5394         yield _pp(
5395             obj=self,
5396             asn1_type_name=self.asn1_type_name,
5397             obj_name=self.__class__.__name__,
5398             decode_path=decode_path,
5399             value=value,
5400             blob=self._value if self._value.__class__ == binary_type else None,
5401             optional=self.optional,
5402             default=self == self.default,
5403             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5404             expl=None if self._expl is None else tag_decode(self._expl),
5405             offset=self.offset,
5406             tlen=self.tlen,
5407             llen=self.llen,
5408             vlen=self.vlen,
5409             expl_offset=self.expl_offset if self.expled else None,
5410             expl_tlen=self.expl_tlen if self.expled else None,
5411             expl_llen=self.expl_llen if self.expled else None,
5412             expl_vlen=self.expl_vlen if self.expled else None,
5413             expl_lenindef=self.expl_lenindef,
5414             lenindef=self.lenindef,
5415             bered=self.bered,
5416         )
5417         defined_by, defined = self.defined or (None, None)
5418         if defined_by is not None:
5419             yield defined.pps(
5420                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
5421             )
5422         for pp in self.pps_lenindef(decode_path):
5423             yield pp
5424
5425
5426 ########################################################################
5427 # ASN.1 constructed types
5428 ########################################################################
5429
5430 def get_def_by_path(defines_by_path, sub_decode_path):
5431     """Get define by decode path
5432     """
5433     for path, define in defines_by_path:
5434         if len(path) != len(sub_decode_path):
5435             continue
5436         for p1, p2 in zip(path, sub_decode_path):
5437             if (not p1 is any) and (p1 != p2):
5438                 break
5439         else:
5440             return define
5441
5442
5443 def abs_decode_path(decode_path, rel_path):
5444     """Create an absolute decode path from current and relative ones
5445
5446     :param decode_path: current decode path, starting point. Tuple of strings
5447     :param rel_path: relative path to ``decode_path``. Tuple of strings.
5448                      If first tuple's element is "/", then treat it as
5449                      an absolute path, ignoring ``decode_path`` as
5450                      starting point. Also this tuple can contain ".."
5451                      elements, stripping the leading element from
5452                      ``decode_path``
5453
5454     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
5455     ("foo", "bar", "baz", "whatever")
5456     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
5457     ("foo", "whatever")
5458     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
5459     ("baz", "whatever")
5460     """
5461     if rel_path[0] == "/":
5462         return rel_path[1:]
5463     if rel_path[0] == "..":
5464         return abs_decode_path(decode_path[:-1], rel_path[1:])
5465     return decode_path + rel_path
5466
5467
5468 SequenceState = namedtuple(
5469     "SequenceState",
5470     BasicState._fields + ("specs", "value",),
5471     **NAMEDTUPLE_KWARGS
5472 )
5473
5474
5475 class Sequence(Obj):
5476     """``SEQUENCE`` structure type
5477
5478     You have to make specification of sequence::
5479
5480         class Extension(Sequence):
5481             schema = (
5482                 ("extnID", ObjectIdentifier()),
5483                 ("critical", Boolean(default=False)),
5484                 ("extnValue", OctetString()),
5485             )
5486
5487     Then, you can work with it as with dictionary.
5488
5489     >>> ext = Extension()
5490     >>> Extension().specs
5491     OrderedDict([
5492         ('extnID', OBJECT IDENTIFIER),
5493         ('critical', BOOLEAN False OPTIONAL DEFAULT),
5494         ('extnValue', OCTET STRING),
5495     ])
5496     >>> ext["extnID"] = "1.2.3"
5497     Traceback (most recent call last):
5498     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5499     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5500
5501     You can determine if sequence is ready to be encoded:
5502
5503     >>> ext.ready
5504     False
5505     >>> ext.encode()
5506     Traceback (most recent call last):
5507     pyderasn.ObjNotReady: object is not ready: extnValue
5508     >>> ext["extnValue"] = OctetString(b"foobar")
5509     >>> ext.ready
5510     True
5511
5512     Value you want to assign, must have the same **type** as in
5513     corresponding specification, but it can have different tags,
5514     optional/default attributes -- they will be taken from specification
5515     automatically::
5516
5517         class TBSCertificate(Sequence):
5518             schema = (
5519                 ("version", Version(expl=tag_ctxc(0), default="v1")),
5520             [...]
5521
5522     >>> tbs = TBSCertificate()
5523     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5524
5525     Assign ``None`` to remove value from sequence.
5526
5527     You can set values in Sequence during its initialization:
5528
5529     >>> AlgorithmIdentifier((
5530         ("algorithm", ObjectIdentifier("1.2.3")),
5531         ("parameters", Any(Null()))
5532     ))
5533     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5534
5535     You can determine if value exists/set in the sequence and take its value:
5536
5537     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5538     (True, True, False)
5539     >>> ext["extnID"]
5540     OBJECT IDENTIFIER 1.2.3
5541
5542     But pay attention that if value has default, then it won't be (not
5543     in) in the sequence (because ``DEFAULT`` must not be encoded in
5544     DER), but you can read its value:
5545
5546     >>> "critical" in ext, ext["critical"]
5547     (False, BOOLEAN False)
5548     >>> ext["critical"] = Boolean(True)
5549     >>> "critical" in ext, ext["critical"]
5550     (True, BOOLEAN True)
5551
5552     All defaulted values are always optional.
5553
5554     .. _allow_default_values_ctx:
5555
5556     DER prohibits default value encoding and will raise an error if
5557     default value is unexpectedly met during decode.
5558     If :ref:`bered <bered_ctx>` context option is set, then no error
5559     will be raised, but ``bered`` attribute set. You can disable strict
5560     defaulted values existence validation by setting
5561     ``"allow_default_values": True`` :ref:`context <ctx>` option.
5562
5563     .. warning::
5564
5565        Check for default value existence is not performed in
5566        ``evgen_mode``, because previously decoded values are not stored
5567        in memory, to be able to compare them.
5568
5569     Two sequences are equal if they have equal specification (schema),
5570     implicit/explicit tagging and the same values.
5571     """
5572     __slots__ = ("specs",)
5573     tag_default = tag_encode(form=TagFormConstructed, num=16)
5574     asn1_type_name = "SEQUENCE"
5575
5576     def __init__(
5577             self,
5578             value=None,
5579             schema=None,
5580             impl=None,
5581             expl=None,
5582             default=None,
5583             optional=False,
5584             _decoded=(0, 0, 0),
5585     ):
5586         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5587         if schema is None:
5588             schema = getattr(self, "schema", ())
5589         self.specs = (
5590             schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5591         )
5592         self._value = {}
5593         if value is not None:
5594             if issubclass(value.__class__, Sequence):
5595                 self._value = value._value
5596             elif hasattr(value, "__iter__"):
5597                 for seq_key, seq_value in value:
5598                     self[seq_key] = seq_value
5599             else:
5600                 raise InvalidValueType((Sequence,))
5601         if default is not None:
5602             if not issubclass(default.__class__, Sequence):
5603                 raise InvalidValueType((Sequence,))
5604             default_value = default._value
5605             default_obj = self.__class__(impl=self.tag, expl=self._expl)
5606             default_obj.specs = self.specs
5607             default_obj._value = default_value
5608             self.default = default_obj
5609             if value is None:
5610                 self._value = copy(default_obj._value)
5611
5612     @property
5613     def ready(self):
5614         for name, spec in iteritems(self.specs):
5615             value = self._value.get(name)
5616             if value is None:
5617                 if spec.optional:
5618                     continue
5619                 return False
5620             if not value.ready:
5621                 return False
5622         return True
5623
5624     @property
5625     def bered(self):
5626         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5627             return True
5628         return any(value.bered for value in itervalues(self._value))
5629
5630     def __getstate__(self):
5631         return SequenceState(
5632             __version__,
5633             self.tag,
5634             self._tag_order,
5635             self._expl,
5636             self.default,
5637             self.optional,
5638             self.offset,
5639             self.llen,
5640             self.vlen,
5641             self.expl_lenindef,
5642             self.lenindef,
5643             self.ber_encoded,
5644             self.specs,
5645             {k: copy(v) for k, v in iteritems(self._value)},
5646         )
5647
5648     def __setstate__(self, state):
5649         super(Sequence, self).__setstate__(state)
5650         self.specs = state.specs
5651         self._value = state.value
5652
5653     def __eq__(self, their):
5654         if not isinstance(their, self.__class__):
5655             return False
5656         return (
5657             self.specs == their.specs and
5658             self.tag == their.tag and
5659             self._expl == their._expl and
5660             self._value == their._value
5661         )
5662
5663     def __call__(
5664             self,
5665             value=None,
5666             impl=None,
5667             expl=None,
5668             default=None,
5669             optional=None,
5670     ):
5671         return self.__class__(
5672             value=value,
5673             schema=self.specs,
5674             impl=self.tag if impl is None else impl,
5675             expl=self._expl if expl is None else expl,
5676             default=self.default if default is None else default,
5677             optional=self.optional if optional is None else optional,
5678         )
5679
5680     def __contains__(self, key):
5681         return key in self._value
5682
5683     def __setitem__(self, key, value):
5684         spec = self.specs.get(key)
5685         if spec is None:
5686             raise ObjUnknown(key)
5687         if value is None:
5688             self._value.pop(key, None)
5689             return
5690         if not isinstance(value, spec.__class__):
5691             raise InvalidValueType((spec.__class__,))
5692         value = spec(value=value)
5693         if spec.default is not None and value == spec.default:
5694             self._value.pop(key, None)
5695             return
5696         self._value[key] = value
5697
5698     def __getitem__(self, key):
5699         value = self._value.get(key)
5700         if value is not None:
5701             return value
5702         spec = self.specs.get(key)
5703         if spec is None:
5704             raise ObjUnknown(key)
5705         if spec.default is not None:
5706             return spec.default
5707         return None
5708
5709     def _values_for_encoding(self):
5710         for name, spec in iteritems(self.specs):
5711             value = self._value.get(name)
5712             if value is None:
5713                 if spec.optional:
5714                     continue
5715                 raise ObjNotReady(name)
5716             yield value
5717
5718     def _encode(self):
5719         v = b"".join(v.encode() for v in self._values_for_encoding())
5720         return b"".join((self.tag, len_encode(len(v)), v))
5721
5722     def _encode_cer(self, writer):
5723         write_full(writer, self.tag + LENINDEF)
5724         for v in self._values_for_encoding():
5725             v.encode_cer(writer)
5726         write_full(writer, EOC)
5727
5728     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5729         try:
5730             t, tlen, lv = tag_strip(tlv)
5731         except DecodeError as err:
5732             raise err.__class__(
5733                 msg=err.msg,
5734                 klass=self.__class__,
5735                 decode_path=decode_path,
5736                 offset=offset,
5737             )
5738         if t != self.tag:
5739             raise TagMismatch(
5740                 klass=self.__class__,
5741                 decode_path=decode_path,
5742                 offset=offset,
5743             )
5744         if tag_only:  # pragma: no cover
5745             yield None
5746             return
5747         lenindef = False
5748         ctx_bered = ctx.get("bered", False)
5749         try:
5750             l, llen, v = len_decode(lv)
5751         except LenIndefForm as err:
5752             if not ctx_bered:
5753                 raise err.__class__(
5754                     msg=err.msg,
5755                     klass=self.__class__,
5756                     decode_path=decode_path,
5757                     offset=offset,
5758                 )
5759             l, llen, v = 0, 1, lv[1:]
5760             lenindef = True
5761         except DecodeError as err:
5762             raise err.__class__(
5763                 msg=err.msg,
5764                 klass=self.__class__,
5765                 decode_path=decode_path,
5766                 offset=offset,
5767             )
5768         if l > len(v):
5769             raise NotEnoughData(
5770                 "encoded length is longer than data",
5771                 klass=self.__class__,
5772                 decode_path=decode_path,
5773                 offset=offset,
5774             )
5775         if not lenindef:
5776             v, tail = v[:l], v[l:]
5777         vlen = 0
5778         sub_offset = offset + tlen + llen
5779         values = {}
5780         ber_encoded = False
5781         ctx_allow_default_values = ctx.get("allow_default_values", False)
5782         for name, spec in iteritems(self.specs):
5783             if spec.optional and (
5784                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5785                     len(v) == 0
5786             ):
5787                 continue
5788             sub_decode_path = decode_path + (name,)
5789             try:
5790                 if evgen_mode:
5791                     for _decode_path, value, v_tail in spec.decode_evgen(
5792                             v,
5793                             sub_offset,
5794                             leavemm=True,
5795                             decode_path=sub_decode_path,
5796                             ctx=ctx,
5797                             _ctx_immutable=False,
5798                     ):
5799                         yield _decode_path, value, v_tail
5800                 else:
5801                     _, value, v_tail = next(spec.decode_evgen(
5802                         v,
5803                         sub_offset,
5804                         leavemm=True,
5805                         decode_path=sub_decode_path,
5806                         ctx=ctx,
5807                         _ctx_immutable=False,
5808                         _evgen_mode=False,
5809                     ))
5810             except TagMismatch as err:
5811                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5812                     continue
5813                 raise
5814
5815             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5816             if not evgen_mode and defined is not None:
5817                 defined_by, defined_spec = defined
5818                 if issubclass(value.__class__, SequenceOf):
5819                     for i, _value in enumerate(value):
5820                         sub_sub_decode_path = sub_decode_path + (
5821                             str(i),
5822                             DecodePathDefBy(defined_by),
5823                         )
5824                         defined_value, defined_tail = defined_spec.decode(
5825                             memoryview(bytes(_value)),
5826                             sub_offset + (
5827                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5828                                 if value.expled else (value.tlen + value.llen)
5829                             ),
5830                             leavemm=True,
5831                             decode_path=sub_sub_decode_path,
5832                             ctx=ctx,
5833                             _ctx_immutable=False,
5834                         )
5835                         if len(defined_tail) > 0:
5836                             raise DecodeError(
5837                                 "remaining data",
5838                                 klass=self.__class__,
5839                                 decode_path=sub_sub_decode_path,
5840                                 offset=offset,
5841                             )
5842                         _value.defined = (defined_by, defined_value)
5843                 else:
5844                     defined_value, defined_tail = defined_spec.decode(
5845                         memoryview(bytes(value)),
5846                         sub_offset + (
5847                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5848                             if value.expled else (value.tlen + value.llen)
5849                         ),
5850                         leavemm=True,
5851                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5852                         ctx=ctx,
5853                         _ctx_immutable=False,
5854                     )
5855                     if len(defined_tail) > 0:
5856                         raise DecodeError(
5857                             "remaining data",
5858                             klass=self.__class__,
5859                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5860                             offset=offset,
5861                         )
5862                     value.defined = (defined_by, defined_value)
5863
5864             value_len = value.fulllen
5865             vlen += value_len
5866             sub_offset += value_len
5867             v = v_tail
5868             if not evgen_mode:
5869                 if spec.default is not None and value == spec.default:
5870                     # This will not work in evgen_mode
5871                     if ctx_bered or ctx_allow_default_values:
5872                         ber_encoded = True
5873                     else:
5874                         raise DecodeError(
5875                             "DEFAULT value met",
5876                             klass=self.__class__,
5877                             decode_path=sub_decode_path,
5878                             offset=sub_offset,
5879                         )
5880                 values[name] = value
5881                 spec_defines = getattr(spec, "defines", ())
5882                 if len(spec_defines) == 0:
5883                     defines_by_path = ctx.get("defines_by_path", ())
5884                     if len(defines_by_path) > 0:
5885                         spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5886                 if spec_defines is not None and len(spec_defines) > 0:
5887                     for rel_path, schema in spec_defines:
5888                         defined = schema.get(value, None)
5889                         if defined is not None:
5890                             ctx.setdefault("_defines", []).append((
5891                                 abs_decode_path(sub_decode_path[:-1], rel_path),
5892                                 (value, defined),
5893                             ))
5894         if lenindef:
5895             if v[:EOC_LEN].tobytes() != EOC:
5896                 raise DecodeError(
5897                     "no EOC",
5898                     klass=self.__class__,
5899                     decode_path=decode_path,
5900                     offset=offset,
5901                 )
5902             tail = v[EOC_LEN:]
5903             vlen += EOC_LEN
5904         elif len(v) > 0:
5905             raise DecodeError(
5906                 "remaining data",
5907                 klass=self.__class__,
5908                 decode_path=decode_path,
5909                 offset=offset,
5910             )
5911         obj = self.__class__(
5912             schema=self.specs,
5913             impl=self.tag,
5914             expl=self._expl,
5915             default=self.default,
5916             optional=self.optional,
5917             _decoded=(offset, llen, vlen),
5918         )
5919         obj._value = values
5920         obj.lenindef = lenindef
5921         obj.ber_encoded = ber_encoded
5922         yield decode_path, obj, tail
5923
5924     def __repr__(self):
5925         value = pp_console_row(next(self.pps()))
5926         cols = []
5927         for name in self.specs:
5928             _value = self._value.get(name)
5929             if _value is None:
5930                 continue
5931             cols.append("%s: %s" % (name, repr(_value)))
5932         return "%s[%s]" % (value, "; ".join(cols))
5933
5934     def pps(self, decode_path=()):
5935         yield _pp(
5936             obj=self,
5937             asn1_type_name=self.asn1_type_name,
5938             obj_name=self.__class__.__name__,
5939             decode_path=decode_path,
5940             optional=self.optional,
5941             default=self == self.default,
5942             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5943             expl=None if self._expl is None else tag_decode(self._expl),
5944             offset=self.offset,
5945             tlen=self.tlen,
5946             llen=self.llen,
5947             vlen=self.vlen,
5948             expl_offset=self.expl_offset if self.expled else None,
5949             expl_tlen=self.expl_tlen if self.expled else None,
5950             expl_llen=self.expl_llen if self.expled else None,
5951             expl_vlen=self.expl_vlen if self.expled else None,
5952             expl_lenindef=self.expl_lenindef,
5953             lenindef=self.lenindef,
5954             ber_encoded=self.ber_encoded,
5955             bered=self.bered,
5956         )
5957         for name in self.specs:
5958             value = self._value.get(name)
5959             if value is None:
5960                 continue
5961             yield value.pps(decode_path=decode_path + (name,))
5962         for pp in self.pps_lenindef(decode_path):
5963             yield pp
5964
5965
5966 class Set(Sequence):
5967     """``SET`` structure type
5968
5969     Its usage is identical to :py:class:`pyderasn.Sequence`.
5970
5971     .. _allow_unordered_set_ctx:
5972
5973     DER prohibits unordered values encoding and will raise an error
5974     during decode. If :ref:`bered <bered_ctx>` context option is set,
5975     then no error will occur. Also you can disable strict values
5976     ordering check by setting ``"allow_unordered_set": True``
5977     :ref:`context <ctx>` option.
5978     """
5979     __slots__ = ()
5980     tag_default = tag_encode(form=TagFormConstructed, num=17)
5981     asn1_type_name = "SET"
5982
5983     def _encode(self):
5984         v = b"".join(value.encode() for value in sorted(
5985             self._values_for_encoding(),
5986             key=attrgetter("tag_order"),
5987         ))
5988         return b"".join((self.tag, len_encode(len(v)), v))
5989
5990     def _encode_cer(self, writer):
5991         write_full(writer, self.tag + LENINDEF)
5992         for v in sorted(
5993                 self._values_for_encoding(),
5994                 key=attrgetter("tag_order_cer"),
5995         ):
5996             v.encode_cer(writer)
5997         write_full(writer, EOC)
5998
5999     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6000         try:
6001             t, tlen, lv = tag_strip(tlv)
6002         except DecodeError as err:
6003             raise err.__class__(
6004                 msg=err.msg,
6005                 klass=self.__class__,
6006                 decode_path=decode_path,
6007                 offset=offset,
6008             )
6009         if t != self.tag:
6010             raise TagMismatch(
6011                 klass=self.__class__,
6012                 decode_path=decode_path,
6013                 offset=offset,
6014             )
6015         if tag_only:
6016             yield None
6017             return
6018         lenindef = False
6019         ctx_bered = ctx.get("bered", False)
6020         try:
6021             l, llen, v = len_decode(lv)
6022         except LenIndefForm as err:
6023             if not ctx_bered:
6024                 raise err.__class__(
6025                     msg=err.msg,
6026                     klass=self.__class__,
6027                     decode_path=decode_path,
6028                     offset=offset,
6029                 )
6030             l, llen, v = 0, 1, lv[1:]
6031             lenindef = True
6032         except DecodeError as err:
6033             raise err.__class__(
6034                 msg=err.msg,
6035                 klass=self.__class__,
6036                 decode_path=decode_path,
6037                 offset=offset,
6038             )
6039         if l > len(v):
6040             raise NotEnoughData(
6041                 "encoded length is longer than data",
6042                 klass=self.__class__,
6043                 offset=offset,
6044             )
6045         if not lenindef:
6046             v, tail = v[:l], v[l:]
6047         vlen = 0
6048         sub_offset = offset + tlen + llen
6049         values = {}
6050         ber_encoded = False
6051         ctx_allow_default_values = ctx.get("allow_default_values", False)
6052         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6053         tag_order_prev = (0, 0)
6054         _specs_items = copy(self.specs)
6055
6056         while len(v) > 0:
6057             if lenindef and v[:EOC_LEN].tobytes() == EOC:
6058                 break
6059             for name, spec in iteritems(_specs_items):
6060                 sub_decode_path = decode_path + (name,)
6061                 try:
6062                     spec.decode(
6063                         v,
6064                         sub_offset,
6065                         leavemm=True,
6066                         decode_path=sub_decode_path,
6067                         ctx=ctx,
6068                         tag_only=True,
6069                         _ctx_immutable=False,
6070                     )
6071                 except TagMismatch:
6072                     continue
6073                 break
6074             else:
6075                 raise TagMismatch(
6076                     klass=self.__class__,
6077                     decode_path=decode_path,
6078                     offset=offset,
6079                 )
6080             if evgen_mode:
6081                 for _decode_path, value, v_tail in spec.decode_evgen(
6082                         v,
6083                         sub_offset,
6084                         leavemm=True,
6085                         decode_path=sub_decode_path,
6086                         ctx=ctx,
6087                         _ctx_immutable=False,
6088                 ):
6089                     yield _decode_path, value, v_tail
6090             else:
6091                 _, value, v_tail = next(spec.decode_evgen(
6092                     v,
6093                     sub_offset,
6094                     leavemm=True,
6095                     decode_path=sub_decode_path,
6096                     ctx=ctx,
6097                     _ctx_immutable=False,
6098                     _evgen_mode=False,
6099                 ))
6100             value_tag_order = value.tag_order
6101             value_len = value.fulllen
6102             if tag_order_prev >= value_tag_order:
6103                 if ctx_bered or ctx_allow_unordered_set:
6104                     ber_encoded = True
6105                 else:
6106                     raise DecodeError(
6107                         "unordered " + self.asn1_type_name,
6108                         klass=self.__class__,
6109                         decode_path=sub_decode_path,
6110                         offset=sub_offset,
6111                     )
6112             if spec.default is None or value != spec.default:
6113                 pass
6114             elif ctx_bered or ctx_allow_default_values:
6115                 ber_encoded = True
6116             else:
6117                 raise DecodeError(
6118                     "DEFAULT value met",
6119                     klass=self.__class__,
6120                     decode_path=sub_decode_path,
6121                     offset=sub_offset,
6122                 )
6123             values[name] = value
6124             del _specs_items[name]
6125             tag_order_prev = value_tag_order
6126             sub_offset += value_len
6127             vlen += value_len
6128             v = v_tail
6129
6130         obj = self.__class__(
6131             schema=self.specs,
6132             impl=self.tag,
6133             expl=self._expl,
6134             default=self.default,
6135             optional=self.optional,
6136             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6137         )
6138         if lenindef:
6139             if v[:EOC_LEN].tobytes() != EOC:
6140                 raise DecodeError(
6141                     "no EOC",
6142                     klass=self.__class__,
6143                     decode_path=decode_path,
6144                     offset=offset,
6145                 )
6146             tail = v[EOC_LEN:]
6147             obj.lenindef = True
6148         for name, spec in iteritems(self.specs):
6149             if name not in values and not spec.optional:
6150                 raise DecodeError(
6151                     "%s value is not ready" % name,
6152                     klass=self.__class__,
6153                     decode_path=decode_path,
6154                     offset=offset,
6155                 )
6156         if not evgen_mode:
6157             obj._value = values
6158         obj.ber_encoded = ber_encoded
6159         yield decode_path, obj, tail
6160
6161
6162 SequenceOfState = namedtuple(
6163     "SequenceOfState",
6164     BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
6165     **NAMEDTUPLE_KWARGS
6166 )
6167
6168
6169 class SequenceOf(Obj):
6170     """``SEQUENCE OF`` sequence type
6171
6172     For that kind of type you must specify the object it will carry on
6173     (bounds are for example here, not required)::
6174
6175         class Ints(SequenceOf):
6176             schema = Integer()
6177             bounds = (0, 2)
6178
6179     >>> ints = Ints()
6180     >>> ints.append(Integer(123))
6181     >>> ints.append(Integer(234))
6182     >>> ints
6183     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
6184     >>> [int(i) for i in ints]
6185     [123, 234]
6186     >>> ints.append(Integer(345))
6187     Traceback (most recent call last):
6188     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
6189     >>> ints[1]
6190     INTEGER 234
6191     >>> ints[1] = Integer(345)
6192     >>> ints
6193     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
6194
6195     Also you can initialize sequence with preinitialized values:
6196
6197     >>> ints = Ints([Integer(123), Integer(234)])
6198     """
6199     __slots__ = ("spec", "_bound_min", "_bound_max")
6200     tag_default = tag_encode(form=TagFormConstructed, num=16)
6201     asn1_type_name = "SEQUENCE OF"
6202
6203     def __init__(
6204             self,
6205             value=None,
6206             schema=None,
6207             bounds=None,
6208             impl=None,
6209             expl=None,
6210             default=None,
6211             optional=False,
6212             _decoded=(0, 0, 0),
6213     ):
6214         super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
6215         if schema is None:
6216             schema = getattr(self, "schema", None)
6217         if schema is None:
6218             raise ValueError("schema must be specified")
6219         self.spec = schema
6220         self._bound_min, self._bound_max = getattr(
6221             self,
6222             "bounds",
6223             (0, float("+inf")),
6224         ) if bounds is None else bounds
6225         self._value = []
6226         if value is not None:
6227             self._value = self._value_sanitize(value)
6228         if default is not None:
6229             default_value = self._value_sanitize(default)
6230             default_obj = self.__class__(
6231                 schema=schema,
6232                 impl=self.tag,
6233                 expl=self._expl,
6234             )
6235             default_obj._value = default_value
6236             self.default = default_obj
6237             if value is None:
6238                 self._value = copy(default_obj._value)
6239
6240     def _value_sanitize(self, value):
6241         if issubclass(value.__class__, SequenceOf):
6242             value = value._value
6243         elif hasattr(value, "__iter__"):
6244             value = list(value)
6245         else:
6246             raise InvalidValueType((self.__class__, iter))
6247         if not self._bound_min <= len(value) <= self._bound_max:
6248             raise BoundsError(self._bound_min, len(value), self._bound_max)
6249         for v in value:
6250             if not isinstance(v, self.spec.__class__):
6251                 raise InvalidValueType((self.spec.__class__,))
6252         return value
6253
6254     @property
6255     def ready(self):
6256         return all(v.ready for v in self._value)
6257
6258     @property
6259     def bered(self):
6260         if self.expl_lenindef or self.lenindef or self.ber_encoded:
6261             return True
6262         return any(v.bered for v in self._value)
6263
6264     def __getstate__(self):
6265         return SequenceOfState(
6266             __version__,
6267             self.tag,
6268             self._tag_order,
6269             self._expl,
6270             self.default,
6271             self.optional,
6272             self.offset,
6273             self.llen,
6274             self.vlen,
6275             self.expl_lenindef,
6276             self.lenindef,
6277             self.ber_encoded,
6278             self.spec,
6279             [copy(v) for v in self._value],
6280             self._bound_min,
6281             self._bound_max,
6282         )
6283
6284     def __setstate__(self, state):
6285         super(SequenceOf, self).__setstate__(state)
6286         self.spec = state.spec
6287         self._value = state.value
6288         self._bound_min = state.bound_min
6289         self._bound_max = state.bound_max
6290
6291     def __eq__(self, their):
6292         if isinstance(their, self.__class__):
6293             return (
6294                 self.spec == their.spec and
6295                 self.tag == their.tag and
6296                 self._expl == their._expl and
6297                 self._value == their._value
6298             )
6299         if hasattr(their, "__iter__"):
6300             return self._value == list(their)
6301         return False
6302
6303     def __call__(
6304             self,
6305             value=None,
6306             bounds=None,
6307             impl=None,
6308             expl=None,
6309             default=None,
6310             optional=None,
6311     ):
6312         return self.__class__(
6313             value=value,
6314             schema=self.spec,
6315             bounds=(
6316                 (self._bound_min, self._bound_max)
6317                 if bounds is None else bounds
6318             ),
6319             impl=self.tag if impl is None else impl,
6320             expl=self._expl if expl is None else expl,
6321             default=self.default if default is None else default,
6322             optional=self.optional if optional is None else optional,
6323         )
6324
6325     def __contains__(self, key):
6326         return key in self._value
6327
6328     def append(self, value):
6329         if not isinstance(value, self.spec.__class__):
6330             raise InvalidValueType((self.spec.__class__,))
6331         if len(self._value) + 1 > self._bound_max:
6332             raise BoundsError(
6333                 self._bound_min,
6334                 len(self._value) + 1,
6335                 self._bound_max,
6336             )
6337         self._value.append(value)
6338
6339     def __iter__(self):
6340         self._assert_ready()
6341         return iter(self._value)
6342
6343     def __len__(self):
6344         self._assert_ready()
6345         return len(self._value)
6346
6347     def __setitem__(self, key, value):
6348         if not isinstance(value, self.spec.__class__):
6349             raise InvalidValueType((self.spec.__class__,))
6350         self._value[key] = self.spec(value=value)
6351
6352     def __getitem__(self, key):
6353         return self._value[key]
6354
6355     def _values_for_encoding(self):
6356         return iter(self._value)
6357
6358     def _encode(self):
6359         v = b"".join(v.encode() for v in self._values_for_encoding())
6360         return b"".join((self.tag, len_encode(len(v)), v))
6361
6362     def _encode_cer(self, writer):
6363         write_full(writer, self.tag + LENINDEF)
6364         for v in self._values_for_encoding():
6365             v.encode_cer(writer)
6366         write_full(writer, EOC)
6367
6368     def _decode(
6369             self,
6370             tlv,
6371             offset,
6372             decode_path,
6373             ctx,
6374             tag_only,
6375             evgen_mode,
6376             ordering_check=False,
6377     ):
6378         try:
6379             t, tlen, lv = tag_strip(tlv)
6380         except DecodeError as err:
6381             raise err.__class__(
6382                 msg=err.msg,
6383                 klass=self.__class__,
6384                 decode_path=decode_path,
6385                 offset=offset,
6386             )
6387         if t != self.tag:
6388             raise TagMismatch(
6389                 klass=self.__class__,
6390                 decode_path=decode_path,
6391                 offset=offset,
6392             )
6393         if tag_only:
6394             yield None
6395             return
6396         lenindef = False
6397         ctx_bered = ctx.get("bered", False)
6398         try:
6399             l, llen, v = len_decode(lv)
6400         except LenIndefForm as err:
6401             if not ctx_bered:
6402                 raise err.__class__(
6403                     msg=err.msg,
6404                     klass=self.__class__,
6405                     decode_path=decode_path,
6406                     offset=offset,
6407                 )
6408             l, llen, v = 0, 1, lv[1:]
6409             lenindef = True
6410         except DecodeError as err:
6411             raise err.__class__(
6412                 msg=err.msg,
6413                 klass=self.__class__,
6414                 decode_path=decode_path,
6415                 offset=offset,
6416             )
6417         if l > len(v):
6418             raise NotEnoughData(
6419                 "encoded length is longer than data",
6420                 klass=self.__class__,
6421                 decode_path=decode_path,
6422                 offset=offset,
6423             )
6424         if not lenindef:
6425             v, tail = v[:l], v[l:]
6426         vlen = 0
6427         sub_offset = offset + tlen + llen
6428         _value = []
6429         _value_count = 0
6430         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6431         value_prev = memoryview(v[:0])
6432         ber_encoded = False
6433         spec = self.spec
6434         while len(v) > 0:
6435             if lenindef and v[:EOC_LEN].tobytes() == EOC:
6436                 break
6437             sub_decode_path = decode_path + (str(_value_count),)
6438             if evgen_mode:
6439                 for _decode_path, value, v_tail in spec.decode_evgen(
6440                         v,
6441                         sub_offset,
6442                         leavemm=True,
6443                         decode_path=sub_decode_path,
6444                         ctx=ctx,
6445                         _ctx_immutable=False,
6446                 ):
6447                     yield _decode_path, value, v_tail
6448             else:
6449                 _, value, v_tail = next(spec.decode_evgen(
6450                     v,
6451                     sub_offset,
6452                     leavemm=True,
6453                     decode_path=sub_decode_path,
6454                     ctx=ctx,
6455                     _ctx_immutable=False,
6456                     _evgen_mode=False,
6457                 ))
6458             value_len = value.fulllen
6459             if ordering_check:
6460                 if value_prev.tobytes() > v[:value_len].tobytes():
6461                     if ctx_bered or ctx_allow_unordered_set:
6462                         ber_encoded = True
6463                     else:
6464                         raise DecodeError(
6465                             "unordered " + self.asn1_type_name,
6466                             klass=self.__class__,
6467                             decode_path=sub_decode_path,
6468                             offset=sub_offset,
6469                         )
6470                 value_prev = v[:value_len]
6471             _value_count += 1
6472             if not evgen_mode:
6473                 _value.append(value)
6474             sub_offset += value_len
6475             vlen += value_len
6476             v = v_tail
6477         if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
6478             raise DecodeError(
6479                 msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
6480                 klass=self.__class__,
6481                 decode_path=decode_path,
6482                 offset=offset,
6483             )
6484         try:
6485             obj = self.__class__(
6486                 value=None if evgen_mode else _value,
6487                 schema=spec,
6488                 bounds=(self._bound_min, self._bound_max),
6489                 impl=self.tag,
6490                 expl=self._expl,
6491                 default=self.default,
6492                 optional=self.optional,
6493                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6494             )
6495         except BoundsError as err:
6496             raise DecodeError(
6497                 msg=str(err),
6498                 klass=self.__class__,
6499                 decode_path=decode_path,
6500                 offset=offset,
6501             )
6502         if lenindef:
6503             if v[:EOC_LEN].tobytes() != EOC:
6504                 raise DecodeError(
6505                     "no EOC",
6506                     klass=self.__class__,
6507                     decode_path=decode_path,
6508                     offset=offset,
6509                 )
6510             obj.lenindef = True
6511             tail = v[EOC_LEN:]
6512         obj.ber_encoded = ber_encoded
6513         yield decode_path, obj, tail
6514
6515     def __repr__(self):
6516         return "%s[%s]" % (
6517             pp_console_row(next(self.pps())),
6518             ", ".join(repr(v) for v in self._value),
6519         )
6520
6521     def pps(self, decode_path=()):
6522         yield _pp(
6523             obj=self,
6524             asn1_type_name=self.asn1_type_name,
6525             obj_name=self.__class__.__name__,
6526             decode_path=decode_path,
6527             optional=self.optional,
6528             default=self == self.default,
6529             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6530             expl=None if self._expl is None else tag_decode(self._expl),
6531             offset=self.offset,
6532             tlen=self.tlen,
6533             llen=self.llen,
6534             vlen=self.vlen,
6535             expl_offset=self.expl_offset if self.expled else None,
6536             expl_tlen=self.expl_tlen if self.expled else None,
6537             expl_llen=self.expl_llen if self.expled else None,
6538             expl_vlen=self.expl_vlen if self.expled else None,
6539             expl_lenindef=self.expl_lenindef,
6540             lenindef=self.lenindef,
6541             ber_encoded=self.ber_encoded,
6542             bered=self.bered,
6543         )
6544         for i, value in enumerate(self._value):
6545             yield value.pps(decode_path=decode_path + (str(i),))
6546         for pp in self.pps_lenindef(decode_path):
6547             yield pp
6548
6549
6550 class SetOf(SequenceOf):
6551     """``SET OF`` sequence type
6552
6553     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6554     """
6555     __slots__ = ()
6556     tag_default = tag_encode(form=TagFormConstructed, num=17)
6557     asn1_type_name = "SET OF"
6558
6559     def _encode(self):
6560         v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
6561         return b"".join((self.tag, len_encode(len(v)), v))
6562
6563     def _encode_cer(self, writer):
6564         write_full(writer, self.tag + LENINDEF)
6565         for v in sorted(encode_cer(v) for v in self._values_for_encoding()):
6566             write_full(writer, v)
6567         write_full(writer, EOC)
6568
6569     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6570         return super(SetOf, self)._decode(
6571             tlv,
6572             offset,
6573             decode_path,
6574             ctx,
6575             tag_only,
6576             evgen_mode,
6577             ordering_check=True,
6578         )
6579
6580
6581 def obj_by_path(pypath):  # pragma: no cover
6582     """Import object specified as string Python path
6583
6584     Modules must be separated from classes/functions with ``:``.
6585
6586     >>> obj_by_path("foo.bar:Baz")
6587     <class 'foo.bar.Baz'>
6588     >>> obj_by_path("foo.bar:Baz.boo")
6589     <classmethod 'foo.bar.Baz.boo'>
6590     """
6591     mod, objs = pypath.rsplit(":", 1)
6592     from importlib import import_module
6593     obj = import_module(mod)
6594     for obj_name in objs.split("."):
6595         obj = getattr(obj, obj_name)
6596     return obj
6597
6598
6599 def generic_decoder():  # pragma: no cover
6600     # All of this below is a big hack with self references
6601     choice = PrimitiveTypes()
6602     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6603     choice.specs["SetOf"] = SetOf(schema=choice)
6604     for i in six_xrange(31):
6605         choice.specs["SequenceOf%d" % i] = SequenceOf(
6606             schema=choice,
6607             expl=tag_ctxc(i),
6608         )
6609     choice.specs["Any"] = Any()
6610
6611     # Class name equals to type name, to omit it from output
6612     class SEQUENCEOF(SequenceOf):
6613         __slots__ = ()
6614         schema = choice
6615
6616     def pprint_any(
6617             obj,
6618             oid_maps=(),
6619             with_colours=False,
6620             with_decode_path=False,
6621             decode_path_only=(),
6622     ):
6623         def _pprint_pps(pps):
6624             for pp in pps:
6625                 if hasattr(pp, "_fields"):
6626                     if (
6627                             decode_path_only != () and
6628                             pp.decode_path[:len(decode_path_only)] != decode_path_only
6629                     ):
6630                         continue
6631                     if pp.asn1_type_name == Choice.asn1_type_name:
6632                         continue
6633                     pp_kwargs = pp._asdict()
6634                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6635                     pp = _pp(**pp_kwargs)
6636                     yield pp_console_row(
6637                         pp,
6638                         oid_maps=oid_maps,
6639                         with_offsets=True,
6640                         with_blob=False,
6641                         with_colours=with_colours,
6642                         with_decode_path=with_decode_path,
6643                         decode_path_len_decrease=len(decode_path_only),
6644                     )
6645                     for row in pp_console_blob(
6646                             pp,
6647                             decode_path_len_decrease=len(decode_path_only),
6648                     ):
6649                         yield row
6650                 else:
6651                     for row in _pprint_pps(pp):
6652                         yield row
6653         return "\n".join(_pprint_pps(obj.pps()))
6654     return SEQUENCEOF(), pprint_any
6655
6656
6657 def main():  # pragma: no cover
6658     import argparse
6659     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6660     parser.add_argument(
6661         "--skip",
6662         type=int,
6663         default=0,
6664         help="Skip that number of bytes from the beginning",
6665     )
6666     parser.add_argument(
6667         "--oids",
6668         help="Python paths to dictionary with OIDs, comma separated",
6669     )
6670     parser.add_argument(
6671         "--schema",
6672         help="Python path to schema definition to use",
6673     )
6674     parser.add_argument(
6675         "--defines-by-path",
6676         help="Python path to decoder's defines_by_path",
6677     )
6678     parser.add_argument(
6679         "--nobered",
6680         action="store_true",
6681         help="Disallow BER encoding",
6682     )
6683     parser.add_argument(
6684         "--print-decode-path",
6685         action="store_true",
6686         help="Print decode paths",
6687     )
6688     parser.add_argument(
6689         "--decode-path-only",
6690         help="Print only specified decode path",
6691     )
6692     parser.add_argument(
6693         "--allow-expl-oob",
6694         action="store_true",
6695         help="Allow explicit tag out-of-bound",
6696     )
6697     parser.add_argument(
6698         "DERFile",
6699         type=argparse.FileType("rb"),
6700         help="Path to DER file you want to decode",
6701     )
6702     args = parser.parse_args()
6703     args.DERFile.seek(args.skip)
6704     der = memoryview(args.DERFile.read())
6705     args.DERFile.close()
6706     oid_maps = (
6707         [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6708         if args.oids else ()
6709     )
6710     if args.schema:
6711         schema = obj_by_path(args.schema)
6712         from functools import partial
6713         pprinter = partial(pprint, big_blobs=True)
6714     else:
6715         schema, pprinter = generic_decoder()
6716     ctx = {
6717         "bered": not args.nobered,
6718         "allow_expl_oob": args.allow_expl_oob,
6719     }
6720     if args.defines_by_path is not None:
6721         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6722     obj, tail = schema().decode(der, ctx=ctx)
6723     from os import environ
6724     print(pprinter(
6725         obj,
6726         oid_maps=oid_maps,
6727         with_colours=environ.get("NO_COLOR") is None,
6728         with_decode_path=args.print_decode_path,
6729         decode_path_only=(
6730             () if args.decode_path_only is None else
6731             tuple(args.decode_path_only.split(":"))
6732         ),
6733     ))
6734     if tail != b"":
6735         print("\nTrailing data: %s" % hexenc(tail))
6736
6737
6738 if __name__ == "__main__":
6739     main()