]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
833e785206f26a4f84ec209a793c8c797fba51cc
[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         obj._value = self._value
1660         obj.tag = self.tag
1661         obj._expl = self._expl
1662         obj.default = self.default
1663         obj.optional = self.optional
1664         obj.offset = self.offset
1665         obj.llen = self.llen
1666         obj.vlen = self.vlen
1667         return obj
1668
1669     def __iter__(self):
1670         self._assert_ready()
1671         for i in six_xrange(self._value[0]):
1672             yield self[i]
1673
1674     @property
1675     def bit_len(self):
1676         self._assert_ready()
1677         return self._value[0]
1678
1679     def __bytes__(self):
1680         self._assert_ready()
1681         return self._value[1]
1682
1683     def __eq__(self, their):
1684         if isinstance(their, bytes):
1685             return self._value[1] == their
1686         if not issubclass(their.__class__, BitString):
1687             return False
1688         return (
1689             self._value == their._value and
1690             self.tag == their.tag and
1691             self._expl == their._expl
1692         )
1693
1694     @property
1695     def named(self):
1696         return [name for name, bit in self.specs.items() if self[bit]]
1697
1698     def __call__(
1699             self,
1700             value=None,
1701             impl=None,
1702             expl=None,
1703             default=None,
1704             optional=None,
1705     ):
1706         return self.__class__(
1707             value=value,
1708             impl=self.tag if impl is None else impl,
1709             expl=self._expl if expl is None else expl,
1710             default=self.default if default is None else default,
1711             optional=self.optional if optional is None else optional,
1712             _specs=self.specs,
1713         )
1714
1715     def __getitem__(self, key):
1716         if isinstance(key, int):
1717             bit_len, octets = self._value
1718             if key >= bit_len:
1719                 return False
1720             return (
1721                 byte2int(memoryview(octets)[key // 8:]) >>
1722                 (7 - (key % 8))
1723             ) & 1 == 1
1724         if isinstance(key, string_types):
1725             value = self.specs.get(key)
1726             if value is None:
1727                 raise ObjUnknown("BitString value: %s" % key)
1728             return self[value]
1729         raise InvalidValueType((int, str))
1730
1731     def _encode(self):
1732         self._assert_ready()
1733         bit_len, octets = self._value
1734         return b"".join((
1735             self.tag,
1736             len_encode(len(octets) + 1),
1737             int2byte((8 - bit_len % 8) % 8),
1738             octets,
1739         ))
1740
1741     def _decode(self, tlv, offset=0, decode_path=()):
1742         try:
1743             t, _, lv = tag_strip(tlv)
1744         except DecodeError as err:
1745             raise err.__class__(
1746                 msg=err.msg,
1747                 klass=self.__class__,
1748                 decode_path=decode_path,
1749                 offset=offset,
1750             )
1751         if t != self.tag:
1752             raise TagMismatch(
1753                 klass=self.__class__,
1754                 decode_path=decode_path,
1755                 offset=offset,
1756             )
1757         try:
1758             l, llen, v = len_decode(lv)
1759         except DecodeError as err:
1760             raise err.__class__(
1761                 msg=err.msg,
1762                 klass=self.__class__,
1763                 decode_path=decode_path,
1764                 offset=offset,
1765             )
1766         if l > len(v):
1767             raise NotEnoughData(
1768                 "encoded length is longer than data",
1769                 klass=self.__class__,
1770                 decode_path=decode_path,
1771                 offset=offset,
1772             )
1773         if l == 0:
1774             raise NotEnoughData(
1775                 "zero length",
1776                 klass=self.__class__,
1777                 decode_path=decode_path,
1778                 offset=offset,
1779             )
1780         pad_size = byte2int(v)
1781         if l == 1 and pad_size != 0:
1782             raise DecodeError(
1783                 "invalid empty value",
1784                 klass=self.__class__,
1785                 decode_path=decode_path,
1786                 offset=offset,
1787             )
1788         if pad_size > 7:
1789             raise DecodeError(
1790                 "too big pad",
1791                 klass=self.__class__,
1792                 decode_path=decode_path,
1793                 offset=offset,
1794             )
1795         if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1796             raise DecodeError(
1797                 "invalid pad",
1798                 klass=self.__class__,
1799                 decode_path=decode_path,
1800                 offset=offset,
1801             )
1802         v, tail = v[:l], v[l:]
1803         obj = self.__class__(
1804             value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1805             impl=self.tag,
1806             expl=self._expl,
1807             default=self.default,
1808             optional=self.optional,
1809             _specs=self.specs,
1810             _decoded=(offset, llen, l),
1811         )
1812         return obj, tail
1813
1814     def __repr__(self):
1815         return pp_console_row(next(self.pps()))
1816
1817     def pps(self, decode_path=()):
1818         value = None
1819         blob = None
1820         if self.ready:
1821             bit_len, blob = self._value
1822             value = "%d bits" % bit_len
1823             if len(self.specs) > 0:
1824                 blob = tuple(self.named)
1825         yield _pp(
1826             asn1_type_name=self.asn1_type_name,
1827             obj_name=self.__class__.__name__,
1828             decode_path=decode_path,
1829             value=value,
1830             blob=blob,
1831             optional=self.optional,
1832             default=self == self.default,
1833             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1834             expl=None if self._expl is None else tag_decode(self._expl),
1835             offset=self.offset,
1836             tlen=self.tlen,
1837             llen=self.llen,
1838             vlen=self.vlen,
1839             expl_offset=self.expl_offset if self.expled else None,
1840             expl_tlen=self.expl_tlen if self.expled else None,
1841             expl_llen=self.expl_llen if self.expled else None,
1842             expl_vlen=self.expl_vlen if self.expled else None,
1843         )
1844
1845
1846 class OctetString(Obj):
1847     """``OCTET STRING`` binary string type
1848
1849     >>> s = OctetString(b"hello world")
1850     OCTET STRING 11 bytes 68656c6c6f20776f726c64
1851     >>> s == OctetString(b"hello world")
1852     True
1853     >>> bytes(s)
1854     b'hello world'
1855
1856     >>> OctetString(b"hello", bounds=(4, 4))
1857     Traceback (most recent call last):
1858     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1859     >>> OctetString(b"hell", bounds=(4, 4))
1860     OCTET STRING 4 bytes 68656c6c
1861     """
1862     __slots__ = ("_bound_min", "_bound_max")
1863     tag_default = tag_encode(4)
1864     asn1_type_name = "OCTET STRING"
1865
1866     def __init__(
1867             self,
1868             value=None,
1869             bounds=None,
1870             impl=None,
1871             expl=None,
1872             default=None,
1873             optional=False,
1874             _decoded=(0, 0, 0),
1875     ):
1876         """
1877         :param value: set the value. Either binary type, or
1878                       :py:class:`pyderasn.OctetString` object
1879         :param bounds: set ``(MIN, MAX)`` value size constraint.
1880                        (-inf, +inf) by default
1881         :param bytes impl: override default tag with ``IMPLICIT`` one
1882         :param bytes expl: override default tag with ``EXPLICIT`` one
1883         :param default: set default value. Type same as in ``value``
1884         :param bool optional: is object ``OPTIONAL`` in sequence
1885         """
1886         super(OctetString, self).__init__(
1887             impl,
1888             expl,
1889             default,
1890             optional,
1891             _decoded,
1892         )
1893         self._value = value
1894         if bounds is None:
1895             self._bound_min, self._bound_max = getattr(
1896                 self,
1897                 "bounds",
1898                 (0, float("+inf")),
1899             )
1900         else:
1901             self._bound_min, self._bound_max = bounds
1902         if value is not None:
1903             self._value = self._value_sanitize(value)
1904         if default is not None:
1905             default = self._value_sanitize(default)
1906             self.default = self.__class__(
1907                 value=default,
1908                 impl=self.tag,
1909                 expl=self._expl,
1910             )
1911             if self._value is None:
1912                 self._value = default
1913
1914     def _value_sanitize(self, value):
1915         if issubclass(value.__class__, OctetString):
1916             value = value._value
1917         elif isinstance(value, binary_type):
1918             pass
1919         else:
1920             raise InvalidValueType((self.__class__, bytes))
1921         if not self._bound_min <= len(value) <= self._bound_max:
1922             raise BoundsError(self._bound_min, len(value), self._bound_max)
1923         return value
1924
1925     @property
1926     def ready(self):
1927         return self._value is not None
1928
1929     def copy(self):
1930         obj = self.__class__()
1931         obj._value = self._value
1932         obj._bound_min = self._bound_min
1933         obj._bound_max = self._bound_max
1934         obj.tag = self.tag
1935         obj._expl = self._expl
1936         obj.default = self.default
1937         obj.optional = self.optional
1938         obj.offset = self.offset
1939         obj.llen = self.llen
1940         obj.vlen = self.vlen
1941         return obj
1942
1943     def __bytes__(self):
1944         self._assert_ready()
1945         return self._value
1946
1947     def __eq__(self, their):
1948         if isinstance(their, binary_type):
1949             return self._value == their
1950         if not issubclass(their.__class__, OctetString):
1951             return False
1952         return (
1953             self._value == their._value and
1954             self.tag == their.tag and
1955             self._expl == their._expl
1956         )
1957
1958     def __lt__(self, their):
1959         return self._value < their._value
1960
1961     def __call__(
1962             self,
1963             value=None,
1964             bounds=None,
1965             impl=None,
1966             expl=None,
1967             default=None,
1968             optional=None,
1969     ):
1970         return self.__class__(
1971             value=value,
1972             bounds=(
1973                 (self._bound_min, self._bound_max)
1974                 if bounds is None else bounds
1975             ),
1976             impl=self.tag if impl is None else impl,
1977             expl=self._expl if expl is None else expl,
1978             default=self.default if default is None else default,
1979             optional=self.optional if optional is None else optional,
1980         )
1981
1982     def _encode(self):
1983         self._assert_ready()
1984         return b"".join((
1985             self.tag,
1986             len_encode(len(self._value)),
1987             self._value,
1988         ))
1989
1990     def _decode(self, tlv, offset=0, decode_path=()):
1991         try:
1992             t, _, lv = tag_strip(tlv)
1993         except DecodeError as err:
1994             raise err.__class__(
1995                 msg=err.msg,
1996                 klass=self.__class__,
1997                 decode_path=decode_path,
1998                 offset=offset,
1999             )
2000         if t != self.tag:
2001             raise TagMismatch(
2002                 klass=self.__class__,
2003                 decode_path=decode_path,
2004                 offset=offset,
2005             )
2006         try:
2007             l, llen, v = len_decode(lv)
2008         except DecodeError as err:
2009             raise err.__class__(
2010                 msg=err.msg,
2011                 klass=self.__class__,
2012                 decode_path=decode_path,
2013                 offset=offset,
2014             )
2015         if l > len(v):
2016             raise NotEnoughData(
2017                 "encoded length is longer than data",
2018                 klass=self.__class__,
2019                 decode_path=decode_path,
2020                 offset=offset,
2021             )
2022         v, tail = v[:l], v[l:]
2023         try:
2024             obj = self.__class__(
2025                 value=v.tobytes(),
2026                 bounds=(self._bound_min, self._bound_max),
2027                 impl=self.tag,
2028                 expl=self._expl,
2029                 default=self.default,
2030                 optional=self.optional,
2031                 _decoded=(offset, llen, l),
2032             )
2033         except BoundsError as err:
2034             raise DecodeError(
2035                 msg=str(err),
2036                 klass=self.__class__,
2037                 decode_path=decode_path,
2038                 offset=offset,
2039             )
2040         return obj, tail
2041
2042     def __repr__(self):
2043         return pp_console_row(next(self.pps()))
2044
2045     def pps(self, decode_path=()):
2046         yield _pp(
2047             asn1_type_name=self.asn1_type_name,
2048             obj_name=self.__class__.__name__,
2049             decode_path=decode_path,
2050             value=("%d bytes" % len(self._value)) if self.ready else None,
2051             blob=self._value if self.ready else None,
2052             optional=self.optional,
2053             default=self == self.default,
2054             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2055             expl=None if self._expl is None else tag_decode(self._expl),
2056             offset=self.offset,
2057             tlen=self.tlen,
2058             llen=self.llen,
2059             vlen=self.vlen,
2060             expl_offset=self.expl_offset if self.expled else None,
2061             expl_tlen=self.expl_tlen if self.expled else None,
2062             expl_llen=self.expl_llen if self.expled else None,
2063             expl_vlen=self.expl_vlen if self.expled else None,
2064         )
2065
2066
2067 class Null(Obj):
2068     """``NULL`` null object
2069
2070     >>> n = Null()
2071     NULL
2072     >>> n.ready
2073     True
2074     """
2075     __slots__ = ()
2076     tag_default = tag_encode(5)
2077     asn1_type_name = "NULL"
2078
2079     def __init__(
2080             self,
2081             value=None,  # unused, but Sequence passes it
2082             impl=None,
2083             expl=None,
2084             optional=False,
2085             _decoded=(0, 0, 0),
2086     ):
2087         """
2088         :param bytes impl: override default tag with ``IMPLICIT`` one
2089         :param bytes expl: override default tag with ``EXPLICIT`` one
2090         :param bool optional: is object ``OPTIONAL`` in sequence
2091         """
2092         super(Null, self).__init__(impl, expl, None, optional, _decoded)
2093         self.default = None
2094
2095     @property
2096     def ready(self):
2097         return True
2098
2099     def copy(self):
2100         obj = self.__class__()
2101         obj.tag = self.tag
2102         obj._expl = self._expl
2103         obj.default = self.default
2104         obj.optional = self.optional
2105         obj.offset = self.offset
2106         obj.llen = self.llen
2107         obj.vlen = self.vlen
2108         return obj
2109
2110     def __eq__(self, their):
2111         if not issubclass(their.__class__, Null):
2112             return False
2113         return (
2114             self.tag == their.tag and
2115             self._expl == their._expl
2116         )
2117
2118     def __call__(
2119             self,
2120             value=None,
2121             impl=None,
2122             expl=None,
2123             optional=None,
2124     ):
2125         return self.__class__(
2126             impl=self.tag if impl is None else impl,
2127             expl=self._expl if expl is None else expl,
2128             optional=self.optional if optional is None else optional,
2129         )
2130
2131     def _encode(self):
2132         return self.tag + len_encode(0)
2133
2134     def _decode(self, tlv, offset=0, decode_path=()):
2135         try:
2136             t, _, lv = tag_strip(tlv)
2137         except DecodeError as err:
2138             raise err.__class__(
2139                 msg=err.msg,
2140                 klass=self.__class__,
2141                 decode_path=decode_path,
2142                 offset=offset,
2143             )
2144         if t != self.tag:
2145             raise TagMismatch(
2146                 klass=self.__class__,
2147                 decode_path=decode_path,
2148                 offset=offset,
2149             )
2150         try:
2151             l, _, v = len_decode(lv)
2152         except DecodeError as err:
2153             raise err.__class__(
2154                 msg=err.msg,
2155                 klass=self.__class__,
2156                 decode_path=decode_path,
2157                 offset=offset,
2158             )
2159         if l != 0:
2160             raise InvalidLength(
2161                 "Null must have zero length",
2162                 klass=self.__class__,
2163                 decode_path=decode_path,
2164                 offset=offset,
2165             )
2166         obj = self.__class__(
2167             impl=self.tag,
2168             expl=self._expl,
2169             optional=self.optional,
2170             _decoded=(offset, 1, 0),
2171         )
2172         return obj, v
2173
2174     def __repr__(self):
2175         return pp_console_row(next(self.pps()))
2176
2177     def pps(self, decode_path=()):
2178         yield _pp(
2179             asn1_type_name=self.asn1_type_name,
2180             obj_name=self.__class__.__name__,
2181             decode_path=decode_path,
2182             optional=self.optional,
2183             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2184             expl=None if self._expl is None else tag_decode(self._expl),
2185             offset=self.offset,
2186             tlen=self.tlen,
2187             llen=self.llen,
2188             vlen=self.vlen,
2189             expl_offset=self.expl_offset if self.expled else None,
2190             expl_tlen=self.expl_tlen if self.expled else None,
2191             expl_llen=self.expl_llen if self.expled else None,
2192             expl_vlen=self.expl_vlen if self.expled else None,
2193         )
2194
2195
2196 class ObjectIdentifier(Obj):
2197     """``OBJECT IDENTIFIER`` OID type
2198
2199     >>> oid = ObjectIdentifier((1, 2, 3))
2200     OBJECT IDENTIFIER 1.2.3
2201     >>> oid == ObjectIdentifier("1.2.3")
2202     True
2203     >>> tuple(oid)
2204     (1, 2, 3)
2205     >>> str(oid)
2206     '1.2.3'
2207     >>> oid + (4, 5) + ObjectIdentifier("1.7")
2208     OBJECT IDENTIFIER 1.2.3.4.5.1.7
2209
2210     >>> str(ObjectIdentifier((3, 1)))
2211     Traceback (most recent call last):
2212     pyderasn.InvalidOID: unacceptable first arc value
2213     """
2214     __slots__ = ()
2215     tag_default = tag_encode(6)
2216     asn1_type_name = "OBJECT IDENTIFIER"
2217
2218     def __init__(
2219             self,
2220             value=None,
2221             impl=None,
2222             expl=None,
2223             default=None,
2224             optional=False,
2225             _decoded=(0, 0, 0),
2226     ):
2227         """
2228         :param value: set the value. Either tuples of integers,
2229                       string of "."-concatenated integers, or
2230                       :py:class:`pyderasn.ObjectIdentifier` object
2231         :param bytes impl: override default tag with ``IMPLICIT`` one
2232         :param bytes expl: override default tag with ``EXPLICIT`` one
2233         :param default: set default value. Type same as in ``value``
2234         :param bool optional: is object ``OPTIONAL`` in sequence
2235         """
2236         super(ObjectIdentifier, self).__init__(
2237             impl,
2238             expl,
2239             default,
2240             optional,
2241             _decoded,
2242         )
2243         self._value = value
2244         if value is not None:
2245             self._value = self._value_sanitize(value)
2246         if default is not None:
2247             default = self._value_sanitize(default)
2248             self.default = self.__class__(
2249                 value=default,
2250                 impl=self.tag,
2251                 expl=self._expl,
2252             )
2253             if self._value is None:
2254                 self._value = default
2255
2256     def __add__(self, their):
2257         if isinstance(their, self.__class__):
2258             return self.__class__(self._value + their._value)
2259         if isinstance(their, tuple):
2260             return self.__class__(self._value + their)
2261         raise InvalidValueType((self.__class__, tuple))
2262
2263     def _value_sanitize(self, value):
2264         if issubclass(value.__class__, ObjectIdentifier):
2265             return value._value
2266         if isinstance(value, string_types):
2267             try:
2268                 value = tuple(int(arc) for arc in value.split("."))
2269             except ValueError:
2270                 raise InvalidOID("unacceptable arcs values")
2271         if isinstance(value, tuple):
2272             if len(value) < 2:
2273                 raise InvalidOID("less than 2 arcs")
2274             first_arc = value[0]
2275             if first_arc in (0, 1):
2276                 if not (0 <= value[1] <= 39):
2277                     raise InvalidOID("second arc is too wide")
2278             elif first_arc == 2:
2279                 pass
2280             else:
2281                 raise InvalidOID("unacceptable first arc value")
2282             return value
2283         raise InvalidValueType((self.__class__, str, tuple))
2284
2285     @property
2286     def ready(self):
2287         return self._value is not None
2288
2289     def copy(self):
2290         obj = self.__class__()
2291         obj._value = self._value
2292         obj.tag = self.tag
2293         obj._expl = self._expl
2294         obj.default = self.default
2295         obj.optional = self.optional
2296         obj.offset = self.offset
2297         obj.llen = self.llen
2298         obj.vlen = self.vlen
2299         return obj
2300
2301     def __iter__(self):
2302         self._assert_ready()
2303         return iter(self._value)
2304
2305     def __str__(self):
2306         return ".".join(str(arc) for arc in self._value or ())
2307
2308     def __hash__(self):
2309         self._assert_ready()
2310         return hash(
2311             self.tag +
2312             bytes(self._expl or b"") +
2313             str(self._value).encode("ascii"),
2314         )
2315
2316     def __eq__(self, their):
2317         if isinstance(their, tuple):
2318             return self._value == their
2319         if not issubclass(their.__class__, ObjectIdentifier):
2320             return False
2321         return (
2322             self.tag == their.tag and
2323             self._expl == their._expl and
2324             self._value == their._value
2325         )
2326
2327     def __lt__(self, their):
2328         return self._value < their._value
2329
2330     def __call__(
2331             self,
2332             value=None,
2333             impl=None,
2334             expl=None,
2335             default=None,
2336             optional=None,
2337     ):
2338         return self.__class__(
2339             value=value,
2340             impl=self.tag if impl is None else impl,
2341             expl=self._expl if expl is None else expl,
2342             default=self.default if default is None else default,
2343             optional=self.optional if optional is None else optional,
2344         )
2345
2346     def _encode(self):
2347         self._assert_ready()
2348         value = self._value
2349         first_value = value[1]
2350         first_arc = value[0]
2351         if first_arc == 0:
2352             pass
2353         elif first_arc == 1:
2354             first_value += 40
2355         elif first_arc == 2:
2356             first_value += 80
2357         else:  # pragma: no cover
2358             raise RuntimeError("invalid arc is stored")
2359         octets = [zero_ended_encode(first_value)]
2360         for arc in value[2:]:
2361             octets.append(zero_ended_encode(arc))
2362         v = b"".join(octets)
2363         return b"".join((self.tag, len_encode(len(v)), v))
2364
2365     def _decode(self, tlv, offset=0, decode_path=()):
2366         try:
2367             t, _, lv = tag_strip(tlv)
2368         except DecodeError as err:
2369             raise err.__class__(
2370                 msg=err.msg,
2371                 klass=self.__class__,
2372                 decode_path=decode_path,
2373                 offset=offset,
2374             )
2375         if t != self.tag:
2376             raise TagMismatch(
2377                 klass=self.__class__,
2378                 decode_path=decode_path,
2379                 offset=offset,
2380             )
2381         try:
2382             l, llen, v = len_decode(lv)
2383         except DecodeError as err:
2384             raise err.__class__(
2385                 msg=err.msg,
2386                 klass=self.__class__,
2387                 decode_path=decode_path,
2388                 offset=offset,
2389             )
2390         if l > len(v):
2391             raise NotEnoughData(
2392                 "encoded length is longer than data",
2393                 klass=self.__class__,
2394                 decode_path=decode_path,
2395                 offset=offset,
2396             )
2397         if l == 0:
2398             raise NotEnoughData(
2399                 "zero length",
2400                 klass=self.__class__,
2401                 decode_path=decode_path,
2402                 offset=offset,
2403             )
2404         v, tail = v[:l], v[l:]
2405         arcs = []
2406         while len(v) > 0:
2407             i = 0
2408             arc = 0
2409             while True:
2410                 octet = indexbytes(v, i)
2411                 arc = (arc << 7) | (octet & 0x7F)
2412                 if octet & 0x80 == 0:
2413                     arcs.append(arc)
2414                     v = v[i + 1:]
2415                     break
2416                 i += 1
2417                 if i == len(v):
2418                     raise DecodeError(
2419                         "unfinished OID",
2420                         klass=self.__class__,
2421                         decode_path=decode_path,
2422                         offset=offset,
2423                     )
2424         first_arc = 0
2425         second_arc = arcs[0]
2426         if 0 <= second_arc <= 39:
2427             first_arc = 0
2428         elif 40 <= second_arc <= 79:
2429             first_arc = 1
2430             second_arc -= 40
2431         else:
2432             first_arc = 2
2433             second_arc -= 80
2434         obj = self.__class__(
2435             value=tuple([first_arc, second_arc] + arcs[1:]),
2436             impl=self.tag,
2437             expl=self._expl,
2438             default=self.default,
2439             optional=self.optional,
2440             _decoded=(offset, llen, l),
2441         )
2442         return obj, tail
2443
2444     def __repr__(self):
2445         return pp_console_row(next(self.pps()))
2446
2447     def pps(self, decode_path=()):
2448         yield _pp(
2449             asn1_type_name=self.asn1_type_name,
2450             obj_name=self.__class__.__name__,
2451             decode_path=decode_path,
2452             value=str(self) if self.ready else None,
2453             optional=self.optional,
2454             default=self == self.default,
2455             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2456             expl=None if self._expl is None else tag_decode(self._expl),
2457             offset=self.offset,
2458             tlen=self.tlen,
2459             llen=self.llen,
2460             vlen=self.vlen,
2461             expl_offset=self.expl_offset if self.expled else None,
2462             expl_tlen=self.expl_tlen if self.expled else None,
2463             expl_llen=self.expl_llen if self.expled else None,
2464             expl_vlen=self.expl_vlen if self.expled else None,
2465         )
2466
2467
2468 class Enumerated(Integer):
2469     """``ENUMERATED`` integer type
2470
2471     This type is identical to :py:class:`pyderasn.Integer`, but requires
2472     schema to be specified and does not accept values missing from it.
2473     """
2474     __slots__ = ()
2475     tag_default = tag_encode(10)
2476     asn1_type_name = "ENUMERATED"
2477
2478     def __init__(
2479             self,
2480             value=None,
2481             impl=None,
2482             expl=None,
2483             default=None,
2484             optional=False,
2485             _specs=None,
2486             _decoded=(0, 0, 0),
2487             bounds=None,  # dummy argument, workability for Integer.decode
2488     ):
2489         super(Enumerated, self).__init__(
2490             value=value,
2491             impl=impl,
2492             expl=expl,
2493             default=default,
2494             optional=optional,
2495             _specs=_specs,
2496             _decoded=_decoded,
2497         )
2498         if len(self.specs) == 0:
2499             raise ValueError("schema must be specified")
2500
2501     def _value_sanitize(self, value):
2502         if isinstance(value, self.__class__):
2503             value = value._value
2504         elif isinstance(value, integer_types):
2505             if value not in list(self.specs.values()):
2506                 raise DecodeError(
2507                     "unknown integer value: %s" % value,
2508                     klass=self.__class__,
2509                 )
2510         elif isinstance(value, string_types):
2511             value = self.specs.get(value)
2512             if value is None:
2513                 raise ObjUnknown("integer value: %s" % value)
2514         else:
2515             raise InvalidValueType((self.__class__, int, str))
2516         return value
2517
2518     def copy(self):
2519         obj = self.__class__(_specs=self.specs)
2520         obj._value = self._value
2521         obj._bound_min = self._bound_min
2522         obj._bound_max = self._bound_max
2523         obj.tag = self.tag
2524         obj._expl = self._expl
2525         obj.default = self.default
2526         obj.optional = self.optional
2527         obj.offset = self.offset
2528         obj.llen = self.llen
2529         obj.vlen = self.vlen
2530         return obj
2531
2532     def __call__(
2533             self,
2534             value=None,
2535             impl=None,
2536             expl=None,
2537             default=None,
2538             optional=None,
2539             _specs=None,
2540     ):
2541         return self.__class__(
2542             value=value,
2543             impl=self.tag if impl is None else impl,
2544             expl=self._expl if expl is None else expl,
2545             default=self.default if default is None else default,
2546             optional=self.optional if optional is None else optional,
2547             _specs=self.specs,
2548         )
2549
2550
2551 class CommonString(OctetString):
2552     """Common class for all strings
2553
2554     Everything resembles :py:class:`pyderasn.OctetString`, except
2555     ability to deal with unicode text strings.
2556
2557     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
2558     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2559     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
2560     True
2561     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
2562     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
2563     >>> str(s)
2564     'привет Ð¼Ð¸Ñ€'
2565     >>> hexenc(bytes(s))
2566     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2567
2568     >>> PrintableString("привет Ð¼Ð¸Ñ€")
2569     Traceback (most recent call last):
2570     UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2571
2572     >>> BMPString("ада", bounds=(2, 2))
2573     Traceback (most recent call last):
2574     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2575     >>> s = BMPString("ад", bounds=(2, 2))
2576     >>> s.encoding
2577     'utf-16-be'
2578     >>> hexenc(bytes(s))
2579     '04300434'
2580
2581     .. list-table::
2582        :header-rows: 1
2583
2584        * - Class
2585          - Text Encoding
2586        * - :py:class:`pyderasn.UTF8String`
2587          - utf-8
2588        * - :py:class:`pyderasn.NumericString`
2589          - ascii
2590        * - :py:class:`pyderasn.PrintableString`
2591          - ascii
2592        * - :py:class:`pyderasn.TeletexString`
2593          - ascii
2594        * - :py:class:`pyderasn.T61String`
2595          - ascii
2596        * - :py:class:`pyderasn.VideotexString`
2597          - iso-8859-1
2598        * - :py:class:`pyderasn.IA5String`
2599          - ascii
2600        * - :py:class:`pyderasn.GraphicString`
2601          - iso-8859-1
2602        * - :py:class:`pyderasn.VisibleString`
2603          - ascii
2604        * - :py:class:`pyderasn.ISO646String`
2605          - ascii
2606        * - :py:class:`pyderasn.GeneralString`
2607          - iso-8859-1
2608        * - :py:class:`pyderasn.UniversalString`
2609          - utf-32-be
2610        * - :py:class:`pyderasn.BMPString`
2611          - utf-16-be
2612     """
2613     __slots__ = ("encoding",)
2614
2615     def _value_sanitize(self, value):
2616         value_raw = None
2617         value_decoded = None
2618         if isinstance(value, self.__class__):
2619             value_raw = value._value
2620         elif isinstance(value, text_type):
2621             value_decoded = value
2622         elif isinstance(value, binary_type):
2623             value_raw = value
2624         else:
2625             raise InvalidValueType((self.__class__, text_type, binary_type))
2626         value_raw = (
2627             value_decoded.encode(self.encoding)
2628             if value_raw is None else value_raw
2629         )
2630         value_decoded = (
2631             value_raw.decode(self.encoding)
2632             if value_decoded is None else value_decoded
2633         )
2634         if not self._bound_min <= len(value_decoded) <= self._bound_max:
2635             raise BoundsError(
2636                 self._bound_min,
2637                 len(value_decoded),
2638                 self._bound_max,
2639             )
2640         return value_raw
2641
2642     def __eq__(self, their):
2643         if isinstance(their, binary_type):
2644             return self._value == their
2645         if isinstance(their, text_type):
2646             return self._value == their.encode(self.encoding)
2647         if not isinstance(their, self.__class__):
2648             return False
2649         return (
2650             self._value == their._value and
2651             self.tag == their.tag and
2652             self._expl == their._expl
2653         )
2654
2655     def __unicode__(self):
2656         if self.ready:
2657             return self._value.decode(self.encoding)
2658         return text_type(self._value)
2659
2660     def __repr__(self):
2661         return pp_console_row(next(self.pps(no_unicode=PY2)))
2662
2663     def pps(self, decode_path=(), no_unicode=False):
2664         value = None
2665         if self.ready:
2666             value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2667         yield _pp(
2668             asn1_type_name=self.asn1_type_name,
2669             obj_name=self.__class__.__name__,
2670             decode_path=decode_path,
2671             value=value,
2672             optional=self.optional,
2673             default=self == self.default,
2674             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2675             expl=None if self._expl is None else tag_decode(self._expl),
2676             offset=self.offset,
2677             tlen=self.tlen,
2678             llen=self.llen,
2679             vlen=self.vlen,
2680         )
2681
2682
2683 class UTF8String(CommonString):
2684     __slots__ = ()
2685     tag_default = tag_encode(12)
2686     encoding = "utf-8"
2687     asn1_type_name = "UTF8String"
2688
2689
2690 class NumericString(CommonString):
2691     __slots__ = ()
2692     tag_default = tag_encode(18)
2693     encoding = "ascii"
2694     asn1_type_name = "NumericString"
2695
2696
2697 class PrintableString(CommonString):
2698     __slots__ = ()
2699     tag_default = tag_encode(19)
2700     encoding = "ascii"
2701     asn1_type_name = "PrintableString"
2702
2703
2704 class TeletexString(CommonString):
2705     __slots__ = ()
2706     tag_default = tag_encode(20)
2707     encoding = "ascii"
2708     asn1_type_name = "TeletexString"
2709
2710
2711 class T61String(TeletexString):
2712     __slots__ = ()
2713     asn1_type_name = "T61String"
2714
2715
2716 class VideotexString(CommonString):
2717     __slots__ = ()
2718     tag_default = tag_encode(21)
2719     encoding = "iso-8859-1"
2720     asn1_type_name = "VideotexString"
2721
2722
2723 class IA5String(CommonString):
2724     __slots__ = ()
2725     tag_default = tag_encode(22)
2726     encoding = "ascii"
2727     asn1_type_name = "IA5"
2728
2729
2730 class UTCTime(CommonString):
2731     """``UTCTime`` datetime type
2732
2733     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2734     UTCTime UTCTime 2017-09-30T22:07:50
2735     >>> str(t)
2736     '170930220750Z'
2737     >>> bytes(t)
2738     b'170930220750Z'
2739     >>> t.todatetime()
2740     datetime.datetime(2017, 9, 30, 22, 7, 50)
2741     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2742     datetime.datetime(1957, 9, 30, 22, 7, 50)
2743     """
2744     __slots__ = ()
2745     tag_default = tag_encode(23)
2746     encoding = "ascii"
2747     asn1_type_name = "UTCTime"
2748
2749     fmt = "%y%m%d%H%M%SZ"
2750
2751     def __init__(
2752             self,
2753             value=None,
2754             impl=None,
2755             expl=None,
2756             default=None,
2757             optional=False,
2758             _decoded=(0, 0, 0),
2759             bounds=None,  # dummy argument, workability for OctetString.decode
2760     ):
2761         """
2762         :param value: set the value. Either datetime type, or
2763                       :py:class:`pyderasn.UTCTime` object
2764         :param bytes impl: override default tag with ``IMPLICIT`` one
2765         :param bytes expl: override default tag with ``EXPLICIT`` one
2766         :param default: set default value. Type same as in ``value``
2767         :param bool optional: is object ``OPTIONAL`` in sequence
2768         """
2769         super(UTCTime, self).__init__(
2770             impl=impl,
2771             expl=expl,
2772             default=default,
2773             optional=optional,
2774             _decoded=_decoded,
2775         )
2776         self._value = value
2777         if value is not None:
2778             self._value = self._value_sanitize(value)
2779         if default is not None:
2780             default = self._value_sanitize(default)
2781             self.default = self.__class__(
2782                 value=default,
2783                 impl=self.tag,
2784                 expl=self._expl,
2785             )
2786             if self._value is None:
2787                 self._value = default
2788
2789     def _value_sanitize(self, value):
2790         if isinstance(value, self.__class__):
2791             return value._value
2792         if isinstance(value, datetime):
2793             return value.strftime(self.fmt).encode("ascii")
2794         if isinstance(value, binary_type):
2795             value_decoded = value.decode("ascii")
2796             if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2797                 try:
2798                     datetime.strptime(value_decoded, self.fmt)
2799                 except ValueError:
2800                     raise DecodeError("invalid UTCTime format")
2801                 return value
2802             else:
2803                 raise DecodeError("invalid UTCTime length")
2804         raise InvalidValueType((self.__class__, datetime))
2805
2806     def __eq__(self, their):
2807         if isinstance(their, binary_type):
2808             return self._value == their
2809         if isinstance(their, datetime):
2810             return self.todatetime() == their
2811         if not isinstance(their, self.__class__):
2812             return False
2813         return (
2814             self._value == their._value and
2815             self.tag == their.tag and
2816             self._expl == their._expl
2817         )
2818
2819     def todatetime(self):
2820         """Convert to datetime
2821
2822         :returns: datetime
2823
2824         Pay attention that UTCTime can not hold full year, so all years
2825         having < 50 years are treated as 20xx, 19xx otherwise, according
2826         to X.509 recomendation.
2827         """
2828         value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2829         year = value.year % 100
2830         return datetime(
2831             year=(2000 + year) if year < 50 else (1900 + year),
2832             month=value.month,
2833             day=value.day,
2834             hour=value.hour,
2835             minute=value.minute,
2836             second=value.second,
2837         )
2838
2839     def __repr__(self):
2840         return pp_console_row(next(self.pps()))
2841
2842     def pps(self, decode_path=()):
2843         yield _pp(
2844             asn1_type_name=self.asn1_type_name,
2845             obj_name=self.__class__.__name__,
2846             decode_path=decode_path,
2847             value=self.todatetime().isoformat() if self.ready else None,
2848             optional=self.optional,
2849             default=self == self.default,
2850             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2851             expl=None if self._expl is None else tag_decode(self._expl),
2852             offset=self.offset,
2853             tlen=self.tlen,
2854             llen=self.llen,
2855             vlen=self.vlen,
2856         )
2857
2858
2859 class GeneralizedTime(UTCTime):
2860     """``GeneralizedTime`` datetime type
2861
2862     This type is similar to :py:class:`pyderasn.UTCTime`.
2863
2864     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2865     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2866     >>> str(t)
2867     '20170930220750.000123Z'
2868     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2869     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2870     """
2871     __slots__ = ()
2872     tag_default = tag_encode(24)
2873     asn1_type_name = "GeneralizedTime"
2874
2875     fmt = "%Y%m%d%H%M%SZ"
2876     fmt_ms = "%Y%m%d%H%M%S.%fZ"
2877
2878     def _value_sanitize(self, value):
2879         if isinstance(value, self.__class__):
2880             return value._value
2881         if isinstance(value, datetime):
2882             return value.strftime(
2883                 self.fmt_ms if value.microsecond > 0 else self.fmt
2884             ).encode("ascii")
2885         if isinstance(value, binary_type):
2886             value_decoded = value.decode("ascii")
2887             if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2888                 try:
2889                     datetime.strptime(value_decoded, self.fmt)
2890                 except ValueError:
2891                     raise DecodeError(
2892                         "invalid GeneralizedTime (without ms) format",
2893                     )
2894                 return value
2895             elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2896                 try:
2897                     datetime.strptime(value_decoded, self.fmt_ms)
2898                 except ValueError:
2899                     raise DecodeError(
2900                         "invalid GeneralizedTime (with ms) format",
2901                     )
2902                 return value
2903             else:
2904                 raise DecodeError(
2905                     "invalid GeneralizedTime length",
2906                     klass=self.__class__,
2907                 )
2908         raise InvalidValueType((self.__class__, datetime))
2909
2910     def todatetime(self):
2911         value = self._value.decode("ascii")
2912         if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2913             return datetime.strptime(value, self.fmt)
2914         return datetime.strptime(value, self.fmt_ms)
2915
2916
2917 class GraphicString(CommonString):
2918     __slots__ = ()
2919     tag_default = tag_encode(25)
2920     encoding = "iso-8859-1"
2921     asn1_type_name = "GraphicString"
2922
2923
2924 class VisibleString(CommonString):
2925     __slots__ = ()
2926     tag_default = tag_encode(26)
2927     encoding = "ascii"
2928     asn1_type_name = "VisibleString"
2929
2930
2931 class ISO646String(VisibleString):
2932     __slots__ = ()
2933     asn1_type_name = "ISO646String"
2934
2935
2936 class GeneralString(CommonString):
2937     __slots__ = ()
2938     tag_default = tag_encode(27)
2939     encoding = "iso-8859-1"
2940     asn1_type_name = "GeneralString"
2941
2942
2943 class UniversalString(CommonString):
2944     __slots__ = ()
2945     tag_default = tag_encode(28)
2946     encoding = "utf-32-be"
2947     asn1_type_name = "UniversalString"
2948
2949
2950 class BMPString(CommonString):
2951     __slots__ = ()
2952     tag_default = tag_encode(30)
2953     encoding = "utf-16-be"
2954     asn1_type_name = "BMPString"
2955
2956
2957 class Choice(Obj):
2958     """``CHOICE`` special type
2959
2960     ::
2961
2962         class GeneralName(Choice):
2963             schema = (
2964                 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2965                 ('dNSName', IA5String(impl=tag_ctxp(2))),
2966             )
2967
2968     >>> gn = GeneralName()
2969     GeneralName CHOICE
2970     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2971     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2972     >>> gn["dNSName"] = IA5String("bar.baz")
2973     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2974     >>> gn["rfc822Name"]
2975     None
2976     >>> gn["dNSName"]
2977     [2] IA5String IA5 bar.baz
2978     >>> gn.choice
2979     'dNSName'
2980     >>> gn.value == gn["dNSName"]
2981     True
2982     >>> gn.specs
2983     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2984
2985     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2986     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2987     """
2988     __slots__ = ("specs",)
2989     tag_default = None
2990     asn1_type_name = "CHOICE"
2991
2992     def __init__(
2993             self,
2994             value=None,
2995             schema=None,
2996             impl=None,
2997             expl=None,
2998             default=None,
2999             optional=False,
3000             _decoded=(0, 0, 0),
3001     ):
3002         """
3003         :param value: set the value. Either ``(choice, value)`` tuple, or
3004                       :py:class:`pyderasn.Choice` object
3005         :param bytes impl: can not be set, do **not** use it
3006         :param bytes expl: override default tag with ``EXPLICIT`` one
3007         :param default: set default value. Type same as in ``value``
3008         :param bool optional: is object ``OPTIONAL`` in sequence
3009         """
3010         if impl is not None:
3011             raise ValueError("no implicit tag allowed for CHOICE")
3012         super(Choice, self).__init__(None, expl, default, optional, _decoded)
3013         if schema is None:
3014             schema = getattr(self, "schema", ())
3015         if len(schema) == 0:
3016             raise ValueError("schema must be specified")
3017         self.specs = (
3018             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3019         )
3020         self._value = None
3021         if value is not None:
3022             self._value = self._value_sanitize(value)
3023         if default is not None:
3024             default_value = self._value_sanitize(default)
3025             default_obj = self.__class__(impl=self.tag, expl=self._expl)
3026             default_obj.specs = self.specs
3027             default_obj._value = default_value
3028             self.default = default_obj
3029             if value is None:
3030                 self._value = default_obj.copy()._value
3031
3032     def _value_sanitize(self, value):
3033         if isinstance(value, self.__class__):
3034             return value._value
3035         if isinstance(value, tuple) and len(value) == 2:
3036             choice, obj = value
3037             spec = self.specs.get(choice)
3038             if spec is None:
3039                 raise ObjUnknown(choice)
3040             if not isinstance(obj, spec.__class__):
3041                 raise InvalidValueType((spec,))
3042             return (choice, spec(obj))
3043         raise InvalidValueType((self.__class__, tuple))
3044
3045     @property
3046     def ready(self):
3047         return self._value is not None and self._value[1].ready
3048
3049     def copy(self):
3050         obj = self.__class__(schema=self.specs)
3051         obj._expl = self._expl
3052         obj.default = self.default
3053         obj.optional = self.optional
3054         obj.offset = self.offset
3055         obj.llen = self.llen
3056         obj.vlen = self.vlen
3057         value = self._value
3058         if value is not None:
3059             obj._value = (value[0], value[1].copy())
3060         return obj
3061
3062     def __eq__(self, their):
3063         if isinstance(their, tuple) and len(their) == 2:
3064             return self._value == their
3065         if not isinstance(their, self.__class__):
3066             return False
3067         return (
3068             self.specs == their.specs and
3069             self._value == their._value
3070         )
3071
3072     def __call__(
3073             self,
3074             value=None,
3075             expl=None,
3076             default=None,
3077             optional=None,
3078     ):
3079         return self.__class__(
3080             value=value,
3081             schema=self.specs,
3082             expl=self._expl if expl is None else expl,
3083             default=self.default if default is None else default,
3084             optional=self.optional if optional is None else optional,
3085         )
3086
3087     @property
3088     def choice(self):
3089         self._assert_ready()
3090         return self._value[0]
3091
3092     @property
3093     def value(self):
3094         self._assert_ready()
3095         return self._value[1]
3096
3097     def __getitem__(self, key):
3098         if key not in self.specs:
3099             raise ObjUnknown(key)
3100         if self._value is None:
3101             return None
3102         choice, value = self._value
3103         if choice != key:
3104             return None
3105         return value
3106
3107     def __setitem__(self, key, value):
3108         spec = self.specs.get(key)
3109         if spec is None:
3110             raise ObjUnknown(key)
3111         if not isinstance(value, spec.__class__):
3112             raise InvalidValueType((spec.__class__,))
3113         self._value = (key, spec(value))
3114
3115     @property
3116     def tlen(self):
3117         return 0
3118
3119     @property
3120     def decoded(self):
3121         return self._value[1].decoded if self.ready else False
3122
3123     def _encode(self):
3124         self._assert_ready()
3125         return self._value[1].encode()
3126
3127     def _decode(self, tlv, offset=0, decode_path=()):
3128         for choice, spec in self.specs.items():
3129             try:
3130                 value, tail = spec.decode(
3131                     tlv,
3132                     offset=offset,
3133                     leavemm=True,
3134                     decode_path=decode_path + (choice,),
3135                 )
3136             except TagMismatch:
3137                 continue
3138             obj = self.__class__(
3139                 schema=self.specs,
3140                 expl=self._expl,
3141                 default=self.default,
3142                 optional=self.optional,
3143                 _decoded=(offset, 0, value.tlvlen),
3144             )
3145             obj._value = (choice, value)
3146             return obj, tail
3147         raise TagMismatch(
3148             klass=self.__class__,
3149             decode_path=decode_path,
3150             offset=offset,
3151         )
3152
3153     def __repr__(self):
3154         value = pp_console_row(next(self.pps()))
3155         if self.ready:
3156             value = "%s[%r]" % (value, self.value)
3157         return value
3158
3159     def pps(self, decode_path=()):
3160         yield _pp(
3161             asn1_type_name=self.asn1_type_name,
3162             obj_name=self.__class__.__name__,
3163             decode_path=decode_path,
3164             value=self.choice if self.ready else None,
3165             optional=self.optional,
3166             default=self == self.default,
3167             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3168             expl=None if self._expl is None else tag_decode(self._expl),
3169             offset=self.offset,
3170             tlen=self.tlen,
3171             llen=self.llen,
3172             vlen=self.vlen,
3173         )
3174         if self.ready:
3175             yield self.value.pps(decode_path=decode_path + (self.choice,))
3176
3177
3178 class PrimitiveTypes(Choice):
3179     """Predefined ``CHOICE`` for all generic primitive types
3180
3181     It could be useful for general decoding of some unspecified values:
3182
3183     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3184     OCTET STRING 3 bytes 666f6f
3185     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3186     INTEGER 1193046
3187     """
3188     __slots__ = ()
3189     schema = tuple((klass.__name__, klass()) for klass in (
3190         Boolean,
3191         Integer,
3192         BitString,
3193         OctetString,
3194         Null,
3195         ObjectIdentifier,
3196         UTF8String,
3197         NumericString,
3198         PrintableString,
3199         TeletexString,
3200         VideotexString,
3201         IA5String,
3202         UTCTime,
3203         GeneralizedTime,
3204         GraphicString,
3205         VisibleString,
3206         ISO646String,
3207         GeneralString,
3208         UniversalString,
3209         BMPString,
3210     ))
3211
3212
3213 class Any(Obj):
3214     """``ANY`` special type
3215
3216     >>> Any(Integer(-123))
3217     ANY 020185
3218     >>> a = Any(OctetString(b"hello world").encode())
3219     ANY 040b68656c6c6f20776f726c64
3220     >>> hexenc(bytes(a))
3221     b'0x040x0bhello world'
3222     """
3223     __slots__ = ()
3224     tag_default = tag_encode(0)
3225     asn1_type_name = "ANY"
3226
3227     def __init__(
3228             self,
3229             value=None,
3230             expl=None,
3231             optional=False,
3232             _decoded=(0, 0, 0),
3233     ):
3234         """
3235         :param value: set the value. Either any kind of pyderasn's
3236                       **ready** object, or bytes. Pay attention that
3237                       **no** validation is performed is raw binary value
3238                       is valid TLV
3239         :param bytes expl: override default tag with ``EXPLICIT`` one
3240         :param bool optional: is object ``OPTIONAL`` in sequence
3241         """
3242         super(Any, self).__init__(None, expl, None, optional, _decoded)
3243         self._value = None if value is None else self._value_sanitize(value)
3244
3245     def _value_sanitize(self, value):
3246         if isinstance(value, self.__class__):
3247             return value._value
3248         if isinstance(value, Obj):
3249             return value.encode()
3250         if isinstance(value, binary_type):
3251             return value
3252         raise InvalidValueType((self.__class__, Obj, binary_type))
3253
3254     @property
3255     def ready(self):
3256         return self._value is not None
3257
3258     def copy(self):
3259         obj = self.__class__()
3260         obj._value = self._value
3261         obj.tag = self.tag
3262         obj._expl = self._expl
3263         obj.optional = self.optional
3264         obj.offset = self.offset
3265         obj.llen = self.llen
3266         obj.vlen = self.vlen
3267         return obj
3268
3269     def __eq__(self, their):
3270         if isinstance(their, binary_type):
3271             return self._value == their
3272         if issubclass(their.__class__, Any):
3273             return self._value == their._value
3274         return False
3275
3276     def __call__(
3277             self,
3278             value=None,
3279             expl=None,
3280             optional=None,
3281     ):
3282         return self.__class__(
3283             value=value,
3284             expl=self._expl if expl is None else expl,
3285             optional=self.optional if optional is None else optional,
3286         )
3287
3288     def __bytes__(self):
3289         self._assert_ready()
3290         return self._value
3291
3292     @property
3293     def tlen(self):
3294         return 0
3295
3296     def _encode(self):
3297         self._assert_ready()
3298         return self._value
3299
3300     def _decode(self, tlv, offset=0, decode_path=()):
3301         try:
3302             t, tlen, lv = tag_strip(tlv)
3303             l, llen, v = len_decode(lv)
3304         except DecodeError as err:
3305             raise err.__class__(
3306                 msg=err.msg,
3307                 klass=self.__class__,
3308                 decode_path=decode_path,
3309                 offset=offset,
3310             )
3311         if l > len(v):
3312             raise NotEnoughData(
3313                 "encoded length is longer than data",
3314                 klass=self.__class__,
3315                 decode_path=decode_path,
3316                 offset=offset,
3317             )
3318         tlvlen = tlen + llen + l
3319         v, tail = tlv[:tlvlen], v[l:]
3320         obj = self.__class__(
3321             value=v.tobytes(),
3322             expl=self._expl,
3323             optional=self.optional,
3324             _decoded=(offset, 0, tlvlen),
3325         )
3326         obj.tag = t
3327         return obj, tail
3328
3329     def __repr__(self):
3330         return pp_console_row(next(self.pps()))
3331
3332     def pps(self, decode_path=()):
3333         yield _pp(
3334             asn1_type_name=self.asn1_type_name,
3335             obj_name=self.__class__.__name__,
3336             decode_path=decode_path,
3337             blob=self._value if self.ready else None,
3338             optional=self.optional,
3339             default=self == self.default,
3340             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3341             expl=None if self._expl is None else tag_decode(self._expl),
3342             offset=self.offset,
3343             tlen=self.tlen,
3344             llen=self.llen,
3345             vlen=self.vlen,
3346             expl_offset=self.expl_offset if self.expled else None,
3347             expl_tlen=self.expl_tlen if self.expled else None,
3348             expl_llen=self.expl_llen if self.expled else None,
3349             expl_vlen=self.expl_vlen if self.expled else None,
3350         )
3351
3352
3353 ########################################################################
3354 # ASN.1 constructed types
3355 ########################################################################
3356
3357 class Sequence(Obj):
3358     """``SEQUENCE`` structure type
3359
3360     You have to make specification of sequence::
3361
3362         class Extension(Sequence):
3363             __slots__ = ()
3364             schema = (
3365                 ("extnID", ObjectIdentifier()),
3366                 ("critical", Boolean(default=False)),
3367                 ("extnValue", OctetString()),
3368             )
3369
3370     Then, you can work with it as with dictionary.
3371
3372     >>> ext = Extension()
3373     >>> Extension().specs
3374     OrderedDict([
3375         ('extnID', OBJECT IDENTIFIER),
3376         ('critical', BOOLEAN False OPTIONAL DEFAULT),
3377         ('extnValue', OCTET STRING),
3378     ])
3379     >>> ext["extnID"] = "1.2.3"
3380     Traceback (most recent call last):
3381     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3382     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3383
3384     You can know if sequence is ready to be encoded:
3385
3386     >>> ext.ready
3387     False
3388     >>> ext.encode()
3389     Traceback (most recent call last):
3390     pyderasn.ObjNotReady: object is not ready: extnValue
3391     >>> ext["extnValue"] = OctetString(b"foobar")
3392     >>> ext.ready
3393     True
3394
3395     Value you want to assign, must have the same **type** as in
3396     corresponding specification, but it can have different tags,
3397     optional/default attributes -- they will be taken from specification
3398     automatically::
3399
3400         class TBSCertificate(Sequence):
3401             schema = (
3402                 ("version", Version(expl=tag_ctxc(0), default="v1")),
3403             [...]
3404
3405     >>> tbs = TBSCertificate()
3406     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3407
3408     You can know if value exists/set in the sequence and take its value:
3409
3410     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3411     (True, True, False)
3412     >>> ext["extnID"]
3413     OBJECT IDENTIFIER 1.2.3
3414
3415     But pay attention that if value has default, then it won't be (not
3416     in) in the sequence (because ``DEFAULT`` must not be encoded in
3417     DER), but you can read its value:
3418
3419     >>> "critical" in ext, ext["critical"]
3420     (False, BOOLEAN False)
3421     >>> ext["critical"] = Boolean(True)
3422     >>> "critical" in ext, ext["critical"]
3423     (True, BOOLEAN True)
3424
3425     All defaulted values are always optional.
3426
3427     .. warning::
3428
3429        When decoded DER contains defaulted value inside, then
3430        technically this is not valid DER encoding. But we allow
3431        and pass it. Of course reencoding of that kind of DER will
3432        result in different binary representation (validly without
3433        defaulted value inside).
3434
3435     Two sequences are equal if they have equal specification (schema),
3436     implicit/explicit tagging and the same values.
3437     """
3438     __slots__ = ("specs",)
3439     tag_default = tag_encode(form=TagFormConstructed, num=16)
3440     asn1_type_name = "SEQUENCE"
3441
3442     def __init__(
3443             self,
3444             value=None,
3445             schema=None,
3446             impl=None,
3447             expl=None,
3448             default=None,
3449             optional=False,
3450             _decoded=(0, 0, 0),
3451     ):
3452         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3453         if schema is None:
3454             schema = getattr(self, "schema", ())
3455         self.specs = (
3456             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3457         )
3458         self._value = {}
3459         if value is not None:
3460             self._value = self._value_sanitize(value)
3461         if default is not None:
3462             default_value = self._value_sanitize(default)
3463             default_obj = self.__class__(impl=self.tag, expl=self._expl)
3464             default_obj.specs = self.specs
3465             default_obj._value = default_value
3466             self.default = default_obj
3467             if value is None:
3468                 self._value = default_obj.copy()._value
3469
3470     def _value_sanitize(self, value):
3471         if not issubclass(value.__class__, Sequence):
3472             raise InvalidValueType((Sequence,))
3473         return value._value
3474
3475     @property
3476     def ready(self):
3477         for name, spec in self.specs.items():
3478             value = self._value.get(name)
3479             if value is None:
3480                 if spec.optional:
3481                     continue
3482                 return False
3483             else:
3484                 if not value.ready:
3485                     return False
3486         return True
3487
3488     def copy(self):
3489         obj = self.__class__(schema=self.specs)
3490         obj.tag = self.tag
3491         obj._expl = self._expl
3492         obj.default = self.default
3493         obj.optional = self.optional
3494         obj.offset = self.offset
3495         obj.llen = self.llen
3496         obj.vlen = self.vlen
3497         obj._value = {k: v.copy() for k, v in self._value.items()}
3498         return obj
3499
3500     def __eq__(self, their):
3501         if not isinstance(their, self.__class__):
3502             return False
3503         return (
3504             self.specs == their.specs and
3505             self.tag == their.tag and
3506             self._expl == their._expl and
3507             self._value == their._value
3508         )
3509
3510     def __call__(
3511             self,
3512             value=None,
3513             impl=None,
3514             expl=None,
3515             default=None,
3516             optional=None,
3517     ):
3518         return self.__class__(
3519             value=value,
3520             schema=self.specs,
3521             impl=self.tag if impl is None else impl,
3522             expl=self._expl if expl is None else expl,
3523             default=self.default if default is None else default,
3524             optional=self.optional if optional is None else optional,
3525         )
3526
3527     def __contains__(self, key):
3528         return key in self._value
3529
3530     def __setitem__(self, key, value):
3531         spec = self.specs.get(key)
3532         if spec is None:
3533             raise ObjUnknown(key)
3534         if value is None:
3535             self._value.pop(key, None)
3536             return
3537         if not isinstance(value, spec.__class__):
3538             raise InvalidValueType((spec.__class__,))
3539         value = spec(value=value)
3540         if spec.default is not None and value == spec.default:
3541             self._value.pop(key, None)
3542             return
3543         self._value[key] = value
3544
3545     def __getitem__(self, key):
3546         value = self._value.get(key)
3547         if value is not None:
3548             return value
3549         spec = self.specs.get(key)
3550         if spec is None:
3551             raise ObjUnknown(key)
3552         if spec.default is not None:
3553             return spec.default
3554         return None
3555
3556     def _encoded_values(self):
3557         raws = []
3558         for name, spec in self.specs.items():
3559             value = self._value.get(name)
3560             if value is None:
3561                 if spec.optional:
3562                     continue
3563                 raise ObjNotReady(name)
3564             raws.append(value.encode())
3565         return raws
3566
3567     def _encode(self):
3568         v = b"".join(self._encoded_values())
3569         return b"".join((self.tag, len_encode(len(v)), v))
3570
3571     def _decode(self, tlv, offset=0, decode_path=()):
3572         try:
3573             t, tlen, lv = tag_strip(tlv)
3574         except DecodeError as err:
3575             raise err.__class__(
3576                 msg=err.msg,
3577                 klass=self.__class__,
3578                 decode_path=decode_path,
3579                 offset=offset,
3580             )
3581         if t != self.tag:
3582             raise TagMismatch(
3583                 klass=self.__class__,
3584                 decode_path=decode_path,
3585                 offset=offset,
3586             )
3587         try:
3588             l, llen, v = len_decode(lv)
3589         except DecodeError as err:
3590             raise err.__class__(
3591                 msg=err.msg,
3592                 klass=self.__class__,
3593                 decode_path=decode_path,
3594                 offset=offset,
3595             )
3596         if l > len(v):
3597             raise NotEnoughData(
3598                 "encoded length is longer than data",
3599                 klass=self.__class__,
3600                 decode_path=decode_path,
3601                 offset=offset,
3602             )
3603         v, tail = v[:l], v[l:]
3604         sub_offset = offset + tlen + llen
3605         values = {}
3606         for name, spec in self.specs.items():
3607             if len(v) == 0 and spec.optional:
3608                 continue
3609             try:
3610                 value, v_tail = spec.decode(
3611                     v,
3612                     sub_offset,
3613                     leavemm=True,
3614                     decode_path=decode_path + (name,),
3615                 )
3616             except TagMismatch:
3617                 if spec.optional:
3618                     continue
3619                 raise
3620             sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3621             v = v_tail
3622             if spec.default is not None and value == spec.default:
3623                 # Encoded default values are not valid in DER,
3624                 # but we still allow that
3625                 continue
3626             values[name] = value
3627         if len(v) > 0:
3628             raise DecodeError(
3629                 "remaining data",
3630                 klass=self.__class__,
3631                 decode_path=decode_path,
3632                 offset=offset,
3633             )
3634         obj = self.__class__(
3635             schema=self.specs,
3636             impl=self.tag,
3637             expl=self._expl,
3638             default=self.default,
3639             optional=self.optional,
3640             _decoded=(offset, llen, l),
3641         )
3642         obj._value = values
3643         return obj, tail
3644
3645     def __repr__(self):
3646         value = pp_console_row(next(self.pps()))
3647         cols = []
3648         for name in self.specs:
3649             _value = self._value.get(name)
3650             if _value is None:
3651                 continue
3652             cols.append(repr(_value))
3653         return "%s[%s]" % (value, ", ".join(cols))
3654
3655     def pps(self, decode_path=()):
3656         yield _pp(
3657             asn1_type_name=self.asn1_type_name,
3658             obj_name=self.__class__.__name__,
3659             decode_path=decode_path,
3660             optional=self.optional,
3661             default=self == self.default,
3662             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3663             expl=None if self._expl is None else tag_decode(self._expl),
3664             offset=self.offset,
3665             tlen=self.tlen,
3666             llen=self.llen,
3667             vlen=self.vlen,
3668             expl_offset=self.expl_offset if self.expled else None,
3669             expl_tlen=self.expl_tlen if self.expled else None,
3670             expl_llen=self.expl_llen if self.expled else None,
3671             expl_vlen=self.expl_vlen if self.expled else None,
3672         )
3673         for name in self.specs:
3674             value = self._value.get(name)
3675             if value is None:
3676                 continue
3677             yield value.pps(decode_path=decode_path + (name,))
3678
3679
3680 class Set(Sequence):
3681     """``SET`` structure type
3682
3683     Its usage is identical to :py:class:`pyderasn.Sequence`.
3684     """
3685     __slots__ = ()
3686     tag_default = tag_encode(form=TagFormConstructed, num=17)
3687     asn1_type_name = "SET"
3688
3689     def _encode(self):
3690         raws = self._encoded_values()
3691         raws.sort()
3692         v = b"".join(raws)
3693         return b"".join((self.tag, len_encode(len(v)), v))
3694
3695     def _decode(self, tlv, offset=0, decode_path=()):
3696         try:
3697             t, tlen, lv = tag_strip(tlv)
3698         except DecodeError as err:
3699             raise err.__class__(
3700                 msg=err.msg,
3701                 klass=self.__class__,
3702                 decode_path=decode_path,
3703                 offset=offset,
3704             )
3705         if t != self.tag:
3706             raise TagMismatch(
3707                 klass=self.__class__,
3708                 decode_path=decode_path,
3709                 offset=offset,
3710             )
3711         try:
3712             l, llen, v = len_decode(lv)
3713         except DecodeError as err:
3714             raise err.__class__(
3715                 msg=err.msg,
3716                 klass=self.__class__,
3717                 decode_path=decode_path,
3718                 offset=offset,
3719             )
3720         if l > len(v):
3721             raise NotEnoughData(
3722                 "encoded length is longer than data",
3723                 klass=self.__class__,
3724                 offset=offset,
3725             )
3726         v, tail = v[:l], v[l:]
3727         sub_offset = offset + tlen + llen
3728         values = {}
3729         specs_items = self.specs.items
3730         while len(v) > 0:
3731             for name, spec in specs_items():
3732                 try:
3733                     value, v_tail = spec.decode(
3734                         v,
3735                         sub_offset,
3736                         leavemm=True,
3737                         decode_path=decode_path + (name,),
3738                     )
3739                 except TagMismatch:
3740                     continue
3741                 sub_offset += (
3742                     value.expl_tlvlen if value.expled else value.tlvlen
3743                 )
3744                 v = v_tail
3745                 if spec.default is None or value != spec.default:  # pragma: no cover
3746                     # SeqMixing.test_encoded_default_accepted covers that place
3747                     values[name] = value
3748                 break
3749             else:
3750                 raise TagMismatch(
3751                     klass=self.__class__,
3752                     decode_path=decode_path,
3753                     offset=offset,
3754                 )
3755         obj = self.__class__(
3756             schema=self.specs,
3757             impl=self.tag,
3758             expl=self._expl,
3759             default=self.default,
3760             optional=self.optional,
3761             _decoded=(offset, llen, l),
3762         )
3763         obj._value = values
3764         return obj, tail
3765
3766
3767 class SequenceOf(Obj):
3768     """``SEQUENCE OF`` sequence type
3769
3770     For that kind of type you must specify the object it will carry on
3771     (bounds are for example here, not required)::
3772
3773         class Ints(SequenceOf):
3774             schema = Integer()
3775             bounds = (0, 2)
3776
3777     >>> ints = Ints()
3778     >>> ints.append(Integer(123))
3779     >>> ints.append(Integer(234))
3780     >>> ints
3781     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3782     >>> [int(i) for i in ints]
3783     [123, 234]
3784     >>> ints.append(Integer(345))
3785     Traceback (most recent call last):
3786     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3787     >>> ints[1]
3788     INTEGER 234
3789     >>> ints[1] = Integer(345)
3790     >>> ints
3791     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3792
3793     Also you can initialize sequence with preinitialized values:
3794
3795     >>> ints = Ints([Integer(123), Integer(234)])
3796     """
3797     __slots__ = ("spec", "_bound_min", "_bound_max")
3798     tag_default = tag_encode(form=TagFormConstructed, num=16)
3799     asn1_type_name = "SEQUENCE OF"
3800
3801     def __init__(
3802             self,
3803             value=None,
3804             schema=None,
3805             bounds=None,
3806             impl=None,
3807             expl=None,
3808             default=None,
3809             optional=False,
3810             _decoded=(0, 0, 0),
3811     ):
3812         super(SequenceOf, self).__init__(
3813             impl,
3814             expl,
3815             default,
3816             optional,
3817             _decoded,
3818         )
3819         if schema is None:
3820             schema = getattr(self, "schema", None)
3821         if schema is None:
3822             raise ValueError("schema must be specified")
3823         self.spec = schema
3824         if bounds is None:
3825             self._bound_min, self._bound_max = getattr(
3826                 self,
3827                 "bounds",
3828                 (0, float("+inf")),
3829             )
3830         else:
3831             self._bound_min, self._bound_max = bounds
3832         self._value = []
3833         if value is not None:
3834             self._value = self._value_sanitize(value)
3835         if default is not None:
3836             default_value = self._value_sanitize(default)
3837             default_obj = self.__class__(
3838                 schema=schema,
3839                 impl=self.tag,
3840                 expl=self._expl,
3841             )
3842             default_obj._value = default_value
3843             self.default = default_obj
3844             if value is None:
3845                 self._value = default_obj.copy()._value
3846
3847     def _value_sanitize(self, value):
3848         if issubclass(value.__class__, SequenceOf):
3849             value = value._value
3850         elif hasattr(value, "__iter__"):
3851             value = list(value)
3852         else:
3853             raise InvalidValueType((self.__class__, iter))
3854         if not self._bound_min <= len(value) <= self._bound_max:
3855             raise BoundsError(self._bound_min, len(value), self._bound_max)
3856         for v in value:
3857             if not isinstance(v, self.spec.__class__):
3858                 raise InvalidValueType((self.spec.__class__,))
3859         return value
3860
3861     @property
3862     def ready(self):
3863         return all(v.ready for v in self._value)
3864
3865     def copy(self):
3866         obj = self.__class__(schema=self.spec)
3867         obj._bound_min = self._bound_min
3868         obj._bound_max = self._bound_max
3869         obj.tag = self.tag
3870         obj._expl = self._expl
3871         obj.default = self.default
3872         obj.optional = self.optional
3873         obj.offset = self.offset
3874         obj.llen = self.llen
3875         obj.vlen = self.vlen
3876         obj._value = [v.copy() for v in self._value]
3877         return obj
3878
3879     def __eq__(self, their):
3880         if isinstance(their, self.__class__):
3881             return (
3882                 self.spec == their.spec and
3883                 self.tag == their.tag and
3884                 self._expl == their._expl and
3885                 self._value == their._value
3886             )
3887         if hasattr(their, "__iter__"):
3888             return self._value == list(their)
3889         return False
3890
3891     def __call__(
3892             self,
3893             value=None,
3894             bounds=None,
3895             impl=None,
3896             expl=None,
3897             default=None,
3898             optional=None,
3899     ):
3900         return self.__class__(
3901             value=value,
3902             schema=self.spec,
3903             bounds=(
3904                 (self._bound_min, self._bound_max)
3905                 if bounds is None else bounds
3906             ),
3907             impl=self.tag if impl is None else impl,
3908             expl=self._expl if expl is None else expl,
3909             default=self.default if default is None else default,
3910             optional=self.optional if optional is None else optional,
3911         )
3912
3913     def __contains__(self, key):
3914         return key in self._value
3915
3916     def append(self, value):
3917         if not isinstance(value, self.spec.__class__):
3918             raise InvalidValueType((self.spec.__class__,))
3919         if len(self._value) + 1 > self._bound_max:
3920             raise BoundsError(
3921                 self._bound_min,
3922                 len(self._value) + 1,
3923                 self._bound_max,
3924             )
3925         self._value.append(value)
3926
3927     def __iter__(self):
3928         self._assert_ready()
3929         return iter(self._value)
3930
3931     def __len__(self):
3932         self._assert_ready()
3933         return len(self._value)
3934
3935     def __setitem__(self, key, value):
3936         if not isinstance(value, self.spec.__class__):
3937             raise InvalidValueType((self.spec.__class__,))
3938         self._value[key] = self.spec(value=value)
3939
3940     def __getitem__(self, key):
3941         return self._value[key]
3942
3943     def _encoded_values(self):
3944         return [v.encode() for v in self._value]
3945
3946     def _encode(self):
3947         v = b"".join(self._encoded_values())
3948         return b"".join((self.tag, len_encode(len(v)), v))
3949
3950     def _decode(self, tlv, offset=0, decode_path=()):
3951         try:
3952             t, tlen, lv = tag_strip(tlv)
3953         except DecodeError as err:
3954             raise err.__class__(
3955                 msg=err.msg,
3956                 klass=self.__class__,
3957                 decode_path=decode_path,
3958                 offset=offset,
3959             )
3960         if t != self.tag:
3961             raise TagMismatch(
3962                 klass=self.__class__,
3963                 decode_path=decode_path,
3964                 offset=offset,
3965             )
3966         try:
3967             l, llen, v = len_decode(lv)
3968         except DecodeError as err:
3969             raise err.__class__(
3970                 msg=err.msg,
3971                 klass=self.__class__,
3972                 decode_path=decode_path,
3973                 offset=offset,
3974             )
3975         if l > len(v):
3976             raise NotEnoughData(
3977                 "encoded length is longer than data",
3978                 klass=self.__class__,
3979                 decode_path=decode_path,
3980                 offset=offset,
3981             )
3982         v, tail = v[:l], v[l:]
3983         sub_offset = offset + tlen + llen
3984         _value = []
3985         spec = self.spec
3986         while len(v) > 0:
3987             value, v_tail = spec.decode(
3988                 v,
3989                 sub_offset,
3990                 leavemm=True,
3991                 decode_path=decode_path + (str(len(_value)),),
3992             )
3993             sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3994             v = v_tail
3995             _value.append(value)
3996         obj = self.__class__(
3997             value=_value,
3998             schema=spec,
3999             bounds=(self._bound_min, self._bound_max),
4000             impl=self.tag,
4001             expl=self._expl,
4002             default=self.default,
4003             optional=self.optional,
4004             _decoded=(offset, llen, l),
4005         )
4006         return obj, tail
4007
4008     def __repr__(self):
4009         return "%s[%s]" % (
4010             pp_console_row(next(self.pps())),
4011             ", ".join(repr(v) for v in self._value),
4012         )
4013
4014     def pps(self, decode_path=()):
4015         yield _pp(
4016             asn1_type_name=self.asn1_type_name,
4017             obj_name=self.__class__.__name__,
4018             decode_path=decode_path,
4019             optional=self.optional,
4020             default=self == self.default,
4021             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4022             expl=None if self._expl is None else tag_decode(self._expl),
4023             offset=self.offset,
4024             tlen=self.tlen,
4025             llen=self.llen,
4026             vlen=self.vlen,
4027             expl_offset=self.expl_offset if self.expled else None,
4028             expl_tlen=self.expl_tlen if self.expled else None,
4029             expl_llen=self.expl_llen if self.expled else None,
4030             expl_vlen=self.expl_vlen if self.expled else None,
4031         )
4032         for i, value in enumerate(self._value):
4033             yield value.pps(decode_path=decode_path + (str(i),))
4034
4035
4036 class SetOf(SequenceOf):
4037     """``SET OF`` sequence type
4038
4039     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4040     """
4041     __slots__ = ()
4042     tag_default = tag_encode(form=TagFormConstructed, num=17)
4043     asn1_type_name = "SET OF"
4044
4045     def _encode(self):
4046         raws = self._encoded_values()
4047         raws.sort()
4048         v = b"".join(raws)
4049         return b"".join((self.tag, len_encode(len(v)), v))
4050
4051
4052 def obj_by_path(pypath):  # pragma: no cover
4053     """Import object specified as string Python path
4054
4055     Modules must be separated from classes/functions with ``:``.
4056
4057     >>> obj_by_path("foo.bar:Baz")
4058     <class 'foo.bar.Baz'>
4059     >>> obj_by_path("foo.bar:Baz.boo")
4060     <classmethod 'foo.bar.Baz.boo'>
4061     """
4062     mod, objs = pypath.rsplit(":", 1)
4063     from importlib import import_module
4064     obj = import_module(mod)
4065     for obj_name in objs.split("."):
4066         obj = getattr(obj, obj_name)
4067     return obj
4068
4069
4070 def main():  # pragma: no cover
4071     import argparse
4072     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4073     parser.add_argument(
4074         "--oids",
4075         help="Python path to dictionary with OIDs",
4076     )
4077     parser.add_argument(
4078         "--schema",
4079         help="Python path to schema definition to use",
4080     )
4081     parser.add_argument(
4082         "DERFile",
4083         type=argparse.FileType("rb"),
4084         help="Path to DER file you want to decode",
4085     )
4086     args = parser.parse_args()
4087     der = memoryview(args.DERFile.read())
4088     args.DERFile.close()
4089     oids = obj_by_path(args.oids) if args.oids else {}
4090     if args.schema:
4091         schema = obj_by_path(args.schema)
4092         from functools import partial
4093         pprinter = partial(pprint, big_blobs=True)
4094     else:
4095         # All of this below is a big hack with self references
4096         choice = PrimitiveTypes()
4097         choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4098         choice.specs["SetOf"] = SetOf(schema=choice)
4099         for i in range(31):
4100             choice.specs["SequenceOf%d" % i] = SequenceOf(
4101                 schema=choice,
4102                 expl=tag_ctxc(i),
4103             )
4104         choice.specs["Any"] = Any()
4105
4106         # Class name equals to type name, to omit it from output
4107         class SEQUENCEOF(SequenceOf):
4108             __slots__ = ()
4109             schema = choice
4110         schema = SEQUENCEOF()
4111
4112         def pprint_any(obj, oids=None):
4113             def _pprint_pps(pps):
4114                 for pp in pps:
4115                     if hasattr(pp, "_fields"):
4116                         if pp.asn1_type_name == Choice.asn1_type_name:
4117                             continue
4118                         pp_kwargs = pp._asdict()
4119                         pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4120                         pp = _pp(**pp_kwargs)
4121                         yield pp_console_row(
4122                             pp,
4123                             oids=oids,
4124                             with_offsets=True,
4125                             with_blob=False,
4126                         )
4127                         for row in pp_console_blob(pp):
4128                             yield row
4129                     else:
4130                         for row in _pprint_pps(pp):
4131                             yield row
4132             return "\n".join(_pprint_pps(obj.pps()))
4133         pprinter = pprint_any
4134     obj, tail = schema().decode(der)
4135     print(pprinter(obj, oids=oids))
4136     if tail != b"":
4137         print("\nTrailing data: %s" % hexenc(tail))
4138
4139
4140 if __name__ == "__main__":
4141     main()