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