]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
CHOICE should proxy lenindef/bered attributed of underlying object
[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``, ``SET OF`` can contain it.
388 * If object has an indefinite length encoding, then its ``lenindef``
389   attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
390   ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
391   contain it.
392 * If object has an indefinite length encoded explicit tag, then
393   ``expl_lenindef`` is set to True.
394
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         obj.lenindef = value.lenindef
3966         obj.bered = value.bered
3967         return obj, tail
3968
3969     def __repr__(self):
3970         value = pp_console_row(next(self.pps()))
3971         if self.ready:
3972             value = "%s[%r]" % (value, self.value)
3973         return value
3974
3975     def pps(self, decode_path=()):
3976         yield _pp(
3977             asn1_type_name=self.asn1_type_name,
3978             obj_name=self.__class__.__name__,
3979             decode_path=decode_path,
3980             value=self.choice if self.ready else None,
3981             optional=self.optional,
3982             default=self == self.default,
3983             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3984             expl=None if self._expl is None else tag_decode(self._expl),
3985             offset=self.offset,
3986             tlen=self.tlen,
3987             llen=self.llen,
3988             vlen=self.vlen,
3989             expl_lenindef=self.expl_lenindef,
3990             lenindef=self.lenindef,
3991             bered=self.bered,
3992         )
3993         if self.ready:
3994             yield self.value.pps(decode_path=decode_path + (self.choice,))
3995         for pp in self.pps_lenindef(decode_path):
3996             yield pp
3997
3998
3999 class PrimitiveTypes(Choice):
4000     """Predefined ``CHOICE`` for all generic primitive types
4001
4002     It could be useful for general decoding of some unspecified values:
4003
4004     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4005     OCTET STRING 3 bytes 666f6f
4006     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4007     INTEGER 1193046
4008     """
4009     __slots__ = ()
4010     schema = tuple((klass.__name__, klass()) for klass in (
4011         Boolean,
4012         Integer,
4013         BitString,
4014         OctetString,
4015         Null,
4016         ObjectIdentifier,
4017         UTF8String,
4018         NumericString,
4019         PrintableString,
4020         TeletexString,
4021         VideotexString,
4022         IA5String,
4023         UTCTime,
4024         GeneralizedTime,
4025         GraphicString,
4026         VisibleString,
4027         ISO646String,
4028         GeneralString,
4029         UniversalString,
4030         BMPString,
4031     ))
4032
4033
4034 class Any(Obj):
4035     """``ANY`` special type
4036
4037     >>> Any(Integer(-123))
4038     ANY 020185
4039     >>> a = Any(OctetString(b"hello world").encode())
4040     ANY 040b68656c6c6f20776f726c64
4041     >>> hexenc(bytes(a))
4042     b'0x040x0bhello world'
4043     """
4044     __slots__ = ("defined",)
4045     tag_default = tag_encode(0)
4046     asn1_type_name = "ANY"
4047
4048     def __init__(
4049             self,
4050             value=None,
4051             expl=None,
4052             optional=False,
4053             _decoded=(0, 0, 0),
4054     ):
4055         """
4056         :param value: set the value. Either any kind of pyderasn's
4057                       **ready** object, or bytes. Pay attention that
4058                       **no** validation is performed is raw binary value
4059                       is valid TLV
4060         :param bytes expl: override default tag with ``EXPLICIT`` one
4061         :param bool optional: is object ``OPTIONAL`` in sequence
4062         """
4063         super(Any, self).__init__(None, expl, None, optional, _decoded)
4064         self._value = None if value is None else self._value_sanitize(value)
4065         self.defined = None
4066
4067     def _value_sanitize(self, value):
4068         if isinstance(value, self.__class__):
4069             return value._value
4070         if isinstance(value, Obj):
4071             return value.encode()
4072         if isinstance(value, binary_type):
4073             return value
4074         raise InvalidValueType((self.__class__, Obj, binary_type))
4075
4076     @property
4077     def ready(self):
4078         return self._value is not None
4079
4080     def copy(self):
4081         obj = self.__class__()
4082         obj._value = self._value
4083         obj.tag = self.tag
4084         obj._expl = self._expl
4085         obj.optional = self.optional
4086         obj.offset = self.offset
4087         obj.llen = self.llen
4088         obj.vlen = self.vlen
4089         return obj
4090
4091     def __eq__(self, their):
4092         if isinstance(their, binary_type):
4093             return self._value == their
4094         if issubclass(their.__class__, Any):
4095             return self._value == their._value
4096         return False
4097
4098     def __call__(
4099             self,
4100             value=None,
4101             expl=None,
4102             optional=None,
4103     ):
4104         return self.__class__(
4105             value=value,
4106             expl=self._expl if expl is None else expl,
4107             optional=self.optional if optional is None else optional,
4108         )
4109
4110     def __bytes__(self):
4111         self._assert_ready()
4112         return self._value
4113
4114     @property
4115     def tlen(self):
4116         return 0
4117
4118     def _encode(self):
4119         self._assert_ready()
4120         return self._value
4121
4122     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4123         try:
4124             t, tlen, lv = tag_strip(tlv)
4125         except DecodeError as err:
4126             raise err.__class__(
4127                 msg=err.msg,
4128                 klass=self.__class__,
4129                 decode_path=decode_path,
4130                 offset=offset,
4131             )
4132         try:
4133             l, llen, v = len_decode(lv)
4134         except LenIndefForm as err:
4135             if not ctx.get("bered", False):
4136                 raise err.__class__(
4137                     msg=err.msg,
4138                     klass=self.__class__,
4139                     decode_path=decode_path,
4140                     offset=offset,
4141                 )
4142             llen, vlen, v = 1, 0, lv[1:]
4143             sub_offset = offset + tlen + llen
4144             chunk_i = 0
4145             while v[:EOC_LEN].tobytes() != EOC:
4146                 chunk, v = Any().decode(
4147                     v,
4148                     offset=sub_offset,
4149                     decode_path=decode_path + (str(chunk_i),),
4150                     leavemm=True,
4151                     ctx=ctx,
4152                 )
4153                 vlen += chunk.tlvlen
4154                 sub_offset += chunk.tlvlen
4155                 chunk_i += 1
4156             tlvlen = tlen + llen + vlen + EOC_LEN
4157             obj = self.__class__(
4158                 value=tlv[:tlvlen].tobytes(),
4159                 expl=self._expl,
4160                 optional=self.optional,
4161                 _decoded=(offset, 0, tlvlen),
4162             )
4163             obj.lenindef = True
4164             obj.tag = t
4165             return obj, v[EOC_LEN:]
4166         except DecodeError as err:
4167             raise err.__class__(
4168                 msg=err.msg,
4169                 klass=self.__class__,
4170                 decode_path=decode_path,
4171                 offset=offset,
4172             )
4173         if l > len(v):
4174             raise NotEnoughData(
4175                 "encoded length is longer than data",
4176                 klass=self.__class__,
4177                 decode_path=decode_path,
4178                 offset=offset,
4179             )
4180         tlvlen = tlen + llen + l
4181         v, tail = tlv[:tlvlen], v[l:]
4182         obj = self.__class__(
4183             value=v.tobytes(),
4184             expl=self._expl,
4185             optional=self.optional,
4186             _decoded=(offset, 0, tlvlen),
4187         )
4188         obj.tag = t
4189         return obj, tail
4190
4191     def __repr__(self):
4192         return pp_console_row(next(self.pps()))
4193
4194     def pps(self, decode_path=()):
4195         yield _pp(
4196             asn1_type_name=self.asn1_type_name,
4197             obj_name=self.__class__.__name__,
4198             decode_path=decode_path,
4199             blob=self._value if self.ready else None,
4200             optional=self.optional,
4201             default=self == self.default,
4202             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4203             expl=None if self._expl is None else tag_decode(self._expl),
4204             offset=self.offset,
4205             tlen=self.tlen,
4206             llen=self.llen,
4207             vlen=self.vlen,
4208             expl_offset=self.expl_offset if self.expled else None,
4209             expl_tlen=self.expl_tlen if self.expled else None,
4210             expl_llen=self.expl_llen if self.expled else None,
4211             expl_vlen=self.expl_vlen if self.expled else None,
4212             expl_lenindef=self.expl_lenindef,
4213             lenindef=self.lenindef,
4214         )
4215         defined_by, defined = self.defined or (None, None)
4216         if defined_by is not None:
4217             yield defined.pps(
4218                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4219             )
4220         for pp in self.pps_lenindef(decode_path):
4221             yield pp
4222
4223
4224 ########################################################################
4225 # ASN.1 constructed types
4226 ########################################################################
4227
4228 def get_def_by_path(defines_by_path, sub_decode_path):
4229     """Get define by decode path
4230     """
4231     for path, define in defines_by_path:
4232         if len(path) != len(sub_decode_path):
4233             continue
4234         for p1, p2 in zip(path, sub_decode_path):
4235             if (p1 != any) and (p1 != p2):
4236                 break
4237         else:
4238             return define
4239
4240
4241 def abs_decode_path(decode_path, rel_path):
4242     """Create an absolute decode path from current and relative ones
4243
4244     :param decode_path: current decode path, starting point.
4245                         Tuple of strings
4246     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4247                      If first tuple's element is "/", then treat it as
4248                      an absolute path, ignoring ``decode_path`` as
4249                      starting point. Also this tuple can contain ".."
4250                      elements, stripping the leading element from
4251                      ``decode_path``
4252
4253     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4254     ("foo", "bar", "baz", "whatever")
4255     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4256     ("foo", "whatever")
4257     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4258     ("baz", "whatever")
4259     """
4260     if rel_path[0] == "/":
4261         return rel_path[1:]
4262     if rel_path[0] == "..":
4263         return abs_decode_path(decode_path[:-1], rel_path[1:])
4264     return decode_path + rel_path
4265
4266
4267 class Sequence(Obj):
4268     """``SEQUENCE`` structure type
4269
4270     You have to make specification of sequence::
4271
4272         class Extension(Sequence):
4273             schema = (
4274                 ("extnID", ObjectIdentifier()),
4275                 ("critical", Boolean(default=False)),
4276                 ("extnValue", OctetString()),
4277             )
4278
4279     Then, you can work with it as with dictionary.
4280
4281     >>> ext = Extension()
4282     >>> Extension().specs
4283     OrderedDict([
4284         ('extnID', OBJECT IDENTIFIER),
4285         ('critical', BOOLEAN False OPTIONAL DEFAULT),
4286         ('extnValue', OCTET STRING),
4287     ])
4288     >>> ext["extnID"] = "1.2.3"
4289     Traceback (most recent call last):
4290     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4291     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4292
4293     You can determine if sequence is ready to be encoded:
4294
4295     >>> ext.ready
4296     False
4297     >>> ext.encode()
4298     Traceback (most recent call last):
4299     pyderasn.ObjNotReady: object is not ready: extnValue
4300     >>> ext["extnValue"] = OctetString(b"foobar")
4301     >>> ext.ready
4302     True
4303
4304     Value you want to assign, must have the same **type** as in
4305     corresponding specification, but it can have different tags,
4306     optional/default attributes -- they will be taken from specification
4307     automatically::
4308
4309         class TBSCertificate(Sequence):
4310             schema = (
4311                 ("version", Version(expl=tag_ctxc(0), default="v1")),
4312             [...]
4313
4314     >>> tbs = TBSCertificate()
4315     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4316
4317     Assign ``None`` to remove value from sequence.
4318
4319     You can set values in Sequence during its initialization:
4320
4321     >>> AlgorithmIdentifier((
4322         ("algorithm", ObjectIdentifier("1.2.3")),
4323         ("parameters", Any(Null()))
4324     ))
4325     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4326
4327     You can determine if value exists/set in the sequence and take its value:
4328
4329     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4330     (True, True, False)
4331     >>> ext["extnID"]
4332     OBJECT IDENTIFIER 1.2.3
4333
4334     But pay attention that if value has default, then it won't be (not
4335     in) in the sequence (because ``DEFAULT`` must not be encoded in
4336     DER), but you can read its value:
4337
4338     >>> "critical" in ext, ext["critical"]
4339     (False, BOOLEAN False)
4340     >>> ext["critical"] = Boolean(True)
4341     >>> "critical" in ext, ext["critical"]
4342     (True, BOOLEAN True)
4343
4344     All defaulted values are always optional.
4345
4346     .. _allow_default_values_ctx:
4347
4348     DER prohibits default value encoding and will raise an error if
4349     default value is unexpectedly met during decode.
4350     If :ref:`bered <bered_ctx>` context option is set, then no error
4351     will be raised, but ``bered`` attribute set. You can disable strict
4352     defaulted values existence validation by setting
4353     ``"allow_default_values": True`` :ref:`context <ctx>` option.
4354
4355     Two sequences are equal if they have equal specification (schema),
4356     implicit/explicit tagging and the same values.
4357     """
4358     __slots__ = ("specs",)
4359     tag_default = tag_encode(form=TagFormConstructed, num=16)
4360     asn1_type_name = "SEQUENCE"
4361
4362     def __init__(
4363             self,
4364             value=None,
4365             schema=None,
4366             impl=None,
4367             expl=None,
4368             default=None,
4369             optional=False,
4370             _decoded=(0, 0, 0),
4371     ):
4372         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4373         if schema is None:
4374             schema = getattr(self, "schema", ())
4375         self.specs = (
4376             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4377         )
4378         self._value = {}
4379         if value is not None:
4380             if issubclass(value.__class__, Sequence):
4381                 self._value = value._value
4382             elif hasattr(value, "__iter__"):
4383                 for seq_key, seq_value in value:
4384                     self[seq_key] = seq_value
4385             else:
4386                 raise InvalidValueType((Sequence,))
4387         if default is not None:
4388             if not issubclass(default.__class__, Sequence):
4389                 raise InvalidValueType((Sequence,))
4390             default_value = default._value
4391             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4392             default_obj.specs = self.specs
4393             default_obj._value = default_value
4394             self.default = default_obj
4395             if value is None:
4396                 self._value = default_obj.copy()._value
4397
4398     @property
4399     def ready(self):
4400         for name, spec in self.specs.items():
4401             value = self._value.get(name)
4402             if value is None:
4403                 if spec.optional:
4404                     continue
4405                 return False
4406             else:
4407                 if not value.ready:
4408                     return False
4409         return True
4410
4411     def copy(self):
4412         obj = self.__class__(schema=self.specs)
4413         obj.tag = self.tag
4414         obj._expl = self._expl
4415         obj.default = self.default
4416         obj.optional = self.optional
4417         obj.offset = self.offset
4418         obj.llen = self.llen
4419         obj.vlen = self.vlen
4420         obj._value = {k: v.copy() for k, v in self._value.items()}
4421         return obj
4422
4423     def __eq__(self, their):
4424         if not isinstance(their, self.__class__):
4425             return False
4426         return (
4427             self.specs == their.specs and
4428             self.tag == their.tag and
4429             self._expl == their._expl and
4430             self._value == their._value
4431         )
4432
4433     def __call__(
4434             self,
4435             value=None,
4436             impl=None,
4437             expl=None,
4438             default=None,
4439             optional=None,
4440     ):
4441         return self.__class__(
4442             value=value,
4443             schema=self.specs,
4444             impl=self.tag if impl is None else impl,
4445             expl=self._expl if expl is None else expl,
4446             default=self.default if default is None else default,
4447             optional=self.optional if optional is None else optional,
4448         )
4449
4450     def __contains__(self, key):
4451         return key in self._value
4452
4453     def __setitem__(self, key, value):
4454         spec = self.specs.get(key)
4455         if spec is None:
4456             raise ObjUnknown(key)
4457         if value is None:
4458             self._value.pop(key, None)
4459             return
4460         if not isinstance(value, spec.__class__):
4461             raise InvalidValueType((spec.__class__,))
4462         value = spec(value=value)
4463         if spec.default is not None and value == spec.default:
4464             self._value.pop(key, None)
4465             return
4466         self._value[key] = value
4467
4468     def __getitem__(self, key):
4469         value = self._value.get(key)
4470         if value is not None:
4471             return value
4472         spec = self.specs.get(key)
4473         if spec is None:
4474             raise ObjUnknown(key)
4475         if spec.default is not None:
4476             return spec.default
4477         return None
4478
4479     def _encoded_values(self):
4480         raws = []
4481         for name, spec in self.specs.items():
4482             value = self._value.get(name)
4483             if value is None:
4484                 if spec.optional:
4485                     continue
4486                 raise ObjNotReady(name)
4487             raws.append(value.encode())
4488         return raws
4489
4490     def _encode(self):
4491         v = b"".join(self._encoded_values())
4492         return b"".join((self.tag, len_encode(len(v)), v))
4493
4494     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4495         try:
4496             t, tlen, lv = tag_strip(tlv)
4497         except DecodeError as err:
4498             raise err.__class__(
4499                 msg=err.msg,
4500                 klass=self.__class__,
4501                 decode_path=decode_path,
4502                 offset=offset,
4503             )
4504         if t != self.tag:
4505             raise TagMismatch(
4506                 klass=self.__class__,
4507                 decode_path=decode_path,
4508                 offset=offset,
4509             )
4510         if tag_only:
4511             return
4512         lenindef = False
4513         ctx_bered = ctx.get("bered", False)
4514         try:
4515             l, llen, v = len_decode(lv)
4516         except LenIndefForm as err:
4517             if not ctx_bered:
4518                 raise err.__class__(
4519                     msg=err.msg,
4520                     klass=self.__class__,
4521                     decode_path=decode_path,
4522                     offset=offset,
4523                 )
4524             l, llen, v = 0, 1, lv[1:]
4525             lenindef = True
4526         except DecodeError as err:
4527             raise err.__class__(
4528                 msg=err.msg,
4529                 klass=self.__class__,
4530                 decode_path=decode_path,
4531                 offset=offset,
4532             )
4533         if l > len(v):
4534             raise NotEnoughData(
4535                 "encoded length is longer than data",
4536                 klass=self.__class__,
4537                 decode_path=decode_path,
4538                 offset=offset,
4539             )
4540         if not lenindef:
4541             v, tail = v[:l], v[l:]
4542         vlen = 0
4543         sub_offset = offset + tlen + llen
4544         values = {}
4545         bered = False
4546         ctx_allow_default_values = ctx.get("allow_default_values", False)
4547         for name, spec in self.specs.items():
4548             if spec.optional and (
4549                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4550                     len(v) == 0
4551             ):
4552                 continue
4553             sub_decode_path = decode_path + (name,)
4554             try:
4555                 value, v_tail = spec.decode(
4556                     v,
4557                     sub_offset,
4558                     leavemm=True,
4559                     decode_path=sub_decode_path,
4560                     ctx=ctx,
4561                 )
4562             except TagMismatch:
4563                 if spec.optional:
4564                     continue
4565                 raise
4566
4567             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4568             if defined is not None:
4569                 defined_by, defined_spec = defined
4570                 if issubclass(value.__class__, SequenceOf):
4571                     for i, _value in enumerate(value):
4572                         sub_sub_decode_path = sub_decode_path + (
4573                             str(i),
4574                             DecodePathDefBy(defined_by),
4575                         )
4576                         defined_value, defined_tail = defined_spec.decode(
4577                             memoryview(bytes(_value)),
4578                             sub_offset + (
4579                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4580                                 if value.expled else (value.tlen + value.llen)
4581                             ),
4582                             leavemm=True,
4583                             decode_path=sub_sub_decode_path,
4584                             ctx=ctx,
4585                         )
4586                         if len(defined_tail) > 0:
4587                             raise DecodeError(
4588                                 "remaining data",
4589                                 klass=self.__class__,
4590                                 decode_path=sub_sub_decode_path,
4591                                 offset=offset,
4592                             )
4593                         _value.defined = (defined_by, defined_value)
4594                 else:
4595                     defined_value, defined_tail = defined_spec.decode(
4596                         memoryview(bytes(value)),
4597                         sub_offset + (
4598                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4599                             if value.expled else (value.tlen + value.llen)
4600                         ),
4601                         leavemm=True,
4602                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4603                         ctx=ctx,
4604                     )
4605                     if len(defined_tail) > 0:
4606                         raise DecodeError(
4607                             "remaining data",
4608                             klass=self.__class__,
4609                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4610                             offset=offset,
4611                         )
4612                     value.defined = (defined_by, defined_value)
4613
4614             value_len = value.fulllen
4615             vlen += value_len
4616             sub_offset += value_len
4617             v = v_tail
4618             if spec.default is not None and value == spec.default:
4619                 if ctx_bered or ctx_allow_default_values:
4620                     bered = True
4621                 else:
4622                     raise DecodeError(
4623                         "DEFAULT value met",
4624                         klass=self.__class__,
4625                         decode_path=sub_decode_path,
4626                         offset=sub_offset,
4627                     )
4628             values[name] = value
4629
4630             spec_defines = getattr(spec, "defines", ())
4631             if len(spec_defines) == 0:
4632                 defines_by_path = ctx.get("defines_by_path", ())
4633                 if len(defines_by_path) > 0:
4634                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4635             if spec_defines is not None and len(spec_defines) > 0:
4636                 for rel_path, schema in spec_defines:
4637                     defined = schema.get(value, None)
4638                     if defined is not None:
4639                         ctx.setdefault("_defines", []).append((
4640                             abs_decode_path(sub_decode_path[:-1], rel_path),
4641                             (value, defined),
4642                         ))
4643         if lenindef:
4644             if v[:EOC_LEN].tobytes() != EOC:
4645                 raise DecodeError(
4646                     "no EOC",
4647                     klass=self.__class__,
4648                     decode_path=decode_path,
4649                     offset=offset,
4650                 )
4651             tail = v[EOC_LEN:]
4652             vlen += EOC_LEN
4653         elif len(v) > 0:
4654             raise DecodeError(
4655                 "remaining data",
4656                 klass=self.__class__,
4657                 decode_path=decode_path,
4658                 offset=offset,
4659             )
4660         obj = self.__class__(
4661             schema=self.specs,
4662             impl=self.tag,
4663             expl=self._expl,
4664             default=self.default,
4665             optional=self.optional,
4666             _decoded=(offset, llen, vlen),
4667         )
4668         obj._value = values
4669         obj.lenindef = lenindef
4670         obj.bered = bered
4671         return obj, tail
4672
4673     def __repr__(self):
4674         value = pp_console_row(next(self.pps()))
4675         cols = []
4676         for name in self.specs:
4677             _value = self._value.get(name)
4678             if _value is None:
4679                 continue
4680             cols.append("%s: %s" % (name, repr(_value)))
4681         return "%s[%s]" % (value, "; ".join(cols))
4682
4683     def pps(self, decode_path=()):
4684         yield _pp(
4685             asn1_type_name=self.asn1_type_name,
4686             obj_name=self.__class__.__name__,
4687             decode_path=decode_path,
4688             optional=self.optional,
4689             default=self == self.default,
4690             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4691             expl=None if self._expl is None else tag_decode(self._expl),
4692             offset=self.offset,
4693             tlen=self.tlen,
4694             llen=self.llen,
4695             vlen=self.vlen,
4696             expl_offset=self.expl_offset if self.expled else None,
4697             expl_tlen=self.expl_tlen if self.expled else None,
4698             expl_llen=self.expl_llen if self.expled else None,
4699             expl_vlen=self.expl_vlen if self.expled else None,
4700             expl_lenindef=self.expl_lenindef,
4701             lenindef=self.lenindef,
4702         )
4703         for name in self.specs:
4704             value = self._value.get(name)
4705             if value is None:
4706                 continue
4707             yield value.pps(decode_path=decode_path + (name,))
4708         for pp in self.pps_lenindef(decode_path):
4709             yield pp
4710
4711
4712 class Set(Sequence):
4713     """``SET`` structure type
4714
4715     Its usage is identical to :py:class:`pyderasn.Sequence`.
4716
4717     .. _allow_unordered_set_ctx:
4718
4719     DER prohibits unordered values encoding and will raise an error
4720     during decode. If If :ref:`bered <bered_ctx>` context option is set,
4721     then no error will occure. Also you can disable strict values
4722     ordering check by setting ``"allow_unordered_set": True``
4723     :ref:`context <ctx>` option.
4724     """
4725     __slots__ = ()
4726     tag_default = tag_encode(form=TagFormConstructed, num=17)
4727     asn1_type_name = "SET"
4728
4729     def _encode(self):
4730         raws = self._encoded_values()
4731         raws.sort()
4732         v = b"".join(raws)
4733         return b"".join((self.tag, len_encode(len(v)), v))
4734
4735     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4736         try:
4737             t, tlen, lv = tag_strip(tlv)
4738         except DecodeError as err:
4739             raise err.__class__(
4740                 msg=err.msg,
4741                 klass=self.__class__,
4742                 decode_path=decode_path,
4743                 offset=offset,
4744             )
4745         if t != self.tag:
4746             raise TagMismatch(
4747                 klass=self.__class__,
4748                 decode_path=decode_path,
4749                 offset=offset,
4750             )
4751         if tag_only:
4752             return
4753         lenindef = False
4754         ctx_bered = ctx.get("bered", False)
4755         try:
4756             l, llen, v = len_decode(lv)
4757         except LenIndefForm as err:
4758             if not ctx_bered:
4759                 raise err.__class__(
4760                     msg=err.msg,
4761                     klass=self.__class__,
4762                     decode_path=decode_path,
4763                     offset=offset,
4764                 )
4765             l, llen, v = 0, 1, lv[1:]
4766             lenindef = True
4767         except DecodeError as err:
4768             raise err.__class__(
4769                 msg=err.msg,
4770                 klass=self.__class__,
4771                 decode_path=decode_path,
4772                 offset=offset,
4773             )
4774         if l > len(v):
4775             raise NotEnoughData(
4776                 "encoded length is longer than data",
4777                 klass=self.__class__,
4778                 offset=offset,
4779             )
4780         if not lenindef:
4781             v, tail = v[:l], v[l:]
4782         vlen = 0
4783         sub_offset = offset + tlen + llen
4784         values = {}
4785         bered = False
4786         ctx_allow_default_values = ctx.get("allow_default_values", False)
4787         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4788         value_prev = memoryview(v[:0])
4789         specs_items = self.specs.items
4790         while len(v) > 0:
4791             if lenindef and v[:EOC_LEN].tobytes() == EOC:
4792                 break
4793             for name, spec in specs_items():
4794                 sub_decode_path = decode_path + (name,)
4795                 try:
4796                     spec.decode(
4797                         v,
4798                         sub_offset,
4799                         leavemm=True,
4800                         decode_path=sub_decode_path,
4801                         ctx=ctx,
4802                         tag_only=True,
4803                     )
4804                 except TagMismatch:
4805                     continue
4806                 break
4807             else:
4808                 raise TagMismatch(
4809                     klass=self.__class__,
4810                     decode_path=decode_path,
4811                     offset=offset,
4812                 )
4813             value, v_tail = spec.decode(
4814                 v,
4815                 sub_offset,
4816                 leavemm=True,
4817                 decode_path=sub_decode_path,
4818                 ctx=ctx,
4819             )
4820             value_len = value.fulllen
4821             if value_prev.tobytes() > v[:value_len].tobytes():
4822                 if ctx_bered or ctx_allow_unordered_set:
4823                     bered = True
4824                 else:
4825                     raise DecodeError(
4826                         "unordered " + self.asn1_type_name,
4827                         klass=self.__class__,
4828                         decode_path=sub_decode_path,
4829                         offset=sub_offset,
4830                     )
4831             if spec.default is None or value != spec.default:
4832                 pass
4833             elif ctx_bered or ctx_allow_default_values:
4834                 bered = True
4835             else:
4836                 raise DecodeError(
4837                     "DEFAULT value met",
4838                     klass=self.__class__,
4839                     decode_path=sub_decode_path,
4840                     offset=sub_offset,
4841                 )
4842             values[name] = value
4843             value_prev = v[:value_len]
4844             sub_offset += value_len
4845             vlen += value_len
4846             v = v_tail
4847         obj = self.__class__(
4848             schema=self.specs,
4849             impl=self.tag,
4850             expl=self._expl,
4851             default=self.default,
4852             optional=self.optional,
4853             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4854         )
4855         if lenindef:
4856             if v[:EOC_LEN].tobytes() != EOC:
4857                 raise DecodeError(
4858                     "no EOC",
4859                     klass=self.__class__,
4860                     decode_path=decode_path,
4861                     offset=offset,
4862                 )
4863             tail = v[EOC_LEN:]
4864             obj.lenindef = True
4865         obj._value = values
4866         if not obj.ready:
4867             raise DecodeError(
4868                 "not all values are ready",
4869                 klass=self.__class__,
4870                 decode_path=decode_path,
4871                 offset=offset,
4872             )
4873         obj.bered = bered
4874         return obj, tail
4875
4876
4877 class SequenceOf(Obj):
4878     """``SEQUENCE OF`` sequence type
4879
4880     For that kind of type you must specify the object it will carry on
4881     (bounds are for example here, not required)::
4882
4883         class Ints(SequenceOf):
4884             schema = Integer()
4885             bounds = (0, 2)
4886
4887     >>> ints = Ints()
4888     >>> ints.append(Integer(123))
4889     >>> ints.append(Integer(234))
4890     >>> ints
4891     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4892     >>> [int(i) for i in ints]
4893     [123, 234]
4894     >>> ints.append(Integer(345))
4895     Traceback (most recent call last):
4896     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4897     >>> ints[1]
4898     INTEGER 234
4899     >>> ints[1] = Integer(345)
4900     >>> ints
4901     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4902
4903     Also you can initialize sequence with preinitialized values:
4904
4905     >>> ints = Ints([Integer(123), Integer(234)])
4906     """
4907     __slots__ = ("spec", "_bound_min", "_bound_max")
4908     tag_default = tag_encode(form=TagFormConstructed, num=16)
4909     asn1_type_name = "SEQUENCE OF"
4910
4911     def __init__(
4912             self,
4913             value=None,
4914             schema=None,
4915             bounds=None,
4916             impl=None,
4917             expl=None,
4918             default=None,
4919             optional=False,
4920             _decoded=(0, 0, 0),
4921     ):
4922         super(SequenceOf, self).__init__(
4923             impl,
4924             expl,
4925             default,
4926             optional,
4927             _decoded,
4928         )
4929         if schema is None:
4930             schema = getattr(self, "schema", None)
4931         if schema is None:
4932             raise ValueError("schema must be specified")
4933         self.spec = schema
4934         self._bound_min, self._bound_max = getattr(
4935             self,
4936             "bounds",
4937             (0, float("+inf")),
4938         ) if bounds is None else bounds
4939         self._value = []
4940         if value is not None:
4941             self._value = self._value_sanitize(value)
4942         if default is not None:
4943             default_value = self._value_sanitize(default)
4944             default_obj = self.__class__(
4945                 schema=schema,
4946                 impl=self.tag,
4947                 expl=self._expl,
4948             )
4949             default_obj._value = default_value
4950             self.default = default_obj
4951             if value is None:
4952                 self._value = default_obj.copy()._value
4953
4954     def _value_sanitize(self, value):
4955         if issubclass(value.__class__, SequenceOf):
4956             value = value._value
4957         elif hasattr(value, "__iter__"):
4958             value = list(value)
4959         else:
4960             raise InvalidValueType((self.__class__, iter))
4961         if not self._bound_min <= len(value) <= self._bound_max:
4962             raise BoundsError(self._bound_min, len(value), self._bound_max)
4963         for v in value:
4964             if not isinstance(v, self.spec.__class__):
4965                 raise InvalidValueType((self.spec.__class__,))
4966         return value
4967
4968     @property
4969     def ready(self):
4970         return all(v.ready for v in self._value)
4971
4972     def copy(self):
4973         obj = self.__class__(schema=self.spec)
4974         obj._bound_min = self._bound_min
4975         obj._bound_max = self._bound_max
4976         obj.tag = self.tag
4977         obj._expl = self._expl
4978         obj.default = self.default
4979         obj.optional = self.optional
4980         obj.offset = self.offset
4981         obj.llen = self.llen
4982         obj.vlen = self.vlen
4983         obj._value = [v.copy() for v in self._value]
4984         return obj
4985
4986     def __eq__(self, their):
4987         if isinstance(their, self.__class__):
4988             return (
4989                 self.spec == their.spec and
4990                 self.tag == their.tag and
4991                 self._expl == their._expl and
4992                 self._value == their._value
4993             )
4994         if hasattr(their, "__iter__"):
4995             return self._value == list(their)
4996         return False
4997
4998     def __call__(
4999             self,
5000             value=None,
5001             bounds=None,
5002             impl=None,
5003             expl=None,
5004             default=None,
5005             optional=None,
5006     ):
5007         return self.__class__(
5008             value=value,
5009             schema=self.spec,
5010             bounds=(
5011                 (self._bound_min, self._bound_max)
5012                 if bounds is None else bounds
5013             ),
5014             impl=self.tag if impl is None else impl,
5015             expl=self._expl if expl is None else expl,
5016             default=self.default if default is None else default,
5017             optional=self.optional if optional is None else optional,
5018         )
5019
5020     def __contains__(self, key):
5021         return key in self._value
5022
5023     def append(self, value):
5024         if not isinstance(value, self.spec.__class__):
5025             raise InvalidValueType((self.spec.__class__,))
5026         if len(self._value) + 1 > self._bound_max:
5027             raise BoundsError(
5028                 self._bound_min,
5029                 len(self._value) + 1,
5030                 self._bound_max,
5031             )
5032         self._value.append(value)
5033
5034     def __iter__(self):
5035         self._assert_ready()
5036         return iter(self._value)
5037
5038     def __len__(self):
5039         self._assert_ready()
5040         return len(self._value)
5041
5042     def __setitem__(self, key, value):
5043         if not isinstance(value, self.spec.__class__):
5044             raise InvalidValueType((self.spec.__class__,))
5045         self._value[key] = self.spec(value=value)
5046
5047     def __getitem__(self, key):
5048         return self._value[key]
5049
5050     def _encoded_values(self):
5051         return [v.encode() for v in self._value]
5052
5053     def _encode(self):
5054         v = b"".join(self._encoded_values())
5055         return b"".join((self.tag, len_encode(len(v)), v))
5056
5057     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5058         try:
5059             t, tlen, lv = tag_strip(tlv)
5060         except DecodeError as err:
5061             raise err.__class__(
5062                 msg=err.msg,
5063                 klass=self.__class__,
5064                 decode_path=decode_path,
5065                 offset=offset,
5066             )
5067         if t != self.tag:
5068             raise TagMismatch(
5069                 klass=self.__class__,
5070                 decode_path=decode_path,
5071                 offset=offset,
5072             )
5073         if tag_only:
5074             return
5075         lenindef = False
5076         ctx_bered = ctx.get("bered", False)
5077         try:
5078             l, llen, v = len_decode(lv)
5079         except LenIndefForm as err:
5080             if not ctx_bered:
5081                 raise err.__class__(
5082                     msg=err.msg,
5083                     klass=self.__class__,
5084                     decode_path=decode_path,
5085                     offset=offset,
5086                 )
5087             l, llen, v = 0, 1, lv[1:]
5088             lenindef = True
5089         except DecodeError as err:
5090             raise err.__class__(
5091                 msg=err.msg,
5092                 klass=self.__class__,
5093                 decode_path=decode_path,
5094                 offset=offset,
5095             )
5096         if l > len(v):
5097             raise NotEnoughData(
5098                 "encoded length is longer than data",
5099                 klass=self.__class__,
5100                 decode_path=decode_path,
5101                 offset=offset,
5102             )
5103         if not lenindef:
5104             v, tail = v[:l], v[l:]
5105         vlen = 0
5106         sub_offset = offset + tlen + llen
5107         _value = []
5108         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5109         value_prev = memoryview(v[:0])
5110         bered = False
5111         spec = self.spec
5112         while len(v) > 0:
5113             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5114                 break
5115             sub_decode_path = decode_path + (str(len(_value)),)
5116             value, v_tail = spec.decode(
5117                 v,
5118                 sub_offset,
5119                 leavemm=True,
5120                 decode_path=sub_decode_path,
5121                 ctx=ctx,
5122             )
5123             value_len = value.fulllen
5124             if ordering_check:
5125                 if value_prev.tobytes() > v[:value_len].tobytes():
5126                     if ctx_bered or ctx_allow_unordered_set:
5127                         bered = True
5128                     else:
5129                         raise DecodeError(
5130                             "unordered " + self.asn1_type_name,
5131                             klass=self.__class__,
5132                             decode_path=sub_decode_path,
5133                             offset=sub_offset,
5134                         )
5135                 value_prev = v[:value_len]
5136             _value.append(value)
5137             sub_offset += value_len
5138             vlen += value_len
5139             v = v_tail
5140         try:
5141             obj = self.__class__(
5142                 value=_value,
5143                 schema=spec,
5144                 bounds=(self._bound_min, self._bound_max),
5145                 impl=self.tag,
5146                 expl=self._expl,
5147                 default=self.default,
5148                 optional=self.optional,
5149                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5150             )
5151         except BoundsError as err:
5152             raise DecodeError(
5153                 msg=str(err),
5154                 klass=self.__class__,
5155                 decode_path=decode_path,
5156                 offset=offset,
5157             )
5158         if lenindef:
5159             if v[:EOC_LEN].tobytes() != EOC:
5160                 raise DecodeError(
5161                     "no EOC",
5162                     klass=self.__class__,
5163                     decode_path=decode_path,
5164                     offset=offset,
5165                 )
5166             obj.lenindef = True
5167             tail = v[EOC_LEN:]
5168         obj.bered = bered
5169         return obj, tail
5170
5171     def __repr__(self):
5172         return "%s[%s]" % (
5173             pp_console_row(next(self.pps())),
5174             ", ".join(repr(v) for v in self._value),
5175         )
5176
5177     def pps(self, decode_path=()):
5178         yield _pp(
5179             asn1_type_name=self.asn1_type_name,
5180             obj_name=self.__class__.__name__,
5181             decode_path=decode_path,
5182             optional=self.optional,
5183             default=self == self.default,
5184             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5185             expl=None if self._expl is None else tag_decode(self._expl),
5186             offset=self.offset,
5187             tlen=self.tlen,
5188             llen=self.llen,
5189             vlen=self.vlen,
5190             expl_offset=self.expl_offset if self.expled else None,
5191             expl_tlen=self.expl_tlen if self.expled else None,
5192             expl_llen=self.expl_llen if self.expled else None,
5193             expl_vlen=self.expl_vlen if self.expled else None,
5194             expl_lenindef=self.expl_lenindef,
5195             lenindef=self.lenindef,
5196         )
5197         for i, value in enumerate(self._value):
5198             yield value.pps(decode_path=decode_path + (str(i),))
5199         for pp in self.pps_lenindef(decode_path):
5200             yield pp
5201
5202
5203 class SetOf(SequenceOf):
5204     """``SET OF`` sequence type
5205
5206     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5207     """
5208     __slots__ = ()
5209     tag_default = tag_encode(form=TagFormConstructed, num=17)
5210     asn1_type_name = "SET OF"
5211
5212     def _encode(self):
5213         raws = self._encoded_values()
5214         raws.sort()
5215         v = b"".join(raws)
5216         return b"".join((self.tag, len_encode(len(v)), v))
5217
5218     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5219         return super(SetOf, self)._decode(
5220             tlv,
5221             offset,
5222             decode_path,
5223             ctx,
5224             tag_only,
5225             ordering_check=True,
5226         )
5227
5228
5229 def obj_by_path(pypath):  # pragma: no cover
5230     """Import object specified as string Python path
5231
5232     Modules must be separated from classes/functions with ``:``.
5233
5234     >>> obj_by_path("foo.bar:Baz")
5235     <class 'foo.bar.Baz'>
5236     >>> obj_by_path("foo.bar:Baz.boo")
5237     <classmethod 'foo.bar.Baz.boo'>
5238     """
5239     mod, objs = pypath.rsplit(":", 1)
5240     from importlib import import_module
5241     obj = import_module(mod)
5242     for obj_name in objs.split("."):
5243         obj = getattr(obj, obj_name)
5244     return obj
5245
5246
5247 def generic_decoder():  # pragma: no cover
5248     # All of this below is a big hack with self references
5249     choice = PrimitiveTypes()
5250     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5251     choice.specs["SetOf"] = SetOf(schema=choice)
5252     for i in range(31):
5253         choice.specs["SequenceOf%d" % i] = SequenceOf(
5254             schema=choice,
5255             expl=tag_ctxc(i),
5256         )
5257     choice.specs["Any"] = Any()
5258
5259     # Class name equals to type name, to omit it from output
5260     class SEQUENCEOF(SequenceOf):
5261         __slots__ = ()
5262         schema = choice
5263
5264     def pprint_any(
5265             obj,
5266             oids=None,
5267             with_colours=False,
5268             with_decode_path=False,
5269             decode_path_only=(),
5270     ):
5271         def _pprint_pps(pps):
5272             for pp in pps:
5273                 if hasattr(pp, "_fields"):
5274                     if (
5275                         decode_path_only != () and
5276                         pp.decode_path[:len(decode_path_only)] != decode_path_only
5277                     ):
5278                         continue
5279                     if pp.asn1_type_name == Choice.asn1_type_name:
5280                         continue
5281                     pp_kwargs = pp._asdict()
5282                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5283                     pp = _pp(**pp_kwargs)
5284                     yield pp_console_row(
5285                         pp,
5286                         oids=oids,
5287                         with_offsets=True,
5288                         with_blob=False,
5289                         with_colours=with_colours,
5290                         with_decode_path=with_decode_path,
5291                         decode_path_len_decrease=len(decode_path_only),
5292                     )
5293                     for row in pp_console_blob(
5294                         pp,
5295                         decode_path_len_decrease=len(decode_path_only),
5296                     ):
5297                         yield row
5298                 else:
5299                     for row in _pprint_pps(pp):
5300                         yield row
5301         return "\n".join(_pprint_pps(obj.pps()))
5302     return SEQUENCEOF(), pprint_any
5303
5304
5305 def main():  # pragma: no cover
5306     import argparse
5307     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5308     parser.add_argument(
5309         "--skip",
5310         type=int,
5311         default=0,
5312         help="Skip that number of bytes from the beginning",
5313     )
5314     parser.add_argument(
5315         "--oids",
5316         help="Python path to dictionary with OIDs",
5317     )
5318     parser.add_argument(
5319         "--schema",
5320         help="Python path to schema definition to use",
5321     )
5322     parser.add_argument(
5323         "--defines-by-path",
5324         help="Python path to decoder's defines_by_path",
5325     )
5326     parser.add_argument(
5327         "--nobered",
5328         action="store_true",
5329         help="Disallow BER encoding",
5330     )
5331     parser.add_argument(
5332         "--print-decode-path",
5333         action="store_true",
5334         help="Print decode paths",
5335     )
5336     parser.add_argument(
5337         "--decode-path-only",
5338         help="Print only specified decode path",
5339     )
5340     parser.add_argument(
5341         "--allow-expl-oob",
5342         action="store_true",
5343         help="Allow explicit tag out-of-bound",
5344     )
5345     parser.add_argument(
5346         "DERFile",
5347         type=argparse.FileType("rb"),
5348         help="Path to DER file you want to decode",
5349     )
5350     args = parser.parse_args()
5351     args.DERFile.seek(args.skip)
5352     der = memoryview(args.DERFile.read())
5353     args.DERFile.close()
5354     oids = obj_by_path(args.oids) if args.oids else {}
5355     if args.schema:
5356         schema = obj_by_path(args.schema)
5357         from functools import partial
5358         pprinter = partial(pprint, big_blobs=True)
5359     else:
5360         schema, pprinter = generic_decoder()
5361     ctx = {
5362         "bered": not args.nobered,
5363         "allow_expl_oob": args.allow_expl_oob,
5364     }
5365     if args.defines_by_path is not None:
5366         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5367     obj, tail = schema().decode(der, ctx=ctx)
5368     print(pprinter(
5369         obj,
5370         oids=oids,
5371         with_colours=True if environ.get("NO_COLOR") is None else False,
5372         with_decode_path=args.print_decode_path,
5373         decode_path_only=(
5374             () if args.decode_path_only is None else
5375             tuple(args.decode_path_only.split(":"))
5376         ),
5377     ))
5378     if tail != b"":
5379         print("\nTrailing data: %s" % hexenc(tail))
5380
5381
5382 if __name__ == "__main__":
5383     main()