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