]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Stricter validation of *Time
[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.1"
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 def pureint(value):
3424     i = int(value)
3425     if (value[0] in "+- ") or (value[-1] == " "):
3426         raise ValueError("non-pure integer")
3427     return i
3428
3429
3430 class ObjectIdentifier(Obj):
3431     """``OBJECT IDENTIFIER`` OID type
3432
3433     >>> oid = ObjectIdentifier((1, 2, 3))
3434     OBJECT IDENTIFIER 1.2.3
3435     >>> oid == ObjectIdentifier("1.2.3")
3436     True
3437     >>> tuple(oid)
3438     (1, 2, 3)
3439     >>> str(oid)
3440     '1.2.3'
3441     >>> oid + (4, 5) + ObjectIdentifier("1.7")
3442     OBJECT IDENTIFIER 1.2.3.4.5.1.7
3443
3444     >>> str(ObjectIdentifier((3, 1)))
3445     Traceback (most recent call last):
3446     pyderasn.InvalidOID: unacceptable first arc value
3447     """
3448     __slots__ = ("defines",)
3449     tag_default = tag_encode(6)
3450     asn1_type_name = "OBJECT IDENTIFIER"
3451
3452     def __init__(
3453             self,
3454             value=None,
3455             defines=(),
3456             impl=None,
3457             expl=None,
3458             default=None,
3459             optional=False,
3460             _decoded=(0, 0, 0),
3461     ):
3462         """
3463         :param value: set the value. Either tuples of integers,
3464                       string of "."-concatenated integers, or
3465                       :py:class:`pyderasn.ObjectIdentifier` object
3466         :param defines: sequence of tuples. Each tuple has two elements.
3467                         First one is relative to current one decode
3468                         path, aiming to the field defined by that OID.
3469                         Read about relative path in
3470                         :py:func:`pyderasn.abs_decode_path`. Second
3471                         tuple element is ``{OID: pyderasn.Obj()}``
3472                         dictionary, mapping between current OID value
3473                         and structure applied to defined field.
3474                         :ref:`Read about DEFINED BY <definedby>`
3475         :param bytes impl: override default tag with ``IMPLICIT`` one
3476         :param bytes expl: override default tag with ``EXPLICIT`` one
3477         :param default: set default value. Type same as in ``value``
3478         :param bool optional: is object ``OPTIONAL`` in sequence
3479         """
3480         super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
3481         self._value = value
3482         if value is not None:
3483             self._value = self._value_sanitize(value)
3484         if default is not None:
3485             default = self._value_sanitize(default)
3486             self.default = self.__class__(
3487                 value=default,
3488                 impl=self.tag,
3489                 expl=self._expl,
3490             )
3491             if self._value is None:
3492                 self._value = default
3493         self.defines = defines
3494
3495     def __add__(self, their):
3496         if isinstance(their, self.__class__):
3497             return self.__class__(self._value + their._value)
3498         if isinstance(their, tuple):
3499             return self.__class__(self._value + their)
3500         raise InvalidValueType((self.__class__, tuple))
3501
3502     def _value_sanitize(self, value):
3503         if issubclass(value.__class__, ObjectIdentifier):
3504             return value._value
3505         if isinstance(value, string_types):
3506             try:
3507                 value = tuple(pureint(arc) for arc in value.split("."))
3508             except ValueError:
3509                 raise InvalidOID("unacceptable arcs values")
3510         if isinstance(value, tuple):
3511             if len(value) < 2:
3512                 raise InvalidOID("less than 2 arcs")
3513             first_arc = value[0]
3514             if first_arc in (0, 1):
3515                 if not (0 <= value[1] <= 39):
3516                     raise InvalidOID("second arc is too wide")
3517             elif first_arc == 2:
3518                 pass
3519             else:
3520                 raise InvalidOID("unacceptable first arc value")
3521             if not all(arc >= 0 for arc in value):
3522                 raise InvalidOID("negative arc value")
3523             return value
3524         raise InvalidValueType((self.__class__, str, tuple))
3525
3526     @property
3527     def ready(self):
3528         return self._value is not None
3529
3530     def __getstate__(self):
3531         return ObjectIdentifierState(
3532             __version__,
3533             self._value,
3534             self.tag,
3535             self._expl,
3536             self.default,
3537             self.optional,
3538             self.offset,
3539             self.llen,
3540             self.vlen,
3541             self.expl_lenindef,
3542             self.lenindef,
3543             self.ber_encoded,
3544             self.defines,
3545         )
3546
3547     def __setstate__(self, state):
3548         super(ObjectIdentifier, self).__setstate__(state)
3549         self._value = state.value
3550         self.tag = state.tag
3551         self._expl = state.expl
3552         self.default = state.default
3553         self.optional = state.optional
3554         self.offset = state.offset
3555         self.llen = state.llen
3556         self.vlen = state.vlen
3557         self.expl_lenindef = state.expl_lenindef
3558         self.lenindef = state.lenindef
3559         self.ber_encoded = state.ber_encoded
3560         self.defines = state.defines
3561
3562     def __iter__(self):
3563         self._assert_ready()
3564         return iter(self._value)
3565
3566     def __str__(self):
3567         return ".".join(str(arc) for arc in self._value or ())
3568
3569     def __hash__(self):
3570         self._assert_ready()
3571         return hash(
3572             self.tag +
3573             bytes(self._expl or b"") +
3574             str(self._value).encode("ascii"),
3575         )
3576
3577     def __eq__(self, their):
3578         if isinstance(their, tuple):
3579             return self._value == their
3580         if not issubclass(their.__class__, ObjectIdentifier):
3581             return False
3582         return (
3583             self.tag == their.tag and
3584             self._expl == their._expl and
3585             self._value == their._value
3586         )
3587
3588     def __lt__(self, their):
3589         return self._value < their._value
3590
3591     def __call__(
3592             self,
3593             value=None,
3594             defines=None,
3595             impl=None,
3596             expl=None,
3597             default=None,
3598             optional=None,
3599     ):
3600         return self.__class__(
3601             value=value,
3602             defines=self.defines if defines is None else defines,
3603             impl=self.tag if impl is None else impl,
3604             expl=self._expl if expl is None else expl,
3605             default=self.default if default is None else default,
3606             optional=self.optional if optional is None else optional,
3607         )
3608
3609     def _encode(self):
3610         self._assert_ready()
3611         value = self._value
3612         first_value = value[1]
3613         first_arc = value[0]
3614         if first_arc == 0:
3615             pass
3616         elif first_arc == 1:
3617             first_value += 40
3618         elif first_arc == 2:
3619             first_value += 80
3620         else:  # pragma: no cover
3621             raise RuntimeError("invalid arc is stored")
3622         octets = [zero_ended_encode(first_value)]
3623         for arc in value[2:]:
3624             octets.append(zero_ended_encode(arc))
3625         v = b"".join(octets)
3626         return b"".join((self.tag, len_encode(len(v)), v))
3627
3628     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3629         try:
3630             t, _, lv = tag_strip(tlv)
3631         except DecodeError as err:
3632             raise err.__class__(
3633                 msg=err.msg,
3634                 klass=self.__class__,
3635                 decode_path=decode_path,
3636                 offset=offset,
3637             )
3638         if t != self.tag:
3639             raise TagMismatch(
3640                 klass=self.__class__,
3641                 decode_path=decode_path,
3642                 offset=offset,
3643             )
3644         if tag_only:  # pragma: no cover
3645             return None
3646         try:
3647             l, llen, v = len_decode(lv)
3648         except DecodeError as err:
3649             raise err.__class__(
3650                 msg=err.msg,
3651                 klass=self.__class__,
3652                 decode_path=decode_path,
3653                 offset=offset,
3654             )
3655         if l > len(v):
3656             raise NotEnoughData(
3657                 "encoded length is longer than data",
3658                 klass=self.__class__,
3659                 decode_path=decode_path,
3660                 offset=offset,
3661             )
3662         if l == 0:
3663             raise NotEnoughData(
3664                 "zero length",
3665                 klass=self.__class__,
3666                 decode_path=decode_path,
3667                 offset=offset,
3668             )
3669         v, tail = v[:l], v[l:]
3670         arcs = []
3671         ber_encoded = False
3672         while len(v) > 0:
3673             i = 0
3674             arc = 0
3675             while True:
3676                 octet = indexbytes(v, i)
3677                 if i == 0 and octet == 0x80:
3678                     if ctx.get("bered", False):
3679                         ber_encoded = True
3680                     else:
3681                         raise DecodeError("non normalized arc encoding")
3682                 arc = (arc << 7) | (octet & 0x7F)
3683                 if octet & 0x80 == 0:
3684                     arcs.append(arc)
3685                     v = v[i + 1:]
3686                     break
3687                 i += 1
3688                 if i == len(v):
3689                     raise DecodeError(
3690                         "unfinished OID",
3691                         klass=self.__class__,
3692                         decode_path=decode_path,
3693                         offset=offset,
3694                     )
3695         first_arc = 0
3696         second_arc = arcs[0]
3697         if 0 <= second_arc <= 39:
3698             first_arc = 0
3699         elif 40 <= second_arc <= 79:
3700             first_arc = 1
3701             second_arc -= 40
3702         else:
3703             first_arc = 2
3704             second_arc -= 80
3705         obj = self.__class__(
3706             value=tuple([first_arc, second_arc] + arcs[1:]),
3707             impl=self.tag,
3708             expl=self._expl,
3709             default=self.default,
3710             optional=self.optional,
3711             _decoded=(offset, llen, l),
3712         )
3713         if ber_encoded:
3714             obj.ber_encoded = True
3715         return obj, tail
3716
3717     def __repr__(self):
3718         return pp_console_row(next(self.pps()))
3719
3720     def pps(self, decode_path=()):
3721         yield _pp(
3722             obj=self,
3723             asn1_type_name=self.asn1_type_name,
3724             obj_name=self.__class__.__name__,
3725             decode_path=decode_path,
3726             value=str(self) if self.ready else None,
3727             optional=self.optional,
3728             default=self == self.default,
3729             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3730             expl=None if self._expl is None else tag_decode(self._expl),
3731             offset=self.offset,
3732             tlen=self.tlen,
3733             llen=self.llen,
3734             vlen=self.vlen,
3735             expl_offset=self.expl_offset if self.expled else None,
3736             expl_tlen=self.expl_tlen if self.expled else None,
3737             expl_llen=self.expl_llen if self.expled else None,
3738             expl_vlen=self.expl_vlen if self.expled else None,
3739             expl_lenindef=self.expl_lenindef,
3740             ber_encoded=self.ber_encoded,
3741             bered=self.bered,
3742         )
3743         for pp in self.pps_lenindef(decode_path):
3744             yield pp
3745
3746
3747 class Enumerated(Integer):
3748     """``ENUMERATED`` integer type
3749
3750     This type is identical to :py:class:`pyderasn.Integer`, but requires
3751     schema to be specified and does not accept values missing from it.
3752     """
3753     __slots__ = ()
3754     tag_default = tag_encode(10)
3755     asn1_type_name = "ENUMERATED"
3756
3757     def __init__(
3758             self,
3759             value=None,
3760             impl=None,
3761             expl=None,
3762             default=None,
3763             optional=False,
3764             _specs=None,
3765             _decoded=(0, 0, 0),
3766             bounds=None,  # dummy argument, workability for Integer.decode
3767     ):
3768         super(Enumerated, self).__init__(
3769             value, bounds, impl, expl, default, optional, _specs, _decoded,
3770         )
3771         if len(self.specs) == 0:
3772             raise ValueError("schema must be specified")
3773
3774     def _value_sanitize(self, value):
3775         if isinstance(value, self.__class__):
3776             value = value._value
3777         elif isinstance(value, integer_types):
3778             for _value in itervalues(self.specs):
3779                 if _value == value:
3780                     break
3781             else:
3782                 raise DecodeError(
3783                     "unknown integer value: %s" % value,
3784                     klass=self.__class__,
3785                 )
3786         elif isinstance(value, string_types):
3787             value = self.specs.get(value)
3788             if value is None:
3789                 raise ObjUnknown("integer value: %s" % value)
3790         else:
3791             raise InvalidValueType((self.__class__, int, str))
3792         return value
3793
3794     def __call__(
3795             self,
3796             value=None,
3797             impl=None,
3798             expl=None,
3799             default=None,
3800             optional=None,
3801             _specs=None,
3802     ):
3803         return self.__class__(
3804             value=value,
3805             impl=self.tag if impl is None else impl,
3806             expl=self._expl if expl is None else expl,
3807             default=self.default if default is None else default,
3808             optional=self.optional if optional is None else optional,
3809             _specs=self.specs,
3810         )
3811
3812
3813 def escape_control_unicode(c):
3814     if unicat(c).startswith("C"):
3815         c = repr(c).lstrip("u").strip("'")
3816     return c
3817
3818
3819 class CommonString(OctetString):
3820     """Common class for all strings
3821
3822     Everything resembles :py:class:`pyderasn.OctetString`, except
3823     ability to deal with unicode text strings.
3824
3825     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
3826     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3827     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
3828     True
3829     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
3830     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
3831     >>> str(s)
3832     'привет Ð¼Ð¸Ñ€'
3833     >>> hexenc(bytes(s))
3834     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3835
3836     >>> PrintableString("привет Ð¼Ð¸Ñ€")
3837     Traceback (most recent call last):
3838     pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3839
3840     >>> BMPString("ада", bounds=(2, 2))
3841     Traceback (most recent call last):
3842     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3843     >>> s = BMPString("ад", bounds=(2, 2))
3844     >>> s.encoding
3845     'utf-16-be'
3846     >>> hexenc(bytes(s))
3847     '04300434'
3848
3849     .. list-table::
3850        :header-rows: 1
3851
3852        * - Class
3853          - Text Encoding
3854        * - :py:class:`pyderasn.UTF8String`
3855          - utf-8
3856        * - :py:class:`pyderasn.NumericString`
3857          - ascii
3858        * - :py:class:`pyderasn.PrintableString`
3859          - ascii
3860        * - :py:class:`pyderasn.TeletexString`
3861          - ascii
3862        * - :py:class:`pyderasn.T61String`
3863          - ascii
3864        * - :py:class:`pyderasn.VideotexString`
3865          - iso-8859-1
3866        * - :py:class:`pyderasn.IA5String`
3867          - ascii
3868        * - :py:class:`pyderasn.GraphicString`
3869          - iso-8859-1
3870        * - :py:class:`pyderasn.VisibleString`
3871          - ascii
3872        * - :py:class:`pyderasn.ISO646String`
3873          - ascii
3874        * - :py:class:`pyderasn.GeneralString`
3875          - iso-8859-1
3876        * - :py:class:`pyderasn.UniversalString`
3877          - utf-32-be
3878        * - :py:class:`pyderasn.BMPString`
3879          - utf-16-be
3880     """
3881     __slots__ = ()
3882
3883     def _value_sanitize(self, value):
3884         value_raw = None
3885         value_decoded = None
3886         if isinstance(value, self.__class__):
3887             value_raw = value._value
3888         elif isinstance(value, text_type):
3889             value_decoded = value
3890         elif isinstance(value, binary_type):
3891             value_raw = value
3892         else:
3893             raise InvalidValueType((self.__class__, text_type, binary_type))
3894         try:
3895             value_raw = (
3896                 value_decoded.encode(self.encoding)
3897                 if value_raw is None else value_raw
3898             )
3899             value_decoded = (
3900                 value_raw.decode(self.encoding)
3901                 if value_decoded is None else value_decoded
3902             )
3903         except (UnicodeEncodeError, UnicodeDecodeError) as err:
3904             raise DecodeError(str(err))
3905         if not self._bound_min <= len(value_decoded) <= self._bound_max:
3906             raise BoundsError(
3907                 self._bound_min,
3908                 len(value_decoded),
3909                 self._bound_max,
3910             )
3911         return value_raw
3912
3913     def __eq__(self, their):
3914         if isinstance(their, binary_type):
3915             return self._value == their
3916         if isinstance(their, text_type):
3917             return self._value == their.encode(self.encoding)
3918         if not isinstance(their, self.__class__):
3919             return False
3920         return (
3921             self._value == their._value and
3922             self.tag == their.tag and
3923             self._expl == their._expl
3924         )
3925
3926     def __unicode__(self):
3927         if self.ready:
3928             return self._value.decode(self.encoding)
3929         return text_type(self._value)
3930
3931     def __repr__(self):
3932         return pp_console_row(next(self.pps(no_unicode=PY2)))
3933
3934     def pps(self, decode_path=(), no_unicode=False):
3935         value = None
3936         if self.ready:
3937             value = (
3938                 hexenc(bytes(self)) if no_unicode else
3939                 "".join(escape_control_unicode(c) for c in self.__unicode__())
3940             )
3941         yield _pp(
3942             obj=self,
3943             asn1_type_name=self.asn1_type_name,
3944             obj_name=self.__class__.__name__,
3945             decode_path=decode_path,
3946             value=value,
3947             optional=self.optional,
3948             default=self == self.default,
3949             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3950             expl=None if self._expl is None else tag_decode(self._expl),
3951             offset=self.offset,
3952             tlen=self.tlen,
3953             llen=self.llen,
3954             vlen=self.vlen,
3955             expl_offset=self.expl_offset if self.expled else None,
3956             expl_tlen=self.expl_tlen if self.expled else None,
3957             expl_llen=self.expl_llen if self.expled else None,
3958             expl_vlen=self.expl_vlen if self.expled else None,
3959             expl_lenindef=self.expl_lenindef,
3960             ber_encoded=self.ber_encoded,
3961             bered=self.bered,
3962         )
3963         for pp in self.pps_lenindef(decode_path):
3964             yield pp
3965
3966
3967 class UTF8String(CommonString):
3968     __slots__ = ()
3969     tag_default = tag_encode(12)
3970     encoding = "utf-8"
3971     asn1_type_name = "UTF8String"
3972
3973
3974 class AllowableCharsMixin(object):
3975     @property
3976     def allowable_chars(self):
3977         if PY2:
3978             return self._allowable_chars
3979         return frozenset(six_unichr(c) for c in self._allowable_chars)
3980
3981
3982 class NumericString(AllowableCharsMixin, CommonString):
3983     """Numeric string
3984
3985     Its value is properly sanitized: only ASCII digits with spaces can
3986     be stored.
3987
3988     >>> NumericString().allowable_chars
3989     frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
3990     """
3991     __slots__ = ()
3992     tag_default = tag_encode(18)
3993     encoding = "ascii"
3994     asn1_type_name = "NumericString"
3995     _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3996
3997     def _value_sanitize(self, value):
3998         value = super(NumericString, self)._value_sanitize(value)
3999         if not frozenset(value) <= self._allowable_chars:
4000             raise DecodeError("non-numeric value")
4001         return value
4002
4003
4004 PrintableStringState = namedtuple(
4005     "PrintableStringState",
4006     OctetStringState._fields + ("allowable_chars",),
4007 )
4008
4009
4010 class PrintableString(AllowableCharsMixin, CommonString):
4011     """Printable string
4012
4013     Its value is properly sanitized: see X.680 41.4 table 10.
4014
4015     >>> PrintableString().allowable_chars
4016     frozenset([' ', "'", ..., 'z'])
4017     >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4018     PrintableString PrintableString foo*bar
4019     >>> obj.allow_asterisk, obj.allow_ampersand
4020     (True, False)
4021     """
4022     __slots__ = ()
4023     tag_default = tag_encode(19)
4024     encoding = "ascii"
4025     asn1_type_name = "PrintableString"
4026     _allowable_chars = frozenset(
4027         (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4028     )
4029     _asterisk = frozenset("*".encode("ascii"))
4030     _ampersand = frozenset("&".encode("ascii"))
4031
4032     def __init__(
4033             self,
4034             value=None,
4035             bounds=None,
4036             impl=None,
4037             expl=None,
4038             default=None,
4039             optional=False,
4040             _decoded=(0, 0, 0),
4041             allow_asterisk=False,
4042             allow_ampersand=False,
4043     ):
4044         """
4045         :param allow_asterisk: allow asterisk character
4046         :param allow_ampersand: allow ampersand character
4047         """
4048         if allow_asterisk:
4049             self._allowable_chars |= self._asterisk
4050         if allow_ampersand:
4051             self._allowable_chars |= self._ampersand
4052         super(PrintableString, self).__init__(
4053             value, bounds, impl, expl, default, optional, _decoded,
4054         )
4055
4056     @property
4057     def allow_asterisk(self):
4058         """Is asterisk character allowed?
4059         """
4060         return self._asterisk <= self._allowable_chars
4061
4062     @property
4063     def allow_ampersand(self):
4064         """Is ampersand character allowed?
4065         """
4066         return self._ampersand <= self._allowable_chars
4067
4068     def _value_sanitize(self, value):
4069         value = super(PrintableString, self)._value_sanitize(value)
4070         if not frozenset(value) <= self._allowable_chars:
4071             raise DecodeError("non-printable value")
4072         return value
4073
4074     def __getstate__(self):
4075         return PrintableStringState(
4076             *super(PrintableString, self).__getstate__(),
4077             **{"allowable_chars": self._allowable_chars}
4078         )
4079
4080     def __setstate__(self, state):
4081         super(PrintableString, self).__setstate__(state)
4082         self._allowable_chars = state.allowable_chars
4083
4084     def __call__(
4085             self,
4086             value=None,
4087             bounds=None,
4088             impl=None,
4089             expl=None,
4090             default=None,
4091             optional=None,
4092     ):
4093         return self.__class__(
4094             value=value,
4095             bounds=(
4096                 (self._bound_min, self._bound_max)
4097                 if bounds is None else bounds
4098             ),
4099             impl=self.tag if impl is None else impl,
4100             expl=self._expl if expl is None else expl,
4101             default=self.default if default is None else default,
4102             optional=self.optional if optional is None else optional,
4103             allow_asterisk=self.allow_asterisk,
4104             allow_ampersand=self.allow_ampersand,
4105         )
4106
4107
4108 class TeletexString(CommonString):
4109     __slots__ = ()
4110     tag_default = tag_encode(20)
4111     encoding = "ascii"
4112     asn1_type_name = "TeletexString"
4113
4114
4115 class T61String(TeletexString):
4116     __slots__ = ()
4117     asn1_type_name = "T61String"
4118
4119
4120 class VideotexString(CommonString):
4121     __slots__ = ()
4122     tag_default = tag_encode(21)
4123     encoding = "iso-8859-1"
4124     asn1_type_name = "VideotexString"
4125
4126
4127 class IA5String(CommonString):
4128     __slots__ = ()
4129     tag_default = tag_encode(22)
4130     encoding = "ascii"
4131     asn1_type_name = "IA5"
4132
4133
4134 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4135 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4136 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4137
4138
4139 class VisibleString(CommonString):
4140     __slots__ = ()
4141     tag_default = tag_encode(26)
4142     encoding = "ascii"
4143     asn1_type_name = "VisibleString"
4144
4145
4146 class UTCTime(VisibleString):
4147     """``UTCTime`` datetime type
4148
4149     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4150     UTCTime UTCTime 2017-09-30T22:07:50
4151     >>> str(t)
4152     '170930220750Z'
4153     >>> bytes(t)
4154     b'170930220750Z'
4155     >>> t.todatetime()
4156     datetime.datetime(2017, 9, 30, 22, 7, 50)
4157     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
4158     datetime.datetime(1957, 9, 30, 22, 7, 50)
4159
4160     .. warning::
4161
4162        BER encoding is unsupported.
4163     """
4164     __slots__ = ()
4165     tag_default = tag_encode(23)
4166     encoding = "ascii"
4167     asn1_type_name = "UTCTime"
4168
4169     def __init__(
4170             self,
4171             value=None,
4172             impl=None,
4173             expl=None,
4174             default=None,
4175             optional=False,
4176             _decoded=(0, 0, 0),
4177             bounds=None,  # dummy argument, workability for OctetString.decode
4178     ):
4179         """
4180         :param value: set the value. Either datetime type, or
4181                       :py:class:`pyderasn.UTCTime` object
4182         :param bytes impl: override default tag with ``IMPLICIT`` one
4183         :param bytes expl: override default tag with ``EXPLICIT`` one
4184         :param default: set default value. Type same as in ``value``
4185         :param bool optional: is object ``OPTIONAL`` in sequence
4186         """
4187         super(UTCTime, self).__init__(
4188             None, None, impl, expl, default, optional, _decoded,
4189         )
4190         self._value = value
4191         if value is not None:
4192             self._value = self._value_sanitize(value)
4193         if default is not None:
4194             default = self._value_sanitize(default)
4195             self.default = self.__class__(
4196                 value=default,
4197                 impl=self.tag,
4198                 expl=self._expl,
4199             )
4200             if self._value is None:
4201                 self._value = default
4202
4203     def _strptime(self, value):
4204         # datetime.strptime's format: %y%m%d%H%M%SZ
4205         if len(value) != LEN_YYMMDDHHMMSSZ:
4206             raise ValueError("invalid UTCTime length")
4207         if value[-1] != "Z":
4208             raise ValueError("non UTC timezone")
4209         return datetime(
4210             2000 + int(value[:2]),  # %y
4211             pureint(value[2:4]),  # %m
4212             pureint(value[4:6]),  # %d
4213             pureint(value[6:8]),  # %H
4214             pureint(value[8:10]),  # %M
4215             pureint(value[10:12]),  # %S
4216         )
4217
4218     def _value_sanitize(self, value):
4219         if isinstance(value, binary_type):
4220             try:
4221                 value_decoded = value.decode("ascii")
4222             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4223                 raise DecodeError("invalid UTCTime encoding: %r" % err)
4224             try:
4225                 self._strptime(value_decoded)
4226             except (TypeError, ValueError) as err:
4227                 raise DecodeError("invalid UTCTime format: %r" % err)
4228             return value
4229         if isinstance(value, self.__class__):
4230             return value._value
4231         if isinstance(value, datetime):
4232             return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
4233         raise InvalidValueType((self.__class__, datetime))
4234
4235     def __eq__(self, their):
4236         if isinstance(their, binary_type):
4237             return self._value == their
4238         if isinstance(their, datetime):
4239             return self.todatetime() == their
4240         if not isinstance(their, self.__class__):
4241             return False
4242         return (
4243             self._value == their._value and
4244             self.tag == their.tag and
4245             self._expl == their._expl
4246         )
4247
4248     def todatetime(self):
4249         """Convert to datetime
4250
4251         :returns: datetime
4252
4253         Pay attention that UTCTime can not hold full year, so all years
4254         having < 50 years are treated as 20xx, 19xx otherwise, according
4255         to X.509 recomendation.
4256         """
4257         value = self._strptime(self._value.decode("ascii"))
4258         year = value.year % 100
4259         return datetime(
4260             year=(2000 + year) if year < 50 else (1900 + year),
4261             month=value.month,
4262             day=value.day,
4263             hour=value.hour,
4264             minute=value.minute,
4265             second=value.second,
4266         )
4267
4268     def __repr__(self):
4269         return pp_console_row(next(self.pps()))
4270
4271     def pps(self, decode_path=()):
4272         yield _pp(
4273             obj=self,
4274             asn1_type_name=self.asn1_type_name,
4275             obj_name=self.__class__.__name__,
4276             decode_path=decode_path,
4277             value=self.todatetime().isoformat() if self.ready else None,
4278             optional=self.optional,
4279             default=self == self.default,
4280             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4281             expl=None if self._expl is None else tag_decode(self._expl),
4282             offset=self.offset,
4283             tlen=self.tlen,
4284             llen=self.llen,
4285             vlen=self.vlen,
4286             expl_offset=self.expl_offset if self.expled else None,
4287             expl_tlen=self.expl_tlen if self.expled else None,
4288             expl_llen=self.expl_llen if self.expled else None,
4289             expl_vlen=self.expl_vlen if self.expled else None,
4290             expl_lenindef=self.expl_lenindef,
4291             ber_encoded=self.ber_encoded,
4292             bered=self.bered,
4293         )
4294         for pp in self.pps_lenindef(decode_path):
4295             yield pp
4296
4297
4298 class GeneralizedTime(UTCTime):
4299     """``GeneralizedTime`` datetime type
4300
4301     This type is similar to :py:class:`pyderasn.UTCTime`.
4302
4303     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4304     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
4305     >>> str(t)
4306     '20170930220750.000123Z'
4307     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
4308     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
4309
4310     .. warning::
4311
4312        BER encoding is unsupported.
4313
4314     .. warning::
4315
4316        Only microsecond fractions are supported.
4317        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
4318        higher precision values.
4319
4320     .. warning::
4321
4322        Zero year is unsupported.
4323     """
4324     __slots__ = ()
4325     tag_default = tag_encode(24)
4326     asn1_type_name = "GeneralizedTime"
4327
4328     def _strptime(self, value):
4329         l = len(value)
4330         if l == LEN_YYYYMMDDHHMMSSZ:
4331             # datetime.strptime's format: %Y%m%d%H%M%SZ
4332             if value[-1] != "Z":
4333                 raise ValueError("non UTC timezone")
4334             return datetime(
4335                 pureint(value[:4]),  # %Y
4336                 pureint(value[4:6]),  # %m
4337                 pureint(value[6:8]),  # %d
4338                 pureint(value[8:10]),  # %H
4339                 pureint(value[10:12]),  # %M
4340                 pureint(value[12:14]),  # %S
4341             )
4342         if l >= LEN_YYYYMMDDHHMMSSDMZ:
4343             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4344             if value[-1] != "Z":
4345                 raise ValueError("non UTC timezone")
4346             if value[14] != ".":
4347                 raise ValueError("no fractions separator")
4348             us = value[15:-1]
4349             if us[-1] == "0":
4350                 raise ValueError("trailing zero")
4351             us_len = len(us)
4352             if us_len > 6:
4353                 raise ValueError("only microsecond fractions are supported")
4354             us = pureint(us + ("0" * (6 - us_len)))
4355             decoded = datetime(
4356                 pureint(value[:4]),  # %Y
4357                 pureint(value[4:6]),  # %m
4358                 pureint(value[6:8]),  # %d
4359                 pureint(value[8:10]),  # %H
4360                 pureint(value[10:12]),  # %M
4361                 pureint(value[12:14]),  # %S
4362                 us,  # %f
4363             )
4364             return decoded
4365         raise ValueError("invalid GeneralizedTime length")
4366
4367     def _value_sanitize(self, value):
4368         if isinstance(value, binary_type):
4369             try:
4370                 value_decoded = value.decode("ascii")
4371             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4372                 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
4373             try:
4374                 self._strptime(value_decoded)
4375             except (TypeError, ValueError) as err:
4376                 raise DecodeError(
4377                     "invalid GeneralizedTime format: %r" % err,
4378                     klass=self.__class__,
4379                 )
4380             return value
4381         if isinstance(value, self.__class__):
4382             return value._value
4383         if isinstance(value, datetime):
4384             encoded = value.strftime("%Y%m%d%H%M%S")
4385             if value.microsecond > 0:
4386                 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4387             return (encoded + "Z").encode("ascii")
4388         raise InvalidValueType((self.__class__, datetime))
4389
4390     def todatetime(self):
4391         return self._strptime(self._value.decode("ascii"))
4392
4393
4394 class GraphicString(CommonString):
4395     __slots__ = ()
4396     tag_default = tag_encode(25)
4397     encoding = "iso-8859-1"
4398     asn1_type_name = "GraphicString"
4399
4400
4401 class ISO646String(VisibleString):
4402     __slots__ = ()
4403     asn1_type_name = "ISO646String"
4404
4405
4406 class GeneralString(CommonString):
4407     __slots__ = ()
4408     tag_default = tag_encode(27)
4409     encoding = "iso-8859-1"
4410     asn1_type_name = "GeneralString"
4411
4412
4413 class UniversalString(CommonString):
4414     __slots__ = ()
4415     tag_default = tag_encode(28)
4416     encoding = "utf-32-be"
4417     asn1_type_name = "UniversalString"
4418
4419
4420 class BMPString(CommonString):
4421     __slots__ = ()
4422     tag_default = tag_encode(30)
4423     encoding = "utf-16-be"
4424     asn1_type_name = "BMPString"
4425
4426
4427 ChoiceState = namedtuple("ChoiceState", (
4428     "version",
4429     "specs",
4430     "value",
4431     "tag",
4432     "expl",
4433     "default",
4434     "optional",
4435     "offset",
4436     "llen",
4437     "vlen",
4438     "expl_lenindef",
4439     "lenindef",
4440     "ber_encoded",
4441 ))
4442
4443
4444 class Choice(Obj):
4445     """``CHOICE`` special type
4446
4447     ::
4448
4449         class GeneralName(Choice):
4450             schema = (
4451                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4452                 ("dNSName", IA5String(impl=tag_ctxp(2))),
4453             )
4454
4455     >>> gn = GeneralName()
4456     GeneralName CHOICE
4457     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4458     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4459     >>> gn["dNSName"] = IA5String("bar.baz")
4460     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4461     >>> gn["rfc822Name"]
4462     None
4463     >>> gn["dNSName"]
4464     [2] IA5String IA5 bar.baz
4465     >>> gn.choice
4466     'dNSName'
4467     >>> gn.value == gn["dNSName"]
4468     True
4469     >>> gn.specs
4470     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4471
4472     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4473     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4474     """
4475     __slots__ = ("specs",)
4476     tag_default = None
4477     asn1_type_name = "CHOICE"
4478
4479     def __init__(
4480             self,
4481             value=None,
4482             schema=None,
4483             impl=None,
4484             expl=None,
4485             default=None,
4486             optional=False,
4487             _decoded=(0, 0, 0),
4488     ):
4489         """
4490         :param value: set the value. Either ``(choice, value)`` tuple, or
4491                       :py:class:`pyderasn.Choice` object
4492         :param bytes impl: can not be set, do **not** use it
4493         :param bytes expl: override default tag with ``EXPLICIT`` one
4494         :param default: set default value. Type same as in ``value``
4495         :param bool optional: is object ``OPTIONAL`` in sequence
4496         """
4497         if impl is not None:
4498             raise ValueError("no implicit tag allowed for CHOICE")
4499         super(Choice, self).__init__(None, expl, default, optional, _decoded)
4500         if schema is None:
4501             schema = getattr(self, "schema", ())
4502         if len(schema) == 0:
4503             raise ValueError("schema must be specified")
4504         self.specs = (
4505             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4506         )
4507         self._value = None
4508         if value is not None:
4509             self._value = self._value_sanitize(value)
4510         if default is not None:
4511             default_value = self._value_sanitize(default)
4512             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4513             default_obj.specs = self.specs
4514             default_obj._value = default_value
4515             self.default = default_obj
4516             if value is None:
4517                 self._value = copy(default_obj._value)
4518
4519     def _value_sanitize(self, value):
4520         if isinstance(value, tuple) and len(value) == 2:
4521             choice, obj = value
4522             spec = self.specs.get(choice)
4523             if spec is None:
4524                 raise ObjUnknown(choice)
4525             if not isinstance(obj, spec.__class__):
4526                 raise InvalidValueType((spec,))
4527             return (choice, spec(obj))
4528         if isinstance(value, self.__class__):
4529             return value._value
4530         raise InvalidValueType((self.__class__, tuple))
4531
4532     @property
4533     def ready(self):
4534         return self._value is not None and self._value[1].ready
4535
4536     @property
4537     def bered(self):
4538         return self.expl_lenindef or (
4539             (self._value is not None) and
4540             self._value[1].bered
4541         )
4542
4543     def __getstate__(self):
4544         return ChoiceState(
4545             __version__,
4546             self.specs,
4547             copy(self._value),
4548             self.tag,
4549             self._expl,
4550             self.default,
4551             self.optional,
4552             self.offset,
4553             self.llen,
4554             self.vlen,
4555             self.expl_lenindef,
4556             self.lenindef,
4557             self.ber_encoded,
4558         )
4559
4560     def __setstate__(self, state):
4561         super(Choice, self).__setstate__(state)
4562         self.specs = state.specs
4563         self._value = state.value
4564         self._expl = state.expl
4565         self.default = state.default
4566         self.optional = state.optional
4567         self.offset = state.offset
4568         self.llen = state.llen
4569         self.vlen = state.vlen
4570         self.expl_lenindef = state.expl_lenindef
4571         self.lenindef = state.lenindef
4572         self.ber_encoded = state.ber_encoded
4573
4574     def __eq__(self, their):
4575         if isinstance(their, tuple) and len(their) == 2:
4576             return self._value == their
4577         if not isinstance(their, self.__class__):
4578             return False
4579         return (
4580             self.specs == their.specs and
4581             self._value == their._value
4582         )
4583
4584     def __call__(
4585             self,
4586             value=None,
4587             expl=None,
4588             default=None,
4589             optional=None,
4590     ):
4591         return self.__class__(
4592             value=value,
4593             schema=self.specs,
4594             expl=self._expl if expl is None else expl,
4595             default=self.default if default is None else default,
4596             optional=self.optional if optional is None else optional,
4597         )
4598
4599     @property
4600     def choice(self):
4601         self._assert_ready()
4602         return self._value[0]
4603
4604     @property
4605     def value(self):
4606         self._assert_ready()
4607         return self._value[1]
4608
4609     def __getitem__(self, key):
4610         if key not in self.specs:
4611             raise ObjUnknown(key)
4612         if self._value is None:
4613             return None
4614         choice, value = self._value
4615         if choice != key:
4616             return None
4617         return value
4618
4619     def __setitem__(self, key, value):
4620         spec = self.specs.get(key)
4621         if spec is None:
4622             raise ObjUnknown(key)
4623         if not isinstance(value, spec.__class__):
4624             raise InvalidValueType((spec.__class__,))
4625         self._value = (key, spec(value))
4626
4627     @property
4628     def tlen(self):
4629         return 0
4630
4631     @property
4632     def decoded(self):
4633         return self._value[1].decoded if self.ready else False
4634
4635     def _encode(self):
4636         self._assert_ready()
4637         return self._value[1].encode()
4638
4639     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4640         for choice, spec in iteritems(self.specs):
4641             sub_decode_path = decode_path + (choice,)
4642             try:
4643                 spec.decode(
4644                     tlv,
4645                     offset=offset,
4646                     leavemm=True,
4647                     decode_path=sub_decode_path,
4648                     ctx=ctx,
4649                     tag_only=True,
4650                     _ctx_immutable=False,
4651                 )
4652             except TagMismatch:
4653                 continue
4654             break
4655         else:
4656             raise TagMismatch(
4657                 klass=self.__class__,
4658                 decode_path=decode_path,
4659                 offset=offset,
4660             )
4661         if tag_only:  # pragma: no cover
4662             return None
4663         value, tail = spec.decode(
4664             tlv,
4665             offset=offset,
4666             leavemm=True,
4667             decode_path=sub_decode_path,
4668             ctx=ctx,
4669             _ctx_immutable=False,
4670         )
4671         obj = self.__class__(
4672             schema=self.specs,
4673             expl=self._expl,
4674             default=self.default,
4675             optional=self.optional,
4676             _decoded=(offset, 0, value.fulllen),
4677         )
4678         obj._value = (choice, value)
4679         return obj, tail
4680
4681     def __repr__(self):
4682         value = pp_console_row(next(self.pps()))
4683         if self.ready:
4684             value = "%s[%r]" % (value, self.value)
4685         return value
4686
4687     def pps(self, decode_path=()):
4688         yield _pp(
4689             obj=self,
4690             asn1_type_name=self.asn1_type_name,
4691             obj_name=self.__class__.__name__,
4692             decode_path=decode_path,
4693             value=self.choice if self.ready else None,
4694             optional=self.optional,
4695             default=self == self.default,
4696             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4697             expl=None if self._expl is None else tag_decode(self._expl),
4698             offset=self.offset,
4699             tlen=self.tlen,
4700             llen=self.llen,
4701             vlen=self.vlen,
4702             expl_lenindef=self.expl_lenindef,
4703             bered=self.bered,
4704         )
4705         if self.ready:
4706             yield self.value.pps(decode_path=decode_path + (self.choice,))
4707         for pp in self.pps_lenindef(decode_path):
4708             yield pp
4709
4710
4711 class PrimitiveTypes(Choice):
4712     """Predefined ``CHOICE`` for all generic primitive types
4713
4714     It could be useful for general decoding of some unspecified values:
4715
4716     >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
4717     OCTET STRING 3 bytes 666f6f
4718     >>> PrimitiveTypes().decod(hexdec("0203123456")).value
4719     INTEGER 1193046
4720     """
4721     __slots__ = ()
4722     schema = tuple((klass.__name__, klass()) for klass in (
4723         Boolean,
4724         Integer,
4725         BitString,
4726         OctetString,
4727         Null,
4728         ObjectIdentifier,
4729         UTF8String,
4730         NumericString,
4731         PrintableString,
4732         TeletexString,
4733         VideotexString,
4734         IA5String,
4735         UTCTime,
4736         GeneralizedTime,
4737         GraphicString,
4738         VisibleString,
4739         ISO646String,
4740         GeneralString,
4741         UniversalString,
4742         BMPString,
4743     ))
4744
4745
4746 AnyState = namedtuple("AnyState", (
4747     "version",
4748     "value",
4749     "tag",
4750     "expl",
4751     "optional",
4752     "offset",
4753     "llen",
4754     "vlen",
4755     "expl_lenindef",
4756     "lenindef",
4757     "ber_encoded",
4758     "defined",
4759 ))
4760
4761
4762 class Any(Obj):
4763     """``ANY`` special type
4764
4765     >>> Any(Integer(-123))
4766     ANY 020185
4767     >>> a = Any(OctetString(b"hello world").encode())
4768     ANY 040b68656c6c6f20776f726c64
4769     >>> hexenc(bytes(a))
4770     b'0x040x0bhello world'
4771     """
4772     __slots__ = ("defined",)
4773     tag_default = tag_encode(0)
4774     asn1_type_name = "ANY"
4775
4776     def __init__(
4777             self,
4778             value=None,
4779             expl=None,
4780             optional=False,
4781             _decoded=(0, 0, 0),
4782     ):
4783         """
4784         :param value: set the value. Either any kind of pyderasn's
4785                       **ready** object, or bytes. Pay attention that
4786                       **no** validation is performed is raw binary value
4787                       is valid TLV
4788         :param bytes expl: override default tag with ``EXPLICIT`` one
4789         :param bool optional: is object ``OPTIONAL`` in sequence
4790         """
4791         super(Any, self).__init__(None, expl, None, optional, _decoded)
4792         self._value = None if value is None else self._value_sanitize(value)
4793         self.defined = None
4794
4795     def _value_sanitize(self, value):
4796         if isinstance(value, binary_type):
4797             return value
4798         if isinstance(value, self.__class__):
4799             return value._value
4800         if isinstance(value, Obj):
4801             return value.encode()
4802         raise InvalidValueType((self.__class__, Obj, binary_type))
4803
4804     @property
4805     def ready(self):
4806         return self._value is not None
4807
4808     @property
4809     def bered(self):
4810         if self.expl_lenindef or self.lenindef:
4811             return True
4812         if self.defined is None:
4813             return False
4814         return self.defined[1].bered
4815
4816     def __getstate__(self):
4817         return AnyState(
4818             __version__,
4819             self._value,
4820             self.tag,
4821             self._expl,
4822             self.optional,
4823             self.offset,
4824             self.llen,
4825             self.vlen,
4826             self.expl_lenindef,
4827             self.lenindef,
4828             self.ber_encoded,
4829             self.defined,
4830         )
4831
4832     def __setstate__(self, state):
4833         super(Any, self).__setstate__(state)
4834         self._value = state.value
4835         self.tag = state.tag
4836         self._expl = state.expl
4837         self.optional = state.optional
4838         self.offset = state.offset
4839         self.llen = state.llen
4840         self.vlen = state.vlen
4841         self.expl_lenindef = state.expl_lenindef
4842         self.lenindef = state.lenindef
4843         self.ber_encoded = state.ber_encoded
4844         self.defined = state.defined
4845
4846     def __eq__(self, their):
4847         if isinstance(their, binary_type):
4848             return self._value == their
4849         if issubclass(their.__class__, Any):
4850             return self._value == their._value
4851         return False
4852
4853     def __call__(
4854             self,
4855             value=None,
4856             expl=None,
4857             optional=None,
4858     ):
4859         return self.__class__(
4860             value=value,
4861             expl=self._expl if expl is None else expl,
4862             optional=self.optional if optional is None else optional,
4863         )
4864
4865     def __bytes__(self):
4866         self._assert_ready()
4867         return self._value
4868
4869     @property
4870     def tlen(self):
4871         return 0
4872
4873     def _encode(self):
4874         self._assert_ready()
4875         return self._value
4876
4877     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4878         try:
4879             t, tlen, lv = tag_strip(tlv)
4880         except DecodeError as err:
4881             raise err.__class__(
4882                 msg=err.msg,
4883                 klass=self.__class__,
4884                 decode_path=decode_path,
4885                 offset=offset,
4886             )
4887         try:
4888             l, llen, v = len_decode(lv)
4889         except LenIndefForm as err:
4890             if not ctx.get("bered", False):
4891                 raise err.__class__(
4892                     msg=err.msg,
4893                     klass=self.__class__,
4894                     decode_path=decode_path,
4895                     offset=offset,
4896                 )
4897             llen, vlen, v = 1, 0, lv[1:]
4898             sub_offset = offset + tlen + llen
4899             chunk_i = 0
4900             while v[:EOC_LEN].tobytes() != EOC:
4901                 chunk, v = Any().decode(
4902                     v,
4903                     offset=sub_offset,
4904                     decode_path=decode_path + (str(chunk_i),),
4905                     leavemm=True,
4906                     ctx=ctx,
4907                     _ctx_immutable=False,
4908                 )
4909                 vlen += chunk.tlvlen
4910                 sub_offset += chunk.tlvlen
4911                 chunk_i += 1
4912             tlvlen = tlen + llen + vlen + EOC_LEN
4913             obj = self.__class__(
4914                 value=tlv[:tlvlen].tobytes(),
4915                 expl=self._expl,
4916                 optional=self.optional,
4917                 _decoded=(offset, 0, tlvlen),
4918             )
4919             obj.lenindef = True
4920             obj.tag = t.tobytes()
4921             return obj, v[EOC_LEN:]
4922         except DecodeError as err:
4923             raise err.__class__(
4924                 msg=err.msg,
4925                 klass=self.__class__,
4926                 decode_path=decode_path,
4927                 offset=offset,
4928             )
4929         if l > len(v):
4930             raise NotEnoughData(
4931                 "encoded length is longer than data",
4932                 klass=self.__class__,
4933                 decode_path=decode_path,
4934                 offset=offset,
4935             )
4936         tlvlen = tlen + llen + l
4937         v, tail = tlv[:tlvlen], v[l:]
4938         obj = self.__class__(
4939             value=v.tobytes(),
4940             expl=self._expl,
4941             optional=self.optional,
4942             _decoded=(offset, 0, tlvlen),
4943         )
4944         obj.tag = t.tobytes()
4945         return obj, tail
4946
4947     def __repr__(self):
4948         return pp_console_row(next(self.pps()))
4949
4950     def pps(self, decode_path=()):
4951         yield _pp(
4952             obj=self,
4953             asn1_type_name=self.asn1_type_name,
4954             obj_name=self.__class__.__name__,
4955             decode_path=decode_path,
4956             blob=self._value if self.ready else None,
4957             optional=self.optional,
4958             default=self == self.default,
4959             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4960             expl=None if self._expl is None else tag_decode(self._expl),
4961             offset=self.offset,
4962             tlen=self.tlen,
4963             llen=self.llen,
4964             vlen=self.vlen,
4965             expl_offset=self.expl_offset if self.expled else None,
4966             expl_tlen=self.expl_tlen if self.expled else None,
4967             expl_llen=self.expl_llen if self.expled else None,
4968             expl_vlen=self.expl_vlen if self.expled else None,
4969             expl_lenindef=self.expl_lenindef,
4970             lenindef=self.lenindef,
4971             bered=self.bered,
4972         )
4973         defined_by, defined = self.defined or (None, None)
4974         if defined_by is not None:
4975             yield defined.pps(
4976                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4977             )
4978         for pp in self.pps_lenindef(decode_path):
4979             yield pp
4980
4981
4982 ########################################################################
4983 # ASN.1 constructed types
4984 ########################################################################
4985
4986 def get_def_by_path(defines_by_path, sub_decode_path):
4987     """Get define by decode path
4988     """
4989     for path, define in defines_by_path:
4990         if len(path) != len(sub_decode_path):
4991             continue
4992         for p1, p2 in zip(path, sub_decode_path):
4993             if (p1 != any) and (p1 != p2):
4994                 break
4995         else:
4996             return define
4997
4998
4999 def abs_decode_path(decode_path, rel_path):
5000     """Create an absolute decode path from current and relative ones
5001
5002     :param decode_path: current decode path, starting point. Tuple of strings
5003     :param rel_path: relative path to ``decode_path``. Tuple of strings.
5004                      If first tuple's element is "/", then treat it as
5005                      an absolute path, ignoring ``decode_path`` as
5006                      starting point. Also this tuple can contain ".."
5007                      elements, stripping the leading element from
5008                      ``decode_path``
5009
5010     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
5011     ("foo", "bar", "baz", "whatever")
5012     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
5013     ("foo", "whatever")
5014     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
5015     ("baz", "whatever")
5016     """
5017     if rel_path[0] == "/":
5018         return rel_path[1:]
5019     if rel_path[0] == "..":
5020         return abs_decode_path(decode_path[:-1], rel_path[1:])
5021     return decode_path + rel_path
5022
5023
5024 SequenceState = namedtuple("SequenceState", (
5025     "version",
5026     "specs",
5027     "value",
5028     "tag",
5029     "expl",
5030     "default",
5031     "optional",
5032     "offset",
5033     "llen",
5034     "vlen",
5035     "expl_lenindef",
5036     "lenindef",
5037     "ber_encoded",
5038 ))
5039
5040
5041 class Sequence(Obj):
5042     """``SEQUENCE`` structure type
5043
5044     You have to make specification of sequence::
5045
5046         class Extension(Sequence):
5047             schema = (
5048                 ("extnID", ObjectIdentifier()),
5049                 ("critical", Boolean(default=False)),
5050                 ("extnValue", OctetString()),
5051             )
5052
5053     Then, you can work with it as with dictionary.
5054
5055     >>> ext = Extension()
5056     >>> Extension().specs
5057     OrderedDict([
5058         ('extnID', OBJECT IDENTIFIER),
5059         ('critical', BOOLEAN False OPTIONAL DEFAULT),
5060         ('extnValue', OCTET STRING),
5061     ])
5062     >>> ext["extnID"] = "1.2.3"
5063     Traceback (most recent call last):
5064     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5065     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5066
5067     You can determine if sequence is ready to be encoded:
5068
5069     >>> ext.ready
5070     False
5071     >>> ext.encode()
5072     Traceback (most recent call last):
5073     pyderasn.ObjNotReady: object is not ready: extnValue
5074     >>> ext["extnValue"] = OctetString(b"foobar")
5075     >>> ext.ready
5076     True
5077
5078     Value you want to assign, must have the same **type** as in
5079     corresponding specification, but it can have different tags,
5080     optional/default attributes -- they will be taken from specification
5081     automatically::
5082
5083         class TBSCertificate(Sequence):
5084             schema = (
5085                 ("version", Version(expl=tag_ctxc(0), default="v1")),
5086             [...]
5087
5088     >>> tbs = TBSCertificate()
5089     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5090
5091     Assign ``None`` to remove value from sequence.
5092
5093     You can set values in Sequence during its initialization:
5094
5095     >>> AlgorithmIdentifier((
5096         ("algorithm", ObjectIdentifier("1.2.3")),
5097         ("parameters", Any(Null()))
5098     ))
5099     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5100
5101     You can determine if value exists/set in the sequence and take its value:
5102
5103     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5104     (True, True, False)
5105     >>> ext["extnID"]
5106     OBJECT IDENTIFIER 1.2.3
5107
5108     But pay attention that if value has default, then it won't be (not
5109     in) in the sequence (because ``DEFAULT`` must not be encoded in
5110     DER), but you can read its value:
5111
5112     >>> "critical" in ext, ext["critical"]
5113     (False, BOOLEAN False)
5114     >>> ext["critical"] = Boolean(True)
5115     >>> "critical" in ext, ext["critical"]
5116     (True, BOOLEAN True)
5117
5118     All defaulted values are always optional.
5119
5120     .. _allow_default_values_ctx:
5121
5122     DER prohibits default value encoding and will raise an error if
5123     default value is unexpectedly met during decode.
5124     If :ref:`bered <bered_ctx>` context option is set, then no error
5125     will be raised, but ``bered`` attribute set. You can disable strict
5126     defaulted values existence validation by setting
5127     ``"allow_default_values": True`` :ref:`context <ctx>` option.
5128
5129     Two sequences are equal if they have equal specification (schema),
5130     implicit/explicit tagging and the same values.
5131     """
5132     __slots__ = ("specs",)
5133     tag_default = tag_encode(form=TagFormConstructed, num=16)
5134     asn1_type_name = "SEQUENCE"
5135
5136     def __init__(
5137             self,
5138             value=None,
5139             schema=None,
5140             impl=None,
5141             expl=None,
5142             default=None,
5143             optional=False,
5144             _decoded=(0, 0, 0),
5145     ):
5146         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5147         if schema is None:
5148             schema = getattr(self, "schema", ())
5149         self.specs = (
5150             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
5151         )
5152         self._value = {}
5153         if value is not None:
5154             if issubclass(value.__class__, Sequence):
5155                 self._value = value._value
5156             elif hasattr(value, "__iter__"):
5157                 for seq_key, seq_value in value:
5158                     self[seq_key] = seq_value
5159             else:
5160                 raise InvalidValueType((Sequence,))
5161         if default is not None:
5162             if not issubclass(default.__class__, Sequence):
5163                 raise InvalidValueType((Sequence,))
5164             default_value = default._value
5165             default_obj = self.__class__(impl=self.tag, expl=self._expl)
5166             default_obj.specs = self.specs
5167             default_obj._value = default_value
5168             self.default = default_obj
5169             if value is None:
5170                 self._value = copy(default_obj._value)
5171
5172     @property
5173     def ready(self):
5174         for name, spec in iteritems(self.specs):
5175             value = self._value.get(name)
5176             if value is None:
5177                 if spec.optional:
5178                     continue
5179                 return False
5180             if not value.ready:
5181                 return False
5182         return True
5183
5184     @property
5185     def bered(self):
5186         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5187             return True
5188         return any(value.bered for value in itervalues(self._value))
5189
5190     def __getstate__(self):
5191         return SequenceState(
5192             __version__,
5193             self.specs,
5194             {k: copy(v) for k, v in iteritems(self._value)},
5195             self.tag,
5196             self._expl,
5197             self.default,
5198             self.optional,
5199             self.offset,
5200             self.llen,
5201             self.vlen,
5202             self.expl_lenindef,
5203             self.lenindef,
5204             self.ber_encoded,
5205         )
5206
5207     def __setstate__(self, state):
5208         super(Sequence, self).__setstate__(state)
5209         self.specs = state.specs
5210         self._value = state.value
5211         self.tag = state.tag
5212         self._expl = state.expl
5213         self.default = state.default
5214         self.optional = state.optional
5215         self.offset = state.offset
5216         self.llen = state.llen
5217         self.vlen = state.vlen
5218         self.expl_lenindef = state.expl_lenindef
5219         self.lenindef = state.lenindef
5220         self.ber_encoded = state.ber_encoded
5221
5222     def __eq__(self, their):
5223         if not isinstance(their, self.__class__):
5224             return False
5225         return (
5226             self.specs == their.specs and
5227             self.tag == their.tag and
5228             self._expl == their._expl and
5229             self._value == their._value
5230         )
5231
5232     def __call__(
5233             self,
5234             value=None,
5235             impl=None,
5236             expl=None,
5237             default=None,
5238             optional=None,
5239     ):
5240         return self.__class__(
5241             value=value,
5242             schema=self.specs,
5243             impl=self.tag if impl is None else impl,
5244             expl=self._expl if expl is None else expl,
5245             default=self.default if default is None else default,
5246             optional=self.optional if optional is None else optional,
5247         )
5248
5249     def __contains__(self, key):
5250         return key in self._value
5251
5252     def __setitem__(self, key, value):
5253         spec = self.specs.get(key)
5254         if spec is None:
5255             raise ObjUnknown(key)
5256         if value is None:
5257             self._value.pop(key, None)
5258             return
5259         if not isinstance(value, spec.__class__):
5260             raise InvalidValueType((spec.__class__,))
5261         value = spec(value=value)
5262         if spec.default is not None and value == spec.default:
5263             self._value.pop(key, None)
5264             return
5265         self._value[key] = value
5266
5267     def __getitem__(self, key):
5268         value = self._value.get(key)
5269         if value is not None:
5270             return value
5271         spec = self.specs.get(key)
5272         if spec is None:
5273             raise ObjUnknown(key)
5274         if spec.default is not None:
5275             return spec.default
5276         return None
5277
5278     def _encoded_values(self):
5279         raws = []
5280         for name, spec in iteritems(self.specs):
5281             value = self._value.get(name)
5282             if value is None:
5283                 if spec.optional:
5284                     continue
5285                 raise ObjNotReady(name)
5286             raws.append(value.encode())
5287         return raws
5288
5289     def _encode(self):
5290         v = b"".join(self._encoded_values())
5291         return b"".join((self.tag, len_encode(len(v)), v))
5292
5293     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5294         try:
5295             t, tlen, lv = tag_strip(tlv)
5296         except DecodeError as err:
5297             raise err.__class__(
5298                 msg=err.msg,
5299                 klass=self.__class__,
5300                 decode_path=decode_path,
5301                 offset=offset,
5302             )
5303         if t != self.tag:
5304             raise TagMismatch(
5305                 klass=self.__class__,
5306                 decode_path=decode_path,
5307                 offset=offset,
5308             )
5309         if tag_only:  # pragma: no cover
5310             return None
5311         lenindef = False
5312         ctx_bered = ctx.get("bered", False)
5313         try:
5314             l, llen, v = len_decode(lv)
5315         except LenIndefForm as err:
5316             if not ctx_bered:
5317                 raise err.__class__(
5318                     msg=err.msg,
5319                     klass=self.__class__,
5320                     decode_path=decode_path,
5321                     offset=offset,
5322                 )
5323             l, llen, v = 0, 1, lv[1:]
5324             lenindef = True
5325         except DecodeError as err:
5326             raise err.__class__(
5327                 msg=err.msg,
5328                 klass=self.__class__,
5329                 decode_path=decode_path,
5330                 offset=offset,
5331             )
5332         if l > len(v):
5333             raise NotEnoughData(
5334                 "encoded length is longer than data",
5335                 klass=self.__class__,
5336                 decode_path=decode_path,
5337                 offset=offset,
5338             )
5339         if not lenindef:
5340             v, tail = v[:l], v[l:]
5341         vlen = 0
5342         sub_offset = offset + tlen + llen
5343         values = {}
5344         ber_encoded = False
5345         ctx_allow_default_values = ctx.get("allow_default_values", False)
5346         for name, spec in iteritems(self.specs):
5347             if spec.optional and (
5348                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5349                     len(v) == 0
5350             ):
5351                 continue
5352             sub_decode_path = decode_path + (name,)
5353             try:
5354                 value, v_tail = spec.decode(
5355                     v,
5356                     sub_offset,
5357                     leavemm=True,
5358                     decode_path=sub_decode_path,
5359                     ctx=ctx,
5360                     _ctx_immutable=False,
5361                 )
5362             except TagMismatch as err:
5363                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5364                     continue
5365                 raise
5366
5367             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5368             if defined is not None:
5369                 defined_by, defined_spec = defined
5370                 if issubclass(value.__class__, SequenceOf):
5371                     for i, _value in enumerate(value):
5372                         sub_sub_decode_path = sub_decode_path + (
5373                             str(i),
5374                             DecodePathDefBy(defined_by),
5375                         )
5376                         defined_value, defined_tail = defined_spec.decode(
5377                             memoryview(bytes(_value)),
5378                             sub_offset + (
5379                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5380                                 if value.expled else (value.tlen + value.llen)
5381                             ),
5382                             leavemm=True,
5383                             decode_path=sub_sub_decode_path,
5384                             ctx=ctx,
5385                             _ctx_immutable=False,
5386                         )
5387                         if len(defined_tail) > 0:
5388                             raise DecodeError(
5389                                 "remaining data",
5390                                 klass=self.__class__,
5391                                 decode_path=sub_sub_decode_path,
5392                                 offset=offset,
5393                             )
5394                         _value.defined = (defined_by, defined_value)
5395                 else:
5396                     defined_value, defined_tail = defined_spec.decode(
5397                         memoryview(bytes(value)),
5398                         sub_offset + (
5399                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5400                             if value.expled else (value.tlen + value.llen)
5401                         ),
5402                         leavemm=True,
5403                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5404                         ctx=ctx,
5405                         _ctx_immutable=False,
5406                     )
5407                     if len(defined_tail) > 0:
5408                         raise DecodeError(
5409                             "remaining data",
5410                             klass=self.__class__,
5411                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5412                             offset=offset,
5413                         )
5414                     value.defined = (defined_by, defined_value)
5415
5416             value_len = value.fulllen
5417             vlen += value_len
5418             sub_offset += value_len
5419             v = v_tail
5420             if spec.default is not None and value == spec.default:
5421                 if ctx_bered or ctx_allow_default_values:
5422                     ber_encoded = True
5423                 else:
5424                     raise DecodeError(
5425                         "DEFAULT value met",
5426                         klass=self.__class__,
5427                         decode_path=sub_decode_path,
5428                         offset=sub_offset,
5429                     )
5430             values[name] = value
5431
5432             spec_defines = getattr(spec, "defines", ())
5433             if len(spec_defines) == 0:
5434                 defines_by_path = ctx.get("defines_by_path", ())
5435                 if len(defines_by_path) > 0:
5436                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5437             if spec_defines is not None and len(spec_defines) > 0:
5438                 for rel_path, schema in spec_defines:
5439                     defined = schema.get(value, None)
5440                     if defined is not None:
5441                         ctx.setdefault("_defines", []).append((
5442                             abs_decode_path(sub_decode_path[:-1], rel_path),
5443                             (value, defined),
5444                         ))
5445         if lenindef:
5446             if v[:EOC_LEN].tobytes() != EOC:
5447                 raise DecodeError(
5448                     "no EOC",
5449                     klass=self.__class__,
5450                     decode_path=decode_path,
5451                     offset=offset,
5452                 )
5453             tail = v[EOC_LEN:]
5454             vlen += EOC_LEN
5455         elif len(v) > 0:
5456             raise DecodeError(
5457                 "remaining data",
5458                 klass=self.__class__,
5459                 decode_path=decode_path,
5460                 offset=offset,
5461             )
5462         obj = self.__class__(
5463             schema=self.specs,
5464             impl=self.tag,
5465             expl=self._expl,
5466             default=self.default,
5467             optional=self.optional,
5468             _decoded=(offset, llen, vlen),
5469         )
5470         obj._value = values
5471         obj.lenindef = lenindef
5472         obj.ber_encoded = ber_encoded
5473         return obj, tail
5474
5475     def __repr__(self):
5476         value = pp_console_row(next(self.pps()))
5477         cols = []
5478         for name in self.specs:
5479             _value = self._value.get(name)
5480             if _value is None:
5481                 continue
5482             cols.append("%s: %s" % (name, repr(_value)))
5483         return "%s[%s]" % (value, "; ".join(cols))
5484
5485     def pps(self, decode_path=()):
5486         yield _pp(
5487             obj=self,
5488             asn1_type_name=self.asn1_type_name,
5489             obj_name=self.__class__.__name__,
5490             decode_path=decode_path,
5491             optional=self.optional,
5492             default=self == self.default,
5493             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5494             expl=None if self._expl is None else tag_decode(self._expl),
5495             offset=self.offset,
5496             tlen=self.tlen,
5497             llen=self.llen,
5498             vlen=self.vlen,
5499             expl_offset=self.expl_offset if self.expled else None,
5500             expl_tlen=self.expl_tlen if self.expled else None,
5501             expl_llen=self.expl_llen if self.expled else None,
5502             expl_vlen=self.expl_vlen if self.expled else None,
5503             expl_lenindef=self.expl_lenindef,
5504             lenindef=self.lenindef,
5505             ber_encoded=self.ber_encoded,
5506             bered=self.bered,
5507         )
5508         for name in self.specs:
5509             value = self._value.get(name)
5510             if value is None:
5511                 continue
5512             yield value.pps(decode_path=decode_path + (name,))
5513         for pp in self.pps_lenindef(decode_path):
5514             yield pp
5515
5516
5517 class Set(Sequence):
5518     """``SET`` structure type
5519
5520     Its usage is identical to :py:class:`pyderasn.Sequence`.
5521
5522     .. _allow_unordered_set_ctx:
5523
5524     DER prohibits unordered values encoding and will raise an error
5525     during decode. If If :ref:`bered <bered_ctx>` context option is set,
5526     then no error will occure. Also you can disable strict values
5527     ordering check by setting ``"allow_unordered_set": True``
5528     :ref:`context <ctx>` option.
5529     """
5530     __slots__ = ()
5531     tag_default = tag_encode(form=TagFormConstructed, num=17)
5532     asn1_type_name = "SET"
5533
5534     def _encode(self):
5535         raws = self._encoded_values()
5536         raws.sort()
5537         v = b"".join(raws)
5538         return b"".join((self.tag, len_encode(len(v)), v))
5539
5540     def _specs_items(self):
5541         return iteritems(self.specs)
5542
5543     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5544         try:
5545             t, tlen, lv = tag_strip(tlv)
5546         except DecodeError as err:
5547             raise err.__class__(
5548                 msg=err.msg,
5549                 klass=self.__class__,
5550                 decode_path=decode_path,
5551                 offset=offset,
5552             )
5553         if t != self.tag:
5554             raise TagMismatch(
5555                 klass=self.__class__,
5556                 decode_path=decode_path,
5557                 offset=offset,
5558             )
5559         if tag_only:
5560             return None
5561         lenindef = False
5562         ctx_bered = ctx.get("bered", False)
5563         try:
5564             l, llen, v = len_decode(lv)
5565         except LenIndefForm as err:
5566             if not ctx_bered:
5567                 raise err.__class__(
5568                     msg=err.msg,
5569                     klass=self.__class__,
5570                     decode_path=decode_path,
5571                     offset=offset,
5572                 )
5573             l, llen, v = 0, 1, lv[1:]
5574             lenindef = True
5575         except DecodeError as err:
5576             raise err.__class__(
5577                 msg=err.msg,
5578                 klass=self.__class__,
5579                 decode_path=decode_path,
5580                 offset=offset,
5581             )
5582         if l > len(v):
5583             raise NotEnoughData(
5584                 "encoded length is longer than data",
5585                 klass=self.__class__,
5586                 offset=offset,
5587             )
5588         if not lenindef:
5589             v, tail = v[:l], v[l:]
5590         vlen = 0
5591         sub_offset = offset + tlen + llen
5592         values = {}
5593         ber_encoded = False
5594         ctx_allow_default_values = ctx.get("allow_default_values", False)
5595         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5596         value_prev = memoryview(v[:0])
5597
5598         while len(v) > 0:
5599             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5600                 break
5601             for name, spec in self._specs_items():
5602                 sub_decode_path = decode_path + (name,)
5603                 try:
5604                     spec.decode(
5605                         v,
5606                         sub_offset,
5607                         leavemm=True,
5608                         decode_path=sub_decode_path,
5609                         ctx=ctx,
5610                         tag_only=True,
5611                         _ctx_immutable=False,
5612                     )
5613                 except TagMismatch:
5614                     continue
5615                 break
5616             else:
5617                 raise TagMismatch(
5618                     klass=self.__class__,
5619                     decode_path=decode_path,
5620                     offset=offset,
5621                 )
5622             value, v_tail = spec.decode(
5623                 v,
5624                 sub_offset,
5625                 leavemm=True,
5626                 decode_path=sub_decode_path,
5627                 ctx=ctx,
5628                 _ctx_immutable=False,
5629             )
5630             value_len = value.fulllen
5631             if value_prev.tobytes() > v[:value_len].tobytes():
5632                 if ctx_bered or ctx_allow_unordered_set:
5633                     ber_encoded = True
5634                 else:
5635                     raise DecodeError(
5636                         "unordered " + self.asn1_type_name,
5637                         klass=self.__class__,
5638                         decode_path=sub_decode_path,
5639                         offset=sub_offset,
5640                     )
5641             if spec.default is None or value != spec.default:
5642                 pass
5643             elif ctx_bered or ctx_allow_default_values:
5644                 ber_encoded = True
5645             else:
5646                 raise DecodeError(
5647                     "DEFAULT value met",
5648                     klass=self.__class__,
5649                     decode_path=sub_decode_path,
5650                     offset=sub_offset,
5651                 )
5652             values[name] = value
5653             value_prev = v[:value_len]
5654             sub_offset += value_len
5655             vlen += value_len
5656             v = v_tail
5657         obj = self.__class__(
5658             schema=self.specs,
5659             impl=self.tag,
5660             expl=self._expl,
5661             default=self.default,
5662             optional=self.optional,
5663             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5664         )
5665         if lenindef:
5666             if v[:EOC_LEN].tobytes() != EOC:
5667                 raise DecodeError(
5668                     "no EOC",
5669                     klass=self.__class__,
5670                     decode_path=decode_path,
5671                     offset=offset,
5672                 )
5673             tail = v[EOC_LEN:]
5674             obj.lenindef = True
5675         obj._value = values
5676         if not obj.ready:
5677             raise DecodeError(
5678                 "not all values are ready",
5679                 klass=self.__class__,
5680                 decode_path=decode_path,
5681                 offset=offset,
5682             )
5683         obj.ber_encoded = ber_encoded
5684         return obj, tail
5685
5686
5687 SequenceOfState = namedtuple("SequenceOfState", (
5688     "version",
5689     "spec",
5690     "value",
5691     "bound_min",
5692     "bound_max",
5693     "tag",
5694     "expl",
5695     "default",
5696     "optional",
5697     "offset",
5698     "llen",
5699     "vlen",
5700     "expl_lenindef",
5701     "lenindef",
5702     "ber_encoded",
5703 ))
5704
5705
5706 class SequenceOf(Obj):
5707     """``SEQUENCE OF`` sequence type
5708
5709     For that kind of type you must specify the object it will carry on
5710     (bounds are for example here, not required)::
5711
5712         class Ints(SequenceOf):
5713             schema = Integer()
5714             bounds = (0, 2)
5715
5716     >>> ints = Ints()
5717     >>> ints.append(Integer(123))
5718     >>> ints.append(Integer(234))
5719     >>> ints
5720     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5721     >>> [int(i) for i in ints]
5722     [123, 234]
5723     >>> ints.append(Integer(345))
5724     Traceback (most recent call last):
5725     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5726     >>> ints[1]
5727     INTEGER 234
5728     >>> ints[1] = Integer(345)
5729     >>> ints
5730     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5731
5732     Also you can initialize sequence with preinitialized values:
5733
5734     >>> ints = Ints([Integer(123), Integer(234)])
5735     """
5736     __slots__ = ("spec", "_bound_min", "_bound_max")
5737     tag_default = tag_encode(form=TagFormConstructed, num=16)
5738     asn1_type_name = "SEQUENCE OF"
5739
5740     def __init__(
5741             self,
5742             value=None,
5743             schema=None,
5744             bounds=None,
5745             impl=None,
5746             expl=None,
5747             default=None,
5748             optional=False,
5749             _decoded=(0, 0, 0),
5750     ):
5751         super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
5752         if schema is None:
5753             schema = getattr(self, "schema", None)
5754         if schema is None:
5755             raise ValueError("schema must be specified")
5756         self.spec = schema
5757         self._bound_min, self._bound_max = getattr(
5758             self,
5759             "bounds",
5760             (0, float("+inf")),
5761         ) if bounds is None else bounds
5762         self._value = []
5763         if value is not None:
5764             self._value = self._value_sanitize(value)
5765         if default is not None:
5766             default_value = self._value_sanitize(default)
5767             default_obj = self.__class__(
5768                 schema=schema,
5769                 impl=self.tag,
5770                 expl=self._expl,
5771             )
5772             default_obj._value = default_value
5773             self.default = default_obj
5774             if value is None:
5775                 self._value = copy(default_obj._value)
5776
5777     def _value_sanitize(self, value):
5778         if issubclass(value.__class__, SequenceOf):
5779             value = value._value
5780         elif hasattr(value, "__iter__"):
5781             value = list(value)
5782         else:
5783             raise InvalidValueType((self.__class__, iter))
5784         if not self._bound_min <= len(value) <= self._bound_max:
5785             raise BoundsError(self._bound_min, len(value), self._bound_max)
5786         for v in value:
5787             if not isinstance(v, self.spec.__class__):
5788                 raise InvalidValueType((self.spec.__class__,))
5789         return value
5790
5791     @property
5792     def ready(self):
5793         return all(v.ready for v in self._value)
5794
5795     @property
5796     def bered(self):
5797         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5798             return True
5799         return any(v.bered for v in self._value)
5800
5801     def __getstate__(self):
5802         return SequenceOfState(
5803             __version__,
5804             self.spec,
5805             [copy(v) for v in self._value],
5806             self._bound_min,
5807             self._bound_max,
5808             self.tag,
5809             self._expl,
5810             self.default,
5811             self.optional,
5812             self.offset,
5813             self.llen,
5814             self.vlen,
5815             self.expl_lenindef,
5816             self.lenindef,
5817             self.ber_encoded,
5818         )
5819
5820     def __setstate__(self, state):
5821         super(SequenceOf, self).__setstate__(state)
5822         self.spec = state.spec
5823         self._value = state.value
5824         self._bound_min = state.bound_min
5825         self._bound_max = state.bound_max
5826         self.tag = state.tag
5827         self._expl = state.expl
5828         self.default = state.default
5829         self.optional = state.optional
5830         self.offset = state.offset
5831         self.llen = state.llen
5832         self.vlen = state.vlen
5833         self.expl_lenindef = state.expl_lenindef
5834         self.lenindef = state.lenindef
5835         self.ber_encoded = state.ber_encoded
5836
5837     def __eq__(self, their):
5838         if isinstance(their, self.__class__):
5839             return (
5840                 self.spec == their.spec and
5841                 self.tag == their.tag and
5842                 self._expl == their._expl and
5843                 self._value == their._value
5844             )
5845         if hasattr(their, "__iter__"):
5846             return self._value == list(their)
5847         return False
5848
5849     def __call__(
5850             self,
5851             value=None,
5852             bounds=None,
5853             impl=None,
5854             expl=None,
5855             default=None,
5856             optional=None,
5857     ):
5858         return self.__class__(
5859             value=value,
5860             schema=self.spec,
5861             bounds=(
5862                 (self._bound_min, self._bound_max)
5863                 if bounds is None else bounds
5864             ),
5865             impl=self.tag if impl is None else impl,
5866             expl=self._expl if expl is None else expl,
5867             default=self.default if default is None else default,
5868             optional=self.optional if optional is None else optional,
5869         )
5870
5871     def __contains__(self, key):
5872         return key in self._value
5873
5874     def append(self, value):
5875         if not isinstance(value, self.spec.__class__):
5876             raise InvalidValueType((self.spec.__class__,))
5877         if len(self._value) + 1 > self._bound_max:
5878             raise BoundsError(
5879                 self._bound_min,
5880                 len(self._value) + 1,
5881                 self._bound_max,
5882             )
5883         self._value.append(value)
5884
5885     def __iter__(self):
5886         self._assert_ready()
5887         return iter(self._value)
5888
5889     def __len__(self):
5890         self._assert_ready()
5891         return len(self._value)
5892
5893     def __setitem__(self, key, value):
5894         if not isinstance(value, self.spec.__class__):
5895             raise InvalidValueType((self.spec.__class__,))
5896         self._value[key] = self.spec(value=value)
5897
5898     def __getitem__(self, key):
5899         return self._value[key]
5900
5901     def _encoded_values(self):
5902         return [v.encode() for v in self._value]
5903
5904     def _encode(self):
5905         v = b"".join(self._encoded_values())
5906         return b"".join((self.tag, len_encode(len(v)), v))
5907
5908     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5909         try:
5910             t, tlen, lv = tag_strip(tlv)
5911         except DecodeError as err:
5912             raise err.__class__(
5913                 msg=err.msg,
5914                 klass=self.__class__,
5915                 decode_path=decode_path,
5916                 offset=offset,
5917             )
5918         if t != self.tag:
5919             raise TagMismatch(
5920                 klass=self.__class__,
5921                 decode_path=decode_path,
5922                 offset=offset,
5923             )
5924         if tag_only:
5925             return None
5926         lenindef = False
5927         ctx_bered = ctx.get("bered", False)
5928         try:
5929             l, llen, v = len_decode(lv)
5930         except LenIndefForm as err:
5931             if not ctx_bered:
5932                 raise err.__class__(
5933                     msg=err.msg,
5934                     klass=self.__class__,
5935                     decode_path=decode_path,
5936                     offset=offset,
5937                 )
5938             l, llen, v = 0, 1, lv[1:]
5939             lenindef = True
5940         except DecodeError as err:
5941             raise err.__class__(
5942                 msg=err.msg,
5943                 klass=self.__class__,
5944                 decode_path=decode_path,
5945                 offset=offset,
5946             )
5947         if l > len(v):
5948             raise NotEnoughData(
5949                 "encoded length is longer than data",
5950                 klass=self.__class__,
5951                 decode_path=decode_path,
5952                 offset=offset,
5953             )
5954         if not lenindef:
5955             v, tail = v[:l], v[l:]
5956         vlen = 0
5957         sub_offset = offset + tlen + llen
5958         _value = []
5959         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5960         value_prev = memoryview(v[:0])
5961         ber_encoded = False
5962         spec = self.spec
5963         while len(v) > 0:
5964             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5965                 break
5966             sub_decode_path = decode_path + (str(len(_value)),)
5967             value, v_tail = spec.decode(
5968                 v,
5969                 sub_offset,
5970                 leavemm=True,
5971                 decode_path=sub_decode_path,
5972                 ctx=ctx,
5973                 _ctx_immutable=False,
5974             )
5975             value_len = value.fulllen
5976             if ordering_check:
5977                 if value_prev.tobytes() > v[:value_len].tobytes():
5978                     if ctx_bered or ctx_allow_unordered_set:
5979                         ber_encoded = True
5980                     else:
5981                         raise DecodeError(
5982                             "unordered " + self.asn1_type_name,
5983                             klass=self.__class__,
5984                             decode_path=sub_decode_path,
5985                             offset=sub_offset,
5986                         )
5987                 value_prev = v[:value_len]
5988             _value.append(value)
5989             sub_offset += value_len
5990             vlen += value_len
5991             v = v_tail
5992         try:
5993             obj = self.__class__(
5994                 value=_value,
5995                 schema=spec,
5996                 bounds=(self._bound_min, self._bound_max),
5997                 impl=self.tag,
5998                 expl=self._expl,
5999                 default=self.default,
6000                 optional=self.optional,
6001                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6002             )
6003         except BoundsError as err:
6004             raise DecodeError(
6005                 msg=str(err),
6006                 klass=self.__class__,
6007                 decode_path=decode_path,
6008                 offset=offset,
6009             )
6010         if lenindef:
6011             if v[:EOC_LEN].tobytes() != EOC:
6012                 raise DecodeError(
6013                     "no EOC",
6014                     klass=self.__class__,
6015                     decode_path=decode_path,
6016                     offset=offset,
6017                 )
6018             obj.lenindef = True
6019             tail = v[EOC_LEN:]
6020         obj.ber_encoded = ber_encoded
6021         return obj, tail
6022
6023     def __repr__(self):
6024         return "%s[%s]" % (
6025             pp_console_row(next(self.pps())),
6026             ", ".join(repr(v) for v in self._value),
6027         )
6028
6029     def pps(self, decode_path=()):
6030         yield _pp(
6031             obj=self,
6032             asn1_type_name=self.asn1_type_name,
6033             obj_name=self.__class__.__name__,
6034             decode_path=decode_path,
6035             optional=self.optional,
6036             default=self == self.default,
6037             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6038             expl=None if self._expl is None else tag_decode(self._expl),
6039             offset=self.offset,
6040             tlen=self.tlen,
6041             llen=self.llen,
6042             vlen=self.vlen,
6043             expl_offset=self.expl_offset if self.expled else None,
6044             expl_tlen=self.expl_tlen if self.expled else None,
6045             expl_llen=self.expl_llen if self.expled else None,
6046             expl_vlen=self.expl_vlen if self.expled else None,
6047             expl_lenindef=self.expl_lenindef,
6048             lenindef=self.lenindef,
6049             ber_encoded=self.ber_encoded,
6050             bered=self.bered,
6051         )
6052         for i, value in enumerate(self._value):
6053             yield value.pps(decode_path=decode_path + (str(i),))
6054         for pp in self.pps_lenindef(decode_path):
6055             yield pp
6056
6057
6058 class SetOf(SequenceOf):
6059     """``SET OF`` sequence type
6060
6061     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6062     """
6063     __slots__ = ()
6064     tag_default = tag_encode(form=TagFormConstructed, num=17)
6065     asn1_type_name = "SET OF"
6066
6067     def _encode(self):
6068         raws = self._encoded_values()
6069         raws.sort()
6070         v = b"".join(raws)
6071         return b"".join((self.tag, len_encode(len(v)), v))
6072
6073     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
6074         return super(SetOf, self)._decode(
6075             tlv,
6076             offset,
6077             decode_path,
6078             ctx,
6079             tag_only,
6080             ordering_check=True,
6081         )
6082
6083
6084 def obj_by_path(pypath):  # pragma: no cover
6085     """Import object specified as string Python path
6086
6087     Modules must be separated from classes/functions with ``:``.
6088
6089     >>> obj_by_path("foo.bar:Baz")
6090     <class 'foo.bar.Baz'>
6091     >>> obj_by_path("foo.bar:Baz.boo")
6092     <classmethod 'foo.bar.Baz.boo'>
6093     """
6094     mod, objs = pypath.rsplit(":", 1)
6095     from importlib import import_module
6096     obj = import_module(mod)
6097     for obj_name in objs.split("."):
6098         obj = getattr(obj, obj_name)
6099     return obj
6100
6101
6102 def generic_decoder():  # pragma: no cover
6103     # All of this below is a big hack with self references
6104     choice = PrimitiveTypes()
6105     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6106     choice.specs["SetOf"] = SetOf(schema=choice)
6107     for i in six_xrange(31):
6108         choice.specs["SequenceOf%d" % i] = SequenceOf(
6109             schema=choice,
6110             expl=tag_ctxc(i),
6111         )
6112     choice.specs["Any"] = Any()
6113
6114     # Class name equals to type name, to omit it from output
6115     class SEQUENCEOF(SequenceOf):
6116         __slots__ = ()
6117         schema = choice
6118
6119     def pprint_any(
6120             obj,
6121             oid_maps=(),
6122             with_colours=False,
6123             with_decode_path=False,
6124             decode_path_only=(),
6125     ):
6126         def _pprint_pps(pps):
6127             for pp in pps:
6128                 if hasattr(pp, "_fields"):
6129                     if (
6130                             decode_path_only != () and
6131                             pp.decode_path[:len(decode_path_only)] != decode_path_only
6132                     ):
6133                         continue
6134                     if pp.asn1_type_name == Choice.asn1_type_name:
6135                         continue
6136                     pp_kwargs = pp._asdict()
6137                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6138                     pp = _pp(**pp_kwargs)
6139                     yield pp_console_row(
6140                         pp,
6141                         oid_maps=oid_maps,
6142                         with_offsets=True,
6143                         with_blob=False,
6144                         with_colours=with_colours,
6145                         with_decode_path=with_decode_path,
6146                         decode_path_len_decrease=len(decode_path_only),
6147                     )
6148                     for row in pp_console_blob(
6149                             pp,
6150                             decode_path_len_decrease=len(decode_path_only),
6151                     ):
6152                         yield row
6153                 else:
6154                     for row in _pprint_pps(pp):
6155                         yield row
6156         return "\n".join(_pprint_pps(obj.pps()))
6157     return SEQUENCEOF(), pprint_any
6158
6159
6160 def main():  # pragma: no cover
6161     import argparse
6162     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6163     parser.add_argument(
6164         "--skip",
6165         type=int,
6166         default=0,
6167         help="Skip that number of bytes from the beginning",
6168     )
6169     parser.add_argument(
6170         "--oids",
6171         help="Python paths to dictionary with OIDs, comma separated",
6172     )
6173     parser.add_argument(
6174         "--schema",
6175         help="Python path to schema definition to use",
6176     )
6177     parser.add_argument(
6178         "--defines-by-path",
6179         help="Python path to decoder's defines_by_path",
6180     )
6181     parser.add_argument(
6182         "--nobered",
6183         action="store_true",
6184         help="Disallow BER encoding",
6185     )
6186     parser.add_argument(
6187         "--print-decode-path",
6188         action="store_true",
6189         help="Print decode paths",
6190     )
6191     parser.add_argument(
6192         "--decode-path-only",
6193         help="Print only specified decode path",
6194     )
6195     parser.add_argument(
6196         "--allow-expl-oob",
6197         action="store_true",
6198         help="Allow explicit tag out-of-bound",
6199     )
6200     parser.add_argument(
6201         "DERFile",
6202         type=argparse.FileType("rb"),
6203         help="Path to DER file you want to decode",
6204     )
6205     args = parser.parse_args()
6206     args.DERFile.seek(args.skip)
6207     der = memoryview(args.DERFile.read())
6208     args.DERFile.close()
6209     oid_maps = (
6210         [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6211         if args.oids else ()
6212     )
6213     if args.schema:
6214         schema = obj_by_path(args.schema)
6215         from functools import partial
6216         pprinter = partial(pprint, big_blobs=True)
6217     else:
6218         schema, pprinter = generic_decoder()
6219     ctx = {
6220         "bered": not args.nobered,
6221         "allow_expl_oob": args.allow_expl_oob,
6222     }
6223     if args.defines_by_path is not None:
6224         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6225     obj, tail = schema().decode(der, ctx=ctx)
6226     print(pprinter(
6227         obj,
6228         oid_maps=oid_maps,
6229         with_colours=environ.get("NO_COLOR") is None,
6230         with_decode_path=args.print_decode_path,
6231         decode_path_only=(
6232             () if args.decode_path_only is None else
6233             tuple(args.decode_path_only.split(":"))
6234         ),
6235     ))
6236     if tail != b"":
6237         print("\nTrailing data: %s" % hexenc(tail))
6238
6239
6240 if __name__ == "__main__":
6241     main()