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 add_metaclass
322 from six import binary_type
323 from six import byte2int
324 from six import indexbytes
325 from six import int2byte
326 from six import integer_types
327 from six import iterbytes
329 from six import string_types
330 from six import text_type
331 from six.moves import xrange as six_xrange
372 "TagClassApplication",
376 "TagFormConstructed",
387 TagClassUniversal = 0
388 TagClassApplication = 1 << 6
389 TagClassContext = 1 << 7
390 TagClassPrivate = 1 << 6 | 1 << 7
392 TagFormConstructed = 1 << 5
395 TagClassApplication: "APPLICATION ",
396 TagClassPrivate: "PRIVATE ",
397 TagClassUniversal: "UNIV ",
401 ########################################################################
403 ########################################################################
405 class DecodeError(Exception):
406 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
408 :param str msg: reason of decode failing
409 :param klass: optional exact DecodeError inherited class (like
410 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
411 :py:exc:`InvalidLength`)
412 :param decode_path: tuple of strings. It contains human
413 readable names of the fields through which
414 decoding process has passed
415 :param int offset: binary offset where failure happened
417 super(DecodeError, self).__init__()
420 self.decode_path = decode_path
426 "" if self.klass is None else self.klass.__name__,
428 ("(%s)" % ".".join(self.decode_path))
429 if len(self.decode_path) > 0 else ""
431 ("(at %d)" % self.offset) if self.offset > 0 else "",
437 return "%s(%s)" % (self.__class__.__name__, self)
440 class NotEnoughData(DecodeError):
444 class TagMismatch(DecodeError):
448 class InvalidLength(DecodeError):
452 class InvalidOID(DecodeError):
456 class ObjUnknown(ValueError):
457 def __init__(self, name):
458 super(ObjUnknown, self).__init__()
462 return "object is unknown: %s" % self.name
465 return "%s(%s)" % (self.__class__.__name__, self)
468 class ObjNotReady(ValueError):
469 def __init__(self, name):
470 super(ObjNotReady, self).__init__()
474 return "object is not ready: %s" % self.name
477 return "%s(%s)" % (self.__class__.__name__, self)
480 class InvalidValueType(ValueError):
481 def __init__(self, expected_types):
482 super(InvalidValueType, self).__init__()
483 self.expected_types = expected_types
486 return "invalid value type, expected: %s" % ", ".join(
487 [repr(t) for t in self.expected_types]
491 return "%s(%s)" % (self.__class__.__name__, self)
494 class BoundsError(ValueError):
495 def __init__(self, bound_min, value, bound_max):
496 super(BoundsError, self).__init__()
497 self.bound_min = bound_min
499 self.bound_max = bound_max
502 return "unsatisfied bounds: %s <= %s <= %s" % (
509 return "%s(%s)" % (self.__class__.__name__, self)
512 ########################################################################
514 ########################################################################
516 _hexdecoder = getdecoder("hex")
517 _hexencoder = getencoder("hex")
521 """Binary data to hexadecimal string convert
523 return _hexdecoder(data)[0]
527 """Hexadecimal string to binary data convert
529 return _hexencoder(data)[0].decode("ascii")
532 def int_bytes_len(num, byte_len=8):
535 return int(ceil(float(num.bit_length()) / byte_len))
538 def zero_ended_encode(num):
539 octets = bytearray(int_bytes_len(num, 7))
541 octets[i] = num & 0x7F
545 octets[i] = 0x80 | (num & 0x7F)
551 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
552 """Encode tag to binary form
554 :param int num: tag's number
555 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
556 :py:data:`pyderasn.TagClassContext`,
557 :py:data:`pyderasn.TagClassApplication`,
558 :py:data:`pyderasn.TagClassPrivate`)
559 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
560 :py:data:`pyderasn.TagFormConstructed`)
564 return int2byte(klass | form | num)
565 # [XX|X|11111][1.......][1.......] ... [0.......]
566 return int2byte(klass | form | 31) + zero_ended_encode(num)
570 """Decode tag from binary form
574 No validation is performed, assuming that it has already passed.
576 It returns tuple with three integers, as
577 :py:func:`pyderasn.tag_encode` accepts.
579 first_octet = byte2int(tag)
580 klass = first_octet & 0xC0
581 form = first_octet & 0x20
582 if first_octet & 0x1F < 0x1F:
583 return (klass, form, first_octet & 0x1F)
585 for octet in iterbytes(tag[1:]):
588 return (klass, form, num)
592 """Create CONTEXT PRIMITIVE tag
594 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
598 """Create CONTEXT CONSTRUCTED tag
600 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
604 """Take off tag from the data
606 :returns: (encoded tag, tag length, remaining data)
609 raise NotEnoughData("no data at all")
610 if byte2int(data) & 0x1F < 31:
611 return data[:1], 1, data[1:]
616 raise DecodeError("unfinished tag")
617 if indexbytes(data, i) & 0x80 == 0:
620 return data[:i], i, data[i:]
626 octets = bytearray(int_bytes_len(l) + 1)
627 octets[0] = 0x80 | (len(octets) - 1)
628 for i in six_xrange(len(octets) - 1, 0, -1):
634 def len_decode(data):
636 raise NotEnoughData("no data at all")
637 first_octet = byte2int(data)
638 if first_octet & 0x80 == 0:
639 return first_octet, 1, data[1:]
640 octets_num = first_octet & 0x7F
641 if octets_num + 1 > len(data):
642 raise NotEnoughData("encoded length is longer than data")
644 raise DecodeError("long form instead of short one")
645 if byte2int(data[1:]) == 0:
646 raise DecodeError("leading zeros")
648 for v in iterbytes(data[1:1 + octets_num]):
651 raise DecodeError("long form instead of short one")
652 return l, 1 + octets_num, data[1 + octets_num:]
655 ########################################################################
657 ########################################################################
659 class AutoAddSlots(type):
660 def __new__(cls, name, bases, _dict):
661 _dict["__slots__"] = _dict.get("__slots__", ())
662 return type.__new__(cls, name, bases, _dict)
665 @add_metaclass(AutoAddSlots)
667 """Common ASN.1 object class
669 All ASN.1 types are inherited from it. It has metaclass that
670 automatically adds ``__slots__`` to all inherited classes.
692 self.tag = getattr(self, "impl", self.tag_default)
695 self._expl = getattr(self, "expl", None) if expl is None else expl
696 if self.tag != self.tag_default and self._expl is not None:
698 "implicit and explicit tags can not be set simultaneously"
700 if default is not None:
702 self.optional = optional
703 self.offset, self.llen, self.vlen = _decoded
707 def ready(self): # pragma: no cover
708 """Is object ready to be encoded?
710 raise NotImplementedError()
712 def _assert_ready(self):
714 raise ObjNotReady(self.__class__.__name__)
718 """Is object decoded?
720 return (self.llen + self.vlen) > 0
722 def copy(self): # pragma: no cover
723 """Make a copy of object, safe to be mutated
725 raise NotImplementedError()
733 return self.tlen + self.llen + self.vlen
735 def __str__(self): # pragma: no cover
736 return self.__bytes__() if PY2 else self.__unicode__()
738 def __eq__(self, their): # pragma: no cover
739 raise NotImplementedError()
741 def __ne__(self, their):
742 return not(self == their)
744 def __lt__(self, their): # pragma: no cover
745 raise NotImplementedError()
747 def __gt__(self, their): # pragma: no cover
748 return not(self < their)
750 def __le__(self, their): # pragma: no cover
751 return (self == their) or (self < their)
753 def __ge__(self, their): # pragma: no cover
754 return (self == their) or (self > their)
756 def _encode(self): # pragma: no cover
757 raise NotImplementedError()
759 def _decode(self, tlv, offset=0, decode_path=()): # pragma: no cover
760 raise NotImplementedError()
764 if self._expl is None:
766 return b"".join((self._expl, len_encode(len(raw)), raw))
768 def decode(self, data, offset=0, leavemm=False, decode_path=()):
771 :param data: either binary or memoryview
772 :param int offset: initial data's offset
773 :param bool leavemm: do we need to leave memoryview of remaining
774 data as is, or convert it to bytes otherwise
775 :returns: (Obj, remaining data)
777 tlv = memoryview(data)
778 if self._expl is None:
779 obj, tail = self._decode(
782 decode_path=decode_path,
786 t, tlen, lv = tag_strip(tlv)
787 except DecodeError as err:
790 klass=self.__class__,
791 decode_path=decode_path,
796 klass=self.__class__,
797 decode_path=decode_path,
801 l, llen, v = len_decode(lv)
802 except DecodeError as err:
805 klass=self.__class__,
806 decode_path=decode_path,
811 "encoded length is longer than data",
812 klass=self.__class__,
813 decode_path=decode_path,
816 obj, tail = self._decode(
818 offset=offset + tlen + llen,
821 return obj, (tail if leavemm else tail.tobytes())
825 return self._expl is not None
833 return len(self._expl)
837 return len(len_encode(self.tlvlen))
840 def expl_offset(self):
841 return self.offset - self.expl_tlen - self.expl_llen
848 def expl_tlvlen(self):
849 return self.expl_tlen + self.expl_llen + self.expl_vlen
852 ########################################################################
854 ########################################################################
856 PP = namedtuple("PP", (
878 asn1_type_name="unknown",
917 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
920 cols.append("%5d%s [%d,%d,%4d]" % (
923 " " if pp.expl_offset is None else
924 ("-%d" % (pp.offset - pp.expl_offset))
930 if len(pp.decode_path) > 0:
931 cols.append(" ." * (len(pp.decode_path)))
932 cols.append("%s:" % pp.decode_path[-1])
933 if pp.expl is not None:
934 klass, _, num = pp.expl
935 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
936 if pp.impl is not None:
937 klass, _, num = pp.impl
938 cols.append("[%s%d]" % (TagClassReprs[klass], num))
939 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
940 cols.append(pp.obj_name)
941 cols.append(pp.asn1_type_name)
942 if pp.value is not None:
946 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
949 value = "%s (%s)" % (oids[value], pp.value)
952 if isinstance(pp.blob, binary_type):
953 cols.append(hexenc(pp.blob))
954 elif isinstance(pp.blob, tuple):
955 cols.append(", ".join(pp.blob))
957 cols.append("OPTIONAL")
959 cols.append("DEFAULT")
960 return " ".join(cols)
963 def pp_console_blob(pp):
964 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
965 if len(pp.decode_path) > 0:
966 cols.append(" ." * (len(pp.decode_path) + 1))
967 if isinstance(pp.blob, binary_type):
968 blob = hexenc(pp.blob).upper()
969 for i in range(0, len(blob), 32):
970 chunk = blob[i:i + 32]
971 yield " ".join(cols + [":".join(
972 chunk[j:j + 2] for j in range(0, len(chunk), 2)
974 elif isinstance(pp.blob, tuple):
975 yield " ".join(cols + [", ".join(pp.blob)])
978 def pprint(obj, oids=None, big_blobs=False):
979 """Pretty print object
981 :param Obj obj: object you want to pretty print
982 :param oids: ``OID <-> humand readable string`` dictionary. When OID
983 from it is met, then its humand readable form is printed
984 :param big_blobs: if large binary objects are met (like OctetString
985 values), do we need to print them too, on separate
988 def _pprint_pps(pps):
990 if hasattr(pp, "_fields"):
992 yield pp_console_row(
998 for row in pp_console_blob(pp):
1001 yield pp_console_row(pp, oids=oids, with_offsets=True)
1003 for row in _pprint_pps(pp):
1005 return "\n".join(_pprint_pps(obj.pps()))
1008 ########################################################################
1009 # ASN.1 primitive types
1010 ########################################################################
1013 """``BOOLEAN`` boolean type
1015 >>> b = Boolean(True)
1017 >>> b == Boolean(True)
1023 tag_default = tag_encode(1)
1024 asn1_type_name = "BOOLEAN"
1036 :param value: set the value. Either boolean type, or
1037 :py:class:`pyderasn.Boolean` object
1038 :param bytes impl: override default tag with ``IMPLICIT`` one
1039 :param bytes expl: override default tag with ``EXPLICIT`` one
1040 :param default: set default value. Type same as in ``value``
1041 :param bool optional: is object ``OPTIONAL`` in sequence
1043 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1044 self._value = None if value is None else self._value_sanitize(value)
1045 if default is not None:
1046 default = self._value_sanitize(default)
1047 self.default = self.__class__(
1053 self._value = default
1055 def _value_sanitize(self, value):
1056 if issubclass(value.__class__, Boolean):
1058 if isinstance(value, bool):
1060 raise InvalidValueType((self.__class__, bool))
1064 return self._value is not None
1067 obj = self.__class__()
1068 obj._value = self._value
1070 obj._expl = self._expl
1071 obj.default = self.default
1072 obj.optional = self.optional
1073 obj.offset = self.offset
1074 obj.llen = self.llen
1075 obj.vlen = self.vlen
1078 def __nonzero__(self):
1079 self._assert_ready()
1083 self._assert_ready()
1086 def __eq__(self, their):
1087 if isinstance(their, bool):
1088 return self._value == their
1089 if not issubclass(their.__class__, Boolean):
1092 self._value == their._value and
1093 self.tag == their.tag and
1094 self._expl == their._expl
1105 return self.__class__(
1107 impl=self.tag if impl is None else impl,
1108 expl=self._expl if expl is None else expl,
1109 default=self.default if default is None else default,
1110 optional=self.optional if optional is None else optional,
1114 self._assert_ready()
1118 (b"\xFF" if self._value else b"\x00"),
1121 def _decode(self, tlv, offset=0, decode_path=()):
1123 t, _, lv = tag_strip(tlv)
1124 except DecodeError as err:
1125 raise err.__class__(
1127 klass=self.__class__,
1128 decode_path=decode_path,
1133 klass=self.__class__,
1134 decode_path=decode_path,
1138 l, _, v = len_decode(lv)
1139 except DecodeError as err:
1140 raise err.__class__(
1142 klass=self.__class__,
1143 decode_path=decode_path,
1147 raise InvalidLength(
1148 "Boolean's length must be equal to 1",
1149 klass=self.__class__,
1150 decode_path=decode_path,
1154 raise NotEnoughData(
1155 "encoded length is longer than data",
1156 klass=self.__class__,
1157 decode_path=decode_path,
1160 first_octet = byte2int(v)
1161 if first_octet == 0:
1163 elif first_octet == 0xFF:
1167 "unacceptable Boolean value",
1168 klass=self.__class__,
1169 decode_path=decode_path,
1172 obj = self.__class__(
1176 default=self.default,
1177 optional=self.optional,
1178 _decoded=(offset, 1, 1),
1183 return pp_console_row(next(self.pps()))
1185 def pps(self, decode_path=()):
1187 asn1_type_name=self.asn1_type_name,
1188 obj_name=self.__class__.__name__,
1189 decode_path=decode_path,
1190 value=str(self._value) if self.ready else None,
1191 optional=self.optional,
1192 default=self == self.default,
1193 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1194 expl=None if self._expl is None else tag_decode(self._expl),
1199 expl_offset=self.expl_offset if self.expled else None,
1200 expl_tlen=self.expl_tlen if self.expled else None,
1201 expl_llen=self.expl_llen if self.expled else None,
1202 expl_vlen=self.expl_vlen if self.expled else None,
1207 """``INTEGER`` integer type
1209 >>> b = Integer(-123)
1211 >>> b == Integer(-123)
1216 >>> Integer(2, bounds=(1, 3))
1218 >>> Integer(5, bounds=(1, 3))
1219 Traceback (most recent call last):
1220 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1224 class Version(Integer):
1231 >>> v = Version("v1")
1238 {'v3': 2, 'v1': 0, 'v2': 1}
1240 __slots__ = ("specs", "_bound_min", "_bound_max")
1241 tag_default = tag_encode(2)
1242 asn1_type_name = "INTEGER"
1256 :param value: set the value. Either integer type, named value
1257 (if ``schema`` is specified in the class), or
1258 :py:class:`pyderasn.Integer` object
1259 :param bounds: set ``(MIN, MAX)`` value constraint.
1260 (-inf, +inf) by default
1261 :param bytes impl: override default tag with ``IMPLICIT`` one
1262 :param bytes expl: override default tag with ``EXPLICIT`` one
1263 :param default: set default value. Type same as in ``value``
1264 :param bool optional: is object ``OPTIONAL`` in sequence
1266 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1268 specs = getattr(self, "schema", {}) if _specs is None else _specs
1269 self.specs = specs if isinstance(specs, dict) else dict(specs)
1271 self._bound_min, self._bound_max = getattr(
1274 (float("-inf"), float("+inf")),
1277 self._bound_min, self._bound_max = bounds
1278 if value is not None:
1279 self._value = self._value_sanitize(value)
1280 if default is not None:
1281 default = self._value_sanitize(default)
1282 self.default = self.__class__(
1288 if self._value is None:
1289 self._value = default
1291 def _value_sanitize(self, value):
1292 if issubclass(value.__class__, Integer):
1293 value = value._value
1294 elif isinstance(value, integer_types):
1296 elif isinstance(value, str):
1297 value = self.specs.get(value)
1299 raise ObjUnknown("integer value: %s" % value)
1301 raise InvalidValueType((self.__class__, int, str))
1302 if not self._bound_min <= value <= self._bound_max:
1303 raise BoundsError(self._bound_min, value, self._bound_max)
1308 return self._value is not None
1311 obj = self.__class__(_specs=self.specs)
1312 obj._value = self._value
1313 obj._bound_min = self._bound_min
1314 obj._bound_max = self._bound_max
1316 obj._expl = self._expl
1317 obj.default = self.default
1318 obj.optional = self.optional
1319 obj.offset = self.offset
1320 obj.llen = self.llen
1321 obj.vlen = self.vlen
1325 self._assert_ready()
1326 return int(self._value)
1329 self._assert_ready()
1332 bytes(self._expl or b"") +
1333 str(self._value).encode("ascii"),
1336 def __eq__(self, their):
1337 if isinstance(their, integer_types):
1338 return self._value == their
1339 if not issubclass(their.__class__, Integer):
1342 self._value == their._value and
1343 self.tag == their.tag and
1344 self._expl == their._expl
1347 def __lt__(self, their):
1348 return self._value < their._value
1352 for name, value in self.specs.items():
1353 if value == self._value:
1365 return self.__class__(
1368 (self._bound_min, self._bound_max)
1369 if bounds is None else bounds
1371 impl=self.tag if impl is None else impl,
1372 expl=self._expl if expl is None else expl,
1373 default=self.default if default is None else default,
1374 optional=self.optional if optional is None else optional,
1379 self._assert_ready()
1383 octets = bytearray([0])
1387 octets = bytearray()
1389 octets.append((value & 0xFF) ^ 0xFF)
1391 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1394 octets = bytearray()
1396 octets.append(value & 0xFF)
1398 if octets[-1] & 0x80 > 0:
1401 octets = bytes(octets)
1403 bytes_len = ceil(value.bit_length() / 8) or 1
1406 octets = value.to_bytes(
1411 except OverflowError:
1415 return b"".join((self.tag, len_encode(len(octets)), octets))
1417 def _decode(self, tlv, offset=0, decode_path=()):
1419 t, _, lv = tag_strip(tlv)
1420 except DecodeError as err:
1421 raise err.__class__(
1423 klass=self.__class__,
1424 decode_path=decode_path,
1429 klass=self.__class__,
1430 decode_path=decode_path,
1434 l, llen, v = len_decode(lv)
1435 except DecodeError as err:
1436 raise err.__class__(
1438 klass=self.__class__,
1439 decode_path=decode_path,
1443 raise NotEnoughData(
1444 "encoded length is longer than data",
1445 klass=self.__class__,
1446 decode_path=decode_path,
1450 raise NotEnoughData(
1452 klass=self.__class__,
1453 decode_path=decode_path,
1456 v, tail = v[:l], v[l:]
1457 first_octet = byte2int(v)
1459 second_octet = byte2int(v[1:])
1461 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1462 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1465 "non normalized integer",
1466 klass=self.__class__,
1467 decode_path=decode_path,
1472 if first_octet & 0x80 > 0:
1473 octets = bytearray()
1474 for octet in bytearray(v):
1475 octets.append(octet ^ 0xFF)
1476 for octet in octets:
1477 value = (value << 8) | octet
1481 for octet in bytearray(v):
1482 value = (value << 8) | octet
1484 value = int.from_bytes(v, byteorder="big", signed=True)
1486 obj = self.__class__(
1488 bounds=(self._bound_min, self._bound_max),
1491 default=self.default,
1492 optional=self.optional,
1494 _decoded=(offset, llen, l),
1496 except BoundsError as err:
1499 klass=self.__class__,
1500 decode_path=decode_path,
1506 return pp_console_row(next(self.pps()))
1508 def pps(self, decode_path=()):
1510 asn1_type_name=self.asn1_type_name,
1511 obj_name=self.__class__.__name__,
1512 decode_path=decode_path,
1513 value=(self.named or str(self._value)) if self.ready else None,
1514 optional=self.optional,
1515 default=self == self.default,
1516 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1517 expl=None if self._expl is None else tag_decode(self._expl),
1522 expl_offset=self.expl_offset if self.expled else None,
1523 expl_tlen=self.expl_tlen if self.expled else None,
1524 expl_llen=self.expl_llen if self.expled else None,
1525 expl_vlen=self.expl_vlen if self.expled else None,
1529 class BitString(Obj):
1530 """``BIT STRING`` bit string type
1532 >>> BitString(b"hello world")
1533 BIT STRING 88 bits 68656c6c6f20776f726c64
1536 >>> b == b"hello world"
1541 >>> b = BitString("'010110000000'B")
1542 BIT STRING 12 bits 5800
1545 >>> b[0], b[1], b[2], b[3]
1546 (False, True, False, True)
1550 [False, True, False, True, True, False, False, False, False, False, False, False]
1554 class KeyUsage(BitString):
1556 ('digitalSignature', 0),
1557 ('nonRepudiation', 1),
1558 ('keyEncipherment', 2),
1561 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1562 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1564 ['nonRepudiation', 'keyEncipherment']
1566 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1568 __slots__ = ("specs",)
1569 tag_default = tag_encode(3)
1570 asn1_type_name = "BIT STRING"
1583 :param value: set the value. Either binary type, tuple of named
1584 values (if ``schema`` is specified in the class),
1585 string in ``'XXX...'B`` form, or
1586 :py:class:`pyderasn.BitString` object
1587 :param bytes impl: override default tag with ``IMPLICIT`` one
1588 :param bytes expl: override default tag with ``EXPLICIT`` one
1589 :param default: set default value. Type same as in ``value``
1590 :param bool optional: is object ``OPTIONAL`` in sequence
1592 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1593 specs = getattr(self, "schema", {}) if _specs is None else _specs
1594 self.specs = specs if isinstance(specs, dict) else dict(specs)
1595 self._value = None if value is None else self._value_sanitize(value)
1596 if default is not None:
1597 default = self._value_sanitize(default)
1598 self.default = self.__class__(
1604 self._value = default
1606 def _bits2octets(self, bits):
1607 if len(self.specs) > 0:
1608 bits = bits.rstrip("0")
1610 bits += "0" * ((8 - (bit_len % 8)) % 8)
1611 octets = bytearray(len(bits) // 8)
1612 for i in six_xrange(len(octets)):
1613 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1614 return bit_len, bytes(octets)
1616 def _value_sanitize(self, value):
1617 if issubclass(value.__class__, BitString):
1619 if isinstance(value, (string_types, binary_type)):
1621 isinstance(value, string_types) and
1622 value.startswith("'") and
1623 value.endswith("'B")
1626 if not set(value) <= set(("0", "1")):
1627 raise ValueError("B's coding contains unacceptable chars")
1628 return self._bits2octets(value)
1629 elif isinstance(value, binary_type):
1630 return (len(value) * 8, value)
1632 raise InvalidValueType((
1637 if isinstance(value, tuple):
1640 isinstance(value[0], integer_types) and
1641 isinstance(value[1], binary_type)
1646 bit = self.specs.get(name)
1648 raise ObjUnknown("BitString value: %s" % name)
1651 return self._bits2octets("")
1653 return self._bits2octets("".join(
1654 ("1" if bit in bits else "0")
1655 for bit in six_xrange(max(bits) + 1)
1657 raise InvalidValueType((self.__class__, binary_type, string_types))
1661 return self._value is not None
1664 obj = self.__class__(_specs=self.specs)
1665 obj._value = self._value
1667 obj._expl = self._expl
1668 obj.default = self.default
1669 obj.optional = self.optional
1670 obj.offset = self.offset
1671 obj.llen = self.llen
1672 obj.vlen = self.vlen
1676 self._assert_ready()
1677 for i in six_xrange(self._value[0]):
1682 self._assert_ready()
1683 return self._value[0]
1685 def __bytes__(self):
1686 self._assert_ready()
1687 return self._value[1]
1689 def __eq__(self, their):
1690 if isinstance(their, bytes):
1691 return self._value[1] == their
1692 if not issubclass(their.__class__, BitString):
1695 self._value == their._value and
1696 self.tag == their.tag and
1697 self._expl == their._expl
1702 return [name for name, bit in self.specs.items() if self[bit]]
1712 return self.__class__(
1714 impl=self.tag if impl is None else impl,
1715 expl=self._expl if expl is None else expl,
1716 default=self.default if default is None else default,
1717 optional=self.optional if optional is None else optional,
1721 def __getitem__(self, key):
1722 if isinstance(key, int):
1723 bit_len, octets = self._value
1727 byte2int(memoryview(octets)[key // 8:]) >>
1730 if isinstance(key, string_types):
1731 value = self.specs.get(key)
1733 raise ObjUnknown("BitString value: %s" % key)
1735 raise InvalidValueType((int, str))
1738 self._assert_ready()
1739 bit_len, octets = self._value
1742 len_encode(len(octets) + 1),
1743 int2byte((8 - bit_len % 8) % 8),
1747 def _decode(self, tlv, offset=0, decode_path=()):
1749 t, _, lv = tag_strip(tlv)
1750 except DecodeError as err:
1751 raise err.__class__(
1753 klass=self.__class__,
1754 decode_path=decode_path,
1759 klass=self.__class__,
1760 decode_path=decode_path,
1764 l, llen, v = len_decode(lv)
1765 except DecodeError as err:
1766 raise err.__class__(
1768 klass=self.__class__,
1769 decode_path=decode_path,
1773 raise NotEnoughData(
1774 "encoded length is longer than data",
1775 klass=self.__class__,
1776 decode_path=decode_path,
1780 raise NotEnoughData(
1782 klass=self.__class__,
1783 decode_path=decode_path,
1786 pad_size = byte2int(v)
1787 if l == 1 and pad_size != 0:
1789 "invalid empty value",
1790 klass=self.__class__,
1791 decode_path=decode_path,
1797 klass=self.__class__,
1798 decode_path=decode_path,
1801 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1804 klass=self.__class__,
1805 decode_path=decode_path,
1808 v, tail = v[:l], v[l:]
1809 obj = self.__class__(
1810 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1813 default=self.default,
1814 optional=self.optional,
1816 _decoded=(offset, llen, l),
1821 return pp_console_row(next(self.pps()))
1823 def pps(self, decode_path=()):
1827 bit_len, blob = self._value
1828 value = "%d bits" % bit_len
1829 if len(self.specs) > 0:
1830 blob = tuple(self.named)
1832 asn1_type_name=self.asn1_type_name,
1833 obj_name=self.__class__.__name__,
1834 decode_path=decode_path,
1837 optional=self.optional,
1838 default=self == self.default,
1839 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1840 expl=None if self._expl is None else tag_decode(self._expl),
1845 expl_offset=self.expl_offset if self.expled else None,
1846 expl_tlen=self.expl_tlen if self.expled else None,
1847 expl_llen=self.expl_llen if self.expled else None,
1848 expl_vlen=self.expl_vlen if self.expled else None,
1852 class OctetString(Obj):
1853 """``OCTET STRING`` binary string type
1855 >>> s = OctetString(b"hello world")
1856 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1857 >>> s == OctetString(b"hello world")
1862 >>> OctetString(b"hello", bounds=(4, 4))
1863 Traceback (most recent call last):
1864 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1865 >>> OctetString(b"hell", bounds=(4, 4))
1866 OCTET STRING 4 bytes 68656c6c
1868 __slots__ = ("_bound_min", "_bound_max")
1869 tag_default = tag_encode(4)
1870 asn1_type_name = "OCTET STRING"
1883 :param value: set the value. Either binary type, or
1884 :py:class:`pyderasn.OctetString` object
1885 :param bounds: set ``(MIN, MAX)`` value size constraint.
1886 (-inf, +inf) by default
1887 :param bytes impl: override default tag with ``IMPLICIT`` one
1888 :param bytes expl: override default tag with ``EXPLICIT`` one
1889 :param default: set default value. Type same as in ``value``
1890 :param bool optional: is object ``OPTIONAL`` in sequence
1892 super(OctetString, self).__init__(
1901 self._bound_min, self._bound_max = getattr(
1907 self._bound_min, self._bound_max = bounds
1908 if value is not None:
1909 self._value = self._value_sanitize(value)
1910 if default is not None:
1911 default = self._value_sanitize(default)
1912 self.default = self.__class__(
1917 if self._value is None:
1918 self._value = default
1920 def _value_sanitize(self, value):
1921 if issubclass(value.__class__, OctetString):
1922 value = value._value
1923 elif isinstance(value, binary_type):
1926 raise InvalidValueType((self.__class__, bytes))
1927 if not self._bound_min <= len(value) <= self._bound_max:
1928 raise BoundsError(self._bound_min, len(value), self._bound_max)
1933 return self._value is not None
1936 obj = self.__class__()
1937 obj._value = self._value
1938 obj._bound_min = self._bound_min
1939 obj._bound_max = self._bound_max
1941 obj._expl = self._expl
1942 obj.default = self.default
1943 obj.optional = self.optional
1944 obj.offset = self.offset
1945 obj.llen = self.llen
1946 obj.vlen = self.vlen
1949 def __bytes__(self):
1950 self._assert_ready()
1953 def __eq__(self, their):
1954 if isinstance(their, binary_type):
1955 return self._value == their
1956 if not issubclass(their.__class__, OctetString):
1959 self._value == their._value and
1960 self.tag == their.tag and
1961 self._expl == their._expl
1964 def __lt__(self, their):
1965 return self._value < their._value
1976 return self.__class__(
1979 (self._bound_min, self._bound_max)
1980 if bounds is None else bounds
1982 impl=self.tag if impl is None else impl,
1983 expl=self._expl if expl is None else expl,
1984 default=self.default if default is None else default,
1985 optional=self.optional if optional is None else optional,
1989 self._assert_ready()
1992 len_encode(len(self._value)),
1996 def _decode(self, tlv, offset=0, decode_path=()):
1998 t, _, lv = tag_strip(tlv)
1999 except DecodeError as err:
2000 raise err.__class__(
2002 klass=self.__class__,
2003 decode_path=decode_path,
2008 klass=self.__class__,
2009 decode_path=decode_path,
2013 l, llen, v = len_decode(lv)
2014 except DecodeError as err:
2015 raise err.__class__(
2017 klass=self.__class__,
2018 decode_path=decode_path,
2022 raise NotEnoughData(
2023 "encoded length is longer than data",
2024 klass=self.__class__,
2025 decode_path=decode_path,
2028 v, tail = v[:l], v[l:]
2030 obj = self.__class__(
2032 bounds=(self._bound_min, self._bound_max),
2035 default=self.default,
2036 optional=self.optional,
2037 _decoded=(offset, llen, l),
2039 except BoundsError as err:
2042 klass=self.__class__,
2043 decode_path=decode_path,
2049 return pp_console_row(next(self.pps()))
2051 def pps(self, decode_path=()):
2053 asn1_type_name=self.asn1_type_name,
2054 obj_name=self.__class__.__name__,
2055 decode_path=decode_path,
2056 value=("%d bytes" % len(self._value)) if self.ready else None,
2057 blob=self._value if self.ready else None,
2058 optional=self.optional,
2059 default=self == self.default,
2060 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2061 expl=None if self._expl is None else tag_decode(self._expl),
2066 expl_offset=self.expl_offset if self.expled else None,
2067 expl_tlen=self.expl_tlen if self.expled else None,
2068 expl_llen=self.expl_llen if self.expled else None,
2069 expl_vlen=self.expl_vlen if self.expled else None,
2074 """``NULL`` null object
2082 tag_default = tag_encode(5)
2083 asn1_type_name = "NULL"
2087 value=None, # unused, but Sequence passes it
2094 :param bytes impl: override default tag with ``IMPLICIT`` one
2095 :param bytes expl: override default tag with ``EXPLICIT`` one
2096 :param bool optional: is object ``OPTIONAL`` in sequence
2098 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2106 obj = self.__class__()
2108 obj._expl = self._expl
2109 obj.default = self.default
2110 obj.optional = self.optional
2111 obj.offset = self.offset
2112 obj.llen = self.llen
2113 obj.vlen = self.vlen
2116 def __eq__(self, their):
2117 if not issubclass(their.__class__, Null):
2120 self.tag == their.tag and
2121 self._expl == their._expl
2131 return self.__class__(
2132 impl=self.tag if impl is None else impl,
2133 expl=self._expl if expl is None else expl,
2134 optional=self.optional if optional is None else optional,
2138 return self.tag + len_encode(0)
2140 def _decode(self, tlv, offset=0, decode_path=()):
2142 t, _, lv = tag_strip(tlv)
2143 except DecodeError as err:
2144 raise err.__class__(
2146 klass=self.__class__,
2147 decode_path=decode_path,
2152 klass=self.__class__,
2153 decode_path=decode_path,
2157 l, _, v = len_decode(lv)
2158 except DecodeError as err:
2159 raise err.__class__(
2161 klass=self.__class__,
2162 decode_path=decode_path,
2166 raise InvalidLength(
2167 "Null must have zero length",
2168 klass=self.__class__,
2169 decode_path=decode_path,
2172 obj = self.__class__(
2175 optional=self.optional,
2176 _decoded=(offset, 1, 0),
2181 return pp_console_row(next(self.pps()))
2183 def pps(self, decode_path=()):
2185 asn1_type_name=self.asn1_type_name,
2186 obj_name=self.__class__.__name__,
2187 decode_path=decode_path,
2188 optional=self.optional,
2189 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2190 expl=None if self._expl is None else tag_decode(self._expl),
2195 expl_offset=self.expl_offset if self.expled else None,
2196 expl_tlen=self.expl_tlen if self.expled else None,
2197 expl_llen=self.expl_llen if self.expled else None,
2198 expl_vlen=self.expl_vlen if self.expled else None,
2202 class ObjectIdentifier(Obj):
2203 """``OBJECT IDENTIFIER`` OID type
2205 >>> oid = ObjectIdentifier((1, 2, 3))
2206 OBJECT IDENTIFIER 1.2.3
2207 >>> oid == ObjectIdentifier("1.2.3")
2213 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2214 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2216 >>> str(ObjectIdentifier((3, 1)))
2217 Traceback (most recent call last):
2218 pyderasn.InvalidOID: unacceptable first arc value
2221 tag_default = tag_encode(6)
2222 asn1_type_name = "OBJECT IDENTIFIER"
2234 :param value: set the value. Either tuples of integers,
2235 string of "."-concatenated integers, or
2236 :py:class:`pyderasn.ObjectIdentifier` object
2237 :param bytes impl: override default tag with ``IMPLICIT`` one
2238 :param bytes expl: override default tag with ``EXPLICIT`` one
2239 :param default: set default value. Type same as in ``value``
2240 :param bool optional: is object ``OPTIONAL`` in sequence
2242 super(ObjectIdentifier, self).__init__(
2250 if value is not None:
2251 self._value = self._value_sanitize(value)
2252 if default is not None:
2253 default = self._value_sanitize(default)
2254 self.default = self.__class__(
2259 if self._value is None:
2260 self._value = default
2262 def __add__(self, their):
2263 if isinstance(their, self.__class__):
2264 return self.__class__(self._value + their._value)
2265 if isinstance(their, tuple):
2266 return self.__class__(self._value + their)
2267 raise InvalidValueType((self.__class__, tuple))
2269 def _value_sanitize(self, value):
2270 if issubclass(value.__class__, ObjectIdentifier):
2272 if isinstance(value, string_types):
2274 value = tuple(int(arc) for arc in value.split("."))
2276 raise InvalidOID("unacceptable arcs values")
2277 if isinstance(value, tuple):
2279 raise InvalidOID("less than 2 arcs")
2280 first_arc = value[0]
2281 if first_arc in (0, 1):
2282 if not (0 <= value[1] <= 39):
2283 raise InvalidOID("second arc is too wide")
2284 elif first_arc == 2:
2287 raise InvalidOID("unacceptable first arc value")
2289 raise InvalidValueType((self.__class__, str, tuple))
2293 return self._value is not None
2296 obj = self.__class__()
2297 obj._value = self._value
2299 obj._expl = self._expl
2300 obj.default = self.default
2301 obj.optional = self.optional
2302 obj.offset = self.offset
2303 obj.llen = self.llen
2304 obj.vlen = self.vlen
2308 self._assert_ready()
2309 return iter(self._value)
2312 return ".".join(str(arc) for arc in self._value or ())
2315 self._assert_ready()
2318 bytes(self._expl or b"") +
2319 str(self._value).encode("ascii"),
2322 def __eq__(self, their):
2323 if isinstance(their, tuple):
2324 return self._value == their
2325 if not issubclass(their.__class__, ObjectIdentifier):
2328 self.tag == their.tag and
2329 self._expl == their._expl and
2330 self._value == their._value
2333 def __lt__(self, their):
2334 return self._value < their._value
2344 return self.__class__(
2346 impl=self.tag if impl is None else impl,
2347 expl=self._expl if expl is None else expl,
2348 default=self.default if default is None else default,
2349 optional=self.optional if optional is None else optional,
2353 self._assert_ready()
2355 first_value = value[1]
2356 first_arc = value[0]
2359 elif first_arc == 1:
2361 elif first_arc == 2:
2363 else: # pragma: no cover
2364 raise RuntimeError("invalid arc is stored")
2365 octets = [zero_ended_encode(first_value)]
2366 for arc in value[2:]:
2367 octets.append(zero_ended_encode(arc))
2368 v = b"".join(octets)
2369 return b"".join((self.tag, len_encode(len(v)), v))
2371 def _decode(self, tlv, offset=0, decode_path=()):
2373 t, _, lv = tag_strip(tlv)
2374 except DecodeError as err:
2375 raise err.__class__(
2377 klass=self.__class__,
2378 decode_path=decode_path,
2383 klass=self.__class__,
2384 decode_path=decode_path,
2388 l, llen, v = len_decode(lv)
2389 except DecodeError as err:
2390 raise err.__class__(
2392 klass=self.__class__,
2393 decode_path=decode_path,
2397 raise NotEnoughData(
2398 "encoded length is longer than data",
2399 klass=self.__class__,
2400 decode_path=decode_path,
2404 raise NotEnoughData(
2406 klass=self.__class__,
2407 decode_path=decode_path,
2410 v, tail = v[:l], v[l:]
2416 octet = indexbytes(v, i)
2417 arc = (arc << 7) | (octet & 0x7F)
2418 if octet & 0x80 == 0:
2426 klass=self.__class__,
2427 decode_path=decode_path,
2431 second_arc = arcs[0]
2432 if 0 <= second_arc <= 39:
2434 elif 40 <= second_arc <= 79:
2440 obj = self.__class__(
2441 value=tuple([first_arc, second_arc] + arcs[1:]),
2444 default=self.default,
2445 optional=self.optional,
2446 _decoded=(offset, llen, l),
2451 return pp_console_row(next(self.pps()))
2453 def pps(self, decode_path=()):
2455 asn1_type_name=self.asn1_type_name,
2456 obj_name=self.__class__.__name__,
2457 decode_path=decode_path,
2458 value=str(self) if self.ready else None,
2459 optional=self.optional,
2460 default=self == self.default,
2461 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2462 expl=None if self._expl is None else tag_decode(self._expl),
2467 expl_offset=self.expl_offset if self.expled else None,
2468 expl_tlen=self.expl_tlen if self.expled else None,
2469 expl_llen=self.expl_llen if self.expled else None,
2470 expl_vlen=self.expl_vlen if self.expled else None,
2474 class Enumerated(Integer):
2475 """``ENUMERATED`` integer type
2477 This type is identical to :py:class:`pyderasn.Integer`, but requires
2478 schema to be specified and does not accept values missing from it.
2481 tag_default = tag_encode(10)
2482 asn1_type_name = "ENUMERATED"
2493 bounds=None, # dummy argument, workability for Integer.decode
2495 super(Enumerated, self).__init__(
2504 if len(self.specs) == 0:
2505 raise ValueError("schema must be specified")
2507 def _value_sanitize(self, value):
2508 if isinstance(value, self.__class__):
2509 value = value._value
2510 elif isinstance(value, integer_types):
2511 if value not in list(self.specs.values()):
2513 "unknown integer value: %s" % value,
2514 klass=self.__class__,
2516 elif isinstance(value, string_types):
2517 value = self.specs.get(value)
2519 raise ObjUnknown("integer value: %s" % value)
2521 raise InvalidValueType((self.__class__, int, str))
2525 obj = self.__class__(_specs=self.specs)
2526 obj._value = self._value
2527 obj._bound_min = self._bound_min
2528 obj._bound_max = self._bound_max
2530 obj._expl = self._expl
2531 obj.default = self.default
2532 obj.optional = self.optional
2533 obj.offset = self.offset
2534 obj.llen = self.llen
2535 obj.vlen = self.vlen
2547 return self.__class__(
2549 impl=self.tag if impl is None else impl,
2550 expl=self._expl if expl is None else expl,
2551 default=self.default if default is None else default,
2552 optional=self.optional if optional is None else optional,
2557 class CommonString(OctetString):
2558 """Common class for all strings
2560 Everything resembles :py:class:`pyderasn.OctetString`, except
2561 ability to deal with unicode text strings.
2563 >>> hexenc("привет мир".encode("utf-8"))
2564 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2565 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2567 >>> s = UTF8String("привет мир")
2568 UTF8String UTF8String привет мир
2570 'привет мир'
2571 >>> hexenc(bytes(s))
2572 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2574 >>> PrintableString("привет мир")
2575 Traceback (most recent call last):
2576 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2578 >>> BMPString("ада", bounds=(2, 2))
2579 Traceback (most recent call last):
2580 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2581 >>> s = BMPString("ад", bounds=(2, 2))
2584 >>> hexenc(bytes(s))
2592 * - :py:class:`pyderasn.UTF8String`
2594 * - :py:class:`pyderasn.NumericString`
2596 * - :py:class:`pyderasn.PrintableString`
2598 * - :py:class:`pyderasn.TeletexString`
2600 * - :py:class:`pyderasn.T61String`
2602 * - :py:class:`pyderasn.VideotexString`
2604 * - :py:class:`pyderasn.IA5String`
2606 * - :py:class:`pyderasn.GraphicString`
2608 * - :py:class:`pyderasn.VisibleString`
2610 * - :py:class:`pyderasn.ISO646String`
2612 * - :py:class:`pyderasn.GeneralString`
2614 * - :py:class:`pyderasn.UniversalString`
2616 * - :py:class:`pyderasn.BMPString`
2619 __slots__ = ("encoding",)
2621 def _value_sanitize(self, value):
2623 value_decoded = None
2624 if isinstance(value, self.__class__):
2625 value_raw = value._value
2626 elif isinstance(value, text_type):
2627 value_decoded = value
2628 elif isinstance(value, binary_type):
2631 raise InvalidValueType((self.__class__, text_type, binary_type))
2633 value_decoded.encode(self.encoding)
2634 if value_raw is None else value_raw
2637 value_raw.decode(self.encoding)
2638 if value_decoded is None else value_decoded
2640 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2648 def __eq__(self, their):
2649 if isinstance(their, binary_type):
2650 return self._value == their
2651 if isinstance(their, text_type):
2652 return self._value == their.encode(self.encoding)
2653 if not isinstance(their, self.__class__):
2656 self._value == their._value and
2657 self.tag == their.tag and
2658 self._expl == their._expl
2661 def __unicode__(self):
2663 return self._value.decode(self.encoding)
2664 return text_type(self._value)
2667 return pp_console_row(next(self.pps(no_unicode=PY2)))
2669 def pps(self, decode_path=(), no_unicode=False):
2672 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2674 asn1_type_name=self.asn1_type_name,
2675 obj_name=self.__class__.__name__,
2676 decode_path=decode_path,
2678 optional=self.optional,
2679 default=self == self.default,
2680 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2681 expl=None if self._expl is None else tag_decode(self._expl),
2689 class UTF8String(CommonString):
2691 tag_default = tag_encode(12)
2693 asn1_type_name = "UTF8String"
2696 class NumericString(CommonString):
2698 tag_default = tag_encode(18)
2700 asn1_type_name = "NumericString"
2703 class PrintableString(CommonString):
2705 tag_default = tag_encode(19)
2707 asn1_type_name = "PrintableString"
2710 class TeletexString(CommonString):
2712 tag_default = tag_encode(20)
2714 asn1_type_name = "TeletexString"
2717 class T61String(TeletexString):
2719 asn1_type_name = "T61String"
2722 class VideotexString(CommonString):
2724 tag_default = tag_encode(21)
2725 encoding = "iso-8859-1"
2726 asn1_type_name = "VideotexString"
2729 class IA5String(CommonString):
2731 tag_default = tag_encode(22)
2733 asn1_type_name = "IA5"
2736 class UTCTime(CommonString):
2737 """``UTCTime`` datetime type
2739 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2740 UTCTime UTCTime 2017-09-30T22:07:50
2746 datetime.datetime(2017, 9, 30, 22, 7, 50)
2747 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2748 datetime.datetime(1957, 9, 30, 22, 7, 50)
2751 tag_default = tag_encode(23)
2753 asn1_type_name = "UTCTime"
2755 fmt = "%y%m%d%H%M%SZ"
2765 bounds=None, # dummy argument, workability for OctetString.decode
2768 :param value: set the value. Either datetime type, or
2769 :py:class:`pyderasn.UTCTime` object
2770 :param bytes impl: override default tag with ``IMPLICIT`` one
2771 :param bytes expl: override default tag with ``EXPLICIT`` one
2772 :param default: set default value. Type same as in ``value``
2773 :param bool optional: is object ``OPTIONAL`` in sequence
2775 super(UTCTime, self).__init__(
2783 if value is not None:
2784 self._value = self._value_sanitize(value)
2785 if default is not None:
2786 default = self._value_sanitize(default)
2787 self.default = self.__class__(
2792 if self._value is None:
2793 self._value = default
2795 def _value_sanitize(self, value):
2796 if isinstance(value, self.__class__):
2798 if isinstance(value, datetime):
2799 return value.strftime(self.fmt).encode("ascii")
2800 if isinstance(value, binary_type):
2801 value_decoded = value.decode("ascii")
2802 if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2804 datetime.strptime(value_decoded, self.fmt)
2806 raise DecodeError("invalid UTCTime format")
2809 raise DecodeError("invalid UTCTime length")
2810 raise InvalidValueType((self.__class__, datetime))
2812 def __eq__(self, their):
2813 if isinstance(their, binary_type):
2814 return self._value == their
2815 if isinstance(their, datetime):
2816 return self.todatetime() == their
2817 if not isinstance(their, self.__class__):
2820 self._value == their._value and
2821 self.tag == their.tag and
2822 self._expl == their._expl
2825 def todatetime(self):
2826 """Convert to datetime
2830 Pay attention that UTCTime can not hold full year, so all years
2831 having < 50 years are treated as 20xx, 19xx otherwise, according
2832 to X.509 recomendation.
2834 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2835 year = value.year % 100
2837 year=(2000 + year) if year < 50 else (1900 + year),
2841 minute=value.minute,
2842 second=value.second,
2846 return pp_console_row(next(self.pps()))
2848 def pps(self, decode_path=()):
2850 asn1_type_name=self.asn1_type_name,
2851 obj_name=self.__class__.__name__,
2852 decode_path=decode_path,
2853 value=self.todatetime().isoformat() if self.ready else None,
2854 optional=self.optional,
2855 default=self == self.default,
2856 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2857 expl=None if self._expl is None else tag_decode(self._expl),
2865 class GeneralizedTime(UTCTime):
2866 """``GeneralizedTime`` datetime type
2868 This type is similar to :py:class:`pyderasn.UTCTime`.
2870 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2871 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2873 '20170930220750.000123Z'
2874 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2875 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2878 tag_default = tag_encode(24)
2879 asn1_type_name = "GeneralizedTime"
2881 fmt = "%Y%m%d%H%M%SZ"
2882 fmt_ms = "%Y%m%d%H%M%S.%fZ"
2884 def _value_sanitize(self, value):
2885 if isinstance(value, self.__class__):
2887 if isinstance(value, datetime):
2888 return value.strftime(
2889 self.fmt_ms if value.microsecond > 0 else self.fmt
2891 if isinstance(value, binary_type):
2892 value_decoded = value.decode("ascii")
2893 if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2895 datetime.strptime(value_decoded, self.fmt)
2898 "invalid GeneralizedTime (without ms) format",
2901 elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2903 datetime.strptime(value_decoded, self.fmt_ms)
2906 "invalid GeneralizedTime (with ms) format",
2911 "invalid GeneralizedTime length",
2912 klass=self.__class__,
2914 raise InvalidValueType((self.__class__, datetime))
2916 def todatetime(self):
2917 value = self._value.decode("ascii")
2918 if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2919 return datetime.strptime(value, self.fmt)
2920 return datetime.strptime(value, self.fmt_ms)
2923 class GraphicString(CommonString):
2925 tag_default = tag_encode(25)
2926 encoding = "iso-8859-1"
2927 asn1_type_name = "GraphicString"
2930 class VisibleString(CommonString):
2932 tag_default = tag_encode(26)
2934 asn1_type_name = "VisibleString"
2937 class ISO646String(VisibleString):
2939 asn1_type_name = "ISO646String"
2942 class GeneralString(CommonString):
2944 tag_default = tag_encode(27)
2945 encoding = "iso-8859-1"
2946 asn1_type_name = "GeneralString"
2949 class UniversalString(CommonString):
2951 tag_default = tag_encode(28)
2952 encoding = "utf-32-be"
2953 asn1_type_name = "UniversalString"
2956 class BMPString(CommonString):
2958 tag_default = tag_encode(30)
2959 encoding = "utf-16-be"
2960 asn1_type_name = "BMPString"
2964 """``CHOICE`` special type
2968 class GeneralName(Choice):
2970 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2971 ('dNSName', IA5String(impl=tag_ctxp(2))),
2974 >>> gn = GeneralName()
2976 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2977 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2978 >>> gn["dNSName"] = IA5String("bar.baz")
2979 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2980 >>> gn["rfc822Name"]
2983 [2] IA5String IA5 bar.baz
2986 >>> gn.value == gn["dNSName"]
2989 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2991 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2992 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2994 __slots__ = ("specs",)
2996 asn1_type_name = "CHOICE"
3009 :param value: set the value. Either ``(choice, value)`` tuple, or
3010 :py:class:`pyderasn.Choice` object
3011 :param bytes impl: can not be set, do **not** use it
3012 :param bytes expl: override default tag with ``EXPLICIT`` one
3013 :param default: set default value. Type same as in ``value``
3014 :param bool optional: is object ``OPTIONAL`` in sequence
3016 if impl is not None:
3017 raise ValueError("no implicit tag allowed for CHOICE")
3018 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3020 schema = getattr(self, "schema", ())
3021 if len(schema) == 0:
3022 raise ValueError("schema must be specified")
3024 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3027 if value is not None:
3028 self._value = self._value_sanitize(value)
3029 if default is not None:
3030 default_value = self._value_sanitize(default)
3031 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3032 default_obj.specs = self.specs
3033 default_obj._value = default_value
3034 self.default = default_obj
3036 self._value = default_obj.copy()._value
3038 def _value_sanitize(self, value):
3039 if isinstance(value, self.__class__):
3041 if isinstance(value, tuple) and len(value) == 2:
3043 spec = self.specs.get(choice)
3045 raise ObjUnknown(choice)
3046 if not isinstance(obj, spec.__class__):
3047 raise InvalidValueType((spec,))
3048 return (choice, spec(obj))
3049 raise InvalidValueType((self.__class__, tuple))
3053 return self._value is not None and self._value[1].ready
3056 obj = self.__class__(schema=self.specs)
3057 obj._expl = self._expl
3058 obj.default = self.default
3059 obj.optional = self.optional
3060 obj.offset = self.offset
3061 obj.llen = self.llen
3062 obj.vlen = self.vlen
3064 if value is not None:
3065 obj._value = (value[0], value[1].copy())
3068 def __eq__(self, their):
3069 if isinstance(their, tuple) and len(their) == 2:
3070 return self._value == their
3071 if not isinstance(their, self.__class__):
3074 self.specs == their.specs and
3075 self._value == their._value
3085 return self.__class__(
3088 expl=self._expl if expl is None else expl,
3089 default=self.default if default is None else default,
3090 optional=self.optional if optional is None else optional,
3095 self._assert_ready()
3096 return self._value[0]
3100 self._assert_ready()
3101 return self._value[1]
3103 def __getitem__(self, key):
3104 if key not in self.specs:
3105 raise ObjUnknown(key)
3106 if self._value is None:
3108 choice, value = self._value
3113 def __setitem__(self, key, value):
3114 spec = self.specs.get(key)
3116 raise ObjUnknown(key)
3117 if not isinstance(value, spec.__class__):
3118 raise InvalidValueType((spec.__class__,))
3119 self._value = (key, spec(value))
3127 return self._value[1].decoded if self.ready else False
3130 self._assert_ready()
3131 return self._value[1].encode()
3133 def _decode(self, tlv, offset=0, decode_path=()):
3134 for choice, spec in self.specs.items():
3136 value, tail = spec.decode(
3140 decode_path=decode_path + (choice,),
3144 obj = self.__class__(
3147 default=self.default,
3148 optional=self.optional,
3149 _decoded=(offset, 0, value.tlvlen),
3151 obj._value = (choice, value)
3154 klass=self.__class__,
3155 decode_path=decode_path,
3160 value = pp_console_row(next(self.pps()))
3162 value = "%s[%r]" % (value, self.value)
3165 def pps(self, decode_path=()):
3167 asn1_type_name=self.asn1_type_name,
3168 obj_name=self.__class__.__name__,
3169 decode_path=decode_path,
3170 value=self.choice if self.ready else None,
3171 optional=self.optional,
3172 default=self == self.default,
3173 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3174 expl=None if self._expl is None else tag_decode(self._expl),
3181 yield self.value.pps(decode_path=decode_path + (self.choice,))
3184 class PrimitiveTypes(Choice):
3185 """Predefined ``CHOICE`` for all generic primitive types
3187 It could be useful for general decoding of some unspecified values:
3189 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3190 OCTET STRING 3 bytes 666f6f
3191 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3195 schema = tuple((klass.__name__, klass()) for klass in (
3220 """``ANY`` special type
3222 >>> Any(Integer(-123))
3224 >>> a = Any(OctetString(b"hello world").encode())
3225 ANY 040b68656c6c6f20776f726c64
3226 >>> hexenc(bytes(a))
3227 b'0x040x0bhello world'
3230 tag_default = tag_encode(0)
3231 asn1_type_name = "ANY"
3241 :param value: set the value. Either any kind of pyderasn's
3242 **ready** object, or bytes. Pay attention that
3243 **no** validation is performed is raw binary value
3245 :param bytes expl: override default tag with ``EXPLICIT`` one
3246 :param bool optional: is object ``OPTIONAL`` in sequence
3248 super(Any, self).__init__(None, expl, None, optional, _decoded)
3249 self._value = None if value is None else self._value_sanitize(value)
3251 def _value_sanitize(self, value):
3252 if isinstance(value, self.__class__):
3254 if isinstance(value, Obj):
3255 return value.encode()
3256 if isinstance(value, binary_type):
3258 raise InvalidValueType((self.__class__, Obj, binary_type))
3262 return self._value is not None
3265 obj = self.__class__()
3266 obj._value = self._value
3268 obj._expl = self._expl
3269 obj.optional = self.optional
3270 obj.offset = self.offset
3271 obj.llen = self.llen
3272 obj.vlen = self.vlen
3275 def __eq__(self, their):
3276 if isinstance(their, binary_type):
3277 return self._value == their
3278 if issubclass(their.__class__, Any):
3279 return self._value == their._value
3288 return self.__class__(
3290 expl=self._expl if expl is None else expl,
3291 optional=self.optional if optional is None else optional,
3294 def __bytes__(self):
3295 self._assert_ready()
3303 self._assert_ready()
3306 def _decode(self, tlv, offset=0, decode_path=()):
3308 t, tlen, lv = tag_strip(tlv)
3309 l, llen, v = len_decode(lv)
3310 except DecodeError as err:
3311 raise err.__class__(
3313 klass=self.__class__,
3314 decode_path=decode_path,
3318 raise NotEnoughData(
3319 "encoded length is longer than data",
3320 klass=self.__class__,
3321 decode_path=decode_path,
3324 tlvlen = tlen + llen + l
3325 v, tail = tlv[:tlvlen], v[l:]
3326 obj = self.__class__(
3329 optional=self.optional,
3330 _decoded=(offset, 0, tlvlen),
3336 return pp_console_row(next(self.pps()))
3338 def pps(self, decode_path=()):
3340 asn1_type_name=self.asn1_type_name,
3341 obj_name=self.__class__.__name__,
3342 decode_path=decode_path,
3343 blob=self._value if self.ready else None,
3344 optional=self.optional,
3345 default=self == self.default,
3346 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3347 expl=None if self._expl is None else tag_decode(self._expl),
3352 expl_offset=self.expl_offset if self.expled else None,
3353 expl_tlen=self.expl_tlen if self.expled else None,
3354 expl_llen=self.expl_llen if self.expled else None,
3355 expl_vlen=self.expl_vlen if self.expled else None,
3359 ########################################################################
3360 # ASN.1 constructed types
3361 ########################################################################
3363 class Sequence(Obj):
3364 """``SEQUENCE`` structure type
3366 You have to make specification of sequence::
3368 class Extension(Sequence):
3371 ("extnID", ObjectIdentifier()),
3372 ("critical", Boolean(default=False)),
3373 ("extnValue", OctetString()),
3376 Then, you can work with it as with dictionary.
3378 >>> ext = Extension()
3379 >>> Extension().specs
3381 ('extnID', OBJECT IDENTIFIER),
3382 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3383 ('extnValue', OCTET STRING),
3385 >>> ext["extnID"] = "1.2.3"
3386 Traceback (most recent call last):
3387 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3388 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3390 You can know if sequence is ready to be encoded:
3395 Traceback (most recent call last):
3396 pyderasn.ObjNotReady: object is not ready: extnValue
3397 >>> ext["extnValue"] = OctetString(b"foobar")
3401 Value you want to assign, must have the same **type** as in
3402 corresponding specification, but it can have different tags,
3403 optional/default attributes -- they will be taken from specification
3406 class TBSCertificate(Sequence):
3408 ("version", Version(expl=tag_ctxc(0), default="v1")),
3411 >>> tbs = TBSCertificate()
3412 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3414 You can know if value exists/set in the sequence and take its value:
3416 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3419 OBJECT IDENTIFIER 1.2.3
3421 But pay attention that if value has default, then it won't be (not
3422 in) in the sequence (because ``DEFAULT`` must not be encoded in
3423 DER), but you can read its value:
3425 >>> "critical" in ext, ext["critical"]
3426 (False, BOOLEAN False)
3427 >>> ext["critical"] = Boolean(True)
3428 >>> "critical" in ext, ext["critical"]
3429 (True, BOOLEAN True)
3431 All defaulted values are always optional.
3435 When decoded DER contains defaulted value inside, then
3436 technically this is not valid DER encoding. But we allow
3437 and pass it. Of course reencoding of that kind of DER will
3438 result in different binary representation (validly without
3439 defaulted value inside).
3441 Two sequences are equal if they have equal specification (schema),
3442 implicit/explicit tagging and the same values.
3444 __slots__ = ("specs",)
3445 tag_default = tag_encode(form=TagFormConstructed, num=16)
3446 asn1_type_name = "SEQUENCE"
3458 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3460 schema = getattr(self, "schema", ())
3462 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3465 if value is not None:
3466 self._value = self._value_sanitize(value)
3467 if default is not None:
3468 default_value = self._value_sanitize(default)
3469 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3470 default_obj.specs = self.specs
3471 default_obj._value = default_value
3472 self.default = default_obj
3474 self._value = default_obj.copy()._value
3476 def _value_sanitize(self, value):
3477 if not issubclass(value.__class__, Sequence):
3478 raise InvalidValueType((Sequence,))
3483 for name, spec in self.specs.items():
3484 value = self._value.get(name)
3495 obj = self.__class__(schema=self.specs)
3497 obj._expl = self._expl
3498 obj.default = self.default
3499 obj.optional = self.optional
3500 obj.offset = self.offset
3501 obj.llen = self.llen
3502 obj.vlen = self.vlen
3503 obj._value = {k: v.copy() for k, v in self._value.items()}
3506 def __eq__(self, their):
3507 if not isinstance(their, self.__class__):
3510 self.specs == their.specs and
3511 self.tag == their.tag and
3512 self._expl == their._expl and
3513 self._value == their._value
3524 return self.__class__(
3527 impl=self.tag if impl is None else impl,
3528 expl=self._expl if expl is None else expl,
3529 default=self.default if default is None else default,
3530 optional=self.optional if optional is None else optional,
3533 def __contains__(self, key):
3534 return key in self._value
3536 def __setitem__(self, key, value):
3537 spec = self.specs.get(key)
3539 raise ObjUnknown(key)
3541 self._value.pop(key, None)
3543 if not isinstance(value, spec.__class__):
3544 raise InvalidValueType((spec.__class__,))
3545 value = spec(value=value)
3546 if spec.default is not None and value == spec.default:
3547 self._value.pop(key, None)
3549 self._value[key] = value
3551 def __getitem__(self, key):
3552 value = self._value.get(key)
3553 if value is not None:
3555 spec = self.specs.get(key)
3557 raise ObjUnknown(key)
3558 if spec.default is not None:
3562 def _encoded_values(self):
3564 for name, spec in self.specs.items():
3565 value = self._value.get(name)
3569 raise ObjNotReady(name)
3570 raws.append(value.encode())
3574 v = b"".join(self._encoded_values())
3575 return b"".join((self.tag, len_encode(len(v)), v))
3577 def _decode(self, tlv, offset=0, decode_path=()):
3579 t, tlen, lv = tag_strip(tlv)
3580 except DecodeError as err:
3581 raise err.__class__(
3583 klass=self.__class__,
3584 decode_path=decode_path,
3589 klass=self.__class__,
3590 decode_path=decode_path,
3594 l, llen, v = len_decode(lv)
3595 except DecodeError as err:
3596 raise err.__class__(
3598 klass=self.__class__,
3599 decode_path=decode_path,
3603 raise NotEnoughData(
3604 "encoded length is longer than data",
3605 klass=self.__class__,
3606 decode_path=decode_path,
3609 v, tail = v[:l], v[l:]
3610 sub_offset = offset + tlen + llen
3612 for name, spec in self.specs.items():
3613 if len(v) == 0 and spec.optional:
3616 value, v_tail = spec.decode(
3620 decode_path=decode_path + (name,),
3626 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3628 if spec.default is not None and value == spec.default:
3629 # Encoded default values are not valid in DER,
3630 # but we still allow that
3632 values[name] = value
3636 klass=self.__class__,
3637 decode_path=decode_path,
3640 obj = self.__class__(
3644 default=self.default,
3645 optional=self.optional,
3646 _decoded=(offset, llen, l),
3652 value = pp_console_row(next(self.pps()))
3654 for name in self.specs:
3655 _value = self._value.get(name)
3658 cols.append(repr(_value))
3659 return "%s[%s]" % (value, ", ".join(cols))
3661 def pps(self, decode_path=()):
3663 asn1_type_name=self.asn1_type_name,
3664 obj_name=self.__class__.__name__,
3665 decode_path=decode_path,
3666 optional=self.optional,
3667 default=self == self.default,
3668 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3669 expl=None if self._expl is None else tag_decode(self._expl),
3674 expl_offset=self.expl_offset if self.expled else None,
3675 expl_tlen=self.expl_tlen if self.expled else None,
3676 expl_llen=self.expl_llen if self.expled else None,
3677 expl_vlen=self.expl_vlen if self.expled else None,
3679 for name in self.specs:
3680 value = self._value.get(name)
3683 yield value.pps(decode_path=decode_path + (name,))
3686 class Set(Sequence):
3687 """``SET`` structure type
3689 Its usage is identical to :py:class:`pyderasn.Sequence`.
3692 tag_default = tag_encode(form=TagFormConstructed, num=17)
3693 asn1_type_name = "SET"
3696 raws = self._encoded_values()
3699 return b"".join((self.tag, len_encode(len(v)), v))
3701 def _decode(self, tlv, offset=0, decode_path=()):
3703 t, tlen, lv = tag_strip(tlv)
3704 except DecodeError as err:
3705 raise err.__class__(
3707 klass=self.__class__,
3708 decode_path=decode_path,
3713 klass=self.__class__,
3714 decode_path=decode_path,
3718 l, llen, v = len_decode(lv)
3719 except DecodeError as err:
3720 raise err.__class__(
3722 klass=self.__class__,
3723 decode_path=decode_path,
3727 raise NotEnoughData(
3728 "encoded length is longer than data",
3729 klass=self.__class__,
3732 v, tail = v[:l], v[l:]
3733 sub_offset = offset + tlen + llen
3735 specs_items = self.specs.items
3737 for name, spec in specs_items():
3739 value, v_tail = spec.decode(
3743 decode_path=decode_path + (name,),
3748 value.expl_tlvlen if value.expled else value.tlvlen
3751 if spec.default is None or value != spec.default: # pragma: no cover
3752 # SeqMixing.test_encoded_default_accepted covers that place
3753 values[name] = value
3757 klass=self.__class__,
3758 decode_path=decode_path,
3761 obj = self.__class__(
3765 default=self.default,
3766 optional=self.optional,
3767 _decoded=(offset, llen, l),
3773 class SequenceOf(Obj):
3774 """``SEQUENCE OF`` sequence type
3776 For that kind of type you must specify the object it will carry on
3777 (bounds are for example here, not required)::
3779 class Ints(SequenceOf):
3784 >>> ints.append(Integer(123))
3785 >>> ints.append(Integer(234))
3787 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3788 >>> [int(i) for i in ints]
3790 >>> ints.append(Integer(345))
3791 Traceback (most recent call last):
3792 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3795 >>> ints[1] = Integer(345)
3797 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3799 Also you can initialize sequence with preinitialized values:
3801 >>> ints = Ints([Integer(123), Integer(234)])
3803 __slots__ = ("spec", "_bound_min", "_bound_max")
3804 tag_default = tag_encode(form=TagFormConstructed, num=16)
3805 asn1_type_name = "SEQUENCE OF"
3818 super(SequenceOf, self).__init__(
3826 schema = getattr(self, "schema", None)
3828 raise ValueError("schema must be specified")
3831 self._bound_min, self._bound_max = getattr(
3837 self._bound_min, self._bound_max = bounds
3839 if value is not None:
3840 self._value = self._value_sanitize(value)
3841 if default is not None:
3842 default_value = self._value_sanitize(default)
3843 default_obj = self.__class__(
3848 default_obj._value = default_value
3849 self.default = default_obj
3851 self._value = default_obj.copy()._value
3853 def _value_sanitize(self, value):
3854 if issubclass(value.__class__, SequenceOf):
3855 value = value._value
3856 elif hasattr(value, "__iter__"):
3859 raise InvalidValueType((self.__class__, iter))
3860 if not self._bound_min <= len(value) <= self._bound_max:
3861 raise BoundsError(self._bound_min, len(value), self._bound_max)
3863 if not isinstance(v, self.spec.__class__):
3864 raise InvalidValueType((self.spec.__class__,))
3869 return all(v.ready for v in self._value)
3872 obj = self.__class__(schema=self.spec)
3873 obj._bound_min = self._bound_min
3874 obj._bound_max = self._bound_max
3876 obj._expl = self._expl
3877 obj.default = self.default
3878 obj.optional = self.optional
3879 obj.offset = self.offset
3880 obj.llen = self.llen
3881 obj.vlen = self.vlen
3882 obj._value = [v.copy() for v in self._value]
3885 def __eq__(self, their):
3886 if isinstance(their, self.__class__):
3888 self.spec == their.spec and
3889 self.tag == their.tag and
3890 self._expl == their._expl and
3891 self._value == their._value
3893 if hasattr(their, "__iter__"):
3894 return self._value == list(their)
3906 return self.__class__(
3910 (self._bound_min, self._bound_max)
3911 if bounds is None else bounds
3913 impl=self.tag if impl is None else impl,
3914 expl=self._expl if expl is None else expl,
3915 default=self.default if default is None else default,
3916 optional=self.optional if optional is None else optional,
3919 def __contains__(self, key):
3920 return key in self._value
3922 def append(self, value):
3923 if not isinstance(value, self.spec.__class__):
3924 raise InvalidValueType((self.spec.__class__,))
3925 if len(self._value) + 1 > self._bound_max:
3928 len(self._value) + 1,
3931 self._value.append(value)
3934 self._assert_ready()
3935 return iter(self._value)
3938 self._assert_ready()
3939 return len(self._value)
3941 def __setitem__(self, key, value):
3942 if not isinstance(value, self.spec.__class__):
3943 raise InvalidValueType((self.spec.__class__,))
3944 self._value[key] = self.spec(value=value)
3946 def __getitem__(self, key):
3947 return self._value[key]
3949 def _encoded_values(self):
3950 return [v.encode() for v in self._value]
3953 v = b"".join(self._encoded_values())
3954 return b"".join((self.tag, len_encode(len(v)), v))
3956 def _decode(self, tlv, offset=0, decode_path=()):
3958 t, tlen, lv = tag_strip(tlv)
3959 except DecodeError as err:
3960 raise err.__class__(
3962 klass=self.__class__,
3963 decode_path=decode_path,
3968 klass=self.__class__,
3969 decode_path=decode_path,
3973 l, llen, v = len_decode(lv)
3974 except DecodeError as err:
3975 raise err.__class__(
3977 klass=self.__class__,
3978 decode_path=decode_path,
3982 raise NotEnoughData(
3983 "encoded length is longer than data",
3984 klass=self.__class__,
3985 decode_path=decode_path,
3988 v, tail = v[:l], v[l:]
3989 sub_offset = offset + tlen + llen
3993 value, v_tail = spec.decode(
3997 decode_path=decode_path + (str(len(_value)),),
3999 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4001 _value.append(value)
4002 obj = self.__class__(
4005 bounds=(self._bound_min, self._bound_max),
4008 default=self.default,
4009 optional=self.optional,
4010 _decoded=(offset, llen, l),
4016 pp_console_row(next(self.pps())),
4017 ", ".join(repr(v) for v in self._value),
4020 def pps(self, decode_path=()):
4022 asn1_type_name=self.asn1_type_name,
4023 obj_name=self.__class__.__name__,
4024 decode_path=decode_path,
4025 optional=self.optional,
4026 default=self == self.default,
4027 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4028 expl=None if self._expl is None else tag_decode(self._expl),
4033 expl_offset=self.expl_offset if self.expled else None,
4034 expl_tlen=self.expl_tlen if self.expled else None,
4035 expl_llen=self.expl_llen if self.expled else None,
4036 expl_vlen=self.expl_vlen if self.expled else None,
4038 for i, value in enumerate(self._value):
4039 yield value.pps(decode_path=decode_path + (str(i),))
4042 class SetOf(SequenceOf):
4043 """``SET OF`` sequence type
4045 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4048 tag_default = tag_encode(form=TagFormConstructed, num=17)
4049 asn1_type_name = "SET OF"
4052 raws = self._encoded_values()
4055 return b"".join((self.tag, len_encode(len(v)), v))
4058 def obj_by_path(pypath): # pragma: no cover
4059 """Import object specified as string Python path
4061 Modules must be separated from classes/functions with ``:``.
4063 >>> obj_by_path("foo.bar:Baz")
4064 <class 'foo.bar.Baz'>
4065 >>> obj_by_path("foo.bar:Baz.boo")
4066 <classmethod 'foo.bar.Baz.boo'>
4068 mod, objs = pypath.rsplit(":", 1)
4069 from importlib import import_module
4070 obj = import_module(mod)
4071 for obj_name in objs.split("."):
4072 obj = getattr(obj, obj_name)
4076 def main(): # pragma: no cover
4078 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4079 parser.add_argument(
4081 help="Python path to dictionary with OIDs",
4083 parser.add_argument(
4085 help="Python path to schema definition to use",
4087 parser.add_argument(
4089 type=argparse.FileType("rb"),
4090 help="Path to DER file you want to decode",
4092 args = parser.parse_args()
4093 der = memoryview(args.DERFile.read())
4094 args.DERFile.close()
4095 oids = obj_by_path(args.oids) if args.oids else {}
4097 schema = obj_by_path(args.schema)
4098 from functools import partial
4099 pprinter = partial(pprint, big_blobs=True)
4101 # All of this below is a big hack with self references
4102 choice = PrimitiveTypes()
4103 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4104 choice.specs["SetOf"] = SetOf(schema=choice)
4106 choice.specs["SequenceOf%d" % i] = SequenceOf(
4110 choice.specs["Any"] = Any()
4112 # Class name equals to type name, to omit it from output
4113 class SEQUENCEOF(SequenceOf):
4116 schema = SEQUENCEOF()
4118 def pprint_any(obj, oids=None):
4119 def _pprint_pps(pps):
4121 if hasattr(pp, "_fields"):
4122 if pp.asn1_type_name == Choice.asn1_type_name:
4124 pp_kwargs = pp._asdict()
4125 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4126 pp = _pp(**pp_kwargs)
4127 yield pp_console_row(
4133 for row in pp_console_blob(pp):
4136 for row in _pprint_pps(pp):
4138 return "\n".join(_pprint_pps(obj.pps()))
4139 pprinter = pprint_any
4140 obj, tail = schema().decode(der)
4141 print(pprinter(obj, oids=oids))
4143 print("\nTrailing data: %s" % hexenc(tail))
4146 if __name__ == "__main__":