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.
692 self.tag = getattr(self, "impl", self.tag_default)
695 self._expl = getattr(self, "expl", None) if expl is None else expl
696 if self.tag != self.tag_default and self._expl is not None:
698 "implicit and explicit tags can not be set simultaneously"
700 if default is not None:
702 self.optional = optional
703 self.offset, self.llen, self.vlen = _decoded
707 def ready(self): # pragma: no cover
708 """Is object ready to be encoded?
710 raise NotImplementedError()
712 def _assert_ready(self):
714 raise ObjNotReady(self.__class__.__name__)
718 """Is object decoded?
720 return (self.llen + self.vlen) > 0
722 def copy(self): # pragma: no cover
723 """Make a copy of object, safe to be mutated
725 raise NotImplementedError()
733 return self.tlen + self.llen + self.vlen
735 def __str__(self): # pragma: no cover
736 return self.__bytes__() if PY2 else self.__unicode__()
738 def __ne__(self, their):
739 return not(self == their)
741 def __gt__(self, their): # pragma: no cover
742 return not(self < their)
744 def __le__(self, their): # pragma: no cover
745 return (self == their) or (self < their)
747 def __ge__(self, their): # pragma: no cover
748 return (self == their) or (self > their)
750 def _encode(self): # pragma: no cover
751 raise NotImplementedError()
753 def _decode(self, tlv, offset=0, decode_path=()): # pragma: no cover
754 raise NotImplementedError()
758 if self._expl is None:
760 return b"".join((self._expl, len_encode(len(raw)), raw))
762 def decode(self, data, offset=0, leavemm=False, decode_path=()):
765 :param data: either binary or memoryview
766 :param int offset: initial data's offset
767 :param bool leavemm: do we need to leave memoryview of remaining
768 data as is, or convert it to bytes otherwise
769 :returns: (Obj, remaining data)
771 tlv = memoryview(data)
772 if self._expl is None:
773 obj, tail = self._decode(
776 decode_path=decode_path,
780 t, tlen, lv = tag_strip(tlv)
781 except DecodeError as err:
784 klass=self.__class__,
785 decode_path=decode_path,
790 klass=self.__class__,
791 decode_path=decode_path,
795 l, llen, v = len_decode(lv)
796 except DecodeError as err:
799 klass=self.__class__,
800 decode_path=decode_path,
805 "encoded length is longer than data",
806 klass=self.__class__,
807 decode_path=decode_path,
810 obj, tail = self._decode(
812 offset=offset + tlen + llen,
815 return obj, (tail if leavemm else tail.tobytes())
819 return self._expl is not None
827 return len(self._expl)
831 return len(len_encode(self.tlvlen))
834 def expl_offset(self):
835 return self.offset - self.expl_tlen - self.expl_llen
842 def expl_tlvlen(self):
843 return self.expl_tlen + self.expl_llen + self.expl_vlen
846 ########################################################################
848 ########################################################################
850 PP = namedtuple("PP", (
872 asn1_type_name="unknown",
911 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
914 cols.append("%5d%s [%d,%d,%4d]" % (
917 " " if pp.expl_offset is None else
918 ("-%d" % (pp.offset - pp.expl_offset))
924 if len(pp.decode_path) > 0:
925 cols.append(" ." * (len(pp.decode_path)))
926 cols.append("%s:" % pp.decode_path[-1])
927 if pp.expl is not None:
928 klass, _, num = pp.expl
929 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
930 if pp.impl is not None:
931 klass, _, num = pp.impl
932 cols.append("[%s%d]" % (TagClassReprs[klass], num))
933 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
934 cols.append(pp.obj_name)
935 cols.append(pp.asn1_type_name)
936 if pp.value is not None:
940 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
943 value = "%s (%s)" % (oids[value], pp.value)
946 if isinstance(pp.blob, binary_type):
947 cols.append(hexenc(pp.blob))
948 elif isinstance(pp.blob, tuple):
949 cols.append(", ".join(pp.blob))
951 cols.append("OPTIONAL")
953 cols.append("DEFAULT")
954 return " ".join(cols)
957 def pp_console_blob(pp):
958 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
959 if len(pp.decode_path) > 0:
960 cols.append(" ." * (len(pp.decode_path) + 1))
961 if isinstance(pp.blob, binary_type):
962 blob = hexenc(pp.blob).upper()
963 for i in range(0, len(blob), 32):
964 chunk = blob[i:i + 32]
965 yield " ".join(cols + [":".join(
966 chunk[j:j + 2] for j in range(0, len(chunk), 2)
968 elif isinstance(pp.blob, tuple):
969 yield " ".join(cols + [", ".join(pp.blob)])
972 def pprint(obj, oids=None, big_blobs=False):
973 """Pretty print object
975 :param Obj obj: object you want to pretty print
976 :param oids: ``OID <-> humand readable string`` dictionary. When OID
977 from it is met, then its humand readable form is printed
978 :param big_blobs: if large binary objects are met (like OctetString
979 values), do we need to print them too, on separate
982 def _pprint_pps(pps):
984 if hasattr(pp, "_fields"):
986 yield pp_console_row(
992 for row in pp_console_blob(pp):
995 yield pp_console_row(pp, oids=oids, with_offsets=True)
997 for row in _pprint_pps(pp):
999 return "\n".join(_pprint_pps(obj.pps()))
1002 ########################################################################
1003 # ASN.1 primitive types
1004 ########################################################################
1007 """``BOOLEAN`` boolean type
1009 >>> b = Boolean(True)
1011 >>> b == Boolean(True)
1017 tag_default = tag_encode(1)
1018 asn1_type_name = "BOOLEAN"
1030 :param value: set the value. Either boolean type, or
1031 :py:class:`pyderasn.Boolean` object
1032 :param bytes impl: override default tag with ``IMPLICIT`` one
1033 :param bytes expl: override default tag with ``EXPLICIT`` one
1034 :param default: set default value. Type same as in ``value``
1035 :param bool optional: is object ``OPTIONAL`` in sequence
1037 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1038 self._value = None if value is None else self._value_sanitize(value)
1039 if default is not None:
1040 default = self._value_sanitize(default)
1041 self.default = self.__class__(
1047 self._value = default
1049 def _value_sanitize(self, value):
1050 if issubclass(value.__class__, Boolean):
1052 if isinstance(value, bool):
1054 raise InvalidValueType((self.__class__, bool))
1058 return self._value is not None
1061 obj = self.__class__()
1062 obj._value = self._value
1064 obj._expl = self._expl
1065 obj.default = self.default
1066 obj.optional = self.optional
1067 obj.offset = self.offset
1068 obj.llen = self.llen
1069 obj.vlen = self.vlen
1072 def __nonzero__(self):
1073 self._assert_ready()
1077 self._assert_ready()
1080 def __eq__(self, their):
1081 if isinstance(their, bool):
1082 return self._value == their
1083 if not issubclass(their.__class__, Boolean):
1086 self._value == their._value and
1087 self.tag == their.tag and
1088 self._expl == their._expl
1099 return self.__class__(
1101 impl=self.tag if impl is None else impl,
1102 expl=self._expl if expl is None else expl,
1103 default=self.default if default is None else default,
1104 optional=self.optional if optional is None else optional,
1108 self._assert_ready()
1112 (b"\xFF" if self._value else b"\x00"),
1115 def _decode(self, tlv, offset=0, decode_path=()):
1117 t, _, lv = tag_strip(tlv)
1118 except DecodeError as err:
1119 raise err.__class__(
1121 klass=self.__class__,
1122 decode_path=decode_path,
1127 klass=self.__class__,
1128 decode_path=decode_path,
1132 l, _, v = len_decode(lv)
1133 except DecodeError as err:
1134 raise err.__class__(
1136 klass=self.__class__,
1137 decode_path=decode_path,
1141 raise InvalidLength(
1142 "Boolean's length must be equal to 1",
1143 klass=self.__class__,
1144 decode_path=decode_path,
1148 raise NotEnoughData(
1149 "encoded length is longer than data",
1150 klass=self.__class__,
1151 decode_path=decode_path,
1154 first_octet = byte2int(v)
1155 if first_octet == 0:
1157 elif first_octet == 0xFF:
1161 "unacceptable Boolean value",
1162 klass=self.__class__,
1163 decode_path=decode_path,
1166 obj = self.__class__(
1170 default=self.default,
1171 optional=self.optional,
1172 _decoded=(offset, 1, 1),
1177 return pp_console_row(next(self.pps()))
1179 def pps(self, decode_path=()):
1181 asn1_type_name=self.asn1_type_name,
1182 obj_name=self.__class__.__name__,
1183 decode_path=decode_path,
1184 value=str(self._value) if self.ready else None,
1185 optional=self.optional,
1186 default=self == self.default,
1187 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1188 expl=None if self._expl is None else tag_decode(self._expl),
1193 expl_offset=self.expl_offset if self.expled else None,
1194 expl_tlen=self.expl_tlen if self.expled else None,
1195 expl_llen=self.expl_llen if self.expled else None,
1196 expl_vlen=self.expl_vlen if self.expled else None,
1201 """``INTEGER`` integer type
1203 >>> b = Integer(-123)
1205 >>> b == Integer(-123)
1210 >>> Integer(2, bounds=(1, 3))
1212 >>> Integer(5, bounds=(1, 3))
1213 Traceback (most recent call last):
1214 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1218 class Version(Integer):
1225 >>> v = Version("v1")
1232 {'v3': 2, 'v1': 0, 'v2': 1}
1234 __slots__ = ("specs", "_bound_min", "_bound_max")
1235 tag_default = tag_encode(2)
1236 asn1_type_name = "INTEGER"
1250 :param value: set the value. Either integer type, named value
1251 (if ``schema`` is specified in the class), or
1252 :py:class:`pyderasn.Integer` object
1253 :param bounds: set ``(MIN, MAX)`` value constraint.
1254 (-inf, +inf) by default
1255 :param bytes impl: override default tag with ``IMPLICIT`` one
1256 :param bytes expl: override default tag with ``EXPLICIT`` one
1257 :param default: set default value. Type same as in ``value``
1258 :param bool optional: is object ``OPTIONAL`` in sequence
1260 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1262 specs = getattr(self, "schema", {}) if _specs is None else _specs
1263 self.specs = specs if isinstance(specs, dict) else dict(specs)
1265 self._bound_min, self._bound_max = getattr(
1268 (float("-inf"), float("+inf")),
1271 self._bound_min, self._bound_max = bounds
1272 if value is not None:
1273 self._value = self._value_sanitize(value)
1274 if default is not None:
1275 default = self._value_sanitize(default)
1276 self.default = self.__class__(
1282 if self._value is None:
1283 self._value = default
1285 def _value_sanitize(self, value):
1286 if issubclass(value.__class__, Integer):
1287 value = value._value
1288 elif isinstance(value, integer_types):
1290 elif isinstance(value, str):
1291 value = self.specs.get(value)
1293 raise ObjUnknown("integer value: %s" % value)
1295 raise InvalidValueType((self.__class__, int, str))
1296 if not self._bound_min <= value <= self._bound_max:
1297 raise BoundsError(self._bound_min, value, self._bound_max)
1302 return self._value is not None
1305 obj = self.__class__(_specs=self.specs)
1306 obj._value = self._value
1307 obj._bound_min = self._bound_min
1308 obj._bound_max = self._bound_max
1310 obj._expl = self._expl
1311 obj.default = self.default
1312 obj.optional = self.optional
1313 obj.offset = self.offset
1314 obj.llen = self.llen
1315 obj.vlen = self.vlen
1319 self._assert_ready()
1320 return int(self._value)
1323 self._assert_ready()
1326 bytes(self._expl or b"") +
1327 str(self._value).encode("ascii"),
1330 def __eq__(self, their):
1331 if isinstance(their, integer_types):
1332 return self._value == their
1333 if not issubclass(their.__class__, Integer):
1336 self._value == their._value and
1337 self.tag == their.tag and
1338 self._expl == their._expl
1341 def __lt__(self, their):
1342 return self._value < their._value
1346 for name, value in self.specs.items():
1347 if value == self._value:
1359 return self.__class__(
1362 (self._bound_min, self._bound_max)
1363 if bounds is None else bounds
1365 impl=self.tag if impl is None else impl,
1366 expl=self._expl if expl is None else expl,
1367 default=self.default if default is None else default,
1368 optional=self.optional if optional is None else optional,
1373 self._assert_ready()
1377 octets = bytearray([0])
1381 octets = bytearray()
1383 octets.append((value & 0xFF) ^ 0xFF)
1385 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1388 octets = bytearray()
1390 octets.append(value & 0xFF)
1392 if octets[-1] & 0x80 > 0:
1395 octets = bytes(octets)
1397 bytes_len = ceil(value.bit_length() / 8) or 1
1400 octets = value.to_bytes(
1405 except OverflowError:
1409 return b"".join((self.tag, len_encode(len(octets)), octets))
1411 def _decode(self, tlv, offset=0, decode_path=()):
1413 t, _, lv = tag_strip(tlv)
1414 except DecodeError as err:
1415 raise err.__class__(
1417 klass=self.__class__,
1418 decode_path=decode_path,
1423 klass=self.__class__,
1424 decode_path=decode_path,
1428 l, llen, v = len_decode(lv)
1429 except DecodeError as err:
1430 raise err.__class__(
1432 klass=self.__class__,
1433 decode_path=decode_path,
1437 raise NotEnoughData(
1438 "encoded length is longer than data",
1439 klass=self.__class__,
1440 decode_path=decode_path,
1444 raise NotEnoughData(
1446 klass=self.__class__,
1447 decode_path=decode_path,
1450 v, tail = v[:l], v[l:]
1451 first_octet = byte2int(v)
1453 second_octet = byte2int(v[1:])
1455 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1456 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1459 "non normalized integer",
1460 klass=self.__class__,
1461 decode_path=decode_path,
1466 if first_octet & 0x80 > 0:
1467 octets = bytearray()
1468 for octet in bytearray(v):
1469 octets.append(octet ^ 0xFF)
1470 for octet in octets:
1471 value = (value << 8) | octet
1475 for octet in bytearray(v):
1476 value = (value << 8) | octet
1478 value = int.from_bytes(v, byteorder="big", signed=True)
1480 obj = self.__class__(
1482 bounds=(self._bound_min, self._bound_max),
1485 default=self.default,
1486 optional=self.optional,
1488 _decoded=(offset, llen, l),
1490 except BoundsError as err:
1493 klass=self.__class__,
1494 decode_path=decode_path,
1500 return pp_console_row(next(self.pps()))
1502 def pps(self, decode_path=()):
1504 asn1_type_name=self.asn1_type_name,
1505 obj_name=self.__class__.__name__,
1506 decode_path=decode_path,
1507 value=(self.named or str(self._value)) if self.ready else None,
1508 optional=self.optional,
1509 default=self == self.default,
1510 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1511 expl=None if self._expl is None else tag_decode(self._expl),
1516 expl_offset=self.expl_offset if self.expled else None,
1517 expl_tlen=self.expl_tlen if self.expled else None,
1518 expl_llen=self.expl_llen if self.expled else None,
1519 expl_vlen=self.expl_vlen if self.expled else None,
1523 class BitString(Obj):
1524 """``BIT STRING`` bit string type
1526 >>> BitString(b"hello world")
1527 BIT STRING 88 bits 68656c6c6f20776f726c64
1530 >>> b == b"hello world"
1535 >>> b = BitString("'010110000000'B")
1536 BIT STRING 12 bits 5800
1539 >>> b[0], b[1], b[2], b[3]
1540 (False, True, False, True)
1544 [False, True, False, True, True, False, False, False, False, False, False, False]
1548 class KeyUsage(BitString):
1550 ('digitalSignature', 0),
1551 ('nonRepudiation', 1),
1552 ('keyEncipherment', 2),
1555 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1556 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1558 ['nonRepudiation', 'keyEncipherment']
1560 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1562 __slots__ = ("specs",)
1563 tag_default = tag_encode(3)
1564 asn1_type_name = "BIT STRING"
1577 :param value: set the value. Either binary type, tuple of named
1578 values (if ``schema`` is specified in the class),
1579 string in ``'XXX...'B`` form, or
1580 :py:class:`pyderasn.BitString` object
1581 :param bytes impl: override default tag with ``IMPLICIT`` one
1582 :param bytes expl: override default tag with ``EXPLICIT`` one
1583 :param default: set default value. Type same as in ``value``
1584 :param bool optional: is object ``OPTIONAL`` in sequence
1586 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1587 specs = getattr(self, "schema", {}) if _specs is None else _specs
1588 self.specs = specs if isinstance(specs, dict) else dict(specs)
1589 self._value = None if value is None else self._value_sanitize(value)
1590 if default is not None:
1591 default = self._value_sanitize(default)
1592 self.default = self.__class__(
1598 self._value = default
1600 def _bits2octets(self, bits):
1601 if len(self.specs) > 0:
1602 bits = bits.rstrip("0")
1604 bits += "0" * ((8 - (bit_len % 8)) % 8)
1605 octets = bytearray(len(bits) // 8)
1606 for i in six_xrange(len(octets)):
1607 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1608 return bit_len, bytes(octets)
1610 def _value_sanitize(self, value):
1611 if issubclass(value.__class__, BitString):
1613 if isinstance(value, (string_types, binary_type)):
1615 isinstance(value, string_types) and
1616 value.startswith("'") and
1617 value.endswith("'B")
1620 if not set(value) <= set(("0", "1")):
1621 raise ValueError("B's coding contains unacceptable chars")
1622 return self._bits2octets(value)
1623 elif isinstance(value, binary_type):
1624 return (len(value) * 8, value)
1626 raise InvalidValueType((
1631 if isinstance(value, tuple):
1634 isinstance(value[0], integer_types) and
1635 isinstance(value[1], binary_type)
1640 bit = self.specs.get(name)
1642 raise ObjUnknown("BitString value: %s" % name)
1645 return self._bits2octets("")
1647 return self._bits2octets("".join(
1648 ("1" if bit in bits else "0")
1649 for bit in six_xrange(max(bits) + 1)
1651 raise InvalidValueType((self.__class__, binary_type, string_types))
1655 return self._value is not None
1658 obj = self.__class__(_specs=self.specs)
1660 if value is not None:
1661 value = (value[0], value[1])
1664 obj._expl = self._expl
1665 obj.default = self.default
1666 obj.optional = self.optional
1667 obj.offset = self.offset
1668 obj.llen = self.llen
1669 obj.vlen = self.vlen
1673 self._assert_ready()
1674 for i in six_xrange(self._value[0]):
1679 self._assert_ready()
1680 return self._value[0]
1682 def __bytes__(self):
1683 self._assert_ready()
1684 return self._value[1]
1686 def __eq__(self, their):
1687 if isinstance(their, bytes):
1688 return self._value[1] == their
1689 if not issubclass(their.__class__, BitString):
1692 self._value == their._value and
1693 self.tag == their.tag and
1694 self._expl == their._expl
1699 return [name for name, bit in self.specs.items() if self[bit]]
1709 return self.__class__(
1711 impl=self.tag if impl is None else impl,
1712 expl=self._expl if expl is None else expl,
1713 default=self.default if default is None else default,
1714 optional=self.optional if optional is None else optional,
1718 def __getitem__(self, key):
1719 if isinstance(key, int):
1720 bit_len, octets = self._value
1724 byte2int(memoryview(octets)[key // 8:]) >>
1727 if isinstance(key, string_types):
1728 value = self.specs.get(key)
1730 raise ObjUnknown("BitString value: %s" % key)
1732 raise InvalidValueType((int, str))
1735 self._assert_ready()
1736 bit_len, octets = self._value
1739 len_encode(len(octets) + 1),
1740 int2byte((8 - bit_len % 8) % 8),
1744 def _decode(self, tlv, offset=0, decode_path=()):
1746 t, _, lv = tag_strip(tlv)
1747 except DecodeError as err:
1748 raise err.__class__(
1750 klass=self.__class__,
1751 decode_path=decode_path,
1756 klass=self.__class__,
1757 decode_path=decode_path,
1761 l, llen, v = len_decode(lv)
1762 except DecodeError as err:
1763 raise err.__class__(
1765 klass=self.__class__,
1766 decode_path=decode_path,
1770 raise NotEnoughData(
1771 "encoded length is longer than data",
1772 klass=self.__class__,
1773 decode_path=decode_path,
1777 raise NotEnoughData(
1779 klass=self.__class__,
1780 decode_path=decode_path,
1783 pad_size = byte2int(v)
1784 if l == 1 and pad_size != 0:
1786 "invalid empty value",
1787 klass=self.__class__,
1788 decode_path=decode_path,
1794 klass=self.__class__,
1795 decode_path=decode_path,
1798 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1801 klass=self.__class__,
1802 decode_path=decode_path,
1805 v, tail = v[:l], v[l:]
1806 obj = self.__class__(
1807 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1810 default=self.default,
1811 optional=self.optional,
1813 _decoded=(offset, llen, l),
1818 return pp_console_row(next(self.pps()))
1820 def pps(self, decode_path=()):
1824 bit_len, blob = self._value
1825 value = "%d bits" % bit_len
1826 if len(self.specs) > 0:
1827 blob = tuple(self.named)
1829 asn1_type_name=self.asn1_type_name,
1830 obj_name=self.__class__.__name__,
1831 decode_path=decode_path,
1834 optional=self.optional,
1835 default=self == self.default,
1836 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1837 expl=None if self._expl is None else tag_decode(self._expl),
1842 expl_offset=self.expl_offset if self.expled else None,
1843 expl_tlen=self.expl_tlen if self.expled else None,
1844 expl_llen=self.expl_llen if self.expled else None,
1845 expl_vlen=self.expl_vlen if self.expled else None,
1849 class OctetString(Obj):
1850 """``OCTET STRING`` binary string type
1852 >>> s = OctetString(b"hello world")
1853 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1854 >>> s == OctetString(b"hello world")
1859 >>> OctetString(b"hello", bounds=(4, 4))
1860 Traceback (most recent call last):
1861 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1862 >>> OctetString(b"hell", bounds=(4, 4))
1863 OCTET STRING 4 bytes 68656c6c
1865 __slots__ = ("_bound_min", "_bound_max")
1866 tag_default = tag_encode(4)
1867 asn1_type_name = "OCTET STRING"
1880 :param value: set the value. Either binary type, or
1881 :py:class:`pyderasn.OctetString` object
1882 :param bounds: set ``(MIN, MAX)`` value size constraint.
1883 (-inf, +inf) by default
1884 :param bytes impl: override default tag with ``IMPLICIT`` one
1885 :param bytes expl: override default tag with ``EXPLICIT`` one
1886 :param default: set default value. Type same as in ``value``
1887 :param bool optional: is object ``OPTIONAL`` in sequence
1889 super(OctetString, self).__init__(
1898 self._bound_min, self._bound_max = getattr(
1904 self._bound_min, self._bound_max = bounds
1905 if value is not None:
1906 self._value = self._value_sanitize(value)
1907 if default is not None:
1908 default = self._value_sanitize(default)
1909 self.default = self.__class__(
1914 if self._value is None:
1915 self._value = default
1917 def _value_sanitize(self, value):
1918 if issubclass(value.__class__, OctetString):
1919 value = value._value
1920 elif isinstance(value, binary_type):
1923 raise InvalidValueType((self.__class__, bytes))
1924 if not self._bound_min <= len(value) <= self._bound_max:
1925 raise BoundsError(self._bound_min, len(value), self._bound_max)
1930 return self._value is not None
1933 obj = self.__class__()
1934 obj._value = self._value
1935 obj._bound_min = self._bound_min
1936 obj._bound_max = self._bound_max
1938 obj._expl = self._expl
1939 obj.default = self.default
1940 obj.optional = self.optional
1941 obj.offset = self.offset
1942 obj.llen = self.llen
1943 obj.vlen = self.vlen
1946 def __bytes__(self):
1947 self._assert_ready()
1950 def __eq__(self, their):
1951 if isinstance(their, binary_type):
1952 return self._value == their
1953 if not issubclass(their.__class__, OctetString):
1956 self._value == their._value and
1957 self.tag == their.tag and
1958 self._expl == their._expl
1961 def __lt__(self, their):
1962 return self._value < their._value
1973 return self.__class__(
1976 (self._bound_min, self._bound_max)
1977 if bounds is None else bounds
1979 impl=self.tag if impl is None else impl,
1980 expl=self._expl if expl is None else expl,
1981 default=self.default if default is None else default,
1982 optional=self.optional if optional is None else optional,
1986 self._assert_ready()
1989 len_encode(len(self._value)),
1993 def _decode(self, tlv, offset=0, decode_path=()):
1995 t, _, lv = tag_strip(tlv)
1996 except DecodeError as err:
1997 raise err.__class__(
1999 klass=self.__class__,
2000 decode_path=decode_path,
2005 klass=self.__class__,
2006 decode_path=decode_path,
2010 l, llen, v = len_decode(lv)
2011 except DecodeError as err:
2012 raise err.__class__(
2014 klass=self.__class__,
2015 decode_path=decode_path,
2019 raise NotEnoughData(
2020 "encoded length is longer than data",
2021 klass=self.__class__,
2022 decode_path=decode_path,
2025 v, tail = v[:l], v[l:]
2027 obj = self.__class__(
2029 bounds=(self._bound_min, self._bound_max),
2032 default=self.default,
2033 optional=self.optional,
2034 _decoded=(offset, llen, l),
2036 except BoundsError as err:
2039 klass=self.__class__,
2040 decode_path=decode_path,
2046 return pp_console_row(next(self.pps()))
2048 def pps(self, decode_path=()):
2050 asn1_type_name=self.asn1_type_name,
2051 obj_name=self.__class__.__name__,
2052 decode_path=decode_path,
2053 value=("%d bytes" % len(self._value)) if self.ready else None,
2054 blob=self._value if self.ready else None,
2055 optional=self.optional,
2056 default=self == self.default,
2057 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2058 expl=None if self._expl is None else tag_decode(self._expl),
2063 expl_offset=self.expl_offset if self.expled else None,
2064 expl_tlen=self.expl_tlen if self.expled else None,
2065 expl_llen=self.expl_llen if self.expled else None,
2066 expl_vlen=self.expl_vlen if self.expled else None,
2071 """``NULL`` null object
2079 tag_default = tag_encode(5)
2080 asn1_type_name = "NULL"
2084 value=None, # unused, but Sequence passes it
2091 :param bytes impl: override default tag with ``IMPLICIT`` one
2092 :param bytes expl: override default tag with ``EXPLICIT`` one
2093 :param bool optional: is object ``OPTIONAL`` in sequence
2095 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2103 obj = self.__class__()
2105 obj._expl = self._expl
2106 obj.default = self.default
2107 obj.optional = self.optional
2108 obj.offset = self.offset
2109 obj.llen = self.llen
2110 obj.vlen = self.vlen
2113 def __eq__(self, their):
2114 if not issubclass(their.__class__, Null):
2117 self.tag == their.tag and
2118 self._expl == their._expl
2128 return self.__class__(
2129 impl=self.tag if impl is None else impl,
2130 expl=self._expl if expl is None else expl,
2131 optional=self.optional if optional is None else optional,
2135 return self.tag + len_encode(0)
2137 def _decode(self, tlv, offset=0, decode_path=()):
2139 t, _, lv = tag_strip(tlv)
2140 except DecodeError as err:
2141 raise err.__class__(
2143 klass=self.__class__,
2144 decode_path=decode_path,
2149 klass=self.__class__,
2150 decode_path=decode_path,
2154 l, _, v = len_decode(lv)
2155 except DecodeError as err:
2156 raise err.__class__(
2158 klass=self.__class__,
2159 decode_path=decode_path,
2163 raise InvalidLength(
2164 "Null must have zero length",
2165 klass=self.__class__,
2166 decode_path=decode_path,
2169 obj = self.__class__(
2172 optional=self.optional,
2173 _decoded=(offset, 1, 0),
2178 return pp_console_row(next(self.pps()))
2180 def pps(self, decode_path=()):
2182 asn1_type_name=self.asn1_type_name,
2183 obj_name=self.__class__.__name__,
2184 decode_path=decode_path,
2185 optional=self.optional,
2186 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2187 expl=None if self._expl is None else tag_decode(self._expl),
2192 expl_offset=self.expl_offset if self.expled else None,
2193 expl_tlen=self.expl_tlen if self.expled else None,
2194 expl_llen=self.expl_llen if self.expled else None,
2195 expl_vlen=self.expl_vlen if self.expled else None,
2199 class ObjectIdentifier(Obj):
2200 """``OBJECT IDENTIFIER`` OID type
2202 >>> oid = ObjectIdentifier((1, 2, 3))
2203 OBJECT IDENTIFIER 1.2.3
2204 >>> oid == ObjectIdentifier("1.2.3")
2210 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2211 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2213 >>> str(ObjectIdentifier((3, 1)))
2214 Traceback (most recent call last):
2215 pyderasn.InvalidOID: unacceptable first arc value
2218 tag_default = tag_encode(6)
2219 asn1_type_name = "OBJECT IDENTIFIER"
2231 :param value: set the value. Either tuples of integers,
2232 string of "."-concatenated integers, or
2233 :py:class:`pyderasn.ObjectIdentifier` object
2234 :param bytes impl: override default tag with ``IMPLICIT`` one
2235 :param bytes expl: override default tag with ``EXPLICIT`` one
2236 :param default: set default value. Type same as in ``value``
2237 :param bool optional: is object ``OPTIONAL`` in sequence
2239 super(ObjectIdentifier, self).__init__(
2247 if value is not None:
2248 self._value = self._value_sanitize(value)
2249 if default is not None:
2250 default = self._value_sanitize(default)
2251 self.default = self.__class__(
2256 if self._value is None:
2257 self._value = default
2259 def __add__(self, their):
2260 if isinstance(their, self.__class__):
2261 return self.__class__(self._value + their._value)
2262 if isinstance(their, tuple):
2263 return self.__class__(self._value + their)
2264 raise InvalidValueType((self.__class__, tuple))
2266 def _value_sanitize(self, value):
2267 if issubclass(value.__class__, ObjectIdentifier):
2269 if isinstance(value, string_types):
2271 value = tuple(int(arc) for arc in value.split("."))
2273 raise InvalidOID("unacceptable arcs values")
2274 if isinstance(value, tuple):
2276 raise InvalidOID("less than 2 arcs")
2277 first_arc = value[0]
2278 if first_arc in (0, 1):
2279 if not (0 <= value[1] <= 39):
2280 raise InvalidOID("second arc is too wide")
2281 elif first_arc == 2:
2284 raise InvalidOID("unacceptable first arc value")
2286 raise InvalidValueType((self.__class__, str, tuple))
2290 return self._value is not None
2293 obj = self.__class__()
2294 obj._value = self._value
2296 obj._expl = self._expl
2297 obj.default = self.default
2298 obj.optional = self.optional
2299 obj.offset = self.offset
2300 obj.llen = self.llen
2301 obj.vlen = self.vlen
2305 self._assert_ready()
2306 return iter(self._value)
2309 return ".".join(str(arc) for arc in self._value or ())
2312 self._assert_ready()
2315 bytes(self._expl or b"") +
2316 str(self._value).encode("ascii"),
2319 def __eq__(self, their):
2320 if isinstance(their, tuple):
2321 return self._value == their
2322 if not issubclass(their.__class__, ObjectIdentifier):
2325 self.tag == their.tag and
2326 self._expl == their._expl and
2327 self._value == their._value
2330 def __lt__(self, their):
2331 return self._value < their._value
2341 return self.__class__(
2343 impl=self.tag if impl is None else impl,
2344 expl=self._expl if expl is None else expl,
2345 default=self.default if default is None else default,
2346 optional=self.optional if optional is None else optional,
2350 self._assert_ready()
2352 first_value = value[1]
2353 first_arc = value[0]
2356 elif first_arc == 1:
2358 elif first_arc == 2:
2360 else: # pragma: no cover
2361 raise RuntimeError("invalid arc is stored")
2362 octets = [zero_ended_encode(first_value)]
2363 for arc in value[2:]:
2364 octets.append(zero_ended_encode(arc))
2365 v = b"".join(octets)
2366 return b"".join((self.tag, len_encode(len(v)), v))
2368 def _decode(self, tlv, offset=0, decode_path=()):
2370 t, _, lv = tag_strip(tlv)
2371 except DecodeError as err:
2372 raise err.__class__(
2374 klass=self.__class__,
2375 decode_path=decode_path,
2380 klass=self.__class__,
2381 decode_path=decode_path,
2385 l, llen, v = len_decode(lv)
2386 except DecodeError as err:
2387 raise err.__class__(
2389 klass=self.__class__,
2390 decode_path=decode_path,
2394 raise NotEnoughData(
2395 "encoded length is longer than data",
2396 klass=self.__class__,
2397 decode_path=decode_path,
2401 raise NotEnoughData(
2403 klass=self.__class__,
2404 decode_path=decode_path,
2407 v, tail = v[:l], v[l:]
2413 octet = indexbytes(v, i)
2414 arc = (arc << 7) | (octet & 0x7F)
2415 if octet & 0x80 == 0:
2423 klass=self.__class__,
2424 decode_path=decode_path,
2428 second_arc = arcs[0]
2429 if 0 <= second_arc <= 39:
2431 elif 40 <= second_arc <= 79:
2437 obj = self.__class__(
2438 value=tuple([first_arc, second_arc] + arcs[1:]),
2441 default=self.default,
2442 optional=self.optional,
2443 _decoded=(offset, llen, l),
2448 return pp_console_row(next(self.pps()))
2450 def pps(self, decode_path=()):
2452 asn1_type_name=self.asn1_type_name,
2453 obj_name=self.__class__.__name__,
2454 decode_path=decode_path,
2455 value=str(self) if self.ready else None,
2456 optional=self.optional,
2457 default=self == self.default,
2458 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2459 expl=None if self._expl is None else tag_decode(self._expl),
2464 expl_offset=self.expl_offset if self.expled else None,
2465 expl_tlen=self.expl_tlen if self.expled else None,
2466 expl_llen=self.expl_llen if self.expled else None,
2467 expl_vlen=self.expl_vlen if self.expled else None,
2471 class Enumerated(Integer):
2472 """``ENUMERATED`` integer type
2474 This type is identical to :py:class:`pyderasn.Integer`, but requires
2475 schema to be specified and does not accept values missing from it.
2478 tag_default = tag_encode(10)
2479 asn1_type_name = "ENUMERATED"
2490 bounds=None, # dummy argument, workability for Integer.decode
2492 super(Enumerated, self).__init__(
2501 if len(self.specs) == 0:
2502 raise ValueError("schema must be specified")
2504 def _value_sanitize(self, value):
2505 if isinstance(value, self.__class__):
2506 value = value._value
2507 elif isinstance(value, integer_types):
2508 if value not in list(self.specs.values()):
2510 "unknown integer value: %s" % value,
2511 klass=self.__class__,
2513 elif isinstance(value, string_types):
2514 value = self.specs.get(value)
2516 raise ObjUnknown("integer value: %s" % value)
2518 raise InvalidValueType((self.__class__, int, str))
2522 obj = self.__class__(_specs=self.specs)
2523 obj._value = self._value
2524 obj._bound_min = self._bound_min
2525 obj._bound_max = self._bound_max
2527 obj._expl = self._expl
2528 obj.default = self.default
2529 obj.optional = self.optional
2530 obj.offset = self.offset
2531 obj.llen = self.llen
2532 obj.vlen = self.vlen
2544 return self.__class__(
2546 impl=self.tag if impl is None else impl,
2547 expl=self._expl if expl is None else expl,
2548 default=self.default if default is None else default,
2549 optional=self.optional if optional is None else optional,
2554 class CommonString(OctetString):
2555 """Common class for all strings
2557 Everything resembles :py:class:`pyderasn.OctetString`, except
2558 ability to deal with unicode text strings.
2560 >>> hexenc("привет мир".encode("utf-8"))
2561 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2562 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2564 >>> s = UTF8String("привет мир")
2565 UTF8String UTF8String привет мир
2567 'привет мир'
2568 >>> hexenc(bytes(s))
2569 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2571 >>> PrintableString("привет мир")
2572 Traceback (most recent call last):
2573 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2575 >>> BMPString("ада", bounds=(2, 2))
2576 Traceback (most recent call last):
2577 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2578 >>> s = BMPString("ад", bounds=(2, 2))
2581 >>> hexenc(bytes(s))
2589 * - :py:class:`pyderasn.UTF8String`
2591 * - :py:class:`pyderasn.NumericString`
2593 * - :py:class:`pyderasn.PrintableString`
2595 * - :py:class:`pyderasn.TeletexString`
2597 * - :py:class:`pyderasn.T61String`
2599 * - :py:class:`pyderasn.VideotexString`
2601 * - :py:class:`pyderasn.IA5String`
2603 * - :py:class:`pyderasn.GraphicString`
2605 * - :py:class:`pyderasn.VisibleString`
2607 * - :py:class:`pyderasn.ISO646String`
2609 * - :py:class:`pyderasn.GeneralString`
2611 * - :py:class:`pyderasn.UniversalString`
2613 * - :py:class:`pyderasn.BMPString`
2616 __slots__ = ("encoding",)
2618 def _value_sanitize(self, value):
2620 value_decoded = None
2621 if isinstance(value, self.__class__):
2622 value_raw = value._value
2623 elif isinstance(value, text_type):
2624 value_decoded = value
2625 elif isinstance(value, binary_type):
2628 raise InvalidValueType((self.__class__, text_type, binary_type))
2630 value_decoded.encode(self.encoding)
2631 if value_raw is None else value_raw
2634 value_raw.decode(self.encoding)
2635 if value_decoded is None else value_decoded
2637 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2645 def __eq__(self, their):
2646 if isinstance(their, binary_type):
2647 return self._value == their
2648 if isinstance(their, text_type):
2649 return self._value == their.encode(self.encoding)
2650 if not isinstance(their, self.__class__):
2653 self._value == their._value and
2654 self.tag == their.tag and
2655 self._expl == their._expl
2658 def __unicode__(self):
2660 return self._value.decode(self.encoding)
2661 return text_type(self._value)
2664 return pp_console_row(next(self.pps(no_unicode=PY2)))
2666 def pps(self, decode_path=(), no_unicode=False):
2669 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2671 asn1_type_name=self.asn1_type_name,
2672 obj_name=self.__class__.__name__,
2673 decode_path=decode_path,
2675 optional=self.optional,
2676 default=self == self.default,
2677 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2678 expl=None if self._expl is None else tag_decode(self._expl),
2686 class UTF8String(CommonString):
2688 tag_default = tag_encode(12)
2690 asn1_type_name = "UTF8String"
2693 class NumericString(CommonString):
2695 tag_default = tag_encode(18)
2697 asn1_type_name = "NumericString"
2700 class PrintableString(CommonString):
2702 tag_default = tag_encode(19)
2704 asn1_type_name = "PrintableString"
2707 class TeletexString(CommonString):
2709 tag_default = tag_encode(20)
2711 asn1_type_name = "TeletexString"
2714 class T61String(TeletexString):
2716 asn1_type_name = "T61String"
2719 class VideotexString(CommonString):
2721 tag_default = tag_encode(21)
2722 encoding = "iso-8859-1"
2723 asn1_type_name = "VideotexString"
2726 class IA5String(CommonString):
2728 tag_default = tag_encode(22)
2730 asn1_type_name = "IA5"
2733 class UTCTime(CommonString):
2734 """``UTCTime`` datetime type
2736 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2737 UTCTime UTCTime 2017-09-30T22:07:50
2743 datetime.datetime(2017, 9, 30, 22, 7, 50)
2744 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2745 datetime.datetime(1957, 9, 30, 22, 7, 50)
2748 tag_default = tag_encode(23)
2750 asn1_type_name = "UTCTime"
2752 fmt = "%y%m%d%H%M%SZ"
2762 bounds=None, # dummy argument, workability for OctetString.decode
2765 :param value: set the value. Either datetime type, or
2766 :py:class:`pyderasn.UTCTime` object
2767 :param bytes impl: override default tag with ``IMPLICIT`` one
2768 :param bytes expl: override default tag with ``EXPLICIT`` one
2769 :param default: set default value. Type same as in ``value``
2770 :param bool optional: is object ``OPTIONAL`` in sequence
2772 super(UTCTime, self).__init__(
2780 if value is not None:
2781 self._value = self._value_sanitize(value)
2782 if default is not None:
2783 default = self._value_sanitize(default)
2784 self.default = self.__class__(
2789 if self._value is None:
2790 self._value = default
2792 def _value_sanitize(self, value):
2793 if isinstance(value, self.__class__):
2795 if isinstance(value, datetime):
2796 return value.strftime(self.fmt).encode("ascii")
2797 if isinstance(value, binary_type):
2798 value_decoded = value.decode("ascii")
2799 if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2801 datetime.strptime(value_decoded, self.fmt)
2803 raise DecodeError("invalid UTCTime format")
2806 raise DecodeError("invalid UTCTime length")
2807 raise InvalidValueType((self.__class__, datetime))
2809 def __eq__(self, their):
2810 if isinstance(their, binary_type):
2811 return self._value == their
2812 if isinstance(their, datetime):
2813 return self.todatetime() == their
2814 if not isinstance(their, self.__class__):
2817 self._value == their._value and
2818 self.tag == their.tag and
2819 self._expl == their._expl
2822 def todatetime(self):
2823 """Convert to datetime
2827 Pay attention that UTCTime can not hold full year, so all years
2828 having < 50 years are treated as 20xx, 19xx otherwise, according
2829 to X.509 recomendation.
2831 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2832 year = value.year % 100
2834 year=(2000 + year) if year < 50 else (1900 + year),
2838 minute=value.minute,
2839 second=value.second,
2843 return pp_console_row(next(self.pps()))
2845 def pps(self, decode_path=()):
2847 asn1_type_name=self.asn1_type_name,
2848 obj_name=self.__class__.__name__,
2849 decode_path=decode_path,
2850 value=self.todatetime().isoformat() if self.ready else None,
2851 optional=self.optional,
2852 default=self == self.default,
2853 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2854 expl=None if self._expl is None else tag_decode(self._expl),
2862 class GeneralizedTime(UTCTime):
2863 """``GeneralizedTime`` datetime type
2865 This type is similar to :py:class:`pyderasn.UTCTime`.
2867 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2868 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2870 '20170930220750.000123Z'
2871 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2872 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2875 tag_default = tag_encode(24)
2876 asn1_type_name = "GeneralizedTime"
2878 fmt = "%Y%m%d%H%M%SZ"
2879 fmt_ms = "%Y%m%d%H%M%S.%fZ"
2881 def _value_sanitize(self, value):
2882 if isinstance(value, self.__class__):
2884 if isinstance(value, datetime):
2885 return value.strftime(
2886 self.fmt_ms if value.microsecond > 0 else self.fmt
2888 if isinstance(value, binary_type):
2889 value_decoded = value.decode("ascii")
2890 if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2892 datetime.strptime(value_decoded, self.fmt)
2895 "invalid GeneralizedTime (without ms) format",
2898 elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2900 datetime.strptime(value_decoded, self.fmt_ms)
2903 "invalid GeneralizedTime (with ms) format",
2908 "invalid GeneralizedTime length",
2909 klass=self.__class__,
2911 raise InvalidValueType((self.__class__, datetime))
2913 def todatetime(self):
2914 value = self._value.decode("ascii")
2915 if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2916 return datetime.strptime(value, self.fmt)
2917 return datetime.strptime(value, self.fmt_ms)
2920 class GraphicString(CommonString):
2922 tag_default = tag_encode(25)
2923 encoding = "iso-8859-1"
2924 asn1_type_name = "GraphicString"
2927 class VisibleString(CommonString):
2929 tag_default = tag_encode(26)
2931 asn1_type_name = "VisibleString"
2934 class ISO646String(VisibleString):
2936 asn1_type_name = "ISO646String"
2939 class GeneralString(CommonString):
2941 tag_default = tag_encode(27)
2942 encoding = "iso-8859-1"
2943 asn1_type_name = "GeneralString"
2946 class UniversalString(CommonString):
2948 tag_default = tag_encode(28)
2949 encoding = "utf-32-be"
2950 asn1_type_name = "UniversalString"
2953 class BMPString(CommonString):
2955 tag_default = tag_encode(30)
2956 encoding = "utf-16-be"
2957 asn1_type_name = "BMPString"
2961 """``CHOICE`` special type
2965 class GeneralName(Choice):
2967 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2968 ('dNSName', IA5String(impl=tag_ctxp(2))),
2971 >>> gn = GeneralName()
2973 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2974 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2975 >>> gn["dNSName"] = IA5String("bar.baz")
2976 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2977 >>> gn["rfc822Name"]
2980 [2] IA5String IA5 bar.baz
2983 >>> gn.value == gn["dNSName"]
2986 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2988 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2989 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2991 __slots__ = ("specs",)
2993 asn1_type_name = "CHOICE"
3006 :param value: set the value. Either ``(choice, value)`` tuple, or
3007 :py:class:`pyderasn.Choice` object
3008 :param bytes impl: can not be set, do **not** use it
3009 :param bytes expl: override default tag with ``EXPLICIT`` one
3010 :param default: set default value. Type same as in ``value``
3011 :param bool optional: is object ``OPTIONAL`` in sequence
3013 if impl is not None:
3014 raise ValueError("no implicit tag allowed for CHOICE")
3015 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3017 schema = getattr(self, "schema", ())
3018 if len(schema) == 0:
3019 raise ValueError("schema must be specified")
3021 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3024 if value is not None:
3025 self._value = self._value_sanitize(value)
3026 if default is not None:
3027 default_value = self._value_sanitize(default)
3028 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3029 default_obj.specs = self.specs
3030 default_obj._value = default_value
3031 self.default = default_obj
3033 self._value = default_obj.copy()._value
3035 def _value_sanitize(self, value):
3036 if isinstance(value, self.__class__):
3038 if isinstance(value, tuple) and len(value) == 2:
3040 spec = self.specs.get(choice)
3042 raise ObjUnknown(choice)
3043 if not isinstance(obj, spec.__class__):
3044 raise InvalidValueType((spec,))
3045 return (choice, spec(obj))
3046 raise InvalidValueType((self.__class__, tuple))
3050 return self._value is not None and self._value[1].ready
3053 obj = self.__class__(schema=self.specs)
3054 obj._expl = self._expl
3055 obj.default = self.default
3056 obj.optional = self.optional
3057 obj.offset = self.offset
3058 obj.llen = self.llen
3059 obj.vlen = self.vlen
3061 if value is not None:
3062 obj._value = (value[0], value[1].copy())
3065 def __eq__(self, their):
3066 if isinstance(their, tuple) and len(their) == 2:
3067 return self._value == their
3068 if not isinstance(their, self.__class__):
3071 self.specs == their.specs and
3072 self._value == their._value
3082 return self.__class__(
3085 expl=self._expl if expl is None else expl,
3086 default=self.default if default is None else default,
3087 optional=self.optional if optional is None else optional,
3092 self._assert_ready()
3093 return self._value[0]
3097 self._assert_ready()
3098 return self._value[1]
3100 def __getitem__(self, key):
3101 if key not in self.specs:
3102 raise ObjUnknown(key)
3103 if self._value is None:
3105 choice, value = self._value
3110 def __setitem__(self, key, value):
3111 spec = self.specs.get(key)
3113 raise ObjUnknown(key)
3114 if not isinstance(value, spec.__class__):
3115 raise InvalidValueType((spec.__class__,))
3116 self._value = (key, spec(value))
3124 return self._value[1].decoded if self.ready else False
3127 self._assert_ready()
3128 return self._value[1].encode()
3130 def _decode(self, tlv, offset=0, decode_path=()):
3131 for choice, spec in self.specs.items():
3133 value, tail = spec.decode(
3137 decode_path=decode_path + (choice,),
3141 obj = self.__class__(
3144 default=self.default,
3145 optional=self.optional,
3146 _decoded=(offset, 0, value.tlvlen),
3148 obj._value = (choice, value)
3151 klass=self.__class__,
3152 decode_path=decode_path,
3157 value = pp_console_row(next(self.pps()))
3159 value = "%s[%r]" % (value, self.value)
3162 def pps(self, decode_path=()):
3164 asn1_type_name=self.asn1_type_name,
3165 obj_name=self.__class__.__name__,
3166 decode_path=decode_path,
3167 value=self.choice if self.ready else None,
3168 optional=self.optional,
3169 default=self == self.default,
3170 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3171 expl=None if self._expl is None else tag_decode(self._expl),
3178 yield self.value.pps(decode_path=decode_path + (self.choice,))
3181 class PrimitiveTypes(Choice):
3182 """Predefined ``CHOICE`` for all generic primitive types
3184 It could be useful for general decoding of some unspecified values:
3186 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3187 OCTET STRING 3 bytes 666f6f
3188 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3192 schema = tuple((klass.__name__, klass()) for klass in (
3217 """``ANY`` special type
3219 >>> Any(Integer(-123))
3221 >>> a = Any(OctetString(b"hello world").encode())
3222 ANY 040b68656c6c6f20776f726c64
3223 >>> hexenc(bytes(a))
3224 b'0x040x0bhello world'
3227 tag_default = tag_encode(0)
3228 asn1_type_name = "ANY"
3238 :param value: set the value. Either any kind of pyderasn's
3239 **ready** object, or bytes. Pay attention that
3240 **no** validation is performed is raw binary value
3242 :param bytes expl: override default tag with ``EXPLICIT`` one
3243 :param bool optional: is object ``OPTIONAL`` in sequence
3245 super(Any, self).__init__(None, expl, None, optional, _decoded)
3246 self._value = None if value is None else self._value_sanitize(value)
3248 def _value_sanitize(self, value):
3249 if isinstance(value, self.__class__):
3251 if isinstance(value, Obj):
3252 return value.encode()
3253 if isinstance(value, binary_type):
3255 raise InvalidValueType((self.__class__, Obj, binary_type))
3259 return self._value is not None
3262 obj = self.__class__()
3263 obj._value = self._value
3265 obj._expl = self._expl
3266 obj.optional = self.optional
3267 obj.offset = self.offset
3268 obj.llen = self.llen
3269 obj.vlen = self.vlen
3272 def __eq__(self, their):
3273 if isinstance(their, binary_type):
3274 return self._value == their
3275 if issubclass(their.__class__, Any):
3276 return self._value == their._value
3285 return self.__class__(
3287 expl=self._expl if expl is None else expl,
3288 optional=self.optional if optional is None else optional,
3291 def __bytes__(self):
3292 self._assert_ready()
3300 self._assert_ready()
3303 def _decode(self, tlv, offset=0, decode_path=()):
3305 t, tlen, lv = tag_strip(tlv)
3306 l, llen, v = len_decode(lv)
3307 except DecodeError as err:
3308 raise err.__class__(
3310 klass=self.__class__,
3311 decode_path=decode_path,
3315 raise NotEnoughData(
3316 "encoded length is longer than data",
3317 klass=self.__class__,
3318 decode_path=decode_path,
3321 tlvlen = tlen + llen + l
3322 v, tail = tlv[:tlvlen], v[l:]
3323 obj = self.__class__(
3326 optional=self.optional,
3327 _decoded=(offset, 0, tlvlen),
3333 return pp_console_row(next(self.pps()))
3335 def pps(self, decode_path=()):
3337 asn1_type_name=self.asn1_type_name,
3338 obj_name=self.__class__.__name__,
3339 decode_path=decode_path,
3340 blob=self._value if self.ready else None,
3341 optional=self.optional,
3342 default=self == self.default,
3343 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3344 expl=None if self._expl is None else tag_decode(self._expl),
3349 expl_offset=self.expl_offset if self.expled else None,
3350 expl_tlen=self.expl_tlen if self.expled else None,
3351 expl_llen=self.expl_llen if self.expled else None,
3352 expl_vlen=self.expl_vlen if self.expled else None,
3356 ########################################################################
3357 # ASN.1 constructed types
3358 ########################################################################
3360 class Sequence(Obj):
3361 """``SEQUENCE`` structure type
3363 You have to make specification of sequence::
3365 class Extension(Sequence):
3368 ("extnID", ObjectIdentifier()),
3369 ("critical", Boolean(default=False)),
3370 ("extnValue", OctetString()),
3373 Then, you can work with it as with dictionary.
3375 >>> ext = Extension()
3376 >>> Extension().specs
3378 ('extnID', OBJECT IDENTIFIER),
3379 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3380 ('extnValue', OCTET STRING),
3382 >>> ext["extnID"] = "1.2.3"
3383 Traceback (most recent call last):
3384 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3385 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3387 You can know if sequence is ready to be encoded:
3392 Traceback (most recent call last):
3393 pyderasn.ObjNotReady: object is not ready: extnValue
3394 >>> ext["extnValue"] = OctetString(b"foobar")
3398 Value you want to assign, must have the same **type** as in
3399 corresponding specification, but it can have different tags,
3400 optional/default attributes -- they will be taken from specification
3403 class TBSCertificate(Sequence):
3405 ("version", Version(expl=tag_ctxc(0), default="v1")),
3408 >>> tbs = TBSCertificate()
3409 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3411 You can know if value exists/set in the sequence and take its value:
3413 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3416 OBJECT IDENTIFIER 1.2.3
3418 But pay attention that if value has default, then it won't be (not
3419 in) in the sequence (because ``DEFAULT`` must not be encoded in
3420 DER), but you can read its value:
3422 >>> "critical" in ext, ext["critical"]
3423 (False, BOOLEAN False)
3424 >>> ext["critical"] = Boolean(True)
3425 >>> "critical" in ext, ext["critical"]
3426 (True, BOOLEAN True)
3428 All defaulted values are always optional.
3432 When decoded DER contains defaulted value inside, then
3433 technically this is not valid DER encoding. But we allow
3434 and pass it. Of course reencoding of that kind of DER will
3435 result in different binary representation (validly without
3436 defaulted value inside).
3438 Two sequences are equal if they have equal specification (schema),
3439 implicit/explicit tagging and the same values.
3441 __slots__ = ("specs",)
3442 tag_default = tag_encode(form=TagFormConstructed, num=16)
3443 asn1_type_name = "SEQUENCE"
3455 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3457 schema = getattr(self, "schema", ())
3459 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3462 if value is not None:
3463 self._value = self._value_sanitize(value)
3464 if default is not None:
3465 default_value = self._value_sanitize(default)
3466 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3467 default_obj.specs = self.specs
3468 default_obj._value = default_value
3469 self.default = default_obj
3471 self._value = default_obj.copy()._value
3473 def _value_sanitize(self, value):
3474 if not issubclass(value.__class__, Sequence):
3475 raise InvalidValueType((Sequence,))
3480 for name, spec in self.specs.items():
3481 value = self._value.get(name)
3492 obj = self.__class__(schema=self.specs)
3494 obj._expl = self._expl
3495 obj.default = self.default
3496 obj.optional = self.optional
3497 obj.offset = self.offset
3498 obj.llen = self.llen
3499 obj.vlen = self.vlen
3500 obj._value = {k: v.copy() for k, v in self._value.items()}
3503 def __eq__(self, their):
3504 if not isinstance(their, self.__class__):
3507 self.specs == their.specs and
3508 self.tag == their.tag and
3509 self._expl == their._expl and
3510 self._value == their._value
3521 return self.__class__(
3524 impl=self.tag if impl is None else impl,
3525 expl=self._expl if expl is None else expl,
3526 default=self.default if default is None else default,
3527 optional=self.optional if optional is None else optional,
3530 def __contains__(self, key):
3531 return key in self._value
3533 def __setitem__(self, key, value):
3534 spec = self.specs.get(key)
3536 raise ObjUnknown(key)
3538 self._value.pop(key, None)
3540 if not isinstance(value, spec.__class__):
3541 raise InvalidValueType((spec.__class__,))
3542 value = spec(value=value)
3543 if spec.default is not None and value == spec.default:
3544 self._value.pop(key, None)
3546 self._value[key] = value
3548 def __getitem__(self, key):
3549 value = self._value.get(key)
3550 if value is not None:
3552 spec = self.specs.get(key)
3554 raise ObjUnknown(key)
3555 if spec.default is not None:
3559 def _encoded_values(self):
3561 for name, spec in self.specs.items():
3562 value = self._value.get(name)
3566 raise ObjNotReady(name)
3567 raws.append(value.encode())
3571 v = b"".join(self._encoded_values())
3572 return b"".join((self.tag, len_encode(len(v)), v))
3574 def _decode(self, tlv, offset=0, decode_path=()):
3576 t, tlen, lv = tag_strip(tlv)
3577 except DecodeError as err:
3578 raise err.__class__(
3580 klass=self.__class__,
3581 decode_path=decode_path,
3586 klass=self.__class__,
3587 decode_path=decode_path,
3591 l, llen, v = len_decode(lv)
3592 except DecodeError as err:
3593 raise err.__class__(
3595 klass=self.__class__,
3596 decode_path=decode_path,
3600 raise NotEnoughData(
3601 "encoded length is longer than data",
3602 klass=self.__class__,
3603 decode_path=decode_path,
3606 v, tail = v[:l], v[l:]
3607 sub_offset = offset + tlen + llen
3609 for name, spec in self.specs.items():
3610 if len(v) == 0 and spec.optional:
3613 value, v_tail = spec.decode(
3617 decode_path=decode_path + (name,),
3623 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3625 if spec.default is not None and value == spec.default:
3626 # Encoded default values are not valid in DER,
3627 # but we still allow that
3629 values[name] = value
3633 klass=self.__class__,
3634 decode_path=decode_path,
3637 obj = self.__class__(
3641 default=self.default,
3642 optional=self.optional,
3643 _decoded=(offset, llen, l),
3649 value = pp_console_row(next(self.pps()))
3651 for name in self.specs:
3652 _value = self._value.get(name)
3655 cols.append(repr(_value))
3656 return "%s[%s]" % (value, ", ".join(cols))
3658 def pps(self, decode_path=()):
3660 asn1_type_name=self.asn1_type_name,
3661 obj_name=self.__class__.__name__,
3662 decode_path=decode_path,
3663 optional=self.optional,
3664 default=self == self.default,
3665 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3666 expl=None if self._expl is None else tag_decode(self._expl),
3671 expl_offset=self.expl_offset if self.expled else None,
3672 expl_tlen=self.expl_tlen if self.expled else None,
3673 expl_llen=self.expl_llen if self.expled else None,
3674 expl_vlen=self.expl_vlen if self.expled else None,
3676 for name in self.specs:
3677 value = self._value.get(name)
3680 yield value.pps(decode_path=decode_path + (name,))
3683 class Set(Sequence):
3684 """``SET`` structure type
3686 Its usage is identical to :py:class:`pyderasn.Sequence`.
3689 tag_default = tag_encode(form=TagFormConstructed, num=17)
3690 asn1_type_name = "SET"
3693 raws = self._encoded_values()
3696 return b"".join((self.tag, len_encode(len(v)), v))
3698 def _decode(self, tlv, offset=0, decode_path=()):
3700 t, tlen, lv = tag_strip(tlv)
3701 except DecodeError as err:
3702 raise err.__class__(
3704 klass=self.__class__,
3705 decode_path=decode_path,
3710 klass=self.__class__,
3711 decode_path=decode_path,
3715 l, llen, v = len_decode(lv)
3716 except DecodeError as err:
3717 raise err.__class__(
3719 klass=self.__class__,
3720 decode_path=decode_path,
3724 raise NotEnoughData(
3725 "encoded length is longer than data",
3726 klass=self.__class__,
3729 v, tail = v[:l], v[l:]
3730 sub_offset = offset + tlen + llen
3732 specs_items = self.specs.items
3734 for name, spec in specs_items():
3736 value, v_tail = spec.decode(
3740 decode_path=decode_path + (name,),
3745 value.expl_tlvlen if value.expled else value.tlvlen
3748 if spec.default is None or value != spec.default: # pragma: no cover
3749 # SeqMixing.test_encoded_default_accepted covers that place
3750 values[name] = value
3754 klass=self.__class__,
3755 decode_path=decode_path,
3758 obj = self.__class__(
3762 default=self.default,
3763 optional=self.optional,
3764 _decoded=(offset, llen, l),
3770 class SequenceOf(Obj):
3771 """``SEQUENCE OF`` sequence type
3773 For that kind of type you must specify the object it will carry on
3774 (bounds are for example here, not required)::
3776 class Ints(SequenceOf):
3781 >>> ints.append(Integer(123))
3782 >>> ints.append(Integer(234))
3784 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3785 >>> [int(i) for i in ints]
3787 >>> ints.append(Integer(345))
3788 Traceback (most recent call last):
3789 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3792 >>> ints[1] = Integer(345)
3794 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3796 Also you can initialize sequence with preinitialized values:
3798 >>> ints = Ints([Integer(123), Integer(234)])
3800 __slots__ = ("spec", "_bound_min", "_bound_max")
3801 tag_default = tag_encode(form=TagFormConstructed, num=16)
3802 asn1_type_name = "SEQUENCE OF"
3815 super(SequenceOf, self).__init__(
3823 schema = getattr(self, "schema", None)
3825 raise ValueError("schema must be specified")
3828 self._bound_min, self._bound_max = getattr(
3834 self._bound_min, self._bound_max = bounds
3836 if value is not None:
3837 self._value = self._value_sanitize(value)
3838 if default is not None:
3839 default_value = self._value_sanitize(default)
3840 default_obj = self.__class__(
3845 default_obj._value = default_value
3846 self.default = default_obj
3848 self._value = default_obj.copy()._value
3850 def _value_sanitize(self, value):
3851 if issubclass(value.__class__, SequenceOf):
3852 value = value._value
3853 elif hasattr(value, "__iter__"):
3856 raise InvalidValueType((self.__class__, iter))
3857 if not self._bound_min <= len(value) <= self._bound_max:
3858 raise BoundsError(self._bound_min, len(value), self._bound_max)
3860 if not isinstance(v, self.spec.__class__):
3861 raise InvalidValueType((self.spec.__class__,))
3866 return all(v.ready for v in self._value)
3869 obj = self.__class__(schema=self.spec)
3870 obj._bound_min = self._bound_min
3871 obj._bound_max = self._bound_max
3873 obj._expl = self._expl
3874 obj.default = self.default
3875 obj.optional = self.optional
3876 obj.offset = self.offset
3877 obj.llen = self.llen
3878 obj.vlen = self.vlen
3879 obj._value = [v.copy() for v in self._value]
3882 def __eq__(self, their):
3883 if isinstance(their, self.__class__):
3885 self.spec == their.spec and
3886 self.tag == their.tag and
3887 self._expl == their._expl and
3888 self._value == their._value
3890 if hasattr(their, "__iter__"):
3891 return self._value == list(their)
3903 return self.__class__(
3907 (self._bound_min, self._bound_max)
3908 if bounds is None else bounds
3910 impl=self.tag if impl is None else impl,
3911 expl=self._expl if expl is None else expl,
3912 default=self.default if default is None else default,
3913 optional=self.optional if optional is None else optional,
3916 def __contains__(self, key):
3917 return key in self._value
3919 def append(self, value):
3920 if not isinstance(value, self.spec.__class__):
3921 raise InvalidValueType((self.spec.__class__,))
3922 if len(self._value) + 1 > self._bound_max:
3925 len(self._value) + 1,
3928 self._value.append(value)
3931 self._assert_ready()
3932 return iter(self._value)
3935 self._assert_ready()
3936 return len(self._value)
3938 def __setitem__(self, key, value):
3939 if not isinstance(value, self.spec.__class__):
3940 raise InvalidValueType((self.spec.__class__,))
3941 self._value[key] = self.spec(value=value)
3943 def __getitem__(self, key):
3944 return self._value[key]
3946 def _encoded_values(self):
3947 return [v.encode() for v in self._value]
3950 v = b"".join(self._encoded_values())
3951 return b"".join((self.tag, len_encode(len(v)), v))
3953 def _decode(self, tlv, offset=0, decode_path=()):
3955 t, tlen, lv = tag_strip(tlv)
3956 except DecodeError as err:
3957 raise err.__class__(
3959 klass=self.__class__,
3960 decode_path=decode_path,
3965 klass=self.__class__,
3966 decode_path=decode_path,
3970 l, llen, v = len_decode(lv)
3971 except DecodeError as err:
3972 raise err.__class__(
3974 klass=self.__class__,
3975 decode_path=decode_path,
3979 raise NotEnoughData(
3980 "encoded length is longer than data",
3981 klass=self.__class__,
3982 decode_path=decode_path,
3985 v, tail = v[:l], v[l:]
3986 sub_offset = offset + tlen + llen
3990 value, v_tail = spec.decode(
3994 decode_path=decode_path + (str(len(_value)),),
3996 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3998 _value.append(value)
3999 obj = self.__class__(
4002 bounds=(self._bound_min, self._bound_max),
4005 default=self.default,
4006 optional=self.optional,
4007 _decoded=(offset, llen, l),
4013 pp_console_row(next(self.pps())),
4014 ", ".join(repr(v) for v in self._value),
4017 def pps(self, decode_path=()):
4019 asn1_type_name=self.asn1_type_name,
4020 obj_name=self.__class__.__name__,
4021 decode_path=decode_path,
4022 optional=self.optional,
4023 default=self == self.default,
4024 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4025 expl=None if self._expl is None else tag_decode(self._expl),
4030 expl_offset=self.expl_offset if self.expled else None,
4031 expl_tlen=self.expl_tlen if self.expled else None,
4032 expl_llen=self.expl_llen if self.expled else None,
4033 expl_vlen=self.expl_vlen if self.expled else None,
4035 for i, value in enumerate(self._value):
4036 yield value.pps(decode_path=decode_path + (str(i),))
4039 class SetOf(SequenceOf):
4040 """``SET OF`` sequence type
4042 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4045 tag_default = tag_encode(form=TagFormConstructed, num=17)
4046 asn1_type_name = "SET OF"
4049 raws = self._encoded_values()
4052 return b"".join((self.tag, len_encode(len(v)), v))
4055 def obj_by_path(pypath): # pragma: no cover
4056 """Import object specified as string Python path
4058 Modules must be separated from classes/functions with ``:``.
4060 >>> obj_by_path("foo.bar:Baz")
4061 <class 'foo.bar.Baz'>
4062 >>> obj_by_path("foo.bar:Baz.boo")
4063 <classmethod 'foo.bar.Baz.boo'>
4065 mod, objs = pypath.rsplit(":", 1)
4066 from importlib import import_module
4067 obj = import_module(mod)
4068 for obj_name in objs.split("."):
4069 obj = getattr(obj, obj_name)
4073 def main(): # pragma: no cover
4075 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4076 parser.add_argument(
4078 help="Python path to dictionary with OIDs",
4080 parser.add_argument(
4082 help="Python path to schema definition to use",
4084 parser.add_argument(
4086 type=argparse.FileType("rb"),
4087 help="Path to DER file you want to decode",
4089 args = parser.parse_args()
4090 der = memoryview(args.DERFile.read())
4091 args.DERFile.close()
4092 oids = obj_by_path(args.oids) if args.oids else {}
4094 schema = obj_by_path(args.schema)
4095 from functools import partial
4096 pprinter = partial(pprint, big_blobs=True)
4098 # All of this below is a big hack with self references
4099 choice = PrimitiveTypes()
4100 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4101 choice.specs["SetOf"] = SetOf(schema=choice)
4103 choice.specs["SequenceOf%d" % i] = SequenceOf(
4107 choice.specs["Any"] = Any()
4109 # Class name equals to type name, to omit it from output
4110 class SEQUENCEOF(SequenceOf):
4113 schema = SEQUENCEOF()
4115 def pprint_any(obj, oids=None):
4116 def _pprint_pps(pps):
4118 if hasattr(pp, "_fields"):
4119 if pp.asn1_type_name == Choice.asn1_type_name:
4121 pp_kwargs = pp._asdict()
4122 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4123 pp = _pp(**pp_kwargs)
4124 yield pp_console_row(
4130 for row in pp_console_blob(pp):
4133 for row in _pprint_pps(pp):
4135 return "\n".join(_pprint_pps(obj.pps()))
4136 pprinter = pprint_any
4137 obj, tail = schema().decode(der)
4138 print(pprinter(obj, oids=oids))
4140 print("\nTrailing data: %s" % hexenc(tail))
4143 if __name__ == "__main__":