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