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