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