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