3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER codec with abstract structures
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments takes
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two object of the same type, but with different implicit/explicit tags
94 You can get objects effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
141 Some objects give ability to set value size constraints. This is either
142 possible integer value, or allowed length of various strings and
143 sequences. Constraints are set in the following way::
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
150 For simplicity you can also set bounds the following way::
152 bounded_x = X(bounds=(MIN, MAX))
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
160 All objects have ``ready`` boolean property, that tells if it is ready
161 to be encoded. If that kind of action is performed on unready object,
162 then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
164 All objects have ``copy()`` method, returning its copy, that can be safely
170 Decoding is performed using ``decode()`` method. ``offset`` optional
171 argument could be used to set initial object's offset in the binary
172 data, for convenience. It returns decoded object and remaining
173 unmarshalled data (tail). Internally all work is done on
174 ``memoryview(data)``, and you can leave returning tail as a memoryview,
175 by specifying ``leavemm=True`` argument.
177 When object is decoded, ``decoded`` property is true and you can safely
178 use following properties:
180 * ``offset`` -- position from initial offset where object's tag is started
181 * ``tlen`` -- length of object's tag
182 * ``llen`` -- length of object's length value
183 * ``vlen`` -- length of object's value
184 * ``tlvlen`` -- length of the whole object
186 Pay attention that those values do **not** include anything related to
187 explicit tag. If you want to know information about it, then use:
188 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
189 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
190 (that actually equals to ordinary ``tlvlen``).
192 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
197 All objects have ``pps()`` method, that is a generator of
198 :py:class:`pyderasn.PP` namedtuple, holding various raw information
199 about the object. If ``pps`` is called on sequences, then all underlying
200 ``PP`` will be yielded.
202 You can use :py:func:`pyderasn.pp_console_row` function, converting
203 those ``PP`` to human readable string. Actually exactly it is used for
204 all object ``repr``. But it is easy to write custom formatters.
206 >>> from pyderasn import pprint
207 >>> encoded = Integer(-12345).encode()
208 >>> obj, tail = Integer().decode(encoded)
209 >>> print(pprint(obj))
210 0 [1,1, 2] INTEGER -12345
217 .. autoclass:: pyderasn.Boolean
222 .. autoclass:: pyderasn.Integer
227 .. autoclass:: pyderasn.BitString
232 .. autoclass:: pyderasn.OctetString
237 .. autoclass:: pyderasn.Null
242 .. autoclass:: pyderasn.ObjectIdentifier
247 .. autoclass:: pyderasn.Enumerated
251 .. autoclass:: pyderasn.CommonString
255 .. autoclass:: pyderasn.UTCTime
256 :members: __init__, todatetime
260 .. autoclass:: pyderasn.GeneralizedTime
267 .. autoclass:: pyderasn.Choice
272 .. autoclass:: PrimitiveTypes
276 .. autoclass:: pyderasn.Any
284 .. autoclass:: pyderasn.Sequence
289 .. autoclass:: pyderasn.Set
294 .. autoclass:: pyderasn.SequenceOf
299 .. autoclass:: pyderasn.SetOf
305 .. autofunction:: pyderasn.hexenc
306 .. autofunction:: pyderasn.hexdec
307 .. autofunction:: pyderasn.tag_encode
308 .. autofunction:: pyderasn.tag_decode
309 .. autofunction:: pyderasn.tag_ctxp
310 .. autofunction:: pyderasn.tag_ctxc
311 .. autoclass:: pyderasn.Obj
314 from codecs import getdecoder
315 from codecs import getencoder
316 from collections import namedtuple
317 from collections import OrderedDict
318 from datetime import datetime
319 from math import ceil
321 from six import add_metaclass
322 from six import binary_type
323 from six import byte2int
324 from six import indexbytes
325 from six import int2byte
326 from six import integer_types
327 from six import iterbytes
329 from six import string_types
330 from six import text_type
331 from six.moves import xrange as six_xrange
372 "TagClassApplication",
376 "TagFormConstructed",
387 TagClassUniversal = 0
388 TagClassApplication = 1 << 6
389 TagClassContext = 1 << 7
390 TagClassPrivate = 1 << 6 | 1 << 7
392 TagFormConstructed = 1 << 5
395 TagClassApplication: "APPLICATION ",
396 TagClassPrivate: "PRIVATE ",
397 TagClassUniversal: "UNIV ",
401 ########################################################################
403 ########################################################################
405 class DecodeError(Exception):
406 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
408 :param str msg: reason of decode failing
409 :param klass: optional exact DecodeError inherited class (like
410 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
411 :py:exc:`InvalidLength`)
412 :param decode_path: tuple of strings. It contains human
413 readable names of the fields through which
414 decoding process has passed
415 :param int offset: binary offset where failure happened
417 super(DecodeError, self).__init__()
420 self.decode_path = decode_path
426 "" if self.klass is None else self.klass.__name__,
428 ("(%s)" % ".".join(self.decode_path))
429 if len(self.decode_path) > 0 else ""
431 ("(at %d)" % self.offset) if self.offset > 0 else "",
437 return "%s(%s)" % (self.__class__.__name__, self)
440 class NotEnoughData(DecodeError):
444 class TagMismatch(DecodeError):
448 class InvalidLength(DecodeError):
452 class InvalidOID(DecodeError):
456 class ObjUnknown(ValueError):
457 def __init__(self, name):
458 super(ObjUnknown, self).__init__()
462 return "object is unknown: %s" % self.name
465 return "%s(%s)" % (self.__class__.__name__, self)
468 class ObjNotReady(ValueError):
469 def __init__(self, name):
470 super(ObjNotReady, self).__init__()
474 return "object is not ready: %s" % self.name
477 return "%s(%s)" % (self.__class__.__name__, self)
480 class InvalidValueType(ValueError):
481 def __init__(self, expected_types):
482 super(InvalidValueType, self).__init__()
483 self.expected_types = expected_types
486 return "invalid value type, expected: %s" % ", ".join(
487 [repr(t) for t in self.expected_types]
491 return "%s(%s)" % (self.__class__.__name__, self)
494 class BoundsError(ValueError):
495 def __init__(self, bound_min, value, bound_max):
496 super(BoundsError, self).__init__()
497 self.bound_min = bound_min
499 self.bound_max = bound_max
502 return "unsatisfied bounds: %s <= %s <= %s" % (
509 return "%s(%s)" % (self.__class__.__name__, self)
512 ########################################################################
514 ########################################################################
516 _hexdecoder = getdecoder("hex")
517 _hexencoder = getencoder("hex")
521 """Binary data to hexadecimal string convert
523 return _hexdecoder(data)[0]
527 """Hexadecimal string to binary data convert
529 return _hexencoder(data)[0].decode("ascii")
532 def int_bytes_len(num, byte_len=8):
535 return int(ceil(float(num.bit_length()) / byte_len))
538 def zero_ended_encode(num):
539 octets = bytearray(int_bytes_len(num, 7))
541 octets[i] = num & 0x7F
545 octets[i] = 0x80 | (num & 0x7F)
551 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
552 """Encode tag to binary form
554 :param int num: tag's number
555 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
556 :py:data:`pyderasn.TagClassContext`,
557 :py:data:`pyderasn.TagClassApplication`,
558 :py:data:`pyderasn.TagClassPrivate`)
559 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
560 :py:data:`pyderasn.TagFormConstructed`)
564 return int2byte(klass | form | num)
565 # [XX|X|11111][1.......][1.......] ... [0.......]
566 return int2byte(klass | form | 31) + zero_ended_encode(num)
570 """Decode tag from binary form
574 No validation is performed, assuming that it has already passed.
576 It returns tuple with three integers, as
577 :py:func:`pyderasn.tag_encode` accepts.
579 first_octet = byte2int(tag)
580 klass = first_octet & 0xC0
581 form = first_octet & 0x20
582 if first_octet & 0x1F < 0x1F:
583 return (klass, form, first_octet & 0x1F)
585 for octet in iterbytes(tag[1:]):
588 return (klass, form, num)
592 """Create CONTEXT PRIMITIVE tag
594 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
598 """Create CONTEXT CONSTRUCTED tag
600 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
604 """Take off tag from the data
606 :returns: (encoded tag, tag length, remaining data)
609 raise NotEnoughData("no data at all")
610 if byte2int(data) & 0x1F < 31:
611 return data[:1], 1, data[1:]
616 raise DecodeError("unfinished tag")
617 if indexbytes(data, i) & 0x80 == 0:
620 return data[:i], i, data[i:]
626 octets = bytearray(int_bytes_len(l) + 1)
627 octets[0] = 0x80 | (len(octets) - 1)
628 for i in six_xrange(len(octets) - 1, 0, -1):
634 def len_decode(data):
636 raise NotEnoughData("no data at all")
637 first_octet = byte2int(data)
638 if first_octet & 0x80 == 0:
639 return first_octet, 1, data[1:]
640 octets_num = first_octet & 0x7F
641 if octets_num + 1 > len(data):
642 raise NotEnoughData("encoded length is longer than data")
644 raise DecodeError("long form instead of short one")
645 if byte2int(data[1:]) == 0:
646 raise DecodeError("leading zeros")
648 for v in iterbytes(data[1:1 + octets_num]):
651 raise DecodeError("long form instead of short one")
652 return l, 1 + octets_num, data[1 + octets_num:]
655 ########################################################################
657 ########################################################################
659 class AutoAddSlots(type):
660 def __new__(cls, name, bases, _dict):
661 _dict["__slots__"] = _dict.get("__slots__", ())
662 return type.__new__(cls, name, bases, _dict)
665 @add_metaclass(AutoAddSlots)
667 """Common ASN.1 object class
669 All ASN.1 types are inherited from it. It has metaclass that
670 automatically adds ``__slots__`` to all inherited classes.
692 self.tag = getattr(self, "impl", self.tag_default)
695 self._expl = getattr(self, "expl", None) if expl is None else expl
696 if self.tag != self.tag_default and self._expl is not None:
698 "implicit and explicit tags can not be set simultaneously"
700 if default is not None:
702 self.optional = optional
703 self.offset, self.llen, self.vlen = _decoded
707 def ready(self): # pragma: no cover
708 """Is object ready to be encoded?
710 raise NotImplementedError()
712 def _assert_ready(self):
714 raise ObjNotReady(self.__class__.__name__)
718 """Is object decoded?
720 return (self.llen + self.vlen) > 0
722 def copy(self): # pragma: no cover
723 """Make a copy of object, safe to be mutated
725 raise NotImplementedError()
733 return self.tlen + self.llen + self.vlen
735 def __str__(self): # pragma: no cover
736 return self.__bytes__() if PY2 else self.__unicode__()
738 def __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)
1659 obj._value = self._value
1661 obj._expl = self._expl
1662 obj.default = self.default
1663 obj.optional = self.optional
1664 obj.offset = self.offset
1665 obj.llen = self.llen
1666 obj.vlen = self.vlen
1670 self._assert_ready()
1671 for i in six_xrange(self._value[0]):
1676 self._assert_ready()
1677 return self._value[0]
1679 def __bytes__(self):
1680 self._assert_ready()
1681 return self._value[1]
1683 def __eq__(self, their):
1684 if isinstance(their, bytes):
1685 return self._value[1] == their
1686 if not issubclass(their.__class__, BitString):
1689 self._value == their._value and
1690 self.tag == their.tag and
1691 self._expl == their._expl
1696 return [name for name, bit in self.specs.items() if self[bit]]
1706 return self.__class__(
1708 impl=self.tag if impl is None else impl,
1709 expl=self._expl if expl is None else expl,
1710 default=self.default if default is None else default,
1711 optional=self.optional if optional is None else optional,
1715 def __getitem__(self, key):
1716 if isinstance(key, int):
1717 bit_len, octets = self._value
1721 byte2int(memoryview(octets)[key // 8:]) >>
1724 if isinstance(key, string_types):
1725 value = self.specs.get(key)
1727 raise ObjUnknown("BitString value: %s" % key)
1729 raise InvalidValueType((int, str))
1732 self._assert_ready()
1733 bit_len, octets = self._value
1736 len_encode(len(octets) + 1),
1737 int2byte((8 - bit_len % 8) % 8),
1741 def _decode(self, tlv, offset=0, decode_path=()):
1743 t, _, lv = tag_strip(tlv)
1744 except DecodeError as err:
1745 raise err.__class__(
1747 klass=self.__class__,
1748 decode_path=decode_path,
1753 klass=self.__class__,
1754 decode_path=decode_path,
1758 l, llen, v = len_decode(lv)
1759 except DecodeError as err:
1760 raise err.__class__(
1762 klass=self.__class__,
1763 decode_path=decode_path,
1767 raise NotEnoughData(
1768 "encoded length is longer than data",
1769 klass=self.__class__,
1770 decode_path=decode_path,
1774 raise NotEnoughData(
1776 klass=self.__class__,
1777 decode_path=decode_path,
1780 pad_size = byte2int(v)
1781 if l == 1 and pad_size != 0:
1783 "invalid empty value",
1784 klass=self.__class__,
1785 decode_path=decode_path,
1791 klass=self.__class__,
1792 decode_path=decode_path,
1795 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1798 klass=self.__class__,
1799 decode_path=decode_path,
1802 v, tail = v[:l], v[l:]
1803 obj = self.__class__(
1804 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1807 default=self.default,
1808 optional=self.optional,
1810 _decoded=(offset, llen, l),
1815 return pp_console_row(next(self.pps()))
1817 def pps(self, decode_path=()):
1821 bit_len, blob = self._value
1822 value = "%d bits" % bit_len
1823 if len(self.specs) > 0:
1824 blob = tuple(self.named)
1826 asn1_type_name=self.asn1_type_name,
1827 obj_name=self.__class__.__name__,
1828 decode_path=decode_path,
1831 optional=self.optional,
1832 default=self == self.default,
1833 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1834 expl=None if self._expl is None else tag_decode(self._expl),
1839 expl_offset=self.expl_offset if self.expled else None,
1840 expl_tlen=self.expl_tlen if self.expled else None,
1841 expl_llen=self.expl_llen if self.expled else None,
1842 expl_vlen=self.expl_vlen if self.expled else None,
1846 class OctetString(Obj):
1847 """``OCTET STRING`` binary string type
1849 >>> s = OctetString(b"hello world")
1850 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1851 >>> s == OctetString(b"hello world")
1856 >>> OctetString(b"hello", bounds=(4, 4))
1857 Traceback (most recent call last):
1858 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1859 >>> OctetString(b"hell", bounds=(4, 4))
1860 OCTET STRING 4 bytes 68656c6c
1862 __slots__ = ("_bound_min", "_bound_max")
1863 tag_default = tag_encode(4)
1864 asn1_type_name = "OCTET STRING"
1877 :param value: set the value. Either binary type, or
1878 :py:class:`pyderasn.OctetString` object
1879 :param bounds: set ``(MIN, MAX)`` value size constraint.
1880 (-inf, +inf) by default
1881 :param bytes impl: override default tag with ``IMPLICIT`` one
1882 :param bytes expl: override default tag with ``EXPLICIT`` one
1883 :param default: set default value. Type same as in ``value``
1884 :param bool optional: is object ``OPTIONAL`` in sequence
1886 super(OctetString, self).__init__(
1895 self._bound_min, self._bound_max = getattr(
1901 self._bound_min, self._bound_max = bounds
1902 if value is not None:
1903 self._value = self._value_sanitize(value)
1904 if default is not None:
1905 default = self._value_sanitize(default)
1906 self.default = self.__class__(
1911 if self._value is None:
1912 self._value = default
1914 def _value_sanitize(self, value):
1915 if issubclass(value.__class__, OctetString):
1916 value = value._value
1917 elif isinstance(value, binary_type):
1920 raise InvalidValueType((self.__class__, bytes))
1921 if not self._bound_min <= len(value) <= self._bound_max:
1922 raise BoundsError(self._bound_min, len(value), self._bound_max)
1927 return self._value is not None
1930 obj = self.__class__()
1931 obj._value = self._value
1932 obj._bound_min = self._bound_min
1933 obj._bound_max = self._bound_max
1935 obj._expl = self._expl
1936 obj.default = self.default
1937 obj.optional = self.optional
1938 obj.offset = self.offset
1939 obj.llen = self.llen
1940 obj.vlen = self.vlen
1943 def __bytes__(self):
1944 self._assert_ready()
1947 def __eq__(self, their):
1948 if isinstance(their, binary_type):
1949 return self._value == their
1950 if not issubclass(their.__class__, OctetString):
1953 self._value == their._value and
1954 self.tag == their.tag and
1955 self._expl == their._expl
1958 def __lt__(self, their):
1959 return self._value < their._value
1970 return self.__class__(
1973 (self._bound_min, self._bound_max)
1974 if bounds is None else bounds
1976 impl=self.tag if impl is None else impl,
1977 expl=self._expl if expl is None else expl,
1978 default=self.default if default is None else default,
1979 optional=self.optional if optional is None else optional,
1983 self._assert_ready()
1986 len_encode(len(self._value)),
1990 def _decode(self, tlv, offset=0, decode_path=()):
1992 t, _, lv = tag_strip(tlv)
1993 except DecodeError as err:
1994 raise err.__class__(
1996 klass=self.__class__,
1997 decode_path=decode_path,
2002 klass=self.__class__,
2003 decode_path=decode_path,
2007 l, llen, v = len_decode(lv)
2008 except DecodeError as err:
2009 raise err.__class__(
2011 klass=self.__class__,
2012 decode_path=decode_path,
2016 raise NotEnoughData(
2017 "encoded length is longer than data",
2018 klass=self.__class__,
2019 decode_path=decode_path,
2022 v, tail = v[:l], v[l:]
2024 obj = self.__class__(
2026 bounds=(self._bound_min, self._bound_max),
2029 default=self.default,
2030 optional=self.optional,
2031 _decoded=(offset, llen, l),
2033 except BoundsError as err:
2036 klass=self.__class__,
2037 decode_path=decode_path,
2043 return pp_console_row(next(self.pps()))
2045 def pps(self, decode_path=()):
2047 asn1_type_name=self.asn1_type_name,
2048 obj_name=self.__class__.__name__,
2049 decode_path=decode_path,
2050 value=("%d bytes" % len(self._value)) if self.ready else None,
2051 blob=self._value if self.ready else None,
2052 optional=self.optional,
2053 default=self == self.default,
2054 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2055 expl=None if self._expl is None else tag_decode(self._expl),
2060 expl_offset=self.expl_offset if self.expled else None,
2061 expl_tlen=self.expl_tlen if self.expled else None,
2062 expl_llen=self.expl_llen if self.expled else None,
2063 expl_vlen=self.expl_vlen if self.expled else None,
2068 """``NULL`` null object
2076 tag_default = tag_encode(5)
2077 asn1_type_name = "NULL"
2081 value=None, # unused, but Sequence passes it
2088 :param bytes impl: override default tag with ``IMPLICIT`` one
2089 :param bytes expl: override default tag with ``EXPLICIT`` one
2090 :param bool optional: is object ``OPTIONAL`` in sequence
2092 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2100 obj = self.__class__()
2102 obj._expl = self._expl
2103 obj.default = self.default
2104 obj.optional = self.optional
2105 obj.offset = self.offset
2106 obj.llen = self.llen
2107 obj.vlen = self.vlen
2110 def __eq__(self, their):
2111 if not issubclass(their.__class__, Null):
2114 self.tag == their.tag and
2115 self._expl == their._expl
2125 return self.__class__(
2126 impl=self.tag if impl is None else impl,
2127 expl=self._expl if expl is None else expl,
2128 optional=self.optional if optional is None else optional,
2132 return self.tag + len_encode(0)
2134 def _decode(self, tlv, offset=0, decode_path=()):
2136 t, _, lv = tag_strip(tlv)
2137 except DecodeError as err:
2138 raise err.__class__(
2140 klass=self.__class__,
2141 decode_path=decode_path,
2146 klass=self.__class__,
2147 decode_path=decode_path,
2151 l, _, v = len_decode(lv)
2152 except DecodeError as err:
2153 raise err.__class__(
2155 klass=self.__class__,
2156 decode_path=decode_path,
2160 raise InvalidLength(
2161 "Null must have zero length",
2162 klass=self.__class__,
2163 decode_path=decode_path,
2166 obj = self.__class__(
2169 optional=self.optional,
2170 _decoded=(offset, 1, 0),
2175 return pp_console_row(next(self.pps()))
2177 def pps(self, decode_path=()):
2179 asn1_type_name=self.asn1_type_name,
2180 obj_name=self.__class__.__name__,
2181 decode_path=decode_path,
2182 optional=self.optional,
2183 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2184 expl=None if self._expl is None else tag_decode(self._expl),
2189 expl_offset=self.expl_offset if self.expled else None,
2190 expl_tlen=self.expl_tlen if self.expled else None,
2191 expl_llen=self.expl_llen if self.expled else None,
2192 expl_vlen=self.expl_vlen if self.expled else None,
2196 class ObjectIdentifier(Obj):
2197 """``OBJECT IDENTIFIER`` OID type
2199 >>> oid = ObjectIdentifier((1, 2, 3))
2200 OBJECT IDENTIFIER 1.2.3
2201 >>> oid == ObjectIdentifier("1.2.3")
2207 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2208 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2210 >>> str(ObjectIdentifier((3, 1)))
2211 Traceback (most recent call last):
2212 pyderasn.InvalidOID: unacceptable first arc value
2215 tag_default = tag_encode(6)
2216 asn1_type_name = "OBJECT IDENTIFIER"
2228 :param value: set the value. Either tuples of integers,
2229 string of "."-concatenated integers, or
2230 :py:class:`pyderasn.ObjectIdentifier` object
2231 :param bytes impl: override default tag with ``IMPLICIT`` one
2232 :param bytes expl: override default tag with ``EXPLICIT`` one
2233 :param default: set default value. Type same as in ``value``
2234 :param bool optional: is object ``OPTIONAL`` in sequence
2236 super(ObjectIdentifier, self).__init__(
2244 if value is not None:
2245 self._value = self._value_sanitize(value)
2246 if default is not None:
2247 default = self._value_sanitize(default)
2248 self.default = self.__class__(
2253 if self._value is None:
2254 self._value = default
2256 def __add__(self, their):
2257 if isinstance(their, self.__class__):
2258 return self.__class__(self._value + their._value)
2259 if isinstance(their, tuple):
2260 return self.__class__(self._value + their)
2261 raise InvalidValueType((self.__class__, tuple))
2263 def _value_sanitize(self, value):
2264 if issubclass(value.__class__, ObjectIdentifier):
2266 if isinstance(value, string_types):
2268 value = tuple(int(arc) for arc in value.split("."))
2270 raise InvalidOID("unacceptable arcs values")
2271 if isinstance(value, tuple):
2273 raise InvalidOID("less than 2 arcs")
2274 first_arc = value[0]
2275 if first_arc in (0, 1):
2276 if not (0 <= value[1] <= 39):
2277 raise InvalidOID("second arc is too wide")
2278 elif first_arc == 2:
2281 raise InvalidOID("unacceptable first arc value")
2283 raise InvalidValueType((self.__class__, str, tuple))
2287 return self._value is not None
2290 obj = self.__class__()
2291 obj._value = self._value
2293 obj._expl = self._expl
2294 obj.default = self.default
2295 obj.optional = self.optional
2296 obj.offset = self.offset
2297 obj.llen = self.llen
2298 obj.vlen = self.vlen
2302 self._assert_ready()
2303 return iter(self._value)
2306 return ".".join(str(arc) for arc in self._value or ())
2309 self._assert_ready()
2312 bytes(self._expl or b"") +
2313 str(self._value).encode("ascii"),
2316 def __eq__(self, their):
2317 if isinstance(their, tuple):
2318 return self._value == their
2319 if not issubclass(their.__class__, ObjectIdentifier):
2322 self.tag == their.tag and
2323 self._expl == their._expl and
2324 self._value == their._value
2327 def __lt__(self, their):
2328 return self._value < their._value
2338 return self.__class__(
2340 impl=self.tag if impl is None else impl,
2341 expl=self._expl if expl is None else expl,
2342 default=self.default if default is None else default,
2343 optional=self.optional if optional is None else optional,
2347 self._assert_ready()
2349 first_value = value[1]
2350 first_arc = value[0]
2353 elif first_arc == 1:
2355 elif first_arc == 2:
2357 else: # pragma: no cover
2358 raise RuntimeError("invalid arc is stored")
2359 octets = [zero_ended_encode(first_value)]
2360 for arc in value[2:]:
2361 octets.append(zero_ended_encode(arc))
2362 v = b"".join(octets)
2363 return b"".join((self.tag, len_encode(len(v)), v))
2365 def _decode(self, tlv, offset=0, decode_path=()):
2367 t, _, lv = tag_strip(tlv)
2368 except DecodeError as err:
2369 raise err.__class__(
2371 klass=self.__class__,
2372 decode_path=decode_path,
2377 klass=self.__class__,
2378 decode_path=decode_path,
2382 l, llen, v = len_decode(lv)
2383 except DecodeError as err:
2384 raise err.__class__(
2386 klass=self.__class__,
2387 decode_path=decode_path,
2391 raise NotEnoughData(
2392 "encoded length is longer than data",
2393 klass=self.__class__,
2394 decode_path=decode_path,
2398 raise NotEnoughData(
2400 klass=self.__class__,
2401 decode_path=decode_path,
2404 v, tail = v[:l], v[l:]
2410 octet = indexbytes(v, i)
2411 arc = (arc << 7) | (octet & 0x7F)
2412 if octet & 0x80 == 0:
2420 klass=self.__class__,
2421 decode_path=decode_path,
2425 second_arc = arcs[0]
2426 if 0 <= second_arc <= 39:
2428 elif 40 <= second_arc <= 79:
2434 obj = self.__class__(
2435 value=tuple([first_arc, second_arc] + arcs[1:]),
2438 default=self.default,
2439 optional=self.optional,
2440 _decoded=(offset, llen, l),
2445 return pp_console_row(next(self.pps()))
2447 def pps(self, decode_path=()):
2449 asn1_type_name=self.asn1_type_name,
2450 obj_name=self.__class__.__name__,
2451 decode_path=decode_path,
2452 value=str(self) if self.ready else None,
2453 optional=self.optional,
2454 default=self == self.default,
2455 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2456 expl=None if self._expl is None else tag_decode(self._expl),
2461 expl_offset=self.expl_offset if self.expled else None,
2462 expl_tlen=self.expl_tlen if self.expled else None,
2463 expl_llen=self.expl_llen if self.expled else None,
2464 expl_vlen=self.expl_vlen if self.expled else None,
2468 class Enumerated(Integer):
2469 """``ENUMERATED`` integer type
2471 This type is identical to :py:class:`pyderasn.Integer`, but requires
2472 schema to be specified and does not accept values missing from it.
2475 tag_default = tag_encode(10)
2476 asn1_type_name = "ENUMERATED"
2487 bounds=None, # dummy argument, workability for Integer.decode
2489 super(Enumerated, self).__init__(
2498 if len(self.specs) == 0:
2499 raise ValueError("schema must be specified")
2501 def _value_sanitize(self, value):
2502 if isinstance(value, self.__class__):
2503 value = value._value
2504 elif isinstance(value, integer_types):
2505 if value not in list(self.specs.values()):
2507 "unknown integer value: %s" % value,
2508 klass=self.__class__,
2510 elif isinstance(value, string_types):
2511 value = self.specs.get(value)
2513 raise ObjUnknown("integer value: %s" % value)
2515 raise InvalidValueType((self.__class__, int, str))
2519 obj = self.__class__(_specs=self.specs)
2520 obj._value = self._value
2521 obj._bound_min = self._bound_min
2522 obj._bound_max = self._bound_max
2524 obj._expl = self._expl
2525 obj.default = self.default
2526 obj.optional = self.optional
2527 obj.offset = self.offset
2528 obj.llen = self.llen
2529 obj.vlen = self.vlen
2541 return self.__class__(
2543 impl=self.tag if impl is None else impl,
2544 expl=self._expl if expl is None else expl,
2545 default=self.default if default is None else default,
2546 optional=self.optional if optional is None else optional,
2551 class CommonString(OctetString):
2552 """Common class for all strings
2554 Everything resembles :py:class:`pyderasn.OctetString`, except
2555 ability to deal with unicode text strings.
2557 >>> hexenc("привет мир".encode("utf-8"))
2558 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2559 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2561 >>> s = UTF8String("привет мир")
2562 UTF8String UTF8String привет мир
2564 'привет мир'
2565 >>> hexenc(bytes(s))
2566 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2568 >>> PrintableString("привет мир")
2569 Traceback (most recent call last):
2570 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2572 >>> BMPString("ада", bounds=(2, 2))
2573 Traceback (most recent call last):
2574 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2575 >>> s = BMPString("ад", bounds=(2, 2))
2578 >>> hexenc(bytes(s))
2586 * - :py:class:`pyderasn.UTF8String`
2588 * - :py:class:`pyderasn.NumericString`
2590 * - :py:class:`pyderasn.PrintableString`
2592 * - :py:class:`pyderasn.TeletexString`
2594 * - :py:class:`pyderasn.T61String`
2596 * - :py:class:`pyderasn.VideotexString`
2598 * - :py:class:`pyderasn.IA5String`
2600 * - :py:class:`pyderasn.GraphicString`
2602 * - :py:class:`pyderasn.VisibleString`
2604 * - :py:class:`pyderasn.ISO646String`
2606 * - :py:class:`pyderasn.GeneralString`
2608 * - :py:class:`pyderasn.UniversalString`
2610 * - :py:class:`pyderasn.BMPString`
2613 __slots__ = ("encoding",)
2615 def _value_sanitize(self, value):
2617 value_decoded = None
2618 if isinstance(value, self.__class__):
2619 value_raw = value._value
2620 elif isinstance(value, text_type):
2621 value_decoded = value
2622 elif isinstance(value, binary_type):
2625 raise InvalidValueType((self.__class__, text_type, binary_type))
2627 value_decoded.encode(self.encoding)
2628 if value_raw is None else value_raw
2631 value_raw.decode(self.encoding)
2632 if value_decoded is None else value_decoded
2634 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2642 def __eq__(self, their):
2643 if isinstance(their, binary_type):
2644 return self._value == their
2645 if isinstance(their, text_type):
2646 return self._value == their.encode(self.encoding)
2647 if not isinstance(their, self.__class__):
2650 self._value == their._value and
2651 self.tag == their.tag and
2652 self._expl == their._expl
2655 def __unicode__(self):
2657 return self._value.decode(self.encoding)
2658 return text_type(self._value)
2661 return pp_console_row(next(self.pps(no_unicode=PY2)))
2663 def pps(self, decode_path=(), no_unicode=False):
2666 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2668 asn1_type_name=self.asn1_type_name,
2669 obj_name=self.__class__.__name__,
2670 decode_path=decode_path,
2672 optional=self.optional,
2673 default=self == self.default,
2674 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2675 expl=None if self._expl is None else tag_decode(self._expl),
2683 class UTF8String(CommonString):
2685 tag_default = tag_encode(12)
2687 asn1_type_name = "UTF8String"
2690 class NumericString(CommonString):
2692 tag_default = tag_encode(18)
2694 asn1_type_name = "NumericString"
2697 class PrintableString(CommonString):
2699 tag_default = tag_encode(19)
2701 asn1_type_name = "PrintableString"
2704 class TeletexString(CommonString):
2706 tag_default = tag_encode(20)
2708 asn1_type_name = "TeletexString"
2711 class T61String(TeletexString):
2713 asn1_type_name = "T61String"
2716 class VideotexString(CommonString):
2718 tag_default = tag_encode(21)
2719 encoding = "iso-8859-1"
2720 asn1_type_name = "VideotexString"
2723 class IA5String(CommonString):
2725 tag_default = tag_encode(22)
2727 asn1_type_name = "IA5"
2730 class UTCTime(CommonString):
2731 """``UTCTime`` datetime type
2733 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2734 UTCTime UTCTime 2017-09-30T22:07:50
2740 datetime.datetime(2017, 9, 30, 22, 7, 50)
2741 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2742 datetime.datetime(1957, 9, 30, 22, 7, 50)
2745 tag_default = tag_encode(23)
2747 asn1_type_name = "UTCTime"
2749 fmt = "%y%m%d%H%M%SZ"
2759 bounds=None, # dummy argument, workability for OctetString.decode
2762 :param value: set the value. Either datetime type, or
2763 :py:class:`pyderasn.UTCTime` object
2764 :param bytes impl: override default tag with ``IMPLICIT`` one
2765 :param bytes expl: override default tag with ``EXPLICIT`` one
2766 :param default: set default value. Type same as in ``value``
2767 :param bool optional: is object ``OPTIONAL`` in sequence
2769 super(UTCTime, self).__init__(
2777 if value is not None:
2778 self._value = self._value_sanitize(value)
2779 if default is not None:
2780 default = self._value_sanitize(default)
2781 self.default = self.__class__(
2786 if self._value is None:
2787 self._value = default
2789 def _value_sanitize(self, value):
2790 if isinstance(value, self.__class__):
2792 if isinstance(value, datetime):
2793 return value.strftime(self.fmt).encode("ascii")
2794 if isinstance(value, binary_type):
2795 value_decoded = value.decode("ascii")
2796 if len(value_decoded) == 2 + 2 + 2 + 2 + 2 + 2 + 1:
2798 datetime.strptime(value_decoded, self.fmt)
2800 raise DecodeError("invalid UTCTime format")
2803 raise DecodeError("invalid UTCTime length")
2804 raise InvalidValueType((self.__class__, datetime))
2806 def __eq__(self, their):
2807 if isinstance(their, binary_type):
2808 return self._value == their
2809 if isinstance(their, datetime):
2810 return self.todatetime() == their
2811 if not isinstance(their, self.__class__):
2814 self._value == their._value and
2815 self.tag == their.tag and
2816 self._expl == their._expl
2819 def todatetime(self):
2820 """Convert to datetime
2824 Pay attention that UTCTime can not hold full year, so all years
2825 having < 50 years are treated as 20xx, 19xx otherwise, according
2826 to X.509 recomendation.
2828 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2829 year = value.year % 100
2831 year=(2000 + year) if year < 50 else (1900 + year),
2835 minute=value.minute,
2836 second=value.second,
2840 return pp_console_row(next(self.pps()))
2842 def pps(self, decode_path=()):
2844 asn1_type_name=self.asn1_type_name,
2845 obj_name=self.__class__.__name__,
2846 decode_path=decode_path,
2847 value=self.todatetime().isoformat() if self.ready else None,
2848 optional=self.optional,
2849 default=self == self.default,
2850 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2851 expl=None if self._expl is None else tag_decode(self._expl),
2859 class GeneralizedTime(UTCTime):
2860 """``GeneralizedTime`` datetime type
2862 This type is similar to :py:class:`pyderasn.UTCTime`.
2864 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2865 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
2867 '20170930220750.000123Z'
2868 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
2869 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
2872 tag_default = tag_encode(24)
2873 asn1_type_name = "GeneralizedTime"
2875 fmt = "%Y%m%d%H%M%SZ"
2876 fmt_ms = "%Y%m%d%H%M%S.%fZ"
2878 def _value_sanitize(self, value):
2879 if isinstance(value, self.__class__):
2881 if isinstance(value, datetime):
2882 return value.strftime(
2883 self.fmt_ms if value.microsecond > 0 else self.fmt
2885 if isinstance(value, binary_type):
2886 value_decoded = value.decode("ascii")
2887 if len(value_decoded) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2889 datetime.strptime(value_decoded, self.fmt)
2892 "invalid GeneralizedTime (without ms) format",
2895 elif len(value_decoded) >= 4 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 1:
2897 datetime.strptime(value_decoded, self.fmt_ms)
2900 "invalid GeneralizedTime (with ms) format",
2905 "invalid GeneralizedTime length",
2906 klass=self.__class__,
2908 raise InvalidValueType((self.__class__, datetime))
2910 def todatetime(self):
2911 value = self._value.decode("ascii")
2912 if len(value) == 4 + 2 + 2 + 2 + 2 + 2 + 1:
2913 return datetime.strptime(value, self.fmt)
2914 return datetime.strptime(value, self.fmt_ms)
2917 class GraphicString(CommonString):
2919 tag_default = tag_encode(25)
2920 encoding = "iso-8859-1"
2921 asn1_type_name = "GraphicString"
2924 class VisibleString(CommonString):
2926 tag_default = tag_encode(26)
2928 asn1_type_name = "VisibleString"
2931 class ISO646String(VisibleString):
2933 asn1_type_name = "ISO646String"
2936 class GeneralString(CommonString):
2938 tag_default = tag_encode(27)
2939 encoding = "iso-8859-1"
2940 asn1_type_name = "GeneralString"
2943 class UniversalString(CommonString):
2945 tag_default = tag_encode(28)
2946 encoding = "utf-32-be"
2947 asn1_type_name = "UniversalString"
2950 class BMPString(CommonString):
2952 tag_default = tag_encode(30)
2953 encoding = "utf-16-be"
2954 asn1_type_name = "BMPString"
2958 """``CHOICE`` special type
2962 class GeneralName(Choice):
2964 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
2965 ('dNSName', IA5String(impl=tag_ctxp(2))),
2968 >>> gn = GeneralName()
2970 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
2971 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2972 >>> gn["dNSName"] = IA5String("bar.baz")
2973 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
2974 >>> gn["rfc822Name"]
2977 [2] IA5String IA5 bar.baz
2980 >>> gn.value == gn["dNSName"]
2983 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
2985 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
2986 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
2988 __slots__ = ("specs",)
2990 asn1_type_name = "CHOICE"
3003 :param value: set the value. Either ``(choice, value)`` tuple, or
3004 :py:class:`pyderasn.Choice` object
3005 :param bytes impl: can not be set, do **not** use it
3006 :param bytes expl: override default tag with ``EXPLICIT`` one
3007 :param default: set default value. Type same as in ``value``
3008 :param bool optional: is object ``OPTIONAL`` in sequence
3010 if impl is not None:
3011 raise ValueError("no implicit tag allowed for CHOICE")
3012 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3014 schema = getattr(self, "schema", ())
3015 if len(schema) == 0:
3016 raise ValueError("schema must be specified")
3018 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3021 if value is not None:
3022 self._value = self._value_sanitize(value)
3023 if default is not None:
3024 default_value = self._value_sanitize(default)
3025 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3026 default_obj.specs = self.specs
3027 default_obj._value = default_value
3028 self.default = default_obj
3030 self._value = default_obj.copy()._value
3032 def _value_sanitize(self, value):
3033 if isinstance(value, self.__class__):
3035 if isinstance(value, tuple) and len(value) == 2:
3037 spec = self.specs.get(choice)
3039 raise ObjUnknown(choice)
3040 if not isinstance(obj, spec.__class__):
3041 raise InvalidValueType((spec,))
3042 return (choice, spec(obj))
3043 raise InvalidValueType((self.__class__, tuple))
3047 return self._value is not None and self._value[1].ready
3050 obj = self.__class__(schema=self.specs)
3051 obj._expl = self._expl
3052 obj.default = self.default
3053 obj.optional = self.optional
3054 obj.offset = self.offset
3055 obj.llen = self.llen
3056 obj.vlen = self.vlen
3058 if value is not None:
3059 obj._value = (value[0], value[1].copy())
3062 def __eq__(self, their):
3063 if isinstance(their, tuple) and len(their) == 2:
3064 return self._value == their
3065 if not isinstance(their, self.__class__):
3068 self.specs == their.specs and
3069 self._value == their._value
3079 return self.__class__(
3082 expl=self._expl if expl is None else expl,
3083 default=self.default if default is None else default,
3084 optional=self.optional if optional is None else optional,
3089 self._assert_ready()
3090 return self._value[0]
3094 self._assert_ready()
3095 return self._value[1]
3097 def __getitem__(self, key):
3098 if key not in self.specs:
3099 raise ObjUnknown(key)
3100 if self._value is None:
3102 choice, value = self._value
3107 def __setitem__(self, key, value):
3108 spec = self.specs.get(key)
3110 raise ObjUnknown(key)
3111 if not isinstance(value, spec.__class__):
3112 raise InvalidValueType((spec.__class__,))
3113 self._value = (key, spec(value))
3121 return self._value[1].decoded if self.ready else False
3124 self._assert_ready()
3125 return self._value[1].encode()
3127 def _decode(self, tlv, offset=0, decode_path=()):
3128 for choice, spec in self.specs.items():
3130 value, tail = spec.decode(
3134 decode_path=decode_path + (choice,),
3138 obj = self.__class__(
3141 default=self.default,
3142 optional=self.optional,
3143 _decoded=(offset, 0, value.tlvlen),
3145 obj._value = (choice, value)
3148 klass=self.__class__,
3149 decode_path=decode_path,
3154 value = pp_console_row(next(self.pps()))
3156 value = "%s[%r]" % (value, self.value)
3159 def pps(self, decode_path=()):
3161 asn1_type_name=self.asn1_type_name,
3162 obj_name=self.__class__.__name__,
3163 decode_path=decode_path,
3164 value=self.choice if self.ready else None,
3165 optional=self.optional,
3166 default=self == self.default,
3167 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3168 expl=None if self._expl is None else tag_decode(self._expl),
3175 yield self.value.pps(decode_path=decode_path + (self.choice,))
3178 class PrimitiveTypes(Choice):
3179 """Predefined ``CHOICE`` for all generic primitive types
3181 It could be useful for general decoding of some unspecified values:
3183 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3184 OCTET STRING 3 bytes 666f6f
3185 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3189 schema = tuple((klass.__name__, klass()) for klass in (
3214 """``ANY`` special type
3216 >>> Any(Integer(-123))
3218 >>> a = Any(OctetString(b"hello world").encode())
3219 ANY 040b68656c6c6f20776f726c64
3220 >>> hexenc(bytes(a))
3221 b'0x040x0bhello world'
3224 tag_default = tag_encode(0)
3225 asn1_type_name = "ANY"
3235 :param value: set the value. Either any kind of pyderasn's
3236 **ready** object, or bytes. Pay attention that
3237 **no** validation is performed is raw binary value
3239 :param bytes expl: override default tag with ``EXPLICIT`` one
3240 :param bool optional: is object ``OPTIONAL`` in sequence
3242 super(Any, self).__init__(None, expl, None, optional, _decoded)
3243 self._value = None if value is None else self._value_sanitize(value)
3245 def _value_sanitize(self, value):
3246 if isinstance(value, self.__class__):
3248 if isinstance(value, Obj):
3249 return value.encode()
3250 if isinstance(value, binary_type):
3252 raise InvalidValueType((self.__class__, Obj, binary_type))
3256 return self._value is not None
3259 obj = self.__class__()
3260 obj._value = self._value
3262 obj._expl = self._expl
3263 obj.optional = self.optional
3264 obj.offset = self.offset
3265 obj.llen = self.llen
3266 obj.vlen = self.vlen
3269 def __eq__(self, their):
3270 if isinstance(their, binary_type):
3271 return self._value == their
3272 if issubclass(their.__class__, Any):
3273 return self._value == their._value
3282 return self.__class__(
3284 expl=self._expl if expl is None else expl,
3285 optional=self.optional if optional is None else optional,
3288 def __bytes__(self):
3289 self._assert_ready()
3297 self._assert_ready()
3300 def _decode(self, tlv, offset=0, decode_path=()):
3302 t, tlen, lv = tag_strip(tlv)
3303 l, llen, v = len_decode(lv)
3304 except DecodeError as err:
3305 raise err.__class__(
3307 klass=self.__class__,
3308 decode_path=decode_path,
3312 raise NotEnoughData(
3313 "encoded length is longer than data",
3314 klass=self.__class__,
3315 decode_path=decode_path,
3318 tlvlen = tlen + llen + l
3319 v, tail = tlv[:tlvlen], v[l:]
3320 obj = self.__class__(
3323 optional=self.optional,
3324 _decoded=(offset, 0, tlvlen),
3330 return pp_console_row(next(self.pps()))
3332 def pps(self, decode_path=()):
3334 asn1_type_name=self.asn1_type_name,
3335 obj_name=self.__class__.__name__,
3336 decode_path=decode_path,
3337 blob=self._value if self.ready else None,
3338 optional=self.optional,
3339 default=self == self.default,
3340 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3341 expl=None if self._expl is None else tag_decode(self._expl),
3346 expl_offset=self.expl_offset if self.expled else None,
3347 expl_tlen=self.expl_tlen if self.expled else None,
3348 expl_llen=self.expl_llen if self.expled else None,
3349 expl_vlen=self.expl_vlen if self.expled else None,
3353 ########################################################################
3354 # ASN.1 constructed types
3355 ########################################################################
3357 class Sequence(Obj):
3358 """``SEQUENCE`` structure type
3360 You have to make specification of sequence::
3362 class Extension(Sequence):
3365 ("extnID", ObjectIdentifier()),
3366 ("critical", Boolean(default=False)),
3367 ("extnValue", OctetString()),
3370 Then, you can work with it as with dictionary.
3372 >>> ext = Extension()
3373 >>> Extension().specs
3375 ('extnID', OBJECT IDENTIFIER),
3376 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3377 ('extnValue', OCTET STRING),
3379 >>> ext["extnID"] = "1.2.3"
3380 Traceback (most recent call last):
3381 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3382 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3384 You can know if sequence is ready to be encoded:
3389 Traceback (most recent call last):
3390 pyderasn.ObjNotReady: object is not ready: extnValue
3391 >>> ext["extnValue"] = OctetString(b"foobar")
3395 Value you want to assign, must have the same **type** as in
3396 corresponding specification, but it can have different tags,
3397 optional/default attributes -- they will be taken from specification
3400 class TBSCertificate(Sequence):
3402 ("version", Version(expl=tag_ctxc(0), default="v1")),
3405 >>> tbs = TBSCertificate()
3406 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3408 You can know if value exists/set in the sequence and take its value:
3410 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3413 OBJECT IDENTIFIER 1.2.3
3415 But pay attention that if value has default, then it won't be (not
3416 in) in the sequence (because ``DEFAULT`` must not be encoded in
3417 DER), but you can read its value:
3419 >>> "critical" in ext, ext["critical"]
3420 (False, BOOLEAN False)
3421 >>> ext["critical"] = Boolean(True)
3422 >>> "critical" in ext, ext["critical"]
3423 (True, BOOLEAN True)
3425 All defaulted values are always optional.
3429 When decoded DER contains defaulted value inside, then
3430 technically this is not valid DER encoding. But we allow
3431 and pass it. Of course reencoding of that kind of DER will
3432 result in different binary representation (validly without
3433 defaulted value inside).
3435 Two sequences are equal if they have equal specification (schema),
3436 implicit/explicit tagging and the same values.
3438 __slots__ = ("specs",)
3439 tag_default = tag_encode(form=TagFormConstructed, num=16)
3440 asn1_type_name = "SEQUENCE"
3452 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3454 schema = getattr(self, "schema", ())
3456 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3459 if value is not None:
3460 self._value = self._value_sanitize(value)
3461 if default is not None:
3462 default_value = self._value_sanitize(default)
3463 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3464 default_obj.specs = self.specs
3465 default_obj._value = default_value
3466 self.default = default_obj
3468 self._value = default_obj.copy()._value
3470 def _value_sanitize(self, value):
3471 if not issubclass(value.__class__, Sequence):
3472 raise InvalidValueType((Sequence,))
3477 for name, spec in self.specs.items():
3478 value = self._value.get(name)
3489 obj = self.__class__(schema=self.specs)
3491 obj._expl = self._expl
3492 obj.default = self.default
3493 obj.optional = self.optional
3494 obj.offset = self.offset
3495 obj.llen = self.llen
3496 obj.vlen = self.vlen
3497 obj._value = {k: v.copy() for k, v in self._value.items()}
3500 def __eq__(self, their):
3501 if not isinstance(their, self.__class__):
3504 self.specs == their.specs and
3505 self.tag == their.tag and
3506 self._expl == their._expl and
3507 self._value == their._value
3518 return self.__class__(
3521 impl=self.tag if impl is None else impl,
3522 expl=self._expl if expl is None else expl,
3523 default=self.default if default is None else default,
3524 optional=self.optional if optional is None else optional,
3527 def __contains__(self, key):
3528 return key in self._value
3530 def __setitem__(self, key, value):
3531 spec = self.specs.get(key)
3533 raise ObjUnknown(key)
3535 self._value.pop(key, None)
3537 if not isinstance(value, spec.__class__):
3538 raise InvalidValueType((spec.__class__,))
3539 value = spec(value=value)
3540 if spec.default is not None and value == spec.default:
3541 self._value.pop(key, None)
3543 self._value[key] = value
3545 def __getitem__(self, key):
3546 value = self._value.get(key)
3547 if value is not None:
3549 spec = self.specs.get(key)
3551 raise ObjUnknown(key)
3552 if spec.default is not None:
3556 def _encoded_values(self):
3558 for name, spec in self.specs.items():
3559 value = self._value.get(name)
3563 raise ObjNotReady(name)
3564 raws.append(value.encode())
3568 v = b"".join(self._encoded_values())
3569 return b"".join((self.tag, len_encode(len(v)), v))
3571 def _decode(self, tlv, offset=0, decode_path=()):
3573 t, tlen, lv = tag_strip(tlv)
3574 except DecodeError as err:
3575 raise err.__class__(
3577 klass=self.__class__,
3578 decode_path=decode_path,
3583 klass=self.__class__,
3584 decode_path=decode_path,
3588 l, llen, v = len_decode(lv)
3589 except DecodeError as err:
3590 raise err.__class__(
3592 klass=self.__class__,
3593 decode_path=decode_path,
3597 raise NotEnoughData(
3598 "encoded length is longer than data",
3599 klass=self.__class__,
3600 decode_path=decode_path,
3603 v, tail = v[:l], v[l:]
3604 sub_offset = offset + tlen + llen
3606 for name, spec in self.specs.items():
3607 if len(v) == 0 and spec.optional:
3610 value, v_tail = spec.decode(
3614 decode_path=decode_path + (name,),
3620 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3622 if spec.default is not None and value == spec.default:
3623 # Encoded default values are not valid in DER,
3624 # but we still allow that
3626 values[name] = value
3630 klass=self.__class__,
3631 decode_path=decode_path,
3634 obj = self.__class__(
3638 default=self.default,
3639 optional=self.optional,
3640 _decoded=(offset, llen, l),
3646 value = pp_console_row(next(self.pps()))
3648 for name in self.specs:
3649 _value = self._value.get(name)
3652 cols.append(repr(_value))
3653 return "%s[%s]" % (value, ", ".join(cols))
3655 def pps(self, decode_path=()):
3657 asn1_type_name=self.asn1_type_name,
3658 obj_name=self.__class__.__name__,
3659 decode_path=decode_path,
3660 optional=self.optional,
3661 default=self == self.default,
3662 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3663 expl=None if self._expl is None else tag_decode(self._expl),
3668 expl_offset=self.expl_offset if self.expled else None,
3669 expl_tlen=self.expl_tlen if self.expled else None,
3670 expl_llen=self.expl_llen if self.expled else None,
3671 expl_vlen=self.expl_vlen if self.expled else None,
3673 for name in self.specs:
3674 value = self._value.get(name)
3677 yield value.pps(decode_path=decode_path + (name,))
3680 class Set(Sequence):
3681 """``SET`` structure type
3683 Its usage is identical to :py:class:`pyderasn.Sequence`.
3686 tag_default = tag_encode(form=TagFormConstructed, num=17)
3687 asn1_type_name = "SET"
3690 raws = self._encoded_values()
3693 return b"".join((self.tag, len_encode(len(v)), v))
3695 def _decode(self, tlv, offset=0, decode_path=()):
3697 t, tlen, lv = tag_strip(tlv)
3698 except DecodeError as err:
3699 raise err.__class__(
3701 klass=self.__class__,
3702 decode_path=decode_path,
3707 klass=self.__class__,
3708 decode_path=decode_path,
3712 l, llen, v = len_decode(lv)
3713 except DecodeError as err:
3714 raise err.__class__(
3716 klass=self.__class__,
3717 decode_path=decode_path,
3721 raise NotEnoughData(
3722 "encoded length is longer than data",
3723 klass=self.__class__,
3726 v, tail = v[:l], v[l:]
3727 sub_offset = offset + tlen + llen
3729 specs_items = self.specs.items
3731 for name, spec in specs_items():
3733 value, v_tail = spec.decode(
3737 decode_path=decode_path + (name,),
3742 value.expl_tlvlen if value.expled else value.tlvlen
3745 if spec.default is None or value != spec.default: # pragma: no cover
3746 # SeqMixing.test_encoded_default_accepted covers that place
3747 values[name] = value
3751 klass=self.__class__,
3752 decode_path=decode_path,
3755 obj = self.__class__(
3759 default=self.default,
3760 optional=self.optional,
3761 _decoded=(offset, llen, l),
3767 class SequenceOf(Obj):
3768 """``SEQUENCE OF`` sequence type
3770 For that kind of type you must specify the object it will carry on
3771 (bounds are for example here, not required)::