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