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