3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER codec with abstract structures
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments takes
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two object of the same type, but with different implicit/explicit tags
94 You can get objects effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
141 Some objects give ability to set value size constraints. This is either
142 possible integer value, or allowed length of various strings and
143 sequences. Constraints are set in the following way::
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
150 For simplicity you can also set bounds the following way::
152 bounded_x = X(bounds=(MIN, MAX))
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
160 All objects have ``ready`` boolean property, that tells if it is ready
161 to be encoded. If that kind of action is performed on unready object,
162 then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
164 All objects have ``copy()`` method, returning its copy, that can be safely
170 Decoding is performed using ``decode()`` method. ``offset`` optional
171 argument could be used to set initial object's offset in the binary
172 data, for convenience. It returns decoded object and remaining
173 unmarshalled data (tail). Internally all work is done on
174 ``memoryview(data)``, and you can leave returning tail as a memoryview,
175 by specifying ``leavemm=True`` argument.
177 When object is decoded, ``decoded`` property is true and you can safely
178 use following properties:
180 * ``offset`` -- position including initial offset where object's tag starts
181 * ``tlen`` -- length of object's tag
182 * ``llen`` -- length of object's length value
183 * ``vlen`` -- length of object's value
184 * ``tlvlen`` -- length of the whole object
186 Pay attention that those values do **not** include anything related to
187 explicit tag. If you want to know information about it, then use:
188 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
189 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
190 (that actually equals to ordinary ``tlvlen``).
192 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
197 All objects have ``pps()`` method, that is a generator of
198 :py:class:`pyderasn.PP` namedtuple, holding various raw information
199 about the object. If ``pps`` is called on sequences, then all underlying
200 ``PP`` will be yielded.
202 You can use :py:func:`pyderasn.pp_console_row` function, converting
203 those ``PP`` to human readable string. Actually exactly it is used for
204 all object ``repr``. But it is easy to write custom formatters.
206 >>> from pyderasn import pprint
207 >>> encoded = Integer(-12345).encode()
208 >>> obj, tail = Integer().decode(encoded)
209 >>> print(pprint(obj))
210 0 [1,1, 2] INTEGER -12345
217 .. autoclass:: pyderasn.Boolean
222 .. autoclass:: pyderasn.Integer
227 .. autoclass:: pyderasn.BitString
232 .. autoclass:: pyderasn.OctetString
237 .. autoclass:: pyderasn.Null
242 .. autoclass:: pyderasn.ObjectIdentifier
247 .. autoclass:: pyderasn.Enumerated
251 .. autoclass:: pyderasn.CommonString
255 .. autoclass:: pyderasn.UTCTime
256 :members: __init__, todatetime
260 .. autoclass:: pyderasn.GeneralizedTime
267 .. autoclass:: pyderasn.Choice
272 .. autoclass:: PrimitiveTypes
276 .. autoclass:: pyderasn.Any
284 .. autoclass:: pyderasn.Sequence
289 .. autoclass:: pyderasn.Set
294 .. autoclass:: pyderasn.SequenceOf
299 .. autoclass:: pyderasn.SetOf
305 .. autofunction:: pyderasn.hexenc
306 .. autofunction:: pyderasn.hexdec
307 .. autofunction:: pyderasn.tag_encode
308 .. autofunction:: pyderasn.tag_decode
309 .. autofunction:: pyderasn.tag_ctxp
310 .. autofunction:: pyderasn.tag_ctxc
311 .. autoclass:: pyderasn.Obj
314 from codecs import getdecoder
315 from codecs import getencoder
316 from collections import namedtuple
317 from collections import OrderedDict
318 from datetime import datetime
319 from math import ceil
321 from six import add_metaclass
322 from six import binary_type
323 from six import byte2int
324 from six import indexbytes
325 from six import int2byte
326 from six import integer_types
327 from six import iterbytes
329 from six import string_types
330 from six import text_type
331 from six.moves import xrange as six_xrange
372 "TagClassApplication",
376 "TagFormConstructed",
387 TagClassUniversal = 0
388 TagClassApplication = 1 << 6
389 TagClassContext = 1 << 7
390 TagClassPrivate = 1 << 6 | 1 << 7
392 TagFormConstructed = 1 << 5
395 TagClassApplication: "APPLICATION ",
396 TagClassPrivate: "PRIVATE ",
397 TagClassUniversal: "UNIV ",
401 ########################################################################
403 ########################################################################
405 class DecodeError(Exception):
406 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
408 :param str msg: reason of decode failing
409 :param klass: optional exact DecodeError inherited class (like
410 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
411 :py:exc:`InvalidLength`)
412 :param decode_path: tuple of strings. It contains human
413 readable names of the fields through which
414 decoding process has passed
415 :param int offset: binary offset where failure happened
417 super(DecodeError, self).__init__()
420 self.decode_path = decode_path
426 "" if self.klass is None else self.klass.__name__,
428 ("(%s)" % ".".join(self.decode_path))
429 if len(self.decode_path) > 0 else ""
431 ("(at %d)" % self.offset) if self.offset > 0 else "",
437 return "%s(%s)" % (self.__class__.__name__, self)
440 class NotEnoughData(DecodeError):
444 class TagMismatch(DecodeError):
448 class InvalidLength(DecodeError):
452 class InvalidOID(DecodeError):
456 class ObjUnknown(ValueError):
457 def __init__(self, name):
458 super(ObjUnknown, self).__init__()
462 return "object is unknown: %s" % self.name
465 return "%s(%s)" % (self.__class__.__name__, self)
468 class ObjNotReady(ValueError):
469 def __init__(self, name):
470 super(ObjNotReady, self).__init__()
474 return "object is not ready: %s" % self.name
477 return "%s(%s)" % (self.__class__.__name__, self)
480 class InvalidValueType(ValueError):
481 def __init__(self, expected_types):
482 super(InvalidValueType, self).__init__()
483 self.expected_types = expected_types
486 return "invalid value type, expected: %s" % ", ".join(
487 [repr(t) for t in self.expected_types]
491 return "%s(%s)" % (self.__class__.__name__, self)
494 class BoundsError(ValueError):
495 def __init__(self, bound_min, value, bound_max):
496 super(BoundsError, self).__init__()
497 self.bound_min = bound_min
499 self.bound_max = bound_max
502 return "unsatisfied bounds: %s <= %s <= %s" % (
509 return "%s(%s)" % (self.__class__.__name__, self)
512 ########################################################################
514 ########################################################################
516 _hexdecoder = getdecoder("hex")
517 _hexencoder = getencoder("hex")
521 """Binary data to hexadecimal string convert
523 return _hexdecoder(data)[0]
527 """Hexadecimal string to binary data convert
529 return _hexencoder(data)[0].decode("ascii")
532 def int_bytes_len(num, byte_len=8):
535 return int(ceil(float(num.bit_length()) / byte_len))
538 def zero_ended_encode(num):
539 octets = bytearray(int_bytes_len(num, 7))
541 octets[i] = num & 0x7F
545 octets[i] = 0x80 | (num & 0x7F)
551 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
552 """Encode tag to binary form
554 :param int num: tag's number
555 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
556 :py:data:`pyderasn.TagClassContext`,
557 :py:data:`pyderasn.TagClassApplication`,
558 :py:data:`pyderasn.TagClassPrivate`)
559 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
560 :py:data:`pyderasn.TagFormConstructed`)
564 return int2byte(klass | form | num)
565 # [XX|X|11111][1.......][1.......] ... [0.......]
566 return int2byte(klass | form | 31) + zero_ended_encode(num)
570 """Decode tag from binary form
574 No validation is performed, assuming that it has already passed.
576 It returns tuple with three integers, as
577 :py:func:`pyderasn.tag_encode` accepts.
579 first_octet = byte2int(tag)
580 klass = first_octet & 0xC0
581 form = first_octet & 0x20
582 if first_octet & 0x1F < 0x1F:
583 return (klass, form, first_octet & 0x1F)
585 for octet in iterbytes(tag[1:]):
588 return (klass, form, num)
592 """Create CONTEXT PRIMITIVE tag
594 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
598 """Create CONTEXT CONSTRUCTED tag
600 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
604 """Take off tag from the data
606 :returns: (encoded tag, tag length, remaining data)
609 raise NotEnoughData("no data at all")
610 if byte2int(data) & 0x1F < 31:
611 return data[:1], 1, data[1:]
616 raise DecodeError("unfinished tag")
617 if indexbytes(data, i) & 0x80 == 0:
620 return data[:i], i, data[i:]
626 octets = bytearray(int_bytes_len(l) + 1)
627 octets[0] = 0x80 | (len(octets) - 1)
628 for i in six_xrange(len(octets) - 1, 0, -1):
634 def len_decode(data):
636 raise NotEnoughData("no data at all")
637 first_octet = byte2int(data)
638 if first_octet & 0x80 == 0:
639 return first_octet, 1, data[1:]
640 octets_num = first_octet & 0x7F
641 if octets_num + 1 > len(data):
642 raise NotEnoughData("encoded length is longer than data")
644 raise DecodeError("long form instead of short one")
645 if byte2int(data[1:]) == 0:
646 raise DecodeError("leading zeros")
648 for v in iterbytes(data[1:1 + octets_num]):
651 raise DecodeError("long form instead of short one")
652 return l, 1 + octets_num, data[1 + octets_num:]
655 ########################################################################
657 ########################################################################
659 class AutoAddSlots(type):
660 def __new__(cls, name, bases, _dict):
661 _dict["__slots__"] = _dict.get("__slots__", ())
662 return type.__new__(cls, name, bases, _dict)
665 @add_metaclass(AutoAddSlots)
667 """Common ASN.1 object class
669 All ASN.1 types are inherited from it. It has metaclass that
670 automatically adds ``__slots__`` to all inherited classes.
691 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
692 self._expl = getattr(self, "expl", None) if expl is None else expl
693 if self.tag != self.tag_default and self._expl is not None:
695 "implicit and explicit tags can not be set simultaneously"
697 if default is not None:
699 self.optional = optional
700 self.offset, self.llen, self.vlen = _decoded
704 def ready(self): # pragma: no cover
705 """Is object ready to be encoded?
707 raise NotImplementedError()
709 def _assert_ready(self):
711 raise ObjNotReady(self.__class__.__name__)
715 """Is object decoded?
717 return (self.llen + self.vlen) > 0
719 def copy(self): # pragma: no cover
720 """Make a copy of object, safe to be mutated
722 raise NotImplementedError()
730 return self.tlen + self.llen + self.vlen
732 def __str__(self): # pragma: no cover
733 return self.__bytes__() if PY2 else self.__unicode__()
735 def __ne__(self, their):
736 return not(self == their)
738 def __gt__(self, their): # pragma: no cover
739 return not(self < their)
741 def __le__(self, their): # pragma: no cover
742 return (self == their) or (self < their)
744 def __ge__(self, their): # pragma: no cover
745 return (self == their) or (self > their)
747 def _encode(self): # pragma: no cover
748 raise NotImplementedError()
750 def _decode(self, tlv, offset=0, decode_path=()): # pragma: no cover
751 raise NotImplementedError()
755 if self._expl is None:
757 return b"".join((self._expl, len_encode(len(raw)), raw))
759 def decode(self, data, offset=0, leavemm=False, decode_path=()):
762 :param data: either binary or memoryview
763 :param int offset: initial data's offset
764 :param bool leavemm: do we need to leave memoryview of remaining
765 data as is, or convert it to bytes otherwise
766 :returns: (Obj, remaining data)
768 tlv = memoryview(data)
769 if self._expl is None:
770 obj, tail = self._decode(
773 decode_path=decode_path,
777 t, tlen, lv = tag_strip(tlv)
778 except DecodeError as err:
781 klass=self.__class__,
782 decode_path=decode_path,
787 klass=self.__class__,
788 decode_path=decode_path,
792 l, llen, v = len_decode(lv)
793 except DecodeError as err:
796 klass=self.__class__,
797 decode_path=decode_path,
802 "encoded length is longer than data",
803 klass=self.__class__,
804 decode_path=decode_path,
807 obj, tail = self._decode(
809 offset=offset + tlen + llen,
812 return obj, (tail if leavemm else tail.tobytes())
816 return self._expl is not None
824 return len(self._expl)
828 return len(len_encode(self.tlvlen))
831 def expl_offset(self):
832 return self.offset - self.expl_tlen - self.expl_llen
839 def expl_tlvlen(self):
840 return self.expl_tlen + self.expl_llen + self.expl_vlen
843 ########################################################################
845 ########################################################################
847 PP = namedtuple("PP", (
869 asn1_type_name="unknown",
908 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
911 cols.append("%5d%s [%d,%d,%4d]" % (
914 " " if pp.expl_offset is None else
915 ("-%d" % (pp.offset - pp.expl_offset))
921 if len(pp.decode_path) > 0:
922 cols.append(" ." * (len(pp.decode_path)))
923 cols.append("%s:" % pp.decode_path[-1])
924 if pp.expl is not None:
925 klass, _, num = pp.expl
926 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
927 if pp.impl is not None:
928 klass, _, num = pp.impl
929 cols.append("[%s%d]" % (TagClassReprs[klass], num))
930 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
931 cols.append(pp.obj_name)
932 cols.append(pp.asn1_type_name)
933 if pp.value is not None:
937 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
940 value = "%s (%s)" % (oids[value], pp.value)
943 if isinstance(pp.blob, binary_type):
944 cols.append(hexenc(pp.blob))
945 elif isinstance(pp.blob, tuple):
946 cols.append(", ".join(pp.blob))
948 cols.append("OPTIONAL")
950 cols.append("DEFAULT")
951 return " ".join(cols)
954 def pp_console_blob(pp):
955 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
956 if len(pp.decode_path) > 0:
957 cols.append(" ." * (len(pp.decode_path) + 1))
958 if isinstance(pp.blob, binary_type):
959 blob = hexenc(pp.blob).upper()
960 for i in range(0, len(blob), 32):
961 chunk = blob[i:i + 32]
962 yield " ".join(cols + [":".join(
963 chunk[j:j + 2] for j in range(0, len(chunk), 2)
965 elif isinstance(pp.blob, tuple):
966 yield " ".join(cols + [", ".join(pp.blob)])
969 def pprint(obj, oids=None, big_blobs=False):
970 """Pretty print object
972 :param Obj obj: object you want to pretty print
973 :param oids: ``OID <-> humand readable string`` dictionary. When OID
974 from it is met, then its humand readable form is printed
975 :param big_blobs: if large binary objects are met (like OctetString
976 values), do we need to print them too, on separate
979 def _pprint_pps(pps):
981 if hasattr(pp, "_fields"):
983 yield pp_console_row(
989 for row in pp_console_blob(pp):
992 yield pp_console_row(pp, oids=oids, with_offsets=True)
994 for row in _pprint_pps(pp):
996 return "\n".join(_pprint_pps(obj.pps()))
999 ########################################################################
1000 # ASN.1 primitive types
1001 ########################################################################
1004 """``BOOLEAN`` boolean type
1006 >>> b = Boolean(True)
1008 >>> b == Boolean(True)
1014 tag_default = tag_encode(1)
1015 asn1_type_name = "BOOLEAN"
1027 :param value: set the value. Either boolean type, or
1028 :py:class:`pyderasn.Boolean` object
1029 :param bytes impl: override default tag with ``IMPLICIT`` one
1030 :param bytes expl: override default tag with ``EXPLICIT`` one
1031 :param default: set default value. Type same as in ``value``
1032 :param bool optional: is object ``OPTIONAL`` in sequence
1034 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1035 self._value = None if value is None else self._value_sanitize(value)
1036 if default is not None:
1037 default = self._value_sanitize(default)
1038 self.default = self.__class__(
1044 self._value = default
1046 def _value_sanitize(self, value):
1047 if issubclass(value.__class__, Boolean):
1049 if isinstance(value, bool):
1051 raise InvalidValueType((self.__class__, bool))
1055 return self._value is not None
1058 obj = self.__class__()
1059 obj._value = self._value
1061 obj._expl = self._expl
1062 obj.default = self.default
1063 obj.optional = self.optional
1064 obj.offset = self.offset
1065 obj.llen = self.llen
1066 obj.vlen = self.vlen
1069 def __nonzero__(self):
1070 self._assert_ready()
1074 self._assert_ready()
1077 def __eq__(self, their):
1078 if isinstance(their, bool):
1079 return self._value == their
1080 if not issubclass(their.__class__, Boolean):
1083 self._value == their._value and
1084 self.tag == their.tag and
1085 self._expl == their._expl
1096 return self.__class__(
1098 impl=self.tag if impl is None else impl,
1099 expl=self._expl if expl is None else expl,
1100 default=self.default if default is None else default,
1101 optional=self.optional if optional is None else optional,
1105 self._assert_ready()
1109 (b"\xFF" if self._value else b"\x00"),
1112 def _decode(self, tlv, offset=0, decode_path=()):
1114 t, _, lv = tag_strip(tlv)
1115 except DecodeError as err:
1116 raise err.__class__(
1118 klass=self.__class__,
1119 decode_path=decode_path,
1124 klass=self.__class__,
1125 decode_path=decode_path,
1129 l, _, v = len_decode(lv)
1130 except DecodeError as err:
1131 raise err.__class__(
1133 klass=self.__class__,
1134 decode_path=decode_path,
1138 raise InvalidLength(
1139 "Boolean's length must be equal to 1",
1140 klass=self.__class__,
1141 decode_path=decode_path,
1145 raise NotEnoughData(
1146 "encoded length is longer than data",
1147 klass=self.__class__,
1148 decode_path=decode_path,
1151 first_octet = byte2int(v)
1152 if first_octet == 0:
1154 elif first_octet == 0xFF:
1158 "unacceptable Boolean value",
1159 klass=self.__class__,
1160 decode_path=decode_path,
1163 obj = self.__class__(
1167 default=self.default,
1168 optional=self.optional,
1169 _decoded=(offset, 1, 1),
1174 return pp_console_row(next(self.pps()))
1176 def pps(self, decode_path=()):
1178 asn1_type_name=self.asn1_type_name,
1179 obj_name=self.__class__.__name__,
1180 decode_path=decode_path,
1181 value=str(self._value) if self.ready else None,
1182 optional=self.optional,
1183 default=self == self.default,
1184 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1185 expl=None if self._expl is None else tag_decode(self._expl),
1190 expl_offset=self.expl_offset if self.expled else None,
1191 expl_tlen=self.expl_tlen if self.expled else None,
1192 expl_llen=self.expl_llen if self.expled else None,
1193 expl_vlen=self.expl_vlen if self.expled else None,
1198 """``INTEGER`` integer type
1200 >>> b = Integer(-123)
1202 >>> b == Integer(-123)
1207 >>> Integer(2, bounds=(1, 3))
1209 >>> Integer(5, bounds=(1, 3))
1210 Traceback (most recent call last):
1211 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1215 class Version(Integer):
1222 >>> v = Version("v1")
1229 {'v3': 2, 'v1': 0, 'v2': 1}
1231 __slots__ = ("specs", "_bound_min", "_bound_max")
1232 tag_default = tag_encode(2)
1233 asn1_type_name = "INTEGER"
1247 :param value: set the value. Either integer type, named value
1248 (if ``schema`` is specified in the class), or
1249 :py:class:`pyderasn.Integer` object
1250 :param bounds: set ``(MIN, MAX)`` value constraint.
1251 (-inf, +inf) by default
1252 :param bytes impl: override default tag with ``IMPLICIT`` one
1253 :param bytes expl: override default tag with ``EXPLICIT`` one
1254 :param default: set default value. Type same as in ``value``
1255 :param bool optional: is object ``OPTIONAL`` in sequence
1257 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1259 specs = getattr(self, "schema", {}) if _specs is None else _specs
1260 self.specs = specs if isinstance(specs, dict) else dict(specs)
1261 self._bound_min, self._bound_max = getattr(
1264 (float("-inf"), float("+inf")),
1265 ) if bounds is None else bounds
1266 if value is not None:
1267 self._value = self._value_sanitize(value)
1268 if default is not None:
1269 default = self._value_sanitize(default)
1270 self.default = self.__class__(
1276 if self._value is None:
1277 self._value = default
1279 def _value_sanitize(self, value):
1280 if issubclass(value.__class__, Integer):
1281 value = value._value
1282 elif isinstance(value, integer_types):
1284 elif isinstance(value, str):
1285 value = self.specs.get(value)
1287 raise ObjUnknown("integer value: %s" % value)
1289 raise InvalidValueType((self.__class__, int, str))
1290 if not self._bound_min <= value <= self._bound_max:
1291 raise BoundsError(self._bound_min, value, self._bound_max)
1296 return self._value is not None
1299 obj = self.__class__(_specs=self.specs)
1300 obj._value = self._value
1301 obj._bound_min = self._bound_min
1302 obj._bound_max = self._bound_max
1304 obj._expl = self._expl
1305 obj.default = self.default
1306 obj.optional = self.optional
1307 obj.offset = self.offset
1308 obj.llen = self.llen
1309 obj.vlen = self.vlen
1313 self._assert_ready()
1314 return int(self._value)
1317 self._assert_ready()
1320 bytes(self._expl or b"") +
1321 str(self._value).encode("ascii"),
1324 def __eq__(self, their):
1325 if isinstance(their, integer_types):
1326 return self._value == their
1327 if not issubclass(their.__class__, Integer):
1330 self._value == their._value and
1331 self.tag == their.tag and
1332 self._expl == their._expl
1335 def __lt__(self, their):
1336 return self._value < their._value
1340 for name, value in self.specs.items():
1341 if value == self._value:
1353 return self.__class__(
1356 (self._bound_min, self._bound_max)
1357 if bounds is None else bounds
1359 impl=self.tag if impl is None else impl,
1360 expl=self._expl if expl is None else expl,
1361 default=self.default if default is None else default,
1362 optional=self.optional if optional is None else optional,
1367 self._assert_ready()
1371 octets = bytearray([0])
1375 octets = bytearray()
1377 octets.append((value & 0xFF) ^ 0xFF)
1379 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1382 octets = bytearray()
1384 octets.append(value & 0xFF)
1386 if octets[-1] & 0x80 > 0:
1389 octets = bytes(octets)
1391 bytes_len = ceil(value.bit_length() / 8) or 1
1394 octets = value.to_bytes(
1399 except OverflowError:
1403 return b"".join((self.tag, len_encode(len(octets)), octets))
1405 def _decode(self, tlv, offset=0, decode_path=()):
1407 t, _, lv = tag_strip(tlv)
1408 except DecodeError as err:
1409 raise err.__class__(
1411 klass=self.__class__,
1412 decode_path=decode_path,
1417 klass=self.__class__,
1418 decode_path=decode_path,
1422 l, llen, v = len_decode(lv)
1423 except DecodeError as err:
1424 raise err.__class__(
1426 klass=self.__class__,
1427 decode_path=decode_path,
1431 raise NotEnoughData(
1432 "encoded length is longer than data",
1433 klass=self.__class__,
1434 decode_path=decode_path,
1438 raise NotEnoughData(
1440 klass=self.__class__,
1441 decode_path=decode_path,
1444 v, tail = v[:l], v[l:]
1445 first_octet = byte2int(v)
1447 second_octet = byte2int(v[1:])
1449 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1450 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1453 "non normalized integer",
1454 klass=self.__class__,
1455 decode_path=decode_path,
1460 if first_octet & 0x80 > 0:
1461 octets = bytearray()
1462 for octet in bytearray(v):
1463 octets.append(octet ^ 0xFF)
1464 for octet in octets:
1465 value = (value << 8) | octet
1469 for octet in bytearray(v):
1470 value = (value << 8) | octet
1472 value = int.from_bytes(v, byteorder="big", signed=True)
1474 obj = self.__class__(
1476 bounds=(self._bound_min, self._bound_max),
1479 default=self.default,
1480 optional=self.optional,
1482 _decoded=(offset, llen, l),
1484 except BoundsError as err:
1487 klass=self.__class__,
1488 decode_path=decode_path,
1494 return pp_console_row(next(self.pps()))
1496 def pps(self, decode_path=()):
1498 asn1_type_name=self.asn1_type_name,
1499 obj_name=self.__class__.__name__,
1500 decode_path=decode_path,
1501 value=(self.named or str(self._value)) if self.ready else None,
1502 optional=self.optional,
1503 default=self == self.default,
1504 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1505 expl=None if self._expl is None else tag_decode(self._expl),
1510 expl_offset=self.expl_offset if self.expled else None,
1511 expl_tlen=self.expl_tlen if self.expled else None,
1512 expl_llen=self.expl_llen if self.expled else None,
1513 expl_vlen=self.expl_vlen if self.expled else None,
1517 class BitString(Obj):
1518 """``BIT STRING`` bit string type
1520 >>> BitString(b"hello world")
1521 BIT STRING 88 bits 68656c6c6f20776f726c64
1524 >>> b == b"hello world"
1529 >>> b = BitString("'010110000000'B")
1530 BIT STRING 12 bits 5800
1533 >>> b[0], b[1], b[2], b[3]
1534 (False, True, False, True)
1538 [False, True, False, True, True, False, False, False, False, False, False, False]
1542 class KeyUsage(BitString):
1544 ('digitalSignature', 0),
1545 ('nonRepudiation', 1),
1546 ('keyEncipherment', 2),
1549 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1550 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1552 ['nonRepudiation', 'keyEncipherment']
1554 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1556 __slots__ = ("specs",)
1557 tag_default = tag_encode(3)
1558 asn1_type_name = "BIT STRING"
1571 :param value: set the value. Either binary type, tuple of named
1572 values (if ``schema`` is specified in the class),
1573 string in ``'XXX...'B`` form, or
1574 :py:class:`pyderasn.BitString` object
1575 :param bytes impl: override default tag with ``IMPLICIT`` one
1576 :param bytes expl: override default tag with ``EXPLICIT`` one
1577 :param default: set default value. Type same as in ``value``
1578 :param bool optional: is object ``OPTIONAL`` in sequence
1580 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1581 specs = getattr(self, "schema", {}) if _specs is None else _specs
1582 self.specs = specs if isinstance(specs, dict) else dict(specs)
1583 self._value = None if value is None else self._value_sanitize(value)
1584 if default is not None:
1585 default = self._value_sanitize(default)
1586 self.default = self.__class__(
1592 self._value = default
1594 def _bits2octets(self, bits):
1595 if len(self.specs) > 0:
1596 bits = bits.rstrip("0")
1598 bits += "0" * ((8 - (bit_len % 8)) % 8)
1599 octets = bytearray(len(bits) // 8)
1600 for i in six_xrange(len(octets)):
1601 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1602 return bit_len, bytes(octets)
1604 def _value_sanitize(self, value):
1605 if issubclass(value.__class__, BitString):
1607 if isinstance(value, (string_types, binary_type)):
1609 isinstance(value, string_types) and
1610 value.startswith("'") and
1611 value.endswith("'B")
1614 if not set(value) <= set(("0", "1")):
1615 raise ValueError("B's coding contains unacceptable chars")
1616 return self._bits2octets(value)
1617 elif isinstance(value, binary_type):
1618 return (len(value) * 8, value)
1620 raise InvalidValueType((
1625 if isinstance(value, tuple):
1628 isinstance(value[0], integer_types) and
1629 isinstance(value[1], binary_type)
1634 bit = self.specs.get(name)
1636 raise ObjUnknown("BitString value: %s" % name)
1639 return self._bits2octets("")
1641 return self._bits2octets("".join(
1642 ("1" if bit in bits else "0")
1643 for bit in six_xrange(max(bits) + 1)
1645 raise InvalidValueType((self.__class__, binary_type, string_types))
1649 return self._value is not None
1652 obj = self.__class__(_specs=self.specs)
1654 if value is not None:
1655 value = (value[0], value[1])
1658 obj._expl = self._expl
1659 obj.default = self.default
1660 obj.optional = self.optional
1661 obj.offset = self.offset
1662 obj.llen = self.llen
1663 obj.vlen = self.vlen
1667 self._assert_ready()
1668 for i in six_xrange(self._value[0]):
1673 self._assert_ready()
1674 return self._value[0]
1676 def __bytes__(self):
1677 self._assert_ready()
1678 return self._value[1]
1680 def __eq__(self, their):
1681 if isinstance(their, bytes):
1682 return self._value[1] == their
1683 if not issubclass(their.__class__, BitString):
1686 self._value == their._value and
1687 self.tag == their.tag and
1688 self._expl == their._expl
1693 return [name for name, bit in self.specs.items() if self[bit]]
1703 return self.__class__(
1705 impl=self.tag if impl is None else impl,
1706 expl=self._expl if expl is None else expl,
1707 default=self.default if default is None else default,
1708 optional=self.optional if optional is None else optional,
1712 def __getitem__(self, key):
1713 if isinstance(key, int):
1714 bit_len, octets = self._value
1718 byte2int(memoryview(octets)[key // 8:]) >>
1721 if isinstance(key, string_types):
1722 value = self.specs.get(key)
1724 raise ObjUnknown("BitString value: %s" % key)
1726 raise InvalidValueType((int, str))
1729 self._assert_ready()
1730 bit_len, octets = self._value
1733 len_encode(len(octets) + 1),
1734 int2byte((8 - bit_len % 8) % 8),
1738 def _decode(self, tlv, offset=0, decode_path=()):
1740 t, _, lv = tag_strip(tlv)
1741 except DecodeError as err:
1742 raise err.__class__(
1744 klass=self.__class__,
1745 decode_path=decode_path,
1750 klass=self.__class__,
1751 decode_path=decode_path,
1755 l, llen, v = len_decode(lv)
1756 except DecodeError as err:
1757 raise err.__class__(
1759 klass=self.__class__,
1760 decode_path=decode_path,
1764 raise NotEnoughData(
1765 "encoded length is longer than data",
1766 klass=self.__class__,
1767 decode_path=decode_path,
1771 raise NotEnoughData(
1773 klass=self.__class__,
1774 decode_path=decode_path,
1777 pad_size = byte2int(v)
1778 if l == 1 and pad_size != 0:
1780 "invalid empty value",
1781 klass=self.__class__,
1782 decode_path=decode_path,
1788 klass=self.__class__,
1789 decode_path=decode_path,
1792 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1795 klass=self.__class__,
1796 decode_path=decode_path,
1799 v, tail = v[:l], v[l:]
1800 obj = self.__class__(
1801 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1804 default=self.default,
1805 optional=self.optional,
1807 _decoded=(offset, llen, l),
1812 return pp_console_row(next(self.pps()))
1814 def pps(self, decode_path=()):
1818 bit_len, blob = self._value
1819 value = "%d bits" % bit_len
1820 if len(self.specs) > 0:
1821 blob = tuple(self.named)
1823 asn1_type_name=self.asn1_type_name,
1824 obj_name=self.__class__.__name__,
1825 decode_path=decode_path,
1828 optional=self.optional,
1829 default=self == self.default,
1830 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1831 expl=None if self._expl is None else tag_decode(self._expl),
1836 expl_offset=self.expl_offset if self.expled else None,
1837 expl_tlen=self.expl_tlen if self.expled else None,
1838 expl_llen=self.expl_llen if self.expled else None,
1839 expl_vlen=self.expl_vlen if self.expled else None,
1843 class OctetString(Obj):
1844 """``OCTET STRING`` binary string type
1846 >>> s = OctetString(b"hello world")
1847 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1848 >>> s == OctetString(b"hello world")
1853 >>> OctetString(b"hello", bounds=(4, 4))
1854 Traceback (most recent call last):
1855 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1856 >>> OctetString(b"hell", bounds=(4, 4))
1857 OCTET STRING 4 bytes 68656c6c
1859 __slots__ = ("_bound_min", "_bound_max")
1860 tag_default = tag_encode(4)
1861 asn1_type_name = "OCTET STRING"
1874 :param value: set the value. Either binary type, or
1875 :py:class:`pyderasn.OctetString` object
1876 :param bounds: set ``(MIN, MAX)`` value size constraint.
1877 (-inf, +inf) by default
1878 :param bytes impl: override default tag with ``IMPLICIT`` one
1879 :param bytes expl: override default tag with ``EXPLICIT`` one
1880 :param default: set default value. Type same as in ``value``
1881 :param bool optional: is object ``OPTIONAL`` in sequence
1883 super(OctetString, self).__init__(
1891 self._bound_min, self._bound_max = getattr(
1895 ) if bounds is None else bounds
1896 if value is not None:
1897 self._value = self._value_sanitize(value)
1898 if default is not None:
1899 default = self._value_sanitize(default)
1900 self.default = self.__class__(
1905 if self._value is None:
1906 self._value = default
1908 def _value_sanitize(self, value):
1909 if issubclass(value.__class__, OctetString):
1910 value = value._value
1911 elif isinstance(value, binary_type):
1914 raise InvalidValueType((self.__class__, bytes))
1915 if not self._bound_min <= len(value) <= self._bound_max:
1916 raise BoundsError(self._bound_min, len(value), self._bound_max)
1921 return self._value is not None
1924 obj = self.__class__()
1925 obj._value = self._value
1926 obj._bound_min = self._bound_min
1927 obj._bound_max = self._bound_max
1929 obj._expl = self._expl
1930 obj.default = self.default
1931 obj.optional = self.optional
1932 obj.offset = self.offset
1933 obj.llen = self.llen
1934 obj.vlen = self.vlen
1937 def __bytes__(self):
1938 self._assert_ready()
1941 def __eq__(self, their):
1942 if isinstance(their, binary_type):
1943 return self._value == their
1944 if not issubclass(their.__class__, OctetString):
1947 self._value == their._value and
1948 self.tag == their.tag and
1949 self._expl == their._expl
1952 def __lt__(self, their):
1953 return self._value < their._value
1964 return self.__class__(
1967 (self._bound_min, self._bound_max)
1968 if bounds is None else bounds
1970 impl=self.tag if impl is None else impl,
1971 expl=self._expl if expl is None else expl,
1972 default=self.default if default is None else default,
1973 optional=self.optional if optional is None else optional,
1977 self._assert_ready()
1980 len_encode(len(self._value)),
1984 def _decode(self, tlv, offset=0, decode_path=()):
1986 t, _, lv = tag_strip(tlv)
1987 except DecodeError as err:
1988 raise err.__class__(
1990 klass=self.__class__,
1991 decode_path=decode_path,
1996 klass=self.__class__,
1997 decode_path=decode_path,
2001 l, llen, v = len_decode(lv)
2002 except DecodeError as err:
2003 raise err.__class__(
2005 klass=self.__class__,
2006 decode_path=decode_path,
2010 raise NotEnoughData(
2011 "encoded length is longer than data",
2012 klass=self.__class__,
2013 decode_path=decode_path,
2016 v, tail = v[:l], v[l:]
2018 obj = self.__class__(
2020 bounds=(self._bound_min, self._bound_max),
2023 default=self.default,
2024 optional=self.optional,
2025 _decoded=(offset, llen, l),
2027 except BoundsError as err:
2030 klass=self.__class__,
2031 decode_path=decode_path,
2037 return pp_console_row(next(self.pps()))
2039 def pps(self, decode_path=()):
2041 asn1_type_name=self.asn1_type_name,
2042 obj_name=self.__class__.__name__,
2043 decode_path=decode_path,
2044 value=("%d bytes" % len(self._value)) if self.ready else None,
2045 blob=self._value if self.ready else None,
2046 optional=self.optional,
2047 default=self == self.default,
2048 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2049 expl=None if self._expl is None else tag_decode(self._expl),
2054 expl_offset=self.expl_offset if self.expled else None,
2055 expl_tlen=self.expl_tlen if self.expled else None,
2056 expl_llen=self.expl_llen if self.expled else None,
2057 expl_vlen=self.expl_vlen if self.expled else None,
2062 """``NULL`` null object
2070 tag_default = tag_encode(5)
2071 asn1_type_name = "NULL"
2075 value=None, # unused, but Sequence passes it
2082 :param bytes impl: override default tag with ``IMPLICIT`` one
2083 :param bytes expl: override default tag with ``EXPLICIT`` one
2084 :param bool optional: is object ``OPTIONAL`` in sequence
2086 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2094 obj = self.__class__()
2096 obj._expl = self._expl
2097 obj.default = self.default
2098 obj.optional = self.optional
2099 obj.offset = self.offset
2100 obj.llen = self.llen
2101 obj.vlen = self.vlen
2104 def __eq__(self, their):
2105 if not issubclass(their.__class__, Null):
2108 self.tag == their.tag and
2109 self._expl == their._expl
2119 return self.__class__(
2120 impl=self.tag if impl is None else impl,
2121 expl=self._expl if expl is None else expl,
2122 optional=self.optional if optional is None else optional,
2126 return self.tag + len_encode(0)
2128 def _decode(self, tlv, offset=0, decode_path=()):
2130 t, _, lv = tag_strip(tlv)
2131 except DecodeError as err:
2132 raise err.__class__(
2134 klass=self.__class__,
2135 decode_path=decode_path,
2140 klass=self.__class__,
2141 decode_path=decode_path,
2145 l, _, v = len_decode(lv)
2146 except DecodeError as err:
2147 raise err.__class__(
2149 klass=self.__class__,
2150 decode_path=decode_path,
2154 raise InvalidLength(
2155 "Null must have zero length",
2156 klass=self.__class__,
2157 decode_path=decode_path,
2160 obj = self.__class__(
2163 optional=self.optional,
2164 _decoded=(offset, 1, 0),
2169 return pp_console_row(next(self.pps()))
2171 def pps(self, decode_path=()):
2173 asn1_type_name=self.asn1_type_name,
2174 obj_name=self.__class__.__name__,
2175 decode_path=decode_path,
2176 optional=self.optional,
2177 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2178 expl=None if self._expl is None else tag_decode(self._expl),
2183 expl_offset=self.expl_offset if self.expled else None,
2184 expl_tlen=self.expl_tlen if self.expled else None,
2185 expl_llen=self.expl_llen if self.expled else None,
2186 expl_vlen=self.expl_vlen if self.expled else None,
2190 class ObjectIdentifier(Obj):
2191 """``OBJECT IDENTIFIER`` OID type
2193 >>> oid = ObjectIdentifier((1, 2, 3))
2194 OBJECT IDENTIFIER 1.2.3
2195 >>> oid == ObjectIdentifier("1.2.3")
2201 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2202 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2204 >>> str(ObjectIdentifier((3, 1)))
2205 Traceback (most recent call last):
2206 pyderasn.InvalidOID: unacceptable first arc value
2209 tag_default = tag_encode(6)
2210 asn1_type_name = "OBJECT IDENTIFIER"
2222 :param value: set the value. Either tuples of integers,
2223 string of "."-concatenated integers, or
2224 :py:class:`pyderasn.ObjectIdentifier` object
2225 :param bytes impl: override default tag with ``IMPLICIT`` one
2226 :param bytes expl: override default tag with ``EXPLICIT`` one
2227 :param default: set default value. Type same as in ``value``
2228 :param bool optional: is object ``OPTIONAL`` in sequence
2230 super(ObjectIdentifier, self).__init__(
2238 if value is not None:
2239 self._value = self._value_sanitize(value)
2240 if default is not None:
2241 default = self._value_sanitize(default)
2242 self.default = self.__class__(
2247 if self._value is None:
2248 self._value = default
2250 def __add__(self, their):
2251 if isinstance(their, self.__class__):
2252 return self.__class__(self._value + their._value)
2253 if isinstance(their, tuple):
2254 return self.__class__(self._value + their)
2255 raise InvalidValueType((self.__class__, tuple))
2257 def _value_sanitize(self, value):
2258 if issubclass(value.__class__, ObjectIdentifier):
2260 if isinstance(value, string_types):
2262 value = tuple(int(arc) for arc in value.split("."))
2264 raise InvalidOID("unacceptable arcs values")
2265 if isinstance(value, tuple):
2267 raise InvalidOID("less than 2 arcs")
2268 first_arc = value[0]
2269 if first_arc in (0, 1):
2270 if not (0 <= value[1] <= 39):
2271 raise InvalidOID("second arc is too wide")
2272 elif first_arc == 2:
2275 raise InvalidOID("unacceptable first arc value")
2277 raise InvalidValueType((self.__class__, str, tuple))
2281 return self._value is not None
2284 obj = self.__class__()
2285 obj._value = self._value
2287 obj._expl = self._expl
2288 obj.default = self.default
2289 obj.optional = self.optional
2290 obj.offset = self.offset
2291 obj.llen = self.llen
2292 obj.vlen = self.vlen
2296 self._assert_ready()
2297 return iter(self._value)
2300 return ".".join(str(arc) for arc in self._value or ())
2303 self._assert_ready()
2306 bytes(self._expl or b"") +
2307 str(self._value).encode("ascii"),
2310 def __eq__(self, their):
2311 if isinstance(their, tuple):
2312 return self._value == their
2313 if not issubclass(their.__class__, ObjectIdentifier):
2316 self.tag == their.tag and
2317 self._expl == their._expl and
2318 self._value == their._value
2321 def __lt__(self, their):
2322 return self._value < their._value
2332 return self.__class__(
2334 impl=self.tag if impl is None else impl,
2335 expl=self._expl if expl is None else expl,
2336 default=self.default if default is None else default,
2337 optional=self.optional if optional is None else optional,
2341 self._assert_ready()
2343 first_value = value[1]
2344 first_arc = value[0]
2347 elif first_arc == 1:
2349 elif first_arc == 2:
2351 else: # pragma: no cover
2352 raise RuntimeError("invalid arc is stored")
2353 octets = [zero_ended_encode(first_value)]
2354 for arc in value[2:]:
2355 octets.append(zero_ended_encode(arc))
2356 v = b"".join(octets)
2357 return b"".join((self.tag, len_encode(len(v)), v))
2359 def _decode(self, tlv, offset=0, decode_path=()):
2361 t, _, lv = tag_strip(tlv)
2362 except DecodeError as err:
2363 raise err.__class__(
2365 klass=self.__class__,
2366 decode_path=decode_path,
2371 klass=self.__class__,
2372 decode_path=decode_path,
2376 l, llen, v = len_decode(lv)
2377 except DecodeError as err:
2378 raise err.__class__(
2380 klass=self.__class__,
2381 decode_path=decode_path,
2385 raise NotEnoughData(
2386 "encoded length is longer than data",
2387 klass=self.__class__,
2388 decode_path=decode_path,
2392 raise NotEnoughData(
2394 klass=self.__class__,
2395 decode_path=decode_path,
2398 v, tail = v[:l], v[l:]
2404 octet = indexbytes(v, i)
2405 arc = (arc << 7) | (octet & 0x7F)
2406 if octet & 0x80 == 0:
2414 klass=self.__class__,
2415 decode_path=decode_path,
2419 second_arc = arcs[0]
2420 if 0 <= second_arc <= 39:
2422 elif 40 <= second_arc <= 79:
2428 obj = self.__class__(
2429 value=tuple([first_arc, second_arc] + arcs[1:]),
2432 default=self.default,
2433 optional=self.optional,
2434 _decoded=(offset, llen, l),
2439 return pp_console_row(next(self.pps()))
2441 def pps(self, decode_path=()):
2443 asn1_type_name=self.asn1_type_name,
2444 obj_name=self.__class__.__name__,
2445 decode_path=decode_path,
2446 value=str(self) if self.ready else None,
2447 optional=self.optional,
2448 default=self == self.default,
2449 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2450 expl=None if self._expl is None else tag_decode(self._expl),
2455 expl_offset=self.expl_offset if self.expled else None,
2456 expl_tlen=self.expl_tlen if self.expled else None,
2457 expl_llen=self.expl_llen if self.expled else None,
2458 expl_vlen=self.expl_vlen if self.expled else None,
2462 class Enumerated(Integer):
2463 """``ENUMERATED`` integer type
2465 This type is identical to :py:class:`pyderasn.Integer`, but requires
2466 schema to be specified and does not accept values missing from it.
2469 tag_default = tag_encode(10)
2470 asn1_type_name = "ENUMERATED"
2481 bounds=None, # dummy argument, workability for Integer.decode
2483 super(Enumerated, self).__init__(
2492 if len(self.specs) == 0:
2493 raise ValueError("schema must be specified")
2495 def _value_sanitize(self, value):
2496 if isinstance(value, self.__class__):
2497 value = value._value
2498 elif isinstance(value, integer_types):
2499 if value not in list(self.specs.values()):
2501 "unknown integer value: %s" % value,
2502 klass=self.__class__,
2504 elif isinstance(value, string_types):
2505 value = self.specs.get(value)
2507 raise ObjUnknown("integer value: %s" % value)
2509 raise InvalidValueType((self.__class__, int, str))
2513 obj = self.__class__(_specs=self.specs)
2514 obj._value = self._value
2515 obj._bound_min = self._bound_min
2516 obj._bound_max = self._bound_max
2518 obj._expl = self._expl
2519 obj.default = self.default
2520 obj.optional = self.optional
2521 obj.offset = self.offset
2522 obj.llen = self.llen
2523 obj.vlen = self.vlen
2535 return self.__class__(
2537 impl=self.tag if impl is None else impl,
2538 expl=self._expl if expl is None else expl,
2539 default=self.default if default is None else default,
2540 optional=self.optional if optional is None else optional,
2545 class CommonString(OctetString):
2546 """Common class for all strings
2548 Everything resembles :py:class:`pyderasn.OctetString`, except
2549 ability to deal with unicode text strings.
2551 >>> hexenc("привет мир".encode("utf-8"))
2552 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2553 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2555 >>> s = UTF8String("привет мир")
2556 UTF8String UTF8String привет мир
2558 'привет мир'
2559 >>> hexenc(bytes(s))
2560 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2562 >>> PrintableString("привет мир")
2563 Traceback (most recent call last):
2564 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2566 >>> BMPString("ада", bounds=(2, 2))
2567 Traceback (most recent call last):
2568 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2569 >>> s = BMPString("ад", bounds=(2, 2))
2572 >>> hexenc(bytes(s))
2580 * - :py:class:`pyderasn.UTF8String`
2582 * - :py:class:`pyderasn.NumericString`
2584 * - :py:class:`pyderasn.PrintableString`
2586 * - :py:class:`pyderasn.TeletexString`
2588 * - :py:class:`pyderasn.T61String`
2590 * - :py:class:`pyderasn.VideotexString`
2592 * - :py:class:`pyderasn.IA5String`
2594 * - :py:class:`pyderasn.GraphicString`
2596 * - :py:class:`pyderasn.VisibleString`
2598 * - :py:class:`pyderasn.ISO646String`
2600 * - :py:class:`pyderasn.GeneralString`
2602 * - :py:class:`pyderasn.UniversalString`
2604 * - :py:class:`pyderasn.BMPString`
2607 __slots__ = ("encoding",)
2609 def _value_sanitize(self, value):
2611 value_decoded = None
2612 if isinstance(value, self.__class__):
2613 value_raw = value._value
2614 elif isinstance(value, text_type):
2615 value_decoded = value
2616 elif isinstance(value, binary_type):
2619 raise InvalidValueType((self.__class__, text_type, binary_type))
2621 value_decoded.encode(self.encoding)
2622 if value_raw is None else value_raw
2625 value_raw.decode(self.encoding)
2626 if value_decoded is None else value_decoded
2628 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2636 def __eq__(self, their):
2637 if isinstance(their, binary_type):
2638 return self._value == their
2639 if isinstance(their, text_type):
2640 return self._value == their.encode(self.encoding)
2641 if not isinstance(their, self.__class__):
2644 self._value == their._value and
2645 self.tag == their.tag and
2646 self._expl == their._expl
2649 def __unicode__(self):
2651 return self._value.decode(self.encoding)
2652 return text_type(self._value)
2655 return pp_console_row(next(self.pps(no_unicode=PY2)))
2657 def pps(self, decode_path=(), no_unicode=False):
2660 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2662 asn1_type_name=self.asn1_type_name,
2663 obj_name=self.__class__.__name__,
2664 decode_path=decode_path,
2666 optional=self.optional,
2667 default=self == self.default,
2668 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2669 expl=None if self._expl is None else tag_decode(self._expl),
2677 class UTF8String(CommonString):
2679 tag_default = tag_encode(12)
2681 asn1_type_name = "UTF8String"
2684 class NumericString(CommonString):
2686 tag_default = tag_encode(18)
2688 asn1_type_name = "NumericString"
2691 class PrintableString(CommonString):
2693 tag_default = tag_encode(19)
2695 asn1_type_name = "PrintableString"
2698 class TeletexString(CommonString):
2700 tag_default = tag_encode(20)
2702 asn1_type_name = "TeletexString"
2705 class T61String(TeletexString):
2707 asn1_type_name = "T61String"
2710 class VideotexString(CommonString):
2712 tag_default = tag_encode(21)
2713 encoding = "iso-8859-1"
2714 asn1_type_name = "VideotexString"
2717 class IA5String(CommonString):
2719 tag_default = tag_encode(22)
2721 asn1_type_name = "IA5"
2724 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):
3364 ("extnID", ObjectIdentifier()),
3365 ("critical", Boolean(default=False)),
3366 ("extnValue", OctetString()),
3369 Then, you can work with it as with dictionary.
3371 >>> ext = Extension()
3372 >>> Extension().specs
3374 ('extnID', OBJECT IDENTIFIER),
3375 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3376 ('extnValue', OCTET STRING),
3378 >>> ext["extnID"] = "1.2.3"
3379 Traceback (most recent call last):
3380 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3381 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3383 You can know if sequence is ready to be encoded:
3388 Traceback (most recent call last):
3389 pyderasn.ObjNotReady: object is not ready: extnValue
3390 >>> ext["extnValue"] = OctetString(b"foobar")
3394 Value you want to assign, must have the same **type** as in
3395 corresponding specification, but it can have different tags,
3396 optional/default attributes -- they will be taken from specification
3399 class TBSCertificate(Sequence):
3401 ("version", Version(expl=tag_ctxc(0), default="v1")),
3404 >>> tbs = TBSCertificate()
3405 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3407 You can know if value exists/set in the sequence and take its value:
3409 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3412 OBJECT IDENTIFIER 1.2.3
3414 But pay attention that if value has default, then it won't be (not
3415 in) in the sequence (because ``DEFAULT`` must not be encoded in
3416 DER), but you can read its value:
3418 >>> "critical" in ext, ext["critical"]
3419 (False, BOOLEAN False)
3420 >>> ext["critical"] = Boolean(True)
3421 >>> "critical" in ext, ext["critical"]
3422 (True, BOOLEAN True)
3424 All defaulted values are always optional.
3428 When decoded DER contains defaulted value inside, then
3429 technically this is not valid DER encoding. But we allow
3430 and pass it. Of course reencoding of that kind of DER will
3431 result in different binary representation (validly without
3432 defaulted value inside).
3434 Two sequences are equal if they have equal specification (schema),
3435 implicit/explicit tagging and the same values.
3437 __slots__ = ("specs",)
3438 tag_default = tag_encode(form=TagFormConstructed, num=16)
3439 asn1_type_name = "SEQUENCE"
3451 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3453 schema = getattr(self, "schema", ())
3455 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3458 if value is not None:
3459 self._value = self._value_sanitize(value)
3460 if default is not None:
3461 default_value = self._value_sanitize(default)
3462 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3463 default_obj.specs = self.specs
3464 default_obj._value = default_value
3465 self.default = default_obj
3467 self._value = default_obj.copy()._value
3469 def _value_sanitize(self, value):
3470 if not issubclass(value.__class__, Sequence):
3471 raise InvalidValueType((Sequence,))
3476 for name, spec in self.specs.items():
3477 value = self._value.get(name)
3488 obj = self.__class__(schema=self.specs)
3490 obj._expl = self._expl
3491 obj.default = self.default
3492 obj.optional = self.optional
3493 obj.offset = self.offset
3494 obj.llen = self.llen
3495 obj.vlen = self.vlen
3496 obj._value = {k: v.copy() for k, v in self._value.items()}
3499 def __eq__(self, their):
3500 if not isinstance(their, self.__class__):
3503 self.specs == their.specs and
3504 self.tag == their.tag and
3505 self._expl == their._expl and
3506 self._value == their._value
3517 return self.__class__(
3520 impl=self.tag if impl is None else impl,
3521 expl=self._expl if expl is None else expl,
3522 default=self.default if default is None else default,
3523 optional=self.optional if optional is None else optional,
3526 def __contains__(self, key):
3527 return key in self._value
3529 def __setitem__(self, key, value):
3530 spec = self.specs.get(key)
3532 raise ObjUnknown(key)
3534 self._value.pop(key, None)
3536 if not isinstance(value, spec.__class__):
3537 raise InvalidValueType((spec.__class__,))
3538 value = spec(value=value)
3539 if spec.default is not None and value == spec.default:
3540 self._value.pop(key, None)
3542 self._value[key] = value
3544 def __getitem__(self, key):
3545 value = self._value.get(key)
3546 if value is not None:
3548 spec = self.specs.get(key)
3550 raise ObjUnknown(key)
3551 if spec.default is not None:
3555 def _encoded_values(self):
3557 for name, spec in self.specs.items():
3558 value = self._value.get(name)
3562 raise ObjNotReady(name)
3563 raws.append(value.encode())
3567 v = b"".join(self._encoded_values())
3568 return b"".join((self.tag, len_encode(len(v)), v))
3570 def _decode(self, tlv, offset=0, decode_path=()):
3572 t, tlen, lv = tag_strip(tlv)
3573 except DecodeError as err:
3574 raise err.__class__(
3576 klass=self.__class__,
3577 decode_path=decode_path,
3582 klass=self.__class__,
3583 decode_path=decode_path,
3587 l, llen, v = len_decode(lv)
3588 except DecodeError as err:
3589 raise err.__class__(
3591 klass=self.__class__,
3592 decode_path=decode_path,
3596 raise NotEnoughData(
3597 "encoded length is longer than data",
3598 klass=self.__class__,
3599 decode_path=decode_path,
3602 v, tail = v[:l], v[l:]
3603 sub_offset = offset + tlen + llen
3605 for name, spec in self.specs.items():
3606 if len(v) == 0 and spec.optional:
3609 value, v_tail = spec.decode(
3613 decode_path=decode_path + (name,),
3619 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3621 if spec.default is not None and value == spec.default:
3622 # Encoded default values are not valid in DER,
3623 # but we still allow that
3625 values[name] = value
3629 klass=self.__class__,
3630 decode_path=decode_path,
3633 obj = self.__class__(
3637 default=self.default,
3638 optional=self.optional,
3639 _decoded=(offset, llen, l),
3645 value = pp_console_row(next(self.pps()))
3647 for name in self.specs:
3648 _value = self._value.get(name)
3651 cols.append(repr(_value))
3652 return "%s[%s]" % (value, ", ".join(cols))
3654 def pps(self, decode_path=()):
3656 asn1_type_name=self.asn1_type_name,
3657 obj_name=self.__class__.__name__,
3658 decode_path=decode_path,
3659 optional=self.optional,
3660 default=self == self.default,
3661 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3662 expl=None if self._expl is None else tag_decode(self._expl),
3667 expl_offset=self.expl_offset if self.expled else None,
3668 expl_tlen=self.expl_tlen if self.expled else None,
3669 expl_llen=self.expl_llen if self.expled else None,
3670 expl_vlen=self.expl_vlen if self.expled else None,
3672 for name in self.specs:
3673 value = self._value.get(name)
3676 yield value.pps(decode_path=decode_path + (name,))
3679 class Set(Sequence):
3680 """``SET`` structure type
3682 Its usage is identical to :py:class:`pyderasn.Sequence`.
3685 tag_default = tag_encode(form=TagFormConstructed, num=17)
3686 asn1_type_name = "SET"
3689 raws = self._encoded_values()
3692 return b"".join((self.tag, len_encode(len(v)), v))
3694 def _decode(self, tlv, offset=0, decode_path=()):
3696 t, tlen, lv = tag_strip(tlv)
3697 except DecodeError as err:
3698 raise err.__class__(
3700 klass=self.__class__,
3701 decode_path=decode_path,
3706 klass=self.__class__,
3707 decode_path=decode_path,
3711 l, llen, v = len_decode(lv)
3712 except DecodeError as err:
3713 raise err.__class__(
3715 klass=self.__class__,
3716 decode_path=decode_path,
3720 raise NotEnoughData(
3721 "encoded length is longer than data",
3722 klass=self.__class__,
3725 v, tail = v[:l], v[l:]
3726 sub_offset = offset + tlen + llen
3728 specs_items = self.specs.items
3730 for name, spec in specs_items():
3732 value, v_tail = spec.decode(
3736 decode_path=decode_path + (name,),
3741 value.expl_tlvlen if value.expled else value.tlvlen
3744 if spec.default is None or value != spec.default: # pragma: no cover
3745 # SeqMixing.test_encoded_default_accepted covers that place
3746 values[name] = value
3750 klass=self.__class__,
3751 decode_path=decode_path,
3754 obj = self.__class__(
3758 default=self.default,
3759 optional=self.optional,
3760 _decoded=(offset, llen, l),
3766 class SequenceOf(Obj):
3767 """``SEQUENCE OF`` sequence type
3769 For that kind of type you must specify the object it will carry on
3770 (bounds are for example here, not required)::
3772 class Ints(SequenceOf):
3777 >>> ints.append(Integer(123))
3778 >>> ints.append(Integer(234))
3780 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3781 >>> [int(i) for i in ints]
3783 >>> ints.append(Integer(345))
3784 Traceback (most recent call last):
3785 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3788 >>> ints[1] = Integer(345)
3790 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3792 Also you can initialize sequence with preinitialized values:
3794 >>> ints = Ints([Integer(123), Integer(234)])
3796 __slots__ = ("spec", "_bound_min", "_bound_max")
3797 tag_default = tag_encode(form=TagFormConstructed, num=16)
3798 asn1_type_name = "SEQUENCE OF"
3811 super(SequenceOf, self).__init__(
3819 schema = getattr(self, "schema", None)
3821 raise ValueError("schema must be specified")
3823 self._bound_min, self._bound_max = getattr(
3827 ) if bounds is None else bounds
3829 if value is not None:
3830 self._value = self._value_sanitize(value)
3831 if default is not None:
3832 default_value = self._value_sanitize(default)
3833 default_obj = self.__class__(
3838 default_obj._value = default_value
3839 self.default = default_obj
3841 self._value = default_obj.copy()._value
3843 def _value_sanitize(self, value):
3844 if issubclass(value.__class__, SequenceOf):
3845 value = value._value
3846 elif hasattr(value, "__iter__"):
3849 raise InvalidValueType((self.__class__, iter))
3850 if not self._bound_min <= len(value) <= self._bound_max:
3851 raise BoundsError(self._bound_min, len(value), self._bound_max)
3853 if not isinstance(v, self.spec.__class__):
3854 raise InvalidValueType((self.spec.__class__,))
3859 return all(v.ready for v in self._value)
3862 obj = self.__class__(schema=self.spec)
3863 obj._bound_min = self._bound_min
3864 obj._bound_max = self._bound_max
3866 obj._expl = self._expl
3867 obj.default = self.default
3868 obj.optional = self.optional
3869 obj.offset = self.offset
3870 obj.llen = self.llen
3871 obj.vlen = self.vlen
3872 obj._value = [v.copy() for v in self._value]
3875 def __eq__(self, their):
3876 if isinstance(their, self.__class__):
3878 self.spec == their.spec and
3879 self.tag == their.tag and
3880 self._expl == their._expl and
3881 self._value == their._value
3883 if hasattr(their, "__iter__"):
3884 return self._value == list(their)
3896 return self.__class__(
3900 (self._bound_min, self._bound_max)
3901 if bounds is None else bounds
3903 impl=self.tag if impl is None else impl,
3904 expl=self._expl if expl is None else expl,
3905 default=self.default if default is None else default,
3906 optional=self.optional if optional is None else optional,
3909 def __contains__(self, key):
3910 return key in self._value
3912 def append(self, value):
3913 if not isinstance(value, self.spec.__class__):
3914 raise InvalidValueType((self.spec.__class__,))
3915 if len(self._value) + 1 > self._bound_max:
3918 len(self._value) + 1,
3921 self._value.append(value)
3924 self._assert_ready()
3925 return iter(self._value)
3928 self._assert_ready()
3929 return len(self._value)
3931 def __setitem__(self, key, value):
3932 if not isinstance(value, self.spec.__class__):
3933 raise InvalidValueType((self.spec.__class__,))
3934 self._value[key] = self.spec(value=value)
3936 def __getitem__(self, key):
3937 return self._value[key]
3939 def _encoded_values(self):
3940 return [v.encode() for v in self._value]
3943 v = b"".join(self._encoded_values())
3944 return b"".join((self.tag, len_encode(len(v)), v))
3946 def _decode(self, tlv, offset=0, decode_path=()):
3948 t, tlen, lv = tag_strip(tlv)
3949 except DecodeError as err:
3950 raise err.__class__(
3952 klass=self.__class__,
3953 decode_path=decode_path,
3958 klass=self.__class__,
3959 decode_path=decode_path,
3963 l, llen, v = len_decode(lv)
3964 except DecodeError as err:
3965 raise err.__class__(
3967 klass=self.__class__,
3968 decode_path=decode_path,
3972 raise NotEnoughData(
3973 "encoded length is longer than data",
3974 klass=self.__class__,
3975 decode_path=decode_path,
3978 v, tail = v[:l], v[l:]
3979 sub_offset = offset + tlen + llen
3983 value, v_tail = spec.decode(
3987 decode_path=decode_path + (str(len(_value)),),
3989 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3991 _value.append(value)
3992 obj = self.__class__(
3995 bounds=(self._bound_min, self._bound_max),
3998 default=self.default,
3999 optional=self.optional,
4000 _decoded=(offset, llen, l),
4006 pp_console_row(next(self.pps())),
4007 ", ".join(repr(v) for v in self._value),
4010 def pps(self, decode_path=()):
4012 asn1_type_name=self.asn1_type_name,
4013 obj_name=self.__class__.__name__,
4014 decode_path=decode_path,
4015 optional=self.optional,
4016 default=self == self.default,
4017 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4018 expl=None if self._expl is None else tag_decode(self._expl),
4023 expl_offset=self.expl_offset if self.expled else None,
4024 expl_tlen=self.expl_tlen if self.expled else None,
4025 expl_llen=self.expl_llen if self.expled else None,
4026 expl_vlen=self.expl_vlen if self.expled else None,
4028 for i, value in enumerate(self._value):
4029 yield value.pps(decode_path=decode_path + (str(i),))
4032 class SetOf(SequenceOf):
4033 """``SET OF`` sequence type
4035 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4038 tag_default = tag_encode(form=TagFormConstructed, num=17)
4039 asn1_type_name = "SET OF"
4042 raws = self._encoded_values()
4045 return b"".join((self.tag, len_encode(len(v)), v))
4048 def obj_by_path(pypath): # pragma: no cover
4049 """Import object specified as string Python path
4051 Modules must be separated from classes/functions with ``:``.
4053 >>> obj_by_path("foo.bar:Baz")
4054 <class 'foo.bar.Baz'>
4055 >>> obj_by_path("foo.bar:Baz.boo")
4056 <classmethod 'foo.bar.Baz.boo'>
4058 mod, objs = pypath.rsplit(":", 1)
4059 from importlib import import_module
4060 obj = import_module(mod)
4061 for obj_name in objs.split("."):
4062 obj = getattr(obj, obj_name)
4066 def main(): # pragma: no cover
4068 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4069 parser.add_argument(
4071 help="Python path to dictionary with OIDs",
4073 parser.add_argument(
4075 help="Python path to schema definition to use",
4077 parser.add_argument(
4079 type=argparse.FileType("rb"),
4080 help="Path to DER file you want to decode",
4082 args = parser.parse_args()
4083 der = memoryview(args.DERFile.read())
4084 args.DERFile.close()
4085 oids = obj_by_path(args.oids) if args.oids else {}
4087 schema = obj_by_path(args.schema)
4088 from functools import partial
4089 pprinter = partial(pprint, big_blobs=True)
4091 # All of this below is a big hack with self references
4092 choice = PrimitiveTypes()
4093 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4094 choice.specs["SetOf"] = SetOf(schema=choice)
4096 choice.specs["SequenceOf%d" % i] = SequenceOf(
4100 choice.specs["Any"] = Any()
4102 # Class name equals to type name, to omit it from output
4103 class SEQUENCEOF(SequenceOf):
4106 schema = SEQUENCEOF()
4108 def pprint_any(obj, oids=None):
4109 def _pprint_pps(pps):
4111 if hasattr(pp, "_fields"):
4112 if pp.asn1_type_name == Choice.asn1_type_name:
4114 pp_kwargs = pp._asdict()
4115 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4116 pp = _pp(**pp_kwargs)
4117 yield pp_console_row(
4123 for row in pp_console_blob(pp):
4126 for row in _pprint_pps(pp):
4128 return "\n".join(_pprint_pps(obj.pps()))
4129 pprinter = pprint_any
4130 obj, tail = schema().decode(der)
4131 print(pprinter(obj, oids=oids))
4133 print("\nTrailing data: %s" % hexenc(tail))
4136 if __name__ == "__main__":