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