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