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