]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Explicitly mention that zero year is unsupported
[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     .. warning::
4306
4307        Zero year is unsupported.
4308     """
4309     __slots__ = ()
4310     tag_default = tag_encode(24)
4311     asn1_type_name = "GeneralizedTime"
4312
4313     def _strptime(self, value):
4314         l = len(value)
4315         if l == LEN_YYYYMMDDHHMMSSZ:
4316             # datetime.strptime's format: %Y%m%d%H%M%SZ
4317             if value[-1] != "Z":
4318                 raise ValueError("non UTC timezone")
4319             return datetime(
4320                 int(value[:4]),  # %Y
4321                 int(value[4:6]),  # %m
4322                 int(value[6:8]),  # %d
4323                 int(value[8:10]),  # %H
4324                 int(value[10:12]),  # %M
4325                 int(value[12:14]),  # %S
4326             )
4327         if l >= LEN_YYYYMMDDHHMMSSDMZ:
4328             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4329             if value[-1] != "Z":
4330                 raise ValueError("non UTC timezone")
4331             if value[14] != ".":
4332                 raise ValueError("no fractions separator")
4333             us = value[15:-1]
4334             if us[-1] == "0":
4335                 raise ValueError("trailing zero")
4336             us_len = len(us)
4337             if us_len > 6:
4338                 raise ValueError("only microsecond fractions are supported")
4339             us = int(us + ("0" * (6 - us_len)))
4340             decoded = datetime(
4341                 int(value[:4]),  # %Y
4342                 int(value[4:6]),  # %m
4343                 int(value[6:8]),  # %d
4344                 int(value[8:10]),  # %H
4345                 int(value[10:12]),  # %M
4346                 int(value[12:14]),  # %S
4347                 us,  # %f
4348             )
4349             return decoded
4350         raise ValueError("invalid GeneralizedTime length")
4351
4352     def _value_sanitize(self, value):
4353         if isinstance(value, binary_type):
4354             try:
4355                 value_decoded = value.decode("ascii")
4356             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4357                 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
4358             try:
4359                 self._strptime(value_decoded)
4360             except (TypeError, ValueError) as err:
4361                 raise DecodeError(
4362                     "invalid GeneralizedTime format: %r" % err,
4363                     klass=self.__class__,
4364                 )
4365             return value
4366         if isinstance(value, self.__class__):
4367             return value._value
4368         if isinstance(value, datetime):
4369             encoded = value.strftime("%Y%m%d%H%M%S")
4370             if value.microsecond > 0:
4371                 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4372             return (encoded + "Z").encode("ascii")
4373         raise InvalidValueType((self.__class__, datetime))
4374
4375     def todatetime(self):
4376         return self._strptime(self._value.decode("ascii"))
4377
4378
4379 class GraphicString(CommonString):
4380     __slots__ = ()
4381     tag_default = tag_encode(25)
4382     encoding = "iso-8859-1"
4383     asn1_type_name = "GraphicString"
4384
4385
4386 class VisibleString(CommonString):
4387     __slots__ = ()
4388     tag_default = tag_encode(26)
4389     encoding = "ascii"
4390     asn1_type_name = "VisibleString"
4391
4392
4393 class ISO646String(VisibleString):
4394     __slots__ = ()
4395     asn1_type_name = "ISO646String"
4396
4397
4398 class GeneralString(CommonString):
4399     __slots__ = ()
4400     tag_default = tag_encode(27)
4401     encoding = "iso-8859-1"
4402     asn1_type_name = "GeneralString"
4403
4404
4405 class UniversalString(CommonString):
4406     __slots__ = ()
4407     tag_default = tag_encode(28)
4408     encoding = "utf-32-be"
4409     asn1_type_name = "UniversalString"
4410
4411
4412 class BMPString(CommonString):
4413     __slots__ = ()
4414     tag_default = tag_encode(30)
4415     encoding = "utf-16-be"
4416     asn1_type_name = "BMPString"
4417
4418
4419 ChoiceState = namedtuple("ChoiceState", (
4420     "version",
4421     "specs",
4422     "value",
4423     "tag",
4424     "expl",
4425     "default",
4426     "optional",
4427     "offset",
4428     "llen",
4429     "vlen",
4430     "expl_lenindef",
4431     "lenindef",
4432     "ber_encoded",
4433 ))
4434
4435
4436 class Choice(Obj):
4437     """``CHOICE`` special type
4438
4439     ::
4440
4441         class GeneralName(Choice):
4442             schema = (
4443                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4444                 ("dNSName", IA5String(impl=tag_ctxp(2))),
4445             )
4446
4447     >>> gn = GeneralName()
4448     GeneralName CHOICE
4449     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4450     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4451     >>> gn["dNSName"] = IA5String("bar.baz")
4452     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4453     >>> gn["rfc822Name"]
4454     None
4455     >>> gn["dNSName"]
4456     [2] IA5String IA5 bar.baz
4457     >>> gn.choice
4458     'dNSName'
4459     >>> gn.value == gn["dNSName"]
4460     True
4461     >>> gn.specs
4462     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4463
4464     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4465     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4466     """
4467     __slots__ = ("specs",)
4468     tag_default = None
4469     asn1_type_name = "CHOICE"
4470
4471     def __init__(
4472             self,
4473             value=None,
4474             schema=None,
4475             impl=None,
4476             expl=None,
4477             default=None,
4478             optional=False,
4479             _decoded=(0, 0, 0),
4480     ):
4481         """
4482         :param value: set the value. Either ``(choice, value)`` tuple, or
4483                       :py:class:`pyderasn.Choice` object
4484         :param bytes impl: can not be set, do **not** use it
4485         :param bytes expl: override default tag with ``EXPLICIT`` one
4486         :param default: set default value. Type same as in ``value``
4487         :param bool optional: is object ``OPTIONAL`` in sequence
4488         """
4489         if impl is not None:
4490             raise ValueError("no implicit tag allowed for CHOICE")
4491         super(Choice, self).__init__(None, expl, default, optional, _decoded)
4492         if schema is None:
4493             schema = getattr(self, "schema", ())
4494         if len(schema) == 0:
4495             raise ValueError("schema must be specified")
4496         self.specs = (
4497             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4498         )
4499         self._value = None
4500         if value is not None:
4501             self._value = self._value_sanitize(value)
4502         if default is not None:
4503             default_value = self._value_sanitize(default)
4504             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4505             default_obj.specs = self.specs
4506             default_obj._value = default_value
4507             self.default = default_obj
4508             if value is None:
4509                 self._value = copy(default_obj._value)
4510
4511     def _value_sanitize(self, value):
4512         if isinstance(value, tuple) and len(value) == 2:
4513             choice, obj = value
4514             spec = self.specs.get(choice)
4515             if spec is None:
4516                 raise ObjUnknown(choice)
4517             if not isinstance(obj, spec.__class__):
4518                 raise InvalidValueType((spec,))
4519             return (choice, spec(obj))
4520         if isinstance(value, self.__class__):
4521             return value._value
4522         raise InvalidValueType((self.__class__, tuple))
4523
4524     @property
4525     def ready(self):
4526         return self._value is not None and self._value[1].ready
4527
4528     @property
4529     def bered(self):
4530         return self.expl_lenindef or (
4531             (self._value is not None) and
4532             self._value[1].bered
4533         )
4534
4535     def __getstate__(self):
4536         return ChoiceState(
4537             __version__,
4538             self.specs,
4539             copy(self._value),
4540             self.tag,
4541             self._expl,
4542             self.default,
4543             self.optional,
4544             self.offset,
4545             self.llen,
4546             self.vlen,
4547             self.expl_lenindef,
4548             self.lenindef,
4549             self.ber_encoded,
4550         )
4551
4552     def __setstate__(self, state):
4553         super(Choice, self).__setstate__(state)
4554         self.specs = state.specs
4555         self._value = state.value
4556         self._expl = state.expl
4557         self.default = state.default
4558         self.optional = state.optional
4559         self.offset = state.offset
4560         self.llen = state.llen
4561         self.vlen = state.vlen
4562         self.expl_lenindef = state.expl_lenindef
4563         self.lenindef = state.lenindef
4564         self.ber_encoded = state.ber_encoded
4565
4566     def __eq__(self, their):
4567         if isinstance(their, tuple) and len(their) == 2:
4568             return self._value == their
4569         if not isinstance(their, self.__class__):
4570             return False
4571         return (
4572             self.specs == their.specs and
4573             self._value == their._value
4574         )
4575
4576     def __call__(
4577             self,
4578             value=None,
4579             expl=None,
4580             default=None,
4581             optional=None,
4582     ):
4583         return self.__class__(
4584             value=value,
4585             schema=self.specs,
4586             expl=self._expl if expl is None else expl,
4587             default=self.default if default is None else default,
4588             optional=self.optional if optional is None else optional,
4589         )
4590
4591     @property
4592     def choice(self):
4593         self._assert_ready()
4594         return self._value[0]
4595
4596     @property
4597     def value(self):
4598         self._assert_ready()
4599         return self._value[1]
4600
4601     def __getitem__(self, key):
4602         if key not in self.specs:
4603             raise ObjUnknown(key)
4604         if self._value is None:
4605             return None
4606         choice, value = self._value
4607         if choice != key:
4608             return None
4609         return value
4610
4611     def __setitem__(self, key, value):
4612         spec = self.specs.get(key)
4613         if spec is None:
4614             raise ObjUnknown(key)
4615         if not isinstance(value, spec.__class__):
4616             raise InvalidValueType((spec.__class__,))
4617         self._value = (key, spec(value))
4618
4619     @property
4620     def tlen(self):
4621         return 0
4622
4623     @property
4624     def decoded(self):
4625         return self._value[1].decoded if self.ready else False
4626
4627     def _encode(self):
4628         self._assert_ready()
4629         return self._value[1].encode()
4630
4631     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4632         for choice, spec in iteritems(self.specs):
4633             sub_decode_path = decode_path + (choice,)
4634             try:
4635                 spec.decode(
4636                     tlv,
4637                     offset=offset,
4638                     leavemm=True,
4639                     decode_path=sub_decode_path,
4640                     ctx=ctx,
4641                     tag_only=True,
4642                     _ctx_immutable=False,
4643                 )
4644             except TagMismatch:
4645                 continue
4646             break
4647         else:
4648             raise TagMismatch(
4649                 klass=self.__class__,
4650                 decode_path=decode_path,
4651                 offset=offset,
4652             )
4653         if tag_only:  # pragma: no cover
4654             return None
4655         value, tail = spec.decode(
4656             tlv,
4657             offset=offset,
4658             leavemm=True,
4659             decode_path=sub_decode_path,
4660             ctx=ctx,
4661             _ctx_immutable=False,
4662         )
4663         obj = self.__class__(
4664             schema=self.specs,
4665             expl=self._expl,
4666             default=self.default,
4667             optional=self.optional,
4668             _decoded=(offset, 0, value.fulllen),
4669         )
4670         obj._value = (choice, value)
4671         return obj, tail
4672
4673     def __repr__(self):
4674         value = pp_console_row(next(self.pps()))
4675         if self.ready:
4676             value = "%s[%r]" % (value, self.value)
4677         return value
4678
4679     def pps(self, decode_path=()):
4680         yield _pp(
4681             obj=self,
4682             asn1_type_name=self.asn1_type_name,
4683             obj_name=self.__class__.__name__,
4684             decode_path=decode_path,
4685             value=self.choice if self.ready else None,
4686             optional=self.optional,
4687             default=self == self.default,
4688             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4689             expl=None if self._expl is None else tag_decode(self._expl),
4690             offset=self.offset,
4691             tlen=self.tlen,
4692             llen=self.llen,
4693             vlen=self.vlen,
4694             expl_lenindef=self.expl_lenindef,
4695             bered=self.bered,
4696         )
4697         if self.ready:
4698             yield self.value.pps(decode_path=decode_path + (self.choice,))
4699         for pp in self.pps_lenindef(decode_path):
4700             yield pp
4701
4702
4703 class PrimitiveTypes(Choice):
4704     """Predefined ``CHOICE`` for all generic primitive types
4705
4706     It could be useful for general decoding of some unspecified values:
4707
4708     >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
4709     OCTET STRING 3 bytes 666f6f
4710     >>> PrimitiveTypes().decod(hexdec("0203123456")).value
4711     INTEGER 1193046
4712     """
4713     __slots__ = ()
4714     schema = tuple((klass.__name__, klass()) for klass in (
4715         Boolean,
4716         Integer,
4717         BitString,
4718         OctetString,
4719         Null,
4720         ObjectIdentifier,
4721         UTF8String,
4722         NumericString,
4723         PrintableString,
4724         TeletexString,
4725         VideotexString,
4726         IA5String,
4727         UTCTime,
4728         GeneralizedTime,
4729         GraphicString,
4730         VisibleString,
4731         ISO646String,
4732         GeneralString,
4733         UniversalString,
4734         BMPString,
4735     ))
4736
4737
4738 AnyState = namedtuple("AnyState", (
4739     "version",
4740     "value",
4741     "tag",
4742     "expl",
4743     "optional",
4744     "offset",
4745     "llen",
4746     "vlen",
4747     "expl_lenindef",
4748     "lenindef",
4749     "ber_encoded",
4750     "defined",
4751 ))
4752
4753
4754 class Any(Obj):
4755     """``ANY`` special type
4756
4757     >>> Any(Integer(-123))
4758     ANY 020185
4759     >>> a = Any(OctetString(b"hello world").encode())
4760     ANY 040b68656c6c6f20776f726c64
4761     >>> hexenc(bytes(a))
4762     b'0x040x0bhello world'
4763     """
4764     __slots__ = ("defined",)
4765     tag_default = tag_encode(0)
4766     asn1_type_name = "ANY"
4767
4768     def __init__(
4769             self,
4770             value=None,
4771             expl=None,
4772             optional=False,
4773             _decoded=(0, 0, 0),
4774     ):
4775         """
4776         :param value: set the value. Either any kind of pyderasn's
4777                       **ready** object, or bytes. Pay attention that
4778                       **no** validation is performed is raw binary value
4779                       is valid TLV
4780         :param bytes expl: override default tag with ``EXPLICIT`` one
4781         :param bool optional: is object ``OPTIONAL`` in sequence
4782         """
4783         super(Any, self).__init__(None, expl, None, optional, _decoded)
4784         self._value = None if value is None else self._value_sanitize(value)
4785         self.defined = None
4786
4787     def _value_sanitize(self, value):
4788         if isinstance(value, binary_type):
4789             return value
4790         if isinstance(value, self.__class__):
4791             return value._value
4792         if isinstance(value, Obj):
4793             return value.encode()
4794         raise InvalidValueType((self.__class__, Obj, binary_type))
4795
4796     @property
4797     def ready(self):
4798         return self._value is not None
4799
4800     @property
4801     def bered(self):
4802         if self.expl_lenindef or self.lenindef:
4803             return True
4804         if self.defined is None:
4805             return False
4806         return self.defined[1].bered
4807
4808     def __getstate__(self):
4809         return AnyState(
4810             __version__,
4811             self._value,
4812             self.tag,
4813             self._expl,
4814             self.optional,
4815             self.offset,
4816             self.llen,
4817             self.vlen,
4818             self.expl_lenindef,
4819             self.lenindef,
4820             self.ber_encoded,
4821             self.defined,
4822         )
4823
4824     def __setstate__(self, state):
4825         super(Any, self).__setstate__(state)
4826         self._value = state.value
4827         self.tag = state.tag
4828         self._expl = state.expl
4829         self.optional = state.optional
4830         self.offset = state.offset
4831         self.llen = state.llen
4832         self.vlen = state.vlen
4833         self.expl_lenindef = state.expl_lenindef
4834         self.lenindef = state.lenindef
4835         self.ber_encoded = state.ber_encoded
4836         self.defined = state.defined
4837
4838     def __eq__(self, their):
4839         if isinstance(their, binary_type):
4840             return self._value == their
4841         if issubclass(their.__class__, Any):
4842             return self._value == their._value
4843         return False
4844
4845     def __call__(
4846             self,
4847             value=None,
4848             expl=None,
4849             optional=None,
4850     ):
4851         return self.__class__(
4852             value=value,
4853             expl=self._expl if expl is None else expl,
4854             optional=self.optional if optional is None else optional,
4855         )
4856
4857     def __bytes__(self):
4858         self._assert_ready()
4859         return self._value
4860
4861     @property
4862     def tlen(self):
4863         return 0
4864
4865     def _encode(self):
4866         self._assert_ready()
4867         return self._value
4868
4869     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4870         try:
4871             t, tlen, lv = tag_strip(tlv)
4872         except DecodeError as err:
4873             raise err.__class__(
4874                 msg=err.msg,
4875                 klass=self.__class__,
4876                 decode_path=decode_path,
4877                 offset=offset,
4878             )
4879         try:
4880             l, llen, v = len_decode(lv)
4881         except LenIndefForm as err:
4882             if not ctx.get("bered", False):
4883                 raise err.__class__(
4884                     msg=err.msg,
4885                     klass=self.__class__,
4886                     decode_path=decode_path,
4887                     offset=offset,
4888                 )
4889             llen, vlen, v = 1, 0, lv[1:]
4890             sub_offset = offset + tlen + llen
4891             chunk_i = 0
4892             while v[:EOC_LEN].tobytes() != EOC:
4893                 chunk, v = Any().decode(
4894                     v,
4895                     offset=sub_offset,
4896                     decode_path=decode_path + (str(chunk_i),),
4897                     leavemm=True,
4898                     ctx=ctx,
4899                     _ctx_immutable=False,
4900                 )
4901                 vlen += chunk.tlvlen
4902                 sub_offset += chunk.tlvlen
4903                 chunk_i += 1
4904             tlvlen = tlen + llen + vlen + EOC_LEN
4905             obj = self.__class__(
4906                 value=tlv[:tlvlen].tobytes(),
4907                 expl=self._expl,
4908                 optional=self.optional,
4909                 _decoded=(offset, 0, tlvlen),
4910             )
4911             obj.lenindef = True
4912             obj.tag = t.tobytes()
4913             return obj, v[EOC_LEN:]
4914         except DecodeError as err:
4915             raise err.__class__(
4916                 msg=err.msg,
4917                 klass=self.__class__,
4918                 decode_path=decode_path,
4919                 offset=offset,
4920             )
4921         if l > len(v):
4922             raise NotEnoughData(
4923                 "encoded length is longer than data",
4924                 klass=self.__class__,
4925                 decode_path=decode_path,
4926                 offset=offset,
4927             )
4928         tlvlen = tlen + llen + l
4929         v, tail = tlv[:tlvlen], v[l:]
4930         obj = self.__class__(
4931             value=v.tobytes(),
4932             expl=self._expl,
4933             optional=self.optional,
4934             _decoded=(offset, 0, tlvlen),
4935         )
4936         obj.tag = t.tobytes()
4937         return obj, tail
4938
4939     def __repr__(self):
4940         return pp_console_row(next(self.pps()))
4941
4942     def pps(self, decode_path=()):
4943         yield _pp(
4944             obj=self,
4945             asn1_type_name=self.asn1_type_name,
4946             obj_name=self.__class__.__name__,
4947             decode_path=decode_path,
4948             blob=self._value if self.ready else None,
4949             optional=self.optional,
4950             default=self == self.default,
4951             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4952             expl=None if self._expl is None else tag_decode(self._expl),
4953             offset=self.offset,
4954             tlen=self.tlen,
4955             llen=self.llen,
4956             vlen=self.vlen,
4957             expl_offset=self.expl_offset if self.expled else None,
4958             expl_tlen=self.expl_tlen if self.expled else None,
4959             expl_llen=self.expl_llen if self.expled else None,
4960             expl_vlen=self.expl_vlen if self.expled else None,
4961             expl_lenindef=self.expl_lenindef,
4962             lenindef=self.lenindef,
4963             bered=self.bered,
4964         )
4965         defined_by, defined = self.defined or (None, None)
4966         if defined_by is not None:
4967             yield defined.pps(
4968                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4969             )
4970         for pp in self.pps_lenindef(decode_path):
4971             yield pp
4972
4973
4974 ########################################################################
4975 # ASN.1 constructed types
4976 ########################################################################
4977
4978 def get_def_by_path(defines_by_path, sub_decode_path):
4979     """Get define by decode path
4980     """
4981     for path, define in defines_by_path:
4982         if len(path) != len(sub_decode_path):
4983             continue
4984         for p1, p2 in zip(path, sub_decode_path):
4985             if (p1 != any) and (p1 != p2):
4986                 break
4987         else:
4988             return define
4989
4990
4991 def abs_decode_path(decode_path, rel_path):
4992     """Create an absolute decode path from current and relative ones
4993
4994     :param decode_path: current decode path, starting point. Tuple of strings
4995     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4996                      If first tuple's element is "/", then treat it as
4997                      an absolute path, ignoring ``decode_path`` as
4998                      starting point. Also this tuple can contain ".."
4999                      elements, stripping the leading element from
5000                      ``decode_path``
5001
5002     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
5003     ("foo", "bar", "baz", "whatever")
5004     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
5005     ("foo", "whatever")
5006     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
5007     ("baz", "whatever")
5008     """
5009     if rel_path[0] == "/":
5010         return rel_path[1:]
5011     if rel_path[0] == "..":
5012         return abs_decode_path(decode_path[:-1], rel_path[1:])
5013     return decode_path + rel_path
5014
5015
5016 SequenceState = namedtuple("SequenceState", (
5017     "version",
5018     "specs",
5019     "value",
5020     "tag",
5021     "expl",
5022     "default",
5023     "optional",
5024     "offset",
5025     "llen",
5026     "vlen",
5027     "expl_lenindef",
5028     "lenindef",
5029     "ber_encoded",
5030 ))
5031
5032
5033 class Sequence(Obj):
5034     """``SEQUENCE`` structure type
5035
5036     You have to make specification of sequence::
5037
5038         class Extension(Sequence):
5039             schema = (
5040                 ("extnID", ObjectIdentifier()),
5041                 ("critical", Boolean(default=False)),
5042                 ("extnValue", OctetString()),
5043             )
5044
5045     Then, you can work with it as with dictionary.
5046
5047     >>> ext = Extension()
5048     >>> Extension().specs
5049     OrderedDict([
5050         ('extnID', OBJECT IDENTIFIER),
5051         ('critical', BOOLEAN False OPTIONAL DEFAULT),
5052         ('extnValue', OCTET STRING),
5053     ])
5054     >>> ext["extnID"] = "1.2.3"
5055     Traceback (most recent call last):
5056     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5057     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5058
5059     You can determine if sequence is ready to be encoded:
5060
5061     >>> ext.ready
5062     False
5063     >>> ext.encode()
5064     Traceback (most recent call last):
5065     pyderasn.ObjNotReady: object is not ready: extnValue
5066     >>> ext["extnValue"] = OctetString(b"foobar")
5067     >>> ext.ready
5068     True
5069
5070     Value you want to assign, must have the same **type** as in
5071     corresponding specification, but it can have different tags,
5072     optional/default attributes -- they will be taken from specification
5073     automatically::
5074
5075         class TBSCertificate(Sequence):
5076             schema = (
5077                 ("version", Version(expl=tag_ctxc(0), default="v1")),
5078             [...]
5079
5080     >>> tbs = TBSCertificate()
5081     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5082
5083     Assign ``None`` to remove value from sequence.
5084
5085     You can set values in Sequence during its initialization:
5086
5087     >>> AlgorithmIdentifier((
5088         ("algorithm", ObjectIdentifier("1.2.3")),
5089         ("parameters", Any(Null()))
5090     ))
5091     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5092
5093     You can determine if value exists/set in the sequence and take its value:
5094
5095     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5096     (True, True, False)
5097     >>> ext["extnID"]
5098     OBJECT IDENTIFIER 1.2.3
5099
5100     But pay attention that if value has default, then it won't be (not
5101     in) in the sequence (because ``DEFAULT`` must not be encoded in
5102     DER), but you can read its value:
5103
5104     >>> "critical" in ext, ext["critical"]
5105     (False, BOOLEAN False)
5106     >>> ext["critical"] = Boolean(True)
5107     >>> "critical" in ext, ext["critical"]
5108     (True, BOOLEAN True)
5109
5110     All defaulted values are always optional.
5111
5112     .. _allow_default_values_ctx:
5113
5114     DER prohibits default value encoding and will raise an error if
5115     default value is unexpectedly met during decode.
5116     If :ref:`bered <bered_ctx>` context option is set, then no error
5117     will be raised, but ``bered`` attribute set. You can disable strict
5118     defaulted values existence validation by setting
5119     ``"allow_default_values": True`` :ref:`context <ctx>` option.
5120
5121     Two sequences are equal if they have equal specification (schema),
5122     implicit/explicit tagging and the same values.
5123     """
5124     __slots__ = ("specs",)
5125     tag_default = tag_encode(form=TagFormConstructed, num=16)
5126     asn1_type_name = "SEQUENCE"
5127
5128     def __init__(
5129             self,
5130             value=None,
5131             schema=None,
5132             impl=None,
5133             expl=None,
5134             default=None,
5135             optional=False,
5136             _decoded=(0, 0, 0),
5137     ):
5138         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5139         if schema is None:
5140             schema = getattr(self, "schema", ())
5141         self.specs = (
5142             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
5143         )
5144         self._value = {}
5145         if value is not None:
5146             if issubclass(value.__class__, Sequence):
5147                 self._value = value._value
5148             elif hasattr(value, "__iter__"):
5149                 for seq_key, seq_value in value:
5150                     self[seq_key] = seq_value
5151             else:
5152                 raise InvalidValueType((Sequence,))
5153         if default is not None:
5154             if not issubclass(default.__class__, Sequence):
5155                 raise InvalidValueType((Sequence,))
5156             default_value = default._value
5157             default_obj = self.__class__(impl=self.tag, expl=self._expl)
5158             default_obj.specs = self.specs
5159             default_obj._value = default_value
5160             self.default = default_obj
5161             if value is None:
5162                 self._value = copy(default_obj._value)
5163
5164     @property
5165     def ready(self):
5166         for name, spec in iteritems(self.specs):
5167             value = self._value.get(name)
5168             if value is None:
5169                 if spec.optional:
5170                     continue
5171                 return False
5172             if not value.ready:
5173                 return False
5174         return True
5175
5176     @property
5177     def bered(self):
5178         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5179             return True
5180         return any(value.bered for value in itervalues(self._value))
5181
5182     def __getstate__(self):
5183         return SequenceState(
5184             __version__,
5185             self.specs,
5186             {k: copy(v) for k, v in iteritems(self._value)},
5187             self.tag,
5188             self._expl,
5189             self.default,
5190             self.optional,
5191             self.offset,
5192             self.llen,
5193             self.vlen,
5194             self.expl_lenindef,
5195             self.lenindef,
5196             self.ber_encoded,
5197         )
5198
5199     def __setstate__(self, state):
5200         super(Sequence, self).__setstate__(state)
5201         self.specs = state.specs
5202         self._value = state.value
5203         self.tag = state.tag
5204         self._expl = state.expl
5205         self.default = state.default
5206         self.optional = state.optional
5207         self.offset = state.offset
5208         self.llen = state.llen
5209         self.vlen = state.vlen
5210         self.expl_lenindef = state.expl_lenindef
5211         self.lenindef = state.lenindef
5212         self.ber_encoded = state.ber_encoded
5213
5214     def __eq__(self, their):
5215         if not isinstance(their, self.__class__):
5216             return False
5217         return (
5218             self.specs == their.specs and
5219             self.tag == their.tag and
5220             self._expl == their._expl and
5221             self._value == their._value
5222         )
5223
5224     def __call__(
5225             self,
5226             value=None,
5227             impl=None,
5228             expl=None,
5229             default=None,
5230             optional=None,
5231     ):
5232         return self.__class__(
5233             value=value,
5234             schema=self.specs,
5235             impl=self.tag if impl is None else impl,
5236             expl=self._expl if expl is None else expl,
5237             default=self.default if default is None else default,
5238             optional=self.optional if optional is None else optional,
5239         )
5240
5241     def __contains__(self, key):
5242         return key in self._value
5243
5244     def __setitem__(self, key, value):
5245         spec = self.specs.get(key)
5246         if spec is None:
5247             raise ObjUnknown(key)
5248         if value is None:
5249             self._value.pop(key, None)
5250             return
5251         if not isinstance(value, spec.__class__):
5252             raise InvalidValueType((spec.__class__,))
5253         value = spec(value=value)
5254         if spec.default is not None and value == spec.default:
5255             self._value.pop(key, None)
5256             return
5257         self._value[key] = value
5258
5259     def __getitem__(self, key):
5260         value = self._value.get(key)
5261         if value is not None:
5262             return value
5263         spec = self.specs.get(key)
5264         if spec is None:
5265             raise ObjUnknown(key)
5266         if spec.default is not None:
5267             return spec.default
5268         return None
5269
5270     def _encoded_values(self):
5271         raws = []
5272         for name, spec in iteritems(self.specs):
5273             value = self._value.get(name)
5274             if value is None:
5275                 if spec.optional:
5276                     continue
5277                 raise ObjNotReady(name)
5278             raws.append(value.encode())
5279         return raws
5280
5281     def _encode(self):
5282         v = b"".join(self._encoded_values())
5283         return b"".join((self.tag, len_encode(len(v)), v))
5284
5285     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5286         try:
5287             t, tlen, lv = tag_strip(tlv)
5288         except DecodeError as err:
5289             raise err.__class__(
5290                 msg=err.msg,
5291                 klass=self.__class__,
5292                 decode_path=decode_path,
5293                 offset=offset,
5294             )
5295         if t != self.tag:
5296             raise TagMismatch(
5297                 klass=self.__class__,
5298                 decode_path=decode_path,
5299                 offset=offset,
5300             )
5301         if tag_only:  # pragma: no cover
5302             return None
5303         lenindef = False
5304         ctx_bered = ctx.get("bered", False)
5305         try:
5306             l, llen, v = len_decode(lv)
5307         except LenIndefForm as err:
5308             if not ctx_bered:
5309                 raise err.__class__(
5310                     msg=err.msg,
5311                     klass=self.__class__,
5312                     decode_path=decode_path,
5313                     offset=offset,
5314                 )
5315             l, llen, v = 0, 1, lv[1:]
5316             lenindef = True
5317         except DecodeError as err:
5318             raise err.__class__(
5319                 msg=err.msg,
5320                 klass=self.__class__,
5321                 decode_path=decode_path,
5322                 offset=offset,
5323             )
5324         if l > len(v):
5325             raise NotEnoughData(
5326                 "encoded length is longer than data",
5327                 klass=self.__class__,
5328                 decode_path=decode_path,
5329                 offset=offset,
5330             )
5331         if not lenindef:
5332             v, tail = v[:l], v[l:]
5333         vlen = 0
5334         sub_offset = offset + tlen + llen
5335         values = {}
5336         ber_encoded = False
5337         ctx_allow_default_values = ctx.get("allow_default_values", False)
5338         for name, spec in iteritems(self.specs):
5339             if spec.optional and (
5340                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5341                     len(v) == 0
5342             ):
5343                 continue
5344             sub_decode_path = decode_path + (name,)
5345             try:
5346                 value, v_tail = spec.decode(
5347                     v,
5348                     sub_offset,
5349                     leavemm=True,
5350                     decode_path=sub_decode_path,
5351                     ctx=ctx,
5352                     _ctx_immutable=False,
5353                 )
5354             except TagMismatch as err:
5355                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5356                     continue
5357                 raise
5358
5359             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5360             if defined is not None:
5361                 defined_by, defined_spec = defined
5362                 if issubclass(value.__class__, SequenceOf):
5363                     for i, _value in enumerate(value):
5364                         sub_sub_decode_path = sub_decode_path + (
5365                             str(i),
5366                             DecodePathDefBy(defined_by),
5367                         )
5368                         defined_value, defined_tail = defined_spec.decode(
5369                             memoryview(bytes(_value)),
5370                             sub_offset + (
5371                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5372                                 if value.expled else (value.tlen + value.llen)
5373                             ),
5374                             leavemm=True,
5375                             decode_path=sub_sub_decode_path,
5376                             ctx=ctx,
5377                             _ctx_immutable=False,
5378                         )
5379                         if len(defined_tail) > 0:
5380                             raise DecodeError(
5381                                 "remaining data",
5382                                 klass=self.__class__,
5383                                 decode_path=sub_sub_decode_path,
5384                                 offset=offset,
5385                             )
5386                         _value.defined = (defined_by, defined_value)
5387                 else:
5388                     defined_value, defined_tail = defined_spec.decode(
5389                         memoryview(bytes(value)),
5390                         sub_offset + (
5391                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5392                             if value.expled else (value.tlen + value.llen)
5393                         ),
5394                         leavemm=True,
5395                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5396                         ctx=ctx,
5397                         _ctx_immutable=False,
5398                     )
5399                     if len(defined_tail) > 0:
5400                         raise DecodeError(
5401                             "remaining data",
5402                             klass=self.__class__,
5403                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5404                             offset=offset,
5405                         )
5406                     value.defined = (defined_by, defined_value)
5407
5408             value_len = value.fulllen
5409             vlen += value_len
5410             sub_offset += value_len
5411             v = v_tail
5412             if spec.default is not None and value == spec.default:
5413                 if ctx_bered or ctx_allow_default_values:
5414                     ber_encoded = True
5415                 else:
5416                     raise DecodeError(
5417                         "DEFAULT value met",
5418                         klass=self.__class__,
5419                         decode_path=sub_decode_path,
5420                         offset=sub_offset,
5421                     )
5422             values[name] = value
5423
5424             spec_defines = getattr(spec, "defines", ())
5425             if len(spec_defines) == 0:
5426                 defines_by_path = ctx.get("defines_by_path", ())
5427                 if len(defines_by_path) > 0:
5428                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5429             if spec_defines is not None and len(spec_defines) > 0:
5430                 for rel_path, schema in spec_defines:
5431                     defined = schema.get(value, None)
5432                     if defined is not None:
5433                         ctx.setdefault("_defines", []).append((
5434                             abs_decode_path(sub_decode_path[:-1], rel_path),
5435                             (value, defined),
5436                         ))
5437         if lenindef:
5438             if v[:EOC_LEN].tobytes() != EOC:
5439                 raise DecodeError(
5440                     "no EOC",
5441                     klass=self.__class__,
5442                     decode_path=decode_path,
5443                     offset=offset,
5444                 )
5445             tail = v[EOC_LEN:]
5446             vlen += EOC_LEN
5447         elif len(v) > 0:
5448             raise DecodeError(
5449                 "remaining data",
5450                 klass=self.__class__,
5451                 decode_path=decode_path,
5452                 offset=offset,
5453             )
5454         obj = self.__class__(
5455             schema=self.specs,
5456             impl=self.tag,
5457             expl=self._expl,
5458             default=self.default,
5459             optional=self.optional,
5460             _decoded=(offset, llen, vlen),
5461         )
5462         obj._value = values
5463         obj.lenindef = lenindef
5464         obj.ber_encoded = ber_encoded
5465         return obj, tail
5466
5467     def __repr__(self):
5468         value = pp_console_row(next(self.pps()))
5469         cols = []
5470         for name in self.specs:
5471             _value = self._value.get(name)
5472             if _value is None:
5473                 continue
5474             cols.append("%s: %s" % (name, repr(_value)))
5475         return "%s[%s]" % (value, "; ".join(cols))
5476
5477     def pps(self, decode_path=()):
5478         yield _pp(
5479             obj=self,
5480             asn1_type_name=self.asn1_type_name,
5481             obj_name=self.__class__.__name__,
5482             decode_path=decode_path,
5483             optional=self.optional,
5484             default=self == self.default,
5485             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5486             expl=None if self._expl is None else tag_decode(self._expl),
5487             offset=self.offset,
5488             tlen=self.tlen,
5489             llen=self.llen,
5490             vlen=self.vlen,
5491             expl_offset=self.expl_offset if self.expled else None,
5492             expl_tlen=self.expl_tlen if self.expled else None,
5493             expl_llen=self.expl_llen if self.expled else None,
5494             expl_vlen=self.expl_vlen if self.expled else None,
5495             expl_lenindef=self.expl_lenindef,
5496             lenindef=self.lenindef,
5497             ber_encoded=self.ber_encoded,
5498             bered=self.bered,
5499         )
5500         for name in self.specs:
5501             value = self._value.get(name)
5502             if value is None:
5503                 continue
5504             yield value.pps(decode_path=decode_path + (name,))
5505         for pp in self.pps_lenindef(decode_path):
5506             yield pp
5507
5508
5509 class Set(Sequence):
5510     """``SET`` structure type
5511
5512     Its usage is identical to :py:class:`pyderasn.Sequence`.
5513
5514     .. _allow_unordered_set_ctx:
5515
5516     DER prohibits unordered values encoding and will raise an error
5517     during decode. If If :ref:`bered <bered_ctx>` context option is set,
5518     then no error will occure. Also you can disable strict values
5519     ordering check by setting ``"allow_unordered_set": True``
5520     :ref:`context <ctx>` option.
5521     """
5522     __slots__ = ()
5523     tag_default = tag_encode(form=TagFormConstructed, num=17)
5524     asn1_type_name = "SET"
5525
5526     def _encode(self):
5527         raws = self._encoded_values()
5528         raws.sort()
5529         v = b"".join(raws)
5530         return b"".join((self.tag, len_encode(len(v)), v))
5531
5532     def _specs_items(self):
5533         return iteritems(self.specs)
5534
5535     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5536         try:
5537             t, tlen, lv = tag_strip(tlv)
5538         except DecodeError as err:
5539             raise err.__class__(
5540                 msg=err.msg,
5541                 klass=self.__class__,
5542                 decode_path=decode_path,
5543                 offset=offset,
5544             )
5545         if t != self.tag:
5546             raise TagMismatch(
5547                 klass=self.__class__,
5548                 decode_path=decode_path,
5549                 offset=offset,
5550             )
5551         if tag_only:
5552             return None
5553         lenindef = False
5554         ctx_bered = ctx.get("bered", False)
5555         try:
5556             l, llen, v = len_decode(lv)
5557         except LenIndefForm as err:
5558             if not ctx_bered:
5559                 raise err.__class__(
5560                     msg=err.msg,
5561                     klass=self.__class__,
5562                     decode_path=decode_path,
5563                     offset=offset,
5564                 )
5565             l, llen, v = 0, 1, lv[1:]
5566             lenindef = True
5567         except DecodeError as err:
5568             raise err.__class__(
5569                 msg=err.msg,
5570                 klass=self.__class__,
5571                 decode_path=decode_path,
5572                 offset=offset,
5573             )
5574         if l > len(v):
5575             raise NotEnoughData(
5576                 "encoded length is longer than data",
5577                 klass=self.__class__,
5578                 offset=offset,
5579             )
5580         if not lenindef:
5581             v, tail = v[:l], v[l:]
5582         vlen = 0
5583         sub_offset = offset + tlen + llen
5584         values = {}
5585         ber_encoded = False
5586         ctx_allow_default_values = ctx.get("allow_default_values", False)
5587         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5588         value_prev = memoryview(v[:0])
5589
5590         while len(v) > 0:
5591             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5592                 break
5593             for name, spec in self._specs_items():
5594                 sub_decode_path = decode_path + (name,)
5595                 try:
5596                     spec.decode(
5597                         v,
5598                         sub_offset,
5599                         leavemm=True,
5600                         decode_path=sub_decode_path,
5601                         ctx=ctx,
5602                         tag_only=True,
5603                         _ctx_immutable=False,
5604                     )
5605                 except TagMismatch:
5606                     continue
5607                 break
5608             else:
5609                 raise TagMismatch(
5610                     klass=self.__class__,
5611                     decode_path=decode_path,
5612                     offset=offset,
5613                 )
5614             value, v_tail = spec.decode(
5615                 v,
5616                 sub_offset,
5617                 leavemm=True,
5618                 decode_path=sub_decode_path,
5619                 ctx=ctx,
5620                 _ctx_immutable=False,
5621             )
5622             value_len = value.fulllen
5623             if value_prev.tobytes() > v[:value_len].tobytes():
5624                 if ctx_bered or ctx_allow_unordered_set:
5625                     ber_encoded = True
5626                 else:
5627                     raise DecodeError(
5628                         "unordered " + self.asn1_type_name,
5629                         klass=self.__class__,
5630                         decode_path=sub_decode_path,
5631                         offset=sub_offset,
5632                     )
5633             if spec.default is None or value != spec.default:
5634                 pass
5635             elif ctx_bered or ctx_allow_default_values:
5636                 ber_encoded = True
5637             else:
5638                 raise DecodeError(
5639                     "DEFAULT value met",
5640                     klass=self.__class__,
5641                     decode_path=sub_decode_path,
5642                     offset=sub_offset,
5643                 )
5644             values[name] = value
5645             value_prev = v[:value_len]
5646             sub_offset += value_len
5647             vlen += value_len
5648             v = v_tail
5649         obj = self.__class__(
5650             schema=self.specs,
5651             impl=self.tag,
5652             expl=self._expl,
5653             default=self.default,
5654             optional=self.optional,
5655             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5656         )
5657         if lenindef:
5658             if v[:EOC_LEN].tobytes() != EOC:
5659                 raise DecodeError(
5660                     "no EOC",
5661                     klass=self.__class__,
5662                     decode_path=decode_path,
5663                     offset=offset,
5664                 )
5665             tail = v[EOC_LEN:]
5666             obj.lenindef = True
5667         obj._value = values
5668         if not obj.ready:
5669             raise DecodeError(
5670                 "not all values are ready",
5671                 klass=self.__class__,
5672                 decode_path=decode_path,
5673                 offset=offset,
5674             )
5675         obj.ber_encoded = ber_encoded
5676         return obj, tail
5677
5678
5679 SequenceOfState = namedtuple("SequenceOfState", (
5680     "version",
5681     "spec",
5682     "value",
5683     "bound_min",
5684     "bound_max",
5685     "tag",
5686     "expl",
5687     "default",
5688     "optional",
5689     "offset",
5690     "llen",
5691     "vlen",
5692     "expl_lenindef",
5693     "lenindef",
5694     "ber_encoded",
5695 ))
5696
5697
5698 class SequenceOf(Obj):
5699     """``SEQUENCE OF`` sequence type
5700
5701     For that kind of type you must specify the object it will carry on
5702     (bounds are for example here, not required)::
5703
5704         class Ints(SequenceOf):
5705             schema = Integer()
5706             bounds = (0, 2)
5707
5708     >>> ints = Ints()
5709     >>> ints.append(Integer(123))
5710     >>> ints.append(Integer(234))
5711     >>> ints
5712     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5713     >>> [int(i) for i in ints]
5714     [123, 234]
5715     >>> ints.append(Integer(345))
5716     Traceback (most recent call last):
5717     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5718     >>> ints[1]
5719     INTEGER 234
5720     >>> ints[1] = Integer(345)
5721     >>> ints
5722     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5723
5724     Also you can initialize sequence with preinitialized values:
5725
5726     >>> ints = Ints([Integer(123), Integer(234)])
5727     """
5728     __slots__ = ("spec", "_bound_min", "_bound_max")
5729     tag_default = tag_encode(form=TagFormConstructed, num=16)
5730     asn1_type_name = "SEQUENCE OF"
5731
5732     def __init__(
5733             self,
5734             value=None,
5735             schema=None,
5736             bounds=None,
5737             impl=None,
5738             expl=None,
5739             default=None,
5740             optional=False,
5741             _decoded=(0, 0, 0),
5742     ):
5743         super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
5744         if schema is None:
5745             schema = getattr(self, "schema", None)
5746         if schema is None:
5747             raise ValueError("schema must be specified")
5748         self.spec = schema
5749         self._bound_min, self._bound_max = getattr(
5750             self,
5751             "bounds",
5752             (0, float("+inf")),
5753         ) if bounds is None else bounds
5754         self._value = []
5755         if value is not None:
5756             self._value = self._value_sanitize(value)
5757         if default is not None:
5758             default_value = self._value_sanitize(default)
5759             default_obj = self.__class__(
5760                 schema=schema,
5761                 impl=self.tag,
5762                 expl=self._expl,
5763             )
5764             default_obj._value = default_value
5765             self.default = default_obj
5766             if value is None:
5767                 self._value = copy(default_obj._value)
5768
5769     def _value_sanitize(self, value):
5770         if issubclass(value.__class__, SequenceOf):
5771             value = value._value
5772         elif hasattr(value, "__iter__"):
5773             value = list(value)
5774         else:
5775             raise InvalidValueType((self.__class__, iter))
5776         if not self._bound_min <= len(value) <= self._bound_max:
5777             raise BoundsError(self._bound_min, len(value), self._bound_max)
5778         for v in value:
5779             if not isinstance(v, self.spec.__class__):
5780                 raise InvalidValueType((self.spec.__class__,))
5781         return value
5782
5783     @property
5784     def ready(self):
5785         return all(v.ready for v in self._value)
5786
5787     @property
5788     def bered(self):
5789         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5790             return True
5791         return any(v.bered for v in self._value)
5792
5793     def __getstate__(self):
5794         return SequenceOfState(
5795             __version__,
5796             self.spec,
5797             [copy(v) for v in self._value],
5798             self._bound_min,
5799             self._bound_max,
5800             self.tag,
5801             self._expl,
5802             self.default,
5803             self.optional,
5804             self.offset,
5805             self.llen,
5806             self.vlen,
5807             self.expl_lenindef,
5808             self.lenindef,
5809             self.ber_encoded,
5810         )
5811
5812     def __setstate__(self, state):
5813         super(SequenceOf, self).__setstate__(state)
5814         self.spec = state.spec
5815         self._value = state.value
5816         self._bound_min = state.bound_min
5817         self._bound_max = state.bound_max
5818         self.tag = state.tag
5819         self._expl = state.expl
5820         self.default = state.default
5821         self.optional = state.optional
5822         self.offset = state.offset
5823         self.llen = state.llen
5824         self.vlen = state.vlen
5825         self.expl_lenindef = state.expl_lenindef
5826         self.lenindef = state.lenindef
5827         self.ber_encoded = state.ber_encoded
5828
5829     def __eq__(self, their):
5830         if isinstance(their, self.__class__):
5831             return (
5832                 self.spec == their.spec and
5833                 self.tag == their.tag and
5834                 self._expl == their._expl and
5835                 self._value == their._value
5836             )
5837         if hasattr(their, "__iter__"):
5838             return self._value == list(their)
5839         return False
5840
5841     def __call__(
5842             self,
5843             value=None,
5844             bounds=None,
5845             impl=None,
5846             expl=None,
5847             default=None,
5848             optional=None,
5849     ):
5850         return self.__class__(
5851             value=value,
5852             schema=self.spec,
5853             bounds=(
5854                 (self._bound_min, self._bound_max)
5855                 if bounds is None else bounds
5856             ),
5857             impl=self.tag if impl is None else impl,
5858             expl=self._expl if expl is None else expl,
5859             default=self.default if default is None else default,
5860             optional=self.optional if optional is None else optional,
5861         )
5862
5863     def __contains__(self, key):
5864         return key in self._value
5865
5866     def append(self, value):
5867         if not isinstance(value, self.spec.__class__):
5868             raise InvalidValueType((self.spec.__class__,))
5869         if len(self._value) + 1 > self._bound_max:
5870             raise BoundsError(
5871                 self._bound_min,
5872                 len(self._value) + 1,
5873                 self._bound_max,
5874             )
5875         self._value.append(value)
5876
5877     def __iter__(self):
5878         self._assert_ready()
5879         return iter(self._value)
5880
5881     def __len__(self):
5882         self._assert_ready()
5883         return len(self._value)
5884
5885     def __setitem__(self, key, value):
5886         if not isinstance(value, self.spec.__class__):
5887             raise InvalidValueType((self.spec.__class__,))
5888         self._value[key] = self.spec(value=value)
5889
5890     def __getitem__(self, key):
5891         return self._value[key]
5892
5893     def _encoded_values(self):
5894         return [v.encode() for v in self._value]
5895
5896     def _encode(self):
5897         v = b"".join(self._encoded_values())
5898         return b"".join((self.tag, len_encode(len(v)), v))
5899
5900     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5901         try:
5902             t, tlen, lv = tag_strip(tlv)
5903         except DecodeError as err:
5904             raise err.__class__(
5905                 msg=err.msg,
5906                 klass=self.__class__,
5907                 decode_path=decode_path,
5908                 offset=offset,
5909             )
5910         if t != self.tag:
5911             raise TagMismatch(
5912                 klass=self.__class__,
5913                 decode_path=decode_path,
5914                 offset=offset,
5915             )
5916         if tag_only:
5917             return None
5918         lenindef = False
5919         ctx_bered = ctx.get("bered", False)
5920         try:
5921             l, llen, v = len_decode(lv)
5922         except LenIndefForm as err:
5923             if not ctx_bered:
5924                 raise err.__class__(
5925                     msg=err.msg,
5926                     klass=self.__class__,
5927                     decode_path=decode_path,
5928                     offset=offset,
5929                 )
5930             l, llen, v = 0, 1, lv[1:]
5931             lenindef = True
5932         except DecodeError as err:
5933             raise err.__class__(
5934                 msg=err.msg,
5935                 klass=self.__class__,
5936                 decode_path=decode_path,
5937                 offset=offset,
5938             )
5939         if l > len(v):
5940             raise NotEnoughData(
5941                 "encoded length is longer than data",
5942                 klass=self.__class__,
5943                 decode_path=decode_path,
5944                 offset=offset,
5945             )
5946         if not lenindef:
5947             v, tail = v[:l], v[l:]
5948         vlen = 0
5949         sub_offset = offset + tlen + llen
5950         _value = []
5951         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5952         value_prev = memoryview(v[:0])
5953         ber_encoded = False
5954         spec = self.spec
5955         while len(v) > 0:
5956             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5957                 break
5958             sub_decode_path = decode_path + (str(len(_value)),)
5959             value, v_tail = spec.decode(
5960                 v,
5961                 sub_offset,
5962                 leavemm=True,
5963                 decode_path=sub_decode_path,
5964                 ctx=ctx,
5965                 _ctx_immutable=False,
5966             )
5967             value_len = value.fulllen
5968             if ordering_check:
5969                 if value_prev.tobytes() > v[:value_len].tobytes():
5970                     if ctx_bered or ctx_allow_unordered_set:
5971                         ber_encoded = True
5972                     else:
5973                         raise DecodeError(
5974                             "unordered " + self.asn1_type_name,
5975                             klass=self.__class__,
5976                             decode_path=sub_decode_path,
5977                             offset=sub_offset,
5978                         )
5979                 value_prev = v[:value_len]
5980             _value.append(value)
5981             sub_offset += value_len
5982             vlen += value_len
5983             v = v_tail
5984         try:
5985             obj = self.__class__(
5986                 value=_value,
5987                 schema=spec,
5988                 bounds=(self._bound_min, self._bound_max),
5989                 impl=self.tag,
5990                 expl=self._expl,
5991                 default=self.default,
5992                 optional=self.optional,
5993                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5994             )
5995         except BoundsError as err:
5996             raise DecodeError(
5997                 msg=str(err),
5998                 klass=self.__class__,
5999                 decode_path=decode_path,
6000                 offset=offset,
6001             )
6002         if lenindef:
6003             if v[:EOC_LEN].tobytes() != EOC:
6004                 raise DecodeError(
6005                     "no EOC",
6006                     klass=self.__class__,
6007                     decode_path=decode_path,
6008                     offset=offset,
6009                 )
6010             obj.lenindef = True
6011             tail = v[EOC_LEN:]
6012         obj.ber_encoded = ber_encoded
6013         return obj, tail
6014
6015     def __repr__(self):
6016         return "%s[%s]" % (
6017             pp_console_row(next(self.pps())),
6018             ", ".join(repr(v) for v in self._value),
6019         )
6020
6021     def pps(self, decode_path=()):
6022         yield _pp(
6023             obj=self,
6024             asn1_type_name=self.asn1_type_name,
6025             obj_name=self.__class__.__name__,
6026             decode_path=decode_path,
6027             optional=self.optional,
6028             default=self == self.default,
6029             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6030             expl=None if self._expl is None else tag_decode(self._expl),
6031             offset=self.offset,
6032             tlen=self.tlen,
6033             llen=self.llen,
6034             vlen=self.vlen,
6035             expl_offset=self.expl_offset if self.expled else None,
6036             expl_tlen=self.expl_tlen if self.expled else None,
6037             expl_llen=self.expl_llen if self.expled else None,
6038             expl_vlen=self.expl_vlen if self.expled else None,
6039             expl_lenindef=self.expl_lenindef,
6040             lenindef=self.lenindef,
6041             ber_encoded=self.ber_encoded,
6042             bered=self.bered,
6043         )
6044         for i, value in enumerate(self._value):
6045             yield value.pps(decode_path=decode_path + (str(i),))
6046         for pp in self.pps_lenindef(decode_path):
6047             yield pp
6048
6049
6050 class SetOf(SequenceOf):
6051     """``SET OF`` sequence type
6052
6053     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6054     """
6055     __slots__ = ()
6056     tag_default = tag_encode(form=TagFormConstructed, num=17)
6057     asn1_type_name = "SET OF"
6058
6059     def _encode(self):
6060         raws = self._encoded_values()
6061         raws.sort()
6062         v = b"".join(raws)
6063         return b"".join((self.tag, len_encode(len(v)), v))
6064
6065     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
6066         return super(SetOf, self)._decode(
6067             tlv,
6068             offset,
6069             decode_path,
6070             ctx,
6071             tag_only,
6072             ordering_check=True,
6073         )
6074
6075
6076 def obj_by_path(pypath):  # pragma: no cover
6077     """Import object specified as string Python path
6078
6079     Modules must be separated from classes/functions with ``:``.
6080
6081     >>> obj_by_path("foo.bar:Baz")
6082     <class 'foo.bar.Baz'>
6083     >>> obj_by_path("foo.bar:Baz.boo")
6084     <classmethod 'foo.bar.Baz.boo'>
6085     """
6086     mod, objs = pypath.rsplit(":", 1)
6087     from importlib import import_module
6088     obj = import_module(mod)
6089     for obj_name in objs.split("."):
6090         obj = getattr(obj, obj_name)
6091     return obj
6092
6093
6094 def generic_decoder():  # pragma: no cover
6095     # All of this below is a big hack with self references
6096     choice = PrimitiveTypes()
6097     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6098     choice.specs["SetOf"] = SetOf(schema=choice)
6099     for i in six_xrange(31):
6100         choice.specs["SequenceOf%d" % i] = SequenceOf(
6101             schema=choice,
6102             expl=tag_ctxc(i),
6103         )
6104     choice.specs["Any"] = Any()
6105
6106     # Class name equals to type name, to omit it from output
6107     class SEQUENCEOF(SequenceOf):
6108         __slots__ = ()
6109         schema = choice
6110
6111     def pprint_any(
6112             obj,
6113             oid_maps=(),
6114             with_colours=False,
6115             with_decode_path=False,
6116             decode_path_only=(),
6117     ):
6118         def _pprint_pps(pps):
6119             for pp in pps:
6120                 if hasattr(pp, "_fields"):
6121                     if (
6122                             decode_path_only != () and
6123                             pp.decode_path[:len(decode_path_only)] != decode_path_only
6124                     ):
6125                         continue
6126                     if pp.asn1_type_name == Choice.asn1_type_name:
6127                         continue
6128                     pp_kwargs = pp._asdict()
6129                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6130                     pp = _pp(**pp_kwargs)
6131                     yield pp_console_row(
6132                         pp,
6133                         oid_maps=oid_maps,
6134                         with_offsets=True,
6135                         with_blob=False,
6136                         with_colours=with_colours,
6137                         with_decode_path=with_decode_path,
6138                         decode_path_len_decrease=len(decode_path_only),
6139                     )
6140                     for row in pp_console_blob(
6141                             pp,
6142                             decode_path_len_decrease=len(decode_path_only),
6143                     ):
6144                         yield row
6145                 else:
6146                     for row in _pprint_pps(pp):
6147                         yield row
6148         return "\n".join(_pprint_pps(obj.pps()))
6149     return SEQUENCEOF(), pprint_any
6150
6151
6152 def main():  # pragma: no cover
6153     import argparse
6154     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6155     parser.add_argument(
6156         "--skip",
6157         type=int,
6158         default=0,
6159         help="Skip that number of bytes from the beginning",
6160     )
6161     parser.add_argument(
6162         "--oids",
6163         help="Python paths to dictionary with OIDs, comma separated",
6164     )
6165     parser.add_argument(
6166         "--schema",
6167         help="Python path to schema definition to use",
6168     )
6169     parser.add_argument(
6170         "--defines-by-path",
6171         help="Python path to decoder's defines_by_path",
6172     )
6173     parser.add_argument(
6174         "--nobered",
6175         action="store_true",
6176         help="Disallow BER encoding",
6177     )
6178     parser.add_argument(
6179         "--print-decode-path",
6180         action="store_true",
6181         help="Print decode paths",
6182     )
6183     parser.add_argument(
6184         "--decode-path-only",
6185         help="Print only specified decode path",
6186     )
6187     parser.add_argument(
6188         "--allow-expl-oob",
6189         action="store_true",
6190         help="Allow explicit tag out-of-bound",
6191     )
6192     parser.add_argument(
6193         "DERFile",
6194         type=argparse.FileType("rb"),
6195         help="Path to DER file you want to decode",
6196     )
6197     args = parser.parse_args()
6198     args.DERFile.seek(args.skip)
6199     der = memoryview(args.DERFile.read())
6200     args.DERFile.close()
6201     oid_maps = (
6202         [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6203         if args.oids else ()
6204     )
6205     if args.schema:
6206         schema = obj_by_path(args.schema)
6207         from functools import partial
6208         pprinter = partial(pprint, big_blobs=True)
6209     else:
6210         schema, pprinter = generic_decoder()
6211     ctx = {
6212         "bered": not args.nobered,
6213         "allow_expl_oob": args.allow_expl_oob,
6214     }
6215     if args.defines_by_path is not None:
6216         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6217     obj, tail = schema().decode(der, ctx=ctx)
6218     print(pprinter(
6219         obj,
6220         oid_maps=oid_maps,
6221         with_colours=environ.get("NO_COLOR") is None,
6222         with_decode_path=args.print_decode_path,
6223         decode_path_only=(
6224             () if args.decode_path_only is None else
6225             tuple(args.decode_path_only.split(":"))
6226         ),
6227     ))
6228     if tail != b"":
6229         print("\nTrailing data: %s" % hexenc(tail))
6230
6231
6232 if __name__ == "__main__":
6233     main()