]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Change order of value sanitizers
[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 isinstance(value, bool):
1555             return value
1556         if issubclass(value.__class__, Boolean):
1557             return value._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 isinstance(value, integer_types):
1804             pass
1805         elif issubclass(value.__class__, Integer):
1806             value = value._value
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 SET01 = frozenset(("0", "1"))
2051
2052
2053 class BitString(Obj):
2054     """``BIT STRING`` bit string type
2055
2056     >>> BitString(b"hello world")
2057     BIT STRING 88 bits 68656c6c6f20776f726c64
2058     >>> bytes(b)
2059     b'hello world'
2060     >>> b == b"hello world"
2061     True
2062     >>> b.bit_len
2063     88
2064
2065     >>> BitString("'0A3B5F291CD'H")
2066     BIT STRING 44 bits 0a3b5f291cd0
2067     >>> b = BitString("'010110000000'B")
2068     BIT STRING 12 bits 5800
2069     >>> b.bit_len
2070     12
2071     >>> b[0], b[1], b[2], b[3]
2072     (False, True, False, True)
2073     >>> b[1000]
2074     False
2075     >>> [v for v in b]
2076     [False, True, False, True, True, False, False, False, False, False, False, False]
2077
2078     ::
2079
2080         class KeyUsage(BitString):
2081             schema = (
2082                 ("digitalSignature", 0),
2083                 ("nonRepudiation", 1),
2084                 ("keyEncipherment", 2),
2085             )
2086
2087     >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2088     KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2089     >>> b.named
2090     ['nonRepudiation', 'keyEncipherment']
2091     >>> b.specs
2092     {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2093
2094     .. note::
2095
2096        Pay attention that BIT STRING can be encoded both in primitive
2097        and constructed forms. Decoder always checks constructed form tag
2098        additionally to specified primitive one. If BER decoding is
2099        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2100        of DER restrictions.
2101     """
2102     __slots__ = ("tag_constructed", "specs", "defined")
2103     tag_default = tag_encode(3)
2104     asn1_type_name = "BIT STRING"
2105
2106     def __init__(
2107             self,
2108             value=None,
2109             impl=None,
2110             expl=None,
2111             default=None,
2112             optional=False,
2113             _specs=None,
2114             _decoded=(0, 0, 0),
2115     ):
2116         """
2117         :param value: set the value. Either binary type, tuple of named
2118                       values (if ``schema`` is specified in the class),
2119                       string in ``'XXX...'B`` form, or
2120                       :py:class:`pyderasn.BitString` object
2121         :param bytes impl: override default tag with ``IMPLICIT`` one
2122         :param bytes expl: override default tag with ``EXPLICIT`` one
2123         :param default: set default value. Type same as in ``value``
2124         :param bool optional: is object ``OPTIONAL`` in sequence
2125         """
2126         super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2127         specs = getattr(self, "schema", {}) if _specs is None else _specs
2128         self.specs = specs if isinstance(specs, dict) else dict(specs)
2129         self._value = None if value is None else self._value_sanitize(value)
2130         if default is not None:
2131             default = self._value_sanitize(default)
2132             self.default = self.__class__(
2133                 value=default,
2134                 impl=self.tag,
2135                 expl=self._expl,
2136             )
2137             if value is None:
2138                 self._value = default
2139         self.defined = None
2140         tag_klass, _, tag_num = tag_decode(self.tag)
2141         self.tag_constructed = tag_encode(
2142             klass=tag_klass,
2143             form=TagFormConstructed,
2144             num=tag_num,
2145         )
2146
2147     def _bits2octets(self, bits):
2148         if len(self.specs) > 0:
2149             bits = bits.rstrip("0")
2150         bit_len = len(bits)
2151         bits += "0" * ((8 - (bit_len % 8)) % 8)
2152         octets = bytearray(len(bits) // 8)
2153         for i in six_xrange(len(octets)):
2154             octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2155         return bit_len, bytes(octets)
2156
2157     def _value_sanitize(self, value):
2158         if isinstance(value, (string_types, binary_type)):
2159             if (
2160                     isinstance(value, string_types) and
2161                     value.startswith("'")
2162             ):
2163                 if value.endswith("'B"):
2164                     value = value[1:-2]
2165                     if not frozenset(value) <= SET01:
2166                         raise ValueError("B's coding contains unacceptable chars")
2167                     return self._bits2octets(value)
2168                 elif value.endswith("'H"):
2169                     value = value[1:-2]
2170                     return (
2171                         len(value) * 4,
2172                         hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2173                     )
2174             if isinstance(value, binary_type):
2175                 return (len(value) * 8, value)
2176             else:
2177                 raise InvalidValueType((self.__class__, string_types, binary_type))
2178         if isinstance(value, tuple):
2179             if (
2180                     len(value) == 2 and
2181                     isinstance(value[0], integer_types) and
2182                     isinstance(value[1], binary_type)
2183             ):
2184                 return value
2185             bits = []
2186             for name in value:
2187                 bit = self.specs.get(name)
2188                 if bit is None:
2189                     raise ObjUnknown("BitString value: %s" % name)
2190                 bits.append(bit)
2191             if len(bits) == 0:
2192                 return self._bits2octets("")
2193             bits = frozenset(bits)
2194             return self._bits2octets("".join(
2195                 ("1" if bit in bits else "0")
2196                 for bit in six_xrange(max(bits) + 1)
2197             ))
2198         if issubclass(value.__class__, BitString):
2199             return value._value
2200         raise InvalidValueType((self.__class__, binary_type, string_types))
2201
2202     @property
2203     def ready(self):
2204         return self._value is not None
2205
2206     def copy(self):
2207         obj = self.__class__(_specs=self.specs)
2208         value = self._value
2209         if value is not None:
2210             value = (value[0], value[1])
2211         obj._value = value
2212         obj.tag = self.tag
2213         obj._expl = self._expl
2214         obj.default = self.default
2215         obj.optional = self.optional
2216         obj.offset = self.offset
2217         obj.llen = self.llen
2218         obj.vlen = self.vlen
2219         obj.expl_lenindef = self.expl_lenindef
2220         obj.lenindef = self.lenindef
2221         obj.ber_encoded = self.ber_encoded
2222         return obj
2223
2224     def __iter__(self):
2225         self._assert_ready()
2226         for i in six_xrange(self._value[0]):
2227             yield self[i]
2228
2229     @property
2230     def bit_len(self):
2231         self._assert_ready()
2232         return self._value[0]
2233
2234     def __bytes__(self):
2235         self._assert_ready()
2236         return self._value[1]
2237
2238     def __eq__(self, their):
2239         if isinstance(their, bytes):
2240             return self._value[1] == their
2241         if not issubclass(their.__class__, BitString):
2242             return False
2243         return (
2244             self._value == their._value and
2245             self.tag == their.tag and
2246             self._expl == their._expl
2247         )
2248
2249     @property
2250     def named(self):
2251         return [name for name, bit in iteritems(self.specs) if self[bit]]
2252
2253     def __call__(
2254             self,
2255             value=None,
2256             impl=None,
2257             expl=None,
2258             default=None,
2259             optional=None,
2260     ):
2261         return self.__class__(
2262             value=value,
2263             impl=self.tag if impl is None else impl,
2264             expl=self._expl if expl is None else expl,
2265             default=self.default if default is None else default,
2266             optional=self.optional if optional is None else optional,
2267             _specs=self.specs,
2268         )
2269
2270     def __getitem__(self, key):
2271         if isinstance(key, int):
2272             bit_len, octets = self._value
2273             if key >= bit_len:
2274                 return False
2275             return (
2276                 byte2int(memoryview(octets)[key // 8:]) >>
2277                 (7 - (key % 8))
2278             ) & 1 == 1
2279         if isinstance(key, string_types):
2280             value = self.specs.get(key)
2281             if value is None:
2282                 raise ObjUnknown("BitString value: %s" % key)
2283             return self[value]
2284         raise InvalidValueType((int, str))
2285
2286     def _encode(self):
2287         self._assert_ready()
2288         bit_len, octets = self._value
2289         return b"".join((
2290             self.tag,
2291             len_encode(len(octets) + 1),
2292             int2byte((8 - bit_len % 8) % 8),
2293             octets,
2294         ))
2295
2296     def _decode_chunk(self, lv, offset, decode_path, ctx):
2297         try:
2298             l, llen, v = len_decode(lv)
2299         except DecodeError as err:
2300             raise err.__class__(
2301                 msg=err.msg,
2302                 klass=self.__class__,
2303                 decode_path=decode_path,
2304                 offset=offset,
2305             )
2306         if l > len(v):
2307             raise NotEnoughData(
2308                 "encoded length is longer than data",
2309                 klass=self.__class__,
2310                 decode_path=decode_path,
2311                 offset=offset,
2312             )
2313         if l == 0:
2314             raise NotEnoughData(
2315                 "zero length",
2316                 klass=self.__class__,
2317                 decode_path=decode_path,
2318                 offset=offset,
2319             )
2320         pad_size = byte2int(v)
2321         if l == 1 and pad_size != 0:
2322             raise DecodeError(
2323                 "invalid empty value",
2324                 klass=self.__class__,
2325                 decode_path=decode_path,
2326                 offset=offset,
2327             )
2328         if pad_size > 7:
2329             raise DecodeError(
2330                 "too big pad",
2331                 klass=self.__class__,
2332                 decode_path=decode_path,
2333                 offset=offset,
2334             )
2335         if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2336             raise DecodeError(
2337                 "invalid pad",
2338                 klass=self.__class__,
2339                 decode_path=decode_path,
2340                 offset=offset,
2341             )
2342         v, tail = v[:l], v[l:]
2343         obj = self.__class__(
2344             value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2345             impl=self.tag,
2346             expl=self._expl,
2347             default=self.default,
2348             optional=self.optional,
2349             _specs=self.specs,
2350             _decoded=(offset, llen, l),
2351         )
2352         return obj, tail
2353
2354     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2355         try:
2356             t, tlen, lv = tag_strip(tlv)
2357         except DecodeError as err:
2358             raise err.__class__(
2359                 msg=err.msg,
2360                 klass=self.__class__,
2361                 decode_path=decode_path,
2362                 offset=offset,
2363             )
2364         if t == self.tag:
2365             if tag_only:  # pragma: no cover
2366                 return
2367             return self._decode_chunk(lv, offset, decode_path, ctx)
2368         if t == self.tag_constructed:
2369             if not ctx.get("bered", False):
2370                 raise DecodeError(
2371                     "unallowed BER constructed encoding",
2372                     klass=self.__class__,
2373                     decode_path=decode_path,
2374                     offset=offset,
2375                 )
2376             if tag_only:  # pragma: no cover
2377                 return
2378             lenindef = False
2379             try:
2380                 l, llen, v = len_decode(lv)
2381             except LenIndefForm:
2382                 llen, l, v = 1, 0, lv[1:]
2383                 lenindef = True
2384             except DecodeError as err:
2385                 raise err.__class__(
2386                     msg=err.msg,
2387                     klass=self.__class__,
2388                     decode_path=decode_path,
2389                     offset=offset,
2390                 )
2391             if l > len(v):
2392                 raise NotEnoughData(
2393                     "encoded length is longer than data",
2394                     klass=self.__class__,
2395                     decode_path=decode_path,
2396                     offset=offset,
2397                 )
2398             if not lenindef and l == 0:
2399                 raise NotEnoughData(
2400                     "zero length",
2401                     klass=self.__class__,
2402                     decode_path=decode_path,
2403                     offset=offset,
2404                 )
2405             chunks = []
2406             sub_offset = offset + tlen + llen
2407             vlen = 0
2408             while True:
2409                 if lenindef:
2410                     if v[:EOC_LEN].tobytes() == EOC:
2411                         break
2412                 else:
2413                     if vlen == l:
2414                         break
2415                     if vlen > l:
2416                         raise DecodeError(
2417                             "chunk out of bounds",
2418                             klass=self.__class__,
2419                             decode_path=decode_path + (str(len(chunks) - 1),),
2420                             offset=chunks[-1].offset,
2421                         )
2422                 sub_decode_path = decode_path + (str(len(chunks)),)
2423                 try:
2424                     chunk, v_tail = BitString().decode(
2425                         v,
2426                         offset=sub_offset,
2427                         decode_path=sub_decode_path,
2428                         leavemm=True,
2429                         ctx=ctx,
2430                         _ctx_immutable=False,
2431                     )
2432                 except TagMismatch:
2433                     raise DecodeError(
2434                         "expected BitString encoded chunk",
2435                         klass=self.__class__,
2436                         decode_path=sub_decode_path,
2437                         offset=sub_offset,
2438                     )
2439                 chunks.append(chunk)
2440                 sub_offset += chunk.tlvlen
2441                 vlen += chunk.tlvlen
2442                 v = v_tail
2443             if len(chunks) == 0:
2444                 raise DecodeError(
2445                     "no chunks",
2446                     klass=self.__class__,
2447                     decode_path=decode_path,
2448                     offset=offset,
2449                 )
2450             values = []
2451             bit_len = 0
2452             for chunk_i, chunk in enumerate(chunks[:-1]):
2453                 if chunk.bit_len % 8 != 0:
2454                     raise DecodeError(
2455                         "BitString chunk is not multiple of 8 bits",
2456                         klass=self.__class__,
2457                         decode_path=decode_path + (str(chunk_i),),
2458                         offset=chunk.offset,
2459                     )
2460                 values.append(bytes(chunk))
2461                 bit_len += chunk.bit_len
2462             chunk_last = chunks[-1]
2463             values.append(bytes(chunk_last))
2464             bit_len += chunk_last.bit_len
2465             obj = self.__class__(
2466                 value=(bit_len, b"".join(values)),
2467                 impl=self.tag,
2468                 expl=self._expl,
2469                 default=self.default,
2470                 optional=self.optional,
2471                 _specs=self.specs,
2472                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2473             )
2474             obj.lenindef = lenindef
2475             obj.ber_encoded = True
2476             return obj, (v[EOC_LEN:] if lenindef else v)
2477         raise TagMismatch(
2478             klass=self.__class__,
2479             decode_path=decode_path,
2480             offset=offset,
2481         )
2482
2483     def __repr__(self):
2484         return pp_console_row(next(self.pps()))
2485
2486     def pps(self, decode_path=()):
2487         value = None
2488         blob = None
2489         if self.ready:
2490             bit_len, blob = self._value
2491             value = "%d bits" % bit_len
2492             if len(self.specs) > 0:
2493                 blob = tuple(self.named)
2494         yield _pp(
2495             obj=self,
2496             asn1_type_name=self.asn1_type_name,
2497             obj_name=self.__class__.__name__,
2498             decode_path=decode_path,
2499             value=value,
2500             blob=blob,
2501             optional=self.optional,
2502             default=self == self.default,
2503             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2504             expl=None if self._expl is None else tag_decode(self._expl),
2505             offset=self.offset,
2506             tlen=self.tlen,
2507             llen=self.llen,
2508             vlen=self.vlen,
2509             expl_offset=self.expl_offset if self.expled else None,
2510             expl_tlen=self.expl_tlen if self.expled else None,
2511             expl_llen=self.expl_llen if self.expled else None,
2512             expl_vlen=self.expl_vlen if self.expled else None,
2513             expl_lenindef=self.expl_lenindef,
2514             lenindef=self.lenindef,
2515             ber_encoded=self.ber_encoded,
2516             bered=self.bered,
2517         )
2518         defined_by, defined = self.defined or (None, None)
2519         if defined_by is not None:
2520             yield defined.pps(
2521                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2522             )
2523         for pp in self.pps_lenindef(decode_path):
2524             yield pp
2525
2526
2527 class OctetString(Obj):
2528     """``OCTET STRING`` binary string type
2529
2530     >>> s = OctetString(b"hello world")
2531     OCTET STRING 11 bytes 68656c6c6f20776f726c64
2532     >>> s == OctetString(b"hello world")
2533     True
2534     >>> bytes(s)
2535     b'hello world'
2536
2537     >>> OctetString(b"hello", bounds=(4, 4))
2538     Traceback (most recent call last):
2539     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2540     >>> OctetString(b"hell", bounds=(4, 4))
2541     OCTET STRING 4 bytes 68656c6c
2542
2543     .. note::
2544
2545        Pay attention that OCTET STRING can be encoded both in primitive
2546        and constructed forms. Decoder always checks constructed form tag
2547        additionally to specified primitive one. If BER decoding is
2548        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2549        of DER restrictions.
2550     """
2551     __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2552     tag_default = tag_encode(4)
2553     asn1_type_name = "OCTET STRING"
2554
2555     def __init__(
2556             self,
2557             value=None,
2558             bounds=None,
2559             impl=None,
2560             expl=None,
2561             default=None,
2562             optional=False,
2563             _decoded=(0, 0, 0),
2564     ):
2565         """
2566         :param value: set the value. Either binary type, or
2567                       :py:class:`pyderasn.OctetString` object
2568         :param bounds: set ``(MIN, MAX)`` value size constraint.
2569                        (-inf, +inf) by default
2570         :param bytes impl: override default tag with ``IMPLICIT`` one
2571         :param bytes expl: override default tag with ``EXPLICIT`` one
2572         :param default: set default value. Type same as in ``value``
2573         :param bool optional: is object ``OPTIONAL`` in sequence
2574         """
2575         super(OctetString, self).__init__(
2576             impl,
2577             expl,
2578             default,
2579             optional,
2580             _decoded,
2581         )
2582         self._value = value
2583         self._bound_min, self._bound_max = getattr(
2584             self,
2585             "bounds",
2586             (0, float("+inf")),
2587         ) if bounds is None else bounds
2588         if value is not None:
2589             self._value = self._value_sanitize(value)
2590         if default is not None:
2591             default = self._value_sanitize(default)
2592             self.default = self.__class__(
2593                 value=default,
2594                 impl=self.tag,
2595                 expl=self._expl,
2596             )
2597             if self._value is None:
2598                 self._value = default
2599         self.defined = None
2600         tag_klass, _, tag_num = tag_decode(self.tag)
2601         self.tag_constructed = tag_encode(
2602             klass=tag_klass,
2603             form=TagFormConstructed,
2604             num=tag_num,
2605         )
2606
2607     def _value_sanitize(self, value):
2608         if isinstance(value, binary_type):
2609             pass
2610         elif issubclass(value.__class__, OctetString):
2611             value = value._value
2612         else:
2613             raise InvalidValueType((self.__class__, bytes))
2614         if not self._bound_min <= len(value) <= self._bound_max:
2615             raise BoundsError(self._bound_min, len(value), self._bound_max)
2616         return value
2617
2618     @property
2619     def ready(self):
2620         return self._value is not None
2621
2622     def copy(self):
2623         obj = self.__class__()
2624         obj._value = self._value
2625         obj._bound_min = self._bound_min
2626         obj._bound_max = self._bound_max
2627         obj.tag = self.tag
2628         obj._expl = self._expl
2629         obj.default = self.default
2630         obj.optional = self.optional
2631         obj.offset = self.offset
2632         obj.llen = self.llen
2633         obj.vlen = self.vlen
2634         obj.expl_lenindef = self.expl_lenindef
2635         obj.lenindef = self.lenindef
2636         obj.ber_encoded = self.ber_encoded
2637         return obj
2638
2639     def __bytes__(self):
2640         self._assert_ready()
2641         return self._value
2642
2643     def __eq__(self, their):
2644         if isinstance(their, binary_type):
2645             return self._value == their
2646         if not issubclass(their.__class__, OctetString):
2647             return False
2648         return (
2649             self._value == their._value and
2650             self.tag == their.tag and
2651             self._expl == their._expl
2652         )
2653
2654     def __lt__(self, their):
2655         return self._value < their._value
2656
2657     def __call__(
2658             self,
2659             value=None,
2660             bounds=None,
2661             impl=None,
2662             expl=None,
2663             default=None,
2664             optional=None,
2665     ):
2666         return self.__class__(
2667             value=value,
2668             bounds=(
2669                 (self._bound_min, self._bound_max)
2670                 if bounds is None else bounds
2671             ),
2672             impl=self.tag if impl is None else impl,
2673             expl=self._expl if expl is None else expl,
2674             default=self.default if default is None else default,
2675             optional=self.optional if optional is None else optional,
2676         )
2677
2678     def _encode(self):
2679         self._assert_ready()
2680         return b"".join((
2681             self.tag,
2682             len_encode(len(self._value)),
2683             self._value,
2684         ))
2685
2686     def _decode_chunk(self, lv, offset, decode_path, ctx):
2687         try:
2688             l, llen, v = len_decode(lv)
2689         except DecodeError as err:
2690             raise err.__class__(
2691                 msg=err.msg,
2692                 klass=self.__class__,
2693                 decode_path=decode_path,
2694                 offset=offset,
2695             )
2696         if l > len(v):
2697             raise NotEnoughData(
2698                 "encoded length is longer than data",
2699                 klass=self.__class__,
2700                 decode_path=decode_path,
2701                 offset=offset,
2702             )
2703         v, tail = v[:l], v[l:]
2704         try:
2705             obj = self.__class__(
2706                 value=v.tobytes(),
2707                 bounds=(self._bound_min, self._bound_max),
2708                 impl=self.tag,
2709                 expl=self._expl,
2710                 default=self.default,
2711                 optional=self.optional,
2712                 _decoded=(offset, llen, l),
2713             )
2714         except DecodeError as err:
2715             raise DecodeError(
2716                 msg=err.msg,
2717                 klass=self.__class__,
2718                 decode_path=decode_path,
2719                 offset=offset,
2720             )
2721         except BoundsError as err:
2722             raise DecodeError(
2723                 msg=str(err),
2724                 klass=self.__class__,
2725                 decode_path=decode_path,
2726                 offset=offset,
2727             )
2728         return obj, tail
2729
2730     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2731         try:
2732             t, tlen, lv = tag_strip(tlv)
2733         except DecodeError as err:
2734             raise err.__class__(
2735                 msg=err.msg,
2736                 klass=self.__class__,
2737                 decode_path=decode_path,
2738                 offset=offset,
2739             )
2740         if t == self.tag:
2741             if tag_only:
2742                 return
2743             return self._decode_chunk(lv, offset, decode_path, ctx)
2744         if t == self.tag_constructed:
2745             if not ctx.get("bered", False):
2746                 raise DecodeError(
2747                     "unallowed BER constructed encoding",
2748                     klass=self.__class__,
2749                     decode_path=decode_path,
2750                     offset=offset,
2751                 )
2752             if tag_only:
2753                 return
2754             lenindef = False
2755             try:
2756                 l, llen, v = len_decode(lv)
2757             except LenIndefForm:
2758                 llen, l, v = 1, 0, lv[1:]
2759                 lenindef = True
2760             except DecodeError as err:
2761                 raise err.__class__(
2762                     msg=err.msg,
2763                     klass=self.__class__,
2764                     decode_path=decode_path,
2765                     offset=offset,
2766                 )
2767             if l > len(v):
2768                 raise NotEnoughData(
2769                     "encoded length is longer than data",
2770                     klass=self.__class__,
2771                     decode_path=decode_path,
2772                     offset=offset,
2773                 )
2774             chunks = []
2775             sub_offset = offset + tlen + llen
2776             vlen = 0
2777             while True:
2778                 if lenindef:
2779                     if v[:EOC_LEN].tobytes() == EOC:
2780                         break
2781                 else:
2782                     if vlen == l:
2783                         break
2784                     if vlen > l:
2785                         raise DecodeError(
2786                             "chunk out of bounds",
2787                             klass=self.__class__,
2788                             decode_path=decode_path + (str(len(chunks) - 1),),
2789                             offset=chunks[-1].offset,
2790                         )
2791                 sub_decode_path = decode_path + (str(len(chunks)),)
2792                 try:
2793                     chunk, v_tail = OctetString().decode(
2794                         v,
2795                         offset=sub_offset,
2796                         decode_path=sub_decode_path,
2797                         leavemm=True,
2798                         ctx=ctx,
2799                         _ctx_immutable=False,
2800                     )
2801                 except TagMismatch:
2802                     raise DecodeError(
2803                         "expected OctetString encoded chunk",
2804                         klass=self.__class__,
2805                         decode_path=sub_decode_path,
2806                         offset=sub_offset,
2807                     )
2808                 chunks.append(chunk)
2809                 sub_offset += chunk.tlvlen
2810                 vlen += chunk.tlvlen
2811                 v = v_tail
2812             try:
2813                 obj = self.__class__(
2814                     value=b"".join(bytes(chunk) for chunk in chunks),
2815                     bounds=(self._bound_min, self._bound_max),
2816                     impl=self.tag,
2817                     expl=self._expl,
2818                     default=self.default,
2819                     optional=self.optional,
2820                     _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2821                 )
2822             except DecodeError as err:
2823                 raise DecodeError(
2824                     msg=err.msg,
2825                     klass=self.__class__,
2826                     decode_path=decode_path,
2827                     offset=offset,
2828                 )
2829             except BoundsError as err:
2830                 raise DecodeError(
2831                     msg=str(err),
2832                     klass=self.__class__,
2833                     decode_path=decode_path,
2834                     offset=offset,
2835                 )
2836             obj.lenindef = lenindef
2837             obj.ber_encoded = True
2838             return obj, (v[EOC_LEN:] if lenindef else v)
2839         raise TagMismatch(
2840             klass=self.__class__,
2841             decode_path=decode_path,
2842             offset=offset,
2843         )
2844
2845     def __repr__(self):
2846         return pp_console_row(next(self.pps()))
2847
2848     def pps(self, decode_path=()):
2849         yield _pp(
2850             obj=self,
2851             asn1_type_name=self.asn1_type_name,
2852             obj_name=self.__class__.__name__,
2853             decode_path=decode_path,
2854             value=("%d bytes" % len(self._value)) if self.ready else None,
2855             blob=self._value if self.ready else None,
2856             optional=self.optional,
2857             default=self == self.default,
2858             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2859             expl=None if self._expl is None else tag_decode(self._expl),
2860             offset=self.offset,
2861             tlen=self.tlen,
2862             llen=self.llen,
2863             vlen=self.vlen,
2864             expl_offset=self.expl_offset if self.expled else None,
2865             expl_tlen=self.expl_tlen if self.expled else None,
2866             expl_llen=self.expl_llen if self.expled else None,
2867             expl_vlen=self.expl_vlen if self.expled else None,
2868             expl_lenindef=self.expl_lenindef,
2869             lenindef=self.lenindef,
2870             ber_encoded=self.ber_encoded,
2871             bered=self.bered,
2872         )
2873         defined_by, defined = self.defined or (None, None)
2874         if defined_by is not None:
2875             yield defined.pps(
2876                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2877             )
2878         for pp in self.pps_lenindef(decode_path):
2879             yield pp
2880
2881
2882 class Null(Obj):
2883     """``NULL`` null object
2884
2885     >>> n = Null()
2886     NULL
2887     >>> n.ready
2888     True
2889     """
2890     __slots__ = ()
2891     tag_default = tag_encode(5)
2892     asn1_type_name = "NULL"
2893
2894     def __init__(
2895             self,
2896             value=None,  # unused, but Sequence passes it
2897             impl=None,
2898             expl=None,
2899             optional=False,
2900             _decoded=(0, 0, 0),
2901     ):
2902         """
2903         :param bytes impl: override default tag with ``IMPLICIT`` one
2904         :param bytes expl: override default tag with ``EXPLICIT`` one
2905         :param bool optional: is object ``OPTIONAL`` in sequence
2906         """
2907         super(Null, self).__init__(impl, expl, None, optional, _decoded)
2908         self.default = None
2909
2910     @property
2911     def ready(self):
2912         return True
2913
2914     def copy(self):
2915         obj = self.__class__()
2916         obj.tag = self.tag
2917         obj._expl = self._expl
2918         obj.default = self.default
2919         obj.optional = self.optional
2920         obj.offset = self.offset
2921         obj.llen = self.llen
2922         obj.vlen = self.vlen
2923         obj.expl_lenindef = self.expl_lenindef
2924         obj.lenindef = self.lenindef
2925         obj.ber_encoded = self.ber_encoded
2926         return obj
2927
2928     def __eq__(self, their):
2929         if not issubclass(their.__class__, Null):
2930             return False
2931         return (
2932             self.tag == their.tag and
2933             self._expl == their._expl
2934         )
2935
2936     def __call__(
2937             self,
2938             value=None,
2939             impl=None,
2940             expl=None,
2941             optional=None,
2942     ):
2943         return self.__class__(
2944             impl=self.tag if impl is None else impl,
2945             expl=self._expl if expl is None else expl,
2946             optional=self.optional if optional is None else optional,
2947         )
2948
2949     def _encode(self):
2950         return self.tag + len_encode(0)
2951
2952     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2953         try:
2954             t, _, lv = tag_strip(tlv)
2955         except DecodeError as err:
2956             raise err.__class__(
2957                 msg=err.msg,
2958                 klass=self.__class__,
2959                 decode_path=decode_path,
2960                 offset=offset,
2961             )
2962         if t != self.tag:
2963             raise TagMismatch(
2964                 klass=self.__class__,
2965                 decode_path=decode_path,
2966                 offset=offset,
2967             )
2968         if tag_only:  # pragma: no cover
2969             return
2970         try:
2971             l, _, v = len_decode(lv)
2972         except DecodeError as err:
2973             raise err.__class__(
2974                 msg=err.msg,
2975                 klass=self.__class__,
2976                 decode_path=decode_path,
2977                 offset=offset,
2978             )
2979         if l != 0:
2980             raise InvalidLength(
2981                 "Null must have zero length",
2982                 klass=self.__class__,
2983                 decode_path=decode_path,
2984                 offset=offset,
2985             )
2986         obj = self.__class__(
2987             impl=self.tag,
2988             expl=self._expl,
2989             optional=self.optional,
2990             _decoded=(offset, 1, 0),
2991         )
2992         return obj, v
2993
2994     def __repr__(self):
2995         return pp_console_row(next(self.pps()))
2996
2997     def pps(self, decode_path=()):
2998         yield _pp(
2999             obj=self,
3000             asn1_type_name=self.asn1_type_name,
3001             obj_name=self.__class__.__name__,
3002             decode_path=decode_path,
3003             optional=self.optional,
3004             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3005             expl=None if self._expl is None else tag_decode(self._expl),
3006             offset=self.offset,
3007             tlen=self.tlen,
3008             llen=self.llen,
3009             vlen=self.vlen,
3010             expl_offset=self.expl_offset if self.expled else None,
3011             expl_tlen=self.expl_tlen if self.expled else None,
3012             expl_llen=self.expl_llen if self.expled else None,
3013             expl_vlen=self.expl_vlen if self.expled else None,
3014             expl_lenindef=self.expl_lenindef,
3015             bered=self.bered,
3016         )
3017         for pp in self.pps_lenindef(decode_path):
3018             yield pp
3019
3020
3021 class ObjectIdentifier(Obj):
3022     """``OBJECT IDENTIFIER`` OID type
3023
3024     >>> oid = ObjectIdentifier((1, 2, 3))
3025     OBJECT IDENTIFIER 1.2.3
3026     >>> oid == ObjectIdentifier("1.2.3")
3027     True
3028     >>> tuple(oid)
3029     (1, 2, 3)
3030     >>> str(oid)
3031     '1.2.3'
3032     >>> oid + (4, 5) + ObjectIdentifier("1.7")
3033     OBJECT IDENTIFIER 1.2.3.4.5.1.7
3034
3035     >>> str(ObjectIdentifier((3, 1)))
3036     Traceback (most recent call last):
3037     pyderasn.InvalidOID: unacceptable first arc value
3038     """
3039     __slots__ = ("defines",)
3040     tag_default = tag_encode(6)
3041     asn1_type_name = "OBJECT IDENTIFIER"
3042
3043     def __init__(
3044             self,
3045             value=None,
3046             defines=(),
3047             impl=None,
3048             expl=None,
3049             default=None,
3050             optional=False,
3051             _decoded=(0, 0, 0),
3052     ):
3053         """
3054         :param value: set the value. Either tuples of integers,
3055                       string of "."-concatenated integers, or
3056                       :py:class:`pyderasn.ObjectIdentifier` object
3057         :param defines: sequence of tuples. Each tuple has two elements.
3058                         First one is relative to current one decode
3059                         path, aiming to the field defined by that OID.
3060                         Read about relative path in
3061                         :py:func:`pyderasn.abs_decode_path`. Second
3062                         tuple element is ``{OID: pyderasn.Obj()}``
3063                         dictionary, mapping between current OID value
3064                         and structure applied to defined field.
3065                         :ref:`Read about DEFINED BY <definedby>`
3066         :param bytes impl: override default tag with ``IMPLICIT`` one
3067         :param bytes expl: override default tag with ``EXPLICIT`` one
3068         :param default: set default value. Type same as in ``value``
3069         :param bool optional: is object ``OPTIONAL`` in sequence
3070         """
3071         super(ObjectIdentifier, self).__init__(
3072             impl,
3073             expl,
3074             default,
3075             optional,
3076             _decoded,
3077         )
3078         self._value = value
3079         if value is not None:
3080             self._value = self._value_sanitize(value)
3081         if default is not None:
3082             default = self._value_sanitize(default)
3083             self.default = self.__class__(
3084                 value=default,
3085                 impl=self.tag,
3086                 expl=self._expl,
3087             )
3088             if self._value is None:
3089                 self._value = default
3090         self.defines = defines
3091
3092     def __add__(self, their):
3093         if isinstance(their, self.__class__):
3094             return self.__class__(self._value + their._value)
3095         if isinstance(their, tuple):
3096             return self.__class__(self._value + their)
3097         raise InvalidValueType((self.__class__, tuple))
3098
3099     def _value_sanitize(self, value):
3100         if issubclass(value.__class__, ObjectIdentifier):
3101             return value._value
3102         if isinstance(value, string_types):
3103             try:
3104                 value = tuple(int(arc) for arc in value.split("."))
3105             except ValueError:
3106                 raise InvalidOID("unacceptable arcs values")
3107         if isinstance(value, tuple):
3108             if len(value) < 2:
3109                 raise InvalidOID("less than 2 arcs")
3110             first_arc = value[0]
3111             if first_arc in (0, 1):
3112                 if not (0 <= value[1] <= 39):
3113                     raise InvalidOID("second arc is too wide")
3114             elif first_arc == 2:
3115                 pass
3116             else:
3117                 raise InvalidOID("unacceptable first arc value")
3118             return value
3119         raise InvalidValueType((self.__class__, str, tuple))
3120
3121     @property
3122     def ready(self):
3123         return self._value is not None
3124
3125     def copy(self):
3126         obj = self.__class__()
3127         obj._value = self._value
3128         obj.defines = self.defines
3129         obj.tag = self.tag
3130         obj._expl = self._expl
3131         obj.default = self.default
3132         obj.optional = self.optional
3133         obj.offset = self.offset
3134         obj.llen = self.llen
3135         obj.vlen = self.vlen
3136         obj.expl_lenindef = self.expl_lenindef
3137         obj.lenindef = self.lenindef
3138         obj.ber_encoded = self.ber_encoded
3139         return obj
3140
3141     def __iter__(self):
3142         self._assert_ready()
3143         return iter(self._value)
3144
3145     def __str__(self):
3146         return ".".join(str(arc) for arc in self._value or ())
3147
3148     def __hash__(self):
3149         self._assert_ready()
3150         return hash(
3151             self.tag +
3152             bytes(self._expl or b"") +
3153             str(self._value).encode("ascii"),
3154         )
3155
3156     def __eq__(self, their):
3157         if isinstance(their, tuple):
3158             return self._value == their
3159         if not issubclass(their.__class__, ObjectIdentifier):
3160             return False
3161         return (
3162             self.tag == their.tag and
3163             self._expl == their._expl and
3164             self._value == their._value
3165         )
3166
3167     def __lt__(self, their):
3168         return self._value < their._value
3169
3170     def __call__(
3171             self,
3172             value=None,
3173             defines=None,
3174             impl=None,
3175             expl=None,
3176             default=None,
3177             optional=None,
3178     ):
3179         return self.__class__(
3180             value=value,
3181             defines=self.defines if defines is None else defines,
3182             impl=self.tag if impl is None else impl,
3183             expl=self._expl if expl is None else expl,
3184             default=self.default if default is None else default,
3185             optional=self.optional if optional is None else optional,
3186         )
3187
3188     def _encode(self):
3189         self._assert_ready()
3190         value = self._value
3191         first_value = value[1]
3192         first_arc = value[0]
3193         if first_arc == 0:
3194             pass
3195         elif first_arc == 1:
3196             first_value += 40
3197         elif first_arc == 2:
3198             first_value += 80
3199         else:  # pragma: no cover
3200             raise RuntimeError("invalid arc is stored")
3201         octets = [zero_ended_encode(first_value)]
3202         for arc in value[2:]:
3203             octets.append(zero_ended_encode(arc))
3204         v = b"".join(octets)
3205         return b"".join((self.tag, len_encode(len(v)), v))
3206
3207     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3208         try:
3209             t, _, lv = tag_strip(tlv)
3210         except DecodeError as err:
3211             raise err.__class__(
3212                 msg=err.msg,
3213                 klass=self.__class__,
3214                 decode_path=decode_path,
3215                 offset=offset,
3216             )
3217         if t != self.tag:
3218             raise TagMismatch(
3219                 klass=self.__class__,
3220                 decode_path=decode_path,
3221                 offset=offset,
3222             )
3223         if tag_only:  # pragma: no cover
3224             return
3225         try:
3226             l, llen, v = len_decode(lv)
3227         except DecodeError as err:
3228             raise err.__class__(
3229                 msg=err.msg,
3230                 klass=self.__class__,
3231                 decode_path=decode_path,
3232                 offset=offset,
3233             )
3234         if l > len(v):
3235             raise NotEnoughData(
3236                 "encoded length is longer than data",
3237                 klass=self.__class__,
3238                 decode_path=decode_path,
3239                 offset=offset,
3240             )
3241         if l == 0:
3242             raise NotEnoughData(
3243                 "zero length",
3244                 klass=self.__class__,
3245                 decode_path=decode_path,
3246                 offset=offset,
3247             )
3248         v, tail = v[:l], v[l:]
3249         arcs = []
3250         ber_encoded = False
3251         while len(v) > 0:
3252             i = 0
3253             arc = 0
3254             while True:
3255                 octet = indexbytes(v, i)
3256                 if i == 0 and octet == 0x80:
3257                     if ctx.get("bered", False):
3258                         ber_encoded = True
3259                     else:
3260                         raise DecodeError("non normalized arc encoding")
3261                 arc = (arc << 7) | (octet & 0x7F)
3262                 if octet & 0x80 == 0:
3263                     arcs.append(arc)
3264                     v = v[i + 1:]
3265                     break
3266                 i += 1
3267                 if i == len(v):
3268                     raise DecodeError(
3269                         "unfinished OID",
3270                         klass=self.__class__,
3271                         decode_path=decode_path,
3272                         offset=offset,
3273                     )
3274         first_arc = 0
3275         second_arc = arcs[0]
3276         if 0 <= second_arc <= 39:
3277             first_arc = 0
3278         elif 40 <= second_arc <= 79:
3279             first_arc = 1
3280             second_arc -= 40
3281         else:
3282             first_arc = 2
3283             second_arc -= 80
3284         obj = self.__class__(
3285             value=tuple([first_arc, second_arc] + arcs[1:]),
3286             impl=self.tag,
3287             expl=self._expl,
3288             default=self.default,
3289             optional=self.optional,
3290             _decoded=(offset, llen, l),
3291         )
3292         if ber_encoded:
3293             obj.ber_encoded = True
3294         return obj, tail
3295
3296     def __repr__(self):
3297         return pp_console_row(next(self.pps()))
3298
3299     def pps(self, decode_path=()):
3300         yield _pp(
3301             obj=self,
3302             asn1_type_name=self.asn1_type_name,
3303             obj_name=self.__class__.__name__,
3304             decode_path=decode_path,
3305             value=str(self) if self.ready else None,
3306             optional=self.optional,
3307             default=self == self.default,
3308             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3309             expl=None if self._expl is None else tag_decode(self._expl),
3310             offset=self.offset,
3311             tlen=self.tlen,
3312             llen=self.llen,
3313             vlen=self.vlen,
3314             expl_offset=self.expl_offset if self.expled else None,
3315             expl_tlen=self.expl_tlen if self.expled else None,
3316             expl_llen=self.expl_llen if self.expled else None,
3317             expl_vlen=self.expl_vlen if self.expled else None,
3318             expl_lenindef=self.expl_lenindef,
3319             ber_encoded=self.ber_encoded,
3320             bered=self.bered,
3321         )
3322         for pp in self.pps_lenindef(decode_path):
3323             yield pp
3324
3325
3326 class Enumerated(Integer):
3327     """``ENUMERATED`` integer type
3328
3329     This type is identical to :py:class:`pyderasn.Integer`, but requires
3330     schema to be specified and does not accept values missing from it.
3331     """
3332     __slots__ = ()
3333     tag_default = tag_encode(10)
3334     asn1_type_name = "ENUMERATED"
3335
3336     def __init__(
3337             self,
3338             value=None,
3339             impl=None,
3340             expl=None,
3341             default=None,
3342             optional=False,
3343             _specs=None,
3344             _decoded=(0, 0, 0),
3345             bounds=None,  # dummy argument, workability for Integer.decode
3346     ):
3347         super(Enumerated, self).__init__(
3348             value=value,
3349             impl=impl,
3350             expl=expl,
3351             default=default,
3352             optional=optional,
3353             _specs=_specs,
3354             _decoded=_decoded,
3355         )
3356         if len(self.specs) == 0:
3357             raise ValueError("schema must be specified")
3358
3359     def _value_sanitize(self, value):
3360         if isinstance(value, self.__class__):
3361             value = value._value
3362         elif isinstance(value, integer_types):
3363             for _value in itervalues(self.specs):
3364                 if _value == value:
3365                     break
3366             else:
3367                 raise DecodeError(
3368                     "unknown integer value: %s" % value,
3369                     klass=self.__class__,
3370                 )
3371         elif isinstance(value, string_types):
3372             value = self.specs.get(value)
3373             if value is None:
3374                 raise ObjUnknown("integer value: %s" % value)
3375         else:
3376             raise InvalidValueType((self.__class__, int, str))
3377         return value
3378
3379     def copy(self):
3380         obj = self.__class__(_specs=self.specs)
3381         obj._value = self._value
3382         obj._bound_min = self._bound_min
3383         obj._bound_max = self._bound_max
3384         obj.tag = self.tag
3385         obj._expl = self._expl
3386         obj.default = self.default
3387         obj.optional = self.optional
3388         obj.offset = self.offset
3389         obj.llen = self.llen
3390         obj.vlen = self.vlen
3391         obj.expl_lenindef = self.expl_lenindef
3392         obj.lenindef = self.lenindef
3393         obj.ber_encoded = self.ber_encoded
3394         return obj
3395
3396     def __call__(
3397             self,
3398             value=None,
3399             impl=None,
3400             expl=None,
3401             default=None,
3402             optional=None,
3403             _specs=None,
3404     ):
3405         return self.__class__(
3406             value=value,
3407             impl=self.tag if impl is None else impl,
3408             expl=self._expl if expl is None else expl,
3409             default=self.default if default is None else default,
3410             optional=self.optional if optional is None else optional,
3411             _specs=self.specs,
3412         )
3413
3414
3415 class CommonString(OctetString):
3416     """Common class for all strings
3417
3418     Everything resembles :py:class:`pyderasn.OctetString`, except
3419     ability to deal with unicode text strings.
3420
3421     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
3422     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3423     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
3424     True
3425     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
3426     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
3427     >>> str(s)
3428     'привет Ð¼Ð¸Ñ€'
3429     >>> hexenc(bytes(s))
3430     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3431
3432     >>> PrintableString("привет Ð¼Ð¸Ñ€")
3433     Traceback (most recent call last):
3434     pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3435
3436     >>> BMPString("ада", bounds=(2, 2))
3437     Traceback (most recent call last):
3438     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3439     >>> s = BMPString("ад", bounds=(2, 2))
3440     >>> s.encoding
3441     'utf-16-be'
3442     >>> hexenc(bytes(s))
3443     '04300434'
3444
3445     .. list-table::
3446        :header-rows: 1
3447
3448        * - Class
3449          - Text Encoding
3450        * - :py:class:`pyderasn.UTF8String`
3451          - utf-8
3452        * - :py:class:`pyderasn.NumericString`
3453          - ascii
3454        * - :py:class:`pyderasn.PrintableString`
3455          - ascii
3456        * - :py:class:`pyderasn.TeletexString`
3457          - ascii
3458        * - :py:class:`pyderasn.T61String`
3459          - ascii
3460        * - :py:class:`pyderasn.VideotexString`
3461          - iso-8859-1
3462        * - :py:class:`pyderasn.IA5String`
3463          - ascii
3464        * - :py:class:`pyderasn.GraphicString`
3465          - iso-8859-1
3466        * - :py:class:`pyderasn.VisibleString`
3467          - ascii
3468        * - :py:class:`pyderasn.ISO646String`
3469          - ascii
3470        * - :py:class:`pyderasn.GeneralString`
3471          - iso-8859-1
3472        * - :py:class:`pyderasn.UniversalString`
3473          - utf-32-be
3474        * - :py:class:`pyderasn.BMPString`
3475          - utf-16-be
3476     """
3477     __slots__ = ("encoding",)
3478
3479     def _value_sanitize(self, value):
3480         value_raw = None
3481         value_decoded = None
3482         if isinstance(value, self.__class__):
3483             value_raw = value._value
3484         elif isinstance(value, text_type):
3485             value_decoded = value
3486         elif isinstance(value, binary_type):
3487             value_raw = value
3488         else:
3489             raise InvalidValueType((self.__class__, text_type, binary_type))
3490         try:
3491             value_raw = (
3492                 value_decoded.encode(self.encoding)
3493                 if value_raw is None else value_raw
3494             )
3495             value_decoded = (
3496                 value_raw.decode(self.encoding)
3497                 if value_decoded is None else value_decoded
3498             )
3499         except (UnicodeEncodeError, UnicodeDecodeError) as err:
3500             raise DecodeError(str(err))
3501         if not self._bound_min <= len(value_decoded) <= self._bound_max:
3502             raise BoundsError(
3503                 self._bound_min,
3504                 len(value_decoded),
3505                 self._bound_max,
3506             )
3507         return value_raw
3508
3509     def __eq__(self, their):
3510         if isinstance(their, binary_type):
3511             return self._value == their
3512         if isinstance(their, text_type):
3513             return self._value == their.encode(self.encoding)
3514         if not isinstance(their, self.__class__):
3515             return False
3516         return (
3517             self._value == their._value and
3518             self.tag == their.tag and
3519             self._expl == their._expl
3520         )
3521
3522     def __unicode__(self):
3523         if self.ready:
3524             return self._value.decode(self.encoding)
3525         return text_type(self._value)
3526
3527     def __repr__(self):
3528         return pp_console_row(next(self.pps(no_unicode=PY2)))
3529
3530     def pps(self, decode_path=(), no_unicode=False):
3531         value = None
3532         if self.ready:
3533             value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3534         yield _pp(
3535             obj=self,
3536             asn1_type_name=self.asn1_type_name,
3537             obj_name=self.__class__.__name__,
3538             decode_path=decode_path,
3539             value=value,
3540             optional=self.optional,
3541             default=self == self.default,
3542             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3543             expl=None if self._expl is None else tag_decode(self._expl),
3544             offset=self.offset,
3545             tlen=self.tlen,
3546             llen=self.llen,
3547             vlen=self.vlen,
3548             expl_offset=self.expl_offset if self.expled else None,
3549             expl_tlen=self.expl_tlen if self.expled else None,
3550             expl_llen=self.expl_llen if self.expled else None,
3551             expl_vlen=self.expl_vlen if self.expled else None,
3552             expl_lenindef=self.expl_lenindef,
3553             ber_encoded=self.ber_encoded,
3554             bered=self.bered,
3555         )
3556         for pp in self.pps_lenindef(decode_path):
3557             yield pp
3558
3559
3560 class UTF8String(CommonString):
3561     __slots__ = ()
3562     tag_default = tag_encode(12)
3563     encoding = "utf-8"
3564     asn1_type_name = "UTF8String"
3565
3566
3567 class AllowableCharsMixin(object):
3568     @property
3569     def allowable_chars(self):
3570         if PY2:
3571             return self._allowable_chars
3572         return frozenset(six_unichr(c) for c in self._allowable_chars)
3573
3574
3575 class NumericString(AllowableCharsMixin, CommonString):
3576     """Numeric string
3577
3578     Its value is properly sanitized: only ASCII digits with spaces can
3579     be stored.
3580
3581     >>> NumericString().allowable_chars
3582     set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3583     """
3584     __slots__ = ()
3585     tag_default = tag_encode(18)
3586     encoding = "ascii"
3587     asn1_type_name = "NumericString"
3588     _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3589
3590     def _value_sanitize(self, value):
3591         value = super(NumericString, self)._value_sanitize(value)
3592         if not frozenset(value) <= self._allowable_chars:
3593             raise DecodeError("non-numeric value")
3594         return value
3595
3596
3597 class PrintableString(AllowableCharsMixin, CommonString):
3598     """Printable string
3599
3600     Its value is properly sanitized: see X.680 41.4 table 10.
3601
3602     >>> PrintableString().allowable_chars
3603     >>> set([' ', "'", ..., 'z'])
3604     """
3605     __slots__ = ()
3606     tag_default = tag_encode(19)
3607     encoding = "ascii"
3608     asn1_type_name = "PrintableString"
3609     _allowable_chars = frozenset(
3610         (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3611     )
3612
3613     def _value_sanitize(self, value):
3614         value = super(PrintableString, self)._value_sanitize(value)
3615         if not frozenset(value) <= self._allowable_chars:
3616             raise DecodeError("non-printable value")
3617         return value
3618
3619
3620 class TeletexString(CommonString):
3621     __slots__ = ()
3622     tag_default = tag_encode(20)
3623     encoding = "ascii"
3624     asn1_type_name = "TeletexString"
3625
3626
3627 class T61String(TeletexString):
3628     __slots__ = ()
3629     asn1_type_name = "T61String"
3630
3631
3632 class VideotexString(CommonString):
3633     __slots__ = ()
3634     tag_default = tag_encode(21)
3635     encoding = "iso-8859-1"
3636     asn1_type_name = "VideotexString"
3637
3638
3639 class IA5String(CommonString):
3640     __slots__ = ()
3641     tag_default = tag_encode(22)
3642     encoding = "ascii"
3643     asn1_type_name = "IA5"
3644
3645
3646 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3647 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3648 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3649
3650
3651 class UTCTime(CommonString):
3652     """``UTCTime`` datetime type
3653
3654     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3655     UTCTime UTCTime 2017-09-30T22:07:50
3656     >>> str(t)
3657     '170930220750Z'
3658     >>> bytes(t)
3659     b'170930220750Z'
3660     >>> t.todatetime()
3661     datetime.datetime(2017, 9, 30, 22, 7, 50)
3662     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3663     datetime.datetime(1957, 9, 30, 22, 7, 50)
3664     """
3665     __slots__ = ()
3666     tag_default = tag_encode(23)
3667     encoding = "ascii"
3668     asn1_type_name = "UTCTime"
3669
3670     fmt = "%y%m%d%H%M%SZ"
3671
3672     def __init__(
3673             self,
3674             value=None,
3675             impl=None,
3676             expl=None,
3677             default=None,
3678             optional=False,
3679             _decoded=(0, 0, 0),
3680             bounds=None,  # dummy argument, workability for OctetString.decode
3681     ):
3682         """
3683         :param value: set the value. Either datetime type, or
3684                       :py:class:`pyderasn.UTCTime` object
3685         :param bytes impl: override default tag with ``IMPLICIT`` one
3686         :param bytes expl: override default tag with ``EXPLICIT`` one
3687         :param default: set default value. Type same as in ``value``
3688         :param bool optional: is object ``OPTIONAL`` in sequence
3689         """
3690         super(UTCTime, self).__init__(
3691             impl=impl,
3692             expl=expl,
3693             default=default,
3694             optional=optional,
3695             _decoded=_decoded,
3696         )
3697         self._value = value
3698         if value is not None:
3699             self._value = self._value_sanitize(value)
3700         if default is not None:
3701             default = self._value_sanitize(default)
3702             self.default = self.__class__(
3703                 value=default,
3704                 impl=self.tag,
3705                 expl=self._expl,
3706             )
3707             if self._value is None:
3708                 self._value = default
3709
3710     def _value_sanitize(self, value):
3711         if isinstance(value, binary_type):
3712             try:
3713                 value_decoded = value.decode("ascii")
3714             except (UnicodeEncodeError, UnicodeDecodeError) as err:
3715                 raise DecodeError("invalid UTCTime encoding")
3716             if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3717                 try:
3718                     datetime.strptime(value_decoded, self.fmt)
3719                 except (TypeError, ValueError):
3720                     raise DecodeError("invalid UTCTime format")
3721                 return value
3722             else:
3723                 raise DecodeError("invalid UTCTime length")
3724         if isinstance(value, self.__class__):
3725             return value._value
3726         if isinstance(value, datetime):
3727             return value.strftime(self.fmt).encode("ascii")
3728         raise InvalidValueType((self.__class__, datetime))
3729
3730     def __eq__(self, their):
3731         if isinstance(their, binary_type):
3732             return self._value == their
3733         if isinstance(their, datetime):
3734             return self.todatetime() == their
3735         if not isinstance(their, self.__class__):
3736             return False
3737         return (
3738             self._value == their._value and
3739             self.tag == their.tag and
3740             self._expl == their._expl
3741         )
3742
3743     def todatetime(self):
3744         """Convert to datetime
3745
3746         :returns: datetime
3747
3748         Pay attention that UTCTime can not hold full year, so all years
3749         having < 50 years are treated as 20xx, 19xx otherwise, according
3750         to X.509 recomendation.
3751         """
3752         value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3753         year = value.year % 100
3754         return datetime(
3755             year=(2000 + year) if year < 50 else (1900 + year),
3756             month=value.month,
3757             day=value.day,
3758             hour=value.hour,
3759             minute=value.minute,
3760             second=value.second,
3761         )
3762
3763     def __repr__(self):
3764         return pp_console_row(next(self.pps()))
3765
3766     def pps(self, decode_path=()):
3767         yield _pp(
3768             obj=self,
3769             asn1_type_name=self.asn1_type_name,
3770             obj_name=self.__class__.__name__,
3771             decode_path=decode_path,
3772             value=self.todatetime().isoformat() if self.ready else None,
3773             optional=self.optional,
3774             default=self == self.default,
3775             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3776             expl=None if self._expl is None else tag_decode(self._expl),
3777             offset=self.offset,
3778             tlen=self.tlen,
3779             llen=self.llen,
3780             vlen=self.vlen,
3781             expl_offset=self.expl_offset if self.expled else None,
3782             expl_tlen=self.expl_tlen if self.expled else None,
3783             expl_llen=self.expl_llen if self.expled else None,
3784             expl_vlen=self.expl_vlen if self.expled else None,
3785             expl_lenindef=self.expl_lenindef,
3786             ber_encoded=self.ber_encoded,
3787             bered=self.bered,
3788         )
3789         for pp in self.pps_lenindef(decode_path):
3790             yield pp
3791
3792
3793 class GeneralizedTime(UTCTime):
3794     """``GeneralizedTime`` datetime type
3795
3796     This type is similar to :py:class:`pyderasn.UTCTime`.
3797
3798     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3799     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3800     >>> str(t)
3801     '20170930220750.000123Z'
3802     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3803     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3804     """
3805     __slots__ = ()
3806     tag_default = tag_encode(24)
3807     asn1_type_name = "GeneralizedTime"
3808
3809     fmt = "%Y%m%d%H%M%SZ"
3810     fmt_ms = "%Y%m%d%H%M%S.%fZ"
3811
3812     def _value_sanitize(self, value):
3813         if isinstance(value, binary_type):
3814             try:
3815                 value_decoded = value.decode("ascii")
3816             except (UnicodeEncodeError, UnicodeDecodeError) as err:
3817                 raise DecodeError("invalid GeneralizedTime encoding")
3818             if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3819                 try:
3820                     datetime.strptime(value_decoded, self.fmt)
3821                 except (TypeError, ValueError):
3822                     raise DecodeError(
3823                         "invalid GeneralizedTime (without ms) format",
3824                     )
3825                 return value
3826             elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3827                 try:
3828                     datetime.strptime(value_decoded, self.fmt_ms)
3829                 except (TypeError, ValueError):
3830                     raise DecodeError(
3831                         "invalid GeneralizedTime (with ms) format",
3832                     )
3833                 return value
3834             else:
3835                 raise DecodeError(
3836                     "invalid GeneralizedTime length",
3837                     klass=self.__class__,
3838                 )
3839         if isinstance(value, self.__class__):
3840             return value._value
3841         if isinstance(value, datetime):
3842             return value.strftime(
3843                 self.fmt_ms if value.microsecond > 0 else self.fmt
3844             ).encode("ascii")
3845         raise InvalidValueType((self.__class__, datetime))
3846
3847     def todatetime(self):
3848         value = self._value.decode("ascii")
3849         if len(value) == LEN_YYYYMMDDHHMMSSZ:
3850             return datetime.strptime(value, self.fmt)
3851         return datetime.strptime(value, self.fmt_ms)
3852
3853
3854 class GraphicString(CommonString):
3855     __slots__ = ()
3856     tag_default = tag_encode(25)
3857     encoding = "iso-8859-1"
3858     asn1_type_name = "GraphicString"
3859
3860
3861 class VisibleString(CommonString):
3862     __slots__ = ()
3863     tag_default = tag_encode(26)
3864     encoding = "ascii"
3865     asn1_type_name = "VisibleString"
3866
3867
3868 class ISO646String(VisibleString):
3869     __slots__ = ()
3870     asn1_type_name = "ISO646String"
3871
3872
3873 class GeneralString(CommonString):
3874     __slots__ = ()
3875     tag_default = tag_encode(27)
3876     encoding = "iso-8859-1"
3877     asn1_type_name = "GeneralString"
3878
3879
3880 class UniversalString(CommonString):
3881     __slots__ = ()
3882     tag_default = tag_encode(28)
3883     encoding = "utf-32-be"
3884     asn1_type_name = "UniversalString"
3885
3886
3887 class BMPString(CommonString):
3888     __slots__ = ()
3889     tag_default = tag_encode(30)
3890     encoding = "utf-16-be"
3891     asn1_type_name = "BMPString"
3892
3893
3894 class Choice(Obj):
3895     """``CHOICE`` special type
3896
3897     ::
3898
3899         class GeneralName(Choice):
3900             schema = (
3901                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3902                 ("dNSName", IA5String(impl=tag_ctxp(2))),
3903             )
3904
3905     >>> gn = GeneralName()
3906     GeneralName CHOICE
3907     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3908     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3909     >>> gn["dNSName"] = IA5String("bar.baz")
3910     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3911     >>> gn["rfc822Name"]
3912     None
3913     >>> gn["dNSName"]
3914     [2] IA5String IA5 bar.baz
3915     >>> gn.choice
3916     'dNSName'
3917     >>> gn.value == gn["dNSName"]
3918     True
3919     >>> gn.specs
3920     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3921
3922     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3923     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3924     """
3925     __slots__ = ("specs",)
3926     tag_default = None
3927     asn1_type_name = "CHOICE"
3928
3929     def __init__(
3930             self,
3931             value=None,
3932             schema=None,
3933             impl=None,
3934             expl=None,
3935             default=None,
3936             optional=False,
3937             _decoded=(0, 0, 0),
3938     ):
3939         """
3940         :param value: set the value. Either ``(choice, value)`` tuple, or
3941                       :py:class:`pyderasn.Choice` object
3942         :param bytes impl: can not be set, do **not** use it
3943         :param bytes expl: override default tag with ``EXPLICIT`` one
3944         :param default: set default value. Type same as in ``value``
3945         :param bool optional: is object ``OPTIONAL`` in sequence
3946         """
3947         if impl is not None:
3948             raise ValueError("no implicit tag allowed for CHOICE")
3949         super(Choice, self).__init__(None, expl, default, optional, _decoded)
3950         if schema is None:
3951             schema = getattr(self, "schema", ())
3952         if len(schema) == 0:
3953             raise ValueError("schema must be specified")
3954         self.specs = (
3955             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3956         )
3957         self._value = None
3958         if value is not None:
3959             self._value = self._value_sanitize(value)
3960         if default is not None:
3961             default_value = self._value_sanitize(default)
3962             default_obj = self.__class__(impl=self.tag, expl=self._expl)
3963             default_obj.specs = self.specs
3964             default_obj._value = default_value
3965             self.default = default_obj
3966             if value is None:
3967                 self._value = default_obj.copy()._value
3968
3969     def _value_sanitize(self, value):
3970         if isinstance(value, tuple) and len(value) == 2:
3971             choice, obj = value
3972             spec = self.specs.get(choice)
3973             if spec is None:
3974                 raise ObjUnknown(choice)
3975             if not isinstance(obj, spec.__class__):
3976                 raise InvalidValueType((spec,))
3977             return (choice, spec(obj))
3978         if isinstance(value, self.__class__):
3979             return value._value
3980         raise InvalidValueType((self.__class__, tuple))
3981
3982     @property
3983     def ready(self):
3984         return self._value is not None and self._value[1].ready
3985
3986     @property
3987     def bered(self):
3988         return self.expl_lenindef or (
3989             (self._value is not None) and
3990             self._value[1].bered
3991         )
3992
3993     def copy(self):
3994         obj = self.__class__(schema=self.specs)
3995         obj._expl = self._expl
3996         obj.default = self.default
3997         obj.optional = self.optional
3998         obj.offset = self.offset
3999         obj.llen = self.llen
4000         obj.vlen = self.vlen
4001         obj.expl_lenindef = self.expl_lenindef
4002         obj.lenindef = self.lenindef
4003         obj.ber_encoded = self.ber_encoded
4004         value = self._value
4005         if value is not None:
4006             obj._value = (value[0], value[1].copy())
4007         return obj
4008
4009     def __eq__(self, their):
4010         if isinstance(their, tuple) and len(their) == 2:
4011             return self._value == their
4012         if not isinstance(their, self.__class__):
4013             return False
4014         return (
4015             self.specs == their.specs and
4016             self._value == their._value
4017         )
4018
4019     def __call__(
4020             self,
4021             value=None,
4022             expl=None,
4023             default=None,
4024             optional=None,
4025     ):
4026         return self.__class__(
4027             value=value,
4028             schema=self.specs,
4029             expl=self._expl if expl is None else expl,
4030             default=self.default if default is None else default,
4031             optional=self.optional if optional is None else optional,
4032         )
4033
4034     @property
4035     def choice(self):
4036         self._assert_ready()
4037         return self._value[0]
4038
4039     @property
4040     def value(self):
4041         self._assert_ready()
4042         return self._value[1]
4043
4044     def __getitem__(self, key):
4045         if key not in self.specs:
4046             raise ObjUnknown(key)
4047         if self._value is None:
4048             return None
4049         choice, value = self._value
4050         if choice != key:
4051             return None
4052         return value
4053
4054     def __setitem__(self, key, value):
4055         spec = self.specs.get(key)
4056         if spec is None:
4057             raise ObjUnknown(key)
4058         if not isinstance(value, spec.__class__):
4059             raise InvalidValueType((spec.__class__,))
4060         self._value = (key, spec(value))
4061
4062     @property
4063     def tlen(self):
4064         return 0
4065
4066     @property
4067     def decoded(self):
4068         return self._value[1].decoded if self.ready else False
4069
4070     def _encode(self):
4071         self._assert_ready()
4072         return self._value[1].encode()
4073
4074     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4075         for choice, spec in iteritems(self.specs):
4076             sub_decode_path = decode_path + (choice,)
4077             try:
4078                 spec.decode(
4079                     tlv,
4080                     offset=offset,
4081                     leavemm=True,
4082                     decode_path=sub_decode_path,
4083                     ctx=ctx,
4084                     tag_only=True,
4085                     _ctx_immutable=False,
4086                 )
4087             except TagMismatch:
4088                 continue
4089             break
4090         else:
4091             raise TagMismatch(
4092                 klass=self.__class__,
4093                 decode_path=decode_path,
4094                 offset=offset,
4095             )
4096         if tag_only:  # pragma: no cover
4097             return
4098         value, tail = spec.decode(
4099             tlv,
4100             offset=offset,
4101             leavemm=True,
4102             decode_path=sub_decode_path,
4103             ctx=ctx,
4104             _ctx_immutable=False,
4105         )
4106         obj = self.__class__(
4107             schema=self.specs,
4108             expl=self._expl,
4109             default=self.default,
4110             optional=self.optional,
4111             _decoded=(offset, 0, value.fulllen),
4112         )
4113         obj._value = (choice, value)
4114         return obj, tail
4115
4116     def __repr__(self):
4117         value = pp_console_row(next(self.pps()))
4118         if self.ready:
4119             value = "%s[%r]" % (value, self.value)
4120         return value
4121
4122     def pps(self, decode_path=()):
4123         yield _pp(
4124             obj=self,
4125             asn1_type_name=self.asn1_type_name,
4126             obj_name=self.__class__.__name__,
4127             decode_path=decode_path,
4128             value=self.choice if self.ready else None,
4129             optional=self.optional,
4130             default=self == self.default,
4131             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4132             expl=None if self._expl is None else tag_decode(self._expl),
4133             offset=self.offset,
4134             tlen=self.tlen,
4135             llen=self.llen,
4136             vlen=self.vlen,
4137             expl_lenindef=self.expl_lenindef,
4138             bered=self.bered,
4139         )
4140         if self.ready:
4141             yield self.value.pps(decode_path=decode_path + (self.choice,))
4142         for pp in self.pps_lenindef(decode_path):
4143             yield pp
4144
4145
4146 class PrimitiveTypes(Choice):
4147     """Predefined ``CHOICE`` for all generic primitive types
4148
4149     It could be useful for general decoding of some unspecified values:
4150
4151     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4152     OCTET STRING 3 bytes 666f6f
4153     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4154     INTEGER 1193046
4155     """
4156     __slots__ = ()
4157     schema = tuple((klass.__name__, klass()) for klass in (
4158         Boolean,
4159         Integer,
4160         BitString,
4161         OctetString,
4162         Null,
4163         ObjectIdentifier,
4164         UTF8String,
4165         NumericString,
4166         PrintableString,
4167         TeletexString,
4168         VideotexString,
4169         IA5String,
4170         UTCTime,
4171         GeneralizedTime,
4172         GraphicString,
4173         VisibleString,
4174         ISO646String,
4175         GeneralString,
4176         UniversalString,
4177         BMPString,
4178     ))
4179
4180
4181 class Any(Obj):
4182     """``ANY`` special type
4183
4184     >>> Any(Integer(-123))
4185     ANY 020185
4186     >>> a = Any(OctetString(b"hello world").encode())
4187     ANY 040b68656c6c6f20776f726c64
4188     >>> hexenc(bytes(a))
4189     b'0x040x0bhello world'
4190     """
4191     __slots__ = ("defined",)
4192     tag_default = tag_encode(0)
4193     asn1_type_name = "ANY"
4194
4195     def __init__(
4196             self,
4197             value=None,
4198             expl=None,
4199             optional=False,
4200             _decoded=(0, 0, 0),
4201     ):
4202         """
4203         :param value: set the value. Either any kind of pyderasn's
4204                       **ready** object, or bytes. Pay attention that
4205                       **no** validation is performed is raw binary value
4206                       is valid TLV
4207         :param bytes expl: override default tag with ``EXPLICIT`` one
4208         :param bool optional: is object ``OPTIONAL`` in sequence
4209         """
4210         super(Any, self).__init__(None, expl, None, optional, _decoded)
4211         self._value = None if value is None else self._value_sanitize(value)
4212         self.defined = None
4213
4214     def _value_sanitize(self, value):
4215         if isinstance(value, binary_type):
4216             return value
4217         if isinstance(value, self.__class__):
4218             return value._value
4219         if isinstance(value, Obj):
4220             return value.encode()
4221         raise InvalidValueType((self.__class__, Obj, binary_type))
4222
4223     @property
4224     def ready(self):
4225         return self._value is not None
4226
4227     @property
4228     def bered(self):
4229         if self.expl_lenindef or self.lenindef:
4230             return True
4231         if self.defined is None:
4232             return False
4233         return self.defined[1].bered
4234
4235     def copy(self):
4236         obj = self.__class__()
4237         obj._value = self._value
4238         obj.tag = self.tag
4239         obj._expl = self._expl
4240         obj.optional = self.optional
4241         obj.offset = self.offset
4242         obj.llen = self.llen
4243         obj.vlen = self.vlen
4244         obj.expl_lenindef = self.expl_lenindef
4245         obj.lenindef = self.lenindef
4246         obj.ber_encoded = self.ber_encoded
4247         return obj
4248
4249     def __eq__(self, their):
4250         if isinstance(their, binary_type):
4251             return self._value == their
4252         if issubclass(their.__class__, Any):
4253             return self._value == their._value
4254         return False
4255
4256     def __call__(
4257             self,
4258             value=None,
4259             expl=None,
4260             optional=None,
4261     ):
4262         return self.__class__(
4263             value=value,
4264             expl=self._expl if expl is None else expl,
4265             optional=self.optional if optional is None else optional,
4266         )
4267
4268     def __bytes__(self):
4269         self._assert_ready()
4270         return self._value
4271
4272     @property
4273     def tlen(self):
4274         return 0
4275
4276     def _encode(self):
4277         self._assert_ready()
4278         return self._value
4279
4280     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4281         try:
4282             t, tlen, lv = tag_strip(tlv)
4283         except DecodeError as err:
4284             raise err.__class__(
4285                 msg=err.msg,
4286                 klass=self.__class__,
4287                 decode_path=decode_path,
4288                 offset=offset,
4289             )
4290         try:
4291             l, llen, v = len_decode(lv)
4292         except LenIndefForm as err:
4293             if not ctx.get("bered", False):
4294                 raise err.__class__(
4295                     msg=err.msg,
4296                     klass=self.__class__,
4297                     decode_path=decode_path,
4298                     offset=offset,
4299                 )
4300             llen, vlen, v = 1, 0, lv[1:]
4301             sub_offset = offset + tlen + llen
4302             chunk_i = 0
4303             while v[:EOC_LEN].tobytes() != EOC:
4304                 chunk, v = Any().decode(
4305                     v,
4306                     offset=sub_offset,
4307                     decode_path=decode_path + (str(chunk_i),),
4308                     leavemm=True,
4309                     ctx=ctx,
4310                     _ctx_immutable=False,
4311                 )
4312                 vlen += chunk.tlvlen
4313                 sub_offset += chunk.tlvlen
4314                 chunk_i += 1
4315             tlvlen = tlen + llen + vlen + EOC_LEN
4316             obj = self.__class__(
4317                 value=tlv[:tlvlen].tobytes(),
4318                 expl=self._expl,
4319                 optional=self.optional,
4320                 _decoded=(offset, 0, tlvlen),
4321             )
4322             obj.lenindef = True
4323             obj.tag = t
4324             return obj, v[EOC_LEN:]
4325         except DecodeError as err:
4326             raise err.__class__(
4327                 msg=err.msg,
4328                 klass=self.__class__,
4329                 decode_path=decode_path,
4330                 offset=offset,
4331             )
4332         if l > len(v):
4333             raise NotEnoughData(
4334                 "encoded length is longer than data",
4335                 klass=self.__class__,
4336                 decode_path=decode_path,
4337                 offset=offset,
4338             )
4339         tlvlen = tlen + llen + l
4340         v, tail = tlv[:tlvlen], v[l:]
4341         obj = self.__class__(
4342             value=v.tobytes(),
4343             expl=self._expl,
4344             optional=self.optional,
4345             _decoded=(offset, 0, tlvlen),
4346         )
4347         obj.tag = t
4348         return obj, tail
4349
4350     def __repr__(self):
4351         return pp_console_row(next(self.pps()))
4352
4353     def pps(self, decode_path=()):
4354         yield _pp(
4355             obj=self,
4356             asn1_type_name=self.asn1_type_name,
4357             obj_name=self.__class__.__name__,
4358             decode_path=decode_path,
4359             blob=self._value if self.ready else None,
4360             optional=self.optional,
4361             default=self == self.default,
4362             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4363             expl=None if self._expl is None else tag_decode(self._expl),
4364             offset=self.offset,
4365             tlen=self.tlen,
4366             llen=self.llen,
4367             vlen=self.vlen,
4368             expl_offset=self.expl_offset if self.expled else None,
4369             expl_tlen=self.expl_tlen if self.expled else None,
4370             expl_llen=self.expl_llen if self.expled else None,
4371             expl_vlen=self.expl_vlen if self.expled else None,
4372             expl_lenindef=self.expl_lenindef,
4373             lenindef=self.lenindef,
4374             bered=self.bered,
4375         )
4376         defined_by, defined = self.defined or (None, None)
4377         if defined_by is not None:
4378             yield defined.pps(
4379                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4380             )
4381         for pp in self.pps_lenindef(decode_path):
4382             yield pp
4383
4384
4385 ########################################################################
4386 # ASN.1 constructed types
4387 ########################################################################
4388
4389 def get_def_by_path(defines_by_path, sub_decode_path):
4390     """Get define by decode path
4391     """
4392     for path, define in defines_by_path:
4393         if len(path) != len(sub_decode_path):
4394             continue
4395         for p1, p2 in zip(path, sub_decode_path):
4396             if (p1 != any) and (p1 != p2):
4397                 break
4398         else:
4399             return define
4400
4401
4402 def abs_decode_path(decode_path, rel_path):
4403     """Create an absolute decode path from current and relative ones
4404
4405     :param decode_path: current decode path, starting point. Tuple of strings
4406     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4407                      If first tuple's element is "/", then treat it as
4408                      an absolute path, ignoring ``decode_path`` as
4409                      starting point. Also this tuple can contain ".."
4410                      elements, stripping the leading element from
4411                      ``decode_path``
4412
4413     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4414     ("foo", "bar", "baz", "whatever")
4415     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4416     ("foo", "whatever")
4417     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4418     ("baz", "whatever")
4419     """
4420     if rel_path[0] == "/":
4421         return rel_path[1:]
4422     if rel_path[0] == "..":
4423         return abs_decode_path(decode_path[:-1], rel_path[1:])
4424     return decode_path + rel_path
4425
4426
4427 class Sequence(Obj):
4428     """``SEQUENCE`` structure type
4429
4430     You have to make specification of sequence::
4431
4432         class Extension(Sequence):
4433             schema = (
4434                 ("extnID", ObjectIdentifier()),
4435                 ("critical", Boolean(default=False)),
4436                 ("extnValue", OctetString()),
4437             )
4438
4439     Then, you can work with it as with dictionary.
4440
4441     >>> ext = Extension()
4442     >>> Extension().specs
4443     OrderedDict([
4444         ('extnID', OBJECT IDENTIFIER),
4445         ('critical', BOOLEAN False OPTIONAL DEFAULT),
4446         ('extnValue', OCTET STRING),
4447     ])
4448     >>> ext["extnID"] = "1.2.3"
4449     Traceback (most recent call last):
4450     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4451     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4452
4453     You can determine if sequence is ready to be encoded:
4454
4455     >>> ext.ready
4456     False
4457     >>> ext.encode()
4458     Traceback (most recent call last):
4459     pyderasn.ObjNotReady: object is not ready: extnValue
4460     >>> ext["extnValue"] = OctetString(b"foobar")
4461     >>> ext.ready
4462     True
4463
4464     Value you want to assign, must have the same **type** as in
4465     corresponding specification, but it can have different tags,
4466     optional/default attributes -- they will be taken from specification
4467     automatically::
4468
4469         class TBSCertificate(Sequence):
4470             schema = (
4471                 ("version", Version(expl=tag_ctxc(0), default="v1")),
4472             [...]
4473
4474     >>> tbs = TBSCertificate()
4475     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4476
4477     Assign ``None`` to remove value from sequence.
4478
4479     You can set values in Sequence during its initialization:
4480
4481     >>> AlgorithmIdentifier((
4482         ("algorithm", ObjectIdentifier("1.2.3")),
4483         ("parameters", Any(Null()))
4484     ))
4485     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4486
4487     You can determine if value exists/set in the sequence and take its value:
4488
4489     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4490     (True, True, False)
4491     >>> ext["extnID"]
4492     OBJECT IDENTIFIER 1.2.3
4493
4494     But pay attention that if value has default, then it won't be (not
4495     in) in the sequence (because ``DEFAULT`` must not be encoded in
4496     DER), but you can read its value:
4497
4498     >>> "critical" in ext, ext["critical"]
4499     (False, BOOLEAN False)
4500     >>> ext["critical"] = Boolean(True)
4501     >>> "critical" in ext, ext["critical"]
4502     (True, BOOLEAN True)
4503
4504     All defaulted values are always optional.
4505
4506     .. _allow_default_values_ctx:
4507
4508     DER prohibits default value encoding and will raise an error if
4509     default value is unexpectedly met during decode.
4510     If :ref:`bered <bered_ctx>` context option is set, then no error
4511     will be raised, but ``bered`` attribute set. You can disable strict
4512     defaulted values existence validation by setting
4513     ``"allow_default_values": True`` :ref:`context <ctx>` option.
4514
4515     Two sequences are equal if they have equal specification (schema),
4516     implicit/explicit tagging and the same values.
4517     """
4518     __slots__ = ("specs",)
4519     tag_default = tag_encode(form=TagFormConstructed, num=16)
4520     asn1_type_name = "SEQUENCE"
4521
4522     def __init__(
4523             self,
4524             value=None,
4525             schema=None,
4526             impl=None,
4527             expl=None,
4528             default=None,
4529             optional=False,
4530             _decoded=(0, 0, 0),
4531     ):
4532         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4533         if schema is None:
4534             schema = getattr(self, "schema", ())
4535         self.specs = (
4536             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4537         )
4538         self._value = {}
4539         if value is not None:
4540             if issubclass(value.__class__, Sequence):
4541                 self._value = value._value
4542             elif hasattr(value, "__iter__"):
4543                 for seq_key, seq_value in value:
4544                     self[seq_key] = seq_value
4545             else:
4546                 raise InvalidValueType((Sequence,))
4547         if default is not None:
4548             if not issubclass(default.__class__, Sequence):
4549                 raise InvalidValueType((Sequence,))
4550             default_value = default._value
4551             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4552             default_obj.specs = self.specs
4553             default_obj._value = default_value
4554             self.default = default_obj
4555             if value is None:
4556                 self._value = default_obj.copy()._value
4557
4558     @property
4559     def ready(self):
4560         for name, spec in iteritems(self.specs):
4561             value = self._value.get(name)
4562             if value is None:
4563                 if spec.optional:
4564                     continue
4565                 return False
4566             else:
4567                 if not value.ready:
4568                     return False
4569         return True
4570
4571     @property
4572     def bered(self):
4573         if self.expl_lenindef or self.lenindef or self.ber_encoded:
4574             return True
4575         return any(value.bered for value in itervalues(self._value))
4576
4577     def copy(self):
4578         obj = self.__class__(schema=self.specs)
4579         obj.tag = self.tag
4580         obj._expl = self._expl
4581         obj.default = self.default
4582         obj.optional = self.optional
4583         obj.offset = self.offset
4584         obj.llen = self.llen
4585         obj.vlen = self.vlen
4586         obj.expl_lenindef = self.expl_lenindef
4587         obj.lenindef = self.lenindef
4588         obj.ber_encoded = self.ber_encoded
4589         obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4590         return obj
4591
4592     def __eq__(self, their):
4593         if not isinstance(their, self.__class__):
4594             return False
4595         return (
4596             self.specs == their.specs and
4597             self.tag == their.tag and
4598             self._expl == their._expl and
4599             self._value == their._value
4600         )
4601
4602     def __call__(
4603             self,
4604             value=None,
4605             impl=None,
4606             expl=None,
4607             default=None,
4608             optional=None,
4609     ):
4610         return self.__class__(
4611             value=value,
4612             schema=self.specs,
4613             impl=self.tag if impl is None else impl,
4614             expl=self._expl if expl is None else expl,
4615             default=self.default if default is None else default,
4616             optional=self.optional if optional is None else optional,
4617         )
4618
4619     def __contains__(self, key):
4620         return key in self._value
4621
4622     def __setitem__(self, key, value):
4623         spec = self.specs.get(key)
4624         if spec is None:
4625             raise ObjUnknown(key)
4626         if value is None:
4627             self._value.pop(key, None)
4628             return
4629         if not isinstance(value, spec.__class__):
4630             raise InvalidValueType((spec.__class__,))
4631         value = spec(value=value)
4632         if spec.default is not None and value == spec.default:
4633             self._value.pop(key, None)
4634             return
4635         self._value[key] = value
4636
4637     def __getitem__(self, key):
4638         value = self._value.get(key)
4639         if value is not None:
4640             return value
4641         spec = self.specs.get(key)
4642         if spec is None:
4643             raise ObjUnknown(key)
4644         if spec.default is not None:
4645             return spec.default
4646         return None
4647
4648     def _encoded_values(self):
4649         raws = []
4650         for name, spec in iteritems(self.specs):
4651             value = self._value.get(name)
4652             if value is None:
4653                 if spec.optional:
4654                     continue
4655                 raise ObjNotReady(name)
4656             raws.append(value.encode())
4657         return raws
4658
4659     def _encode(self):
4660         v = b"".join(self._encoded_values())
4661         return b"".join((self.tag, len_encode(len(v)), v))
4662
4663     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4664         try:
4665             t, tlen, lv = tag_strip(tlv)
4666         except DecodeError as err:
4667             raise err.__class__(
4668                 msg=err.msg,
4669                 klass=self.__class__,
4670                 decode_path=decode_path,
4671                 offset=offset,
4672             )
4673         if t != self.tag:
4674             raise TagMismatch(
4675                 klass=self.__class__,
4676                 decode_path=decode_path,
4677                 offset=offset,
4678             )
4679         if tag_only:  # pragma: no cover
4680             return
4681         lenindef = False
4682         ctx_bered = ctx.get("bered", False)
4683         try:
4684             l, llen, v = len_decode(lv)
4685         except LenIndefForm as err:
4686             if not ctx_bered:
4687                 raise err.__class__(
4688                     msg=err.msg,
4689                     klass=self.__class__,
4690                     decode_path=decode_path,
4691                     offset=offset,
4692                 )
4693             l, llen, v = 0, 1, lv[1:]
4694             lenindef = True
4695         except DecodeError as err:
4696             raise err.__class__(
4697                 msg=err.msg,
4698                 klass=self.__class__,
4699                 decode_path=decode_path,
4700                 offset=offset,
4701             )
4702         if l > len(v):
4703             raise NotEnoughData(
4704                 "encoded length is longer than data",
4705                 klass=self.__class__,
4706                 decode_path=decode_path,
4707                 offset=offset,
4708             )
4709         if not lenindef:
4710             v, tail = v[:l], v[l:]
4711         vlen = 0
4712         sub_offset = offset + tlen + llen
4713         values = {}
4714         ber_encoded = False
4715         ctx_allow_default_values = ctx.get("allow_default_values", False)
4716         for name, spec in iteritems(self.specs):
4717             if spec.optional and (
4718                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4719                     len(v) == 0
4720             ):
4721                 continue
4722             sub_decode_path = decode_path + (name,)
4723             try:
4724                 value, v_tail = spec.decode(
4725                     v,
4726                     sub_offset,
4727                     leavemm=True,
4728                     decode_path=sub_decode_path,
4729                     ctx=ctx,
4730                     _ctx_immutable=False,
4731                 )
4732             except TagMismatch:
4733                 if spec.optional:
4734                     continue
4735                 raise
4736
4737             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4738             if defined is not None:
4739                 defined_by, defined_spec = defined
4740                 if issubclass(value.__class__, SequenceOf):
4741                     for i, _value in enumerate(value):
4742                         sub_sub_decode_path = sub_decode_path + (
4743                             str(i),
4744                             DecodePathDefBy(defined_by),
4745                         )
4746                         defined_value, defined_tail = defined_spec.decode(
4747                             memoryview(bytes(_value)),
4748                             sub_offset + (
4749                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4750                                 if value.expled else (value.tlen + value.llen)
4751                             ),
4752                             leavemm=True,
4753                             decode_path=sub_sub_decode_path,
4754                             ctx=ctx,
4755                             _ctx_immutable=False,
4756                         )
4757                         if len(defined_tail) > 0:
4758                             raise DecodeError(
4759                                 "remaining data",
4760                                 klass=self.__class__,
4761                                 decode_path=sub_sub_decode_path,
4762                                 offset=offset,
4763                             )
4764                         _value.defined = (defined_by, defined_value)
4765                 else:
4766                     defined_value, defined_tail = defined_spec.decode(
4767                         memoryview(bytes(value)),
4768                         sub_offset + (
4769                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4770                             if value.expled else (value.tlen + value.llen)
4771                         ),
4772                         leavemm=True,
4773                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4774                         ctx=ctx,
4775                         _ctx_immutable=False,
4776                     )
4777                     if len(defined_tail) > 0:
4778                         raise DecodeError(
4779                             "remaining data",
4780                             klass=self.__class__,
4781                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4782                             offset=offset,
4783                         )
4784                     value.defined = (defined_by, defined_value)
4785
4786             value_len = value.fulllen
4787             vlen += value_len
4788             sub_offset += value_len
4789             v = v_tail
4790             if spec.default is not None and value == spec.default:
4791                 if ctx_bered or ctx_allow_default_values:
4792                     ber_encoded = True
4793                 else:
4794                     raise DecodeError(
4795                         "DEFAULT value met",
4796                         klass=self.__class__,
4797                         decode_path=sub_decode_path,
4798                         offset=sub_offset,
4799                     )
4800             values[name] = value
4801
4802             spec_defines = getattr(spec, "defines", ())
4803             if len(spec_defines) == 0:
4804                 defines_by_path = ctx.get("defines_by_path", ())
4805                 if len(defines_by_path) > 0:
4806                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4807             if spec_defines is not None and len(spec_defines) > 0:
4808                 for rel_path, schema in spec_defines:
4809                     defined = schema.get(value, None)
4810                     if defined is not None:
4811                         ctx.setdefault("_defines", []).append((
4812                             abs_decode_path(sub_decode_path[:-1], rel_path),
4813                             (value, defined),
4814                         ))
4815         if lenindef:
4816             if v[:EOC_LEN].tobytes() != EOC:
4817                 raise DecodeError(
4818                     "no EOC",
4819                     klass=self.__class__,
4820                     decode_path=decode_path,
4821                     offset=offset,
4822                 )
4823             tail = v[EOC_LEN:]
4824             vlen += EOC_LEN
4825         elif len(v) > 0:
4826             raise DecodeError(
4827                 "remaining data",
4828                 klass=self.__class__,
4829                 decode_path=decode_path,
4830                 offset=offset,
4831             )
4832         obj = self.__class__(
4833             schema=self.specs,
4834             impl=self.tag,
4835             expl=self._expl,
4836             default=self.default,
4837             optional=self.optional,
4838             _decoded=(offset, llen, vlen),
4839         )
4840         obj._value = values
4841         obj.lenindef = lenindef
4842         obj.ber_encoded = ber_encoded
4843         return obj, tail
4844
4845     def __repr__(self):
4846         value = pp_console_row(next(self.pps()))
4847         cols = []
4848         for name in self.specs:
4849             _value = self._value.get(name)
4850             if _value is None:
4851                 continue
4852             cols.append("%s: %s" % (name, repr(_value)))
4853         return "%s[%s]" % (value, "; ".join(cols))
4854
4855     def pps(self, decode_path=()):
4856         yield _pp(
4857             obj=self,
4858             asn1_type_name=self.asn1_type_name,
4859             obj_name=self.__class__.__name__,
4860             decode_path=decode_path,
4861             optional=self.optional,
4862             default=self == self.default,
4863             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4864             expl=None if self._expl is None else tag_decode(self._expl),
4865             offset=self.offset,
4866             tlen=self.tlen,
4867             llen=self.llen,
4868             vlen=self.vlen,
4869             expl_offset=self.expl_offset if self.expled else None,
4870             expl_tlen=self.expl_tlen if self.expled else None,
4871             expl_llen=self.expl_llen if self.expled else None,
4872             expl_vlen=self.expl_vlen if self.expled else None,
4873             expl_lenindef=self.expl_lenindef,
4874             lenindef=self.lenindef,
4875             ber_encoded=self.ber_encoded,
4876             bered=self.bered,
4877         )
4878         for name in self.specs:
4879             value = self._value.get(name)
4880             if value is None:
4881                 continue
4882             yield value.pps(decode_path=decode_path + (name,))
4883         for pp in self.pps_lenindef(decode_path):
4884             yield pp
4885
4886
4887 class Set(Sequence):
4888     """``SET`` structure type
4889
4890     Its usage is identical to :py:class:`pyderasn.Sequence`.
4891
4892     .. _allow_unordered_set_ctx:
4893
4894     DER prohibits unordered values encoding and will raise an error
4895     during decode. If If :ref:`bered <bered_ctx>` context option is set,
4896     then no error will occure. Also you can disable strict values
4897     ordering check by setting ``"allow_unordered_set": True``
4898     :ref:`context <ctx>` option.
4899     """
4900     __slots__ = ()
4901     tag_default = tag_encode(form=TagFormConstructed, num=17)
4902     asn1_type_name = "SET"
4903
4904     def _encode(self):
4905         raws = self._encoded_values()
4906         raws.sort()
4907         v = b"".join(raws)
4908         return b"".join((self.tag, len_encode(len(v)), v))
4909
4910     def _specs_items(self):
4911         return iteritems(self.specs)
4912
4913     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4914         try:
4915             t, tlen, lv = tag_strip(tlv)
4916         except DecodeError as err:
4917             raise err.__class__(
4918                 msg=err.msg,
4919                 klass=self.__class__,
4920                 decode_path=decode_path,
4921                 offset=offset,
4922             )
4923         if t != self.tag:
4924             raise TagMismatch(
4925                 klass=self.__class__,
4926                 decode_path=decode_path,
4927                 offset=offset,
4928             )
4929         if tag_only:
4930             return
4931         lenindef = False
4932         ctx_bered = ctx.get("bered", False)
4933         try:
4934             l, llen, v = len_decode(lv)
4935         except LenIndefForm as err:
4936             if not ctx_bered:
4937                 raise err.__class__(
4938                     msg=err.msg,
4939                     klass=self.__class__,
4940                     decode_path=decode_path,
4941                     offset=offset,
4942                 )
4943             l, llen, v = 0, 1, lv[1:]
4944             lenindef = True
4945         except DecodeError as err:
4946             raise err.__class__(
4947                 msg=err.msg,
4948                 klass=self.__class__,
4949                 decode_path=decode_path,
4950                 offset=offset,
4951             )
4952         if l > len(v):
4953             raise NotEnoughData(
4954                 "encoded length is longer than data",
4955                 klass=self.__class__,
4956                 offset=offset,
4957             )
4958         if not lenindef:
4959             v, tail = v[:l], v[l:]
4960         vlen = 0
4961         sub_offset = offset + tlen + llen
4962         values = {}
4963         ber_encoded = False
4964         ctx_allow_default_values = ctx.get("allow_default_values", False)
4965         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4966         value_prev = memoryview(v[:0])
4967
4968         while len(v) > 0:
4969             if lenindef and v[:EOC_LEN].tobytes() == EOC:
4970                 break
4971             for name, spec in self._specs_items():
4972                 sub_decode_path = decode_path + (name,)
4973                 try:
4974                     spec.decode(
4975                         v,
4976                         sub_offset,
4977                         leavemm=True,
4978                         decode_path=sub_decode_path,
4979                         ctx=ctx,
4980                         tag_only=True,
4981                         _ctx_immutable=False,
4982                     )
4983                 except TagMismatch:
4984                     continue
4985                 break
4986             else:
4987                 raise TagMismatch(
4988                     klass=self.__class__,
4989                     decode_path=decode_path,
4990                     offset=offset,
4991                 )
4992             value, v_tail = spec.decode(
4993                 v,
4994                 sub_offset,
4995                 leavemm=True,
4996                 decode_path=sub_decode_path,
4997                 ctx=ctx,
4998                 _ctx_immutable=False,
4999             )
5000             value_len = value.fulllen
5001             if value_prev.tobytes() > v[:value_len].tobytes():
5002                 if ctx_bered or ctx_allow_unordered_set:
5003                     ber_encoded = True
5004                 else:
5005                     raise DecodeError(
5006                         "unordered " + self.asn1_type_name,
5007                         klass=self.__class__,
5008                         decode_path=sub_decode_path,
5009                         offset=sub_offset,
5010                     )
5011             if spec.default is None or value != spec.default:
5012                 pass
5013             elif ctx_bered or ctx_allow_default_values:
5014                 ber_encoded = True
5015             else:
5016                 raise DecodeError(
5017                     "DEFAULT value met",
5018                     klass=self.__class__,
5019                     decode_path=sub_decode_path,
5020                     offset=sub_offset,
5021                 )
5022             values[name] = value
5023             value_prev = v[:value_len]
5024             sub_offset += value_len
5025             vlen += value_len
5026             v = v_tail
5027         obj = self.__class__(
5028             schema=self.specs,
5029             impl=self.tag,
5030             expl=self._expl,
5031             default=self.default,
5032             optional=self.optional,
5033             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5034         )
5035         if lenindef:
5036             if v[:EOC_LEN].tobytes() != EOC:
5037                 raise DecodeError(
5038                     "no EOC",
5039                     klass=self.__class__,
5040                     decode_path=decode_path,
5041                     offset=offset,
5042                 )
5043             tail = v[EOC_LEN:]
5044             obj.lenindef = True
5045         obj._value = values
5046         if not obj.ready:
5047             raise DecodeError(
5048                 "not all values are ready",
5049                 klass=self.__class__,
5050                 decode_path=decode_path,
5051                 offset=offset,
5052             )
5053         obj.ber_encoded = ber_encoded
5054         return obj, tail
5055
5056
5057 class SequenceOf(Obj):
5058     """``SEQUENCE OF`` sequence type
5059
5060     For that kind of type you must specify the object it will carry on
5061     (bounds are for example here, not required)::
5062
5063         class Ints(SequenceOf):
5064             schema = Integer()
5065             bounds = (0, 2)
5066
5067     >>> ints = Ints()
5068     >>> ints.append(Integer(123))
5069     >>> ints.append(Integer(234))
5070     >>> ints
5071     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5072     >>> [int(i) for i in ints]
5073     [123, 234]
5074     >>> ints.append(Integer(345))
5075     Traceback (most recent call last):
5076     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5077     >>> ints[1]
5078     INTEGER 234
5079     >>> ints[1] = Integer(345)
5080     >>> ints
5081     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5082
5083     Also you can initialize sequence with preinitialized values:
5084
5085     >>> ints = Ints([Integer(123), Integer(234)])
5086     """
5087     __slots__ = ("spec", "_bound_min", "_bound_max")
5088     tag_default = tag_encode(form=TagFormConstructed, num=16)
5089     asn1_type_name = "SEQUENCE OF"
5090
5091     def __init__(
5092             self,
5093             value=None,
5094             schema=None,
5095             bounds=None,
5096             impl=None,
5097             expl=None,
5098             default=None,
5099             optional=False,
5100             _decoded=(0, 0, 0),
5101     ):
5102         super(SequenceOf, self).__init__(
5103             impl,
5104             expl,
5105             default,
5106             optional,
5107             _decoded,
5108         )
5109         if schema is None:
5110             schema = getattr(self, "schema", None)
5111         if schema is None:
5112             raise ValueError("schema must be specified")
5113         self.spec = schema
5114         self._bound_min, self._bound_max = getattr(
5115             self,
5116             "bounds",
5117             (0, float("+inf")),
5118         ) if bounds is None else bounds
5119         self._value = []
5120         if value is not None:
5121             self._value = self._value_sanitize(value)
5122         if default is not None:
5123             default_value = self._value_sanitize(default)
5124             default_obj = self.__class__(
5125                 schema=schema,
5126                 impl=self.tag,
5127                 expl=self._expl,
5128             )
5129             default_obj._value = default_value
5130             self.default = default_obj
5131             if value is None:
5132                 self._value = default_obj.copy()._value
5133
5134     def _value_sanitize(self, value):
5135         if issubclass(value.__class__, SequenceOf):
5136             value = value._value
5137         elif hasattr(value, "__iter__"):
5138             value = list(value)
5139         else:
5140             raise InvalidValueType((self.__class__, iter))
5141         if not self._bound_min <= len(value) <= self._bound_max:
5142             raise BoundsError(self._bound_min, len(value), self._bound_max)
5143         for v in value:
5144             if not isinstance(v, self.spec.__class__):
5145                 raise InvalidValueType((self.spec.__class__,))
5146         return value
5147
5148     @property
5149     def ready(self):
5150         return all(v.ready for v in self._value)
5151
5152     @property
5153     def bered(self):
5154         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5155             return True
5156         return any(v.bered for v in self._value)
5157
5158     def copy(self):
5159         obj = self.__class__(schema=self.spec)
5160         obj._bound_min = self._bound_min
5161         obj._bound_max = self._bound_max
5162         obj.tag = self.tag
5163         obj._expl = self._expl
5164         obj.default = self.default
5165         obj.optional = self.optional
5166         obj.offset = self.offset
5167         obj.llen = self.llen
5168         obj.vlen = self.vlen
5169         obj.expl_lenindef = self.expl_lenindef
5170         obj.lenindef = self.lenindef
5171         obj.ber_encoded = self.ber_encoded
5172         obj._value = [v.copy() for v in self._value]
5173         return obj
5174
5175     def __eq__(self, their):
5176         if isinstance(their, self.__class__):
5177             return (
5178                 self.spec == their.spec and
5179                 self.tag == their.tag and
5180                 self._expl == their._expl and
5181                 self._value == their._value
5182             )
5183         if hasattr(their, "__iter__"):
5184             return self._value == list(their)
5185         return False
5186
5187     def __call__(
5188             self,
5189             value=None,
5190             bounds=None,
5191             impl=None,
5192             expl=None,
5193             default=None,
5194             optional=None,
5195     ):
5196         return self.__class__(
5197             value=value,
5198             schema=self.spec,
5199             bounds=(
5200                 (self._bound_min, self._bound_max)
5201                 if bounds is None else bounds
5202             ),
5203             impl=self.tag if impl is None else impl,
5204             expl=self._expl if expl is None else expl,
5205             default=self.default if default is None else default,
5206             optional=self.optional if optional is None else optional,
5207         )
5208
5209     def __contains__(self, key):
5210         return key in self._value
5211
5212     def append(self, value):
5213         if not isinstance(value, self.spec.__class__):
5214             raise InvalidValueType((self.spec.__class__,))
5215         if len(self._value) + 1 > self._bound_max:
5216             raise BoundsError(
5217                 self._bound_min,
5218                 len(self._value) + 1,
5219                 self._bound_max,
5220             )
5221         self._value.append(value)
5222
5223     def __iter__(self):
5224         self._assert_ready()
5225         return iter(self._value)
5226
5227     def __len__(self):
5228         self._assert_ready()
5229         return len(self._value)
5230
5231     def __setitem__(self, key, value):
5232         if not isinstance(value, self.spec.__class__):
5233             raise InvalidValueType((self.spec.__class__,))
5234         self._value[key] = self.spec(value=value)
5235
5236     def __getitem__(self, key):
5237         return self._value[key]
5238
5239     def _encoded_values(self):
5240         return [v.encode() for v in self._value]
5241
5242     def _encode(self):
5243         v = b"".join(self._encoded_values())
5244         return b"".join((self.tag, len_encode(len(v)), v))
5245
5246     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5247         try:
5248             t, tlen, lv = tag_strip(tlv)
5249         except DecodeError as err:
5250             raise err.__class__(
5251                 msg=err.msg,
5252                 klass=self.__class__,
5253                 decode_path=decode_path,
5254                 offset=offset,
5255             )
5256         if t != self.tag:
5257             raise TagMismatch(
5258                 klass=self.__class__,
5259                 decode_path=decode_path,
5260                 offset=offset,
5261             )
5262         if tag_only:
5263             return
5264         lenindef = False
5265         ctx_bered = ctx.get("bered", False)
5266         try:
5267             l, llen, v = len_decode(lv)
5268         except LenIndefForm as err:
5269             if not ctx_bered:
5270                 raise err.__class__(
5271                     msg=err.msg,
5272                     klass=self.__class__,
5273                     decode_path=decode_path,
5274                     offset=offset,
5275                 )
5276             l, llen, v = 0, 1, lv[1:]
5277             lenindef = True
5278         except DecodeError as err:
5279             raise err.__class__(
5280                 msg=err.msg,
5281                 klass=self.__class__,
5282                 decode_path=decode_path,
5283                 offset=offset,
5284             )
5285         if l > len(v):
5286             raise NotEnoughData(
5287                 "encoded length is longer than data",
5288                 klass=self.__class__,
5289                 decode_path=decode_path,
5290                 offset=offset,
5291             )
5292         if not lenindef:
5293             v, tail = v[:l], v[l:]
5294         vlen = 0
5295         sub_offset = offset + tlen + llen
5296         _value = []
5297         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5298         value_prev = memoryview(v[:0])
5299         ber_encoded = False
5300         spec = self.spec
5301         while len(v) > 0:
5302             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5303                 break
5304             sub_decode_path = decode_path + (str(len(_value)),)
5305             value, v_tail = spec.decode(
5306                 v,
5307                 sub_offset,
5308                 leavemm=True,
5309                 decode_path=sub_decode_path,
5310                 ctx=ctx,
5311                 _ctx_immutable=False,
5312             )
5313             value_len = value.fulllen
5314             if ordering_check:
5315                 if value_prev.tobytes() > v[:value_len].tobytes():
5316                     if ctx_bered or ctx_allow_unordered_set:
5317                         ber_encoded = True
5318                     else:
5319                         raise DecodeError(
5320                             "unordered " + self.asn1_type_name,
5321                             klass=self.__class__,
5322                             decode_path=sub_decode_path,
5323                             offset=sub_offset,
5324                         )
5325                 value_prev = v[:value_len]
5326             _value.append(value)
5327             sub_offset += value_len
5328             vlen += value_len
5329             v = v_tail
5330         try:
5331             obj = self.__class__(
5332                 value=_value,
5333                 schema=spec,
5334                 bounds=(self._bound_min, self._bound_max),
5335                 impl=self.tag,
5336                 expl=self._expl,
5337                 default=self.default,
5338                 optional=self.optional,
5339                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5340             )
5341         except BoundsError as err:
5342             raise DecodeError(
5343                 msg=str(err),
5344                 klass=self.__class__,
5345                 decode_path=decode_path,
5346                 offset=offset,
5347             )
5348         if lenindef:
5349             if v[:EOC_LEN].tobytes() != EOC:
5350                 raise DecodeError(
5351                     "no EOC",
5352                     klass=self.__class__,
5353                     decode_path=decode_path,
5354                     offset=offset,
5355                 )
5356             obj.lenindef = True
5357             tail = v[EOC_LEN:]
5358         obj.ber_encoded = ber_encoded
5359         return obj, tail
5360
5361     def __repr__(self):
5362         return "%s[%s]" % (
5363             pp_console_row(next(self.pps())),
5364             ", ".join(repr(v) for v in self._value),
5365         )
5366
5367     def pps(self, decode_path=()):
5368         yield _pp(
5369             obj=self,
5370             asn1_type_name=self.asn1_type_name,
5371             obj_name=self.__class__.__name__,
5372             decode_path=decode_path,
5373             optional=self.optional,
5374             default=self == self.default,
5375             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5376             expl=None if self._expl is None else tag_decode(self._expl),
5377             offset=self.offset,
5378             tlen=self.tlen,
5379             llen=self.llen,
5380             vlen=self.vlen,
5381             expl_offset=self.expl_offset if self.expled else None,
5382             expl_tlen=self.expl_tlen if self.expled else None,
5383             expl_llen=self.expl_llen if self.expled else None,
5384             expl_vlen=self.expl_vlen if self.expled else None,
5385             expl_lenindef=self.expl_lenindef,
5386             lenindef=self.lenindef,
5387             ber_encoded=self.ber_encoded,
5388             bered=self.bered,
5389         )
5390         for i, value in enumerate(self._value):
5391             yield value.pps(decode_path=decode_path + (str(i),))
5392         for pp in self.pps_lenindef(decode_path):
5393             yield pp
5394
5395
5396 class SetOf(SequenceOf):
5397     """``SET OF`` sequence type
5398
5399     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5400     """
5401     __slots__ = ()
5402     tag_default = tag_encode(form=TagFormConstructed, num=17)
5403     asn1_type_name = "SET OF"
5404
5405     def _encode(self):
5406         raws = self._encoded_values()
5407         raws.sort()
5408         v = b"".join(raws)
5409         return b"".join((self.tag, len_encode(len(v)), v))
5410
5411     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5412         return super(SetOf, self)._decode(
5413             tlv,
5414             offset,
5415             decode_path,
5416             ctx,
5417             tag_only,
5418             ordering_check=True,
5419         )
5420
5421
5422 def obj_by_path(pypath):  # pragma: no cover
5423     """Import object specified as string Python path
5424
5425     Modules must be separated from classes/functions with ``:``.
5426
5427     >>> obj_by_path("foo.bar:Baz")
5428     <class 'foo.bar.Baz'>
5429     >>> obj_by_path("foo.bar:Baz.boo")
5430     <classmethod 'foo.bar.Baz.boo'>
5431     """
5432     mod, objs = pypath.rsplit(":", 1)
5433     from importlib import import_module
5434     obj = import_module(mod)
5435     for obj_name in objs.split("."):
5436         obj = getattr(obj, obj_name)
5437     return obj
5438
5439
5440 def generic_decoder():  # pragma: no cover
5441     # All of this below is a big hack with self references
5442     choice = PrimitiveTypes()
5443     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5444     choice.specs["SetOf"] = SetOf(schema=choice)
5445     for i in six_xrange(31):
5446         choice.specs["SequenceOf%d" % i] = SequenceOf(
5447             schema=choice,
5448             expl=tag_ctxc(i),
5449         )
5450     choice.specs["Any"] = Any()
5451
5452     # Class name equals to type name, to omit it from output
5453     class SEQUENCEOF(SequenceOf):
5454         __slots__ = ()
5455         schema = choice
5456
5457     def pprint_any(
5458             obj,
5459             oids=None,
5460             with_colours=False,
5461             with_decode_path=False,
5462             decode_path_only=(),
5463     ):
5464         def _pprint_pps(pps):
5465             for pp in pps:
5466                 if hasattr(pp, "_fields"):
5467                     if (
5468                         decode_path_only != () and
5469                         pp.decode_path[:len(decode_path_only)] != decode_path_only
5470                     ):
5471                         continue
5472                     if pp.asn1_type_name == Choice.asn1_type_name:
5473                         continue
5474                     pp_kwargs = pp._asdict()
5475                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5476                     pp = _pp(**pp_kwargs)
5477                     yield pp_console_row(
5478                         pp,
5479                         oids=oids,
5480                         with_offsets=True,
5481                         with_blob=False,
5482                         with_colours=with_colours,
5483                         with_decode_path=with_decode_path,
5484                         decode_path_len_decrease=len(decode_path_only),
5485                     )
5486                     for row in pp_console_blob(
5487                         pp,
5488                         decode_path_len_decrease=len(decode_path_only),
5489                     ):
5490                         yield row
5491                 else:
5492                     for row in _pprint_pps(pp):
5493                         yield row
5494         return "\n".join(_pprint_pps(obj.pps()))
5495     return SEQUENCEOF(), pprint_any
5496
5497
5498 def main():  # pragma: no cover
5499     import argparse
5500     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5501     parser.add_argument(
5502         "--skip",
5503         type=int,
5504         default=0,
5505         help="Skip that number of bytes from the beginning",
5506     )
5507     parser.add_argument(
5508         "--oids",
5509         help="Python path to dictionary with OIDs",
5510     )
5511     parser.add_argument(
5512         "--schema",
5513         help="Python path to schema definition to use",
5514     )
5515     parser.add_argument(
5516         "--defines-by-path",
5517         help="Python path to decoder's defines_by_path",
5518     )
5519     parser.add_argument(
5520         "--nobered",
5521         action="store_true",
5522         help="Disallow BER encoding",
5523     )
5524     parser.add_argument(
5525         "--print-decode-path",
5526         action="store_true",
5527         help="Print decode paths",
5528     )
5529     parser.add_argument(
5530         "--decode-path-only",
5531         help="Print only specified decode path",
5532     )
5533     parser.add_argument(
5534         "--allow-expl-oob",
5535         action="store_true",
5536         help="Allow explicit tag out-of-bound",
5537     )
5538     parser.add_argument(
5539         "DERFile",
5540         type=argparse.FileType("rb"),
5541         help="Path to DER file you want to decode",
5542     )
5543     args = parser.parse_args()
5544     args.DERFile.seek(args.skip)
5545     der = memoryview(args.DERFile.read())
5546     args.DERFile.close()
5547     oids = obj_by_path(args.oids) if args.oids else {}
5548     if args.schema:
5549         schema = obj_by_path(args.schema)
5550         from functools import partial
5551         pprinter = partial(pprint, big_blobs=True)
5552     else:
5553         schema, pprinter = generic_decoder()
5554     ctx = {
5555         "bered": not args.nobered,
5556         "allow_expl_oob": args.allow_expl_oob,
5557     }
5558     if args.defines_by_path is not None:
5559         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5560     obj, tail = schema().decode(der, ctx=ctx)
5561     print(pprinter(
5562         obj,
5563         oids=oids,
5564         with_colours=True if environ.get("NO_COLOR") is None else False,
5565         with_decode_path=args.print_decode_path,
5566         decode_path_only=(
5567             () if args.decode_path_only is None else
5568             tuple(args.decode_path_only.split(":"))
5569         ),
5570     ))
5571     if tail != b"":
5572         print("\nTrailing data: %s" % hexenc(tail))
5573
5574
5575 if __name__ == "__main__":
5576     main()