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