]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Explicit mention that *Time do not support BER
[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     .. warning::
3666
3667        No BER encodings are supported. Only DER.
3668     """
3669     __slots__ = ()
3670     tag_default = tag_encode(23)
3671     encoding = "ascii"
3672     asn1_type_name = "UTCTime"
3673
3674     def __init__(
3675             self,
3676             value=None,
3677             impl=None,
3678             expl=None,
3679             default=None,
3680             optional=False,
3681             _decoded=(0, 0, 0),
3682             bounds=None,  # dummy argument, workability for OctetString.decode
3683     ):
3684         """
3685         :param value: set the value. Either datetime type, or
3686                       :py:class:`pyderasn.UTCTime` object
3687         :param bytes impl: override default tag with ``IMPLICIT`` one
3688         :param bytes expl: override default tag with ``EXPLICIT`` one
3689         :param default: set default value. Type same as in ``value``
3690         :param bool optional: is object ``OPTIONAL`` in sequence
3691         """
3692         super(UTCTime, self).__init__(
3693             impl=impl,
3694             expl=expl,
3695             default=default,
3696             optional=optional,
3697             _decoded=_decoded,
3698         )
3699         self._value = value
3700         if value is not None:
3701             self._value = self._value_sanitize(value)
3702         if default is not None:
3703             default = self._value_sanitize(default)
3704             self.default = self.__class__(
3705                 value=default,
3706                 impl=self.tag,
3707                 expl=self._expl,
3708             )
3709             if self._value is None:
3710                 self._value = default
3711
3712     def _strptime(self, value):
3713         # datetime.strptime's format: %y%m%d%H%M%SZ
3714         if len(value) != LEN_YYMMDDHHMMSSZ:
3715             raise ValueError("invalid UTCTime length")
3716         if value[-1] != "Z":
3717             raise ValueError("non UTC timezone")
3718         return datetime(
3719             2000 + int(value[:2]),  # %y
3720             int(value[2:4]),  # %m
3721             int(value[4:6]),  # %d
3722             int(value[6:8]),  # %H
3723             int(value[8:10]),  # %M
3724             int(value[10:12]),  # %S
3725         )
3726
3727     def _value_sanitize(self, value):
3728         if isinstance(value, binary_type):
3729             try:
3730                 value_decoded = value.decode("ascii")
3731             except (UnicodeEncodeError, UnicodeDecodeError) as err:
3732                 raise DecodeError("invalid UTCTime encoding")
3733             try:
3734                 self._strptime(value_decoded)
3735             except (TypeError, ValueError) as err:
3736                 raise DecodeError("invalid UTCTime format: %r" % err)
3737             return value
3738         if isinstance(value, self.__class__):
3739             return value._value
3740         if isinstance(value, datetime):
3741             return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
3742         raise InvalidValueType((self.__class__, datetime))
3743
3744     def __eq__(self, their):
3745         if isinstance(their, binary_type):
3746             return self._value == their
3747         if isinstance(their, datetime):
3748             return self.todatetime() == their
3749         if not isinstance(their, self.__class__):
3750             return False
3751         return (
3752             self._value == their._value and
3753             self.tag == their.tag and
3754             self._expl == their._expl
3755         )
3756
3757     def todatetime(self):
3758         """Convert to datetime
3759
3760         :returns: datetime
3761
3762         Pay attention that UTCTime can not hold full year, so all years
3763         having < 50 years are treated as 20xx, 19xx otherwise, according
3764         to X.509 recomendation.
3765         """
3766         value = self._strptime(self._value.decode("ascii"))
3767         year = value.year % 100
3768         return datetime(
3769             year=(2000 + year) if year < 50 else (1900 + year),
3770             month=value.month,
3771             day=value.day,
3772             hour=value.hour,
3773             minute=value.minute,
3774             second=value.second,
3775         )
3776
3777     def __repr__(self):
3778         return pp_console_row(next(self.pps()))
3779
3780     def pps(self, decode_path=()):
3781         yield _pp(
3782             obj=self,
3783             asn1_type_name=self.asn1_type_name,
3784             obj_name=self.__class__.__name__,
3785             decode_path=decode_path,
3786             value=self.todatetime().isoformat() if self.ready else None,
3787             optional=self.optional,
3788             default=self == self.default,
3789             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3790             expl=None if self._expl is None else tag_decode(self._expl),
3791             offset=self.offset,
3792             tlen=self.tlen,
3793             llen=self.llen,
3794             vlen=self.vlen,
3795             expl_offset=self.expl_offset if self.expled else None,
3796             expl_tlen=self.expl_tlen if self.expled else None,
3797             expl_llen=self.expl_llen if self.expled else None,
3798             expl_vlen=self.expl_vlen if self.expled else None,
3799             expl_lenindef=self.expl_lenindef,
3800             ber_encoded=self.ber_encoded,
3801             bered=self.bered,
3802         )
3803         for pp in self.pps_lenindef(decode_path):
3804             yield pp
3805
3806
3807 class GeneralizedTime(UTCTime):
3808     """``GeneralizedTime`` datetime type
3809
3810     This type is similar to :py:class:`pyderasn.UTCTime`.
3811
3812     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3813     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3814     >>> str(t)
3815     '20170930220750.000123Z'
3816     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3817     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3818
3819     .. warning::
3820
3821        No BER encodings are supported. Only DER.
3822
3823     .. warning::
3824
3825        Only microsecond fractions are supported.
3826        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
3827        higher precision values.
3828     """
3829     __slots__ = ()
3830     tag_default = tag_encode(24)
3831     asn1_type_name = "GeneralizedTime"
3832
3833     def _strptime(self, value):
3834         l = len(value)
3835         if l == LEN_YYYYMMDDHHMMSSZ:
3836             # datetime.strptime's format: %y%m%d%H%M%SZ
3837             if value[-1] != "Z":
3838                 raise ValueError("non UTC timezone")
3839             return datetime(
3840                 int(value[:4]),  # %Y
3841                 int(value[4:6]),  # %m
3842                 int(value[6:8]),  # %d
3843                 int(value[8:10]),  # %H
3844                 int(value[10:12]),  # %M
3845                 int(value[12:14]),  # %S
3846             )
3847         if l >= LEN_YYYYMMDDHHMMSSDMZ:
3848             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
3849             if value[-1] != "Z":
3850                 raise ValueError("non UTC timezone")
3851             if value[14] != ".":
3852                 raise ValueError("no fractions separator")
3853             us = value[15:-1]
3854             if us[-1] == "0":
3855                 raise ValueError("trailing zero")
3856             us_len = len(us)
3857             if us_len > 6:
3858                 raise ValueError("only microsecond fractions are supported")
3859             us = int(us + ("0" * (6 - us_len)))
3860             decoded = datetime(
3861                 int(value[:4]),  # %Y
3862                 int(value[4:6]),  # %m
3863                 int(value[6:8]),  # %d
3864                 int(value[8:10]),  # %H
3865                 int(value[10:12]),  # %M
3866                 int(value[12:14]),  # %S
3867                 us,  # %f
3868             )
3869             return decoded
3870         raise ValueError("invalid GeneralizedTime length")
3871
3872     def _value_sanitize(self, value):
3873         if isinstance(value, binary_type):
3874             try:
3875                 value_decoded = value.decode("ascii")
3876             except (UnicodeEncodeError, UnicodeDecodeError) as err:
3877                 raise DecodeError("invalid GeneralizedTime encoding")
3878             try:
3879                 self._strptime(value_decoded)
3880             except (TypeError, ValueError) as err:
3881                 raise DecodeError(
3882                     "invalid GeneralizedTime format: %r" % err,
3883                     klass=self.__class__,
3884                 )
3885             return value
3886         if isinstance(value, self.__class__):
3887             return value._value
3888         if isinstance(value, datetime):
3889             encoded = value.strftime("%Y%m%d%H%M%S")
3890             if value.microsecond > 0:
3891                 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
3892             return (encoded + "Z").encode("ascii")
3893         raise InvalidValueType((self.__class__, datetime))
3894
3895     def todatetime(self):
3896         return self._strptime(self._value.decode("ascii"))
3897
3898
3899 class GraphicString(CommonString):
3900     __slots__ = ()
3901     tag_default = tag_encode(25)
3902     encoding = "iso-8859-1"
3903     asn1_type_name = "GraphicString"
3904
3905
3906 class VisibleString(CommonString):
3907     __slots__ = ()
3908     tag_default = tag_encode(26)
3909     encoding = "ascii"
3910     asn1_type_name = "VisibleString"
3911
3912
3913 class ISO646String(VisibleString):
3914     __slots__ = ()
3915     asn1_type_name = "ISO646String"
3916
3917
3918 class GeneralString(CommonString):
3919     __slots__ = ()
3920     tag_default = tag_encode(27)
3921     encoding = "iso-8859-1"
3922     asn1_type_name = "GeneralString"
3923
3924
3925 class UniversalString(CommonString):
3926     __slots__ = ()
3927     tag_default = tag_encode(28)
3928     encoding = "utf-32-be"
3929     asn1_type_name = "UniversalString"
3930
3931
3932 class BMPString(CommonString):
3933     __slots__ = ()
3934     tag_default = tag_encode(30)
3935     encoding = "utf-16-be"
3936     asn1_type_name = "BMPString"
3937
3938
3939 class Choice(Obj):
3940     """``CHOICE`` special type
3941
3942     ::
3943
3944         class GeneralName(Choice):
3945             schema = (
3946                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3947                 ("dNSName", IA5String(impl=tag_ctxp(2))),
3948             )
3949
3950     >>> gn = GeneralName()
3951     GeneralName CHOICE
3952     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3953     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3954     >>> gn["dNSName"] = IA5String("bar.baz")
3955     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3956     >>> gn["rfc822Name"]
3957     None
3958     >>> gn["dNSName"]
3959     [2] IA5String IA5 bar.baz
3960     >>> gn.choice
3961     'dNSName'
3962     >>> gn.value == gn["dNSName"]
3963     True
3964     >>> gn.specs
3965     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3966
3967     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3968     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3969     """
3970     __slots__ = ("specs",)
3971     tag_default = None
3972     asn1_type_name = "CHOICE"
3973
3974     def __init__(
3975             self,
3976             value=None,
3977             schema=None,
3978             impl=None,
3979             expl=None,
3980             default=None,
3981             optional=False,
3982             _decoded=(0, 0, 0),
3983     ):
3984         """
3985         :param value: set the value. Either ``(choice, value)`` tuple, or
3986                       :py:class:`pyderasn.Choice` object
3987         :param bytes impl: can not be set, do **not** use it
3988         :param bytes expl: override default tag with ``EXPLICIT`` one
3989         :param default: set default value. Type same as in ``value``
3990         :param bool optional: is object ``OPTIONAL`` in sequence
3991         """
3992         if impl is not None:
3993             raise ValueError("no implicit tag allowed for CHOICE")
3994         super(Choice, self).__init__(None, expl, default, optional, _decoded)
3995         if schema is None:
3996             schema = getattr(self, "schema", ())
3997         if len(schema) == 0:
3998             raise ValueError("schema must be specified")
3999         self.specs = (
4000             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4001         )
4002         self._value = None
4003         if value is not None:
4004             self._value = self._value_sanitize(value)
4005         if default is not None:
4006             default_value = self._value_sanitize(default)
4007             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4008             default_obj.specs = self.specs
4009             default_obj._value = default_value
4010             self.default = default_obj
4011             if value is None:
4012                 self._value = default_obj.copy()._value
4013
4014     def _value_sanitize(self, value):
4015         if isinstance(value, tuple) and len(value) == 2:
4016             choice, obj = value
4017             spec = self.specs.get(choice)
4018             if spec is None:
4019                 raise ObjUnknown(choice)
4020             if not isinstance(obj, spec.__class__):
4021                 raise InvalidValueType((spec,))
4022             return (choice, spec(obj))
4023         if isinstance(value, self.__class__):
4024             return value._value
4025         raise InvalidValueType((self.__class__, tuple))
4026
4027     @property
4028     def ready(self):
4029         return self._value is not None and self._value[1].ready
4030
4031     @property
4032     def bered(self):
4033         return self.expl_lenindef or (
4034             (self._value is not None) and
4035             self._value[1].bered
4036         )
4037
4038     def copy(self):
4039         obj = self.__class__(schema=self.specs)
4040         obj._expl = self._expl
4041         obj.default = self.default
4042         obj.optional = self.optional
4043         obj.offset = self.offset
4044         obj.llen = self.llen
4045         obj.vlen = self.vlen
4046         obj.expl_lenindef = self.expl_lenindef
4047         obj.lenindef = self.lenindef
4048         obj.ber_encoded = self.ber_encoded
4049         value = self._value
4050         if value is not None:
4051             obj._value = (value[0], value[1].copy())
4052         return obj
4053
4054     def __eq__(self, their):
4055         if isinstance(their, tuple) and len(their) == 2:
4056             return self._value == their
4057         if not isinstance(their, self.__class__):
4058             return False
4059         return (
4060             self.specs == their.specs and
4061             self._value == their._value
4062         )
4063
4064     def __call__(
4065             self,
4066             value=None,
4067             expl=None,
4068             default=None,
4069             optional=None,
4070     ):
4071         return self.__class__(
4072             value=value,
4073             schema=self.specs,
4074             expl=self._expl if expl is None else expl,
4075             default=self.default if default is None else default,
4076             optional=self.optional if optional is None else optional,
4077         )
4078
4079     @property
4080     def choice(self):
4081         self._assert_ready()
4082         return self._value[0]
4083
4084     @property
4085     def value(self):
4086         self._assert_ready()
4087         return self._value[1]
4088
4089     def __getitem__(self, key):
4090         if key not in self.specs:
4091             raise ObjUnknown(key)
4092         if self._value is None:
4093             return None
4094         choice, value = self._value
4095         if choice != key:
4096             return None
4097         return value
4098
4099     def __setitem__(self, key, value):
4100         spec = self.specs.get(key)
4101         if spec is None:
4102             raise ObjUnknown(key)
4103         if not isinstance(value, spec.__class__):
4104             raise InvalidValueType((spec.__class__,))
4105         self._value = (key, spec(value))
4106
4107     @property
4108     def tlen(self):
4109         return 0
4110
4111     @property
4112     def decoded(self):
4113         return self._value[1].decoded if self.ready else False
4114
4115     def _encode(self):
4116         self._assert_ready()
4117         return self._value[1].encode()
4118
4119     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4120         for choice, spec in iteritems(self.specs):
4121             sub_decode_path = decode_path + (choice,)
4122             try:
4123                 spec.decode(
4124                     tlv,
4125                     offset=offset,
4126                     leavemm=True,
4127                     decode_path=sub_decode_path,
4128                     ctx=ctx,
4129                     tag_only=True,
4130                     _ctx_immutable=False,
4131                 )
4132             except TagMismatch:
4133                 continue
4134             break
4135         else:
4136             raise TagMismatch(
4137                 klass=self.__class__,
4138                 decode_path=decode_path,
4139                 offset=offset,
4140             )
4141         if tag_only:  # pragma: no cover
4142             return
4143         value, tail = spec.decode(
4144             tlv,
4145             offset=offset,
4146             leavemm=True,
4147             decode_path=sub_decode_path,
4148             ctx=ctx,
4149             _ctx_immutable=False,
4150         )
4151         obj = self.__class__(
4152             schema=self.specs,
4153             expl=self._expl,
4154             default=self.default,
4155             optional=self.optional,
4156             _decoded=(offset, 0, value.fulllen),
4157         )
4158         obj._value = (choice, value)
4159         return obj, tail
4160
4161     def __repr__(self):
4162         value = pp_console_row(next(self.pps()))
4163         if self.ready:
4164             value = "%s[%r]" % (value, self.value)
4165         return value
4166
4167     def pps(self, decode_path=()):
4168         yield _pp(
4169             obj=self,
4170             asn1_type_name=self.asn1_type_name,
4171             obj_name=self.__class__.__name__,
4172             decode_path=decode_path,
4173             value=self.choice if self.ready else None,
4174             optional=self.optional,
4175             default=self == self.default,
4176             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4177             expl=None if self._expl is None else tag_decode(self._expl),
4178             offset=self.offset,
4179             tlen=self.tlen,
4180             llen=self.llen,
4181             vlen=self.vlen,
4182             expl_lenindef=self.expl_lenindef,
4183             bered=self.bered,
4184         )
4185         if self.ready:
4186             yield self.value.pps(decode_path=decode_path + (self.choice,))
4187         for pp in self.pps_lenindef(decode_path):
4188             yield pp
4189
4190
4191 class PrimitiveTypes(Choice):
4192     """Predefined ``CHOICE`` for all generic primitive types
4193
4194     It could be useful for general decoding of some unspecified values:
4195
4196     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4197     OCTET STRING 3 bytes 666f6f
4198     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4199     INTEGER 1193046
4200     """
4201     __slots__ = ()
4202     schema = tuple((klass.__name__, klass()) for klass in (
4203         Boolean,
4204         Integer,
4205         BitString,
4206         OctetString,
4207         Null,
4208         ObjectIdentifier,
4209         UTF8String,
4210         NumericString,
4211         PrintableString,
4212         TeletexString,
4213         VideotexString,
4214         IA5String,
4215         UTCTime,
4216         GeneralizedTime,
4217         GraphicString,
4218         VisibleString,
4219         ISO646String,
4220         GeneralString,
4221         UniversalString,
4222         BMPString,
4223     ))
4224
4225
4226 class Any(Obj):
4227     """``ANY`` special type
4228
4229     >>> Any(Integer(-123))
4230     ANY 020185
4231     >>> a = Any(OctetString(b"hello world").encode())
4232     ANY 040b68656c6c6f20776f726c64
4233     >>> hexenc(bytes(a))
4234     b'0x040x0bhello world'
4235     """
4236     __slots__ = ("defined",)
4237     tag_default = tag_encode(0)
4238     asn1_type_name = "ANY"
4239
4240     def __init__(
4241             self,
4242             value=None,
4243             expl=None,
4244             optional=False,
4245             _decoded=(0, 0, 0),
4246     ):
4247         """
4248         :param value: set the value. Either any kind of pyderasn's
4249                       **ready** object, or bytes. Pay attention that
4250                       **no** validation is performed is raw binary value
4251                       is valid TLV
4252         :param bytes expl: override default tag with ``EXPLICIT`` one
4253         :param bool optional: is object ``OPTIONAL`` in sequence
4254         """
4255         super(Any, self).__init__(None, expl, None, optional, _decoded)
4256         self._value = None if value is None else self._value_sanitize(value)
4257         self.defined = None
4258
4259     def _value_sanitize(self, value):
4260         if isinstance(value, binary_type):
4261             return value
4262         if isinstance(value, self.__class__):
4263             return value._value
4264         if isinstance(value, Obj):
4265             return value.encode()
4266         raise InvalidValueType((self.__class__, Obj, binary_type))
4267
4268     @property
4269     def ready(self):
4270         return self._value is not None
4271
4272     @property
4273     def bered(self):
4274         if self.expl_lenindef or self.lenindef:
4275             return True
4276         if self.defined is None:
4277             return False
4278         return self.defined[1].bered
4279
4280     def copy(self):
4281         obj = self.__class__()
4282         obj._value = self._value
4283         obj.tag = self.tag
4284         obj._expl = self._expl
4285         obj.optional = self.optional
4286         obj.offset = self.offset
4287         obj.llen = self.llen
4288         obj.vlen = self.vlen
4289         obj.expl_lenindef = self.expl_lenindef
4290         obj.lenindef = self.lenindef
4291         obj.ber_encoded = self.ber_encoded
4292         return obj
4293
4294     def __eq__(self, their):
4295         if isinstance(their, binary_type):
4296             return self._value == their
4297         if issubclass(their.__class__, Any):
4298             return self._value == their._value
4299         return False
4300
4301     def __call__(
4302             self,
4303             value=None,
4304             expl=None,
4305             optional=None,
4306     ):
4307         return self.__class__(
4308             value=value,
4309             expl=self._expl if expl is None else expl,
4310             optional=self.optional if optional is None else optional,
4311         )
4312
4313     def __bytes__(self):
4314         self._assert_ready()
4315         return self._value
4316
4317     @property
4318     def tlen(self):
4319         return 0
4320
4321     def _encode(self):
4322         self._assert_ready()
4323         return self._value
4324
4325     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4326         try:
4327             t, tlen, lv = tag_strip(tlv)
4328         except DecodeError as err:
4329             raise err.__class__(
4330                 msg=err.msg,
4331                 klass=self.__class__,
4332                 decode_path=decode_path,
4333                 offset=offset,
4334             )
4335         try:
4336             l, llen, v = len_decode(lv)
4337         except LenIndefForm as err:
4338             if not ctx.get("bered", False):
4339                 raise err.__class__(
4340                     msg=err.msg,
4341                     klass=self.__class__,
4342                     decode_path=decode_path,
4343                     offset=offset,
4344                 )
4345             llen, vlen, v = 1, 0, lv[1:]
4346             sub_offset = offset + tlen + llen
4347             chunk_i = 0
4348             while v[:EOC_LEN].tobytes() != EOC:
4349                 chunk, v = Any().decode(
4350                     v,
4351                     offset=sub_offset,
4352                     decode_path=decode_path + (str(chunk_i),),
4353                     leavemm=True,
4354                     ctx=ctx,
4355                     _ctx_immutable=False,
4356                 )
4357                 vlen += chunk.tlvlen
4358                 sub_offset += chunk.tlvlen
4359                 chunk_i += 1
4360             tlvlen = tlen + llen + vlen + EOC_LEN
4361             obj = self.__class__(
4362                 value=tlv[:tlvlen].tobytes(),
4363                 expl=self._expl,
4364                 optional=self.optional,
4365                 _decoded=(offset, 0, tlvlen),
4366             )
4367             obj.lenindef = True
4368             obj.tag = t
4369             return obj, v[EOC_LEN:]
4370         except DecodeError as err:
4371             raise err.__class__(
4372                 msg=err.msg,
4373                 klass=self.__class__,
4374                 decode_path=decode_path,
4375                 offset=offset,
4376             )
4377         if l > len(v):
4378             raise NotEnoughData(
4379                 "encoded length is longer than data",
4380                 klass=self.__class__,
4381                 decode_path=decode_path,
4382                 offset=offset,
4383             )
4384         tlvlen = tlen + llen + l
4385         v, tail = tlv[:tlvlen], v[l:]
4386         obj = self.__class__(
4387             value=v.tobytes(),
4388             expl=self._expl,
4389             optional=self.optional,
4390             _decoded=(offset, 0, tlvlen),
4391         )
4392         obj.tag = t
4393         return obj, tail
4394
4395     def __repr__(self):
4396         return pp_console_row(next(self.pps()))
4397
4398     def pps(self, decode_path=()):
4399         yield _pp(
4400             obj=self,
4401             asn1_type_name=self.asn1_type_name,
4402             obj_name=self.__class__.__name__,
4403             decode_path=decode_path,
4404             blob=self._value if self.ready else None,
4405             optional=self.optional,
4406             default=self == self.default,
4407             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4408             expl=None if self._expl is None else tag_decode(self._expl),
4409             offset=self.offset,
4410             tlen=self.tlen,
4411             llen=self.llen,
4412             vlen=self.vlen,
4413             expl_offset=self.expl_offset if self.expled else None,
4414             expl_tlen=self.expl_tlen if self.expled else None,
4415             expl_llen=self.expl_llen if self.expled else None,
4416             expl_vlen=self.expl_vlen if self.expled else None,
4417             expl_lenindef=self.expl_lenindef,
4418             lenindef=self.lenindef,
4419             bered=self.bered,
4420         )
4421         defined_by, defined = self.defined or (None, None)
4422         if defined_by is not None:
4423             yield defined.pps(
4424                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4425             )
4426         for pp in self.pps_lenindef(decode_path):
4427             yield pp
4428
4429
4430 ########################################################################
4431 # ASN.1 constructed types
4432 ########################################################################
4433
4434 def get_def_by_path(defines_by_path, sub_decode_path):
4435     """Get define by decode path
4436     """
4437     for path, define in defines_by_path:
4438         if len(path) != len(sub_decode_path):
4439             continue
4440         for p1, p2 in zip(path, sub_decode_path):
4441             if (p1 != any) and (p1 != p2):
4442                 break
4443         else:
4444             return define
4445
4446
4447 def abs_decode_path(decode_path, rel_path):
4448     """Create an absolute decode path from current and relative ones
4449
4450     :param decode_path: current decode path, starting point. Tuple of strings
4451     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4452                      If first tuple's element is "/", then treat it as
4453                      an absolute path, ignoring ``decode_path`` as
4454                      starting point. Also this tuple can contain ".."
4455                      elements, stripping the leading element from
4456                      ``decode_path``
4457
4458     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4459     ("foo", "bar", "baz", "whatever")
4460     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4461     ("foo", "whatever")
4462     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4463     ("baz", "whatever")
4464     """
4465     if rel_path[0] == "/":
4466         return rel_path[1:]
4467     if rel_path[0] == "..":
4468         return abs_decode_path(decode_path[:-1], rel_path[1:])
4469     return decode_path + rel_path
4470
4471
4472 class Sequence(Obj):
4473     """``SEQUENCE`` structure type
4474
4475     You have to make specification of sequence::
4476
4477         class Extension(Sequence):
4478             schema = (
4479                 ("extnID", ObjectIdentifier()),
4480                 ("critical", Boolean(default=False)),
4481                 ("extnValue", OctetString()),
4482             )
4483
4484     Then, you can work with it as with dictionary.
4485
4486     >>> ext = Extension()
4487     >>> Extension().specs
4488     OrderedDict([
4489         ('extnID', OBJECT IDENTIFIER),
4490         ('critical', BOOLEAN False OPTIONAL DEFAULT),
4491         ('extnValue', OCTET STRING),
4492     ])
4493     >>> ext["extnID"] = "1.2.3"
4494     Traceback (most recent call last):
4495     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4496     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4497
4498     You can determine if sequence is ready to be encoded:
4499
4500     >>> ext.ready
4501     False
4502     >>> ext.encode()
4503     Traceback (most recent call last):
4504     pyderasn.ObjNotReady: object is not ready: extnValue
4505     >>> ext["extnValue"] = OctetString(b"foobar")
4506     >>> ext.ready
4507     True
4508
4509     Value you want to assign, must have the same **type** as in
4510     corresponding specification, but it can have different tags,
4511     optional/default attributes -- they will be taken from specification
4512     automatically::
4513
4514         class TBSCertificate(Sequence):
4515             schema = (
4516                 ("version", Version(expl=tag_ctxc(0), default="v1")),
4517             [...]
4518
4519     >>> tbs = TBSCertificate()
4520     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4521
4522     Assign ``None`` to remove value from sequence.
4523
4524     You can set values in Sequence during its initialization:
4525
4526     >>> AlgorithmIdentifier((
4527         ("algorithm", ObjectIdentifier("1.2.3")),
4528         ("parameters", Any(Null()))
4529     ))
4530     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4531
4532     You can determine if value exists/set in the sequence and take its value:
4533
4534     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4535     (True, True, False)
4536     >>> ext["extnID"]
4537     OBJECT IDENTIFIER 1.2.3
4538
4539     But pay attention that if value has default, then it won't be (not
4540     in) in the sequence (because ``DEFAULT`` must not be encoded in
4541     DER), but you can read its value:
4542
4543     >>> "critical" in ext, ext["critical"]
4544     (False, BOOLEAN False)
4545     >>> ext["critical"] = Boolean(True)
4546     >>> "critical" in ext, ext["critical"]
4547     (True, BOOLEAN True)
4548
4549     All defaulted values are always optional.
4550
4551     .. _allow_default_values_ctx:
4552
4553     DER prohibits default value encoding and will raise an error if
4554     default value is unexpectedly met during decode.
4555     If :ref:`bered <bered_ctx>` context option is set, then no error
4556     will be raised, but ``bered`` attribute set. You can disable strict
4557     defaulted values existence validation by setting
4558     ``"allow_default_values": True`` :ref:`context <ctx>` option.
4559
4560     Two sequences are equal if they have equal specification (schema),
4561     implicit/explicit tagging and the same values.
4562     """
4563     __slots__ = ("specs",)
4564     tag_default = tag_encode(form=TagFormConstructed, num=16)
4565     asn1_type_name = "SEQUENCE"
4566
4567     def __init__(
4568             self,
4569             value=None,
4570             schema=None,
4571             impl=None,
4572             expl=None,
4573             default=None,
4574             optional=False,
4575             _decoded=(0, 0, 0),
4576     ):
4577         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4578         if schema is None:
4579             schema = getattr(self, "schema", ())
4580         self.specs = (
4581             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4582         )
4583         self._value = {}
4584         if value is not None:
4585             if issubclass(value.__class__, Sequence):
4586                 self._value = value._value
4587             elif hasattr(value, "__iter__"):
4588                 for seq_key, seq_value in value:
4589                     self[seq_key] = seq_value
4590             else:
4591                 raise InvalidValueType((Sequence,))
4592         if default is not None:
4593             if not issubclass(default.__class__, Sequence):
4594                 raise InvalidValueType((Sequence,))
4595             default_value = default._value
4596             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4597             default_obj.specs = self.specs
4598             default_obj._value = default_value
4599             self.default = default_obj
4600             if value is None:
4601                 self._value = default_obj.copy()._value
4602
4603     @property
4604     def ready(self):
4605         for name, spec in iteritems(self.specs):
4606             value = self._value.get(name)
4607             if value is None:
4608                 if spec.optional:
4609                     continue
4610                 return False
4611             else:
4612                 if not value.ready:
4613                     return False
4614         return True
4615
4616     @property
4617     def bered(self):
4618         if self.expl_lenindef or self.lenindef or self.ber_encoded:
4619             return True
4620         return any(value.bered for value in itervalues(self._value))
4621
4622     def copy(self):
4623         obj = self.__class__(schema=self.specs)
4624         obj.tag = self.tag
4625         obj._expl = self._expl
4626         obj.default = self.default
4627         obj.optional = self.optional
4628         obj.offset = self.offset
4629         obj.llen = self.llen
4630         obj.vlen = self.vlen
4631         obj.expl_lenindef = self.expl_lenindef
4632         obj.lenindef = self.lenindef
4633         obj.ber_encoded = self.ber_encoded
4634         obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4635         return obj
4636
4637     def __eq__(self, their):
4638         if not isinstance(their, self.__class__):
4639             return False
4640         return (
4641             self.specs == their.specs and
4642             self.tag == their.tag and
4643             self._expl == their._expl and
4644             self._value == their._value
4645         )
4646
4647     def __call__(
4648             self,
4649             value=None,
4650             impl=None,
4651             expl=None,
4652             default=None,
4653             optional=None,
4654     ):
4655         return self.__class__(
4656             value=value,
4657             schema=self.specs,
4658             impl=self.tag if impl is None else impl,
4659             expl=self._expl if expl is None else expl,
4660             default=self.default if default is None else default,
4661             optional=self.optional if optional is None else optional,
4662         )
4663
4664     def __contains__(self, key):
4665         return key in self._value
4666
4667     def __setitem__(self, key, value):
4668         spec = self.specs.get(key)
4669         if spec is None:
4670             raise ObjUnknown(key)
4671         if value is None:
4672             self._value.pop(key, None)
4673             return
4674         if not isinstance(value, spec.__class__):
4675             raise InvalidValueType((spec.__class__,))
4676         value = spec(value=value)
4677         if spec.default is not None and value == spec.default:
4678             self._value.pop(key, None)
4679             return
4680         self._value[key] = value
4681
4682     def __getitem__(self, key):
4683         value = self._value.get(key)
4684         if value is not None:
4685             return value
4686         spec = self.specs.get(key)
4687         if spec is None:
4688             raise ObjUnknown(key)
4689         if spec.default is not None:
4690             return spec.default
4691         return None
4692
4693     def _encoded_values(self):
4694         raws = []
4695         for name, spec in iteritems(self.specs):
4696             value = self._value.get(name)
4697             if value is None:
4698                 if spec.optional:
4699                     continue
4700                 raise ObjNotReady(name)
4701             raws.append(value.encode())
4702         return raws
4703
4704     def _encode(self):
4705         v = b"".join(self._encoded_values())
4706         return b"".join((self.tag, len_encode(len(v)), v))
4707
4708     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4709         try:
4710             t, tlen, lv = tag_strip(tlv)
4711         except DecodeError as err:
4712             raise err.__class__(
4713                 msg=err.msg,
4714                 klass=self.__class__,
4715                 decode_path=decode_path,
4716                 offset=offset,
4717             )
4718         if t != self.tag:
4719             raise TagMismatch(
4720                 klass=self.__class__,
4721                 decode_path=decode_path,
4722                 offset=offset,
4723             )
4724         if tag_only:  # pragma: no cover
4725             return
4726         lenindef = False
4727         ctx_bered = ctx.get("bered", False)
4728         try:
4729             l, llen, v = len_decode(lv)
4730         except LenIndefForm as err:
4731             if not ctx_bered:
4732                 raise err.__class__(
4733                     msg=err.msg,
4734                     klass=self.__class__,
4735                     decode_path=decode_path,
4736                     offset=offset,
4737                 )
4738             l, llen, v = 0, 1, lv[1:]
4739             lenindef = True
4740         except DecodeError as err:
4741             raise err.__class__(
4742                 msg=err.msg,
4743                 klass=self.__class__,
4744                 decode_path=decode_path,
4745                 offset=offset,
4746             )
4747         if l > len(v):
4748             raise NotEnoughData(
4749                 "encoded length is longer than data",
4750                 klass=self.__class__,
4751                 decode_path=decode_path,
4752                 offset=offset,
4753             )
4754         if not lenindef:
4755             v, tail = v[:l], v[l:]
4756         vlen = 0
4757         sub_offset = offset + tlen + llen
4758         values = {}
4759         ber_encoded = False
4760         ctx_allow_default_values = ctx.get("allow_default_values", False)
4761         for name, spec in iteritems(self.specs):
4762             if spec.optional and (
4763                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4764                     len(v) == 0
4765             ):
4766                 continue
4767             sub_decode_path = decode_path + (name,)
4768             try:
4769                 value, v_tail = spec.decode(
4770                     v,
4771                     sub_offset,
4772                     leavemm=True,
4773                     decode_path=sub_decode_path,
4774                     ctx=ctx,
4775                     _ctx_immutable=False,
4776                 )
4777             except TagMismatch:
4778                 if spec.optional:
4779                     continue
4780                 raise
4781
4782             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4783             if defined is not None:
4784                 defined_by, defined_spec = defined
4785                 if issubclass(value.__class__, SequenceOf):
4786                     for i, _value in enumerate(value):
4787                         sub_sub_decode_path = sub_decode_path + (
4788                             str(i),
4789                             DecodePathDefBy(defined_by),
4790                         )
4791                         defined_value, defined_tail = defined_spec.decode(
4792                             memoryview(bytes(_value)),
4793                             sub_offset + (
4794                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4795                                 if value.expled else (value.tlen + value.llen)
4796                             ),
4797                             leavemm=True,
4798                             decode_path=sub_sub_decode_path,
4799                             ctx=ctx,
4800                             _ctx_immutable=False,
4801                         )
4802                         if len(defined_tail) > 0:
4803                             raise DecodeError(
4804                                 "remaining data",
4805                                 klass=self.__class__,
4806                                 decode_path=sub_sub_decode_path,
4807                                 offset=offset,
4808                             )
4809                         _value.defined = (defined_by, defined_value)
4810                 else:
4811                     defined_value, defined_tail = defined_spec.decode(
4812                         memoryview(bytes(value)),
4813                         sub_offset + (
4814                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4815                             if value.expled else (value.tlen + value.llen)
4816                         ),
4817                         leavemm=True,
4818                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4819                         ctx=ctx,
4820                         _ctx_immutable=False,
4821                     )
4822                     if len(defined_tail) > 0:
4823                         raise DecodeError(
4824                             "remaining data",
4825                             klass=self.__class__,
4826                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4827                             offset=offset,
4828                         )
4829                     value.defined = (defined_by, defined_value)
4830
4831             value_len = value.fulllen
4832             vlen += value_len
4833             sub_offset += value_len
4834             v = v_tail
4835             if spec.default is not None and value == spec.default:
4836                 if ctx_bered or ctx_allow_default_values:
4837                     ber_encoded = True
4838                 else:
4839                     raise DecodeError(
4840                         "DEFAULT value met",
4841                         klass=self.__class__,
4842                         decode_path=sub_decode_path,
4843                         offset=sub_offset,
4844                     )
4845             values[name] = value
4846
4847             spec_defines = getattr(spec, "defines", ())
4848             if len(spec_defines) == 0:
4849                 defines_by_path = ctx.get("defines_by_path", ())
4850                 if len(defines_by_path) > 0:
4851                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4852             if spec_defines is not None and len(spec_defines) > 0:
4853                 for rel_path, schema in spec_defines:
4854                     defined = schema.get(value, None)
4855                     if defined is not None:
4856                         ctx.setdefault("_defines", []).append((
4857                             abs_decode_path(sub_decode_path[:-1], rel_path),
4858                             (value, defined),
4859                         ))
4860         if lenindef:
4861             if v[:EOC_LEN].tobytes() != EOC:
4862                 raise DecodeError(
4863                     "no EOC",
4864                     klass=self.__class__,
4865                     decode_path=decode_path,
4866                     offset=offset,
4867                 )
4868             tail = v[EOC_LEN:]
4869             vlen += EOC_LEN
4870         elif len(v) > 0:
4871             raise DecodeError(
4872                 "remaining data",
4873                 klass=self.__class__,
4874                 decode_path=decode_path,
4875                 offset=offset,
4876             )
4877         obj = self.__class__(
4878             schema=self.specs,
4879             impl=self.tag,
4880             expl=self._expl,
4881             default=self.default,
4882             optional=self.optional,
4883             _decoded=(offset, llen, vlen),
4884         )
4885         obj._value = values
4886         obj.lenindef = lenindef
4887         obj.ber_encoded = ber_encoded
4888         return obj, tail
4889
4890     def __repr__(self):
4891         value = pp_console_row(next(self.pps()))
4892         cols = []
4893         for name in self.specs:
4894             _value = self._value.get(name)
4895             if _value is None:
4896                 continue
4897             cols.append("%s: %s" % (name, repr(_value)))
4898         return "%s[%s]" % (value, "; ".join(cols))
4899
4900     def pps(self, decode_path=()):
4901         yield _pp(
4902             obj=self,
4903             asn1_type_name=self.asn1_type_name,
4904             obj_name=self.__class__.__name__,
4905             decode_path=decode_path,
4906             optional=self.optional,
4907             default=self == self.default,
4908             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4909             expl=None if self._expl is None else tag_decode(self._expl),
4910             offset=self.offset,
4911             tlen=self.tlen,
4912             llen=self.llen,
4913             vlen=self.vlen,
4914             expl_offset=self.expl_offset if self.expled else None,
4915             expl_tlen=self.expl_tlen if self.expled else None,
4916             expl_llen=self.expl_llen if self.expled else None,
4917             expl_vlen=self.expl_vlen if self.expled else None,
4918             expl_lenindef=self.expl_lenindef,
4919             lenindef=self.lenindef,
4920             ber_encoded=self.ber_encoded,
4921             bered=self.bered,
4922         )
4923         for name in self.specs:
4924             value = self._value.get(name)
4925             if value is None:
4926                 continue
4927             yield value.pps(decode_path=decode_path + (name,))
4928         for pp in self.pps_lenindef(decode_path):
4929             yield pp
4930
4931
4932 class Set(Sequence):
4933     """``SET`` structure type
4934
4935     Its usage is identical to :py:class:`pyderasn.Sequence`.
4936
4937     .. _allow_unordered_set_ctx:
4938
4939     DER prohibits unordered values encoding and will raise an error
4940     during decode. If If :ref:`bered <bered_ctx>` context option is set,
4941     then no error will occure. Also you can disable strict values
4942     ordering check by setting ``"allow_unordered_set": True``
4943     :ref:`context <ctx>` option.
4944     """
4945     __slots__ = ()
4946     tag_default = tag_encode(form=TagFormConstructed, num=17)
4947     asn1_type_name = "SET"
4948
4949     def _encode(self):
4950         raws = self._encoded_values()
4951         raws.sort()
4952         v = b"".join(raws)
4953         return b"".join((self.tag, len_encode(len(v)), v))
4954
4955     def _specs_items(self):
4956         return iteritems(self.specs)
4957
4958     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4959         try:
4960             t, tlen, lv = tag_strip(tlv)
4961         except DecodeError as err:
4962             raise err.__class__(
4963                 msg=err.msg,
4964                 klass=self.__class__,
4965                 decode_path=decode_path,
4966                 offset=offset,
4967             )
4968         if t != self.tag:
4969             raise TagMismatch(
4970                 klass=self.__class__,
4971                 decode_path=decode_path,
4972                 offset=offset,
4973             )
4974         if tag_only:
4975             return
4976         lenindef = False
4977         ctx_bered = ctx.get("bered", False)
4978         try:
4979             l, llen, v = len_decode(lv)
4980         except LenIndefForm as err:
4981             if not ctx_bered:
4982                 raise err.__class__(
4983                     msg=err.msg,
4984                     klass=self.__class__,
4985                     decode_path=decode_path,
4986                     offset=offset,
4987                 )
4988             l, llen, v = 0, 1, lv[1:]
4989             lenindef = True
4990         except DecodeError as err:
4991             raise err.__class__(
4992                 msg=err.msg,
4993                 klass=self.__class__,
4994                 decode_path=decode_path,
4995                 offset=offset,
4996             )
4997         if l > len(v):
4998             raise NotEnoughData(
4999                 "encoded length is longer than data",
5000                 klass=self.__class__,
5001                 offset=offset,
5002             )
5003         if not lenindef:
5004             v, tail = v[:l], v[l:]
5005         vlen = 0
5006         sub_offset = offset + tlen + llen
5007         values = {}
5008         ber_encoded = False
5009         ctx_allow_default_values = ctx.get("allow_default_values", False)
5010         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5011         value_prev = memoryview(v[:0])
5012
5013         while len(v) > 0:
5014             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5015                 break
5016             for name, spec in self._specs_items():
5017                 sub_decode_path = decode_path + (name,)
5018                 try:
5019                     spec.decode(
5020                         v,
5021                         sub_offset,
5022                         leavemm=True,
5023                         decode_path=sub_decode_path,
5024                         ctx=ctx,
5025                         tag_only=True,
5026                         _ctx_immutable=False,
5027                     )
5028                 except TagMismatch:
5029                     continue
5030                 break
5031             else:
5032                 raise TagMismatch(
5033                     klass=self.__class__,
5034                     decode_path=decode_path,
5035                     offset=offset,
5036                 )
5037             value, v_tail = spec.decode(
5038                 v,
5039                 sub_offset,
5040                 leavemm=True,
5041                 decode_path=sub_decode_path,
5042                 ctx=ctx,
5043                 _ctx_immutable=False,
5044             )
5045             value_len = value.fulllen
5046             if value_prev.tobytes() > v[:value_len].tobytes():
5047                 if ctx_bered or ctx_allow_unordered_set:
5048                     ber_encoded = True
5049                 else:
5050                     raise DecodeError(
5051                         "unordered " + self.asn1_type_name,
5052                         klass=self.__class__,
5053                         decode_path=sub_decode_path,
5054                         offset=sub_offset,
5055                     )
5056             if spec.default is None or value != spec.default:
5057                 pass
5058             elif ctx_bered or ctx_allow_default_values:
5059                 ber_encoded = True
5060             else:
5061                 raise DecodeError(
5062                     "DEFAULT value met",
5063                     klass=self.__class__,
5064                     decode_path=sub_decode_path,
5065                     offset=sub_offset,
5066                 )
5067             values[name] = value
5068             value_prev = v[:value_len]
5069             sub_offset += value_len
5070             vlen += value_len
5071             v = v_tail
5072         obj = self.__class__(
5073             schema=self.specs,
5074             impl=self.tag,
5075             expl=self._expl,
5076             default=self.default,
5077             optional=self.optional,
5078             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5079         )
5080         if lenindef:
5081             if v[:EOC_LEN].tobytes() != EOC:
5082                 raise DecodeError(
5083                     "no EOC",
5084                     klass=self.__class__,
5085                     decode_path=decode_path,
5086                     offset=offset,
5087                 )
5088             tail = v[EOC_LEN:]
5089             obj.lenindef = True
5090         obj._value = values
5091         if not obj.ready:
5092             raise DecodeError(
5093                 "not all values are ready",
5094                 klass=self.__class__,
5095                 decode_path=decode_path,
5096                 offset=offset,
5097             )
5098         obj.ber_encoded = ber_encoded
5099         return obj, tail
5100
5101
5102 class SequenceOf(Obj):
5103     """``SEQUENCE OF`` sequence type
5104
5105     For that kind of type you must specify the object it will carry on
5106     (bounds are for example here, not required)::
5107
5108         class Ints(SequenceOf):
5109             schema = Integer()
5110             bounds = (0, 2)
5111
5112     >>> ints = Ints()
5113     >>> ints.append(Integer(123))
5114     >>> ints.append(Integer(234))
5115     >>> ints
5116     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5117     >>> [int(i) for i in ints]
5118     [123, 234]
5119     >>> ints.append(Integer(345))
5120     Traceback (most recent call last):
5121     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5122     >>> ints[1]
5123     INTEGER 234
5124     >>> ints[1] = Integer(345)
5125     >>> ints
5126     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5127
5128     Also you can initialize sequence with preinitialized values:
5129
5130     >>> ints = Ints([Integer(123), Integer(234)])
5131     """
5132     __slots__ = ("spec", "_bound_min", "_bound_max")
5133     tag_default = tag_encode(form=TagFormConstructed, num=16)
5134     asn1_type_name = "SEQUENCE OF"
5135
5136     def __init__(
5137             self,
5138             value=None,
5139             schema=None,
5140             bounds=None,
5141             impl=None,
5142             expl=None,
5143             default=None,
5144             optional=False,
5145             _decoded=(0, 0, 0),
5146     ):
5147         super(SequenceOf, self).__init__(
5148             impl,
5149             expl,
5150             default,
5151             optional,
5152             _decoded,
5153         )
5154         if schema is None:
5155             schema = getattr(self, "schema", None)
5156         if schema is None:
5157             raise ValueError("schema must be specified")
5158         self.spec = schema
5159         self._bound_min, self._bound_max = getattr(
5160             self,
5161             "bounds",
5162             (0, float("+inf")),
5163         ) if bounds is None else bounds
5164         self._value = []
5165         if value is not None:
5166             self._value = self._value_sanitize(value)
5167         if default is not None:
5168             default_value = self._value_sanitize(default)
5169             default_obj = self.__class__(
5170                 schema=schema,
5171                 impl=self.tag,
5172                 expl=self._expl,
5173             )
5174             default_obj._value = default_value
5175             self.default = default_obj
5176             if value is None:
5177                 self._value = default_obj.copy()._value
5178
5179     def _value_sanitize(self, value):
5180         if issubclass(value.__class__, SequenceOf):
5181             value = value._value
5182         elif hasattr(value, "__iter__"):
5183             value = list(value)
5184         else:
5185             raise InvalidValueType((self.__class__, iter))
5186         if not self._bound_min <= len(value) <= self._bound_max:
5187             raise BoundsError(self._bound_min, len(value), self._bound_max)
5188         for v in value:
5189             if not isinstance(v, self.spec.__class__):
5190                 raise InvalidValueType((self.spec.__class__,))
5191         return value
5192
5193     @property
5194     def ready(self):
5195         return all(v.ready for v in self._value)
5196
5197     @property
5198     def bered(self):
5199         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5200             return True
5201         return any(v.bered for v in self._value)
5202
5203     def copy(self):
5204         obj = self.__class__(schema=self.spec)
5205         obj._bound_min = self._bound_min
5206         obj._bound_max = self._bound_max
5207         obj.tag = self.tag
5208         obj._expl = self._expl
5209         obj.default = self.default
5210         obj.optional = self.optional
5211         obj.offset = self.offset
5212         obj.llen = self.llen
5213         obj.vlen = self.vlen
5214         obj.expl_lenindef = self.expl_lenindef
5215         obj.lenindef = self.lenindef
5216         obj.ber_encoded = self.ber_encoded
5217         obj._value = [v.copy() for v in self._value]
5218         return obj
5219
5220     def __eq__(self, their):
5221         if isinstance(their, self.__class__):
5222             return (
5223                 self.spec == their.spec and
5224                 self.tag == their.tag and
5225                 self._expl == their._expl and
5226                 self._value == their._value
5227             )
5228         if hasattr(their, "__iter__"):
5229             return self._value == list(their)
5230         return False
5231
5232     def __call__(
5233             self,
5234             value=None,
5235             bounds=None,
5236             impl=None,
5237             expl=None,
5238             default=None,
5239             optional=None,
5240     ):
5241         return self.__class__(
5242             value=value,
5243             schema=self.spec,
5244             bounds=(
5245                 (self._bound_min, self._bound_max)
5246                 if bounds is None else bounds
5247             ),
5248             impl=self.tag if impl is None else impl,
5249             expl=self._expl if expl is None else expl,
5250             default=self.default if default is None else default,
5251             optional=self.optional if optional is None else optional,
5252         )
5253
5254     def __contains__(self, key):
5255         return key in self._value
5256
5257     def append(self, value):
5258         if not isinstance(value, self.spec.__class__):
5259             raise InvalidValueType((self.spec.__class__,))
5260         if len(self._value) + 1 > self._bound_max:
5261             raise BoundsError(
5262                 self._bound_min,
5263                 len(self._value) + 1,
5264                 self._bound_max,
5265             )
5266         self._value.append(value)
5267
5268     def __iter__(self):
5269         self._assert_ready()
5270         return iter(self._value)
5271
5272     def __len__(self):
5273         self._assert_ready()
5274         return len(self._value)
5275
5276     def __setitem__(self, key, value):
5277         if not isinstance(value, self.spec.__class__):
5278             raise InvalidValueType((self.spec.__class__,))
5279         self._value[key] = self.spec(value=value)
5280
5281     def __getitem__(self, key):
5282         return self._value[key]
5283
5284     def _encoded_values(self):
5285         return [v.encode() for v in self._value]
5286
5287     def _encode(self):
5288         v = b"".join(self._encoded_values())
5289         return b"".join((self.tag, len_encode(len(v)), v))
5290
5291     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5292         try:
5293             t, tlen, lv = tag_strip(tlv)
5294         except DecodeError as err:
5295             raise err.__class__(
5296                 msg=err.msg,
5297                 klass=self.__class__,
5298                 decode_path=decode_path,
5299                 offset=offset,
5300             )
5301         if t != self.tag:
5302             raise TagMismatch(
5303                 klass=self.__class__,
5304                 decode_path=decode_path,
5305                 offset=offset,
5306             )
5307         if tag_only:
5308             return
5309         lenindef = False
5310         ctx_bered = ctx.get("bered", False)
5311         try:
5312             l, llen, v = len_decode(lv)
5313         except LenIndefForm as err:
5314             if not ctx_bered:
5315                 raise err.__class__(
5316                     msg=err.msg,
5317                     klass=self.__class__,
5318                     decode_path=decode_path,
5319                     offset=offset,
5320                 )
5321             l, llen, v = 0, 1, lv[1:]
5322             lenindef = True
5323         except DecodeError as err:
5324             raise err.__class__(
5325                 msg=err.msg,
5326                 klass=self.__class__,
5327                 decode_path=decode_path,
5328                 offset=offset,
5329             )
5330         if l > len(v):
5331             raise NotEnoughData(
5332                 "encoded length is longer than data",
5333                 klass=self.__class__,
5334                 decode_path=decode_path,
5335                 offset=offset,
5336             )
5337         if not lenindef:
5338             v, tail = v[:l], v[l:]
5339         vlen = 0
5340         sub_offset = offset + tlen + llen
5341         _value = []
5342         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5343         value_prev = memoryview(v[:0])
5344         ber_encoded = False
5345         spec = self.spec
5346         while len(v) > 0:
5347             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5348                 break
5349             sub_decode_path = decode_path + (str(len(_value)),)
5350             value, v_tail = spec.decode(
5351                 v,
5352                 sub_offset,
5353                 leavemm=True,
5354                 decode_path=sub_decode_path,
5355                 ctx=ctx,
5356                 _ctx_immutable=False,
5357             )
5358             value_len = value.fulllen
5359             if ordering_check:
5360                 if value_prev.tobytes() > v[:value_len].tobytes():
5361                     if ctx_bered or ctx_allow_unordered_set:
5362                         ber_encoded = True
5363                     else:
5364                         raise DecodeError(
5365                             "unordered " + self.asn1_type_name,
5366                             klass=self.__class__,
5367                             decode_path=sub_decode_path,
5368                             offset=sub_offset,
5369                         )
5370                 value_prev = v[:value_len]
5371             _value.append(value)
5372             sub_offset += value_len
5373             vlen += value_len
5374             v = v_tail
5375         try:
5376             obj = self.__class__(
5377                 value=_value,
5378                 schema=spec,
5379                 bounds=(self._bound_min, self._bound_max),
5380                 impl=self.tag,
5381                 expl=self._expl,
5382                 default=self.default,
5383                 optional=self.optional,
5384                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5385             )
5386         except BoundsError as err:
5387             raise DecodeError(
5388                 msg=str(err),
5389                 klass=self.__class__,
5390                 decode_path=decode_path,
5391                 offset=offset,
5392             )
5393         if lenindef:
5394             if v[:EOC_LEN].tobytes() != EOC:
5395                 raise DecodeError(
5396                     "no EOC",
5397                     klass=self.__class__,
5398                     decode_path=decode_path,
5399                     offset=offset,
5400                 )
5401             obj.lenindef = True
5402             tail = v[EOC_LEN:]
5403         obj.ber_encoded = ber_encoded
5404         return obj, tail
5405
5406     def __repr__(self):
5407         return "%s[%s]" % (
5408             pp_console_row(next(self.pps())),
5409             ", ".join(repr(v) for v in self._value),
5410         )
5411
5412     def pps(self, decode_path=()):
5413         yield _pp(
5414             obj=self,
5415             asn1_type_name=self.asn1_type_name,
5416             obj_name=self.__class__.__name__,
5417             decode_path=decode_path,
5418             optional=self.optional,
5419             default=self == self.default,
5420             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5421             expl=None if self._expl is None else tag_decode(self._expl),
5422             offset=self.offset,
5423             tlen=self.tlen,
5424             llen=self.llen,
5425             vlen=self.vlen,
5426             expl_offset=self.expl_offset if self.expled else None,
5427             expl_tlen=self.expl_tlen if self.expled else None,
5428             expl_llen=self.expl_llen if self.expled else None,
5429             expl_vlen=self.expl_vlen if self.expled else None,
5430             expl_lenindef=self.expl_lenindef,
5431             lenindef=self.lenindef,
5432             ber_encoded=self.ber_encoded,
5433             bered=self.bered,
5434         )
5435         for i, value in enumerate(self._value):
5436             yield value.pps(decode_path=decode_path + (str(i),))
5437         for pp in self.pps_lenindef(decode_path):
5438             yield pp
5439
5440
5441 class SetOf(SequenceOf):
5442     """``SET OF`` sequence type
5443
5444     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5445     """
5446     __slots__ = ()
5447     tag_default = tag_encode(form=TagFormConstructed, num=17)
5448     asn1_type_name = "SET OF"
5449
5450     def _encode(self):
5451         raws = self._encoded_values()
5452         raws.sort()
5453         v = b"".join(raws)
5454         return b"".join((self.tag, len_encode(len(v)), v))
5455
5456     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5457         return super(SetOf, self)._decode(
5458             tlv,
5459             offset,
5460             decode_path,
5461             ctx,
5462             tag_only,
5463             ordering_check=True,
5464         )
5465
5466
5467 def obj_by_path(pypath):  # pragma: no cover
5468     """Import object specified as string Python path
5469
5470     Modules must be separated from classes/functions with ``:``.
5471
5472     >>> obj_by_path("foo.bar:Baz")
5473     <class 'foo.bar.Baz'>
5474     >>> obj_by_path("foo.bar:Baz.boo")
5475     <classmethod 'foo.bar.Baz.boo'>
5476     """
5477     mod, objs = pypath.rsplit(":", 1)
5478     from importlib import import_module
5479     obj = import_module(mod)
5480     for obj_name in objs.split("."):
5481         obj = getattr(obj, obj_name)
5482     return obj
5483
5484
5485 def generic_decoder():  # pragma: no cover
5486     # All of this below is a big hack with self references
5487     choice = PrimitiveTypes()
5488     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5489     choice.specs["SetOf"] = SetOf(schema=choice)
5490     for i in six_xrange(31):
5491         choice.specs["SequenceOf%d" % i] = SequenceOf(
5492             schema=choice,
5493             expl=tag_ctxc(i),
5494         )
5495     choice.specs["Any"] = Any()
5496
5497     # Class name equals to type name, to omit it from output
5498     class SEQUENCEOF(SequenceOf):
5499         __slots__ = ()
5500         schema = choice
5501
5502     def pprint_any(
5503             obj,
5504             oids=None,
5505             with_colours=False,
5506             with_decode_path=False,
5507             decode_path_only=(),
5508     ):
5509         def _pprint_pps(pps):
5510             for pp in pps:
5511                 if hasattr(pp, "_fields"):
5512                     if (
5513                         decode_path_only != () and
5514                         pp.decode_path[:len(decode_path_only)] != decode_path_only
5515                     ):
5516                         continue
5517                     if pp.asn1_type_name == Choice.asn1_type_name:
5518                         continue
5519                     pp_kwargs = pp._asdict()
5520                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5521                     pp = _pp(**pp_kwargs)
5522                     yield pp_console_row(
5523                         pp,
5524                         oids=oids,
5525                         with_offsets=True,
5526                         with_blob=False,
5527                         with_colours=with_colours,
5528                         with_decode_path=with_decode_path,
5529                         decode_path_len_decrease=len(decode_path_only),
5530                     )
5531                     for row in pp_console_blob(
5532                         pp,
5533                         decode_path_len_decrease=len(decode_path_only),
5534                     ):
5535                         yield row
5536                 else:
5537                     for row in _pprint_pps(pp):
5538                         yield row
5539         return "\n".join(_pprint_pps(obj.pps()))
5540     return SEQUENCEOF(), pprint_any
5541
5542
5543 def main():  # pragma: no cover
5544     import argparse
5545     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5546     parser.add_argument(
5547         "--skip",
5548         type=int,
5549         default=0,
5550         help="Skip that number of bytes from the beginning",
5551     )
5552     parser.add_argument(
5553         "--oids",
5554         help="Python path to dictionary with OIDs",
5555     )
5556     parser.add_argument(
5557         "--schema",
5558         help="Python path to schema definition to use",
5559     )
5560     parser.add_argument(
5561         "--defines-by-path",
5562         help="Python path to decoder's defines_by_path",
5563     )
5564     parser.add_argument(
5565         "--nobered",
5566         action="store_true",
5567         help="Disallow BER encoding",
5568     )
5569     parser.add_argument(
5570         "--print-decode-path",
5571         action="store_true",
5572         help="Print decode paths",
5573     )
5574     parser.add_argument(
5575         "--decode-path-only",
5576         help="Print only specified decode path",
5577     )
5578     parser.add_argument(
5579         "--allow-expl-oob",
5580         action="store_true",
5581         help="Allow explicit tag out-of-bound",
5582     )
5583     parser.add_argument(
5584         "DERFile",
5585         type=argparse.FileType("rb"),
5586         help="Path to DER file you want to decode",
5587     )
5588     args = parser.parse_args()
5589     args.DERFile.seek(args.skip)
5590     der = memoryview(args.DERFile.read())
5591     args.DERFile.close()
5592     oids = obj_by_path(args.oids) if args.oids else {}
5593     if args.schema:
5594         schema = obj_by_path(args.schema)
5595         from functools import partial
5596         pprinter = partial(pprint, big_blobs=True)
5597     else:
5598         schema, pprinter = generic_decoder()
5599     ctx = {
5600         "bered": not args.nobered,
5601         "allow_expl_oob": args.allow_expl_oob,
5602     }
5603     if args.defines_by_path is not None:
5604         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5605     obj, tail = schema().decode(der, ctx=ctx)
5606     print(pprinter(
5607         obj,
5608         oids=oids,
5609         with_colours=True if environ.get("NO_COLOR") is None else False,
5610         with_decode_path=args.print_decode_path,
5611         decode_path_only=(
5612             () if args.decode_path_only is None else
5613             tuple(args.decode_path_only.split(":"))
5614         ),
5615     ))
5616     if tail != b"":
5617         print("\nTrailing data: %s" % hexenc(tail))
5618
5619
5620 if __name__ == "__main__":
5621     main()