]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Group ready-related methods together
[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     def _assert_ready(self):
962         if not self.ready:
963             raise ObjNotReady(self.__class__.__name__)
964
965     @property
966     def bered(self):
967         """Is either object or any elements inside is BER encoded?
968         """
969         return self.expl_lenindef or self.lenindef or self.ber_encoded
970
971     @property
972     def decoded(self):
973         """Is object decoded?
974         """
975         return (self.llen + self.vlen) > 0
976
977     def copy(self):  # pragma: no cover
978         """Make a copy of object, safe to be mutated
979         """
980         raise NotImplementedError()
981
982     @property
983     def tlen(self):
984         return len(self.tag)
985
986     @property
987     def tlvlen(self):
988         return self.tlen + self.llen + self.vlen
989
990     def __str__(self):  # pragma: no cover
991         return self.__bytes__() if PY2 else self.__unicode__()
992
993     def __ne__(self, their):
994         return not(self == their)
995
996     def __gt__(self, their):  # pragma: no cover
997         return not(self < their)
998
999     def __le__(self, their):  # pragma: no cover
1000         return (self == their) or (self < their)
1001
1002     def __ge__(self, their):  # pragma: no cover
1003         return (self == their) or (self > their)
1004
1005     def _encode(self):  # pragma: no cover
1006         raise NotImplementedError()
1007
1008     def _decode(self, tlv, offset, decode_path, ctx, tag_only):  # pragma: no cover
1009         raise NotImplementedError()
1010
1011     def encode(self):
1012         raw = self._encode()
1013         if self._expl is None:
1014             return raw
1015         return b"".join((self._expl, len_encode(len(raw)), raw))
1016
1017     def decode(
1018             self,
1019             data,
1020             offset=0,
1021             leavemm=False,
1022             decode_path=(),
1023             ctx=None,
1024             tag_only=False,
1025     ):
1026         """Decode the data
1027
1028         :param data: either binary or memoryview
1029         :param int offset: initial data's offset
1030         :param bool leavemm: do we need to leave memoryview of remaining
1031                     data as is, or convert it to bytes otherwise
1032         :param ctx: optional :ref:`context <ctx>` governing decoding process.
1033         :param tag_only: decode only the tag, without length and contents
1034                          (used only in Choice and Set structures, trying to
1035                          determine if tag satisfies the scheme)
1036         :returns: (Obj, remaining data)
1037         """
1038         if ctx is None:
1039             ctx = {}
1040         tlv = memoryview(data)
1041         if self._expl is None:
1042             result = self._decode(
1043                 tlv,
1044                 offset,
1045                 decode_path=decode_path,
1046                 ctx=ctx,
1047                 tag_only=tag_only,
1048             )
1049             if tag_only:
1050                 return
1051             obj, tail = result
1052         else:
1053             try:
1054                 t, tlen, lv = tag_strip(tlv)
1055             except DecodeError as err:
1056                 raise err.__class__(
1057                     msg=err.msg,
1058                     klass=self.__class__,
1059                     decode_path=decode_path,
1060                     offset=offset,
1061                 )
1062             if t != self._expl:
1063                 raise TagMismatch(
1064                     klass=self.__class__,
1065                     decode_path=decode_path,
1066                     offset=offset,
1067                 )
1068             try:
1069                 l, llen, v = len_decode(lv)
1070             except LenIndefForm as err:
1071                 if not ctx.get("bered", False):
1072                     raise err.__class__(
1073                         msg=err.msg,
1074                         klass=self.__class__,
1075                         decode_path=decode_path,
1076                         offset=offset,
1077                     )
1078                 llen, v = 1, lv[1:]
1079                 offset += tlen + llen
1080                 result = self._decode(
1081                     v,
1082                     offset=offset,
1083                     decode_path=decode_path,
1084                     ctx=ctx,
1085                     tag_only=tag_only,
1086                 )
1087                 if tag_only:
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         return obj, tail
4002
4003     def __repr__(self):
4004         value = pp_console_row(next(self.pps()))
4005         if self.ready:
4006             value = "%s[%r]" % (value, self.value)
4007         return value
4008
4009     def pps(self, decode_path=()):
4010         yield _pp(
4011             asn1_type_name=self.asn1_type_name,
4012             obj_name=self.__class__.__name__,
4013             decode_path=decode_path,
4014             value=self.choice if self.ready else None,
4015             optional=self.optional,
4016             default=self == self.default,
4017             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4018             expl=None if self._expl is None else tag_decode(self._expl),
4019             offset=self.offset,
4020             tlen=self.tlen,
4021             llen=self.llen,
4022             vlen=self.vlen,
4023             expl_lenindef=self.expl_lenindef,
4024             bered=self.bered,
4025         )
4026         if self.ready:
4027             yield self.value.pps(decode_path=decode_path + (self.choice,))
4028         for pp in self.pps_lenindef(decode_path):
4029             yield pp
4030
4031
4032 class PrimitiveTypes(Choice):
4033     """Predefined ``CHOICE`` for all generic primitive types
4034
4035     It could be useful for general decoding of some unspecified values:
4036
4037     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4038     OCTET STRING 3 bytes 666f6f
4039     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4040     INTEGER 1193046
4041     """
4042     __slots__ = ()
4043     schema = tuple((klass.__name__, klass()) for klass in (
4044         Boolean,
4045         Integer,
4046         BitString,
4047         OctetString,
4048         Null,
4049         ObjectIdentifier,
4050         UTF8String,
4051         NumericString,
4052         PrintableString,
4053         TeletexString,
4054         VideotexString,
4055         IA5String,
4056         UTCTime,
4057         GeneralizedTime,
4058         GraphicString,
4059         VisibleString,
4060         ISO646String,
4061         GeneralString,
4062         UniversalString,
4063         BMPString,
4064     ))
4065
4066
4067 class Any(Obj):
4068     """``ANY`` special type
4069
4070     >>> Any(Integer(-123))
4071     ANY 020185
4072     >>> a = Any(OctetString(b"hello world").encode())
4073     ANY 040b68656c6c6f20776f726c64
4074     >>> hexenc(bytes(a))
4075     b'0x040x0bhello world'
4076     """
4077     __slots__ = ("defined",)
4078     tag_default = tag_encode(0)
4079     asn1_type_name = "ANY"
4080
4081     def __init__(
4082             self,
4083             value=None,
4084             expl=None,
4085             optional=False,
4086             _decoded=(0, 0, 0),
4087     ):
4088         """
4089         :param value: set the value. Either any kind of pyderasn's
4090                       **ready** object, or bytes. Pay attention that
4091                       **no** validation is performed is raw binary value
4092                       is valid TLV
4093         :param bytes expl: override default tag with ``EXPLICIT`` one
4094         :param bool optional: is object ``OPTIONAL`` in sequence
4095         """
4096         super(Any, self).__init__(None, expl, None, optional, _decoded)
4097         self._value = None if value is None else self._value_sanitize(value)
4098         self.defined = None
4099
4100     def _value_sanitize(self, value):
4101         if isinstance(value, self.__class__):
4102             return value._value
4103         if isinstance(value, Obj):
4104             return value.encode()
4105         if isinstance(value, binary_type):
4106             return value
4107         raise InvalidValueType((self.__class__, Obj, binary_type))
4108
4109     @property
4110     def ready(self):
4111         return self._value is not None
4112
4113     @property
4114     def bered(self):
4115         if self.expl_lenindef or self.lenindef:
4116             return True
4117         if self.defined is None:
4118             return False
4119         return self.defined[1].bered
4120
4121     def copy(self):
4122         obj = self.__class__()
4123         obj._value = self._value
4124         obj.tag = self.tag
4125         obj._expl = self._expl
4126         obj.optional = self.optional
4127         obj.offset = self.offset
4128         obj.llen = self.llen
4129         obj.vlen = self.vlen
4130         return obj
4131
4132     def __eq__(self, their):
4133         if isinstance(their, binary_type):
4134             return self._value == their
4135         if issubclass(their.__class__, Any):
4136             return self._value == their._value
4137         return False
4138
4139     def __call__(
4140             self,
4141             value=None,
4142             expl=None,
4143             optional=None,
4144     ):
4145         return self.__class__(
4146             value=value,
4147             expl=self._expl if expl is None else expl,
4148             optional=self.optional if optional is None else optional,
4149         )
4150
4151     def __bytes__(self):
4152         self._assert_ready()
4153         return self._value
4154
4155     @property
4156     def tlen(self):
4157         return 0
4158
4159     def _encode(self):
4160         self._assert_ready()
4161         return self._value
4162
4163     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4164         try:
4165             t, tlen, lv = tag_strip(tlv)
4166         except DecodeError as err:
4167             raise err.__class__(
4168                 msg=err.msg,
4169                 klass=self.__class__,
4170                 decode_path=decode_path,
4171                 offset=offset,
4172             )
4173         try:
4174             l, llen, v = len_decode(lv)
4175         except LenIndefForm as err:
4176             if not ctx.get("bered", False):
4177                 raise err.__class__(
4178                     msg=err.msg,
4179                     klass=self.__class__,
4180                     decode_path=decode_path,
4181                     offset=offset,
4182                 )
4183             llen, vlen, v = 1, 0, lv[1:]
4184             sub_offset = offset + tlen + llen
4185             chunk_i = 0
4186             while v[:EOC_LEN].tobytes() != EOC:
4187                 chunk, v = Any().decode(
4188                     v,
4189                     offset=sub_offset,
4190                     decode_path=decode_path + (str(chunk_i),),
4191                     leavemm=True,
4192                     ctx=ctx,
4193                 )
4194                 vlen += chunk.tlvlen
4195                 sub_offset += chunk.tlvlen
4196                 chunk_i += 1
4197             tlvlen = tlen + llen + vlen + EOC_LEN
4198             obj = self.__class__(
4199                 value=tlv[:tlvlen].tobytes(),
4200                 expl=self._expl,
4201                 optional=self.optional,
4202                 _decoded=(offset, 0, tlvlen),
4203             )
4204             obj.lenindef = True
4205             obj.tag = t
4206             return obj, v[EOC_LEN:]
4207         except DecodeError as err:
4208             raise err.__class__(
4209                 msg=err.msg,
4210                 klass=self.__class__,
4211                 decode_path=decode_path,
4212                 offset=offset,
4213             )
4214         if l > len(v):
4215             raise NotEnoughData(
4216                 "encoded length is longer than data",
4217                 klass=self.__class__,
4218                 decode_path=decode_path,
4219                 offset=offset,
4220             )
4221         tlvlen = tlen + llen + l
4222         v, tail = tlv[:tlvlen], v[l:]
4223         obj = self.__class__(
4224             value=v.tobytes(),
4225             expl=self._expl,
4226             optional=self.optional,
4227             _decoded=(offset, 0, tlvlen),
4228         )
4229         obj.tag = t
4230         return obj, tail
4231
4232     def __repr__(self):
4233         return pp_console_row(next(self.pps()))
4234
4235     def pps(self, decode_path=()):
4236         yield _pp(
4237             asn1_type_name=self.asn1_type_name,
4238             obj_name=self.__class__.__name__,
4239             decode_path=decode_path,
4240             blob=self._value if self.ready else None,
4241             optional=self.optional,
4242             default=self == self.default,
4243             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4244             expl=None if self._expl is None else tag_decode(self._expl),
4245             offset=self.offset,
4246             tlen=self.tlen,
4247             llen=self.llen,
4248             vlen=self.vlen,
4249             expl_offset=self.expl_offset if self.expled else None,
4250             expl_tlen=self.expl_tlen if self.expled else None,
4251             expl_llen=self.expl_llen if self.expled else None,
4252             expl_vlen=self.expl_vlen if self.expled else None,
4253             expl_lenindef=self.expl_lenindef,
4254             lenindef=self.lenindef,
4255             bered=self.bered,
4256         )
4257         defined_by, defined = self.defined or (None, None)
4258         if defined_by is not None:
4259             yield defined.pps(
4260                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4261             )
4262         for pp in self.pps_lenindef(decode_path):
4263             yield pp
4264
4265
4266 ########################################################################
4267 # ASN.1 constructed types
4268 ########################################################################
4269
4270 def get_def_by_path(defines_by_path, sub_decode_path):
4271     """Get define by decode path
4272     """
4273     for path, define in defines_by_path:
4274         if len(path) != len(sub_decode_path):
4275             continue
4276         for p1, p2 in zip(path, sub_decode_path):
4277             if (p1 != any) and (p1 != p2):
4278                 break
4279         else:
4280             return define
4281
4282
4283 def abs_decode_path(decode_path, rel_path):
4284     """Create an absolute decode path from current and relative ones
4285
4286     :param decode_path: current decode path, starting point.
4287                         Tuple of strings
4288     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4289                      If first tuple's element is "/", then treat it as
4290                      an absolute path, ignoring ``decode_path`` as
4291                      starting point. Also this tuple can contain ".."
4292                      elements, stripping the leading element from
4293                      ``decode_path``
4294
4295     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4296     ("foo", "bar", "baz", "whatever")
4297     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4298     ("foo", "whatever")
4299     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4300     ("baz", "whatever")
4301     """
4302     if rel_path[0] == "/":
4303         return rel_path[1:]
4304     if rel_path[0] == "..":
4305         return abs_decode_path(decode_path[:-1], rel_path[1:])
4306     return decode_path + rel_path
4307
4308
4309 class Sequence(Obj):
4310     """``SEQUENCE`` structure type
4311
4312     You have to make specification of sequence::
4313
4314         class Extension(Sequence):
4315             schema = (
4316                 ("extnID", ObjectIdentifier()),
4317                 ("critical", Boolean(default=False)),
4318                 ("extnValue", OctetString()),
4319             )
4320
4321     Then, you can work with it as with dictionary.
4322
4323     >>> ext = Extension()
4324     >>> Extension().specs
4325     OrderedDict([
4326         ('extnID', OBJECT IDENTIFIER),
4327         ('critical', BOOLEAN False OPTIONAL DEFAULT),
4328         ('extnValue', OCTET STRING),
4329     ])
4330     >>> ext["extnID"] = "1.2.3"
4331     Traceback (most recent call last):
4332     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4333     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4334
4335     You can determine if sequence is ready to be encoded:
4336
4337     >>> ext.ready
4338     False
4339     >>> ext.encode()
4340     Traceback (most recent call last):
4341     pyderasn.ObjNotReady: object is not ready: extnValue
4342     >>> ext["extnValue"] = OctetString(b"foobar")
4343     >>> ext.ready
4344     True
4345
4346     Value you want to assign, must have the same **type** as in
4347     corresponding specification, but it can have different tags,
4348     optional/default attributes -- they will be taken from specification
4349     automatically::
4350
4351         class TBSCertificate(Sequence):
4352             schema = (
4353                 ("version", Version(expl=tag_ctxc(0), default="v1")),
4354             [...]
4355
4356     >>> tbs = TBSCertificate()
4357     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4358
4359     Assign ``None`` to remove value from sequence.
4360
4361     You can set values in Sequence during its initialization:
4362
4363     >>> AlgorithmIdentifier((
4364         ("algorithm", ObjectIdentifier("1.2.3")),
4365         ("parameters", Any(Null()))
4366     ))
4367     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4368
4369     You can determine if value exists/set in the sequence and take its value:
4370
4371     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4372     (True, True, False)
4373     >>> ext["extnID"]
4374     OBJECT IDENTIFIER 1.2.3
4375
4376     But pay attention that if value has default, then it won't be (not
4377     in) in the sequence (because ``DEFAULT`` must not be encoded in
4378     DER), but you can read its value:
4379
4380     >>> "critical" in ext, ext["critical"]
4381     (False, BOOLEAN False)
4382     >>> ext["critical"] = Boolean(True)
4383     >>> "critical" in ext, ext["critical"]
4384     (True, BOOLEAN True)
4385
4386     All defaulted values are always optional.
4387
4388     .. _allow_default_values_ctx:
4389
4390     DER prohibits default value encoding and will raise an error if
4391     default value is unexpectedly met during decode.
4392     If :ref:`bered <bered_ctx>` context option is set, then no error
4393     will be raised, but ``bered`` attribute set. You can disable strict
4394     defaulted values existence validation by setting
4395     ``"allow_default_values": True`` :ref:`context <ctx>` option.
4396
4397     Two sequences are equal if they have equal specification (schema),
4398     implicit/explicit tagging and the same values.
4399     """
4400     __slots__ = ("specs",)
4401     tag_default = tag_encode(form=TagFormConstructed, num=16)
4402     asn1_type_name = "SEQUENCE"
4403
4404     def __init__(
4405             self,
4406             value=None,
4407             schema=None,
4408             impl=None,
4409             expl=None,
4410             default=None,
4411             optional=False,
4412             _decoded=(0, 0, 0),
4413     ):
4414         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4415         if schema is None:
4416             schema = getattr(self, "schema", ())
4417         self.specs = (
4418             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4419         )
4420         self._value = {}
4421         if value is not None:
4422             if issubclass(value.__class__, Sequence):
4423                 self._value = value._value
4424             elif hasattr(value, "__iter__"):
4425                 for seq_key, seq_value in value:
4426                     self[seq_key] = seq_value
4427             else:
4428                 raise InvalidValueType((Sequence,))
4429         if default is not None:
4430             if not issubclass(default.__class__, Sequence):
4431                 raise InvalidValueType((Sequence,))
4432             default_value = default._value
4433             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4434             default_obj.specs = self.specs
4435             default_obj._value = default_value
4436             self.default = default_obj
4437             if value is None:
4438                 self._value = default_obj.copy()._value
4439
4440     @property
4441     def ready(self):
4442         for name, spec in self.specs.items():
4443             value = self._value.get(name)
4444             if value is None:
4445                 if spec.optional:
4446                     continue
4447                 return False
4448             else:
4449                 if not value.ready:
4450                     return False
4451         return True
4452
4453     @property
4454     def bered(self):
4455         if self.expl_lenindef or self.lenindef or self.ber_encoded:
4456             return True
4457         return any(value.bered for value in self._value.values())
4458
4459     def copy(self):
4460         obj = self.__class__(schema=self.specs)
4461         obj.tag = self.tag
4462         obj._expl = self._expl
4463         obj.default = self.default
4464         obj.optional = self.optional
4465         obj.offset = self.offset
4466         obj.llen = self.llen
4467         obj.vlen = self.vlen
4468         obj._value = {k: v.copy() for k, v in self._value.items()}
4469         return obj
4470
4471     def __eq__(self, their):
4472         if not isinstance(their, self.__class__):
4473             return False
4474         return (
4475             self.specs == their.specs and
4476             self.tag == their.tag and
4477             self._expl == their._expl and
4478             self._value == their._value
4479         )
4480
4481     def __call__(
4482             self,
4483             value=None,
4484             impl=None,
4485             expl=None,
4486             default=None,
4487             optional=None,
4488     ):
4489         return self.__class__(
4490             value=value,
4491             schema=self.specs,
4492             impl=self.tag if impl is None else impl,
4493             expl=self._expl if expl is None else expl,
4494             default=self.default if default is None else default,
4495             optional=self.optional if optional is None else optional,
4496         )
4497
4498     def __contains__(self, key):
4499         return key in self._value
4500
4501     def __setitem__(self, key, value):
4502         spec = self.specs.get(key)
4503         if spec is None:
4504             raise ObjUnknown(key)
4505         if value is None:
4506             self._value.pop(key, None)
4507             return
4508         if not isinstance(value, spec.__class__):
4509             raise InvalidValueType((spec.__class__,))
4510         value = spec(value=value)
4511         if spec.default is not None and value == spec.default:
4512             self._value.pop(key, None)
4513             return
4514         self._value[key] = value
4515
4516     def __getitem__(self, key):
4517         value = self._value.get(key)
4518         if value is not None:
4519             return value
4520         spec = self.specs.get(key)
4521         if spec is None:
4522             raise ObjUnknown(key)
4523         if spec.default is not None:
4524             return spec.default
4525         return None
4526
4527     def _encoded_values(self):
4528         raws = []
4529         for name, spec in self.specs.items():
4530             value = self._value.get(name)
4531             if value is None:
4532                 if spec.optional:
4533                     continue
4534                 raise ObjNotReady(name)
4535             raws.append(value.encode())
4536         return raws
4537
4538     def _encode(self):
4539         v = b"".join(self._encoded_values())
4540         return b"".join((self.tag, len_encode(len(v)), v))
4541
4542     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4543         try:
4544             t, tlen, lv = tag_strip(tlv)
4545         except DecodeError as err:
4546             raise err.__class__(
4547                 msg=err.msg,
4548                 klass=self.__class__,
4549                 decode_path=decode_path,
4550                 offset=offset,
4551             )
4552         if t != self.tag:
4553             raise TagMismatch(
4554                 klass=self.__class__,
4555                 decode_path=decode_path,
4556                 offset=offset,
4557             )
4558         if tag_only:
4559             return
4560         lenindef = False
4561         ctx_bered = ctx.get("bered", False)
4562         try:
4563             l, llen, v = len_decode(lv)
4564         except LenIndefForm as err:
4565             if not ctx_bered:
4566                 raise err.__class__(
4567                     msg=err.msg,
4568                     klass=self.__class__,
4569                     decode_path=decode_path,
4570                     offset=offset,
4571                 )
4572             l, llen, v = 0, 1, lv[1:]
4573             lenindef = True
4574         except DecodeError as err:
4575             raise err.__class__(
4576                 msg=err.msg,
4577                 klass=self.__class__,
4578                 decode_path=decode_path,
4579                 offset=offset,
4580             )
4581         if l > len(v):
4582             raise NotEnoughData(
4583                 "encoded length is longer than data",
4584                 klass=self.__class__,
4585                 decode_path=decode_path,
4586                 offset=offset,
4587             )
4588         if not lenindef:
4589             v, tail = v[:l], v[l:]
4590         vlen = 0
4591         sub_offset = offset + tlen + llen
4592         values = {}
4593         ber_encoded = False
4594         ctx_allow_default_values = ctx.get("allow_default_values", False)
4595         for name, spec in self.specs.items():
4596             if spec.optional and (
4597                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4598                     len(v) == 0
4599             ):
4600                 continue
4601             sub_decode_path = decode_path + (name,)
4602             try:
4603                 value, v_tail = spec.decode(
4604                     v,
4605                     sub_offset,
4606                     leavemm=True,
4607                     decode_path=sub_decode_path,
4608                     ctx=ctx,
4609                 )
4610             except TagMismatch:
4611                 if spec.optional:
4612                     continue
4613                 raise
4614
4615             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4616             if defined is not None:
4617                 defined_by, defined_spec = defined
4618                 if issubclass(value.__class__, SequenceOf):
4619                     for i, _value in enumerate(value):
4620                         sub_sub_decode_path = sub_decode_path + (
4621                             str(i),
4622                             DecodePathDefBy(defined_by),
4623                         )
4624                         defined_value, defined_tail = defined_spec.decode(
4625                             memoryview(bytes(_value)),
4626                             sub_offset + (
4627                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4628                                 if value.expled else (value.tlen + value.llen)
4629                             ),
4630                             leavemm=True,
4631                             decode_path=sub_sub_decode_path,
4632                             ctx=ctx,
4633                         )
4634                         if len(defined_tail) > 0:
4635                             raise DecodeError(
4636                                 "remaining data",
4637                                 klass=self.__class__,
4638                                 decode_path=sub_sub_decode_path,
4639                                 offset=offset,
4640                             )
4641                         _value.defined = (defined_by, defined_value)
4642                 else:
4643                     defined_value, defined_tail = defined_spec.decode(
4644                         memoryview(bytes(value)),
4645                         sub_offset + (
4646                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4647                             if value.expled else (value.tlen + value.llen)
4648                         ),
4649                         leavemm=True,
4650                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4651                         ctx=ctx,
4652                     )
4653                     if len(defined_tail) > 0:
4654                         raise DecodeError(
4655                             "remaining data",
4656                             klass=self.__class__,
4657                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4658                             offset=offset,
4659                         )
4660                     value.defined = (defined_by, defined_value)
4661
4662             value_len = value.fulllen
4663             vlen += value_len
4664             sub_offset += value_len
4665             v = v_tail
4666             if spec.default is not None and value == spec.default:
4667                 if ctx_bered or ctx_allow_default_values:
4668                     ber_encoded = True
4669                 else:
4670                     raise DecodeError(
4671                         "DEFAULT value met",
4672                         klass=self.__class__,
4673                         decode_path=sub_decode_path,
4674                         offset=sub_offset,
4675                     )
4676             values[name] = value
4677
4678             spec_defines = getattr(spec, "defines", ())
4679             if len(spec_defines) == 0:
4680                 defines_by_path = ctx.get("defines_by_path", ())
4681                 if len(defines_by_path) > 0:
4682                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4683             if spec_defines is not None and len(spec_defines) > 0:
4684                 for rel_path, schema in spec_defines:
4685                     defined = schema.get(value, None)
4686                     if defined is not None:
4687                         ctx.setdefault("_defines", []).append((
4688                             abs_decode_path(sub_decode_path[:-1], rel_path),
4689                             (value, defined),
4690                         ))
4691         if lenindef:
4692             if v[:EOC_LEN].tobytes() != EOC:
4693                 raise DecodeError(
4694                     "no EOC",
4695                     klass=self.__class__,
4696                     decode_path=decode_path,
4697                     offset=offset,
4698                 )
4699             tail = v[EOC_LEN:]
4700             vlen += EOC_LEN
4701         elif len(v) > 0:
4702             raise DecodeError(
4703                 "remaining data",
4704                 klass=self.__class__,
4705                 decode_path=decode_path,
4706                 offset=offset,
4707             )
4708         obj = self.__class__(
4709             schema=self.specs,
4710             impl=self.tag,
4711             expl=self._expl,
4712             default=self.default,
4713             optional=self.optional,
4714             _decoded=(offset, llen, vlen),
4715         )
4716         obj._value = values
4717         obj.lenindef = lenindef
4718         obj.ber_encoded = ber_encoded
4719         return obj, tail
4720
4721     def __repr__(self):
4722         value = pp_console_row(next(self.pps()))
4723         cols = []
4724         for name in self.specs:
4725             _value = self._value.get(name)
4726             if _value is None:
4727                 continue
4728             cols.append("%s: %s" % (name, repr(_value)))
4729         return "%s[%s]" % (value, "; ".join(cols))
4730
4731     def pps(self, decode_path=()):
4732         yield _pp(
4733             asn1_type_name=self.asn1_type_name,
4734             obj_name=self.__class__.__name__,
4735             decode_path=decode_path,
4736             optional=self.optional,
4737             default=self == self.default,
4738             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4739             expl=None if self._expl is None else tag_decode(self._expl),
4740             offset=self.offset,
4741             tlen=self.tlen,
4742             llen=self.llen,
4743             vlen=self.vlen,
4744             expl_offset=self.expl_offset if self.expled else None,
4745             expl_tlen=self.expl_tlen if self.expled else None,
4746             expl_llen=self.expl_llen if self.expled else None,
4747             expl_vlen=self.expl_vlen if self.expled else None,
4748             expl_lenindef=self.expl_lenindef,
4749             lenindef=self.lenindef,
4750             ber_encoded=self.ber_encoded,
4751             bered=self.bered,
4752         )
4753         for name in self.specs:
4754             value = self._value.get(name)
4755             if value is None:
4756                 continue
4757             yield value.pps(decode_path=decode_path + (name,))
4758         for pp in self.pps_lenindef(decode_path):
4759             yield pp
4760
4761
4762 class Set(Sequence):
4763     """``SET`` structure type
4764
4765     Its usage is identical to :py:class:`pyderasn.Sequence`.
4766
4767     .. _allow_unordered_set_ctx:
4768
4769     DER prohibits unordered values encoding and will raise an error
4770     during decode. If If :ref:`bered <bered_ctx>` context option is set,
4771     then no error will occure. Also you can disable strict values
4772     ordering check by setting ``"allow_unordered_set": True``
4773     :ref:`context <ctx>` option.
4774     """
4775     __slots__ = ()
4776     tag_default = tag_encode(form=TagFormConstructed, num=17)
4777     asn1_type_name = "SET"
4778
4779     def _encode(self):
4780         raws = self._encoded_values()
4781         raws.sort()
4782         v = b"".join(raws)
4783         return b"".join((self.tag, len_encode(len(v)), v))
4784
4785     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4786         try:
4787             t, tlen, lv = tag_strip(tlv)
4788         except DecodeError as err:
4789             raise err.__class__(
4790                 msg=err.msg,
4791                 klass=self.__class__,
4792                 decode_path=decode_path,
4793                 offset=offset,
4794             )
4795         if t != self.tag:
4796             raise TagMismatch(
4797                 klass=self.__class__,
4798                 decode_path=decode_path,
4799                 offset=offset,
4800             )
4801         if tag_only:
4802             return
4803         lenindef = False
4804         ctx_bered = ctx.get("bered", False)
4805         try:
4806             l, llen, v = len_decode(lv)
4807         except LenIndefForm as err:
4808             if not ctx_bered:
4809                 raise err.__class__(
4810                     msg=err.msg,
4811                     klass=self.__class__,
4812                     decode_path=decode_path,
4813                     offset=offset,
4814                 )
4815             l, llen, v = 0, 1, lv[1:]
4816             lenindef = True
4817         except DecodeError as err:
4818             raise err.__class__(
4819                 msg=err.msg,
4820                 klass=self.__class__,
4821                 decode_path=decode_path,
4822                 offset=offset,
4823             )
4824         if l > len(v):
4825             raise NotEnoughData(
4826                 "encoded length is longer than data",
4827                 klass=self.__class__,
4828                 offset=offset,
4829             )
4830         if not lenindef:
4831             v, tail = v[:l], v[l:]
4832         vlen = 0
4833         sub_offset = offset + tlen + llen
4834         values = {}
4835         ber_encoded = False
4836         ctx_allow_default_values = ctx.get("allow_default_values", False)
4837         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4838         value_prev = memoryview(v[:0])
4839         specs_items = self.specs.items
4840         while len(v) > 0:
4841             if lenindef and v[:EOC_LEN].tobytes() == EOC:
4842                 break
4843             for name, spec in specs_items():
4844                 sub_decode_path = decode_path + (name,)
4845                 try:
4846                     spec.decode(
4847                         v,
4848                         sub_offset,
4849                         leavemm=True,
4850                         decode_path=sub_decode_path,
4851                         ctx=ctx,
4852                         tag_only=True,
4853                     )
4854                 except TagMismatch:
4855                     continue
4856                 break
4857             else:
4858                 raise TagMismatch(
4859                     klass=self.__class__,
4860                     decode_path=decode_path,
4861                     offset=offset,
4862                 )
4863             value, v_tail = spec.decode(
4864                 v,
4865                 sub_offset,
4866                 leavemm=True,
4867                 decode_path=sub_decode_path,
4868                 ctx=ctx,
4869             )
4870             value_len = value.fulllen
4871             if value_prev.tobytes() > v[:value_len].tobytes():
4872                 if ctx_bered or ctx_allow_unordered_set:
4873                     ber_encoded = True
4874                 else:
4875                     raise DecodeError(
4876                         "unordered " + self.asn1_type_name,
4877                         klass=self.__class__,
4878                         decode_path=sub_decode_path,
4879                         offset=sub_offset,
4880                     )
4881             if spec.default is None or value != spec.default:
4882                 pass
4883             elif ctx_bered or ctx_allow_default_values:
4884                 ber_encoded = True
4885             else:
4886                 raise DecodeError(
4887                     "DEFAULT value met",
4888                     klass=self.__class__,
4889                     decode_path=sub_decode_path,
4890                     offset=sub_offset,
4891                 )
4892             values[name] = value
4893             value_prev = v[:value_len]
4894             sub_offset += value_len
4895             vlen += value_len
4896             v = v_tail
4897         obj = self.__class__(
4898             schema=self.specs,
4899             impl=self.tag,
4900             expl=self._expl,
4901             default=self.default,
4902             optional=self.optional,
4903             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4904         )
4905         if lenindef:
4906             if v[:EOC_LEN].tobytes() != EOC:
4907                 raise DecodeError(
4908                     "no EOC",
4909                     klass=self.__class__,
4910                     decode_path=decode_path,
4911                     offset=offset,
4912                 )
4913             tail = v[EOC_LEN:]
4914             obj.lenindef = True
4915         obj._value = values
4916         if not obj.ready:
4917             raise DecodeError(
4918                 "not all values are ready",
4919                 klass=self.__class__,
4920                 decode_path=decode_path,
4921                 offset=offset,
4922             )
4923         obj.ber_encoded = ber_encoded
4924         return obj, tail
4925
4926
4927 class SequenceOf(Obj):
4928     """``SEQUENCE OF`` sequence type
4929
4930     For that kind of type you must specify the object it will carry on
4931     (bounds are for example here, not required)::
4932
4933         class Ints(SequenceOf):
4934             schema = Integer()
4935             bounds = (0, 2)
4936
4937     >>> ints = Ints()
4938     >>> ints.append(Integer(123))
4939     >>> ints.append(Integer(234))
4940     >>> ints
4941     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4942     >>> [int(i) for i in ints]
4943     [123, 234]
4944     >>> ints.append(Integer(345))
4945     Traceback (most recent call last):
4946     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4947     >>> ints[1]
4948     INTEGER 234
4949     >>> ints[1] = Integer(345)
4950     >>> ints
4951     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4952
4953     Also you can initialize sequence with preinitialized values:
4954
4955     >>> ints = Ints([Integer(123), Integer(234)])
4956     """
4957     __slots__ = ("spec", "_bound_min", "_bound_max")
4958     tag_default = tag_encode(form=TagFormConstructed, num=16)
4959     asn1_type_name = "SEQUENCE OF"
4960
4961     def __init__(
4962             self,
4963             value=None,
4964             schema=None,
4965             bounds=None,
4966             impl=None,
4967             expl=None,
4968             default=None,
4969             optional=False,
4970             _decoded=(0, 0, 0),
4971     ):
4972         super(SequenceOf, self).__init__(
4973             impl,
4974             expl,
4975             default,
4976             optional,
4977             _decoded,
4978         )
4979         if schema is None:
4980             schema = getattr(self, "schema", None)
4981         if schema is None:
4982             raise ValueError("schema must be specified")
4983         self.spec = schema
4984         self._bound_min, self._bound_max = getattr(
4985             self,
4986             "bounds",
4987             (0, float("+inf")),
4988         ) if bounds is None else bounds
4989         self._value = []
4990         if value is not None:
4991             self._value = self._value_sanitize(value)
4992         if default is not None:
4993             default_value = self._value_sanitize(default)
4994             default_obj = self.__class__(
4995                 schema=schema,
4996                 impl=self.tag,
4997                 expl=self._expl,
4998             )
4999             default_obj._value = default_value
5000             self.default = default_obj
5001             if value is None:
5002                 self._value = default_obj.copy()._value
5003
5004     def _value_sanitize(self, value):
5005         if issubclass(value.__class__, SequenceOf):
5006             value = value._value
5007         elif hasattr(value, "__iter__"):
5008             value = list(value)
5009         else:
5010             raise InvalidValueType((self.__class__, iter))
5011         if not self._bound_min <= len(value) <= self._bound_max:
5012             raise BoundsError(self._bound_min, len(value), self._bound_max)
5013         for v in value:
5014             if not isinstance(v, self.spec.__class__):
5015                 raise InvalidValueType((self.spec.__class__,))
5016         return value
5017
5018     @property
5019     def ready(self):
5020         return all(v.ready for v in self._value)
5021
5022     @property
5023     def bered(self):
5024         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5025             return True
5026         return any(v.bered for v in self._value)
5027
5028     def copy(self):
5029         obj = self.__class__(schema=self.spec)
5030         obj._bound_min = self._bound_min
5031         obj._bound_max = self._bound_max
5032         obj.tag = self.tag
5033         obj._expl = self._expl
5034         obj.default = self.default
5035         obj.optional = self.optional
5036         obj.offset = self.offset
5037         obj.llen = self.llen
5038         obj.vlen = self.vlen
5039         obj._value = [v.copy() for v in self._value]
5040         return obj
5041
5042     def __eq__(self, their):
5043         if isinstance(their, self.__class__):
5044             return (
5045                 self.spec == their.spec and
5046                 self.tag == their.tag and
5047                 self._expl == their._expl and
5048                 self._value == their._value
5049             )
5050         if hasattr(their, "__iter__"):
5051             return self._value == list(their)
5052         return False
5053
5054     def __call__(
5055             self,
5056             value=None,
5057             bounds=None,
5058             impl=None,
5059             expl=None,
5060             default=None,
5061             optional=None,
5062     ):
5063         return self.__class__(
5064             value=value,
5065             schema=self.spec,
5066             bounds=(
5067                 (self._bound_min, self._bound_max)
5068                 if bounds is None else bounds
5069             ),
5070             impl=self.tag if impl is None else impl,
5071             expl=self._expl if expl is None else expl,
5072             default=self.default if default is None else default,
5073             optional=self.optional if optional is None else optional,
5074         )
5075
5076     def __contains__(self, key):
5077         return key in self._value
5078
5079     def append(self, value):
5080         if not isinstance(value, self.spec.__class__):
5081             raise InvalidValueType((self.spec.__class__,))
5082         if len(self._value) + 1 > self._bound_max:
5083             raise BoundsError(
5084                 self._bound_min,
5085                 len(self._value) + 1,
5086                 self._bound_max,
5087             )
5088         self._value.append(value)
5089
5090     def __iter__(self):
5091         self._assert_ready()
5092         return iter(self._value)
5093
5094     def __len__(self):
5095         self._assert_ready()
5096         return len(self._value)
5097
5098     def __setitem__(self, key, value):
5099         if not isinstance(value, self.spec.__class__):
5100             raise InvalidValueType((self.spec.__class__,))
5101         self._value[key] = self.spec(value=value)
5102
5103     def __getitem__(self, key):
5104         return self._value[key]
5105
5106     def _encoded_values(self):
5107         return [v.encode() for v in self._value]
5108
5109     def _encode(self):
5110         v = b"".join(self._encoded_values())
5111         return b"".join((self.tag, len_encode(len(v)), v))
5112
5113     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5114         try:
5115             t, tlen, lv = tag_strip(tlv)
5116         except DecodeError as err:
5117             raise err.__class__(
5118                 msg=err.msg,
5119                 klass=self.__class__,
5120                 decode_path=decode_path,
5121                 offset=offset,
5122             )
5123         if t != self.tag:
5124             raise TagMismatch(
5125                 klass=self.__class__,
5126                 decode_path=decode_path,
5127                 offset=offset,
5128             )
5129         if tag_only:
5130             return
5131         lenindef = False
5132         ctx_bered = ctx.get("bered", False)
5133         try:
5134             l, llen, v = len_decode(lv)
5135         except LenIndefForm as err:
5136             if not ctx_bered:
5137                 raise err.__class__(
5138                     msg=err.msg,
5139                     klass=self.__class__,
5140                     decode_path=decode_path,
5141                     offset=offset,
5142                 )
5143             l, llen, v = 0, 1, lv[1:]
5144             lenindef = True
5145         except DecodeError as err:
5146             raise err.__class__(
5147                 msg=err.msg,
5148                 klass=self.__class__,
5149                 decode_path=decode_path,
5150                 offset=offset,
5151             )
5152         if l > len(v):
5153             raise NotEnoughData(
5154                 "encoded length is longer than data",
5155                 klass=self.__class__,
5156                 decode_path=decode_path,
5157                 offset=offset,
5158             )
5159         if not lenindef:
5160             v, tail = v[:l], v[l:]
5161         vlen = 0
5162         sub_offset = offset + tlen + llen
5163         _value = []
5164         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5165         value_prev = memoryview(v[:0])
5166         ber_encoded = False
5167         spec = self.spec
5168         while len(v) > 0:
5169             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5170                 break
5171             sub_decode_path = decode_path + (str(len(_value)),)
5172             value, v_tail = spec.decode(
5173                 v,
5174                 sub_offset,
5175                 leavemm=True,
5176                 decode_path=sub_decode_path,
5177                 ctx=ctx,
5178             )
5179             value_len = value.fulllen
5180             if ordering_check:
5181                 if value_prev.tobytes() > v[:value_len].tobytes():
5182                     if ctx_bered or ctx_allow_unordered_set:
5183                         ber_encoded = True
5184                     else:
5185                         raise DecodeError(
5186                             "unordered " + self.asn1_type_name,
5187                             klass=self.__class__,
5188                             decode_path=sub_decode_path,
5189                             offset=sub_offset,
5190                         )
5191                 value_prev = v[:value_len]
5192             _value.append(value)
5193             sub_offset += value_len
5194             vlen += value_len
5195             v = v_tail
5196         try:
5197             obj = self.__class__(
5198                 value=_value,
5199                 schema=spec,
5200                 bounds=(self._bound_min, self._bound_max),
5201                 impl=self.tag,
5202                 expl=self._expl,
5203                 default=self.default,
5204                 optional=self.optional,
5205                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5206             )
5207         except BoundsError as err:
5208             raise DecodeError(
5209                 msg=str(err),
5210                 klass=self.__class__,
5211                 decode_path=decode_path,
5212                 offset=offset,
5213             )
5214         if lenindef:
5215             if v[:EOC_LEN].tobytes() != EOC:
5216                 raise DecodeError(
5217                     "no EOC",
5218                     klass=self.__class__,
5219                     decode_path=decode_path,
5220                     offset=offset,
5221                 )
5222             obj.lenindef = True
5223             tail = v[EOC_LEN:]
5224         obj.ber_encoded = ber_encoded
5225         return obj, tail
5226
5227     def __repr__(self):
5228         return "%s[%s]" % (
5229             pp_console_row(next(self.pps())),
5230             ", ".join(repr(v) for v in self._value),
5231         )
5232
5233     def pps(self, decode_path=()):
5234         yield _pp(
5235             asn1_type_name=self.asn1_type_name,
5236             obj_name=self.__class__.__name__,
5237             decode_path=decode_path,
5238             optional=self.optional,
5239             default=self == self.default,
5240             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5241             expl=None if self._expl is None else tag_decode(self._expl),
5242             offset=self.offset,
5243             tlen=self.tlen,
5244             llen=self.llen,
5245             vlen=self.vlen,
5246             expl_offset=self.expl_offset if self.expled else None,
5247             expl_tlen=self.expl_tlen if self.expled else None,
5248             expl_llen=self.expl_llen if self.expled else None,
5249             expl_vlen=self.expl_vlen if self.expled else None,
5250             expl_lenindef=self.expl_lenindef,
5251             lenindef=self.lenindef,
5252             ber_encoded=self.ber_encoded,
5253             bered=self.bered,
5254         )
5255         for i, value in enumerate(self._value):
5256             yield value.pps(decode_path=decode_path + (str(i),))
5257         for pp in self.pps_lenindef(decode_path):
5258             yield pp
5259
5260
5261 class SetOf(SequenceOf):
5262     """``SET OF`` sequence type
5263
5264     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5265     """
5266     __slots__ = ()
5267     tag_default = tag_encode(form=TagFormConstructed, num=17)
5268     asn1_type_name = "SET OF"
5269
5270     def _encode(self):
5271         raws = self._encoded_values()
5272         raws.sort()
5273         v = b"".join(raws)
5274         return b"".join((self.tag, len_encode(len(v)), v))
5275
5276     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5277         return super(SetOf, self)._decode(
5278             tlv,
5279             offset,
5280             decode_path,
5281             ctx,
5282             tag_only,
5283             ordering_check=True,
5284         )
5285
5286
5287 def obj_by_path(pypath):  # pragma: no cover
5288     """Import object specified as string Python path
5289
5290     Modules must be separated from classes/functions with ``:``.
5291
5292     >>> obj_by_path("foo.bar:Baz")
5293     <class 'foo.bar.Baz'>
5294     >>> obj_by_path("foo.bar:Baz.boo")
5295     <classmethod 'foo.bar.Baz.boo'>
5296     """
5297     mod, objs = pypath.rsplit(":", 1)
5298     from importlib import import_module
5299     obj = import_module(mod)
5300     for obj_name in objs.split("."):
5301         obj = getattr(obj, obj_name)
5302     return obj
5303
5304
5305 def generic_decoder():  # pragma: no cover
5306     # All of this below is a big hack with self references
5307     choice = PrimitiveTypes()
5308     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5309     choice.specs["SetOf"] = SetOf(schema=choice)
5310     for i in range(31):
5311         choice.specs["SequenceOf%d" % i] = SequenceOf(
5312             schema=choice,
5313             expl=tag_ctxc(i),
5314         )
5315     choice.specs["Any"] = Any()
5316
5317     # Class name equals to type name, to omit it from output
5318     class SEQUENCEOF(SequenceOf):
5319         __slots__ = ()
5320         schema = choice
5321
5322     def pprint_any(
5323             obj,
5324             oids=None,
5325             with_colours=False,
5326             with_decode_path=False,
5327             decode_path_only=(),
5328     ):
5329         def _pprint_pps(pps):
5330             for pp in pps:
5331                 if hasattr(pp, "_fields"):
5332                     if (
5333                         decode_path_only != () and
5334                         pp.decode_path[:len(decode_path_only)] != decode_path_only
5335                     ):
5336                         continue
5337                     if pp.asn1_type_name == Choice.asn1_type_name:
5338                         continue
5339                     pp_kwargs = pp._asdict()
5340                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5341                     pp = _pp(**pp_kwargs)
5342                     yield pp_console_row(
5343                         pp,
5344                         oids=oids,
5345                         with_offsets=True,
5346                         with_blob=False,
5347                         with_colours=with_colours,
5348                         with_decode_path=with_decode_path,
5349                         decode_path_len_decrease=len(decode_path_only),
5350                     )
5351                     for row in pp_console_blob(
5352                         pp,
5353                         decode_path_len_decrease=len(decode_path_only),
5354                     ):
5355                         yield row
5356                 else:
5357                     for row in _pprint_pps(pp):
5358                         yield row
5359         return "\n".join(_pprint_pps(obj.pps()))
5360     return SEQUENCEOF(), pprint_any
5361
5362
5363 def main():  # pragma: no cover
5364     import argparse
5365     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5366     parser.add_argument(
5367         "--skip",
5368         type=int,
5369         default=0,
5370         help="Skip that number of bytes from the beginning",
5371     )
5372     parser.add_argument(
5373         "--oids",
5374         help="Python path to dictionary with OIDs",
5375     )
5376     parser.add_argument(
5377         "--schema",
5378         help="Python path to schema definition to use",
5379     )
5380     parser.add_argument(
5381         "--defines-by-path",
5382         help="Python path to decoder's defines_by_path",
5383     )
5384     parser.add_argument(
5385         "--nobered",
5386         action="store_true",
5387         help="Disallow BER encoding",
5388     )
5389     parser.add_argument(
5390         "--print-decode-path",
5391         action="store_true",
5392         help="Print decode paths",
5393     )
5394     parser.add_argument(
5395         "--decode-path-only",
5396         help="Print only specified decode path",
5397     )
5398     parser.add_argument(
5399         "--allow-expl-oob",
5400         action="store_true",
5401         help="Allow explicit tag out-of-bound",
5402     )
5403     parser.add_argument(
5404         "DERFile",
5405         type=argparse.FileType("rb"),
5406         help="Path to DER file you want to decode",
5407     )
5408     args = parser.parse_args()
5409     args.DERFile.seek(args.skip)
5410     der = memoryview(args.DERFile.read())
5411     args.DERFile.close()
5412     oids = obj_by_path(args.oids) if args.oids else {}
5413     if args.schema:
5414         schema = obj_by_path(args.schema)
5415         from functools import partial
5416         pprinter = partial(pprint, big_blobs=True)
5417     else:
5418         schema, pprinter = generic_decoder()
5419     ctx = {
5420         "bered": not args.nobered,
5421         "allow_expl_oob": args.allow_expl_oob,
5422     }
5423     if args.defines_by_path is not None:
5424         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5425     obj, tail = schema().decode(der, ctx=ctx)
5426     print(pprinter(
5427         obj,
5428         oids=oids,
5429         with_colours=True if environ.get("NO_COLOR") is None else False,
5430         with_decode_path=args.print_decode_path,
5431         decode_path_only=(
5432             () if args.decode_path_only is None else
5433             tuple(args.decode_path_only.split(":"))
5434         ),
5435     ))
5436     if tail != b"":
5437         print("\nTrailing data: %s" % hexenc(tail))
5438
5439
5440 if __name__ == "__main__":
5441     main()