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