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