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