]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
fulllen and fulloffset useful properties
[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             value_decoded = value.decode("ascii")
3526             if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3527                 try:
3528                     datetime.strptime(value_decoded, self.fmt)
3529                 except ValueError:
3530                     raise DecodeError("invalid UTCTime format")
3531                 return value
3532             else:
3533                 raise DecodeError("invalid UTCTime length")
3534         raise InvalidValueType((self.__class__, datetime))
3535
3536     def __eq__(self, their):
3537         if isinstance(their, binary_type):
3538             return self._value == their
3539         if isinstance(their, datetime):
3540             return self.todatetime() == their
3541         if not isinstance(their, self.__class__):
3542             return False
3543         return (
3544             self._value == their._value and
3545             self.tag == their.tag and
3546             self._expl == their._expl
3547         )
3548
3549     def todatetime(self):
3550         """Convert to datetime
3551
3552         :returns: datetime
3553
3554         Pay attention that UTCTime can not hold full year, so all years
3555         having < 50 years are treated as 20xx, 19xx otherwise, according
3556         to X.509 recomendation.
3557         """
3558         value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3559         year = value.year % 100
3560         return datetime(
3561             year=(2000 + year) if year < 50 else (1900 + year),
3562             month=value.month,
3563             day=value.day,
3564             hour=value.hour,
3565             minute=value.minute,
3566             second=value.second,
3567         )
3568
3569     def __repr__(self):
3570         return pp_console_row(next(self.pps()))
3571
3572     def pps(self, decode_path=()):
3573         yield _pp(
3574             asn1_type_name=self.asn1_type_name,
3575             obj_name=self.__class__.__name__,
3576             decode_path=decode_path,
3577             value=self.todatetime().isoformat() if self.ready else None,
3578             optional=self.optional,
3579             default=self == self.default,
3580             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3581             expl=None if self._expl is None else tag_decode(self._expl),
3582             offset=self.offset,
3583             tlen=self.tlen,
3584             llen=self.llen,
3585             vlen=self.vlen,
3586             expl_offset=self.expl_offset if self.expled else None,
3587             expl_tlen=self.expl_tlen if self.expled else None,
3588             expl_llen=self.expl_llen if self.expled else None,
3589             expl_vlen=self.expl_vlen if self.expled else None,
3590             expl_lenindef=self.expl_lenindef,
3591         )
3592         for pp in self.pps_lenindef(decode_path):
3593             yield pp
3594
3595
3596 class GeneralizedTime(UTCTime):
3597     """``GeneralizedTime`` datetime type
3598
3599     This type is similar to :py:class:`pyderasn.UTCTime`.
3600
3601     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3602     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3603     >>> str(t)
3604     '20170930220750.000123Z'
3605     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3606     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3607     """
3608     __slots__ = ()
3609     tag_default = tag_encode(24)
3610     asn1_type_name = "GeneralizedTime"
3611
3612     fmt = "%Y%m%d%H%M%SZ"
3613     fmt_ms = "%Y%m%d%H%M%S.%fZ"
3614
3615     def _value_sanitize(self, value):
3616         if isinstance(value, self.__class__):
3617             return value._value
3618         if isinstance(value, datetime):
3619             return value.strftime(
3620                 self.fmt_ms if value.microsecond > 0 else self.fmt
3621             ).encode("ascii")
3622         if isinstance(value, binary_type):
3623             value_decoded = value.decode("ascii")
3624             if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3625                 try:
3626                     datetime.strptime(value_decoded, self.fmt)
3627                 except ValueError:
3628                     raise DecodeError(
3629                         "invalid GeneralizedTime (without ms) format",
3630                     )
3631                 return value
3632             elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3633                 try:
3634                     datetime.strptime(value_decoded, self.fmt_ms)
3635                 except ValueError:
3636                     raise DecodeError(
3637                         "invalid GeneralizedTime (with ms) format",
3638                     )
3639                 return value
3640             else:
3641                 raise DecodeError(
3642                     "invalid GeneralizedTime length",
3643                     klass=self.__class__,
3644                 )
3645         raise InvalidValueType((self.__class__, datetime))
3646
3647     def todatetime(self):
3648         value = self._value.decode("ascii")
3649         if len(value) == LEN_YYYYMMDDHHMMSSZ:
3650             return datetime.strptime(value, self.fmt)
3651         return datetime.strptime(value, self.fmt_ms)
3652
3653
3654 class GraphicString(CommonString):
3655     __slots__ = ()
3656     tag_default = tag_encode(25)
3657     encoding = "iso-8859-1"
3658     asn1_type_name = "GraphicString"
3659
3660
3661 class VisibleString(CommonString):
3662     __slots__ = ()
3663     tag_default = tag_encode(26)
3664     encoding = "ascii"
3665     asn1_type_name = "VisibleString"
3666
3667
3668 class ISO646String(VisibleString):
3669     __slots__ = ()
3670     asn1_type_name = "ISO646String"
3671
3672
3673 class GeneralString(CommonString):
3674     __slots__ = ()
3675     tag_default = tag_encode(27)
3676     encoding = "iso-8859-1"
3677     asn1_type_name = "GeneralString"
3678
3679
3680 class UniversalString(CommonString):
3681     __slots__ = ()
3682     tag_default = tag_encode(28)
3683     encoding = "utf-32-be"
3684     asn1_type_name = "UniversalString"
3685
3686
3687 class BMPString(CommonString):
3688     __slots__ = ()
3689     tag_default = tag_encode(30)
3690     encoding = "utf-16-be"
3691     asn1_type_name = "BMPString"
3692
3693
3694 class Choice(Obj):
3695     """``CHOICE`` special type
3696
3697     ::
3698
3699         class GeneralName(Choice):
3700             schema = (
3701                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3702                 ("dNSName", IA5String(impl=tag_ctxp(2))),
3703             )
3704
3705     >>> gn = GeneralName()
3706     GeneralName CHOICE
3707     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3708     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3709     >>> gn["dNSName"] = IA5String("bar.baz")
3710     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3711     >>> gn["rfc822Name"]
3712     None
3713     >>> gn["dNSName"]
3714     [2] IA5String IA5 bar.baz
3715     >>> gn.choice
3716     'dNSName'
3717     >>> gn.value == gn["dNSName"]
3718     True
3719     >>> gn.specs
3720     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3721
3722     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3723     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3724     """
3725     __slots__ = ("specs",)
3726     tag_default = None
3727     asn1_type_name = "CHOICE"
3728
3729     def __init__(
3730             self,
3731             value=None,
3732             schema=None,
3733             impl=None,
3734             expl=None,
3735             default=None,
3736             optional=False,
3737             _decoded=(0, 0, 0),
3738     ):
3739         """
3740         :param value: set the value. Either ``(choice, value)`` tuple, or
3741                       :py:class:`pyderasn.Choice` object
3742         :param bytes impl: can not be set, do **not** use it
3743         :param bytes expl: override default tag with ``EXPLICIT`` one
3744         :param default: set default value. Type same as in ``value``
3745         :param bool optional: is object ``OPTIONAL`` in sequence
3746         """
3747         if impl is not None:
3748             raise ValueError("no implicit tag allowed for CHOICE")
3749         super(Choice, self).__init__(None, expl, default, optional, _decoded)
3750         if schema is None:
3751             schema = getattr(self, "schema", ())
3752         if len(schema) == 0:
3753             raise ValueError("schema must be specified")
3754         self.specs = (
3755             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3756         )
3757         self._value = None
3758         if value is not None:
3759             self._value = self._value_sanitize(value)
3760         if default is not None:
3761             default_value = self._value_sanitize(default)
3762             default_obj = self.__class__(impl=self.tag, expl=self._expl)
3763             default_obj.specs = self.specs
3764             default_obj._value = default_value
3765             self.default = default_obj
3766             if value is None:
3767                 self._value = default_obj.copy()._value
3768
3769     def _value_sanitize(self, value):
3770         if isinstance(value, self.__class__):
3771             return value._value
3772         if isinstance(value, tuple) and len(value) == 2:
3773             choice, obj = value
3774             spec = self.specs.get(choice)
3775             if spec is None:
3776                 raise ObjUnknown(choice)
3777             if not isinstance(obj, spec.__class__):
3778                 raise InvalidValueType((spec,))
3779             return (choice, spec(obj))
3780         raise InvalidValueType((self.__class__, tuple))
3781
3782     @property
3783     def ready(self):
3784         return self._value is not None and self._value[1].ready
3785
3786     def copy(self):
3787         obj = self.__class__(schema=self.specs)
3788         obj._expl = self._expl
3789         obj.default = self.default
3790         obj.optional = self.optional
3791         obj.offset = self.offset
3792         obj.llen = self.llen
3793         obj.vlen = self.vlen
3794         value = self._value
3795         if value is not None:
3796             obj._value = (value[0], value[1].copy())
3797         return obj
3798
3799     def __eq__(self, their):
3800         if isinstance(their, tuple) and len(their) == 2:
3801             return self._value == their
3802         if not isinstance(their, self.__class__):
3803             return False
3804         return (
3805             self.specs == their.specs and
3806             self._value == their._value
3807         )
3808
3809     def __call__(
3810             self,
3811             value=None,
3812             expl=None,
3813             default=None,
3814             optional=None,
3815     ):
3816         return self.__class__(
3817             value=value,
3818             schema=self.specs,
3819             expl=self._expl if expl is None else expl,
3820             default=self.default if default is None else default,
3821             optional=self.optional if optional is None else optional,
3822         )
3823
3824     @property
3825     def choice(self):
3826         self._assert_ready()
3827         return self._value[0]
3828
3829     @property
3830     def value(self):
3831         self._assert_ready()
3832         return self._value[1]
3833
3834     def __getitem__(self, key):
3835         if key not in self.specs:
3836             raise ObjUnknown(key)
3837         if self._value is None:
3838             return None
3839         choice, value = self._value
3840         if choice != key:
3841             return None
3842         return value
3843
3844     def __setitem__(self, key, value):
3845         spec = self.specs.get(key)
3846         if spec is None:
3847             raise ObjUnknown(key)
3848         if not isinstance(value, spec.__class__):
3849             raise InvalidValueType((spec.__class__,))
3850         self._value = (key, spec(value))
3851
3852     @property
3853     def tlen(self):
3854         return 0
3855
3856     @property
3857     def decoded(self):
3858         return self._value[1].decoded if self.ready else False
3859
3860     def _encode(self):
3861         self._assert_ready()
3862         return self._value[1].encode()
3863
3864     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3865         for choice, spec in self.specs.items():
3866             sub_decode_path = decode_path + (choice,)
3867             try:
3868                 spec.decode(
3869                     tlv,
3870                     offset=offset,
3871                     leavemm=True,
3872                     decode_path=sub_decode_path,
3873                     ctx=ctx,
3874                     tag_only=True,
3875                 )
3876             except TagMismatch:
3877                 continue
3878             break
3879         else:
3880             raise TagMismatch(
3881                 klass=self.__class__,
3882                 decode_path=decode_path,
3883                 offset=offset,
3884             )
3885         if tag_only:
3886             return
3887         value, tail = spec.decode(
3888             tlv,
3889             offset=offset,
3890             leavemm=True,
3891             decode_path=sub_decode_path,
3892             ctx=ctx,
3893         )
3894         obj = self.__class__(
3895             schema=self.specs,
3896             expl=self._expl,
3897             default=self.default,
3898             optional=self.optional,
3899             _decoded=(offset, 0, value.tlvlen),
3900         )
3901         obj._value = (choice, value)
3902         return obj, tail
3903
3904     def __repr__(self):
3905         value = pp_console_row(next(self.pps()))
3906         if self.ready:
3907             value = "%s[%r]" % (value, self.value)
3908         return value
3909
3910     def pps(self, decode_path=()):
3911         yield _pp(
3912             asn1_type_name=self.asn1_type_name,
3913             obj_name=self.__class__.__name__,
3914             decode_path=decode_path,
3915             value=self.choice if self.ready else None,
3916             optional=self.optional,
3917             default=self == self.default,
3918             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3919             expl=None if self._expl is None else tag_decode(self._expl),
3920             offset=self.offset,
3921             tlen=self.tlen,
3922             llen=self.llen,
3923             vlen=self.vlen,
3924             expl_lenindef=self.expl_lenindef,
3925         )
3926         if self.ready:
3927             yield self.value.pps(decode_path=decode_path + (self.choice,))
3928         for pp in self.pps_lenindef(decode_path):
3929             yield pp
3930
3931
3932 class PrimitiveTypes(Choice):
3933     """Predefined ``CHOICE`` for all generic primitive types
3934
3935     It could be useful for general decoding of some unspecified values:
3936
3937     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3938     OCTET STRING 3 bytes 666f6f
3939     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3940     INTEGER 1193046
3941     """
3942     __slots__ = ()
3943     schema = tuple((klass.__name__, klass()) for klass in (
3944         Boolean,
3945         Integer,
3946         BitString,
3947         OctetString,
3948         Null,
3949         ObjectIdentifier,
3950         UTF8String,
3951         NumericString,
3952         PrintableString,
3953         TeletexString,
3954         VideotexString,
3955         IA5String,
3956         UTCTime,
3957         GeneralizedTime,
3958         GraphicString,
3959         VisibleString,
3960         ISO646String,
3961         GeneralString,
3962         UniversalString,
3963         BMPString,
3964     ))
3965
3966
3967 class Any(Obj):
3968     """``ANY`` special type
3969
3970     >>> Any(Integer(-123))
3971     ANY 020185
3972     >>> a = Any(OctetString(b"hello world").encode())
3973     ANY 040b68656c6c6f20776f726c64
3974     >>> hexenc(bytes(a))
3975     b'0x040x0bhello world'
3976     """
3977     __slots__ = ("defined",)
3978     tag_default = tag_encode(0)
3979     asn1_type_name = "ANY"
3980
3981     def __init__(
3982             self,
3983             value=None,
3984             expl=None,
3985             optional=False,
3986             _decoded=(0, 0, 0),
3987     ):
3988         """
3989         :param value: set the value. Either any kind of pyderasn's
3990                       **ready** object, or bytes. Pay attention that
3991                       **no** validation is performed is raw binary value
3992                       is valid TLV
3993         :param bytes expl: override default tag with ``EXPLICIT`` one
3994         :param bool optional: is object ``OPTIONAL`` in sequence
3995         """
3996         super(Any, self).__init__(None, expl, None, optional, _decoded)
3997         self._value = None if value is None else self._value_sanitize(value)
3998         self.defined = None
3999
4000     def _value_sanitize(self, value):
4001         if isinstance(value, self.__class__):
4002             return value._value
4003         if isinstance(value, Obj):
4004             return value.encode()
4005         if isinstance(value, binary_type):
4006             return value
4007         raise InvalidValueType((self.__class__, Obj, binary_type))
4008
4009     @property
4010     def ready(self):
4011         return self._value is not None
4012
4013     def copy(self):
4014         obj = self.__class__()
4015         obj._value = self._value
4016         obj.tag = self.tag
4017         obj._expl = self._expl
4018         obj.optional = self.optional
4019         obj.offset = self.offset
4020         obj.llen = self.llen
4021         obj.vlen = self.vlen
4022         return obj
4023
4024     def __eq__(self, their):
4025         if isinstance(their, binary_type):
4026             return self._value == their
4027         if issubclass(their.__class__, Any):
4028             return self._value == their._value
4029         return False
4030
4031     def __call__(
4032             self,
4033             value=None,
4034             expl=None,
4035             optional=None,
4036     ):
4037         return self.__class__(
4038             value=value,
4039             expl=self._expl if expl is None else expl,
4040             optional=self.optional if optional is None else optional,
4041         )
4042
4043     def __bytes__(self):
4044         self._assert_ready()
4045         return self._value
4046
4047     @property
4048     def tlen(self):
4049         return 0
4050
4051     def _encode(self):
4052         self._assert_ready()
4053         return self._value
4054
4055     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4056         try:
4057             t, tlen, lv = tag_strip(tlv)
4058         except DecodeError as err:
4059             raise err.__class__(
4060                 msg=err.msg,
4061                 klass=self.__class__,
4062                 decode_path=decode_path,
4063                 offset=offset,
4064             )
4065         try:
4066             l, llen, v = len_decode(lv)
4067         except LenIndefForm as err:
4068             if not ctx.get("bered", False):
4069                 raise err.__class__(
4070                     msg=err.msg,
4071                     klass=self.__class__,
4072                     decode_path=decode_path,
4073                     offset=offset,
4074                 )
4075             llen, vlen, v = 1, 0, lv[1:]
4076             sub_offset = offset + tlen + llen
4077             chunk_i = 0
4078             while v[:EOC_LEN].tobytes() != EOC:
4079                 chunk, v = Any().decode(
4080                     v,
4081                     offset=sub_offset,
4082                     decode_path=decode_path + (str(chunk_i),),
4083                     leavemm=True,
4084                     ctx=ctx,
4085                 )
4086                 vlen += chunk.tlvlen
4087                 sub_offset += chunk.tlvlen
4088                 chunk_i += 1
4089             tlvlen = tlen + llen + vlen + EOC_LEN
4090             obj = self.__class__(
4091                 value=tlv[:tlvlen].tobytes(),
4092                 expl=self._expl,
4093                 optional=self.optional,
4094                 _decoded=(offset, 0, tlvlen),
4095             )
4096             obj.lenindef = True
4097             obj.tag = t
4098             return obj, v[EOC_LEN:]
4099         except DecodeError as err:
4100             raise err.__class__(
4101                 msg=err.msg,
4102                 klass=self.__class__,
4103                 decode_path=decode_path,
4104                 offset=offset,
4105             )
4106         if l > len(v):
4107             raise NotEnoughData(
4108                 "encoded length is longer than data",
4109                 klass=self.__class__,
4110                 decode_path=decode_path,
4111                 offset=offset,
4112             )
4113         tlvlen = tlen + llen + l
4114         v, tail = tlv[:tlvlen], v[l:]
4115         obj = self.__class__(
4116             value=v.tobytes(),
4117             expl=self._expl,
4118             optional=self.optional,
4119             _decoded=(offset, 0, tlvlen),
4120         )
4121         obj.tag = t
4122         return obj, tail
4123
4124     def __repr__(self):
4125         return pp_console_row(next(self.pps()))
4126
4127     def pps(self, decode_path=()):
4128         yield _pp(
4129             asn1_type_name=self.asn1_type_name,
4130             obj_name=self.__class__.__name__,
4131             decode_path=decode_path,
4132             blob=self._value if self.ready else None,
4133             optional=self.optional,
4134             default=self == self.default,
4135             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4136             expl=None if self._expl is None else tag_decode(self._expl),
4137             offset=self.offset,
4138             tlen=self.tlen,
4139             llen=self.llen,
4140             vlen=self.vlen,
4141             expl_offset=self.expl_offset if self.expled else None,
4142             expl_tlen=self.expl_tlen if self.expled else None,
4143             expl_llen=self.expl_llen if self.expled else None,
4144             expl_vlen=self.expl_vlen if self.expled else None,
4145             expl_lenindef=self.expl_lenindef,
4146             lenindef=self.lenindef,
4147         )
4148         defined_by, defined = self.defined or (None, None)
4149         if defined_by is not None:
4150             yield defined.pps(
4151                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4152             )
4153         for pp in self.pps_lenindef(decode_path):
4154             yield pp
4155
4156
4157 ########################################################################
4158 # ASN.1 constructed types
4159 ########################################################################
4160
4161 def get_def_by_path(defines_by_path, sub_decode_path):
4162     """Get define by decode path
4163     """
4164     for path, define in defines_by_path:
4165         if len(path) != len(sub_decode_path):
4166             continue
4167         for p1, p2 in zip(path, sub_decode_path):
4168             if (p1 != any) and (p1 != p2):
4169                 break
4170         else:
4171             return define
4172
4173
4174 def abs_decode_path(decode_path, rel_path):
4175     """Create an absolute decode path from current and relative ones
4176
4177     :param decode_path: current decode path, starting point.
4178                         Tuple of strings
4179     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4180                      If first tuple's element is "/", then treat it as
4181                      an absolute path, ignoring ``decode_path`` as
4182                      starting point. Also this tuple can contain ".."
4183                      elements, stripping the leading element from
4184                      ``decode_path``
4185
4186     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4187     ("foo", "bar", "baz", "whatever")
4188     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4189     ("foo", "whatever")
4190     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4191     ("baz", "whatever")
4192     """
4193     if rel_path[0] == "/":
4194         return rel_path[1:]
4195     if rel_path[0] == "..":
4196         return abs_decode_path(decode_path[:-1], rel_path[1:])
4197     return decode_path + rel_path
4198
4199
4200 class Sequence(Obj):
4201     """``SEQUENCE`` structure type
4202
4203     You have to make specification of sequence::
4204
4205         class Extension(Sequence):
4206             schema = (
4207                 ("extnID", ObjectIdentifier()),
4208                 ("critical", Boolean(default=False)),
4209                 ("extnValue", OctetString()),
4210             )
4211
4212     Then, you can work with it as with dictionary.
4213
4214     >>> ext = Extension()
4215     >>> Extension().specs
4216     OrderedDict([
4217         ('extnID', OBJECT IDENTIFIER),
4218         ('critical', BOOLEAN False OPTIONAL DEFAULT),
4219         ('extnValue', OCTET STRING),
4220     ])
4221     >>> ext["extnID"] = "1.2.3"
4222     Traceback (most recent call last):
4223     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4224     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4225
4226     You can determine if sequence is ready to be encoded:
4227
4228     >>> ext.ready
4229     False
4230     >>> ext.encode()
4231     Traceback (most recent call last):
4232     pyderasn.ObjNotReady: object is not ready: extnValue
4233     >>> ext["extnValue"] = OctetString(b"foobar")
4234     >>> ext.ready
4235     True
4236
4237     Value you want to assign, must have the same **type** as in
4238     corresponding specification, but it can have different tags,
4239     optional/default attributes -- they will be taken from specification
4240     automatically::
4241
4242         class TBSCertificate(Sequence):
4243             schema = (
4244                 ("version", Version(expl=tag_ctxc(0), default="v1")),
4245             [...]
4246
4247     >>> tbs = TBSCertificate()
4248     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4249
4250     Assign ``None`` to remove value from sequence.
4251
4252     You can set values in Sequence during its initialization:
4253
4254     >>> AlgorithmIdentifier((
4255         ("algorithm", ObjectIdentifier("1.2.3")),
4256         ("parameters", Any(Null()))
4257     ))
4258     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4259
4260     You can determine if value exists/set in the sequence and take its value:
4261
4262     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4263     (True, True, False)
4264     >>> ext["extnID"]
4265     OBJECT IDENTIFIER 1.2.3
4266
4267     But pay attention that if value has default, then it won't be (not
4268     in) in the sequence (because ``DEFAULT`` must not be encoded in
4269     DER), but you can read its value:
4270
4271     >>> "critical" in ext, ext["critical"]
4272     (False, BOOLEAN False)
4273     >>> ext["critical"] = Boolean(True)
4274     >>> "critical" in ext, ext["critical"]
4275     (True, BOOLEAN True)
4276
4277     All defaulted values are always optional.
4278
4279     .. _strict_default_existence_ctx:
4280
4281     .. warning::
4282
4283        When decoded DER contains defaulted value inside, then
4284        technically this is not valid DER encoding. But we allow and pass
4285        it **by default**. Of course reencoding of that kind of DER will
4286        result in different binary representation (validly without
4287        defaulted value inside). You can enable strict defaulted values
4288        existence validation by setting ``"strict_default_existence":
4289        True`` :ref:`context <ctx>` option -- decoding process will raise
4290        an exception if defaulted value is met.
4291
4292     Two sequences are equal if they have equal specification (schema),
4293     implicit/explicit tagging and the same values.
4294     """
4295     __slots__ = ("specs",)
4296     tag_default = tag_encode(form=TagFormConstructed, num=16)
4297     asn1_type_name = "SEQUENCE"
4298
4299     def __init__(
4300             self,
4301             value=None,
4302             schema=None,
4303             impl=None,
4304             expl=None,
4305             default=None,
4306             optional=False,
4307             _decoded=(0, 0, 0),
4308     ):
4309         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4310         if schema is None:
4311             schema = getattr(self, "schema", ())
4312         self.specs = (
4313             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4314         )
4315         self._value = {}
4316         if value is not None:
4317             if issubclass(value.__class__, Sequence):
4318                 self._value = value._value
4319             elif hasattr(value, "__iter__"):
4320                 for seq_key, seq_value in value:
4321                     self[seq_key] = seq_value
4322             else:
4323                 raise InvalidValueType((Sequence,))
4324         if default is not None:
4325             if not issubclass(default.__class__, Sequence):
4326                 raise InvalidValueType((Sequence,))
4327             default_value = default._value
4328             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4329             default_obj.specs = self.specs
4330             default_obj._value = default_value
4331             self.default = default_obj
4332             if value is None:
4333                 self._value = default_obj.copy()._value
4334
4335     @property
4336     def ready(self):
4337         for name, spec in self.specs.items():
4338             value = self._value.get(name)
4339             if value is None:
4340                 if spec.optional:
4341                     continue
4342                 return False
4343             else:
4344                 if not value.ready:
4345                     return False
4346         return True
4347
4348     def copy(self):
4349         obj = self.__class__(schema=self.specs)
4350         obj.tag = self.tag
4351         obj._expl = self._expl
4352         obj.default = self.default
4353         obj.optional = self.optional
4354         obj.offset = self.offset
4355         obj.llen = self.llen
4356         obj.vlen = self.vlen
4357         obj._value = {k: v.copy() for k, v in self._value.items()}
4358         return obj
4359
4360     def __eq__(self, their):
4361         if not isinstance(their, self.__class__):
4362             return False
4363         return (
4364             self.specs == their.specs and
4365             self.tag == their.tag and
4366             self._expl == their._expl and
4367             self._value == their._value
4368         )
4369
4370     def __call__(
4371             self,
4372             value=None,
4373             impl=None,
4374             expl=None,
4375             default=None,
4376             optional=None,
4377     ):
4378         return self.__class__(
4379             value=value,
4380             schema=self.specs,
4381             impl=self.tag if impl is None else impl,
4382             expl=self._expl if expl is None else expl,
4383             default=self.default if default is None else default,
4384             optional=self.optional if optional is None else optional,
4385         )
4386
4387     def __contains__(self, key):
4388         return key in self._value
4389
4390     def __setitem__(self, key, value):
4391         spec = self.specs.get(key)
4392         if spec is None:
4393             raise ObjUnknown(key)
4394         if value is None:
4395             self._value.pop(key, None)
4396             return
4397         if not isinstance(value, spec.__class__):
4398             raise InvalidValueType((spec.__class__,))
4399         value = spec(value=value)
4400         if spec.default is not None and value == spec.default:
4401             self._value.pop(key, None)
4402             return
4403         self._value[key] = value
4404
4405     def __getitem__(self, key):
4406         value = self._value.get(key)
4407         if value is not None:
4408             return value
4409         spec = self.specs.get(key)
4410         if spec is None:
4411             raise ObjUnknown(key)
4412         if spec.default is not None:
4413             return spec.default
4414         return None
4415
4416     def _encoded_values(self):
4417         raws = []
4418         for name, spec in self.specs.items():
4419             value = self._value.get(name)
4420             if value is None:
4421                 if spec.optional:
4422                     continue
4423                 raise ObjNotReady(name)
4424             raws.append(value.encode())
4425         return raws
4426
4427     def _encode(self):
4428         v = b"".join(self._encoded_values())
4429         return b"".join((self.tag, len_encode(len(v)), v))
4430
4431     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4432         try:
4433             t, tlen, lv = tag_strip(tlv)
4434         except DecodeError as err:
4435             raise err.__class__(
4436                 msg=err.msg,
4437                 klass=self.__class__,
4438                 decode_path=decode_path,
4439                 offset=offset,
4440             )
4441         if t != self.tag:
4442             raise TagMismatch(
4443                 klass=self.__class__,
4444                 decode_path=decode_path,
4445                 offset=offset,
4446             )
4447         if tag_only:
4448             return
4449         lenindef = False
4450         try:
4451             l, llen, v = len_decode(lv)
4452         except LenIndefForm as err:
4453             if not ctx.get("bered", False):
4454                 raise err.__class__(
4455                     msg=err.msg,
4456                     klass=self.__class__,
4457                     decode_path=decode_path,
4458                     offset=offset,
4459                 )
4460             l, llen, v = 0, 1, lv[1:]
4461             lenindef = True
4462         except DecodeError as err:
4463             raise err.__class__(
4464                 msg=err.msg,
4465                 klass=self.__class__,
4466                 decode_path=decode_path,
4467                 offset=offset,
4468             )
4469         if l > len(v):
4470             raise NotEnoughData(
4471                 "encoded length is longer than data",
4472                 klass=self.__class__,
4473                 decode_path=decode_path,
4474                 offset=offset,
4475             )
4476         if not lenindef:
4477             v, tail = v[:l], v[l:]
4478         vlen = 0
4479         sub_offset = offset + tlen + llen
4480         values = {}
4481         for name, spec in self.specs.items():
4482             if spec.optional and (
4483                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4484                     len(v) == 0
4485             ):
4486                 continue
4487             sub_decode_path = decode_path + (name,)
4488             try:
4489                 value, v_tail = spec.decode(
4490                     v,
4491                     sub_offset,
4492                     leavemm=True,
4493                     decode_path=sub_decode_path,
4494                     ctx=ctx,
4495                 )
4496             except TagMismatch:
4497                 if spec.optional:
4498                     continue
4499                 raise
4500
4501             defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4502             if defined is not None:
4503                 defined_by, defined_spec = defined
4504                 if issubclass(value.__class__, SequenceOf):
4505                     for i, _value in enumerate(value):
4506                         sub_sub_decode_path = sub_decode_path + (
4507                             str(i),
4508                             DecodePathDefBy(defined_by),
4509                         )
4510                         defined_value, defined_tail = defined_spec.decode(
4511                             memoryview(bytes(_value)),
4512                             sub_offset + (
4513                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4514                                 if value.expled else (value.tlen + value.llen)
4515                             ),
4516                             leavemm=True,
4517                             decode_path=sub_sub_decode_path,
4518                             ctx=ctx,
4519                         )
4520                         if len(defined_tail) > 0:
4521                             raise DecodeError(
4522                                 "remaining data",
4523                                 klass=self.__class__,
4524                                 decode_path=sub_sub_decode_path,
4525                                 offset=offset,
4526                             )
4527                         _value.defined = (defined_by, defined_value)
4528                 else:
4529                     defined_value, defined_tail = defined_spec.decode(
4530                         memoryview(bytes(value)),
4531                         sub_offset + (
4532                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4533                             if value.expled else (value.tlen + value.llen)
4534                         ),
4535                         leavemm=True,
4536                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4537                         ctx=ctx,
4538                     )
4539                     if len(defined_tail) > 0:
4540                         raise DecodeError(
4541                             "remaining data",
4542                             klass=self.__class__,
4543                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4544                             offset=offset,
4545                         )
4546                     value.defined = (defined_by, defined_value)
4547
4548             value_len = value.fulllen
4549             vlen += value_len
4550             sub_offset += value_len
4551             v = v_tail
4552             if spec.default is not None and value == spec.default:
4553                 if ctx.get("strict_default_existence", False):
4554                     raise DecodeError(
4555                         "DEFAULT value met",
4556                         klass=self.__class__,
4557                         decode_path=sub_decode_path,
4558                         offset=sub_offset,
4559                     )
4560                 else:
4561                     continue
4562             values[name] = value
4563
4564             spec_defines = getattr(spec, "defines", ())
4565             if len(spec_defines) == 0:
4566                 defines_by_path = ctx.get("defines_by_path", ())
4567                 if len(defines_by_path) > 0:
4568                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4569             if spec_defines is not None and len(spec_defines) > 0:
4570                 for rel_path, schema in spec_defines:
4571                     defined = schema.get(value, None)
4572                     if defined is not None:
4573                         ctx.setdefault("defines", []).append((
4574                             abs_decode_path(sub_decode_path[:-1], rel_path),
4575                             (value, defined),
4576                         ))
4577         if lenindef:
4578             if v[:EOC_LEN].tobytes() != EOC:
4579                 raise DecodeError(
4580                     "no EOC",
4581                     klass=self.__class__,
4582                     decode_path=decode_path,
4583                     offset=offset,
4584                 )
4585             tail = v[EOC_LEN:]
4586             vlen += EOC_LEN
4587         elif len(v) > 0:
4588             raise DecodeError(
4589                 "remaining data",
4590                 klass=self.__class__,
4591                 decode_path=decode_path,
4592                 offset=offset,
4593             )
4594         obj = self.__class__(
4595             schema=self.specs,
4596             impl=self.tag,
4597             expl=self._expl,
4598             default=self.default,
4599             optional=self.optional,
4600             _decoded=(offset, llen, vlen),
4601         )
4602         obj._value = values
4603         obj.lenindef = lenindef
4604         return obj, tail
4605
4606     def __repr__(self):
4607         value = pp_console_row(next(self.pps()))
4608         cols = []
4609         for name in self.specs:
4610             _value = self._value.get(name)
4611             if _value is None:
4612                 continue
4613             cols.append("%s: %s" % (name, repr(_value)))
4614         return "%s[%s]" % (value, "; ".join(cols))
4615
4616     def pps(self, decode_path=()):
4617         yield _pp(
4618             asn1_type_name=self.asn1_type_name,
4619             obj_name=self.__class__.__name__,
4620             decode_path=decode_path,
4621             optional=self.optional,
4622             default=self == self.default,
4623             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4624             expl=None if self._expl is None else tag_decode(self._expl),
4625             offset=self.offset,
4626             tlen=self.tlen,
4627             llen=self.llen,
4628             vlen=self.vlen,
4629             expl_offset=self.expl_offset if self.expled else None,
4630             expl_tlen=self.expl_tlen if self.expled else None,
4631             expl_llen=self.expl_llen if self.expled else None,
4632             expl_vlen=self.expl_vlen if self.expled else None,
4633             expl_lenindef=self.expl_lenindef,
4634             lenindef=self.lenindef,
4635         )
4636         for name in self.specs:
4637             value = self._value.get(name)
4638             if value is None:
4639                 continue
4640             yield value.pps(decode_path=decode_path + (name,))
4641         for pp in self.pps_lenindef(decode_path):
4642             yield pp
4643
4644
4645 class Set(Sequence):
4646     """``SET`` structure type
4647
4648     Its usage is identical to :py:class:`pyderasn.Sequence`.
4649     """
4650     __slots__ = ()
4651     tag_default = tag_encode(form=TagFormConstructed, num=17)
4652     asn1_type_name = "SET"
4653
4654     def _encode(self):
4655         raws = self._encoded_values()
4656         raws.sort()
4657         v = b"".join(raws)
4658         return b"".join((self.tag, len_encode(len(v)), v))
4659
4660     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4661         try:
4662             t, tlen, lv = tag_strip(tlv)
4663         except DecodeError as err:
4664             raise err.__class__(
4665                 msg=err.msg,
4666                 klass=self.__class__,
4667                 decode_path=decode_path,
4668                 offset=offset,
4669             )
4670         if t != self.tag:
4671             raise TagMismatch(
4672                 klass=self.__class__,
4673                 decode_path=decode_path,
4674                 offset=offset,
4675             )
4676         if tag_only:
4677             return
4678         lenindef = False
4679         try:
4680             l, llen, v = len_decode(lv)
4681         except LenIndefForm as err:
4682             if not ctx.get("bered", False):
4683                 raise err.__class__(
4684                     msg=err.msg,
4685                     klass=self.__class__,
4686                     decode_path=decode_path,
4687                     offset=offset,
4688                 )
4689             l, llen, v = 0, 1, lv[1:]
4690             lenindef = True
4691         except DecodeError as err:
4692             raise err.__class__(
4693                 msg=err.msg,
4694                 klass=self.__class__,
4695                 decode_path=decode_path,
4696                 offset=offset,
4697             )
4698         if l > len(v):
4699             raise NotEnoughData(
4700                 "encoded length is longer than data",
4701                 klass=self.__class__,
4702                 offset=offset,
4703             )
4704         if not lenindef:
4705             v, tail = v[:l], v[l:]
4706         vlen = 0
4707         sub_offset = offset + tlen + llen
4708         values = {}
4709         specs_items = self.specs.items
4710         while len(v) > 0:
4711             if lenindef and v[:EOC_LEN].tobytes() == EOC:
4712                 break
4713             for name, spec in specs_items():
4714                 sub_decode_path = decode_path + (name,)
4715                 try:
4716                     spec.decode(
4717                         v,
4718                         sub_offset,
4719                         leavemm=True,
4720                         decode_path=sub_decode_path,
4721                         ctx=ctx,
4722                         tag_only=True,
4723                     )
4724                 except TagMismatch:
4725                     continue
4726                 break
4727             else:
4728                 raise TagMismatch(
4729                     klass=self.__class__,
4730                     decode_path=decode_path,
4731                     offset=offset,
4732                 )
4733             value, v_tail = spec.decode(
4734                 v,
4735                 sub_offset,
4736                 leavemm=True,
4737                 decode_path=sub_decode_path,
4738                 ctx=ctx,
4739             )
4740             value_len = value.fulllen
4741             sub_offset += value_len
4742             vlen += value_len
4743             v = v_tail
4744             if spec.default is None or value != spec.default:  # pragma: no cover
4745                 # SeqMixing.test_encoded_default_accepted covers that place
4746                 values[name] = value
4747         obj = self.__class__(
4748             schema=self.specs,
4749             impl=self.tag,
4750             expl=self._expl,
4751             default=self.default,
4752             optional=self.optional,
4753             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4754         )
4755         obj._value = values
4756         if lenindef:
4757             if v[:EOC_LEN].tobytes() != EOC:
4758                 raise DecodeError(
4759                     "no EOC",
4760                     klass=self.__class__,
4761                     decode_path=decode_path,
4762                     offset=offset,
4763                 )
4764             tail = v[EOC_LEN:]
4765             obj.lenindef = True
4766         if not obj.ready:
4767             raise DecodeError(
4768                 "not all values are ready",
4769                 klass=self.__class__,
4770                 decode_path=decode_path,
4771                 offset=offset,
4772             )
4773         return obj, tail
4774
4775
4776 class SequenceOf(Obj):
4777     """``SEQUENCE OF`` sequence type
4778
4779     For that kind of type you must specify the object it will carry on
4780     (bounds are for example here, not required)::
4781
4782         class Ints(SequenceOf):
4783             schema = Integer()
4784             bounds = (0, 2)
4785
4786     >>> ints = Ints()
4787     >>> ints.append(Integer(123))
4788     >>> ints.append(Integer(234))
4789     >>> ints
4790     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4791     >>> [int(i) for i in ints]
4792     [123, 234]
4793     >>> ints.append(Integer(345))
4794     Traceback (most recent call last):
4795     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4796     >>> ints[1]
4797     INTEGER 234
4798     >>> ints[1] = Integer(345)
4799     >>> ints
4800     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4801
4802     Also you can initialize sequence with preinitialized values:
4803
4804     >>> ints = Ints([Integer(123), Integer(234)])
4805     """
4806     __slots__ = ("spec", "_bound_min", "_bound_max")
4807     tag_default = tag_encode(form=TagFormConstructed, num=16)
4808     asn1_type_name = "SEQUENCE OF"
4809
4810     def __init__(
4811             self,
4812             value=None,
4813             schema=None,
4814             bounds=None,
4815             impl=None,
4816             expl=None,
4817             default=None,
4818             optional=False,
4819             _decoded=(0, 0, 0),
4820     ):
4821         super(SequenceOf, self).__init__(
4822             impl,
4823             expl,
4824             default,
4825             optional,
4826             _decoded,
4827         )
4828         if schema is None:
4829             schema = getattr(self, "schema", None)
4830         if schema is None:
4831             raise ValueError("schema must be specified")
4832         self.spec = schema
4833         self._bound_min, self._bound_max = getattr(
4834             self,
4835             "bounds",
4836             (0, float("+inf")),
4837         ) if bounds is None else bounds
4838         self._value = []
4839         if value is not None:
4840             self._value = self._value_sanitize(value)
4841         if default is not None:
4842             default_value = self._value_sanitize(default)
4843             default_obj = self.__class__(
4844                 schema=schema,
4845                 impl=self.tag,
4846                 expl=self._expl,
4847             )
4848             default_obj._value = default_value
4849             self.default = default_obj
4850             if value is None:
4851                 self._value = default_obj.copy()._value
4852
4853     def _value_sanitize(self, value):
4854         if issubclass(value.__class__, SequenceOf):
4855             value = value._value
4856         elif hasattr(value, "__iter__"):
4857             value = list(value)
4858         else:
4859             raise InvalidValueType((self.__class__, iter))
4860         if not self._bound_min <= len(value) <= self._bound_max:
4861             raise BoundsError(self._bound_min, len(value), self._bound_max)
4862         for v in value:
4863             if not isinstance(v, self.spec.__class__):
4864                 raise InvalidValueType((self.spec.__class__,))
4865         return value
4866
4867     @property
4868     def ready(self):
4869         return all(v.ready for v in self._value)
4870
4871     def copy(self):
4872         obj = self.__class__(schema=self.spec)
4873         obj._bound_min = self._bound_min
4874         obj._bound_max = self._bound_max
4875         obj.tag = self.tag
4876         obj._expl = self._expl
4877         obj.default = self.default
4878         obj.optional = self.optional
4879         obj.offset = self.offset
4880         obj.llen = self.llen
4881         obj.vlen = self.vlen
4882         obj._value = [v.copy() for v in self._value]
4883         return obj
4884
4885     def __eq__(self, their):
4886         if isinstance(their, self.__class__):
4887             return (
4888                 self.spec == their.spec and
4889                 self.tag == their.tag and
4890                 self._expl == their._expl and
4891                 self._value == their._value
4892             )
4893         if hasattr(their, "__iter__"):
4894             return self._value == list(their)
4895         return False
4896
4897     def __call__(
4898             self,
4899             value=None,
4900             bounds=None,
4901             impl=None,
4902             expl=None,
4903             default=None,
4904             optional=None,
4905     ):
4906         return self.__class__(
4907             value=value,
4908             schema=self.spec,
4909             bounds=(
4910                 (self._bound_min, self._bound_max)
4911                 if bounds is None else bounds
4912             ),
4913             impl=self.tag if impl is None else impl,
4914             expl=self._expl if expl is None else expl,
4915             default=self.default if default is None else default,
4916             optional=self.optional if optional is None else optional,
4917         )
4918
4919     def __contains__(self, key):
4920         return key in self._value
4921
4922     def append(self, value):
4923         if not isinstance(value, self.spec.__class__):
4924             raise InvalidValueType((self.spec.__class__,))
4925         if len(self._value) + 1 > self._bound_max:
4926             raise BoundsError(
4927                 self._bound_min,
4928                 len(self._value) + 1,
4929                 self._bound_max,
4930             )
4931         self._value.append(value)
4932
4933     def __iter__(self):
4934         self._assert_ready()
4935         return iter(self._value)
4936
4937     def __len__(self):
4938         self._assert_ready()
4939         return len(self._value)
4940
4941     def __setitem__(self, key, value):
4942         if not isinstance(value, self.spec.__class__):
4943             raise InvalidValueType((self.spec.__class__,))
4944         self._value[key] = self.spec(value=value)
4945
4946     def __getitem__(self, key):
4947         return self._value[key]
4948
4949     def _encoded_values(self):
4950         return [v.encode() for v in self._value]
4951
4952     def _encode(self):
4953         v = b"".join(self._encoded_values())
4954         return b"".join((self.tag, len_encode(len(v)), v))
4955
4956     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4957         try:
4958             t, tlen, lv = tag_strip(tlv)
4959         except DecodeError as err:
4960             raise err.__class__(
4961                 msg=err.msg,
4962                 klass=self.__class__,
4963                 decode_path=decode_path,
4964                 offset=offset,
4965             )
4966         if t != self.tag:
4967             raise TagMismatch(
4968                 klass=self.__class__,
4969                 decode_path=decode_path,
4970                 offset=offset,
4971             )
4972         if tag_only:
4973             return
4974         lenindef = False
4975         try:
4976             l, llen, v = len_decode(lv)
4977         except LenIndefForm as err:
4978             if not ctx.get("bered", False):
4979                 raise err.__class__(
4980                     msg=err.msg,
4981                     klass=self.__class__,
4982                     decode_path=decode_path,
4983                     offset=offset,
4984                 )
4985             l, llen, v = 0, 1, lv[1:]
4986             lenindef = True
4987         except DecodeError as err:
4988             raise err.__class__(
4989                 msg=err.msg,
4990                 klass=self.__class__,
4991                 decode_path=decode_path,
4992                 offset=offset,
4993             )
4994         if l > len(v):
4995             raise NotEnoughData(
4996                 "encoded length is longer than data",
4997                 klass=self.__class__,
4998                 decode_path=decode_path,
4999                 offset=offset,
5000             )
5001         if not lenindef:
5002             v, tail = v[:l], v[l:]
5003         vlen = 0
5004         sub_offset = offset + tlen + llen
5005         _value = []
5006         spec = self.spec
5007         while len(v) > 0:
5008             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5009                 break
5010             value, v_tail = spec.decode(
5011                 v,
5012                 sub_offset,
5013                 leavemm=True,
5014                 decode_path=decode_path + (str(len(_value)),),
5015                 ctx=ctx,
5016             )
5017             value_len = value.fulllen
5018             sub_offset += value_len
5019             vlen += value_len
5020             v = v_tail
5021             _value.append(value)
5022         obj = self.__class__(
5023             value=_value,
5024             schema=spec,
5025             bounds=(self._bound_min, self._bound_max),
5026             impl=self.tag,
5027             expl=self._expl,
5028             default=self.default,
5029             optional=self.optional,
5030             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5031         )
5032         if lenindef:
5033             if v[:EOC_LEN].tobytes() != EOC:
5034                 raise DecodeError(
5035                     "no EOC",
5036                     klass=self.__class__,
5037                     decode_path=decode_path,
5038                     offset=offset,
5039                 )
5040             obj.lenindef = True
5041             tail = v[EOC_LEN:]
5042         return obj, tail
5043
5044     def __repr__(self):
5045         return "%s[%s]" % (
5046             pp_console_row(next(self.pps())),
5047             ", ".join(repr(v) for v in self._value),
5048         )
5049
5050     def pps(self, decode_path=()):
5051         yield _pp(
5052             asn1_type_name=self.asn1_type_name,
5053             obj_name=self.__class__.__name__,
5054             decode_path=decode_path,
5055             optional=self.optional,
5056             default=self == self.default,
5057             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5058             expl=None if self._expl is None else tag_decode(self._expl),
5059             offset=self.offset,
5060             tlen=self.tlen,
5061             llen=self.llen,
5062             vlen=self.vlen,
5063             expl_offset=self.expl_offset if self.expled else None,
5064             expl_tlen=self.expl_tlen if self.expled else None,
5065             expl_llen=self.expl_llen if self.expled else None,
5066             expl_vlen=self.expl_vlen if self.expled else None,
5067             expl_lenindef=self.expl_lenindef,
5068             lenindef=self.lenindef,
5069         )
5070         for i, value in enumerate(self._value):
5071             yield value.pps(decode_path=decode_path + (str(i),))
5072         for pp in self.pps_lenindef(decode_path):
5073             yield pp
5074
5075
5076 class SetOf(SequenceOf):
5077     """``SET OF`` sequence type
5078
5079     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5080     """
5081     __slots__ = ()
5082     tag_default = tag_encode(form=TagFormConstructed, num=17)
5083     asn1_type_name = "SET OF"
5084
5085     def _encode(self):
5086         raws = self._encoded_values()
5087         raws.sort()
5088         v = b"".join(raws)
5089         return b"".join((self.tag, len_encode(len(v)), v))
5090
5091
5092 def obj_by_path(pypath):  # pragma: no cover
5093     """Import object specified as string Python path
5094
5095     Modules must be separated from classes/functions with ``:``.
5096
5097     >>> obj_by_path("foo.bar:Baz")
5098     <class 'foo.bar.Baz'>
5099     >>> obj_by_path("foo.bar:Baz.boo")
5100     <classmethod 'foo.bar.Baz.boo'>
5101     """
5102     mod, objs = pypath.rsplit(":", 1)
5103     from importlib import import_module
5104     obj = import_module(mod)
5105     for obj_name in objs.split("."):
5106         obj = getattr(obj, obj_name)
5107     return obj
5108
5109
5110 def generic_decoder():  # pragma: no cover
5111     # All of this below is a big hack with self references
5112     choice = PrimitiveTypes()
5113     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5114     choice.specs["SetOf"] = SetOf(schema=choice)
5115     for i in range(31):
5116         choice.specs["SequenceOf%d" % i] = SequenceOf(
5117             schema=choice,
5118             expl=tag_ctxc(i),
5119         )
5120     choice.specs["Any"] = Any()
5121
5122     # Class name equals to type name, to omit it from output
5123     class SEQUENCEOF(SequenceOf):
5124         __slots__ = ()
5125         schema = choice
5126
5127     def pprint_any(obj, oids=None, with_colours=False):
5128         def _pprint_pps(pps):
5129             for pp in pps:
5130                 if hasattr(pp, "_fields"):
5131                     if pp.asn1_type_name == Choice.asn1_type_name:
5132                         continue
5133                     pp_kwargs = pp._asdict()
5134                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5135                     pp = _pp(**pp_kwargs)
5136                     yield pp_console_row(
5137                         pp,
5138                         oids=oids,
5139                         with_offsets=True,
5140                         with_blob=False,
5141                         with_colours=with_colours,
5142                     )
5143                     for row in pp_console_blob(pp):
5144                         yield row
5145                 else:
5146                     for row in _pprint_pps(pp):
5147                         yield row
5148         return "\n".join(_pprint_pps(obj.pps()))
5149     return SEQUENCEOF(), pprint_any
5150
5151
5152 def main():  # pragma: no cover
5153     import argparse
5154     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5155     parser.add_argument(
5156         "--skip",
5157         type=int,
5158         default=0,
5159         help="Skip that number of bytes from the beginning",
5160     )
5161     parser.add_argument(
5162         "--oids",
5163         help="Python path to dictionary with OIDs",
5164     )
5165     parser.add_argument(
5166         "--schema",
5167         help="Python path to schema definition to use",
5168     )
5169     parser.add_argument(
5170         "--defines-by-path",
5171         help="Python path to decoder's defines_by_path",
5172     )
5173     parser.add_argument(
5174         "--nobered",
5175         action='store_true',
5176         help="Disallow BER encoding",
5177     )
5178     parser.add_argument(
5179         "DERFile",
5180         type=argparse.FileType("rb"),
5181         help="Path to DER file you want to decode",
5182     )
5183     args = parser.parse_args()
5184     args.DERFile.seek(args.skip)
5185     der = memoryview(args.DERFile.read())
5186     args.DERFile.close()
5187     oids = obj_by_path(args.oids) if args.oids else {}
5188     if args.schema:
5189         schema = obj_by_path(args.schema)
5190         from functools import partial
5191         pprinter = partial(pprint, big_blobs=True)
5192     else:
5193         schema, pprinter = generic_decoder()
5194     ctx = {"bered": not args.nobered}
5195     if args.defines_by_path is not None:
5196         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5197     obj, tail = schema().decode(der, ctx=ctx)
5198     print(pprinter(
5199         obj,
5200         oids=oids,
5201         with_colours=True if environ.get("NO_COLOR") is None else False,
5202     ))
5203     if tail != b"":
5204         print("\nTrailing data: %s" % hexenc(tail))
5205
5206
5207 if __name__ == "__main__":
5208     main()