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