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 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
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
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
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
317 from six import string_types
318 from six import text_type
319 from six.moves import xrange as six_xrange
360 "TagClassApplication",
364 "TagFormConstructed",
375 TagClassUniversal = 0
376 TagClassApplication = 1 << 6
377 TagClassContext = 1 << 7
378 TagClassPrivate = 1 << 6 | 1 << 7
380 TagFormConstructed = 1 << 5
383 TagClassApplication: "APPLICATION ",
384 TagClassPrivate: "PRIVATE ",
385 TagClassUniversal: "UNIV ",
389 ########################################################################
391 ########################################################################
393 class DecodeError(Exception):
394 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
395 super(DecodeError, self).__init__()
398 self.decode_path = decode_path
404 "" if self.klass is None else self.klass.__name__,
406 ("(%s)" % ".".join(self.decode_path))
407 if len(self.decode_path) > 0 else ""
409 ("(at %d)" % self.offset) if self.offset > 0 else "",
415 return "%s(%s)" % (self.__class__.__name__, self)
418 class NotEnoughData(DecodeError):
422 class TagMismatch(DecodeError):
426 class InvalidLength(DecodeError):
430 class InvalidOID(DecodeError):
434 class ObjUnknown(ValueError):
435 def __init__(self, name):
436 super(ObjUnknown, self).__init__()
440 return "object is unknown: %s" % self.name
443 return "%s(%s)" % (self.__class__.__name__, self)
446 class ObjNotReady(ValueError):
447 def __init__(self, name):
448 super(ObjNotReady, self).__init__()
452 return "object is not ready: %s" % self.name
455 return "%s(%s)" % (self.__class__.__name__, self)
458 class InvalidValueType(ValueError):
459 def __init__(self, expected_types):
460 super(InvalidValueType, self).__init__()
461 self.expected_types = expected_types
464 return "invalid value type, expected: %s" % ", ".join(
465 [repr(t) for t in self.expected_types]
469 return "%s(%s)" % (self.__class__.__name__, self)
472 class BoundsError(ValueError):
473 def __init__(self, bound_min, value, bound_max):
474 super(BoundsError, self).__init__()
475 self.bound_min = bound_min
477 self.bound_max = bound_max
480 return "unsatisfied bounds: %s <= %s <= %s" % (
487 return "%s(%s)" % (self.__class__.__name__, self)
490 ########################################################################
492 ########################################################################
494 _hexdecoder = getdecoder("hex")
495 _hexencoder = getencoder("hex")
499 return _hexdecoder(data)[0]
503 return _hexencoder(data)[0].decode("ascii")
506 def int_bytes_len(num, byte_len=8):
509 return int(ceil(float(num.bit_length()) / byte_len))
512 def zero_ended_encode(num):
513 octets = bytearray(int_bytes_len(num, 7))
515 octets[i] = num & 0x7F
519 octets[i] = 0x80 | (num & 0x7F)
525 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
528 return int2byte(klass | form | num)
529 # [XX|X|11111][1.......][1.......] ... [0.......]
530 return int2byte(klass | form | 31) + zero_ended_encode(num)
535 assume that data is validated
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)
543 for octet in iterbytes(tag[1:]):
546 return (klass, form, num)
550 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
554 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
558 """Take off tag from the data
560 :returns: (encoded tag, tag length, remaining data)
563 raise NotEnoughData("no data at all")
564 if byte2int(data) & 0x1F < 31:
565 return data[:1], 1, data[1:]
570 raise DecodeError("unfinished tag")
571 if indexbytes(data, i) & 0x80 == 0:
574 return data[:i], i, data[i:]
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):
588 def len_decode(data):
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")
598 raise DecodeError("long form instead of short one")
599 if byte2int(data[1:]) == 0:
600 raise DecodeError("leading zeros")
602 for v in iterbytes(data[1:1 + octets_num]):
605 raise DecodeError("long form instead of short one")
606 return l, 1 + octets_num, data[1 + octets_num:]
609 ########################################################################
611 ########################################################################
614 """Common ASN.1 object class
636 self.tag = getattr(self, "impl", self.tag_default)
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:
642 "implicit and explicit tags can not be set simultaneously"
644 if default is not None:
646 self.optional = optional
647 self.offset, self.llen, self.vlen = _decoded
651 def ready(self): # pragma: no cover
652 raise NotImplementedError()
654 def _assert_ready(self):
656 raise ObjNotReady(self.__class__.__name__)
662 def copy(self): # pragma: no cover
663 raise NotImplementedError()
671 return self.tlen + self.llen + self.vlen
673 def __str__(self): # pragma: no cover
674 return self.__bytes__() if PY2 else self.__unicode__()
676 def _encode(self): # pragma: no cover
677 raise NotImplementedError()
679 def _decode(self, tlv, offset=0, decode_path=()): # pragma: no cover
680 raise NotImplementedError()
684 if self._expl is None:
686 return b"".join((self._expl, len_encode(len(raw)), raw))
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(
694 decode_path=decode_path,
698 t, tlen, lv = tag_strip(tlv)
699 except DecodeError as err:
702 klass=self.__class__,
703 decode_path=decode_path,
708 klass=self.__class__,
709 decode_path=decode_path,
713 l, llen, v = len_decode(lv)
714 except DecodeError as err:
717 klass=self.__class__,
718 decode_path=decode_path,
723 "encoded length is longer than data",
724 klass=self.__class__,
725 decode_path=decode_path,
728 obj, tail = self._decode(
730 offset=offset + tlen + llen,
733 return obj, (tail if leavemm else tail.tobytes())
737 return self._expl is not None
745 return len(self._expl)
749 return len(len_encode(self.tlvlen))
752 def expl_offset(self):
753 return self.offset - self.expl_tlen - self.expl_llen
760 def expl_tlvlen(self):
761 return self.expl_tlen + self.expl_llen + self.expl_vlen
764 ########################################################################
766 ########################################################################
768 PP = namedtuple("PP", (
790 asn1_type_name="unknown",
829 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
832 cols.append("%5d%s [%d,%d,%4d]" % (
835 " " if pp.expl_offset is None else
836 ("-%d" % (pp.offset - pp.expl_offset))
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:
858 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
861 value = "%s (%s)" % (oids[value], pp.value)
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))
869 cols.append("OPTIONAL")
871 cols.append("DEFAULT")
872 return " ".join(cols)
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)
886 elif isinstance(pp.blob, tuple):
887 yield " ".join(cols + [", ".join(pp.blob)])
890 def pprint(obj, oids=None, big_blobs=False):
891 def _pprint_pps(pps):
893 if hasattr(pp, "_fields"):
895 yield pp_console_row(
901 for row in pp_console_blob(pp):
904 yield pp_console_row(pp, oids=oids, with_offsets=True)
906 for row in _pprint_pps(pp):
908 return "\n".join(_pprint_pps(obj.pps()))
911 ########################################################################
912 # ASN.1 primitive types
913 ########################################################################
916 """``BOOLEAN`` boolean type
918 >>> b = Boolean(True)
920 >>> b == Boolean(True)
924 >>> Boolean(optional=True)
926 >>> Boolean(impl=tag_ctxp(1), default=False)
927 [1] BOOLEAN False OPTIONAL DEFAULT
930 tag_default = tag_encode(1)
931 asn1_type_name = "BOOLEAN"
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
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__(
960 self._value = default
962 def _value_sanitize(self, value):
963 if issubclass(value.__class__, Boolean):
965 if isinstance(value, bool):
967 raise InvalidValueType((self.__class__, bool))
971 return self._value is not None
974 obj = self.__class__()
975 obj._value = self._value
977 obj._expl = self._expl
978 obj.default = self.default
979 obj.optional = self.optional
980 obj.offset = self.offset
985 def __nonzero__(self):
993 def __eq__(self, their):
994 if isinstance(their, bool):
995 return self._value == their
996 if not issubclass(their.__class__, Boolean):
999 self._value == their._value and
1000 self.tag == their.tag and
1001 self._expl == their._expl
1012 return self.__class__(
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,
1021 self._assert_ready()
1025 (b"\xFF" if self._value else b"\x00"),
1028 def _decode(self, tlv, offset=0, decode_path=()):
1030 t, _, lv = tag_strip(tlv)
1031 except DecodeError as err:
1032 raise err.__class__(
1034 klass=self.__class__,
1035 decode_path=decode_path,
1040 klass=self.__class__,
1041 decode_path=decode_path,
1045 l, _, v = len_decode(lv)
1046 except DecodeError as err:
1047 raise err.__class__(
1049 klass=self.__class__,
1050 decode_path=decode_path,
1054 raise InvalidLength(
1055 "Boolean's length must be equal to 1",
1056 klass=self.__class__,
1057 decode_path=decode_path,
1061 raise NotEnoughData(
1062 "encoded length is longer than data",
1063 klass=self.__class__,
1064 decode_path=decode_path,
1067 first_octet = byte2int(v)
1068 if first_octet == 0:
1070 elif first_octet == 0xFF:
1074 "unacceptable Boolean value",
1075 klass=self.__class__,
1076 decode_path=decode_path,
1079 obj = self.__class__(
1083 default=self.default,
1084 optional=self.optional,
1085 _decoded=(offset, 1, 1),
1090 return pp_console_row(next(self.pps()))
1092 def pps(self, decode_path=()):
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),
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,
1114 """``INTEGER`` integer type
1116 >>> b = Integer(-123)
1118 >>> b == Integer(-123)
1122 >>> Integer(optional=True)
1124 >>> Integer(impl=tag_ctxp(1), default=123)
1125 [1] INTEGER 123 OPTIONAL DEFAULT
1127 >>> Integer(2, bounds=(1, 3))
1129 >>> Integer(5, bounds=(1, 3))
1130 Traceback (most recent call last):
1131 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1135 class Version(Integer):
1142 >>> v = Version("v1")
1149 {'v3': 2, 'v1': 0, 'v2': 1}
1151 __slots__ = ("specs", "_bound_min", "_bound_max")
1152 tag_default = tag_encode(2)
1153 asn1_type_name = "INTEGER"
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
1177 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1179 specs = getattr(self, "schema", {}) if _specs is None else _specs
1180 self.specs = specs if isinstance(specs, dict) else dict(specs)
1182 self._bound_min, self._bound_max = getattr(
1185 (float("-inf"), float("+inf")),
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__(
1199 if self._value is None:
1200 self._value = default
1202 def _value_sanitize(self, value):
1203 if issubclass(value.__class__, Integer):
1204 value = value._value
1205 elif isinstance(value, integer_types):
1207 elif isinstance(value, str):
1208 value = self.specs.get(value)
1210 raise ObjUnknown("integer value: %s" % value)
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)
1219 return self._value is not None
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
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
1236 self._assert_ready()
1237 return int(self._value)
1240 self._assert_ready()
1243 bytes(self._expl or b"") +
1244 str(self._value).encode("ascii"),
1247 def __eq__(self, their):
1248 if isinstance(their, integer_types):
1249 return self._value == their
1250 if not issubclass(their.__class__, Integer):
1253 self._value == their._value and
1254 self.tag == their.tag and
1255 self._expl == their._expl
1258 def __lt__(self, their):
1259 return self._value < their
1261 def __gt__(self, their):
1262 return self._value > their
1266 for name, value in self.specs.items():
1267 if value == self._value:
1279 return self.__class__(
1282 (self._bound_min, self._bound_max)
1283 if bounds is None else bounds
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,
1293 self._assert_ready()
1297 octets = bytearray([0])
1301 octets = bytearray()
1303 octets.append((value & 0xFF) ^ 0xFF)
1305 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1308 octets = bytearray()
1310 octets.append(value & 0xFF)
1312 if octets[-1] & 0x80 > 0:
1315 octets = bytes(octets)
1317 bytes_len = ceil(value.bit_length() / 8) or 1
1320 octets = value.to_bytes(
1325 except OverflowError:
1329 return b"".join((self.tag, len_encode(len(octets)), octets))
1331 def _decode(self, tlv, offset=0, decode_path=()):
1333 t, _, lv = tag_strip(tlv)
1334 except DecodeError as err:
1335 raise err.__class__(
1337 klass=self.__class__,
1338 decode_path=decode_path,
1343 klass=self.__class__,
1344 decode_path=decode_path,
1348 l, llen, v = len_decode(lv)
1349 except DecodeError as err:
1350 raise err.__class__(
1352 klass=self.__class__,
1353 decode_path=decode_path,
1357 raise NotEnoughData(
1358 "encoded length is longer than data",
1359 klass=self.__class__,
1360 decode_path=decode_path,
1364 raise NotEnoughData(
1366 klass=self.__class__,
1367 decode_path=decode_path,
1370 v, tail = v[:l], v[l:]
1371 first_octet = byte2int(v)
1373 second_octet = byte2int(v[1:])
1375 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1376 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1379 "non normalized integer",
1380 klass=self.__class__,
1381 decode_path=decode_path,
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
1395 for octet in bytearray(v):
1396 value = (value << 8) | octet
1398 value = int.from_bytes(v, byteorder="big", signed=True)
1400 obj = self.__class__(
1402 bounds=(self._bound_min, self._bound_max),
1405 default=self.default,
1406 optional=self.optional,
1408 _decoded=(offset, llen, l),
1410 except BoundsError as err:
1413 klass=self.__class__,
1414 decode_path=decode_path,
1420 return pp_console_row(next(self.pps()))
1422 def pps(self, decode_path=()):
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),
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,
1443 class BitString(Obj):
1444 """``BIT STRING`` bit string type
1446 >>> BitString(b"hello world")
1447 BIT STRING 88 bits 68656c6c6f20776f726c64
1450 >>> b == b"hello world"
1455 >>> b = BitString("'010110000000'B")
1456 BIT STRING 12 bits 5800
1459 >>> b[0], b[1], b[2], b[3]
1460 (False, True, False, True)
1464 [False, True, False, True, True, False, False, False, False, False, False, False]
1468 class KeyUsage(BitString):
1470 ('digitalSignature', 0),
1471 ('nonRepudiation', 1),
1472 ('keyEncipherment', 2),
1475 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1476 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1478 ['nonRepudiation', 'keyEncipherment']
1480 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1482 __slots__ = ("specs",)
1483 tag_default = tag_encode(3)
1484 asn1_type_name = "BIT STRING"
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
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__(
1518 self._value = default
1520 def _bits2octets(self, bits):
1521 if len(self.specs) > 0:
1522 bits = bits.rstrip("0")
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)
1530 def _value_sanitize(self, value):
1531 if issubclass(value.__class__, BitString):
1533 if isinstance(value, (string_types, binary_type)):
1535 isinstance(value, string_types) and
1536 value.startswith("'") and
1537 value.endswith("'B")
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)
1546 raise InvalidValueType((
1551 if isinstance(value, tuple):
1554 isinstance(value[0], integer_types) and
1555 isinstance(value[1], binary_type)
1560 bit = self.specs.get(name)
1562 raise ObjUnknown("BitString value: %s" % name)
1565 return self._bits2octets("")
1567 return self._bits2octets("".join(
1568 ("1" if bit in bits else "0")
1569 for bit in six_xrange(max(bits) + 1)
1571 raise InvalidValueType((self.__class__, binary_type, string_types))
1575 return self._value is not None
1578 obj = self.__class__(_specs=self.specs)
1579 obj._value = self._value
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
1590 self._assert_ready()
1591 for i in six_xrange(self._value[0]):
1596 self._assert_ready()
1597 return self._value[0]
1599 def __bytes__(self):
1600 self._assert_ready()
1601 return self._value[1]
1603 def __eq__(self, their):
1604 if isinstance(their, bytes):
1605 return self._value[1] == their
1606 if not issubclass(their.__class__, BitString):
1609 self._value == their._value and
1610 self.tag == their.tag and
1611 self._expl == their._expl
1616 return [name for name, bit in self.specs.items() if self[bit]]
1626 return self.__class__(
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,
1635 def __getitem__(self, key):
1636 if isinstance(key, int):
1637 bit_len, octets = self._value
1641 byte2int(memoryview(octets)[key // 8:]) >>
1644 if isinstance(key, string_types):
1645 value = self.specs.get(key)
1647 raise ObjUnknown("BitString value: %s" % key)
1649 raise InvalidValueType((int, str))
1652 self._assert_ready()
1653 bit_len, octets = self._value
1656 len_encode(len(octets) + 1),
1657 int2byte((8 - bit_len % 8) % 8),
1661 def _decode(self, tlv, offset=0, decode_path=()):
1663 t, _, lv = tag_strip(tlv)
1664 except DecodeError as err:
1665 raise err.__class__(
1667 klass=self.__class__,
1668 decode_path=decode_path,
1673 klass=self.__class__,
1674 decode_path=decode_path,
1678 l, llen, v = len_decode(lv)
1679 except DecodeError as err:
1680 raise err.__class__(
1682 klass=self.__class__,
1683 decode_path=decode_path,
1687 raise NotEnoughData(
1688 "encoded length is longer than data",
1689 klass=self.__class__,
1690 decode_path=decode_path,
1694 raise NotEnoughData(
1696 klass=self.__class__,
1697 decode_path=decode_path,
1700 pad_size = byte2int(v)
1701 if l == 1 and pad_size != 0:
1703 "invalid empty value",
1704 klass=self.__class__,
1705 decode_path=decode_path,
1711 klass=self.__class__,
1712 decode_path=decode_path,
1715 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1718 klass=self.__class__,
1719 decode_path=decode_path,
1722 v, tail = v[:l], v[l:]
1723 obj = self.__class__(
1724 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1727 default=self.default,
1728 optional=self.optional,
1730 _decoded=(offset, llen, l),
1735 return pp_console_row(next(self.pps()))
1737 def pps(self, decode_path=()):
1741 bit_len, blob = self._value
1742 value = "%d bits" % bit_len
1743 if len(self.specs) > 0:
1744 blob = tuple(self.named)
1746 asn1_type_name=self.asn1_type_name,
1747 obj_name=self.__class__.__name__,
1748 decode_path=decode_path,
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),
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,
1766 class OctetString(Obj):
1767 """``OCTET STRING`` binary string type
1769 >>> s = OctetString(b"hello world")
1770 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1771 >>> s == OctetString(b"hello world")
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
1782 __slots__ = ("_bound_min", "_bound_max")
1783 tag_default = tag_encode(4)
1784 asn1_type_name = "OCTET STRING"
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
1806 super(OctetString, self).__init__(
1815 self._bound_min, self._bound_max = getattr(
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__(
1831 if self._value is None:
1832 self._value = default
1834 def _value_sanitize(self, value):
1835 if issubclass(value.__class__, OctetString):
1836 value = value._value
1837 elif isinstance(value, binary_type):
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)
1847 return self._value is not None
1850 obj = self.__class__()
1851 obj._value = self._value
1852 obj._bound_min = self._bound_min
1853 obj._bound_max = self._bound_max
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
1863 def __bytes__(self):
1864 self._assert_ready()
1867 def __eq__(self, their):
1868 if isinstance(their, binary_type):
1869 return self._value == their
1870 if not issubclass(their.__class__, OctetString):
1873 self._value == their._value and
1874 self.tag == their.tag and
1875 self._expl == their._expl
1887 return self.__class__(
1890 (self._bound_min, self._bound_max)
1891 if bounds is None else bounds
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,
1900 self._assert_ready()
1903 len_encode(len(self._value)),
1907 def _decode(self, tlv, offset=0, decode_path=()):
1909 t, _, lv = tag_strip(tlv)
1910 except DecodeError as err:
1911 raise err.__class__(
1913 klass=self.__class__,
1914 decode_path=decode_path,
1919 klass=self.__class__,
1920 decode_path=decode_path,
1924 l, llen, v = len_decode(lv)
1925 except DecodeError as err:
1926 raise err.__class__(
1928 klass=self.__class__,
1929 decode_path=decode_path,
1933 raise NotEnoughData(
1934 "encoded length is longer than data",
1935 klass=self.__class__,
1936 decode_path=decode_path,
1939 v, tail = v[:l], v[l:]
1941 obj = self.__class__(
1943 bounds=(self._bound_min, self._bound_max),
1946 default=self.default,
1947 optional=self.optional,
1948 _decoded=(offset, llen, l),
1950 except BoundsError as err:
1953 klass=self.__class__,
1954 decode_path=decode_path,
1960 return pp_console_row(next(self.pps()))
1962 def pps(self, decode_path=()):
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),
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,
1985 """``NULL`` null object
1993 tag_default = tag_encode(5)
1994 asn1_type_name = "NULL"
1998 value=None, # unused, but Sequence passes it
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
2009 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2017 obj = self.__class__()
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
2027 def __eq__(self, their):
2028 if not issubclass(their.__class__, Null):
2031 self.tag == their.tag and
2032 self._expl == their._expl
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,
2049 return self.tag + len_encode(0)
2051 def _decode(self, tlv, offset=0, decode_path=()):
2053 t, _, lv = tag_strip(tlv)
2054 except DecodeError as err:
2055 raise err.__class__(
2057 klass=self.__class__,
2058 decode_path=decode_path,
2063 klass=self.__class__,
2064 decode_path=decode_path,
2068 l, _, v = len_decode(lv)
2069 except DecodeError as err:
2070 raise err.__class__(
2072 klass=self.__class__,
2073 decode_path=decode_path,
2077 raise InvalidLength(
2078 "Null must have zero length",
2079 klass=self.__class__,
2080 decode_path=decode_path,
2083 obj = self.__class__(
2086 optional=self.optional,
2087 _decoded=(offset, 1, 0),
2092 return pp_console_row(next(self.pps()))
2094 def pps(self, decode_path=()):
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),
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,
2113 class ObjectIdentifier(Obj):
2114 """``OBJECT IDENTIFIER`` OID type
2116 >>> oid = ObjectIdentifier((1, 2, 3))
2117 OBJECT IDENTIFIER 1.2.3
2118 >>> oid == ObjectIdentifier("1.2.3")
2124 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2125 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2127 >>> str(ObjectIdentifier((3, 1)))
2128 Traceback (most recent call last):
2129 pyderasn.InvalidOID: unacceptable first arc value
2132 tag_default = tag_encode(6)
2133 asn1_type_name = "OBJECT IDENTIFIER"
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
2153 super(ObjectIdentifier, self).__init__(
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__(
2170 if self._value is None:
2171 self._value = default
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))
2180 def _value_sanitize(self, value):
2181 if issubclass(value.__class__, ObjectIdentifier):
2183 if isinstance(value, string_types):
2185 value = tuple(int(arc) for arc in value.split("."))
2187 raise InvalidOID("unacceptable arcs values")
2188 if isinstance(value, tuple):
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:
2198 raise InvalidOID("unacceptable first arc value")
2200 raise InvalidValueType((self.__class__, str, tuple))
2204 return self._value is not None
2207 obj = self.__class__()
2208 obj._value = self._value
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
2219 self._assert_ready()
2220 return iter(self._value)
2223 return ".".join(str(arc) for arc in self._value or ())
2226 self._assert_ready()
2229 bytes(self._expl or b"") +
2230 str(self._value).encode("ascii"),
2233 def __eq__(self, their):
2234 if isinstance(their, tuple):
2235 return self._value == their
2236 if not issubclass(their.__class__, ObjectIdentifier):
2239 self.tag == their.tag and
2240 self._expl == their._expl and
2241 self._value == their._value
2244 def __lt__(self, their):
2245 return self._value < their
2247 def __gt__(self, their):
2248 return self._value > their
2258 return self.__class__(
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,
2267 self._assert_ready()
2269 first_value = value[1]
2270 first_arc = value[0]
2273 elif first_arc == 1:
2275 elif first_arc == 2:
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))
2285 def _decode(self, tlv, offset=0, decode_path=()):
2287 t, _, lv = tag_strip(tlv)
2288 except DecodeError as err:
2289 raise err.__class__(
2291 klass=self.__class__,
2292 decode_path=decode_path,
2297 klass=self.__class__,
2298 decode_path=decode_path,
2302 l, llen, v = len_decode(lv)
2303 except DecodeError as err:
2304 raise err.__class__(
2306 klass=self.__class__,
2307 decode_path=decode_path,
2311 raise NotEnoughData(
2312 "encoded length is longer than data",
2313 klass=self.__class__,
2314 decode_path=decode_path,
2318 raise NotEnoughData(
2320 klass=self.__class__,
2321 decode_path=decode_path,
2324 v, tail = v[:l], v[l:]
2330 octet = indexbytes(v, i)
2331 arc = (arc << 7) | (octet & 0x7F)
2332 if octet & 0x80 == 0:
2340 klass=self.__class__,
2341 decode_path=decode_path,
2345 second_arc = arcs[0]
2346 if 0 <= second_arc <= 39:
2348 elif 40 <= second_arc <= 79:
2354 obj = self.__class__(
2355 value=tuple([first_arc, second_arc] + arcs[1:]),
2358 default=self.default,
2359 optional=self.optional,
2360 _decoded=(offset, llen, l),
2365 return pp_console_row(next(self.pps()))
2367 def pps(self, decode_path=()):
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),
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,
2388 class Enumerated(Integer):
2389 """``ENUMERATED`` integer type
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.
2395 tag_default = tag_encode(10)
2396 asn1_type_name = "ENUMERATED"
2407 bounds=None, # dummy argument, workability for Integer.decode
2409 super(Enumerated, self).__init__(
2418 if len(self.specs) == 0:
2419 raise ValueError("schema must be specified")
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()):
2427 "unknown integer value: %s" % value,
2428 klass=self.__class__,
2430 elif isinstance(value, string_types):
2431 value = self.specs.get(value)
2433 raise ObjUnknown("integer value: %s" % value)
2435 raise InvalidValueType((self.__class__, int, str))
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
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
2461 return self.__class__(
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,
2471 class CommonString(OctetString):
2472 """Common class for all strings
2474 Everything resembles :py:class:`pyderasn.OctetString`, except
2475 ability to deal with unicode text strings.
2477 >>> hexenc("привет мир".encode("utf-8"))
2478 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2479 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2481 >>> s = UTF8String("привет мир")
2482 UTF8String UTF8String привет мир
2484 'привет мир'
2485 >>> hexenc(bytes(s))
2486 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
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)
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))
2498 >>> hexenc(bytes(s))
2506 * - :py:class:`pyderasn.UTF8String`
2508 * - :py:class:`pyderasn.NumericString`
2510 * - :py:class:`pyderasn.PrintableString`
2512 * - :py:class:`pyderasn.TeletexString`
2514 * - :py:class:`pyderasn.T61String`
2516 * - :py:class:`pyderasn.VideotexString`
2518 * - :py:class:`pyderasn.IA5String`
2520 * - :py:class:`pyderasn.GraphicString`
2522 * - :py:class:`pyderasn.VisibleString`
2524 * - :py:class:`pyderasn.ISO646String`
2526 * - :py:class:`pyderasn.GeneralString`
2528 * - :py:class:`pyderasn.UniversalString`
2530 * - :py:class:`pyderasn.BMPString`
2533 __slots__ = ("encoding",)
2535 def _value_sanitize(self, value):
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):
2545 raise InvalidValueType((self.__class__, text_type, binary_type))
2547 value_decoded.encode(self.encoding)
2548 if value_raw is None else value_raw
2551 value_raw.decode(self.encoding)
2552 if value_decoded is None else value_decoded
2554 if not self._bound_min <= len(value_decoded) <= self._bound_max:
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__):
2570 self._value == their._value and
2571 self.tag == their.tag and
2572 self._expl == their._expl
2575 def __unicode__(self):
2577 return self._value.decode(self.encoding)
2578 return text_type(self._value)
2581 return pp_console_row(next(self.pps(no_unicode=PY2)))
2583 def pps(self, decode_path=(), no_unicode=False):
2586 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2588 asn1_type_name=self.asn1_type_name,
2589 obj_name=self.__class__.__name__,
2590 decode_path=decode_path,
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),
2603 class UTF8String(CommonString):
2605 tag_default = tag_encode(12)
2607 asn1_type_name = "UTF8String"
2610 class NumericString(CommonString):
2612 tag_default = tag_encode(18)
2614 asn1_type_name = "NumericString"
2617 class PrintableString(CommonString):
2619 tag_default = tag_encode(19)
2621 asn1_type_name = "PrintableString"
2624 class TeletexString(CommonString):
2626 tag_default = tag_encode(20)
2628 asn1_type_name = "TeletexString"
2631 class T61String(TeletexString):
2633 asn1_type_name = "T61String"
2636 class VideotexString(CommonString):
2638 tag_default = tag_encode(21)
2639 encoding = "iso-8859-1"
2640 asn1_type_name = "VideotexString"
2643 class IA5String(CommonString):
2645 tag_default = tag_encode(22)
2647 asn1_type_name = "IA5"
2650 class UTCTime(CommonString):
2651 """``UTCTime`` datetime type
2653 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2654 UTCTime UTCTime 2017-09-30T22:07:50
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)
2665 tag_default = tag_encode(23)
2667 asn1_type_name = "UTCTime"
2669 fmt = "%y%m%d%H%M%SZ"
2679 bounds=None, # dummy argument, workability for OctetString.decode
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
2689 super(UTCTime, self).__init__(
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__(
2706 if self._value is None:
2707 self._value = default
2709 def _value_sanitize(self, value):
2710 if isinstance(value, self.__class__):
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:
2718 datetime.strptime(value_decoded, self.fmt)
2720 raise DecodeError("invalid UTCTime format")
2723 raise DecodeError("invalid UTCTime length")
2724 raise InvalidValueType((self.__class__, datetime))
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__):
2734 self._value == their._value and
2735 self.tag == their.tag and
2736 self._expl == their._expl
2739 def todatetime(self):
2740 """Convert to datetime
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.
2748 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2749 year = value.year % 100
2751 year=(2000 + year) if year < 50 else (1900 + year),
2755 minute=value.minute,
2756 second=value.second,
2760 return pp_console_row(next(self.pps()))
2762 def pps(self, decode_path=()):
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),
2779 class GeneralizedTime(UTCTime):
2780 """``GeneralizedTime`` datetime type
2782 This type is similar to :py:class:`pyderasn.UTCTime`.
2784 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2785 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2787 '20170930220750.000123Z'
2788 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2789 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2792 tag_default = tag_encode(24)
2793 asn1_type_name = "GeneralizedTime"
2795 fmt = "%Y%m%d%H%M%SZ"
2796 fmt_ms = "%Y%m%d%H%M%S.%fZ"
2798 def _value_sanitize(self, value):
2799 if isinstance(value, self.__class__):
2801 if isinstance(value, datetime):
2802 return value.strftime(
2803 self.fmt_ms if value.microsecond > 0 else self.fmt
2805 if isinstance(value, binary_type):
2806 value_decoded = value.decode("ascii")
2807 if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2809 datetime.strptime(value_decoded, self.fmt)
2812 "invalid GeneralizedTime (without ms) format",
2815 elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2817 datetime.strptime(value_decoded, self.fmt_ms)
2820 "invalid GeneralizedTime (with ms) format",
2825 "invalid GeneralizedTime length",
2826 klass=self.__class__,
2828 raise InvalidValueType((self.__class__, datetime))
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)
2837 class GraphicString(CommonString):
2839 tag_default = tag_encode(25)
2840 encoding = "iso-8859-1"
2841 asn1_type_name = "GraphicString"
2844 class VisibleString(CommonString):
2846 tag_default = tag_encode(26)
2848 asn1_type_name = "VisibleString"
2851 class ISO646String(VisibleString):
2853 asn1_type_name = "ISO646String"
2856 class GeneralString(CommonString):
2858 tag_default = tag_encode(27)
2859 encoding = "iso-8859-1"
2860 asn1_type_name = "GeneralString"
2863 class UniversalString(CommonString):
2865 tag_default = tag_encode(28)
2866 encoding = "utf-32-be"
2867 asn1_type_name = "UniversalString"
2870 class BMPString(CommonString):
2872 tag_default = tag_encode(30)
2873 encoding = "utf-16-be"
2874 asn1_type_name = "BMPString"
2878 """``CHOICE`` special type
2882 class GeneralName(Choice):
2884 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2885 ('dNSName', IA5String(impl=tag_ctxp(2))),
2888 >>> gn = GeneralName()
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"]
2897 [2] IA5String IA5 bar.baz
2900 >>> gn.value == gn["dNSName"]
2903 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2905 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2906 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2908 __slots__ = ("specs",)
2910 asn1_type_name = "CHOICE"
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
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)
2934 schema = getattr(self, "schema", ())
2935 if len(schema) == 0:
2936 raise ValueError("schema must be specified")
2938 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
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
2950 self._value = default_obj.copy()._value
2952 def _value_sanitize(self, value):
2953 if isinstance(value, self.__class__):
2955 if isinstance(value, tuple) and len(value) == 2:
2957 spec = self.specs.get(choice)
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))
2967 return self._value is not None and self._value[1].ready
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
2978 if value is not None:
2979 obj._value = (value[0], value[1].copy())
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__):
2988 self.specs == their.specs and
2989 self._value == their._value
2999 return self.__class__(
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,
3009 self._assert_ready()
3010 return self._value[0]
3014 self._assert_ready()
3015 return self._value[1]
3017 def __getitem__(self, key):
3018 if key not in self.specs:
3019 raise ObjUnknown(key)
3020 if self._value is None:
3022 choice, value = self._value
3027 def __setitem__(self, key, value):
3028 spec = self.specs.get(key)
3030 raise ObjUnknown(key)
3031 if not isinstance(value, spec.__class__):
3032 raise InvalidValueType((spec.__class__,))
3033 self._value = (key, spec(value))
3041 return self._value[1].decoded if self.ready else False
3044 self._assert_ready()
3045 return self._value[1].encode()
3047 def _decode(self, tlv, offset=0, decode_path=()):
3048 for choice, spec in self.specs.items():
3050 value, tail = spec.decode(
3054 decode_path=decode_path + (choice,),
3058 obj = self.__class__(
3061 default=self.default,
3062 optional=self.optional,
3063 _decoded=(offset, 0, value.tlvlen),
3065 obj._value = (choice, value)
3068 klass=self.__class__,
3069 decode_path=decode_path,
3074 value = pp_console_row(next(self.pps()))
3076 value = "%s[%r]" % (value, self.value)
3079 def pps(self, decode_path=()):
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),
3095 yield self.value.pps(decode_path=decode_path + (self.choice,))
3098 class PrimitiveTypes(Choice):
3099 """Predefined ``CHOICE`` for all generic primitive types
3101 It could be useful for general decoding of some unspecified values:
3103 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3104 OCTET STRING 3 bytes 666f6f
3105 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3109 schema = tuple((klass.__name__, klass()) for klass in (
3134 """``ANY`` special type
3136 >>> Any(Integer(-123))
3138 >>> a = Any(OctetString(b"hello world").encode())
3139 ANY 040b68656c6c6f20776f726c64
3140 >>> hexenc(bytes(a))
3141 b'0x040x0bhello world'
3144 tag_default = tag_encode(0)
3145 asn1_type_name = "ANY"
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
3159 :param bytes expl: override default tag with ``EXPLICIT`` one
3160 :param bool optional: is object ``OPTIONAL`` in sequence
3162 super(Any, self).__init__(None, expl, None, optional, _decoded)
3163 self._value = None if value is None else self._value_sanitize(value)
3165 def _value_sanitize(self, value):
3166 if isinstance(value, self.__class__):
3168 if isinstance(value, Obj):
3169 return value.encode()
3170 if isinstance(value, binary_type):
3172 raise InvalidValueType((self.__class__, Obj, binary_type))
3176 return self._value is not None
3179 obj = self.__class__()
3180 obj._value = self._value
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
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
3202 return self.__class__(
3204 expl=self._expl if expl is None else expl,
3205 optional=self.optional if optional is None else optional,
3208 def __bytes__(self):
3209 self._assert_ready()
3217 self._assert_ready()
3220 def _decode(self, tlv, offset=0, decode_path=()):
3222 t, tlen, lv = tag_strip(tlv)
3223 l, llen, v = len_decode(lv)
3224 except DecodeError as err:
3225 raise err.__class__(
3227 klass=self.__class__,
3228 decode_path=decode_path,
3232 raise NotEnoughData(
3233 "encoded length is longer than data",
3234 klass=self.__class__,
3235 decode_path=decode_path,
3238 tlvlen = tlen + llen + l
3239 v, tail = tlv[:tlvlen], v[l:]
3240 obj = self.__class__(
3243 optional=self.optional,
3244 _decoded=(offset, 0, tlvlen),
3250 return pp_console_row(next(self.pps()))
3252 def pps(self, decode_path=()):
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),
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,
3273 ########################################################################
3274 # ASN.1 constructed types
3275 ########################################################################
3277 class Sequence(Obj):
3278 __slots__ = ("specs",)
3279 tag_default = tag_encode(form=TagFormConstructed, num=16)
3280 asn1_type_name = "SEQUENCE"
3292 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3294 schema = getattr(self, "schema", ())
3296 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
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
3308 self._value = default_obj.copy()._value
3310 def _value_sanitize(self, value):
3311 if not issubclass(value.__class__, Sequence):
3312 raise InvalidValueType((Sequence,))
3317 for name, spec in self.specs.items():
3318 value = self._value.get(name)
3329 obj = self.__class__(schema=self.specs)
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()}
3340 def __eq__(self, their):
3341 if not isinstance(their, self.__class__):
3344 self.specs == their.specs and
3345 self.tag == their.tag and
3346 self._expl == their._expl and
3347 self._value == their._value
3358 return self.__class__(
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,
3367 def __contains__(self, key):
3368 return key in self._value
3370 def __setitem__(self, key, value):
3371 spec = self.specs.get(key)
3373 raise ObjUnknown(key)
3375 self._value.pop(key, None)
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)
3383 self._value[key] = value
3385 def __getitem__(self, key):
3386 value = self._value.get(key)
3387 if value is not None:
3389 spec = self.specs.get(key)
3391 raise ObjUnknown(key)
3392 if spec.default is not None:
3396 def _encoded_values(self):
3398 for name, spec in self.specs.items():
3399 value = self._value.get(name)
3403 raise ObjNotReady(name)
3404 raws.append(value.encode())
3408 v = b"".join(self._encoded_values())
3409 return b"".join((self.tag, len_encode(len(v)), v))
3411 def _decode(self, tlv, offset=0, decode_path=()):
3413 t, tlen, lv = tag_strip(tlv)
3414 except DecodeError as err:
3415 raise err.__class__(
3417 klass=self.__class__,
3418 decode_path=decode_path,
3423 klass=self.__class__,
3424 decode_path=decode_path,
3428 l, llen, v = len_decode(lv)
3429 except DecodeError as err:
3430 raise err.__class__(
3432 klass=self.__class__,
3433 decode_path=decode_path,
3437 raise NotEnoughData(
3438 "encoded length is longer than data",
3439 klass=self.__class__,
3440 decode_path=decode_path,
3443 v, tail = v[:l], v[l:]
3444 sub_offset = offset + tlen + llen
3446 for name, spec in self.specs.items():
3447 if len(v) == 0 and spec.optional:
3450 value, v_tail = spec.decode(
3454 decode_path=decode_path + (name,),
3460 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
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
3466 values[name] = value
3470 klass=self.__class__,
3471 decode_path=decode_path,
3474 obj = self.__class__(
3478 default=self.default,
3479 optional=self.optional,
3480 _decoded=(offset, llen, l),
3486 value = pp_console_row(next(self.pps()))
3488 for name in self.specs:
3489 _value = self._value.get(name)
3492 cols.append(repr(_value))
3493 return "%s[%s]" % (value, ", ".join(cols))
3495 def pps(self, decode_path=()):
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),
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,
3513 for name in self.specs:
3514 value = self._value.get(name)
3517 yield value.pps(decode_path=decode_path + (name,))
3520 class Set(Sequence):
3522 tag_default = tag_encode(form=TagFormConstructed, num=17)
3523 asn1_type_name = "SET"
3526 raws = self._encoded_values()
3529 return b"".join((self.tag, len_encode(len(v)), v))
3531 def _decode(self, tlv, offset=0, decode_path=()):
3533 t, tlen, lv = tag_strip(tlv)
3534 except DecodeError as err:
3535 raise err.__class__(
3537 klass=self.__class__,
3538 decode_path=decode_path,
3543 klass=self.__class__,
3544 decode_path=decode_path,
3548 l, llen, v = len_decode(lv)
3549 except DecodeError as err:
3550 raise err.__class__(
3552 klass=self.__class__,
3553 decode_path=decode_path,
3557 raise NotEnoughData(
3558 "encoded length is longer than data",
3559 klass=self.__class__,
3562 v, tail = v[:l], v[l:]
3563 sub_offset = offset + tlen + llen
3565 specs_items = self.specs.items
3567 for name, spec in specs_items():
3569 value, v_tail = spec.decode(
3573 decode_path=decode_path + (name,),
3578 value.expl_tlvlen if value.expled else value.tlvlen
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
3587 klass=self.__class__,
3588 decode_path=decode_path,
3591 obj = self.__class__(
3595 default=self.default,
3596 optional=self.optional,
3597 _decoded=(offset, llen, l),
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"
3619 super(SequenceOf, self).__init__(
3627 schema = getattr(self, "schema", None)
3629 raise ValueError("schema must be specified")
3632 self._bound_min, self._bound_max = getattr(
3638 self._bound_min, self._bound_max = bounds
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__(
3649 default_obj._value = default_value
3650 self.default = default_obj
3652 self._value = default_obj.copy()._value
3654 def _value_sanitize(self, value):
3655 if issubclass(value.__class__, SequenceOf):
3656 value = value._value
3657 elif hasattr(value, "__iter__"):
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)
3664 if not isinstance(v, self.spec.__class__):
3665 raise InvalidValueType((self.spec.__class__,))
3670 return all(v.ready for v in self._value)
3673 obj = self.__class__(schema=self.spec)
3674 obj._bound_min = self._bound_min
3675 obj._bound_max = self._bound_max
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]
3686 def __eq__(self, their):
3687 if isinstance(their, self.__class__):
3689 self.spec == their.spec and
3690 self.tag == their.tag and
3691 self._expl == their._expl and
3692 self._value == their._value
3694 if hasattr(their, "__iter__"):
3695 return self._value == list(their)
3707 return self.__class__(
3711 (self._bound_min, self._bound_max)
3712 if bounds is None else bounds
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,
3720 def __contains__(self, key):
3721 return key in self._value
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:
3729 len(self._value) + 1,
3732 self._value.append(value)
3735 self._assert_ready()
3736 return iter(self._value)
3739 self._assert_ready()
3740 return len(self._value)
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)
3747 def __getitem__(self, key):
3748 return self._value[key]
3750 def _encoded_values(self):
3751 return [v.encode() for v in self._value]
3754 v = b"".join(self._encoded_values())
3755 return b"".join((self.tag, len_encode(len(v)), v))
3757 def _decode(self, tlv, offset=0, decode_path=()):
3759 t, tlen, lv = tag_strip(tlv)
3760 except DecodeError as err:
3761 raise err.__class__(
3763 klass=self.__class__,
3764 decode_path=decode_path,
3769 klass=self.__class__,
3770 decode_path=decode_path,
3774 l, llen, v = len_decode(lv)
3775 except DecodeError as err:
3776 raise err.__class__(
3778 klass=self.__class__,
3779 decode_path=decode_path,
3783 raise NotEnoughData(
3784 "encoded length is longer than data",
3785 klass=self.__class__,
3786 decode_path=decode_path,
3789 v, tail = v[:l], v[l:]
3790 sub_offset = offset + tlen + llen
3794 value, v_tail = spec.decode(
3798 decode_path=decode_path + (str(len(_value)),),
3800 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3802 _value.append(value)
3803 obj = self.__class__(
3806 bounds=(self._bound_min, self._bound_max),
3809 default=self.default,
3810 optional=self.optional,
3811 _decoded=(offset, llen, l),
3817 pp_console_row(next(self.pps())),
3818 ", ".join(repr(v) for v in self._value),
3821 def pps(self, decode_path=()):
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),
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,
3839 for i, value in enumerate(self._value):
3840 yield value.pps(decode_path=decode_path + (str(i),))
3843 class SetOf(SequenceOf):
3845 tag_default = tag_encode(form=TagFormConstructed, num=17)
3846 asn1_type_name = "SET OF"
3849 raws = self._encoded_values()
3852 return b"".join((self.tag, len_encode(len(v)), v))
3855 def obj_by_path(pypath): # pragma: no cover
3856 """Import object specified as string Python path
3858 Modules must be separated from classes/functions with ``:``.
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'>
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)
3873 def main(): # pragma: no cover
3875 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
3876 parser.add_argument(
3878 help="Python path to dictionary with OIDs",
3880 parser.add_argument(
3882 help="Python path to schema definition to use",
3884 parser.add_argument(
3886 type=argparse.FileType("rb"),
3887 help="Python path to schema definition to use",
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 {}
3894 schema = obj_by_path(args.schema)
3895 from functools import partial
3896 pprinter = partial(pprint, big_blobs=True)
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)
3903 choice.specs["SequenceOf%d" % i] = SequenceOf(
3907 choice.specs["Any"] = Any()
3909 # Class name equals to type name, to omit it from output
3910 class SEQUENCEOF(SequenceOf):
3913 schema = SEQUENCEOF()
3915 def pprint_any(obj, oids=None):
3916 def _pprint_pps(pps):
3918 if hasattr(pp, "_fields"):
3919 if pp.asn1_type_name == Choice.asn1_type_name:
3921 pp_kwargs = pp._asdict()
3922 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
3923 pp = _pp(**pp_kwargs)
3924 yield pp_console_row(
3930 for row in pp_console_blob(pp):
3933 for row in _pprint_pps(pp):
3935 return "\n".join(_pprint_pps(obj.pps()))
3936 pprinter = pprint_any
3937 obj, tail = schema().decode(der)
3938 print(pprinter(obj, oids=oids))
3940 print("\nTrailing data: %s" % hexenc(tail))
3943 if __name__ == "__main__":