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