]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Auto add __slots__ to all inherited classes
[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 from initial offset where object's tag is started
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         if impl is None:
692             self.tag = getattr(self, "impl", self.tag_default)
693         else:
694             self.tag = impl
695         self._expl = getattr(self, "expl", None) if expl is None else expl
696         if self.tag != self.tag_default and self._expl is not None:
697             raise ValueError(
698                 "implicit and explicit tags can not be set simultaneously"
699             )
700         if default is not None:
701             optional = True
702         self.optional = optional
703         self.offset, self.llen, self.vlen = _decoded
704         self.default = None
705
706     @property
707     def ready(self):  # pragma: no cover
708         """Is object ready to be encoded?
709         """
710         raise NotImplementedError()
711
712     def _assert_ready(self):
713         if not self.ready:
714             raise ObjNotReady(self.__class__.__name__)
715
716     @property
717     def decoded(self):
718         """Is object decoded?
719         """
720         return self.llen > 0
721
722     def copy(self):  # pragma: no cover
723         """Make a copy of object, safe to be mutated
724         """
725         raise NotImplementedError()
726
727     @property
728     def tlen(self):
729         return len(self.tag)
730
731     @property
732     def tlvlen(self):
733         return self.tlen + self.llen + self.vlen
734
735     def __str__(self):  # pragma: no cover
736         return self.__bytes__() if PY2 else self.__unicode__()
737
738     def _encode(self):  # pragma: no cover
739         raise NotImplementedError()
740
741     def _decode(self, tlv, offset=0, decode_path=()):  # pragma: no cover
742         raise NotImplementedError()
743
744     def encode(self):
745         raw = self._encode()
746         if self._expl is None:
747             return raw
748         return b"".join((self._expl, len_encode(len(raw)), raw))
749
750     def decode(self, data, offset=0, leavemm=False, decode_path=()):
751         """Decode the data
752
753         :param data: either binary or memoryview
754         :param int offset: initial data's offset
755         :param bool leavemm: do we need to leave memoryview of remaining
756                     data as is, or convert it to bytes otherwise
757         :returns: (Obj, remaining data)
758         """
759         tlv = memoryview(data)
760         if self._expl is None:
761             obj, tail = self._decode(
762                 tlv,
763                 offset,
764                 decode_path=decode_path,
765             )
766         else:
767             try:
768                 t, tlen, lv = tag_strip(tlv)
769             except DecodeError as err:
770                 raise err.__class__(
771                     msg=err.msg,
772                     klass=self.__class__,
773                     decode_path=decode_path,
774                     offset=offset,
775                 )
776             if t != self._expl:
777                 raise TagMismatch(
778                     klass=self.__class__,
779                     decode_path=decode_path,
780                     offset=offset,
781                 )
782             try:
783                 l, llen, v = len_decode(lv)
784             except DecodeError as err:
785                 raise err.__class__(
786                     msg=err.msg,
787                     klass=self.__class__,
788                     decode_path=decode_path,
789                     offset=offset,
790                 )
791             if l > len(v):
792                 raise NotEnoughData(
793                     "encoded length is longer than data",
794                     klass=self.__class__,
795                     decode_path=decode_path,
796                     offset=offset,
797                 )
798             obj, tail = self._decode(
799                 v,
800                 offset=offset + tlen + llen,
801                 decode_path=(),
802             )
803         return obj, (tail if leavemm else tail.tobytes())
804
805     @property
806     def expled(self):
807         return self._expl is not None
808
809     @property
810     def expl_tag(self):
811         return self._expl
812
813     @property
814     def expl_tlen(self):
815         return len(self._expl)
816
817     @property
818     def expl_llen(self):
819         return len(len_encode(self.tlvlen))
820
821     @property
822     def expl_offset(self):
823         return self.offset - self.expl_tlen - self.expl_llen
824
825     @property
826     def expl_vlen(self):
827         return self.tlvlen
828
829     @property
830     def expl_tlvlen(self):
831         return self.expl_tlen + self.expl_llen + self.expl_vlen
832
833
834 ########################################################################
835 # Pretty printing
836 ########################################################################
837
838 PP = namedtuple("PP", (
839     "asn1_type_name",
840     "obj_name",
841     "decode_path",
842     "value",
843     "blob",
844     "optional",
845     "default",
846     "impl",
847     "expl",
848     "offset",
849     "tlen",
850     "llen",
851     "vlen",
852     "expl_offset",
853     "expl_tlen",
854     "expl_llen",
855     "expl_vlen",
856 ))
857
858
859 def _pp(
860         asn1_type_name="unknown",
861         obj_name="unknown",
862         decode_path=(),
863         value=None,
864         blob=None,
865         optional=False,
866         default=False,
867         impl=None,
868         expl=None,
869         offset=0,
870         tlen=0,
871         llen=0,
872         vlen=0,
873         expl_offset=None,
874         expl_tlen=None,
875         expl_llen=None,
876         expl_vlen=None,
877 ):
878     return PP(
879         asn1_type_name,
880         obj_name,
881         decode_path,
882         value,
883         blob,
884         optional,
885         default,
886         impl,
887         expl,
888         offset,
889         tlen,
890         llen,
891         vlen,
892         expl_offset,
893         expl_tlen,
894         expl_llen,
895         expl_vlen,
896     )
897
898
899 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
900     cols = []
901     if with_offsets:
902         cols.append("%5d%s [%d,%d,%4d]" % (
903             pp.offset,
904             (
905                 "  " if pp.expl_offset is None else
906                 ("-%d" % (pp.offset - pp.expl_offset))
907             ),
908             pp.tlen,
909             pp.llen,
910             pp.vlen,
911         ))
912     if len(pp.decode_path) > 0:
913         cols.append(" ." * (len(pp.decode_path)))
914         cols.append("%s:" % pp.decode_path[-1])
915     if pp.expl is not None:
916         klass, _, num = pp.expl
917         cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
918     if pp.impl is not None:
919         klass, _, num = pp.impl
920         cols.append("[%s%d]" % (TagClassReprs[klass], num))
921     if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
922         cols.append(pp.obj_name)
923     cols.append(pp.asn1_type_name)
924     if pp.value is not None:
925         value = pp.value
926         if (
927                 oids is not None and
928                 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
929                 value in oids
930         ):
931             value = "%s (%s)" % (oids[value], pp.value)
932         cols.append(value)
933     if with_blob:
934         if isinstance(pp.blob, binary_type):
935             cols.append(hexenc(pp.blob))
936         elif isinstance(pp.blob, tuple):
937             cols.append(", ".join(pp.blob))
938     if pp.optional:
939         cols.append("OPTIONAL")
940     if pp.default:
941         cols.append("DEFAULT")
942     return " ".join(cols)
943
944
945 def pp_console_blob(pp):
946     cols = [" " * len("XXXXXYY [X,X,XXXX]")]
947     if len(pp.decode_path) > 0:
948         cols.append(" ." * (len(pp.decode_path) + 1))
949     if isinstance(pp.blob, binary_type):
950         blob = hexenc(pp.blob).upper()
951         for i in range(0, len(blob), 32):
952             chunk = blob[i:i + 32]
953             yield " ".join(cols + [":".join(
954                 chunk[j:j + 2] for j in range(0, len(chunk), 2)
955             )])
956     elif isinstance(pp.blob, tuple):
957         yield " ".join(cols + [", ".join(pp.blob)])
958
959
960 def pprint(obj, oids=None, big_blobs=False):
961     """Pretty print object
962
963     :param Obj obj: object you want to pretty print
964     :param oids: ``OID <-> humand readable string`` dictionary. When OID
965                  from it is met, then its humand readable form is printed
966     :param big_blobs: if large binary objects are met (like OctetString
967                       values), do we need to print them too, on separate
968                       lines
969     """
970     def _pprint_pps(pps):
971         for pp in pps:
972             if hasattr(pp, "_fields"):
973                 if big_blobs:
974                     yield pp_console_row(
975                         pp,
976                         oids=oids,
977                         with_offsets=True,
978                         with_blob=False,
979                     )
980                     for row in pp_console_blob(pp):
981                         yield row
982                 else:
983                     yield pp_console_row(pp, oids=oids, with_offsets=True)
984             else:
985                 for row in _pprint_pps(pp):
986                     yield row
987     return "\n".join(_pprint_pps(obj.pps()))
988
989
990 ########################################################################
991 # ASN.1 primitive types
992 ########################################################################
993
994 class Boolean(Obj):
995     """``BOOLEAN`` boolean type
996
997     >>> b = Boolean(True)
998     BOOLEAN True
999     >>> b == Boolean(True)
1000     True
1001     >>> bool(b)
1002     True
1003     """
1004     __slots__ = ()
1005     tag_default = tag_encode(1)
1006     asn1_type_name = "BOOLEAN"
1007
1008     def __init__(
1009             self,
1010             value=None,
1011             impl=None,
1012             expl=None,
1013             default=None,
1014             optional=False,
1015             _decoded=(0, 0, 0),
1016     ):
1017         """
1018         :param value: set the value. Either boolean type, or
1019                       :py:class:`pyderasn.Boolean` object
1020         :param bytes impl: override default tag with ``IMPLICIT`` one
1021         :param bytes expl: override default tag with ``EXPLICIT`` one
1022         :param default: set default value. Type same as in ``value``
1023         :param bool optional: is object ``OPTIONAL`` in sequence
1024         """
1025         super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1026         self._value = None if value is None else self._value_sanitize(value)
1027         if default is not None:
1028             default = self._value_sanitize(default)
1029             self.default = self.__class__(
1030                 value=default,
1031                 impl=self.tag,
1032                 expl=self._expl,
1033             )
1034             if value is None:
1035                 self._value = default
1036
1037     def _value_sanitize(self, value):
1038         if issubclass(value.__class__, Boolean):
1039             return value._value
1040         if isinstance(value, bool):
1041             return value
1042         raise InvalidValueType((self.__class__, bool))
1043
1044     @property
1045     def ready(self):
1046         return self._value is not None
1047
1048     def copy(self):
1049         obj = self.__class__()
1050         obj._value = self._value
1051         obj.tag = self.tag
1052         obj._expl = self._expl
1053         obj.default = self.default
1054         obj.optional = self.optional
1055         obj.offset = self.offset
1056         obj.llen = self.llen
1057         obj.vlen = self.vlen
1058         return obj
1059
1060     def __nonzero__(self):
1061         self._assert_ready()
1062         return self._value
1063
1064     def __bool__(self):
1065         self._assert_ready()
1066         return self._value
1067
1068     def __eq__(self, their):
1069         if isinstance(their, bool):
1070             return self._value == their
1071         if not issubclass(their.__class__, Boolean):
1072             return False
1073         return (
1074             self._value == their._value and
1075             self.tag == their.tag and
1076             self._expl == their._expl
1077         )
1078
1079     def __call__(
1080             self,
1081             value=None,
1082             impl=None,
1083             expl=None,
1084             default=None,
1085             optional=None,
1086     ):
1087         return self.__class__(
1088             value=value,
1089             impl=self.tag if impl is None else impl,
1090             expl=self._expl if expl is None else expl,
1091             default=self.default if default is None else default,
1092             optional=self.optional if optional is None else optional,
1093         )
1094
1095     def _encode(self):
1096         self._assert_ready()
1097         return b"".join((
1098             self.tag,
1099             len_encode(1),
1100             (b"\xFF" if self._value else b"\x00"),
1101         ))
1102
1103     def _decode(self, tlv, offset=0, decode_path=()):
1104         try:
1105             t, _, lv = tag_strip(tlv)
1106         except DecodeError as err:
1107             raise err.__class__(
1108                 msg=err.msg,
1109                 klass=self.__class__,
1110                 decode_path=decode_path,
1111                 offset=offset,
1112             )
1113         if t != self.tag:
1114             raise TagMismatch(
1115                 klass=self.__class__,
1116                 decode_path=decode_path,
1117                 offset=offset,
1118             )
1119         try:
1120             l, _, v = len_decode(lv)
1121         except DecodeError as err:
1122             raise err.__class__(
1123                 msg=err.msg,
1124                 klass=self.__class__,
1125                 decode_path=decode_path,
1126                 offset=offset,
1127             )
1128         if l != 1:
1129             raise InvalidLength(
1130                 "Boolean's length must be equal to 1",
1131                 klass=self.__class__,
1132                 decode_path=decode_path,
1133                 offset=offset,
1134             )
1135         if l > len(v):
1136             raise NotEnoughData(
1137                 "encoded length is longer than data",
1138                 klass=self.__class__,
1139                 decode_path=decode_path,
1140                 offset=offset,
1141             )
1142         first_octet = byte2int(v)
1143         if first_octet == 0:
1144             value = False
1145         elif first_octet == 0xFF:
1146             value = True
1147         else:
1148             raise DecodeError(
1149                 "unacceptable Boolean value",
1150                 klass=self.__class__,
1151                 decode_path=decode_path,
1152                 offset=offset,
1153             )
1154         obj = self.__class__(
1155             value=value,
1156             impl=self.tag,
1157             expl=self._expl,
1158             default=self.default,
1159             optional=self.optional,
1160             _decoded=(offset, 1, 1),
1161         )
1162         return obj, v[1:]
1163
1164     def __repr__(self):
1165         return pp_console_row(next(self.pps()))
1166
1167     def pps(self, decode_path=()):
1168         yield _pp(
1169             asn1_type_name=self.asn1_type_name,
1170             obj_name=self.__class__.__name__,
1171             decode_path=decode_path,
1172             value=str(self._value) if self.ready else None,
1173             optional=self.optional,
1174             default=self == self.default,
1175             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1176             expl=None if self._expl is None else tag_decode(self._expl),
1177             offset=self.offset,
1178             tlen=self.tlen,
1179             llen=self.llen,
1180             vlen=self.vlen,
1181             expl_offset=self.expl_offset if self.expled else None,
1182             expl_tlen=self.expl_tlen if self.expled else None,
1183             expl_llen=self.expl_llen if self.expled else None,
1184             expl_vlen=self.expl_vlen if self.expled else None,
1185         )
1186
1187
1188 class Integer(Obj):
1189     """``INTEGER`` integer type
1190
1191     >>> b = Integer(-123)
1192     INTEGER -123
1193     >>> b == Integer(-123)
1194     True
1195     >>> int(b)
1196     -123
1197
1198     >>> Integer(2, bounds=(1, 3))
1199     INTEGER 2
1200     >>> Integer(5, bounds=(1, 3))
1201     Traceback (most recent call last):
1202     pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1203
1204     ::
1205
1206         class Version(Integer):
1207             schema = (
1208                 ("v1", 0),
1209                 ("v2", 1),
1210                 ("v3", 2),
1211             )
1212
1213     >>> v = Version("v1")
1214     Version INTEGER v1
1215     >>> int(v)
1216     0
1217     >>> v.named
1218     'v1'
1219     >>> v.specs
1220     {'v3': 2, 'v1': 0, 'v2': 1}
1221     """
1222     __slots__ = ("specs", "_bound_min", "_bound_max")
1223     tag_default = tag_encode(2)
1224     asn1_type_name = "INTEGER"
1225
1226     def __init__(
1227             self,
1228             value=None,
1229             bounds=None,
1230             impl=None,
1231             expl=None,
1232             default=None,
1233             optional=False,
1234             _specs=None,
1235             _decoded=(0, 0, 0),
1236     ):
1237         """
1238         :param value: set the value. Either integer type, named value
1239                       (if ``schema`` is specified in the class), or
1240                       :py:class:`pyderasn.Integer` object
1241         :param bounds: set ``(MIN, MAX)`` value constraint.
1242                        (-inf, +inf) by default
1243         :param bytes impl: override default tag with ``IMPLICIT`` one
1244         :param bytes expl: override default tag with ``EXPLICIT`` one
1245         :param default: set default value. Type same as in ``value``
1246         :param bool optional: is object ``OPTIONAL`` in sequence
1247         """
1248         super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1249         self._value = value
1250         specs = getattr(self, "schema", {}) if _specs is None else _specs
1251         self.specs = specs if isinstance(specs, dict) else dict(specs)
1252         if bounds is None:
1253             self._bound_min, self._bound_max = getattr(
1254                 self,
1255                 "bounds",
1256                 (float("-inf"), float("+inf")),
1257             )
1258         else:
1259             self._bound_min, self._bound_max = bounds
1260         if value is not None:
1261             self._value = self._value_sanitize(value)
1262         if default is not None:
1263             default = self._value_sanitize(default)
1264             self.default = self.__class__(
1265                 value=default,
1266                 impl=self.tag,
1267                 expl=self._expl,
1268                 _specs=self.specs,
1269             )
1270             if self._value is None:
1271                 self._value = default
1272
1273     def _value_sanitize(self, value):
1274         if issubclass(value.__class__, Integer):
1275             value = value._value
1276         elif isinstance(value, integer_types):
1277             pass
1278         elif isinstance(value, str):
1279             value = self.specs.get(value)
1280             if value is None:
1281                 raise ObjUnknown("integer value: %s" % value)
1282         else:
1283             raise InvalidValueType((self.__class__, int, str))
1284         if not self._bound_min <= value <= self._bound_max:
1285             raise BoundsError(self._bound_min, value, self._bound_max)
1286         return value
1287
1288     @property
1289     def ready(self):
1290         return self._value is not None
1291
1292     def copy(self):
1293         obj = self.__class__(_specs=self.specs)
1294         obj._value = self._value
1295         obj._bound_min = self._bound_min
1296         obj._bound_max = self._bound_max
1297         obj.tag = self.tag
1298         obj._expl = self._expl
1299         obj.default = self.default
1300         obj.optional = self.optional
1301         obj.offset = self.offset
1302         obj.llen = self.llen
1303         obj.vlen = self.vlen
1304         return obj
1305
1306     def __int__(self):
1307         self._assert_ready()
1308         return int(self._value)
1309
1310     def __hash__(self):
1311         self._assert_ready()
1312         return hash(
1313             self.tag +
1314             bytes(self._expl or b"") +
1315             str(self._value).encode("ascii"),
1316         )
1317
1318     def __eq__(self, their):
1319         if isinstance(their, integer_types):
1320             return self._value == their
1321         if not issubclass(their.__class__, Integer):
1322             return False
1323         return (
1324             self._value == their._value and
1325             self.tag == their.tag and
1326             self._expl == their._expl
1327         )
1328
1329     def __lt__(self, their):
1330         return self._value < their
1331
1332     def __gt__(self, their):
1333         return self._value > their
1334
1335     @property
1336     def named(self):
1337         for name, value in self.specs.items():
1338             if value == self._value:
1339                 return name
1340
1341     def __call__(
1342             self,
1343             value=None,
1344             bounds=None,
1345             impl=None,
1346             expl=None,
1347             default=None,
1348             optional=None,
1349     ):
1350         return self.__class__(
1351             value=value,
1352             bounds=(
1353                 (self._bound_min, self._bound_max)
1354                 if bounds is None else bounds
1355             ),
1356             impl=self.tag if impl is None else impl,
1357             expl=self._expl if expl is None else expl,
1358             default=self.default if default is None else default,
1359             optional=self.optional if optional is None else optional,
1360             _specs=self.specs,
1361         )
1362
1363     def _encode(self):
1364         self._assert_ready()
1365         value = self._value
1366         if PY2:
1367             if value == 0:
1368                 octets = bytearray([0])
1369             elif value < 0:
1370                 value = -value
1371                 value -= 1
1372                 octets = bytearray()
1373                 while value > 0:
1374                     octets.append((value & 0xFF) ^ 0xFF)
1375                     value >>= 8
1376                 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1377                     octets.append(0xFF)
1378             else:
1379                 octets = bytearray()
1380                 while value > 0:
1381                     octets.append(value & 0xFF)
1382                     value >>= 8
1383                 if octets[-1] & 0x80 > 0:
1384                     octets.append(0x00)
1385             octets.reverse()
1386             octets = bytes(octets)
1387         else:
1388             bytes_len = ceil(value.bit_length() / 8) or 1
1389             while True:
1390                 try:
1391                     octets = value.to_bytes(
1392                         bytes_len,
1393                         byteorder="big",
1394                         signed=True,
1395                     )
1396                 except OverflowError:
1397                     bytes_len += 1
1398                 else:
1399                     break
1400         return b"".join((self.tag, len_encode(len(octets)), octets))
1401
1402     def _decode(self, tlv, offset=0, decode_path=()):
1403         try:
1404             t, _, lv = tag_strip(tlv)
1405         except DecodeError as err:
1406             raise err.__class__(
1407                 msg=err.msg,
1408                 klass=self.__class__,
1409                 decode_path=decode_path,
1410                 offset=offset,
1411             )
1412         if t != self.tag:
1413             raise TagMismatch(
1414                 klass=self.__class__,
1415                 decode_path=decode_path,
1416                 offset=offset,
1417             )
1418         try:
1419             l, llen, v = len_decode(lv)
1420         except DecodeError as err:
1421             raise err.__class__(
1422                 msg=err.msg,
1423                 klass=self.__class__,
1424                 decode_path=decode_path,
1425                 offset=offset,
1426             )
1427         if l > len(v):
1428             raise NotEnoughData(
1429                 "encoded length is longer than data",
1430                 klass=self.__class__,
1431                 decode_path=decode_path,
1432                 offset=offset,
1433             )
1434         if l == 0:
1435             raise NotEnoughData(
1436                 "zero length",
1437                 klass=self.__class__,
1438                 decode_path=decode_path,
1439                 offset=offset,
1440             )
1441         v, tail = v[:l], v[l:]
1442         first_octet = byte2int(v)
1443         if l > 1:
1444             second_octet = byte2int(v[1:])
1445             if (
1446                     ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1447                     ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1448             ):
1449                 raise DecodeError(
1450                     "non normalized integer",
1451                     klass=self.__class__,
1452                     decode_path=decode_path,
1453                     offset=offset,
1454                 )
1455         if PY2:
1456             value = 0
1457             if first_octet & 0x80 > 0:
1458                 octets = bytearray()
1459                 for octet in bytearray(v):
1460                     octets.append(octet ^ 0xFF)
1461                 for octet in octets:
1462                     value = (value << 8) | octet
1463                 value += 1
1464                 value = -value
1465             else:
1466                 for octet in bytearray(v):
1467                     value = (value << 8) | octet
1468         else:
1469             value = int.from_bytes(v, byteorder="big", signed=True)
1470         try:
1471             obj = self.__class__(
1472                 value=value,
1473                 bounds=(self._bound_min, self._bound_max),
1474                 impl=self.tag,
1475                 expl=self._expl,
1476                 default=self.default,
1477                 optional=self.optional,
1478                 _specs=self.specs,
1479                 _decoded=(offset, llen, l),
1480             )
1481         except BoundsError as err:
1482             raise DecodeError(
1483                 msg=str(err),
1484                 klass=self.__class__,
1485                 decode_path=decode_path,
1486                 offset=offset,
1487             )
1488         return obj, tail
1489
1490     def __repr__(self):
1491         return pp_console_row(next(self.pps()))
1492
1493     def pps(self, decode_path=()):
1494         yield _pp(
1495             asn1_type_name=self.asn1_type_name,
1496             obj_name=self.__class__.__name__,
1497             decode_path=decode_path,
1498             value=(self.named or str(self._value)) if self.ready else None,
1499             optional=self.optional,
1500             default=self == self.default,
1501             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1502             expl=None if self._expl is None else tag_decode(self._expl),
1503             offset=self.offset,
1504             tlen=self.tlen,
1505             llen=self.llen,
1506             vlen=self.vlen,
1507             expl_offset=self.expl_offset if self.expled else None,
1508             expl_tlen=self.expl_tlen if self.expled else None,
1509             expl_llen=self.expl_llen if self.expled else None,
1510             expl_vlen=self.expl_vlen if self.expled else None,
1511         )
1512
1513
1514 class BitString(Obj):
1515     """``BIT STRING`` bit string type
1516
1517     >>> BitString(b"hello world")
1518     BIT STRING 88 bits 68656c6c6f20776f726c64
1519     >>> bytes(b)
1520     b'hello world'
1521     >>> b == b"hello world"
1522     True
1523     >>> b.bit_len
1524     88
1525
1526     >>> b = BitString("'010110000000'B")
1527     BIT STRING 12 bits 5800
1528     >>> b.bit_len
1529     12
1530     >>> b[0], b[1], b[2], b[3]
1531     (False, True, False, True)
1532     >>> b[1000]
1533     False
1534     >>> [v for v in b]
1535     [False, True, False, True, True, False, False, False, False, False, False, False]
1536
1537     ::
1538
1539         class KeyUsage(BitString):
1540             schema = (
1541                 ('digitalSignature', 0),
1542                 ('nonRepudiation', 1),
1543                 ('keyEncipherment', 2),
1544             )
1545
1546     >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1547     KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1548     >>> b.named
1549     ['nonRepudiation', 'keyEncipherment']
1550     >>> b.specs
1551     {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1552     """
1553     __slots__ = ("specs",)
1554     tag_default = tag_encode(3)
1555     asn1_type_name = "BIT STRING"
1556
1557     def __init__(
1558             self,
1559             value=None,
1560             impl=None,
1561             expl=None,
1562             default=None,
1563             optional=False,
1564             _specs=None,
1565             _decoded=(0, 0, 0),
1566     ):
1567         """
1568         :param value: set the value. Either binary type, tuple of named
1569                       values (if ``schema`` is specified in the class),
1570                       string in ``'XXX...'B`` form, or
1571                       :py:class:`pyderasn.BitString` object
1572         :param bytes impl: override default tag with ``IMPLICIT`` one
1573         :param bytes expl: override default tag with ``EXPLICIT`` one
1574         :param default: set default value. Type same as in ``value``
1575         :param bool optional: is object ``OPTIONAL`` in sequence
1576         """
1577         super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1578         specs = getattr(self, "schema", {}) if _specs is None else _specs
1579         self.specs = specs if isinstance(specs, dict) else dict(specs)
1580         self._value = None if value is None else self._value_sanitize(value)
1581         if default is not None:
1582             default = self._value_sanitize(default)
1583             self.default = self.__class__(
1584                 value=default,
1585                 impl=self.tag,
1586                 expl=self._expl,
1587             )
1588             if value is None:
1589                 self._value = default
1590
1591     def _bits2octets(self, bits):
1592         if len(self.specs) > 0:
1593             bits = bits.rstrip("0")
1594         bit_len = len(bits)
1595         bits += "0" * ((8 - (bit_len % 8)) % 8)
1596         octets = bytearray(len(bits) // 8)
1597         for i in six_xrange(len(octets)):
1598             octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1599         return bit_len, bytes(octets)
1600
1601     def _value_sanitize(self, value):
1602         if issubclass(value.__class__, BitString):
1603             return value._value
1604         if isinstance(value, (string_types, binary_type)):
1605             if (
1606                     isinstance(value, string_types) and
1607                     value.startswith("'") and
1608                     value.endswith("'B")
1609             ):
1610                 value = value[1:-2]
1611                 if not set(value) <= set(("0", "1")):
1612                     raise ValueError("B's coding contains unacceptable chars")
1613                 return self._bits2octets(value)
1614             elif isinstance(value, binary_type):
1615                 return (len(value) * 8, value)
1616             else:
1617                 raise InvalidValueType((
1618                     self.__class__,
1619                     string_types,
1620                     binary_type,
1621                 ))
1622         if isinstance(value, tuple):
1623             if (
1624                     len(value) == 2 and
1625                     isinstance(value[0], integer_types) and
1626                     isinstance(value[1], binary_type)
1627             ):
1628                 return value
1629             bits = []
1630             for name in value:
1631                 bit = self.specs.get(name)
1632                 if bit is None:
1633                     raise ObjUnknown("BitString value: %s" % name)
1634                 bits.append(bit)
1635             if len(bits) == 0:
1636                 return self._bits2octets("")
1637             bits = set(bits)
1638             return self._bits2octets("".join(
1639                 ("1" if bit in bits else "0")
1640                 for bit in six_xrange(max(bits) + 1)
1641             ))
1642         raise InvalidValueType((self.__class__, binary_type, string_types))
1643
1644     @property
1645     def ready(self):
1646         return self._value is not None
1647
1648     def copy(self):
1649         obj = self.__class__(_specs=self.specs)
1650         obj._value = self._value
1651         obj.tag = self.tag
1652         obj._expl = self._expl
1653         obj.default = self.default
1654         obj.optional = self.optional
1655         obj.offset = self.offset
1656         obj.llen = self.llen
1657         obj.vlen = self.vlen
1658         return obj
1659
1660     def __iter__(self):
1661         self._assert_ready()
1662         for i in six_xrange(self._value[0]):
1663             yield self[i]
1664
1665     @property
1666     def bit_len(self):
1667         self._assert_ready()
1668         return self._value[0]
1669
1670     def __bytes__(self):
1671         self._assert_ready()
1672         return self._value[1]
1673
1674     def __eq__(self, their):
1675         if isinstance(their, bytes):
1676             return self._value[1] == their
1677         if not issubclass(their.__class__, BitString):
1678             return False
1679         return (
1680             self._value == their._value and
1681             self.tag == their.tag and
1682             self._expl == their._expl
1683         )
1684
1685     @property
1686     def named(self):
1687         return [name for name, bit in self.specs.items() if self[bit]]
1688
1689     def __call__(
1690             self,
1691             value=None,
1692             impl=None,
1693             expl=None,
1694             default=None,
1695             optional=None,
1696     ):
1697         return self.__class__(
1698             value=value,
1699             impl=self.tag if impl is None else impl,
1700             expl=self._expl if expl is None else expl,
1701             default=self.default if default is None else default,
1702             optional=self.optional if optional is None else optional,
1703             _specs=self.specs,
1704         )
1705
1706     def __getitem__(self, key):
1707         if isinstance(key, int):
1708             bit_len, octets = self._value
1709             if key >= bit_len:
1710                 return False
1711             return (
1712                 byte2int(memoryview(octets)[key // 8:]) >>
1713                 (7 - (key % 8))
1714             ) & 1 == 1
1715         if isinstance(key, string_types):
1716             value = self.specs.get(key)
1717             if value is None:
1718                 raise ObjUnknown("BitString value: %s" % key)
1719             return self[value]
1720         raise InvalidValueType((int, str))
1721
1722     def _encode(self):
1723         self._assert_ready()
1724         bit_len, octets = self._value
1725         return b"".join((
1726             self.tag,
1727             len_encode(len(octets) + 1),
1728             int2byte((8 - bit_len % 8) % 8),
1729             octets,
1730         ))
1731
1732     def _decode(self, tlv, offset=0, decode_path=()):
1733         try:
1734             t, _, lv = tag_strip(tlv)
1735         except DecodeError as err:
1736             raise err.__class__(
1737                 msg=err.msg,
1738                 klass=self.__class__,
1739                 decode_path=decode_path,
1740                 offset=offset,
1741             )
1742         if t != self.tag:
1743             raise TagMismatch(
1744                 klass=self.__class__,
1745                 decode_path=decode_path,
1746                 offset=offset,
1747             )
1748         try:
1749             l, llen, v = len_decode(lv)
1750         except DecodeError as err:
1751             raise err.__class__(
1752                 msg=err.msg,
1753                 klass=self.__class__,
1754                 decode_path=decode_path,
1755                 offset=offset,
1756             )
1757         if l > len(v):
1758             raise NotEnoughData(
1759                 "encoded length is longer than data",
1760                 klass=self.__class__,
1761                 decode_path=decode_path,
1762                 offset=offset,
1763             )
1764         if l == 0:
1765             raise NotEnoughData(
1766                 "zero length",
1767                 klass=self.__class__,
1768                 decode_path=decode_path,
1769                 offset=offset,
1770             )
1771         pad_size = byte2int(v)
1772         if l == 1 and pad_size != 0:
1773             raise DecodeError(
1774                 "invalid empty value",
1775                 klass=self.__class__,
1776                 decode_path=decode_path,
1777                 offset=offset,
1778             )
1779         if pad_size > 7:
1780             raise DecodeError(
1781                 "too big pad",
1782                 klass=self.__class__,
1783                 decode_path=decode_path,
1784                 offset=offset,
1785             )
1786         if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1787             raise DecodeError(
1788                 "invalid pad",
1789                 klass=self.__class__,
1790                 decode_path=decode_path,
1791                 offset=offset,
1792             )
1793         v, tail = v[:l], v[l:]
1794         obj = self.__class__(
1795             value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1796             impl=self.tag,
1797             expl=self._expl,
1798             default=self.default,
1799             optional=self.optional,
1800             _specs=self.specs,
1801             _decoded=(offset, llen, l),
1802         )
1803         return obj, tail
1804
1805     def __repr__(self):
1806         return pp_console_row(next(self.pps()))
1807
1808     def pps(self, decode_path=()):
1809         value = None
1810         blob = None
1811         if self.ready:
1812             bit_len, blob = self._value
1813             value = "%d bits" % bit_len
1814             if len(self.specs) > 0:
1815                 blob = tuple(self.named)
1816         yield _pp(
1817             asn1_type_name=self.asn1_type_name,
1818             obj_name=self.__class__.__name__,
1819             decode_path=decode_path,
1820             value=value,
1821             blob=blob,
1822             optional=self.optional,
1823             default=self == self.default,
1824             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1825             expl=None if self._expl is None else tag_decode(self._expl),
1826             offset=self.offset,
1827             tlen=self.tlen,
1828             llen=self.llen,
1829             vlen=self.vlen,
1830             expl_offset=self.expl_offset if self.expled else None,
1831             expl_tlen=self.expl_tlen if self.expled else None,
1832             expl_llen=self.expl_llen if self.expled else None,
1833             expl_vlen=self.expl_vlen if self.expled else None,
1834         )
1835
1836
1837 class OctetString(Obj):
1838     """``OCTET STRING`` binary string type
1839
1840     >>> s = OctetString(b"hello world")
1841     OCTET STRING 11 bytes 68656c6c6f20776f726c64
1842     >>> s == OctetString(b"hello world")
1843     True
1844     >>> bytes(s)
1845     b'hello world'
1846
1847     >>> OctetString(b"hello", bounds=(4, 4))
1848     Traceback (most recent call last):
1849     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1850     >>> OctetString(b"hell", bounds=(4, 4))
1851     OCTET STRING 4 bytes 68656c6c
1852     """
1853     __slots__ = ("_bound_min", "_bound_max")
1854     tag_default = tag_encode(4)
1855     asn1_type_name = "OCTET STRING"
1856
1857     def __init__(
1858             self,
1859             value=None,
1860             bounds=None,
1861             impl=None,
1862             expl=None,
1863             default=None,
1864             optional=False,
1865             _decoded=(0, 0, 0),
1866     ):
1867         """
1868         :param value: set the value. Either binary type, or
1869                       :py:class:`pyderasn.OctetString` object
1870         :param bounds: set ``(MIN, MAX)`` value size constraint.
1871                        (-inf, +inf) by default
1872         :param bytes impl: override default tag with ``IMPLICIT`` one
1873         :param bytes expl: override default tag with ``EXPLICIT`` one
1874         :param default: set default value. Type same as in ``value``
1875         :param bool optional: is object ``OPTIONAL`` in sequence
1876         """
1877         super(OctetString, self).__init__(
1878             impl,
1879             expl,
1880             default,
1881             optional,
1882             _decoded,
1883         )
1884         self._value = value
1885         if bounds is None:
1886             self._bound_min, self._bound_max = getattr(
1887                 self,
1888                 "bounds",
1889                 (0, float("+inf")),
1890             )
1891         else:
1892             self._bound_min, self._bound_max = bounds
1893         if value is not None:
1894             self._value = self._value_sanitize(value)
1895         if default is not None:
1896             default = self._value_sanitize(default)
1897             self.default = self.__class__(
1898                 value=default,
1899                 impl=self.tag,
1900                 expl=self._expl,
1901             )
1902             if self._value is None:
1903                 self._value = default
1904
1905     def _value_sanitize(self, value):
1906         if issubclass(value.__class__, OctetString):
1907             value = value._value
1908         elif isinstance(value, binary_type):
1909             pass
1910         else:
1911             raise InvalidValueType((self.__class__, bytes))
1912         if not self._bound_min <= len(value) <= self._bound_max:
1913             raise BoundsError(self._bound_min, len(value), self._bound_max)
1914         return value
1915
1916     @property
1917     def ready(self):
1918         return self._value is not None
1919
1920     def copy(self):
1921         obj = self.__class__()
1922         obj._value = self._value
1923         obj._bound_min = self._bound_min
1924         obj._bound_max = self._bound_max
1925         obj.tag = self.tag
1926         obj._expl = self._expl
1927         obj.default = self.default
1928         obj.optional = self.optional
1929         obj.offset = self.offset
1930         obj.llen = self.llen
1931         obj.vlen = self.vlen
1932         return obj
1933
1934     def __bytes__(self):
1935         self._assert_ready()
1936         return self._value
1937
1938     def __eq__(self, their):
1939         if isinstance(their, binary_type):
1940             return self._value == their
1941         if not issubclass(their.__class__, OctetString):
1942             return False
1943         return (
1944             self._value == their._value and
1945             self.tag == their.tag and
1946             self._expl == their._expl
1947         )
1948
1949     def __call__(
1950             self,
1951             value=None,
1952             bounds=None,
1953             impl=None,
1954             expl=None,
1955             default=None,
1956             optional=None,
1957     ):
1958         return self.__class__(
1959             value=value,
1960             bounds=(
1961                 (self._bound_min, self._bound_max)
1962                 if bounds is None else bounds
1963             ),
1964             impl=self.tag if impl is None else impl,
1965             expl=self._expl if expl is None else expl,
1966             default=self.default if default is None else default,
1967             optional=self.optional if optional is None else optional,
1968         )
1969
1970     def _encode(self):
1971         self._assert_ready()
1972         return b"".join((
1973             self.tag,
1974             len_encode(len(self._value)),
1975             self._value,
1976         ))
1977
1978     def _decode(self, tlv, offset=0, decode_path=()):
1979         try:
1980             t, _, lv = tag_strip(tlv)
1981         except DecodeError as err:
1982             raise err.__class__(
1983                 msg=err.msg,
1984                 klass=self.__class__,
1985                 decode_path=decode_path,
1986                 offset=offset,
1987             )
1988         if t != self.tag:
1989             raise TagMismatch(
1990                 klass=self.__class__,
1991                 decode_path=decode_path,
1992                 offset=offset,
1993             )
1994         try:
1995             l, llen, v = len_decode(lv)
1996         except DecodeError as err:
1997             raise err.__class__(
1998                 msg=err.msg,
1999                 klass=self.__class__,
2000                 decode_path=decode_path,
2001                 offset=offset,
2002             )
2003         if l > len(v):
2004             raise NotEnoughData(
2005                 "encoded length is longer than data",
2006                 klass=self.__class__,
2007                 decode_path=decode_path,
2008                 offset=offset,
2009             )
2010         v, tail = v[:l], v[l:]
2011         try:
2012             obj = self.__class__(
2013                 value=v.tobytes(),
2014                 bounds=(self._bound_min, self._bound_max),
2015                 impl=self.tag,
2016                 expl=self._expl,
2017                 default=self.default,
2018                 optional=self.optional,
2019                 _decoded=(offset, llen, l),
2020             )
2021         except BoundsError as err:
2022             raise DecodeError(
2023                 msg=str(err),
2024                 klass=self.__class__,
2025                 decode_path=decode_path,
2026                 offset=offset,
2027             )
2028         return obj, tail
2029
2030     def __repr__(self):
2031         return pp_console_row(next(self.pps()))
2032
2033     def pps(self, decode_path=()):
2034         yield _pp(
2035             asn1_type_name=self.asn1_type_name,
2036             obj_name=self.__class__.__name__,
2037             decode_path=decode_path,
2038             value=("%d bytes" % len(self._value)) if self.ready else None,
2039             blob=self._value if self.ready else None,
2040             optional=self.optional,
2041             default=self == self.default,
2042             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2043             expl=None if self._expl is None else tag_decode(self._expl),
2044             offset=self.offset,
2045             tlen=self.tlen,
2046             llen=self.llen,
2047             vlen=self.vlen,
2048             expl_offset=self.expl_offset if self.expled else None,
2049             expl_tlen=self.expl_tlen if self.expled else None,
2050             expl_llen=self.expl_llen if self.expled else None,
2051             expl_vlen=self.expl_vlen if self.expled else None,
2052         )
2053
2054
2055 class Null(Obj):
2056     """``NULL`` null object
2057
2058     >>> n = Null()
2059     NULL
2060     >>> n.ready
2061     True
2062     """
2063     __slots__ = ()
2064     tag_default = tag_encode(5)
2065     asn1_type_name = "NULL"
2066
2067     def __init__(
2068             self,
2069             value=None,  # unused, but Sequence passes it
2070             impl=None,
2071             expl=None,
2072             optional=False,
2073             _decoded=(0, 0, 0),
2074     ):
2075         """
2076         :param bytes impl: override default tag with ``IMPLICIT`` one
2077         :param bytes expl: override default tag with ``EXPLICIT`` one
2078         :param bool optional: is object ``OPTIONAL`` in sequence
2079         """
2080         super(Null, self).__init__(impl, expl, None, optional, _decoded)
2081         self.default = None
2082
2083     @property
2084     def ready(self):
2085         return True
2086
2087     def copy(self):
2088         obj = self.__class__()
2089         obj.tag = self.tag
2090         obj._expl = self._expl
2091         obj.default = self.default
2092         obj.optional = self.optional
2093         obj.offset = self.offset
2094         obj.llen = self.llen
2095         obj.vlen = self.vlen
2096         return obj
2097
2098     def __eq__(self, their):
2099         if not issubclass(their.__class__, Null):
2100             return False
2101         return (
2102             self.tag == their.tag and
2103             self._expl == their._expl
2104         )
2105
2106     def __call__(
2107             self,
2108             value=None,
2109             impl=None,
2110             expl=None,
2111             optional=None,
2112     ):
2113         return self.__class__(
2114             impl=self.tag if impl is None else impl,
2115             expl=self._expl if expl is None else expl,
2116             optional=self.optional if optional is None else optional,
2117         )
2118
2119     def _encode(self):
2120         return self.tag + len_encode(0)
2121
2122     def _decode(self, tlv, offset=0, decode_path=()):
2123         try:
2124             t, _, lv = tag_strip(tlv)
2125         except DecodeError as err:
2126             raise err.__class__(
2127                 msg=err.msg,
2128                 klass=self.__class__,
2129                 decode_path=decode_path,
2130                 offset=offset,
2131             )
2132         if t != self.tag:
2133             raise TagMismatch(
2134                 klass=self.__class__,
2135                 decode_path=decode_path,
2136                 offset=offset,
2137             )
2138         try:
2139             l, _, v = len_decode(lv)
2140         except DecodeError as err:
2141             raise err.__class__(
2142                 msg=err.msg,
2143                 klass=self.__class__,
2144                 decode_path=decode_path,
2145                 offset=offset,
2146             )
2147         if l != 0:
2148             raise InvalidLength(
2149                 "Null must have zero length",
2150                 klass=self.__class__,
2151                 decode_path=decode_path,
2152                 offset=offset,
2153             )
2154         obj = self.__class__(
2155             impl=self.tag,
2156             expl=self._expl,
2157             optional=self.optional,
2158             _decoded=(offset, 1, 0),
2159         )
2160         return obj, v
2161
2162     def __repr__(self):
2163         return pp_console_row(next(self.pps()))
2164
2165     def pps(self, decode_path=()):
2166         yield _pp(
2167             asn1_type_name=self.asn1_type_name,
2168             obj_name=self.__class__.__name__,
2169             decode_path=decode_path,
2170             optional=self.optional,
2171             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2172             expl=None if self._expl is None else tag_decode(self._expl),
2173             offset=self.offset,
2174             tlen=self.tlen,
2175             llen=self.llen,
2176             vlen=self.vlen,
2177             expl_offset=self.expl_offset if self.expled else None,
2178             expl_tlen=self.expl_tlen if self.expled else None,
2179             expl_llen=self.expl_llen if self.expled else None,
2180             expl_vlen=self.expl_vlen if self.expled else None,
2181         )
2182
2183
2184 class ObjectIdentifier(Obj):
2185     """``OBJECT IDENTIFIER`` OID type
2186
2187     >>> oid = ObjectIdentifier((1, 2, 3))
2188     OBJECT IDENTIFIER 1.2.3
2189     >>> oid == ObjectIdentifier("1.2.3")
2190     True
2191     >>> tuple(oid)
2192     (1, 2, 3)
2193     >>> str(oid)
2194     '1.2.3'
2195     >>> oid + (4, 5) + ObjectIdentifier("1.7")
2196     OBJECT IDENTIFIER 1.2.3.4.5.1.7
2197
2198     >>> str(ObjectIdentifier((3, 1)))
2199     Traceback (most recent call last):
2200     pyderasn.InvalidOID: unacceptable first arc value
2201     """
2202     __slots__ = ()
2203     tag_default = tag_encode(6)
2204     asn1_type_name = "OBJECT IDENTIFIER"
2205
2206     def __init__(
2207             self,
2208             value=None,
2209             impl=None,
2210             expl=None,
2211             default=None,
2212             optional=False,
2213             _decoded=(0, 0, 0),
2214     ):
2215         """
2216         :param value: set the value. Either tuples of integers,
2217                       string of "."-concatenated integers, or
2218                       :py:class:`pyderasn.ObjectIdentifier` object
2219         :param bytes impl: override default tag with ``IMPLICIT`` one
2220         :param bytes expl: override default tag with ``EXPLICIT`` one
2221         :param default: set default value. Type same as in ``value``
2222         :param bool optional: is object ``OPTIONAL`` in sequence
2223         """
2224         super(ObjectIdentifier, self).__init__(
2225             impl,
2226             expl,
2227             default,
2228             optional,
2229             _decoded,
2230         )
2231         self._value = value
2232         if value is not None:
2233             self._value = self._value_sanitize(value)
2234         if default is not None:
2235             default = self._value_sanitize(default)
2236             self.default = self.__class__(
2237                 value=default,
2238                 impl=self.tag,
2239                 expl=self._expl,
2240             )
2241             if self._value is None:
2242                 self._value = default
2243
2244     def __add__(self, their):
2245         if isinstance(their, self.__class__):
2246             return self.__class__(self._value + their._value)
2247         if isinstance(their, tuple):
2248             return self.__class__(self._value + their)
2249         raise InvalidValueType((self.__class__, tuple))
2250
2251     def _value_sanitize(self, value):
2252         if issubclass(value.__class__, ObjectIdentifier):
2253             return value._value
2254         if isinstance(value, string_types):
2255             try:
2256                 value = tuple(int(arc) for arc in value.split("."))
2257             except ValueError:
2258                 raise InvalidOID("unacceptable arcs values")
2259         if isinstance(value, tuple):
2260             if len(value) < 2:
2261                 raise InvalidOID("less than 2 arcs")
2262             first_arc = value[0]
2263             if first_arc in (0, 1):
2264                 if not (0 <= value[1] <= 39):
2265                     raise InvalidOID("second arc is too wide")
2266             elif first_arc == 2:
2267                 pass
2268             else:
2269                 raise InvalidOID("unacceptable first arc value")
2270             return value
2271         raise InvalidValueType((self.__class__, str, tuple))
2272
2273     @property
2274     def ready(self):
2275         return self._value is not None
2276
2277     def copy(self):
2278         obj = self.__class__()
2279         obj._value = self._value
2280         obj.tag = self.tag
2281         obj._expl = self._expl
2282         obj.default = self.default
2283         obj.optional = self.optional
2284         obj.offset = self.offset
2285         obj.llen = self.llen
2286         obj.vlen = self.vlen
2287         return obj
2288
2289     def __iter__(self):
2290         self._assert_ready()
2291         return iter(self._value)
2292
2293     def __str__(self):
2294         return ".".join(str(arc) for arc in self._value or ())
2295
2296     def __hash__(self):
2297         self._assert_ready()
2298         return hash(
2299             self.tag +
2300             bytes(self._expl or b"") +
2301             str(self._value).encode("ascii"),
2302         )
2303
2304     def __eq__(self, their):
2305         if isinstance(their, tuple):
2306             return self._value == their
2307         if not issubclass(their.__class__, ObjectIdentifier):
2308             return False
2309         return (
2310             self.tag == their.tag and
2311             self._expl == their._expl and
2312             self._value == their._value
2313         )
2314
2315     def __lt__(self, their):
2316         return self._value < their
2317
2318     def __gt__(self, their):
2319         return self._value > their
2320
2321     def __call__(
2322             self,
2323             value=None,
2324             impl=None,
2325             expl=None,
2326             default=None,
2327             optional=None,
2328     ):
2329         return self.__class__(
2330             value=value,
2331             impl=self.tag if impl is None else impl,
2332             expl=self._expl if expl is None else expl,
2333             default=self.default if default is None else default,
2334             optional=self.optional if optional is None else optional,
2335         )
2336
2337     def _encode(self):
2338         self._assert_ready()
2339         value = self._value
2340         first_value = value[1]
2341         first_arc = value[0]
2342         if first_arc == 0:
2343             pass
2344         elif first_arc == 1:
2345             first_value += 40
2346         elif first_arc == 2:
2347             first_value += 80
2348         else:  # pragma: no cover
2349             raise RuntimeError("invalid arc is stored")
2350         octets = [zero_ended_encode(first_value)]
2351         for arc in value[2:]:
2352             octets.append(zero_ended_encode(arc))
2353         v = b"".join(octets)
2354         return b"".join((self.tag, len_encode(len(v)), v))
2355
2356     def _decode(self, tlv, offset=0, decode_path=()):
2357         try:
2358             t, _, lv = tag_strip(tlv)
2359         except DecodeError as err:
2360             raise err.__class__(
2361                 msg=err.msg,
2362                 klass=self.__class__,
2363                 decode_path=decode_path,
2364                 offset=offset,
2365             )
2366         if t != self.tag:
2367             raise TagMismatch(
2368                 klass=self.__class__,
2369                 decode_path=decode_path,
2370                 offset=offset,
2371             )
2372         try:
2373             l, llen, v = len_decode(lv)
2374         except DecodeError as err:
2375             raise err.__class__(
2376                 msg=err.msg,
2377                 klass=self.__class__,
2378                 decode_path=decode_path,
2379                 offset=offset,
2380             )
2381         if l > len(v):
2382             raise NotEnoughData(
2383                 "encoded length is longer than data",
2384                 klass=self.__class__,
2385                 decode_path=decode_path,
2386                 offset=offset,
2387             )
2388         if l == 0:
2389             raise NotEnoughData(
2390                 "zero length",
2391                 klass=self.__class__,
2392                 decode_path=decode_path,
2393                 offset=offset,
2394             )
2395         v, tail = v[:l], v[l:]
2396         arcs = []
2397         while len(v) > 0:
2398             i = 0
2399             arc = 0
2400             while True:
2401                 octet = indexbytes(v, i)
2402                 arc = (arc << 7) | (octet & 0x7F)
2403                 if octet & 0x80 == 0:
2404                     arcs.append(arc)
2405                     v = v[i + 1:]
2406                     break
2407                 i += 1
2408                 if i == len(v):
2409                     raise DecodeError(
2410                         "unfinished OID",
2411                         klass=self.__class__,
2412                         decode_path=decode_path,
2413                         offset=offset,
2414                     )
2415         first_arc = 0
2416         second_arc = arcs[0]
2417         if 0 <= second_arc <= 39:
2418             first_arc = 0
2419         elif 40 <= second_arc <= 79:
2420             first_arc = 1
2421             second_arc -= 40
2422         else:
2423             first_arc = 2
2424             second_arc -= 80
2425         obj = self.__class__(
2426             value=tuple([first_arc, second_arc] + arcs[1:]),
2427             impl=self.tag,
2428             expl=self._expl,
2429             default=self.default,
2430             optional=self.optional,
2431             _decoded=(offset, llen, l),
2432         )
2433         return obj, tail
2434
2435     def __repr__(self):
2436         return pp_console_row(next(self.pps()))
2437
2438     def pps(self, decode_path=()):
2439         yield _pp(
2440             asn1_type_name=self.asn1_type_name,
2441             obj_name=self.__class__.__name__,
2442             decode_path=decode_path,
2443             value=str(self) if self.ready else None,
2444             optional=self.optional,
2445             default=self == self.default,
2446             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2447             expl=None if self._expl is None else tag_decode(self._expl),
2448             offset=self.offset,
2449             tlen=self.tlen,
2450             llen=self.llen,
2451             vlen=self.vlen,
2452             expl_offset=self.expl_offset if self.expled else None,
2453             expl_tlen=self.expl_tlen if self.expled else None,
2454             expl_llen=self.expl_llen if self.expled else None,
2455             expl_vlen=self.expl_vlen if self.expled else None,
2456         )
2457
2458
2459 class Enumerated(Integer):
2460     """``ENUMERATED`` integer type
2461
2462     This type is identical to :py:class:`pyderasn.Integer`, but requires
2463     schema to be specified and does not accept values missing from it.
2464     """
2465     __slots__ = ()
2466     tag_default = tag_encode(10)
2467     asn1_type_name = "ENUMERATED"
2468
2469     def __init__(
2470             self,
2471             value=None,
2472             impl=None,
2473             expl=None,
2474             default=None,
2475             optional=False,
2476             _specs=None,
2477             _decoded=(0, 0, 0),
2478             bounds=None,  # dummy argument, workability for Integer.decode
2479     ):
2480         super(Enumerated, self).__init__(
2481             value=value,
2482             impl=impl,
2483             expl=expl,
2484             default=default,
2485             optional=optional,
2486             _specs=_specs,
2487             _decoded=_decoded,
2488         )
2489         if len(self.specs) == 0:
2490             raise ValueError("schema must be specified")
2491
2492     def _value_sanitize(self, value):
2493         if isinstance(value, self.__class__):
2494             value = value._value
2495         elif isinstance(value, integer_types):
2496             if value not in list(self.specs.values()):
2497                 raise DecodeError(
2498                     "unknown integer value: %s" % value,
2499                     klass=self.__class__,
2500                 )
2501         elif isinstance(value, string_types):
2502             value = self.specs.get(value)
2503             if value is None:
2504                 raise ObjUnknown("integer value: %s" % value)
2505         else:
2506             raise InvalidValueType((self.__class__, int, str))
2507         return value
2508
2509     def copy(self):
2510         obj = self.__class__(_specs=self.specs)
2511         obj._value = self._value
2512         obj._bound_min = self._bound_min
2513         obj._bound_max = self._bound_max
2514         obj.tag = self.tag
2515         obj._expl = self._expl
2516         obj.default = self.default
2517         obj.optional = self.optional
2518         obj.offset = self.offset
2519         obj.llen = self.llen
2520         obj.vlen = self.vlen
2521         return obj
2522
2523     def __call__(
2524             self,
2525             value=None,
2526             impl=None,
2527             expl=None,
2528             default=None,
2529             optional=None,
2530             _specs=None,
2531     ):
2532         return self.__class__(
2533             value=value,
2534             impl=self.tag if impl is None else impl,
2535             expl=self._expl if expl is None else expl,
2536             default=self.default if default is None else default,
2537             optional=self.optional if optional is None else optional,
2538             _specs=self.specs,
2539         )
2540
2541
2542 class CommonString(OctetString):
2543     """Common class for all strings
2544
2545     Everything resembles :py:class:`pyderasn.OctetString`, except
2546     ability to deal with unicode text strings.
2547
2548     >>> hexenc("привет мир".encode("utf-8"))
2549     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2550     >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2551     True
2552     >>> s = UTF8String("привет мир")
2553     UTF8String UTF8String привет мир
2554     >>> str(s)
2555     'привет мир'
2556     >>> hexenc(bytes(s))
2557     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2558
2559     >>> PrintableString("привет мир")
2560     Traceback (most recent call last):
2561     UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2562
2563     >>> BMPString("ада", bounds=(2, 2))
2564     Traceback (most recent call last):
2565     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2566     >>> s = BMPString("ад", bounds=(2, 2))
2567     >>> s.encoding
2568     'utf-16-be'
2569     >>> hexenc(bytes(s))
2570     '04300434'
2571
2572     .. list-table::
2573        :header-rows: 1
2574
2575        * - Class
2576          - Text Encoding
2577        * - :py:class:`pyderasn.UTF8String`
2578          - utf-8
2579        * - :py:class:`pyderasn.NumericString`
2580          - ascii
2581        * - :py:class:`pyderasn.PrintableString`
2582          - ascii
2583        * - :py:class:`pyderasn.TeletexString`
2584          - ascii
2585        * - :py:class:`pyderasn.T61String`
2586          - ascii
2587        * - :py:class:`pyderasn.VideotexString`
2588          - iso-8859-1
2589        * - :py:class:`pyderasn.IA5String`
2590          - ascii
2591        * - :py:class:`pyderasn.GraphicString`
2592          - iso-8859-1
2593        * - :py:class:`pyderasn.VisibleString`
2594          - ascii
2595        * - :py:class:`pyderasn.ISO646String`
2596          - ascii
2597        * - :py:class:`pyderasn.GeneralString`
2598          - iso-8859-1
2599        * - :py:class:`pyderasn.UniversalString`
2600          - utf-32-be
2601        * - :py:class:`pyderasn.BMPString`
2602          - utf-16-be
2603     """
2604     __slots__ = ("encoding",)
2605
2606     def _value_sanitize(self, value):
2607         value_raw = None
2608         value_decoded = None
2609         if isinstance(value, self.__class__):
2610             value_raw = value._value
2611         elif isinstance(value, text_type):
2612             value_decoded = value
2613         elif isinstance(value, binary_type):
2614             value_raw = value
2615         else:
2616             raise InvalidValueType((self.__class__, text_type, binary_type))
2617         value_raw = (
2618             value_decoded.encode(self.encoding)
2619             if value_raw is None else value_raw
2620         )
2621         value_decoded = (
2622             value_raw.decode(self.encoding)
2623             if value_decoded is None else value_decoded
2624         )
2625         if not self._bound_min <= len(value_decoded) <= self._bound_max:
2626             raise BoundsError(
2627                 self._bound_min,
2628                 len(value_decoded),
2629                 self._bound_max,
2630             )
2631         return value_raw
2632
2633     def __eq__(self, their):
2634         if isinstance(their, binary_type):
2635             return self._value == their
2636         if isinstance(their, text_type):
2637             return self._value == their.encode(self.encoding)
2638         if not isinstance(their, self.__class__):
2639             return False
2640         return (
2641             self._value == their._value and
2642             self.tag == their.tag and
2643             self._expl == their._expl
2644         )
2645
2646     def __unicode__(self):
2647         if self.ready:
2648             return self._value.decode(self.encoding)
2649         return text_type(self._value)
2650
2651     def __repr__(self):
2652         return pp_console_row(next(self.pps(no_unicode=PY2)))
2653
2654     def pps(self, decode_path=(), no_unicode=False):
2655         value = None
2656         if self.ready:
2657             value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2658         yield _pp(
2659             asn1_type_name=self.asn1_type_name,
2660             obj_name=self.__class__.__name__,
2661             decode_path=decode_path,
2662             value=value,
2663             optional=self.optional,
2664             default=self == self.default,
2665             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2666             expl=None if self._expl is None else tag_decode(self._expl),
2667             offset=self.offset,
2668             tlen=self.tlen,
2669             llen=self.llen,
2670             vlen=self.vlen,
2671         )
2672
2673
2674 class UTF8String(CommonString):
2675     __slots__ = ()
2676     tag_default = tag_encode(12)
2677     encoding = "utf-8"
2678     asn1_type_name = "UTF8String"
2679
2680
2681 class NumericString(CommonString):
2682     __slots__ = ()
2683     tag_default = tag_encode(18)
2684     encoding = "ascii"
2685     asn1_type_name = "NumericString"
2686
2687
2688 class PrintableString(CommonString):
2689     __slots__ = ()
2690     tag_default = tag_encode(19)
2691     encoding = "ascii"
2692     asn1_type_name = "PrintableString"
2693
2694
2695 class TeletexString(CommonString):
2696     __slots__ = ()
2697     tag_default = tag_encode(20)
2698     encoding = "ascii"
2699     asn1_type_name = "TeletexString"
2700
2701
2702 class T61String(TeletexString):
2703     __slots__ = ()
2704     asn1_type_name = "T61String"
2705
2706
2707 class VideotexString(CommonString):
2708     __slots__ = ()
2709     tag_default = tag_encode(21)
2710     encoding = "iso-8859-1"
2711     asn1_type_name = "VideotexString"
2712
2713
2714 class IA5String(CommonString):
2715     __slots__ = ()
2716     tag_default = tag_encode(22)
2717     encoding = "ascii"
2718     asn1_type_name = "IA5"
2719
2720
2721 class UTCTime(CommonString):
2722     """``UTCTime`` datetime type
2723
2724     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2725     UTCTime UTCTime 2017-09-30T22:07:50
2726     >>> str(t)
2727     '170930220750Z'
2728     >>> bytes(t)
2729     b'170930220750Z'
2730     >>> t.todatetime()
2731     datetime.datetime(2017, 9, 30, 22, 7, 50)
2732     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2733     datetime.datetime(1957, 9, 30, 22, 7, 50)
2734     """
2735     __slots__ = ()
2736     tag_default = tag_encode(23)
2737     encoding = "ascii"
2738     asn1_type_name = "UTCTime"
2739
2740     fmt = "%y%m%d%H%M%SZ"
2741
2742     def __init__(
2743             self,
2744             value=None,
2745             impl=None,
2746             expl=None,
2747             default=None,
2748             optional=False,
2749             _decoded=(0, 0, 0),
2750             bounds=None,  # dummy argument, workability for OctetString.decode
2751     ):
2752         """
2753         :param value: set the value. Either datetime type, or
2754                       :py:class:`pyderasn.UTCTime` object
2755         :param bytes impl: override default tag with ``IMPLICIT`` one
2756         :param bytes expl: override default tag with ``EXPLICIT`` one
2757         :param default: set default value. Type same as in ``value``
2758         :param bool optional: is object ``OPTIONAL`` in sequence
2759         """
2760         super(UTCTime, self).__init__(
2761             impl=impl,
2762             expl=expl,
2763             default=default,
2764             optional=optional,
2765             _decoded=_decoded,
2766         )
2767         self._value = value
2768         if value is not None:
2769             self._value = self._value_sanitize(value)
2770         if default is not None:
2771             default = self._value_sanitize(default)
2772             self.default = self.__class__(
2773                 value=default,
2774                 impl=self.tag,
2775                 expl=self._expl,
2776             )
2777             if self._value is None:
2778                 self._value = default
2779
2780     def _value_sanitize(self, value):
2781         if isinstance(value, self.__class__):
2782             return value._value
2783         if isinstance(value, datetime):
2784             return value.strftime(self.fmt).encode("ascii")
2785         if isinstance(value, binary_type):
2786             value_decoded = value.decode("ascii")
2787             if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2788                 try:
2789                     datetime.strptime(value_decoded, self.fmt)
2790                 except ValueError:
2791                     raise DecodeError("invalid UTCTime format")
2792                 return value
2793             else:
2794                 raise DecodeError("invalid UTCTime length")
2795         raise InvalidValueType((self.__class__, datetime))
2796
2797     def __eq__(self, their):
2798         if isinstance(their, binary_type):
2799             return self._value == their
2800         if isinstance(their, datetime):
2801             return self.todatetime() == their
2802         if not isinstance(their, self.__class__):
2803             return False
2804         return (
2805             self._value == their._value and
2806             self.tag == their.tag and
2807             self._expl == their._expl
2808         )
2809
2810     def todatetime(self):
2811         """Convert to datetime
2812
2813         :returns: datetime
2814
2815         Pay attention that UTCTime can not hold full year, so all years
2816         having < 50 years are treated as 20xx, 19xx otherwise, according
2817         to X.509 recomendation.
2818         """
2819         value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2820         year = value.year % 100
2821         return datetime(
2822             year=(2000 + year) if year < 50 else (1900 + year),
2823             month=value.month,
2824             day=value.day,
2825             hour=value.hour,
2826             minute=value.minute,
2827             second=value.second,
2828         )
2829
2830     def __repr__(self):
2831         return pp_console_row(next(self.pps()))
2832
2833     def pps(self, decode_path=()):
2834         yield _pp(
2835             asn1_type_name=self.asn1_type_name,
2836             obj_name=self.__class__.__name__,
2837             decode_path=decode_path,
2838             value=self.todatetime().isoformat() if self.ready else None,
2839             optional=self.optional,
2840             default=self == self.default,
2841             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2842             expl=None if self._expl is None else tag_decode(self._expl),
2843             offset=self.offset,
2844             tlen=self.tlen,
2845             llen=self.llen,
2846             vlen=self.vlen,
2847         )
2848
2849
2850 class GeneralizedTime(UTCTime):
2851     """``GeneralizedTime`` datetime type
2852
2853     This type is similar to :py:class:`pyderasn.UTCTime`.
2854
2855     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2856     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2857     >>> str(t)
2858     '20170930220750.000123Z'
2859     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2860     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2861     """
2862     __slots__ = ()
2863     tag_default = tag_encode(24)
2864     asn1_type_name = "GeneralizedTime"
2865
2866     fmt = "%Y%m%d%H%M%SZ"
2867     fmt_ms = "%Y%m%d%H%M%S.%fZ"
2868
2869     def _value_sanitize(self, value):
2870         if isinstance(value, self.__class__):
2871             return value._value
2872         if isinstance(value, datetime):
2873             return value.strftime(
2874                 self.fmt_ms if value.microsecond > 0 else self.fmt
2875             ).encode("ascii")
2876         if isinstance(value, binary_type):
2877             value_decoded = value.decode("ascii")
2878             if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2879                 try:
2880                     datetime.strptime(value_decoded, self.fmt)
2881                 except ValueError:
2882                     raise DecodeError(
2883                         "invalid GeneralizedTime (without ms) format",
2884                     )
2885                 return value
2886             elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2887                 try:
2888                     datetime.strptime(value_decoded, self.fmt_ms)
2889                 except ValueError:
2890                     raise DecodeError(
2891                         "invalid GeneralizedTime (with ms) format",
2892                     )
2893                 return value
2894             else:
2895                 raise DecodeError(
2896                     "invalid GeneralizedTime length",
2897                     klass=self.__class__,
2898                 )
2899         raise InvalidValueType((self.__class__, datetime))
2900
2901     def todatetime(self):
2902         value = self._value.decode("ascii")
2903         if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2904             return datetime.strptime(value, self.fmt)
2905         return datetime.strptime(value, self.fmt_ms)
2906
2907
2908 class GraphicString(CommonString):
2909     __slots__ = ()
2910     tag_default = tag_encode(25)
2911     encoding = "iso-8859-1"
2912     asn1_type_name = "GraphicString"
2913
2914
2915 class VisibleString(CommonString):
2916     __slots__ = ()
2917     tag_default = tag_encode(26)
2918     encoding = "ascii"
2919     asn1_type_name = "VisibleString"
2920
2921
2922 class ISO646String(VisibleString):
2923     __slots__ = ()
2924     asn1_type_name = "ISO646String"
2925
2926
2927 class GeneralString(CommonString):
2928     __slots__ = ()
2929     tag_default = tag_encode(27)
2930     encoding = "iso-8859-1"
2931     asn1_type_name = "GeneralString"
2932
2933
2934 class UniversalString(CommonString):
2935     __slots__ = ()
2936     tag_default = tag_encode(28)
2937     encoding = "utf-32-be"
2938     asn1_type_name = "UniversalString"
2939
2940
2941 class BMPString(CommonString):
2942     __slots__ = ()
2943     tag_default = tag_encode(30)
2944     encoding = "utf-16-be"
2945     asn1_type_name = "BMPString"
2946
2947
2948 class Choice(Obj):
2949     """``CHOICE`` special type
2950
2951     ::
2952
2953         class GeneralName(Choice):
2954             schema = (
2955                 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2956                 ('dNSName', IA5String(impl=tag_ctxp(2))),
2957             )
2958
2959     >>> gn = GeneralName()
2960     GeneralName CHOICE
2961     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2962     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2963     >>> gn["dNSName"] = IA5String("bar.baz")
2964     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2965     >>> gn["rfc822Name"]
2966     None
2967     >>> gn["dNSName"]
2968     [2] IA5String IA5 bar.baz
2969     >>> gn.choice
2970     'dNSName'
2971     >>> gn.value == gn["dNSName"]
2972     True
2973     >>> gn.specs
2974     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2975
2976     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2977     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2978     """
2979     __slots__ = ("specs",)
2980     tag_default = None
2981     asn1_type_name = "CHOICE"
2982
2983     def __init__(
2984             self,
2985             value=None,
2986             schema=None,
2987             impl=None,
2988             expl=None,
2989             default=None,
2990             optional=False,
2991             _decoded=(0, 0, 0),
2992     ):
2993         """
2994         :param value: set the value. Either ``(choice, value)`` tuple, or
2995                       :py:class:`pyderasn.Choice` object
2996         :param bytes impl: can not be set, do **not** use it
2997         :param bytes expl: override default tag with ``EXPLICIT`` one
2998         :param default: set default value. Type same as in ``value``
2999         :param bool optional: is object ``OPTIONAL`` in sequence
3000         """
3001         if impl is not None:
3002             raise ValueError("no implicit tag allowed for CHOICE")
3003         super(Choice, self).__init__(None, expl, default, optional, _decoded)
3004         if schema is None:
3005             schema = getattr(self, "schema", ())
3006         if len(schema) == 0:
3007             raise ValueError("schema must be specified")
3008         self.specs = (
3009             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3010         )
3011         self._value = None
3012         if value is not None:
3013             self._value = self._value_sanitize(value)
3014         if default is not None:
3015             default_value = self._value_sanitize(default)
3016             default_obj = self.__class__(impl=self.tag, expl=self._expl)
3017             default_obj.specs = self.specs
3018             default_obj._value = default_value
3019             self.default = default_obj
3020             if value is None:
3021                 self._value = default_obj.copy()._value
3022
3023     def _value_sanitize(self, value):
3024         if isinstance(value, self.__class__):
3025             return value._value
3026         if isinstance(value, tuple) and len(value) == 2:
3027             choice, obj = value
3028             spec = self.specs.get(choice)
3029             if spec is None:
3030                 raise ObjUnknown(choice)
3031             if not isinstance(obj, spec.__class__):
3032                 raise InvalidValueType((spec,))
3033             return (choice, spec(obj))
3034         raise InvalidValueType((self.__class__, tuple))
3035
3036     @property
3037     def ready(self):
3038         return self._value is not None and self._value[1].ready
3039
3040     def copy(self):
3041         obj = self.__class__(schema=self.specs)
3042         obj._expl = self._expl
3043         obj.default = self.default
3044         obj.optional = self.optional
3045         obj.offset = self.offset
3046         obj.llen = self.llen
3047         obj.vlen = self.vlen
3048         value = self._value
3049         if value is not None:
3050             obj._value = (value[0], value[1].copy())
3051         return obj
3052
3053     def __eq__(self, their):
3054         if isinstance(their, tuple) and len(their) == 2:
3055             return self._value == their
3056         if not isinstance(their, self.__class__):
3057             return False
3058         return (
3059             self.specs == their.specs and
3060             self._value == their._value
3061         )
3062
3063     def __call__(
3064             self,
3065             value=None,
3066             expl=None,
3067             default=None,
3068             optional=None,
3069     ):
3070         return self.__class__(
3071             value=value,
3072             schema=self.specs,
3073             expl=self._expl if expl is None else expl,
3074             default=self.default if default is None else default,
3075             optional=self.optional if optional is None else optional,
3076         )
3077
3078     @property
3079     def choice(self):
3080         self._assert_ready()
3081         return self._value[0]
3082
3083     @property
3084     def value(self):
3085         self._assert_ready()
3086         return self._value[1]
3087
3088     def __getitem__(self, key):
3089         if key not in self.specs:
3090             raise ObjUnknown(key)
3091         if self._value is None:
3092             return None
3093         choice, value = self._value
3094         if choice != key:
3095             return None
3096         return value
3097
3098     def __setitem__(self, key, value):
3099         spec = self.specs.get(key)
3100         if spec is None:
3101             raise ObjUnknown(key)
3102         if not isinstance(value, spec.__class__):
3103             raise InvalidValueType((spec.__class__,))
3104         self._value = (key, spec(value))
3105
3106     @property
3107     def tlen(self):
3108         return 0
3109
3110     @property
3111     def decoded(self):
3112         return self._value[1].decoded if self.ready else False
3113
3114     def _encode(self):
3115         self._assert_ready()
3116         return self._value[1].encode()
3117
3118     def _decode(self, tlv, offset=0, decode_path=()):
3119         for choice, spec in self.specs.items():
3120             try:
3121                 value, tail = spec.decode(
3122                     tlv,
3123                     offset=offset,
3124                     leavemm=True,
3125                     decode_path=decode_path + (choice,),
3126                 )
3127             except TagMismatch:
3128                 continue
3129             obj = self.__class__(
3130                 schema=self.specs,
3131                 expl=self._expl,
3132                 default=self.default,
3133                 optional=self.optional,
3134                 _decoded=(offset, 0, value.tlvlen),
3135             )
3136             obj._value = (choice, value)
3137             return obj, tail
3138         raise TagMismatch(
3139             klass=self.__class__,
3140             decode_path=decode_path,
3141             offset=offset,
3142         )
3143
3144     def __repr__(self):
3145         value = pp_console_row(next(self.pps()))
3146         if self.ready:
3147             value = "%s[%r]" % (value, self.value)
3148         return value
3149
3150     def pps(self, decode_path=()):
3151         yield _pp(
3152             asn1_type_name=self.asn1_type_name,
3153             obj_name=self.__class__.__name__,
3154             decode_path=decode_path,
3155             value=self.choice if self.ready else None,
3156             optional=self.optional,
3157             default=self == self.default,
3158             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3159             expl=None if self._expl is None else tag_decode(self._expl),
3160             offset=self.offset,
3161             tlen=self.tlen,
3162             llen=self.llen,
3163             vlen=self.vlen,
3164         )
3165         if self.ready:
3166             yield self.value.pps(decode_path=decode_path + (self.choice,))
3167
3168
3169 class PrimitiveTypes(Choice):
3170     """Predefined ``CHOICE`` for all generic primitive types
3171
3172     It could be useful for general decoding of some unspecified values:
3173
3174     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3175     OCTET STRING 3 bytes 666f6f
3176     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3177     INTEGER 1193046
3178     """
3179     __slots__ = ()
3180     schema = tuple((klass.__name__, klass()) for klass in (
3181         Boolean,
3182         Integer,
3183         BitString,
3184         OctetString,
3185         Null,
3186         ObjectIdentifier,
3187         UTF8String,
3188         NumericString,
3189         PrintableString,
3190         TeletexString,
3191         VideotexString,
3192         IA5String,
3193         UTCTime,
3194         GeneralizedTime,
3195         GraphicString,
3196         VisibleString,
3197         ISO646String,
3198         GeneralString,
3199         UniversalString,
3200         BMPString,
3201     ))
3202
3203
3204 class Any(Obj):
3205     """``ANY`` special type
3206
3207     >>> Any(Integer(-123))
3208     ANY 020185
3209     >>> a = Any(OctetString(b"hello world").encode())
3210     ANY 040b68656c6c6f20776f726c64
3211     >>> hexenc(bytes(a))
3212     b'0x040x0bhello world'
3213     """
3214     __slots__ = ()
3215     tag_default = tag_encode(0)
3216     asn1_type_name = "ANY"
3217
3218     def __init__(
3219             self,
3220             value=None,
3221             expl=None,
3222             optional=False,
3223             _decoded=(0, 0, 0),
3224     ):
3225         """
3226         :param value: set the value. Either any kind of pyderasn's
3227                       **ready** object, or bytes. Pay attention that
3228                       **no** validation is performed is raw binary value
3229                       is valid TLV
3230         :param bytes expl: override default tag with ``EXPLICIT`` one
3231         :param bool optional: is object ``OPTIONAL`` in sequence
3232         """
3233         super(Any, self).__init__(None, expl, None, optional, _decoded)
3234         self._value = None if value is None else self._value_sanitize(value)
3235
3236     def _value_sanitize(self, value):
3237         if isinstance(value, self.__class__):
3238             return value._value
3239         if isinstance(value, Obj):
3240             return value.encode()
3241         if isinstance(value, binary_type):
3242             return value
3243         raise InvalidValueType((self.__class__, Obj, binary_type))
3244
3245     @property
3246     def ready(self):
3247         return self._value is not None
3248
3249     def copy(self):
3250         obj = self.__class__()
3251         obj._value = self._value
3252         obj.tag = self.tag
3253         obj._expl = self._expl
3254         obj.optional = self.optional
3255         obj.offset = self.offset
3256         obj.llen = self.llen
3257         obj.vlen = self.vlen
3258         return obj
3259
3260     def __eq__(self, their):
3261         if isinstance(their, binary_type):
3262             return self._value == their
3263         if issubclass(their.__class__, Any):
3264             return self._value == their._value
3265         return False
3266
3267     def __call__(
3268             self,
3269             value=None,
3270             expl=None,
3271             optional=None,
3272     ):
3273         return self.__class__(
3274             value=value,
3275             expl=self._expl if expl is None else expl,
3276             optional=self.optional if optional is None else optional,
3277         )
3278
3279     def __bytes__(self):
3280         self._assert_ready()
3281         return self._value
3282
3283     @property
3284     def tlen(self):
3285         return 0
3286
3287     def _encode(self):
3288         self._assert_ready()
3289         return self._value
3290
3291     def _decode(self, tlv, offset=0, decode_path=()):
3292         try:
3293             t, tlen, lv = tag_strip(tlv)
3294             l, llen, v = len_decode(lv)
3295         except DecodeError as err:
3296             raise err.__class__(
3297                 msg=err.msg,
3298                 klass=self.__class__,
3299                 decode_path=decode_path,
3300                 offset=offset,
3301             )
3302         if l > len(v):
3303             raise NotEnoughData(
3304                 "encoded length is longer than data",
3305                 klass=self.__class__,
3306                 decode_path=decode_path,
3307                 offset=offset,
3308             )
3309         tlvlen = tlen + llen + l
3310         v, tail = tlv[:tlvlen], v[l:]
3311         obj = self.__class__(
3312             value=v.tobytes(),
3313             expl=self._expl,
3314             optional=self.optional,
3315             _decoded=(offset, 0, tlvlen),
3316         )
3317         obj.tag = t
3318         return obj, tail
3319
3320     def __repr__(self):
3321         return pp_console_row(next(self.pps()))
3322
3323     def pps(self, decode_path=()):
3324         yield _pp(
3325             asn1_type_name=self.asn1_type_name,
3326             obj_name=self.__class__.__name__,
3327             decode_path=decode_path,
3328             blob=self._value if self.ready else None,
3329             optional=self.optional,
3330             default=self == self.default,
3331             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3332             expl=None if self._expl is None else tag_decode(self._expl),
3333             offset=self.offset,
3334             tlen=self.tlen,
3335             llen=self.llen,
3336             vlen=self.vlen,
3337             expl_offset=self.expl_offset if self.expled else None,
3338             expl_tlen=self.expl_tlen if self.expled else None,
3339             expl_llen=self.expl_llen if self.expled else None,
3340             expl_vlen=self.expl_vlen if self.expled else None,
3341         )
3342
3343
3344 ########################################################################
3345 # ASN.1 constructed types
3346 ########################################################################
3347
3348 class Sequence(Obj):
3349     """``SEQUENCE`` structure type
3350
3351     You have to make specification of sequence::
3352
3353         class Extension(Sequence):
3354             __slots__ = ()
3355             schema = (
3356                 ("extnID", ObjectIdentifier()),
3357                 ("critical", Boolean(default=False)),
3358                 ("extnValue", OctetString()),
3359             )
3360
3361     Then, you can work with it as with dictionary.
3362
3363     >>> ext = Extension()
3364     >>> Extension().specs
3365     OrderedDict([
3366         ('extnID', OBJECT IDENTIFIER),
3367         ('critical', BOOLEAN False OPTIONAL DEFAULT),
3368         ('extnValue', OCTET STRING),
3369     ])
3370     >>> ext["extnID"] = "1.2.3"
3371     Traceback (most recent call last):
3372     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3373     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3374
3375     You can know if sequence is ready to be encoded:
3376
3377     >>> ext.ready
3378     False
3379     >>> ext.encode()
3380     Traceback (most recent call last):
3381     pyderasn.ObjNotReady: object is not ready: extnValue
3382     >>> ext["extnValue"] = OctetString(b"foobar")
3383     >>> ext.ready
3384     True
3385
3386     Value you want to assign, must have the same **type** as in
3387     corresponding specification, but it can have different tags,
3388     optional/default attributes -- they will be taken from specification
3389     automatically::
3390
3391         class TBSCertificate(Sequence):
3392             schema = (
3393                 ("version", Version(expl=tag_ctxc(0), default="v1")),
3394             [...]
3395
3396     >>> tbs = TBSCertificate()
3397     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3398
3399     You can know if value exists/set in the sequence and take its value:
3400
3401     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3402     (True, True, False)
3403     >>> ext["extnID"]
3404     OBJECT IDENTIFIER 1.2.3
3405
3406     But pay attention that if value has default, then it won't be (not
3407     in) in the sequence (because ``DEFAULT`` must not be encoded in
3408     DER), but you can read its value:
3409
3410     >>> "critical" in ext, ext["critical"]
3411     (False, BOOLEAN False)
3412     >>> ext["critical"] = Boolean(True)
3413     >>> "critical" in ext, ext["critical"]
3414     (True, BOOLEAN True)
3415
3416     All defaulted values are always optional.
3417
3418     .. warning::
3419
3420        When decoded DER contains defaulted value inside, then
3421        technically this is not valid DER encoding. But we allow
3422        and pass it. Of course reencoding of that kind of DER will
3423        result in different binary representation (validly without
3424        defaulted value inside).
3425
3426     Two sequences are equal if they have equal specification (schema),
3427     implicit/explicit tagging and the same values.
3428     """
3429     __slots__ = ("specs",)
3430     tag_default = tag_encode(form=TagFormConstructed, num=16)
3431     asn1_type_name = "SEQUENCE"
3432
3433     def __init__(
3434             self,
3435             value=None,
3436             schema=None,
3437             impl=None,
3438             expl=None,
3439             default=None,
3440             optional=False,
3441             _decoded=(0, 0, 0),
3442     ):
3443         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3444         if schema is None:
3445             schema = getattr(self, "schema", ())
3446         self.specs = (
3447             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3448         )
3449         self._value = {}
3450         if value is not None:
3451             self._value = self._value_sanitize(value)
3452         if default is not None:
3453             default_value = self._value_sanitize(default)
3454             default_obj = self.__class__(impl=self.tag, expl=self._expl)
3455             default_obj.specs = self.specs
3456             default_obj._value = default_value
3457             self.default = default_obj
3458             if value is None:
3459                 self._value = default_obj.copy()._value
3460
3461     def _value_sanitize(self, value):
3462         if not issubclass(value.__class__, Sequence):
3463             raise InvalidValueType((Sequence,))
3464         return value._value
3465
3466     @property
3467     def ready(self):
3468         for name, spec in self.specs.items():
3469             value = self._value.get(name)
3470             if value is None:
3471                 if spec.optional:
3472                     continue
3473                 return False
3474             else:
3475                 if not value.ready:
3476                     return False
3477         return True
3478
3479     def copy(self):
3480         obj = self.__class__(schema=self.specs)
3481         obj.tag = self.tag
3482         obj._expl = self._expl
3483         obj.default = self.default
3484         obj.optional = self.optional
3485         obj.offset = self.offset
3486         obj.llen = self.llen
3487         obj.vlen = self.vlen
3488         obj._value = {k: v.copy() for k, v in self._value.items()}
3489         return obj
3490
3491     def __eq__(self, their):
3492         if not isinstance(their, self.__class__):
3493             return False
3494         return (
3495             self.specs == their.specs and
3496             self.tag == their.tag and
3497             self._expl == their._expl and
3498             self._value == their._value
3499         )
3500
3501     def __call__(
3502             self,
3503             value=None,
3504             impl=None,
3505             expl=None,
3506             default=None,
3507             optional=None,
3508     ):
3509         return self.__class__(
3510             value=value,
3511             schema=self.specs,
3512             impl=self.tag if impl is None else impl,
3513             expl=self._expl if expl is None else expl,
3514             default=self.default if default is None else default,
3515             optional=self.optional if optional is None else optional,
3516         )
3517
3518     def __contains__(self, key):
3519         return key in self._value
3520
3521     def __setitem__(self, key, value):
3522         spec = self.specs.get(key)
3523         if spec is None:
3524             raise ObjUnknown(key)
3525         if value is None:
3526             self._value.pop(key, None)
3527             return
3528         if not isinstance(value, spec.__class__):
3529             raise InvalidValueType((spec.__class__,))
3530         value = spec(value=value)
3531         if spec.default is not None and value == spec.default:
3532             self._value.pop(key, None)
3533             return
3534         self._value[key] = value
3535
3536     def __getitem__(self, key):
3537         value = self._value.get(key)
3538         if value is not None:
3539             return value
3540         spec = self.specs.get(key)
3541         if spec is None:
3542             raise ObjUnknown(key)
3543         if spec.default is not None:
3544             return spec.default
3545         return None
3546
3547     def _encoded_values(self):
3548         raws = []
3549         for name, spec in self.specs.items():
3550             value = self._value.get(name)
3551             if value is None:
3552                 if spec.optional:
3553                     continue
3554                 raise ObjNotReady(name)
3555             raws.append(value.encode())
3556         return raws
3557
3558     def _encode(self):
3559         v = b"".join(self._encoded_values())
3560         return b"".join((self.tag, len_encode(len(v)), v))
3561
3562     def _decode(self, tlv, offset=0, decode_path=()):
3563         try:
3564             t, tlen, lv = tag_strip(tlv)
3565         except DecodeError as err:
3566             raise err.__class__(
3567                 msg=err.msg,
3568                 klass=self.__class__,
3569                 decode_path=decode_path,
3570                 offset=offset,
3571             )
3572         if t != self.tag:
3573             raise TagMismatch(
3574                 klass=self.__class__,
3575                 decode_path=decode_path,
3576                 offset=offset,
3577             )
3578         try:
3579             l, llen, v = len_decode(lv)
3580         except DecodeError as err:
3581             raise err.__class__(
3582                 msg=err.msg,
3583                 klass=self.__class__,
3584                 decode_path=decode_path,
3585                 offset=offset,
3586             )
3587         if l > len(v):
3588             raise NotEnoughData(
3589                 "encoded length is longer than data",
3590                 klass=self.__class__,
3591                 decode_path=decode_path,
3592                 offset=offset,
3593             )
3594         v, tail = v[:l], v[l:]
3595         sub_offset = offset + tlen + llen
3596         values = {}
3597         for name, spec in self.specs.items():
3598             if len(v) == 0 and spec.optional:
3599                 continue
3600             try:
3601                 value, v_tail = spec.decode(
3602                     v,
3603                     sub_offset,
3604                     leavemm=True,
3605                     decode_path=decode_path + (name,),
3606                 )
3607             except TagMismatch:
3608                 if spec.optional:
3609                     continue
3610                 raise
3611             sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3612             v = v_tail
3613             if spec.default is not None and value == spec.default:
3614                 # Encoded default values are not valid in DER,
3615                 # but we still allow that
3616                 continue
3617             values[name] = value
3618         if len(v) > 0:
3619             raise DecodeError(
3620                 "remaining data",
3621                 klass=self.__class__,
3622                 decode_path=decode_path,
3623                 offset=offset,
3624             )
3625         obj = self.__class__(
3626             schema=self.specs,
3627             impl=self.tag,
3628             expl=self._expl,
3629             default=self.default,
3630             optional=self.optional,
3631             _decoded=(offset, llen, l),
3632         )
3633         obj._value = values
3634         return obj, tail
3635
3636     def __repr__(self):
3637         value = pp_console_row(next(self.pps()))
3638         cols = []
3639         for name in self.specs:
3640             _value = self._value.get(name)
3641             if _value is None:
3642                 continue
3643             cols.append(repr(_value))
3644         return "%s[%s]" % (value, ", ".join(cols))
3645
3646     def pps(self, decode_path=()):
3647         yield _pp(
3648             asn1_type_name=self.asn1_type_name,
3649             obj_name=self.__class__.__name__,
3650             decode_path=decode_path,
3651             optional=self.optional,
3652             default=self == self.default,
3653             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3654             expl=None if self._expl is None else tag_decode(self._expl),
3655             offset=self.offset,
3656             tlen=self.tlen,
3657             llen=self.llen,
3658             vlen=self.vlen,
3659             expl_offset=self.expl_offset if self.expled else None,
3660             expl_tlen=self.expl_tlen if self.expled else None,
3661             expl_llen=self.expl_llen if self.expled else None,
3662             expl_vlen=self.expl_vlen if self.expled else None,
3663         )
3664         for name in self.specs:
3665             value = self._value.get(name)
3666             if value is None:
3667                 continue
3668             yield value.pps(decode_path=decode_path + (name,))
3669
3670
3671 class Set(Sequence):
3672     """``SET`` structure type
3673
3674     Its usage is identical to :py:class:`pyderasn.Sequence`.
3675     """
3676     __slots__ = ()
3677     tag_default = tag_encode(form=TagFormConstructed, num=17)
3678     asn1_type_name = "SET"
3679
3680     def _encode(self):
3681         raws = self._encoded_values()
3682         raws.sort()
3683         v = b"".join(raws)
3684         return b"".join((self.tag, len_encode(len(v)), v))
3685
3686     def _decode(self, tlv, offset=0, decode_path=()):
3687         try:
3688             t, tlen, lv = tag_strip(tlv)
3689         except DecodeError as err:
3690             raise err.__class__(
3691                 msg=err.msg,
3692                 klass=self.__class__,
3693                 decode_path=decode_path,
3694                 offset=offset,
3695             )
3696         if t != self.tag:
3697             raise TagMismatch(
3698                 klass=self.__class__,
3699                 decode_path=decode_path,
3700                 offset=offset,
3701             )
3702         try:
3703             l, llen, v = len_decode(lv)
3704         except DecodeError as err:
3705             raise err.__class__(
3706                 msg=err.msg,
3707                 klass=self.__class__,
3708                 decode_path=decode_path,
3709                 offset=offset,
3710             )
3711         if l > len(v):
3712             raise NotEnoughData(
3713                 "encoded length is longer than data",
3714                 klass=self.__class__,
3715                 offset=offset,
3716             )
3717         v, tail = v[:l], v[l:]
3718         sub_offset = offset + tlen + llen
3719         values = {}
3720         specs_items = self.specs.items
3721         while len(v) > 0:
3722             for name, spec in specs_items():
3723                 try:
3724                     value, v_tail = spec.decode(
3725                         v,
3726                         sub_offset,
3727                         leavemm=True,
3728                         decode_path=decode_path + (name,),
3729                     )
3730                 except TagMismatch:
3731                     continue
3732                 sub_offset += (
3733                     value.expl_tlvlen if value.expled else value.tlvlen
3734                 )
3735                 v = v_tail
3736                 if spec.default is None or value != spec.default:  # pragma: no cover
3737                     # SeqMixing.test_encoded_default_accepted covers that place
3738                     values[name] = value
3739                 break
3740             else:
3741                 raise TagMismatch(
3742                     klass=self.__class__,
3743                     decode_path=decode_path,
3744                     offset=offset,
3745                 )
3746         obj = self.__class__(
3747             schema=self.specs,
3748             impl=self.tag,
3749             expl=self._expl,
3750             default=self.default,
3751             optional=self.optional,
3752             _decoded=(offset, llen, l),
3753         )
3754         obj._value = values
3755         return obj, tail
3756
3757
3758 class SequenceOf(Obj):
3759     """``SEQUENCE OF`` sequence type
3760
3761     For that kind of type you must specify the object it will carry on
3762     (bounds are for example here, not required)::
3763
3764         class Ints(SequenceOf):
3765             schema = Integer()
3766             bounds = (0, 2)
3767
3768     >>> ints = Ints()
3769     >>> ints.append(Integer(123))
3770     >>> ints.append(Integer(234))
3771     >>> ints
3772     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3773     >>> [int(i) for i in ints]
3774     [123, 234]
3775     >>> ints.append(Integer(345))
3776     Traceback (most recent call last):
3777     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3778     >>> ints[1]
3779     INTEGER 234
3780     >>> ints[1] = Integer(345)
3781     >>> ints
3782     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3783
3784     Also you can initialize sequence with preinitialized values:
3785
3786     >>> ints = Ints([Integer(123), Integer(234)])
3787     """
3788     __slots__ = ("spec", "_bound_min", "_bound_max")
3789     tag_default = tag_encode(form=TagFormConstructed, num=16)
3790     asn1_type_name = "SEQUENCE OF"
3791
3792     def __init__(
3793             self,
3794             value=None,
3795             schema=None,
3796             bounds=None,
3797             impl=None,
3798             expl=None,
3799             default=None,
3800             optional=False,
3801             _decoded=(0, 0, 0),
3802     ):
3803         super(SequenceOf, self).__init__(
3804             impl,
3805             expl,
3806             default,
3807             optional,
3808             _decoded,
3809         )
3810         if schema is None:
3811             schema = getattr(self, "schema", None)
3812         if schema is None:
3813             raise ValueError("schema must be specified")
3814         self.spec = schema
3815         if bounds is None:
3816             self._bound_min, self._bound_max = getattr(
3817                 self,
3818                 "bounds",
3819                 (0, float("+inf")),
3820             )
3821         else:
3822             self._bound_min, self._bound_max = 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()