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