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