]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Grammar typos
[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 ``str(OID) <-> human readable string`` dictionary.
1673                      Its human readable form is printed when OID is met
1674     :param big_blobs: if large binary objects are met (like OctetString
1675                       values), do we need to print them too, on separate
1676                       lines
1677     :param with_colours: colourize output, if ``termcolor`` library
1678                          is available
1679     :param with_decode_path: print decode path
1680     :param decode_path_only: print only that specified decode path
1681     """
1682     def _pprint_pps(pps):
1683         for pp in pps:
1684             if hasattr(pp, "_fields"):
1685                 if (
1686                         decode_path_only != () and
1687                         tuple(
1688                             str(p) for p in pp.decode_path[:len(decode_path_only)]
1689                         ) != decode_path_only
1690                 ):
1691                     continue
1692                 if big_blobs:
1693                     yield pp_console_row(
1694                         pp,
1695                         oid_maps=oid_maps,
1696                         with_offsets=True,
1697                         with_blob=False,
1698                         with_colours=with_colours,
1699                         with_decode_path=with_decode_path,
1700                         decode_path_len_decrease=len(decode_path_only),
1701                     )
1702                     for row in pp_console_blob(
1703                             pp,
1704                             decode_path_len_decrease=len(decode_path_only),
1705                     ):
1706                         yield row
1707                 else:
1708                     yield pp_console_row(
1709                         pp,
1710                         oid_maps=oid_maps,
1711                         with_offsets=True,
1712                         with_blob=True,
1713                         with_colours=with_colours,
1714                         with_decode_path=with_decode_path,
1715                         decode_path_len_decrease=len(decode_path_only),
1716                     )
1717             else:
1718                 for row in _pprint_pps(pp):
1719                     yield row
1720     return "\n".join(_pprint_pps(obj.pps()))
1721
1722
1723 ########################################################################
1724 # ASN.1 primitive types
1725 ########################################################################
1726
1727 BooleanState = namedtuple("BooleanState", (
1728     "version",
1729     "value",
1730     "tag",
1731     "expl",
1732     "default",
1733     "optional",
1734     "offset",
1735     "llen",
1736     "vlen",
1737     "expl_lenindef",
1738     "lenindef",
1739     "ber_encoded",
1740 ))
1741
1742
1743 class Boolean(Obj):
1744     """``BOOLEAN`` boolean type
1745
1746     >>> b = Boolean(True)
1747     BOOLEAN True
1748     >>> b == Boolean(True)
1749     True
1750     >>> bool(b)
1751     True
1752     """
1753     __slots__ = ()
1754     tag_default = tag_encode(1)
1755     asn1_type_name = "BOOLEAN"
1756
1757     def __init__(
1758             self,
1759             value=None,
1760             impl=None,
1761             expl=None,
1762             default=None,
1763             optional=False,
1764             _decoded=(0, 0, 0),
1765     ):
1766         """
1767         :param value: set the value. Either boolean type, or
1768                       :py:class:`pyderasn.Boolean` object
1769         :param bytes impl: override default tag with ``IMPLICIT`` one
1770         :param bytes expl: override default tag with ``EXPLICIT`` one
1771         :param default: set default value. Type same as in ``value``
1772         :param bool optional: is object ``OPTIONAL`` in sequence
1773         """
1774         super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1775         self._value = None if value is None else self._value_sanitize(value)
1776         if default is not None:
1777             default = self._value_sanitize(default)
1778             self.default = self.__class__(
1779                 value=default,
1780                 impl=self.tag,
1781                 expl=self._expl,
1782             )
1783             if value is None:
1784                 self._value = default
1785
1786     def _value_sanitize(self, value):
1787         if isinstance(value, bool):
1788             return value
1789         if issubclass(value.__class__, Boolean):
1790             return value._value
1791         raise InvalidValueType((self.__class__, bool))
1792
1793     @property
1794     def ready(self):
1795         return self._value is not None
1796
1797     def __getstate__(self):
1798         return BooleanState(
1799             __version__,
1800             self._value,
1801             self.tag,
1802             self._expl,
1803             self.default,
1804             self.optional,
1805             self.offset,
1806             self.llen,
1807             self.vlen,
1808             self.expl_lenindef,
1809             self.lenindef,
1810             self.ber_encoded,
1811         )
1812
1813     def __setstate__(self, state):
1814         super(Boolean, self).__setstate__(state)
1815         self._value = state.value
1816         self.tag = state.tag
1817         self._expl = state.expl
1818         self.default = state.default
1819         self.optional = state.optional
1820         self.offset = state.offset
1821         self.llen = state.llen
1822         self.vlen = state.vlen
1823         self.expl_lenindef = state.expl_lenindef
1824         self.lenindef = state.lenindef
1825         self.ber_encoded = state.ber_encoded
1826
1827     def __nonzero__(self):
1828         self._assert_ready()
1829         return self._value
1830
1831     def __bool__(self):
1832         self._assert_ready()
1833         return self._value
1834
1835     def __eq__(self, their):
1836         if isinstance(their, bool):
1837             return self._value == their
1838         if not issubclass(their.__class__, Boolean):
1839             return False
1840         return (
1841             self._value == their._value and
1842             self.tag == their.tag and
1843             self._expl == their._expl
1844         )
1845
1846     def __call__(
1847             self,
1848             value=None,
1849             impl=None,
1850             expl=None,
1851             default=None,
1852             optional=None,
1853     ):
1854         return self.__class__(
1855             value=value,
1856             impl=self.tag if impl is None else impl,
1857             expl=self._expl if expl is None else expl,
1858             default=self.default if default is None else default,
1859             optional=self.optional if optional is None else optional,
1860         )
1861
1862     def _encode(self):
1863         self._assert_ready()
1864         return b"".join((
1865             self.tag,
1866             len_encode(1),
1867             (b"\xFF" if self._value else b"\x00"),
1868         ))
1869
1870     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1871         try:
1872             t, _, lv = tag_strip(tlv)
1873         except DecodeError as err:
1874             raise err.__class__(
1875                 msg=err.msg,
1876                 klass=self.__class__,
1877                 decode_path=decode_path,
1878                 offset=offset,
1879             )
1880         if t != self.tag:
1881             raise TagMismatch(
1882                 klass=self.__class__,
1883                 decode_path=decode_path,
1884                 offset=offset,
1885             )
1886         if tag_only:
1887             return None
1888         try:
1889             l, _, v = len_decode(lv)
1890         except DecodeError as err:
1891             raise err.__class__(
1892                 msg=err.msg,
1893                 klass=self.__class__,
1894                 decode_path=decode_path,
1895                 offset=offset,
1896             )
1897         if l != 1:
1898             raise InvalidLength(
1899                 "Boolean's length must be equal to 1",
1900                 klass=self.__class__,
1901                 decode_path=decode_path,
1902                 offset=offset,
1903             )
1904         if l > len(v):
1905             raise NotEnoughData(
1906                 "encoded length is longer than data",
1907                 klass=self.__class__,
1908                 decode_path=decode_path,
1909                 offset=offset,
1910             )
1911         first_octet = byte2int(v)
1912         ber_encoded = False
1913         if first_octet == 0:
1914             value = False
1915         elif first_octet == 0xFF:
1916             value = True
1917         elif ctx.get("bered", False):
1918             value = True
1919             ber_encoded = True
1920         else:
1921             raise DecodeError(
1922                 "unacceptable Boolean value",
1923                 klass=self.__class__,
1924                 decode_path=decode_path,
1925                 offset=offset,
1926             )
1927         obj = self.__class__(
1928             value=value,
1929             impl=self.tag,
1930             expl=self._expl,
1931             default=self.default,
1932             optional=self.optional,
1933             _decoded=(offset, 1, 1),
1934         )
1935         obj.ber_encoded = ber_encoded
1936         return obj, v[1:]
1937
1938     def __repr__(self):
1939         return pp_console_row(next(self.pps()))
1940
1941     def pps(self, decode_path=()):
1942         yield _pp(
1943             obj=self,
1944             asn1_type_name=self.asn1_type_name,
1945             obj_name=self.__class__.__name__,
1946             decode_path=decode_path,
1947             value=str(self._value) if self.ready else None,
1948             optional=self.optional,
1949             default=self == self.default,
1950             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1951             expl=None if self._expl is None else tag_decode(self._expl),
1952             offset=self.offset,
1953             tlen=self.tlen,
1954             llen=self.llen,
1955             vlen=self.vlen,
1956             expl_offset=self.expl_offset if self.expled else None,
1957             expl_tlen=self.expl_tlen if self.expled else None,
1958             expl_llen=self.expl_llen if self.expled else None,
1959             expl_vlen=self.expl_vlen if self.expled else None,
1960             expl_lenindef=self.expl_lenindef,
1961             ber_encoded=self.ber_encoded,
1962             bered=self.bered,
1963         )
1964         for pp in self.pps_lenindef(decode_path):
1965             yield pp
1966
1967
1968 IntegerState = namedtuple("IntegerState", (
1969     "version",
1970     "specs",
1971     "value",
1972     "bound_min",
1973     "bound_max",
1974     "tag",
1975     "expl",
1976     "default",
1977     "optional",
1978     "offset",
1979     "llen",
1980     "vlen",
1981     "expl_lenindef",
1982     "lenindef",
1983     "ber_encoded",
1984 ))
1985
1986
1987 class Integer(Obj):
1988     """``INTEGER`` integer type
1989
1990     >>> b = Integer(-123)
1991     INTEGER -123
1992     >>> b == Integer(-123)
1993     True
1994     >>> int(b)
1995     -123
1996
1997     >>> Integer(2, bounds=(1, 3))
1998     INTEGER 2
1999     >>> Integer(5, bounds=(1, 3))
2000     Traceback (most recent call last):
2001     pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2002
2003     ::
2004
2005         class Version(Integer):
2006             schema = (
2007                 ("v1", 0),
2008                 ("v2", 1),
2009                 ("v3", 2),
2010             )
2011
2012     >>> v = Version("v1")
2013     Version INTEGER v1
2014     >>> int(v)
2015     0
2016     >>> v.named
2017     'v1'
2018     >>> v.specs
2019     {'v3': 2, 'v1': 0, 'v2': 1}
2020     """
2021     __slots__ = ("specs", "_bound_min", "_bound_max")
2022     tag_default = tag_encode(2)
2023     asn1_type_name = "INTEGER"
2024
2025     def __init__(
2026             self,
2027             value=None,
2028             bounds=None,
2029             impl=None,
2030             expl=None,
2031             default=None,
2032             optional=False,
2033             _specs=None,
2034             _decoded=(0, 0, 0),
2035     ):
2036         """
2037         :param value: set the value. Either integer type, named value
2038                       (if ``schema`` is specified in the class), or
2039                       :py:class:`pyderasn.Integer` object
2040         :param bounds: set ``(MIN, MAX)`` value constraint.
2041                        (-inf, +inf) by default
2042         :param bytes impl: override default tag with ``IMPLICIT`` one
2043         :param bytes expl: override default tag with ``EXPLICIT`` one
2044         :param default: set default value. Type same as in ``value``
2045         :param bool optional: is object ``OPTIONAL`` in sequence
2046         """
2047         super(Integer, self).__init__(impl, expl, default, optional, _decoded)
2048         self._value = value
2049         specs = getattr(self, "schema", {}) if _specs is None else _specs
2050         self.specs = specs if isinstance(specs, dict) else dict(specs)
2051         self._bound_min, self._bound_max = getattr(
2052             self,
2053             "bounds",
2054             (float("-inf"), float("+inf")),
2055         ) if bounds is None else bounds
2056         if value is not None:
2057             self._value = self._value_sanitize(value)
2058         if default is not None:
2059             default = self._value_sanitize(default)
2060             self.default = self.__class__(
2061                 value=default,
2062                 impl=self.tag,
2063                 expl=self._expl,
2064                 _specs=self.specs,
2065             )
2066             if self._value is None:
2067                 self._value = default
2068
2069     def _value_sanitize(self, value):
2070         if isinstance(value, integer_types):
2071             pass
2072         elif issubclass(value.__class__, Integer):
2073             value = value._value
2074         elif isinstance(value, str):
2075             value = self.specs.get(value)
2076             if value is None:
2077                 raise ObjUnknown("integer value: %s" % value)
2078         else:
2079             raise InvalidValueType((self.__class__, int, str))
2080         if not self._bound_min <= value <= self._bound_max:
2081             raise BoundsError(self._bound_min, value, self._bound_max)
2082         return value
2083
2084     @property
2085     def ready(self):
2086         return self._value is not None
2087
2088     def __getstate__(self):
2089         return IntegerState(
2090             __version__,
2091             self.specs,
2092             self._value,
2093             self._bound_min,
2094             self._bound_max,
2095             self.tag,
2096             self._expl,
2097             self.default,
2098             self.optional,
2099             self.offset,
2100             self.llen,
2101             self.vlen,
2102             self.expl_lenindef,
2103             self.lenindef,
2104             self.ber_encoded,
2105         )
2106
2107     def __setstate__(self, state):
2108         super(Integer, self).__setstate__(state)
2109         self.specs = state.specs
2110         self._value = state.value
2111         self._bound_min = state.bound_min
2112         self._bound_max = state.bound_max
2113         self.tag = state.tag
2114         self._expl = state.expl
2115         self.default = state.default
2116         self.optional = state.optional
2117         self.offset = state.offset
2118         self.llen = state.llen
2119         self.vlen = state.vlen
2120         self.expl_lenindef = state.expl_lenindef
2121         self.lenindef = state.lenindef
2122         self.ber_encoded = state.ber_encoded
2123
2124     def __int__(self):
2125         self._assert_ready()
2126         return int(self._value)
2127
2128     def __hash__(self):
2129         self._assert_ready()
2130         return hash(
2131             self.tag +
2132             bytes(self._expl or b"") +
2133             str(self._value).encode("ascii"),
2134         )
2135
2136     def __eq__(self, their):
2137         if isinstance(their, integer_types):
2138             return self._value == their
2139         if not issubclass(their.__class__, Integer):
2140             return False
2141         return (
2142             self._value == their._value and
2143             self.tag == their.tag and
2144             self._expl == their._expl
2145         )
2146
2147     def __lt__(self, their):
2148         return self._value < their._value
2149
2150     @property
2151     def named(self):
2152         for name, value in iteritems(self.specs):
2153             if value == self._value:
2154                 return name
2155         return None
2156
2157     def __call__(
2158             self,
2159             value=None,
2160             bounds=None,
2161             impl=None,
2162             expl=None,
2163             default=None,
2164             optional=None,
2165     ):
2166         return self.__class__(
2167             value=value,
2168             bounds=(
2169                 (self._bound_min, self._bound_max)
2170                 if bounds is None else bounds
2171             ),
2172             impl=self.tag if impl is None else impl,
2173             expl=self._expl if expl is None else expl,
2174             default=self.default if default is None else default,
2175             optional=self.optional if optional is None else optional,
2176             _specs=self.specs,
2177         )
2178
2179     def _encode(self):
2180         self._assert_ready()
2181         value = self._value
2182         if PY2:
2183             if value == 0:
2184                 octets = bytearray([0])
2185             elif value < 0:
2186                 value = -value
2187                 value -= 1
2188                 octets = bytearray()
2189                 while value > 0:
2190                     octets.append((value & 0xFF) ^ 0xFF)
2191                     value >>= 8
2192                 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2193                     octets.append(0xFF)
2194             else:
2195                 octets = bytearray()
2196                 while value > 0:
2197                     octets.append(value & 0xFF)
2198                     value >>= 8
2199                 if octets[-1] & 0x80 > 0:
2200                     octets.append(0x00)
2201             octets.reverse()
2202             octets = bytes(octets)
2203         else:
2204             bytes_len = ceil(value.bit_length() / 8) or 1
2205             while True:
2206                 try:
2207                     octets = value.to_bytes(
2208                         bytes_len,
2209                         byteorder="big",
2210                         signed=True,
2211                     )
2212                 except OverflowError:
2213                     bytes_len += 1
2214                 else:
2215                     break
2216         return b"".join((self.tag, len_encode(len(octets)), octets))
2217
2218     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2219         try:
2220             t, _, lv = tag_strip(tlv)
2221         except DecodeError as err:
2222             raise err.__class__(
2223                 msg=err.msg,
2224                 klass=self.__class__,
2225                 decode_path=decode_path,
2226                 offset=offset,
2227             )
2228         if t != self.tag:
2229             raise TagMismatch(
2230                 klass=self.__class__,
2231                 decode_path=decode_path,
2232                 offset=offset,
2233             )
2234         if tag_only:
2235             return None
2236         try:
2237             l, llen, v = len_decode(lv)
2238         except DecodeError as err:
2239             raise err.__class__(
2240                 msg=err.msg,
2241                 klass=self.__class__,
2242                 decode_path=decode_path,
2243                 offset=offset,
2244             )
2245         if l > len(v):
2246             raise NotEnoughData(
2247                 "encoded length is longer than data",
2248                 klass=self.__class__,
2249                 decode_path=decode_path,
2250                 offset=offset,
2251             )
2252         if l == 0:
2253             raise NotEnoughData(
2254                 "zero length",
2255                 klass=self.__class__,
2256                 decode_path=decode_path,
2257                 offset=offset,
2258             )
2259         v, tail = v[:l], v[l:]
2260         first_octet = byte2int(v)
2261         if l > 1:
2262             second_octet = byte2int(v[1:])
2263             if (
2264                     ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2265                     ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2266             ):
2267                 raise DecodeError(
2268                     "non normalized integer",
2269                     klass=self.__class__,
2270                     decode_path=decode_path,
2271                     offset=offset,
2272                 )
2273         if PY2:
2274             value = 0
2275             if first_octet & 0x80 > 0:
2276                 octets = bytearray()
2277                 for octet in bytearray(v):
2278                     octets.append(octet ^ 0xFF)
2279                 for octet in octets:
2280                     value = (value << 8) | octet
2281                 value += 1
2282                 value = -value
2283             else:
2284                 for octet in bytearray(v):
2285                     value = (value << 8) | octet
2286         else:
2287             value = int.from_bytes(v, byteorder="big", signed=True)
2288         try:
2289             obj = self.__class__(
2290                 value=value,
2291                 bounds=(self._bound_min, self._bound_max),
2292                 impl=self.tag,
2293                 expl=self._expl,
2294                 default=self.default,
2295                 optional=self.optional,
2296                 _specs=self.specs,
2297                 _decoded=(offset, llen, l),
2298             )
2299         except BoundsError as err:
2300             raise DecodeError(
2301                 msg=str(err),
2302                 klass=self.__class__,
2303                 decode_path=decode_path,
2304                 offset=offset,
2305             )
2306         return obj, tail
2307
2308     def __repr__(self):
2309         return pp_console_row(next(self.pps()))
2310
2311     def pps(self, decode_path=()):
2312         yield _pp(
2313             obj=self,
2314             asn1_type_name=self.asn1_type_name,
2315             obj_name=self.__class__.__name__,
2316             decode_path=decode_path,
2317             value=(self.named or str(self._value)) if self.ready else None,
2318             optional=self.optional,
2319             default=self == self.default,
2320             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2321             expl=None if self._expl is None else tag_decode(self._expl),
2322             offset=self.offset,
2323             tlen=self.tlen,
2324             llen=self.llen,
2325             vlen=self.vlen,
2326             expl_offset=self.expl_offset if self.expled else None,
2327             expl_tlen=self.expl_tlen if self.expled else None,
2328             expl_llen=self.expl_llen if self.expled else None,
2329             expl_vlen=self.expl_vlen if self.expled else None,
2330             expl_lenindef=self.expl_lenindef,
2331             bered=self.bered,
2332         )
2333         for pp in self.pps_lenindef(decode_path):
2334             yield pp
2335
2336
2337 SET01 = frozenset(("0", "1"))
2338 BitStringState = namedtuple("BitStringState", (
2339     "version",
2340     "specs",
2341     "value",
2342     "tag",
2343     "expl",
2344     "default",
2345     "optional",
2346     "offset",
2347     "llen",
2348     "vlen",
2349     "expl_lenindef",
2350     "lenindef",
2351     "ber_encoded",
2352     "tag_constructed",
2353     "defined",
2354 ))
2355
2356
2357 class BitString(Obj):
2358     """``BIT STRING`` bit string type
2359
2360     >>> BitString(b"hello world")
2361     BIT STRING 88 bits 68656c6c6f20776f726c64
2362     >>> bytes(b)
2363     b'hello world'
2364     >>> b == b"hello world"
2365     True
2366     >>> b.bit_len
2367     88
2368
2369     >>> BitString("'0A3B5F291CD'H")
2370     BIT STRING 44 bits 0a3b5f291cd0
2371     >>> b = BitString("'010110000000'B")
2372     BIT STRING 12 bits 5800
2373     >>> b.bit_len
2374     12
2375     >>> b[0], b[1], b[2], b[3]
2376     (False, True, False, True)
2377     >>> b[1000]
2378     False
2379     >>> [v for v in b]
2380     [False, True, False, True, True, False, False, False, False, False, False, False]
2381
2382     ::
2383
2384         class KeyUsage(BitString):
2385             schema = (
2386                 ("digitalSignature", 0),
2387                 ("nonRepudiation", 1),
2388                 ("keyEncipherment", 2),
2389             )
2390
2391     >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2392     KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2393     >>> b.named
2394     ['nonRepudiation', 'keyEncipherment']
2395     >>> b.specs
2396     {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2397
2398     .. note::
2399
2400        Pay attention that BIT STRING can be encoded both in primitive
2401        and constructed forms. Decoder always checks constructed form tag
2402        additionally to specified primitive one. If BER decoding is
2403        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2404        of DER restrictions.
2405     """
2406     __slots__ = ("tag_constructed", "specs", "defined")
2407     tag_default = tag_encode(3)
2408     asn1_type_name = "BIT STRING"
2409
2410     def __init__(
2411             self,
2412             value=None,
2413             impl=None,
2414             expl=None,
2415             default=None,
2416             optional=False,
2417             _specs=None,
2418             _decoded=(0, 0, 0),
2419     ):
2420         """
2421         :param value: set the value. Either binary type, tuple of named
2422                       values (if ``schema`` is specified in the class),
2423                       string in ``'XXX...'B`` form, or
2424                       :py:class:`pyderasn.BitString` object
2425         :param bytes impl: override default tag with ``IMPLICIT`` one
2426         :param bytes expl: override default tag with ``EXPLICIT`` one
2427         :param default: set default value. Type same as in ``value``
2428         :param bool optional: is object ``OPTIONAL`` in sequence
2429         """
2430         super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2431         specs = getattr(self, "schema", {}) if _specs is None else _specs
2432         self.specs = specs if isinstance(specs, dict) else dict(specs)
2433         self._value = None if value is None else self._value_sanitize(value)
2434         if default is not None:
2435             default = self._value_sanitize(default)
2436             self.default = self.__class__(
2437                 value=default,
2438                 impl=self.tag,
2439                 expl=self._expl,
2440             )
2441             if value is None:
2442                 self._value = default
2443         self.defined = None
2444         tag_klass, _, tag_num = tag_decode(self.tag)
2445         self.tag_constructed = tag_encode(
2446             klass=tag_klass,
2447             form=TagFormConstructed,
2448             num=tag_num,
2449         )
2450
2451     def _bits2octets(self, bits):
2452         if len(self.specs) > 0:
2453             bits = bits.rstrip("0")
2454         bit_len = len(bits)
2455         bits += "0" * ((8 - (bit_len % 8)) % 8)
2456         octets = bytearray(len(bits) // 8)
2457         for i in six_xrange(len(octets)):
2458             octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2459         return bit_len, bytes(octets)
2460
2461     def _value_sanitize(self, value):
2462         if isinstance(value, (string_types, binary_type)):
2463             if (
2464                     isinstance(value, string_types) and
2465                     value.startswith("'")
2466             ):
2467                 if value.endswith("'B"):
2468                     value = value[1:-2]
2469                     if not frozenset(value) <= SET01:
2470                         raise ValueError("B's coding contains unacceptable chars")
2471                     return self._bits2octets(value)
2472                 if value.endswith("'H"):
2473                     value = value[1:-2]
2474                     return (
2475                         len(value) * 4,
2476                         hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2477                     )
2478             if isinstance(value, binary_type):
2479                 return (len(value) * 8, value)
2480             raise InvalidValueType((self.__class__, string_types, binary_type))
2481         if isinstance(value, tuple):
2482             if (
2483                     len(value) == 2 and
2484                     isinstance(value[0], integer_types) and
2485                     isinstance(value[1], binary_type)
2486             ):
2487                 return value
2488             bits = []
2489             for name in value:
2490                 bit = self.specs.get(name)
2491                 if bit is None:
2492                     raise ObjUnknown("BitString value: %s" % name)
2493                 bits.append(bit)
2494             if len(bits) == 0:
2495                 return self._bits2octets("")
2496             bits = frozenset(bits)
2497             return self._bits2octets("".join(
2498                 ("1" if bit in bits else "0")
2499                 for bit in six_xrange(max(bits) + 1)
2500             ))
2501         if issubclass(value.__class__, BitString):
2502             return value._value
2503         raise InvalidValueType((self.__class__, binary_type, string_types))
2504
2505     @property
2506     def ready(self):
2507         return self._value is not None
2508
2509     def __getstate__(self):
2510         return BitStringState(
2511             __version__,
2512             self.specs,
2513             self._value,
2514             self.tag,
2515             self._expl,
2516             self.default,
2517             self.optional,
2518             self.offset,
2519             self.llen,
2520             self.vlen,
2521             self.expl_lenindef,
2522             self.lenindef,
2523             self.ber_encoded,
2524             self.tag_constructed,
2525             self.defined,
2526         )
2527
2528     def __setstate__(self, state):
2529         super(BitString, self).__setstate__(state)
2530         self.specs = state.specs
2531         self._value = state.value
2532         self.tag = state.tag
2533         self._expl = state.expl
2534         self.default = state.default
2535         self.optional = state.optional
2536         self.offset = state.offset
2537         self.llen = state.llen
2538         self.vlen = state.vlen
2539         self.expl_lenindef = state.expl_lenindef
2540         self.lenindef = state.lenindef
2541         self.ber_encoded = state.ber_encoded
2542         self.tag_constructed = state.tag_constructed
2543         self.defined = state.defined
2544
2545     def __iter__(self):
2546         self._assert_ready()
2547         for i in six_xrange(self._value[0]):
2548             yield self[i]
2549
2550     @property
2551     def bit_len(self):
2552         self._assert_ready()
2553         return self._value[0]
2554
2555     def __bytes__(self):
2556         self._assert_ready()
2557         return self._value[1]
2558
2559     def __eq__(self, their):
2560         if isinstance(their, bytes):
2561             return self._value[1] == their
2562         if not issubclass(their.__class__, BitString):
2563             return False
2564         return (
2565             self._value == their._value and
2566             self.tag == their.tag and
2567             self._expl == their._expl
2568         )
2569
2570     @property
2571     def named(self):
2572         return [name for name, bit in iteritems(self.specs) if self[bit]]
2573
2574     def __call__(
2575             self,
2576             value=None,
2577             impl=None,
2578             expl=None,
2579             default=None,
2580             optional=None,
2581     ):
2582         return self.__class__(
2583             value=value,
2584             impl=self.tag if impl is None else impl,
2585             expl=self._expl if expl is None else expl,
2586             default=self.default if default is None else default,
2587             optional=self.optional if optional is None else optional,
2588             _specs=self.specs,
2589         )
2590
2591     def __getitem__(self, key):
2592         if isinstance(key, int):
2593             bit_len, octets = self._value
2594             if key >= bit_len:
2595                 return False
2596             return (
2597                 byte2int(memoryview(octets)[key // 8:]) >>
2598                 (7 - (key % 8))
2599             ) & 1 == 1
2600         if isinstance(key, string_types):
2601             value = self.specs.get(key)
2602             if value is None:
2603                 raise ObjUnknown("BitString value: %s" % key)
2604             return self[value]
2605         raise InvalidValueType((int, str))
2606
2607     def _encode(self):
2608         self._assert_ready()
2609         bit_len, octets = self._value
2610         return b"".join((
2611             self.tag,
2612             len_encode(len(octets) + 1),
2613             int2byte((8 - bit_len % 8) % 8),
2614             octets,
2615         ))
2616
2617     def _decode_chunk(self, lv, offset, decode_path):
2618         try:
2619             l, llen, v = len_decode(lv)
2620         except DecodeError as err:
2621             raise err.__class__(
2622                 msg=err.msg,
2623                 klass=self.__class__,
2624                 decode_path=decode_path,
2625                 offset=offset,
2626             )
2627         if l > len(v):
2628             raise NotEnoughData(
2629                 "encoded length is longer than data",
2630                 klass=self.__class__,
2631                 decode_path=decode_path,
2632                 offset=offset,
2633             )
2634         if l == 0:
2635             raise NotEnoughData(
2636                 "zero length",
2637                 klass=self.__class__,
2638                 decode_path=decode_path,
2639                 offset=offset,
2640             )
2641         pad_size = byte2int(v)
2642         if l == 1 and pad_size != 0:
2643             raise DecodeError(
2644                 "invalid empty value",
2645                 klass=self.__class__,
2646                 decode_path=decode_path,
2647                 offset=offset,
2648             )
2649         if pad_size > 7:
2650             raise DecodeError(
2651                 "too big pad",
2652                 klass=self.__class__,
2653                 decode_path=decode_path,
2654                 offset=offset,
2655             )
2656         if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2657             raise DecodeError(
2658                 "invalid pad",
2659                 klass=self.__class__,
2660                 decode_path=decode_path,
2661                 offset=offset,
2662             )
2663         v, tail = v[:l], v[l:]
2664         obj = self.__class__(
2665             value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2666             impl=self.tag,
2667             expl=self._expl,
2668             default=self.default,
2669             optional=self.optional,
2670             _specs=self.specs,
2671             _decoded=(offset, llen, l),
2672         )
2673         return obj, tail
2674
2675     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2676         try:
2677             t, tlen, lv = tag_strip(tlv)
2678         except DecodeError as err:
2679             raise err.__class__(
2680                 msg=err.msg,
2681                 klass=self.__class__,
2682                 decode_path=decode_path,
2683                 offset=offset,
2684             )
2685         if t == self.tag:
2686             if tag_only:  # pragma: no cover
2687                 return None
2688             return self._decode_chunk(lv, offset, decode_path)
2689         if t == self.tag_constructed:
2690             if not ctx.get("bered", False):
2691                 raise DecodeError(
2692                     "unallowed BER constructed encoding",
2693                     klass=self.__class__,
2694                     decode_path=decode_path,
2695                     offset=offset,
2696                 )
2697             if tag_only:  # pragma: no cover
2698                 return None
2699             lenindef = False
2700             try:
2701                 l, llen, v = len_decode(lv)
2702             except LenIndefForm:
2703                 llen, l, v = 1, 0, lv[1:]
2704                 lenindef = True
2705             except DecodeError as err:
2706                 raise err.__class__(
2707                     msg=err.msg,
2708                     klass=self.__class__,
2709                     decode_path=decode_path,
2710                     offset=offset,
2711                 )
2712             if l > len(v):
2713                 raise NotEnoughData(
2714                     "encoded length is longer than data",
2715                     klass=self.__class__,
2716                     decode_path=decode_path,
2717                     offset=offset,
2718                 )
2719             if not lenindef and l == 0:
2720                 raise NotEnoughData(
2721                     "zero length",
2722                     klass=self.__class__,
2723                     decode_path=decode_path,
2724                     offset=offset,
2725                 )
2726             chunks = []
2727             sub_offset = offset + tlen + llen
2728             vlen = 0
2729             while True:
2730                 if lenindef:
2731                     if v[:EOC_LEN].tobytes() == EOC:
2732                         break
2733                 else:
2734                     if vlen == l:
2735                         break
2736                     if vlen > l:
2737                         raise DecodeError(
2738                             "chunk out of bounds",
2739                             klass=self.__class__,
2740                             decode_path=decode_path + (str(len(chunks) - 1),),
2741                             offset=chunks[-1].offset,
2742                         )
2743                 sub_decode_path = decode_path + (str(len(chunks)),)
2744                 try:
2745                     chunk, v_tail = BitString().decode(
2746                         v,
2747                         offset=sub_offset,
2748                         decode_path=sub_decode_path,
2749                         leavemm=True,
2750                         ctx=ctx,
2751                         _ctx_immutable=False,
2752                     )
2753                 except TagMismatch:
2754                     raise DecodeError(
2755                         "expected BitString encoded chunk",
2756                         klass=self.__class__,
2757                         decode_path=sub_decode_path,
2758                         offset=sub_offset,
2759                     )
2760                 chunks.append(chunk)
2761                 sub_offset += chunk.tlvlen
2762                 vlen += chunk.tlvlen
2763                 v = v_tail
2764             if len(chunks) == 0:
2765                 raise DecodeError(
2766                     "no chunks",
2767                     klass=self.__class__,
2768                     decode_path=decode_path,
2769                     offset=offset,
2770                 )
2771             values = []
2772             bit_len = 0
2773             for chunk_i, chunk in enumerate(chunks[:-1]):
2774                 if chunk.bit_len % 8 != 0:
2775                     raise DecodeError(
2776                         "BitString chunk is not multiple of 8 bits",
2777                         klass=self.__class__,
2778                         decode_path=decode_path + (str(chunk_i),),
2779                         offset=chunk.offset,
2780                     )
2781                 values.append(bytes(chunk))
2782                 bit_len += chunk.bit_len
2783             chunk_last = chunks[-1]
2784             values.append(bytes(chunk_last))
2785             bit_len += chunk_last.bit_len
2786             obj = self.__class__(
2787                 value=(bit_len, b"".join(values)),
2788                 impl=self.tag,
2789                 expl=self._expl,
2790                 default=self.default,
2791                 optional=self.optional,
2792                 _specs=self.specs,
2793                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2794             )
2795             obj.lenindef = lenindef
2796             obj.ber_encoded = True
2797             return obj, (v[EOC_LEN:] if lenindef else v)
2798         raise TagMismatch(
2799             klass=self.__class__,
2800             decode_path=decode_path,
2801             offset=offset,
2802         )
2803
2804     def __repr__(self):
2805         return pp_console_row(next(self.pps()))
2806
2807     def pps(self, decode_path=()):
2808         value = None
2809         blob = None
2810         if self.ready:
2811             bit_len, blob = self._value
2812             value = "%d bits" % bit_len
2813             if len(self.specs) > 0:
2814                 blob = tuple(self.named)
2815         yield _pp(
2816             obj=self,
2817             asn1_type_name=self.asn1_type_name,
2818             obj_name=self.__class__.__name__,
2819             decode_path=decode_path,
2820             value=value,
2821             blob=blob,
2822             optional=self.optional,
2823             default=self == self.default,
2824             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2825             expl=None if self._expl is None else tag_decode(self._expl),
2826             offset=self.offset,
2827             tlen=self.tlen,
2828             llen=self.llen,
2829             vlen=self.vlen,
2830             expl_offset=self.expl_offset if self.expled else None,
2831             expl_tlen=self.expl_tlen if self.expled else None,
2832             expl_llen=self.expl_llen if self.expled else None,
2833             expl_vlen=self.expl_vlen if self.expled else None,
2834             expl_lenindef=self.expl_lenindef,
2835             lenindef=self.lenindef,
2836             ber_encoded=self.ber_encoded,
2837             bered=self.bered,
2838         )
2839         defined_by, defined = self.defined or (None, None)
2840         if defined_by is not None:
2841             yield defined.pps(
2842                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2843             )
2844         for pp in self.pps_lenindef(decode_path):
2845             yield pp
2846
2847
2848 OctetStringState = namedtuple("OctetStringState", (
2849     "version",
2850     "value",
2851     "bound_min",
2852     "bound_max",
2853     "tag",
2854     "expl",
2855     "default",
2856     "optional",
2857     "offset",
2858     "llen",
2859     "vlen",
2860     "expl_lenindef",
2861     "lenindef",
2862     "ber_encoded",
2863     "tag_constructed",
2864     "defined",
2865 ))
2866
2867
2868 class OctetString(Obj):
2869     """``OCTET STRING`` binary string type
2870
2871     >>> s = OctetString(b"hello world")
2872     OCTET STRING 11 bytes 68656c6c6f20776f726c64
2873     >>> s == OctetString(b"hello world")
2874     True
2875     >>> bytes(s)
2876     b'hello world'
2877
2878     >>> OctetString(b"hello", bounds=(4, 4))
2879     Traceback (most recent call last):
2880     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2881     >>> OctetString(b"hell", bounds=(4, 4))
2882     OCTET STRING 4 bytes 68656c6c
2883
2884     .. note::
2885
2886        Pay attention that OCTET STRING can be encoded both in primitive
2887        and constructed forms. Decoder always checks constructed form tag
2888        additionally to specified primitive one. If BER decoding is
2889        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2890        of DER restrictions.
2891     """
2892     __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2893     tag_default = tag_encode(4)
2894     asn1_type_name = "OCTET STRING"
2895
2896     def __init__(
2897             self,
2898             value=None,
2899             bounds=None,
2900             impl=None,
2901             expl=None,
2902             default=None,
2903             optional=False,
2904             _decoded=(0, 0, 0),
2905     ):
2906         """
2907         :param value: set the value. Either binary type, or
2908                       :py:class:`pyderasn.OctetString` object
2909         :param bounds: set ``(MIN, MAX)`` value size constraint.
2910                        (-inf, +inf) by default
2911         :param bytes impl: override default tag with ``IMPLICIT`` one
2912         :param bytes expl: override default tag with ``EXPLICIT`` one
2913         :param default: set default value. Type same as in ``value``
2914         :param bool optional: is object ``OPTIONAL`` in sequence
2915         """
2916         super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
2917         self._value = value
2918         self._bound_min, self._bound_max = getattr(
2919             self,
2920             "bounds",
2921             (0, float("+inf")),
2922         ) if bounds is None else bounds
2923         if value is not None:
2924             self._value = self._value_sanitize(value)
2925         if default is not None:
2926             default = self._value_sanitize(default)
2927             self.default = self.__class__(
2928                 value=default,
2929                 impl=self.tag,
2930                 expl=self._expl,
2931             )
2932             if self._value is None:
2933                 self._value = default
2934         self.defined = None
2935         tag_klass, _, tag_num = tag_decode(self.tag)
2936         self.tag_constructed = tag_encode(
2937             klass=tag_klass,
2938             form=TagFormConstructed,
2939             num=tag_num,
2940         )
2941
2942     def _value_sanitize(self, value):
2943         if isinstance(value, binary_type):
2944             pass
2945         elif issubclass(value.__class__, OctetString):
2946             value = value._value
2947         else:
2948             raise InvalidValueType((self.__class__, bytes))
2949         if not self._bound_min <= len(value) <= self._bound_max:
2950             raise BoundsError(self._bound_min, len(value), self._bound_max)
2951         return value
2952
2953     @property
2954     def ready(self):
2955         return self._value is not None
2956
2957     def __getstate__(self):
2958         return OctetStringState(
2959             __version__,
2960             self._value,
2961             self._bound_min,
2962             self._bound_max,
2963             self.tag,
2964             self._expl,
2965             self.default,
2966             self.optional,
2967             self.offset,
2968             self.llen,
2969             self.vlen,
2970             self.expl_lenindef,
2971             self.lenindef,
2972             self.ber_encoded,
2973             self.tag_constructed,
2974             self.defined,
2975         )
2976
2977     def __setstate__(self, state):
2978         super(OctetString, self).__setstate__(state)
2979         self._value = state.value
2980         self._bound_min = state.bound_min
2981         self._bound_max = state.bound_max
2982         self.tag = state.tag
2983         self._expl = state.expl
2984         self.default = state.default
2985         self.optional = state.optional
2986         self.offset = state.offset
2987         self.llen = state.llen
2988         self.vlen = state.vlen
2989         self.expl_lenindef = state.expl_lenindef
2990         self.lenindef = state.lenindef
2991         self.ber_encoded = state.ber_encoded
2992         self.tag_constructed = state.tag_constructed
2993         self.defined = state.defined
2994
2995     def __bytes__(self):
2996         self._assert_ready()
2997         return self._value
2998
2999     def __eq__(self, their):
3000         if isinstance(their, binary_type):
3001             return self._value == their
3002         if not issubclass(their.__class__, OctetString):
3003             return False
3004         return (
3005             self._value == their._value and
3006             self.tag == their.tag and
3007             self._expl == their._expl
3008         )
3009
3010     def __lt__(self, their):
3011         return self._value < their._value
3012
3013     def __call__(
3014             self,
3015             value=None,
3016             bounds=None,
3017             impl=None,
3018             expl=None,
3019             default=None,
3020             optional=None,
3021     ):
3022         return self.__class__(
3023             value=value,
3024             bounds=(
3025                 (self._bound_min, self._bound_max)
3026                 if bounds is None else bounds
3027             ),
3028             impl=self.tag if impl is None else impl,
3029             expl=self._expl if expl is None else expl,
3030             default=self.default if default is None else default,
3031             optional=self.optional if optional is None else optional,
3032         )
3033
3034     def _encode(self):
3035         self._assert_ready()
3036         return b"".join((
3037             self.tag,
3038             len_encode(len(self._value)),
3039             self._value,
3040         ))
3041
3042     def _decode_chunk(self, lv, offset, decode_path):
3043         try:
3044             l, llen, v = len_decode(lv)
3045         except DecodeError as err:
3046             raise err.__class__(
3047                 msg=err.msg,
3048                 klass=self.__class__,
3049                 decode_path=decode_path,
3050                 offset=offset,
3051             )
3052         if l > len(v):
3053             raise NotEnoughData(
3054                 "encoded length is longer than data",
3055                 klass=self.__class__,
3056                 decode_path=decode_path,
3057                 offset=offset,
3058             )
3059         v, tail = v[:l], v[l:]
3060         try:
3061             obj = self.__class__(
3062                 value=v.tobytes(),
3063                 bounds=(self._bound_min, self._bound_max),
3064                 impl=self.tag,
3065                 expl=self._expl,
3066                 default=self.default,
3067                 optional=self.optional,
3068                 _decoded=(offset, llen, l),
3069             )
3070         except DecodeError as err:
3071             raise DecodeError(
3072                 msg=err.msg,
3073                 klass=self.__class__,
3074                 decode_path=decode_path,
3075                 offset=offset,
3076             )
3077         except BoundsError as err:
3078             raise DecodeError(
3079                 msg=str(err),
3080                 klass=self.__class__,
3081                 decode_path=decode_path,
3082                 offset=offset,
3083             )
3084         return obj, tail
3085
3086     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3087         try:
3088             t, tlen, lv = tag_strip(tlv)
3089         except DecodeError as err:
3090             raise err.__class__(
3091                 msg=err.msg,
3092                 klass=self.__class__,
3093                 decode_path=decode_path,
3094                 offset=offset,
3095             )
3096         if t == self.tag:
3097             if tag_only:
3098                 return None
3099             return self._decode_chunk(lv, offset, decode_path)
3100         if t == self.tag_constructed:
3101             if not ctx.get("bered", False):
3102                 raise DecodeError(
3103                     "unallowed BER constructed encoding",
3104                     klass=self.__class__,
3105                     decode_path=decode_path,
3106                     offset=offset,
3107                 )
3108             if tag_only:
3109                 return None
3110             lenindef = False
3111             try:
3112                 l, llen, v = len_decode(lv)
3113             except LenIndefForm:
3114                 llen, l, v = 1, 0, lv[1:]
3115                 lenindef = True
3116             except DecodeError as err:
3117                 raise err.__class__(
3118                     msg=err.msg,
3119                     klass=self.__class__,
3120                     decode_path=decode_path,
3121                     offset=offset,
3122                 )
3123             if l > len(v):
3124                 raise NotEnoughData(
3125                     "encoded length is longer than data",
3126                     klass=self.__class__,
3127                     decode_path=decode_path,
3128                     offset=offset,
3129                 )
3130             chunks = []
3131             sub_offset = offset + tlen + llen
3132             vlen = 0
3133             while True:
3134                 if lenindef:
3135                     if v[:EOC_LEN].tobytes() == EOC:
3136                         break
3137                 else:
3138                     if vlen == l:
3139                         break
3140                     if vlen > l:
3141                         raise DecodeError(
3142                             "chunk out of bounds",
3143                             klass=self.__class__,
3144                             decode_path=decode_path + (str(len(chunks) - 1),),
3145                             offset=chunks[-1].offset,
3146                         )
3147                 sub_decode_path = decode_path + (str(len(chunks)),)
3148                 try:
3149                     chunk, v_tail = OctetString().decode(
3150                         v,
3151                         offset=sub_offset,
3152                         decode_path=sub_decode_path,
3153                         leavemm=True,
3154                         ctx=ctx,
3155                         _ctx_immutable=False,
3156                     )
3157                 except TagMismatch:
3158                     raise DecodeError(
3159                         "expected OctetString encoded chunk",
3160                         klass=self.__class__,
3161                         decode_path=sub_decode_path,
3162                         offset=sub_offset,
3163                     )
3164                 chunks.append(chunk)
3165                 sub_offset += chunk.tlvlen
3166                 vlen += chunk.tlvlen
3167                 v = v_tail
3168             try:
3169                 obj = self.__class__(
3170                     value=b"".join(bytes(chunk) for chunk in chunks),
3171                     bounds=(self._bound_min, self._bound_max),
3172                     impl=self.tag,
3173                     expl=self._expl,
3174                     default=self.default,
3175                     optional=self.optional,
3176                     _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3177                 )
3178             except DecodeError as err:
3179                 raise DecodeError(
3180                     msg=err.msg,
3181                     klass=self.__class__,
3182                     decode_path=decode_path,
3183                     offset=offset,
3184                 )
3185             except BoundsError as err:
3186                 raise DecodeError(
3187                     msg=str(err),
3188                     klass=self.__class__,
3189                     decode_path=decode_path,
3190                     offset=offset,
3191                 )
3192             obj.lenindef = lenindef
3193             obj.ber_encoded = True
3194             return obj, (v[EOC_LEN:] if lenindef else v)
3195         raise TagMismatch(
3196             klass=self.__class__,
3197             decode_path=decode_path,
3198             offset=offset,
3199         )
3200
3201     def __repr__(self):
3202         return pp_console_row(next(self.pps()))
3203
3204     def pps(self, decode_path=()):
3205         yield _pp(
3206             obj=self,
3207             asn1_type_name=self.asn1_type_name,
3208             obj_name=self.__class__.__name__,
3209             decode_path=decode_path,
3210             value=("%d bytes" % len(self._value)) if self.ready else None,
3211             blob=self._value if self.ready else None,
3212             optional=self.optional,
3213             default=self == self.default,
3214             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3215             expl=None if self._expl is None else tag_decode(self._expl),
3216             offset=self.offset,
3217             tlen=self.tlen,
3218             llen=self.llen,
3219             vlen=self.vlen,
3220             expl_offset=self.expl_offset if self.expled else None,
3221             expl_tlen=self.expl_tlen if self.expled else None,
3222             expl_llen=self.expl_llen if self.expled else None,
3223             expl_vlen=self.expl_vlen if self.expled else None,
3224             expl_lenindef=self.expl_lenindef,
3225             lenindef=self.lenindef,
3226             ber_encoded=self.ber_encoded,
3227             bered=self.bered,
3228         )
3229         defined_by, defined = self.defined or (None, None)
3230         if defined_by is not None:
3231             yield defined.pps(
3232                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3233             )
3234         for pp in self.pps_lenindef(decode_path):
3235             yield pp
3236
3237
3238 NullState = namedtuple("NullState", (
3239     "version",
3240     "tag",
3241     "expl",
3242     "default",
3243     "optional",
3244     "offset",
3245     "llen",
3246     "vlen",
3247     "expl_lenindef",
3248     "lenindef",
3249     "ber_encoded",
3250 ))
3251
3252
3253 class Null(Obj):
3254     """``NULL`` null object
3255
3256     >>> n = Null()
3257     NULL
3258     >>> n.ready
3259     True
3260     """
3261     __slots__ = ()
3262     tag_default = tag_encode(5)
3263     asn1_type_name = "NULL"
3264
3265     def __init__(
3266             self,
3267             value=None,  # unused, but Sequence passes it
3268             impl=None,
3269             expl=None,
3270             optional=False,
3271             _decoded=(0, 0, 0),
3272     ):
3273         """
3274         :param bytes impl: override default tag with ``IMPLICIT`` one
3275         :param bytes expl: override default tag with ``EXPLICIT`` one
3276         :param bool optional: is object ``OPTIONAL`` in sequence
3277         """
3278         super(Null, self).__init__(impl, expl, None, optional, _decoded)
3279         self.default = None
3280
3281     @property
3282     def ready(self):
3283         return True
3284
3285     def __getstate__(self):
3286         return NullState(
3287             __version__,
3288             self.tag,
3289             self._expl,
3290             self.default,
3291             self.optional,
3292             self.offset,
3293             self.llen,
3294             self.vlen,
3295             self.expl_lenindef,
3296             self.lenindef,
3297             self.ber_encoded,
3298         )
3299
3300     def __setstate__(self, state):
3301         super(Null, self).__setstate__(state)
3302         self.tag = state.tag
3303         self._expl = state.expl
3304         self.default = state.default
3305         self.optional = state.optional
3306         self.offset = state.offset
3307         self.llen = state.llen
3308         self.vlen = state.vlen
3309         self.expl_lenindef = state.expl_lenindef
3310         self.lenindef = state.lenindef
3311         self.ber_encoded = state.ber_encoded
3312
3313     def __eq__(self, their):
3314         if not issubclass(their.__class__, Null):
3315             return False
3316         return (
3317             self.tag == their.tag and
3318             self._expl == their._expl
3319         )
3320
3321     def __call__(
3322             self,
3323             value=None,
3324             impl=None,
3325             expl=None,
3326             optional=None,
3327     ):
3328         return self.__class__(
3329             impl=self.tag if impl is None else impl,
3330             expl=self._expl if expl is None else expl,
3331             optional=self.optional if optional is None else optional,
3332         )
3333
3334     def _encode(self):
3335         return self.tag + len_encode(0)
3336
3337     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3338         try:
3339             t, _, lv = tag_strip(tlv)
3340         except DecodeError as err:
3341             raise err.__class__(
3342                 msg=err.msg,
3343                 klass=self.__class__,
3344                 decode_path=decode_path,
3345                 offset=offset,
3346             )
3347         if t != self.tag:
3348             raise TagMismatch(
3349                 klass=self.__class__,
3350                 decode_path=decode_path,
3351                 offset=offset,
3352             )
3353         if tag_only:  # pragma: no cover
3354             return None
3355         try:
3356             l, _, v = len_decode(lv)
3357         except DecodeError as err:
3358             raise err.__class__(
3359                 msg=err.msg,
3360                 klass=self.__class__,
3361                 decode_path=decode_path,
3362                 offset=offset,
3363             )
3364         if l != 0:
3365             raise InvalidLength(
3366                 "Null must have zero length",
3367                 klass=self.__class__,
3368                 decode_path=decode_path,
3369                 offset=offset,
3370             )
3371         obj = self.__class__(
3372             impl=self.tag,
3373             expl=self._expl,
3374             optional=self.optional,
3375             _decoded=(offset, 1, 0),
3376         )
3377         return obj, v
3378
3379     def __repr__(self):
3380         return pp_console_row(next(self.pps()))
3381
3382     def pps(self, decode_path=()):
3383         yield _pp(
3384             obj=self,
3385             asn1_type_name=self.asn1_type_name,
3386             obj_name=self.__class__.__name__,
3387             decode_path=decode_path,
3388             optional=self.optional,
3389             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3390             expl=None if self._expl is None else tag_decode(self._expl),
3391             offset=self.offset,
3392             tlen=self.tlen,
3393             llen=self.llen,
3394             vlen=self.vlen,
3395             expl_offset=self.expl_offset if self.expled else None,
3396             expl_tlen=self.expl_tlen if self.expled else None,
3397             expl_llen=self.expl_llen if self.expled else None,
3398             expl_vlen=self.expl_vlen if self.expled else None,
3399             expl_lenindef=self.expl_lenindef,
3400             bered=self.bered,
3401         )
3402         for pp in self.pps_lenindef(decode_path):
3403             yield pp
3404
3405
3406 ObjectIdentifierState = namedtuple("ObjectIdentifierState", (
3407     "version",
3408     "value",
3409     "tag",
3410     "expl",
3411     "default",
3412     "optional",
3413     "offset",
3414     "llen",
3415     "vlen",
3416     "expl_lenindef",
3417     "lenindef",
3418     "ber_encoded",
3419     "defines",
3420 ))
3421
3422
3423 class ObjectIdentifier(Obj):
3424     """``OBJECT IDENTIFIER`` OID type
3425
3426     >>> oid = ObjectIdentifier((1, 2, 3))
3427     OBJECT IDENTIFIER 1.2.3
3428     >>> oid == ObjectIdentifier("1.2.3")
3429     True
3430     >>> tuple(oid)
3431     (1, 2, 3)
3432     >>> str(oid)
3433     '1.2.3'
3434     >>> oid + (4, 5) + ObjectIdentifier("1.7")
3435     OBJECT IDENTIFIER 1.2.3.4.5.1.7
3436
3437     >>> str(ObjectIdentifier((3, 1)))
3438     Traceback (most recent call last):
3439     pyderasn.InvalidOID: unacceptable first arc value
3440     """
3441     __slots__ = ("defines",)
3442     tag_default = tag_encode(6)
3443     asn1_type_name = "OBJECT IDENTIFIER"
3444
3445     def __init__(
3446             self,
3447             value=None,
3448             defines=(),
3449             impl=None,
3450             expl=None,
3451             default=None,
3452             optional=False,
3453             _decoded=(0, 0, 0),
3454     ):
3455         """
3456         :param value: set the value. Either tuples of integers,
3457                       string of "."-concatenated integers, or
3458                       :py:class:`pyderasn.ObjectIdentifier` object
3459         :param defines: sequence of tuples. Each tuple has two elements.
3460                         First one is relative to current one decode
3461                         path, aiming to the field defined by that OID.
3462                         Read about relative path in
3463                         :py:func:`pyderasn.abs_decode_path`. Second
3464                         tuple element is ``{OID: pyderasn.Obj()}``
3465                         dictionary, mapping between current OID value
3466                         and structure applied to defined field.
3467                         :ref:`Read about DEFINED BY <definedby>`
3468         :param bytes impl: override default tag with ``IMPLICIT`` one
3469         :param bytes expl: override default tag with ``EXPLICIT`` one
3470         :param default: set default value. Type same as in ``value``
3471         :param bool optional: is object ``OPTIONAL`` in sequence
3472         """
3473         super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
3474         self._value = value
3475         if value is not None:
3476             self._value = self._value_sanitize(value)
3477         if default is not None:
3478             default = self._value_sanitize(default)
3479             self.default = self.__class__(
3480                 value=default,
3481                 impl=self.tag,
3482                 expl=self._expl,
3483             )
3484             if self._value is None:
3485                 self._value = default
3486         self.defines = defines
3487
3488     def __add__(self, their):
3489         if isinstance(their, self.__class__):
3490             return self.__class__(self._value + their._value)
3491         if isinstance(their, tuple):
3492             return self.__class__(self._value + their)
3493         raise InvalidValueType((self.__class__, tuple))
3494
3495     def _value_sanitize(self, value):
3496         if issubclass(value.__class__, ObjectIdentifier):
3497             return value._value
3498         if isinstance(value, string_types):
3499             try:
3500                 value = tuple(int(arc) for arc in value.split("."))
3501             except ValueError:
3502                 raise InvalidOID("unacceptable arcs values")
3503         if isinstance(value, tuple):
3504             if len(value) < 2:
3505                 raise InvalidOID("less than 2 arcs")
3506             first_arc = value[0]
3507             if first_arc in (0, 1):
3508                 if not (0 <= value[1] <= 39):
3509                     raise InvalidOID("second arc is too wide")
3510             elif first_arc == 2:
3511                 pass
3512             else:
3513                 raise InvalidOID("unacceptable first arc value")
3514             return value
3515         raise InvalidValueType((self.__class__, str, tuple))
3516
3517     @property
3518     def ready(self):
3519         return self._value is not None
3520
3521     def __getstate__(self):
3522         return ObjectIdentifierState(
3523             __version__,
3524             self._value,
3525             self.tag,
3526             self._expl,
3527             self.default,
3528             self.optional,
3529             self.offset,
3530             self.llen,
3531             self.vlen,
3532             self.expl_lenindef,
3533             self.lenindef,
3534             self.ber_encoded,
3535             self.defines,
3536         )
3537
3538     def __setstate__(self, state):
3539         super(ObjectIdentifier, self).__setstate__(state)
3540         self._value = state.value
3541         self.tag = state.tag
3542         self._expl = state.expl
3543         self.default = state.default
3544         self.optional = state.optional
3545         self.offset = state.offset
3546         self.llen = state.llen
3547         self.vlen = state.vlen
3548         self.expl_lenindef = state.expl_lenindef
3549         self.lenindef = state.lenindef
3550         self.ber_encoded = state.ber_encoded
3551         self.defines = state.defines
3552
3553     def __iter__(self):
3554         self._assert_ready()
3555         return iter(self._value)
3556
3557     def __str__(self):
3558         return ".".join(str(arc) for arc in self._value or ())
3559
3560     def __hash__(self):
3561         self._assert_ready()
3562         return hash(
3563             self.tag +
3564             bytes(self._expl or b"") +
3565             str(self._value).encode("ascii"),
3566         )
3567
3568     def __eq__(self, their):
3569         if isinstance(their, tuple):
3570             return self._value == their
3571         if not issubclass(their.__class__, ObjectIdentifier):
3572             return False
3573         return (
3574             self.tag == their.tag and
3575             self._expl == their._expl and
3576             self._value == their._value
3577         )
3578
3579     def __lt__(self, their):
3580         return self._value < their._value
3581
3582     def __call__(
3583             self,
3584             value=None,
3585             defines=None,
3586             impl=None,
3587             expl=None,
3588             default=None,
3589             optional=None,
3590     ):
3591         return self.__class__(
3592             value=value,
3593             defines=self.defines if defines is None else defines,
3594             impl=self.tag if impl is None else impl,
3595             expl=self._expl if expl is None else expl,
3596             default=self.default if default is None else default,
3597             optional=self.optional if optional is None else optional,
3598         )
3599
3600     def _encode(self):
3601         self._assert_ready()
3602         value = self._value
3603         first_value = value[1]
3604         first_arc = value[0]
3605         if first_arc == 0:
3606             pass
3607         elif first_arc == 1:
3608             first_value += 40
3609         elif first_arc == 2:
3610             first_value += 80
3611         else:  # pragma: no cover
3612             raise RuntimeError("invalid arc is stored")
3613         octets = [zero_ended_encode(first_value)]
3614         for arc in value[2:]:
3615             octets.append(zero_ended_encode(arc))
3616         v = b"".join(octets)
3617         return b"".join((self.tag, len_encode(len(v)), v))
3618
3619     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3620         try:
3621             t, _, lv = tag_strip(tlv)
3622         except DecodeError as err:
3623             raise err.__class__(
3624                 msg=err.msg,
3625                 klass=self.__class__,
3626                 decode_path=decode_path,
3627                 offset=offset,
3628             )
3629         if t != self.tag:
3630             raise TagMismatch(
3631                 klass=self.__class__,
3632                 decode_path=decode_path,
3633                 offset=offset,
3634             )
3635         if tag_only:  # pragma: no cover
3636             return None
3637         try:
3638             l, llen, v = len_decode(lv)
3639         except DecodeError as err:
3640             raise err.__class__(
3641                 msg=err.msg,
3642                 klass=self.__class__,
3643                 decode_path=decode_path,
3644                 offset=offset,
3645             )
3646         if l > len(v):
3647             raise NotEnoughData(
3648                 "encoded length is longer than data",
3649                 klass=self.__class__,
3650                 decode_path=decode_path,
3651                 offset=offset,
3652             )
3653         if l == 0:
3654             raise NotEnoughData(
3655                 "zero length",
3656                 klass=self.__class__,
3657                 decode_path=decode_path,
3658                 offset=offset,
3659             )
3660         v, tail = v[:l], v[l:]
3661         arcs = []
3662         ber_encoded = False
3663         while len(v) > 0:
3664             i = 0
3665             arc = 0
3666             while True:
3667                 octet = indexbytes(v, i)
3668                 if i == 0 and octet == 0x80:
3669                     if ctx.get("bered", False):
3670                         ber_encoded = True
3671                     else:
3672                         raise DecodeError("non normalized arc encoding")
3673                 arc = (arc << 7) | (octet & 0x7F)
3674                 if octet & 0x80 == 0:
3675                     arcs.append(arc)
3676                     v = v[i + 1:]
3677                     break
3678                 i += 1
3679                 if i == len(v):
3680                     raise DecodeError(
3681                         "unfinished OID",
3682                         klass=self.__class__,
3683                         decode_path=decode_path,
3684                         offset=offset,
3685                     )
3686         first_arc = 0
3687         second_arc = arcs[0]
3688         if 0 <= second_arc <= 39:
3689             first_arc = 0
3690         elif 40 <= second_arc <= 79:
3691             first_arc = 1
3692             second_arc -= 40
3693         else:
3694             first_arc = 2
3695             second_arc -= 80
3696         obj = self.__class__(
3697             value=tuple([first_arc, second_arc] + arcs[1:]),
3698             impl=self.tag,
3699             expl=self._expl,
3700             default=self.default,
3701             optional=self.optional,
3702             _decoded=(offset, llen, l),
3703         )
3704         if ber_encoded:
3705             obj.ber_encoded = True
3706         return obj, tail
3707
3708     def __repr__(self):
3709         return pp_console_row(next(self.pps()))
3710
3711     def pps(self, decode_path=()):
3712         yield _pp(
3713             obj=self,
3714             asn1_type_name=self.asn1_type_name,
3715             obj_name=self.__class__.__name__,
3716             decode_path=decode_path,
3717             value=str(self) if self.ready else None,
3718             optional=self.optional,
3719             default=self == self.default,
3720             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3721             expl=None if self._expl is None else tag_decode(self._expl),
3722             offset=self.offset,
3723             tlen=self.tlen,
3724             llen=self.llen,
3725             vlen=self.vlen,
3726             expl_offset=self.expl_offset if self.expled else None,
3727             expl_tlen=self.expl_tlen if self.expled else None,
3728             expl_llen=self.expl_llen if self.expled else None,
3729             expl_vlen=self.expl_vlen if self.expled else None,
3730             expl_lenindef=self.expl_lenindef,
3731             ber_encoded=self.ber_encoded,
3732             bered=self.bered,
3733         )
3734         for pp in self.pps_lenindef(decode_path):
3735             yield pp
3736
3737
3738 class Enumerated(Integer):
3739     """``ENUMERATED`` integer type
3740
3741     This type is identical to :py:class:`pyderasn.Integer`, but requires
3742     schema to be specified and does not accept values missing from it.
3743     """
3744     __slots__ = ()
3745     tag_default = tag_encode(10)
3746     asn1_type_name = "ENUMERATED"
3747
3748     def __init__(
3749             self,
3750             value=None,
3751             impl=None,
3752             expl=None,
3753             default=None,
3754             optional=False,
3755             _specs=None,
3756             _decoded=(0, 0, 0),
3757             bounds=None,  # dummy argument, workability for Integer.decode
3758     ):
3759         super(Enumerated, self).__init__(
3760             value, bounds, impl, expl, default, optional, _specs, _decoded,
3761         )
3762         if len(self.specs) == 0:
3763             raise ValueError("schema must be specified")
3764
3765     def _value_sanitize(self, value):
3766         if isinstance(value, self.__class__):
3767             value = value._value
3768         elif isinstance(value, integer_types):
3769             for _value in itervalues(self.specs):
3770                 if _value == value:
3771                     break
3772             else:
3773                 raise DecodeError(
3774                     "unknown integer value: %s" % value,
3775                     klass=self.__class__,
3776                 )
3777         elif isinstance(value, string_types):
3778             value = self.specs.get(value)
3779             if value is None:
3780                 raise ObjUnknown("integer value: %s" % value)
3781         else:
3782             raise InvalidValueType((self.__class__, int, str))
3783         return value
3784
3785     def __call__(
3786             self,
3787             value=None,
3788             impl=None,
3789             expl=None,
3790             default=None,
3791             optional=None,
3792             _specs=None,
3793     ):
3794         return self.__class__(
3795             value=value,
3796             impl=self.tag if impl is None else impl,
3797             expl=self._expl if expl is None else expl,
3798             default=self.default if default is None else default,
3799             optional=self.optional if optional is None else optional,
3800             _specs=self.specs,
3801         )
3802
3803
3804 def escape_control_unicode(c):
3805     if unicat(c).startswith("C"):
3806         c = repr(c).lstrip("u").strip("'")
3807     return c
3808
3809
3810 class CommonString(OctetString):
3811     """Common class for all strings
3812
3813     Everything resembles :py:class:`pyderasn.OctetString`, except
3814     ability to deal with unicode text strings.
3815
3816     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
3817     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3818     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
3819     True
3820     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
3821     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
3822     >>> str(s)
3823     'привет Ð¼Ð¸Ñ€'
3824     >>> hexenc(bytes(s))
3825     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3826
3827     >>> PrintableString("привет Ð¼Ð¸Ñ€")
3828     Traceback (most recent call last):
3829     pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3830
3831     >>> BMPString("ада", bounds=(2, 2))
3832     Traceback (most recent call last):
3833     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3834     >>> s = BMPString("ад", bounds=(2, 2))
3835     >>> s.encoding
3836     'utf-16-be'
3837     >>> hexenc(bytes(s))
3838     '04300434'
3839
3840     .. list-table::
3841        :header-rows: 1
3842
3843        * - Class
3844          - Text Encoding
3845        * - :py:class:`pyderasn.UTF8String`
3846          - utf-8
3847        * - :py:class:`pyderasn.NumericString`
3848          - ascii
3849        * - :py:class:`pyderasn.PrintableString`
3850          - ascii
3851        * - :py:class:`pyderasn.TeletexString`
3852          - ascii
3853        * - :py:class:`pyderasn.T61String`
3854          - ascii
3855        * - :py:class:`pyderasn.VideotexString`
3856          - iso-8859-1
3857        * - :py:class:`pyderasn.IA5String`
3858          - ascii
3859        * - :py:class:`pyderasn.GraphicString`
3860          - iso-8859-1
3861        * - :py:class:`pyderasn.VisibleString`
3862          - ascii
3863        * - :py:class:`pyderasn.ISO646String`
3864          - ascii
3865        * - :py:class:`pyderasn.GeneralString`
3866          - iso-8859-1
3867        * - :py:class:`pyderasn.UniversalString`
3868          - utf-32-be
3869        * - :py:class:`pyderasn.BMPString`
3870          - utf-16-be
3871     """
3872     __slots__ = ()
3873
3874     def _value_sanitize(self, value):
3875         value_raw = None
3876         value_decoded = None
3877         if isinstance(value, self.__class__):
3878             value_raw = value._value
3879         elif isinstance(value, text_type):
3880             value_decoded = value
3881         elif isinstance(value, binary_type):
3882             value_raw = value
3883         else:
3884             raise InvalidValueType((self.__class__, text_type, binary_type))
3885         try:
3886             value_raw = (
3887                 value_decoded.encode(self.encoding)
3888                 if value_raw is None else value_raw
3889             )
3890             value_decoded = (
3891                 value_raw.decode(self.encoding)
3892                 if value_decoded is None else value_decoded
3893             )
3894         except (UnicodeEncodeError, UnicodeDecodeError) as err:
3895             raise DecodeError(str(err))
3896         if not self._bound_min <= len(value_decoded) <= self._bound_max:
3897             raise BoundsError(
3898                 self._bound_min,
3899                 len(value_decoded),
3900                 self._bound_max,
3901             )
3902         return value_raw
3903
3904     def __eq__(self, their):
3905         if isinstance(their, binary_type):
3906             return self._value == their
3907         if isinstance(their, text_type):
3908             return self._value == their.encode(self.encoding)
3909         if not isinstance(their, self.__class__):
3910             return False
3911         return (
3912             self._value == their._value and
3913             self.tag == their.tag and
3914             self._expl == their._expl
3915         )
3916
3917     def __unicode__(self):
3918         if self.ready:
3919             return self._value.decode(self.encoding)
3920         return text_type(self._value)
3921
3922     def __repr__(self):
3923         return pp_console_row(next(self.pps(no_unicode=PY2)))
3924
3925     def pps(self, decode_path=(), no_unicode=False):
3926         value = None
3927         if self.ready:
3928             value = (
3929                 hexenc(bytes(self)) if no_unicode else
3930                 "".join(escape_control_unicode(c) for c in self.__unicode__())
3931             )
3932         yield _pp(
3933             obj=self,
3934             asn1_type_name=self.asn1_type_name,
3935             obj_name=self.__class__.__name__,
3936             decode_path=decode_path,
3937             value=value,
3938             optional=self.optional,
3939             default=self == self.default,
3940             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3941             expl=None if self._expl is None else tag_decode(self._expl),
3942             offset=self.offset,
3943             tlen=self.tlen,
3944             llen=self.llen,
3945             vlen=self.vlen,
3946             expl_offset=self.expl_offset if self.expled else None,
3947             expl_tlen=self.expl_tlen if self.expled else None,
3948             expl_llen=self.expl_llen if self.expled else None,
3949             expl_vlen=self.expl_vlen if self.expled else None,
3950             expl_lenindef=self.expl_lenindef,
3951             ber_encoded=self.ber_encoded,
3952             bered=self.bered,
3953         )
3954         for pp in self.pps_lenindef(decode_path):
3955             yield pp
3956
3957
3958 class UTF8String(CommonString):
3959     __slots__ = ()
3960     tag_default = tag_encode(12)
3961     encoding = "utf-8"
3962     asn1_type_name = "UTF8String"
3963
3964
3965 class AllowableCharsMixin(object):
3966     @property
3967     def allowable_chars(self):
3968         if PY2:
3969             return self._allowable_chars
3970         return frozenset(six_unichr(c) for c in self._allowable_chars)
3971
3972
3973 class NumericString(AllowableCharsMixin, CommonString):
3974     """Numeric string
3975
3976     Its value is properly sanitized: only ASCII digits with spaces can
3977     be stored.
3978
3979     >>> NumericString().allowable_chars
3980     frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
3981     """
3982     __slots__ = ()
3983     tag_default = tag_encode(18)
3984     encoding = "ascii"
3985     asn1_type_name = "NumericString"
3986     _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3987
3988     def _value_sanitize(self, value):
3989         value = super(NumericString, self)._value_sanitize(value)
3990         if not frozenset(value) <= self._allowable_chars:
3991             raise DecodeError("non-numeric value")
3992         return value
3993
3994
3995 PrintableStringState = namedtuple(
3996     "PrintableStringState",
3997     OctetStringState._fields + ("allowable_chars",),
3998 )
3999
4000
4001 class PrintableString(AllowableCharsMixin, CommonString):
4002     """Printable string
4003
4004     Its value is properly sanitized: see X.680 41.4 table 10.
4005
4006     >>> PrintableString().allowable_chars
4007     frozenset([' ', "'", ..., 'z'])
4008     >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4009     PrintableString PrintableString foo*bar
4010     >>> obj.allow_asterisk, obj.allow_ampersand
4011     (True, False)
4012     """
4013     __slots__ = ()
4014     tag_default = tag_encode(19)
4015     encoding = "ascii"
4016     asn1_type_name = "PrintableString"
4017     _allowable_chars = frozenset(
4018         (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4019     )
4020     _asterisk = frozenset("*".encode("ascii"))
4021     _ampersand = frozenset("&".encode("ascii"))
4022
4023     def __init__(
4024             self,
4025             value=None,
4026             bounds=None,
4027             impl=None,
4028             expl=None,
4029             default=None,
4030             optional=False,
4031             _decoded=(0, 0, 0),
4032             allow_asterisk=False,
4033             allow_ampersand=False,
4034     ):
4035         """
4036         :param allow_asterisk: allow asterisk character
4037         :param allow_ampersand: allow ampersand character
4038         """
4039         if allow_asterisk:
4040             self._allowable_chars |= self._asterisk
4041         if allow_ampersand:
4042             self._allowable_chars |= self._ampersand
4043         super(PrintableString, self).__init__(
4044             value, bounds, impl, expl, default, optional, _decoded,
4045         )
4046
4047     @property
4048     def allow_asterisk(self):
4049         """Is asterisk character allowed?
4050         """
4051         return self._asterisk <= self._allowable_chars
4052
4053     @property
4054     def allow_ampersand(self):
4055         """Is ampersand character allowed?
4056         """
4057         return self._ampersand <= self._allowable_chars
4058
4059     def _value_sanitize(self, value):
4060         value = super(PrintableString, self)._value_sanitize(value)
4061         if not frozenset(value) <= self._allowable_chars:
4062             raise DecodeError("non-printable value")
4063         return value
4064
4065     def __getstate__(self):
4066         return PrintableStringState(
4067             *super(PrintableString, self).__getstate__(),
4068             **{"allowable_chars": self._allowable_chars}
4069         )
4070
4071     def __setstate__(self, state):
4072         super(PrintableString, self).__setstate__(state)
4073         self._allowable_chars = state.allowable_chars
4074
4075     def __call__(
4076             self,
4077             value=None,
4078             bounds=None,
4079             impl=None,
4080             expl=None,
4081             default=None,
4082             optional=None,
4083     ):
4084         return self.__class__(
4085             value=value,
4086             bounds=(
4087                 (self._bound_min, self._bound_max)
4088                 if bounds is None else bounds
4089             ),
4090             impl=self.tag if impl is None else impl,
4091             expl=self._expl if expl is None else expl,
4092             default=self.default if default is None else default,
4093             optional=self.optional if optional is None else optional,
4094             allow_asterisk=self.allow_asterisk,
4095             allow_ampersand=self.allow_ampersand,
4096         )
4097
4098
4099 class TeletexString(CommonString):
4100     __slots__ = ()
4101     tag_default = tag_encode(20)
4102     encoding = "ascii"
4103     asn1_type_name = "TeletexString"
4104
4105
4106 class T61String(TeletexString):
4107     __slots__ = ()
4108     asn1_type_name = "T61String"
4109
4110
4111 class VideotexString(CommonString):
4112     __slots__ = ()
4113     tag_default = tag_encode(21)
4114     encoding = "iso-8859-1"
4115     asn1_type_name = "VideotexString"
4116
4117
4118 class IA5String(CommonString):
4119     __slots__ = ()
4120     tag_default = tag_encode(22)
4121     encoding = "ascii"
4122     asn1_type_name = "IA5"
4123
4124
4125 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4126 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4127 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4128
4129
4130 class UTCTime(CommonString):
4131     """``UTCTime`` datetime type
4132
4133     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4134     UTCTime UTCTime 2017-09-30T22:07:50
4135     >>> str(t)
4136     '170930220750Z'
4137     >>> bytes(t)
4138     b'170930220750Z'
4139     >>> t.todatetime()
4140     datetime.datetime(2017, 9, 30, 22, 7, 50)
4141     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
4142     datetime.datetime(1957, 9, 30, 22, 7, 50)
4143
4144     .. warning::
4145
4146        BER encoding is unsupported.
4147     """
4148     __slots__ = ()
4149     tag_default = tag_encode(23)
4150     encoding = "ascii"
4151     asn1_type_name = "UTCTime"
4152
4153     def __init__(
4154             self,
4155             value=None,
4156             impl=None,
4157             expl=None,
4158             default=None,
4159             optional=False,
4160             _decoded=(0, 0, 0),
4161             bounds=None,  # dummy argument, workability for OctetString.decode
4162     ):
4163         """
4164         :param value: set the value. Either datetime type, or
4165                       :py:class:`pyderasn.UTCTime` object
4166         :param bytes impl: override default tag with ``IMPLICIT`` one
4167         :param bytes expl: override default tag with ``EXPLICIT`` one
4168         :param default: set default value. Type same as in ``value``
4169         :param bool optional: is object ``OPTIONAL`` in sequence
4170         """
4171         super(UTCTime, self).__init__(
4172             None, None, impl, expl, default, optional, _decoded,
4173         )
4174         self._value = value
4175         if value is not None:
4176             self._value = self._value_sanitize(value)
4177         if default is not None:
4178             default = self._value_sanitize(default)
4179             self.default = self.__class__(
4180                 value=default,
4181                 impl=self.tag,
4182                 expl=self._expl,
4183             )
4184             if self._value is None:
4185                 self._value = default
4186
4187     def _strptime(self, value):
4188         # datetime.strptime's format: %y%m%d%H%M%SZ
4189         if len(value) != LEN_YYMMDDHHMMSSZ:
4190             raise ValueError("invalid UTCTime length")
4191         if value[-1] != "Z":
4192             raise ValueError("non UTC timezone")
4193         return datetime(
4194             2000 + int(value[:2]),  # %y
4195             int(value[2:4]),  # %m
4196             int(value[4:6]),  # %d
4197             int(value[6:8]),  # %H
4198             int(value[8:10]),  # %M
4199             int(value[10:12]),  # %S
4200         )
4201
4202     def _value_sanitize(self, value):
4203         if isinstance(value, binary_type):
4204             try:
4205                 value_decoded = value.decode("ascii")
4206             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4207                 raise DecodeError("invalid UTCTime encoding: %r" % err)
4208             try:
4209                 self._strptime(value_decoded)
4210             except (TypeError, ValueError) as err:
4211                 raise DecodeError("invalid UTCTime format: %r" % err)
4212             return value
4213         if isinstance(value, self.__class__):
4214             return value._value
4215         if isinstance(value, datetime):
4216             return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
4217         raise InvalidValueType((self.__class__, datetime))
4218
4219     def __eq__(self, their):
4220         if isinstance(their, binary_type):
4221             return self._value == their
4222         if isinstance(their, datetime):
4223             return self.todatetime() == their
4224         if not isinstance(their, self.__class__):
4225             return False
4226         return (
4227             self._value == their._value and
4228             self.tag == their.tag and
4229             self._expl == their._expl
4230         )
4231
4232     def todatetime(self):
4233         """Convert to datetime
4234
4235         :returns: datetime
4236
4237         Pay attention that UTCTime can not hold full year, so all years
4238         having < 50 years are treated as 20xx, 19xx otherwise, according
4239         to X.509 recomendation.
4240         """
4241         value = self._strptime(self._value.decode("ascii"))
4242         year = value.year % 100
4243         return datetime(
4244             year=(2000 + year) if year < 50 else (1900 + year),
4245             month=value.month,
4246             day=value.day,
4247             hour=value.hour,
4248             minute=value.minute,
4249             second=value.second,
4250         )
4251
4252     def __repr__(self):
4253         return pp_console_row(next(self.pps()))
4254
4255     def pps(self, decode_path=()):
4256         yield _pp(
4257             obj=self,
4258             asn1_type_name=self.asn1_type_name,
4259             obj_name=self.__class__.__name__,
4260             decode_path=decode_path,
4261             value=self.todatetime().isoformat() if self.ready else None,
4262             optional=self.optional,
4263             default=self == self.default,
4264             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4265             expl=None if self._expl is None else tag_decode(self._expl),
4266             offset=self.offset,
4267             tlen=self.tlen,
4268             llen=self.llen,
4269             vlen=self.vlen,
4270             expl_offset=self.expl_offset if self.expled else None,
4271             expl_tlen=self.expl_tlen if self.expled else None,
4272             expl_llen=self.expl_llen if self.expled else None,
4273             expl_vlen=self.expl_vlen if self.expled else None,
4274             expl_lenindef=self.expl_lenindef,
4275             ber_encoded=self.ber_encoded,
4276             bered=self.bered,
4277         )
4278         for pp in self.pps_lenindef(decode_path):
4279             yield pp
4280
4281
4282 class GeneralizedTime(UTCTime):
4283     """``GeneralizedTime`` datetime type
4284
4285     This type is similar to :py:class:`pyderasn.UTCTime`.
4286
4287     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4288     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
4289     >>> str(t)
4290     '20170930220750.000123Z'
4291     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
4292     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
4293
4294     .. warning::
4295
4296        BER encoding is unsupported.
4297
4298     .. warning::
4299
4300        Only microsecond fractions are supported.
4301        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
4302        higher precision values.
4303
4304     .. warning::
4305
4306        Zero year is unsupported.
4307     """
4308     __slots__ = ()
4309     tag_default = tag_encode(24)
4310     asn1_type_name = "GeneralizedTime"
4311
4312     def _strptime(self, value):
4313         l = len(value)
4314         if l == LEN_YYYYMMDDHHMMSSZ:
4315             # datetime.strptime's format: %Y%m%d%H%M%SZ
4316             if value[-1] != "Z":
4317                 raise ValueError("non UTC timezone")
4318             return datetime(
4319                 int(value[:4]),  # %Y
4320                 int(value[4:6]),  # %m
4321                 int(value[6:8]),  # %d
4322                 int(value[8:10]),  # %H
4323                 int(value[10:12]),  # %M
4324                 int(value[12:14]),  # %S
4325             )
4326         if l >= LEN_YYYYMMDDHHMMSSDMZ:
4327             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4328             if value[-1] != "Z":
4329                 raise ValueError("non UTC timezone")
4330             if value[14] != ".":
4331                 raise ValueError("no fractions separator")
4332             us = value[15:-1]
4333             if us[-1] == "0":
4334                 raise ValueError("trailing zero")
4335             us_len = len(us)
4336             if us_len > 6:
4337                 raise ValueError("only microsecond fractions are supported")
4338             us = int(us + ("0" * (6 - us_len)))
4339             decoded = datetime(
4340                 int(value[:4]),  # %Y
4341                 int(value[4:6]),  # %m
4342                 int(value[6:8]),  # %d
4343                 int(value[8:10]),  # %H
4344                 int(value[10:12]),  # %M
4345                 int(value[12:14]),  # %S
4346                 us,  # %f
4347             )
4348             return decoded
4349         raise ValueError("invalid GeneralizedTime length")
4350
4351     def _value_sanitize(self, value):
4352         if isinstance(value, binary_type):
4353             try:
4354                 value_decoded = value.decode("ascii")
4355             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4356                 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
4357             try:
4358                 self._strptime(value_decoded)
4359             except (TypeError, ValueError) as err:
4360                 raise DecodeError(
4361                     "invalid GeneralizedTime format: %r" % err,
4362                     klass=self.__class__,
4363                 )
4364             return value
4365         if isinstance(value, self.__class__):
4366             return value._value
4367         if isinstance(value, datetime):
4368             encoded = value.strftime("%Y%m%d%H%M%S")
4369             if value.microsecond > 0:
4370                 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4371             return (encoded + "Z").encode("ascii")
4372         raise InvalidValueType((self.__class__, datetime))
4373
4374     def todatetime(self):
4375         return self._strptime(self._value.decode("ascii"))
4376
4377
4378 class GraphicString(CommonString):
4379     __slots__ = ()
4380     tag_default = tag_encode(25)
4381     encoding = "iso-8859-1"
4382     asn1_type_name = "GraphicString"
4383
4384
4385 class VisibleString(CommonString):
4386     __slots__ = ()
4387     tag_default = tag_encode(26)
4388     encoding = "ascii"
4389     asn1_type_name = "VisibleString"
4390
4391
4392 class ISO646String(VisibleString):
4393     __slots__ = ()
4394     asn1_type_name = "ISO646String"
4395
4396
4397 class GeneralString(CommonString):
4398     __slots__ = ()
4399     tag_default = tag_encode(27)
4400     encoding = "iso-8859-1"
4401     asn1_type_name = "GeneralString"
4402
4403
4404 class UniversalString(CommonString):
4405     __slots__ = ()
4406     tag_default = tag_encode(28)
4407     encoding = "utf-32-be"
4408     asn1_type_name = "UniversalString"
4409
4410
4411 class BMPString(CommonString):
4412     __slots__ = ()
4413     tag_default = tag_encode(30)
4414     encoding = "utf-16-be"
4415     asn1_type_name = "BMPString"
4416
4417
4418 ChoiceState = namedtuple("ChoiceState", (
4419     "version",
4420     "specs",
4421     "value",
4422     "tag",
4423     "expl",
4424     "default",
4425     "optional",
4426     "offset",
4427     "llen",
4428     "vlen",
4429     "expl_lenindef",
4430     "lenindef",
4431     "ber_encoded",
4432 ))
4433
4434
4435 class Choice(Obj):
4436     """``CHOICE`` special type
4437
4438     ::
4439
4440         class GeneralName(Choice):
4441             schema = (
4442                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4443                 ("dNSName", IA5String(impl=tag_ctxp(2))),
4444             )
4445
4446     >>> gn = GeneralName()
4447     GeneralName CHOICE
4448     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4449     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4450     >>> gn["dNSName"] = IA5String("bar.baz")
4451     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4452     >>> gn["rfc822Name"]
4453     None
4454     >>> gn["dNSName"]
4455     [2] IA5String IA5 bar.baz
4456     >>> gn.choice
4457     'dNSName'
4458     >>> gn.value == gn["dNSName"]
4459     True
4460     >>> gn.specs
4461     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4462
4463     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4464     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4465     """
4466     __slots__ = ("specs",)
4467     tag_default = None
4468     asn1_type_name = "CHOICE"
4469
4470     def __init__(
4471             self,
4472             value=None,
4473             schema=None,
4474             impl=None,
4475             expl=None,
4476             default=None,
4477             optional=False,
4478             _decoded=(0, 0, 0),
4479     ):
4480         """
4481         :param value: set the value. Either ``(choice, value)`` tuple, or
4482                       :py:class:`pyderasn.Choice` object
4483         :param bytes impl: can not be set, do **not** use it
4484         :param bytes expl: override default tag with ``EXPLICIT`` one
4485         :param default: set default value. Type same as in ``value``
4486         :param bool optional: is object ``OPTIONAL`` in sequence
4487         """
4488         if impl is not None:
4489             raise ValueError("no implicit tag allowed for CHOICE")
4490         super(Choice, self).__init__(None, expl, default, optional, _decoded)
4491         if schema is None:
4492             schema = getattr(self, "schema", ())
4493         if len(schema) == 0:
4494             raise ValueError("schema must be specified")
4495         self.specs = (
4496             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4497         )
4498         self._value = None
4499         if value is not None:
4500             self._value = self._value_sanitize(value)
4501         if default is not None:
4502             default_value = self._value_sanitize(default)
4503             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4504             default_obj.specs = self.specs
4505             default_obj._value = default_value
4506             self.default = default_obj
4507             if value is None:
4508                 self._value = copy(default_obj._value)
4509
4510     def _value_sanitize(self, value):
4511         if isinstance(value, tuple) and len(value) == 2:
4512             choice, obj = value
4513             spec = self.specs.get(choice)
4514             if spec is None:
4515                 raise ObjUnknown(choice)
4516             if not isinstance(obj, spec.__class__):
4517                 raise InvalidValueType((spec,))
4518             return (choice, spec(obj))
4519         if isinstance(value, self.__class__):
4520             return value._value
4521         raise InvalidValueType((self.__class__, tuple))
4522
4523     @property
4524     def ready(self):
4525         return self._value is not None and self._value[1].ready
4526
4527     @property
4528     def bered(self):
4529         return self.expl_lenindef or (
4530             (self._value is not None) and
4531             self._value[1].bered
4532         )
4533
4534     def __getstate__(self):
4535         return ChoiceState(
4536             __version__,
4537             self.specs,
4538             copy(self._value),
4539             self.tag,
4540             self._expl,
4541             self.default,
4542             self.optional,
4543             self.offset,
4544             self.llen,
4545             self.vlen,
4546             self.expl_lenindef,
4547             self.lenindef,
4548             self.ber_encoded,
4549         )
4550
4551     def __setstate__(self, state):
4552         super(Choice, self).__setstate__(state)
4553         self.specs = state.specs
4554         self._value = state.value
4555         self._expl = state.expl
4556         self.default = state.default
4557         self.optional = state.optional
4558         self.offset = state.offset
4559         self.llen = state.llen
4560         self.vlen = state.vlen
4561         self.expl_lenindef = state.expl_lenindef
4562         self.lenindef = state.lenindef
4563         self.ber_encoded = state.ber_encoded
4564
4565     def __eq__(self, their):
4566         if isinstance(their, tuple) and len(their) == 2:
4567             return self._value == their
4568         if not isinstance(their, self.__class__):
4569             return False
4570         return (
4571             self.specs == their.specs and
4572             self._value == their._value
4573         )
4574
4575     def __call__(
4576             self,
4577             value=None,
4578             expl=None,
4579             default=None,
4580             optional=None,
4581     ):
4582         return self.__class__(
4583             value=value,
4584             schema=self.specs,
4585             expl=self._expl if expl is None else expl,
4586             default=self.default if default is None else default,
4587             optional=self.optional if optional is None else optional,
4588         )
4589
4590     @property
4591     def choice(self):
4592         self._assert_ready()
4593         return self._value[0]
4594
4595     @property
4596     def value(self):
4597         self._assert_ready()
4598         return self._value[1]
4599
4600     def __getitem__(self, key):
4601         if key not in self.specs:
4602             raise ObjUnknown(key)
4603         if self._value is None:
4604             return None
4605         choice, value = self._value
4606         if choice != key:
4607             return None
4608         return value
4609
4610     def __setitem__(self, key, value):
4611         spec = self.specs.get(key)
4612         if spec is None:
4613             raise ObjUnknown(key)
4614         if not isinstance(value, spec.__class__):
4615             raise InvalidValueType((spec.__class__,))
4616         self._value = (key, spec(value))
4617
4618     @property
4619     def tlen(self):
4620         return 0
4621
4622     @property
4623     def decoded(self):
4624         return self._value[1].decoded if self.ready else False
4625
4626     def _encode(self):
4627         self._assert_ready()
4628         return self._value[1].encode()
4629
4630     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4631         for choice, spec in iteritems(self.specs):
4632             sub_decode_path = decode_path + (choice,)
4633             try:
4634                 spec.decode(
4635                     tlv,
4636                     offset=offset,
4637                     leavemm=True,
4638                     decode_path=sub_decode_path,
4639                     ctx=ctx,
4640                     tag_only=True,
4641                     _ctx_immutable=False,
4642                 )
4643             except TagMismatch:
4644                 continue
4645             break
4646         else:
4647             raise TagMismatch(
4648                 klass=self.__class__,
4649                 decode_path=decode_path,
4650                 offset=offset,
4651             )
4652         if tag_only:  # pragma: no cover
4653             return None
4654         value, tail = spec.decode(
4655             tlv,
4656             offset=offset,
4657             leavemm=True,
4658             decode_path=sub_decode_path,
4659             ctx=ctx,
4660             _ctx_immutable=False,
4661         )
4662         obj = self.__class__(
4663             schema=self.specs,
4664             expl=self._expl,
4665             default=self.default,
4666             optional=self.optional,
4667             _decoded=(offset, 0, value.fulllen),
4668         )
4669         obj._value = (choice, value)
4670         return obj, tail
4671
4672     def __repr__(self):
4673         value = pp_console_row(next(self.pps()))
4674         if self.ready:
4675             value = "%s[%r]" % (value, self.value)
4676         return value
4677
4678     def pps(self, decode_path=()):
4679         yield _pp(
4680             obj=self,
4681             asn1_type_name=self.asn1_type_name,
4682             obj_name=self.__class__.__name__,
4683             decode_path=decode_path,
4684             value=self.choice if self.ready else None,
4685             optional=self.optional,
4686             default=self == self.default,
4687             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4688             expl=None if self._expl is None else tag_decode(self._expl),
4689             offset=self.offset,
4690             tlen=self.tlen,
4691             llen=self.llen,
4692             vlen=self.vlen,
4693             expl_lenindef=self.expl_lenindef,
4694             bered=self.bered,
4695         )
4696         if self.ready:
4697             yield self.value.pps(decode_path=decode_path + (self.choice,))
4698         for pp in self.pps_lenindef(decode_path):
4699             yield pp
4700
4701
4702 class PrimitiveTypes(Choice):
4703     """Predefined ``CHOICE`` for all generic primitive types
4704
4705     It could be useful for general decoding of some unspecified values:
4706
4707     >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
4708     OCTET STRING 3 bytes 666f6f
4709     >>> PrimitiveTypes().decod(hexdec("0203123456")).value
4710     INTEGER 1193046
4711     """
4712     __slots__ = ()
4713     schema = tuple((klass.__name__, klass()) for klass in (
4714         Boolean,
4715         Integer,
4716         BitString,
4717         OctetString,
4718         Null,
4719         ObjectIdentifier,
4720         UTF8String,
4721         NumericString,
4722         PrintableString,
4723         TeletexString,
4724         VideotexString,
4725         IA5String,
4726         UTCTime,
4727         GeneralizedTime,
4728         GraphicString,
4729         VisibleString,
4730         ISO646String,
4731         GeneralString,
4732         UniversalString,
4733         BMPString,
4734     ))
4735
4736
4737 AnyState = namedtuple("AnyState", (
4738     "version",
4739     "value",
4740     "tag",
4741     "expl",
4742     "optional",
4743     "offset",
4744     "llen",
4745     "vlen",
4746     "expl_lenindef",
4747     "lenindef",
4748     "ber_encoded",
4749     "defined",
4750 ))
4751
4752
4753 class Any(Obj):
4754     """``ANY`` special type
4755
4756     >>> Any(Integer(-123))
4757     ANY 020185
4758     >>> a = Any(OctetString(b"hello world").encode())
4759     ANY 040b68656c6c6f20776f726c64
4760     >>> hexenc(bytes(a))
4761     b'0x040x0bhello world'
4762     """
4763     __slots__ = ("defined",)
4764     tag_default = tag_encode(0)
4765     asn1_type_name = "ANY"
4766
4767     def __init__(
4768             self,
4769             value=None,
4770             expl=None,
4771             optional=False,
4772             _decoded=(0, 0, 0),
4773     ):
4774         """
4775         :param value: set the value. Either any kind of pyderasn's
4776                       **ready** object, or bytes. Pay attention that
4777                       **no** validation is performed is raw binary value
4778                       is valid TLV
4779         :param bytes expl: override default tag with ``EXPLICIT`` one
4780         :param bool optional: is object ``OPTIONAL`` in sequence
4781         """
4782         super(Any, self).__init__(None, expl, None, optional, _decoded)
4783         self._value = None if value is None else self._value_sanitize(value)
4784         self.defined = None
4785
4786     def _value_sanitize(self, value):
4787         if isinstance(value, binary_type):
4788             return value
4789         if isinstance(value, self.__class__):
4790             return value._value
4791         if isinstance(value, Obj):
4792             return value.encode()
4793         raise InvalidValueType((self.__class__, Obj, binary_type))
4794
4795     @property
4796     def ready(self):
4797         return self._value is not None
4798
4799     @property
4800     def bered(self):
4801         if self.expl_lenindef or self.lenindef:
4802             return True
4803         if self.defined is None:
4804             return False
4805         return self.defined[1].bered
4806
4807     def __getstate__(self):
4808         return AnyState(
4809             __version__,
4810             self._value,
4811             self.tag,
4812             self._expl,
4813             self.optional,
4814             self.offset,
4815             self.llen,
4816             self.vlen,
4817             self.expl_lenindef,
4818             self.lenindef,
4819             self.ber_encoded,
4820             self.defined,
4821         )
4822
4823     def __setstate__(self, state):
4824         super(Any, self).__setstate__(state)
4825         self._value = state.value
4826         self.tag = state.tag
4827         self._expl = state.expl
4828         self.optional = state.optional
4829         self.offset = state.offset
4830         self.llen = state.llen
4831         self.vlen = state.vlen
4832         self.expl_lenindef = state.expl_lenindef
4833         self.lenindef = state.lenindef
4834         self.ber_encoded = state.ber_encoded
4835         self.defined = state.defined
4836
4837     def __eq__(self, their):
4838         if isinstance(their, binary_type):
4839             return self._value == their
4840         if issubclass(their.__class__, Any):
4841             return self._value == their._value
4842         return False
4843
4844     def __call__(
4845             self,
4846             value=None,
4847             expl=None,
4848             optional=None,
4849     ):
4850         return self.__class__(
4851             value=value,
4852             expl=self._expl if expl is None else expl,
4853             optional=self.optional if optional is None else optional,
4854         )
4855
4856     def __bytes__(self):
4857         self._assert_ready()
4858         return self._value
4859
4860     @property
4861     def tlen(self):
4862         return 0
4863
4864     def _encode(self):
4865         self._assert_ready()
4866         return self._value
4867
4868     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4869         try:
4870             t, tlen, lv = tag_strip(tlv)
4871         except DecodeError as err:
4872             raise err.__class__(
4873                 msg=err.msg,
4874                 klass=self.__class__,
4875                 decode_path=decode_path,
4876                 offset=offset,
4877             )
4878         try:
4879             l, llen, v = len_decode(lv)
4880         except LenIndefForm as err:
4881             if not ctx.get("bered", False):
4882                 raise err.__class__(
4883                     msg=err.msg,
4884                     klass=self.__class__,
4885                     decode_path=decode_path,
4886                     offset=offset,
4887                 )
4888             llen, vlen, v = 1, 0, lv[1:]
4889             sub_offset = offset + tlen + llen
4890             chunk_i = 0
4891             while v[:EOC_LEN].tobytes() != EOC:
4892                 chunk, v = Any().decode(
4893                     v,
4894                     offset=sub_offset,
4895                     decode_path=decode_path + (str(chunk_i),),
4896                     leavemm=True,
4897                     ctx=ctx,
4898                     _ctx_immutable=False,
4899                 )
4900                 vlen += chunk.tlvlen
4901                 sub_offset += chunk.tlvlen
4902                 chunk_i += 1
4903             tlvlen = tlen + llen + vlen + EOC_LEN
4904             obj = self.__class__(
4905                 value=tlv[:tlvlen].tobytes(),
4906                 expl=self._expl,
4907                 optional=self.optional,
4908                 _decoded=(offset, 0, tlvlen),
4909             )
4910             obj.lenindef = True
4911             obj.tag = t.tobytes()
4912             return obj, v[EOC_LEN:]
4913         except DecodeError as err:
4914             raise err.__class__(
4915                 msg=err.msg,
4916                 klass=self.__class__,
4917                 decode_path=decode_path,
4918                 offset=offset,
4919             )
4920         if l > len(v):
4921             raise NotEnoughData(
4922                 "encoded length is longer than data",
4923                 klass=self.__class__,
4924                 decode_path=decode_path,
4925                 offset=offset,
4926             )
4927         tlvlen = tlen + llen + l
4928         v, tail = tlv[:tlvlen], v[l:]
4929         obj = self.__class__(
4930             value=v.tobytes(),
4931             expl=self._expl,
4932             optional=self.optional,
4933             _decoded=(offset, 0, tlvlen),
4934         )
4935         obj.tag = t.tobytes()
4936         return obj, tail
4937
4938     def __repr__(self):
4939         return pp_console_row(next(self.pps()))
4940
4941     def pps(self, decode_path=()):
4942         yield _pp(
4943             obj=self,
4944             asn1_type_name=self.asn1_type_name,
4945             obj_name=self.__class__.__name__,
4946             decode_path=decode_path,
4947             blob=self._value if self.ready else None,
4948             optional=self.optional,
4949             default=self == self.default,
4950             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4951             expl=None if self._expl is None else tag_decode(self._expl),
4952             offset=self.offset,
4953             tlen=self.tlen,
4954             llen=self.llen,
4955             vlen=self.vlen,
4956             expl_offset=self.expl_offset if self.expled else None,
4957             expl_tlen=self.expl_tlen if self.expled else None,
4958             expl_llen=self.expl_llen if self.expled else None,
4959             expl_vlen=self.expl_vlen if self.expled else None,
4960             expl_lenindef=self.expl_lenindef,
4961             lenindef=self.lenindef,
4962             bered=self.bered,
4963         )
4964         defined_by, defined = self.defined or (None, None)
4965         if defined_by is not None:
4966             yield defined.pps(
4967                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4968             )
4969         for pp in self.pps_lenindef(decode_path):
4970             yield pp
4971
4972
4973 ########################################################################
4974 # ASN.1 constructed types
4975 ########################################################################
4976
4977 def get_def_by_path(defines_by_path, sub_decode_path):
4978     """Get define by decode path
4979     """
4980     for path, define in defines_by_path:
4981         if len(path) != len(sub_decode_path):
4982             continue
4983         for p1, p2 in zip(path, sub_decode_path):
4984             if (p1 != any) and (p1 != p2):
4985                 break
4986         else:
4987             return define
4988
4989
4990 def abs_decode_path(decode_path, rel_path):
4991     """Create an absolute decode path from current and relative ones
4992
4993     :param decode_path: current decode path, starting point. Tuple of strings
4994     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4995                      If first tuple's element is "/", then treat it as
4996                      an absolute path, ignoring ``decode_path`` as
4997                      starting point. Also this tuple can contain ".."
4998                      elements, stripping the leading element from
4999                      ``decode_path``
5000
5001     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
5002     ("foo", "bar", "baz", "whatever")
5003     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
5004     ("foo", "whatever")
5005     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
5006     ("baz", "whatever")
5007     """
5008     if rel_path[0] == "/":
5009         return rel_path[1:]
5010     if rel_path[0] == "..":
5011         return abs_decode_path(decode_path[:-1], rel_path[1:])
5012     return decode_path + rel_path
5013
5014
5015 SequenceState = namedtuple("SequenceState", (
5016     "version",
5017     "specs",
5018     "value",
5019     "tag",
5020     "expl",
5021     "default",
5022     "optional",
5023     "offset",
5024     "llen",
5025     "vlen",
5026     "expl_lenindef",
5027     "lenindef",
5028     "ber_encoded",
5029 ))
5030
5031
5032 class Sequence(Obj):
5033     """``SEQUENCE`` structure type
5034
5035     You have to make specification of sequence::
5036
5037         class Extension(Sequence):
5038             schema = (
5039                 ("extnID", ObjectIdentifier()),
5040                 ("critical", Boolean(default=False)),
5041                 ("extnValue", OctetString()),
5042             )
5043
5044     Then, you can work with it as with dictionary.
5045
5046     >>> ext = Extension()
5047     >>> Extension().specs
5048     OrderedDict([
5049         ('extnID', OBJECT IDENTIFIER),
5050         ('critical', BOOLEAN False OPTIONAL DEFAULT),
5051         ('extnValue', OCTET STRING),
5052     ])
5053     >>> ext["extnID"] = "1.2.3"
5054     Traceback (most recent call last):
5055     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5056     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5057
5058     You can determine if sequence is ready to be encoded:
5059
5060     >>> ext.ready
5061     False
5062     >>> ext.encode()
5063     Traceback (most recent call last):
5064     pyderasn.ObjNotReady: object is not ready: extnValue
5065     >>> ext["extnValue"] = OctetString(b"foobar")
5066     >>> ext.ready
5067     True
5068
5069     Value you want to assign, must have the same **type** as in
5070     corresponding specification, but it can have different tags,
5071     optional/default attributes -- they will be taken from specification
5072     automatically::
5073
5074         class TBSCertificate(Sequence):
5075             schema = (
5076                 ("version", Version(expl=tag_ctxc(0), default="v1")),
5077             [...]
5078
5079     >>> tbs = TBSCertificate()
5080     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5081
5082     Assign ``None`` to remove value from sequence.
5083
5084     You can set values in Sequence during its initialization:
5085
5086     >>> AlgorithmIdentifier((
5087         ("algorithm", ObjectIdentifier("1.2.3")),
5088         ("parameters", Any(Null()))
5089     ))
5090     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5091
5092     You can determine if value exists/set in the sequence and take its value:
5093
5094     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5095     (True, True, False)
5096     >>> ext["extnID"]
5097     OBJECT IDENTIFIER 1.2.3
5098
5099     But pay attention that if value has default, then it won't be (not
5100     in) in the sequence (because ``DEFAULT`` must not be encoded in
5101     DER), but you can read its value:
5102
5103     >>> "critical" in ext, ext["critical"]
5104     (False, BOOLEAN False)
5105     >>> ext["critical"] = Boolean(True)
5106     >>> "critical" in ext, ext["critical"]
5107     (True, BOOLEAN True)
5108
5109     All defaulted values are always optional.
5110
5111     .. _allow_default_values_ctx:
5112
5113     DER prohibits default value encoding and will raise an error if
5114     default value is unexpectedly met during decode.
5115     If :ref:`bered <bered_ctx>` context option is set, then no error
5116     will be raised, but ``bered`` attribute set. You can disable strict
5117     defaulted values existence validation by setting
5118     ``"allow_default_values": True`` :ref:`context <ctx>` option.
5119
5120     Two sequences are equal if they have equal specification (schema),
5121     implicit/explicit tagging and the same values.
5122     """
5123     __slots__ = ("specs",)
5124     tag_default = tag_encode(form=TagFormConstructed, num=16)
5125     asn1_type_name = "SEQUENCE"
5126
5127     def __init__(
5128             self,
5129             value=None,
5130             schema=None,
5131             impl=None,
5132             expl=None,
5133             default=None,
5134             optional=False,
5135             _decoded=(0, 0, 0),
5136     ):
5137         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5138         if schema is None:
5139             schema = getattr(self, "schema", ())
5140         self.specs = (
5141             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
5142         )
5143         self._value = {}
5144         if value is not None:
5145             if issubclass(value.__class__, Sequence):
5146                 self._value = value._value
5147             elif hasattr(value, "__iter__"):
5148                 for seq_key, seq_value in value:
5149                     self[seq_key] = seq_value
5150             else:
5151                 raise InvalidValueType((Sequence,))
5152         if default is not None:
5153             if not issubclass(default.__class__, Sequence):
5154                 raise InvalidValueType((Sequence,))
5155             default_value = default._value
5156             default_obj = self.__class__(impl=self.tag, expl=self._expl)
5157             default_obj.specs = self.specs
5158             default_obj._value = default_value
5159             self.default = default_obj
5160             if value is None:
5161                 self._value = copy(default_obj._value)
5162
5163     @property
5164     def ready(self):
5165         for name, spec in iteritems(self.specs):
5166             value = self._value.get(name)
5167             if value is None:
5168                 if spec.optional:
5169                     continue
5170                 return False
5171             if not value.ready:
5172                 return False
5173         return True
5174
5175     @property
5176     def bered(self):
5177         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5178             return True
5179         return any(value.bered for value in itervalues(self._value))
5180
5181     def __getstate__(self):
5182         return SequenceState(
5183             __version__,
5184             self.specs,
5185             {k: copy(v) for k, v in iteritems(self._value)},
5186             self.tag,
5187             self._expl,
5188             self.default,
5189             self.optional,
5190             self.offset,
5191             self.llen,
5192             self.vlen,
5193             self.expl_lenindef,
5194             self.lenindef,
5195             self.ber_encoded,
5196         )
5197
5198     def __setstate__(self, state):
5199         super(Sequence, self).__setstate__(state)
5200         self.specs = state.specs
5201         self._value = state.value
5202         self.tag = state.tag
5203         self._expl = state.expl
5204         self.default = state.default
5205         self.optional = state.optional
5206         self.offset = state.offset
5207         self.llen = state.llen
5208         self.vlen = state.vlen
5209         self.expl_lenindef = state.expl_lenindef
5210         self.lenindef = state.lenindef
5211         self.ber_encoded = state.ber_encoded
5212
5213     def __eq__(self, their):
5214         if not isinstance(their, self.__class__):
5215             return False
5216         return (
5217             self.specs == their.specs and
5218             self.tag == their.tag and
5219             self._expl == their._expl and
5220             self._value == their._value
5221         )
5222
5223     def __call__(
5224             self,
5225             value=None,
5226             impl=None,
5227             expl=None,
5228             default=None,
5229             optional=None,
5230     ):
5231         return self.__class__(
5232             value=value,
5233             schema=self.specs,
5234             impl=self.tag if impl is None else impl,
5235             expl=self._expl if expl is None else expl,
5236             default=self.default if default is None else default,
5237             optional=self.optional if optional is None else optional,
5238         )
5239
5240     def __contains__(self, key):
5241         return key in self._value
5242
5243     def __setitem__(self, key, value):
5244         spec = self.specs.get(key)
5245         if spec is None:
5246             raise ObjUnknown(key)
5247         if value is None:
5248             self._value.pop(key, None)
5249             return
5250         if not isinstance(value, spec.__class__):
5251             raise InvalidValueType((spec.__class__,))
5252         value = spec(value=value)
5253         if spec.default is not None and value == spec.default:
5254             self._value.pop(key, None)
5255             return
5256         self._value[key] = value
5257
5258     def __getitem__(self, key):
5259         value = self._value.get(key)
5260         if value is not None:
5261             return value
5262         spec = self.specs.get(key)
5263         if spec is None:
5264             raise ObjUnknown(key)
5265         if spec.default is not None:
5266             return spec.default
5267         return None
5268
5269     def _encoded_values(self):
5270         raws = []
5271         for name, spec in iteritems(self.specs):
5272             value = self._value.get(name)
5273             if value is None:
5274                 if spec.optional:
5275                     continue
5276                 raise ObjNotReady(name)
5277             raws.append(value.encode())
5278         return raws
5279
5280     def _encode(self):
5281         v = b"".join(self._encoded_values())
5282         return b"".join((self.tag, len_encode(len(v)), v))
5283
5284     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5285         try:
5286             t, tlen, lv = tag_strip(tlv)
5287         except DecodeError as err:
5288             raise err.__class__(
5289                 msg=err.msg,
5290                 klass=self.__class__,
5291                 decode_path=decode_path,
5292                 offset=offset,
5293             )
5294         if t != self.tag:
5295             raise TagMismatch(
5296                 klass=self.__class__,
5297                 decode_path=decode_path,
5298                 offset=offset,
5299             )
5300         if tag_only:  # pragma: no cover
5301             return None
5302         lenindef = False
5303         ctx_bered = ctx.get("bered", False)
5304         try:
5305             l, llen, v = len_decode(lv)
5306         except LenIndefForm as err:
5307             if not ctx_bered:
5308                 raise err.__class__(
5309                     msg=err.msg,
5310                     klass=self.__class__,
5311                     decode_path=decode_path,
5312                     offset=offset,
5313                 )
5314             l, llen, v = 0, 1, lv[1:]
5315             lenindef = True
5316         except DecodeError as err:
5317             raise err.__class__(
5318                 msg=err.msg,
5319                 klass=self.__class__,
5320                 decode_path=decode_path,
5321                 offset=offset,
5322             )
5323         if l > len(v):
5324             raise NotEnoughData(
5325                 "encoded length is longer than data",
5326                 klass=self.__class__,
5327                 decode_path=decode_path,
5328                 offset=offset,
5329             )
5330         if not lenindef:
5331             v, tail = v[:l], v[l:]
5332         vlen = 0
5333         sub_offset = offset + tlen + llen
5334         values = {}
5335         ber_encoded = False
5336         ctx_allow_default_values = ctx.get("allow_default_values", False)
5337         for name, spec in iteritems(self.specs):
5338             if spec.optional and (
5339                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5340                     len(v) == 0
5341             ):
5342                 continue
5343             sub_decode_path = decode_path + (name,)
5344             try:
5345                 value, v_tail = spec.decode(
5346                     v,
5347                     sub_offset,
5348                     leavemm=True,
5349                     decode_path=sub_decode_path,
5350                     ctx=ctx,
5351                     _ctx_immutable=False,
5352                 )
5353             except TagMismatch as err:
5354                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5355                     continue
5356                 raise
5357
5358             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5359             if defined is not None:
5360                 defined_by, defined_spec = defined
5361                 if issubclass(value.__class__, SequenceOf):
5362                     for i, _value in enumerate(value):
5363                         sub_sub_decode_path = sub_decode_path + (
5364                             str(i),
5365                             DecodePathDefBy(defined_by),
5366                         )
5367                         defined_value, defined_tail = defined_spec.decode(
5368                             memoryview(bytes(_value)),
5369                             sub_offset + (
5370                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5371                                 if value.expled else (value.tlen + value.llen)
5372                             ),
5373                             leavemm=True,
5374                             decode_path=sub_sub_decode_path,
5375                             ctx=ctx,
5376                             _ctx_immutable=False,
5377                         )
5378                         if len(defined_tail) > 0:
5379                             raise DecodeError(
5380                                 "remaining data",
5381                                 klass=self.__class__,
5382                                 decode_path=sub_sub_decode_path,
5383                                 offset=offset,
5384                             )
5385                         _value.defined = (defined_by, defined_value)
5386                 else:
5387                     defined_value, defined_tail = defined_spec.decode(
5388                         memoryview(bytes(value)),
5389                         sub_offset + (
5390                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5391                             if value.expled else (value.tlen + value.llen)
5392                         ),
5393                         leavemm=True,
5394                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5395                         ctx=ctx,
5396                         _ctx_immutable=False,
5397                     )
5398                     if len(defined_tail) > 0:
5399                         raise DecodeError(
5400                             "remaining data",
5401                             klass=self.__class__,
5402                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5403                             offset=offset,
5404                         )
5405                     value.defined = (defined_by, defined_value)
5406
5407             value_len = value.fulllen
5408             vlen += value_len
5409             sub_offset += value_len
5410             v = v_tail
5411             if spec.default is not None and value == spec.default:
5412                 if ctx_bered or ctx_allow_default_values:
5413                     ber_encoded = True
5414                 else:
5415                     raise DecodeError(
5416                         "DEFAULT value met",
5417                         klass=self.__class__,
5418                         decode_path=sub_decode_path,
5419                         offset=sub_offset,
5420                     )
5421             values[name] = value
5422
5423             spec_defines = getattr(spec, "defines", ())
5424             if len(spec_defines) == 0:
5425                 defines_by_path = ctx.get("defines_by_path", ())
5426                 if len(defines_by_path) > 0:
5427                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5428             if spec_defines is not None and len(spec_defines) > 0:
5429                 for rel_path, schema in spec_defines:
5430                     defined = schema.get(value, None)
5431                     if defined is not None:
5432                         ctx.setdefault("_defines", []).append((
5433                             abs_decode_path(sub_decode_path[:-1], rel_path),
5434                             (value, defined),
5435                         ))
5436         if lenindef:
5437             if v[:EOC_LEN].tobytes() != EOC:
5438                 raise DecodeError(
5439                     "no EOC",
5440                     klass=self.__class__,
5441                     decode_path=decode_path,
5442                     offset=offset,
5443                 )
5444             tail = v[EOC_LEN:]
5445             vlen += EOC_LEN
5446         elif len(v) > 0:
5447             raise DecodeError(
5448                 "remaining data",
5449                 klass=self.__class__,
5450                 decode_path=decode_path,
5451                 offset=offset,
5452             )
5453         obj = self.__class__(
5454             schema=self.specs,
5455             impl=self.tag,
5456             expl=self._expl,
5457             default=self.default,
5458             optional=self.optional,
5459             _decoded=(offset, llen, vlen),
5460         )
5461         obj._value = values
5462         obj.lenindef = lenindef
5463         obj.ber_encoded = ber_encoded
5464         return obj, tail
5465
5466     def __repr__(self):
5467         value = pp_console_row(next(self.pps()))
5468         cols = []
5469         for name in self.specs:
5470             _value = self._value.get(name)
5471             if _value is None:
5472                 continue
5473             cols.append("%s: %s" % (name, repr(_value)))
5474         return "%s[%s]" % (value, "; ".join(cols))
5475
5476     def pps(self, decode_path=()):
5477         yield _pp(
5478             obj=self,
5479             asn1_type_name=self.asn1_type_name,
5480             obj_name=self.__class__.__name__,
5481             decode_path=decode_path,
5482             optional=self.optional,
5483             default=self == self.default,
5484             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5485             expl=None if self._expl is None else tag_decode(self._expl),
5486             offset=self.offset,
5487             tlen=self.tlen,
5488             llen=self.llen,
5489             vlen=self.vlen,
5490             expl_offset=self.expl_offset if self.expled else None,
5491             expl_tlen=self.expl_tlen if self.expled else None,
5492             expl_llen=self.expl_llen if self.expled else None,
5493             expl_vlen=self.expl_vlen if self.expled else None,
5494             expl_lenindef=self.expl_lenindef,
5495             lenindef=self.lenindef,
5496             ber_encoded=self.ber_encoded,
5497             bered=self.bered,
5498         )
5499         for name in self.specs:
5500             value = self._value.get(name)
5501             if value is None:
5502                 continue
5503             yield value.pps(decode_path=decode_path + (name,))
5504         for pp in self.pps_lenindef(decode_path):
5505             yield pp
5506
5507
5508 class Set(Sequence):
5509     """``SET`` structure type
5510
5511     Its usage is identical to :py:class:`pyderasn.Sequence`.
5512
5513     .. _allow_unordered_set_ctx:
5514
5515     DER prohibits unordered values encoding and will raise an error
5516     during decode. If If :ref:`bered <bered_ctx>` context option is set,
5517     then no error will occure. Also you can disable strict values
5518     ordering check by setting ``"allow_unordered_set": True``
5519     :ref:`context <ctx>` option.
5520     """
5521     __slots__ = ()
5522     tag_default = tag_encode(form=TagFormConstructed, num=17)
5523     asn1_type_name = "SET"
5524
5525     def _encode(self):
5526         raws = self._encoded_values()
5527         raws.sort()
5528         v = b"".join(raws)
5529         return b"".join((self.tag, len_encode(len(v)), v))
5530
5531     def _specs_items(self):
5532         return iteritems(self.specs)
5533
5534     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5535         try:
5536             t, tlen, lv = tag_strip(tlv)
5537         except DecodeError as err:
5538             raise err.__class__(
5539                 msg=err.msg,
5540                 klass=self.__class__,
5541                 decode_path=decode_path,
5542                 offset=offset,
5543             )
5544         if t != self.tag:
5545             raise TagMismatch(
5546                 klass=self.__class__,
5547                 decode_path=decode_path,
5548                 offset=offset,
5549             )
5550         if tag_only:
5551             return None
5552         lenindef = False
5553         ctx_bered = ctx.get("bered", False)
5554         try:
5555             l, llen, v = len_decode(lv)
5556         except LenIndefForm as err:
5557             if not ctx_bered:
5558                 raise err.__class__(
5559                     msg=err.msg,
5560                     klass=self.__class__,
5561                     decode_path=decode_path,
5562                     offset=offset,
5563                 )
5564             l, llen, v = 0, 1, lv[1:]
5565             lenindef = True
5566         except DecodeError as err:
5567             raise err.__class__(
5568                 msg=err.msg,
5569                 klass=self.__class__,
5570                 decode_path=decode_path,
5571                 offset=offset,
5572             )
5573         if l > len(v):
5574             raise NotEnoughData(
5575                 "encoded length is longer than data",
5576                 klass=self.__class__,
5577                 offset=offset,
5578             )
5579         if not lenindef:
5580             v, tail = v[:l], v[l:]
5581         vlen = 0
5582         sub_offset = offset + tlen + llen
5583         values = {}
5584         ber_encoded = False
5585         ctx_allow_default_values = ctx.get("allow_default_values", False)
5586         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5587         value_prev = memoryview(v[:0])
5588
5589         while len(v) > 0:
5590             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5591                 break
5592             for name, spec in self._specs_items():
5593                 sub_decode_path = decode_path + (name,)
5594                 try:
5595                     spec.decode(
5596                         v,
5597                         sub_offset,
5598                         leavemm=True,
5599                         decode_path=sub_decode_path,
5600                         ctx=ctx,
5601                         tag_only=True,
5602                         _ctx_immutable=False,
5603                     )
5604                 except TagMismatch:
5605                     continue
5606                 break
5607             else:
5608                 raise TagMismatch(
5609                     klass=self.__class__,
5610                     decode_path=decode_path,
5611                     offset=offset,
5612                 )
5613             value, v_tail = spec.decode(
5614                 v,
5615                 sub_offset,
5616                 leavemm=True,
5617                 decode_path=sub_decode_path,
5618                 ctx=ctx,
5619                 _ctx_immutable=False,
5620             )
5621             value_len = value.fulllen
5622             if value_prev.tobytes() > v[:value_len].tobytes():
5623                 if ctx_bered or ctx_allow_unordered_set:
5624                     ber_encoded = True
5625                 else:
5626                     raise DecodeError(
5627                         "unordered " + self.asn1_type_name,
5628                         klass=self.__class__,
5629                         decode_path=sub_decode_path,
5630                         offset=sub_offset,
5631                     )
5632             if spec.default is None or value != spec.default:
5633                 pass
5634             elif ctx_bered or ctx_allow_default_values:
5635                 ber_encoded = True
5636             else:
5637                 raise DecodeError(
5638                     "DEFAULT value met",
5639                     klass=self.__class__,
5640                     decode_path=sub_decode_path,
5641                     offset=sub_offset,
5642                 )
5643             values[name] = value
5644             value_prev = v[:value_len]
5645             sub_offset += value_len
5646             vlen += value_len
5647             v = v_tail
5648         obj = self.__class__(
5649             schema=self.specs,
5650             impl=self.tag,
5651             expl=self._expl,
5652             default=self.default,
5653             optional=self.optional,
5654             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5655         )
5656         if lenindef:
5657             if v[:EOC_LEN].tobytes() != EOC:
5658                 raise DecodeError(
5659                     "no EOC",
5660                     klass=self.__class__,
5661                     decode_path=decode_path,
5662                     offset=offset,
5663                 )
5664             tail = v[EOC_LEN:]
5665             obj.lenindef = True
5666         obj._value = values
5667         if not obj.ready:
5668             raise DecodeError(
5669                 "not all values are ready",
5670                 klass=self.__class__,
5671                 decode_path=decode_path,
5672                 offset=offset,
5673             )
5674         obj.ber_encoded = ber_encoded
5675         return obj, tail
5676
5677
5678 SequenceOfState = namedtuple("SequenceOfState", (
5679     "version",
5680     "spec",
5681     "value",
5682     "bound_min",
5683     "bound_max",
5684     "tag",
5685     "expl",
5686     "default",
5687     "optional",
5688     "offset",
5689     "llen",
5690     "vlen",
5691     "expl_lenindef",
5692     "lenindef",
5693     "ber_encoded",
5694 ))
5695
5696
5697 class SequenceOf(Obj):
5698     """``SEQUENCE OF`` sequence type
5699
5700     For that kind of type you must specify the object it will carry on
5701     (bounds are for example here, not required)::
5702
5703         class Ints(SequenceOf):
5704             schema = Integer()
5705             bounds = (0, 2)
5706
5707     >>> ints = Ints()
5708     >>> ints.append(Integer(123))
5709     >>> ints.append(Integer(234))
5710     >>> ints
5711     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5712     >>> [int(i) for i in ints]
5713     [123, 234]
5714     >>> ints.append(Integer(345))
5715     Traceback (most recent call last):
5716     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5717     >>> ints[1]
5718     INTEGER 234
5719     >>> ints[1] = Integer(345)
5720     >>> ints
5721     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5722
5723     Also you can initialize sequence with preinitialized values:
5724
5725     >>> ints = Ints([Integer(123), Integer(234)])
5726     """
5727     __slots__ = ("spec", "_bound_min", "_bound_max")
5728     tag_default = tag_encode(form=TagFormConstructed, num=16)
5729     asn1_type_name = "SEQUENCE OF"
5730
5731     def __init__(
5732             self,
5733             value=None,
5734             schema=None,
5735             bounds=None,
5736             impl=None,
5737             expl=None,
5738             default=None,
5739             optional=False,
5740             _decoded=(0, 0, 0),
5741     ):
5742         super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
5743         if schema is None:
5744             schema = getattr(self, "schema", None)
5745         if schema is None:
5746             raise ValueError("schema must be specified")
5747         self.spec = schema
5748         self._bound_min, self._bound_max = getattr(
5749             self,
5750             "bounds",
5751             (0, float("+inf")),
5752         ) if bounds is None else bounds
5753         self._value = []
5754         if value is not None:
5755             self._value = self._value_sanitize(value)
5756         if default is not None:
5757             default_value = self._value_sanitize(default)
5758             default_obj = self.__class__(
5759                 schema=schema,
5760                 impl=self.tag,
5761                 expl=self._expl,
5762             )
5763             default_obj._value = default_value
5764             self.default = default_obj
5765             if value is None:
5766                 self._value = copy(default_obj._value)
5767
5768     def _value_sanitize(self, value):
5769         if issubclass(value.__class__, SequenceOf):
5770             value = value._value
5771         elif hasattr(value, "__iter__"):
5772             value = list(value)
5773         else:
5774             raise InvalidValueType((self.__class__, iter))
5775         if not self._bound_min <= len(value) <= self._bound_max:
5776             raise BoundsError(self._bound_min, len(value), self._bound_max)
5777         for v in value:
5778             if not isinstance(v, self.spec.__class__):
5779                 raise InvalidValueType((self.spec.__class__,))
5780         return value
5781
5782     @property
5783     def ready(self):
5784         return all(v.ready for v in self._value)
5785
5786     @property
5787     def bered(self):
5788         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5789             return True
5790         return any(v.bered for v in self._value)
5791
5792     def __getstate__(self):
5793         return SequenceOfState(
5794             __version__,
5795             self.spec,
5796             [copy(v) for v in self._value],
5797             self._bound_min,
5798             self._bound_max,
5799             self.tag,
5800             self._expl,
5801             self.default,
5802             self.optional,
5803             self.offset,
5804             self.llen,
5805             self.vlen,
5806             self.expl_lenindef,
5807             self.lenindef,
5808             self.ber_encoded,
5809         )
5810
5811     def __setstate__(self, state):
5812         super(SequenceOf, self).__setstate__(state)
5813         self.spec = state.spec
5814         self._value = state.value
5815         self._bound_min = state.bound_min
5816         self._bound_max = state.bound_max
5817         self.tag = state.tag
5818         self._expl = state.expl
5819         self.default = state.default
5820         self.optional = state.optional
5821         self.offset = state.offset
5822         self.llen = state.llen
5823         self.vlen = state.vlen
5824         self.expl_lenindef = state.expl_lenindef
5825         self.lenindef = state.lenindef
5826         self.ber_encoded = state.ber_encoded
5827
5828     def __eq__(self, their):
5829         if isinstance(their, self.__class__):
5830             return (
5831                 self.spec == their.spec and
5832                 self.tag == their.tag and
5833                 self._expl == their._expl and
5834                 self._value == their._value
5835             )
5836         if hasattr(their, "__iter__"):
5837             return self._value == list(their)
5838         return False
5839
5840     def __call__(
5841             self,
5842             value=None,
5843             bounds=None,
5844             impl=None,
5845             expl=None,
5846             default=None,
5847             optional=None,
5848     ):
5849         return self.__class__(
5850             value=value,
5851             schema=self.spec,
5852             bounds=(
5853                 (self._bound_min, self._bound_max)
5854                 if bounds is None else bounds
5855             ),
5856             impl=self.tag if impl is None else impl,
5857             expl=self._expl if expl is None else expl,
5858             default=self.default if default is None else default,
5859             optional=self.optional if optional is None else optional,
5860         )
5861
5862     def __contains__(self, key):
5863         return key in self._value
5864
5865     def append(self, value):
5866         if not isinstance(value, self.spec.__class__):
5867             raise InvalidValueType((self.spec.__class__,))
5868         if len(self._value) + 1 > self._bound_max:
5869             raise BoundsError(
5870                 self._bound_min,
5871                 len(self._value) + 1,
5872                 self._bound_max,
5873             )
5874         self._value.append(value)
5875
5876     def __iter__(self):
5877         self._assert_ready()
5878         return iter(self._value)
5879
5880     def __len__(self):
5881         self._assert_ready()
5882         return len(self._value)
5883
5884     def __setitem__(self, key, value):
5885         if not isinstance(value, self.spec.__class__):
5886             raise InvalidValueType((self.spec.__class__,))
5887         self._value[key] = self.spec(value=value)
5888
5889     def __getitem__(self, key):
5890         return self._value[key]
5891
5892     def _encoded_values(self):
5893         return [v.encode() for v in self._value]
5894
5895     def _encode(self):
5896         v = b"".join(self._encoded_values())
5897         return b"".join((self.tag, len_encode(len(v)), v))
5898
5899     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5900         try:
5901             t, tlen, lv = tag_strip(tlv)
5902         except DecodeError as err:
5903             raise err.__class__(
5904                 msg=err.msg,
5905                 klass=self.__class__,
5906                 decode_path=decode_path,
5907                 offset=offset,
5908             )
5909         if t != self.tag:
5910             raise TagMismatch(
5911                 klass=self.__class__,
5912                 decode_path=decode_path,
5913                 offset=offset,
5914             )
5915         if tag_only:
5916             return None
5917         lenindef = False
5918         ctx_bered = ctx.get("bered", False)
5919         try:
5920             l, llen, v = len_decode(lv)
5921         except LenIndefForm as err:
5922             if not ctx_bered:
5923                 raise err.__class__(
5924                     msg=err.msg,
5925                     klass=self.__class__,
5926                     decode_path=decode_path,
5927                     offset=offset,
5928                 )
5929             l, llen, v = 0, 1, lv[1:]
5930             lenindef = True
5931         except DecodeError as err:
5932             raise err.__class__(
5933                 msg=err.msg,
5934                 klass=self.__class__,
5935                 decode_path=decode_path,
5936                 offset=offset,
5937             )
5938         if l > len(v):
5939             raise NotEnoughData(
5940                 "encoded length is longer than data",
5941                 klass=self.__class__,
5942                 decode_path=decode_path,
5943                 offset=offset,
5944             )
5945         if not lenindef:
5946             v, tail = v[:l], v[l:]
5947         vlen = 0
5948         sub_offset = offset + tlen + llen
5949         _value = []
5950         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5951         value_prev = memoryview(v[:0])
5952         ber_encoded = False
5953         spec = self.spec
5954         while len(v) > 0:
5955             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5956                 break
5957             sub_decode_path = decode_path + (str(len(_value)),)
5958             value, v_tail = spec.decode(
5959                 v,
5960                 sub_offset,
5961                 leavemm=True,
5962                 decode_path=sub_decode_path,
5963                 ctx=ctx,
5964                 _ctx_immutable=False,
5965             )
5966             value_len = value.fulllen
5967             if ordering_check:
5968                 if value_prev.tobytes() > v[:value_len].tobytes():
5969                     if ctx_bered or ctx_allow_unordered_set:
5970                         ber_encoded = True
5971                     else:
5972                         raise DecodeError(
5973                             "unordered " + self.asn1_type_name,
5974                             klass=self.__class__,
5975                             decode_path=sub_decode_path,
5976                             offset=sub_offset,
5977                         )
5978                 value_prev = v[:value_len]
5979             _value.append(value)
5980             sub_offset += value_len
5981             vlen += value_len
5982             v = v_tail
5983         try:
5984             obj = self.__class__(
5985                 value=_value,
5986                 schema=spec,
5987                 bounds=(self._bound_min, self._bound_max),
5988                 impl=self.tag,
5989                 expl=self._expl,
5990                 default=self.default,
5991                 optional=self.optional,
5992                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5993             )
5994         except BoundsError as err:
5995             raise DecodeError(
5996                 msg=str(err),
5997                 klass=self.__class__,
5998                 decode_path=decode_path,
5999                 offset=offset,
6000             )
6001         if lenindef:
6002             if v[:EOC_LEN].tobytes() != EOC:
6003                 raise DecodeError(
6004                     "no EOC",
6005                     klass=self.__class__,
6006                     decode_path=decode_path,
6007                     offset=offset,
6008                 )
6009             obj.lenindef = True
6010             tail = v[EOC_LEN:]
6011         obj.ber_encoded = ber_encoded
6012         return obj, tail
6013
6014     def __repr__(self):
6015         return "%s[%s]" % (
6016             pp_console_row(next(self.pps())),
6017             ", ".join(repr(v) for v in self._value),
6018         )
6019
6020     def pps(self, decode_path=()):
6021         yield _pp(
6022             obj=self,
6023             asn1_type_name=self.asn1_type_name,
6024             obj_name=self.__class__.__name__,
6025             decode_path=decode_path,
6026             optional=self.optional,
6027             default=self == self.default,
6028             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6029             expl=None if self._expl is None else tag_decode(self._expl),
6030             offset=self.offset,
6031             tlen=self.tlen,
6032             llen=self.llen,
6033             vlen=self.vlen,
6034             expl_offset=self.expl_offset if self.expled else None,
6035             expl_tlen=self.expl_tlen if self.expled else None,
6036             expl_llen=self.expl_llen if self.expled else None,
6037             expl_vlen=self.expl_vlen if self.expled else None,
6038             expl_lenindef=self.expl_lenindef,
6039             lenindef=self.lenindef,
6040             ber_encoded=self.ber_encoded,
6041             bered=self.bered,
6042         )
6043         for i, value in enumerate(self._value):
6044             yield value.pps(decode_path=decode_path + (str(i),))
6045         for pp in self.pps_lenindef(decode_path):
6046             yield pp
6047
6048
6049 class SetOf(SequenceOf):
6050     """``SET OF`` sequence type
6051
6052     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6053     """
6054     __slots__ = ()
6055     tag_default = tag_encode(form=TagFormConstructed, num=17)
6056     asn1_type_name = "SET OF"
6057
6058     def _encode(self):
6059         raws = self._encoded_values()
6060         raws.sort()
6061         v = b"".join(raws)
6062         return b"".join((self.tag, len_encode(len(v)), v))
6063
6064     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
6065         return super(SetOf, self)._decode(
6066             tlv,
6067             offset,
6068             decode_path,
6069             ctx,
6070             tag_only,
6071             ordering_check=True,
6072         )
6073
6074
6075 def obj_by_path(pypath):  # pragma: no cover
6076     """Import object specified as string Python path
6077
6078     Modules must be separated from classes/functions with ``:``.
6079
6080     >>> obj_by_path("foo.bar:Baz")
6081     <class 'foo.bar.Baz'>
6082     >>> obj_by_path("foo.bar:Baz.boo")
6083     <classmethod 'foo.bar.Baz.boo'>
6084     """
6085     mod, objs = pypath.rsplit(":", 1)
6086     from importlib import import_module
6087     obj = import_module(mod)
6088     for obj_name in objs.split("."):
6089         obj = getattr(obj, obj_name)
6090     return obj
6091
6092
6093 def generic_decoder():  # pragma: no cover
6094     # All of this below is a big hack with self references
6095     choice = PrimitiveTypes()
6096     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6097     choice.specs["SetOf"] = SetOf(schema=choice)
6098     for i in six_xrange(31):
6099         choice.specs["SequenceOf%d" % i] = SequenceOf(
6100             schema=choice,
6101             expl=tag_ctxc(i),
6102         )
6103     choice.specs["Any"] = Any()
6104
6105     # Class name equals to type name, to omit it from output
6106     class SEQUENCEOF(SequenceOf):
6107         __slots__ = ()
6108         schema = choice
6109
6110     def pprint_any(
6111             obj,
6112             oid_maps=(),
6113             with_colours=False,
6114             with_decode_path=False,
6115             decode_path_only=(),
6116     ):
6117         def _pprint_pps(pps):
6118             for pp in pps:
6119                 if hasattr(pp, "_fields"):
6120                     if (
6121                             decode_path_only != () and
6122                             pp.decode_path[:len(decode_path_only)] != decode_path_only
6123                     ):
6124                         continue
6125                     if pp.asn1_type_name == Choice.asn1_type_name:
6126                         continue
6127                     pp_kwargs = pp._asdict()
6128                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6129                     pp = _pp(**pp_kwargs)
6130                     yield pp_console_row(
6131                         pp,
6132                         oid_maps=oid_maps,
6133                         with_offsets=True,
6134                         with_blob=False,
6135                         with_colours=with_colours,
6136                         with_decode_path=with_decode_path,
6137                         decode_path_len_decrease=len(decode_path_only),
6138                     )
6139                     for row in pp_console_blob(
6140                             pp,
6141                             decode_path_len_decrease=len(decode_path_only),
6142                     ):
6143                         yield row
6144                 else:
6145                     for row in _pprint_pps(pp):
6146                         yield row
6147         return "\n".join(_pprint_pps(obj.pps()))
6148     return SEQUENCEOF(), pprint_any
6149
6150
6151 def main():  # pragma: no cover
6152     import argparse
6153     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6154     parser.add_argument(
6155         "--skip",
6156         type=int,
6157         default=0,
6158         help="Skip that number of bytes from the beginning",
6159     )
6160     parser.add_argument(
6161         "--oids",
6162         help="Python paths to dictionary with OIDs, comma separated",
6163     )
6164     parser.add_argument(
6165         "--schema",
6166         help="Python path to schema definition to use",
6167     )
6168     parser.add_argument(
6169         "--defines-by-path",
6170         help="Python path to decoder's defines_by_path",
6171     )
6172     parser.add_argument(
6173         "--nobered",
6174         action="store_true",
6175         help="Disallow BER encoding",
6176     )
6177     parser.add_argument(
6178         "--print-decode-path",
6179         action="store_true",
6180         help="Print decode paths",
6181     )
6182     parser.add_argument(
6183         "--decode-path-only",
6184         help="Print only specified decode path",
6185     )
6186     parser.add_argument(
6187         "--allow-expl-oob",
6188         action="store_true",
6189         help="Allow explicit tag out-of-bound",
6190     )
6191     parser.add_argument(
6192         "DERFile",
6193         type=argparse.FileType("rb"),
6194         help="Path to DER file you want to decode",
6195     )
6196     args = parser.parse_args()
6197     args.DERFile.seek(args.skip)
6198     der = memoryview(args.DERFile.read())
6199     args.DERFile.close()
6200     oid_maps = (
6201         [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6202         if args.oids else ()
6203     )
6204     if args.schema:
6205         schema = obj_by_path(args.schema)
6206         from functools import partial
6207         pprinter = partial(pprint, big_blobs=True)
6208     else:
6209         schema, pprinter = generic_decoder()
6210     ctx = {
6211         "bered": not args.nobered,
6212         "allow_expl_oob": args.allow_expl_oob,
6213     }
6214     if args.defines_by_path is not None:
6215         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6216     obj, tail = schema().decode(der, ctx=ctx)
6217     print(pprinter(
6218         obj,
6219         oid_maps=oid_maps,
6220         with_colours=environ.get("NO_COLOR") is None,
6221         with_decode_path=args.print_decode_path,
6222         decode_path_only=(
6223             () if args.decode_path_only is None else
6224             tuple(args.decode_path_only.split(":"))
6225         ),
6226     ))
6227     if tail != b"":
6228         print("\nTrailing data: %s" % hexenc(tail))
6229
6230
6231 if __name__ == "__main__":
6232     main()