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