3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER codec with abstract structures
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments takes
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two object of the same type, but with different implicit/explicit tags
94 You can get objects effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
141 Some objects give ability to set value size constraints. This is either
142 possible integer value, or allowed length of various strings and
143 sequences. Constraints are set in the following way::
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
150 For simplicity you can also set bounds the following way::
152 bounded_x = X(bounds=(MIN, MAX))
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
160 All objects have ``ready`` boolean property, that tells if it is ready
161 to be encoded. If that kind of action is performed on unready object,
162 then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
164 All objects have ``copy()`` method, returning its copy, that can be safely
170 Decoding is performed using ``decode()`` method. ``offset`` optional
171 argument could be used to set initial object's offset in the binary
172 data, for convenience. It returns decoded object and remaining
173 unmarshalled data (tail). Internally all work is done on
174 ``memoryview(data)``, and you can leave returning tail as a memoryview,
175 by specifying ``leavemm=True`` argument.
177 When object is decoded, ``decoded`` property is true and you can safely
178 use following properties:
180 * ``offset`` -- position from initial offset where object's tag is started
181 * ``tlen`` -- length of object's tag
182 * ``llen`` -- length of object's length value
183 * ``vlen`` -- length of object's value
184 * ``tlvlen`` -- length of the whole object
186 Pay attention that those values do **not** include anything related to
187 explicit tag. If you want to know information about it, then use:
188 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
189 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
190 (that actually equals to ordinary ``tlvlen``).
192 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
197 All objects have ``pps()`` method, that is a generator of
198 :py:class:`pyderasn.PP` namedtuple, holding various raw information
199 about the object. If ``pps`` is called on sequences, then all underlying
200 ``PP`` will be yielded.
202 You can use :py:func:`pyderasn.pp_console_row` function, converting
203 those ``PP`` to human readable string. Actually exactly it is used for
204 all object ``repr``. But it is easy to write custom formatters.
206 >>> from pyderasn import pprint
207 >>> encoded = Integer(-12345).encode()
208 >>> obj, tail = Integer().decode(encoded)
209 >>> print(pprint(obj))
210 0 [1,1, 2] INTEGER -12345
217 .. autoclass:: pyderasn.Boolean
222 .. autoclass:: pyderasn.Integer
227 .. autoclass:: pyderasn.BitString
232 .. autoclass:: pyderasn.OctetString
237 .. autoclass:: pyderasn.Null
242 .. autoclass:: pyderasn.ObjectIdentifier
247 .. autoclass:: pyderasn.Enumerated
251 .. autoclass:: pyderasn.CommonString
255 .. autoclass:: pyderasn.UTCTime
256 :members: __init__, todatetime
260 .. autoclass:: pyderasn.GeneralizedTime
267 .. autoclass:: pyderasn.Choice
272 .. autoclass:: PrimitiveTypes
276 .. autoclass:: pyderasn.Any
284 .. autoclass:: pyderasn.Sequence
289 .. autoclass:: pyderasn.Set
294 .. autoclass:: pyderasn.SequenceOf
299 .. autoclass:: pyderasn.SetOf
305 .. autofunction:: pyderasn.hexenc
306 .. autofunction:: pyderasn.hexdec
307 .. autofunction:: pyderasn.tag_encode
308 .. autofunction:: pyderasn.tag_decode
309 .. autofunction:: pyderasn.tag_ctxp
310 .. autofunction:: pyderasn.tag_ctxc
311 .. autoclass:: pyderasn.Obj
314 from codecs import getdecoder
315 from codecs import getencoder
316 from collections import namedtuple
317 from collections import OrderedDict
318 from datetime import datetime
319 from math import ceil
321 from six import add_metaclass
322 from six import binary_type
323 from six import byte2int
324 from six import indexbytes
325 from six import int2byte
326 from six import integer_types
327 from six import iterbytes
329 from six import string_types
330 from six import text_type
331 from six.moves import xrange as six_xrange
372 "TagClassApplication",
376 "TagFormConstructed",
387 TagClassUniversal = 0
388 TagClassApplication = 1 << 6
389 TagClassContext = 1 << 7
390 TagClassPrivate = 1 << 6 | 1 << 7
392 TagFormConstructed = 1 << 5
395 TagClassApplication: "APPLICATION ",
396 TagClassPrivate: "PRIVATE ",
397 TagClassUniversal: "UNIV ",
401 ########################################################################
403 ########################################################################
405 class DecodeError(Exception):
406 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
408 :param str msg: reason of decode failing
409 :param klass: optional exact DecodeError inherited class (like
410 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
411 :py:exc:`InvalidLength`)
412 :param decode_path: tuple of strings. It contains human
413 readable names of the fields through which
414 decoding process has passed
415 :param int offset: binary offset where failure happened
417 super(DecodeError, self).__init__()
420 self.decode_path = decode_path
426 "" if self.klass is None else self.klass.__name__,
428 ("(%s)" % ".".join(self.decode_path))
429 if len(self.decode_path) > 0 else ""
431 ("(at %d)" % self.offset) if self.offset > 0 else "",
437 return "%s(%s)" % (self.__class__.__name__, self)
440 class NotEnoughData(DecodeError):
444 class TagMismatch(DecodeError):
448 class InvalidLength(DecodeError):
452 class InvalidOID(DecodeError):
456 class ObjUnknown(ValueError):
457 def __init__(self, name):
458 super(ObjUnknown, self).__init__()
462 return "object is unknown: %s" % self.name
465 return "%s(%s)" % (self.__class__.__name__, self)
468 class ObjNotReady(ValueError):
469 def __init__(self, name):
470 super(ObjNotReady, self).__init__()
474 return "object is not ready: %s" % self.name
477 return "%s(%s)" % (self.__class__.__name__, self)
480 class InvalidValueType(ValueError):
481 def __init__(self, expected_types):
482 super(InvalidValueType, self).__init__()
483 self.expected_types = expected_types
486 return "invalid value type, expected: %s" % ", ".join(
487 [repr(t) for t in self.expected_types]
491 return "%s(%s)" % (self.__class__.__name__, self)
494 class BoundsError(ValueError):
495 def __init__(self, bound_min, value, bound_max):
496 super(BoundsError, self).__init__()
497 self.bound_min = bound_min
499 self.bound_max = bound_max
502 return "unsatisfied bounds: %s <= %s <= %s" % (
509 return "%s(%s)" % (self.__class__.__name__, self)
512 ########################################################################
514 ########################################################################
516 _hexdecoder = getdecoder("hex")
517 _hexencoder = getencoder("hex")
521 """Binary data to hexadecimal string convert
523 return _hexdecoder(data)[0]
527 """Hexadecimal string to binary data convert
529 return _hexencoder(data)[0].decode("ascii")
532 def int_bytes_len(num, byte_len=8):
535 return int(ceil(float(num.bit_length()) / byte_len))
538 def zero_ended_encode(num):
539 octets = bytearray(int_bytes_len(num, 7))
541 octets[i] = num & 0x7F
545 octets[i] = 0x80 | (num & 0x7F)
551 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
552 """Encode tag to binary form
554 :param int num: tag's number
555 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
556 :py:data:`pyderasn.TagClassContext`,
557 :py:data:`pyderasn.TagClassApplication`,
558 :py:data:`pyderasn.TagClassPrivate`)
559 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
560 :py:data:`pyderasn.TagFormConstructed`)
564 return int2byte(klass | form | num)
565 # [XX|X|11111][1.......][1.......] ... [0.......]
566 return int2byte(klass | form | 31) + zero_ended_encode(num)
570 """Decode tag from binary form
574 No validation is performed, assuming that it has already passed.
576 It returns tuple with three integers, as
577 :py:func:`pyderasn.tag_encode` accepts.
579 first_octet = byte2int(tag)
580 klass = first_octet & 0xC0
581 form = first_octet & 0x20
582 if first_octet & 0x1F < 0x1F:
583 return (klass, form, first_octet & 0x1F)
585 for octet in iterbytes(tag[1:]):
588 return (klass, form, num)
592 """Create CONTEXT PRIMITIVE tag
594 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
598 """Create CONTEXT CONSTRUCTED tag
600 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
604 """Take off tag from the data
606 :returns: (encoded tag, tag length, remaining data)
609 raise NotEnoughData("no data at all")
610 if byte2int(data) & 0x1F < 31:
611 return data[:1], 1, data[1:]
616 raise DecodeError("unfinished tag")
617 if indexbytes(data, i) & 0x80 == 0:
620 return data[:i], i, data[i:]
626 octets = bytearray(int_bytes_len(l) + 1)
627 octets[0] = 0x80 | (len(octets) - 1)
628 for i in six_xrange(len(octets) - 1, 0, -1):
634 def len_decode(data):
636 raise NotEnoughData("no data at all")
637 first_octet = byte2int(data)
638 if first_octet & 0x80 == 0:
639 return first_octet, 1, data[1:]
640 octets_num = first_octet & 0x7F
641 if octets_num + 1 > len(data):
642 raise NotEnoughData("encoded length is longer than data")
644 raise DecodeError("long form instead of short one")
645 if byte2int(data[1:]) == 0:
646 raise DecodeError("leading zeros")
648 for v in iterbytes(data[1:1 + octets_num]):
651 raise DecodeError("long form instead of short one")
652 return l, 1 + octets_num, data[1 + octets_num:]
655 ########################################################################
657 ########################################################################
659 class AutoAddSlots(type):
660 def __new__(cls, name, bases, _dict):
661 _dict["__slots__"] = _dict.get("__slots__", ())
662 return type.__new__(cls, name, bases, _dict)
665 @add_metaclass(AutoAddSlots)
667 """Common ASN.1 object class
669 All ASN.1 types are inherited from it. It has metaclass that
670 automatically adds ``__slots__`` to all inherited classes.
692 self.tag = getattr(self, "impl", self.tag_default)
695 self._expl = getattr(self, "expl", None) if expl is None else expl
696 if self.tag != self.tag_default and self._expl is not None:
698 "implicit and explicit tags can not be set simultaneously"
700 if default is not None:
702 self.optional = optional
703 self.offset, self.llen, self.vlen = _decoded
707 def ready(self): # pragma: no cover
708 """Is object ready to be encoded?
710 raise NotImplementedError()
712 def _assert_ready(self):
714 raise ObjNotReady(self.__class__.__name__)
718 """Is object decoded?
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 _encode(self): # pragma: no cover
739 raise NotImplementedError()
741 def _decode(self, tlv, offset=0, decode_path=()): # pragma: no cover
742 raise NotImplementedError()
746 if self._expl is None:
748 return b"".join((self._expl, len_encode(len(raw)), raw))
750 def decode(self, data, offset=0, leavemm=False, decode_path=()):
753 :param data: either binary or memoryview
754 :param int offset: initial data's offset
755 :param bool leavemm: do we need to leave memoryview of remaining
756 data as is, or convert it to bytes otherwise
757 :returns: (Obj, remaining data)
759 tlv = memoryview(data)
760 if self._expl is None:
761 obj, tail = self._decode(
764 decode_path=decode_path,
768 t, tlen, lv = tag_strip(tlv)
769 except DecodeError as err:
772 klass=self.__class__,
773 decode_path=decode_path,
778 klass=self.__class__,
779 decode_path=decode_path,
783 l, llen, v = len_decode(lv)
784 except DecodeError as err:
787 klass=self.__class__,
788 decode_path=decode_path,
793 "encoded length is longer than data",
794 klass=self.__class__,
795 decode_path=decode_path,
798 obj, tail = self._decode(
800 offset=offset + tlen + llen,
803 return obj, (tail if leavemm else tail.tobytes())
807 return self._expl is not None
815 return len(self._expl)
819 return len(len_encode(self.tlvlen))
822 def expl_offset(self):
823 return self.offset - self.expl_tlen - self.expl_llen
830 def expl_tlvlen(self):
831 return self.expl_tlen + self.expl_llen + self.expl_vlen
834 ########################################################################
836 ########################################################################
838 PP = namedtuple("PP", (
860 asn1_type_name="unknown",
899 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
902 cols.append("%5d%s [%d,%d,%4d]" % (
905 " " if pp.expl_offset is None else
906 ("-%d" % (pp.offset - pp.expl_offset))
912 if len(pp.decode_path) > 0:
913 cols.append(" ." * (len(pp.decode_path)))
914 cols.append("%s:" % pp.decode_path[-1])
915 if pp.expl is not None:
916 klass, _, num = pp.expl
917 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
918 if pp.impl is not None:
919 klass, _, num = pp.impl
920 cols.append("[%s%d]" % (TagClassReprs[klass], num))
921 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
922 cols.append(pp.obj_name)
923 cols.append(pp.asn1_type_name)
924 if pp.value is not None:
928 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
931 value = "%s (%s)" % (oids[value], pp.value)
934 if isinstance(pp.blob, binary_type):
935 cols.append(hexenc(pp.blob))
936 elif isinstance(pp.blob, tuple):
937 cols.append(", ".join(pp.blob))
939 cols.append("OPTIONAL")
941 cols.append("DEFAULT")
942 return " ".join(cols)
945 def pp_console_blob(pp):
946 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
947 if len(pp.decode_path) > 0:
948 cols.append(" ." * (len(pp.decode_path) + 1))
949 if isinstance(pp.blob, binary_type):
950 blob = hexenc(pp.blob).upper()
951 for i in range(0, len(blob), 32):
952 chunk = blob[i:i + 32]
953 yield " ".join(cols + [":".join(
954 chunk[j:j + 2] for j in range(0, len(chunk), 2)
956 elif isinstance(pp.blob, tuple):
957 yield " ".join(cols + [", ".join(pp.blob)])
960 def pprint(obj, oids=None, big_blobs=False):
961 """Pretty print object
963 :param Obj obj: object you want to pretty print
964 :param oids: ``OID <-> humand readable string`` dictionary. When OID
965 from it is met, then its humand readable form is printed
966 :param big_blobs: if large binary objects are met (like OctetString
967 values), do we need to print them too, on separate
970 def _pprint_pps(pps):
972 if hasattr(pp, "_fields"):
974 yield pp_console_row(
980 for row in pp_console_blob(pp):
983 yield pp_console_row(pp, oids=oids, with_offsets=True)
985 for row in _pprint_pps(pp):
987 return "\n".join(_pprint_pps(obj.pps()))
990 ########################################################################
991 # ASN.1 primitive types
992 ########################################################################
995 """``BOOLEAN`` boolean type
997 >>> b = Boolean(True)
999 >>> b == Boolean(True)
1005 tag_default = tag_encode(1)
1006 asn1_type_name = "BOOLEAN"
1018 :param value: set the value. Either boolean type, or
1019 :py:class:`pyderasn.Boolean` object
1020 :param bytes impl: override default tag with ``IMPLICIT`` one
1021 :param bytes expl: override default tag with ``EXPLICIT`` one
1022 :param default: set default value. Type same as in ``value``
1023 :param bool optional: is object ``OPTIONAL`` in sequence
1025 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1026 self._value = None if value is None else self._value_sanitize(value)
1027 if default is not None:
1028 default = self._value_sanitize(default)
1029 self.default = self.__class__(
1035 self._value = default
1037 def _value_sanitize(self, value):
1038 if issubclass(value.__class__, Boolean):
1040 if isinstance(value, bool):
1042 raise InvalidValueType((self.__class__, bool))
1046 return self._value is not None
1049 obj = self.__class__()
1050 obj._value = self._value
1052 obj._expl = self._expl
1053 obj.default = self.default
1054 obj.optional = self.optional
1055 obj.offset = self.offset
1056 obj.llen = self.llen
1057 obj.vlen = self.vlen
1060 def __nonzero__(self):
1061 self._assert_ready()
1065 self._assert_ready()
1068 def __eq__(self, their):
1069 if isinstance(their, bool):
1070 return self._value == their
1071 if not issubclass(their.__class__, Boolean):
1074 self._value == their._value and
1075 self.tag == their.tag and
1076 self._expl == their._expl
1087 return self.__class__(
1089 impl=self.tag if impl is None else impl,
1090 expl=self._expl if expl is None else expl,
1091 default=self.default if default is None else default,
1092 optional=self.optional if optional is None else optional,
1096 self._assert_ready()
1100 (b"\xFF" if self._value else b"\x00"),
1103 def _decode(self, tlv, offset=0, decode_path=()):
1105 t, _, lv = tag_strip(tlv)
1106 except DecodeError as err:
1107 raise err.__class__(
1109 klass=self.__class__,
1110 decode_path=decode_path,
1115 klass=self.__class__,
1116 decode_path=decode_path,
1120 l, _, v = len_decode(lv)
1121 except DecodeError as err:
1122 raise err.__class__(
1124 klass=self.__class__,
1125 decode_path=decode_path,
1129 raise InvalidLength(
1130 "Boolean's length must be equal to 1",
1131 klass=self.__class__,
1132 decode_path=decode_path,
1136 raise NotEnoughData(
1137 "encoded length is longer than data",
1138 klass=self.__class__,
1139 decode_path=decode_path,
1142 first_octet = byte2int(v)
1143 if first_octet == 0:
1145 elif first_octet == 0xFF:
1149 "unacceptable Boolean value",
1150 klass=self.__class__,
1151 decode_path=decode_path,
1154 obj = self.__class__(
1158 default=self.default,
1159 optional=self.optional,
1160 _decoded=(offset, 1, 1),
1165 return pp_console_row(next(self.pps()))
1167 def pps(self, decode_path=()):
1169 asn1_type_name=self.asn1_type_name,
1170 obj_name=self.__class__.__name__,
1171 decode_path=decode_path,
1172 value=str(self._value) if self.ready else None,
1173 optional=self.optional,
1174 default=self == self.default,
1175 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1176 expl=None if self._expl is None else tag_decode(self._expl),
1181 expl_offset=self.expl_offset if self.expled else None,
1182 expl_tlen=self.expl_tlen if self.expled else None,
1183 expl_llen=self.expl_llen if self.expled else None,
1184 expl_vlen=self.expl_vlen if self.expled else None,
1189 """``INTEGER`` integer type
1191 >>> b = Integer(-123)
1193 >>> b == Integer(-123)
1198 >>> Integer(2, bounds=(1, 3))
1200 >>> Integer(5, bounds=(1, 3))
1201 Traceback (most recent call last):
1202 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1206 class Version(Integer):
1213 >>> v = Version("v1")
1220 {'v3': 2, 'v1': 0, 'v2': 1}
1222 __slots__ = ("specs", "_bound_min", "_bound_max")
1223 tag_default = tag_encode(2)
1224 asn1_type_name = "INTEGER"
1238 :param value: set the value. Either integer type, named value
1239 (if ``schema`` is specified in the class), or
1240 :py:class:`pyderasn.Integer` object
1241 :param bounds: set ``(MIN, MAX)`` value constraint.
1242 (-inf, +inf) by default
1243 :param bytes impl: override default tag with ``IMPLICIT`` one
1244 :param bytes expl: override default tag with ``EXPLICIT`` one
1245 :param default: set default value. Type same as in ``value``
1246 :param bool optional: is object ``OPTIONAL`` in sequence
1248 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1250 specs = getattr(self, "schema", {}) if _specs is None else _specs
1251 self.specs = specs if isinstance(specs, dict) else dict(specs)
1253 self._bound_min, self._bound_max = getattr(
1256 (float("-inf"), float("+inf")),
1259 self._bound_min, self._bound_max = bounds
1260 if value is not None:
1261 self._value = self._value_sanitize(value)
1262 if default is not None:
1263 default = self._value_sanitize(default)
1264 self.default = self.__class__(
1270 if self._value is None:
1271 self._value = default
1273 def _value_sanitize(self, value):
1274 if issubclass(value.__class__, Integer):
1275 value = value._value
1276 elif isinstance(value, integer_types):
1278 elif isinstance(value, str):
1279 value = self.specs.get(value)
1281 raise ObjUnknown("integer value: %s" % value)
1283 raise InvalidValueType((self.__class__, int, str))
1284 if not self._bound_min <= value <= self._bound_max:
1285 raise BoundsError(self._bound_min, value, self._bound_max)
1290 return self._value is not None
1293 obj = self.__class__(_specs=self.specs)
1294 obj._value = self._value
1295 obj._bound_min = self._bound_min
1296 obj._bound_max = self._bound_max
1298 obj._expl = self._expl
1299 obj.default = self.default
1300 obj.optional = self.optional
1301 obj.offset = self.offset
1302 obj.llen = self.llen
1303 obj.vlen = self.vlen
1307 self._assert_ready()
1308 return int(self._value)
1311 self._assert_ready()
1314 bytes(self._expl or b"") +
1315 str(self._value).encode("ascii"),
1318 def __eq__(self, their):
1319 if isinstance(their, integer_types):
1320 return self._value == their
1321 if not issubclass(their.__class__, Integer):
1324 self._value == their._value and
1325 self.tag == their.tag and
1326 self._expl == their._expl
1329 def __lt__(self, their):
1330 return self._value < their
1332 def __gt__(self, their):
1333 return self._value > their
1337 for name, value in self.specs.items():
1338 if value == self._value:
1350 return self.__class__(
1353 (self._bound_min, self._bound_max)
1354 if bounds is None else bounds
1356 impl=self.tag if impl is None else impl,
1357 expl=self._expl if expl is None else expl,
1358 default=self.default if default is None else default,
1359 optional=self.optional if optional is None else optional,
1364 self._assert_ready()
1368 octets = bytearray([0])
1372 octets = bytearray()
1374 octets.append((value & 0xFF) ^ 0xFF)
1376 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1379 octets = bytearray()
1381 octets.append(value & 0xFF)
1383 if octets[-1] & 0x80 > 0:
1386 octets = bytes(octets)
1388 bytes_len = ceil(value.bit_length() / 8) or 1
1391 octets = value.to_bytes(
1396 except OverflowError:
1400 return b"".join((self.tag, len_encode(len(octets)), octets))
1402 def _decode(self, tlv, offset=0, decode_path=()):
1404 t, _, lv = tag_strip(tlv)
1405 except DecodeError as err:
1406 raise err.__class__(
1408 klass=self.__class__,
1409 decode_path=decode_path,
1414 klass=self.__class__,
1415 decode_path=decode_path,
1419 l, llen, v = len_decode(lv)
1420 except DecodeError as err:
1421 raise err.__class__(
1423 klass=self.__class__,
1424 decode_path=decode_path,
1428 raise NotEnoughData(
1429 "encoded length is longer than data",
1430 klass=self.__class__,
1431 decode_path=decode_path,
1435 raise NotEnoughData(
1437 klass=self.__class__,
1438 decode_path=decode_path,
1441 v, tail = v[:l], v[l:]
1442 first_octet = byte2int(v)
1444 second_octet = byte2int(v[1:])
1446 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1447 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1450 "non normalized integer",
1451 klass=self.__class__,
1452 decode_path=decode_path,
1457 if first_octet & 0x80 > 0:
1458 octets = bytearray()
1459 for octet in bytearray(v):
1460 octets.append(octet ^ 0xFF)
1461 for octet in octets:
1462 value = (value << 8) | octet
1466 for octet in bytearray(v):
1467 value = (value << 8) | octet
1469 value = int.from_bytes(v, byteorder="big", signed=True)
1471 obj = self.__class__(
1473 bounds=(self._bound_min, self._bound_max),
1476 default=self.default,
1477 optional=self.optional,
1479 _decoded=(offset, llen, l),
1481 except BoundsError as err:
1484 klass=self.__class__,
1485 decode_path=decode_path,
1491 return pp_console_row(next(self.pps()))
1493 def pps(self, decode_path=()):
1495 asn1_type_name=self.asn1_type_name,
1496 obj_name=self.__class__.__name__,
1497 decode_path=decode_path,
1498 value=(self.named or str(self._value)) if self.ready else None,
1499 optional=self.optional,
1500 default=self == self.default,
1501 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1502 expl=None if self._expl is None else tag_decode(self._expl),
1507 expl_offset=self.expl_offset if self.expled else None,
1508 expl_tlen=self.expl_tlen if self.expled else None,
1509 expl_llen=self.expl_llen if self.expled else None,
1510 expl_vlen=self.expl_vlen if self.expled else None,
1514 class BitString(Obj):
1515 """``BIT STRING`` bit string type
1517 >>> BitString(b"hello world")
1518 BIT STRING 88 bits 68656c6c6f20776f726c64
1521 >>> b == b"hello world"
1526 >>> b = BitString("'010110000000'B")
1527 BIT STRING 12 bits 5800
1530 >>> b[0], b[1], b[2], b[3]
1531 (False, True, False, True)
1535 [False, True, False, True, True, False, False, False, False, False, False, False]
1539 class KeyUsage(BitString):
1541 ('digitalSignature', 0),
1542 ('nonRepudiation', 1),
1543 ('keyEncipherment', 2),
1546 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1547 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1549 ['nonRepudiation', 'keyEncipherment']
1551 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1553 __slots__ = ("specs",)
1554 tag_default = tag_encode(3)
1555 asn1_type_name = "BIT STRING"
1568 :param value: set the value. Either binary type, tuple of named
1569 values (if ``schema`` is specified in the class),
1570 string in ``'XXX...'B`` form, or
1571 :py:class:`pyderasn.BitString` object
1572 :param bytes impl: override default tag with ``IMPLICIT`` one
1573 :param bytes expl: override default tag with ``EXPLICIT`` one
1574 :param default: set default value. Type same as in ``value``
1575 :param bool optional: is object ``OPTIONAL`` in sequence
1577 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1578 specs = getattr(self, "schema", {}) if _specs is None else _specs
1579 self.specs = specs if isinstance(specs, dict) else dict(specs)
1580 self._value = None if value is None else self._value_sanitize(value)
1581 if default is not None:
1582 default = self._value_sanitize(default)
1583 self.default = self.__class__(
1589 self._value = default
1591 def _bits2octets(self, bits):
1592 if len(self.specs) > 0:
1593 bits = bits.rstrip("0")
1595 bits += "0" * ((8 - (bit_len % 8)) % 8)
1596 octets = bytearray(len(bits) // 8)
1597 for i in six_xrange(len(octets)):
1598 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1599 return bit_len, bytes(octets)
1601 def _value_sanitize(self, value):
1602 if issubclass(value.__class__, BitString):
1604 if isinstance(value, (string_types, binary_type)):
1606 isinstance(value, string_types) and
1607 value.startswith("'") and
1608 value.endswith("'B")
1611 if not set(value) <= set(("0", "1")):
1612 raise ValueError("B's coding contains unacceptable chars")
1613 return self._bits2octets(value)
1614 elif isinstance(value, binary_type):
1615 return (len(value) * 8, value)
1617 raise InvalidValueType((
1622 if isinstance(value, tuple):
1625 isinstance(value[0], integer_types) and
1626 isinstance(value[1], binary_type)
1631 bit = self.specs.get(name)
1633 raise ObjUnknown("BitString value: %s" % name)
1636 return self._bits2octets("")
1638 return self._bits2octets("".join(
1639 ("1" if bit in bits else "0")
1640 for bit in six_xrange(max(bits) + 1)
1642 raise InvalidValueType((self.__class__, binary_type, string_types))
1646 return self._value is not None
1649 obj = self.__class__(_specs=self.specs)
1650 obj._value = self._value
1652 obj._expl = self._expl
1653 obj.default = self.default
1654 obj.optional = self.optional
1655 obj.offset = self.offset
1656 obj.llen = self.llen
1657 obj.vlen = self.vlen
1661 self._assert_ready()
1662 for i in six_xrange(self._value[0]):
1667 self._assert_ready()
1668 return self._value[0]
1670 def __bytes__(self):
1671 self._assert_ready()
1672 return self._value[1]
1674 def __eq__(self, their):
1675 if isinstance(their, bytes):
1676 return self._value[1] == their
1677 if not issubclass(their.__class__, BitString):
1680 self._value == their._value and
1681 self.tag == their.tag and
1682 self._expl == their._expl
1687 return [name for name, bit in self.specs.items() if self[bit]]
1697 return self.__class__(
1699 impl=self.tag if impl is None else impl,
1700 expl=self._expl if expl is None else expl,
1701 default=self.default if default is None else default,
1702 optional=self.optional if optional is None else optional,
1706 def __getitem__(self, key):
1707 if isinstance(key, int):
1708 bit_len, octets = self._value
1712 byte2int(memoryview(octets)[key // 8:]) >>
1715 if isinstance(key, string_types):
1716 value = self.specs.get(key)
1718 raise ObjUnknown("BitString value: %s" % key)
1720 raise InvalidValueType((int, str))
1723 self._assert_ready()
1724 bit_len, octets = self._value
1727 len_encode(len(octets) + 1),
1728 int2byte((8 - bit_len % 8) % 8),
1732 def _decode(self, tlv, offset=0, decode_path=()):
1734 t, _, lv = tag_strip(tlv)
1735 except DecodeError as err:
1736 raise err.__class__(
1738 klass=self.__class__,
1739 decode_path=decode_path,
1744 klass=self.__class__,
1745 decode_path=decode_path,
1749 l, llen, v = len_decode(lv)
1750 except DecodeError as err:
1751 raise err.__class__(
1753 klass=self.__class__,
1754 decode_path=decode_path,
1758 raise NotEnoughData(
1759 "encoded length is longer than data",
1760 klass=self.__class__,
1761 decode_path=decode_path,
1765 raise NotEnoughData(
1767 klass=self.__class__,
1768 decode_path=decode_path,
1771 pad_size = byte2int(v)
1772 if l == 1 and pad_size != 0:
1774 "invalid empty value",
1775 klass=self.__class__,
1776 decode_path=decode_path,
1782 klass=self.__class__,
1783 decode_path=decode_path,
1786 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1789 klass=self.__class__,
1790 decode_path=decode_path,
1793 v, tail = v[:l], v[l:]
1794 obj = self.__class__(
1795 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1798 default=self.default,
1799 optional=self.optional,
1801 _decoded=(offset, llen, l),
1806 return pp_console_row(next(self.pps()))
1808 def pps(self, decode_path=()):
1812 bit_len, blob = self._value
1813 value = "%d bits" % bit_len
1814 if len(self.specs) > 0:
1815 blob = tuple(self.named)
1817 asn1_type_name=self.asn1_type_name,
1818 obj_name=self.__class__.__name__,
1819 decode_path=decode_path,
1822 optional=self.optional,
1823 default=self == self.default,
1824 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1825 expl=None if self._expl is None else tag_decode(self._expl),
1830 expl_offset=self.expl_offset if self.expled else None,
1831 expl_tlen=self.expl_tlen if self.expled else None,
1832 expl_llen=self.expl_llen if self.expled else None,
1833 expl_vlen=self.expl_vlen if self.expled else None,
1837 class OctetString(Obj):
1838 """``OCTET STRING`` binary string type
1840 >>> s = OctetString(b"hello world")
1841 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1842 >>> s == OctetString(b"hello world")
1847 >>> OctetString(b"hello", bounds=(4, 4))
1848 Traceback (most recent call last):
1849 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1850 >>> OctetString(b"hell", bounds=(4, 4))
1851 OCTET STRING 4 bytes 68656c6c
1853 __slots__ = ("_bound_min", "_bound_max")
1854 tag_default = tag_encode(4)
1855 asn1_type_name = "OCTET STRING"
1868 :param value: set the value. Either binary type, or
1869 :py:class:`pyderasn.OctetString` object
1870 :param bounds: set ``(MIN, MAX)`` value size constraint.
1871 (-inf, +inf) by default
1872 :param bytes impl: override default tag with ``IMPLICIT`` one
1873 :param bytes expl: override default tag with ``EXPLICIT`` one
1874 :param default: set default value. Type same as in ``value``
1875 :param bool optional: is object ``OPTIONAL`` in sequence
1877 super(OctetString, self).__init__(
1886 self._bound_min, self._bound_max = getattr(
1892 self._bound_min, self._bound_max = bounds
1893 if value is not None:
1894 self._value = self._value_sanitize(value)
1895 if default is not None:
1896 default = self._value_sanitize(default)
1897 self.default = self.__class__(
1902 if self._value is None:
1903 self._value = default
1905 def _value_sanitize(self, value):
1906 if issubclass(value.__class__, OctetString):
1907 value = value._value
1908 elif isinstance(value, binary_type):
1911 raise InvalidValueType((self.__class__, bytes))
1912 if not self._bound_min <= len(value) <= self._bound_max:
1913 raise BoundsError(self._bound_min, len(value), self._bound_max)
1918 return self._value is not None
1921 obj = self.__class__()
1922 obj._value = self._value
1923 obj._bound_min = self._bound_min
1924 obj._bound_max = self._bound_max
1926 obj._expl = self._expl
1927 obj.default = self.default
1928 obj.optional = self.optional
1929 obj.offset = self.offset
1930 obj.llen = self.llen
1931 obj.vlen = self.vlen
1934 def __bytes__(self):
1935 self._assert_ready()
1938 def __eq__(self, their):
1939 if isinstance(their, binary_type):
1940 return self._value == their
1941 if not issubclass(their.__class__, OctetString):
1944 self._value == their._value and
1945 self.tag == their.tag and
1946 self._expl == their._expl
1958 return self.__class__(
1961 (self._bound_min, self._bound_max)
1962 if bounds is None else bounds
1964 impl=self.tag if impl is None else impl,
1965 expl=self._expl if expl is None else expl,
1966 default=self.default if default is None else default,
1967 optional=self.optional if optional is None else optional,
1971 self._assert_ready()
1974 len_encode(len(self._value)),
1978 def _decode(self, tlv, offset=0, decode_path=()):
1980 t, _, lv = tag_strip(tlv)
1981 except DecodeError as err:
1982 raise err.__class__(
1984 klass=self.__class__,
1985 decode_path=decode_path,
1990 klass=self.__class__,
1991 decode_path=decode_path,
1995 l, llen, v = len_decode(lv)
1996 except DecodeError as err:
1997 raise err.__class__(
1999 klass=self.__class__,
2000 decode_path=decode_path,
2004 raise NotEnoughData(
2005 "encoded length is longer than data",
2006 klass=self.__class__,
2007 decode_path=decode_path,
2010 v, tail = v[:l], v[l:]
2012 obj = self.__class__(
2014 bounds=(self._bound_min, self._bound_max),
2017 default=self.default,
2018 optional=self.optional,
2019 _decoded=(offset, llen, l),
2021 except BoundsError as err:
2024 klass=self.__class__,
2025 decode_path=decode_path,
2031 return pp_console_row(next(self.pps()))
2033 def pps(self, decode_path=()):
2035 asn1_type_name=self.asn1_type_name,
2036 obj_name=self.__class__.__name__,
2037 decode_path=decode_path,
2038 value=("%d bytes" % len(self._value)) if self.ready else None,
2039 blob=self._value if self.ready else None,
2040 optional=self.optional,
2041 default=self == self.default,
2042 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2043 expl=None if self._expl is None else tag_decode(self._expl),
2048 expl_offset=self.expl_offset if self.expled else None,
2049 expl_tlen=self.expl_tlen if self.expled else None,
2050 expl_llen=self.expl_llen if self.expled else None,
2051 expl_vlen=self.expl_vlen if self.expled else None,
2056 """``NULL`` null object
2064 tag_default = tag_encode(5)
2065 asn1_type_name = "NULL"
2069 value=None, # unused, but Sequence passes it
2076 :param bytes impl: override default tag with ``IMPLICIT`` one
2077 :param bytes expl: override default tag with ``EXPLICIT`` one
2078 :param bool optional: is object ``OPTIONAL`` in sequence
2080 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2088 obj = self.__class__()
2090 obj._expl = self._expl
2091 obj.default = self.default
2092 obj.optional = self.optional
2093 obj.offset = self.offset
2094 obj.llen = self.llen
2095 obj.vlen = self.vlen
2098 def __eq__(self, their):
2099 if not issubclass(their.__class__, Null):
2102 self.tag == their.tag and
2103 self._expl == their._expl
2113 return self.__class__(
2114 impl=self.tag if impl is None else impl,
2115 expl=self._expl if expl is None else expl,
2116 optional=self.optional if optional is None else optional,
2120 return self.tag + len_encode(0)
2122 def _decode(self, tlv, offset=0, decode_path=()):
2124 t, _, lv = tag_strip(tlv)
2125 except DecodeError as err:
2126 raise err.__class__(
2128 klass=self.__class__,
2129 decode_path=decode_path,
2134 klass=self.__class__,
2135 decode_path=decode_path,
2139 l, _, v = len_decode(lv)
2140 except DecodeError as err:
2141 raise err.__class__(
2143 klass=self.__class__,
2144 decode_path=decode_path,
2148 raise InvalidLength(
2149 "Null must have zero length",
2150 klass=self.__class__,
2151 decode_path=decode_path,
2154 obj = self.__class__(
2157 optional=self.optional,
2158 _decoded=(offset, 1, 0),
2163 return pp_console_row(next(self.pps()))
2165 def pps(self, decode_path=()):
2167 asn1_type_name=self.asn1_type_name,
2168 obj_name=self.__class__.__name__,
2169 decode_path=decode_path,
2170 optional=self.optional,
2171 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2172 expl=None if self._expl is None else tag_decode(self._expl),
2177 expl_offset=self.expl_offset if self.expled else None,
2178 expl_tlen=self.expl_tlen if self.expled else None,
2179 expl_llen=self.expl_llen if self.expled else None,
2180 expl_vlen=self.expl_vlen if self.expled else None,
2184 class ObjectIdentifier(Obj):
2185 """``OBJECT IDENTIFIER`` OID type
2187 >>> oid = ObjectIdentifier((1, 2, 3))
2188 OBJECT IDENTIFIER 1.2.3
2189 >>> oid == ObjectIdentifier("1.2.3")
2195 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2196 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2198 >>> str(ObjectIdentifier((3, 1)))
2199 Traceback (most recent call last):
2200 pyderasn.InvalidOID: unacceptable first arc value
2203 tag_default = tag_encode(6)
2204 asn1_type_name = "OBJECT IDENTIFIER"
2216 :param value: set the value. Either tuples of integers,
2217 string of "."-concatenated integers, or
2218 :py:class:`pyderasn.ObjectIdentifier` object
2219 :param bytes impl: override default tag with ``IMPLICIT`` one
2220 :param bytes expl: override default tag with ``EXPLICIT`` one
2221 :param default: set default value. Type same as in ``value``
2222 :param bool optional: is object ``OPTIONAL`` in sequence
2224 super(ObjectIdentifier, self).__init__(
2232 if value is not None:
2233 self._value = self._value_sanitize(value)
2234 if default is not None:
2235 default = self._value_sanitize(default)
2236 self.default = self.__class__(
2241 if self._value is None:
2242 self._value = default
2244 def __add__(self, their):
2245 if isinstance(their, self.__class__):
2246 return self.__class__(self._value + their._value)
2247 if isinstance(their, tuple):
2248 return self.__class__(self._value + their)
2249 raise InvalidValueType((self.__class__, tuple))
2251 def _value_sanitize(self, value):
2252 if issubclass(value.__class__, ObjectIdentifier):
2254 if isinstance(value, string_types):
2256 value = tuple(int(arc) for arc in value.split("."))
2258 raise InvalidOID("unacceptable arcs values")
2259 if isinstance(value, tuple):
2261 raise InvalidOID("less than 2 arcs")
2262 first_arc = value[0]
2263 if first_arc in (0, 1):
2264 if not (0 <= value[1] <= 39):
2265 raise InvalidOID("second arc is too wide")
2266 elif first_arc == 2:
2269 raise InvalidOID("unacceptable first arc value")
2271 raise InvalidValueType((self.__class__, str, tuple))
2275 return self._value is not None
2278 obj = self.__class__()
2279 obj._value = self._value
2281 obj._expl = self._expl
2282 obj.default = self.default
2283 obj.optional = self.optional
2284 obj.offset = self.offset
2285 obj.llen = self.llen
2286 obj.vlen = self.vlen
2290 self._assert_ready()
2291 return iter(self._value)
2294 return ".".join(str(arc) for arc in self._value or ())
2297 self._assert_ready()
2300 bytes(self._expl or b"") +
2301 str(self._value).encode("ascii"),
2304 def __eq__(self, their):
2305 if isinstance(their, tuple):
2306 return self._value == their
2307 if not issubclass(their.__class__, ObjectIdentifier):
2310 self.tag == their.tag and
2311 self._expl == their._expl and
2312 self._value == their._value
2315 def __lt__(self, their):
2316 return self._value < their
2318 def __gt__(self, their):
2319 return self._value > their
2329 return self.__class__(
2331 impl=self.tag if impl is None else impl,
2332 expl=self._expl if expl is None else expl,
2333 default=self.default if default is None else default,
2334 optional=self.optional if optional is None else optional,
2338 self._assert_ready()
2340 first_value = value[1]
2341 first_arc = value[0]
2344 elif first_arc == 1:
2346 elif first_arc == 2:
2348 else: # pragma: no cover
2349 raise RuntimeError("invalid arc is stored")
2350 octets = [zero_ended_encode(first_value)]
2351 for arc in value[2:]:
2352 octets.append(zero_ended_encode(arc))
2353 v = b"".join(octets)
2354 return b"".join((self.tag, len_encode(len(v)), v))
2356 def _decode(self, tlv, offset=0, decode_path=()):
2358 t, _, lv = tag_strip(tlv)
2359 except DecodeError as err:
2360 raise err.__class__(
2362 klass=self.__class__,
2363 decode_path=decode_path,
2368 klass=self.__class__,
2369 decode_path=decode_path,
2373 l, llen, v = len_decode(lv)
2374 except DecodeError as err:
2375 raise err.__class__(
2377 klass=self.__class__,
2378 decode_path=decode_path,
2382 raise NotEnoughData(
2383 "encoded length is longer than data",
2384 klass=self.__class__,
2385 decode_path=decode_path,
2389 raise NotEnoughData(
2391 klass=self.__class__,
2392 decode_path=decode_path,
2395 v, tail = v[:l], v[l:]
2401 octet = indexbytes(v, i)
2402 arc = (arc << 7) | (octet & 0x7F)
2403 if octet & 0x80 == 0:
2411 klass=self.__class__,
2412 decode_path=decode_path,
2416 second_arc = arcs[0]
2417 if 0 <= second_arc <= 39:
2419 elif 40 <= second_arc <= 79:
2425 obj = self.__class__(
2426 value=tuple([first_arc, second_arc] + arcs[1:]),
2429 default=self.default,
2430 optional=self.optional,
2431 _decoded=(offset, llen, l),
2436 return pp_console_row(next(self.pps()))
2438 def pps(self, decode_path=()):
2440 asn1_type_name=self.asn1_type_name,
2441 obj_name=self.__class__.__name__,
2442 decode_path=decode_path,
2443 value=str(self) if self.ready else None,
2444 optional=self.optional,
2445 default=self == self.default,
2446 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2447 expl=None if self._expl is None else tag_decode(self._expl),
2452 expl_offset=self.expl_offset if self.expled else None,
2453 expl_tlen=self.expl_tlen if self.expled else None,
2454 expl_llen=self.expl_llen if self.expled else None,
2455 expl_vlen=self.expl_vlen if self.expled else None,
2459 class Enumerated(Integer):
2460 """``ENUMERATED`` integer type
2462 This type is identical to :py:class:`pyderasn.Integer`, but requires
2463 schema to be specified and does not accept values missing from it.
2466 tag_default = tag_encode(10)
2467 asn1_type_name = "ENUMERATED"
2478 bounds=None, # dummy argument, workability for Integer.decode
2480 super(Enumerated, self).__init__(
2489 if len(self.specs) == 0:
2490 raise ValueError("schema must be specified")
2492 def _value_sanitize(self, value):
2493 if isinstance(value, self.__class__):
2494 value = value._value
2495 elif isinstance(value, integer_types):
2496 if value not in list(self.specs.values()):
2498 "unknown integer value: %s" % value,
2499 klass=self.__class__,
2501 elif isinstance(value, string_types):
2502 value = self.specs.get(value)
2504 raise ObjUnknown("integer value: %s" % value)
2506 raise InvalidValueType((self.__class__, int, str))
2510 obj = self.__class__(_specs=self.specs)
2511 obj._value = self._value
2512 obj._bound_min = self._bound_min
2513 obj._bound_max = self._bound_max
2515 obj._expl = self._expl
2516 obj.default = self.default
2517 obj.optional = self.optional
2518 obj.offset = self.offset
2519 obj.llen = self.llen
2520 obj.vlen = self.vlen
2532 return self.__class__(
2534 impl=self.tag if impl is None else impl,
2535 expl=self._expl if expl is None else expl,
2536 default=self.default if default is None else default,
2537 optional=self.optional if optional is None else optional,
2542 class CommonString(OctetString):
2543 """Common class for all strings
2545 Everything resembles :py:class:`pyderasn.OctetString`, except
2546 ability to deal with unicode text strings.
2548 >>> hexenc("привет мир".encode("utf-8"))
2549 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2550 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2552 >>> s = UTF8String("привет мир")
2553 UTF8String UTF8String привет мир
2555 'привет мир'
2556 >>> hexenc(bytes(s))
2557 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2559 >>> PrintableString("привет мир")
2560 Traceback (most recent call last):
2561 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2563 >>> BMPString("ада", bounds=(2, 2))
2564 Traceback (most recent call last):
2565 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2566 >>> s = BMPString("ад", bounds=(2, 2))
2569 >>> hexenc(bytes(s))
2577 * - :py:class:`pyderasn.UTF8String`
2579 * - :py:class:`pyderasn.NumericString`
2581 * - :py:class:`pyderasn.PrintableString`
2583 * - :py:class:`pyderasn.TeletexString`
2585 * - :py:class:`pyderasn.T61String`
2587 * - :py:class:`pyderasn.VideotexString`
2589 * - :py:class:`pyderasn.IA5String`
2591 * - :py:class:`pyderasn.GraphicString`
2593 * - :py:class:`pyderasn.VisibleString`
2595 * - :py:class:`pyderasn.ISO646String`
2597 * - :py:class:`pyderasn.GeneralString`
2599 * - :py:class:`pyderasn.UniversalString`
2601 * - :py:class:`pyderasn.BMPString`
2604 __slots__ = ("encoding",)
2606 def _value_sanitize(self, value):
2608 value_decoded = None
2609 if isinstance(value, self.__class__):
2610 value_raw = value._value
2611 elif isinstance(value, text_type):
2612 value_decoded = value
2613 elif isinstance(value, binary_type):
2616 raise InvalidValueType((self.__class__, text_type, binary_type))
2618 value_decoded.encode(self.encoding)
2619 if value_raw is None else value_raw
2622 value_raw.decode(self.encoding)
2623 if value_decoded is None else value_decoded
2625 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2633 def __eq__(self, their):
2634 if isinstance(their, binary_type):
2635 return self._value == their
2636 if isinstance(their, text_type):
2637 return self._value == their.encode(self.encoding)
2638 if not isinstance(their, self.__class__):
2641 self._value == their._value and
2642 self.tag == their.tag and
2643 self._expl == their._expl
2646 def __unicode__(self):
2648 return self._value.decode(self.encoding)
2649 return text_type(self._value)
2652 return pp_console_row(next(self.pps(no_unicode=PY2)))
2654 def pps(self, decode_path=(), no_unicode=False):
2657 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2659 asn1_type_name=self.asn1_type_name,
2660 obj_name=self.__class__.__name__,
2661 decode_path=decode_path,
2663 optional=self.optional,
2664 default=self == self.default,
2665 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2666 expl=None if self._expl is None else tag_decode(self._expl),
2674 class UTF8String(CommonString):
2676 tag_default = tag_encode(12)
2678 asn1_type_name = "UTF8String"
2681 class NumericString(CommonString):
2683 tag_default = tag_encode(18)
2685 asn1_type_name = "NumericString"
2688 class PrintableString(CommonString):
2690 tag_default = tag_encode(19)
2692 asn1_type_name = "PrintableString"
2695 class TeletexString(CommonString):
2697 tag_default = tag_encode(20)
2699 asn1_type_name = "TeletexString"
2702 class T61String(TeletexString):
2704 asn1_type_name = "T61String"
2707 class VideotexString(CommonString):
2709 tag_default = tag_encode(21)
2710 encoding = "iso-8859-1"
2711 asn1_type_name = "VideotexString"
2714 class IA5String(CommonString):
2716 tag_default = tag_encode(22)
2718 asn1_type_name = "IA5"
2721 class UTCTime(CommonString):
2722 """``UTCTime`` datetime type
2724 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2725 UTCTime UTCTime 2017-09-30T22:07:50
2731 datetime.datetime(2017, 9, 30, 22, 7, 50)
2732 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2733 datetime.datetime(1957, 9, 30, 22, 7, 50)
2736 tag_default = tag_encode(23)
2738 asn1_type_name = "UTCTime"
2740 fmt = "%y%m%d%H%M%SZ"
2750 bounds=None, # dummy argument, workability for OctetString.decode
2753 :param value: set the value. Either datetime type, or
2754 :py:class:`pyderasn.UTCTime` object
2755 :param bytes impl: override default tag with ``IMPLICIT`` one
2756 :param bytes expl: override default tag with ``EXPLICIT`` one
2757 :param default: set default value. Type same as in ``value``
2758 :param bool optional: is object ``OPTIONAL`` in sequence
2760 super(UTCTime, self).__init__(
2768 if value is not None:
2769 self._value = self._value_sanitize(value)
2770 if default is not None:
2771 default = self._value_sanitize(default)
2772 self.default = self.__class__(
2777 if self._value is None:
2778 self._value = default
2780 def _value_sanitize(self, value):
2781 if isinstance(value, self.__class__):
2783 if isinstance(value, datetime):
2784 return value.strftime(self.fmt).encode("ascii")
2785 if isinstance(value, binary_type):
2786 value_decoded = value.decode("ascii")
2787 if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2789 datetime.strptime(value_decoded, self.fmt)
2791 raise DecodeError("invalid UTCTime format")
2794 raise DecodeError("invalid UTCTime length")
2795 raise InvalidValueType((self.__class__, datetime))
2797 def __eq__(self, their):
2798 if isinstance(their, binary_type):
2799 return self._value == their
2800 if isinstance(their, datetime):
2801 return self.todatetime() == their
2802 if not isinstance(their, self.__class__):
2805 self._value == their._value and
2806 self.tag == their.tag and
2807 self._expl == their._expl
2810 def todatetime(self):
2811 """Convert to datetime
2815 Pay attention that UTCTime can not hold full year, so all years
2816 having < 50 years are treated as 20xx, 19xx otherwise, according
2817 to X.509 recomendation.
2819 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2820 year = value.year % 100
2822 year=(2000 + year) if year < 50 else (1900 + year),
2826 minute=value.minute,
2827 second=value.second,
2831 return pp_console_row(next(self.pps()))
2833 def pps(self, decode_path=()):
2835 asn1_type_name=self.asn1_type_name,
2836 obj_name=self.__class__.__name__,
2837 decode_path=decode_path,
2838 value=self.todatetime().isoformat() if self.ready else None,
2839 optional=self.optional,
2840 default=self == self.default,
2841 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2842 expl=None if self._expl is None else tag_decode(self._expl),
2850 class GeneralizedTime(UTCTime):
2851 """``GeneralizedTime`` datetime type
2853 This type is similar to :py:class:`pyderasn.UTCTime`.
2855 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2856 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2858 '20170930220750.000123Z'
2859 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2860 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2863 tag_default = tag_encode(24)
2864 asn1_type_name = "GeneralizedTime"
2866 fmt = "%Y%m%d%H%M%SZ"
2867 fmt_ms = "%Y%m%d%H%M%S.%fZ"
2869 def _value_sanitize(self, value):
2870 if isinstance(value, self.__class__):
2872 if isinstance(value, datetime):
2873 return value.strftime(
2874 self.fmt_ms if value.microsecond > 0 else self.fmt
2876 if isinstance(value, binary_type):
2877 value_decoded = value.decode("ascii")
2878 if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2880 datetime.strptime(value_decoded, self.fmt)
2883 "invalid GeneralizedTime (without ms) format",
2886 elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2888 datetime.strptime(value_decoded, self.fmt_ms)
2891 "invalid GeneralizedTime (with ms) format",
2896 "invalid GeneralizedTime length",
2897 klass=self.__class__,
2899 raise InvalidValueType((self.__class__, datetime))
2901 def todatetime(self):
2902 value = self._value.decode("ascii")
2903 if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2904 return datetime.strptime(value, self.fmt)
2905 return datetime.strptime(value, self.fmt_ms)
2908 class GraphicString(CommonString):
2910 tag_default = tag_encode(25)
2911 encoding = "iso-8859-1"
2912 asn1_type_name = "GraphicString"
2915 class VisibleString(CommonString):
2917 tag_default = tag_encode(26)
2919 asn1_type_name = "VisibleString"
2922 class ISO646String(VisibleString):
2924 asn1_type_name = "ISO646String"
2927 class GeneralString(CommonString):
2929 tag_default = tag_encode(27)
2930 encoding = "iso-8859-1"
2931 asn1_type_name = "GeneralString"
2934 class UniversalString(CommonString):
2936 tag_default = tag_encode(28)
2937 encoding = "utf-32-be"
2938 asn1_type_name = "UniversalString"
2941 class BMPString(CommonString):
2943 tag_default = tag_encode(30)
2944 encoding = "utf-16-be"
2945 asn1_type_name = "BMPString"
2949 """``CHOICE`` special type
2953 class GeneralName(Choice):
2955 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2956 ('dNSName', IA5String(impl=tag_ctxp(2))),
2959 >>> gn = GeneralName()
2961 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2962 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2963 >>> gn["dNSName"] = IA5String("bar.baz")
2964 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2965 >>> gn["rfc822Name"]
2968 [2] IA5String IA5 bar.baz
2971 >>> gn.value == gn["dNSName"]
2974 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2976 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2977 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2979 __slots__ = ("specs",)
2981 asn1_type_name = "CHOICE"
2994 :param value: set the value. Either ``(choice, value)`` tuple, or
2995 :py:class:`pyderasn.Choice` object
2996 :param bytes impl: can not be set, do **not** use it
2997 :param bytes expl: override default tag with ``EXPLICIT`` one
2998 :param default: set default value. Type same as in ``value``
2999 :param bool optional: is object ``OPTIONAL`` in sequence
3001 if impl is not None:
3002 raise ValueError("no implicit tag allowed for CHOICE")
3003 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3005 schema = getattr(self, "schema", ())
3006 if len(schema) == 0:
3007 raise ValueError("schema must be specified")
3009 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3012 if value is not None:
3013 self._value = self._value_sanitize(value)
3014 if default is not None:
3015 default_value = self._value_sanitize(default)
3016 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3017 default_obj.specs = self.specs
3018 default_obj._value = default_value
3019 self.default = default_obj
3021 self._value = default_obj.copy()._value
3023 def _value_sanitize(self, value):
3024 if isinstance(value, self.__class__):
3026 if isinstance(value, tuple) and len(value) == 2:
3028 spec = self.specs.get(choice)
3030 raise ObjUnknown(choice)
3031 if not isinstance(obj, spec.__class__):
3032 raise InvalidValueType((spec,))
3033 return (choice, spec(obj))
3034 raise InvalidValueType((self.__class__, tuple))
3038 return self._value is not None and self._value[1].ready
3041 obj = self.__class__(schema=self.specs)
3042 obj._expl = self._expl
3043 obj.default = self.default
3044 obj.optional = self.optional
3045 obj.offset = self.offset
3046 obj.llen = self.llen
3047 obj.vlen = self.vlen
3049 if value is not None:
3050 obj._value = (value[0], value[1].copy())
3053 def __eq__(self, their):
3054 if isinstance(their, tuple) and len(their) == 2:
3055 return self._value == their
3056 if not isinstance(their, self.__class__):
3059 self.specs == their.specs and
3060 self._value == their._value
3070 return self.__class__(
3073 expl=self._expl if expl is None else expl,
3074 default=self.default if default is None else default,
3075 optional=self.optional if optional is None else optional,
3080 self._assert_ready()
3081 return self._value[0]
3085 self._assert_ready()
3086 return self._value[1]
3088 def __getitem__(self, key):
3089 if key not in self.specs:
3090 raise ObjUnknown(key)
3091 if self._value is None:
3093 choice, value = self._value
3098 def __setitem__(self, key, value):
3099 spec = self.specs.get(key)
3101 raise ObjUnknown(key)
3102 if not isinstance(value, spec.__class__):
3103 raise InvalidValueType((spec.__class__,))
3104 self._value = (key, spec(value))
3112 return self._value[1].decoded if self.ready else False
3115 self._assert_ready()
3116 return self._value[1].encode()
3118 def _decode(self, tlv, offset=0, decode_path=()):
3119 for choice, spec in self.specs.items():
3121 value, tail = spec.decode(
3125 decode_path=decode_path + (choice,),
3129 obj = self.__class__(
3132 default=self.default,
3133 optional=self.optional,
3134 _decoded=(offset, 0, value.tlvlen),
3136 obj._value = (choice, value)
3139 klass=self.__class__,
3140 decode_path=decode_path,
3145 value = pp_console_row(next(self.pps()))
3147 value = "%s[%r]" % (value, self.value)
3150 def pps(self, decode_path=()):
3152 asn1_type_name=self.asn1_type_name,
3153 obj_name=self.__class__.__name__,
3154 decode_path=decode_path,
3155 value=self.choice if self.ready else None,
3156 optional=self.optional,
3157 default=self == self.default,
3158 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3159 expl=None if self._expl is None else tag_decode(self._expl),
3166 yield self.value.pps(decode_path=decode_path + (self.choice,))
3169 class PrimitiveTypes(Choice):
3170 """Predefined ``CHOICE`` for all generic primitive types
3172 It could be useful for general decoding of some unspecified values:
3174 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3175 OCTET STRING 3 bytes 666f6f
3176 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3180 schema = tuple((klass.__name__, klass()) for klass in (
3205 """``ANY`` special type
3207 >>> Any(Integer(-123))
3209 >>> a = Any(OctetString(b"hello world").encode())
3210 ANY 040b68656c6c6f20776f726c64
3211 >>> hexenc(bytes(a))
3212 b'0x040x0bhello world'
3215 tag_default = tag_encode(0)
3216 asn1_type_name = "ANY"
3226 :param value: set the value. Either any kind of pyderasn's
3227 **ready** object, or bytes. Pay attention that
3228 **no** validation is performed is raw binary value
3230 :param bytes expl: override default tag with ``EXPLICIT`` one
3231 :param bool optional: is object ``OPTIONAL`` in sequence
3233 super(Any, self).__init__(None, expl, None, optional, _decoded)
3234 self._value = None if value is None else self._value_sanitize(value)
3236 def _value_sanitize(self, value):
3237 if isinstance(value, self.__class__):
3239 if isinstance(value, Obj):
3240 return value.encode()
3241 if isinstance(value, binary_type):
3243 raise InvalidValueType((self.__class__, Obj, binary_type))
3247 return self._value is not None
3250 obj = self.__class__()
3251 obj._value = self._value
3253 obj._expl = self._expl
3254 obj.optional = self.optional
3255 obj.offset = self.offset
3256 obj.llen = self.llen
3257 obj.vlen = self.vlen
3260 def __eq__(self, their):
3261 if isinstance(their, binary_type):
3262 return self._value == their
3263 if issubclass(their.__class__, Any):
3264 return self._value == their._value
3273 return self.__class__(
3275 expl=self._expl if expl is None else expl,
3276 optional=self.optional if optional is None else optional,
3279 def __bytes__(self):
3280 self._assert_ready()
3288 self._assert_ready()
3291 def _decode(self, tlv, offset=0, decode_path=()):
3293 t, tlen, lv = tag_strip(tlv)
3294 l, llen, v = len_decode(lv)
3295 except DecodeError as err:
3296 raise err.__class__(
3298 klass=self.__class__,
3299 decode_path=decode_path,
3303 raise NotEnoughData(
3304 "encoded length is longer than data",
3305 klass=self.__class__,
3306 decode_path=decode_path,
3309 tlvlen = tlen + llen + l
3310 v, tail = tlv[:tlvlen], v[l:]
3311 obj = self.__class__(
3314 optional=self.optional,
3315 _decoded=(offset, 0, tlvlen),
3321 return pp_console_row(next(self.pps()))
3323 def pps(self, decode_path=()):
3325 asn1_type_name=self.asn1_type_name,
3326 obj_name=self.__class__.__name__,
3327 decode_path=decode_path,
3328 blob=self._value if self.ready else None,
3329 optional=self.optional,
3330 default=self == self.default,
3331 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3332 expl=None if self._expl is None else tag_decode(self._expl),
3337 expl_offset=self.expl_offset if self.expled else None,
3338 expl_tlen=self.expl_tlen if self.expled else None,
3339 expl_llen=self.expl_llen if self.expled else None,
3340 expl_vlen=self.expl_vlen if self.expled else None,
3344 ########################################################################
3345 # ASN.1 constructed types
3346 ########################################################################
3348 class Sequence(Obj):
3349 """``SEQUENCE`` structure type
3351 You have to make specification of sequence::
3353 class Extension(Sequence):
3356 ("extnID", ObjectIdentifier()),
3357 ("critical", Boolean(default=False)),
3358 ("extnValue", OctetString()),
3361 Then, you can work with it as with dictionary.
3363 >>> ext = Extension()
3364 >>> Extension().specs
3366 ('extnID', OBJECT IDENTIFIER),
3367 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3368 ('extnValue', OCTET STRING),
3370 >>> ext["extnID"] = "1.2.3"
3371 Traceback (most recent call last):
3372 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3373 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3375 You can know if sequence is ready to be encoded:
3380 Traceback (most recent call last):
3381 pyderasn.ObjNotReady: object is not ready: extnValue
3382 >>> ext["extnValue"] = OctetString(b"foobar")
3386 Value you want to assign, must have the same **type** as in
3387 corresponding specification, but it can have different tags,
3388 optional/default attributes -- they will be taken from specification
3391 class TBSCertificate(Sequence):
3393 ("version", Version(expl=tag_ctxc(0), default="v1")),
3396 >>> tbs = TBSCertificate()
3397 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3399 You can know if value exists/set in the sequence and take its value:
3401 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3404 OBJECT IDENTIFIER 1.2.3
3406 But pay attention that if value has default, then it won't be (not
3407 in) in the sequence (because ``DEFAULT`` must not be encoded in
3408 DER), but you can read its value:
3410 >>> "critical" in ext, ext["critical"]
3411 (False, BOOLEAN False)
3412 >>> ext["critical"] = Boolean(True)
3413 >>> "critical" in ext, ext["critical"]
3414 (True, BOOLEAN True)
3416 All defaulted values are always optional.
3420 When decoded DER contains defaulted value inside, then
3421 technically this is not valid DER encoding. But we allow
3422 and pass it. Of course reencoding of that kind of DER will
3423 result in different binary representation (validly without
3424 defaulted value inside).
3426 Two sequences are equal if they have equal specification (schema),
3427 implicit/explicit tagging and the same values.
3429 __slots__ = ("specs",)
3430 tag_default = tag_encode(form=TagFormConstructed, num=16)
3431 asn1_type_name = "SEQUENCE"
3443 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3445 schema = getattr(self, "schema", ())
3447 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3450 if value is not None:
3451 self._value = self._value_sanitize(value)
3452 if default is not None:
3453 default_value = self._value_sanitize(default)
3454 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3455 default_obj.specs = self.specs
3456 default_obj._value = default_value
3457 self.default = default_obj
3459 self._value = default_obj.copy()._value
3461 def _value_sanitize(self, value):
3462 if not issubclass(value.__class__, Sequence):
3463 raise InvalidValueType((Sequence,))
3468 for name, spec in self.specs.items():
3469 value = self._value.get(name)
3480 obj = self.__class__(schema=self.specs)
3482 obj._expl = self._expl
3483 obj.default = self.default
3484 obj.optional = self.optional
3485 obj.offset = self.offset
3486 obj.llen = self.llen
3487 obj.vlen = self.vlen
3488 obj._value = {k: v.copy() for k, v in self._value.items()}
3491 def __eq__(self, their):
3492 if not isinstance(their, self.__class__):
3495 self.specs == their.specs and
3496 self.tag == their.tag and
3497 self._expl == their._expl and
3498 self._value == their._value
3509 return self.__class__(
3512 impl=self.tag if impl is None else impl,
3513 expl=self._expl if expl is None else expl,
3514 default=self.default if default is None else default,
3515 optional=self.optional if optional is None else optional,
3518 def __contains__(self, key):
3519 return key in self._value
3521 def __setitem__(self, key, value):
3522 spec = self.specs.get(key)
3524 raise ObjUnknown(key)
3526 self._value.pop(key, None)
3528 if not isinstance(value, spec.__class__):
3529 raise InvalidValueType((spec.__class__,))
3530 value = spec(value=value)
3531 if spec.default is not None and value == spec.default:
3532 self._value.pop(key, None)
3534 self._value[key] = value
3536 def __getitem__(self, key):
3537 value = self._value.get(key)
3538 if value is not None:
3540 spec = self.specs.get(key)
3542 raise ObjUnknown(key)
3543 if spec.default is not None:
3547 def _encoded_values(self):
3549 for name, spec in self.specs.items():
3550 value = self._value.get(name)
3554 raise ObjNotReady(name)
3555 raws.append(value.encode())
3559 v = b"".join(self._encoded_values())
3560 return b"".join((self.tag, len_encode(len(v)), v))
3562 def _decode(self, tlv, offset=0, decode_path=()):
3564 t, tlen, lv = tag_strip(tlv)
3565 except DecodeError as err:
3566 raise err.__class__(
3568 klass=self.__class__,
3569 decode_path=decode_path,
3574 klass=self.__class__,
3575 decode_path=decode_path,
3579 l, llen, v = len_decode(lv)
3580 except DecodeError as err:
3581 raise err.__class__(
3583 klass=self.__class__,
3584 decode_path=decode_path,
3588 raise NotEnoughData(
3589 "encoded length is longer than data",
3590 klass=self.__class__,
3591 decode_path=decode_path,
3594 v, tail = v[:l], v[l:]
3595 sub_offset = offset + tlen + llen
3597 for name, spec in self.specs.items():
3598 if len(v) == 0 and spec.optional:
3601 value, v_tail = spec.decode(
3605 decode_path=decode_path + (name,),
3611 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3613 if spec.default is not None and value == spec.default:
3614 # Encoded default values are not valid in DER,
3615 # but we still allow that
3617 values[name] = value
3621 klass=self.__class__,
3622 decode_path=decode_path,
3625 obj = self.__class__(
3629 default=self.default,
3630 optional=self.optional,
3631 _decoded=(offset, llen, l),
3637 value = pp_console_row(next(self.pps()))
3639 for name in self.specs:
3640 _value = self._value.get(name)
3643 cols.append(repr(_value))
3644 return "%s[%s]" % (value, ", ".join(cols))
3646 def pps(self, decode_path=()):
3648 asn1_type_name=self.asn1_type_name,
3649 obj_name=self.__class__.__name__,
3650 decode_path=decode_path,
3651 optional=self.optional,
3652 default=self == self.default,
3653 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3654 expl=None if self._expl is None else tag_decode(self._expl),
3659 expl_offset=self.expl_offset if self.expled else None,
3660 expl_tlen=self.expl_tlen if self.expled else None,
3661 expl_llen=self.expl_llen if self.expled else None,
3662 expl_vlen=self.expl_vlen if self.expled else None,
3664 for name in self.specs:
3665 value = self._value.get(name)
3668 yield value.pps(decode_path=decode_path + (name,))
3671 class Set(Sequence):
3672 """``SET`` structure type
3674 Its usage is identical to :py:class:`pyderasn.Sequence`.
3677 tag_default = tag_encode(form=TagFormConstructed, num=17)
3678 asn1_type_name = "SET"
3681 raws = self._encoded_values()
3684 return b"".join((self.tag, len_encode(len(v)), v))
3686 def _decode(self, tlv, offset=0, decode_path=()):
3688 t, tlen, lv = tag_strip(tlv)
3689 except DecodeError as err:
3690 raise err.__class__(
3692 klass=self.__class__,
3693 decode_path=decode_path,
3698 klass=self.__class__,
3699 decode_path=decode_path,
3703 l, llen, v = len_decode(lv)
3704 except DecodeError as err:
3705 raise err.__class__(
3707 klass=self.__class__,
3708 decode_path=decode_path,
3712 raise NotEnoughData(
3713 "encoded length is longer than data",
3714 klass=self.__class__,
3717 v, tail = v[:l], v[l:]
3718 sub_offset = offset + tlen + llen
3720 specs_items = self.specs.items
3722 for name, spec in specs_items():
3724 value, v_tail = spec.decode(
3728 decode_path=decode_path + (name,),
3733 value.expl_tlvlen if value.expled else value.tlvlen
3736 if spec.default is None or value != spec.default: # pragma: no cover
3737 # SeqMixing.test_encoded_default_accepted covers that place
3738 values[name] = value
3742 klass=self.__class__,
3743 decode_path=decode_path,
3746 obj = self.__class__(
3750 default=self.default,
3751 optional=self.optional,
3752 _decoded=(offset, llen, l),
3758 class SequenceOf(Obj):
3759 """``SEQUENCE OF`` sequence type
3761 For that kind of type you must specify the object it will carry on
3762 (bounds are for example here, not required)::
3764 class Ints(SequenceOf):
3769 >>> ints.append(Integer(123))
3770 >>> ints.append(Integer(234))
3772 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
3773 >>> [int(i) for i in ints]
3775 >>> ints.append(Integer(345))
3776 Traceback (most recent call last):
3777 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
3780 >>> ints[1] = Integer(345)
3782 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
3784 Also you can initialize sequence with preinitialized values:
3786 >>> ints = Ints([Integer(123), Integer(234)])
3788 __slots__ = ("spec", "_bound_min", "_bound_max")
3789 tag_default = tag_encode(form=TagFormConstructed, num=16)
3790 asn1_type_name = "SEQUENCE OF"
3803 super(SequenceOf, self).__init__(
3811 schema = getattr(self, "schema", None)
3813 raise ValueError("schema must be specified")
3816 self._bound_min, self._bound_max = getattr(
3822 self._bound_min, self._bound_max = bounds
3824 if value is not None:
3825 self._value = self._value_sanitize(value)
3826 if default is not None:
3827 default_value = self._value_sanitize(default)
3828 default_obj = self.__class__(
3833 default_obj._value = default_value
3834 self.default = default_obj
3836 self._value = default_obj.copy()._value
3838 def _value_sanitize(self, value):
3839 if issubclass(value.__class__, SequenceOf):
3840 value = value._value
3841 elif hasattr(value, "__iter__"):
3844 raise InvalidValueType((self.__class__, iter))
3845 if not self._bound_min <= len(value) <= self._bound_max:
3846 raise BoundsError(self._bound_min, len(value), self._bound_max)
3848 if not isinstance(v, self.spec.__class__):
3849 raise InvalidValueType((self.spec.__class__,))
3854 return all(v.ready for v in self._value)
3857 obj = self.__class__(schema=self.spec)
3858 obj._bound_min = self._bound_min
3859 obj._bound_max = self._bound_max
3861 obj._expl = self._expl
3862 obj.default = self.default
3863 obj.optional = self.optional
3864 obj.offset = self.offset
3865 obj.llen = self.llen
3866 obj.vlen = self.vlen
3867 obj._value = [v.copy() for v in self._value]
3870 def __eq__(self, their):
3871 if isinstance(their, self.__class__):
3873 self.spec == their.spec and
3874 self.tag == their.tag and
3875 self._expl == their._expl and
3876 self._value == their._value
3878 if hasattr(their, "__iter__"):
3879 return self._value == list(their)
3891 return self.__class__(
3895 (self._bound_min, self._bound_max)
3896 if bounds is None else bounds
3898 impl=self.tag if impl is None else impl,
3899 expl=self._expl if expl is None else expl,
3900 default=self.default if default is None else default,
3901 optional=self.optional if optional is None else optional,
3904 def __contains__(self, key):
3905 return key in self._value
3907 def append(self, value):
3908 if not isinstance(value, self.spec.__class__):
3909 raise InvalidValueType((self.spec.__class__,))
3910 if len(self._value) + 1 > self._bound_max:
3913 len(self._value) + 1,
3916 self._value.append(value)
3919 self._assert_ready()
3920 return iter(self._value)
3923 self._assert_ready()
3924 return len(self._value)
3926 def __setitem__(self, key, value):
3927 if not isinstance(value, self.spec.__class__):
3928 raise InvalidValueType((self.spec.__class__,))
3929 self._value[key] = self.spec(value=value)
3931 def __getitem__(self, key):
3932 return self._value[key]
3934 def _encoded_values(self):
3935 return [v.encode() for v in self._value]
3938 v = b"".join(self._encoded_values())
3939 return b"".join((self.tag, len_encode(len(v)), v))
3941 def _decode(self, tlv, offset=0, decode_path=()):
3943 t, tlen, lv = tag_strip(tlv)
3944 except DecodeError as err:
3945 raise err.__class__(
3947 klass=self.__class__,
3948 decode_path=decode_path,
3953 klass=self.__class__,
3954 decode_path=decode_path,
3958 l, llen, v = len_decode(lv)
3959 except DecodeError as err:
3960 raise err.__class__(
3962 klass=self.__class__,
3963 decode_path=decode_path,
3967 raise NotEnoughData(
3968 "encoded length is longer than data",
3969 klass=self.__class__,
3970 decode_path=decode_path,
3973 v, tail = v[:l], v[l:]
3974 sub_offset = offset + tlen + llen
3978 value, v_tail = spec.decode(
3982 decode_path=decode_path + (str(len(_value)),),
3984 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3986 _value.append(value)
3987 obj = self.__class__(
3990 bounds=(self._bound_min, self._bound_max),
3993 default=self.default,
3994 optional=self.optional,
3995 _decoded=(offset, llen, l),
4001 pp_console_row(next(self.pps())),
4002 ", ".join(repr(v) for v in self._value),
4005 def pps(self, decode_path=()):
4007 asn1_type_name=self.asn1_type_name,
4008 obj_name=self.__class__.__name__,
4009 decode_path=decode_path,
4010 optional=self.optional,
4011 default=self == self.default,
4012 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4013 expl=None if self._expl is None else tag_decode(self._expl),
4018 expl_offset=self.expl_offset if self.expled else None,
4019 expl_tlen=self.expl_tlen if self.expled else None,
4020 expl_llen=self.expl_llen if self.expled else None,
4021 expl_vlen=self.expl_vlen if self.expled else None,
4023 for i, value in enumerate(self._value):
4024 yield value.pps(decode_path=decode_path + (str(i),))
4027 class SetOf(SequenceOf):
4028 """``SET OF`` sequence type
4030 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4033 tag_default = tag_encode(form=TagFormConstructed, num=17)
4034 asn1_type_name = "SET OF"
4037 raws = self._encoded_values()
4040 return b"".join((self.tag, len_encode(len(v)), v))
4043 def obj_by_path(pypath): # pragma: no cover
4044 """Import object specified as string Python path
4046 Modules must be separated from classes/functions with ``:``.
4048 >>> obj_by_path("foo.bar:Baz")
4049 <class 'foo.bar.Baz'>
4050 >>> obj_by_path("foo.bar:Baz.boo")
4051 <classmethod 'foo.bar.Baz.boo'>
4053 mod, objs = pypath.rsplit(":", 1)
4054 from importlib import import_module
4055 obj = import_module(mod)
4056 for obj_name in objs.split("."):
4057 obj = getattr(obj, obj_name)
4061 def main(): # pragma: no cover
4063 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4064 parser.add_argument(
4066 help="Python path to dictionary with OIDs",
4068 parser.add_argument(
4070 help="Python path to schema definition to use",
4072 parser.add_argument(
4074 type=argparse.FileType("rb"),
4075 help="Path to DER file you want to decode",
4077 args = parser.parse_args()
4078 der = memoryview(args.DERFile.read())
4079 args.DERFile.close()
4080 oids = obj_by_path(args.oids) if args.oids else {}
4082 schema = obj_by_path(args.schema)
4083 from functools import partial
4084 pprinter = partial(pprint, big_blobs=True)
4086 # All of this below is a big hack with self references
4087 choice = PrimitiveTypes()
4088 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4089 choice.specs["SetOf"] = SetOf(schema=choice)
4091 choice.specs["SequenceOf%d" % i] = SequenceOf(
4095 choice.specs["Any"] = Any()
4097 # Class name equals to type name, to omit it from output
4098 class SEQUENCEOF(SequenceOf):
4101 schema = SEQUENCEOF()
4103 def pprint_any(obj, oids=None):
4104 def _pprint_pps(pps):
4106 if hasattr(pp, "_fields"):
4107 if pp.asn1_type_name == Choice.asn1_type_name:
4109 pp_kwargs = pp._asdict()
4110 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4111 pp = _pp(**pp_kwargs)
4112 yield pp_console_row(
4118 for row in pp_console_blob(pp):
4121 for row in _pprint_pps(pp):
4123 return "\n".join(_pprint_pps(obj.pps()))
4124 pprinter = pprint_any
4125 obj, tail = schema().decode(der)
4126 print(pprinter(obj, oids=oids))
4128 print("\nTrailing data: %s" % hexenc(tail))
4131 if __name__ == "__main__":