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
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 binary_type
322 from six import byte2int
323 from six import indexbytes
324 from six import int2byte
325 from six import integer_types
326 from six import iterbytes
328 from six import string_types
329 from six import text_type
330 from six.moves import xrange as six_xrange
371 "TagClassApplication",
375 "TagFormConstructed",
386 TagClassUniversal = 0
387 TagClassApplication = 1 << 6
388 TagClassContext = 1 << 7
389 TagClassPrivate = 1 << 6 | 1 << 7
391 TagFormConstructed = 1 << 5
394 TagClassApplication: "APPLICATION ",
395 TagClassPrivate: "PRIVATE ",
396 TagClassUniversal: "UNIV ",
400 ########################################################################
402 ########################################################################
404 class DecodeError(Exception):
405 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
407 :param str msg: reason of decode failing
408 :param klass: optional exact DecodeError inherited class (like
409 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
410 :py:exc:`InvalidLength`)
411 :param decode_path: tuple of strings. It contains human
412 readable names of the fields through which
413 decoding process has passed
414 :param int offset: binary offset where failure happened
416 super(DecodeError, self).__init__()
419 self.decode_path = decode_path
425 "" if self.klass is None else self.klass.__name__,
427 ("(%s)" % ".".join(self.decode_path))
428 if len(self.decode_path) > 0 else ""
430 ("(at %d)" % self.offset) if self.offset > 0 else "",
436 return "%s(%s)" % (self.__class__.__name__, self)
439 class NotEnoughData(DecodeError):
443 class TagMismatch(DecodeError):
447 class InvalidLength(DecodeError):
451 class InvalidOID(DecodeError):
455 class ObjUnknown(ValueError):
456 def __init__(self, name):
457 super(ObjUnknown, self).__init__()
461 return "object is unknown: %s" % self.name
464 return "%s(%s)" % (self.__class__.__name__, self)
467 class ObjNotReady(ValueError):
468 def __init__(self, name):
469 super(ObjNotReady, self).__init__()
473 return "object is not ready: %s" % self.name
476 return "%s(%s)" % (self.__class__.__name__, self)
479 class InvalidValueType(ValueError):
480 def __init__(self, expected_types):
481 super(InvalidValueType, self).__init__()
482 self.expected_types = expected_types
485 return "invalid value type, expected: %s" % ", ".join(
486 [repr(t) for t in self.expected_types]
490 return "%s(%s)" % (self.__class__.__name__, self)
493 class BoundsError(ValueError):
494 def __init__(self, bound_min, value, bound_max):
495 super(BoundsError, self).__init__()
496 self.bound_min = bound_min
498 self.bound_max = bound_max
501 return "unsatisfied bounds: %s <= %s <= %s" % (
508 return "%s(%s)" % (self.__class__.__name__, self)
511 ########################################################################
513 ########################################################################
515 _hexdecoder = getdecoder("hex")
516 _hexencoder = getencoder("hex")
520 """Binary data to hexadecimal string convert
522 return _hexdecoder(data)[0]
526 """Hexadecimal string to binary data convert
528 return _hexencoder(data)[0].decode("ascii")
531 def int_bytes_len(num, byte_len=8):
534 return int(ceil(float(num.bit_length()) / byte_len))
537 def zero_ended_encode(num):
538 octets = bytearray(int_bytes_len(num, 7))
540 octets[i] = num & 0x7F
544 octets[i] = 0x80 | (num & 0x7F)
550 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
551 """Encode tag to binary form
553 :param int num: tag's number
554 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
555 :py:data:`pyderasn.TagClassContext`,
556 :py:data:`pyderasn.TagClassApplication`,
557 :py:data:`pyderasn.TagClassPrivate`)
558 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
559 :py:data:`pyderasn.TagFormConstructed`)
563 return int2byte(klass | form | num)
564 # [XX|X|11111][1.......][1.......] ... [0.......]
565 return int2byte(klass | form | 31) + zero_ended_encode(num)
569 """Decode tag from binary form
573 No validation is performed, assuming that it has already passed.
575 It returns tuple with three integers, as
576 :py:func:`pyderasn.tag_encode` accepts.
578 first_octet = byte2int(tag)
579 klass = first_octet & 0xC0
580 form = first_octet & 0x20
581 if first_octet & 0x1F < 0x1F:
582 return (klass, form, first_octet & 0x1F)
584 for octet in iterbytes(tag[1:]):
587 return (klass, form, num)
591 """Create CONTEXT PRIMITIVE tag
593 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
597 """Create CONTEXT CONSTRUCTED tag
599 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
603 """Take off tag from the data
605 :returns: (encoded tag, tag length, remaining data)
608 raise NotEnoughData("no data at all")
609 if byte2int(data) & 0x1F < 31:
610 return data[:1], 1, data[1:]
615 raise DecodeError("unfinished tag")
616 if indexbytes(data, i) & 0x80 == 0:
619 return data[:i], i, data[i:]
625 octets = bytearray(int_bytes_len(l) + 1)
626 octets[0] = 0x80 | (len(octets) - 1)
627 for i in six_xrange(len(octets) - 1, 0, -1):
633 def len_decode(data):
635 raise NotEnoughData("no data at all")
636 first_octet = byte2int(data)
637 if first_octet & 0x80 == 0:
638 return first_octet, 1, data[1:]
639 octets_num = first_octet & 0x7F
640 if octets_num + 1 > len(data):
641 raise NotEnoughData("encoded length is longer than data")
643 raise DecodeError("long form instead of short one")
644 if byte2int(data[1:]) == 0:
645 raise DecodeError("leading zeros")
647 for v in iterbytes(data[1:1 + octets_num]):
650 raise DecodeError("long form instead of short one")
651 return l, 1 + octets_num, data[1 + octets_num:]
654 ########################################################################
656 ########################################################################
659 """Common ASN.1 object class
661 All ASN.1 types are inherited from it. It has metaclass that
662 automatically adds ``__slots__`` to all inherited classes.
684 self.tag = getattr(self, "impl", self.tag_default)
687 self._expl = getattr(self, "expl", None) if expl is None else expl
688 if self.tag != self.tag_default and self._expl is not None:
690 "implicit and explicit tags can not be set simultaneously"
692 if default is not None:
694 self.optional = optional
695 self.offset, self.llen, self.vlen = _decoded
699 def ready(self): # pragma: no cover
700 """Is object ready to be encoded?
702 raise NotImplementedError()
704 def _assert_ready(self):
706 raise ObjNotReady(self.__class__.__name__)
710 """Is object decoded?
714 def copy(self): # pragma: no cover
715 """Make a copy of object, safe to be mutated
717 raise NotImplementedError()
725 return self.tlen + self.llen + self.vlen
727 def __str__(self): # pragma: no cover
728 return self.__bytes__() if PY2 else self.__unicode__()
730 def _encode(self): # pragma: no cover
731 raise NotImplementedError()
733 def _decode(self, tlv, offset=0, decode_path=()): # pragma: no cover
734 raise NotImplementedError()
738 if self._expl is None:
740 return b"".join((self._expl, len_encode(len(raw)), raw))
742 def decode(self, data, offset=0, leavemm=False, decode_path=()):
745 :param data: either binary or memoryview
746 :param int offset: initial data's offset
747 :param bool leavemm: do we need to leave memoryview of remaining
748 data as is, or convert it to bytes otherwise
749 :returns: (Obj, remaining data)
751 tlv = memoryview(data)
752 if self._expl is None:
753 obj, tail = self._decode(
756 decode_path=decode_path,
760 t, tlen, lv = tag_strip(tlv)
761 except DecodeError as err:
764 klass=self.__class__,
765 decode_path=decode_path,
770 klass=self.__class__,
771 decode_path=decode_path,
775 l, llen, v = len_decode(lv)
776 except DecodeError as err:
779 klass=self.__class__,
780 decode_path=decode_path,
785 "encoded length is longer than data",
786 klass=self.__class__,
787 decode_path=decode_path,
790 obj, tail = self._decode(
792 offset=offset + tlen + llen,
795 return obj, (tail if leavemm else tail.tobytes())
799 return self._expl is not None
807 return len(self._expl)
811 return len(len_encode(self.tlvlen))
814 def expl_offset(self):
815 return self.offset - self.expl_tlen - self.expl_llen
822 def expl_tlvlen(self):
823 return self.expl_tlen + self.expl_llen + self.expl_vlen
826 ########################################################################
828 ########################################################################
830 PP = namedtuple("PP", (
852 asn1_type_name="unknown",
891 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
894 cols.append("%5d%s [%d,%d,%4d]" % (
897 " " if pp.expl_offset is None else
898 ("-%d" % (pp.offset - pp.expl_offset))
904 if len(pp.decode_path) > 0:
905 cols.append(" ." * (len(pp.decode_path)))
906 cols.append("%s:" % pp.decode_path[-1])
907 if pp.expl is not None:
908 klass, _, num = pp.expl
909 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
910 if pp.impl is not None:
911 klass, _, num = pp.impl
912 cols.append("[%s%d]" % (TagClassReprs[klass], num))
913 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
914 cols.append(pp.obj_name)
915 cols.append(pp.asn1_type_name)
916 if pp.value is not None:
920 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
923 value = "%s (%s)" % (oids[value], pp.value)
926 if isinstance(pp.blob, binary_type):
927 cols.append(hexenc(pp.blob))
928 elif isinstance(pp.blob, tuple):
929 cols.append(", ".join(pp.blob))
931 cols.append("OPTIONAL")
933 cols.append("DEFAULT")
934 return " ".join(cols)
937 def pp_console_blob(pp):
938 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
939 if len(pp.decode_path) > 0:
940 cols.append(" ." * (len(pp.decode_path) + 1))
941 if isinstance(pp.blob, binary_type):
942 blob = hexenc(pp.blob).upper()
943 for i in range(0, len(blob), 32):
944 chunk = blob[i:i + 32]
945 yield " ".join(cols + [":".join(
946 chunk[j:j + 2] for j in range(0, len(chunk), 2)
948 elif isinstance(pp.blob, tuple):
949 yield " ".join(cols + [", ".join(pp.blob)])
952 def pprint(obj, oids=None, big_blobs=False):
953 """Pretty print object
955 :param Obj obj: object you want to pretty print
956 :param oids: ``OID <-> humand readable string`` dictionary. When OID
957 from it is met, then its humand readable form is printed
958 :param big_blobs: if large binary objects are met (like OctetString
959 values), do we need to print them too, on separate
962 def _pprint_pps(pps):
964 if hasattr(pp, "_fields"):
966 yield pp_console_row(
972 for row in pp_console_blob(pp):
975 yield pp_console_row(pp, oids=oids, with_offsets=True)
977 for row in _pprint_pps(pp):
979 return "\n".join(_pprint_pps(obj.pps()))
982 ########################################################################
983 # ASN.1 primitive types
984 ########################################################################
987 """``BOOLEAN`` boolean type
989 >>> b = Boolean(True)
991 >>> b == Boolean(True)
997 tag_default = tag_encode(1)
998 asn1_type_name = "BOOLEAN"
1010 :param value: set the value. Either boolean type, or
1011 :py:class:`pyderasn.Boolean` object
1012 :param bytes impl: override default tag with ``IMPLICIT`` one
1013 :param bytes expl: override default tag with ``EXPLICIT`` one
1014 :param default: set default value. Type same as in ``value``
1015 :param bool optional: is object ``OPTIONAL`` in sequence
1017 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1018 self._value = None if value is None else self._value_sanitize(value)
1019 if default is not None:
1020 default = self._value_sanitize(default)
1021 self.default = self.__class__(
1027 self._value = default
1029 def _value_sanitize(self, value):
1030 if issubclass(value.__class__, Boolean):
1032 if isinstance(value, bool):
1034 raise InvalidValueType((self.__class__, bool))
1038 return self._value is not None
1041 obj = self.__class__()
1042 obj._value = self._value
1044 obj._expl = self._expl
1045 obj.default = self.default
1046 obj.optional = self.optional
1047 obj.offset = self.offset
1048 obj.llen = self.llen
1049 obj.vlen = self.vlen
1052 def __nonzero__(self):
1053 self._assert_ready()
1057 self._assert_ready()
1060 def __eq__(self, their):
1061 if isinstance(their, bool):
1062 return self._value == their
1063 if not issubclass(their.__class__, Boolean):
1066 self._value == their._value and
1067 self.tag == their.tag and
1068 self._expl == their._expl
1079 return self.__class__(
1081 impl=self.tag if impl is None else impl,
1082 expl=self._expl if expl is None else expl,
1083 default=self.default if default is None else default,
1084 optional=self.optional if optional is None else optional,
1088 self._assert_ready()
1092 (b"\xFF" if self._value else b"\x00"),
1095 def _decode(self, tlv, offset=0, decode_path=()):
1097 t, _, lv = tag_strip(tlv)
1098 except DecodeError as err:
1099 raise err.__class__(
1101 klass=self.__class__,
1102 decode_path=decode_path,
1107 klass=self.__class__,
1108 decode_path=decode_path,
1112 l, _, v = len_decode(lv)
1113 except DecodeError as err:
1114 raise err.__class__(
1116 klass=self.__class__,
1117 decode_path=decode_path,
1121 raise InvalidLength(
1122 "Boolean's length must be equal to 1",
1123 klass=self.__class__,
1124 decode_path=decode_path,
1128 raise NotEnoughData(
1129 "encoded length is longer than data",
1130 klass=self.__class__,
1131 decode_path=decode_path,
1134 first_octet = byte2int(v)
1135 if first_octet == 0:
1137 elif first_octet == 0xFF:
1141 "unacceptable Boolean value",
1142 klass=self.__class__,
1143 decode_path=decode_path,
1146 obj = self.__class__(
1150 default=self.default,
1151 optional=self.optional,
1152 _decoded=(offset, 1, 1),
1157 return pp_console_row(next(self.pps()))
1159 def pps(self, decode_path=()):
1161 asn1_type_name=self.asn1_type_name,
1162 obj_name=self.__class__.__name__,
1163 decode_path=decode_path,
1164 value=str(self._value) if self.ready else None,
1165 optional=self.optional,
1166 default=self == self.default,
1167 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1168 expl=None if self._expl is None else tag_decode(self._expl),
1173 expl_offset=self.expl_offset if self.expled else None,
1174 expl_tlen=self.expl_tlen if self.expled else None,
1175 expl_llen=self.expl_llen if self.expled else None,
1176 expl_vlen=self.expl_vlen if self.expled else None,
1181 """``INTEGER`` integer type
1183 >>> b = Integer(-123)
1185 >>> b == Integer(-123)
1190 >>> Integer(2, bounds=(1, 3))
1192 >>> Integer(5, bounds=(1, 3))
1193 Traceback (most recent call last):
1194 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1198 class Version(Integer):
1205 >>> v = Version("v1")
1212 {'v3': 2, 'v1': 0, 'v2': 1}
1214 __slots__ = ("specs", "_bound_min", "_bound_max")
1215 tag_default = tag_encode(2)
1216 asn1_type_name = "INTEGER"
1230 :param value: set the value. Either integer type, named value
1231 (if ``schema`` is specified in the class), or
1232 :py:class:`pyderasn.Integer` object
1233 :param bounds: set ``(MIN, MAX)`` value constraint.
1234 (-inf, +inf) by default
1235 :param bytes impl: override default tag with ``IMPLICIT`` one
1236 :param bytes expl: override default tag with ``EXPLICIT`` one
1237 :param default: set default value. Type same as in ``value``
1238 :param bool optional: is object ``OPTIONAL`` in sequence
1240 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1242 specs = getattr(self, "schema", {}) if _specs is None else _specs
1243 self.specs = specs if isinstance(specs, dict) else dict(specs)
1245 self._bound_min, self._bound_max = getattr(
1248 (float("-inf"), float("+inf")),
1251 self._bound_min, self._bound_max = bounds
1252 if value is not None:
1253 self._value = self._value_sanitize(value)
1254 if default is not None:
1255 default = self._value_sanitize(default)
1256 self.default = self.__class__(
1262 if self._value is None:
1263 self._value = default
1265 def _value_sanitize(self, value):
1266 if issubclass(value.__class__, Integer):
1267 value = value._value
1268 elif isinstance(value, integer_types):
1270 elif isinstance(value, str):
1271 value = self.specs.get(value)
1273 raise ObjUnknown("integer value: %s" % value)
1275 raise InvalidValueType((self.__class__, int, str))
1276 if not self._bound_min <= value <= self._bound_max:
1277 raise BoundsError(self._bound_min, value, self._bound_max)
1282 return self._value is not None
1285 obj = self.__class__(_specs=self.specs)
1286 obj._value = self._value
1287 obj._bound_min = self._bound_min
1288 obj._bound_max = self._bound_max
1290 obj._expl = self._expl
1291 obj.default = self.default
1292 obj.optional = self.optional
1293 obj.offset = self.offset
1294 obj.llen = self.llen
1295 obj.vlen = self.vlen
1299 self._assert_ready()
1300 return int(self._value)
1303 self._assert_ready()
1306 bytes(self._expl or b"") +
1307 str(self._value).encode("ascii"),
1310 def __eq__(self, their):
1311 if isinstance(their, integer_types):
1312 return self._value == their
1313 if not issubclass(their.__class__, Integer):
1316 self._value == their._value and
1317 self.tag == their.tag and
1318 self._expl == their._expl
1321 def __lt__(self, their):
1322 return self._value < their
1324 def __gt__(self, their):
1325 return self._value > their
1329 for name, value in self.specs.items():
1330 if value == self._value:
1342 return self.__class__(
1345 (self._bound_min, self._bound_max)
1346 if bounds is None else bounds
1348 impl=self.tag if impl is None else impl,
1349 expl=self._expl if expl is None else expl,
1350 default=self.default if default is None else default,
1351 optional=self.optional if optional is None else optional,
1356 self._assert_ready()
1360 octets = bytearray([0])
1364 octets = bytearray()
1366 octets.append((value & 0xFF) ^ 0xFF)
1368 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1371 octets = bytearray()
1373 octets.append(value & 0xFF)
1375 if octets[-1] & 0x80 > 0:
1378 octets = bytes(octets)
1380 bytes_len = ceil(value.bit_length() / 8) or 1
1383 octets = value.to_bytes(
1388 except OverflowError:
1392 return b"".join((self.tag, len_encode(len(octets)), octets))
1394 def _decode(self, tlv, offset=0, decode_path=()):
1396 t, _, lv = tag_strip(tlv)
1397 except DecodeError as err:
1398 raise err.__class__(
1400 klass=self.__class__,
1401 decode_path=decode_path,
1406 klass=self.__class__,
1407 decode_path=decode_path,
1411 l, llen, v = len_decode(lv)
1412 except DecodeError as err:
1413 raise err.__class__(
1415 klass=self.__class__,
1416 decode_path=decode_path,
1420 raise NotEnoughData(
1421 "encoded length is longer than data",
1422 klass=self.__class__,
1423 decode_path=decode_path,
1427 raise NotEnoughData(
1429 klass=self.__class__,
1430 decode_path=decode_path,
1433 v, tail = v[:l], v[l:]
1434 first_octet = byte2int(v)
1436 second_octet = byte2int(v[1:])
1438 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1439 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1442 "non normalized integer",
1443 klass=self.__class__,
1444 decode_path=decode_path,
1449 if first_octet & 0x80 > 0:
1450 octets = bytearray()
1451 for octet in bytearray(v):
1452 octets.append(octet ^ 0xFF)
1453 for octet in octets:
1454 value = (value << 8) | octet
1458 for octet in bytearray(v):
1459 value = (value << 8) | octet
1461 value = int.from_bytes(v, byteorder="big", signed=True)
1463 obj = self.__class__(
1465 bounds=(self._bound_min, self._bound_max),
1468 default=self.default,
1469 optional=self.optional,
1471 _decoded=(offset, llen, l),
1473 except BoundsError as err:
1476 klass=self.__class__,
1477 decode_path=decode_path,
1483 return pp_console_row(next(self.pps()))
1485 def pps(self, decode_path=()):
1487 asn1_type_name=self.asn1_type_name,
1488 obj_name=self.__class__.__name__,
1489 decode_path=decode_path,
1490 value=(self.named or str(self._value)) if self.ready else None,
1491 optional=self.optional,
1492 default=self == self.default,
1493 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1494 expl=None if self._expl is None else tag_decode(self._expl),
1499 expl_offset=self.expl_offset if self.expled else None,
1500 expl_tlen=self.expl_tlen if self.expled else None,
1501 expl_llen=self.expl_llen if self.expled else None,
1502 expl_vlen=self.expl_vlen if self.expled else None,
1506 class BitString(Obj):
1507 """``BIT STRING`` bit string type
1509 >>> BitString(b"hello world")
1510 BIT STRING 88 bits 68656c6c6f20776f726c64
1513 >>> b == b"hello world"
1518 >>> b = BitString("'010110000000'B")
1519 BIT STRING 12 bits 5800
1522 >>> b[0], b[1], b[2], b[3]
1523 (False, True, False, True)
1527 [False, True, False, True, True, False, False, False, False, False, False, False]
1531 class KeyUsage(BitString):
1533 ('digitalSignature', 0),
1534 ('nonRepudiation', 1),
1535 ('keyEncipherment', 2),
1538 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1539 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1541 ['nonRepudiation', 'keyEncipherment']
1543 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1545 __slots__ = ("specs",)
1546 tag_default = tag_encode(3)
1547 asn1_type_name = "BIT STRING"
1560 :param value: set the value. Either binary type, tuple of named
1561 values (if ``schema`` is specified in the class),
1562 string in ``'XXX...'B`` form, or
1563 :py:class:`pyderasn.BitString` object
1564 :param bytes impl: override default tag with ``IMPLICIT`` one
1565 :param bytes expl: override default tag with ``EXPLICIT`` one
1566 :param default: set default value. Type same as in ``value``
1567 :param bool optional: is object ``OPTIONAL`` in sequence
1569 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1570 specs = getattr(self, "schema", {}) if _specs is None else _specs
1571 self.specs = specs if isinstance(specs, dict) else dict(specs)
1572 self._value = None if value is None else self._value_sanitize(value)
1573 if default is not None:
1574 default = self._value_sanitize(default)
1575 self.default = self.__class__(
1581 self._value = default
1583 def _bits2octets(self, bits):
1584 if len(self.specs) > 0:
1585 bits = bits.rstrip("0")
1587 bits += "0" * ((8 - (bit_len % 8)) % 8)
1588 octets = bytearray(len(bits) // 8)
1589 for i in six_xrange(len(octets)):
1590 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1591 return bit_len, bytes(octets)
1593 def _value_sanitize(self, value):
1594 if issubclass(value.__class__, BitString):
1596 if isinstance(value, (string_types, binary_type)):
1598 isinstance(value, string_types) and
1599 value.startswith("'") and
1600 value.endswith("'B")
1603 if not set(value) <= set(("0", "1")):
1604 raise ValueError("B's coding contains unacceptable chars")
1605 return self._bits2octets(value)
1606 elif isinstance(value, binary_type):
1607 return (len(value) * 8, value)
1609 raise InvalidValueType((
1614 if isinstance(value, tuple):
1617 isinstance(value[0], integer_types) and
1618 isinstance(value[1], binary_type)
1623 bit = self.specs.get(name)
1625 raise ObjUnknown("BitString value: %s" % name)
1628 return self._bits2octets("")
1630 return self._bits2octets("".join(
1631 ("1" if bit in bits else "0")
1632 for bit in six_xrange(max(bits) + 1)
1634 raise InvalidValueType((self.__class__, binary_type, string_types))
1638 return self._value is not None
1641 obj = self.__class__(_specs=self.specs)
1642 obj._value = self._value
1644 obj._expl = self._expl
1645 obj.default = self.default
1646 obj.optional = self.optional
1647 obj.offset = self.offset
1648 obj.llen = self.llen
1649 obj.vlen = self.vlen
1653 self._assert_ready()
1654 for i in six_xrange(self._value[0]):
1659 self._assert_ready()
1660 return self._value[0]
1662 def __bytes__(self):
1663 self._assert_ready()
1664 return self._value[1]
1666 def __eq__(self, their):
1667 if isinstance(their, bytes):
1668 return self._value[1] == their
1669 if not issubclass(their.__class__, BitString):
1672 self._value == their._value and
1673 self.tag == their.tag and
1674 self._expl == their._expl
1679 return [name for name, bit in self.specs.items() if self[bit]]
1689 return self.__class__(
1691 impl=self.tag if impl is None else impl,
1692 expl=self._expl if expl is None else expl,
1693 default=self.default if default is None else default,
1694 optional=self.optional if optional is None else optional,
1698 def __getitem__(self, key):
1699 if isinstance(key, int):
1700 bit_len, octets = self._value
1704 byte2int(memoryview(octets)[key // 8:]) >>
1707 if isinstance(key, string_types):
1708 value = self.specs.get(key)
1710 raise ObjUnknown("BitString value: %s" % key)
1712 raise InvalidValueType((int, str))
1715 self._assert_ready()
1716 bit_len, octets = self._value
1719 len_encode(len(octets) + 1),
1720 int2byte((8 - bit_len % 8) % 8),
1724 def _decode(self, tlv, offset=0, decode_path=()):
1726 t, _, lv = tag_strip(tlv)
1727 except DecodeError as err:
1728 raise err.__class__(
1730 klass=self.__class__,
1731 decode_path=decode_path,
1736 klass=self.__class__,
1737 decode_path=decode_path,
1741 l, llen, v = len_decode(lv)
1742 except DecodeError as err:
1743 raise err.__class__(
1745 klass=self.__class__,
1746 decode_path=decode_path,
1750 raise NotEnoughData(
1751 "encoded length is longer than data",
1752 klass=self.__class__,
1753 decode_path=decode_path,
1757 raise NotEnoughData(
1759 klass=self.__class__,
1760 decode_path=decode_path,
1763 pad_size = byte2int(v)
1764 if l == 1 and pad_size != 0:
1766 "invalid empty value",
1767 klass=self.__class__,
1768 decode_path=decode_path,
1774 klass=self.__class__,
1775 decode_path=decode_path,
1778 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1781 klass=self.__class__,
1782 decode_path=decode_path,
1785 v, tail = v[:l], v[l:]
1786 obj = self.__class__(
1787 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1790 default=self.default,
1791 optional=self.optional,
1793 _decoded=(offset, llen, l),
1798 return pp_console_row(next(self.pps()))
1800 def pps(self, decode_path=()):
1804 bit_len, blob = self._value
1805 value = "%d bits" % bit_len
1806 if len(self.specs) > 0:
1807 blob = tuple(self.named)
1809 asn1_type_name=self.asn1_type_name,
1810 obj_name=self.__class__.__name__,
1811 decode_path=decode_path,
1814 optional=self.optional,
1815 default=self == self.default,
1816 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1817 expl=None if self._expl is None else tag_decode(self._expl),
1822 expl_offset=self.expl_offset if self.expled else None,
1823 expl_tlen=self.expl_tlen if self.expled else None,
1824 expl_llen=self.expl_llen if self.expled else None,
1825 expl_vlen=self.expl_vlen if self.expled else None,
1829 class OctetString(Obj):
1830 """``OCTET STRING`` binary string type
1832 >>> s = OctetString(b"hello world")
1833 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1834 >>> s == OctetString(b"hello world")
1839 >>> OctetString(b"hello", bounds=(4, 4))
1840 Traceback (most recent call last):
1841 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1842 >>> OctetString(b"hell", bounds=(4, 4))
1843 OCTET STRING 4 bytes 68656c6c
1845 __slots__ = ("_bound_min", "_bound_max")
1846 tag_default = tag_encode(4)
1847 asn1_type_name = "OCTET STRING"
1860 :param value: set the value. Either binary type, or
1861 :py:class:`pyderasn.OctetString` object
1862 :param bounds: set ``(MIN, MAX)`` value size constraint.
1863 (-inf, +inf) by default
1864 :param bytes impl: override default tag with ``IMPLICIT`` one
1865 :param bytes expl: override default tag with ``EXPLICIT`` one
1866 :param default: set default value. Type same as in ``value``
1867 :param bool optional: is object ``OPTIONAL`` in sequence
1869 super(OctetString, self).__init__(
1878 self._bound_min, self._bound_max = getattr(
1884 self._bound_min, self._bound_max = bounds
1885 if value is not None:
1886 self._value = self._value_sanitize(value)
1887 if default is not None:
1888 default = self._value_sanitize(default)
1889 self.default = self.__class__(
1894 if self._value is None:
1895 self._value = default
1897 def _value_sanitize(self, value):
1898 if issubclass(value.__class__, OctetString):
1899 value = value._value
1900 elif isinstance(value, binary_type):
1903 raise InvalidValueType((self.__class__, bytes))
1904 if not self._bound_min <= len(value) <= self._bound_max:
1905 raise BoundsError(self._bound_min, len(value), self._bound_max)
1910 return self._value is not None
1913 obj = self.__class__()
1914 obj._value = self._value
1915 obj._bound_min = self._bound_min
1916 obj._bound_max = self._bound_max
1918 obj._expl = self._expl
1919 obj.default = self.default
1920 obj.optional = self.optional
1921 obj.offset = self.offset
1922 obj.llen = self.llen
1923 obj.vlen = self.vlen
1926 def __bytes__(self):
1927 self._assert_ready()
1930 def __eq__(self, their):
1931 if isinstance(their, binary_type):
1932 return self._value == their
1933 if not issubclass(their.__class__, OctetString):
1936 self._value == their._value and
1937 self.tag == their.tag and
1938 self._expl == their._expl
1950 return self.__class__(
1953 (self._bound_min, self._bound_max)
1954 if bounds is None else bounds
1956 impl=self.tag if impl is None else impl,
1957 expl=self._expl if expl is None else expl,
1958 default=self.default if default is None else default,
1959 optional=self.optional if optional is None else optional,
1963 self._assert_ready()
1966 len_encode(len(self._value)),
1970 def _decode(self, tlv, offset=0, decode_path=()):
1972 t, _, lv = tag_strip(tlv)
1973 except DecodeError as err:
1974 raise err.__class__(
1976 klass=self.__class__,
1977 decode_path=decode_path,
1982 klass=self.__class__,
1983 decode_path=decode_path,
1987 l, llen, v = len_decode(lv)
1988 except DecodeError as err:
1989 raise err.__class__(
1991 klass=self.__class__,
1992 decode_path=decode_path,
1996 raise NotEnoughData(
1997 "encoded length is longer than data",
1998 klass=self.__class__,
1999 decode_path=decode_path,
2002 v, tail = v[:l], v[l:]
2004 obj = self.__class__(
2006 bounds=(self._bound_min, self._bound_max),
2009 default=self.default,
2010 optional=self.optional,
2011 _decoded=(offset, llen, l),
2013 except BoundsError as err:
2016 klass=self.__class__,
2017 decode_path=decode_path,
2023 return pp_console_row(next(self.pps()))
2025 def pps(self, decode_path=()):
2027 asn1_type_name=self.asn1_type_name,
2028 obj_name=self.__class__.__name__,
2029 decode_path=decode_path,
2030 value=("%d bytes" % len(self._value)) if self.ready else None,
2031 blob=self._value if self.ready else None,
2032 optional=self.optional,
2033 default=self == self.default,
2034 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2035 expl=None if self._expl is None else tag_decode(self._expl),
2040 expl_offset=self.expl_offset if self.expled else None,
2041 expl_tlen=self.expl_tlen if self.expled else None,
2042 expl_llen=self.expl_llen if self.expled else None,
2043 expl_vlen=self.expl_vlen if self.expled else None,
2048 """``NULL`` null object
2056 tag_default = tag_encode(5)
2057 asn1_type_name = "NULL"
2061 value=None, # unused, but Sequence passes it
2068 :param bytes impl: override default tag with ``IMPLICIT`` one
2069 :param bytes expl: override default tag with ``EXPLICIT`` one
2070 :param bool optional: is object ``OPTIONAL`` in sequence
2072 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2080 obj = self.__class__()
2082 obj._expl = self._expl
2083 obj.default = self.default
2084 obj.optional = self.optional
2085 obj.offset = self.offset
2086 obj.llen = self.llen
2087 obj.vlen = self.vlen
2090 def __eq__(self, their):
2091 if not issubclass(their.__class__, Null):
2094 self.tag == their.tag and
2095 self._expl == their._expl
2105 return self.__class__(
2106 impl=self.tag if impl is None else impl,
2107 expl=self._expl if expl is None else expl,
2108 optional=self.optional if optional is None else optional,
2112 return self.tag + len_encode(0)
2114 def _decode(self, tlv, offset=0, decode_path=()):
2116 t, _, lv = tag_strip(tlv)
2117 except DecodeError as err:
2118 raise err.__class__(
2120 klass=self.__class__,
2121 decode_path=decode_path,
2126 klass=self.__class__,
2127 decode_path=decode_path,
2131 l, _, v = len_decode(lv)
2132 except DecodeError as err:
2133 raise err.__class__(
2135 klass=self.__class__,
2136 decode_path=decode_path,
2140 raise InvalidLength(
2141 "Null must have zero length",
2142 klass=self.__class__,
2143 decode_path=decode_path,
2146 obj = self.__class__(
2149 optional=self.optional,
2150 _decoded=(offset, 1, 0),
2155 return pp_console_row(next(self.pps()))
2157 def pps(self, decode_path=()):
2159 asn1_type_name=self.asn1_type_name,
2160 obj_name=self.__class__.__name__,
2161 decode_path=decode_path,
2162 optional=self.optional,
2163 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2164 expl=None if self._expl is None else tag_decode(self._expl),
2169 expl_offset=self.expl_offset if self.expled else None,
2170 expl_tlen=self.expl_tlen if self.expled else None,
2171 expl_llen=self.expl_llen if self.expled else None,
2172 expl_vlen=self.expl_vlen if self.expled else None,
2176 class ObjectIdentifier(Obj):
2177 """``OBJECT IDENTIFIER`` OID type
2179 >>> oid = ObjectIdentifier((1, 2, 3))
2180 OBJECT IDENTIFIER 1.2.3
2181 >>> oid == ObjectIdentifier("1.2.3")
2187 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2188 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2190 >>> str(ObjectIdentifier((3, 1)))
2191 Traceback (most recent call last):
2192 pyderasn.InvalidOID: unacceptable first arc value
2195 tag_default = tag_encode(6)
2196 asn1_type_name = "OBJECT IDENTIFIER"
2208 :param value: set the value. Either tuples of integers,
2209 string of "."-concatenated integers, or
2210 :py:class:`pyderasn.ObjectIdentifier` object
2211 :param bytes impl: override default tag with ``IMPLICIT`` one
2212 :param bytes expl: override default tag with ``EXPLICIT`` one
2213 :param default: set default value. Type same as in ``value``
2214 :param bool optional: is object ``OPTIONAL`` in sequence
2216 super(ObjectIdentifier, self).__init__(
2224 if value is not None:
2225 self._value = self._value_sanitize(value)
2226 if default is not None:
2227 default = self._value_sanitize(default)
2228 self.default = self.__class__(
2233 if self._value is None:
2234 self._value = default
2236 def __add__(self, their):
2237 if isinstance(their, self.__class__):
2238 return self.__class__(self._value + their._value)
2239 if isinstance(their, tuple):
2240 return self.__class__(self._value + their)
2241 raise InvalidValueType((self.__class__, tuple))
2243 def _value_sanitize(self, value):
2244 if issubclass(value.__class__, ObjectIdentifier):
2246 if isinstance(value, string_types):
2248 value = tuple(int(arc) for arc in value.split("."))
2250 raise InvalidOID("unacceptable arcs values")
2251 if isinstance(value, tuple):
2253 raise InvalidOID("less than 2 arcs")
2254 first_arc = value[0]
2255 if first_arc in (0, 1):
2256 if not (0 <= value[1] <= 39):
2257 raise InvalidOID("second arc is too wide")
2258 elif first_arc == 2:
2261 raise InvalidOID("unacceptable first arc value")
2263 raise InvalidValueType((self.__class__, str, tuple))
2267 return self._value is not None
2270 obj = self.__class__()
2271 obj._value = self._value
2273 obj._expl = self._expl
2274 obj.default = self.default
2275 obj.optional = self.optional
2276 obj.offset = self.offset
2277 obj.llen = self.llen
2278 obj.vlen = self.vlen
2282 self._assert_ready()
2283 return iter(self._value)
2286 return ".".join(str(arc) for arc in self._value or ())
2289 self._assert_ready()
2292 bytes(self._expl or b"") +
2293 str(self._value).encode("ascii"),
2296 def __eq__(self, their):
2297 if isinstance(their, tuple):
2298 return self._value == their
2299 if not issubclass(their.__class__, ObjectIdentifier):
2302 self.tag == their.tag and
2303 self._expl == their._expl and
2304 self._value == their._value
2307 def __lt__(self, their):
2308 return self._value < their
2310 def __gt__(self, their):
2311 return self._value > their
2321 return self.__class__(
2323 impl=self.tag if impl is None else impl,
2324 expl=self._expl if expl is None else expl,
2325 default=self.default if default is None else default,
2326 optional=self.optional if optional is None else optional,
2330 self._assert_ready()
2332 first_value = value[1]
2333 first_arc = value[0]
2336 elif first_arc == 1:
2338 elif first_arc == 2:
2340 else: # pragma: no cover
2341 raise RuntimeError("invalid arc is stored")
2342 octets = [zero_ended_encode(first_value)]
2343 for arc in value[2:]:
2344 octets.append(zero_ended_encode(arc))
2345 v = b"".join(octets)
2346 return b"".join((self.tag, len_encode(len(v)), v))
2348 def _decode(self, tlv, offset=0, decode_path=()):
2350 t, _, lv = tag_strip(tlv)
2351 except DecodeError as err:
2352 raise err.__class__(
2354 klass=self.__class__,
2355 decode_path=decode_path,
2360 klass=self.__class__,
2361 decode_path=decode_path,
2365 l, llen, v = len_decode(lv)
2366 except DecodeError as err:
2367 raise err.__class__(
2369 klass=self.__class__,
2370 decode_path=decode_path,
2374 raise NotEnoughData(
2375 "encoded length is longer than data",
2376 klass=self.__class__,
2377 decode_path=decode_path,
2381 raise NotEnoughData(
2383 klass=self.__class__,
2384 decode_path=decode_path,
2387 v, tail = v[:l], v[l:]
2393 octet = indexbytes(v, i)
2394 arc = (arc << 7) | (octet & 0x7F)
2395 if octet & 0x80 == 0:
2403 klass=self.__class__,
2404 decode_path=decode_path,
2408 second_arc = arcs[0]
2409 if 0 <= second_arc <= 39:
2411 elif 40 <= second_arc <= 79:
2417 obj = self.__class__(
2418 value=tuple([first_arc, second_arc] + arcs[1:]),
2421 default=self.default,
2422 optional=self.optional,
2423 _decoded=(offset, llen, l),
2428 return pp_console_row(next(self.pps()))
2430 def pps(self, decode_path=()):
2432 asn1_type_name=self.asn1_type_name,
2433 obj_name=self.__class__.__name__,
2434 decode_path=decode_path,
2435 value=str(self) if self.ready else None,
2436 optional=self.optional,
2437 default=self == self.default,
2438 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2439 expl=None if self._expl is None else tag_decode(self._expl),
2444 expl_offset=self.expl_offset if self.expled else None,
2445 expl_tlen=self.expl_tlen if self.expled else None,
2446 expl_llen=self.expl_llen if self.expled else None,
2447 expl_vlen=self.expl_vlen if self.expled else None,
2451 class Enumerated(Integer):
2452 """``ENUMERATED`` integer type
2454 This type is identical to :py:class:`pyderasn.Integer`, but requires
2455 schema to be specified and does not accept values missing from it.
2458 tag_default = tag_encode(10)
2459 asn1_type_name = "ENUMERATED"
2470 bounds=None, # dummy argument, workability for Integer.decode
2472 super(Enumerated, self).__init__(
2481 if len(self.specs) == 0:
2482 raise ValueError("schema must be specified")
2484 def _value_sanitize(self, value):
2485 if isinstance(value, self.__class__):
2486 value = value._value
2487 elif isinstance(value, integer_types):
2488 if value not in list(self.specs.values()):
2490 "unknown integer value: %s" % value,
2491 klass=self.__class__,
2493 elif isinstance(value, string_types):
2494 value = self.specs.get(value)
2496 raise ObjUnknown("integer value: %s" % value)
2498 raise InvalidValueType((self.__class__, int, str))
2502 obj = self.__class__(_specs=self.specs)
2503 obj._value = self._value
2504 obj._bound_min = self._bound_min
2505 obj._bound_max = self._bound_max
2507 obj._expl = self._expl
2508 obj.default = self.default
2509 obj.optional = self.optional
2510 obj.offset = self.offset
2511 obj.llen = self.llen
2512 obj.vlen = self.vlen
2524 return self.__class__(
2526 impl=self.tag if impl is None else impl,
2527 expl=self._expl if expl is None else expl,
2528 default=self.default if default is None else default,
2529 optional=self.optional if optional is None else optional,
2534 class CommonString(OctetString):
2535 """Common class for all strings
2537 Everything resembles :py:class:`pyderasn.OctetString`, except
2538 ability to deal with unicode text strings.
2540 >>> hexenc("привет мир".encode("utf-8"))
2541 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2542 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2544 >>> s = UTF8String("привет мир")
2545 UTF8String UTF8String привет мир
2547 'привет мир'
2548 >>> hexenc(bytes(s))
2549 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2551 >>> PrintableString("привет мир")
2552 Traceback (most recent call last):
2553 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2555 >>> BMPString("ада", bounds=(2, 2))
2556 Traceback (most recent call last):
2557 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2558 >>> s = BMPString("ад", bounds=(2, 2))
2561 >>> hexenc(bytes(s))
2569 * - :py:class:`pyderasn.UTF8String`
2571 * - :py:class:`pyderasn.NumericString`
2573 * - :py:class:`pyderasn.PrintableString`
2575 * - :py:class:`pyderasn.TeletexString`
2577 * - :py:class:`pyderasn.T61String`
2579 * - :py:class:`pyderasn.VideotexString`
2581 * - :py:class:`pyderasn.IA5String`
2583 * - :py:class:`pyderasn.GraphicString`
2585 * - :py:class:`pyderasn.VisibleString`
2587 * - :py:class:`pyderasn.ISO646String`
2589 * - :py:class:`pyderasn.GeneralString`
2591 * - :py:class:`pyderasn.UniversalString`
2593 * - :py:class:`pyderasn.BMPString`
2596 __slots__ = ("encoding",)
2598 def _value_sanitize(self, value):
2600 value_decoded = None
2601 if isinstance(value, self.__class__):
2602 value_raw = value._value
2603 elif isinstance(value, text_type):
2604 value_decoded = value
2605 elif isinstance(value, binary_type):
2608 raise InvalidValueType((self.__class__, text_type, binary_type))
2610 value_decoded.encode(self.encoding)
2611 if value_raw is None else value_raw
2614 value_raw.decode(self.encoding)
2615 if value_decoded is None else value_decoded
2617 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2625 def __eq__(self, their):
2626 if isinstance(their, binary_type):
2627 return self._value == their
2628 if isinstance(their, text_type):
2629 return self._value == their.encode(self.encoding)
2630 if not isinstance(their, self.__class__):
2633 self._value == their._value and
2634 self.tag == their.tag and
2635 self._expl == their._expl
2638 def __unicode__(self):
2640 return self._value.decode(self.encoding)
2641 return text_type(self._value)
2644 return pp_console_row(next(self.pps(no_unicode=PY2)))
2646 def pps(self, decode_path=(), no_unicode=False):
2649 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2651 asn1_type_name=self.asn1_type_name,
2652 obj_name=self.__class__.__name__,
2653 decode_path=decode_path,
2655 optional=self.optional,
2656 default=self == self.default,
2657 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2658 expl=None if self._expl is None else tag_decode(self._expl),
2666 class UTF8String(CommonString):
2668 tag_default = tag_encode(12)
2670 asn1_type_name = "UTF8String"
2673 class NumericString(CommonString):
2675 tag_default = tag_encode(18)
2677 asn1_type_name = "NumericString"
2680 class PrintableString(CommonString):
2682 tag_default = tag_encode(19)
2684 asn1_type_name = "PrintableString"
2687 class TeletexString(CommonString):
2689 tag_default = tag_encode(20)
2691 asn1_type_name = "TeletexString"
2694 class T61String(TeletexString):
2696 asn1_type_name = "T61String"
2699 class VideotexString(CommonString):
2701 tag_default = tag_encode(21)
2702 encoding = "iso-8859-1"
2703 asn1_type_name = "VideotexString"
2706 class IA5String(CommonString):
2708 tag_default = tag_encode(22)
2710 asn1_type_name = "IA5"
2713 class UTCTime(CommonString):
2714 """``UTCTime`` datetime type
2716 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2717 UTCTime UTCTime 2017-09-30T22:07:50
2723 datetime.datetime(2017, 9, 30, 22, 7, 50)
2724 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2725 datetime.datetime(1957, 9, 30, 22, 7, 50)
2728 tag_default = tag_encode(23)
2730 asn1_type_name = "UTCTime"
2732 fmt = "%y%m%d%H%M%SZ"
2742 bounds=None, # dummy argument, workability for OctetString.decode
2745 :param value: set the value. Either datetime type, or
2746 :py:class:`pyderasn.UTCTime` object
2747 :param bytes impl: override default tag with ``IMPLICIT`` one
2748 :param bytes expl: override default tag with ``EXPLICIT`` one
2749 :param default: set default value. Type same as in ``value``
2750 :param bool optional: is object ``OPTIONAL`` in sequence
2752 super(UTCTime, self).__init__(
2760 if value is not None:
2761 self._value = self._value_sanitize(value)
2762 if default is not None:
2763 default = self._value_sanitize(default)
2764 self.default = self.__class__(
2769 if self._value is None:
2770 self._value = default
2772 def _value_sanitize(self, value):
2773 if isinstance(value, self.__class__):
2775 if isinstance(value, datetime):
2776 return value.strftime(self.fmt).encode("ascii")
2777 if isinstance(value, binary_type):
2778 value_decoded = value.decode("ascii")
2779 if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2781 datetime.strptime(value_decoded, self.fmt)
2783 raise DecodeError("invalid UTCTime format")
2786 raise DecodeError("invalid UTCTime length")
2787 raise InvalidValueType((self.__class__, datetime))
2789 def __eq__(self, their):
2790 if isinstance(their, binary_type):
2791 return self._value == their
2792 if isinstance(their, datetime):
2793 return self.todatetime() == their
2794 if not isinstance(their, self.__class__):
2797 self._value == their._value and
2798 self.tag == their.tag and
2799 self._expl == their._expl
2802 def todatetime(self):
2803 """Convert to datetime
2807 Pay attention that UTCTime can not hold full year, so all years
2808 having < 50 years are treated as 20xx, 19xx otherwise, according
2809 to X.509 recomendation.
2811 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2812 year = value.year % 100
2814 year=(2000 + year) if year < 50 else (1900 + year),
2818 minute=value.minute,
2819 second=value.second,
2823 return pp_console_row(next(self.pps()))
2825 def pps(self, decode_path=()):
2827 asn1_type_name=self.asn1_type_name,
2828 obj_name=self.__class__.__name__,
2829 decode_path=decode_path,
2830 value=self.todatetime().isoformat() if self.ready else None,
2831 optional=self.optional,
2832 default=self == self.default,
2833 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2834 expl=None if self._expl is None else tag_decode(self._expl),
2842 class GeneralizedTime(UTCTime):
2843 """``GeneralizedTime`` datetime type
2845 This type is similar to :py:class:`pyderasn.UTCTime`.
2847 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2848 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2850 '20170930220750.000123Z'
2851 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2852 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2855 tag_default = tag_encode(24)
2856 asn1_type_name = "GeneralizedTime"
2858 fmt = "%Y%m%d%H%M%SZ"
2859 fmt_ms = "%Y%m%d%H%M%S.%fZ"
2861 def _value_sanitize(self, value):
2862 if isinstance(value, self.__class__):
2864 if isinstance(value, datetime):
2865 return value.strftime(
2866 self.fmt_ms if value.microsecond > 0 else self.fmt
2868 if isinstance(value, binary_type):
2869 value_decoded = value.decode("ascii")
2870 if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2872 datetime.strptime(value_decoded, self.fmt)
2875 "invalid GeneralizedTime (without ms) format",
2878 elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2880 datetime.strptime(value_decoded, self.fmt_ms)
2883 "invalid GeneralizedTime (with ms) format",
2888 "invalid GeneralizedTime length",
2889 klass=self.__class__,
2891 raise InvalidValueType((self.__class__, datetime))
2893 def todatetime(self):
2894 value = self._value.decode("ascii")
2895 if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2896 return datetime.strptime(value, self.fmt)
2897 return datetime.strptime(value, self.fmt_ms)
2900 class GraphicString(CommonString):
2902 tag_default = tag_encode(25)
2903 encoding = "iso-8859-1"
2904 asn1_type_name = "GraphicString"
2907 class VisibleString(CommonString):
2909 tag_default = tag_encode(26)
2911 asn1_type_name = "VisibleString"
2914 class ISO646String(VisibleString):
2916 asn1_type_name = "ISO646String"
2919 class GeneralString(CommonString):
2921 tag_default = tag_encode(27)
2922 encoding = "iso-8859-1"
2923 asn1_type_name = "GeneralString"
2926 class UniversalString(CommonString):
2928 tag_default = tag_encode(28)
2929 encoding = "utf-32-be"
2930 asn1_type_name = "UniversalString"
2933 class BMPString(CommonString):
2935 tag_default = tag_encode(30)
2936 encoding = "utf-16-be"
2937 asn1_type_name = "BMPString"
2941 """``CHOICE`` special type
2945 class GeneralName(Choice):
2947 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2948 ('dNSName', IA5String(impl=tag_ctxp(2))),
2951 >>> gn = GeneralName()
2953 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2954 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2955 >>> gn["dNSName"] = IA5String("bar.baz")
2956 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2957 >>> gn["rfc822Name"]
2960 [2] IA5String IA5 bar.baz
2963 >>> gn.value == gn["dNSName"]
2966 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2968 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2969 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2971 __slots__ = ("specs",)
2973 asn1_type_name = "CHOICE"
2986 :param value: set the value. Either ``(choice, value)`` tuple, or
2987 :py:class:`pyderasn.Choice` object
2988 :param bytes impl: can not be set, do **not** use it
2989 :param bytes expl: override default tag with ``EXPLICIT`` one
2990 :param default: set default value. Type same as in ``value``
2991 :param bool optional: is object ``OPTIONAL`` in sequence
2993 if impl is not None:
2994 raise ValueError("no implicit tag allowed for CHOICE")
2995 super(Choice, self).__init__(None, expl, default, optional, _decoded)
2997 schema = getattr(self, "schema", ())
2998 if len(schema) == 0:
2999 raise ValueError("schema must be specified")
3001 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3004 if value is not None:
3005 self._value = self._value_sanitize(value)
3006 if default is not None:
3007 default_value = self._value_sanitize(default)
3008 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3009 default_obj.specs = self.specs
3010 default_obj._value = default_value
3011 self.default = default_obj
3013 self._value = default_obj.copy()._value
3015 def _value_sanitize(self, value):
3016 if isinstance(value, self.__class__):
3018 if isinstance(value, tuple) and len(value) == 2:
3020 spec = self.specs.get(choice)
3022 raise ObjUnknown(choice)
3023 if not isinstance(obj, spec.__class__):
3024 raise InvalidValueType((spec,))
3025 return (choice, spec(obj))
3026 raise InvalidValueType((self.__class__, tuple))
3030 return self._value is not None and self._value[1].ready
3033 obj = self.__class__(schema=self.specs)
3034 obj._expl = self._expl
3035 obj.default = self.default
3036 obj.optional = self.optional
3037 obj.offset = self.offset
3038 obj.llen = self.llen
3039 obj.vlen = self.vlen
3041 if value is not None:
3042 obj._value = (value[0], value[1].copy())
3045 def __eq__(self, their):
3046 if isinstance(their, tuple) and len(their) == 2:
3047 return self._value == their
3048 if not isinstance(their, self.__class__):
3051 self.specs == their.specs and
3052 self._value == their._value
3062 return self.__class__(
3065 expl=self._expl if expl is None else expl,
3066 default=self.default if default is None else default,
3067 optional=self.optional if optional is None else optional,
3072 self._assert_ready()
3073 return self._value[0]
3077 self._assert_ready()
3078 return self._value[1]
3080 def __getitem__(self, key):
3081 if key not in self.specs:
3082 raise ObjUnknown(key)
3083 if self._value is None:
3085 choice, value = self._value
3090 def __setitem__(self, key, value):
3091 spec = self.specs.get(key)
3093 raise ObjUnknown(key)
3094 if not isinstance(value, spec.__class__):
3095 raise InvalidValueType((spec.__class__,))
3096 self._value = (key, spec(value))
3104 return self._value[1].decoded if self.ready else False
3107 self._assert_ready()
3108 return self._value[1].encode()
3110 def _decode(self, tlv, offset=0, decode_path=()):
3111 for choice, spec in self.specs.items():
3113 value, tail = spec.decode(
3117 decode_path=decode_path + (choice,),
3121 obj = self.__class__(
3124 default=self.default,
3125 optional=self.optional,
3126 _decoded=(offset, 0, value.tlvlen),
3128 obj._value = (choice, value)
3131 klass=self.__class__,
3132 decode_path=decode_path,
3137 value = pp_console_row(next(self.pps()))
3139 value = "%s[%r]" % (value, self.value)
3142 def pps(self, decode_path=()):
3144 asn1_type_name=self.asn1_type_name,
3145 obj_name=self.__class__.__name__,
3146 decode_path=decode_path,
3147 value=self.choice if self.ready else None,
3148 optional=self.optional,
3149 default=self == self.default,
3150 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3151 expl=None if self._expl is None else tag_decode(self._expl),
3158 yield self.value.pps(decode_path=decode_path + (self.choice,))
3161 class PrimitiveTypes(Choice):
3162 """Predefined ``CHOICE`` for all generic primitive types
3164 It could be useful for general decoding of some unspecified values:
3166 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3167 OCTET STRING 3 bytes 666f6f
3168 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3172 schema = tuple((klass.__name__, klass()) for klass in (
3197 """``ANY`` special type
3199 >>> Any(Integer(-123))
3201 >>> a = Any(OctetString(b"hello world").encode())
3202 ANY 040b68656c6c6f20776f726c64
3203 >>> hexenc(bytes(a))
3204 b'0x040x0bhello world'
3207 tag_default = tag_encode(0)
3208 asn1_type_name = "ANY"
3218 :param value: set the value. Either any kind of pyderasn's
3219 **ready** object, or bytes. Pay attention that
3220 **no** validation is performed is raw binary value
3222 :param bytes expl: override default tag with ``EXPLICIT`` one
3223 :param bool optional: is object ``OPTIONAL`` in sequence
3225 super(Any, self).__init__(None, expl, None, optional, _decoded)
3226 self._value = None if value is None else self._value_sanitize(value)
3228 def _value_sanitize(self, value):
3229 if isinstance(value, self.__class__):
3231 if isinstance(value, Obj):
3232 return value.encode()
3233 if isinstance(value, binary_type):
3235 raise InvalidValueType((self.__class__, Obj, binary_type))
3239 return self._value is not None
3242 obj = self.__class__()
3243 obj._value = self._value
3245 obj._expl = self._expl
3246 obj.optional = self.optional
3247 obj.offset = self.offset
3248 obj.llen = self.llen
3249 obj.vlen = self.vlen
3252 def __eq__(self, their):
3253 if isinstance(their, binary_type):
3254 return self._value == their
3255 if issubclass(their.__class__, Any):
3256 return self._value == their._value
3265 return self.__class__(
3267 expl=self._expl if expl is None else expl,
3268 optional=self.optional if optional is None else optional,
3271 def __bytes__(self):
3272 self._assert_ready()
3280 self._assert_ready()
3283 def _decode(self, tlv, offset=0, decode_path=()):
3285 t, tlen, lv = tag_strip(tlv)
3286 l, llen, v = len_decode(lv)
3287 except DecodeError as err:
3288 raise err.__class__(
3290 klass=self.__class__,
3291 decode_path=decode_path,
3295 raise NotEnoughData(
3296 "encoded length is longer than data",
3297 klass=self.__class__,
3298 decode_path=decode_path,
3301 tlvlen = tlen + llen + l
3302 v, tail = tlv[:tlvlen], v[l:]
3303 obj = self.__class__(
3306 optional=self.optional,
3307 _decoded=(offset, 0, tlvlen),
3313 return pp_console_row(next(self.pps()))
3315 def pps(self, decode_path=()):
3317 asn1_type_name=self.asn1_type_name,
3318 obj_name=self.__class__.__name__,
3319 decode_path=decode_path,
3320 blob=self._value if self.ready else None,
3321 optional=self.optional,
3322 default=self == self.default,
3323 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3324 expl=None if self._expl is None else tag_decode(self._expl),
3329 expl_offset=self.expl_offset if self.expled else None,
3330 expl_tlen=self.expl_tlen if self.expled else None,
3331 expl_llen=self.expl_llen if self.expled else None,
3332 expl_vlen=self.expl_vlen if self.expled else None,
3336 ########################################################################
3337 # ASN.1 constructed types
3338 ########################################################################
3340 class Sequence(Obj):
3341 """``SEQUENCE`` structure type
3343 You have to make specification of sequence::
3345 class Extension(Sequence):
3348 ("extnID", ObjectIdentifier()),
3349 ("critical", Boolean(default=False)),
3350 ("extnValue", OctetString()),
3353 Then, you can work with it as with dictionary.
3355 >>> ext = Extension()
3356 >>> Extension().specs
3358 ('extnID', OBJECT IDENTIFIER),
3359 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3360 ('extnValue', OCTET STRING),
3362 >>> ext["extnID"] = "1.2.3"
3363 Traceback (most recent call last):
3364 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3365 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3367 You can know if sequence is ready to be encoded:
3372 Traceback (most recent call last):
3373 pyderasn.ObjNotReady: object is not ready: extnValue
3374 >>> ext["extnValue"] = OctetString(b"foobar")
3378 Value you want to assign, must have the same **type** as in
3379 corresponding specification, but it can have different tags,
3380 optional/default attributes -- they will be taken from specification
3383 class TBSCertificate(Sequence):
3385 ("version", Version(expl=tag_ctxc(0), default="v1")),
3388 >>> tbs = TBSCertificate()
3389 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3391 You can know if value exists/set in the sequence and take its value:
3393 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3396 OBJECT IDENTIFIER 1.2.3
3398 But pay attention that if value has default, then it won't be (not
3399 in) in the sequence (because ``DEFAULT`` must not be encoded in
3400 DER), but you can read its value:
3402 >>> "critical" in ext, ext["critical"]
3403 (False, BOOLEAN False)
3404 >>> ext["critical"] = Boolean(True)
3405 >>> "critical" in ext, ext["critical"]
3406 (True, BOOLEAN True)
3408 All defaulted values are always optional.
3412 When decoded DER contains defaulted value inside, then
3413 technically this is not valid DER encoding. But we allow
3414 and pass it. Of course reencoding of that kind of DER will
3415 result in different binary representation (validly without
3416 defaulted value inside).
3418 Two sequences are equal if they have equal specification (schema),
3419 implicit/explicit tagging and the same values.
3421 __slots__ = ("specs",)
3422 tag_default = tag_encode(form=TagFormConstructed, num=16)
3423 asn1_type_name = "SEQUENCE"
3435 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3437 schema = getattr(self, "schema", ())
3439 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3442 if value is not None:
3443 self._value = self._value_sanitize(value)
3444 if default is not None:
3445 default_value = self._value_sanitize(default)
3446 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3447 default_obj.specs = self.specs
3448 default_obj._value = default_value
3449 self.default = default_obj
3451 self._value = default_obj.copy()._value
3453 def _value_sanitize(self, value):
3454 if not issubclass(value.__class__, Sequence):
3455 raise InvalidValueType((Sequence,))
3460 for name, spec in self.specs.items():
3461 value = self._value.get(name)
3472 obj = self.__class__(schema=self.specs)
3474 obj._expl = self._expl
3475 obj.default = self.default
3476 obj.optional = self.optional
3477 obj.offset = self.offset
3478 obj.llen = self.llen
3479 obj.vlen = self.vlen
3480 obj._value = {k: v.copy() for k, v in self._value.items()}
3483 def __eq__(self, their):
3484 if not isinstance(their, self.__class__):
3487 self.specs == their.specs and
3488 self.tag == their.tag and
3489 self._expl == their._expl and
3490 self._value == their._value
3501 return self.__class__(
3504 impl=self.tag if impl is None else impl,
3505 expl=self._expl if expl is None else expl,
3506 default=self.default if default is None else default,
3507 optional=self.optional if optional is None else optional,
3510 def __contains__(self, key):
3511 return key in self._value
3513 def __setitem__(self, key, value):
3514 spec = self.specs.get(key)
3516 raise ObjUnknown(key)
3518 self._value.pop(key, None)
3520 if not isinstance(value, spec.__class__):
3521 raise InvalidValueType((spec.__class__,))
3522 value = spec(value=value)
3523 if spec.default is not None and value == spec.default:
3524 self._value.pop(key, None)
3526 self._value[key] = value
3528 def __getitem__(self, key):
3529 value = self._value.get(key)
3530 if value is not None:
3532 spec = self.specs.get(key)
3534 raise ObjUnknown(key)
3535 if spec.default is not None:
3539 def _encoded_values(self):
3541 for name, spec in self.specs.items():
3542 value = self._value.get(name)
3546 raise ObjNotReady(name)
3547 raws.append(value.encode())
3551 v = b"".join(self._encoded_values())
3552 return b"".join((self.tag, len_encode(len(v)), v))
3554 def _decode(self, tlv, offset=0, decode_path=()):
3556 t, tlen, lv = tag_strip(tlv)
3557 except DecodeError as err:
3558 raise err.__class__(
3560 klass=self.__class__,
3561 decode_path=decode_path,
3566 klass=self.__class__,
3567 decode_path=decode_path,
3571 l, llen, v = len_decode(lv)
3572 except DecodeError as err:
3573 raise err.__class__(
3575 klass=self.__class__,
3576 decode_path=decode_path,
3580 raise NotEnoughData(
3581 "encoded length is longer than data",
3582 klass=self.__class__,
3583 decode_path=decode_path,
3586 v, tail = v[:l], v[l:]
3587 sub_offset = offset + tlen + llen
3589 for name, spec in self.specs.items():
3590 if len(v) == 0 and spec.optional:
3593 value, v_tail = spec.decode(
3597 decode_path=decode_path + (name,),
3603 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3605 if spec.default is not None and value == spec.default:
3606 # Encoded default values are not valid in DER,
3607 # but we still allow that
3609 values[name] = value
3613 klass=self.__class__,
3614 decode_path=decode_path,
3617 obj = self.__class__(
3621 default=self.default,
3622 optional=self.optional,
3623 _decoded=(offset, llen, l),
3629 value = pp_console_row(next(self.pps()))
3631 for name in self.specs:
3632 _value = self._value.get(name)
3635 cols.append(repr(_value))
3636 return "%s[%s]" % (value, ", ".join(cols))
3638 def pps(self, decode_path=()):
3640 asn1_type_name=self.asn1_type_name,
3641 obj_name=self.__class__.__name__,
3642 decode_path=decode_path,
3643 optional=self.optional,
3644 default=self == self.default,
3645 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3646 expl=None if self._expl is None else tag_decode(self._expl),
3651 expl_offset=self.expl_offset if self.expled else None,
3652 expl_tlen=self.expl_tlen if self.expled else None,
3653 expl_llen=self.expl_llen if self.expled else None,
3654 expl_vlen=self.expl_vlen if self.expled else None,
3656 for name in self.specs:
3657 value = self._value.get(name)
3660 yield value.pps(decode_path=decode_path + (name,))
3663 class Set(Sequence):
3664 """``SET`` structure type
3666 Its usage is identical to :py:class:`pyderasn.Sequence`.
3669 tag_default = tag_encode(form=TagFormConstructed, num=17)
3670 asn1_type_name = "SET"
3673 raws = self._encoded_values()
3676 return b"".join((self.tag, len_encode(len(v)), v))
3678 def _decode(self, tlv, offset=0, decode_path=()):
3680 t, tlen, lv = tag_strip(tlv)
3681 except DecodeError as err:
3682 raise err.__class__(
3684 klass=self.__class__,
3685 decode_path=decode_path,
3690 klass=self.__class__,
3691 decode_path=decode_path,
3695 l, llen, v = len_decode(lv)
3696 except DecodeError as err:
3697 raise err.__class__(
3699 klass=self.__class__,
3700 decode_path=decode_path,
3704 raise NotEnoughData(
3705 "encoded length is longer than data",
3706 klass=self.__class__,
3709 v, tail = v[:l], v[l:]
3710 sub_offset = offset + tlen + llen
3712 specs_items = self.specs.items
3714 for name, spec in specs_items():
3716 value, v_tail = spec.decode(
3720 decode_path=decode_path + (name,),
3725 value.expl_tlvlen if value.expled else value.tlvlen
3728 if spec.default is None or value != spec.default: # pragma: no cover
3729 # SeqMixing.test_encoded_default_accepted covers that place
3730 values[name] = value
3734 klass=self.__class__,
3735 decode_path=decode_path,
3738 obj = self.__class__(
3742 default=self.default,
3743 optional=self.optional,
3744 _decoded=(offset, llen, l),
3750 class SequenceOf(Obj):
3751 """``SEQUENCE OF`` sequence type
3753 For that kind of type you must specify the object it will carry on
3754 (bounds are for example here, not required)::
3756 class Ints(SequenceOf):
3761 >>> ints.append(Integer(123))
3762 >>> ints.append(Integer(234))
3764 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3765 >>> [int(i) for i in ints]
3767 >>> ints.append(Integer(345))
3768 Traceback (most recent call last):
3769 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3772 >>> ints[1] = Integer(345)
3774 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3776 Also you can initialize sequence with preinitialized values:
3778 >>> ints = Ints([Integer(123), Integer(234)])
3780 __slots__ = ("spec", "_bound_min", "_bound_max")
3781 tag_default = tag_encode(form=TagFormConstructed, num=16)
3782 asn1_type_name = "SEQUENCE OF"
3795 super(SequenceOf, self).__init__(
3803 schema = getattr(self, "schema", None)
3805 raise ValueError("schema must be specified")
3808 self._bound_min, self._bound_max = getattr(
3814 self._bound_min, self._bound_max = bounds
3816 if value is not None:
3817 self._value = self._value_sanitize(value)
3818 if default is not None:
3819 default_value = self._value_sanitize(default)
3820 default_obj = self.__class__(
3825 default_obj._value = default_value
3826 self.default = default_obj
3828 self._value = default_obj.copy()._value
3830 def _value_sanitize(self, value):
3831 if issubclass(value.__class__, SequenceOf):
3832 value = value._value
3833 elif hasattr(value, "__iter__"):
3836 raise InvalidValueType((self.__class__, iter))
3837 if not self._bound_min <= len(value) <= self._bound_max:
3838 raise BoundsError(self._bound_min, len(value), self._bound_max)
3840 if not isinstance(v, self.spec.__class__):
3841 raise InvalidValueType((self.spec.__class__,))
3846 return all(v.ready for v in self._value)
3849 obj = self.__class__(schema=self.spec)
3850 obj._bound_min = self._bound_min
3851 obj._bound_max = self._bound_max
3853 obj._expl = self._expl
3854 obj.default = self.default
3855 obj.optional = self.optional
3856 obj.offset = self.offset
3857 obj.llen = self.llen
3858 obj.vlen = self.vlen
3859 obj._value = [v.copy() for v in self._value]
3862 def __eq__(self, their):
3863 if isinstance(their, self.__class__):
3865 self.spec == their.spec and
3866 self.tag == their.tag and
3867 self._expl == their._expl and
3868 self._value == their._value
3870 if hasattr(their, "__iter__"):
3871 return self._value == list(their)
3883 return self.__class__(
3887 (self._bound_min, self._bound_max)
3888 if bounds is None else bounds
3890 impl=self.tag if impl is None else impl,
3891 expl=self._expl if expl is None else expl,
3892 default=self.default if default is None else default,
3893 optional=self.optional if optional is None else optional,
3896 def __contains__(self, key):
3897 return key in self._value
3899 def append(self, value):
3900 if not isinstance(value, self.spec.__class__):
3901 raise InvalidValueType((self.spec.__class__,))
3902 if len(self._value) + 1 > self._bound_max:
3905 len(self._value) + 1,
3908 self._value.append(value)
3911 self._assert_ready()
3912 return iter(self._value)
3915 self._assert_ready()
3916 return len(self._value)
3918 def __setitem__(self, key, value):
3919 if not isinstance(value, self.spec.__class__):
3920 raise InvalidValueType((self.spec.__class__,))
3921 self._value[key] = self.spec(value=value)
3923 def __getitem__(self, key):
3924 return self._value[key]
3926 def _encoded_values(self):
3927 return [v.encode() for v in self._value]
3930 v = b"".join(self._encoded_values())
3931 return b"".join((self.tag, len_encode(len(v)), v))
3933 def _decode(self, tlv, offset=0, decode_path=()):
3935 t, tlen, lv = tag_strip(tlv)
3936 except DecodeError as err:
3937 raise err.__class__(
3939 klass=self.__class__,
3940 decode_path=decode_path,
3945 klass=self.__class__,
3946 decode_path=decode_path,
3950 l, llen, v = len_decode(lv)
3951 except DecodeError as err:
3952 raise err.__class__(
3954 klass=self.__class__,
3955 decode_path=decode_path,
3959 raise NotEnoughData(
3960 "encoded length is longer than data",
3961 klass=self.__class__,
3962 decode_path=decode_path,
3965 v, tail = v[:l], v[l:]
3966 sub_offset = offset + tlen + llen
3970 value, v_tail = spec.decode(
3974 decode_path=decode_path + (str(len(_value)),),
3976 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3978 _value.append(value)
3979 obj = self.__class__(
3982 bounds=(self._bound_min, self._bound_max),
3985 default=self.default,
3986 optional=self.optional,
3987 _decoded=(offset, llen, l),
3993 pp_console_row(next(self.pps())),
3994 ", ".join(repr(v) for v in self._value),
3997 def pps(self, decode_path=()):
3999 asn1_type_name=self.asn1_type_name,
4000 obj_name=self.__class__.__name__,
4001 decode_path=decode_path,
4002 optional=self.optional,
4003 default=self == self.default,
4004 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4005 expl=None if self._expl is None else tag_decode(self._expl),
4010 expl_offset=self.expl_offset if self.expled else None,
4011 expl_tlen=self.expl_tlen if self.expled else None,
4012 expl_llen=self.expl_llen if self.expled else None,
4013 expl_vlen=self.expl_vlen if self.expled else None,
4015 for i, value in enumerate(self._value):
4016 yield value.pps(decode_path=decode_path + (str(i),))
4019 class SetOf(SequenceOf):
4020 """``SET OF`` sequence type
4022 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4025 tag_default = tag_encode(form=TagFormConstructed, num=17)
4026 asn1_type_name = "SET OF"
4029 raws = self._encoded_values()
4032 return b"".join((self.tag, len_encode(len(v)), v))
4035 def obj_by_path(pypath): # pragma: no cover
4036 """Import object specified as string Python path
4038 Modules must be separated from classes/functions with ``:``.
4040 >>> obj_by_path("foo.bar:Baz")
4041 <class 'foo.bar.Baz'>
4042 >>> obj_by_path("foo.bar:Baz.boo")
4043 <classmethod 'foo.bar.Baz.boo'>
4045 mod, objs = pypath.rsplit(":", 1)
4046 from importlib import import_module
4047 obj = import_module(mod)
4048 for obj_name in objs.split("."):
4049 obj = getattr(obj, obj_name)
4053 def main(): # pragma: no cover
4055 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4056 parser.add_argument(
4058 help="Python path to dictionary with OIDs",
4060 parser.add_argument(
4062 help="Python path to schema definition to use",
4064 parser.add_argument(
4066 type=argparse.FileType("rb"),
4067 help="Path to DER file you want to decode",
4069 args = parser.parse_args()
4070 der = memoryview(args.DERFile.read())
4071 args.DERFile.close()
4072 oids = obj_by_path(args.oids) if args.oids else {}
4074 schema = obj_by_path(args.schema)
4075 from functools import partial
4076 pprinter = partial(pprint, big_blobs=True)
4078 # All of this below is a big hack with self references
4079 choice = PrimitiveTypes()
4080 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4081 choice.specs["SetOf"] = SetOf(schema=choice)
4083 choice.specs["SequenceOf%d" % i] = SequenceOf(
4087 choice.specs["Any"] = Any()
4089 # Class name equals to type name, to omit it from output
4090 class SEQUENCEOF(SequenceOf):
4093 schema = SEQUENCEOF()
4095 def pprint_any(obj, oids=None):
4096 def _pprint_pps(pps):
4098 if hasattr(pp, "_fields"):
4099 if pp.asn1_type_name == Choice.asn1_type_name:
4101 pp_kwargs = pp._asdict()
4102 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4103 pp = _pp(**pp_kwargs)
4104 yield pp_console_row(
4110 for row in pp_console_blob(pp):
4113 for row in _pprint_pps(pp):
4115 return "\n".join(_pprint_pps(obj.pps()))
4116 pprinter = pprint_any
4117 obj, tail = schema().decode(der)
4118 print(pprinter(obj, oids=oids))
4120 print("\nTrailing data: %s" % hexenc(tail))
4123 if __name__ == "__main__":