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