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