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