3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
26 >>> Integer().decode(raw) == i
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`.
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.
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
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two object of the same type, but with different implicit/explicit tags
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`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
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.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
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
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
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::
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
150 For simplicity you can also set bounds the following way::
152 bounded_x = X(bounds=(MIN, MAX))
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
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.
164 All objects have ``copy()`` method, returning its copy, that can be safely
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.
177 When object is decoded, ``decoded`` property is true and you can safely
178 use following properties:
180 * ``offset`` -- position including initial offset where object's tag starts
181 * ``tlen`` -- length of object's tag
182 * ``llen`` -- length of object's length value
183 * ``vlen`` -- length of object's value
184 * ``tlvlen`` -- length of the whole object
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``).
192 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
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.
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.
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
217 .. autoclass:: pyderasn.Boolean
222 .. autoclass:: pyderasn.Integer
227 .. autoclass:: pyderasn.BitString
232 .. autoclass:: pyderasn.OctetString
237 .. autoclass:: pyderasn.Null
242 .. autoclass:: pyderasn.ObjectIdentifier
247 .. autoclass:: pyderasn.Enumerated
251 .. autoclass:: pyderasn.CommonString
255 .. autoclass:: pyderasn.UTCTime
256 :members: __init__, todatetime
260 .. autoclass:: pyderasn.GeneralizedTime
267 .. autoclass:: pyderasn.Choice
272 .. autoclass:: PrimitiveTypes
276 .. autoclass:: pyderasn.Any
284 .. autoclass:: pyderasn.Sequence
289 .. autoclass:: pyderasn.Set
294 .. autoclass:: pyderasn.SequenceOf
299 .. autoclass:: pyderasn.SetOf
305 .. autofunction:: pyderasn.hexenc
306 .. autofunction:: pyderasn.hexdec
307 .. autofunction:: pyderasn.tag_encode
308 .. autofunction:: pyderasn.tag_decode
309 .. autofunction:: pyderasn.tag_ctxp
310 .. autofunction:: pyderasn.tag_ctxc
311 .. autoclass:: pyderasn.Obj
314 from codecs import getdecoder
315 from codecs import getencoder
316 from collections import namedtuple
317 from collections import OrderedDict
318 from datetime import datetime
319 from math import ceil
321 from six import add_metaclass
322 from six import binary_type
323 from six import byte2int
324 from six import indexbytes
325 from six import int2byte
326 from six import integer_types
327 from six import iterbytes
329 from six import string_types
330 from six import text_type
331 from six.moves import xrange as six_xrange
372 "TagClassApplication",
376 "TagFormConstructed",
387 TagClassUniversal = 0
388 TagClassApplication = 1 << 6
389 TagClassContext = 1 << 7
390 TagClassPrivate = 1 << 6 | 1 << 7
392 TagFormConstructed = 1 << 5
395 TagClassApplication: "APPLICATION ",
396 TagClassPrivate: "PRIVATE ",
397 TagClassUniversal: "UNIV ",
401 ########################################################################
403 ########################################################################
405 class DecodeError(Exception):
406 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
408 :param str msg: reason of decode failing
409 :param klass: optional exact DecodeError inherited class (like
410 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
411 :py:exc:`InvalidLength`)
412 :param decode_path: tuple of strings. It contains human
413 readable names of the fields through which
414 decoding process has passed
415 :param int offset: binary offset where failure happened
417 super(DecodeError, self).__init__()
420 self.decode_path = decode_path
426 "" if self.klass is None else self.klass.__name__,
428 ("(%s)" % ".".join(self.decode_path))
429 if len(self.decode_path) > 0 else ""
431 ("(at %d)" % self.offset) if self.offset > 0 else "",
437 return "%s(%s)" % (self.__class__.__name__, self)
440 class NotEnoughData(DecodeError):
444 class TagMismatch(DecodeError):
448 class InvalidLength(DecodeError):
452 class InvalidOID(DecodeError):
456 class ObjUnknown(ValueError):
457 def __init__(self, name):
458 super(ObjUnknown, self).__init__()
462 return "object is unknown: %s" % self.name
465 return "%s(%s)" % (self.__class__.__name__, self)
468 class ObjNotReady(ValueError):
469 def __init__(self, name):
470 super(ObjNotReady, self).__init__()
474 return "object is not ready: %s" % self.name
477 return "%s(%s)" % (self.__class__.__name__, self)
480 class InvalidValueType(ValueError):
481 def __init__(self, expected_types):
482 super(InvalidValueType, self).__init__()
483 self.expected_types = expected_types
486 return "invalid value type, expected: %s" % ", ".join(
487 [repr(t) for t in self.expected_types]
491 return "%s(%s)" % (self.__class__.__name__, self)
494 class BoundsError(ValueError):
495 def __init__(self, bound_min, value, bound_max):
496 super(BoundsError, self).__init__()
497 self.bound_min = bound_min
499 self.bound_max = bound_max
502 return "unsatisfied bounds: %s <= %s <= %s" % (
509 return "%s(%s)" % (self.__class__.__name__, self)
512 ########################################################################
514 ########################################################################
516 _hexdecoder = getdecoder("hex")
517 _hexencoder = getencoder("hex")
521 """Binary data to hexadecimal string convert
523 return _hexdecoder(data)[0]
527 """Hexadecimal string to binary data convert
529 return _hexencoder(data)[0].decode("ascii")
532 def int_bytes_len(num, byte_len=8):
535 return int(ceil(float(num.bit_length()) / byte_len))
538 def zero_ended_encode(num):
539 octets = bytearray(int_bytes_len(num, 7))
541 octets[i] = num & 0x7F
545 octets[i] = 0x80 | (num & 0x7F)
551 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
552 """Encode tag to binary form
554 :param int num: tag's number
555 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
556 :py:data:`pyderasn.TagClassContext`,
557 :py:data:`pyderasn.TagClassApplication`,
558 :py:data:`pyderasn.TagClassPrivate`)
559 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
560 :py:data:`pyderasn.TagFormConstructed`)
564 return int2byte(klass | form | num)
565 # [XX|X|11111][1.......][1.......] ... [0.......]
566 return int2byte(klass | form | 31) + zero_ended_encode(num)
570 """Decode tag from binary form
574 No validation is performed, assuming that it has already passed.
576 It returns tuple with three integers, as
577 :py:func:`pyderasn.tag_encode` accepts.
579 first_octet = byte2int(tag)
580 klass = first_octet & 0xC0
581 form = first_octet & 0x20
582 if first_octet & 0x1F < 0x1F:
583 return (klass, form, first_octet & 0x1F)
585 for octet in iterbytes(tag[1:]):
588 return (klass, form, num)
592 """Create CONTEXT PRIMITIVE tag
594 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
598 """Create CONTEXT CONSTRUCTED tag
600 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
604 """Take off tag from the data
606 :returns: (encoded tag, tag length, remaining data)
609 raise NotEnoughData("no data at all")
610 if byte2int(data) & 0x1F < 31:
611 return data[:1], 1, data[1:]
616 raise DecodeError("unfinished tag")
617 if indexbytes(data, i) & 0x80 == 0:
620 return data[:i], i, data[i:]
626 octets = bytearray(int_bytes_len(l) + 1)
627 octets[0] = 0x80 | (len(octets) - 1)
628 for i in six_xrange(len(octets) - 1, 0, -1):
634 def len_decode(data):
636 raise NotEnoughData("no data at all")
637 first_octet = byte2int(data)
638 if first_octet & 0x80 == 0:
639 return first_octet, 1, data[1:]
640 octets_num = first_octet & 0x7F
641 if octets_num + 1 > len(data):
642 raise NotEnoughData("encoded length is longer than data")
644 raise DecodeError("long form instead of short one")
645 if byte2int(data[1:]) == 0:
646 raise DecodeError("leading zeros")
648 for v in iterbytes(data[1:1 + octets_num]):
651 raise DecodeError("long form instead of short one")
652 return l, 1 + octets_num, data[1 + octets_num:]
655 ########################################################################
657 ########################################################################
659 class AutoAddSlots(type):
660 def __new__(cls, name, bases, _dict):
661 _dict["__slots__"] = _dict.get("__slots__", ())
662 return type.__new__(cls, name, bases, _dict)
665 @add_metaclass(AutoAddSlots)
667 """Common ASN.1 object class
669 All ASN.1 types are inherited from it. It has metaclass that
670 automatically adds ``__slots__`` to all inherited classes.
691 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
692 self._expl = getattr(self, "expl", None) if expl is None else expl
693 if self.tag != self.tag_default and self._expl is not None:
695 "implicit and explicit tags can not be set simultaneously"
697 if default is not None:
699 self.optional = optional
700 self.offset, self.llen, self.vlen = _decoded
704 def ready(self): # pragma: no cover
705 """Is object ready to be encoded?
707 raise NotImplementedError()
709 def _assert_ready(self):
711 raise ObjNotReady(self.__class__.__name__)
715 """Is object decoded?
717 return (self.llen + self.vlen) > 0
719 def copy(self): # pragma: no cover
720 """Make a copy of object, safe to be mutated
722 raise NotImplementedError()
730 return self.tlen + self.llen + self.vlen
732 def __str__(self): # pragma: no cover
733 return self.__bytes__() if PY2 else self.__unicode__()
735 def __ne__(self, their):
736 return not(self == their)
738 def __gt__(self, their): # pragma: no cover
739 return not(self < their)
741 def __le__(self, their): # pragma: no cover
742 return (self == their) or (self < their)
744 def __ge__(self, their): # pragma: no cover
745 return (self == their) or (self > their)
747 def _encode(self): # pragma: no cover
748 raise NotImplementedError()
750 def _decode(self, tlv, offset=0, decode_path=()): # pragma: no cover
751 raise NotImplementedError()
755 if self._expl is None:
757 return b"".join((self._expl, len_encode(len(raw)), raw))
759 def decode(self, data, offset=0, leavemm=False, decode_path=()):
762 :param data: either binary or memoryview
763 :param int offset: initial data's offset
764 :param bool leavemm: do we need to leave memoryview of remaining
765 data as is, or convert it to bytes otherwise
766 :returns: (Obj, remaining data)
768 tlv = memoryview(data)
769 if self._expl is None:
770 obj, tail = self._decode(
773 decode_path=decode_path,
777 t, tlen, lv = tag_strip(tlv)
778 except DecodeError as err:
781 klass=self.__class__,
782 decode_path=decode_path,
787 klass=self.__class__,
788 decode_path=decode_path,
792 l, llen, v = len_decode(lv)
793 except DecodeError as err:
796 klass=self.__class__,
797 decode_path=decode_path,
802 "encoded length is longer than data",
803 klass=self.__class__,
804 decode_path=decode_path,
807 obj, tail = self._decode(
809 offset=offset + tlen + llen,
812 return obj, (tail if leavemm else tail.tobytes())
816 return self._expl is not None
824 return len(self._expl)
828 return len(len_encode(self.tlvlen))
831 def expl_offset(self):
832 return self.offset - self.expl_tlen - self.expl_llen
839 def expl_tlvlen(self):
840 return self.expl_tlen + self.expl_llen + self.expl_vlen
843 ########################################################################
845 ########################################################################
847 PP = namedtuple("PP", (
869 asn1_type_name="unknown",
908 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
911 cols.append("%5d%s [%d,%d,%4d]" % (
914 " " if pp.expl_offset is None else
915 ("-%d" % (pp.offset - pp.expl_offset))
921 if len(pp.decode_path) > 0:
922 cols.append(" ." * (len(pp.decode_path)))
923 cols.append("%s:" % pp.decode_path[-1])
924 if pp.expl is not None:
925 klass, _, num = pp.expl
926 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
927 if pp.impl is not None:
928 klass, _, num = pp.impl
929 cols.append("[%s%d]" % (TagClassReprs[klass], num))
930 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
931 cols.append(pp.obj_name)
932 cols.append(pp.asn1_type_name)
933 if pp.value is not None:
937 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
940 value = "%s (%s)" % (oids[value], pp.value)
943 if isinstance(pp.blob, binary_type):
944 cols.append(hexenc(pp.blob))
945 elif isinstance(pp.blob, tuple):
946 cols.append(", ".join(pp.blob))
948 cols.append("OPTIONAL")
950 cols.append("DEFAULT")
951 return " ".join(cols)
954 def pp_console_blob(pp):
955 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
956 if len(pp.decode_path) > 0:
957 cols.append(" ." * (len(pp.decode_path) + 1))
958 if isinstance(pp.blob, binary_type):
959 blob = hexenc(pp.blob).upper()
960 for i in range(0, len(blob), 32):
961 chunk = blob[i:i + 32]
962 yield " ".join(cols + [":".join(
963 chunk[j:j + 2] for j in range(0, len(chunk), 2)
965 elif isinstance(pp.blob, tuple):
966 yield " ".join(cols + [", ".join(pp.blob)])
969 def pprint(obj, oids=None, big_blobs=False):
970 """Pretty print object
972 :param Obj obj: object you want to pretty print
973 :param oids: ``OID <-> humand readable string`` dictionary. When OID
974 from it is met, then its humand readable form is printed
975 :param big_blobs: if large binary objects are met (like OctetString
976 values), do we need to print them too, on separate
979 def _pprint_pps(pps):
981 if hasattr(pp, "_fields"):
983 yield pp_console_row(
989 for row in pp_console_blob(pp):
992 yield pp_console_row(pp, oids=oids, with_offsets=True)
994 for row in _pprint_pps(pp):
996 return "\n".join(_pprint_pps(obj.pps()))
999 ########################################################################
1000 # ASN.1 primitive types
1001 ########################################################################
1004 """``BOOLEAN`` boolean type
1006 >>> b = Boolean(True)
1008 >>> b == Boolean(True)
1014 tag_default = tag_encode(1)
1015 asn1_type_name = "BOOLEAN"
1027 :param value: set the value. Either boolean type, or
1028 :py:class:`pyderasn.Boolean` object
1029 :param bytes impl: override default tag with ``IMPLICIT`` one
1030 :param bytes expl: override default tag with ``EXPLICIT`` one
1031 :param default: set default value. Type same as in ``value``
1032 :param bool optional: is object ``OPTIONAL`` in sequence
1034 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1035 self._value = None if value is None else self._value_sanitize(value)
1036 if default is not None:
1037 default = self._value_sanitize(default)
1038 self.default = self.__class__(
1044 self._value = default
1046 def _value_sanitize(self, value):
1047 if issubclass(value.__class__, Boolean):
1049 if isinstance(value, bool):
1051 raise InvalidValueType((self.__class__, bool))
1055 return self._value is not None
1058 obj = self.__class__()
1059 obj._value = self._value
1061 obj._expl = self._expl
1062 obj.default = self.default
1063 obj.optional = self.optional
1064 obj.offset = self.offset
1065 obj.llen = self.llen
1066 obj.vlen = self.vlen
1069 def __nonzero__(self):
1070 self._assert_ready()
1074 self._assert_ready()
1077 def __eq__(self, their):
1078 if isinstance(their, bool):
1079 return self._value == their
1080 if not issubclass(their.__class__, Boolean):
1083 self._value == their._value and
1084 self.tag == their.tag and
1085 self._expl == their._expl
1096 return self.__class__(
1098 impl=self.tag if impl is None else impl,
1099 expl=self._expl if expl is None else expl,
1100 default=self.default if default is None else default,
1101 optional=self.optional if optional is None else optional,
1105 self._assert_ready()
1109 (b"\xFF" if self._value else b"\x00"),
1112 def _decode(self, tlv, offset=0, decode_path=()):
1114 t, _, lv = tag_strip(tlv)
1115 except DecodeError as err:
1116 raise err.__class__(
1118 klass=self.__class__,
1119 decode_path=decode_path,
1124 klass=self.__class__,
1125 decode_path=decode_path,
1129 l, _, v = len_decode(lv)
1130 except DecodeError as err:
1131 raise err.__class__(
1133 klass=self.__class__,
1134 decode_path=decode_path,
1138 raise InvalidLength(
1139 "Boolean's length must be equal to 1",
1140 klass=self.__class__,
1141 decode_path=decode_path,
1145 raise NotEnoughData(
1146 "encoded length is longer than data",
1147 klass=self.__class__,
1148 decode_path=decode_path,
1151 first_octet = byte2int(v)
1152 if first_octet == 0:
1154 elif first_octet == 0xFF:
1158 "unacceptable Boolean value",
1159 klass=self.__class__,
1160 decode_path=decode_path,
1163 obj = self.__class__(
1167 default=self.default,
1168 optional=self.optional,
1169 _decoded=(offset, 1, 1),
1174 return pp_console_row(next(self.pps()))
1176 def pps(self, decode_path=()):
1178 asn1_type_name=self.asn1_type_name,
1179 obj_name=self.__class__.__name__,
1180 decode_path=decode_path,
1181 value=str(self._value) if self.ready else None,
1182 optional=self.optional,
1183 default=self == self.default,
1184 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1185 expl=None if self._expl is None else tag_decode(self._expl),
1190 expl_offset=self.expl_offset if self.expled else None,
1191 expl_tlen=self.expl_tlen if self.expled else None,
1192 expl_llen=self.expl_llen if self.expled else None,
1193 expl_vlen=self.expl_vlen if self.expled else None,
1198 """``INTEGER`` integer type
1200 >>> b = Integer(-123)
1202 >>> b == Integer(-123)
1207 >>> Integer(2, bounds=(1, 3))
1209 >>> Integer(5, bounds=(1, 3))
1210 Traceback (most recent call last):
1211 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1215 class Version(Integer):
1222 >>> v = Version("v1")
1229 {'v3': 2, 'v1': 0, 'v2': 1}
1231 __slots__ = ("specs", "_bound_min", "_bound_max")
1232 tag_default = tag_encode(2)
1233 asn1_type_name = "INTEGER"
1247 :param value: set the value. Either integer type, named value
1248 (if ``schema`` is specified in the class), or
1249 :py:class:`pyderasn.Integer` object
1250 :param bounds: set ``(MIN, MAX)`` value constraint.
1251 (-inf, +inf) by default
1252 :param bytes impl: override default tag with ``IMPLICIT`` one
1253 :param bytes expl: override default tag with ``EXPLICIT`` one
1254 :param default: set default value. Type same as in ``value``
1255 :param bool optional: is object ``OPTIONAL`` in sequence
1257 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1259 specs = getattr(self, "schema", {}) if _specs is None else _specs
1260 self.specs = specs if isinstance(specs, dict) else dict(specs)
1261 self._bound_min, self._bound_max = getattr(
1264 (float("-inf"), float("+inf")),
1265 ) if bounds is None else bounds
1266 if value is not None:
1267 self._value = self._value_sanitize(value)
1268 if default is not None:
1269 default = self._value_sanitize(default)
1270 self.default = self.__class__(
1276 if self._value is None:
1277 self._value = default
1279 def _value_sanitize(self, value):
1280 if issubclass(value.__class__, Integer):
1281 value = value._value
1282 elif isinstance(value, integer_types):
1284 elif isinstance(value, str):
1285 value = self.specs.get(value)
1287 raise ObjUnknown("integer value: %s" % value)
1289 raise InvalidValueType((self.__class__, int, str))
1290 if not self._bound_min <= value <= self._bound_max:
1291 raise BoundsError(self._bound_min, value, self._bound_max)
1296 return self._value is not None
1299 obj = self.__class__(_specs=self.specs)
1300 obj._value = self._value
1301 obj._bound_min = self._bound_min
1302 obj._bound_max = self._bound_max
1304 obj._expl = self._expl
1305 obj.default = self.default
1306 obj.optional = self.optional
1307 obj.offset = self.offset
1308 obj.llen = self.llen
1309 obj.vlen = self.vlen
1313 self._assert_ready()
1314 return int(self._value)
1317 self._assert_ready()
1320 bytes(self._expl or b"") +
1321 str(self._value).encode("ascii"),
1324 def __eq__(self, their):
1325 if isinstance(their, integer_types):
1326 return self._value == their
1327 if not issubclass(their.__class__, Integer):
1330 self._value == their._value and
1331 self.tag == their.tag and
1332 self._expl == their._expl
1335 def __lt__(self, their):
1336 return self._value < their._value
1340 for name, value in self.specs.items():
1341 if value == self._value:
1353 return self.__class__(
1356 (self._bound_min, self._bound_max)
1357 if bounds is None else bounds
1359 impl=self.tag if impl is None else impl,
1360 expl=self._expl if expl is None else expl,
1361 default=self.default if default is None else default,
1362 optional=self.optional if optional is None else optional,
1367 self._assert_ready()
1371 octets = bytearray([0])
1375 octets = bytearray()
1377 octets.append((value & 0xFF) ^ 0xFF)
1379 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1382 octets = bytearray()
1384 octets.append(value & 0xFF)
1386 if octets[-1] & 0x80 > 0:
1389 octets = bytes(octets)
1391 bytes_len = ceil(value.bit_length() / 8) or 1
1394 octets = value.to_bytes(
1399 except OverflowError:
1403 return b"".join((self.tag, len_encode(len(octets)), octets))
1405 def _decode(self, tlv, offset=0, decode_path=()):
1407 t, _, lv = tag_strip(tlv)
1408 except DecodeError as err:
1409 raise err.__class__(
1411 klass=self.__class__,
1412 decode_path=decode_path,
1417 klass=self.__class__,
1418 decode_path=decode_path,
1422 l, llen, v = len_decode(lv)
1423 except DecodeError as err:
1424 raise err.__class__(
1426 klass=self.__class__,
1427 decode_path=decode_path,
1431 raise NotEnoughData(
1432 "encoded length is longer than data",
1433 klass=self.__class__,
1434 decode_path=decode_path,
1438 raise NotEnoughData(
1440 klass=self.__class__,
1441 decode_path=decode_path,
1444 v, tail = v[:l], v[l:]
1445 first_octet = byte2int(v)
1447 second_octet = byte2int(v[1:])
1449 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1450 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1453 "non normalized integer",
1454 klass=self.__class__,
1455 decode_path=decode_path,
1460 if first_octet & 0x80 > 0:
1461 octets = bytearray()
1462 for octet in bytearray(v):
1463 octets.append(octet ^ 0xFF)
1464 for octet in octets:
1465 value = (value << 8) | octet
1469 for octet in bytearray(v):
1470 value = (value << 8) | octet
1472 value = int.from_bytes(v, byteorder="big", signed=True)
1474 obj = self.__class__(
1476 bounds=(self._bound_min, self._bound_max),
1479 default=self.default,
1480 optional=self.optional,
1482 _decoded=(offset, llen, l),
1484 except BoundsError as err:
1487 klass=self.__class__,
1488 decode_path=decode_path,
1494 return pp_console_row(next(self.pps()))
1496 def pps(self, decode_path=()):
1498 asn1_type_name=self.asn1_type_name,
1499 obj_name=self.__class__.__name__,
1500 decode_path=decode_path,
1501 value=(self.named or str(self._value)) if self.ready else None,
1502 optional=self.optional,
1503 default=self == self.default,
1504 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1505 expl=None if self._expl is None else tag_decode(self._expl),
1510 expl_offset=self.expl_offset if self.expled else None,
1511 expl_tlen=self.expl_tlen if self.expled else None,
1512 expl_llen=self.expl_llen if self.expled else None,
1513 expl_vlen=self.expl_vlen if self.expled else None,
1517 class BitString(Obj):
1518 """``BIT STRING`` bit string type
1520 >>> BitString(b"hello world")
1521 BIT STRING 88 bits 68656c6c6f20776f726c64
1524 >>> b == b"hello world"
1529 >>> b = BitString("'010110000000'B")
1530 BIT STRING 12 bits 5800
1533 >>> b[0], b[1], b[2], b[3]
1534 (False, True, False, True)
1538 [False, True, False, True, True, False, False, False, False, False, False, False]
1542 class KeyUsage(BitString):
1544 ('digitalSignature', 0),
1545 ('nonRepudiation', 1),
1546 ('keyEncipherment', 2),
1549 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1550 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1552 ['nonRepudiation', 'keyEncipherment']
1554 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1556 __slots__ = ("specs",)
1557 tag_default = tag_encode(3)
1558 asn1_type_name = "BIT STRING"
1571 :param value: set the value. Either binary type, tuple of named
1572 values (if ``schema`` is specified in the class),
1573 string in ``'XXX...'B`` form, or
1574 :py:class:`pyderasn.BitString` object
1575 :param bytes impl: override default tag with ``IMPLICIT`` one
1576 :param bytes expl: override default tag with ``EXPLICIT`` one
1577 :param default: set default value. Type same as in ``value``
1578 :param bool optional: is object ``OPTIONAL`` in sequence
1580 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1581 specs = getattr(self, "schema", {}) if _specs is None else _specs
1582 self.specs = specs if isinstance(specs, dict) else dict(specs)
1583 self._value = None if value is None else self._value_sanitize(value)
1584 if default is not None:
1585 default = self._value_sanitize(default)
1586 self.default = self.__class__(
1592 self._value = default
1594 def _bits2octets(self, bits):
1595 if len(self.specs) > 0:
1596 bits = bits.rstrip("0")
1598 bits += "0" * ((8 - (bit_len % 8)) % 8)
1599 octets = bytearray(len(bits) // 8)
1600 for i in six_xrange(len(octets)):
1601 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1602 return bit_len, bytes(octets)
1604 def _value_sanitize(self, value):
1605 if issubclass(value.__class__, BitString):
1607 if isinstance(value, (string_types, binary_type)):
1609 isinstance(value, string_types) and
1610 value.startswith("'") and
1611 value.endswith("'B")
1614 if not set(value) <= set(("0", "1")):
1615 raise ValueError("B's coding contains unacceptable chars")
1616 return self._bits2octets(value)
1617 elif isinstance(value, binary_type):
1618 return (len(value) * 8, value)
1620 raise InvalidValueType((
1625 if isinstance(value, tuple):
1628 isinstance(value[0], integer_types) and
1629 isinstance(value[1], binary_type)
1634 bit = self.specs.get(name)
1636 raise ObjUnknown("BitString value: %s" % name)
1639 return self._bits2octets("")
1641 return self._bits2octets("".join(
1642 ("1" if bit in bits else "0")
1643 for bit in six_xrange(max(bits) + 1)
1645 raise InvalidValueType((self.__class__, binary_type, string_types))
1649 return self._value is not None
1652 obj = self.__class__(_specs=self.specs)
1654 if value is not None:
1655 value = (value[0], value[1])
1658 obj._expl = self._expl
1659 obj.default = self.default
1660 obj.optional = self.optional
1661 obj.offset = self.offset
1662 obj.llen = self.llen
1663 obj.vlen = self.vlen
1667 self._assert_ready()
1668 for i in six_xrange(self._value[0]):
1673 self._assert_ready()
1674 return self._value[0]
1676 def __bytes__(self):
1677 self._assert_ready()
1678 return self._value[1]
1680 def __eq__(self, their):
1681 if isinstance(their, bytes):
1682 return self._value[1] == their
1683 if not issubclass(their.__class__, BitString):
1686 self._value == their._value and
1687 self.tag == their.tag and
1688 self._expl == their._expl
1693 return [name for name, bit in self.specs.items() if self[bit]]
1703 return self.__class__(
1705 impl=self.tag if impl is None else impl,
1706 expl=self._expl if expl is None else expl,
1707 default=self.default if default is None else default,
1708 optional=self.optional if optional is None else optional,
1712 def __getitem__(self, key):
1713 if isinstance(key, int):
1714 bit_len, octets = self._value
1718 byte2int(memoryview(octets)[key // 8:]) >>
1721 if isinstance(key, string_types):
1722 value = self.specs.get(key)
1724 raise ObjUnknown("BitString value: %s" % key)
1726 raise InvalidValueType((int, str))
1729 self._assert_ready()
1730 bit_len, octets = self._value
1733 len_encode(len(octets) + 1),
1734 int2byte((8 - bit_len % 8) % 8),
1738 def _decode(self, tlv, offset=0, decode_path=()):
1740 t, _, lv = tag_strip(tlv)
1741 except DecodeError as err:
1742 raise err.__class__(
1744 klass=self.__class__,
1745 decode_path=decode_path,
1750 klass=self.__class__,
1751 decode_path=decode_path,
1755 l, llen, v = len_decode(lv)
1756 except DecodeError as err:
1757 raise err.__class__(
1759 klass=self.__class__,
1760 decode_path=decode_path,
1764 raise NotEnoughData(
1765 "encoded length is longer than data",
1766 klass=self.__class__,
1767 decode_path=decode_path,
1771 raise NotEnoughData(
1773 klass=self.__class__,
1774 decode_path=decode_path,
1777 pad_size = byte2int(v)
1778 if l == 1 and pad_size != 0:
1780 "invalid empty value",
1781 klass=self.__class__,
1782 decode_path=decode_path,
1788 klass=self.__class__,
1789 decode_path=decode_path,
1792 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1795 klass=self.__class__,
1796 decode_path=decode_path,
1799 v, tail = v[:l], v[l:]
1800 obj = self.__class__(
1801 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1804 default=self.default,
1805 optional=self.optional,
1807 _decoded=(offset, llen, l),
1812 return pp_console_row(next(self.pps()))
1814 def pps(self, decode_path=()):
1818 bit_len, blob = self._value
1819 value = "%d bits" % bit_len
1820 if len(self.specs) > 0:
1821 blob = tuple(self.named)
1823 asn1_type_name=self.asn1_type_name,
1824 obj_name=self.__class__.__name__,
1825 decode_path=decode_path,
1828 optional=self.optional,
1829 default=self == self.default,
1830 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1831 expl=None if self._expl is None else tag_decode(self._expl),
1836 expl_offset=self.expl_offset if self.expled else None,
1837 expl_tlen=self.expl_tlen if self.expled else None,
1838 expl_llen=self.expl_llen if self.expled else None,
1839 expl_vlen=self.expl_vlen if self.expled else None,
1843 class OctetString(Obj):
1844 """``OCTET STRING`` binary string type
1846 >>> s = OctetString(b"hello world")
1847 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1848 >>> s == OctetString(b"hello world")
1853 >>> OctetString(b"hello", bounds=(4, 4))
1854 Traceback (most recent call last):
1855 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1856 >>> OctetString(b"hell", bounds=(4, 4))
1857 OCTET STRING 4 bytes 68656c6c
1859 __slots__ = ("_bound_min", "_bound_max")
1860 tag_default = tag_encode(4)
1861 asn1_type_name = "OCTET STRING"
1874 :param value: set the value. Either binary type, or
1875 :py:class:`pyderasn.OctetString` object
1876 :param bounds: set ``(MIN, MAX)`` value size constraint.
1877 (-inf, +inf) by default
1878 :param bytes impl: override default tag with ``IMPLICIT`` one
1879 :param bytes expl: override default tag with ``EXPLICIT`` one
1880 :param default: set default value. Type same as in ``value``
1881 :param bool optional: is object ``OPTIONAL`` in sequence
1883 super(OctetString, self).__init__(
1891 self._bound_min, self._bound_max = getattr(
1895 ) if bounds is None else bounds
1896 if value is not None:
1897 self._value = self._value_sanitize(value)
1898 if default is not None:
1899 default = self._value_sanitize(default)
1900 self.default = self.__class__(
1905 if self._value is None:
1906 self._value = default
1908 def _value_sanitize(self, value):
1909 if issubclass(value.__class__, OctetString):
1910 value = value._value
1911 elif isinstance(value, binary_type):
1914 raise InvalidValueType((self.__class__, bytes))
1915 if not self._bound_min <= len(value) <= self._bound_max:
1916 raise BoundsError(self._bound_min, len(value), self._bound_max)
1921 return self._value is not None
1924 obj = self.__class__()
1925 obj._value = self._value
1926 obj._bound_min = self._bound_min
1927 obj._bound_max = self._bound_max
1929 obj._expl = self._expl
1930 obj.default = self.default
1931 obj.optional = self.optional
1932 obj.offset = self.offset
1933 obj.llen = self.llen
1934 obj.vlen = self.vlen
1937 def __bytes__(self):
1938 self._assert_ready()
1941 def __eq__(self, their):
1942 if isinstance(their, binary_type):
1943 return self._value == their
1944 if not issubclass(their.__class__, OctetString):
1947 self._value == their._value and
1948 self.tag == their.tag and
1949 self._expl == their._expl
1952 def __lt__(self, their):
1953 return self._value < their._value
1964 return self.__class__(
1967 (self._bound_min, self._bound_max)
1968 if bounds is None else bounds
1970 impl=self.tag if impl is None else impl,
1971 expl=self._expl if expl is None else expl,
1972 default=self.default if default is None else default,
1973 optional=self.optional if optional is None else optional,
1977 self._assert_ready()
1980 len_encode(len(self._value)),
1984 def _decode(self, tlv, offset=0, decode_path=()):
1986 t, _, lv = tag_strip(tlv)
1987 except DecodeError as err:
1988 raise err.__class__(
1990 klass=self.__class__,
1991 decode_path=decode_path,
1996 klass=self.__class__,
1997 decode_path=decode_path,
2001 l, llen, v = len_decode(lv)
2002 except DecodeError as err:
2003 raise err.__class__(
2005 klass=self.__class__,
2006 decode_path=decode_path,
2010 raise NotEnoughData(
2011 "encoded length is longer than data",
2012 klass=self.__class__,
2013 decode_path=decode_path,
2016 v, tail = v[:l], v[l:]
2018 obj = self.__class__(
2020 bounds=(self._bound_min, self._bound_max),
2023 default=self.default,
2024 optional=self.optional,
2025 _decoded=(offset, llen, l),
2027 except BoundsError as err:
2030 klass=self.__class__,
2031 decode_path=decode_path,
2037 return pp_console_row(next(self.pps()))
2039 def pps(self, decode_path=()):
2041 asn1_type_name=self.asn1_type_name,
2042 obj_name=self.__class__.__name__,
2043 decode_path=decode_path,
2044 value=("%d bytes" % len(self._value)) if self.ready else None,
2045 blob=self._value if self.ready else None,
2046 optional=self.optional,
2047 default=self == self.default,
2048 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2049 expl=None if self._expl is None else tag_decode(self._expl),
2054 expl_offset=self.expl_offset if self.expled else None,
2055 expl_tlen=self.expl_tlen if self.expled else None,
2056 expl_llen=self.expl_llen if self.expled else None,
2057 expl_vlen=self.expl_vlen if self.expled else None,
2062 """``NULL`` null object
2070 tag_default = tag_encode(5)
2071 asn1_type_name = "NULL"
2075 value=None, # unused, but Sequence passes it
2082 :param bytes impl: override default tag with ``IMPLICIT`` one
2083 :param bytes expl: override default tag with ``EXPLICIT`` one
2084 :param bool optional: is object ``OPTIONAL`` in sequence
2086 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2094 obj = self.__class__()
2096 obj._expl = self._expl
2097 obj.default = self.default
2098 obj.optional = self.optional
2099 obj.offset = self.offset
2100 obj.llen = self.llen
2101 obj.vlen = self.vlen
2104 def __eq__(self, their):
2105 if not issubclass(their.__class__, Null):
2108 self.tag == their.tag and
2109 self._expl == their._expl
2119 return self.__class__(
2120 impl=self.tag if impl is None else impl,
2121 expl=self._expl if expl is None else expl,
2122 optional=self.optional if optional is None else optional,
2126 return self.tag + len_encode(0)
2128 def _decode(self, tlv, offset=0, decode_path=()):
2130 t, _, lv = tag_strip(tlv)
2131 except DecodeError as err:
2132 raise err.__class__(
2134 klass=self.__class__,
2135 decode_path=decode_path,
2140 klass=self.__class__,
2141 decode_path=decode_path,
2145 l, _, v = len_decode(lv)
2146 except DecodeError as err:
2147 raise err.__class__(
2149 klass=self.__class__,
2150 decode_path=decode_path,
2154 raise InvalidLength(
2155 "Null must have zero length",
2156 klass=self.__class__,
2157 decode_path=decode_path,
2160 obj = self.__class__(
2163 optional=self.optional,
2164 _decoded=(offset, 1, 0),
2169 return pp_console_row(next(self.pps()))
2171 def pps(self, decode_path=()):
2173 asn1_type_name=self.asn1_type_name,
2174 obj_name=self.__class__.__name__,
2175 decode_path=decode_path,
2176 optional=self.optional,
2177 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2178 expl=None if self._expl is None else tag_decode(self._expl),
2183 expl_offset=self.expl_offset if self.expled else None,
2184 expl_tlen=self.expl_tlen if self.expled else None,
2185 expl_llen=self.expl_llen if self.expled else None,
2186 expl_vlen=self.expl_vlen if self.expled else None,
2190 class ObjectIdentifier(Obj):
2191 """``OBJECT IDENTIFIER`` OID type
2193 >>> oid = ObjectIdentifier((1, 2, 3))
2194 OBJECT IDENTIFIER 1.2.3
2195 >>> oid == ObjectIdentifier("1.2.3")
2201 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2202 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2204 >>> str(ObjectIdentifier((3, 1)))
2205 Traceback (most recent call last):
2206 pyderasn.InvalidOID: unacceptable first arc value
2209 tag_default = tag_encode(6)
2210 asn1_type_name = "OBJECT IDENTIFIER"
2222 :param value: set the value. Either tuples of integers,
2223 string of "."-concatenated integers, or
2224 :py:class:`pyderasn.ObjectIdentifier` object
2225 :param bytes impl: override default tag with ``IMPLICIT`` one
2226 :param bytes expl: override default tag with ``EXPLICIT`` one
2227 :param default: set default value. Type same as in ``value``
2228 :param bool optional: is object ``OPTIONAL`` in sequence
2230 super(ObjectIdentifier, self).__init__(
2238 if value is not None:
2239 self._value = self._value_sanitize(value)
2240 if default is not None:
2241 default = self._value_sanitize(default)
2242 self.default = self.__class__(
2247 if self._value is None:
2248 self._value = default
2250 def __add__(self, their):
2251 if isinstance(their, self.__class__):
2252 return self.__class__(self._value + their._value)
2253 if isinstance(their, tuple):
2254 return self.__class__(self._value + their)
2255 raise InvalidValueType((self.__class__, tuple))
2257 def _value_sanitize(self, value):
2258 if issubclass(value.__class__, ObjectIdentifier):
2260 if isinstance(value, string_types):
2262 value = tuple(int(arc) for arc in value.split("."))
2264 raise InvalidOID("unacceptable arcs values")
2265 if isinstance(value, tuple):
2267 raise InvalidOID("less than 2 arcs")
2268 first_arc = value[0]
2269 if first_arc in (0, 1):
2270 if not (0 <= value[1] <= 39):
2271 raise InvalidOID("second arc is too wide")
2272 elif first_arc == 2:
2275 raise InvalidOID("unacceptable first arc value")
2277 raise InvalidValueType((self.__class__, str, tuple))
2281 return self._value is not None
2284 obj = self.__class__()
2285 obj._value = self._value
2287 obj._expl = self._expl
2288 obj.default = self.default
2289 obj.optional = self.optional
2290 obj.offset = self.offset
2291 obj.llen = self.llen
2292 obj.vlen = self.vlen
2296 self._assert_ready()
2297 return iter(self._value)
2300 return ".".join(str(arc) for arc in self._value or ())
2303 self._assert_ready()
2306 bytes(self._expl or b"") +
2307 str(self._value).encode("ascii"),
2310 def __eq__(self, their):
2311 if isinstance(their, tuple):
2312 return self._value == their
2313 if not issubclass(their.__class__, ObjectIdentifier):
2316 self.tag == their.tag and
2317 self._expl == their._expl and
2318 self._value == their._value
2321 def __lt__(self, their):
2322 return self._value < their._value
2332 return self.__class__(
2334 impl=self.tag if impl is None else impl,
2335 expl=self._expl if expl is None else expl,
2336 default=self.default if default is None else default,
2337 optional=self.optional if optional is None else optional,
2341 self._assert_ready()
2343 first_value = value[1]
2344 first_arc = value[0]
2347 elif first_arc == 1:
2349 elif first_arc == 2:
2351 else: # pragma: no cover
2352 raise RuntimeError("invalid arc is stored")
2353 octets = [zero_ended_encode(first_value)]
2354 for arc in value[2:]:
2355 octets.append(zero_ended_encode(arc))
2356 v = b"".join(octets)
2357 return b"".join((self.tag, len_encode(len(v)), v))
2359 def _decode(self, tlv, offset=0, decode_path=()):
2361 t, _, lv = tag_strip(tlv)
2362 except DecodeError as err:
2363 raise err.__class__(
2365 klass=self.__class__,
2366 decode_path=decode_path,
2371 klass=self.__class__,
2372 decode_path=decode_path,
2376 l, llen, v = len_decode(lv)
2377 except DecodeError as err:
2378 raise err.__class__(
2380 klass=self.__class__,
2381 decode_path=decode_path,
2385 raise NotEnoughData(
2386 "encoded length is longer than data",
2387 klass=self.__class__,
2388 decode_path=decode_path,
2392 raise NotEnoughData(
2394 klass=self.__class__,
2395 decode_path=decode_path,
2398 v, tail = v[:l], v[l:]
2404 octet = indexbytes(v, i)
2405 arc = (arc << 7) | (octet & 0x7F)
2406 if octet & 0x80 == 0:
2414 klass=self.__class__,
2415 decode_path=decode_path,
2419 second_arc = arcs[0]
2420 if 0 <= second_arc <= 39:
2422 elif 40 <= second_arc <= 79:
2428 obj = self.__class__(
2429 value=tuple([first_arc, second_arc] + arcs[1:]),
2432 default=self.default,
2433 optional=self.optional,
2434 _decoded=(offset, llen, l),
2439 return pp_console_row(next(self.pps()))
2441 def pps(self, decode_path=()):
2443 asn1_type_name=self.asn1_type_name,
2444 obj_name=self.__class__.__name__,
2445 decode_path=decode_path,
2446 value=str(self) if self.ready else None,
2447 optional=self.optional,
2448 default=self == self.default,
2449 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2450 expl=None if self._expl is None else tag_decode(self._expl),
2455 expl_offset=self.expl_offset if self.expled else None,
2456 expl_tlen=self.expl_tlen if self.expled else None,
2457 expl_llen=self.expl_llen if self.expled else None,
2458 expl_vlen=self.expl_vlen if self.expled else None,
2462 class Enumerated(Integer):
2463 """``ENUMERATED`` integer type
2465 This type is identical to :py:class:`pyderasn.Integer`, but requires
2466 schema to be specified and does not accept values missing from it.
2469 tag_default = tag_encode(10)
2470 asn1_type_name = "ENUMERATED"
2481 bounds=None, # dummy argument, workability for Integer.decode
2483 super(Enumerated, self).__init__(
2492 if len(self.specs) == 0:
2493 raise ValueError("schema must be specified")
2495 def _value_sanitize(self, value):
2496 if isinstance(value, self.__class__):
2497 value = value._value
2498 elif isinstance(value, integer_types):
2499 if value not in list(self.specs.values()):
2501 "unknown integer value: %s" % value,
2502 klass=self.__class__,
2504 elif isinstance(value, string_types):
2505 value = self.specs.get(value)
2507 raise ObjUnknown("integer value: %s" % value)
2509 raise InvalidValueType((self.__class__, int, str))
2513 obj = self.__class__(_specs=self.specs)
2514 obj._value = self._value
2515 obj._bound_min = self._bound_min
2516 obj._bound_max = self._bound_max
2518 obj._expl = self._expl
2519 obj.default = self.default
2520 obj.optional = self.optional
2521 obj.offset = self.offset
2522 obj.llen = self.llen
2523 obj.vlen = self.vlen
2535 return self.__class__(
2537 impl=self.tag if impl is None else impl,
2538 expl=self._expl if expl is None else expl,
2539 default=self.default if default is None else default,
2540 optional=self.optional if optional is None else optional,
2545 class CommonString(OctetString):
2546 """Common class for all strings
2548 Everything resembles :py:class:`pyderasn.OctetString`, except
2549 ability to deal with unicode text strings.
2551 >>> hexenc("привет мир".encode("utf-8"))
2552 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2553 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2555 >>> s = UTF8String("привет мир")
2556 UTF8String UTF8String привет мир
2558 'привет мир'
2559 >>> hexenc(bytes(s))
2560 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2562 >>> PrintableString("привет мир")
2563 Traceback (most recent call last):
2564 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2566 >>> BMPString("ада", bounds=(2, 2))
2567 Traceback (most recent call last):
2568 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2569 >>> s = BMPString("ад", bounds=(2, 2))
2572 >>> hexenc(bytes(s))
2580 * - :py:class:`pyderasn.UTF8String`
2582 * - :py:class:`pyderasn.NumericString`
2584 * - :py:class:`pyderasn.PrintableString`
2586 * - :py:class:`pyderasn.TeletexString`
2588 * - :py:class:`pyderasn.T61String`
2590 * - :py:class:`pyderasn.VideotexString`
2592 * - :py:class:`pyderasn.IA5String`
2594 * - :py:class:`pyderasn.GraphicString`
2596 * - :py:class:`pyderasn.VisibleString`
2598 * - :py:class:`pyderasn.ISO646String`
2600 * - :py:class:`pyderasn.GeneralString`
2602 * - :py:class:`pyderasn.UniversalString`
2604 * - :py:class:`pyderasn.BMPString`
2607 __slots__ = ("encoding",)
2609 def _value_sanitize(self, value):
2611 value_decoded = None
2612 if isinstance(value, self.__class__):
2613 value_raw = value._value
2614 elif isinstance(value, text_type):
2615 value_decoded = value
2616 elif isinstance(value, binary_type):
2619 raise InvalidValueType((self.__class__, text_type, binary_type))
2621 value_decoded.encode(self.encoding)
2622 if value_raw is None else value_raw
2625 value_raw.decode(self.encoding)
2626 if value_decoded is None else value_decoded
2628 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2636 def __eq__(self, their):
2637 if isinstance(their, binary_type):
2638 return self._value == their
2639 if isinstance(their, text_type):
2640 return self._value == their.encode(self.encoding)
2641 if not isinstance(their, self.__class__):
2644 self._value == their._value and
2645 self.tag == their.tag and
2646 self._expl == their._expl
2649 def __unicode__(self):
2651 return self._value.decode(self.encoding)
2652 return text_type(self._value)
2655 return pp_console_row(next(self.pps(no_unicode=PY2)))
2657 def pps(self, decode_path=(), no_unicode=False):
2660 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2662 asn1_type_name=self.asn1_type_name,
2663 obj_name=self.__class__.__name__,
2664 decode_path=decode_path,
2666 optional=self.optional,
2667 default=self == self.default,
2668 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2669 expl=None if self._expl is None else tag_decode(self._expl),
2677 class UTF8String(CommonString):
2679 tag_default = tag_encode(12)
2681 asn1_type_name = "UTF8String"
2684 class NumericString(CommonString):
2686 tag_default = tag_encode(18)
2688 asn1_type_name = "NumericString"
2691 class PrintableString(CommonString):
2693 tag_default = tag_encode(19)
2695 asn1_type_name = "PrintableString"
2698 class TeletexString(CommonString):
2700 tag_default = tag_encode(20)
2702 asn1_type_name = "TeletexString"
2705 class T61String(TeletexString):
2707 asn1_type_name = "T61String"
2710 class VideotexString(CommonString):
2712 tag_default = tag_encode(21)
2713 encoding = "iso-8859-1"
2714 asn1_type_name = "VideotexString"
2717 class IA5String(CommonString):
2719 tag_default = tag_encode(22)
2721 asn1_type_name = "IA5"
2724 class UTCTime(CommonString):
2725 """``UTCTime`` datetime type
2727 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2728 UTCTime UTCTime 2017-09-30T22:07:50
2734 datetime.datetime(2017, 9, 30, 22, 7, 50)
2735 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2736 datetime.datetime(1957, 9, 30, 22, 7, 50)
2739 tag_default = tag_encode(23)
2741 asn1_type_name = "UTCTime"
2743 fmt = "%y%m%d%H%M%SZ"
2753 bounds=None, # dummy argument, workability for OctetString.decode
2756 :param value: set the value. Either datetime type, or
2757 :py:class:`pyderasn.UTCTime` object
2758 :param bytes impl: override default tag with ``IMPLICIT`` one
2759 :param bytes expl: override default tag with ``EXPLICIT`` one
2760 :param default: set default value. Type same as in ``value``
2761 :param bool optional: is object ``OPTIONAL`` in sequence
2763 super(UTCTime, self).__init__(
2771 if value is not None:
2772 self._value = self._value_sanitize(value)
2773 if default is not None:
2774 default = self._value_sanitize(default)
2775 self.default = self.__class__(
2780 if self._value is None:
2781 self._value = default
2783 def _value_sanitize(self, value):
2784 if isinstance(value, self.__class__):
2786 if isinstance(value, datetime):
2787 return value.strftime(self.fmt).encode("ascii")
2788 if isinstance(value, binary_type):
2789 value_decoded = value.decode("ascii")
2790 if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2792 datetime.strptime(value_decoded, self.fmt)
2794 raise DecodeError("invalid UTCTime format")
2797 raise DecodeError("invalid UTCTime length")
2798 raise InvalidValueType((self.__class__, datetime))
2800 def __eq__(self, their):
2801 if isinstance(their, binary_type):
2802 return self._value == their
2803 if isinstance(their, datetime):
2804 return self.todatetime() == their
2805 if not isinstance(their, self.__class__):
2808 self._value == their._value and
2809 self.tag == their.tag and
2810 self._expl == their._expl
2813 def todatetime(self):
2814 """Convert to datetime
2818 Pay attention that UTCTime can not hold full year, so all years
2819 having < 50 years are treated as 20xx, 19xx otherwise, according
2820 to X.509 recomendation.
2822 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2823 year = value.year % 100
2825 year=(2000 + year) if year < 50 else (1900 + year),
2829 minute=value.minute,
2830 second=value.second,
2834 return pp_console_row(next(self.pps()))
2836 def pps(self, decode_path=()):
2838 asn1_type_name=self.asn1_type_name,
2839 obj_name=self.__class__.__name__,
2840 decode_path=decode_path,
2841 value=self.todatetime().isoformat() if self.ready else None,
2842 optional=self.optional,
2843 default=self == self.default,
2844 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2845 expl=None if self._expl is None else tag_decode(self._expl),
2853 class GeneralizedTime(UTCTime):
2854 """``GeneralizedTime`` datetime type
2856 This type is similar to :py:class:`pyderasn.UTCTime`.
2858 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2859 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2861 '20170930220750.000123Z'
2862 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2863 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2866 tag_default = tag_encode(24)
2867 asn1_type_name = "GeneralizedTime"
2869 fmt = "%Y%m%d%H%M%SZ"
2870 fmt_ms = "%Y%m%d%H%M%S.%fZ"
2872 def _value_sanitize(self, value):
2873 if isinstance(value, self.__class__):
2875 if isinstance(value, datetime):
2876 return value.strftime(
2877 self.fmt_ms if value.microsecond > 0 else self.fmt
2879 if isinstance(value, binary_type):
2880 value_decoded = value.decode("ascii")
2881 if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2883 datetime.strptime(value_decoded, self.fmt)
2886 "invalid GeneralizedTime (without ms) format",
2889 elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2891 datetime.strptime(value_decoded, self.fmt_ms)
2894 "invalid GeneralizedTime (with ms) format",
2899 "invalid GeneralizedTime length",
2900 klass=self.__class__,
2902 raise InvalidValueType((self.__class__, datetime))
2904 def todatetime(self):
2905 value = self._value.decode("ascii")
2906 if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2907 return datetime.strptime(value, self.fmt)
2908 return datetime.strptime(value, self.fmt_ms)
2911 class GraphicString(CommonString):
2913 tag_default = tag_encode(25)
2914 encoding = "iso-8859-1"
2915 asn1_type_name = "GraphicString"
2918 class VisibleString(CommonString):
2920 tag_default = tag_encode(26)
2922 asn1_type_name = "VisibleString"
2925 class ISO646String(VisibleString):
2927 asn1_type_name = "ISO646String"
2930 class GeneralString(CommonString):
2932 tag_default = tag_encode(27)
2933 encoding = "iso-8859-1"
2934 asn1_type_name = "GeneralString"
2937 class UniversalString(CommonString):
2939 tag_default = tag_encode(28)
2940 encoding = "utf-32-be"
2941 asn1_type_name = "UniversalString"
2944 class BMPString(CommonString):
2946 tag_default = tag_encode(30)
2947 encoding = "utf-16-be"
2948 asn1_type_name = "BMPString"
2952 """``CHOICE`` special type
2956 class GeneralName(Choice):
2958 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2959 ('dNSName', IA5String(impl=tag_ctxp(2))),
2962 >>> gn = GeneralName()
2964 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2965 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2966 >>> gn["dNSName"] = IA5String("bar.baz")
2967 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2968 >>> gn["rfc822Name"]
2971 [2] IA5String IA5 bar.baz
2974 >>> gn.value == gn["dNSName"]
2977 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2979 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2980 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2982 __slots__ = ("specs",)
2984 asn1_type_name = "CHOICE"
2997 :param value: set the value. Either ``(choice, value)`` tuple, or
2998 :py:class:`pyderasn.Choice` object
2999 :param bytes impl: can not be set, do **not** use it
3000 :param bytes expl: override default tag with ``EXPLICIT`` one
3001 :param default: set default value. Type same as in ``value``
3002 :param bool optional: is object ``OPTIONAL`` in sequence
3004 if impl is not None:
3005 raise ValueError("no implicit tag allowed for CHOICE")
3006 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3008 schema = getattr(self, "schema", ())
3009 if len(schema) == 0:
3010 raise ValueError("schema must be specified")
3012 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3015 if value is not None:
3016 self._value = self._value_sanitize(value)
3017 if default is not None:
3018 default_value = self._value_sanitize(default)
3019 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3020 default_obj.specs = self.specs
3021 default_obj._value = default_value
3022 self.default = default_obj
3024 self._value = default_obj.copy()._value
3026 def _value_sanitize(self, value):
3027 if isinstance(value, self.__class__):
3029 if isinstance(value, tuple) and len(value) == 2:
3031 spec = self.specs.get(choice)
3033 raise ObjUnknown(choice)
3034 if not isinstance(obj, spec.__class__):
3035 raise InvalidValueType((spec,))
3036 return (choice, spec(obj))
3037 raise InvalidValueType((self.__class__, tuple))
3041 return self._value is not None and self._value[1].ready
3044 obj = self.__class__(schema=self.specs)
3045 obj._expl = self._expl
3046 obj.default = self.default
3047 obj.optional = self.optional
3048 obj.offset = self.offset
3049 obj.llen = self.llen
3050 obj.vlen = self.vlen
3052 if value is not None:
3053 obj._value = (value[0], value[1].copy())
3056 def __eq__(self, their):
3057 if isinstance(their, tuple) and len(their) == 2:
3058 return self._value == their
3059 if not isinstance(their, self.__class__):
3062 self.specs == their.specs and
3063 self._value == their._value
3073 return self.__class__(
3076 expl=self._expl if expl is None else expl,
3077 default=self.default if default is None else default,
3078 optional=self.optional if optional is None else optional,
3083 self._assert_ready()
3084 return self._value[0]
3088 self._assert_ready()
3089 return self._value[1]
3091 def __getitem__(self, key):
3092 if key not in self.specs:
3093 raise ObjUnknown(key)
3094 if self._value is None:
3096 choice, value = self._value
3101 def __setitem__(self, key, value):
3102 spec = self.specs.get(key)
3104 raise ObjUnknown(key)
3105 if not isinstance(value, spec.__class__):
3106 raise InvalidValueType((spec.__class__,))
3107 self._value = (key, spec(value))
3115 return self._value[1].decoded if self.ready else False
3118 self._assert_ready()
3119 return self._value[1].encode()
3121 def _decode(self, tlv, offset=0, decode_path=()):
3122 for choice, spec in self.specs.items():
3124 value, tail = spec.decode(
3128 decode_path=decode_path + (choice,),
3132 obj = self.__class__(
3135 default=self.default,
3136 optional=self.optional,
3137 _decoded=(offset, 0, value.tlvlen),
3139 obj._value = (choice, value)
3142 klass=self.__class__,
3143 decode_path=decode_path,
3148 value = pp_console_row(next(self.pps()))
3150 value = "%s[%r]" % (value, self.value)
3153 def pps(self, decode_path=()):
3155 asn1_type_name=self.asn1_type_name,
3156 obj_name=self.__class__.__name__,
3157 decode_path=decode_path,
3158 value=self.choice if self.ready else None,
3159 optional=self.optional,
3160 default=self == self.default,
3161 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3162 expl=None if self._expl is None else tag_decode(self._expl),
3169 yield self.value.pps(decode_path=decode_path + (self.choice,))
3172 class PrimitiveTypes(Choice):
3173 """Predefined ``CHOICE`` for all generic primitive types
3175 It could be useful for general decoding of some unspecified values:
3177 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3178 OCTET STRING 3 bytes 666f6f
3179 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3183 schema = tuple((klass.__name__, klass()) for klass in (
3208 """``ANY`` special type
3210 >>> Any(Integer(-123))
3212 >>> a = Any(OctetString(b"hello world").encode())
3213 ANY 040b68656c6c6f20776f726c64
3214 >>> hexenc(bytes(a))
3215 b'0x040x0bhello world'
3218 tag_default = tag_encode(0)
3219 asn1_type_name = "ANY"
3229 :param value: set the value. Either any kind of pyderasn's
3230 **ready** object, or bytes. Pay attention that
3231 **no** validation is performed is raw binary value
3233 :param bytes expl: override default tag with ``EXPLICIT`` one
3234 :param bool optional: is object ``OPTIONAL`` in sequence
3236 super(Any, self).__init__(None, expl, None, optional, _decoded)
3237 self._value = None if value is None else self._value_sanitize(value)
3239 def _value_sanitize(self, value):
3240 if isinstance(value, self.__class__):
3242 if isinstance(value, Obj):
3243 return value.encode()
3244 if isinstance(value, binary_type):
3246 raise InvalidValueType((self.__class__, Obj, binary_type))
3250 return self._value is not None
3253 obj = self.__class__()
3254 obj._value = self._value
3256 obj._expl = self._expl
3257 obj.optional = self.optional
3258 obj.offset = self.offset
3259 obj.llen = self.llen
3260 obj.vlen = self.vlen
3263 def __eq__(self, their):
3264 if isinstance(their, binary_type):
3265 return self._value == their
3266 if issubclass(their.__class__, Any):
3267 return self._value == their._value
3276 return self.__class__(
3278 expl=self._expl if expl is None else expl,
3279 optional=self.optional if optional is None else optional,
3282 def __bytes__(self):
3283 self._assert_ready()
3291 self._assert_ready()
3294 def _decode(self, tlv, offset=0, decode_path=()):
3296 t, tlen, lv = tag_strip(tlv)
3297 l, llen, v = len_decode(lv)
3298 except DecodeError as err:
3299 raise err.__class__(
3301 klass=self.__class__,
3302 decode_path=decode_path,
3306 raise NotEnoughData(
3307 "encoded length is longer than data",
3308 klass=self.__class__,
3309 decode_path=decode_path,
3312 tlvlen = tlen + llen + l
3313 v, tail = tlv[:tlvlen], v[l:]
3314 obj = self.__class__(
3317 optional=self.optional,
3318 _decoded=(offset, 0, tlvlen),
3324 return pp_console_row(next(self.pps()))
3326 def pps(self, decode_path=()):
3328 asn1_type_name=self.asn1_type_name,
3329 obj_name=self.__class__.__name__,
3330 decode_path=decode_path,
3331 blob=self._value if self.ready else None,
3332 optional=self.optional,
3333 default=self == self.default,
3334 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3335 expl=None if self._expl is None else tag_decode(self._expl),
3340 expl_offset=self.expl_offset if self.expled else None,
3341 expl_tlen=self.expl_tlen if self.expled else None,
3342 expl_llen=self.expl_llen if self.expled else None,
3343 expl_vlen=self.expl_vlen if self.expled else None,
3347 ########################################################################
3348 # ASN.1 constructed types
3349 ########################################################################
3351 class Sequence(Obj):
3352 """``SEQUENCE`` structure type
3354 You have to make specification of sequence::
3356 class Extension(Sequence):
3359 ("extnID", ObjectIdentifier()),
3360 ("critical", Boolean(default=False)),
3361 ("extnValue", OctetString()),
3364 Then, you can work with it as with dictionary.
3366 >>> ext = Extension()
3367 >>> Extension().specs
3369 ('extnID', OBJECT IDENTIFIER),
3370 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3371 ('extnValue', OCTET STRING),
3373 >>> ext["extnID"] = "1.2.3"
3374 Traceback (most recent call last):
3375 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3376 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3378 You can know if sequence is ready to be encoded:
3383 Traceback (most recent call last):
3384 pyderasn.ObjNotReady: object is not ready: extnValue
3385 >>> ext["extnValue"] = OctetString(b"foobar")
3389 Value you want to assign, must have the same **type** as in
3390 corresponding specification, but it can have different tags,
3391 optional/default attributes -- they will be taken from specification
3394 class TBSCertificate(Sequence):
3396 ("version", Version(expl=tag_ctxc(0), default="v1")),
3399 >>> tbs = TBSCertificate()
3400 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3402 You can know if value exists/set in the sequence and take its value:
3404 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3407 OBJECT IDENTIFIER 1.2.3
3409 But pay attention that if value has default, then it won't be (not
3410 in) in the sequence (because ``DEFAULT`` must not be encoded in
3411 DER), but you can read its value:
3413 >>> "critical" in ext, ext["critical"]
3414 (False, BOOLEAN False)
3415 >>> ext["critical"] = Boolean(True)
3416 >>> "critical" in ext, ext["critical"]
3417 (True, BOOLEAN True)
3419 All defaulted values are always optional.
3423 When decoded DER contains defaulted value inside, then
3424 technically this is not valid DER encoding. But we allow
3425 and pass it. Of course reencoding of that kind of DER will
3426 result in different binary representation (validly without
3427 defaulted value inside).
3429 Two sequences are equal if they have equal specification (schema),
3430 implicit/explicit tagging and the same values.
3432 __slots__ = ("specs",)
3433 tag_default = tag_encode(form=TagFormConstructed, num=16)
3434 asn1_type_name = "SEQUENCE"
3446 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3448 schema = getattr(self, "schema", ())
3450 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3453 if value is not None:
3454 self._value = self._value_sanitize(value)
3455 if default is not None:
3456 default_value = self._value_sanitize(default)
3457 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3458 default_obj.specs = self.specs
3459 default_obj._value = default_value
3460 self.default = default_obj
3462 self._value = default_obj.copy()._value
3464 def _value_sanitize(self, value):
3465 if not issubclass(value.__class__, Sequence):
3466 raise InvalidValueType((Sequence,))
3471 for name, spec in self.specs.items():
3472 value = self._value.get(name)
3483 obj = self.__class__(schema=self.specs)
3485 obj._expl = self._expl
3486 obj.default = self.default
3487 obj.optional = self.optional
3488 obj.offset = self.offset
3489 obj.llen = self.llen
3490 obj.vlen = self.vlen
3491 obj._value = {k: v.copy() for k, v in self._value.items()}
3494 def __eq__(self, their):
3495 if not isinstance(their, self.__class__):
3498 self.specs == their.specs and
3499 self.tag == their.tag and
3500 self._expl == their._expl and
3501 self._value == their._value
3512 return self.__class__(
3515 impl=self.tag if impl is None else impl,
3516 expl=self._expl if expl is None else expl,
3517 default=self.default if default is None else default,
3518 optional=self.optional if optional is None else optional,
3521 def __contains__(self, key):
3522 return key in self._value
3524 def __setitem__(self, key, value):
3525 spec = self.specs.get(key)
3527 raise ObjUnknown(key)
3529 self._value.pop(key, None)
3531 if not isinstance(value, spec.__class__):
3532 raise InvalidValueType((spec.__class__,))
3533 value = spec(value=value)
3534 if spec.default is not None and value == spec.default:
3535 self._value.pop(key, None)
3537 self._value[key] = value
3539 def __getitem__(self, key):
3540 value = self._value.get(key)
3541 if value is not None:
3543 spec = self.specs.get(key)
3545 raise ObjUnknown(key)
3546 if spec.default is not None:
3550 def _encoded_values(self):
3552 for name, spec in self.specs.items():
3553 value = self._value.get(name)
3557 raise ObjNotReady(name)
3558 raws.append(value.encode())
3562 v = b"".join(self._encoded_values())
3563 return b"".join((self.tag, len_encode(len(v)), v))
3565 def _decode(self, tlv, offset=0, decode_path=()):
3567 t, tlen, lv = tag_strip(tlv)
3568 except DecodeError as err:
3569 raise err.__class__(
3571 klass=self.__class__,
3572 decode_path=decode_path,
3577 klass=self.__class__,
3578 decode_path=decode_path,
3582 l, llen, v = len_decode(lv)
3583 except DecodeError as err:
3584 raise err.__class__(
3586 klass=self.__class__,
3587 decode_path=decode_path,
3591 raise NotEnoughData(
3592 "encoded length is longer than data",
3593 klass=self.__class__,
3594 decode_path=decode_path,
3597 v, tail = v[:l], v[l:]
3598 sub_offset = offset + tlen + llen
3600 for name, spec in self.specs.items():
3601 if len(v) == 0 and spec.optional:
3604 value, v_tail = spec.decode(
3608 decode_path=decode_path + (name,),
3614 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3616 if spec.default is not None and value == spec.default:
3617 # Encoded default values are not valid in DER,
3618 # but we still allow that
3620 values[name] = value
3624 klass=self.__class__,
3625 decode_path=decode_path,
3628 obj = self.__class__(
3632 default=self.default,
3633 optional=self.optional,
3634 _decoded=(offset, llen, l),
3640 value = pp_console_row(next(self.pps()))
3642 for name in self.specs:
3643 _value = self._value.get(name)
3646 cols.append(repr(_value))
3647 return "%s[%s]" % (value, ", ".join(cols))
3649 def pps(self, decode_path=()):
3651 asn1_type_name=self.asn1_type_name,
3652 obj_name=self.__class__.__name__,
3653 decode_path=decode_path,
3654 optional=self.optional,
3655 default=self == self.default,
3656 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3657 expl=None if self._expl is None else tag_decode(self._expl),
3662 expl_offset=self.expl_offset if self.expled else None,
3663 expl_tlen=self.expl_tlen if self.expled else None,
3664 expl_llen=self.expl_llen if self.expled else None,
3665 expl_vlen=self.expl_vlen if self.expled else None,
3667 for name in self.specs:
3668 value = self._value.get(name)
3671 yield value.pps(decode_path=decode_path + (name,))
3674 class Set(Sequence):
3675 """``SET`` structure type
3677 Its usage is identical to :py:class:`pyderasn.Sequence`.
3680 tag_default = tag_encode(form=TagFormConstructed, num=17)
3681 asn1_type_name = "SET"
3684 raws = self._encoded_values()
3687 return b"".join((self.tag, len_encode(len(v)), v))
3689 def _decode(self, tlv, offset=0, decode_path=()):
3691 t, tlen, lv = tag_strip(tlv)
3692 except DecodeError as err:
3693 raise err.__class__(
3695 klass=self.__class__,
3696 decode_path=decode_path,
3701 klass=self.__class__,
3702 decode_path=decode_path,
3706 l, llen, v = len_decode(lv)
3707 except DecodeError as err:
3708 raise err.__class__(
3710 klass=self.__class__,
3711 decode_path=decode_path,
3715 raise NotEnoughData(
3716 "encoded length is longer than data",
3717 klass=self.__class__,
3720 v, tail = v[:l], v[l:]
3721 sub_offset = offset + tlen + llen
3723 specs_items = self.specs.items
3725 for name, spec in specs_items():
3727 value, v_tail = spec.decode(
3731 decode_path=decode_path + (name,),
3736 value.expl_tlvlen if value.expled else value.tlvlen
3739 if spec.default is None or value != spec.default: # pragma: no cover
3740 # SeqMixing.test_encoded_default_accepted covers that place
3741 values[name] = value
3745 klass=self.__class__,
3746 decode_path=decode_path,
3749 obj = self.__class__(
3753 default=self.default,
3754 optional=self.optional,
3755 _decoded=(offset, llen, l),
3761 class SequenceOf(Obj):
3762 """``SEQUENCE OF`` sequence type
3764 For that kind of type you must specify the object it will carry on
3765 (bounds are for example here, not required)::
3767 class Ints(SequenceOf):
3772 >>> ints.append(Integer(123))
3773 >>> ints.append(Integer(234))
3775 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3776 >>> [int(i) for i in ints]
3778 >>> ints.append(Integer(345))
3779 Traceback (most recent call last):
3780 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3783 >>> ints[1] = Integer(345)
3785 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3787 Also you can initialize sequence with preinitialized values:
3789 >>> ints = Ints([Integer(123), Integer(234)])
3791 __slots__ = ("spec", "_bound_min", "_bound_max")
3792 tag_default = tag_encode(form=TagFormConstructed, num=16)
3793 asn1_type_name = "SEQUENCE OF"
3806 super(SequenceOf, self).__init__(
3814 schema = getattr(self, "schema", None)
3816 raise ValueError("schema must be specified")
3818 self._bound_min, self._bound_max = getattr(
3822 ) if bounds is None else bounds
3824 if value is not None:
3825 self._value = self._value_sanitize(value)
3826 if default is not None:
3827 default_value = self._value_sanitize(default)
3828 default_obj = self.__class__(
3833 default_obj._value = default_value
3834 self.default = default_obj
3836 self._value = default_obj.copy()._value
3838 def _value_sanitize(self, value):
3839 if issubclass(value.__class__, SequenceOf):
3840 value = value._value
3841 elif hasattr(value, "__iter__"):
3844 raise InvalidValueType((self.__class__, iter))
3845 if not self._bound_min <= len(value) <= self._bound_max:
3846 raise BoundsError(self._bound_min, len(value), self._bound_max)
3848 if not isinstance(v, self.spec.__class__):
3849 raise InvalidValueType((self.spec.__class__,))
3854 return all(v.ready for v in self._value)
3857 obj = self.__class__(schema=self.spec)
3858 obj._bound_min = self._bound_min
3859 obj._bound_max = self._bound_max
3861 obj._expl = self._expl
3862 obj.default = self.default
3863 obj.optional = self.optional
3864 obj.offset = self.offset
3865 obj.llen = self.llen
3866 obj.vlen = self.vlen
3867 obj._value = [v.copy() for v in self._value]
3870 def __eq__(self, their):
3871 if isinstance(their, self.__class__):
3873 self.spec == their.spec and
3874 self.tag == their.tag and
3875 self._expl == their._expl and
3876 self._value == their._value
3878 if hasattr(their, "__iter__"):
3879 return self._value == list(their)
3891 return self.__class__(
3895 (self._bound_min, self._bound_max)
3896 if bounds is None else bounds
3898 impl=self.tag if impl is None else impl,
3899 expl=self._expl if expl is None else expl,
3900 default=self.default if default is None else default,
3901 optional=self.optional if optional is None else optional,
3904 def __contains__(self, key):
3905 return key in self._value
3907 def append(self, value):
3908 if not isinstance(value, self.spec.__class__):
3909 raise InvalidValueType((self.spec.__class__,))
3910 if len(self._value) + 1 > self._bound_max:
3913 len(self._value) + 1,
3916 self._value.append(value)
3919 self._assert_ready()
3920 return iter(self._value)
3923 self._assert_ready()
3924 return len(self._value)
3926 def __setitem__(self, key, value):
3927 if not isinstance(value, self.spec.__class__):
3928 raise InvalidValueType((self.spec.__class__,))
3929 self._value[key] = self.spec(value=value)
3931 def __getitem__(self, key):
3932 return self._value[key]
3934 def _encoded_values(self):
3935 return [v.encode() for v in self._value]
3938 v = b"".join(self._encoded_values())
3939 return b"".join((self.tag, len_encode(len(v)), v))
3941 def _decode(self, tlv, offset=0, decode_path=()):
3943 t, tlen, lv = tag_strip(tlv)
3944 except DecodeError as err:
3945 raise err.__class__(
3947 klass=self.__class__,
3948 decode_path=decode_path,
3953 klass=self.__class__,
3954 decode_path=decode_path,
3958 l, llen, v = len_decode(lv)
3959 except DecodeError as err:
3960 raise err.__class__(
3962 klass=self.__class__,
3963 decode_path=decode_path,
3967 raise NotEnoughData(
3968 "encoded length is longer than data",
3969 klass=self.__class__,
3970 decode_path=decode_path,
3973 v, tail = v[:l], v[l:]
3974 sub_offset = offset + tlen + llen
3978 value, v_tail = spec.decode(
3982 decode_path=decode_path + (str(len(_value)),),
3984 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3986 _value.append(value)
3987 obj = self.__class__(
3990 bounds=(self._bound_min, self._bound_max),
3993 default=self.default,
3994 optional=self.optional,
3995 _decoded=(offset, llen, l),
4001 pp_console_row(next(self.pps())),
4002 ", ".join(repr(v) for v in self._value),
4005 def pps(self, decode_path=()):
4007 asn1_type_name=self.asn1_type_name,
4008 obj_name=self.__class__.__name__,
4009 decode_path=decode_path,
4010 optional=self.optional,
4011 default=self == self.default,
4012 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4013 expl=None if self._expl is None else tag_decode(self._expl),
4018 expl_offset=self.expl_offset if self.expled else None,
4019 expl_tlen=self.expl_tlen if self.expled else None,
4020 expl_llen=self.expl_llen if self.expled else None,
4021 expl_vlen=self.expl_vlen if self.expled else None,
4023 for i, value in enumerate(self._value):
4024 yield value.pps(decode_path=decode_path + (str(i),))
4027 class SetOf(SequenceOf):
4028 """``SET OF`` sequence type
4030 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4033 tag_default = tag_encode(form=TagFormConstructed, num=17)
4034 asn1_type_name = "SET OF"
4037 raws = self._encoded_values()
4040 return b"".join((self.tag, len_encode(len(v)), v))
4043 def obj_by_path(pypath): # pragma: no cover
4044 """Import object specified as string Python path
4046 Modules must be separated from classes/functions with ``:``.
4048 >>> obj_by_path("foo.bar:Baz")
4049 <class 'foo.bar.Baz'>
4050 >>> obj_by_path("foo.bar:Baz.boo")
4051 <classmethod 'foo.bar.Baz.boo'>
4053 mod, objs = pypath.rsplit(":", 1)
4054 from importlib import import_module
4055 obj = import_module(mod)
4056 for obj_name in objs.split("."):
4057 obj = getattr(obj, obj_name)
4061 def main(): # pragma: no cover
4063 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4064 parser.add_argument(
4066 help="Python path to dictionary with OIDs",
4068 parser.add_argument(
4070 help="Python path to schema definition to use",
4072 parser.add_argument(
4074 type=argparse.FileType("rb"),
4075 help="Path to DER file you want to decode",
4077 args = parser.parse_args()
4078 der = memoryview(args.DERFile.read())
4079 args.DERFile.close()
4080 oids = obj_by_path(args.oids) if args.oids else {}
4082 schema = obj_by_path(args.schema)
4083 from functools import partial
4084 pprinter = partial(pprint, big_blobs=True)
4086 # All of this below is a big hack with self references
4087 choice = PrimitiveTypes()
4088 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4089 choice.specs["SetOf"] = SetOf(schema=choice)
4091 choice.specs["SequenceOf%d" % i] = SequenceOf(
4095 choice.specs["Any"] = Any()
4097 # Class name equals to type name, to omit it from output
4098 class SEQUENCEOF(SequenceOf):
4101 schema = SEQUENCEOF()
4103 def pprint_any(obj, oids=None):
4104 def _pprint_pps(pps):
4106 if hasattr(pp, "_fields"):
4107 if pp.asn1_type_name == Choice.asn1_type_name:
4109 pp_kwargs = pp._asdict()
4110 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4111 pp = _pp(**pp_kwargs)
4112 yield pp_console_row(
4118 for row in pp_console_blob(pp):
4121 for row in _pprint_pps(pp):
4123 return "\n".join(_pprint_pps(obj.pps()))
4124 pprinter = pprint_any
4125 obj, tail = schema().decode(der)
4126 print(pprinter(obj, oids=oids))
4128 print("\nTrailing data: %s" % hexenc(tail))
4131 if __name__ == "__main__":