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