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