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