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