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