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