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