]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Replace some branches with if/else operator
[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 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 takes
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 object of the same type, but with different implicit/explicit tags
92 are **not** equal.
93
94 You can get objects 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 Size constraints
139 ________________
140
141 Some objects give ability to set value size constraints. This is either
142 possible integer value, or allowed length of various strings and
143 sequences. Constraints are set in the following way::
144
145     class X(...):
146         bounds = (MIN, MAX)
147
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
149
150 For simplicity you can also set bounds the following way::
151
152     bounded_x = X(bounds=(MIN, MAX))
153
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
155 raised.
156
157 Common methods
158 ______________
159
160 All objects have ``ready`` boolean property, that tells if it is ready
161 to be encoded. If that kind of action is performed on unready object,
162 then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
163
164 All objects have ``copy()`` method, returning its copy, that can be safely
165 mutated.
166
167 Decoding
168 ________
169
170 Decoding is performed using ``decode()`` method. ``offset`` optional
171 argument could be used to set initial object's offset in the binary
172 data, for convenience. It returns decoded object and remaining
173 unmarshalled data (tail). Internally all work is done on
174 ``memoryview(data)``, and you can leave returning tail as a memoryview,
175 by specifying ``leavemm=True`` argument.
176
177 When object is decoded, ``decoded`` property is true and you can safely
178 use following properties:
179
180 * ``offset`` -- position including initial offset where object's tag starts
181 * ``tlen`` -- length of object's tag
182 * ``llen`` -- length of object's length value
183 * ``vlen`` -- length of object's value
184 * ``tlvlen`` -- length of the whole object
185
186 Pay attention that those values do **not** include anything related to
187 explicit tag. If you want to know information about it, then use:
188 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
189 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
190 (that actually equals to ordinary ``tlvlen``).
191
192 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
193
194 Pretty printing
195 _______________
196
197 All objects have ``pps()`` method, that is a generator of
198 :py:class:`pyderasn.PP` namedtuple, holding various raw information
199 about the object. If ``pps`` is called on sequences, then all underlying
200 ``PP`` will be yielded.
201
202 You can use :py:func:`pyderasn.pp_console_row` function, converting
203 those ``PP`` to human readable string. Actually exactly it is used for
204 all object ``repr``. But it is easy to write custom formatters.
205
206     >>> from pyderasn import pprint
207     >>> encoded = Integer(-12345).encode()
208     >>> obj, tail = Integer().decode(encoded)
209     >>> print(pprint(obj))
210         0   [1,1,   2] INTEGER -12345
211
212 Primitive types
213 ---------------
214
215 Boolean
216 _______
217 .. autoclass:: pyderasn.Boolean
218    :members: __init__
219
220 Integer
221 _______
222 .. autoclass:: pyderasn.Integer
223    :members: __init__
224
225 BitString
226 _________
227 .. autoclass:: pyderasn.BitString
228    :members: __init__
229
230 OctetString
231 ___________
232 .. autoclass:: pyderasn.OctetString
233    :members: __init__
234
235 Null
236 ____
237 .. autoclass:: pyderasn.Null
238    :members: __init__
239
240 ObjectIdentifier
241 ________________
242 .. autoclass:: pyderasn.ObjectIdentifier
243    :members: __init__
244
245 Enumerated
246 __________
247 .. autoclass:: pyderasn.Enumerated
248
249 CommonString
250 ____________
251 .. autoclass:: pyderasn.CommonString
252
253 UTCTime
254 _______
255 .. autoclass:: pyderasn.UTCTime
256    :members: __init__, todatetime
257
258 GeneralizedTime
259 _______________
260 .. autoclass:: pyderasn.GeneralizedTime
261
262 Special types
263 -------------
264
265 Choice
266 ______
267 .. autoclass:: pyderasn.Choice
268    :members: __init__
269
270 PrimitiveTypes
271 ______________
272 .. autoclass:: PrimitiveTypes
273
274 Any
275 ___
276 .. autoclass:: pyderasn.Any
277    :members: __init__
278
279 Constructed types
280 -----------------
281
282 Sequence
283 ________
284 .. autoclass:: pyderasn.Sequence
285    :members: __init__
286
287 Set
288 ___
289 .. autoclass:: pyderasn.Set
290    :members: __init__
291
292 SequenceOf
293 __________
294 .. autoclass:: pyderasn.SequenceOf
295    :members: __init__
296
297 SetOf
298 _____
299 .. autoclass:: pyderasn.SetOf
300    :members: __init__
301
302 Various
303 -------
304
305 .. autofunction:: pyderasn.hexenc
306 .. autofunction:: pyderasn.hexdec
307 .. autofunction:: pyderasn.tag_encode
308 .. autofunction:: pyderasn.tag_decode
309 .. autofunction:: pyderasn.tag_ctxp
310 .. autofunction:: pyderasn.tag_ctxc
311 .. autoclass:: pyderasn.Obj
312 """
313
314 from codecs import getdecoder
315 from codecs import getencoder
316 from collections import namedtuple
317 from collections import OrderedDict
318 from datetime import datetime
319 from math import ceil
320
321 from six import add_metaclass
322 from six import binary_type
323 from six import byte2int
324 from six import indexbytes
325 from six import int2byte
326 from six import integer_types
327 from six import iterbytes
328 from six import PY2
329 from six import string_types
330 from six import text_type
331 from six.moves import xrange as six_xrange
332
333
334 __all__ = (
335     "Any",
336     "BitString",
337     "BMPString",
338     "Boolean",
339     "BoundsError",
340     "Choice",
341     "DecodeError",
342     "Enumerated",
343     "GeneralizedTime",
344     "GeneralString",
345     "GraphicString",
346     "hexdec",
347     "hexenc",
348     "IA5String",
349     "Integer",
350     "InvalidLength",
351     "InvalidOID",
352     "InvalidValueType",
353     "ISO646String",
354     "NotEnoughData",
355     "Null",
356     "NumericString",
357     "obj_by_path",
358     "ObjectIdentifier",
359     "ObjNotReady",
360     "ObjUnknown",
361     "OctetString",
362     "PrimitiveTypes",
363     "PrintableString",
364     "Sequence",
365     "SequenceOf",
366     "Set",
367     "SetOf",
368     "T61String",
369     "tag_ctxc",
370     "tag_ctxp",
371     "tag_decode",
372     "TagClassApplication",
373     "TagClassContext",
374     "TagClassPrivate",
375     "TagClassUniversal",
376     "TagFormConstructed",
377     "TagFormPrimitive",
378     "TagMismatch",
379     "TeletexString",
380     "UniversalString",
381     "UTCTime",
382     "UTF8String",
383     "VideotexString",
384     "VisibleString",
385 )
386
387 TagClassUniversal = 0
388 TagClassApplication = 1 << 6
389 TagClassContext = 1 << 7
390 TagClassPrivate = 1 << 6 | 1 << 7
391 TagFormPrimitive = 0
392 TagFormConstructed = 1 << 5
393 TagClassReprs = {
394     TagClassContext: "",
395     TagClassApplication: "APPLICATION ",
396     TagClassPrivate: "PRIVATE ",
397     TagClassUniversal: "UNIV ",
398 }
399
400
401 ########################################################################
402 # Errors
403 ########################################################################
404
405 class DecodeError(Exception):
406     def __init__(self, msg="", klass=None, decode_path=(), offset=0):
407         """
408         :param str msg: reason of decode failing
409         :param klass: optional exact DecodeError inherited class (like
410                       :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
411                       :py:exc:`InvalidLength`)
412         :param decode_path: tuple of strings. It contains human
413                             readable names of the fields through which
414                             decoding process has passed
415         :param int offset: binary offset where failure happened
416         """
417         super(DecodeError, self).__init__()
418         self.msg = msg
419         self.klass = klass
420         self.decode_path = decode_path
421         self.offset = offset
422
423     def __str__(self):
424         return " ".join(
425             c for c in (
426                 "" if self.klass is None else self.klass.__name__,
427                 (
428                     ("(%s)" % ".".join(self.decode_path))
429                     if len(self.decode_path) > 0 else ""
430                 ),
431                 ("(at %d)" % self.offset) if self.offset > 0 else "",
432                 self.msg,
433             ) if c != ""
434         )
435
436     def __repr__(self):
437         return "%s(%s)" % (self.__class__.__name__, self)
438
439
440 class NotEnoughData(DecodeError):
441     pass
442
443
444 class TagMismatch(DecodeError):
445     pass
446
447
448 class InvalidLength(DecodeError):
449     pass
450
451
452 class InvalidOID(DecodeError):
453     pass
454
455
456 class ObjUnknown(ValueError):
457     def __init__(self, name):
458         super(ObjUnknown, self).__init__()
459         self.name = name
460
461     def __str__(self):
462         return "object is unknown: %s" % self.name
463
464     def __repr__(self):
465         return "%s(%s)" % (self.__class__.__name__, self)
466
467
468 class ObjNotReady(ValueError):
469     def __init__(self, name):
470         super(ObjNotReady, self).__init__()
471         self.name = name
472
473     def __str__(self):
474         return "object is not ready: %s" % self.name
475
476     def __repr__(self):
477         return "%s(%s)" % (self.__class__.__name__, self)
478
479
480 class InvalidValueType(ValueError):
481     def __init__(self, expected_types):
482         super(InvalidValueType, self).__init__()
483         self.expected_types = expected_types
484
485     def __str__(self):
486         return "invalid value type, expected: %s" % ", ".join(
487             [repr(t) for t in self.expected_types]
488         )
489
490     def __repr__(self):
491         return "%s(%s)" % (self.__class__.__name__, self)
492
493
494 class BoundsError(ValueError):
495     def __init__(self, bound_min, value, bound_max):
496         super(BoundsError, self).__init__()
497         self.bound_min = bound_min
498         self.value = value
499         self.bound_max = bound_max
500
501     def __str__(self):
502         return "unsatisfied bounds: %s <= %s <= %s" % (
503             self.bound_min,
504             self.value,
505             self.bound_max,
506         )
507
508     def __repr__(self):
509         return "%s(%s)" % (self.__class__.__name__, self)
510
511
512 ########################################################################
513 # Basic coders
514 ########################################################################
515
516 _hexdecoder = getdecoder("hex")
517 _hexencoder = getencoder("hex")
518
519
520 def hexdec(data):
521     """Binary data to hexadecimal string convert
522     """
523     return _hexdecoder(data)[0]
524
525
526 def hexenc(data):
527     """Hexadecimal string to binary data convert
528     """
529     return _hexencoder(data)[0].decode("ascii")
530
531
532 def int_bytes_len(num, byte_len=8):
533     if num == 0:
534         return 1
535     return int(ceil(float(num.bit_length()) / byte_len))
536
537
538 def zero_ended_encode(num):
539     octets = bytearray(int_bytes_len(num, 7))
540     i = len(octets) - 1
541     octets[i] = num & 0x7F
542     num >>= 7
543     i -= 1
544     while num > 0:
545         octets[i] = 0x80 | (num & 0x7F)
546         num >>= 7
547         i -= 1
548     return bytes(octets)
549
550
551 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
552     """Encode tag to binary form
553
554     :param int num: tag's number
555     :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
556                       :py:data:`pyderasn.TagClassContext`,
557                       :py:data:`pyderasn.TagClassApplication`,
558                       :py:data:`pyderasn.TagClassPrivate`)
559     :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
560                      :py:data:`pyderasn.TagFormConstructed`)
561     """
562     if num < 31:
563         # [XX|X|.....]
564         return int2byte(klass | form | num)
565     # [XX|X|11111][1.......][1.......] ... [0.......]
566     return int2byte(klass | form | 31) + zero_ended_encode(num)
567
568
569 def tag_decode(tag):
570     """Decode tag from binary form
571
572     .. warning::
573
574        No validation is performed, assuming that it has already passed.
575
576     It returns tuple with three integers, as
577     :py:func:`pyderasn.tag_encode` accepts.
578     """
579     first_octet = byte2int(tag)
580     klass = first_octet & 0xC0
581     form = first_octet & 0x20
582     if first_octet & 0x1F < 0x1F:
583         return (klass, form, first_octet & 0x1F)
584     num = 0
585     for octet in iterbytes(tag[1:]):
586         num <<= 7
587         num |= octet & 0x7F
588     return (klass, form, num)
589
590
591 def tag_ctxp(num):
592     """Create CONTEXT PRIMITIVE tag
593     """
594     return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
595
596
597 def tag_ctxc(num):
598     """Create CONTEXT CONSTRUCTED tag
599     """
600     return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
601
602
603 def tag_strip(data):
604     """Take off tag from the data
605
606     :returns: (encoded tag, tag length, remaining data)
607     """
608     if len(data) == 0:
609         raise NotEnoughData("no data at all")
610     if byte2int(data) & 0x1F < 31:
611         return data[:1], 1, data[1:]
612     i = 0
613     while True:
614         i += 1
615         if i == len(data):
616             raise DecodeError("unfinished tag")
617         if indexbytes(data, i) & 0x80 == 0:
618             break
619     i += 1
620     return data[:i], i, data[i:]
621
622
623 def len_encode(l):
624     if l < 0x80:
625         return int2byte(l)
626     octets = bytearray(int_bytes_len(l) + 1)
627     octets[0] = 0x80 | (len(octets) - 1)
628     for i in six_xrange(len(octets) - 1, 0, -1):
629         octets[i] = l & 0xFF
630         l >>= 8
631     return bytes(octets)
632
633
634 def len_decode(data):
635     if len(data) == 0:
636         raise NotEnoughData("no data at all")
637     first_octet = byte2int(data)
638     if first_octet & 0x80 == 0:
639         return first_octet, 1, data[1:]
640     octets_num = first_octet & 0x7F
641     if octets_num + 1 > len(data):
642         raise NotEnoughData("encoded length is longer than data")
643     if octets_num == 0:
644         raise DecodeError("long form instead of short one")
645     if byte2int(data[1:]) == 0:
646         raise DecodeError("leading zeros")
647     l = 0
648     for v in iterbytes(data[1:1 + octets_num]):
649         l = (l << 8) | v
650     if l <= 127:
651         raise DecodeError("long form instead of short one")
652     return l, 1 + octets_num, data[1 + octets_num:]
653
654
655 ########################################################################
656 # Base class
657 ########################################################################
658
659 class AutoAddSlots(type):
660     def __new__(cls, name, bases, _dict):
661         _dict["__slots__"] = _dict.get("__slots__", ())
662         return type.__new__(cls, name, bases, _dict)
663
664
665 @add_metaclass(AutoAddSlots)
666 class Obj(object):
667     """Common ASN.1 object class
668
669     All ASN.1 types are inherited from it. It has metaclass that
670     automatically adds ``__slots__`` to all inherited classes.
671     """
672     __slots__ = (
673         "tag",
674         "_value",
675         "_expl",
676         "default",
677         "optional",
678         "offset",
679         "llen",
680         "vlen",
681     )
682
683     def __init__(
684             self,
685             impl=None,
686             expl=None,
687             default=None,
688             optional=False,
689             _decoded=(0, 0, 0),
690     ):
691         self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
692         self._expl = getattr(self, "expl", None) if expl is None else expl
693         if self.tag != self.tag_default and self._expl is not None:
694             raise ValueError(
695                 "implicit and explicit tags can not be set simultaneously"
696             )
697         if default is not None:
698             optional = True
699         self.optional = optional
700         self.offset, self.llen, self.vlen = _decoded
701         self.default = None
702
703     @property
704     def ready(self):  # pragma: no cover
705         """Is object ready to be encoded?
706         """
707         raise NotImplementedError()
708
709     def _assert_ready(self):
710         if not self.ready:
711             raise ObjNotReady(self.__class__.__name__)
712
713     @property
714     def decoded(self):
715         """Is object decoded?
716         """
717         return (self.llen + self.vlen) > 0
718
719     def copy(self):  # pragma: no cover
720         """Make a copy of object, safe to be mutated
721         """
722         raise NotImplementedError()
723
724     @property
725     def tlen(self):
726         return len(self.tag)
727
728     @property
729     def tlvlen(self):
730         return self.tlen + self.llen + self.vlen
731
732     def __str__(self):  # pragma: no cover
733         return self.__bytes__() if PY2 else self.__unicode__()
734
735     def __ne__(self, their):
736         return not(self == their)
737
738     def __gt__(self, their):  # pragma: no cover
739         return not(self < their)
740
741     def __le__(self, their):  # pragma: no cover
742         return (self == their) or (self < their)
743
744     def __ge__(self, their):  # pragma: no cover
745         return (self == their) or (self > their)
746
747     def _encode(self):  # pragma: no cover
748         raise NotImplementedError()
749
750     def _decode(self, tlv, offset=0, decode_path=()):  # pragma: no cover
751         raise NotImplementedError()
752
753     def encode(self):
754         raw = self._encode()
755         if self._expl is None:
756             return raw
757         return b"".join((self._expl, len_encode(len(raw)), raw))
758
759     def decode(self, data, offset=0, leavemm=False, decode_path=()):
760         """Decode the data
761
762         :param data: either binary or memoryview
763         :param int offset: initial data's offset
764         :param bool leavemm: do we need to leave memoryview of remaining
765                     data as is, or convert it to bytes otherwise
766         :returns: (Obj, remaining data)
767         """
768         tlv = memoryview(data)
769         if self._expl is None:
770             obj, tail = self._decode(
771                 tlv,
772                 offset,
773                 decode_path=decode_path,
774             )
775         else:
776             try:
777                 t, tlen, lv = tag_strip(tlv)
778             except DecodeError as err:
779                 raise err.__class__(
780                     msg=err.msg,
781                     klass=self.__class__,
782                     decode_path=decode_path,
783                     offset=offset,
784                 )
785             if t != self._expl:
786                 raise TagMismatch(
787                     klass=self.__class__,
788                     decode_path=decode_path,
789                     offset=offset,
790                 )
791             try:
792                 l, llen, v = len_decode(lv)
793             except DecodeError as err:
794                 raise err.__class__(
795                     msg=err.msg,
796                     klass=self.__class__,
797                     decode_path=decode_path,
798                     offset=offset,
799                 )
800             if l > len(v):
801                 raise NotEnoughData(
802                     "encoded length is longer than data",
803                     klass=self.__class__,
804                     decode_path=decode_path,
805                     offset=offset,
806                 )
807             obj, tail = self._decode(
808                 v,
809                 offset=offset + tlen + llen,
810                 decode_path=(),
811             )
812         return obj, (tail if leavemm else tail.tobytes())
813
814     @property
815     def expled(self):
816         return self._expl is not None
817
818     @property
819     def expl_tag(self):
820         return self._expl
821
822     @property
823     def expl_tlen(self):
824         return len(self._expl)
825
826     @property
827     def expl_llen(self):
828         return len(len_encode(self.tlvlen))
829
830     @property
831     def expl_offset(self):
832         return self.offset - self.expl_tlen - self.expl_llen
833
834     @property
835     def expl_vlen(self):
836         return self.tlvlen
837
838     @property
839     def expl_tlvlen(self):
840         return self.expl_tlen + self.expl_llen + self.expl_vlen
841
842
843 ########################################################################
844 # Pretty printing
845 ########################################################################
846
847 PP = namedtuple("PP", (
848     "asn1_type_name",
849     "obj_name",
850     "decode_path",
851     "value",
852     "blob",
853     "optional",
854     "default",
855     "impl",
856     "expl",
857     "offset",
858     "tlen",
859     "llen",
860     "vlen",
861     "expl_offset",
862     "expl_tlen",
863     "expl_llen",
864     "expl_vlen",
865 ))
866
867
868 def _pp(
869         asn1_type_name="unknown",
870         obj_name="unknown",
871         decode_path=(),
872         value=None,
873         blob=None,
874         optional=False,
875         default=False,
876         impl=None,
877         expl=None,
878         offset=0,
879         tlen=0,
880         llen=0,
881         vlen=0,
882         expl_offset=None,
883         expl_tlen=None,
884         expl_llen=None,
885         expl_vlen=None,
886 ):
887     return PP(
888         asn1_type_name,
889         obj_name,
890         decode_path,
891         value,
892         blob,
893         optional,
894         default,
895         impl,
896         expl,
897         offset,
898         tlen,
899         llen,
900         vlen,
901         expl_offset,
902         expl_tlen,
903         expl_llen,
904         expl_vlen,
905     )
906
907
908 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
909     cols = []
910     if with_offsets:
911         cols.append("%5d%s [%d,%d,%4d]" % (
912             pp.offset,
913             (
914                 "  " if pp.expl_offset is None else
915                 ("-%d" % (pp.offset - pp.expl_offset))
916             ),
917             pp.tlen,
918             pp.llen,
919             pp.vlen,
920         ))
921     if len(pp.decode_path) > 0:
922         cols.append(" ." * (len(pp.decode_path)))
923         cols.append("%s:" % pp.decode_path[-1])
924     if pp.expl is not None:
925         klass, _, num = pp.expl
926         cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
927     if pp.impl is not None:
928         klass, _, num = pp.impl
929         cols.append("[%s%d]" % (TagClassReprs[klass], num))
930     if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
931         cols.append(pp.obj_name)
932     cols.append(pp.asn1_type_name)
933     if pp.value is not None:
934         value = pp.value
935         if (
936                 oids is not None and
937                 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
938                 value in oids
939         ):
940             value = "%s (%s)" % (oids[value], pp.value)
941         cols.append(value)
942     if with_blob:
943         if isinstance(pp.blob, binary_type):
944             cols.append(hexenc(pp.blob))
945         elif isinstance(pp.blob, tuple):
946             cols.append(", ".join(pp.blob))
947     if pp.optional:
948         cols.append("OPTIONAL")
949     if pp.default:
950         cols.append("DEFAULT")
951     return " ".join(cols)
952
953
954 def pp_console_blob(pp):
955     cols = [" " * len("XXXXXYY [X,X,XXXX]")]
956     if len(pp.decode_path) > 0:
957         cols.append(" ." * (len(pp.decode_path) + 1))
958     if isinstance(pp.blob, binary_type):
959         blob = hexenc(pp.blob).upper()
960         for i in range(0, len(blob), 32):
961             chunk = blob[i:i + 32]
962             yield " ".join(cols + [":".join(
963                 chunk[j:j + 2] for j in range(0, len(chunk), 2)
964             )])
965     elif isinstance(pp.blob, tuple):
966         yield " ".join(cols + [", ".join(pp.blob)])
967
968
969 def pprint(obj, oids=None, big_blobs=False):
970     """Pretty print object
971
972     :param Obj obj: object you want to pretty print
973     :param oids: ``OID <-> humand readable string`` dictionary. When OID
974                  from it is met, then its humand readable form is printed
975     :param big_blobs: if large binary objects are met (like OctetString
976                       values), do we need to print them too, on separate
977                       lines
978     """
979     def _pprint_pps(pps):
980         for pp in pps:
981             if hasattr(pp, "_fields"):
982                 if big_blobs:
983                     yield pp_console_row(
984                         pp,
985                         oids=oids,
986                         with_offsets=True,
987                         with_blob=False,
988                     )
989                     for row in pp_console_blob(pp):
990                         yield row
991                 else:
992                     yield pp_console_row(pp, oids=oids, with_offsets=True)
993             else:
994                 for row in _pprint_pps(pp):
995                     yield row
996     return "\n".join(_pprint_pps(obj.pps()))
997
998
999 ########################################################################
1000 # ASN.1 primitive types
1001 ########################################################################
1002
1003 class Boolean(Obj):
1004     """``BOOLEAN`` boolean type
1005
1006     >>> b = Boolean(True)
1007     BOOLEAN True
1008     >>> b == Boolean(True)
1009     True
1010     >>> bool(b)
1011     True
1012     """
1013     __slots__ = ()
1014     tag_default = tag_encode(1)
1015     asn1_type_name = "BOOLEAN"
1016
1017     def __init__(
1018             self,
1019             value=None,
1020             impl=None,
1021             expl=None,
1022             default=None,
1023             optional=False,
1024             _decoded=(0, 0, 0),
1025     ):
1026         """
1027         :param value: set the value. Either boolean type, or
1028                       :py:class:`pyderasn.Boolean` object
1029         :param bytes impl: override default tag with ``IMPLICIT`` one
1030         :param bytes expl: override default tag with ``EXPLICIT`` one
1031         :param default: set default value. Type same as in ``value``
1032         :param bool optional: is object ``OPTIONAL`` in sequence
1033         """
1034         super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1035         self._value = None if value is None else self._value_sanitize(value)
1036         if default is not None:
1037             default = self._value_sanitize(default)
1038             self.default = self.__class__(
1039                 value=default,
1040                 impl=self.tag,
1041                 expl=self._expl,
1042             )
1043             if value is None:
1044                 self._value = default
1045
1046     def _value_sanitize(self, value):
1047         if issubclass(value.__class__, Boolean):
1048             return value._value
1049         if isinstance(value, bool):
1050             return value
1051         raise InvalidValueType((self.__class__, bool))
1052
1053     @property
1054     def ready(self):
1055         return self._value is not None
1056
1057     def copy(self):
1058         obj = self.__class__()
1059         obj._value = self._value
1060         obj.tag = self.tag
1061         obj._expl = self._expl
1062         obj.default = self.default
1063         obj.optional = self.optional
1064         obj.offset = self.offset
1065         obj.llen = self.llen
1066         obj.vlen = self.vlen
1067         return obj
1068
1069     def __nonzero__(self):
1070         self._assert_ready()
1071         return self._value
1072
1073     def __bool__(self):
1074         self._assert_ready()
1075         return self._value
1076
1077     def __eq__(self, their):
1078         if isinstance(their, bool):
1079             return self._value == their
1080         if not issubclass(their.__class__, Boolean):
1081             return False
1082         return (
1083             self._value == their._value and
1084             self.tag == their.tag and
1085             self._expl == their._expl
1086         )
1087
1088     def __call__(
1089             self,
1090             value=None,
1091             impl=None,
1092             expl=None,
1093             default=None,
1094             optional=None,
1095     ):
1096         return self.__class__(
1097             value=value,
1098             impl=self.tag if impl is None else impl,
1099             expl=self._expl if expl is None else expl,
1100             default=self.default if default is None else default,
1101             optional=self.optional if optional is None else optional,
1102         )
1103
1104     def _encode(self):
1105         self._assert_ready()
1106         return b"".join((
1107             self.tag,
1108             len_encode(1),
1109             (b"\xFF" if self._value else b"\x00"),
1110         ))
1111
1112     def _decode(self, tlv, offset=0, decode_path=()):
1113         try:
1114             t, _, lv = tag_strip(tlv)
1115         except DecodeError as err:
1116             raise err.__class__(
1117                 msg=err.msg,
1118                 klass=self.__class__,
1119                 decode_path=decode_path,
1120                 offset=offset,
1121             )
1122         if t != self.tag:
1123             raise TagMismatch(
1124                 klass=self.__class__,
1125                 decode_path=decode_path,
1126                 offset=offset,
1127             )
1128         try:
1129             l, _, v = len_decode(lv)
1130         except DecodeError as err:
1131             raise err.__class__(
1132                 msg=err.msg,
1133                 klass=self.__class__,
1134                 decode_path=decode_path,
1135                 offset=offset,
1136             )
1137         if l != 1:
1138             raise InvalidLength(
1139                 "Boolean's length must be equal to 1",
1140                 klass=self.__class__,
1141                 decode_path=decode_path,
1142                 offset=offset,
1143             )
1144         if l > len(v):
1145             raise NotEnoughData(
1146                 "encoded length is longer than data",
1147                 klass=self.__class__,
1148                 decode_path=decode_path,
1149                 offset=offset,
1150             )
1151         first_octet = byte2int(v)
1152         if first_octet == 0:
1153             value = False
1154         elif first_octet == 0xFF:
1155             value = True
1156         else:
1157             raise DecodeError(
1158                 "unacceptable Boolean value",
1159                 klass=self.__class__,
1160                 decode_path=decode_path,
1161                 offset=offset,
1162             )
1163         obj = self.__class__(
1164             value=value,
1165             impl=self.tag,
1166             expl=self._expl,
1167             default=self.default,
1168             optional=self.optional,
1169             _decoded=(offset, 1, 1),
1170         )
1171         return obj, v[1:]
1172
1173     def __repr__(self):
1174         return pp_console_row(next(self.pps()))
1175
1176     def pps(self, decode_path=()):
1177         yield _pp(
1178             asn1_type_name=self.asn1_type_name,
1179             obj_name=self.__class__.__name__,
1180             decode_path=decode_path,
1181             value=str(self._value) if self.ready else None,
1182             optional=self.optional,
1183             default=self == self.default,
1184             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1185             expl=None if self._expl is None else tag_decode(self._expl),
1186             offset=self.offset,
1187             tlen=self.tlen,
1188             llen=self.llen,
1189             vlen=self.vlen,
1190             expl_offset=self.expl_offset if self.expled else None,
1191             expl_tlen=self.expl_tlen if self.expled else None,
1192             expl_llen=self.expl_llen if self.expled else None,
1193             expl_vlen=self.expl_vlen if self.expled else None,
1194         )
1195
1196
1197 class Integer(Obj):
1198     """``INTEGER`` integer type
1199
1200     >>> b = Integer(-123)
1201     INTEGER -123
1202     >>> b == Integer(-123)
1203     True
1204     >>> int(b)
1205     -123
1206
1207     >>> Integer(2, bounds=(1, 3))
1208     INTEGER 2
1209     >>> Integer(5, bounds=(1, 3))
1210     Traceback (most recent call last):
1211     pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1212
1213     ::
1214
1215         class Version(Integer):
1216             schema = (
1217                 ("v1", 0),
1218                 ("v2", 1),
1219                 ("v3", 2),
1220             )
1221
1222     >>> v = Version("v1")
1223     Version INTEGER v1
1224     >>> int(v)
1225     0
1226     >>> v.named
1227     'v1'
1228     >>> v.specs
1229     {'v3': 2, 'v1': 0, 'v2': 1}
1230     """
1231     __slots__ = ("specs", "_bound_min", "_bound_max")
1232     tag_default = tag_encode(2)
1233     asn1_type_name = "INTEGER"
1234
1235     def __init__(
1236             self,
1237             value=None,
1238             bounds=None,
1239             impl=None,
1240             expl=None,
1241             default=None,
1242             optional=False,
1243             _specs=None,
1244             _decoded=(0, 0, 0),
1245     ):
1246         """
1247         :param value: set the value. Either integer type, named value
1248                       (if ``schema`` is specified in the class), or
1249                       :py:class:`pyderasn.Integer` object
1250         :param bounds: set ``(MIN, MAX)`` value constraint.
1251                        (-inf, +inf) by default
1252         :param bytes impl: override default tag with ``IMPLICIT`` one
1253         :param bytes expl: override default tag with ``EXPLICIT`` one
1254         :param default: set default value. Type same as in ``value``
1255         :param bool optional: is object ``OPTIONAL`` in sequence
1256         """
1257         super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1258         self._value = value
1259         specs = getattr(self, "schema", {}) if _specs is None else _specs
1260         self.specs = specs if isinstance(specs, dict) else dict(specs)
1261         self._bound_min, self._bound_max = getattr(
1262             self,
1263             "bounds",
1264             (float("-inf"), float("+inf")),
1265         ) if bounds is None else bounds
1266         if value is not None:
1267             self._value = self._value_sanitize(value)
1268         if default is not None:
1269             default = self._value_sanitize(default)
1270             self.default = self.__class__(
1271                 value=default,
1272                 impl=self.tag,
1273                 expl=self._expl,
1274                 _specs=self.specs,
1275             )
1276             if self._value is None:
1277                 self._value = default
1278
1279     def _value_sanitize(self, value):
1280         if issubclass(value.__class__, Integer):
1281             value = value._value
1282         elif isinstance(value, integer_types):
1283             pass
1284         elif isinstance(value, str):
1285             value = self.specs.get(value)
1286             if value is None:
1287                 raise ObjUnknown("integer value: %s" % value)
1288         else:
1289             raise InvalidValueType((self.__class__, int, str))
1290         if not self._bound_min <= value <= self._bound_max:
1291             raise BoundsError(self._bound_min, value, self._bound_max)
1292         return value
1293
1294     @property
1295     def ready(self):
1296         return self._value is not None
1297
1298     def copy(self):
1299         obj = self.__class__(_specs=self.specs)
1300         obj._value = self._value
1301         obj._bound_min = self._bound_min
1302         obj._bound_max = self._bound_max
1303         obj.tag = self.tag
1304         obj._expl = self._expl
1305         obj.default = self.default
1306         obj.optional = self.optional
1307         obj.offset = self.offset
1308         obj.llen = self.llen
1309         obj.vlen = self.vlen
1310         return obj
1311
1312     def __int__(self):
1313         self._assert_ready()
1314         return int(self._value)
1315
1316     def __hash__(self):
1317         self._assert_ready()
1318         return hash(
1319             self.tag +
1320             bytes(self._expl or b"") +
1321             str(self._value).encode("ascii"),
1322         )
1323
1324     def __eq__(self, their):
1325         if isinstance(their, integer_types):
1326             return self._value == their
1327         if not issubclass(their.__class__, Integer):
1328             return False
1329         return (
1330             self._value == their._value and
1331             self.tag == their.tag and
1332             self._expl == their._expl
1333         )
1334
1335     def __lt__(self, their):
1336         return self._value < their._value
1337
1338     @property
1339     def named(self):
1340         for name, value in self.specs.items():
1341             if value == self._value:
1342                 return name
1343
1344     def __call__(
1345             self,
1346             value=None,
1347             bounds=None,
1348             impl=None,
1349             expl=None,
1350             default=None,
1351             optional=None,
1352     ):
1353         return self.__class__(
1354             value=value,
1355             bounds=(
1356                 (self._bound_min, self._bound_max)
1357                 if bounds is None else bounds
1358             ),
1359             impl=self.tag if impl is None else impl,
1360             expl=self._expl if expl is None else expl,
1361             default=self.default if default is None else default,
1362             optional=self.optional if optional is None else optional,
1363             _specs=self.specs,
1364         )
1365
1366     def _encode(self):
1367         self._assert_ready()
1368         value = self._value
1369         if PY2:
1370             if value == 0:
1371                 octets = bytearray([0])
1372             elif value < 0:
1373                 value = -value
1374                 value -= 1
1375                 octets = bytearray()
1376                 while value > 0:
1377                     octets.append((value & 0xFF) ^ 0xFF)
1378                     value >>= 8
1379                 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1380                     octets.append(0xFF)
1381             else:
1382                 octets = bytearray()
1383                 while value > 0:
1384                     octets.append(value & 0xFF)
1385                     value >>= 8
1386                 if octets[-1] & 0x80 > 0:
1387                     octets.append(0x00)
1388             octets.reverse()
1389             octets = bytes(octets)
1390         else:
1391             bytes_len = ceil(value.bit_length() / 8) or 1
1392             while True:
1393                 try:
1394                     octets = value.to_bytes(
1395                         bytes_len,
1396                         byteorder="big",
1397                         signed=True,
1398                     )
1399                 except OverflowError:
1400                     bytes_len += 1
1401                 else:
1402                     break
1403         return b"".join((self.tag, len_encode(len(octets)), octets))
1404
1405     def _decode(self, tlv, offset=0, decode_path=()):
1406         try:
1407             t, _, lv = tag_strip(tlv)
1408         except DecodeError as err:
1409             raise err.__class__(
1410                 msg=err.msg,
1411                 klass=self.__class__,
1412                 decode_path=decode_path,
1413                 offset=offset,
1414             )
1415         if t != self.tag:
1416             raise TagMismatch(
1417                 klass=self.__class__,
1418                 decode_path=decode_path,
1419                 offset=offset,
1420             )
1421         try:
1422             l, llen, 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 > len(v):
1431             raise NotEnoughData(
1432                 "encoded length is longer than data",
1433                 klass=self.__class__,
1434                 decode_path=decode_path,
1435                 offset=offset,
1436             )
1437         if l == 0:
1438             raise NotEnoughData(
1439                 "zero length",
1440                 klass=self.__class__,
1441                 decode_path=decode_path,
1442                 offset=offset,
1443             )
1444         v, tail = v[:l], v[l:]
1445         first_octet = byte2int(v)
1446         if l > 1:
1447             second_octet = byte2int(v[1:])
1448             if (
1449                     ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1450                     ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1451             ):
1452                 raise DecodeError(
1453                     "non normalized integer",
1454                     klass=self.__class__,
1455                     decode_path=decode_path,
1456                     offset=offset,
1457                 )
1458         if PY2:
1459             value = 0
1460             if first_octet & 0x80 > 0:
1461                 octets = bytearray()
1462                 for octet in bytearray(v):
1463                     octets.append(octet ^ 0xFF)
1464                 for octet in octets:
1465                     value = (value << 8) | octet
1466                 value += 1
1467                 value = -value
1468             else:
1469                 for octet in bytearray(v):
1470                     value = (value << 8) | octet
1471         else:
1472             value = int.from_bytes(v, byteorder="big", signed=True)
1473         try:
1474             obj = self.__class__(
1475                 value=value,
1476                 bounds=(self._bound_min, self._bound_max),
1477                 impl=self.tag,
1478                 expl=self._expl,
1479                 default=self.default,
1480                 optional=self.optional,
1481                 _specs=self.specs,
1482                 _decoded=(offset, llen, l),
1483             )
1484         except BoundsError as err:
1485             raise DecodeError(
1486                 msg=str(err),
1487                 klass=self.__class__,
1488                 decode_path=decode_path,
1489                 offset=offset,
1490             )
1491         return obj, tail
1492
1493     def __repr__(self):
1494         return pp_console_row(next(self.pps()))
1495
1496     def pps(self, decode_path=()):
1497         yield _pp(
1498             asn1_type_name=self.asn1_type_name,
1499             obj_name=self.__class__.__name__,
1500             decode_path=decode_path,
1501             value=(self.named or str(self._value)) if self.ready else None,
1502             optional=self.optional,
1503             default=self == self.default,
1504             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1505             expl=None if self._expl is None else tag_decode(self._expl),
1506             offset=self.offset,
1507             tlen=self.tlen,
1508             llen=self.llen,
1509             vlen=self.vlen,
1510             expl_offset=self.expl_offset if self.expled else None,
1511             expl_tlen=self.expl_tlen if self.expled else None,
1512             expl_llen=self.expl_llen if self.expled else None,
1513             expl_vlen=self.expl_vlen if self.expled else None,
1514         )
1515
1516
1517 class BitString(Obj):
1518     """``BIT STRING`` bit string type
1519
1520     >>> BitString(b"hello world")
1521     BIT STRING 88 bits 68656c6c6f20776f726c64
1522     >>> bytes(b)
1523     b'hello world'
1524     >>> b == b"hello world"
1525     True
1526     >>> b.bit_len
1527     88
1528
1529     >>> b = BitString("'010110000000'B")
1530     BIT STRING 12 bits 5800
1531     >>> b.bit_len
1532     12
1533     >>> b[0], b[1], b[2], b[3]
1534     (False, True, False, True)
1535     >>> b[1000]
1536     False
1537     >>> [v for v in b]
1538     [False, True, False, True, True, False, False, False, False, False, False, False]
1539
1540     ::
1541
1542         class KeyUsage(BitString):
1543             schema = (
1544                 ('digitalSignature', 0),
1545                 ('nonRepudiation', 1),
1546                 ('keyEncipherment', 2),
1547             )
1548
1549     >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1550     KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1551     >>> b.named
1552     ['nonRepudiation', 'keyEncipherment']
1553     >>> b.specs
1554     {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1555     """
1556     __slots__ = ("specs",)
1557     tag_default = tag_encode(3)
1558     asn1_type_name = "BIT STRING"
1559
1560     def __init__(
1561             self,
1562             value=None,
1563             impl=None,
1564             expl=None,
1565             default=None,
1566             optional=False,
1567             _specs=None,
1568             _decoded=(0, 0, 0),
1569     ):
1570         """
1571         :param value: set the value. Either binary type, tuple of named
1572                       values (if ``schema`` is specified in the class),
1573                       string in ``'XXX...'B`` form, or
1574                       :py:class:`pyderasn.BitString` object
1575         :param bytes impl: override default tag with ``IMPLICIT`` one
1576         :param bytes expl: override default tag with ``EXPLICIT`` one
1577         :param default: set default value. Type same as in ``value``
1578         :param bool optional: is object ``OPTIONAL`` in sequence
1579         """
1580         super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1581         specs = getattr(self, "schema", {}) if _specs is None else _specs
1582         self.specs = specs if isinstance(specs, dict) else dict(specs)
1583         self._value = None if value is None else self._value_sanitize(value)
1584         if default is not None:
1585             default = self._value_sanitize(default)
1586             self.default = self.__class__(
1587                 value=default,
1588                 impl=self.tag,
1589                 expl=self._expl,
1590             )
1591             if value is None:
1592                 self._value = default
1593
1594     def _bits2octets(self, bits):
1595         if len(self.specs) > 0:
1596             bits = bits.rstrip("0")
1597         bit_len = len(bits)
1598         bits += "0" * ((8 - (bit_len % 8)) % 8)
1599         octets = bytearray(len(bits) // 8)
1600         for i in six_xrange(len(octets)):
1601             octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1602         return bit_len, bytes(octets)
1603
1604     def _value_sanitize(self, value):
1605         if issubclass(value.__class__, BitString):
1606             return value._value
1607         if isinstance(value, (string_types, binary_type)):
1608             if (
1609                     isinstance(value, string_types) and
1610                     value.startswith("'") and
1611                     value.endswith("'B")
1612             ):
1613                 value = value[1:-2]
1614                 if not set(value) <= set(("0", "1")):
1615                     raise ValueError("B's coding contains unacceptable chars")
1616                 return self._bits2octets(value)
1617             elif isinstance(value, binary_type):
1618                 return (len(value) * 8, value)
1619             else:
1620                 raise InvalidValueType((
1621                     self.__class__,
1622                     string_types,
1623                     binary_type,
1624                 ))
1625         if isinstance(value, tuple):
1626             if (
1627                     len(value) == 2 and
1628                     isinstance(value[0], integer_types) and
1629                     isinstance(value[1], binary_type)
1630             ):
1631                 return value
1632             bits = []
1633             for name in value:
1634                 bit = self.specs.get(name)
1635                 if bit is None:
1636                     raise ObjUnknown("BitString value: %s" % name)
1637                 bits.append(bit)
1638             if len(bits) == 0:
1639                 return self._bits2octets("")
1640             bits = set(bits)
1641             return self._bits2octets("".join(
1642                 ("1" if bit in bits else "0")
1643                 for bit in six_xrange(max(bits) + 1)
1644             ))
1645         raise InvalidValueType((self.__class__, binary_type, string_types))
1646
1647     @property
1648     def ready(self):
1649         return self._value is not None
1650
1651     def copy(self):
1652         obj = self.__class__(_specs=self.specs)
1653         value = self._value
1654         if value is not None:
1655             value = (value[0], value[1])
1656         obj._value = value
1657         obj.tag = self.tag
1658         obj._expl = self._expl
1659         obj.default = self.default
1660         obj.optional = self.optional
1661         obj.offset = self.offset
1662         obj.llen = self.llen
1663         obj.vlen = self.vlen
1664         return obj
1665
1666     def __iter__(self):
1667         self._assert_ready()
1668         for i in six_xrange(self._value[0]):
1669             yield self[i]
1670
1671     @property
1672     def bit_len(self):
1673         self._assert_ready()
1674         return self._value[0]
1675
1676     def __bytes__(self):
1677         self._assert_ready()
1678         return self._value[1]
1679
1680     def __eq__(self, their):
1681         if isinstance(their, bytes):
1682             return self._value[1] == their
1683         if not issubclass(their.__class__, BitString):
1684             return False
1685         return (
1686             self._value == their._value and
1687             self.tag == their.tag and
1688             self._expl == their._expl
1689         )
1690
1691     @property
1692     def named(self):
1693         return [name for name, bit in self.specs.items() if self[bit]]
1694
1695     def __call__(
1696             self,
1697             value=None,
1698             impl=None,
1699             expl=None,
1700             default=None,
1701             optional=None,
1702     ):
1703         return self.__class__(
1704             value=value,
1705             impl=self.tag if impl is None else impl,
1706             expl=self._expl if expl is None else expl,
1707             default=self.default if default is None else default,
1708             optional=self.optional if optional is None else optional,
1709             _specs=self.specs,
1710         )
1711
1712     def __getitem__(self, key):
1713         if isinstance(key, int):
1714             bit_len, octets = self._value
1715             if key >= bit_len:
1716                 return False
1717             return (
1718                 byte2int(memoryview(octets)[key // 8:]) >>
1719                 (7 - (key % 8))
1720             ) & 1 == 1
1721         if isinstance(key, string_types):
1722             value = self.specs.get(key)
1723             if value is None:
1724                 raise ObjUnknown("BitString value: %s" % key)
1725             return self[value]
1726         raise InvalidValueType((int, str))
1727
1728     def _encode(self):
1729         self._assert_ready()
1730         bit_len, octets = self._value
1731         return b"".join((
1732             self.tag,
1733             len_encode(len(octets) + 1),
1734             int2byte((8 - bit_len % 8) % 8),
1735             octets,
1736         ))
1737
1738     def _decode(self, tlv, offset=0, decode_path=()):
1739         try:
1740             t, _, lv = tag_strip(tlv)
1741         except DecodeError as err:
1742             raise err.__class__(
1743                 msg=err.msg,
1744                 klass=self.__class__,
1745                 decode_path=decode_path,
1746                 offset=offset,
1747             )
1748         if t != self.tag:
1749             raise TagMismatch(
1750                 klass=self.__class__,
1751                 decode_path=decode_path,
1752                 offset=offset,
1753             )
1754         try:
1755             l, llen, v = len_decode(lv)
1756         except DecodeError as err:
1757             raise err.__class__(
1758                 msg=err.msg,
1759                 klass=self.__class__,
1760                 decode_path=decode_path,
1761                 offset=offset,
1762             )
1763         if l > len(v):
1764             raise NotEnoughData(
1765                 "encoded length is longer than data",
1766                 klass=self.__class__,
1767                 decode_path=decode_path,
1768                 offset=offset,
1769             )
1770         if l == 0:
1771             raise NotEnoughData(
1772                 "zero length",
1773                 klass=self.__class__,
1774                 decode_path=decode_path,
1775                 offset=offset,
1776             )
1777         pad_size = byte2int(v)
1778         if l == 1 and pad_size != 0:
1779             raise DecodeError(
1780                 "invalid empty value",
1781                 klass=self.__class__,
1782                 decode_path=decode_path,
1783                 offset=offset,
1784             )
1785         if pad_size > 7:
1786             raise DecodeError(
1787                 "too big pad",
1788                 klass=self.__class__,
1789                 decode_path=decode_path,
1790                 offset=offset,
1791             )
1792         if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1793             raise DecodeError(
1794                 "invalid pad",
1795                 klass=self.__class__,
1796                 decode_path=decode_path,
1797                 offset=offset,
1798             )
1799         v, tail = v[:l], v[l:]
1800         obj = self.__class__(
1801             value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1802             impl=self.tag,
1803             expl=self._expl,
1804             default=self.default,
1805             optional=self.optional,
1806             _specs=self.specs,
1807             _decoded=(offset, llen, l),
1808         )
1809         return obj, tail
1810
1811     def __repr__(self):
1812         return pp_console_row(next(self.pps()))
1813
1814     def pps(self, decode_path=()):
1815         value = None
1816         blob = None
1817         if self.ready:
1818             bit_len, blob = self._value
1819             value = "%d bits" % bit_len
1820             if len(self.specs) > 0:
1821                 blob = tuple(self.named)
1822         yield _pp(
1823             asn1_type_name=self.asn1_type_name,
1824             obj_name=self.__class__.__name__,
1825             decode_path=decode_path,
1826             value=value,
1827             blob=blob,
1828             optional=self.optional,
1829             default=self == self.default,
1830             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1831             expl=None if self._expl is None else tag_decode(self._expl),
1832             offset=self.offset,
1833             tlen=self.tlen,
1834             llen=self.llen,
1835             vlen=self.vlen,
1836             expl_offset=self.expl_offset if self.expled else None,
1837             expl_tlen=self.expl_tlen if self.expled else None,
1838             expl_llen=self.expl_llen if self.expled else None,
1839             expl_vlen=self.expl_vlen if self.expled else None,
1840         )
1841
1842
1843 class OctetString(Obj):
1844     """``OCTET STRING`` binary string type
1845
1846     >>> s = OctetString(b"hello world")
1847     OCTET STRING 11 bytes 68656c6c6f20776f726c64
1848     >>> s == OctetString(b"hello world")
1849     True
1850     >>> bytes(s)
1851     b'hello world'
1852
1853     >>> OctetString(b"hello", bounds=(4, 4))
1854     Traceback (most recent call last):
1855     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1856     >>> OctetString(b"hell", bounds=(4, 4))
1857     OCTET STRING 4 bytes 68656c6c
1858     """
1859     __slots__ = ("_bound_min", "_bound_max")
1860     tag_default = tag_encode(4)
1861     asn1_type_name = "OCTET STRING"
1862
1863     def __init__(
1864             self,
1865             value=None,
1866             bounds=None,
1867             impl=None,
1868             expl=None,
1869             default=None,
1870             optional=False,
1871             _decoded=(0, 0, 0),
1872     ):
1873         """
1874         :param value: set the value. Either binary type, or
1875                       :py:class:`pyderasn.OctetString` object
1876         :param bounds: set ``(MIN, MAX)`` value size constraint.
1877                        (-inf, +inf) by default
1878         :param bytes impl: override default tag with ``IMPLICIT`` one
1879         :param bytes expl: override default tag with ``EXPLICIT`` one
1880         :param default: set default value. Type same as in ``value``
1881         :param bool optional: is object ``OPTIONAL`` in sequence
1882         """
1883         super(OctetString, self).__init__(
1884             impl,
1885             expl,
1886             default,
1887             optional,
1888             _decoded,
1889         )
1890         self._value = value
1891         self._bound_min, self._bound_max = getattr(
1892             self,
1893             "bounds",
1894             (0, float("+inf")),
1895         ) if bounds is None else bounds
1896         if value is not None:
1897             self._value = self._value_sanitize(value)
1898         if default is not None:
1899             default = self._value_sanitize(default)
1900             self.default = self.__class__(
1901                 value=default,
1902                 impl=self.tag,
1903                 expl=self._expl,
1904             )
1905             if self._value is None:
1906                 self._value = default
1907
1908     def _value_sanitize(self, value):
1909         if issubclass(value.__class__, OctetString):
1910             value = value._value
1911         elif isinstance(value, binary_type):
1912             pass
1913         else:
1914             raise InvalidValueType((self.__class__, bytes))
1915         if not self._bound_min <= len(value) <= self._bound_max:
1916             raise BoundsError(self._bound_min, len(value), self._bound_max)
1917         return value
1918
1919     @property
1920     def ready(self):
1921         return self._value is not None
1922
1923     def copy(self):
1924         obj = self.__class__()
1925         obj._value = self._value
1926         obj._bound_min = self._bound_min
1927         obj._bound_max = self._bound_max
1928         obj.tag = self.tag
1929         obj._expl = self._expl
1930         obj.default = self.default
1931         obj.optional = self.optional
1932         obj.offset = self.offset
1933         obj.llen = self.llen
1934         obj.vlen = self.vlen
1935         return obj
1936
1937     def __bytes__(self):
1938         self._assert_ready()
1939         return self._value
1940
1941     def __eq__(self, their):
1942         if isinstance(their, binary_type):
1943             return self._value == their
1944         if not issubclass(their.__class__, OctetString):
1945             return False
1946         return (
1947             self._value == their._value and
1948             self.tag == their.tag and
1949             self._expl == their._expl
1950         )
1951
1952     def __lt__(self, their):
1953         return self._value < their._value
1954
1955     def __call__(
1956             self,
1957             value=None,
1958             bounds=None,
1959             impl=None,
1960             expl=None,
1961             default=None,
1962             optional=None,
1963     ):
1964         return self.__class__(
1965             value=value,
1966             bounds=(
1967                 (self._bound_min, self._bound_max)
1968                 if bounds is None else bounds
1969             ),
1970             impl=self.tag if impl is None else impl,
1971             expl=self._expl if expl is None else expl,
1972             default=self.default if default is None else default,
1973             optional=self.optional if optional is None else optional,
1974         )
1975
1976     def _encode(self):
1977         self._assert_ready()
1978         return b"".join((
1979             self.tag,
1980             len_encode(len(self._value)),
1981             self._value,
1982         ))
1983
1984     def _decode(self, tlv, offset=0, decode_path=()):
1985         try:
1986             t, _, lv = tag_strip(tlv)
1987         except DecodeError as err:
1988             raise err.__class__(
1989                 msg=err.msg,
1990                 klass=self.__class__,
1991                 decode_path=decode_path,
1992                 offset=offset,
1993             )
1994         if t != self.tag:
1995             raise TagMismatch(
1996                 klass=self.__class__,
1997                 decode_path=decode_path,
1998                 offset=offset,
1999             )
2000         try:
2001             l, llen, v = len_decode(lv)
2002         except DecodeError as err:
2003             raise err.__class__(
2004                 msg=err.msg,
2005                 klass=self.__class__,
2006                 decode_path=decode_path,
2007                 offset=offset,
2008             )
2009         if l > len(v):
2010             raise NotEnoughData(
2011                 "encoded length is longer than data",
2012                 klass=self.__class__,
2013                 decode_path=decode_path,
2014                 offset=offset,
2015             )
2016         v, tail = v[:l], v[l:]
2017         try:
2018             obj = self.__class__(
2019                 value=v.tobytes(),
2020                 bounds=(self._bound_min, self._bound_max),
2021                 impl=self.tag,
2022                 expl=self._expl,
2023                 default=self.default,
2024                 optional=self.optional,
2025                 _decoded=(offset, llen, l),
2026             )
2027         except BoundsError as err:
2028             raise DecodeError(
2029                 msg=str(err),
2030                 klass=self.__class__,
2031                 decode_path=decode_path,
2032                 offset=offset,
2033             )
2034         return obj, tail
2035
2036     def __repr__(self):
2037         return pp_console_row(next(self.pps()))
2038
2039     def pps(self, decode_path=()):
2040         yield _pp(
2041             asn1_type_name=self.asn1_type_name,
2042             obj_name=self.__class__.__name__,
2043             decode_path=decode_path,
2044             value=("%d bytes" % len(self._value)) if self.ready else None,
2045             blob=self._value if self.ready else None,
2046             optional=self.optional,
2047             default=self == self.default,
2048             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2049             expl=None if self._expl is None else tag_decode(self._expl),
2050             offset=self.offset,
2051             tlen=self.tlen,
2052             llen=self.llen,
2053             vlen=self.vlen,
2054             expl_offset=self.expl_offset if self.expled else None,
2055             expl_tlen=self.expl_tlen if self.expled else None,
2056             expl_llen=self.expl_llen if self.expled else None,
2057             expl_vlen=self.expl_vlen if self.expled else None,
2058         )
2059
2060
2061 class Null(Obj):
2062     """``NULL`` null object
2063
2064     >>> n = Null()
2065     NULL
2066     >>> n.ready
2067     True
2068     """
2069     __slots__ = ()
2070     tag_default = tag_encode(5)
2071     asn1_type_name = "NULL"
2072
2073     def __init__(
2074             self,
2075             value=None,  # unused, but Sequence passes it
2076             impl=None,
2077             expl=None,
2078             optional=False,
2079             _decoded=(0, 0, 0),
2080     ):
2081         """
2082         :param bytes impl: override default tag with ``IMPLICIT`` one
2083         :param bytes expl: override default tag with ``EXPLICIT`` one
2084         :param bool optional: is object ``OPTIONAL`` in sequence
2085         """
2086         super(Null, self).__init__(impl, expl, None, optional, _decoded)
2087         self.default = None
2088
2089     @property
2090     def ready(self):
2091         return True
2092
2093     def copy(self):
2094         obj = self.__class__()
2095         obj.tag = self.tag
2096         obj._expl = self._expl
2097         obj.default = self.default
2098         obj.optional = self.optional
2099         obj.offset = self.offset
2100         obj.llen = self.llen
2101         obj.vlen = self.vlen
2102         return obj
2103
2104     def __eq__(self, their):
2105         if not issubclass(their.__class__, Null):
2106             return False
2107         return (
2108             self.tag == their.tag and
2109             self._expl == their._expl
2110         )
2111
2112     def __call__(
2113             self,
2114             value=None,
2115             impl=None,
2116             expl=None,
2117             optional=None,
2118     ):
2119         return self.__class__(
2120             impl=self.tag if impl is None else impl,
2121             expl=self._expl if expl is None else expl,
2122             optional=self.optional if optional is None else optional,
2123         )
2124
2125     def _encode(self):
2126         return self.tag + len_encode(0)
2127
2128     def _decode(self, tlv, offset=0, decode_path=()):
2129         try:
2130             t, _, lv = tag_strip(tlv)
2131         except DecodeError as err:
2132             raise err.__class__(
2133                 msg=err.msg,
2134                 klass=self.__class__,
2135                 decode_path=decode_path,
2136                 offset=offset,
2137             )
2138         if t != self.tag:
2139             raise TagMismatch(
2140                 klass=self.__class__,
2141                 decode_path=decode_path,
2142                 offset=offset,
2143             )
2144         try:
2145             l, _, v = len_decode(lv)
2146         except DecodeError as err:
2147             raise err.__class__(
2148                 msg=err.msg,
2149                 klass=self.__class__,
2150                 decode_path=decode_path,
2151                 offset=offset,
2152             )
2153         if l != 0:
2154             raise InvalidLength(
2155                 "Null must have zero length",
2156                 klass=self.__class__,
2157                 decode_path=decode_path,
2158                 offset=offset,
2159             )
2160         obj = self.__class__(
2161             impl=self.tag,
2162             expl=self._expl,
2163             optional=self.optional,
2164             _decoded=(offset, 1, 0),
2165         )
2166         return obj, v
2167
2168     def __repr__(self):
2169         return pp_console_row(next(self.pps()))
2170
2171     def pps(self, decode_path=()):
2172         yield _pp(
2173             asn1_type_name=self.asn1_type_name,
2174             obj_name=self.__class__.__name__,
2175             decode_path=decode_path,
2176             optional=self.optional,
2177             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2178             expl=None if self._expl is None else tag_decode(self._expl),
2179             offset=self.offset,
2180             tlen=self.tlen,
2181             llen=self.llen,
2182             vlen=self.vlen,
2183             expl_offset=self.expl_offset if self.expled else None,
2184             expl_tlen=self.expl_tlen if self.expled else None,
2185             expl_llen=self.expl_llen if self.expled else None,
2186             expl_vlen=self.expl_vlen if self.expled else None,
2187         )
2188
2189
2190 class ObjectIdentifier(Obj):
2191     """``OBJECT IDENTIFIER`` OID type
2192
2193     >>> oid = ObjectIdentifier((1, 2, 3))
2194     OBJECT IDENTIFIER 1.2.3
2195     >>> oid == ObjectIdentifier("1.2.3")
2196     True
2197     >>> tuple(oid)
2198     (1, 2, 3)
2199     >>> str(oid)
2200     '1.2.3'
2201     >>> oid + (4, 5) + ObjectIdentifier("1.7")
2202     OBJECT IDENTIFIER 1.2.3.4.5.1.7
2203
2204     >>> str(ObjectIdentifier((3, 1)))
2205     Traceback (most recent call last):
2206     pyderasn.InvalidOID: unacceptable first arc value
2207     """
2208     __slots__ = ()
2209     tag_default = tag_encode(6)
2210     asn1_type_name = "OBJECT IDENTIFIER"
2211
2212     def __init__(
2213             self,
2214             value=None,
2215             impl=None,
2216             expl=None,
2217             default=None,
2218             optional=False,
2219             _decoded=(0, 0, 0),
2220     ):
2221         """
2222         :param value: set the value. Either tuples of integers,
2223                       string of "."-concatenated integers, or
2224                       :py:class:`pyderasn.ObjectIdentifier` object
2225         :param bytes impl: override default tag with ``IMPLICIT`` one
2226         :param bytes expl: override default tag with ``EXPLICIT`` one
2227         :param default: set default value. Type same as in ``value``
2228         :param bool optional: is object ``OPTIONAL`` in sequence
2229         """
2230         super(ObjectIdentifier, self).__init__(
2231             impl,
2232             expl,
2233             default,
2234             optional,
2235             _decoded,
2236         )
2237         self._value = value
2238         if value is not None:
2239             self._value = self._value_sanitize(value)
2240         if default is not None:
2241             default = self._value_sanitize(default)
2242             self.default = self.__class__(
2243                 value=default,
2244                 impl=self.tag,
2245                 expl=self._expl,
2246             )
2247             if self._value is None:
2248                 self._value = default
2249
2250     def __add__(self, their):
2251         if isinstance(their, self.__class__):
2252             return self.__class__(self._value + their._value)
2253         if isinstance(their, tuple):
2254             return self.__class__(self._value + their)
2255         raise InvalidValueType((self.__class__, tuple))
2256
2257     def _value_sanitize(self, value):
2258         if issubclass(value.__class__, ObjectIdentifier):
2259             return value._value
2260         if isinstance(value, string_types):
2261             try:
2262                 value = tuple(int(arc) for arc in value.split("."))
2263             except ValueError:
2264                 raise InvalidOID("unacceptable arcs values")
2265         if isinstance(value, tuple):
2266             if len(value) < 2:
2267                 raise InvalidOID("less than 2 arcs")
2268             first_arc = value[0]
2269             if first_arc in (0, 1):
2270                 if not (0 <= value[1] <= 39):
2271                     raise InvalidOID("second arc is too wide")
2272             elif first_arc == 2:
2273                 pass
2274             else:
2275                 raise InvalidOID("unacceptable first arc value")
2276             return value
2277         raise InvalidValueType((self.__class__, str, tuple))
2278
2279     @property
2280     def ready(self):
2281         return self._value is not None
2282
2283     def copy(self):
2284         obj = self.__class__()
2285         obj._value = self._value
2286         obj.tag = self.tag
2287         obj._expl = self._expl
2288         obj.default = self.default
2289         obj.optional = self.optional
2290         obj.offset = self.offset
2291         obj.llen = self.llen
2292         obj.vlen = self.vlen
2293         return obj
2294
2295     def __iter__(self):
2296         self._assert_ready()
2297         return iter(self._value)
2298
2299     def __str__(self):
2300         return ".".join(str(arc) for arc in self._value or ())
2301
2302     def __hash__(self):
2303         self._assert_ready()
2304         return hash(
2305             self.tag +
2306             bytes(self._expl or b"") +
2307             str(self._value).encode("ascii"),
2308         )
2309
2310     def __eq__(self, their):
2311         if isinstance(their, tuple):
2312             return self._value == their
2313         if not issubclass(their.__class__, ObjectIdentifier):
2314             return False
2315         return (
2316             self.tag == their.tag and
2317             self._expl == their._expl and
2318             self._value == their._value
2319         )
2320
2321     def __lt__(self, their):
2322         return self._value < their._value
2323
2324     def __call__(
2325             self,
2326             value=None,
2327             impl=None,
2328             expl=None,
2329             default=None,
2330             optional=None,
2331     ):
2332         return self.__class__(
2333             value=value,
2334             impl=self.tag if impl is None else impl,
2335             expl=self._expl if expl is None else expl,
2336             default=self.default if default is None else default,
2337             optional=self.optional if optional is None else optional,
2338         )
2339
2340     def _encode(self):
2341         self._assert_ready()
2342         value = self._value
2343         first_value = value[1]
2344         first_arc = value[0]
2345         if first_arc == 0:
2346             pass
2347         elif first_arc == 1:
2348             first_value += 40
2349         elif first_arc == 2:
2350             first_value += 80
2351         else:  # pragma: no cover
2352             raise RuntimeError("invalid arc is stored")
2353         octets = [zero_ended_encode(first_value)]
2354         for arc in value[2:]:
2355             octets.append(zero_ended_encode(arc))
2356         v = b"".join(octets)
2357         return b"".join((self.tag, len_encode(len(v)), v))
2358
2359     def _decode(self, tlv, offset=0, decode_path=()):
2360         try:
2361             t, _, lv = tag_strip(tlv)
2362         except DecodeError as err:
2363             raise err.__class__(
2364                 msg=err.msg,
2365                 klass=self.__class__,
2366                 decode_path=decode_path,
2367                 offset=offset,
2368             )
2369         if t != self.tag:
2370             raise TagMismatch(
2371                 klass=self.__class__,
2372                 decode_path=decode_path,
2373                 offset=offset,
2374             )
2375         try:
2376             l, llen, v = len_decode(lv)
2377         except DecodeError as err:
2378             raise err.__class__(
2379                 msg=err.msg,
2380                 klass=self.__class__,
2381                 decode_path=decode_path,
2382                 offset=offset,
2383             )
2384         if l > len(v):
2385             raise NotEnoughData(
2386                 "encoded length is longer than data",
2387                 klass=self.__class__,
2388                 decode_path=decode_path,
2389                 offset=offset,
2390             )
2391         if l == 0:
2392             raise NotEnoughData(
2393                 "zero length",
2394                 klass=self.__class__,
2395                 decode_path=decode_path,
2396                 offset=offset,
2397             )
2398         v, tail = v[:l], v[l:]
2399         arcs = []
2400         while len(v) > 0:
2401             i = 0
2402             arc = 0
2403             while True:
2404                 octet = indexbytes(v, i)
2405                 arc = (arc << 7) | (octet & 0x7F)
2406                 if octet & 0x80 == 0:
2407                     arcs.append(arc)
2408                     v = v[i + 1:]
2409                     break
2410                 i += 1
2411                 if i == len(v):
2412                     raise DecodeError(
2413                         "unfinished OID",
2414                         klass=self.__class__,
2415                         decode_path=decode_path,
2416                         offset=offset,
2417                     )
2418         first_arc = 0
2419         second_arc = arcs[0]
2420         if 0 <= second_arc <= 39:
2421             first_arc = 0
2422         elif 40 <= second_arc <= 79:
2423             first_arc = 1
2424             second_arc -= 40
2425         else:
2426             first_arc = 2
2427             second_arc -= 80
2428         obj = self.__class__(
2429             value=tuple([first_arc, second_arc] + arcs[1:]),
2430             impl=self.tag,
2431             expl=self._expl,
2432             default=self.default,
2433             optional=self.optional,
2434             _decoded=(offset, llen, l),
2435         )
2436         return obj, tail
2437
2438     def __repr__(self):
2439         return pp_console_row(next(self.pps()))
2440
2441     def pps(self, decode_path=()):
2442         yield _pp(
2443             asn1_type_name=self.asn1_type_name,
2444             obj_name=self.__class__.__name__,
2445             decode_path=decode_path,
2446             value=str(self) if self.ready else None,
2447             optional=self.optional,
2448             default=self == self.default,
2449             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2450             expl=None if self._expl is None else tag_decode(self._expl),
2451             offset=self.offset,
2452             tlen=self.tlen,
2453             llen=self.llen,
2454             vlen=self.vlen,
2455             expl_offset=self.expl_offset if self.expled else None,
2456             expl_tlen=self.expl_tlen if self.expled else None,
2457             expl_llen=self.expl_llen if self.expled else None,
2458             expl_vlen=self.expl_vlen if self.expled else None,
2459         )
2460
2461
2462 class Enumerated(Integer):
2463     """``ENUMERATED`` integer type
2464
2465     This type is identical to :py:class:`pyderasn.Integer`, but requires
2466     schema to be specified and does not accept values missing from it.
2467     """
2468     __slots__ = ()
2469     tag_default = tag_encode(10)
2470     asn1_type_name = "ENUMERATED"
2471
2472     def __init__(
2473             self,
2474             value=None,
2475             impl=None,
2476             expl=None,
2477             default=None,
2478             optional=False,
2479             _specs=None,
2480             _decoded=(0, 0, 0),
2481             bounds=None,  # dummy argument, workability for Integer.decode
2482     ):
2483         super(Enumerated, self).__init__(
2484             value=value,
2485             impl=impl,
2486             expl=expl,
2487             default=default,
2488             optional=optional,
2489             _specs=_specs,
2490             _decoded=_decoded,
2491         )
2492         if len(self.specs) == 0:
2493             raise ValueError("schema must be specified")
2494
2495     def _value_sanitize(self, value):
2496         if isinstance(value, self.__class__):
2497             value = value._value
2498         elif isinstance(value, integer_types):
2499             if value not in list(self.specs.values()):
2500                 raise DecodeError(
2501                     "unknown integer value: %s" % value,
2502                     klass=self.__class__,
2503                 )
2504         elif isinstance(value, string_types):
2505             value = self.specs.get(value)
2506             if value is None:
2507                 raise ObjUnknown("integer value: %s" % value)
2508         else:
2509             raise InvalidValueType((self.__class__, int, str))
2510         return value
2511
2512     def copy(self):
2513         obj = self.__class__(_specs=self.specs)
2514         obj._value = self._value
2515         obj._bound_min = self._bound_min
2516         obj._bound_max = self._bound_max
2517         obj.tag = self.tag
2518         obj._expl = self._expl
2519         obj.default = self.default
2520         obj.optional = self.optional
2521         obj.offset = self.offset
2522         obj.llen = self.llen
2523         obj.vlen = self.vlen
2524         return obj
2525
2526     def __call__(
2527             self,
2528             value=None,
2529             impl=None,
2530             expl=None,
2531             default=None,
2532             optional=None,
2533             _specs=None,
2534     ):
2535         return self.__class__(
2536             value=value,
2537             impl=self.tag if impl is None else impl,
2538             expl=self._expl if expl is None else expl,
2539             default=self.default if default is None else default,
2540             optional=self.optional if optional is None else optional,
2541             _specs=self.specs,
2542         )
2543
2544
2545 class CommonString(OctetString):
2546     """Common class for all strings
2547
2548     Everything resembles :py:class:`pyderasn.OctetString`, except
2549     ability to deal with unicode text strings.
2550
2551     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
2552     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2553     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
2554     True
2555     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
2556     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
2557     >>> str(s)
2558     'привет Ð¼Ð¸Ñ€'
2559     >>> hexenc(bytes(s))
2560     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2561
2562     >>> PrintableString("привет Ð¼Ð¸Ñ€")
2563     Traceback (most recent call last):
2564     UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2565
2566     >>> BMPString("ада", bounds=(2, 2))
2567     Traceback (most recent call last):
2568     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2569     >>> s = BMPString("ад", bounds=(2, 2))
2570     >>> s.encoding
2571     'utf-16-be'
2572     >>> hexenc(bytes(s))
2573     '04300434'
2574
2575     .. list-table::
2576        :header-rows: 1
2577
2578        * - Class
2579          - Text Encoding
2580        * - :py:class:`pyderasn.UTF8String`
2581          - utf-8
2582        * - :py:class:`pyderasn.NumericString`
2583          - ascii
2584        * - :py:class:`pyderasn.PrintableString`
2585          - ascii
2586        * - :py:class:`pyderasn.TeletexString`
2587          - ascii
2588        * - :py:class:`pyderasn.T61String`
2589          - ascii
2590        * - :py:class:`pyderasn.VideotexString`
2591          - iso-8859-1
2592        * - :py:class:`pyderasn.IA5String`
2593          - ascii
2594        * - :py:class:`pyderasn.GraphicString`
2595          - iso-8859-1
2596        * - :py:class:`pyderasn.VisibleString`
2597          - ascii
2598        * - :py:class:`pyderasn.ISO646String`
2599          - ascii
2600        * - :py:class:`pyderasn.GeneralString`
2601          - iso-8859-1
2602        * - :py:class:`pyderasn.UniversalString`
2603          - utf-32-be
2604        * - :py:class:`pyderasn.BMPString`
2605          - utf-16-be
2606     """
2607     __slots__ = ("encoding",)
2608
2609     def _value_sanitize(self, value):
2610         value_raw = None
2611         value_decoded = None
2612         if isinstance(value, self.__class__):
2613             value_raw = value._value
2614         elif isinstance(value, text_type):
2615             value_decoded = value
2616         elif isinstance(value, binary_type):
2617             value_raw = value
2618         else:
2619             raise InvalidValueType((self.__class__, text_type, binary_type))
2620         value_raw = (
2621             value_decoded.encode(self.encoding)
2622             if value_raw is None else value_raw
2623         )
2624         value_decoded = (
2625             value_raw.decode(self.encoding)
2626             if value_decoded is None else value_decoded
2627         )
2628         if not self._bound_min <= len(value_decoded) <= self._bound_max:
2629             raise BoundsError(
2630                 self._bound_min,
2631                 len(value_decoded),
2632                 self._bound_max,
2633             )
2634         return value_raw
2635
2636     def __eq__(self, their):
2637         if isinstance(their, binary_type):
2638             return self._value == their
2639         if isinstance(their, text_type):
2640             return self._value == their.encode(self.encoding)
2641         if not isinstance(their, self.__class__):
2642             return False
2643         return (
2644             self._value == their._value and
2645             self.tag == their.tag and
2646             self._expl == their._expl
2647         )
2648
2649     def __unicode__(self):
2650         if self.ready:
2651             return self._value.decode(self.encoding)
2652         return text_type(self._value)
2653
2654     def __repr__(self):
2655         return pp_console_row(next(self.pps(no_unicode=PY2)))
2656
2657     def pps(self, decode_path=(), no_unicode=False):
2658         value = None
2659         if self.ready:
2660             value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2661         yield _pp(
2662             asn1_type_name=self.asn1_type_name,
2663             obj_name=self.__class__.__name__,
2664             decode_path=decode_path,
2665             value=value,
2666             optional=self.optional,
2667             default=self == self.default,
2668             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2669             expl=None if self._expl is None else tag_decode(self._expl),
2670             offset=self.offset,
2671             tlen=self.tlen,
2672             llen=self.llen,
2673             vlen=self.vlen,
2674         )
2675
2676
2677 class UTF8String(CommonString):
2678     __slots__ = ()
2679     tag_default = tag_encode(12)
2680     encoding = "utf-8"
2681     asn1_type_name = "UTF8String"
2682
2683
2684 class NumericString(CommonString):
2685     __slots__ = ()
2686     tag_default = tag_encode(18)
2687     encoding = "ascii"
2688     asn1_type_name = "NumericString"
2689
2690
2691 class PrintableString(CommonString):
2692     __slots__ = ()
2693     tag_default = tag_encode(19)
2694     encoding = "ascii"
2695     asn1_type_name = "PrintableString"
2696
2697
2698 class TeletexString(CommonString):
2699     __slots__ = ()
2700     tag_default = tag_encode(20)
2701     encoding = "ascii"
2702     asn1_type_name = "TeletexString"
2703
2704
2705 class T61String(TeletexString):
2706     __slots__ = ()
2707     asn1_type_name = "T61String"
2708
2709
2710 class VideotexString(CommonString):
2711     __slots__ = ()
2712     tag_default = tag_encode(21)
2713     encoding = "iso-8859-1"
2714     asn1_type_name = "VideotexString"
2715
2716
2717 class IA5String(CommonString):
2718     __slots__ = ()
2719     tag_default = tag_encode(22)
2720     encoding = "ascii"
2721     asn1_type_name = "IA5"
2722
2723
2724 class UTCTime(CommonString):
2725     """``UTCTime`` datetime type
2726
2727     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2728     UTCTime UTCTime 2017-09-30T22:07:50
2729     >>> str(t)
2730     '170930220750Z'
2731     >>> bytes(t)
2732     b'170930220750Z'
2733     >>> t.todatetime()
2734     datetime.datetime(2017, 9, 30, 22, 7, 50)
2735     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2736     datetime.datetime(1957, 9, 30, 22, 7, 50)
2737     """
2738     __slots__ = ()
2739     tag_default = tag_encode(23)
2740     encoding = "ascii"
2741     asn1_type_name = "UTCTime"
2742
2743     fmt = "%y%m%d%H%M%SZ"
2744
2745     def __init__(
2746             self,
2747             value=None,
2748             impl=None,
2749             expl=None,
2750             default=None,
2751             optional=False,
2752             _decoded=(0, 0, 0),
2753             bounds=None,  # dummy argument, workability for OctetString.decode
2754     ):
2755         """
2756         :param value: set the value. Either datetime type, or
2757                       :py:class:`pyderasn.UTCTime` object
2758         :param bytes impl: override default tag with ``IMPLICIT`` one
2759         :param bytes expl: override default tag with ``EXPLICIT`` one
2760         :param default: set default value. Type same as in ``value``
2761         :param bool optional: is object ``OPTIONAL`` in sequence
2762         """
2763         super(UTCTime, self).__init__(
2764             impl=impl,
2765             expl=expl,
2766             default=default,
2767             optional=optional,
2768             _decoded=_decoded,
2769         )
2770         self._value = value
2771         if value is not None:
2772             self._value = self._value_sanitize(value)
2773         if default is not None:
2774             default = self._value_sanitize(default)
2775             self.default = self.__class__(
2776                 value=default,
2777                 impl=self.tag,
2778                 expl=self._expl,
2779             )
2780             if self._value is None:
2781                 self._value = default
2782
2783     def _value_sanitize(self, value):
2784         if isinstance(value, self.__class__):
2785             return value._value
2786         if isinstance(value, datetime):
2787             return value.strftime(self.fmt).encode("ascii")
2788         if isinstance(value, binary_type):
2789             value_decoded = value.decode("ascii")
2790             if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2791                 try:
2792                     datetime.strptime(value_decoded, self.fmt)
2793                 except ValueError:
2794                     raise DecodeError("invalid UTCTime format")
2795                 return value
2796             else:
2797                 raise DecodeError("invalid UTCTime length")
2798         raise InvalidValueType((self.__class__, datetime))
2799
2800     def __eq__(self, their):
2801         if isinstance(their, binary_type):
2802             return self._value == their
2803         if isinstance(their, datetime):
2804             return self.todatetime() == their
2805         if not isinstance(their, self.__class__):
2806             return False
2807         return (
2808             self._value == their._value and
2809             self.tag == their.tag and
2810             self._expl == their._expl
2811         )
2812
2813     def todatetime(self):
2814         """Convert to datetime
2815
2816         :returns: datetime
2817
2818         Pay attention that UTCTime can not hold full year, so all years
2819         having < 50 years are treated as 20xx, 19xx otherwise, according
2820         to X.509 recomendation.
2821         """
2822         value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2823         year = value.year % 100
2824         return datetime(
2825             year=(2000 + year) if year < 50 else (1900 + year),
2826             month=value.month,
2827             day=value.day,
2828             hour=value.hour,
2829             minute=value.minute,
2830             second=value.second,
2831         )
2832
2833     def __repr__(self):
2834         return pp_console_row(next(self.pps()))
2835
2836     def pps(self, decode_path=()):
2837         yield _pp(
2838             asn1_type_name=self.asn1_type_name,
2839             obj_name=self.__class__.__name__,
2840             decode_path=decode_path,
2841             value=self.todatetime().isoformat() if self.ready else None,
2842             optional=self.optional,
2843             default=self == self.default,
2844             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2845             expl=None if self._expl is None else tag_decode(self._expl),
2846             offset=self.offset,
2847             tlen=self.tlen,
2848             llen=self.llen,
2849             vlen=self.vlen,
2850         )
2851
2852
2853 class GeneralizedTime(UTCTime):
2854     """``GeneralizedTime`` datetime type
2855
2856     This type is similar to :py:class:`pyderasn.UTCTime`.
2857
2858     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2859     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2860     >>> str(t)
2861     '20170930220750.000123Z'
2862     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2863     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2864     """
2865     __slots__ = ()
2866     tag_default = tag_encode(24)
2867     asn1_type_name = "GeneralizedTime"
2868
2869     fmt = "%Y%m%d%H%M%SZ"
2870     fmt_ms = "%Y%m%d%H%M%S.%fZ"
2871
2872     def _value_sanitize(self, value):
2873         if isinstance(value, self.__class__):
2874             return value._value
2875         if isinstance(value, datetime):
2876             return value.strftime(
2877                 self.fmt_ms if value.microsecond > 0 else self.fmt
2878             ).encode("ascii")
2879         if isinstance(value, binary_type):
2880             value_decoded = value.decode("ascii")
2881             if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2882                 try:
2883                     datetime.strptime(value_decoded, self.fmt)
2884                 except ValueError:
2885                     raise DecodeError(
2886                         "invalid GeneralizedTime (without ms) format",
2887                     )
2888                 return value
2889             elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2890                 try:
2891                     datetime.strptime(value_decoded, self.fmt_ms)
2892                 except ValueError:
2893                     raise DecodeError(
2894                         "invalid GeneralizedTime (with ms) format",
2895                     )
2896                 return value
2897             else:
2898                 raise DecodeError(
2899                     "invalid GeneralizedTime length",
2900                     klass=self.__class__,
2901                 )
2902         raise InvalidValueType((self.__class__, datetime))
2903
2904     def todatetime(self):
2905         value = self._value.decode("ascii")
2906         if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2907             return datetime.strptime(value, self.fmt)
2908         return datetime.strptime(value, self.fmt_ms)
2909
2910
2911 class GraphicString(CommonString):
2912     __slots__ = ()
2913     tag_default = tag_encode(25)
2914     encoding = "iso-8859-1"
2915     asn1_type_name = "GraphicString"
2916
2917
2918 class VisibleString(CommonString):
2919     __slots__ = ()
2920     tag_default = tag_encode(26)
2921     encoding = "ascii"
2922     asn1_type_name = "VisibleString"
2923
2924
2925 class ISO646String(VisibleString):
2926     __slots__ = ()
2927     asn1_type_name = "ISO646String"
2928
2929
2930 class GeneralString(CommonString):
2931     __slots__ = ()
2932     tag_default = tag_encode(27)
2933     encoding = "iso-8859-1"
2934     asn1_type_name = "GeneralString"
2935
2936
2937 class UniversalString(CommonString):
2938     __slots__ = ()
2939     tag_default = tag_encode(28)
2940     encoding = "utf-32-be"
2941     asn1_type_name = "UniversalString"
2942
2943
2944 class BMPString(CommonString):
2945     __slots__ = ()
2946     tag_default = tag_encode(30)
2947     encoding = "utf-16-be"
2948     asn1_type_name = "BMPString"
2949
2950
2951 class Choice(Obj):
2952     """``CHOICE`` special type
2953
2954     ::
2955
2956         class GeneralName(Choice):
2957             schema = (
2958                 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2959                 ('dNSName', IA5String(impl=tag_ctxp(2))),
2960             )
2961
2962     >>> gn = GeneralName()
2963     GeneralName CHOICE
2964     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2965     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2966     >>> gn["dNSName"] = IA5String("bar.baz")
2967     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2968     >>> gn["rfc822Name"]
2969     None
2970     >>> gn["dNSName"]
2971     [2] IA5String IA5 bar.baz
2972     >>> gn.choice
2973     'dNSName'
2974     >>> gn.value == gn["dNSName"]
2975     True
2976     >>> gn.specs
2977     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2978
2979     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2980     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2981     """
2982     __slots__ = ("specs",)
2983     tag_default = None
2984     asn1_type_name = "CHOICE"
2985
2986     def __init__(
2987             self,
2988             value=None,
2989             schema=None,
2990             impl=None,
2991             expl=None,
2992             default=None,
2993             optional=False,
2994             _decoded=(0, 0, 0),
2995     ):
2996         """
2997         :param value: set the value. Either ``(choice, value)`` tuple, or
2998                       :py:class:`pyderasn.Choice` object
2999         :param bytes impl: can not be set, do **not** use it
3000         :param bytes expl: override default tag with ``EXPLICIT`` one
3001         :param default: set default value. Type same as in ``value``
3002         :param bool optional: is object ``OPTIONAL`` in sequence
3003         """
3004         if impl is not None:
3005             raise ValueError("no implicit tag allowed for CHOICE")
3006         super(Choice, self).__init__(None, expl, default, optional, _decoded)
3007         if schema is None:
3008             schema = getattr(self, "schema", ())
3009         if len(schema) == 0:
3010             raise ValueError("schema must be specified")
3011         self.specs = (
3012             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3013         )
3014         self._value = None
3015         if value is not None:
3016             self._value = self._value_sanitize(value)
3017         if default is not None:
3018             default_value = self._value_sanitize(default)
3019             default_obj = self.__class__(impl=self.tag, expl=self._expl)
3020             default_obj.specs = self.specs
3021             default_obj._value = default_value
3022             self.default = default_obj
3023             if value is None:
3024                 self._value = default_obj.copy()._value
3025
3026     def _value_sanitize(self, value):
3027         if isinstance(value, self.__class__):
3028             return value._value
3029         if isinstance(value, tuple) and len(value) == 2:
3030             choice, obj = value
3031             spec = self.specs.get(choice)
3032             if spec is None:
3033                 raise ObjUnknown(choice)
3034             if not isinstance(obj, spec.__class__):
3035                 raise InvalidValueType((spec,))
3036             return (choice, spec(obj))
3037         raise InvalidValueType((self.__class__, tuple))
3038
3039     @property
3040     def ready(self):
3041         return self._value is not None and self._value[1].ready
3042
3043     def copy(self):
3044         obj = self.__class__(schema=self.specs)
3045         obj._expl = self._expl
3046         obj.default = self.default
3047         obj.optional = self.optional
3048         obj.offset = self.offset
3049         obj.llen = self.llen
3050         obj.vlen = self.vlen
3051         value = self._value
3052         if value is not None:
3053             obj._value = (value[0], value[1].copy())
3054         return obj
3055
3056     def __eq__(self, their):
3057         if isinstance(their, tuple) and len(their) == 2:
3058             return self._value == their
3059         if not isinstance(their, self.__class__):
3060             return False
3061         return (
3062             self.specs == their.specs and
3063             self._value == their._value
3064         )
3065
3066     def __call__(
3067             self,
3068             value=None,
3069             expl=None,
3070             default=None,
3071             optional=None,
3072     ):
3073         return self.__class__(
3074             value=value,
3075             schema=self.specs,
3076             expl=self._expl if expl is None else expl,
3077             default=self.default if default is None else default,
3078             optional=self.optional if optional is None else optional,
3079         )
3080
3081     @property
3082     def choice(self):
3083         self._assert_ready()
3084         return self._value[0]
3085
3086     @property
3087     def value(self):
3088         self._assert_ready()
3089         return self._value[1]
3090
3091     def __getitem__(self, key):
3092         if key not in self.specs:
3093             raise ObjUnknown(key)
3094         if self._value is None:
3095             return None
3096         choice, value = self._value
3097         if choice != key:
3098             return None
3099         return value
3100
3101     def __setitem__(self, key, value):
3102         spec = self.specs.get(key)
3103         if spec is None:
3104             raise ObjUnknown(key)
3105         if not isinstance(value, spec.__class__):
3106             raise InvalidValueType((spec.__class__,))
3107         self._value = (key, spec(value))
3108
3109     @property
3110     def tlen(self):
3111         return 0
3112
3113     @property
3114     def decoded(self):
3115         return self._value[1].decoded if self.ready else False
3116
3117     def _encode(self):
3118         self._assert_ready()
3119         return self._value[1].encode()
3120
3121     def _decode(self, tlv, offset=0, decode_path=()):
3122         for choice, spec in self.specs.items():
3123             try:
3124                 value, tail = spec.decode(
3125                     tlv,
3126                     offset=offset,
3127                     leavemm=True,
3128                     decode_path=decode_path + (choice,),
3129                 )
3130             except TagMismatch:
3131                 continue
3132             obj = self.__class__(
3133                 schema=self.specs,
3134                 expl=self._expl,
3135                 default=self.default,
3136                 optional=self.optional,
3137                 _decoded=(offset, 0, value.tlvlen),
3138             )
3139             obj._value = (choice, value)
3140             return obj, tail
3141         raise TagMismatch(
3142             klass=self.__class__,
3143             decode_path=decode_path,
3144             offset=offset,
3145         )
3146
3147     def __repr__(self):
3148         value = pp_console_row(next(self.pps()))
3149         if self.ready:
3150             value = "%s[%r]" % (value, self.value)
3151         return value
3152
3153     def pps(self, decode_path=()):
3154         yield _pp(
3155             asn1_type_name=self.asn1_type_name,
3156             obj_name=self.__class__.__name__,
3157             decode_path=decode_path,
3158             value=self.choice if self.ready else None,
3159             optional=self.optional,
3160             default=self == self.default,
3161             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3162             expl=None if self._expl is None else tag_decode(self._expl),
3163             offset=self.offset,
3164             tlen=self.tlen,
3165             llen=self.llen,
3166             vlen=self.vlen,
3167         )
3168         if self.ready:
3169             yield self.value.pps(decode_path=decode_path + (self.choice,))
3170
3171
3172 class PrimitiveTypes(Choice):
3173     """Predefined ``CHOICE`` for all generic primitive types
3174
3175     It could be useful for general decoding of some unspecified values:
3176
3177     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3178     OCTET STRING 3 bytes 666f6f
3179     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3180     INTEGER 1193046
3181     """
3182     __slots__ = ()
3183     schema = tuple((klass.__name__, klass()) for klass in (
3184         Boolean,
3185         Integer,
3186         BitString,
3187         OctetString,
3188         Null,
3189         ObjectIdentifier,
3190         UTF8String,
3191         NumericString,
3192         PrintableString,
3193         TeletexString,
3194         VideotexString,
3195         IA5String,
3196         UTCTime,
3197         GeneralizedTime,
3198         GraphicString,
3199         VisibleString,
3200         ISO646String,
3201         GeneralString,
3202         UniversalString,
3203         BMPString,
3204     ))
3205
3206
3207 class Any(Obj):
3208     """``ANY`` special type
3209
3210     >>> Any(Integer(-123))
3211     ANY 020185
3212     >>> a = Any(OctetString(b"hello world").encode())
3213     ANY 040b68656c6c6f20776f726c64
3214     >>> hexenc(bytes(a))
3215     b'0x040x0bhello world'
3216     """
3217     __slots__ = ()
3218     tag_default = tag_encode(0)
3219     asn1_type_name = "ANY"
3220
3221     def __init__(
3222             self,
3223             value=None,
3224             expl=None,
3225             optional=False,
3226             _decoded=(0, 0, 0),
3227     ):
3228         """
3229         :param value: set the value. Either any kind of pyderasn's
3230                       **ready** object, or bytes. Pay attention that
3231                       **no** validation is performed is raw binary value
3232                       is valid TLV
3233         :param bytes expl: override default tag with ``EXPLICIT`` one
3234         :param bool optional: is object ``OPTIONAL`` in sequence
3235         """
3236         super(Any, self).__init__(None, expl, None, optional, _decoded)
3237         self._value = None if value is None else self._value_sanitize(value)
3238
3239     def _value_sanitize(self, value):
3240         if isinstance(value, self.__class__):
3241             return value._value
3242         if isinstance(value, Obj):
3243             return value.encode()
3244         if isinstance(value, binary_type):
3245             return value
3246         raise InvalidValueType((self.__class__, Obj, binary_type))
3247
3248     @property
3249     def ready(self):
3250         return self._value is not None
3251
3252     def copy(self):
3253         obj = self.__class__()
3254         obj._value = self._value
3255         obj.tag = self.tag
3256         obj._expl = self._expl
3257         obj.optional = self.optional
3258         obj.offset = self.offset
3259         obj.llen = self.llen
3260         obj.vlen = self.vlen
3261         return obj
3262
3263     def __eq__(self, their):
3264         if isinstance(their, binary_type):
3265             return self._value == their
3266         if issubclass(their.__class__, Any):
3267             return self._value == their._value
3268         return False
3269
3270     def __call__(
3271             self,
3272             value=None,
3273             expl=None,
3274             optional=None,
3275     ):
3276         return self.__class__(
3277             value=value,
3278             expl=self._expl if expl is None else expl,
3279             optional=self.optional if optional is None else optional,
3280         )
3281
3282     def __bytes__(self):
3283         self._assert_ready()
3284         return self._value
3285
3286     @property
3287     def tlen(self):
3288         return 0
3289
3290     def _encode(self):
3291         self._assert_ready()
3292         return self._value
3293
3294     def _decode(self, tlv, offset=0, decode_path=()):
3295         try:
3296             t, tlen, lv = tag_strip(tlv)
3297             l, llen, v = len_decode(lv)
3298         except DecodeError as err:
3299             raise err.__class__(
3300                 msg=err.msg,
3301                 klass=self.__class__,
3302                 decode_path=decode_path,
3303                 offset=offset,
3304             )
3305         if l > len(v):
3306             raise NotEnoughData(
3307                 "encoded length is longer than data",
3308                 klass=self.__class__,
3309                 decode_path=decode_path,
3310                 offset=offset,
3311             )
3312         tlvlen = tlen + llen + l
3313         v, tail = tlv[:tlvlen], v[l:]
3314         obj = self.__class__(
3315             value=v.tobytes(),
3316             expl=self._expl,
3317             optional=self.optional,
3318             _decoded=(offset, 0, tlvlen),
3319         )
3320         obj.tag = t
3321         return obj, tail
3322
3323     def __repr__(self):
3324         return pp_console_row(next(self.pps()))
3325
3326     def pps(self, decode_path=()):
3327         yield _pp(
3328             asn1_type_name=self.asn1_type_name,
3329             obj_name=self.__class__.__name__,
3330             decode_path=decode_path,
3331             blob=self._value if self.ready else None,
3332             optional=self.optional,
3333             default=self == self.default,
3334             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3335             expl=None if self._expl is None else tag_decode(self._expl),
3336             offset=self.offset,
3337             tlen=self.tlen,
3338             llen=self.llen,
3339             vlen=self.vlen,
3340             expl_offset=self.expl_offset if self.expled else None,
3341             expl_tlen=self.expl_tlen if self.expled else None,
3342             expl_llen=self.expl_llen if self.expled else None,
3343             expl_vlen=self.expl_vlen if self.expled else None,
3344         )
3345
3346
3347 ########################################################################
3348 # ASN.1 constructed types
3349 ########################################################################
3350
3351 class Sequence(Obj):
3352     """``SEQUENCE`` structure type
3353
3354     You have to make specification of sequence::
3355
3356         class Extension(Sequence):
3357             __slots__ = ()
3358             schema = (
3359                 ("extnID", ObjectIdentifier()),
3360                 ("critical", Boolean(default=False)),
3361                 ("extnValue", OctetString()),
3362             )
3363
3364     Then, you can work with it as with dictionary.
3365
3366     >>> ext = Extension()
3367     >>> Extension().specs
3368     OrderedDict([
3369         ('extnID', OBJECT IDENTIFIER),
3370         ('critical', BOOLEAN False OPTIONAL DEFAULT),
3371         ('extnValue', OCTET STRING),
3372     ])
3373     >>> ext["extnID"] = "1.2.3"
3374     Traceback (most recent call last):
3375     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3376     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3377
3378     You can know if sequence is ready to be encoded:
3379
3380     >>> ext.ready
3381     False
3382     >>> ext.encode()
3383     Traceback (most recent call last):
3384     pyderasn.ObjNotReady: object is not ready: extnValue
3385     >>> ext["extnValue"] = OctetString(b"foobar")
3386     >>> ext.ready
3387     True
3388
3389     Value you want to assign, must have the same **type** as in
3390     corresponding specification, but it can have different tags,
3391     optional/default attributes -- they will be taken from specification
3392     automatically::
3393
3394         class TBSCertificate(Sequence):
3395             schema = (
3396                 ("version", Version(expl=tag_ctxc(0), default="v1")),
3397             [...]
3398
3399     >>> tbs = TBSCertificate()
3400     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3401
3402     You can know if value exists/set in the sequence and take its value:
3403
3404     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3405     (True, True, False)
3406     >>> ext["extnID"]
3407     OBJECT IDENTIFIER 1.2.3
3408
3409     But pay attention that if value has default, then it won't be (not
3410     in) in the sequence (because ``DEFAULT`` must not be encoded in
3411     DER), but you can read its value:
3412
3413     >>> "critical" in ext, ext["critical"]
3414     (False, BOOLEAN False)
3415     >>> ext["critical"] = Boolean(True)
3416     >>> "critical" in ext, ext["critical"]
3417     (True, BOOLEAN True)
3418
3419     All defaulted values are always optional.
3420
3421     .. warning::
3422
3423        When decoded DER contains defaulted value inside, then
3424        technically this is not valid DER encoding. But we allow
3425        and pass it. Of course reencoding of that kind of DER will
3426        result in different binary representation (validly without
3427        defaulted value inside).
3428
3429     Two sequences are equal if they have equal specification (schema),
3430     implicit/explicit tagging and the same values.
3431     """
3432     __slots__ = ("specs",)
3433     tag_default = tag_encode(form=TagFormConstructed, num=16)
3434     asn1_type_name = "SEQUENCE"
3435
3436     def __init__(
3437             self,
3438             value=None,
3439             schema=None,
3440             impl=None,
3441             expl=None,
3442             default=None,
3443             optional=False,
3444             _decoded=(0, 0, 0),
3445     ):
3446         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3447         if schema is None:
3448             schema = getattr(self, "schema", ())
3449         self.specs = (
3450             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3451         )
3452         self._value = {}
3453         if value is not None:
3454             self._value = self._value_sanitize(value)
3455         if default is not None:
3456             default_value = self._value_sanitize(default)
3457             default_obj = self.__class__(impl=self.tag, expl=self._expl)
3458             default_obj.specs = self.specs
3459             default_obj._value = default_value
3460             self.default = default_obj
3461             if value is None:
3462                 self._value = default_obj.copy()._value
3463
3464     def _value_sanitize(self, value):
3465         if not issubclass(value.__class__, Sequence):
3466             raise InvalidValueType((Sequence,))
3467         return value._value
3468
3469     @property
3470     def ready(self):
3471         for name, spec in self.specs.items():
3472             value = self._value.get(name)
3473             if value is None:
3474                 if spec.optional:
3475                     continue
3476                 return False
3477             else:
3478                 if not value.ready:
3479                     return False
3480         return True
3481
3482     def copy(self):
3483         obj = self.__class__(schema=self.specs)
3484         obj.tag = self.tag
3485         obj._expl = self._expl
3486         obj.default = self.default
3487         obj.optional = self.optional
3488         obj.offset = self.offset
3489         obj.llen = self.llen
3490         obj.vlen = self.vlen
3491         obj._value = {k: v.copy() for k, v in self._value.items()}
3492         return obj
3493
3494     def __eq__(self, their):
3495         if not isinstance(their, self.__class__):
3496             return False
3497         return (
3498             self.specs == their.specs and
3499             self.tag == their.tag and
3500             self._expl == their._expl and
3501             self._value == their._value
3502         )
3503
3504     def __call__(
3505             self,
3506             value=None,
3507             impl=None,
3508             expl=None,
3509             default=None,
3510             optional=None,
3511     ):
3512         return self.__class__(
3513             value=value,
3514             schema=self.specs,
3515             impl=self.tag if impl is None else impl,
3516             expl=self._expl if expl is None else expl,
3517             default=self.default if default is None else default,
3518             optional=self.optional if optional is None else optional,
3519         )
3520
3521     def __contains__(self, key):
3522         return key in self._value
3523
3524     def __setitem__(self, key, value):
3525         spec = self.specs.get(key)
3526         if spec is None:
3527             raise ObjUnknown(key)
3528         if value is None:
3529             self._value.pop(key, None)
3530             return
3531         if not isinstance(value, spec.__class__):
3532             raise InvalidValueType((spec.__class__,))
3533         value = spec(value=value)
3534         if spec.default is not None and value == spec.default:
3535             self._value.pop(key, None)
3536             return
3537         self._value[key] = value
3538
3539     def __getitem__(self, key):
3540         value = self._value.get(key)
3541         if value is not None:
3542             return value
3543         spec = self.specs.get(key)
3544         if spec is None:
3545             raise ObjUnknown(key)
3546         if spec.default is not None:
3547             return spec.default
3548         return None
3549
3550     def _encoded_values(self):
3551         raws = []
3552         for name, spec in self.specs.items():
3553             value = self._value.get(name)
3554             if value is None:
3555                 if spec.optional:
3556                     continue
3557                 raise ObjNotReady(name)
3558             raws.append(value.encode())
3559         return raws
3560
3561     def _encode(self):
3562         v = b"".join(self._encoded_values())
3563         return b"".join((self.tag, len_encode(len(v)), v))
3564
3565     def _decode(self, tlv, offset=0, decode_path=()):
3566         try:
3567             t, tlen, lv = tag_strip(tlv)
3568         except DecodeError as err:
3569             raise err.__class__(
3570                 msg=err.msg,
3571                 klass=self.__class__,
3572                 decode_path=decode_path,
3573                 offset=offset,
3574             )
3575         if t != self.tag:
3576             raise TagMismatch(
3577                 klass=self.__class__,
3578                 decode_path=decode_path,
3579                 offset=offset,
3580             )
3581         try:
3582             l, llen, v = len_decode(lv)
3583         except DecodeError as err:
3584             raise err.__class__(
3585                 msg=err.msg,
3586                 klass=self.__class__,
3587                 decode_path=decode_path,
3588                 offset=offset,
3589             )
3590         if l > len(v):
3591             raise NotEnoughData(
3592                 "encoded length is longer than data",
3593                 klass=self.__class__,
3594                 decode_path=decode_path,
3595                 offset=offset,
3596             )
3597         v, tail = v[:l], v[l:]
3598         sub_offset = offset + tlen + llen
3599         values = {}
3600         for name, spec in self.specs.items():
3601             if len(v) == 0 and spec.optional:
3602                 continue
3603             try:
3604                 value, v_tail = spec.decode(
3605                     v,
3606                     sub_offset,
3607                     leavemm=True,
3608                     decode_path=decode_path + (name,),
3609                 )
3610             except TagMismatch:
3611                 if spec.optional:
3612                     continue
3613                 raise
3614             sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3615             v = v_tail
3616             if spec.default is not None and value == spec.default:
3617                 # Encoded default values are not valid in DER,
3618                 # but we still allow that
3619                 continue
3620             values[name] = value
3621         if len(v) > 0:
3622             raise DecodeError(
3623                 "remaining data",
3624                 klass=self.__class__,
3625                 decode_path=decode_path,
3626                 offset=offset,
3627             )
3628         obj = self.__class__(
3629             schema=self.specs,
3630             impl=self.tag,
3631             expl=self._expl,
3632             default=self.default,
3633             optional=self.optional,
3634             _decoded=(offset, llen, l),
3635         )
3636         obj._value = values
3637         return obj, tail
3638
3639     def __repr__(self):
3640         value = pp_console_row(next(self.pps()))
3641         cols = []
3642         for name in self.specs:
3643             _value = self._value.get(name)
3644             if _value is None:
3645                 continue
3646             cols.append(repr(_value))
3647         return "%s[%s]" % (value, ", ".join(cols))
3648
3649     def pps(self, decode_path=()):
3650         yield _pp(
3651             asn1_type_name=self.asn1_type_name,
3652             obj_name=self.__class__.__name__,
3653             decode_path=decode_path,
3654             optional=self.optional,
3655             default=self == self.default,
3656             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3657             expl=None if self._expl is None else tag_decode(self._expl),
3658             offset=self.offset,
3659             tlen=self.tlen,
3660             llen=self.llen,
3661             vlen=self.vlen,
3662             expl_offset=self.expl_offset if self.expled else None,
3663             expl_tlen=self.expl_tlen if self.expled else None,
3664             expl_llen=self.expl_llen if self.expled else None,
3665             expl_vlen=self.expl_vlen if self.expled else None,
3666         )
3667         for name in self.specs:
3668             value = self._value.get(name)
3669             if value is None:
3670                 continue
3671             yield value.pps(decode_path=decode_path + (name,))
3672
3673
3674 class Set(Sequence):
3675     """``SET`` structure type
3676
3677     Its usage is identical to :py:class:`pyderasn.Sequence`.
3678     """
3679     __slots__ = ()
3680     tag_default = tag_encode(form=TagFormConstructed, num=17)
3681     asn1_type_name = "SET"
3682
3683     def _encode(self):
3684         raws = self._encoded_values()
3685         raws.sort()
3686         v = b"".join(raws)
3687         return b"".join((self.tag, len_encode(len(v)), v))
3688
3689     def _decode(self, tlv, offset=0, decode_path=()):
3690         try:
3691             t, tlen, lv = tag_strip(tlv)
3692         except DecodeError as err:
3693             raise err.__class__(
3694                 msg=err.msg,
3695                 klass=self.__class__,
3696                 decode_path=decode_path,
3697                 offset=offset,
3698             )
3699         if t != self.tag:
3700             raise TagMismatch(
3701                 klass=self.__class__,
3702                 decode_path=decode_path,
3703                 offset=offset,
3704             )
3705         try:
3706             l, llen, v = len_decode(lv)
3707         except DecodeError as err:
3708             raise err.__class__(
3709                 msg=err.msg,
3710                 klass=self.__class__,
3711                 decode_path=decode_path,
3712                 offset=offset,
3713             )
3714         if l > len(v):
3715             raise NotEnoughData(
3716                 "encoded length is longer than data",
3717                 klass=self.__class__,
3718                 offset=offset,
3719             )
3720         v, tail = v[:l], v[l:]
3721         sub_offset = offset + tlen + llen
3722         values = {}
3723         specs_items = self.specs.items
3724         while len(v) > 0:
3725             for name, spec in specs_items():
3726                 try:
3727                     value, v_tail = spec.decode(
3728                         v,
3729                         sub_offset,
3730                         leavemm=True,
3731                         decode_path=decode_path + (name,),
3732                     )
3733                 except TagMismatch:
3734                     continue
3735                 sub_offset += (
3736                     value.expl_tlvlen if value.expled else value.tlvlen
3737                 )
3738                 v = v_tail
3739                 if spec.default is None or value != spec.default:  # pragma: no cover
3740                     # SeqMixing.test_encoded_default_accepted covers that place
3741                     values[name] = value
3742                 break
3743             else:
3744                 raise TagMismatch(
3745                     klass=self.__class__,
3746                     decode_path=decode_path,
3747                     offset=offset,
3748                 )
3749         obj = self.__class__(
3750             schema=self.specs,
3751             impl=self.tag,
3752             expl=self._expl,
3753             default=self.default,
3754             optional=self.optional,
3755             _decoded=(offset, llen, l),
3756         )
3757         obj._value = values
3758         return obj, tail
3759
3760
3761 class SequenceOf(Obj):
3762     """``SEQUENCE OF`` sequence type
3763
3764     For that kind of type you must specify the object it will carry on
3765     (bounds are for example here, not required)::
3766
3767         class Ints(SequenceOf):
3768             schema = Integer()
3769             bounds = (0, 2)
3770
3771     >>> ints = Ints()
3772     >>> ints.append(Integer(123))
3773     >>> ints.append(Integer(234))
3774     >>> ints
3775     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3776     >>> [int(i) for i in ints]
3777     [123, 234]
3778     >>> ints.append(Integer(345))
3779     Traceback (most recent call last):
3780     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3781     >>> ints[1]
3782     INTEGER 234
3783     >>> ints[1] = Integer(345)
3784     >>> ints
3785     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3786
3787     Also you can initialize sequence with preinitialized values:
3788
3789     >>> ints = Ints([Integer(123), Integer(234)])
3790     """
3791     __slots__ = ("spec", "_bound_min", "_bound_max")
3792     tag_default = tag_encode(form=TagFormConstructed, num=16)
3793     asn1_type_name = "SEQUENCE OF"
3794
3795     def __init__(
3796             self,
3797             value=None,
3798             schema=None,
3799             bounds=None,
3800             impl=None,
3801             expl=None,
3802             default=None,
3803             optional=False,
3804             _decoded=(0, 0, 0),
3805     ):
3806         super(SequenceOf, self).__init__(
3807             impl,
3808             expl,
3809             default,
3810             optional,
3811             _decoded,
3812         )
3813         if schema is None:
3814             schema = getattr(self, "schema", None)
3815         if schema is None:
3816             raise ValueError("schema must be specified")
3817         self.spec = schema
3818         self._bound_min, self._bound_max = getattr(
3819             self,
3820             "bounds",
3821             (0, float("+inf")),
3822         ) if bounds is None else bounds
3823         self._value = []
3824         if value is not None:
3825             self._value = self._value_sanitize(value)
3826         if default is not None:
3827             default_value = self._value_sanitize(default)
3828             default_obj = self.__class__(
3829                 schema=schema,
3830                 impl=self.tag,
3831                 expl=self._expl,
3832             )
3833             default_obj._value = default_value
3834             self.default = default_obj
3835             if value is None:
3836                 self._value = default_obj.copy()._value
3837
3838     def _value_sanitize(self, value):
3839         if issubclass(value.__class__, SequenceOf):
3840             value = value._value
3841         elif hasattr(value, "__iter__"):
3842             value = list(value)
3843         else:
3844             raise InvalidValueType((self.__class__, iter))
3845         if not self._bound_min <= len(value) <= self._bound_max:
3846             raise BoundsError(self._bound_min, len(value), self._bound_max)
3847         for v in value:
3848             if not isinstance(v, self.spec.__class__):
3849                 raise InvalidValueType((self.spec.__class__,))
3850         return value
3851
3852     @property
3853     def ready(self):
3854         return all(v.ready for v in self._value)
3855
3856     def copy(self):
3857         obj = self.__class__(schema=self.spec)
3858         obj._bound_min = self._bound_min
3859         obj._bound_max = self._bound_max
3860         obj.tag = self.tag
3861         obj._expl = self._expl
3862         obj.default = self.default
3863         obj.optional = self.optional
3864         obj.offset = self.offset
3865         obj.llen = self.llen
3866         obj.vlen = self.vlen
3867         obj._value = [v.copy() for v in self._value]
3868         return obj
3869
3870     def __eq__(self, their):
3871         if isinstance(their, self.__class__):
3872             return (
3873                 self.spec == their.spec and
3874                 self.tag == their.tag and
3875                 self._expl == their._expl and
3876                 self._value == their._value
3877             )
3878         if hasattr(their, "__iter__"):
3879             return self._value == list(their)
3880         return False
3881
3882     def __call__(
3883             self,
3884             value=None,
3885             bounds=None,
3886             impl=None,
3887             expl=None,
3888             default=None,
3889             optional=None,
3890     ):
3891         return self.__class__(
3892             value=value,
3893             schema=self.spec,
3894             bounds=(
3895                 (self._bound_min, self._bound_max)
3896                 if bounds is None else bounds
3897             ),
3898             impl=self.tag if impl is None else impl,
3899             expl=self._expl if expl is None else expl,
3900             default=self.default if default is None else default,
3901             optional=self.optional if optional is None else optional,
3902         )
3903
3904     def __contains__(self, key):
3905         return key in self._value
3906
3907     def append(self, value):
3908         if not isinstance(value, self.spec.__class__):
3909             raise InvalidValueType((self.spec.__class__,))
3910         if len(self._value) + 1 > self._bound_max:
3911             raise BoundsError(
3912                 self._bound_min,
3913                 len(self._value) + 1,
3914                 self._bound_max,
3915             )
3916         self._value.append(value)
3917
3918     def __iter__(self):
3919         self._assert_ready()
3920         return iter(self._value)
3921
3922     def __len__(self):
3923         self._assert_ready()
3924         return len(self._value)
3925
3926     def __setitem__(self, key, value):
3927         if not isinstance(value, self.spec.__class__):
3928             raise InvalidValueType((self.spec.__class__,))
3929         self._value[key] = self.spec(value=value)
3930
3931     def __getitem__(self, key):
3932         return self._value[key]
3933
3934     def _encoded_values(self):
3935         return [v.encode() for v in self._value]
3936
3937     def _encode(self):
3938         v = b"".join(self._encoded_values())
3939         return b"".join((self.tag, len_encode(len(v)), v))
3940
3941     def _decode(self, tlv, offset=0, decode_path=()):
3942         try:
3943             t, tlen, lv = tag_strip(tlv)
3944         except DecodeError as err:
3945             raise err.__class__(
3946                 msg=err.msg,
3947                 klass=self.__class__,
3948                 decode_path=decode_path,
3949                 offset=offset,
3950             )
3951         if t != self.tag:
3952             raise TagMismatch(
3953                 klass=self.__class__,
3954                 decode_path=decode_path,
3955                 offset=offset,
3956             )
3957         try:
3958             l, llen, v = len_decode(lv)
3959         except DecodeError as err:
3960             raise err.__class__(
3961                 msg=err.msg,
3962                 klass=self.__class__,
3963                 decode_path=decode_path,
3964                 offset=offset,
3965             )
3966         if l > len(v):
3967             raise NotEnoughData(
3968                 "encoded length is longer than data",
3969                 klass=self.__class__,
3970                 decode_path=decode_path,
3971                 offset=offset,
3972             )
3973         v, tail = v[:l], v[l:]
3974         sub_offset = offset + tlen + llen
3975         _value = []
3976         spec = self.spec
3977         while len(v) > 0:
3978             value, v_tail = spec.decode(
3979                 v,
3980                 sub_offset,
3981                 leavemm=True,
3982                 decode_path=decode_path + (str(len(_value)),),
3983             )
3984             sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3985             v = v_tail
3986             _value.append(value)
3987         obj = self.__class__(
3988             value=_value,
3989             schema=spec,
3990             bounds=(self._bound_min, self._bound_max),
3991             impl=self.tag,
3992             expl=self._expl,
3993             default=self.default,
3994             optional=self.optional,
3995             _decoded=(offset, llen, l),
3996         )
3997         return obj, tail
3998
3999     def __repr__(self):
4000         return "%s[%s]" % (
4001             pp_console_row(next(self.pps())),
4002             ", ".join(repr(v) for v in self._value),
4003         )
4004
4005     def pps(self, decode_path=()):
4006         yield _pp(
4007             asn1_type_name=self.asn1_type_name,
4008             obj_name=self.__class__.__name__,
4009             decode_path=decode_path,
4010             optional=self.optional,
4011             default=self == self.default,
4012             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4013             expl=None if self._expl is None else tag_decode(self._expl),
4014             offset=self.offset,
4015             tlen=self.tlen,
4016             llen=self.llen,
4017             vlen=self.vlen,
4018             expl_offset=self.expl_offset if self.expled else None,
4019             expl_tlen=self.expl_tlen if self.expled else None,
4020             expl_llen=self.expl_llen if self.expled else None,
4021             expl_vlen=self.expl_vlen if self.expled else None,
4022         )
4023         for i, value in enumerate(self._value):
4024             yield value.pps(decode_path=decode_path + (str(i),))
4025
4026
4027 class SetOf(SequenceOf):
4028     """``SET OF`` sequence type
4029
4030     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4031     """
4032     __slots__ = ()
4033     tag_default = tag_encode(form=TagFormConstructed, num=17)
4034     asn1_type_name = "SET OF"
4035
4036     def _encode(self):
4037         raws = self._encoded_values()
4038         raws.sort()
4039         v = b"".join(raws)
4040         return b"".join((self.tag, len_encode(len(v)), v))
4041
4042
4043 def obj_by_path(pypath):  # pragma: no cover
4044     """Import object specified as string Python path
4045
4046     Modules must be separated from classes/functions with ``:``.
4047
4048     >>> obj_by_path("foo.bar:Baz")
4049     <class 'foo.bar.Baz'>
4050     >>> obj_by_path("foo.bar:Baz.boo")
4051     <classmethod 'foo.bar.Baz.boo'>
4052     """
4053     mod, objs = pypath.rsplit(":", 1)
4054     from importlib import import_module
4055     obj = import_module(mod)
4056     for obj_name in objs.split("."):
4057         obj = getattr(obj, obj_name)
4058     return obj
4059
4060
4061 def main():  # pragma: no cover
4062     import argparse
4063     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4064     parser.add_argument(
4065         "--oids",
4066         help="Python path to dictionary with OIDs",
4067     )
4068     parser.add_argument(
4069         "--schema",
4070         help="Python path to schema definition to use",
4071     )
4072     parser.add_argument(
4073         "DERFile",
4074         type=argparse.FileType("rb"),
4075         help="Path to DER file you want to decode",
4076     )
4077     args = parser.parse_args()
4078     der = memoryview(args.DERFile.read())
4079     args.DERFile.close()
4080     oids = obj_by_path(args.oids) if args.oids else {}
4081     if args.schema:
4082         schema = obj_by_path(args.schema)
4083         from functools import partial
4084         pprinter = partial(pprint, big_blobs=True)
4085     else:
4086         # All of this below is a big hack with self references
4087         choice = PrimitiveTypes()
4088         choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4089         choice.specs["SetOf"] = SetOf(schema=choice)
4090         for i in range(31):
4091             choice.specs["SequenceOf%d" % i] = SequenceOf(
4092                 schema=choice,
4093                 expl=tag_ctxc(i),
4094             )
4095         choice.specs["Any"] = Any()
4096
4097         # Class name equals to type name, to omit it from output
4098         class SEQUENCEOF(SequenceOf):
4099             __slots__ = ()
4100             schema = choice
4101         schema = SEQUENCEOF()
4102
4103         def pprint_any(obj, oids=None):
4104             def _pprint_pps(pps):
4105                 for pp in pps:
4106                     if hasattr(pp, "_fields"):
4107                         if pp.asn1_type_name == Choice.asn1_type_name:
4108                             continue
4109                         pp_kwargs = pp._asdict()
4110                         pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4111                         pp = _pp(**pp_kwargs)
4112                         yield pp_console_row(
4113                             pp,
4114                             oids=oids,
4115                             with_offsets=True,
4116                             with_blob=False,
4117                         )
4118                         for row in pp_console_blob(pp):
4119                             yield row
4120                     else:
4121                         for row in _pprint_pps(pp):
4122                             yield row
4123             return "\n".join(_pprint_pps(obj.pps()))
4124         pprinter = pprint_any
4125     obj, tail = schema().decode(der)
4126     print(pprinter(obj, oids=oids))
4127     if tail != b"":
4128         print("\nTrailing data: %s" % hexenc(tail))
4129
4130
4131 if __name__ == "__main__":
4132     main()