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