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