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