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 take
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 objects of the same type, but with different implicit/explicit tags
94 You can get object's 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
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
203 All objects have ``pps()`` method, that is a generator of
204 :py:class:`pyderasn.PP` namedtuple, holding various raw information
205 about the object. If ``pps`` is called on sequences, then all underlying
206 ``PP`` will be yielded.
208 You can use :py:func:`pyderasn.pp_console_row` function, converting
209 those ``PP`` to human readable string. Actually exactly it is used for
210 all object ``repr``. But it is easy to write custom formatters.
212 >>> from pyderasn import pprint
213 >>> encoded = Integer(-12345).encode()
214 >>> obj, tail = Integer().decode(encoded)
215 >>> print(pprint(obj))
216 0 [1,1, 2] INTEGER -12345
223 ASN.1 structures often have ANY and OCTET STRING fields, that are
224 DEFINED BY some previously met ObjectIdentifier. This library provides
225 ability to specify mapping between some OID and field that must be
226 decoded with specific specification.
231 :py:class:`pyderasn.ObjectIdentifier` field inside
232 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
233 necessary for decoding structrures. For example, CMS (:rfc:`5652`)
236 class ContentInfo(Sequence):
238 ("contentType", ContentType(defines=("content", {
239 id_digestedData: DigestedData(),
240 id_signedData: SignedData(),
242 ("content", Any(expl=tag_ctxc(0))),
245 ``contentType`` field tells that it defines that ``content`` must be
246 decoded with ``SignedData`` specification, if ``contentType`` equals to
247 ``id-signedData``. The same applies to ``DigestedData``. If
248 ``contentType`` contains unknown OID, then no automatic decoding is
251 Following types can be automatically decoded (DEFINED BY):
253 * :py:class:`pyderasn.Any`
254 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
255 * :py:class:`pyderasn.OctetString`
256 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
257 ``Any``/``OctetString``-s
259 When any of those fields is automatically decoded, then ``.defined``
260 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
261 was defined, ``value`` contains corresponding decoded value. For example
262 above, ``content_info["content"].defined == (id_signedData,
265 .. _defines_by_path_kwarg:
267 defines_by_path kwarg
268 _____________________
270 Sometimes you either can not or do not want to explicitly set *defines*
271 in the scheme. You can dynamically apply those definitions when calling
272 ``.decode()`` method.
274 Decode method takes optional ``defines_by_path`` keyword argument that
275 must be sequence of following tuples::
277 (decode_path, defines)
279 where ``decode_path`` is a tuple holding so-called decode path to the
280 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
281 ``defines``, holding exactly the same value as accepted in its keyword
284 For example, again for CMS, you want to automatically decode
285 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
286 structures it may hold. Also, automatically decode ``controlSequence``
289 content_info, tail = ContentInfo().decode(data, defines_by_path=(
292 ("content", {id_signedData: SignedData()}),
297 decode_path_defby(id_signedData),
302 id_cct_PKIData: PKIData(),
303 id_cct_PKIResponse: PKIResponse(),
309 decode_path_defby(id_signedData),
312 decode_path_defby(id_cct_PKIResponse),
318 id_cmc_recipientNonce: RecipientNonce(),
319 id_cmc_senderNonce: SenderNonce(),
320 id_cmc_statusInfoV2: CMCStatusInfoV2(),
321 id_cmc_transactionId: TransactionId(),
326 Pay attention for :py:func:`pyderasn.decode_path_defby` and ``any``.
327 First function is useful for path construction when some automatic
328 decoding is already done. ``any`` means literally any value it meet --
329 useful for SEQUENCE/SET OF-s.
336 .. autoclass:: pyderasn.Boolean
341 .. autoclass:: pyderasn.Integer
346 .. autoclass:: pyderasn.BitString
351 .. autoclass:: pyderasn.OctetString
356 .. autoclass:: pyderasn.Null
361 .. autoclass:: pyderasn.ObjectIdentifier
366 .. autoclass:: pyderasn.Enumerated
370 .. autoclass:: pyderasn.CommonString
374 .. autoclass:: pyderasn.UTCTime
375 :members: __init__, todatetime
379 .. autoclass:: pyderasn.GeneralizedTime
386 .. autoclass:: pyderasn.Choice
391 .. autoclass:: PrimitiveTypes
395 .. autoclass:: pyderasn.Any
403 .. autoclass:: pyderasn.Sequence
408 .. autoclass:: pyderasn.Set
413 .. autoclass:: pyderasn.SequenceOf
418 .. autoclass:: pyderasn.SetOf
424 .. autofunction:: pyderasn.hexenc
425 .. autofunction:: pyderasn.hexdec
426 .. autofunction:: pyderasn.tag_encode
427 .. autofunction:: pyderasn.tag_decode
428 .. autofunction:: pyderasn.tag_ctxp
429 .. autofunction:: pyderasn.tag_ctxc
430 .. autoclass:: pyderasn.Obj
433 from codecs import getdecoder
434 from codecs import getencoder
435 from collections import namedtuple
436 from collections import OrderedDict
437 from datetime import datetime
438 from math import ceil
440 from six import add_metaclass
441 from six import binary_type
442 from six import byte2int
443 from six import indexbytes
444 from six import int2byte
445 from six import integer_types
446 from six import iterbytes
448 from six import string_types
449 from six import text_type
450 from six.moves import xrange as six_xrange
492 "TagClassApplication",
496 "TagFormConstructed",
507 TagClassUniversal = 0
508 TagClassApplication = 1 << 6
509 TagClassContext = 1 << 7
510 TagClassPrivate = 1 << 6 | 1 << 7
512 TagFormConstructed = 1 << 5
515 TagClassApplication: "APPLICATION ",
516 TagClassPrivate: "PRIVATE ",
517 TagClassUniversal: "UNIV ",
521 ########################################################################
523 ########################################################################
525 class DecodeError(Exception):
526 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
528 :param str msg: reason of decode failing
529 :param klass: optional exact DecodeError inherited class (like
530 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
531 :py:exc:`InvalidLength`)
532 :param decode_path: tuple of strings. It contains human
533 readable names of the fields through which
534 decoding process has passed
535 :param int offset: binary offset where failure happened
537 super(DecodeError, self).__init__()
540 self.decode_path = decode_path
546 "" if self.klass is None else self.klass.__name__,
548 ("(%s)" % ".".join(self.decode_path))
549 if len(self.decode_path) > 0 else ""
551 ("(at %d)" % self.offset) if self.offset > 0 else "",
557 return "%s(%s)" % (self.__class__.__name__, self)
560 class NotEnoughData(DecodeError):
564 class TagMismatch(DecodeError):
568 class InvalidLength(DecodeError):
572 class InvalidOID(DecodeError):
576 class ObjUnknown(ValueError):
577 def __init__(self, name):
578 super(ObjUnknown, self).__init__()
582 return "object is unknown: %s" % self.name
585 return "%s(%s)" % (self.__class__.__name__, self)
588 class ObjNotReady(ValueError):
589 def __init__(self, name):
590 super(ObjNotReady, self).__init__()
594 return "object is not ready: %s" % self.name
597 return "%s(%s)" % (self.__class__.__name__, self)
600 class InvalidValueType(ValueError):
601 def __init__(self, expected_types):
602 super(InvalidValueType, self).__init__()
603 self.expected_types = expected_types
606 return "invalid value type, expected: %s" % ", ".join(
607 [repr(t) for t in self.expected_types]
611 return "%s(%s)" % (self.__class__.__name__, self)
614 class BoundsError(ValueError):
615 def __init__(self, bound_min, value, bound_max):
616 super(BoundsError, self).__init__()
617 self.bound_min = bound_min
619 self.bound_max = bound_max
622 return "unsatisfied bounds: %s <= %s <= %s" % (
629 return "%s(%s)" % (self.__class__.__name__, self)
632 ########################################################################
634 ########################################################################
636 _hexdecoder = getdecoder("hex")
637 _hexencoder = getencoder("hex")
641 """Binary data to hexadecimal string convert
643 return _hexdecoder(data)[0]
647 """Hexadecimal string to binary data convert
649 return _hexencoder(data)[0].decode("ascii")
652 def int_bytes_len(num, byte_len=8):
655 return int(ceil(float(num.bit_length()) / byte_len))
658 def zero_ended_encode(num):
659 octets = bytearray(int_bytes_len(num, 7))
661 octets[i] = num & 0x7F
665 octets[i] = 0x80 | (num & 0x7F)
671 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
672 """Encode tag to binary form
674 :param int num: tag's number
675 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
676 :py:data:`pyderasn.TagClassContext`,
677 :py:data:`pyderasn.TagClassApplication`,
678 :py:data:`pyderasn.TagClassPrivate`)
679 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
680 :py:data:`pyderasn.TagFormConstructed`)
684 return int2byte(klass | form | num)
685 # [XX|X|11111][1.......][1.......] ... [0.......]
686 return int2byte(klass | form | 31) + zero_ended_encode(num)
690 """Decode tag from binary form
694 No validation is performed, assuming that it has already passed.
696 It returns tuple with three integers, as
697 :py:func:`pyderasn.tag_encode` accepts.
699 first_octet = byte2int(tag)
700 klass = first_octet & 0xC0
701 form = first_octet & 0x20
702 if first_octet & 0x1F < 0x1F:
703 return (klass, form, first_octet & 0x1F)
705 for octet in iterbytes(tag[1:]):
708 return (klass, form, num)
712 """Create CONTEXT PRIMITIVE tag
714 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
718 """Create CONTEXT CONSTRUCTED tag
720 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
724 """Take off tag from the data
726 :returns: (encoded tag, tag length, remaining data)
729 raise NotEnoughData("no data at all")
730 if byte2int(data) & 0x1F < 31:
731 return data[:1], 1, data[1:]
736 raise DecodeError("unfinished tag")
737 if indexbytes(data, i) & 0x80 == 0:
740 return data[:i], i, data[i:]
746 octets = bytearray(int_bytes_len(l) + 1)
747 octets[0] = 0x80 | (len(octets) - 1)
748 for i in six_xrange(len(octets) - 1, 0, -1):
754 def len_decode(data):
756 raise NotEnoughData("no data at all")
757 first_octet = byte2int(data)
758 if first_octet & 0x80 == 0:
759 return first_octet, 1, data[1:]
760 octets_num = first_octet & 0x7F
761 if octets_num + 1 > len(data):
762 raise NotEnoughData("encoded length is longer than data")
764 raise DecodeError("long form instead of short one")
765 if byte2int(data[1:]) == 0:
766 raise DecodeError("leading zeros")
768 for v in iterbytes(data[1:1 + octets_num]):
771 raise DecodeError("long form instead of short one")
772 return l, 1 + octets_num, data[1 + octets_num:]
775 ########################################################################
777 ########################################################################
779 class AutoAddSlots(type):
780 def __new__(cls, name, bases, _dict):
781 _dict["__slots__"] = _dict.get("__slots__", ())
782 return type.__new__(cls, name, bases, _dict)
785 @add_metaclass(AutoAddSlots)
787 """Common ASN.1 object class
789 All ASN.1 types are inherited from it. It has metaclass that
790 automatically adds ``__slots__`` to all inherited classes.
811 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
812 self._expl = getattr(self, "expl", None) if expl is None else expl
813 if self.tag != self.tag_default and self._expl is not None:
815 "implicit and explicit tags can not be set simultaneously"
817 if default is not None:
819 self.optional = optional
820 self.offset, self.llen, self.vlen = _decoded
824 def ready(self): # pragma: no cover
825 """Is object ready to be encoded?
827 raise NotImplementedError()
829 def _assert_ready(self):
831 raise ObjNotReady(self.__class__.__name__)
835 """Is object decoded?
837 return (self.llen + self.vlen) > 0
839 def copy(self): # pragma: no cover
840 """Make a copy of object, safe to be mutated
842 raise NotImplementedError()
850 return self.tlen + self.llen + self.vlen
852 def __str__(self): # pragma: no cover
853 return self.__bytes__() if PY2 else self.__unicode__()
855 def __ne__(self, their):
856 return not(self == their)
858 def __gt__(self, their): # pragma: no cover
859 return not(self < their)
861 def __le__(self, their): # pragma: no cover
862 return (self == their) or (self < their)
864 def __ge__(self, their): # pragma: no cover
865 return (self == their) or (self > their)
867 def _encode(self): # pragma: no cover
868 raise NotImplementedError()
870 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None): # pragma: no cover
871 raise NotImplementedError()
875 if self._expl is None:
877 return b"".join((self._expl, len_encode(len(raw)), raw))
879 def decode(self, data, offset=0, leavemm=False, decode_path=(), defines_by_path=None):
882 :param data: either binary or memoryview
883 :param int offset: initial data's offset
884 :param bool leavemm: do we need to leave memoryview of remaining
885 data as is, or convert it to bytes otherwise
886 :param defines_by_path: :ref:`Read about DEFINED BY <definedby>`
887 :returns: (Obj, remaining data)
889 tlv = memoryview(data)
890 if self._expl is None:
891 obj, tail = self._decode(
894 decode_path=decode_path,
895 defines_by_path=defines_by_path,
899 t, tlen, lv = tag_strip(tlv)
900 except DecodeError as err:
903 klass=self.__class__,
904 decode_path=decode_path,
909 klass=self.__class__,
910 decode_path=decode_path,
914 l, llen, v = len_decode(lv)
915 except DecodeError as err:
918 klass=self.__class__,
919 decode_path=decode_path,
924 "encoded length is longer than data",
925 klass=self.__class__,
926 decode_path=decode_path,
929 obj, tail = self._decode(
931 offset=offset + tlen + llen,
932 decode_path=decode_path,
933 defines_by_path=defines_by_path,
935 return obj, (tail if leavemm else tail.tobytes())
939 return self._expl is not None
947 return len(self._expl)
951 return len(len_encode(self.tlvlen))
954 def expl_offset(self):
955 return self.offset - self.expl_tlen - self.expl_llen
962 def expl_tlvlen(self):
963 return self.expl_tlen + self.expl_llen + self.expl_vlen
966 def decode_path_defby(defined_by):
967 """DEFINED BY representation inside decode path
969 return "DEFINED BY (%s)" % defined_by
972 ########################################################################
974 ########################################################################
976 PP = namedtuple("PP", (
998 asn1_type_name="unknown",
1037 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
1040 cols.append("%5d%s [%d,%d,%4d]" % (
1043 " " if pp.expl_offset is None else
1044 ("-%d" % (pp.offset - pp.expl_offset))
1050 if len(pp.decode_path) > 0:
1051 cols.append(" ." * (len(pp.decode_path)))
1052 cols.append("%s:" % pp.decode_path[-1])
1053 if pp.expl is not None:
1054 klass, _, num = pp.expl
1055 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
1056 if pp.impl is not None:
1057 klass, _, num = pp.impl
1058 cols.append("[%s%d]" % (TagClassReprs[klass], num))
1059 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1060 cols.append(pp.obj_name)
1061 cols.append(pp.asn1_type_name)
1062 if pp.value is not None:
1065 oids is not None and
1066 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1069 value = "%s (%s)" % (oids[value], pp.value)
1072 if isinstance(pp.blob, binary_type):
1073 cols.append(hexenc(pp.blob))
1074 elif isinstance(pp.blob, tuple):
1075 cols.append(", ".join(pp.blob))
1077 cols.append("OPTIONAL")
1079 cols.append("DEFAULT")
1080 return " ".join(cols)
1083 def pp_console_blob(pp):
1084 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1085 if len(pp.decode_path) > 0:
1086 cols.append(" ." * (len(pp.decode_path) + 1))
1087 if isinstance(pp.blob, binary_type):
1088 blob = hexenc(pp.blob).upper()
1089 for i in range(0, len(blob), 32):
1090 chunk = blob[i:i + 32]
1091 yield " ".join(cols + [":".join(
1092 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1094 elif isinstance(pp.blob, tuple):
1095 yield " ".join(cols + [", ".join(pp.blob)])
1098 def pprint(obj, oids=None, big_blobs=False):
1099 """Pretty print object
1101 :param Obj obj: object you want to pretty print
1102 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1103 from it is met, then its humand readable form is printed
1104 :param big_blobs: if large binary objects are met (like OctetString
1105 values), do we need to print them too, on separate
1108 def _pprint_pps(pps):
1110 if hasattr(pp, "_fields"):
1112 yield pp_console_row(
1118 for row in pp_console_blob(pp):
1121 yield pp_console_row(pp, oids=oids, with_offsets=True)
1123 for row in _pprint_pps(pp):
1125 return "\n".join(_pprint_pps(obj.pps()))
1128 ########################################################################
1129 # ASN.1 primitive types
1130 ########################################################################
1133 """``BOOLEAN`` boolean type
1135 >>> b = Boolean(True)
1137 >>> b == Boolean(True)
1143 tag_default = tag_encode(1)
1144 asn1_type_name = "BOOLEAN"
1156 :param value: set the value. Either boolean type, or
1157 :py:class:`pyderasn.Boolean` object
1158 :param bytes impl: override default tag with ``IMPLICIT`` one
1159 :param bytes expl: override default tag with ``EXPLICIT`` one
1160 :param default: set default value. Type same as in ``value``
1161 :param bool optional: is object ``OPTIONAL`` in sequence
1163 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1164 self._value = None if value is None else self._value_sanitize(value)
1165 if default is not None:
1166 default = self._value_sanitize(default)
1167 self.default = self.__class__(
1173 self._value = default
1175 def _value_sanitize(self, value):
1176 if issubclass(value.__class__, Boolean):
1178 if isinstance(value, bool):
1180 raise InvalidValueType((self.__class__, bool))
1184 return self._value is not None
1187 obj = self.__class__()
1188 obj._value = self._value
1190 obj._expl = self._expl
1191 obj.default = self.default
1192 obj.optional = self.optional
1193 obj.offset = self.offset
1194 obj.llen = self.llen
1195 obj.vlen = self.vlen
1198 def __nonzero__(self):
1199 self._assert_ready()
1203 self._assert_ready()
1206 def __eq__(self, their):
1207 if isinstance(their, bool):
1208 return self._value == their
1209 if not issubclass(their.__class__, Boolean):
1212 self._value == their._value and
1213 self.tag == their.tag and
1214 self._expl == their._expl
1225 return self.__class__(
1227 impl=self.tag if impl is None else impl,
1228 expl=self._expl if expl is None else expl,
1229 default=self.default if default is None else default,
1230 optional=self.optional if optional is None else optional,
1234 self._assert_ready()
1238 (b"\xFF" if self._value else b"\x00"),
1241 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
1243 t, _, lv = tag_strip(tlv)
1244 except DecodeError as err:
1245 raise err.__class__(
1247 klass=self.__class__,
1248 decode_path=decode_path,
1253 klass=self.__class__,
1254 decode_path=decode_path,
1258 l, _, v = len_decode(lv)
1259 except DecodeError as err:
1260 raise err.__class__(
1262 klass=self.__class__,
1263 decode_path=decode_path,
1267 raise InvalidLength(
1268 "Boolean's length must be equal to 1",
1269 klass=self.__class__,
1270 decode_path=decode_path,
1274 raise NotEnoughData(
1275 "encoded length is longer than data",
1276 klass=self.__class__,
1277 decode_path=decode_path,
1280 first_octet = byte2int(v)
1281 if first_octet == 0:
1283 elif first_octet == 0xFF:
1287 "unacceptable Boolean value",
1288 klass=self.__class__,
1289 decode_path=decode_path,
1292 obj = self.__class__(
1296 default=self.default,
1297 optional=self.optional,
1298 _decoded=(offset, 1, 1),
1303 return pp_console_row(next(self.pps()))
1305 def pps(self, decode_path=()):
1307 asn1_type_name=self.asn1_type_name,
1308 obj_name=self.__class__.__name__,
1309 decode_path=decode_path,
1310 value=str(self._value) if self.ready else None,
1311 optional=self.optional,
1312 default=self == self.default,
1313 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1314 expl=None if self._expl is None else tag_decode(self._expl),
1319 expl_offset=self.expl_offset if self.expled else None,
1320 expl_tlen=self.expl_tlen if self.expled else None,
1321 expl_llen=self.expl_llen if self.expled else None,
1322 expl_vlen=self.expl_vlen if self.expled else None,
1327 """``INTEGER`` integer type
1329 >>> b = Integer(-123)
1331 >>> b == Integer(-123)
1336 >>> Integer(2, bounds=(1, 3))
1338 >>> Integer(5, bounds=(1, 3))
1339 Traceback (most recent call last):
1340 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1344 class Version(Integer):
1351 >>> v = Version("v1")
1358 {'v3': 2, 'v1': 0, 'v2': 1}
1360 __slots__ = ("specs", "_bound_min", "_bound_max")
1361 tag_default = tag_encode(2)
1362 asn1_type_name = "INTEGER"
1376 :param value: set the value. Either integer type, named value
1377 (if ``schema`` is specified in the class), or
1378 :py:class:`pyderasn.Integer` object
1379 :param bounds: set ``(MIN, MAX)`` value constraint.
1380 (-inf, +inf) by default
1381 :param bytes impl: override default tag with ``IMPLICIT`` one
1382 :param bytes expl: override default tag with ``EXPLICIT`` one
1383 :param default: set default value. Type same as in ``value``
1384 :param bool optional: is object ``OPTIONAL`` in sequence
1386 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1388 specs = getattr(self, "schema", {}) if _specs is None else _specs
1389 self.specs = specs if isinstance(specs, dict) else dict(specs)
1390 self._bound_min, self._bound_max = getattr(
1393 (float("-inf"), float("+inf")),
1394 ) if bounds is None else bounds
1395 if value is not None:
1396 self._value = self._value_sanitize(value)
1397 if default is not None:
1398 default = self._value_sanitize(default)
1399 self.default = self.__class__(
1405 if self._value is None:
1406 self._value = default
1408 def _value_sanitize(self, value):
1409 if issubclass(value.__class__, Integer):
1410 value = value._value
1411 elif isinstance(value, integer_types):
1413 elif isinstance(value, str):
1414 value = self.specs.get(value)
1416 raise ObjUnknown("integer value: %s" % value)
1418 raise InvalidValueType((self.__class__, int, str))
1419 if not self._bound_min <= value <= self._bound_max:
1420 raise BoundsError(self._bound_min, value, self._bound_max)
1425 return self._value is not None
1428 obj = self.__class__(_specs=self.specs)
1429 obj._value = self._value
1430 obj._bound_min = self._bound_min
1431 obj._bound_max = self._bound_max
1433 obj._expl = self._expl
1434 obj.default = self.default
1435 obj.optional = self.optional
1436 obj.offset = self.offset
1437 obj.llen = self.llen
1438 obj.vlen = self.vlen
1442 self._assert_ready()
1443 return int(self._value)
1446 self._assert_ready()
1449 bytes(self._expl or b"") +
1450 str(self._value).encode("ascii"),
1453 def __eq__(self, their):
1454 if isinstance(their, integer_types):
1455 return self._value == their
1456 if not issubclass(their.__class__, Integer):
1459 self._value == their._value and
1460 self.tag == their.tag and
1461 self._expl == their._expl
1464 def __lt__(self, their):
1465 return self._value < their._value
1469 for name, value in self.specs.items():
1470 if value == self._value:
1482 return self.__class__(
1485 (self._bound_min, self._bound_max)
1486 if bounds is None else bounds
1488 impl=self.tag if impl is None else impl,
1489 expl=self._expl if expl is None else expl,
1490 default=self.default if default is None else default,
1491 optional=self.optional if optional is None else optional,
1496 self._assert_ready()
1500 octets = bytearray([0])
1504 octets = bytearray()
1506 octets.append((value & 0xFF) ^ 0xFF)
1508 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1511 octets = bytearray()
1513 octets.append(value & 0xFF)
1515 if octets[-1] & 0x80 > 0:
1518 octets = bytes(octets)
1520 bytes_len = ceil(value.bit_length() / 8) or 1
1523 octets = value.to_bytes(
1528 except OverflowError:
1532 return b"".join((self.tag, len_encode(len(octets)), octets))
1534 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
1536 t, _, lv = tag_strip(tlv)
1537 except DecodeError as err:
1538 raise err.__class__(
1540 klass=self.__class__,
1541 decode_path=decode_path,
1546 klass=self.__class__,
1547 decode_path=decode_path,
1551 l, llen, v = len_decode(lv)
1552 except DecodeError as err:
1553 raise err.__class__(
1555 klass=self.__class__,
1556 decode_path=decode_path,
1560 raise NotEnoughData(
1561 "encoded length is longer than data",
1562 klass=self.__class__,
1563 decode_path=decode_path,
1567 raise NotEnoughData(
1569 klass=self.__class__,
1570 decode_path=decode_path,
1573 v, tail = v[:l], v[l:]
1574 first_octet = byte2int(v)
1576 second_octet = byte2int(v[1:])
1578 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1579 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1582 "non normalized integer",
1583 klass=self.__class__,
1584 decode_path=decode_path,
1589 if first_octet & 0x80 > 0:
1590 octets = bytearray()
1591 for octet in bytearray(v):
1592 octets.append(octet ^ 0xFF)
1593 for octet in octets:
1594 value = (value << 8) | octet
1598 for octet in bytearray(v):
1599 value = (value << 8) | octet
1601 value = int.from_bytes(v, byteorder="big", signed=True)
1603 obj = self.__class__(
1605 bounds=(self._bound_min, self._bound_max),
1608 default=self.default,
1609 optional=self.optional,
1611 _decoded=(offset, llen, l),
1613 except BoundsError as err:
1616 klass=self.__class__,
1617 decode_path=decode_path,
1623 return pp_console_row(next(self.pps()))
1625 def pps(self, decode_path=()):
1627 asn1_type_name=self.asn1_type_name,
1628 obj_name=self.__class__.__name__,
1629 decode_path=decode_path,
1630 value=(self.named or str(self._value)) if self.ready else None,
1631 optional=self.optional,
1632 default=self == self.default,
1633 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1634 expl=None if self._expl is None else tag_decode(self._expl),
1639 expl_offset=self.expl_offset if self.expled else None,
1640 expl_tlen=self.expl_tlen if self.expled else None,
1641 expl_llen=self.expl_llen if self.expled else None,
1642 expl_vlen=self.expl_vlen if self.expled else None,
1646 class BitString(Obj):
1647 """``BIT STRING`` bit string type
1649 >>> BitString(b"hello world")
1650 BIT STRING 88 bits 68656c6c6f20776f726c64
1653 >>> b == b"hello world"
1658 >>> b = BitString("'010110000000'B")
1659 BIT STRING 12 bits 5800
1662 >>> b[0], b[1], b[2], b[3]
1663 (False, True, False, True)
1667 [False, True, False, True, True, False, False, False, False, False, False, False]
1671 class KeyUsage(BitString):
1673 ('digitalSignature', 0),
1674 ('nonRepudiation', 1),
1675 ('keyEncipherment', 2),
1678 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1679 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1681 ['nonRepudiation', 'keyEncipherment']
1683 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1685 __slots__ = ("specs", "defined")
1686 tag_default = tag_encode(3)
1687 asn1_type_name = "BIT STRING"
1700 :param value: set the value. Either binary type, tuple of named
1701 values (if ``schema`` is specified in the class),
1702 string in ``'XXX...'B`` form, or
1703 :py:class:`pyderasn.BitString` object
1704 :param bytes impl: override default tag with ``IMPLICIT`` one
1705 :param bytes expl: override default tag with ``EXPLICIT`` one
1706 :param default: set default value. Type same as in ``value``
1707 :param bool optional: is object ``OPTIONAL`` in sequence
1709 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1710 specs = getattr(self, "schema", {}) if _specs is None else _specs
1711 self.specs = specs if isinstance(specs, dict) else dict(specs)
1712 self._value = None if value is None else self._value_sanitize(value)
1713 if default is not None:
1714 default = self._value_sanitize(default)
1715 self.default = self.__class__(
1721 self._value = default
1724 def _bits2octets(self, bits):
1725 if len(self.specs) > 0:
1726 bits = bits.rstrip("0")
1728 bits += "0" * ((8 - (bit_len % 8)) % 8)
1729 octets = bytearray(len(bits) // 8)
1730 for i in six_xrange(len(octets)):
1731 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1732 return bit_len, bytes(octets)
1734 def _value_sanitize(self, value):
1735 if issubclass(value.__class__, BitString):
1737 if isinstance(value, (string_types, binary_type)):
1739 isinstance(value, string_types) and
1740 value.startswith("'") and
1741 value.endswith("'B")
1744 if not set(value) <= set(("0", "1")):
1745 raise ValueError("B's coding contains unacceptable chars")
1746 return self._bits2octets(value)
1747 elif isinstance(value, binary_type):
1748 return (len(value) * 8, value)
1750 raise InvalidValueType((
1755 if isinstance(value, tuple):
1758 isinstance(value[0], integer_types) and
1759 isinstance(value[1], binary_type)
1764 bit = self.specs.get(name)
1766 raise ObjUnknown("BitString value: %s" % name)
1769 return self._bits2octets("")
1771 return self._bits2octets("".join(
1772 ("1" if bit in bits else "0")
1773 for bit in six_xrange(max(bits) + 1)
1775 raise InvalidValueType((self.__class__, binary_type, string_types))
1779 return self._value is not None
1782 obj = self.__class__(_specs=self.specs)
1784 if value is not None:
1785 value = (value[0], value[1])
1788 obj._expl = self._expl
1789 obj.default = self.default
1790 obj.optional = self.optional
1791 obj.offset = self.offset
1792 obj.llen = self.llen
1793 obj.vlen = self.vlen
1797 self._assert_ready()
1798 for i in six_xrange(self._value[0]):
1803 self._assert_ready()
1804 return self._value[0]
1806 def __bytes__(self):
1807 self._assert_ready()
1808 return self._value[1]
1810 def __eq__(self, their):
1811 if isinstance(their, bytes):
1812 return self._value[1] == their
1813 if not issubclass(their.__class__, BitString):
1816 self._value == their._value and
1817 self.tag == their.tag and
1818 self._expl == their._expl
1823 return [name for name, bit in self.specs.items() if self[bit]]
1833 return self.__class__(
1835 impl=self.tag if impl is None else impl,
1836 expl=self._expl if expl is None else expl,
1837 default=self.default if default is None else default,
1838 optional=self.optional if optional is None else optional,
1842 def __getitem__(self, key):
1843 if isinstance(key, int):
1844 bit_len, octets = self._value
1848 byte2int(memoryview(octets)[key // 8:]) >>
1851 if isinstance(key, string_types):
1852 value = self.specs.get(key)
1854 raise ObjUnknown("BitString value: %s" % key)
1856 raise InvalidValueType((int, str))
1859 self._assert_ready()
1860 bit_len, octets = self._value
1863 len_encode(len(octets) + 1),
1864 int2byte((8 - bit_len % 8) % 8),
1868 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
1870 t, _, lv = tag_strip(tlv)
1871 except DecodeError as err:
1872 raise err.__class__(
1874 klass=self.__class__,
1875 decode_path=decode_path,
1880 klass=self.__class__,
1881 decode_path=decode_path,
1885 l, llen, v = len_decode(lv)
1886 except DecodeError as err:
1887 raise err.__class__(
1889 klass=self.__class__,
1890 decode_path=decode_path,
1894 raise NotEnoughData(
1895 "encoded length is longer than data",
1896 klass=self.__class__,
1897 decode_path=decode_path,
1901 raise NotEnoughData(
1903 klass=self.__class__,
1904 decode_path=decode_path,
1907 pad_size = byte2int(v)
1908 if l == 1 and pad_size != 0:
1910 "invalid empty value",
1911 klass=self.__class__,
1912 decode_path=decode_path,
1918 klass=self.__class__,
1919 decode_path=decode_path,
1922 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1925 klass=self.__class__,
1926 decode_path=decode_path,
1929 v, tail = v[:l], v[l:]
1930 obj = self.__class__(
1931 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1934 default=self.default,
1935 optional=self.optional,
1937 _decoded=(offset, llen, l),
1942 return pp_console_row(next(self.pps()))
1944 def pps(self, decode_path=()):
1948 bit_len, blob = self._value
1949 value = "%d bits" % bit_len
1950 if len(self.specs) > 0:
1951 blob = tuple(self.named)
1953 asn1_type_name=self.asn1_type_name,
1954 obj_name=self.__class__.__name__,
1955 decode_path=decode_path,
1958 optional=self.optional,
1959 default=self == self.default,
1960 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1961 expl=None if self._expl is None else tag_decode(self._expl),
1966 expl_offset=self.expl_offset if self.expled else None,
1967 expl_tlen=self.expl_tlen if self.expled else None,
1968 expl_llen=self.expl_llen if self.expled else None,
1969 expl_vlen=self.expl_vlen if self.expled else None,
1971 defined_by, defined = self.defined or (None, None)
1972 if defined_by is not None:
1974 decode_path=decode_path + (decode_path_defby(defined_by),)
1978 class OctetString(Obj):
1979 """``OCTET STRING`` binary string type
1981 >>> s = OctetString(b"hello world")
1982 OCTET STRING 11 bytes 68656c6c6f20776f726c64
1983 >>> s == OctetString(b"hello world")
1988 >>> OctetString(b"hello", bounds=(4, 4))
1989 Traceback (most recent call last):
1990 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
1991 >>> OctetString(b"hell", bounds=(4, 4))
1992 OCTET STRING 4 bytes 68656c6c
1994 __slots__ = ("_bound_min", "_bound_max", "defined")
1995 tag_default = tag_encode(4)
1996 asn1_type_name = "OCTET STRING"
2009 :param value: set the value. Either binary type, or
2010 :py:class:`pyderasn.OctetString` object
2011 :param bounds: set ``(MIN, MAX)`` value size constraint.
2012 (-inf, +inf) by default
2013 :param bytes impl: override default tag with ``IMPLICIT`` one
2014 :param bytes expl: override default tag with ``EXPLICIT`` one
2015 :param default: set default value. Type same as in ``value``
2016 :param bool optional: is object ``OPTIONAL`` in sequence
2018 super(OctetString, self).__init__(
2026 self._bound_min, self._bound_max = getattr(
2030 ) if bounds is None else bounds
2031 if value is not None:
2032 self._value = self._value_sanitize(value)
2033 if default is not None:
2034 default = self._value_sanitize(default)
2035 self.default = self.__class__(
2040 if self._value is None:
2041 self._value = default
2044 def _value_sanitize(self, value):
2045 if issubclass(value.__class__, OctetString):
2046 value = value._value
2047 elif isinstance(value, binary_type):
2050 raise InvalidValueType((self.__class__, bytes))
2051 if not self._bound_min <= len(value) <= self._bound_max:
2052 raise BoundsError(self._bound_min, len(value), self._bound_max)
2057 return self._value is not None
2060 obj = self.__class__()
2061 obj._value = self._value
2062 obj._bound_min = self._bound_min
2063 obj._bound_max = self._bound_max
2065 obj._expl = self._expl
2066 obj.default = self.default
2067 obj.optional = self.optional
2068 obj.offset = self.offset
2069 obj.llen = self.llen
2070 obj.vlen = self.vlen
2073 def __bytes__(self):
2074 self._assert_ready()
2077 def __eq__(self, their):
2078 if isinstance(their, binary_type):
2079 return self._value == their
2080 if not issubclass(their.__class__, OctetString):
2083 self._value == their._value and
2084 self.tag == their.tag and
2085 self._expl == their._expl
2088 def __lt__(self, their):
2089 return self._value < their._value
2100 return self.__class__(
2103 (self._bound_min, self._bound_max)
2104 if bounds is None else bounds
2106 impl=self.tag if impl is None else impl,
2107 expl=self._expl if expl is None else expl,
2108 default=self.default if default is None else default,
2109 optional=self.optional if optional is None else optional,
2113 self._assert_ready()
2116 len_encode(len(self._value)),
2120 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
2122 t, _, lv = tag_strip(tlv)
2123 except DecodeError as err:
2124 raise err.__class__(
2126 klass=self.__class__,
2127 decode_path=decode_path,
2132 klass=self.__class__,
2133 decode_path=decode_path,
2137 l, llen, v = len_decode(lv)
2138 except DecodeError as err:
2139 raise err.__class__(
2141 klass=self.__class__,
2142 decode_path=decode_path,
2146 raise NotEnoughData(
2147 "encoded length is longer than data",
2148 klass=self.__class__,
2149 decode_path=decode_path,
2152 v, tail = v[:l], v[l:]
2154 obj = self.__class__(
2156 bounds=(self._bound_min, self._bound_max),
2159 default=self.default,
2160 optional=self.optional,
2161 _decoded=(offset, llen, l),
2163 except BoundsError as err:
2166 klass=self.__class__,
2167 decode_path=decode_path,
2173 return pp_console_row(next(self.pps()))
2175 def pps(self, decode_path=()):
2177 asn1_type_name=self.asn1_type_name,
2178 obj_name=self.__class__.__name__,
2179 decode_path=decode_path,
2180 value=("%d bytes" % len(self._value)) if self.ready else None,
2181 blob=self._value if self.ready else None,
2182 optional=self.optional,
2183 default=self == self.default,
2184 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2185 expl=None if self._expl is None else tag_decode(self._expl),
2190 expl_offset=self.expl_offset if self.expled else None,
2191 expl_tlen=self.expl_tlen if self.expled else None,
2192 expl_llen=self.expl_llen if self.expled else None,
2193 expl_vlen=self.expl_vlen if self.expled else None,
2195 defined_by, defined = self.defined or (None, None)
2196 if defined_by is not None:
2198 decode_path=decode_path + (decode_path_defby(defined_by),)
2203 """``NULL`` null object
2211 tag_default = tag_encode(5)
2212 asn1_type_name = "NULL"
2216 value=None, # unused, but Sequence passes it
2223 :param bytes impl: override default tag with ``IMPLICIT`` one
2224 :param bytes expl: override default tag with ``EXPLICIT`` one
2225 :param bool optional: is object ``OPTIONAL`` in sequence
2227 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2235 obj = self.__class__()
2237 obj._expl = self._expl
2238 obj.default = self.default
2239 obj.optional = self.optional
2240 obj.offset = self.offset
2241 obj.llen = self.llen
2242 obj.vlen = self.vlen
2245 def __eq__(self, their):
2246 if not issubclass(their.__class__, Null):
2249 self.tag == their.tag and
2250 self._expl == their._expl
2260 return self.__class__(
2261 impl=self.tag if impl is None else impl,
2262 expl=self._expl if expl is None else expl,
2263 optional=self.optional if optional is None else optional,
2267 return self.tag + len_encode(0)
2269 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
2271 t, _, lv = tag_strip(tlv)
2272 except DecodeError as err:
2273 raise err.__class__(
2275 klass=self.__class__,
2276 decode_path=decode_path,
2281 klass=self.__class__,
2282 decode_path=decode_path,
2286 l, _, v = len_decode(lv)
2287 except DecodeError as err:
2288 raise err.__class__(
2290 klass=self.__class__,
2291 decode_path=decode_path,
2295 raise InvalidLength(
2296 "Null must have zero length",
2297 klass=self.__class__,
2298 decode_path=decode_path,
2301 obj = self.__class__(
2304 optional=self.optional,
2305 _decoded=(offset, 1, 0),
2310 return pp_console_row(next(self.pps()))
2312 def pps(self, decode_path=()):
2314 asn1_type_name=self.asn1_type_name,
2315 obj_name=self.__class__.__name__,
2316 decode_path=decode_path,
2317 optional=self.optional,
2318 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2319 expl=None if self._expl is None else tag_decode(self._expl),
2324 expl_offset=self.expl_offset if self.expled else None,
2325 expl_tlen=self.expl_tlen if self.expled else None,
2326 expl_llen=self.expl_llen if self.expled else None,
2327 expl_vlen=self.expl_vlen if self.expled else None,
2331 class ObjectIdentifier(Obj):
2332 """``OBJECT IDENTIFIER`` OID type
2334 >>> oid = ObjectIdentifier((1, 2, 3))
2335 OBJECT IDENTIFIER 1.2.3
2336 >>> oid == ObjectIdentifier("1.2.3")
2342 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2343 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2345 >>> str(ObjectIdentifier((3, 1)))
2346 Traceback (most recent call last):
2347 pyderasn.InvalidOID: unacceptable first arc value
2349 __slots__ = ("defines",)
2350 tag_default = tag_encode(6)
2351 asn1_type_name = "OBJECT IDENTIFIER"
2364 :param value: set the value. Either tuples of integers,
2365 string of "."-concatenated integers, or
2366 :py:class:`pyderasn.ObjectIdentifier` object
2367 :param defines: tuple of two elements. First one is a name of
2368 field inside :py:class:`pyderasn.Sequence`,
2369 defining with that OID. Second element is a
2370 ``{OID: pyderasn.Obj()}`` dictionary, mapping
2371 between current OID value and structure applied
2373 :ref:`Read about DEFINED BY <definedby>`
2374 :param bytes impl: override default tag with ``IMPLICIT`` one
2375 :param bytes expl: override default tag with ``EXPLICIT`` one
2376 :param default: set default value. Type same as in ``value``
2377 :param bool optional: is object ``OPTIONAL`` in sequence
2379 super(ObjectIdentifier, self).__init__(
2387 if value is not None:
2388 self._value = self._value_sanitize(value)
2389 if default is not None:
2390 default = self._value_sanitize(default)
2391 self.default = self.__class__(
2396 if self._value is None:
2397 self._value = default
2398 self.defines = defines
2400 def __add__(self, their):
2401 if isinstance(their, self.__class__):
2402 return self.__class__(self._value + their._value)
2403 if isinstance(their, tuple):
2404 return self.__class__(self._value + their)
2405 raise InvalidValueType((self.__class__, tuple))
2407 def _value_sanitize(self, value):
2408 if issubclass(value.__class__, ObjectIdentifier):
2410 if isinstance(value, string_types):
2412 value = tuple(int(arc) for arc in value.split("."))
2414 raise InvalidOID("unacceptable arcs values")
2415 if isinstance(value, tuple):
2417 raise InvalidOID("less than 2 arcs")
2418 first_arc = value[0]
2419 if first_arc in (0, 1):
2420 if not (0 <= value[1] <= 39):
2421 raise InvalidOID("second arc is too wide")
2422 elif first_arc == 2:
2425 raise InvalidOID("unacceptable first arc value")
2427 raise InvalidValueType((self.__class__, str, tuple))
2431 return self._value is not None
2434 obj = self.__class__()
2435 obj._value = self._value
2436 obj.defines = self.defines
2438 obj._expl = self._expl
2439 obj.default = self.default
2440 obj.optional = self.optional
2441 obj.offset = self.offset
2442 obj.llen = self.llen
2443 obj.vlen = self.vlen
2447 self._assert_ready()
2448 return iter(self._value)
2451 return ".".join(str(arc) for arc in self._value or ())
2454 self._assert_ready()
2457 bytes(self._expl or b"") +
2458 str(self._value).encode("ascii"),
2461 def __eq__(self, their):
2462 if isinstance(their, tuple):
2463 return self._value == their
2464 if not issubclass(their.__class__, ObjectIdentifier):
2467 self.tag == their.tag and
2468 self._expl == their._expl and
2469 self._value == their._value
2472 def __lt__(self, their):
2473 return self._value < their._value
2484 return self.__class__(
2486 defines=self.defines if defines is None else defines,
2487 impl=self.tag if impl is None else impl,
2488 expl=self._expl if expl is None else expl,
2489 default=self.default if default is None else default,
2490 optional=self.optional if optional is None else optional,
2494 self._assert_ready()
2496 first_value = value[1]
2497 first_arc = value[0]
2500 elif first_arc == 1:
2502 elif first_arc == 2:
2504 else: # pragma: no cover
2505 raise RuntimeError("invalid arc is stored")
2506 octets = [zero_ended_encode(first_value)]
2507 for arc in value[2:]:
2508 octets.append(zero_ended_encode(arc))
2509 v = b"".join(octets)
2510 return b"".join((self.tag, len_encode(len(v)), v))
2512 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
2514 t, _, lv = tag_strip(tlv)
2515 except DecodeError as err:
2516 raise err.__class__(
2518 klass=self.__class__,
2519 decode_path=decode_path,
2524 klass=self.__class__,
2525 decode_path=decode_path,
2529 l, llen, v = len_decode(lv)
2530 except DecodeError as err:
2531 raise err.__class__(
2533 klass=self.__class__,
2534 decode_path=decode_path,
2538 raise NotEnoughData(
2539 "encoded length is longer than data",
2540 klass=self.__class__,
2541 decode_path=decode_path,
2545 raise NotEnoughData(
2547 klass=self.__class__,
2548 decode_path=decode_path,
2551 v, tail = v[:l], v[l:]
2557 octet = indexbytes(v, i)
2558 arc = (arc << 7) | (octet & 0x7F)
2559 if octet & 0x80 == 0:
2567 klass=self.__class__,
2568 decode_path=decode_path,
2572 second_arc = arcs[0]
2573 if 0 <= second_arc <= 39:
2575 elif 40 <= second_arc <= 79:
2581 obj = self.__class__(
2582 value=tuple([first_arc, second_arc] + arcs[1:]),
2585 default=self.default,
2586 optional=self.optional,
2587 _decoded=(offset, llen, l),
2592 return pp_console_row(next(self.pps()))
2594 def pps(self, decode_path=()):
2596 asn1_type_name=self.asn1_type_name,
2597 obj_name=self.__class__.__name__,
2598 decode_path=decode_path,
2599 value=str(self) if self.ready else None,
2600 optional=self.optional,
2601 default=self == self.default,
2602 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2603 expl=None if self._expl is None else tag_decode(self._expl),
2608 expl_offset=self.expl_offset if self.expled else None,
2609 expl_tlen=self.expl_tlen if self.expled else None,
2610 expl_llen=self.expl_llen if self.expled else None,
2611 expl_vlen=self.expl_vlen if self.expled else None,
2615 class Enumerated(Integer):
2616 """``ENUMERATED`` integer type
2618 This type is identical to :py:class:`pyderasn.Integer`, but requires
2619 schema to be specified and does not accept values missing from it.
2622 tag_default = tag_encode(10)
2623 asn1_type_name = "ENUMERATED"
2634 bounds=None, # dummy argument, workability for Integer.decode
2636 super(Enumerated, self).__init__(
2645 if len(self.specs) == 0:
2646 raise ValueError("schema must be specified")
2648 def _value_sanitize(self, value):
2649 if isinstance(value, self.__class__):
2650 value = value._value
2651 elif isinstance(value, integer_types):
2652 if value not in list(self.specs.values()):
2654 "unknown integer value: %s" % value,
2655 klass=self.__class__,
2657 elif isinstance(value, string_types):
2658 value = self.specs.get(value)
2660 raise ObjUnknown("integer value: %s" % value)
2662 raise InvalidValueType((self.__class__, int, str))
2666 obj = self.__class__(_specs=self.specs)
2667 obj._value = self._value
2668 obj._bound_min = self._bound_min
2669 obj._bound_max = self._bound_max
2671 obj._expl = self._expl
2672 obj.default = self.default
2673 obj.optional = self.optional
2674 obj.offset = self.offset
2675 obj.llen = self.llen
2676 obj.vlen = self.vlen
2688 return self.__class__(
2690 impl=self.tag if impl is None else impl,
2691 expl=self._expl if expl is None else expl,
2692 default=self.default if default is None else default,
2693 optional=self.optional if optional is None else optional,
2698 class CommonString(OctetString):
2699 """Common class for all strings
2701 Everything resembles :py:class:`pyderasn.OctetString`, except
2702 ability to deal with unicode text strings.
2704 >>> hexenc("привет мир".encode("utf-8"))
2705 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2706 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2708 >>> s = UTF8String("привет мир")
2709 UTF8String UTF8String привет мир
2711 'привет мир'
2712 >>> hexenc(bytes(s))
2713 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2715 >>> PrintableString("привет мир")
2716 Traceback (most recent call last):
2717 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2719 >>> BMPString("ада", bounds=(2, 2))
2720 Traceback (most recent call last):
2721 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2722 >>> s = BMPString("ад", bounds=(2, 2))
2725 >>> hexenc(bytes(s))
2733 * - :py:class:`pyderasn.UTF8String`
2735 * - :py:class:`pyderasn.NumericString`
2737 * - :py:class:`pyderasn.PrintableString`
2739 * - :py:class:`pyderasn.TeletexString`
2741 * - :py:class:`pyderasn.T61String`
2743 * - :py:class:`pyderasn.VideotexString`
2745 * - :py:class:`pyderasn.IA5String`
2747 * - :py:class:`pyderasn.GraphicString`
2749 * - :py:class:`pyderasn.VisibleString`
2751 * - :py:class:`pyderasn.ISO646String`
2753 * - :py:class:`pyderasn.GeneralString`
2755 * - :py:class:`pyderasn.UniversalString`
2757 * - :py:class:`pyderasn.BMPString`
2760 __slots__ = ("encoding",)
2762 def _value_sanitize(self, value):
2764 value_decoded = None
2765 if isinstance(value, self.__class__):
2766 value_raw = value._value
2767 elif isinstance(value, text_type):
2768 value_decoded = value
2769 elif isinstance(value, binary_type):
2772 raise InvalidValueType((self.__class__, text_type, binary_type))
2774 value_decoded.encode(self.encoding)
2775 if value_raw is None else value_raw
2778 value_raw.decode(self.encoding)
2779 if value_decoded is None else value_decoded
2781 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2789 def __eq__(self, their):
2790 if isinstance(their, binary_type):
2791 return self._value == their
2792 if isinstance(their, text_type):
2793 return self._value == their.encode(self.encoding)
2794 if not isinstance(their, self.__class__):
2797 self._value == their._value and
2798 self.tag == their.tag and
2799 self._expl == their._expl
2802 def __unicode__(self):
2804 return self._value.decode(self.encoding)
2805 return text_type(self._value)
2808 return pp_console_row(next(self.pps(no_unicode=PY2)))
2810 def pps(self, decode_path=(), no_unicode=False):
2813 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2815 asn1_type_name=self.asn1_type_name,
2816 obj_name=self.__class__.__name__,
2817 decode_path=decode_path,
2819 optional=self.optional,
2820 default=self == self.default,
2821 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2822 expl=None if self._expl is None else tag_decode(self._expl),
2830 class UTF8String(CommonString):
2832 tag_default = tag_encode(12)
2834 asn1_type_name = "UTF8String"
2837 class NumericString(CommonString):
2839 tag_default = tag_encode(18)
2841 asn1_type_name = "NumericString"
2844 class PrintableString(CommonString):
2846 tag_default = tag_encode(19)
2848 asn1_type_name = "PrintableString"
2851 class TeletexString(CommonString):
2853 tag_default = tag_encode(20)
2855 asn1_type_name = "TeletexString"
2858 class T61String(TeletexString):
2860 asn1_type_name = "T61String"
2863 class VideotexString(CommonString):
2865 tag_default = tag_encode(21)
2866 encoding = "iso-8859-1"
2867 asn1_type_name = "VideotexString"
2870 class IA5String(CommonString):
2872 tag_default = tag_encode(22)
2874 asn1_type_name = "IA5"
2877 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
2878 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
2879 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
2882 class UTCTime(CommonString):
2883 """``UTCTime`` datetime type
2885 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2886 UTCTime UTCTime 2017-09-30T22:07:50
2892 datetime.datetime(2017, 9, 30, 22, 7, 50)
2893 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2894 datetime.datetime(1957, 9, 30, 22, 7, 50)
2897 tag_default = tag_encode(23)
2899 asn1_type_name = "UTCTime"
2901 fmt = "%y%m%d%H%M%SZ"
2911 bounds=None, # dummy argument, workability for OctetString.decode
2914 :param value: set the value. Either datetime type, or
2915 :py:class:`pyderasn.UTCTime` object
2916 :param bytes impl: override default tag with ``IMPLICIT`` one
2917 :param bytes expl: override default tag with ``EXPLICIT`` one
2918 :param default: set default value. Type same as in ``value``
2919 :param bool optional: is object ``OPTIONAL`` in sequence
2921 super(UTCTime, self).__init__(
2929 if value is not None:
2930 self._value = self._value_sanitize(value)
2931 if default is not None:
2932 default = self._value_sanitize(default)
2933 self.default = self.__class__(
2938 if self._value is None:
2939 self._value = default
2941 def _value_sanitize(self, value):
2942 if isinstance(value, self.__class__):
2944 if isinstance(value, datetime):
2945 return value.strftime(self.fmt).encode("ascii")
2946 if isinstance(value, binary_type):
2947 value_decoded = value.decode("ascii")
2948 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
2950 datetime.strptime(value_decoded, self.fmt)
2952 raise DecodeError("invalid UTCTime format")
2955 raise DecodeError("invalid UTCTime length")
2956 raise InvalidValueType((self.__class__, datetime))
2958 def __eq__(self, their):
2959 if isinstance(their, binary_type):
2960 return self._value == their
2961 if isinstance(their, datetime):
2962 return self.todatetime() == their
2963 if not isinstance(their, self.__class__):
2966 self._value == their._value and
2967 self.tag == their.tag and
2968 self._expl == their._expl
2971 def todatetime(self):
2972 """Convert to datetime
2976 Pay attention that UTCTime can not hold full year, so all years
2977 having < 50 years are treated as 20xx, 19xx otherwise, according
2978 to X.509 recomendation.
2980 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
2981 year = value.year % 100
2983 year=(2000 + year) if year < 50 else (1900 + year),
2987 minute=value.minute,
2988 second=value.second,
2992 return pp_console_row(next(self.pps()))
2994 def pps(self, decode_path=()):
2996 asn1_type_name=self.asn1_type_name,
2997 obj_name=self.__class__.__name__,
2998 decode_path=decode_path,
2999 value=self.todatetime().isoformat() if self.ready else None,
3000 optional=self.optional,
3001 default=self == self.default,
3002 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3003 expl=None if self._expl is None else tag_decode(self._expl),
3011 class GeneralizedTime(UTCTime):
3012 """``GeneralizedTime`` datetime type
3014 This type is similar to :py:class:`pyderasn.UTCTime`.
3016 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3017 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3019 '20170930220750.000123Z'
3020 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3021 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3024 tag_default = tag_encode(24)
3025 asn1_type_name = "GeneralizedTime"
3027 fmt = "%Y%m%d%H%M%SZ"
3028 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3030 def _value_sanitize(self, value):
3031 if isinstance(value, self.__class__):
3033 if isinstance(value, datetime):
3034 return value.strftime(
3035 self.fmt_ms if value.microsecond > 0 else self.fmt
3037 if isinstance(value, binary_type):
3038 value_decoded = value.decode("ascii")
3039 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3041 datetime.strptime(value_decoded, self.fmt)
3044 "invalid GeneralizedTime (without ms) format",
3047 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3049 datetime.strptime(value_decoded, self.fmt_ms)
3052 "invalid GeneralizedTime (with ms) format",
3057 "invalid GeneralizedTime length",
3058 klass=self.__class__,
3060 raise InvalidValueType((self.__class__, datetime))
3062 def todatetime(self):
3063 value = self._value.decode("ascii")
3064 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3065 return datetime.strptime(value, self.fmt)
3066 return datetime.strptime(value, self.fmt_ms)
3069 class GraphicString(CommonString):
3071 tag_default = tag_encode(25)
3072 encoding = "iso-8859-1"
3073 asn1_type_name = "GraphicString"
3076 class VisibleString(CommonString):
3078 tag_default = tag_encode(26)
3080 asn1_type_name = "VisibleString"
3083 class ISO646String(VisibleString):
3085 asn1_type_name = "ISO646String"
3088 class GeneralString(CommonString):
3090 tag_default = tag_encode(27)
3091 encoding = "iso-8859-1"
3092 asn1_type_name = "GeneralString"
3095 class UniversalString(CommonString):
3097 tag_default = tag_encode(28)
3098 encoding = "utf-32-be"
3099 asn1_type_name = "UniversalString"
3102 class BMPString(CommonString):
3104 tag_default = tag_encode(30)
3105 encoding = "utf-16-be"
3106 asn1_type_name = "BMPString"
3110 """``CHOICE`` special type
3114 class GeneralName(Choice):
3116 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
3117 ('dNSName', IA5String(impl=tag_ctxp(2))),
3120 >>> gn = GeneralName()
3122 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3123 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3124 >>> gn["dNSName"] = IA5String("bar.baz")
3125 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3126 >>> gn["rfc822Name"]
3129 [2] IA5String IA5 bar.baz
3132 >>> gn.value == gn["dNSName"]
3135 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3137 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3138 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3140 __slots__ = ("specs",)
3142 asn1_type_name = "CHOICE"
3155 :param value: set the value. Either ``(choice, value)`` tuple, or
3156 :py:class:`pyderasn.Choice` object
3157 :param bytes impl: can not be set, do **not** use it
3158 :param bytes expl: override default tag with ``EXPLICIT`` one
3159 :param default: set default value. Type same as in ``value``
3160 :param bool optional: is object ``OPTIONAL`` in sequence
3162 if impl is not None:
3163 raise ValueError("no implicit tag allowed for CHOICE")
3164 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3166 schema = getattr(self, "schema", ())
3167 if len(schema) == 0:
3168 raise ValueError("schema must be specified")
3170 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3173 if value is not None:
3174 self._value = self._value_sanitize(value)
3175 if default is not None:
3176 default_value = self._value_sanitize(default)
3177 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3178 default_obj.specs = self.specs
3179 default_obj._value = default_value
3180 self.default = default_obj
3182 self._value = default_obj.copy()._value
3184 def _value_sanitize(self, value):
3185 if isinstance(value, self.__class__):
3187 if isinstance(value, tuple) and len(value) == 2:
3189 spec = self.specs.get(choice)
3191 raise ObjUnknown(choice)
3192 if not isinstance(obj, spec.__class__):
3193 raise InvalidValueType((spec,))
3194 return (choice, spec(obj))
3195 raise InvalidValueType((self.__class__, tuple))
3199 return self._value is not None and self._value[1].ready
3202 obj = self.__class__(schema=self.specs)
3203 obj._expl = self._expl
3204 obj.default = self.default
3205 obj.optional = self.optional
3206 obj.offset = self.offset
3207 obj.llen = self.llen
3208 obj.vlen = self.vlen
3210 if value is not None:
3211 obj._value = (value[0], value[1].copy())
3214 def __eq__(self, their):
3215 if isinstance(their, tuple) and len(their) == 2:
3216 return self._value == their
3217 if not isinstance(their, self.__class__):
3220 self.specs == their.specs and
3221 self._value == their._value
3231 return self.__class__(
3234 expl=self._expl if expl is None else expl,
3235 default=self.default if default is None else default,
3236 optional=self.optional if optional is None else optional,
3241 self._assert_ready()
3242 return self._value[0]
3246 self._assert_ready()
3247 return self._value[1]
3249 def __getitem__(self, key):
3250 if key not in self.specs:
3251 raise ObjUnknown(key)
3252 if self._value is None:
3254 choice, value = self._value
3259 def __setitem__(self, key, value):
3260 spec = self.specs.get(key)
3262 raise ObjUnknown(key)
3263 if not isinstance(value, spec.__class__):
3264 raise InvalidValueType((spec.__class__,))
3265 self._value = (key, spec(value))
3273 return self._value[1].decoded if self.ready else False
3276 self._assert_ready()
3277 return self._value[1].encode()
3279 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
3280 for choice, spec in self.specs.items():
3282 value, tail = spec.decode(
3286 decode_path=decode_path + (choice,),
3287 defines_by_path=defines_by_path,
3291 obj = self.__class__(
3294 default=self.default,
3295 optional=self.optional,
3296 _decoded=(offset, 0, value.tlvlen),
3298 obj._value = (choice, value)
3301 klass=self.__class__,
3302 decode_path=decode_path,
3307 value = pp_console_row(next(self.pps()))
3309 value = "%s[%r]" % (value, self.value)
3312 def pps(self, decode_path=()):
3314 asn1_type_name=self.asn1_type_name,
3315 obj_name=self.__class__.__name__,
3316 decode_path=decode_path,
3317 value=self.choice if self.ready else None,
3318 optional=self.optional,
3319 default=self == self.default,
3320 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3321 expl=None if self._expl is None else tag_decode(self._expl),
3328 yield self.value.pps(decode_path=decode_path + (self.choice,))
3331 class PrimitiveTypes(Choice):
3332 """Predefined ``CHOICE`` for all generic primitive types
3334 It could be useful for general decoding of some unspecified values:
3336 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3337 OCTET STRING 3 bytes 666f6f
3338 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3342 schema = tuple((klass.__name__, klass()) for klass in (
3367 """``ANY`` special type
3369 >>> Any(Integer(-123))
3371 >>> a = Any(OctetString(b"hello world").encode())
3372 ANY 040b68656c6c6f20776f726c64
3373 >>> hexenc(bytes(a))
3374 b'0x040x0bhello world'
3376 __slots__ = ("defined",)
3377 tag_default = tag_encode(0)
3378 asn1_type_name = "ANY"
3388 :param value: set the value. Either any kind of pyderasn's
3389 **ready** object, or bytes. Pay attention that
3390 **no** validation is performed is raw binary value
3392 :param bytes expl: override default tag with ``EXPLICIT`` one
3393 :param bool optional: is object ``OPTIONAL`` in sequence
3395 super(Any, self).__init__(None, expl, None, optional, _decoded)
3396 self._value = None if value is None else self._value_sanitize(value)
3399 def _value_sanitize(self, value):
3400 if isinstance(value, self.__class__):
3402 if isinstance(value, Obj):
3403 return value.encode()
3404 if isinstance(value, binary_type):
3406 raise InvalidValueType((self.__class__, Obj, binary_type))
3410 return self._value is not None
3413 obj = self.__class__()
3414 obj._value = self._value
3416 obj._expl = self._expl
3417 obj.optional = self.optional
3418 obj.offset = self.offset
3419 obj.llen = self.llen
3420 obj.vlen = self.vlen
3423 def __eq__(self, their):
3424 if isinstance(their, binary_type):
3425 return self._value == their
3426 if issubclass(their.__class__, Any):
3427 return self._value == their._value
3436 return self.__class__(
3438 expl=self._expl if expl is None else expl,
3439 optional=self.optional if optional is None else optional,
3442 def __bytes__(self):
3443 self._assert_ready()
3451 self._assert_ready()
3454 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
3456 t, tlen, lv = tag_strip(tlv)
3457 l, llen, v = len_decode(lv)
3458 except DecodeError as err:
3459 raise err.__class__(
3461 klass=self.__class__,
3462 decode_path=decode_path,
3466 raise NotEnoughData(
3467 "encoded length is longer than data",
3468 klass=self.__class__,
3469 decode_path=decode_path,
3472 tlvlen = tlen + llen + l
3473 v, tail = tlv[:tlvlen], v[l:]
3474 obj = self.__class__(
3477 optional=self.optional,
3478 _decoded=(offset, 0, tlvlen),
3484 return pp_console_row(next(self.pps()))
3486 def pps(self, decode_path=()):
3488 asn1_type_name=self.asn1_type_name,
3489 obj_name=self.__class__.__name__,
3490 decode_path=decode_path,
3491 blob=self._value if self.ready else None,
3492 optional=self.optional,
3493 default=self == self.default,
3494 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3495 expl=None if self._expl is None else tag_decode(self._expl),
3500 expl_offset=self.expl_offset if self.expled else None,
3501 expl_tlen=self.expl_tlen if self.expled else None,
3502 expl_llen=self.expl_llen if self.expled else None,
3503 expl_vlen=self.expl_vlen if self.expled else None,
3505 defined_by, defined = self.defined or (None, None)
3506 if defined_by is not None:
3508 decode_path=decode_path + (decode_path_defby(defined_by),)
3512 ########################################################################
3513 # ASN.1 constructed types
3514 ########################################################################
3516 def get_def_by_path(defines_by_path, sub_decode_path):
3517 """Get define by decode path
3519 for path, define in defines_by_path:
3520 if len(path) != len(sub_decode_path):
3522 for p1, p2 in zip(path, sub_decode_path):
3523 if (p1 != any) and (p1 != p2):
3529 class Sequence(Obj):
3530 """``SEQUENCE`` structure type
3532 You have to make specification of sequence::
3534 class Extension(Sequence):
3536 ("extnID", ObjectIdentifier()),
3537 ("critical", Boolean(default=False)),
3538 ("extnValue", OctetString()),
3541 Then, you can work with it as with dictionary.
3543 >>> ext = Extension()
3544 >>> Extension().specs
3546 ('extnID', OBJECT IDENTIFIER),
3547 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3548 ('extnValue', OCTET STRING),
3550 >>> ext["extnID"] = "1.2.3"
3551 Traceback (most recent call last):
3552 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3553 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3555 You can know if sequence is ready to be encoded:
3560 Traceback (most recent call last):
3561 pyderasn.ObjNotReady: object is not ready: extnValue
3562 >>> ext["extnValue"] = OctetString(b"foobar")
3566 Value you want to assign, must have the same **type** as in
3567 corresponding specification, but it can have different tags,
3568 optional/default attributes -- they will be taken from specification
3571 class TBSCertificate(Sequence):
3573 ("version", Version(expl=tag_ctxc(0), default="v1")),
3576 >>> tbs = TBSCertificate()
3577 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3579 You can know if value exists/set in the sequence and take its value:
3581 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3584 OBJECT IDENTIFIER 1.2.3
3586 But pay attention that if value has default, then it won't be (not
3587 in) in the sequence (because ``DEFAULT`` must not be encoded in
3588 DER), but you can read its value:
3590 >>> "critical" in ext, ext["critical"]
3591 (False, BOOLEAN False)
3592 >>> ext["critical"] = Boolean(True)
3593 >>> "critical" in ext, ext["critical"]
3594 (True, BOOLEAN True)
3596 All defaulted values are always optional.
3600 When decoded DER contains defaulted value inside, then
3601 technically this is not valid DER encoding. But we allow
3602 and pass it. Of course reencoding of that kind of DER will
3603 result in different binary representation (validly without
3604 defaulted value inside).
3606 Two sequences are equal if they have equal specification (schema),
3607 implicit/explicit tagging and the same values.
3609 __slots__ = ("specs",)
3610 tag_default = tag_encode(form=TagFormConstructed, num=16)
3611 asn1_type_name = "SEQUENCE"
3623 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3625 schema = getattr(self, "schema", ())
3627 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3630 if value is not None:
3631 self._value = self._value_sanitize(value)
3632 if default is not None:
3633 default_value = self._value_sanitize(default)
3634 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3635 default_obj.specs = self.specs
3636 default_obj._value = default_value
3637 self.default = default_obj
3639 self._value = default_obj.copy()._value
3641 def _value_sanitize(self, value):
3642 if not issubclass(value.__class__, Sequence):
3643 raise InvalidValueType((Sequence,))
3648 for name, spec in self.specs.items():
3649 value = self._value.get(name)
3660 obj = self.__class__(schema=self.specs)
3662 obj._expl = self._expl
3663 obj.default = self.default
3664 obj.optional = self.optional
3665 obj.offset = self.offset
3666 obj.llen = self.llen
3667 obj.vlen = self.vlen
3668 obj._value = {k: v.copy() for k, v in self._value.items()}
3671 def __eq__(self, their):
3672 if not isinstance(their, self.__class__):
3675 self.specs == their.specs and
3676 self.tag == their.tag and
3677 self._expl == their._expl and
3678 self._value == their._value
3689 return self.__class__(
3692 impl=self.tag if impl is None else impl,
3693 expl=self._expl if expl is None else expl,
3694 default=self.default if default is None else default,
3695 optional=self.optional if optional is None else optional,
3698 def __contains__(self, key):
3699 return key in self._value
3701 def __setitem__(self, key, value):
3702 spec = self.specs.get(key)
3704 raise ObjUnknown(key)
3706 self._value.pop(key, None)
3708 if not isinstance(value, spec.__class__):
3709 raise InvalidValueType((spec.__class__,))
3710 value = spec(value=value)
3711 if spec.default is not None and value == spec.default:
3712 self._value.pop(key, None)
3714 self._value[key] = value
3716 def __getitem__(self, key):
3717 value = self._value.get(key)
3718 if value is not None:
3720 spec = self.specs.get(key)
3722 raise ObjUnknown(key)
3723 if spec.default is not None:
3727 def _encoded_values(self):
3729 for name, spec in self.specs.items():
3730 value = self._value.get(name)
3734 raise ObjNotReady(name)
3735 raws.append(value.encode())
3739 v = b"".join(self._encoded_values())
3740 return b"".join((self.tag, len_encode(len(v)), v))
3742 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
3744 t, tlen, lv = tag_strip(tlv)
3745 except DecodeError as err:
3746 raise err.__class__(
3748 klass=self.__class__,
3749 decode_path=decode_path,
3754 klass=self.__class__,
3755 decode_path=decode_path,
3759 l, llen, v = len_decode(lv)
3760 except DecodeError as err:
3761 raise err.__class__(
3763 klass=self.__class__,
3764 decode_path=decode_path,
3768 raise NotEnoughData(
3769 "encoded length is longer than data",
3770 klass=self.__class__,
3771 decode_path=decode_path,
3774 v, tail = v[:l], v[l:]
3775 sub_offset = offset + tlen + llen
3778 for name, spec in self.specs.items():
3779 if len(v) == 0 and spec.optional:
3781 sub_decode_path = decode_path + (name,)
3783 value, v_tail = spec.decode(
3787 decode_path=sub_decode_path,
3788 defines_by_path=defines_by_path,
3795 defined = defines.pop(name, None)
3796 if defined is not None:
3797 defined_by, defined_spec = defined
3798 if issubclass(value.__class__, SequenceOf):
3799 for i, _value in enumerate(value):
3800 sub_sub_decode_path = sub_decode_path + (
3802 decode_path_defby(defined_by),
3804 defined_value, defined_tail = defined_spec.decode(
3805 memoryview(bytes(_value)),
3806 sub_offset + value.tlen + value.llen,
3808 decode_path=sub_sub_decode_path,
3809 defines_by_path=defines_by_path,
3811 if len(defined_tail) > 0:
3814 klass=self.__class__,
3815 decode_path=sub_sub_decode_path,
3818 _value.defined = (defined_by, defined_value)
3820 defined_value, defined_tail = defined_spec.decode(
3821 memoryview(bytes(value)),
3822 sub_offset + value.tlen + value.llen,
3824 decode_path=sub_decode_path + (decode_path_defby(defined_by),),
3825 defines_by_path=defines_by_path,
3827 if len(defined_tail) > 0:
3830 klass=self.__class__,
3831 decode_path=sub_decode_path + (decode_path_defby(defined_by),),
3834 value.defined = (defined_by, defined_value)
3836 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3838 if spec.default is not None and value == spec.default:
3839 # Encoded default values are not valid in DER,
3840 # but we allow that anyway
3842 values[name] = value
3844 spec_defines = getattr(spec, "defines", None)
3845 if defines_by_path is not None and spec_defines is None:
3846 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
3847 if spec_defines is not None:
3848 what, schema = spec_defines
3849 defined = schema.get(value, None)
3850 if defined is not None:
3851 defines[what] = (value, defined)
3855 klass=self.__class__,
3856 decode_path=decode_path,
3859 obj = self.__class__(
3863 default=self.default,
3864 optional=self.optional,
3865 _decoded=(offset, llen, l),
3871 value = pp_console_row(next(self.pps()))
3873 for name in self.specs:
3874 _value = self._value.get(name)
3877 cols.append(repr(_value))
3878 return "%s[%s]" % (value, ", ".join(cols))
3880 def pps(self, decode_path=()):
3882 asn1_type_name=self.asn1_type_name,
3883 obj_name=self.__class__.__name__,
3884 decode_path=decode_path,
3885 optional=self.optional,
3886 default=self == self.default,
3887 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3888 expl=None if self._expl is None else tag_decode(self._expl),
3893 expl_offset=self.expl_offset if self.expled else None,
3894 expl_tlen=self.expl_tlen if self.expled else None,
3895 expl_llen=self.expl_llen if self.expled else None,
3896 expl_vlen=self.expl_vlen if self.expled else None,
3898 for name in self.specs:
3899 value = self._value.get(name)
3902 yield value.pps(decode_path=decode_path + (name,))
3905 class Set(Sequence):
3906 """``SET`` structure type
3908 Its usage is identical to :py:class:`pyderasn.Sequence`.
3911 tag_default = tag_encode(form=TagFormConstructed, num=17)
3912 asn1_type_name = "SET"
3915 raws = self._encoded_values()
3918 return b"".join((self.tag, len_encode(len(v)), v))
3920 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
3922 t, tlen, lv = tag_strip(tlv)
3923 except DecodeError as err:
3924 raise err.__class__(
3926 klass=self.__class__,
3927 decode_path=decode_path,
3932 klass=self.__class__,
3933 decode_path=decode_path,
3937 l, llen, v = len_decode(lv)
3938 except DecodeError as err:
3939 raise err.__class__(
3941 klass=self.__class__,
3942 decode_path=decode_path,
3946 raise NotEnoughData(
3947 "encoded length is longer than data",
3948 klass=self.__class__,
3951 v, tail = v[:l], v[l:]
3952 sub_offset = offset + tlen + llen
3954 specs_items = self.specs.items
3956 for name, spec in specs_items():
3958 value, v_tail = spec.decode(
3962 decode_path=decode_path + (name,),
3963 defines_by_path=defines_by_path,
3968 value.expl_tlvlen if value.expled else value.tlvlen
3971 if spec.default is None or value != spec.default: # pragma: no cover
3972 # SeqMixing.test_encoded_default_accepted covers that place
3973 values[name] = value
3977 klass=self.__class__,
3978 decode_path=decode_path,
3981 obj = self.__class__(
3985 default=self.default,
3986 optional=self.optional,
3987 _decoded=(offset, llen, l),
3993 class SequenceOf(Obj):
3994 """``SEQUENCE OF`` sequence type
3996 For that kind of type you must specify the object it will carry on
3997 (bounds are for example here, not required)::
3999 class Ints(SequenceOf):
4004 >>> ints.append(Integer(123))
4005 >>> ints.append(Integer(234))
4007 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4008 >>> [int(i) for i in ints]
4010 >>> ints.append(Integer(345))
4011 Traceback (most recent call last):
4012 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4015 >>> ints[1] = Integer(345)
4017 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4019 Also you can initialize sequence with preinitialized values:
4021 >>> ints = Ints([Integer(123), Integer(234)])
4023 __slots__ = ("spec", "_bound_min", "_bound_max")
4024 tag_default = tag_encode(form=TagFormConstructed, num=16)
4025 asn1_type_name = "SEQUENCE OF"
4038 super(SequenceOf, self).__init__(
4046 schema = getattr(self, "schema", None)
4048 raise ValueError("schema must be specified")
4050 self._bound_min, self._bound_max = getattr(
4054 ) if bounds is None else bounds
4056 if value is not None:
4057 self._value = self._value_sanitize(value)
4058 if default is not None:
4059 default_value = self._value_sanitize(default)
4060 default_obj = self.__class__(
4065 default_obj._value = default_value
4066 self.default = default_obj
4068 self._value = default_obj.copy()._value
4070 def _value_sanitize(self, value):
4071 if issubclass(value.__class__, SequenceOf):
4072 value = value._value
4073 elif hasattr(value, "__iter__"):
4076 raise InvalidValueType((self.__class__, iter))
4077 if not self._bound_min <= len(value) <= self._bound_max:
4078 raise BoundsError(self._bound_min, len(value), self._bound_max)
4080 if not isinstance(v, self.spec.__class__):
4081 raise InvalidValueType((self.spec.__class__,))
4086 return all(v.ready for v in self._value)
4089 obj = self.__class__(schema=self.spec)
4090 obj._bound_min = self._bound_min
4091 obj._bound_max = self._bound_max
4093 obj._expl = self._expl
4094 obj.default = self.default
4095 obj.optional = self.optional
4096 obj.offset = self.offset
4097 obj.llen = self.llen
4098 obj.vlen = self.vlen
4099 obj._value = [v.copy() for v in self._value]
4102 def __eq__(self, their):
4103 if isinstance(their, self.__class__):
4105 self.spec == their.spec and
4106 self.tag == their.tag and
4107 self._expl == their._expl and
4108 self._value == their._value
4110 if hasattr(their, "__iter__"):
4111 return self._value == list(their)
4123 return self.__class__(
4127 (self._bound_min, self._bound_max)
4128 if bounds is None else bounds
4130 impl=self.tag if impl is None else impl,
4131 expl=self._expl if expl is None else expl,
4132 default=self.default if default is None else default,
4133 optional=self.optional if optional is None else optional,
4136 def __contains__(self, key):
4137 return key in self._value
4139 def append(self, value):
4140 if not isinstance(value, self.spec.__class__):
4141 raise InvalidValueType((self.spec.__class__,))
4142 if len(self._value) + 1 > self._bound_max:
4145 len(self._value) + 1,
4148 self._value.append(value)
4151 self._assert_ready()
4152 return iter(self._value)
4155 self._assert_ready()
4156 return len(self._value)
4158 def __setitem__(self, key, value):
4159 if not isinstance(value, self.spec.__class__):
4160 raise InvalidValueType((self.spec.__class__,))
4161 self._value[key] = self.spec(value=value)
4163 def __getitem__(self, key):
4164 return self._value[key]
4166 def _encoded_values(self):
4167 return [v.encode() for v in self._value]
4170 v = b"".join(self._encoded_values())
4171 return b"".join((self.tag, len_encode(len(v)), v))
4173 def _decode(self, tlv, offset=0, decode_path=(), defines_by_path=None):
4175 t, tlen, lv = tag_strip(tlv)
4176 except DecodeError as err:
4177 raise err.__class__(
4179 klass=self.__class__,
4180 decode_path=decode_path,
4185 klass=self.__class__,
4186 decode_path=decode_path,
4190 l, llen, v = len_decode(lv)
4191 except DecodeError as err:
4192 raise err.__class__(
4194 klass=self.__class__,
4195 decode_path=decode_path,
4199 raise NotEnoughData(
4200 "encoded length is longer than data",
4201 klass=self.__class__,
4202 decode_path=decode_path,
4205 v, tail = v[:l], v[l:]
4206 sub_offset = offset + tlen + llen
4210 value, v_tail = spec.decode(
4214 decode_path=decode_path + (str(len(_value)),),
4215 defines_by_path=defines_by_path,
4217 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4219 _value.append(value)
4220 obj = self.__class__(
4223 bounds=(self._bound_min, self._bound_max),
4226 default=self.default,
4227 optional=self.optional,
4228 _decoded=(offset, llen, l),
4234 pp_console_row(next(self.pps())),
4235 ", ".join(repr(v) for v in self._value),
4238 def pps(self, decode_path=()):
4240 asn1_type_name=self.asn1_type_name,
4241 obj_name=self.__class__.__name__,
4242 decode_path=decode_path,
4243 optional=self.optional,
4244 default=self == self.default,
4245 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4246 expl=None if self._expl is None else tag_decode(self._expl),
4251 expl_offset=self.expl_offset if self.expled else None,
4252 expl_tlen=self.expl_tlen if self.expled else None,
4253 expl_llen=self.expl_llen if self.expled else None,
4254 expl_vlen=self.expl_vlen if self.expled else None,
4256 for i, value in enumerate(self._value):
4257 yield value.pps(decode_path=decode_path + (str(i),))
4260 class SetOf(SequenceOf):
4261 """``SET OF`` sequence type
4263 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4266 tag_default = tag_encode(form=TagFormConstructed, num=17)
4267 asn1_type_name = "SET OF"
4270 raws = self._encoded_values()
4273 return b"".join((self.tag, len_encode(len(v)), v))
4276 def obj_by_path(pypath): # pragma: no cover
4277 """Import object specified as string Python path
4279 Modules must be separated from classes/functions with ``:``.
4281 >>> obj_by_path("foo.bar:Baz")
4282 <class 'foo.bar.Baz'>
4283 >>> obj_by_path("foo.bar:Baz.boo")
4284 <classmethod 'foo.bar.Baz.boo'>
4286 mod, objs = pypath.rsplit(":", 1)
4287 from importlib import import_module
4288 obj = import_module(mod)
4289 for obj_name in objs.split("."):
4290 obj = getattr(obj, obj_name)
4294 def generic_decoder(): # pragma: no cover
4295 # All of this below is a big hack with self references
4296 choice = PrimitiveTypes()
4297 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4298 choice.specs["SetOf"] = SetOf(schema=choice)
4300 choice.specs["SequenceOf%d" % i] = SequenceOf(
4304 choice.specs["Any"] = Any()
4306 # Class name equals to type name, to omit it from output
4307 class SEQUENCEOF(SequenceOf):
4311 def pprint_any(obj, oids=None):
4312 def _pprint_pps(pps):
4314 if hasattr(pp, "_fields"):
4315 if pp.asn1_type_name == Choice.asn1_type_name:
4317 pp_kwargs = pp._asdict()
4318 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4319 pp = _pp(**pp_kwargs)
4320 yield pp_console_row(
4326 for row in pp_console_blob(pp):
4329 for row in _pprint_pps(pp):
4331 return "\n".join(_pprint_pps(obj.pps()))
4332 return SEQUENCEOF(), pprint_any
4335 def main(): # pragma: no cover
4337 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4338 parser.add_argument(
4342 help="Skip that number of bytes from the beginning",
4344 parser.add_argument(
4346 help="Python path to dictionary with OIDs",
4348 parser.add_argument(
4350 help="Python path to schema definition to use",
4352 parser.add_argument(
4353 "--defines-by-path",
4354 help="Python path to decoder's defines_by_path",
4356 parser.add_argument(
4358 type=argparse.FileType("rb"),
4359 help="Path to DER file you want to decode",
4361 args = parser.parse_args()
4362 args.DERFile.seek(args.skip)
4363 der = memoryview(args.DERFile.read())
4364 args.DERFile.close()
4365 oids = obj_by_path(args.oids) if args.oids else {}
4367 schema = obj_by_path(args.schema)
4368 from functools import partial
4369 pprinter = partial(pprint, big_blobs=True)
4371 schema, pprinter = generic_decoder()
4372 obj, tail = schema().decode(
4375 None if args.defines_by_path is None
4376 else obj_by_path(args.defines_by_path)
4379 print(pprinter(obj, oids=oids))
4381 print("\nTrailing data: %s" % hexenc(tail))
4384 if __name__ == "__main__":