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