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