3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER codec with abstract structures
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments takes
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two object of the same type, but with different implicit/explicit tags
94 You can get objects effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
141 Some objects give ability to set value size constraints. This is either
142 possible integer value, or allowed length of various strings and
143 sequences. Constraints are set in the following way::
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
150 For simplicity you can also set bounds the following way::
152 bounded_x = X(bounds=(MIN, MAX))
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
160 All objects have ``ready`` boolean property, that tells if it is ready
161 to be encoded. If that kind of action is performed on unready object,
162 then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
164 All objects have ``copy()`` method, returning its copy, that can be safely
170 Decoding is performed using ``decode()`` method. ``offset`` optional
171 argument could be used to set initial object's offset in the binary
172 data, for convenience. It returns decoded object and remaining
173 unmarshalled data (tail). Internally all work is done on
174 ``memoryview(data)``, and you can leave returning tail as a memoryview,
175 by specifying ``leavemm=True`` argument.
177 When object is decoded, ``decoded`` property is true and you can safely
178 use following properties:
180 * ``offset`` -- position including initial offset where object's tag starts
181 * ``tlen`` -- length of object's tag
182 * ``llen`` -- length of object's length value
183 * ``vlen`` -- length of object's value
184 * ``tlvlen`` -- length of the whole object
186 Pay attention that those values do **not** include anything related to
187 explicit tag. If you want to know information about it, then use:
188 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
189 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
190 (that actually equals to ordinary ``tlvlen``).
192 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
197 All objects have ``pps()`` method, that is a generator of
198 :py:class:`pyderasn.PP` namedtuple, holding various raw information
199 about the object. If ``pps`` is called on sequences, then all underlying
200 ``PP`` will be yielded.
202 You can use :py:func:`pyderasn.pp_console_row` function, converting
203 those ``PP`` to human readable string. Actually exactly it is used for
204 all object ``repr``. But it is easy to write custom formatters.
206 >>> from pyderasn import pprint
207 >>> encoded = Integer(-12345).encode()
208 >>> obj, tail = Integer().decode(encoded)
209 >>> print(pprint(obj))
210 0 [1,1, 2] INTEGER -12345
217 .. autoclass:: pyderasn.Boolean
222 .. autoclass:: pyderasn.Integer
227 .. autoclass:: pyderasn.BitString
232 .. autoclass:: pyderasn.OctetString
237 .. autoclass:: pyderasn.Null
242 .. autoclass:: pyderasn.ObjectIdentifier
247 .. autoclass:: pyderasn.Enumerated
251 .. autoclass:: pyderasn.CommonString
255 .. autoclass:: pyderasn.UTCTime
256 :members: __init__, todatetime
260 .. autoclass:: pyderasn.GeneralizedTime
267 .. autoclass:: pyderasn.Choice
272 .. autoclass:: PrimitiveTypes
276 .. autoclass:: pyderasn.Any
284 .. autoclass:: pyderasn.Sequence
289 .. autoclass:: pyderasn.Set
294 .. autoclass:: pyderasn.SequenceOf
299 .. autoclass:: pyderasn.SetOf
305 .. autofunction:: pyderasn.hexenc
306 .. autofunction:: pyderasn.hexdec
307 .. autofunction:: pyderasn.tag_encode
308 .. autofunction:: pyderasn.tag_decode
309 .. autofunction:: pyderasn.tag_ctxp
310 .. autofunction:: pyderasn.tag_ctxc
311 .. autoclass:: pyderasn.Obj
314 from codecs import getdecoder
315 from codecs import getencoder
316 from collections import namedtuple
317 from collections import OrderedDict
318 from datetime import datetime
319 from math import ceil
321 from six import add_metaclass
322 from six import binary_type
323 from six import byte2int
324 from six import indexbytes
325 from six import int2byte
326 from six import integer_types
327 from six import iterbytes
329 from six import string_types
330 from six import text_type
331 from six.moves import xrange as six_xrange
372 "TagClassApplication",
376 "TagFormConstructed",
387 TagClassUniversal = 0
388 TagClassApplication = 1 << 6
389 TagClassContext = 1 << 7
390 TagClassPrivate = 1 << 6 | 1 << 7
392 TagFormConstructed = 1 << 5
395 TagClassApplication: "APPLICATION ",
396 TagClassPrivate: "PRIVATE ",
397 TagClassUniversal: "UNIV ",
401 ########################################################################
403 ########################################################################
405 class DecodeError(Exception):
406 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
408 :param str msg: reason of decode failing
409 :param klass: optional exact DecodeError inherited class (like
410 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
411 :py:exc:`InvalidLength`)
412 :param decode_path: tuple of strings. It contains human
413 readable names of the fields through which
414 decoding process has passed
415 :param int offset: binary offset where failure happened
417 super(DecodeError, self).__init__()
420 self.decode_path = decode_path
426 "" if self.klass is None else self.klass.__name__,
428 ("(%s)" % ".".join(self.decode_path))
429 if len(self.decode_path) > 0 else ""
431 ("(at %d)" % self.offset) if self.offset > 0 else "",
437 return "%s(%s)" % (self.__class__.__name__, self)
440 class NotEnoughData(DecodeError):
444 class TagMismatch(DecodeError):
448 class InvalidLength(DecodeError):
452 class InvalidOID(DecodeError):
456 class ObjUnknown(ValueError):
457 def __init__(self, name):
458 super(ObjUnknown, self).__init__()
462 return "object is unknown: %s" % self.name
465 return "%s(%s)" % (self.__class__.__name__, self)
468 class ObjNotReady(ValueError):
469 def __init__(self, name):
470 super(ObjNotReady, self).__init__()
474 return "object is not ready: %s" % self.name
477 return "%s(%s)" % (self.__class__.__name__, self)
480 class InvalidValueType(ValueError):
481 def __init__(self, expected_types):
482 super(InvalidValueType, self).__init__()
483 self.expected_types = expected_types
486 return "invalid value type, expected: %s" % ", ".join(
487 [repr(t) for t in self.expected_types]
491 return "%s(%s)" % (self.__class__.__name__, self)
494 class BoundsError(ValueError):
495 def __init__(self, bound_min, value, bound_max):
496 super(BoundsError, self).__init__()
497 self.bound_min = bound_min
499 self.bound_max = bound_max
502 return "unsatisfied bounds: %s <= %s <= %s" % (
509 return "%s(%s)" % (self.__class__.__name__, self)
512 ########################################################################
514 ########################################################################
516 _hexdecoder = getdecoder("hex")
517 _hexencoder = getencoder("hex")
521 """Binary data to hexadecimal string convert
523 return _hexdecoder(data)[0]
527 """Hexadecimal string to binary data convert
529 return _hexencoder(data)[0].decode("ascii")
532 def int_bytes_len(num, byte_len=8):
535 return int(ceil(float(num.bit_length()) / byte_len))
538 def zero_ended_encode(num):
539 octets = bytearray(int_bytes_len(num, 7))
541 octets[i] = num & 0x7F
545 octets[i] = 0x80 | (num & 0x7F)
551 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
552 """Encode tag to binary form
554 :param int num: tag's number
555 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
556 :py:data:`pyderasn.TagClassContext`,
557 :py:data:`pyderasn.TagClassApplication`,
558 :py:data:`pyderasn.TagClassPrivate`)
559 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
560 :py:data:`pyderasn.TagFormConstructed`)
564 return int2byte(klass | form | num)
565 # [XX|X|11111][1.......][1.......] ... [0.......]
566 return int2byte(klass | form | 31) + zero_ended_encode(num)
570 """Decode tag from binary form
574 No validation is performed, assuming that it has already passed.
576 It returns tuple with three integers, as
577 :py:func:`pyderasn.tag_encode` accepts.
579 first_octet = byte2int(tag)
580 klass = first_octet & 0xC0
581 form = first_octet & 0x20
582 if first_octet & 0x1F < 0x1F:
583 return (klass, form, first_octet & 0x1F)
585 for octet in iterbytes(tag[1:]):
588 return (klass, form, num)
592 """Create CONTEXT PRIMITIVE tag
594 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
598 """Create CONTEXT CONSTRUCTED tag
600 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
604 """Take off tag from the data
606 :returns: (encoded tag, tag length, remaining data)
609 raise NotEnoughData("no data at all")
610 if byte2int(data) & 0x1F < 31:
611 return data[:1], 1, data[1:]
616 raise DecodeError("unfinished tag")
617 if indexbytes(data, i) & 0x80 == 0:
620 return data[:i], i, data[i:]
626 octets = bytearray(int_bytes_len(l) + 1)
627 octets[0] = 0x80 | (len(octets) - 1)
628 for i in six_xrange(len(octets) - 1, 0, -1):
634 def len_decode(data):
636 raise NotEnoughData("no data at all")
637 first_octet = byte2int(data)
638 if first_octet & 0x80 == 0:
639 return first_octet, 1, data[1:]
640 octets_num = first_octet & 0x7F
641 if octets_num + 1 > len(data):
642 raise NotEnoughData("encoded length is longer than data")
644 raise DecodeError("long form instead of short one")
645 if byte2int(data[1:]) == 0:
646 raise DecodeError("leading zeros")
648 for v in iterbytes(data[1:1 + octets_num]):
651 raise DecodeError("long form instead of short one")
652 return l, 1 + octets_num, data[1 + octets_num:]
655 ########################################################################
657 ########################################################################
659 class AutoAddSlots(type):
660 def __new__(cls, name, bases, _dict):
661 _dict["__slots__"] = _dict.get("__slots__", ())
662 return type.__new__(cls, name, bases, _dict)
665 @add_metaclass(AutoAddSlots)
667 """Common ASN.1 object class
669 All ASN.1 types are inherited from it. It has metaclass that
670 automatically adds ``__slots__`` to all inherited classes.
691 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
692 self._expl = getattr(self, "expl", None) if expl is None else expl
693 if self.tag != self.tag_default and self._expl is not None:
695 "implicit and explicit tags can not be set simultaneously"
697 if default is not None:
699 self.optional = optional
700 self.offset, self.llen, self.vlen = _decoded
704 def ready(self): # pragma: no cover
705 """Is object ready to be encoded?
707 raise NotImplementedError()
709 def _assert_ready(self):
711 raise ObjNotReady(self.__class__.__name__)
715 """Is object decoded?
717 return (self.llen + self.vlen) > 0
719 def copy(self): # pragma: no cover
720 """Make a copy of object, safe to be mutated
722 raise NotImplementedError()
730 return self.tlen + self.llen + self.vlen
732 def __str__(self): # pragma: no cover
733 return self.__bytes__() if PY2 else self.__unicode__()
735 def __ne__(self, their):
736 return not(self == their)
738 def __gt__(self, their): # pragma: no cover
739 return not(self < their)
741 def __le__(self, their): # pragma: no cover
742 return (self == their) or (self < their)
744 def __ge__(self, their): # pragma: no cover
745 return (self == their) or (self > their)
747 def _encode(self): # pragma: no cover
748 raise NotImplementedError()
750 def _decode(self, tlv, offset=0, decode_path=()): # pragma: no cover
751 raise NotImplementedError()
755 if self._expl is None:
757 return b"".join((self._expl, len_encode(len(raw)), raw))
759 def decode(self, data, offset=0, leavemm=False, decode_path=()):
762 :param data: either binary or memoryview
763 :param int offset: initial data's offset
764 :param bool leavemm: do we need to leave memoryview of remaining
765 data as is, or convert it to bytes otherwise
766 :returns: (Obj, remaining data)
768 tlv = memoryview(data)
769 if self._expl is None:
770 obj, tail = self._decode(
773 decode_path=decode_path,
777 t, tlen, lv = tag_strip(tlv)
778 except DecodeError as err:
781 klass=self.__class__,
782 decode_path=decode_path,
787 klass=self.__class__,
788 decode_path=decode_path,
792 l, llen, v = len_decode(lv)
793 except DecodeError as err:
796 klass=self.__class__,
797 decode_path=decode_path,
802 "encoded length is longer than data",
803 klass=self.__class__,
804 decode_path=decode_path,
807 obj, tail = self._decode(
809 offset=offset + tlen + llen,
810 decode_path=decode_path,
812 return obj, (tail if leavemm else tail.tobytes())
816 return self._expl is not None
824 return len(self._expl)
828 return len(len_encode(self.tlvlen))
831 def expl_offset(self):
832 return self.offset - self.expl_tlen - self.expl_llen
839 def expl_tlvlen(self):
840 return self.expl_tlen + self.expl_llen + self.expl_vlen
843 ########################################################################
845 ########################################################################
847 PP = namedtuple("PP", (
869 asn1_type_name="unknown",
908 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
911 cols.append("%5d%s [%d,%d,%4d]" % (
914 " " if pp.expl_offset is None else
915 ("-%d" % (pp.offset - pp.expl_offset))
921 if len(pp.decode_path) > 0:
922 cols.append(" ." * (len(pp.decode_path)))
923 cols.append("%s:" % pp.decode_path[-1])
924 if pp.expl is not None:
925 klass, _, num = pp.expl
926 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
927 if pp.impl is not None:
928 klass, _, num = pp.impl
929 cols.append("[%s%d]" % (TagClassReprs[klass], num))
930 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
931 cols.append(pp.obj_name)
932 cols.append(pp.asn1_type_name)
933 if pp.value is not None:
937 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
940 value = "%s (%s)" % (oids[value], pp.value)
943 if isinstance(pp.blob, binary_type):
944 cols.append(hexenc(pp.blob))
945 elif isinstance(pp.blob, tuple):
946 cols.append(", ".join(pp.blob))
948 cols.append("OPTIONAL")
950 cols.append("DEFAULT")
951 return " ".join(cols)
954 def pp_console_blob(pp):
955 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
956 if len(pp.decode_path) > 0:
957 cols.append(" ." * (len(pp.decode_path) + 1))
958 if isinstance(pp.blob, binary_type):
959 blob = hexenc(pp.blob).upper()
960 for i in range(0, len(blob), 32):
961 chunk = blob[i:i + 32]
962 yield " ".join(cols + [":".join(
963 chunk[j:j + 2] for j in range(0, len(chunk), 2)
965 elif isinstance(pp.blob, tuple):
966 yield " ".join(cols + [", ".join(pp.blob)])
969 def pprint(obj, oids=None, big_blobs=False):
970 """Pretty print object
972 :param Obj obj: object you want to pretty print
973 :param oids: ``OID <-> humand readable string`` dictionary. When OID
974 from it is met, then its humand readable form is printed
975 :param big_blobs: if large binary objects are met (like OctetString
976 values), do we need to print them too, on separate
979 def _pprint_pps(pps):
981 if hasattr(pp, "_fields"):
983 yield pp_console_row(
989 for row in pp_console_blob(pp):
992 yield pp_console_row(pp, oids=oids, with_offsets=True)
994 for row in _pprint_pps(pp):
996 return "\n".join(_pprint_pps(obj.pps()))
999 ########################################################################
1000 # ASN.1 primitive types
1001 ########################################################################
1004 """``BOOLEAN`` boolean type
1006 >>> b = Boolean(True)
1008 >>> b == Boolean(True)
1014 tag_default = tag_encode(1)
1015 asn1_type_name = "BOOLEAN"
1027 :param value: set the value. Either boolean type, or
1028 :py:class:`pyderasn.Boolean` object
1029 :param bytes impl: override default tag with ``IMPLICIT`` one
1030 :param bytes expl: override default tag with ``EXPLICIT`` one
1031 :param default: set default value. Type same as in ``value``
1032 :param bool optional: is object ``OPTIONAL`` in sequence
1034 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1035 self._value = None if value is None else self._value_sanitize(value)
1036 if default is not None:
1037 default = self._value_sanitize(default)
1038 self.default = self.__class__(
1044 self._value = default
1046 def _value_sanitize(self, value):
1047 if issubclass(value.__class__, Boolean):
1049 if isinstance(value, bool):
1051 raise InvalidValueType((self.__class__, bool))
1055 return self._value is not None
1058 obj = self.__class__()
1059 obj._value = self._value
1061 obj._expl = self._expl
1062 obj.default = self.default
1063 obj.optional = self.optional
1064 obj.offset = self.offset
1065 obj.llen = self.llen
1066 obj.vlen = self.vlen
1069 def __nonzero__(self):
1070 self._assert_ready()
1074 self._assert_ready()
1077 def __eq__(self, their):
1078 if isinstance(their, bool):
1079 return self._value == their
1080 if not issubclass(their.__class__, Boolean):
1083 self._value == their._value and
1084 self.tag == their.tag and
1085 self._expl == their._expl
1096 return self.__class__(
1098 impl=self.tag if impl is None else impl,
1099 expl=self._expl if expl is None else expl,
1100 default=self.default if default is None else default,
1101 optional=self.optional if optional is None else optional,
1105 self._assert_ready()
1109 (b"\xFF" if self._value else b"\x00"),
1112 def _decode(self, tlv, offset=0, decode_path=()):
1114 t, _, lv = tag_strip(tlv)
1115 except DecodeError as err:
1116 raise err.__class__(
1118 klass=self.__class__,
1119 decode_path=decode_path,
1124 klass=self.__class__,
1125 decode_path=decode_path,
1129 l, _, v = len_decode(lv)
1130 except DecodeError as err:
1131 raise err.__class__(
1133 klass=self.__class__,
1134 decode_path=decode_path,
1138 raise InvalidLength(
1139 "Boolean's length must be equal to 1",
1140 klass=self.__class__,
1141 decode_path=decode_path,
1145 raise NotEnoughData(
1146 "encoded length is longer than data",
1147 klass=self.__class__,
1148 decode_path=decode_path,
1151 first_octet = byte2int(v)
1152 if first_octet == 0:
1154 elif first_octet == 0xFF:
1158 "unacceptable Boolean value",
1159 klass=self.__class__,
1160 decode_path=decode_path,
1163 obj = self.__class__(
1167 default=self.default,
1168 optional=self.optional,
1169 _decoded=(offset, 1, 1),
1174 return pp_console_row(next(self.pps()))
1176 def pps(self, decode_path=()):
1178 asn1_type_name=self.asn1_type_name,
1179 obj_name=self.__class__.__name__,
1180 decode_path=decode_path,
1181 value=str(self._value) if self.ready else None,
1182 optional=self.optional,
1183 default=self == self.default,
1184 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1185 expl=None if self._expl is None else tag_decode(self._expl),
1190 expl_offset=self.expl_offset if self.expled else None,
1191 expl_tlen=self.expl_tlen if self.expled else None,
1192 expl_llen=self.expl_llen if self.expled else None,
1193 expl_vlen=self.expl_vlen if self.expled else None,
1198 """``INTEGER`` integer type
1200 >>> b = Integer(-123)
1202 >>> b == Integer(-123)
1207 >>> Integer(2, bounds=(1, 3))
1209 >>> Integer(5, bounds=(1, 3))
1210 Traceback (most recent call last):
1211 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1215 class Version(Integer):
1222 >>> v = Version("v1")
1229 {'v3': 2, 'v1': 0, 'v2': 1}
1231 __slots__ = ("specs", "_bound_min", "_bound_max")
1232 tag_default = tag_encode(2)
1233 asn1_type_name = "INTEGER"
1247 :param value: set the value. Either integer type, named value
1248 (if ``schema`` is specified in the class), or
1249 :py:class:`pyderasn.Integer` object
1250 :param bounds: set ``(MIN, MAX)`` value constraint.
1251 (-inf, +inf) by default
1252 :param bytes impl: override default tag with ``IMPLICIT`` one
1253 :param bytes expl: override default tag with ``EXPLICIT`` one
1254 :param default: set default value. Type same as in ``value``
1255 :param bool optional: is object ``OPTIONAL`` in sequence
1257 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1259 specs = getattr(self, "schema", {}) if _specs is None else _specs
1260 self.specs = specs if isinstance(specs, dict) else dict(specs)
1261 self._bound_min, self._bound_max = getattr(
1264 (float("-inf"), float("+inf")),
1265 ) if bounds is None else bounds
1266 if value is not None:
1267 self._value = self._value_sanitize(value)
1268 if default is not None:
1269 default = self._value_sanitize(default)
1270 self.default = self.__class__(
1276 if self._value is None:
1277 self._value = default
1279 def _value_sanitize(self, value):
1280 if issubclass(value.__class__, Integer):
1281 value = value._value
1282 elif isinstance(value, integer_types):
1284 elif isinstance(value, str):
1285 value = self.specs.get(value)
1287 raise ObjUnknown("integer value: %s" % value)
1289 raise InvalidValueType((self.__class__, int, str))
1290 if not self._bound_min <= value <= self._bound_max:
1291 raise BoundsError(self._bound_min, value, self._bound_max)
1296 return self._value is not None
1299 obj = self.__class__(_specs=self.specs)
1300 obj._value = self._value
1301 obj._bound_min = self._bound_min
1302 obj._bound_max = self._bound_max
1304 obj._expl = self._expl
1305 obj.default = self.default
1306 obj.optional = self.optional
1307 obj.offset = self.offset
1308 obj.llen = self.llen
1309 obj.vlen = self.vlen
1313 self._assert_ready()
1314 return int(self._value)
1317 self._assert_ready()
1320 bytes(self._expl or b"") +
1321 str(self._value).encode("ascii"),
1324 def __eq__(self, their):
1325 if isinstance(their, integer_types):
1326 return self._value == their
1327 if not issubclass(their.__class__, Integer):
1330 self._value == their._value and
1331 self.tag == their.tag and
1332 self._expl == their._expl
1335 def __lt__(self, their):
1336 return self._value < their._value
1340 for name, value in self.specs.items():
1341 if value == self._value:
1353 return self.__class__(
1356 (self._bound_min, self._bound_max)
1357 if bounds is None else bounds
1359 impl=self.tag if impl is None else impl,
1360 expl=self._expl if expl is None else expl,
1361 default=self.default if default is None else default,
1362 optional=self.optional if optional is None else optional,
1367 self._assert_ready()
1371 octets = bytearray([0])
1375 octets = bytearray()
1377 octets.append((value & 0xFF) ^ 0xFF)
1379 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1382 octets = bytearray()
1384 octets.append(value & 0xFF)
1386 if octets[-1] & 0x80 > 0:
1389 octets = bytes(octets)
1391 bytes_len = ceil(value.bit_length() / 8) or 1
1394 octets = value.to_bytes(
1399 except OverflowError:
1403 return b"".join((self.tag, len_encode(len(octets)), octets))
1405 def _decode(self, tlv, offset=0, decode_path=()):
1407 t, _, lv = tag_strip(tlv)
1408 except DecodeError as err:
1409 raise err.__class__(
1411 klass=self.__class__,
1412 decode_path=decode_path,
1417 klass=self.__class__,
1418 decode_path=decode_path,
1422 l, llen, v = len_decode(lv)
1423 except DecodeError as err:
1424 raise err.__class__(
1426 klass=self.__class__,
1427 decode_path=decode_path,
1431 raise NotEnoughData(
1432 "encoded length is longer than data",
1433 klass=self.__class__,
1434 decode_path=decode_path,
1438 raise NotEnoughData(
1440 klass=self.__class__,
1441 decode_path=decode_path,
1444 v, tail = v[:l], v[l:]
1445 first_octet = byte2int(v)
1447 second_octet = byte2int(v[1:])
1449 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1450 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1453 "non normalized integer",
1454 klass=self.__class__,
1455 decode_path=decode_path,
1460 if first_octet & 0x80 > 0:
1461 octets = bytearray()
1462 for octet in bytearray(v):
1463 octets.append(octet ^ 0xFF)
1464 for octet in octets:
1465 value = (value << 8) | octet
1469 for octet in bytearray(v):
1470 value = (value << 8) | octet
1472 value = int.from_bytes(v, byteorder="big", signed=True)
1474 obj = self.__class__(
1476 bounds=(self._bound_min, self._bound_max),
1479 default=self.default,
1480 optional=self.optional,
1482 _decoded=(offset, llen, l),
1484 except BoundsError as err:
1487 klass=self.__class__,
1488 decode_path=decode_path,
1494 return pp_console_row(next(self.pps()))
1496 def pps(self, decode_path=()):
1498 asn1_type_name=self.asn1_type_name,
1499 obj_name=self.__class__.__name__,
1500 decode_path=decode_path,
1501 value=(self.named or str(self._value)) if self.ready else None,
1502 optional=self.optional,
1503 default=self == self.default,
1504 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1505 expl=None if self._expl is None else tag_decode(self._expl),
1510 expl_offset=self.expl_offset if self.expled else None,
1511 expl_tlen=self.expl_tlen if self.expled else None,
1512 expl_llen=self.expl_llen if self.expled else None,
1513 expl_vlen=self.expl_vlen if self.expled else None,
1517 class BitString(Obj):
1518 """``BIT STRING`` bit string type
1520 >>> BitString(b"hello world")
1521 BIT STRING 88 bits 68656c6c6f20776f726c64
1524 >>> b == b"hello world"
1529 >>> b = BitString("'010110000000'B")
1530 BIT STRING 12 bits 5800
1533 >>> b[0], b[1], b[2], b[3]
1534 (False, True, False, True)
1538 [False, True, False, True, True, False, False, False, False, False, False, False]
1542 class KeyUsage(BitString):
1544 ('digitalSignature', 0),
1545 ('nonRepudiation', 1),
1546 ('keyEncipherment', 2),
1549 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1550 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1552 ['nonRepudiation', 'keyEncipherment']
1554 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1556 __slots__ = ("specs",)
1557 tag_default = tag_encode(3)
1558 asn1_type_name = "BIT STRING"
1571 :param value: set the value. Either binary type, tuple of named
1572 values (if ``schema`` is specified in the class),
1573 string in ``'XXX...'B`` form, or
1574 :py:class:`pyderasn.BitString` object
1575 :param bytes impl: override default tag with ``IMPLICIT`` one
1576 :param bytes expl: override default tag with ``EXPLICIT`` one
1577 :param default: set default value. Type same as in ``value``
1578 :param bool optional: is object ``OPTIONAL`` in sequence
1580 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1581 specs = getattr(self, "schema", {}) if _specs is None else _specs
1582 self.specs = specs if isinstance(specs, dict) else dict(specs)
1583 self._value = None if value is None else self._value_sanitize(value)
1584 if default is not None:
1585 default = self._value_sanitize(default)
1586 self.default = self.__class__(
1592 self._value = default
1594 def _bits2octets(self, bits):
1595 if len(self.specs) > 0:
1596 bits = bits.rstrip("0")
1598 bits += "0" * ((8 - (bit_len % 8)) % 8)
1599 octets = bytearray(len(bits) // 8)
1600 for i in six_xrange(len(octets)):
1601 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1602 return bit_len, bytes(octets)
1604 def _value_sanitize(self, value):
1605 if issubclass(value.__class__, BitString):
1607 if isinstance(value, (string_types, binary_type)):
1609 isinstance(value, string_types) and
1610 value.startswith("'") and
1611 value.endswith("'B")
1614 if not set(value) <= set(("0", "1")):
1615 raise ValueError("B's coding contains unacceptable chars")
1616 return self._bits2octets(value)
1617 elif isinstance(value, binary_type):
1618 return (len(value) * 8, value)
1620 raise InvalidValueType((
1625 if isinstance(value, tuple):
1628 isinstance(value[0], integer_types) and
1629 isinstance(value[1], binary_type)
1634 bit = self.specs.get(name)
1636 raise ObjUnknown("BitString value: %s" % name)
1639 return self._bits2octets("")
1641 return self._bits2octets("".join(
1642 ("1" if bit in bits else "0")
1643 for bit in six_xrange(max(bits) + 1)
1645 raise InvalidValueType((self.__class__, binary_type, string_types))
1649 return self._value is not None
1652 obj = self.__class__(_specs=self.specs)
1654 if value is not None:
1655 value = (value[0], value[1])
1658 obj._expl = self._expl
1659 obj.default = self.default
1660 obj.optional = self.optional
1661 obj.offset = self.offset
1662 obj.llen = self.llen
1663 obj.vlen = self.vlen
1667 self._assert_ready()
1668 for i in six_xrange(self._value[0]):
1673 self._assert_ready()
1674 return self._value[0]
1676 def __bytes__(self):
1677 self._assert_ready()
1678 return self._value[1]
1680 def __eq__(self, their):
1681 if isinstance(their, bytes):
1682 return self._value[1] == their
1683 if not issubclass(their.__class__, BitString):
1686 self._value == their._value and
1687 self.tag == their.tag and
1688 self._expl == their._expl
1693 return [name for name, bit in self.specs.items() if self[bit]]
1703 return self.__class__(
1705 impl=self.tag if impl is None else impl,
1706 expl=self._expl if expl is None else expl,
1707 default=self.default if default is None else default,
1708 optional=self.optional if optional is None else optional,
1712 def __getitem__(self, key):
1713 if isinstance(key, int):
1714 bit_len, octets = self._value
1718 byte2int(memoryview(octets)[key // 8:]) >>
1721 if isinstance(key, string_types):
1722 value = self.specs.get(key)
1724 raise ObjUnknown("BitString value: %s" % key)
1726 raise InvalidValueType((int, str))
1729 self._assert_ready()
1730 bit_len, octets = self._value
1733 len_encode(len(octets) + 1),
1734 int2byte((8 - bit_len % 8) % 8),
1738 def _decode(self, tlv, offset=0, decode_path=()):
1740 t, _, lv = tag_strip(tlv)
1741 except DecodeError as err:
1742 raise err.__class__(
1744 klass=self.__class__,
1745 decode_path=decode_path,
1750 klass=self.__class__,
1751 decode_path=decode_path,
1755 l, llen, v = len_decode(lv)
1756 except DecodeError as err:
1757 raise err.__class__(
1759 klass=self.__class__,
1760 decode_path=decode_path,
1764 raise NotEnoughData(
1765 "encoded length is longer than data",
1766 klass=self.__class__,
1767 decode_path=decode_path,
1771 raise NotEnoughData(
1773 klass=self.__class__,
1774 decode_path=decode_path,
1777 pad_size = byte2int(v)
1778 if l == 1 and pad_size != 0:
1780 "invalid empty value",
1781 klass=self.__class__,
1782 decode_path=decode_path,
1788 klass=self.__class__,
1789 decode_path=decode_path,
1792 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1795 klass=self.__class__,
1796 decode_path=decode_path,
1799 v, tail = v[:l], v[l:]
1800 obj = self.__class__(
1801 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1804 default=self.default,
1805 optional=self.optional,
1807 _decoded=(offset, llen, l),
1812 return pp_console_row(next(self.pps()))
1814 def pps(self, decode_path=()):
1818 bit_len, blob = self._value
1819 value = "%d bits" % bit_len
1820 if len(self.specs) > 0:
1821 blob = tuple(self.named)
1823 asn1_type_name=self.asn1_type_name,
1824 obj_name=self.__class__.__name__,
1825 decode_path=decode_path,
1828 optional=self.optional,
1829 default=self == self.default,
1830 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1831 expl=None if self._expl is None else tag_decode(self._expl),
1836 expl_offset=self.expl_offset if self.expled else None,
1837 expl_tlen=self.expl_tlen if self.expled else None,
1838 expl_llen=self.expl_llen if self.expled else None,
1839 expl_vlen=self.expl_vlen if self.expled else None,
1843 class OctetString(Obj):
1844 """``OCTET STRING`` binary string type
1846 >>> s = OctetString(b"hello world")
1847 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1848 >>> s == OctetString(b"hello world")
1853 >>> OctetString(b"hello", bounds=(4, 4))
1854 Traceback (most recent call last):
1855 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1856 >>> OctetString(b"hell", bounds=(4, 4))
1857 OCTET STRING 4 bytes 68656c6c
1859 __slots__ = ("_bound_min", "_bound_max")
1860 tag_default = tag_encode(4)
1861 asn1_type_name = "OCTET STRING"
1874 :param value: set the value. Either binary type, or
1875 :py:class:`pyderasn.OctetString` object
1876 :param bounds: set ``(MIN, MAX)`` value size constraint.
1877 (-inf, +inf) by default
1878 :param bytes impl: override default tag with ``IMPLICIT`` one
1879 :param bytes expl: override default tag with ``EXPLICIT`` one
1880 :param default: set default value. Type same as in ``value``
1881 :param bool optional: is object ``OPTIONAL`` in sequence
1883 super(OctetString, self).__init__(
1891 self._bound_min, self._bound_max = getattr(
1895 ) if bounds is None else bounds
1896 if value is not None:
1897 self._value = self._value_sanitize(value)
1898 if default is not None:
1899 default = self._value_sanitize(default)
1900 self.default = self.__class__(
1905 if self._value is None:
1906 self._value = default
1908 def _value_sanitize(self, value):
1909 if issubclass(value.__class__, OctetString):
1910 value = value._value
1911 elif isinstance(value, binary_type):
1914 raise InvalidValueType((self.__class__, bytes))
1915 if not self._bound_min <= len(value) <= self._bound_max:
1916 raise BoundsError(self._bound_min, len(value), self._bound_max)
1921 return self._value is not None
1924 obj = self.__class__()
1925 obj._value = self._value
1926 obj._bound_min = self._bound_min
1927 obj._bound_max = self._bound_max
1929 obj._expl = self._expl
1930 obj.default = self.default
1931 obj.optional = self.optional
1932 obj.offset = self.offset
1933 obj.llen = self.llen
1934 obj.vlen = self.vlen
1937 def __bytes__(self):
1938 self._assert_ready()
1941 def __eq__(self, their):
1942 if isinstance(their, binary_type):
1943 return self._value == their
1944 if not issubclass(their.__class__, OctetString):
1947 self._value == their._value and
1948 self.tag == their.tag and
1949 self._expl == their._expl
1952 def __lt__(self, their):
1953 return self._value < their._value
1964 return self.__class__(
1967 (self._bound_min, self._bound_max)
1968 if bounds is None else bounds
1970 impl=self.tag if impl is None else impl,
1971 expl=self._expl if expl is None else expl,
1972 default=self.default if default is None else default,
1973 optional=self.optional if optional is None else optional,
1977 self._assert_ready()
1980 len_encode(len(self._value)),
1984 def _decode(self, tlv, offset=0, decode_path=()):
1986 t, _, lv = tag_strip(tlv)
1987 except DecodeError as err:
1988 raise err.__class__(
1990 klass=self.__class__,
1991 decode_path=decode_path,
1996 klass=self.__class__,
1997 decode_path=decode_path,
2001 l, llen, v = len_decode(lv)
2002 except DecodeError as err:
2003 raise err.__class__(
2005 klass=self.__class__,
2006 decode_path=decode_path,
2010 raise NotEnoughData(
2011 "encoded length is longer than data",
2012 klass=self.__class__,
2013 decode_path=decode_path,
2016 v, tail = v[:l], v[l:]
2018 obj = self.__class__(
2020 bounds=(self._bound_min, self._bound_max),
2023 default=self.default,
2024 optional=self.optional,
2025 _decoded=(offset, llen, l),
2027 except BoundsError as err:
2030 klass=self.__class__,
2031 decode_path=decode_path,
2037 return pp_console_row(next(self.pps()))
2039 def pps(self, decode_path=()):
2041 asn1_type_name=self.asn1_type_name,
2042 obj_name=self.__class__.__name__,
2043 decode_path=decode_path,
2044 value=("%d bytes" % len(self._value)) if self.ready else None,
2045 blob=self._value if self.ready else None,
2046 optional=self.optional,
2047 default=self == self.default,
2048 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2049 expl=None if self._expl is None else tag_decode(self._expl),
2054 expl_offset=self.expl_offset if self.expled else None,
2055 expl_tlen=self.expl_tlen if self.expled else None,
2056 expl_llen=self.expl_llen if self.expled else None,
2057 expl_vlen=self.expl_vlen if self.expled else None,
2062 """``NULL`` null object
2070 tag_default = tag_encode(5)
2071 asn1_type_name = "NULL"
2075 value=None, # unused, but Sequence passes it
2082 :param bytes impl: override default tag with ``IMPLICIT`` one
2083 :param bytes expl: override default tag with ``EXPLICIT`` one
2084 :param bool optional: is object ``OPTIONAL`` in sequence
2086 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2094 obj = self.__class__()
2096 obj._expl = self._expl
2097 obj.default = self.default
2098 obj.optional = self.optional
2099 obj.offset = self.offset
2100 obj.llen = self.llen
2101 obj.vlen = self.vlen
2104 def __eq__(self, their):
2105 if not issubclass(their.__class__, Null):
2108 self.tag == their.tag and
2109 self._expl == their._expl
2119 return self.__class__(
2120 impl=self.tag if impl is None else impl,
2121 expl=self._expl if expl is None else expl,
2122 optional=self.optional if optional is None else optional,
2126 return self.tag + len_encode(0)
2128 def _decode(self, tlv, offset=0, decode_path=()):
2130 t, _, lv = tag_strip(tlv)
2131 except DecodeError as err:
2132 raise err.__class__(
2134 klass=self.__class__,
2135 decode_path=decode_path,
2140 klass=self.__class__,
2141 decode_path=decode_path,
2145 l, _, v = len_decode(lv)
2146 except DecodeError as err:
2147 raise err.__class__(
2149 klass=self.__class__,
2150 decode_path=decode_path,
2154 raise InvalidLength(
2155 "Null must have zero length",
2156 klass=self.__class__,
2157 decode_path=decode_path,
2160 obj = self.__class__(
2163 optional=self.optional,
2164 _decoded=(offset, 1, 0),
2169 return pp_console_row(next(self.pps()))
2171 def pps(self, decode_path=()):
2173 asn1_type_name=self.asn1_type_name,
2174 obj_name=self.__class__.__name__,
2175 decode_path=decode_path,
2176 optional=self.optional,
2177 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2178 expl=None if self._expl is None else tag_decode(self._expl),
2183 expl_offset=self.expl_offset if self.expled else None,
2184 expl_tlen=self.expl_tlen if self.expled else None,
2185 expl_llen=self.expl_llen if self.expled else None,
2186 expl_vlen=self.expl_vlen if self.expled else None,
2190 class ObjectIdentifier(Obj):
2191 """``OBJECT IDENTIFIER`` OID type
2193 >>> oid = ObjectIdentifier((1, 2, 3))
2194 OBJECT IDENTIFIER 1.2.3
2195 >>> oid == ObjectIdentifier("1.2.3")
2201 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2202 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2204 >>> str(ObjectIdentifier((3, 1)))
2205 Traceback (most recent call last):
2206 pyderasn.InvalidOID: unacceptable first arc value
2209 tag_default = tag_encode(6)
2210 asn1_type_name = "OBJECT IDENTIFIER"
2222 :param value: set the value. Either tuples of integers,
2223 string of "."-concatenated integers, or
2224 :py:class:`pyderasn.ObjectIdentifier` object
2225 :param bytes impl: override default tag with ``IMPLICIT`` one
2226 :param bytes expl: override default tag with ``EXPLICIT`` one
2227 :param default: set default value. Type same as in ``value``
2228 :param bool optional: is object ``OPTIONAL`` in sequence
2230 super(ObjectIdentifier, self).__init__(
2238 if value is not None:
2239 self._value = self._value_sanitize(value)
2240 if default is not None:
2241 default = self._value_sanitize(default)
2242 self.default = self.__class__(
2247 if self._value is None:
2248 self._value = default
2250 def __add__(self, their):
2251 if isinstance(their, self.__class__):
2252 return self.__class__(self._value + their._value)
2253 if isinstance(their, tuple):
2254 return self.__class__(self._value + their)
2255 raise InvalidValueType((self.__class__, tuple))
2257 def _value_sanitize(self, value):
2258 if issubclass(value.__class__, ObjectIdentifier):
2260 if isinstance(value, string_types):
2262 value = tuple(int(arc) for arc in value.split("."))
2264 raise InvalidOID("unacceptable arcs values")
2265 if isinstance(value, tuple):
2267 raise InvalidOID("less than 2 arcs")
2268 first_arc = value[0]
2269 if first_arc in (0, 1):
2270 if not (0 <= value[1] <= 39):
2271 raise InvalidOID("second arc is too wide")
2272 elif first_arc == 2:
2275 raise InvalidOID("unacceptable first arc value")
2277 raise InvalidValueType((self.__class__, str, tuple))
2281 return self._value is not None
2284 obj = self.__class__()
2285 obj._value = self._value
2287 obj._expl = self._expl
2288 obj.default = self.default
2289 obj.optional = self.optional
2290 obj.offset = self.offset
2291 obj.llen = self.llen
2292 obj.vlen = self.vlen
2296 self._assert_ready()
2297 return iter(self._value)
2300 return ".".join(str(arc) for arc in self._value or ())
2303 self._assert_ready()
2306 bytes(self._expl or b"") +
2307 str(self._value).encode("ascii"),
2310 def __eq__(self, their):
2311 if isinstance(their, tuple):
2312 return self._value == their
2313 if not issubclass(their.__class__, ObjectIdentifier):
2316 self.tag == their.tag and
2317 self._expl == their._expl and
2318 self._value == their._value
2321 def __lt__(self, their):
2322 return self._value < their._value
2332 return self.__class__(
2334 impl=self.tag if impl is None else impl,
2335 expl=self._expl if expl is None else expl,
2336 default=self.default if default is None else default,
2337 optional=self.optional if optional is None else optional,
2341 self._assert_ready()
2343 first_value = value[1]
2344 first_arc = value[0]
2347 elif first_arc == 1:
2349 elif first_arc == 2:
2351 else: # pragma: no cover
2352 raise RuntimeError("invalid arc is stored")
2353 octets = [zero_ended_encode(first_value)]
2354 for arc in value[2:]:
2355 octets.append(zero_ended_encode(arc))
2356 v = b"".join(octets)
2357 return b"".join((self.tag, len_encode(len(v)), v))
2359 def _decode(self, tlv, offset=0, decode_path=()):
2361 t, _, lv = tag_strip(tlv)
2362 except DecodeError as err:
2363 raise err.__class__(
2365 klass=self.__class__,
2366 decode_path=decode_path,
2371 klass=self.__class__,
2372 decode_path=decode_path,
2376 l, llen, v = len_decode(lv)
2377 except DecodeError as err:
2378 raise err.__class__(
2380 klass=self.__class__,
2381 decode_path=decode_path,
2385 raise NotEnoughData(
2386 "encoded length is longer than data",
2387 klass=self.__class__,
2388 decode_path=decode_path,
2392 raise NotEnoughData(
2394 klass=self.__class__,
2395 decode_path=decode_path,
2398 v, tail = v[:l], v[l:]
2404 octet = indexbytes(v, i)
2405 arc = (arc << 7) | (octet & 0x7F)
2406 if octet & 0x80 == 0:
2414 klass=self.__class__,
2415 decode_path=decode_path,
2419 second_arc = arcs[0]
2420 if 0 <= second_arc <= 39:
2422 elif 40 <= second_arc <= 79:
2428 obj = self.__class__(
2429 value=tuple([first_arc, second_arc] + arcs[1:]),
2432 default=self.default,
2433 optional=self.optional,
2434 _decoded=(offset, llen, l),
2439 return pp_console_row(next(self.pps()))
2441 def pps(self, decode_path=()):
2443 asn1_type_name=self.asn1_type_name,
2444 obj_name=self.__class__.__name__,
2445 decode_path=decode_path,
2446 value=str(self) if self.ready else None,
2447 optional=self.optional,
2448 default=self == self.default,
2449 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2450 expl=None if self._expl is None else tag_decode(self._expl),
2455 expl_offset=self.expl_offset if self.expled else None,
2456 expl_tlen=self.expl_tlen if self.expled else None,
2457 expl_llen=self.expl_llen if self.expled else None,
2458 expl_vlen=self.expl_vlen if self.expled else None,
2462 class Enumerated(Integer):
2463 """``ENUMERATED`` integer type
2465 This type is identical to :py:class:`pyderasn.Integer`, but requires
2466 schema to be specified and does not accept values missing from it.
2469 tag_default = tag_encode(10)
2470 asn1_type_name = "ENUMERATED"
2481 bounds=None, # dummy argument, workability for Integer.decode
2483 super(Enumerated, self).__init__(
2492 if len(self.specs) == 0:
2493 raise ValueError("schema must be specified")
2495 def _value_sanitize(self, value):
2496 if isinstance(value, self.__class__):
2497 value = value._value
2498 elif isinstance(value, integer_types):
2499 if value not in list(self.specs.values()):
2501 "unknown integer value: %s" % value,
2502 klass=self.__class__,
2504 elif isinstance(value, string_types):
2505 value = self.specs.get(value)
2507 raise ObjUnknown("integer value: %s" % value)
2509 raise InvalidValueType((self.__class__, int, str))
2513 obj = self.__class__(_specs=self.specs)
2514 obj._value = self._value
2515 obj._bound_min = self._bound_min
2516 obj._bound_max = self._bound_max
2518 obj._expl = self._expl
2519 obj.default = self.default
2520 obj.optional = self.optional
2521 obj.offset = self.offset
2522 obj.llen = self.llen
2523 obj.vlen = self.vlen
2535 return self.__class__(
2537 impl=self.tag if impl is None else impl,
2538 expl=self._expl if expl is None else expl,
2539 default=self.default if default is None else default,
2540 optional=self.optional if optional is None else optional,
2545 class CommonString(OctetString):
2546 """Common class for all strings
2548 Everything resembles :py:class:`pyderasn.OctetString`, except
2549 ability to deal with unicode text strings.
2551 >>> hexenc("привет мир".encode("utf-8"))
2552 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2553 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2555 >>> s = UTF8String("привет мир")
2556 UTF8String UTF8String привет мир
2558 'привет мир'
2559 >>> hexenc(bytes(s))
2560 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2562 >>> PrintableString("привет мир")
2563 Traceback (most recent call last):
2564 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2566 >>> BMPString("ада", bounds=(2, 2))
2567 Traceback (most recent call last):
2568 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2569 >>> s = BMPString("ад", bounds=(2, 2))
2572 >>> hexenc(bytes(s))
2580 * - :py:class:`pyderasn.UTF8String`
2582 * - :py:class:`pyderasn.NumericString`
2584 * - :py:class:`pyderasn.PrintableString`
2586 * - :py:class:`pyderasn.TeletexString`
2588 * - :py:class:`pyderasn.T61String`
2590 * - :py:class:`pyderasn.VideotexString`
2592 * - :py:class:`pyderasn.IA5String`
2594 * - :py:class:`pyderasn.GraphicString`
2596 * - :py:class:`pyderasn.VisibleString`
2598 * - :py:class:`pyderasn.ISO646String`
2600 * - :py:class:`pyderasn.GeneralString`
2602 * - :py:class:`pyderasn.UniversalString`
2604 * - :py:class:`pyderasn.BMPString`
2607 __slots__ = ("encoding",)
2609 def _value_sanitize(self, value):
2611 value_decoded = None
2612 if isinstance(value, self.__class__):
2613 value_raw = value._value
2614 elif isinstance(value, text_type):
2615 value_decoded = value
2616 elif isinstance(value, binary_type):
2619 raise InvalidValueType((self.__class__, text_type, binary_type))
2621 value_decoded.encode(self.encoding)
2622 if value_raw is None else value_raw
2625 value_raw.decode(self.encoding)
2626 if value_decoded is None else value_decoded
2628 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2636 def __eq__(self, their):
2637 if isinstance(their, binary_type):
2638 return self._value == their
2639 if isinstance(their, text_type):
2640 return self._value == their.encode(self.encoding)
2641 if not isinstance(their, self.__class__):
2644 self._value == their._value and
2645 self.tag == their.tag and
2646 self._expl == their._expl
2649 def __unicode__(self):
2651 return self._value.decode(self.encoding)
2652 return text_type(self._value)
2655 return pp_console_row(next(self.pps(no_unicode=PY2)))
2657 def pps(self, decode_path=(), no_unicode=False):
2660 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2662 asn1_type_name=self.asn1_type_name,
2663 obj_name=self.__class__.__name__,
2664 decode_path=decode_path,
2666 optional=self.optional,
2667 default=self == self.default,
2668 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2669 expl=None if self._expl is None else tag_decode(self._expl),
2677 class UTF8String(CommonString):
2679 tag_default = tag_encode(12)
2681 asn1_type_name = "UTF8String"
2684 class NumericString(CommonString):
2686 tag_default = tag_encode(18)
2688 asn1_type_name = "NumericString"
2691 class PrintableString(CommonString):
2693 tag_default = tag_encode(19)
2695 asn1_type_name = "PrintableString"
2698 class TeletexString(CommonString):
2700 tag_default = tag_encode(20)
2702 asn1_type_name = "TeletexString"
2705 class T61String(TeletexString):
2707 asn1_type_name = "T61String"
2710 class VideotexString(CommonString):
2712 tag_default = tag_encode(21)
2713 encoding = "iso-8859-1"
2714 asn1_type_name = "VideotexString"
2717 class IA5String(CommonString):
2719 tag_default = tag_encode(22)
2721 asn1_type_name = "IA5"
2724 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
2725 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
2726 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
2729 class UTCTime(CommonString):
2730 """``UTCTime`` datetime type
2732 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2733 UTCTime UTCTime 2017-09-30T22:07:50
2739 datetime.datetime(2017, 9, 30, 22, 7, 50)
2740 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2741 datetime.datetime(1957, 9, 30, 22, 7, 50)
2744 tag_default = tag_encode(23)
2746 asn1_type_name = "UTCTime"
2748 fmt = "%y%m%d%H%M%SZ"
2758 bounds=None, # dummy argument, workability for OctetString.decode
2761 :param value: set the value. Either datetime type, or
2762 :py:class:`pyderasn.UTCTime` object
2763 :param bytes impl: override default tag with ``IMPLICIT`` one
2764 :param bytes expl: override default tag with ``EXPLICIT`` one
2765 :param default: set default value. Type same as in ``value``
2766 :param bool optional: is object ``OPTIONAL`` in sequence
2768 super(UTCTime, self).__init__(
2776 if value is not None:
2777 self._value = self._value_sanitize(value)
2778 if default is not None:
2779 default = self._value_sanitize(default)
2780 self.default = self.__class__(
2785 if self._value is None:
2786 self._value = default
2788 def _value_sanitize(self, value):
2789 if isinstance(value, self.__class__):
2791 if isinstance(value, datetime):
2792 return value.strftime(self.fmt).encode("ascii")
2793 if isinstance(value, binary_type):
2794 value_decoded = value.decode("ascii")
2795 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
2797 datetime.strptime(value_decoded, self.fmt)
2799 raise DecodeError("invalid UTCTime format")
2802 raise DecodeError("invalid UTCTime length")
2803 raise InvalidValueType((self.__class__, datetime))
2805 def __eq__(self, their):
2806 if isinstance(their, binary_type):
2807 return self._value == their
2808 if isinstance(their, datetime):
2809 return self.todatetime() == their
2810 if not isinstance(their, self.__class__):
2813 self._value == their._value and
2814 self.tag == their.tag and
2815 self._expl == their._expl
2818 def todatetime(self):
2819 """Convert to datetime
2823 Pay attention that UTCTime can not hold full year, so all years
2824 having < 50 years are treated as 20xx, 19xx otherwise, according
2825 to X.509 recomendation.
2827 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2828 year = value.year % 100
2830 year=(2000 + year) if year < 50 else (1900 + year),
2834 minute=value.minute,
2835 second=value.second,
2839 return pp_console_row(next(self.pps()))
2841 def pps(self, decode_path=()):
2843 asn1_type_name=self.asn1_type_name,
2844 obj_name=self.__class__.__name__,
2845 decode_path=decode_path,
2846 value=self.todatetime().isoformat() if self.ready else None,
2847 optional=self.optional,
2848 default=self == self.default,
2849 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2850 expl=None if self._expl is None else tag_decode(self._expl),
2858 class GeneralizedTime(UTCTime):
2859 """``GeneralizedTime`` datetime type
2861 This type is similar to :py:class:`pyderasn.UTCTime`.
2863 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2864 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2866 '20170930220750.000123Z'
2867 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2868 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2871 tag_default = tag_encode(24)
2872 asn1_type_name = "GeneralizedTime"
2874 fmt = "%Y%m%d%H%M%SZ"
2875 fmt_ms = "%Y%m%d%H%M%S.%fZ"
2877 def _value_sanitize(self, value):
2878 if isinstance(value, self.__class__):
2880 if isinstance(value, datetime):
2881 return value.strftime(
2882 self.fmt_ms if value.microsecond > 0 else self.fmt
2884 if isinstance(value, binary_type):
2885 value_decoded = value.decode("ascii")
2886 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
2888 datetime.strptime(value_decoded, self.fmt)
2891 "invalid GeneralizedTime (without ms) format",
2894 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
2896 datetime.strptime(value_decoded, self.fmt_ms)
2899 "invalid GeneralizedTime (with ms) format",
2904 "invalid GeneralizedTime length",
2905 klass=self.__class__,
2907 raise InvalidValueType((self.__class__, datetime))
2909 def todatetime(self):
2910 value = self._value.decode("ascii")
2911 if len(value) == LEN_YYYYMMDDHHMMSSZ:
2912 return datetime.strptime(value, self.fmt)
2913 return datetime.strptime(value, self.fmt_ms)
2916 class GraphicString(CommonString):
2918 tag_default = tag_encode(25)
2919 encoding = "iso-8859-1"
2920 asn1_type_name = "GraphicString"
2923 class VisibleString(CommonString):
2925 tag_default = tag_encode(26)
2927 asn1_type_name = "VisibleString"
2930 class ISO646String(VisibleString):
2932 asn1_type_name = "ISO646String"
2935 class GeneralString(CommonString):
2937 tag_default = tag_encode(27)
2938 encoding = "iso-8859-1"
2939 asn1_type_name = "GeneralString"
2942 class UniversalString(CommonString):
2944 tag_default = tag_encode(28)
2945 encoding = "utf-32-be"
2946 asn1_type_name = "UniversalString"
2949 class BMPString(CommonString):
2951 tag_default = tag_encode(30)
2952 encoding = "utf-16-be"
2953 asn1_type_name = "BMPString"
2957 """``CHOICE`` special type
2961 class GeneralName(Choice):
2963 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2964 ('dNSName', IA5String(impl=tag_ctxp(2))),
2967 >>> gn = GeneralName()
2969 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2970 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2971 >>> gn["dNSName"] = IA5String("bar.baz")
2972 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2973 >>> gn["rfc822Name"]
2976 [2] IA5String IA5 bar.baz
2979 >>> gn.value == gn["dNSName"]
2982 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2984 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2985 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2987 __slots__ = ("specs",)
2989 asn1_type_name = "CHOICE"
3002 :param value: set the value. Either ``(choice, value)`` tuple, or
3003 :py:class:`pyderasn.Choice` object
3004 :param bytes impl: can not be set, do **not** use it
3005 :param bytes expl: override default tag with ``EXPLICIT`` one
3006 :param default: set default value. Type same as in ``value``
3007 :param bool optional: is object ``OPTIONAL`` in sequence
3009 if impl is not None:
3010 raise ValueError("no implicit tag allowed for CHOICE")
3011 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3013 schema = getattr(self, "schema", ())
3014 if len(schema) == 0:
3015 raise ValueError("schema must be specified")
3017 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3020 if value is not None:
3021 self._value = self._value_sanitize(value)
3022 if default is not None:
3023 default_value = self._value_sanitize(default)
3024 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3025 default_obj.specs = self.specs
3026 default_obj._value = default_value
3027 self.default = default_obj
3029 self._value = default_obj.copy()._value
3031 def _value_sanitize(self, value):
3032 if isinstance(value, self.__class__):
3034 if isinstance(value, tuple) and len(value) == 2:
3036 spec = self.specs.get(choice)
3038 raise ObjUnknown(choice)
3039 if not isinstance(obj, spec.__class__):
3040 raise InvalidValueType((spec,))
3041 return (choice, spec(obj))
3042 raise InvalidValueType((self.__class__, tuple))
3046 return self._value is not None and self._value[1].ready
3049 obj = self.__class__(schema=self.specs)
3050 obj._expl = self._expl
3051 obj.default = self.default
3052 obj.optional = self.optional
3053 obj.offset = self.offset
3054 obj.llen = self.llen
3055 obj.vlen = self.vlen
3057 if value is not None:
3058 obj._value = (value[0], value[1].copy())
3061 def __eq__(self, their):
3062 if isinstance(their, tuple) and len(their) == 2:
3063 return self._value == their
3064 if not isinstance(their, self.__class__):
3067 self.specs == their.specs and
3068 self._value == their._value
3078 return self.__class__(
3081 expl=self._expl if expl is None else expl,
3082 default=self.default if default is None else default,
3083 optional=self.optional if optional is None else optional,
3088 self._assert_ready()
3089 return self._value[0]
3093 self._assert_ready()
3094 return self._value[1]
3096 def __getitem__(self, key):
3097 if key not in self.specs:
3098 raise ObjUnknown(key)
3099 if self._value is None:
3101 choice, value = self._value
3106 def __setitem__(self, key, value):
3107 spec = self.specs.get(key)
3109 raise ObjUnknown(key)
3110 if not isinstance(value, spec.__class__):
3111 raise InvalidValueType((spec.__class__,))
3112 self._value = (key, spec(value))
3120 return self._value[1].decoded if self.ready else False
3123 self._assert_ready()
3124 return self._value[1].encode()
3126 def _decode(self, tlv, offset=0, decode_path=()):
3127 for choice, spec in self.specs.items():
3129 value, tail = spec.decode(
3133 decode_path=decode_path + (choice,),
3137 obj = self.__class__(
3140 default=self.default,
3141 optional=self.optional,
3142 _decoded=(offset, 0, value.tlvlen),
3144 obj._value = (choice, value)
3147 klass=self.__class__,
3148 decode_path=decode_path,
3153 value = pp_console_row(next(self.pps()))
3155 value = "%s[%r]" % (value, self.value)
3158 def pps(self, decode_path=()):
3160 asn1_type_name=self.asn1_type_name,
3161 obj_name=self.__class__.__name__,
3162 decode_path=decode_path,
3163 value=self.choice if self.ready else None,
3164 optional=self.optional,
3165 default=self == self.default,
3166 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3167 expl=None if self._expl is None else tag_decode(self._expl),
3174 yield self.value.pps(decode_path=decode_path + (self.choice,))
3177 class PrimitiveTypes(Choice):
3178 """Predefined ``CHOICE`` for all generic primitive types
3180 It could be useful for general decoding of some unspecified values:
3182 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3183 OCTET STRING 3 bytes 666f6f
3184 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3188 schema = tuple((klass.__name__, klass()) for klass in (
3213 """``ANY`` special type
3215 >>> Any(Integer(-123))
3217 >>> a = Any(OctetString(b"hello world").encode())
3218 ANY 040b68656c6c6f20776f726c64
3219 >>> hexenc(bytes(a))
3220 b'0x040x0bhello world'
3223 tag_default = tag_encode(0)
3224 asn1_type_name = "ANY"
3234 :param value: set the value. Either any kind of pyderasn's
3235 **ready** object, or bytes. Pay attention that
3236 **no** validation is performed is raw binary value
3238 :param bytes expl: override default tag with ``EXPLICIT`` one
3239 :param bool optional: is object ``OPTIONAL`` in sequence
3241 super(Any, self).__init__(None, expl, None, optional, _decoded)
3242 self._value = None if value is None else self._value_sanitize(value)
3244 def _value_sanitize(self, value):
3245 if isinstance(value, self.__class__):
3247 if isinstance(value, Obj):
3248 return value.encode()
3249 if isinstance(value, binary_type):
3251 raise InvalidValueType((self.__class__, Obj, binary_type))
3255 return self._value is not None
3258 obj = self.__class__()
3259 obj._value = self._value
3261 obj._expl = self._expl
3262 obj.optional = self.optional
3263 obj.offset = self.offset
3264 obj.llen = self.llen
3265 obj.vlen = self.vlen
3268 def __eq__(self, their):
3269 if isinstance(their, binary_type):
3270 return self._value == their
3271 if issubclass(their.__class__, Any):
3272 return self._value == their._value
3281 return self.__class__(
3283 expl=self._expl if expl is None else expl,
3284 optional=self.optional if optional is None else optional,
3287 def __bytes__(self):
3288 self._assert_ready()
3296 self._assert_ready()
3299 def _decode(self, tlv, offset=0, decode_path=()):
3301 t, tlen, lv = tag_strip(tlv)
3302 l, llen, v = len_decode(lv)
3303 except DecodeError as err:
3304 raise err.__class__(
3306 klass=self.__class__,
3307 decode_path=decode_path,
3311 raise NotEnoughData(
3312 "encoded length is longer than data",
3313 klass=self.__class__,
3314 decode_path=decode_path,
3317 tlvlen = tlen + llen + l
3318 v, tail = tlv[:tlvlen], v[l:]
3319 obj = self.__class__(
3322 optional=self.optional,
3323 _decoded=(offset, 0, tlvlen),
3329 return pp_console_row(next(self.pps()))
3331 def pps(self, decode_path=()):
3333 asn1_type_name=self.asn1_type_name,
3334 obj_name=self.__class__.__name__,
3335 decode_path=decode_path,
3336 blob=self._value if self.ready else None,
3337 optional=self.optional,
3338 default=self == self.default,
3339 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3340 expl=None if self._expl is None else tag_decode(self._expl),
3345 expl_offset=self.expl_offset if self.expled else None,
3346 expl_tlen=self.expl_tlen if self.expled else None,
3347 expl_llen=self.expl_llen if self.expled else None,
3348 expl_vlen=self.expl_vlen if self.expled else None,
3352 ########################################################################
3353 # ASN.1 constructed types
3354 ########################################################################
3356 class Sequence(Obj):
3357 """``SEQUENCE`` structure type
3359 You have to make specification of sequence::
3361 class Extension(Sequence):
3363 ("extnID", ObjectIdentifier()),
3364 ("critical", Boolean(default=False)),
3365 ("extnValue", OctetString()),
3368 Then, you can work with it as with dictionary.
3370 >>> ext = Extension()
3371 >>> Extension().specs
3373 ('extnID', OBJECT IDENTIFIER),
3374 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3375 ('extnValue', OCTET STRING),
3377 >>> ext["extnID"] = "1.2.3"
3378 Traceback (most recent call last):
3379 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3380 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3382 You can know if sequence is ready to be encoded:
3387 Traceback (most recent call last):
3388 pyderasn.ObjNotReady: object is not ready: extnValue
3389 >>> ext["extnValue"] = OctetString(b"foobar")
3393 Value you want to assign, must have the same **type** as in
3394 corresponding specification, but it can have different tags,
3395 optional/default attributes -- they will be taken from specification
3398 class TBSCertificate(Sequence):
3400 ("version", Version(expl=tag_ctxc(0), default="v1")),
3403 >>> tbs = TBSCertificate()
3404 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3406 You can know if value exists/set in the sequence and take its value:
3408 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3411 OBJECT IDENTIFIER 1.2.3
3413 But pay attention that if value has default, then it won't be (not
3414 in) in the sequence (because ``DEFAULT`` must not be encoded in
3415 DER), but you can read its value:
3417 >>> "critical" in ext, ext["critical"]
3418 (False, BOOLEAN False)
3419 >>> ext["critical"] = Boolean(True)
3420 >>> "critical" in ext, ext["critical"]
3421 (True, BOOLEAN True)
3423 All defaulted values are always optional.
3427 When decoded DER contains defaulted value inside, then
3428 technically this is not valid DER encoding. But we allow
3429 and pass it. Of course reencoding of that kind of DER will
3430 result in different binary representation (validly without
3431 defaulted value inside).
3433 Two sequences are equal if they have equal specification (schema),
3434 implicit/explicit tagging and the same values.
3436 __slots__ = ("specs",)
3437 tag_default = tag_encode(form=TagFormConstructed, num=16)
3438 asn1_type_name = "SEQUENCE"
3450 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3452 schema = getattr(self, "schema", ())
3454 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3457 if value is not None:
3458 self._value = self._value_sanitize(value)
3459 if default is not None:
3460 default_value = self._value_sanitize(default)
3461 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3462 default_obj.specs = self.specs
3463 default_obj._value = default_value
3464 self.default = default_obj
3466 self._value = default_obj.copy()._value
3468 def _value_sanitize(self, value):
3469 if not issubclass(value.__class__, Sequence):
3470 raise InvalidValueType((Sequence,))
3475 for name, spec in self.specs.items():
3476 value = self._value.get(name)
3487 obj = self.__class__(schema=self.specs)
3489 obj._expl = self._expl
3490 obj.default = self.default
3491 obj.optional = self.optional
3492 obj.offset = self.offset
3493 obj.llen = self.llen
3494 obj.vlen = self.vlen
3495 obj._value = {k: v.copy() for k, v in self._value.items()}
3498 def __eq__(self, their):
3499 if not isinstance(their, self.__class__):
3502 self.specs == their.specs and
3503 self.tag == their.tag and
3504 self._expl == their._expl and
3505 self._value == their._value
3516 return self.__class__(
3519 impl=self.tag if impl is None else impl,
3520 expl=self._expl if expl is None else expl,
3521 default=self.default if default is None else default,
3522 optional=self.optional if optional is None else optional,
3525 def __contains__(self, key):
3526 return key in self._value
3528 def __setitem__(self, key, value):
3529 spec = self.specs.get(key)
3531 raise ObjUnknown(key)
3533 self._value.pop(key, None)
3535 if not isinstance(value, spec.__class__):
3536 raise InvalidValueType((spec.__class__,))
3537 value = spec(value=value)
3538 if spec.default is not None and value == spec.default:
3539 self._value.pop(key, None)
3541 self._value[key] = value
3543 def __getitem__(self, key):
3544 value = self._value.get(key)
3545 if value is not None:
3547 spec = self.specs.get(key)
3549 raise ObjUnknown(key)
3550 if spec.default is not None:
3554 def _encoded_values(self):
3556 for name, spec in self.specs.items():
3557 value = self._value.get(name)
3561 raise ObjNotReady(name)
3562 raws.append(value.encode())
3566 v = b"".join(self._encoded_values())
3567 return b"".join((self.tag, len_encode(len(v)), v))
3569 def _decode(self, tlv, offset=0, decode_path=()):
3571 t, tlen, lv = tag_strip(tlv)
3572 except DecodeError as err:
3573 raise err.__class__(
3575 klass=self.__class__,
3576 decode_path=decode_path,
3581 klass=self.__class__,
3582 decode_path=decode_path,
3586 l, llen, v = len_decode(lv)
3587 except DecodeError as err:
3588 raise err.__class__(
3590 klass=self.__class__,
3591 decode_path=decode_path,
3595 raise NotEnoughData(
3596 "encoded length is longer than data",
3597 klass=self.__class__,
3598 decode_path=decode_path,
3601 v, tail = v[:l], v[l:]
3602 sub_offset = offset + tlen + llen
3604 for name, spec in self.specs.items():
3605 if len(v) == 0 and spec.optional:
3608 value, v_tail = spec.decode(
3612 decode_path=decode_path + (name,),
3618 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3620 if spec.default is not None and value == spec.default:
3621 # Encoded default values are not valid in DER,
3622 # but we allow that anyway
3624 values[name] = value
3628 klass=self.__class__,
3629 decode_path=decode_path,
3632 obj = self.__class__(
3636 default=self.default,
3637 optional=self.optional,
3638 _decoded=(offset, llen, l),
3644 value = pp_console_row(next(self.pps()))
3646 for name in self.specs:
3647 _value = self._value.get(name)
3650 cols.append(repr(_value))
3651 return "%s[%s]" % (value, ", ".join(cols))
3653 def pps(self, decode_path=()):
3655 asn1_type_name=self.asn1_type_name,
3656 obj_name=self.__class__.__name__,
3657 decode_path=decode_path,
3658 optional=self.optional,
3659 default=self == self.default,
3660 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3661 expl=None if self._expl is None else tag_decode(self._expl),
3666 expl_offset=self.expl_offset if self.expled else None,
3667 expl_tlen=self.expl_tlen if self.expled else None,
3668 expl_llen=self.expl_llen if self.expled else None,
3669 expl_vlen=self.expl_vlen if self.expled else None,
3671 for name in self.specs:
3672 value = self._value.get(name)
3675 yield value.pps(decode_path=decode_path + (name,))
3678 class Set(Sequence):
3679 """``SET`` structure type
3681 Its usage is identical to :py:class:`pyderasn.Sequence`.
3684 tag_default = tag_encode(form=TagFormConstructed, num=17)
3685 asn1_type_name = "SET"
3688 raws = self._encoded_values()
3691 return b"".join((self.tag, len_encode(len(v)), v))
3693 def _decode(self, tlv, offset=0, decode_path=()):
3695 t, tlen, lv = tag_strip(tlv)
3696 except DecodeError as err:
3697 raise err.__class__(
3699 klass=self.__class__,
3700 decode_path=decode_path,
3705 klass=self.__class__,
3706 decode_path=decode_path,
3710 l, llen, v = len_decode(lv)
3711 except DecodeError as err:
3712 raise err.__class__(
3714 klass=self.__class__,
3715 decode_path=decode_path,
3719 raise NotEnoughData(
3720 "encoded length is longer than data",
3721 klass=self.__class__,
3724 v, tail = v[:l], v[l:]
3725 sub_offset = offset + tlen + llen
3727 specs_items = self.specs.items
3729 for name, spec in specs_items():
3731 value, v_tail = spec.decode(
3735 decode_path=decode_path + (name,),
3740 value.expl_tlvlen if value.expled else value.tlvlen
3743 if spec.default is None or value != spec.default: # pragma: no cover
3744 # SeqMixing.test_encoded_default_accepted covers that place
3745 values[name] = value
3749 klass=self.__class__,
3750 decode_path=decode_path,
3753 obj = self.__class__(
3757 default=self.default,
3758 optional=self.optional,
3759 _decoded=(offset, llen, l),
3765 class SequenceOf(Obj):
3766 """``SEQUENCE OF`` sequence type
3768 For that kind of type you must specify the object it will carry on
3769 (bounds are for example here, not required)::
3771 class Ints(SequenceOf):
3776 >>> ints.append(Integer(123))
3777 >>> ints.append(Integer(234))
3779 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3780 >>> [int(i) for i in ints]
3782 >>> ints.append(Integer(345))
3783 Traceback (most recent call last):
3784 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3787 >>> ints[1] = Integer(345)
3789 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3791 Also you can initialize sequence with preinitialized values:
3793 >>> ints = Ints([Integer(123), Integer(234)])
3795 __slots__ = ("spec", "_bound_min", "_bound_max")
3796 tag_default = tag_encode(form=TagFormConstructed, num=16)
3797 asn1_type_name = "SEQUENCE OF"
3810 super(SequenceOf, self).__init__(
3818 schema = getattr(self, "schema", None)
3820 raise ValueError("schema must be specified")
3822 self._bound_min, self._bound_max = getattr(
3826 ) if bounds is None else bounds
3828 if value is not None:
3829 self._value = self._value_sanitize(value)
3830 if default is not None:
3831 default_value = self._value_sanitize(default)
3832 default_obj = self.__class__(
3837 default_obj._value = default_value
3838 self.default = default_obj
3840 self._value = default_obj.copy()._value
3842 def _value_sanitize(self, value):
3843 if issubclass(value.__class__, SequenceOf):
3844 value = value._value
3845 elif hasattr(value, "__iter__"):
3848 raise InvalidValueType((self.__class__, iter))
3849 if not self._bound_min <= len(value) <= self._bound_max:
3850 raise BoundsError(self._bound_min, len(value), self._bound_max)
3852 if not isinstance(v, self.spec.__class__):
3853 raise InvalidValueType((self.spec.__class__,))
3858 return all(v.ready for v in self._value)
3861 obj = self.__class__(schema=self.spec)
3862 obj._bound_min = self._bound_min
3863 obj._bound_max = self._bound_max
3865 obj._expl = self._expl
3866 obj.default = self.default
3867 obj.optional = self.optional
3868 obj.offset = self.offset
3869 obj.llen = self.llen
3870 obj.vlen = self.vlen
3871 obj._value = [v.copy() for v in self._value]
3874 def __eq__(self, their):
3875 if isinstance(their, self.__class__):
3877 self.spec == their.spec and
3878 self.tag == their.tag and
3879 self._expl == their._expl and
3880 self._value == their._value
3882 if hasattr(their, "__iter__"):
3883 return self._value == list(their)
3895 return self.__class__(
3899 (self._bound_min, self._bound_max)
3900 if bounds is None else bounds
3902 impl=self.tag if impl is None else impl,
3903 expl=self._expl if expl is None else expl,
3904 default=self.default if default is None else default,
3905 optional=self.optional if optional is None else optional,
3908 def __contains__(self, key):
3909 return key in self._value
3911 def append(self, value):
3912 if not isinstance(value, self.spec.__class__):
3913 raise InvalidValueType((self.spec.__class__,))
3914 if len(self._value) + 1 > self._bound_max:
3917 len(self._value) + 1,
3920 self._value.append(value)
3923 self._assert_ready()
3924 return iter(self._value)
3927 self._assert_ready()
3928 return len(self._value)
3930 def __setitem__(self, key, value):
3931 if not isinstance(value, self.spec.__class__):
3932 raise InvalidValueType((self.spec.__class__,))
3933 self._value[key] = self.spec(value=value)
3935 def __getitem__(self, key):
3936 return self._value[key]
3938 def _encoded_values(self):
3939 return [v.encode() for v in self._value]
3942 v = b"".join(self._encoded_values())
3943 return b"".join((self.tag, len_encode(len(v)), v))
3945 def _decode(self, tlv, offset=0, decode_path=()):
3947 t, tlen, lv = tag_strip(tlv)
3948 except DecodeError as err:
3949 raise err.__class__(
3951 klass=self.__class__,
3952 decode_path=decode_path,
3957 klass=self.__class__,
3958 decode_path=decode_path,
3962 l, llen, v = len_decode(lv)
3963 except DecodeError as err:
3964 raise err.__class__(
3966 klass=self.__class__,
3967 decode_path=decode_path,
3971 raise NotEnoughData(
3972 "encoded length is longer than data",
3973 klass=self.__class__,
3974 decode_path=decode_path,
3977 v, tail = v[:l], v[l:]
3978 sub_offset = offset + tlen + llen
3982 value, v_tail = spec.decode(
3986 decode_path=decode_path + (str(len(_value)),),
3988 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3990 _value.append(value)
3991 obj = self.__class__(
3994 bounds=(self._bound_min, self._bound_max),
3997 default=self.default,
3998 optional=self.optional,
3999 _decoded=(offset, llen, l),
4005 pp_console_row(next(self.pps())),
4006 ", ".join(repr(v) for v in self._value),
4009 def pps(self, decode_path=()):
4011 asn1_type_name=self.asn1_type_name,
4012 obj_name=self.__class__.__name__,
4013 decode_path=decode_path,
4014 optional=self.optional,
4015 default=self == self.default,
4016 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4017 expl=None if self._expl is None else tag_decode(self._expl),
4022 expl_offset=self.expl_offset if self.expled else None,
4023 expl_tlen=self.expl_tlen if self.expled else None,
4024 expl_llen=self.expl_llen if self.expled else None,
4025 expl_vlen=self.expl_vlen if self.expled else None,
4027 for i, value in enumerate(self._value):
4028 yield value.pps(decode_path=decode_path + (str(i),))
4031 class SetOf(SequenceOf):
4032 """``SET OF`` sequence type
4034 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4037 tag_default = tag_encode(form=TagFormConstructed, num=17)
4038 asn1_type_name = "SET OF"
4041 raws = self._encoded_values()
4044 return b"".join((self.tag, len_encode(len(v)), v))
4047 def obj_by_path(pypath): # pragma: no cover
4048 """Import object specified as string Python path
4050 Modules must be separated from classes/functions with ``:``.
4052 >>> obj_by_path("foo.bar:Baz")
4053 <class 'foo.bar.Baz'>
4054 >>> obj_by_path("foo.bar:Baz.boo")
4055 <classmethod 'foo.bar.Baz.boo'>
4057 mod, objs = pypath.rsplit(":", 1)
4058 from importlib import import_module
4059 obj = import_module(mod)
4060 for obj_name in objs.split("."):
4061 obj = getattr(obj, obj_name)
4065 def main(): # pragma: no cover
4067 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4068 parser.add_argument(
4070 help="Python path to dictionary with OIDs",
4072 parser.add_argument(
4074 help="Python path to schema definition to use",
4076 parser.add_argument(
4078 type=argparse.FileType("rb"),
4079 help="Path to DER file you want to decode",
4081 args = parser.parse_args()
4082 der = memoryview(args.DERFile.read())
4083 args.DERFile.close()
4084 oids = obj_by_path(args.oids) if args.oids else {}
4086 schema = obj_by_path(args.schema)
4087 from functools import partial
4088 pprinter = partial(pprint, big_blobs=True)
4090 # All of this below is a big hack with self references
4091 choice = PrimitiveTypes()
4092 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4093 choice.specs["SetOf"] = SetOf(schema=choice)
4095 choice.specs["SequenceOf%d" % i] = SequenceOf(
4099 choice.specs["Any"] = Any()
4101 # Class name equals to type name, to omit it from output
4102 class SEQUENCEOF(SequenceOf):
4105 schema = SEQUENCEOF()
4107 def pprint_any(obj, oids=None):
4108 def _pprint_pps(pps):
4110 if hasattr(pp, "_fields"):
4111 if pp.asn1_type_name == Choice.asn1_type_name:
4113 pp_kwargs = pp._asdict()
4114 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4115 pp = _pp(**pp_kwargs)
4116 yield pp_console_row(
4122 for row in pp_console_blob(pp):
4125 for row in _pprint_pps(pp):
4127 return "\n".join(_pprint_pps(obj.pps()))
4128 pprinter = pprint_any
4129 obj, tail = schema().decode(der)
4130 print(pprinter(obj, oids=oids))
4132 print("\nTrailing data: %s" % hexenc(tail))
4135 if __name__ == "__main__":