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