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