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