3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017-2018 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 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`defines_by_path <defines_by_path_ctx>`
216 All objects have ``pps()`` method, that is a generator of
217 :py:class:`pyderasn.PP` namedtuple, holding various raw information
218 about the object. If ``pps`` is called on sequences, then all underlying
219 ``PP`` will be yielded.
221 You can use :py:func:`pyderasn.pp_console_row` function, converting
222 those ``PP`` to human readable string. Actually exactly it is used for
223 all object ``repr``. But it is easy to write custom formatters.
225 >>> from pyderasn import pprint
226 >>> encoded = Integer(-12345).encode()
227 >>> obj, tail = Integer().decode(encoded)
228 >>> print(pprint(obj))
229 0 [1,1, 2] INTEGER -12345
236 ASN.1 structures often have ANY and OCTET STRING fields, that are
237 DEFINED BY some previously met ObjectIdentifier. This library provides
238 ability to specify mapping between some OID and field that must be
239 decoded with specific specification.
244 :py:class:`pyderasn.ObjectIdentifier` field inside
245 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
246 necessary for decoding structures. For example, CMS (:rfc:`5652`)
249 class ContentInfo(Sequence):
251 ("contentType", ContentType(defines=((("content",), {
252 id_digestedData: DigestedData(),
253 id_signedData: SignedData(),
255 ("content", Any(expl=tag_ctxc(0))),
258 ``contentType`` field tells that it defines that ``content`` must be
259 decoded with ``SignedData`` specification, if ``contentType`` equals to
260 ``id-signedData``. The same applies to ``DigestedData``. If
261 ``contentType`` contains unknown OID, then no automatic decoding is
264 You can specify multiple fields, that will be autodecoded -- that is why
265 ``defines`` kwarg is a sequence. You can specify defined field
266 relatively or absolutely to current decode path. For example ``defines``
267 for AlgorithmIdentifier of X.509's
268 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
272 id_ecPublicKey: ECParameters(),
273 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
275 (('..', 'subjectPublicKey'), {
276 id_rsaEncryption: RSAPublicKey(),
277 id_GostR3410_2001: OctetString(),
281 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
282 autodecode its parameters inside SPKI's algorithm and its public key
285 Following types can be automatically decoded (DEFINED BY):
287 * :py:class:`pyderasn.Any`
288 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
289 * :py:class:`pyderasn.OctetString`
290 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
291 ``Any``/``OctetString``-s
293 When any of those fields is automatically decoded, then ``.defined``
294 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
295 was defined, ``value`` contains corresponding decoded value. For example
296 above, ``content_info["content"].defined == (id_signedData,
299 .. _defines_by_path_ctx:
301 defines_by_path context option
302 ______________________________
304 Sometimes you either can not or do not want to explicitly set *defines*
305 in the scheme. You can dynamically apply those definitions when calling
306 ``.decode()`` method.
308 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
309 value must be sequence of following tuples::
311 (decode_path, defines)
313 where ``decode_path`` is a tuple holding so-called decode path to the
314 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
315 ``defines``, holding exactly the same value as accepted in its keyword
318 For example, again for CMS, you want to automatically decode
319 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
320 structures it may hold. Also, automatically decode ``controlSequence``
323 content_info, tail = ContentInfo().decode(data, defines_by_path=(
326 ((("content",), {id_signedData: SignedData()}),),
331 decode_path_defby(id_signedData),
336 id_cct_PKIData: PKIData(),
337 id_cct_PKIResponse: PKIResponse(),
343 decode_path_defby(id_signedData),
346 decode_path_defby(id_cct_PKIResponse),
352 id_cmc_recipientNonce: RecipientNonce(),
353 id_cmc_senderNonce: SenderNonce(),
354 id_cmc_statusInfoV2: CMCStatusInfoV2(),
355 id_cmc_transactionId: TransactionId(),
360 Pay attention for :py:func:`pyderasn.decode_path_defby` and ``any``.
361 First function is useful for path construction when some automatic
362 decoding is already done. ``any`` means literally any value it meet --
363 useful for SEQUENCE/SET OF-s.
370 .. autoclass:: pyderasn.Boolean
375 .. autoclass:: pyderasn.Integer
380 .. autoclass:: pyderasn.BitString
385 .. autoclass:: pyderasn.OctetString
390 .. autoclass:: pyderasn.Null
395 .. autoclass:: pyderasn.ObjectIdentifier
400 .. autoclass:: pyderasn.Enumerated
404 .. autoclass:: pyderasn.CommonString
408 .. autoclass:: pyderasn.UTCTime
409 :members: __init__, todatetime
413 .. autoclass:: pyderasn.GeneralizedTime
420 .. autoclass:: pyderasn.Choice
425 .. autoclass:: PrimitiveTypes
429 .. autoclass:: pyderasn.Any
437 .. autoclass:: pyderasn.Sequence
442 .. autoclass:: pyderasn.Set
447 .. autoclass:: pyderasn.SequenceOf
452 .. autoclass:: pyderasn.SetOf
458 .. autofunction:: pyderasn.abs_decode_path
459 .. autofunction:: pyderasn.hexenc
460 .. autofunction:: pyderasn.hexdec
461 .. autofunction:: pyderasn.tag_encode
462 .. autofunction:: pyderasn.tag_decode
463 .. autofunction:: pyderasn.tag_ctxp
464 .. autofunction:: pyderasn.tag_ctxc
465 .. autoclass:: pyderasn.Obj
468 from codecs import getdecoder
469 from codecs import getencoder
470 from collections import namedtuple
471 from collections import OrderedDict
472 from datetime import datetime
473 from math import ceil
475 from six import add_metaclass
476 from six import binary_type
477 from six import byte2int
478 from six import indexbytes
479 from six import int2byte
480 from six import integer_types
481 from six import iterbytes
483 from six import string_types
484 from six import text_type
485 from six.moves import xrange as six_xrange
527 "TagClassApplication",
531 "TagFormConstructed",
542 TagClassUniversal = 0
543 TagClassApplication = 1 << 6
544 TagClassContext = 1 << 7
545 TagClassPrivate = 1 << 6 | 1 << 7
547 TagFormConstructed = 1 << 5
550 TagClassApplication: "APPLICATION ",
551 TagClassPrivate: "PRIVATE ",
552 TagClassUniversal: "UNIV ",
556 ########################################################################
558 ########################################################################
560 class DecodeError(Exception):
561 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
563 :param str msg: reason of decode failing
564 :param klass: optional exact DecodeError inherited class (like
565 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
566 :py:exc:`InvalidLength`)
567 :param decode_path: tuple of strings. It contains human
568 readable names of the fields through which
569 decoding process has passed
570 :param int offset: binary offset where failure happened
572 super(DecodeError, self).__init__()
575 self.decode_path = decode_path
581 "" if self.klass is None else self.klass.__name__,
583 ("(%s)" % ".".join(self.decode_path))
584 if len(self.decode_path) > 0 else ""
586 ("(at %d)" % self.offset) if self.offset > 0 else "",
592 return "%s(%s)" % (self.__class__.__name__, self)
595 class NotEnoughData(DecodeError):
599 class TagMismatch(DecodeError):
603 class InvalidLength(DecodeError):
607 class InvalidOID(DecodeError):
611 class ObjUnknown(ValueError):
612 def __init__(self, name):
613 super(ObjUnknown, self).__init__()
617 return "object is unknown: %s" % self.name
620 return "%s(%s)" % (self.__class__.__name__, self)
623 class ObjNotReady(ValueError):
624 def __init__(self, name):
625 super(ObjNotReady, self).__init__()
629 return "object is not ready: %s" % self.name
632 return "%s(%s)" % (self.__class__.__name__, self)
635 class InvalidValueType(ValueError):
636 def __init__(self, expected_types):
637 super(InvalidValueType, self).__init__()
638 self.expected_types = expected_types
641 return "invalid value type, expected: %s" % ", ".join(
642 [repr(t) for t in self.expected_types]
646 return "%s(%s)" % (self.__class__.__name__, self)
649 class BoundsError(ValueError):
650 def __init__(self, bound_min, value, bound_max):
651 super(BoundsError, self).__init__()
652 self.bound_min = bound_min
654 self.bound_max = bound_max
657 return "unsatisfied bounds: %s <= %s <= %s" % (
664 return "%s(%s)" % (self.__class__.__name__, self)
667 ########################################################################
669 ########################################################################
671 _hexdecoder = getdecoder("hex")
672 _hexencoder = getencoder("hex")
676 """Binary data to hexadecimal string convert
678 return _hexdecoder(data)[0]
682 """Hexadecimal string to binary data convert
684 return _hexencoder(data)[0].decode("ascii")
687 def int_bytes_len(num, byte_len=8):
690 return int(ceil(float(num.bit_length()) / byte_len))
693 def zero_ended_encode(num):
694 octets = bytearray(int_bytes_len(num, 7))
696 octets[i] = num & 0x7F
700 octets[i] = 0x80 | (num & 0x7F)
706 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
707 """Encode tag to binary form
709 :param int num: tag's number
710 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
711 :py:data:`pyderasn.TagClassContext`,
712 :py:data:`pyderasn.TagClassApplication`,
713 :py:data:`pyderasn.TagClassPrivate`)
714 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
715 :py:data:`pyderasn.TagFormConstructed`)
719 return int2byte(klass | form | num)
720 # [XX|X|11111][1.......][1.......] ... [0.......]
721 return int2byte(klass | form | 31) + zero_ended_encode(num)
725 """Decode tag from binary form
729 No validation is performed, assuming that it has already passed.
731 It returns tuple with three integers, as
732 :py:func:`pyderasn.tag_encode` accepts.
734 first_octet = byte2int(tag)
735 klass = first_octet & 0xC0
736 form = first_octet & 0x20
737 if first_octet & 0x1F < 0x1F:
738 return (klass, form, first_octet & 0x1F)
740 for octet in iterbytes(tag[1:]):
743 return (klass, form, num)
747 """Create CONTEXT PRIMITIVE tag
749 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
753 """Create CONTEXT CONSTRUCTED tag
755 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
759 """Take off tag from the data
761 :returns: (encoded tag, tag length, remaining data)
764 raise NotEnoughData("no data at all")
765 if byte2int(data) & 0x1F < 31:
766 return data[:1], 1, data[1:]
771 raise DecodeError("unfinished tag")
772 if indexbytes(data, i) & 0x80 == 0:
775 return data[:i], i, data[i:]
781 octets = bytearray(int_bytes_len(l) + 1)
782 octets[0] = 0x80 | (len(octets) - 1)
783 for i in six_xrange(len(octets) - 1, 0, -1):
789 def len_decode(data):
791 raise NotEnoughData("no data at all")
792 first_octet = byte2int(data)
793 if first_octet & 0x80 == 0:
794 return first_octet, 1, data[1:]
795 octets_num = first_octet & 0x7F
796 if octets_num + 1 > len(data):
797 raise NotEnoughData("encoded length is longer than data")
799 raise DecodeError("long form instead of short one")
800 if byte2int(data[1:]) == 0:
801 raise DecodeError("leading zeros")
803 for v in iterbytes(data[1:1 + octets_num]):
806 raise DecodeError("long form instead of short one")
807 return l, 1 + octets_num, data[1 + octets_num:]
810 ########################################################################
812 ########################################################################
814 class AutoAddSlots(type):
815 def __new__(mcs, name, bases, _dict):
816 _dict["__slots__"] = _dict.get("__slots__", ())
817 return type.__new__(mcs, name, bases, _dict)
820 @add_metaclass(AutoAddSlots)
822 """Common ASN.1 object class
824 All ASN.1 types are inherited from it. It has metaclass that
825 automatically adds ``__slots__`` to all inherited classes.
846 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
847 self._expl = getattr(self, "expl", None) if expl is None else expl
848 if self.tag != self.tag_default and self._expl is not None:
850 "implicit and explicit tags can not be set simultaneously"
852 if default is not None:
854 self.optional = optional
855 self.offset, self.llen, self.vlen = _decoded
859 def ready(self): # pragma: no cover
860 """Is object ready to be encoded?
862 raise NotImplementedError()
864 def _assert_ready(self):
866 raise ObjNotReady(self.__class__.__name__)
870 """Is object decoded?
872 return (self.llen + self.vlen) > 0
874 def copy(self): # pragma: no cover
875 """Make a copy of object, safe to be mutated
877 raise NotImplementedError()
885 return self.tlen + self.llen + self.vlen
887 def __str__(self): # pragma: no cover
888 return self.__bytes__() if PY2 else self.__unicode__()
890 def __ne__(self, their):
891 return not(self == their)
893 def __gt__(self, their): # pragma: no cover
894 return not(self < their)
896 def __le__(self, their): # pragma: no cover
897 return (self == their) or (self < their)
899 def __ge__(self, their): # pragma: no cover
900 return (self == their) or (self > their)
902 def _encode(self): # pragma: no cover
903 raise NotImplementedError()
905 def _decode(self, tlv, offset, decode_path, ctx): # pragma: no cover
906 raise NotImplementedError()
910 if self._expl is None:
912 return b"".join((self._expl, len_encode(len(raw)), raw))
914 def decode(self, data, offset=0, leavemm=False, decode_path=(), ctx=None):
917 :param data: either binary or memoryview
918 :param int offset: initial data's offset
919 :param bool leavemm: do we need to leave memoryview of remaining
920 data as is, or convert it to bytes otherwise
921 :param ctx: optional :ref:`context <ctx>` governing decoding process.
922 :returns: (Obj, remaining data)
926 tlv = memoryview(data)
927 if self._expl is None:
928 obj, tail = self._decode(
931 decode_path=decode_path,
936 t, tlen, lv = tag_strip(tlv)
937 except DecodeError as err:
940 klass=self.__class__,
941 decode_path=decode_path,
946 klass=self.__class__,
947 decode_path=decode_path,
951 l, llen, v = len_decode(lv)
952 except DecodeError as err:
955 klass=self.__class__,
956 decode_path=decode_path,
961 "encoded length is longer than data",
962 klass=self.__class__,
963 decode_path=decode_path,
966 obj, tail = self._decode(
968 offset=offset + tlen + llen,
969 decode_path=decode_path,
972 return obj, (tail if leavemm else tail.tobytes())
976 return self._expl is not None
984 return len(self._expl)
988 return len(len_encode(self.tlvlen))
991 def expl_offset(self):
992 return self.offset - self.expl_tlen - self.expl_llen
999 def expl_tlvlen(self):
1000 return self.expl_tlen + self.expl_llen + self.expl_vlen
1003 def decode_path_defby(defined_by):
1004 """DEFINED BY representation inside decode path
1006 return "DEFINED BY (%s)" % defined_by
1009 ########################################################################
1011 ########################################################################
1013 PP = namedtuple("PP", (
1035 asn1_type_name="unknown",
1074 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
1077 cols.append("%5d%s [%d,%d,%4d]" % (
1080 " " if pp.expl_offset is None else
1081 ("-%d" % (pp.offset - pp.expl_offset))
1087 if len(pp.decode_path) > 0:
1088 cols.append(" ." * (len(pp.decode_path)))
1089 cols.append("%s:" % pp.decode_path[-1])
1090 if pp.expl is not None:
1091 klass, _, num = pp.expl
1092 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
1093 if pp.impl is not None:
1094 klass, _, num = pp.impl
1095 cols.append("[%s%d]" % (TagClassReprs[klass], num))
1096 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1097 cols.append(pp.obj_name)
1098 cols.append(pp.asn1_type_name)
1099 if pp.value is not None:
1102 oids is not None and
1103 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1106 value = "%s (%s)" % (oids[value], pp.value)
1109 if isinstance(pp.blob, binary_type):
1110 cols.append(hexenc(pp.blob))
1111 elif isinstance(pp.blob, tuple):
1112 cols.append(", ".join(pp.blob))
1114 cols.append("OPTIONAL")
1116 cols.append("DEFAULT")
1117 return " ".join(cols)
1120 def pp_console_blob(pp):
1121 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1122 if len(pp.decode_path) > 0:
1123 cols.append(" ." * (len(pp.decode_path) + 1))
1124 if isinstance(pp.blob, binary_type):
1125 blob = hexenc(pp.blob).upper()
1126 for i in range(0, len(blob), 32):
1127 chunk = blob[i:i + 32]
1128 yield " ".join(cols + [":".join(
1129 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1131 elif isinstance(pp.blob, tuple):
1132 yield " ".join(cols + [", ".join(pp.blob)])
1135 def pprint(obj, oids=None, big_blobs=False):
1136 """Pretty print object
1138 :param Obj obj: object you want to pretty print
1139 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1140 from it is met, then its humand readable form is printed
1141 :param big_blobs: if large binary objects are met (like OctetString
1142 values), do we need to print them too, on separate
1145 def _pprint_pps(pps):
1147 if hasattr(pp, "_fields"):
1149 yield pp_console_row(
1155 for row in pp_console_blob(pp):
1158 yield pp_console_row(pp, oids=oids, with_offsets=True)
1160 for row in _pprint_pps(pp):
1162 return "\n".join(_pprint_pps(obj.pps()))
1165 ########################################################################
1166 # ASN.1 primitive types
1167 ########################################################################
1170 """``BOOLEAN`` boolean type
1172 >>> b = Boolean(True)
1174 >>> b == Boolean(True)
1180 tag_default = tag_encode(1)
1181 asn1_type_name = "BOOLEAN"
1193 :param value: set the value. Either boolean type, or
1194 :py:class:`pyderasn.Boolean` object
1195 :param bytes impl: override default tag with ``IMPLICIT`` one
1196 :param bytes expl: override default tag with ``EXPLICIT`` one
1197 :param default: set default value. Type same as in ``value``
1198 :param bool optional: is object ``OPTIONAL`` in sequence
1200 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1201 self._value = None if value is None else self._value_sanitize(value)
1202 if default is not None:
1203 default = self._value_sanitize(default)
1204 self.default = self.__class__(
1210 self._value = default
1212 def _value_sanitize(self, value):
1213 if issubclass(value.__class__, Boolean):
1215 if isinstance(value, bool):
1217 raise InvalidValueType((self.__class__, bool))
1221 return self._value is not None
1224 obj = self.__class__()
1225 obj._value = self._value
1227 obj._expl = self._expl
1228 obj.default = self.default
1229 obj.optional = self.optional
1230 obj.offset = self.offset
1231 obj.llen = self.llen
1232 obj.vlen = self.vlen
1235 def __nonzero__(self):
1236 self._assert_ready()
1240 self._assert_ready()
1243 def __eq__(self, their):
1244 if isinstance(their, bool):
1245 return self._value == their
1246 if not issubclass(their.__class__, Boolean):
1249 self._value == their._value and
1250 self.tag == their.tag and
1251 self._expl == their._expl
1262 return self.__class__(
1264 impl=self.tag if impl is None else impl,
1265 expl=self._expl if expl is None else expl,
1266 default=self.default if default is None else default,
1267 optional=self.optional if optional is None else optional,
1271 self._assert_ready()
1275 (b"\xFF" if self._value else b"\x00"),
1278 def _decode(self, tlv, offset, decode_path, ctx):
1280 t, _, lv = tag_strip(tlv)
1281 except DecodeError as err:
1282 raise err.__class__(
1284 klass=self.__class__,
1285 decode_path=decode_path,
1290 klass=self.__class__,
1291 decode_path=decode_path,
1295 l, _, v = len_decode(lv)
1296 except DecodeError as err:
1297 raise err.__class__(
1299 klass=self.__class__,
1300 decode_path=decode_path,
1304 raise InvalidLength(
1305 "Boolean's length must be equal to 1",
1306 klass=self.__class__,
1307 decode_path=decode_path,
1311 raise NotEnoughData(
1312 "encoded length is longer than data",
1313 klass=self.__class__,
1314 decode_path=decode_path,
1317 first_octet = byte2int(v)
1318 if first_octet == 0:
1320 elif first_octet == 0xFF:
1324 "unacceptable Boolean value",
1325 klass=self.__class__,
1326 decode_path=decode_path,
1329 obj = self.__class__(
1333 default=self.default,
1334 optional=self.optional,
1335 _decoded=(offset, 1, 1),
1340 return pp_console_row(next(self.pps()))
1342 def pps(self, decode_path=()):
1344 asn1_type_name=self.asn1_type_name,
1345 obj_name=self.__class__.__name__,
1346 decode_path=decode_path,
1347 value=str(self._value) if self.ready else None,
1348 optional=self.optional,
1349 default=self == self.default,
1350 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1351 expl=None if self._expl is None else tag_decode(self._expl),
1356 expl_offset=self.expl_offset if self.expled else None,
1357 expl_tlen=self.expl_tlen if self.expled else None,
1358 expl_llen=self.expl_llen if self.expled else None,
1359 expl_vlen=self.expl_vlen if self.expled else None,
1364 """``INTEGER`` integer type
1366 >>> b = Integer(-123)
1368 >>> b == Integer(-123)
1373 >>> Integer(2, bounds=(1, 3))
1375 >>> Integer(5, bounds=(1, 3))
1376 Traceback (most recent call last):
1377 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1381 class Version(Integer):
1388 >>> v = Version("v1")
1395 {'v3': 2, 'v1': 0, 'v2': 1}
1397 __slots__ = ("specs", "_bound_min", "_bound_max")
1398 tag_default = tag_encode(2)
1399 asn1_type_name = "INTEGER"
1413 :param value: set the value. Either integer type, named value
1414 (if ``schema`` is specified in the class), or
1415 :py:class:`pyderasn.Integer` object
1416 :param bounds: set ``(MIN, MAX)`` value constraint.
1417 (-inf, +inf) by default
1418 :param bytes impl: override default tag with ``IMPLICIT`` one
1419 :param bytes expl: override default tag with ``EXPLICIT`` one
1420 :param default: set default value. Type same as in ``value``
1421 :param bool optional: is object ``OPTIONAL`` in sequence
1423 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1425 specs = getattr(self, "schema", {}) if _specs is None else _specs
1426 self.specs = specs if isinstance(specs, dict) else dict(specs)
1427 self._bound_min, self._bound_max = getattr(
1430 (float("-inf"), float("+inf")),
1431 ) if bounds is None else bounds
1432 if value is not None:
1433 self._value = self._value_sanitize(value)
1434 if default is not None:
1435 default = self._value_sanitize(default)
1436 self.default = self.__class__(
1442 if self._value is None:
1443 self._value = default
1445 def _value_sanitize(self, value):
1446 if issubclass(value.__class__, Integer):
1447 value = value._value
1448 elif isinstance(value, integer_types):
1450 elif isinstance(value, str):
1451 value = self.specs.get(value)
1453 raise ObjUnknown("integer value: %s" % value)
1455 raise InvalidValueType((self.__class__, int, str))
1456 if not self._bound_min <= value <= self._bound_max:
1457 raise BoundsError(self._bound_min, value, self._bound_max)
1462 return self._value is not None
1465 obj = self.__class__(_specs=self.specs)
1466 obj._value = self._value
1467 obj._bound_min = self._bound_min
1468 obj._bound_max = self._bound_max
1470 obj._expl = self._expl
1471 obj.default = self.default
1472 obj.optional = self.optional
1473 obj.offset = self.offset
1474 obj.llen = self.llen
1475 obj.vlen = self.vlen
1479 self._assert_ready()
1480 return int(self._value)
1483 self._assert_ready()
1486 bytes(self._expl or b"") +
1487 str(self._value).encode("ascii"),
1490 def __eq__(self, their):
1491 if isinstance(their, integer_types):
1492 return self._value == their
1493 if not issubclass(their.__class__, Integer):
1496 self._value == their._value and
1497 self.tag == their.tag and
1498 self._expl == their._expl
1501 def __lt__(self, their):
1502 return self._value < their._value
1506 for name, value in self.specs.items():
1507 if value == self._value:
1519 return self.__class__(
1522 (self._bound_min, self._bound_max)
1523 if bounds is None else bounds
1525 impl=self.tag if impl is None else impl,
1526 expl=self._expl if expl is None else expl,
1527 default=self.default if default is None else default,
1528 optional=self.optional if optional is None else optional,
1533 self._assert_ready()
1537 octets = bytearray([0])
1541 octets = bytearray()
1543 octets.append((value & 0xFF) ^ 0xFF)
1545 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1548 octets = bytearray()
1550 octets.append(value & 0xFF)
1552 if octets[-1] & 0x80 > 0:
1555 octets = bytes(octets)
1557 bytes_len = ceil(value.bit_length() / 8) or 1
1560 octets = value.to_bytes(
1565 except OverflowError:
1569 return b"".join((self.tag, len_encode(len(octets)), octets))
1571 def _decode(self, tlv, offset, decode_path, ctx):
1573 t, _, lv = tag_strip(tlv)
1574 except DecodeError as err:
1575 raise err.__class__(
1577 klass=self.__class__,
1578 decode_path=decode_path,
1583 klass=self.__class__,
1584 decode_path=decode_path,
1588 l, llen, v = len_decode(lv)
1589 except DecodeError as err:
1590 raise err.__class__(
1592 klass=self.__class__,
1593 decode_path=decode_path,
1597 raise NotEnoughData(
1598 "encoded length is longer than data",
1599 klass=self.__class__,
1600 decode_path=decode_path,
1604 raise NotEnoughData(
1606 klass=self.__class__,
1607 decode_path=decode_path,
1610 v, tail = v[:l], v[l:]
1611 first_octet = byte2int(v)
1613 second_octet = byte2int(v[1:])
1615 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1616 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1619 "non normalized integer",
1620 klass=self.__class__,
1621 decode_path=decode_path,
1626 if first_octet & 0x80 > 0:
1627 octets = bytearray()
1628 for octet in bytearray(v):
1629 octets.append(octet ^ 0xFF)
1630 for octet in octets:
1631 value = (value << 8) | octet
1635 for octet in bytearray(v):
1636 value = (value << 8) | octet
1638 value = int.from_bytes(v, byteorder="big", signed=True)
1640 obj = self.__class__(
1642 bounds=(self._bound_min, self._bound_max),
1645 default=self.default,
1646 optional=self.optional,
1648 _decoded=(offset, llen, l),
1650 except BoundsError as err:
1653 klass=self.__class__,
1654 decode_path=decode_path,
1660 return pp_console_row(next(self.pps()))
1662 def pps(self, decode_path=()):
1664 asn1_type_name=self.asn1_type_name,
1665 obj_name=self.__class__.__name__,
1666 decode_path=decode_path,
1667 value=(self.named or str(self._value)) if self.ready else None,
1668 optional=self.optional,
1669 default=self == self.default,
1670 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1671 expl=None if self._expl is None else tag_decode(self._expl),
1676 expl_offset=self.expl_offset if self.expled else None,
1677 expl_tlen=self.expl_tlen if self.expled else None,
1678 expl_llen=self.expl_llen if self.expled else None,
1679 expl_vlen=self.expl_vlen if self.expled else None,
1683 class BitString(Obj):
1684 """``BIT STRING`` bit string type
1686 >>> BitString(b"hello world")
1687 BIT STRING 88 bits 68656c6c6f20776f726c64
1690 >>> b == b"hello world"
1695 >>> b = BitString("'010110000000'B")
1696 BIT STRING 12 bits 5800
1699 >>> b[0], b[1], b[2], b[3]
1700 (False, True, False, True)
1704 [False, True, False, True, True, False, False, False, False, False, False, False]
1708 class KeyUsage(BitString):
1710 ('digitalSignature', 0),
1711 ('nonRepudiation', 1),
1712 ('keyEncipherment', 2),
1715 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1716 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1718 ['nonRepudiation', 'keyEncipherment']
1720 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1722 __slots__ = ("specs", "defined")
1723 tag_default = tag_encode(3)
1724 asn1_type_name = "BIT STRING"
1737 :param value: set the value. Either binary type, tuple of named
1738 values (if ``schema`` is specified in the class),
1739 string in ``'XXX...'B`` form, or
1740 :py:class:`pyderasn.BitString` object
1741 :param bytes impl: override default tag with ``IMPLICIT`` one
1742 :param bytes expl: override default tag with ``EXPLICIT`` one
1743 :param default: set default value. Type same as in ``value``
1744 :param bool optional: is object ``OPTIONAL`` in sequence
1746 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1747 specs = getattr(self, "schema", {}) if _specs is None else _specs
1748 self.specs = specs if isinstance(specs, dict) else dict(specs)
1749 self._value = None if value is None else self._value_sanitize(value)
1750 if default is not None:
1751 default = self._value_sanitize(default)
1752 self.default = self.__class__(
1758 self._value = default
1761 def _bits2octets(self, bits):
1762 if len(self.specs) > 0:
1763 bits = bits.rstrip("0")
1765 bits += "0" * ((8 - (bit_len % 8)) % 8)
1766 octets = bytearray(len(bits) // 8)
1767 for i in six_xrange(len(octets)):
1768 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1769 return bit_len, bytes(octets)
1771 def _value_sanitize(self, value):
1772 if issubclass(value.__class__, BitString):
1774 if isinstance(value, (string_types, binary_type)):
1776 isinstance(value, string_types) and
1777 value.startswith("'") and
1778 value.endswith("'B")
1781 if not set(value) <= set(("0", "1")):
1782 raise ValueError("B's coding contains unacceptable chars")
1783 return self._bits2octets(value)
1784 elif isinstance(value, binary_type):
1785 return (len(value) * 8, value)
1787 raise InvalidValueType((
1792 if isinstance(value, tuple):
1795 isinstance(value[0], integer_types) and
1796 isinstance(value[1], binary_type)
1801 bit = self.specs.get(name)
1803 raise ObjUnknown("BitString value: %s" % name)
1806 return self._bits2octets("")
1808 return self._bits2octets("".join(
1809 ("1" if bit in bits else "0")
1810 for bit in six_xrange(max(bits) + 1)
1812 raise InvalidValueType((self.__class__, binary_type, string_types))
1816 return self._value is not None
1819 obj = self.__class__(_specs=self.specs)
1821 if value is not None:
1822 value = (value[0], value[1])
1825 obj._expl = self._expl
1826 obj.default = self.default
1827 obj.optional = self.optional
1828 obj.offset = self.offset
1829 obj.llen = self.llen
1830 obj.vlen = self.vlen
1834 self._assert_ready()
1835 for i in six_xrange(self._value[0]):
1840 self._assert_ready()
1841 return self._value[0]
1843 def __bytes__(self):
1844 self._assert_ready()
1845 return self._value[1]
1847 def __eq__(self, their):
1848 if isinstance(their, bytes):
1849 return self._value[1] == their
1850 if not issubclass(their.__class__, BitString):
1853 self._value == their._value and
1854 self.tag == their.tag and
1855 self._expl == their._expl
1860 return [name for name, bit in self.specs.items() if self[bit]]
1870 return self.__class__(
1872 impl=self.tag if impl is None else impl,
1873 expl=self._expl if expl is None else expl,
1874 default=self.default if default is None else default,
1875 optional=self.optional if optional is None else optional,
1879 def __getitem__(self, key):
1880 if isinstance(key, int):
1881 bit_len, octets = self._value
1885 byte2int(memoryview(octets)[key // 8:]) >>
1888 if isinstance(key, string_types):
1889 value = self.specs.get(key)
1891 raise ObjUnknown("BitString value: %s" % key)
1893 raise InvalidValueType((int, str))
1896 self._assert_ready()
1897 bit_len, octets = self._value
1900 len_encode(len(octets) + 1),
1901 int2byte((8 - bit_len % 8) % 8),
1905 def _decode(self, tlv, offset, decode_path, ctx):
1907 t, _, lv = tag_strip(tlv)
1908 except DecodeError as err:
1909 raise err.__class__(
1911 klass=self.__class__,
1912 decode_path=decode_path,
1917 klass=self.__class__,
1918 decode_path=decode_path,
1922 l, llen, v = len_decode(lv)
1923 except DecodeError as err:
1924 raise err.__class__(
1926 klass=self.__class__,
1927 decode_path=decode_path,
1931 raise NotEnoughData(
1932 "encoded length is longer than data",
1933 klass=self.__class__,
1934 decode_path=decode_path,
1938 raise NotEnoughData(
1940 klass=self.__class__,
1941 decode_path=decode_path,
1944 pad_size = byte2int(v)
1945 if l == 1 and pad_size != 0:
1947 "invalid empty value",
1948 klass=self.__class__,
1949 decode_path=decode_path,
1955 klass=self.__class__,
1956 decode_path=decode_path,
1959 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1962 klass=self.__class__,
1963 decode_path=decode_path,
1966 v, tail = v[:l], v[l:]
1967 obj = self.__class__(
1968 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1971 default=self.default,
1972 optional=self.optional,
1974 _decoded=(offset, llen, l),
1979 return pp_console_row(next(self.pps()))
1981 def pps(self, decode_path=()):
1985 bit_len, blob = self._value
1986 value = "%d bits" % bit_len
1987 if len(self.specs) > 0:
1988 blob = tuple(self.named)
1990 asn1_type_name=self.asn1_type_name,
1991 obj_name=self.__class__.__name__,
1992 decode_path=decode_path,
1995 optional=self.optional,
1996 default=self == self.default,
1997 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1998 expl=None if self._expl is None else tag_decode(self._expl),
2003 expl_offset=self.expl_offset if self.expled else None,
2004 expl_tlen=self.expl_tlen if self.expled else None,
2005 expl_llen=self.expl_llen if self.expled else None,
2006 expl_vlen=self.expl_vlen if self.expled else None,
2008 defined_by, defined = self.defined or (None, None)
2009 if defined_by is not None:
2011 decode_path=decode_path + (decode_path_defby(defined_by),)
2015 class OctetString(Obj):
2016 """``OCTET STRING`` binary string type
2018 >>> s = OctetString(b"hello world")
2019 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2020 >>> s == OctetString(b"hello world")
2025 >>> OctetString(b"hello", bounds=(4, 4))
2026 Traceback (most recent call last):
2027 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2028 >>> OctetString(b"hell", bounds=(4, 4))
2029 OCTET STRING 4 bytes 68656c6c
2031 __slots__ = ("_bound_min", "_bound_max", "defined")
2032 tag_default = tag_encode(4)
2033 asn1_type_name = "OCTET STRING"
2046 :param value: set the value. Either binary type, or
2047 :py:class:`pyderasn.OctetString` object
2048 :param bounds: set ``(MIN, MAX)`` value size constraint.
2049 (-inf, +inf) by default
2050 :param bytes impl: override default tag with ``IMPLICIT`` one
2051 :param bytes expl: override default tag with ``EXPLICIT`` one
2052 :param default: set default value. Type same as in ``value``
2053 :param bool optional: is object ``OPTIONAL`` in sequence
2055 super(OctetString, self).__init__(
2063 self._bound_min, self._bound_max = getattr(
2067 ) if bounds is None else bounds
2068 if value is not None:
2069 self._value = self._value_sanitize(value)
2070 if default is not None:
2071 default = self._value_sanitize(default)
2072 self.default = self.__class__(
2077 if self._value is None:
2078 self._value = default
2081 def _value_sanitize(self, value):
2082 if issubclass(value.__class__, OctetString):
2083 value = value._value
2084 elif isinstance(value, binary_type):
2087 raise InvalidValueType((self.__class__, bytes))
2088 if not self._bound_min <= len(value) <= self._bound_max:
2089 raise BoundsError(self._bound_min, len(value), self._bound_max)
2094 return self._value is not None
2097 obj = self.__class__()
2098 obj._value = self._value
2099 obj._bound_min = self._bound_min
2100 obj._bound_max = self._bound_max
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 __bytes__(self):
2111 self._assert_ready()
2114 def __eq__(self, their):
2115 if isinstance(their, binary_type):
2116 return self._value == their
2117 if not issubclass(their.__class__, OctetString):
2120 self._value == their._value and
2121 self.tag == their.tag and
2122 self._expl == their._expl
2125 def __lt__(self, their):
2126 return self._value < their._value
2137 return self.__class__(
2140 (self._bound_min, self._bound_max)
2141 if bounds is None else bounds
2143 impl=self.tag if impl is None else impl,
2144 expl=self._expl if expl is None else expl,
2145 default=self.default if default is None else default,
2146 optional=self.optional if optional is None else optional,
2150 self._assert_ready()
2153 len_encode(len(self._value)),
2157 def _decode(self, tlv, offset, decode_path, ctx):
2159 t, _, lv = tag_strip(tlv)
2160 except DecodeError as err:
2161 raise err.__class__(
2163 klass=self.__class__,
2164 decode_path=decode_path,
2169 klass=self.__class__,
2170 decode_path=decode_path,
2174 l, llen, v = len_decode(lv)
2175 except DecodeError as err:
2176 raise err.__class__(
2178 klass=self.__class__,
2179 decode_path=decode_path,
2183 raise NotEnoughData(
2184 "encoded length is longer than data",
2185 klass=self.__class__,
2186 decode_path=decode_path,
2189 v, tail = v[:l], v[l:]
2191 obj = self.__class__(
2193 bounds=(self._bound_min, self._bound_max),
2196 default=self.default,
2197 optional=self.optional,
2198 _decoded=(offset, llen, l),
2200 except BoundsError as err:
2203 klass=self.__class__,
2204 decode_path=decode_path,
2210 return pp_console_row(next(self.pps()))
2212 def pps(self, decode_path=()):
2214 asn1_type_name=self.asn1_type_name,
2215 obj_name=self.__class__.__name__,
2216 decode_path=decode_path,
2217 value=("%d bytes" % len(self._value)) if self.ready else None,
2218 blob=self._value if self.ready else None,
2219 optional=self.optional,
2220 default=self == self.default,
2221 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2222 expl=None if self._expl is None else tag_decode(self._expl),
2227 expl_offset=self.expl_offset if self.expled else None,
2228 expl_tlen=self.expl_tlen if self.expled else None,
2229 expl_llen=self.expl_llen if self.expled else None,
2230 expl_vlen=self.expl_vlen if self.expled else None,
2232 defined_by, defined = self.defined or (None, None)
2233 if defined_by is not None:
2235 decode_path=decode_path + (decode_path_defby(defined_by),)
2240 """``NULL`` null object
2248 tag_default = tag_encode(5)
2249 asn1_type_name = "NULL"
2253 value=None, # unused, but Sequence passes it
2260 :param bytes impl: override default tag with ``IMPLICIT`` one
2261 :param bytes expl: override default tag with ``EXPLICIT`` one
2262 :param bool optional: is object ``OPTIONAL`` in sequence
2264 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2272 obj = self.__class__()
2274 obj._expl = self._expl
2275 obj.default = self.default
2276 obj.optional = self.optional
2277 obj.offset = self.offset
2278 obj.llen = self.llen
2279 obj.vlen = self.vlen
2282 def __eq__(self, their):
2283 if not issubclass(their.__class__, Null):
2286 self.tag == their.tag and
2287 self._expl == their._expl
2297 return self.__class__(
2298 impl=self.tag if impl is None else impl,
2299 expl=self._expl if expl is None else expl,
2300 optional=self.optional if optional is None else optional,
2304 return self.tag + len_encode(0)
2306 def _decode(self, tlv, offset, decode_path, ctx):
2308 t, _, lv = tag_strip(tlv)
2309 except DecodeError as err:
2310 raise err.__class__(
2312 klass=self.__class__,
2313 decode_path=decode_path,
2318 klass=self.__class__,
2319 decode_path=decode_path,
2323 l, _, v = len_decode(lv)
2324 except DecodeError as err:
2325 raise err.__class__(
2327 klass=self.__class__,
2328 decode_path=decode_path,
2332 raise InvalidLength(
2333 "Null must have zero length",
2334 klass=self.__class__,
2335 decode_path=decode_path,
2338 obj = self.__class__(
2341 optional=self.optional,
2342 _decoded=(offset, 1, 0),
2347 return pp_console_row(next(self.pps()))
2349 def pps(self, decode_path=()):
2351 asn1_type_name=self.asn1_type_name,
2352 obj_name=self.__class__.__name__,
2353 decode_path=decode_path,
2354 optional=self.optional,
2355 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2356 expl=None if self._expl is None else tag_decode(self._expl),
2361 expl_offset=self.expl_offset if self.expled else None,
2362 expl_tlen=self.expl_tlen if self.expled else None,
2363 expl_llen=self.expl_llen if self.expled else None,
2364 expl_vlen=self.expl_vlen if self.expled else None,
2368 class ObjectIdentifier(Obj):
2369 """``OBJECT IDENTIFIER`` OID type
2371 >>> oid = ObjectIdentifier((1, 2, 3))
2372 OBJECT IDENTIFIER 1.2.3
2373 >>> oid == ObjectIdentifier("1.2.3")
2379 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2380 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2382 >>> str(ObjectIdentifier((3, 1)))
2383 Traceback (most recent call last):
2384 pyderasn.InvalidOID: unacceptable first arc value
2386 __slots__ = ("defines",)
2387 tag_default = tag_encode(6)
2388 asn1_type_name = "OBJECT IDENTIFIER"
2401 :param value: set the value. Either tuples of integers,
2402 string of "."-concatenated integers, or
2403 :py:class:`pyderasn.ObjectIdentifier` object
2404 :param defines: sequence of tuples. Each tuple has two elements.
2405 First one is relative to current one decode
2406 path, aiming to the field defined by that OID.
2407 Read about relative path in
2408 :py:func:`pyderasn.abs_decode_path`. Second
2409 tuple element is ``{OID: pyderasn.Obj()}``
2410 dictionary, mapping between current OID value
2411 and structure applied to defined field.
2412 :ref:`Read about DEFINED BY <definedby>`
2413 :param bytes impl: override default tag with ``IMPLICIT`` one
2414 :param bytes expl: override default tag with ``EXPLICIT`` one
2415 :param default: set default value. Type same as in ``value``
2416 :param bool optional: is object ``OPTIONAL`` in sequence
2418 super(ObjectIdentifier, self).__init__(
2426 if value is not None:
2427 self._value = self._value_sanitize(value)
2428 if default is not None:
2429 default = self._value_sanitize(default)
2430 self.default = self.__class__(
2435 if self._value is None:
2436 self._value = default
2437 self.defines = defines
2439 def __add__(self, their):
2440 if isinstance(their, self.__class__):
2441 return self.__class__(self._value + their._value)
2442 if isinstance(their, tuple):
2443 return self.__class__(self._value + their)
2444 raise InvalidValueType((self.__class__, tuple))
2446 def _value_sanitize(self, value):
2447 if issubclass(value.__class__, ObjectIdentifier):
2449 if isinstance(value, string_types):
2451 value = tuple(int(arc) for arc in value.split("."))
2453 raise InvalidOID("unacceptable arcs values")
2454 if isinstance(value, tuple):
2456 raise InvalidOID("less than 2 arcs")
2457 first_arc = value[0]
2458 if first_arc in (0, 1):
2459 if not (0 <= value[1] <= 39):
2460 raise InvalidOID("second arc is too wide")
2461 elif first_arc == 2:
2464 raise InvalidOID("unacceptable first arc value")
2466 raise InvalidValueType((self.__class__, str, tuple))
2470 return self._value is not None
2473 obj = self.__class__()
2474 obj._value = self._value
2475 obj.defines = self.defines
2477 obj._expl = self._expl
2478 obj.default = self.default
2479 obj.optional = self.optional
2480 obj.offset = self.offset
2481 obj.llen = self.llen
2482 obj.vlen = self.vlen
2486 self._assert_ready()
2487 return iter(self._value)
2490 return ".".join(str(arc) for arc in self._value or ())
2493 self._assert_ready()
2496 bytes(self._expl or b"") +
2497 str(self._value).encode("ascii"),
2500 def __eq__(self, their):
2501 if isinstance(their, tuple):
2502 return self._value == their
2503 if not issubclass(their.__class__, ObjectIdentifier):
2506 self.tag == their.tag and
2507 self._expl == their._expl and
2508 self._value == their._value
2511 def __lt__(self, their):
2512 return self._value < their._value
2523 return self.__class__(
2525 defines=self.defines if defines is None else defines,
2526 impl=self.tag if impl is None else impl,
2527 expl=self._expl if expl is None else expl,
2528 default=self.default if default is None else default,
2529 optional=self.optional if optional is None else optional,
2533 self._assert_ready()
2535 first_value = value[1]
2536 first_arc = value[0]
2539 elif first_arc == 1:
2541 elif first_arc == 2:
2543 else: # pragma: no cover
2544 raise RuntimeError("invalid arc is stored")
2545 octets = [zero_ended_encode(first_value)]
2546 for arc in value[2:]:
2547 octets.append(zero_ended_encode(arc))
2548 v = b"".join(octets)
2549 return b"".join((self.tag, len_encode(len(v)), v))
2551 def _decode(self, tlv, offset, decode_path, ctx):
2553 t, _, lv = tag_strip(tlv)
2554 except DecodeError as err:
2555 raise err.__class__(
2557 klass=self.__class__,
2558 decode_path=decode_path,
2563 klass=self.__class__,
2564 decode_path=decode_path,
2568 l, llen, v = len_decode(lv)
2569 except DecodeError as err:
2570 raise err.__class__(
2572 klass=self.__class__,
2573 decode_path=decode_path,
2577 raise NotEnoughData(
2578 "encoded length is longer than data",
2579 klass=self.__class__,
2580 decode_path=decode_path,
2584 raise NotEnoughData(
2586 klass=self.__class__,
2587 decode_path=decode_path,
2590 v, tail = v[:l], v[l:]
2596 octet = indexbytes(v, i)
2597 arc = (arc << 7) | (octet & 0x7F)
2598 if octet & 0x80 == 0:
2606 klass=self.__class__,
2607 decode_path=decode_path,
2611 second_arc = arcs[0]
2612 if 0 <= second_arc <= 39:
2614 elif 40 <= second_arc <= 79:
2620 obj = self.__class__(
2621 value=tuple([first_arc, second_arc] + arcs[1:]),
2624 default=self.default,
2625 optional=self.optional,
2626 _decoded=(offset, llen, l),
2631 return pp_console_row(next(self.pps()))
2633 def pps(self, decode_path=()):
2635 asn1_type_name=self.asn1_type_name,
2636 obj_name=self.__class__.__name__,
2637 decode_path=decode_path,
2638 value=str(self) if self.ready else None,
2639 optional=self.optional,
2640 default=self == self.default,
2641 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2642 expl=None if self._expl is None else tag_decode(self._expl),
2647 expl_offset=self.expl_offset if self.expled else None,
2648 expl_tlen=self.expl_tlen if self.expled else None,
2649 expl_llen=self.expl_llen if self.expled else None,
2650 expl_vlen=self.expl_vlen if self.expled else None,
2654 class Enumerated(Integer):
2655 """``ENUMERATED`` integer type
2657 This type is identical to :py:class:`pyderasn.Integer`, but requires
2658 schema to be specified and does not accept values missing from it.
2661 tag_default = tag_encode(10)
2662 asn1_type_name = "ENUMERATED"
2673 bounds=None, # dummy argument, workability for Integer.decode
2675 super(Enumerated, self).__init__(
2684 if len(self.specs) == 0:
2685 raise ValueError("schema must be specified")
2687 def _value_sanitize(self, value):
2688 if isinstance(value, self.__class__):
2689 value = value._value
2690 elif isinstance(value, integer_types):
2691 if value not in list(self.specs.values()):
2693 "unknown integer value: %s" % value,
2694 klass=self.__class__,
2696 elif isinstance(value, string_types):
2697 value = self.specs.get(value)
2699 raise ObjUnknown("integer value: %s" % value)
2701 raise InvalidValueType((self.__class__, int, str))
2705 obj = self.__class__(_specs=self.specs)
2706 obj._value = self._value
2707 obj._bound_min = self._bound_min
2708 obj._bound_max = self._bound_max
2710 obj._expl = self._expl
2711 obj.default = self.default
2712 obj.optional = self.optional
2713 obj.offset = self.offset
2714 obj.llen = self.llen
2715 obj.vlen = self.vlen
2727 return self.__class__(
2729 impl=self.tag if impl is None else impl,
2730 expl=self._expl if expl is None else expl,
2731 default=self.default if default is None else default,
2732 optional=self.optional if optional is None else optional,
2737 class CommonString(OctetString):
2738 """Common class for all strings
2740 Everything resembles :py:class:`pyderasn.OctetString`, except
2741 ability to deal with unicode text strings.
2743 >>> hexenc("привет мир".encode("utf-8"))
2744 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2745 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2747 >>> s = UTF8String("привет мир")
2748 UTF8String UTF8String привет мир
2750 'привет мир'
2751 >>> hexenc(bytes(s))
2752 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2754 >>> PrintableString("привет мир")
2755 Traceback (most recent call last):
2756 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2758 >>> BMPString("ада", bounds=(2, 2))
2759 Traceback (most recent call last):
2760 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2761 >>> s = BMPString("ад", bounds=(2, 2))
2764 >>> hexenc(bytes(s))
2772 * - :py:class:`pyderasn.UTF8String`
2774 * - :py:class:`pyderasn.NumericString`
2776 * - :py:class:`pyderasn.PrintableString`
2778 * - :py:class:`pyderasn.TeletexString`
2780 * - :py:class:`pyderasn.T61String`
2782 * - :py:class:`pyderasn.VideotexString`
2784 * - :py:class:`pyderasn.IA5String`
2786 * - :py:class:`pyderasn.GraphicString`
2788 * - :py:class:`pyderasn.VisibleString`
2790 * - :py:class:`pyderasn.ISO646String`
2792 * - :py:class:`pyderasn.GeneralString`
2794 * - :py:class:`pyderasn.UniversalString`
2796 * - :py:class:`pyderasn.BMPString`
2799 __slots__ = ("encoding",)
2801 def _value_sanitize(self, value):
2803 value_decoded = None
2804 if isinstance(value, self.__class__):
2805 value_raw = value._value
2806 elif isinstance(value, text_type):
2807 value_decoded = value
2808 elif isinstance(value, binary_type):
2811 raise InvalidValueType((self.__class__, text_type, binary_type))
2813 value_decoded.encode(self.encoding)
2814 if value_raw is None else value_raw
2817 value_raw.decode(self.encoding)
2818 if value_decoded is None else value_decoded
2820 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2828 def __eq__(self, their):
2829 if isinstance(their, binary_type):
2830 return self._value == their
2831 if isinstance(their, text_type):
2832 return self._value == their.encode(self.encoding)
2833 if not isinstance(their, self.__class__):
2836 self._value == their._value and
2837 self.tag == their.tag and
2838 self._expl == their._expl
2841 def __unicode__(self):
2843 return self._value.decode(self.encoding)
2844 return text_type(self._value)
2847 return pp_console_row(next(self.pps(no_unicode=PY2)))
2849 def pps(self, decode_path=(), no_unicode=False):
2852 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2854 asn1_type_name=self.asn1_type_name,
2855 obj_name=self.__class__.__name__,
2856 decode_path=decode_path,
2858 optional=self.optional,
2859 default=self == self.default,
2860 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2861 expl=None if self._expl is None else tag_decode(self._expl),
2869 class UTF8String(CommonString):
2871 tag_default = tag_encode(12)
2873 asn1_type_name = "UTF8String"
2876 class NumericString(CommonString):
2878 tag_default = tag_encode(18)
2880 asn1_type_name = "NumericString"
2883 class PrintableString(CommonString):
2885 tag_default = tag_encode(19)
2887 asn1_type_name = "PrintableString"
2890 class TeletexString(CommonString):
2892 tag_default = tag_encode(20)
2894 asn1_type_name = "TeletexString"
2897 class T61String(TeletexString):
2899 asn1_type_name = "T61String"
2902 class VideotexString(CommonString):
2904 tag_default = tag_encode(21)
2905 encoding = "iso-8859-1"
2906 asn1_type_name = "VideotexString"
2909 class IA5String(CommonString):
2911 tag_default = tag_encode(22)
2913 asn1_type_name = "IA5"
2916 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
2917 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
2918 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
2921 class UTCTime(CommonString):
2922 """``UTCTime`` datetime type
2924 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2925 UTCTime UTCTime 2017-09-30T22:07:50
2931 datetime.datetime(2017, 9, 30, 22, 7, 50)
2932 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2933 datetime.datetime(1957, 9, 30, 22, 7, 50)
2936 tag_default = tag_encode(23)
2938 asn1_type_name = "UTCTime"
2940 fmt = "%y%m%d%H%M%SZ"
2950 bounds=None, # dummy argument, workability for OctetString.decode
2953 :param value: set the value. Either datetime type, or
2954 :py:class:`pyderasn.UTCTime` object
2955 :param bytes impl: override default tag with ``IMPLICIT`` one
2956 :param bytes expl: override default tag with ``EXPLICIT`` one
2957 :param default: set default value. Type same as in ``value``
2958 :param bool optional: is object ``OPTIONAL`` in sequence
2960 super(UTCTime, self).__init__(
2968 if value is not None:
2969 self._value = self._value_sanitize(value)
2970 if default is not None:
2971 default = self._value_sanitize(default)
2972 self.default = self.__class__(
2977 if self._value is None:
2978 self._value = default
2980 def _value_sanitize(self, value):
2981 if isinstance(value, self.__class__):
2983 if isinstance(value, datetime):
2984 return value.strftime(self.fmt).encode("ascii")
2985 if isinstance(value, binary_type):
2986 value_decoded = value.decode("ascii")
2987 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
2989 datetime.strptime(value_decoded, self.fmt)
2991 raise DecodeError("invalid UTCTime format")
2994 raise DecodeError("invalid UTCTime length")
2995 raise InvalidValueType((self.__class__, datetime))
2997 def __eq__(self, their):
2998 if isinstance(their, binary_type):
2999 return self._value == their
3000 if isinstance(their, datetime):
3001 return self.todatetime() == their
3002 if not isinstance(their, self.__class__):
3005 self._value == their._value and
3006 self.tag == their.tag and
3007 self._expl == their._expl
3010 def todatetime(self):
3011 """Convert to datetime
3015 Pay attention that UTCTime can not hold full year, so all years
3016 having < 50 years are treated as 20xx, 19xx otherwise, according
3017 to X.509 recomendation.
3019 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3020 year = value.year % 100
3022 year=(2000 + year) if year < 50 else (1900 + year),
3026 minute=value.minute,
3027 second=value.second,
3031 return pp_console_row(next(self.pps()))
3033 def pps(self, decode_path=()):
3035 asn1_type_name=self.asn1_type_name,
3036 obj_name=self.__class__.__name__,
3037 decode_path=decode_path,
3038 value=self.todatetime().isoformat() if self.ready else None,
3039 optional=self.optional,
3040 default=self == self.default,
3041 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3042 expl=None if self._expl is None else tag_decode(self._expl),
3050 class GeneralizedTime(UTCTime):
3051 """``GeneralizedTime`` datetime type
3053 This type is similar to :py:class:`pyderasn.UTCTime`.
3055 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3056 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3058 '20170930220750.000123Z'
3059 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3060 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3063 tag_default = tag_encode(24)
3064 asn1_type_name = "GeneralizedTime"
3066 fmt = "%Y%m%d%H%M%SZ"
3067 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3069 def _value_sanitize(self, value):
3070 if isinstance(value, self.__class__):
3072 if isinstance(value, datetime):
3073 return value.strftime(
3074 self.fmt_ms if value.microsecond > 0 else self.fmt
3076 if isinstance(value, binary_type):
3077 value_decoded = value.decode("ascii")
3078 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3080 datetime.strptime(value_decoded, self.fmt)
3083 "invalid GeneralizedTime (without ms) format",
3086 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3088 datetime.strptime(value_decoded, self.fmt_ms)
3091 "invalid GeneralizedTime (with ms) format",
3096 "invalid GeneralizedTime length",
3097 klass=self.__class__,
3099 raise InvalidValueType((self.__class__, datetime))
3101 def todatetime(self):
3102 value = self._value.decode("ascii")
3103 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3104 return datetime.strptime(value, self.fmt)
3105 return datetime.strptime(value, self.fmt_ms)
3108 class GraphicString(CommonString):
3110 tag_default = tag_encode(25)
3111 encoding = "iso-8859-1"
3112 asn1_type_name = "GraphicString"
3115 class VisibleString(CommonString):
3117 tag_default = tag_encode(26)
3119 asn1_type_name = "VisibleString"
3122 class ISO646String(VisibleString):
3124 asn1_type_name = "ISO646String"
3127 class GeneralString(CommonString):
3129 tag_default = tag_encode(27)
3130 encoding = "iso-8859-1"
3131 asn1_type_name = "GeneralString"
3134 class UniversalString(CommonString):
3136 tag_default = tag_encode(28)
3137 encoding = "utf-32-be"
3138 asn1_type_name = "UniversalString"
3141 class BMPString(CommonString):
3143 tag_default = tag_encode(30)
3144 encoding = "utf-16-be"
3145 asn1_type_name = "BMPString"
3149 """``CHOICE`` special type
3153 class GeneralName(Choice):
3155 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
3156 ('dNSName', IA5String(impl=tag_ctxp(2))),
3159 >>> gn = GeneralName()
3161 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3162 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3163 >>> gn["dNSName"] = IA5String("bar.baz")
3164 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3165 >>> gn["rfc822Name"]
3168 [2] IA5String IA5 bar.baz
3171 >>> gn.value == gn["dNSName"]
3174 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3176 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3177 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3179 __slots__ = ("specs",)
3181 asn1_type_name = "CHOICE"
3194 :param value: set the value. Either ``(choice, value)`` tuple, or
3195 :py:class:`pyderasn.Choice` object
3196 :param bytes impl: can not be set, do **not** use it
3197 :param bytes expl: override default tag with ``EXPLICIT`` one
3198 :param default: set default value. Type same as in ``value``
3199 :param bool optional: is object ``OPTIONAL`` in sequence
3201 if impl is not None:
3202 raise ValueError("no implicit tag allowed for CHOICE")
3203 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3205 schema = getattr(self, "schema", ())
3206 if len(schema) == 0:
3207 raise ValueError("schema must be specified")
3209 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3212 if value is not None:
3213 self._value = self._value_sanitize(value)
3214 if default is not None:
3215 default_value = self._value_sanitize(default)
3216 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3217 default_obj.specs = self.specs
3218 default_obj._value = default_value
3219 self.default = default_obj
3221 self._value = default_obj.copy()._value
3223 def _value_sanitize(self, value):
3224 if isinstance(value, self.__class__):
3226 if isinstance(value, tuple) and len(value) == 2:
3228 spec = self.specs.get(choice)
3230 raise ObjUnknown(choice)
3231 if not isinstance(obj, spec.__class__):
3232 raise InvalidValueType((spec,))
3233 return (choice, spec(obj))
3234 raise InvalidValueType((self.__class__, tuple))
3238 return self._value is not None and self._value[1].ready
3241 obj = self.__class__(schema=self.specs)
3242 obj._expl = self._expl
3243 obj.default = self.default
3244 obj.optional = self.optional
3245 obj.offset = self.offset
3246 obj.llen = self.llen
3247 obj.vlen = self.vlen
3249 if value is not None:
3250 obj._value = (value[0], value[1].copy())
3253 def __eq__(self, their):
3254 if isinstance(their, tuple) and len(their) == 2:
3255 return self._value == their
3256 if not isinstance(their, self.__class__):
3259 self.specs == their.specs and
3260 self._value == their._value
3270 return self.__class__(
3273 expl=self._expl if expl is None else expl,
3274 default=self.default if default is None else default,
3275 optional=self.optional if optional is None else optional,
3280 self._assert_ready()
3281 return self._value[0]
3285 self._assert_ready()
3286 return self._value[1]
3288 def __getitem__(self, key):
3289 if key not in self.specs:
3290 raise ObjUnknown(key)
3291 if self._value is None:
3293 choice, value = self._value
3298 def __setitem__(self, key, value):
3299 spec = self.specs.get(key)
3301 raise ObjUnknown(key)
3302 if not isinstance(value, spec.__class__):
3303 raise InvalidValueType((spec.__class__,))
3304 self._value = (key, spec(value))
3312 return self._value[1].decoded if self.ready else False
3315 self._assert_ready()
3316 return self._value[1].encode()
3318 def _decode(self, tlv, offset, decode_path, ctx):
3319 for choice, spec in self.specs.items():
3321 value, tail = spec.decode(
3325 decode_path=decode_path + (choice,),
3330 obj = self.__class__(
3333 default=self.default,
3334 optional=self.optional,
3335 _decoded=(offset, 0, value.tlvlen),
3337 obj._value = (choice, value)
3340 klass=self.__class__,
3341 decode_path=decode_path,
3346 value = pp_console_row(next(self.pps()))
3348 value = "%s[%r]" % (value, self.value)
3351 def pps(self, decode_path=()):
3353 asn1_type_name=self.asn1_type_name,
3354 obj_name=self.__class__.__name__,
3355 decode_path=decode_path,
3356 value=self.choice if self.ready else None,
3357 optional=self.optional,
3358 default=self == self.default,
3359 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3360 expl=None if self._expl is None else tag_decode(self._expl),
3367 yield self.value.pps(decode_path=decode_path + (self.choice,))
3370 class PrimitiveTypes(Choice):
3371 """Predefined ``CHOICE`` for all generic primitive types
3373 It could be useful for general decoding of some unspecified values:
3375 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3376 OCTET STRING 3 bytes 666f6f
3377 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3381 schema = tuple((klass.__name__, klass()) for klass in (
3406 """``ANY`` special type
3408 >>> Any(Integer(-123))
3410 >>> a = Any(OctetString(b"hello world").encode())
3411 ANY 040b68656c6c6f20776f726c64
3412 >>> hexenc(bytes(a))
3413 b'0x040x0bhello world'
3415 __slots__ = ("defined",)
3416 tag_default = tag_encode(0)
3417 asn1_type_name = "ANY"
3427 :param value: set the value. Either any kind of pyderasn's
3428 **ready** object, or bytes. Pay attention that
3429 **no** validation is performed is raw binary value
3431 :param bytes expl: override default tag with ``EXPLICIT`` one
3432 :param bool optional: is object ``OPTIONAL`` in sequence
3434 super(Any, self).__init__(None, expl, None, optional, _decoded)
3435 self._value = None if value is None else self._value_sanitize(value)
3438 def _value_sanitize(self, value):
3439 if isinstance(value, self.__class__):
3441 if isinstance(value, Obj):
3442 return value.encode()
3443 if isinstance(value, binary_type):
3445 raise InvalidValueType((self.__class__, Obj, binary_type))
3449 return self._value is not None
3452 obj = self.__class__()
3453 obj._value = self._value
3455 obj._expl = self._expl
3456 obj.optional = self.optional
3457 obj.offset = self.offset
3458 obj.llen = self.llen
3459 obj.vlen = self.vlen
3462 def __eq__(self, their):
3463 if isinstance(their, binary_type):
3464 return self._value == their
3465 if issubclass(their.__class__, Any):
3466 return self._value == their._value
3475 return self.__class__(
3477 expl=self._expl if expl is None else expl,
3478 optional=self.optional if optional is None else optional,
3481 def __bytes__(self):
3482 self._assert_ready()
3490 self._assert_ready()
3493 def _decode(self, tlv, offset, decode_path, ctx):
3495 t, tlen, lv = tag_strip(tlv)
3496 l, llen, v = len_decode(lv)
3497 except DecodeError as err:
3498 raise err.__class__(
3500 klass=self.__class__,
3501 decode_path=decode_path,
3505 raise NotEnoughData(
3506 "encoded length is longer than data",
3507 klass=self.__class__,
3508 decode_path=decode_path,
3511 tlvlen = tlen + llen + l
3512 v, tail = tlv[:tlvlen], v[l:]
3513 obj = self.__class__(
3516 optional=self.optional,
3517 _decoded=(offset, 0, tlvlen),
3523 return pp_console_row(next(self.pps()))
3525 def pps(self, decode_path=()):
3527 asn1_type_name=self.asn1_type_name,
3528 obj_name=self.__class__.__name__,
3529 decode_path=decode_path,
3530 blob=self._value if self.ready else None,
3531 optional=self.optional,
3532 default=self == self.default,
3533 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3534 expl=None if self._expl is None else tag_decode(self._expl),
3539 expl_offset=self.expl_offset if self.expled else None,
3540 expl_tlen=self.expl_tlen if self.expled else None,
3541 expl_llen=self.expl_llen if self.expled else None,
3542 expl_vlen=self.expl_vlen if self.expled else None,
3544 defined_by, defined = self.defined or (None, None)
3545 if defined_by is not None:
3547 decode_path=decode_path + (decode_path_defby(defined_by),)
3551 ########################################################################
3552 # ASN.1 constructed types
3553 ########################################################################
3555 def get_def_by_path(defines_by_path, sub_decode_path):
3556 """Get define by decode path
3558 for path, define in defines_by_path:
3559 if len(path) != len(sub_decode_path):
3561 for p1, p2 in zip(path, sub_decode_path):
3562 if (p1 != any) and (p1 != p2):
3568 def abs_decode_path(decode_path, rel_path):
3569 """Create an absolute decode path from current and relative ones
3571 :param decode_path: current decode path, starting point.
3573 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3574 If first tuple's element is "/", then treat it as
3575 an absolute path, ignoring ``decode_path`` as
3576 starting point. Also this tuple can contain ".."
3577 elements, stripping the leading element from
3580 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3581 ("foo", "bar", "baz", "whatever")
3582 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3584 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3587 if rel_path[0] == "/":
3589 if rel_path[0] == "..":
3590 return abs_decode_path(decode_path[:-1], rel_path[1:])
3591 return decode_path + rel_path
3594 class Sequence(Obj):
3595 """``SEQUENCE`` structure type
3597 You have to make specification of sequence::
3599 class Extension(Sequence):
3601 ("extnID", ObjectIdentifier()),
3602 ("critical", Boolean(default=False)),
3603 ("extnValue", OctetString()),
3606 Then, you can work with it as with dictionary.
3608 >>> ext = Extension()
3609 >>> Extension().specs
3611 ('extnID', OBJECT IDENTIFIER),
3612 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3613 ('extnValue', OCTET STRING),
3615 >>> ext["extnID"] = "1.2.3"
3616 Traceback (most recent call last):
3617 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3618 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3620 You can know if sequence is ready to be encoded:
3625 Traceback (most recent call last):
3626 pyderasn.ObjNotReady: object is not ready: extnValue
3627 >>> ext["extnValue"] = OctetString(b"foobar")
3631 Value you want to assign, must have the same **type** as in
3632 corresponding specification, but it can have different tags,
3633 optional/default attributes -- they will be taken from specification
3636 class TBSCertificate(Sequence):
3638 ("version", Version(expl=tag_ctxc(0), default="v1")),
3641 >>> tbs = TBSCertificate()
3642 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3644 You can know if value exists/set in the sequence and take its value:
3646 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3649 OBJECT IDENTIFIER 1.2.3
3651 But pay attention that if value has default, then it won't be (not
3652 in) in the sequence (because ``DEFAULT`` must not be encoded in
3653 DER), but you can read its value:
3655 >>> "critical" in ext, ext["critical"]
3656 (False, BOOLEAN False)
3657 >>> ext["critical"] = Boolean(True)
3658 >>> "critical" in ext, ext["critical"]
3659 (True, BOOLEAN True)
3661 All defaulted values are always optional.
3665 When decoded DER contains defaulted value inside, then
3666 technically this is not valid DER encoding. But we allow
3667 and pass it. Of course reencoding of that kind of DER will
3668 result in different binary representation (validly without
3669 defaulted value inside).
3671 Two sequences are equal if they have equal specification (schema),
3672 implicit/explicit tagging and the same values.
3674 __slots__ = ("specs",)
3675 tag_default = tag_encode(form=TagFormConstructed, num=16)
3676 asn1_type_name = "SEQUENCE"
3688 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3690 schema = getattr(self, "schema", ())
3692 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3695 if value is not None:
3696 self._value = self._value_sanitize(value)
3697 if default is not None:
3698 default_value = self._value_sanitize(default)
3699 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3700 default_obj.specs = self.specs
3701 default_obj._value = default_value
3702 self.default = default_obj
3704 self._value = default_obj.copy()._value
3706 def _value_sanitize(self, value):
3707 if not issubclass(value.__class__, Sequence):
3708 raise InvalidValueType((Sequence,))
3713 for name, spec in self.specs.items():
3714 value = self._value.get(name)
3725 obj = self.__class__(schema=self.specs)
3727 obj._expl = self._expl
3728 obj.default = self.default
3729 obj.optional = self.optional
3730 obj.offset = self.offset
3731 obj.llen = self.llen
3732 obj.vlen = self.vlen
3733 obj._value = {k: v.copy() for k, v in self._value.items()}
3736 def __eq__(self, their):
3737 if not isinstance(their, self.__class__):
3740 self.specs == their.specs and
3741 self.tag == their.tag and
3742 self._expl == their._expl and
3743 self._value == their._value
3754 return self.__class__(
3757 impl=self.tag if impl is None else impl,
3758 expl=self._expl if expl is None else expl,
3759 default=self.default if default is None else default,
3760 optional=self.optional if optional is None else optional,
3763 def __contains__(self, key):
3764 return key in self._value
3766 def __setitem__(self, key, value):
3767 spec = self.specs.get(key)
3769 raise ObjUnknown(key)
3771 self._value.pop(key, None)
3773 if not isinstance(value, spec.__class__):
3774 raise InvalidValueType((spec.__class__,))
3775 value = spec(value=value)
3776 if spec.default is not None and value == spec.default:
3777 self._value.pop(key, None)
3779 self._value[key] = value
3781 def __getitem__(self, key):
3782 value = self._value.get(key)
3783 if value is not None:
3785 spec = self.specs.get(key)
3787 raise ObjUnknown(key)
3788 if spec.default is not None:
3792 def _encoded_values(self):
3794 for name, spec in self.specs.items():
3795 value = self._value.get(name)
3799 raise ObjNotReady(name)
3800 raws.append(value.encode())
3804 v = b"".join(self._encoded_values())
3805 return b"".join((self.tag, len_encode(len(v)), v))
3807 def _decode(self, tlv, offset, decode_path, ctx):
3809 t, tlen, lv = tag_strip(tlv)
3810 except DecodeError as err:
3811 raise err.__class__(
3813 klass=self.__class__,
3814 decode_path=decode_path,
3819 klass=self.__class__,
3820 decode_path=decode_path,
3824 l, llen, v = len_decode(lv)
3825 except DecodeError as err:
3826 raise err.__class__(
3828 klass=self.__class__,
3829 decode_path=decode_path,
3833 raise NotEnoughData(
3834 "encoded length is longer than data",
3835 klass=self.__class__,
3836 decode_path=decode_path,
3839 v, tail = v[:l], v[l:]
3840 sub_offset = offset + tlen + llen
3842 for name, spec in self.specs.items():
3843 if len(v) == 0 and spec.optional:
3845 sub_decode_path = decode_path + (name,)
3847 value, v_tail = spec.decode(
3851 decode_path=sub_decode_path,
3859 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
3860 if defined is not None:
3861 defined_by, defined_spec = defined
3862 if issubclass(value.__class__, SequenceOf):
3863 for i, _value in enumerate(value):
3864 sub_sub_decode_path = sub_decode_path + (
3866 decode_path_defby(defined_by),
3868 defined_value, defined_tail = defined_spec.decode(
3869 memoryview(bytes(_value)),
3870 sub_offset + value.tlen + value.llen,
3872 decode_path=sub_sub_decode_path,
3875 if len(defined_tail) > 0:
3878 klass=self.__class__,
3879 decode_path=sub_sub_decode_path,
3882 _value.defined = (defined_by, defined_value)
3884 defined_value, defined_tail = defined_spec.decode(
3885 memoryview(bytes(value)),
3886 sub_offset + value.tlen + value.llen,
3888 decode_path=sub_decode_path + (decode_path_defby(defined_by),),
3891 if len(defined_tail) > 0:
3894 klass=self.__class__,
3895 decode_path=sub_decode_path + (decode_path_defby(defined_by),),
3898 value.defined = (defined_by, defined_value)
3900 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3902 if spec.default is not None and value == spec.default:
3903 # Encoded default values are not valid in DER,
3904 # but we allow that anyway
3906 values[name] = value
3908 spec_defines = getattr(spec, "defines", ())
3909 if len(spec_defines) == 0:
3910 defines_by_path = ctx.get("defines_by_path", ())
3911 if len(defines_by_path) > 0:
3912 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
3913 if spec_defines is not None and len(spec_defines) > 0:
3914 for rel_path, schema in spec_defines:
3915 defined = schema.get(value, None)
3916 if defined is not None:
3917 ctx.setdefault("defines", []).append((
3918 abs_decode_path(sub_decode_path[:-1], rel_path),
3924 klass=self.__class__,
3925 decode_path=decode_path,
3928 obj = self.__class__(
3932 default=self.default,
3933 optional=self.optional,
3934 _decoded=(offset, llen, l),
3940 value = pp_console_row(next(self.pps()))
3942 for name in self.specs:
3943 _value = self._value.get(name)
3946 cols.append(repr(_value))
3947 return "%s[%s]" % (value, ", ".join(cols))
3949 def pps(self, decode_path=()):
3951 asn1_type_name=self.asn1_type_name,
3952 obj_name=self.__class__.__name__,
3953 decode_path=decode_path,
3954 optional=self.optional,
3955 default=self == self.default,
3956 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3957 expl=None if self._expl is None else tag_decode(self._expl),
3962 expl_offset=self.expl_offset if self.expled else None,
3963 expl_tlen=self.expl_tlen if self.expled else None,
3964 expl_llen=self.expl_llen if self.expled else None,
3965 expl_vlen=self.expl_vlen if self.expled else None,
3967 for name in self.specs:
3968 value = self._value.get(name)
3971 yield value.pps(decode_path=decode_path + (name,))
3974 class Set(Sequence):
3975 """``SET`` structure type
3977 Its usage is identical to :py:class:`pyderasn.Sequence`.
3980 tag_default = tag_encode(form=TagFormConstructed, num=17)
3981 asn1_type_name = "SET"
3984 raws = self._encoded_values()
3987 return b"".join((self.tag, len_encode(len(v)), v))
3989 def _decode(self, tlv, offset, decode_path, ctx):
3991 t, tlen, lv = tag_strip(tlv)
3992 except DecodeError as err:
3993 raise err.__class__(
3995 klass=self.__class__,
3996 decode_path=decode_path,
4001 klass=self.__class__,
4002 decode_path=decode_path,
4006 l, llen, v = len_decode(lv)
4007 except DecodeError as err:
4008 raise err.__class__(
4010 klass=self.__class__,
4011 decode_path=decode_path,
4015 raise NotEnoughData(
4016 "encoded length is longer than data",
4017 klass=self.__class__,
4020 v, tail = v[:l], v[l:]
4021 sub_offset = offset + tlen + llen
4023 specs_items = self.specs.items
4025 for name, spec in specs_items():
4027 value, v_tail = spec.decode(
4031 decode_path=decode_path + (name,),
4037 value.expl_tlvlen if value.expled else value.tlvlen
4040 if spec.default is None or value != spec.default: # pragma: no cover
4041 # SeqMixing.test_encoded_default_accepted covers that place
4042 values[name] = value
4046 klass=self.__class__,
4047 decode_path=decode_path,
4050 obj = self.__class__(
4054 default=self.default,
4055 optional=self.optional,
4056 _decoded=(offset, llen, l),
4062 class SequenceOf(Obj):
4063 """``SEQUENCE OF`` sequence type
4065 For that kind of type you must specify the object it will carry on
4066 (bounds are for example here, not required)::
4068 class Ints(SequenceOf):
4073 >>> ints.append(Integer(123))
4074 >>> ints.append(Integer(234))
4076 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4077 >>> [int(i) for i in ints]
4079 >>> ints.append(Integer(345))
4080 Traceback (most recent call last):
4081 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4084 >>> ints[1] = Integer(345)
4086 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4088 Also you can initialize sequence with preinitialized values:
4090 >>> ints = Ints([Integer(123), Integer(234)])
4092 __slots__ = ("spec", "_bound_min", "_bound_max")
4093 tag_default = tag_encode(form=TagFormConstructed, num=16)
4094 asn1_type_name = "SEQUENCE OF"
4107 super(SequenceOf, self).__init__(
4115 schema = getattr(self, "schema", None)
4117 raise ValueError("schema must be specified")
4119 self._bound_min, self._bound_max = getattr(
4123 ) if bounds is None else bounds
4125 if value is not None:
4126 self._value = self._value_sanitize(value)
4127 if default is not None:
4128 default_value = self._value_sanitize(default)
4129 default_obj = self.__class__(
4134 default_obj._value = default_value
4135 self.default = default_obj
4137 self._value = default_obj.copy()._value
4139 def _value_sanitize(self, value):
4140 if issubclass(value.__class__, SequenceOf):
4141 value = value._value
4142 elif hasattr(value, "__iter__"):
4145 raise InvalidValueType((self.__class__, iter))
4146 if not self._bound_min <= len(value) <= self._bound_max:
4147 raise BoundsError(self._bound_min, len(value), self._bound_max)
4149 if not isinstance(v, self.spec.__class__):
4150 raise InvalidValueType((self.spec.__class__,))
4155 return all(v.ready for v in self._value)
4158 obj = self.__class__(schema=self.spec)
4159 obj._bound_min = self._bound_min
4160 obj._bound_max = self._bound_max
4162 obj._expl = self._expl
4163 obj.default = self.default
4164 obj.optional = self.optional
4165 obj.offset = self.offset
4166 obj.llen = self.llen
4167 obj.vlen = self.vlen
4168 obj._value = [v.copy() for v in self._value]
4171 def __eq__(self, their):
4172 if isinstance(their, self.__class__):
4174 self.spec == their.spec and
4175 self.tag == their.tag and
4176 self._expl == their._expl and
4177 self._value == their._value
4179 if hasattr(their, "__iter__"):
4180 return self._value == list(their)
4192 return self.__class__(
4196 (self._bound_min, self._bound_max)
4197 if bounds is None else bounds
4199 impl=self.tag if impl is None else impl,
4200 expl=self._expl if expl is None else expl,
4201 default=self.default if default is None else default,
4202 optional=self.optional if optional is None else optional,
4205 def __contains__(self, key):
4206 return key in self._value
4208 def append(self, value):
4209 if not isinstance(value, self.spec.__class__):
4210 raise InvalidValueType((self.spec.__class__,))
4211 if len(self._value) + 1 > self._bound_max:
4214 len(self._value) + 1,
4217 self._value.append(value)
4220 self._assert_ready()
4221 return iter(self._value)
4224 self._assert_ready()
4225 return len(self._value)
4227 def __setitem__(self, key, value):
4228 if not isinstance(value, self.spec.__class__):
4229 raise InvalidValueType((self.spec.__class__,))
4230 self._value[key] = self.spec(value=value)
4232 def __getitem__(self, key):
4233 return self._value[key]
4235 def _encoded_values(self):
4236 return [v.encode() for v in self._value]
4239 v = b"".join(self._encoded_values())
4240 return b"".join((self.tag, len_encode(len(v)), v))
4242 def _decode(self, tlv, offset, decode_path, ctx):
4244 t, tlen, lv = tag_strip(tlv)
4245 except DecodeError as err:
4246 raise err.__class__(
4248 klass=self.__class__,
4249 decode_path=decode_path,
4254 klass=self.__class__,
4255 decode_path=decode_path,
4259 l, llen, v = len_decode(lv)
4260 except DecodeError as err:
4261 raise err.__class__(
4263 klass=self.__class__,
4264 decode_path=decode_path,
4268 raise NotEnoughData(
4269 "encoded length is longer than data",
4270 klass=self.__class__,
4271 decode_path=decode_path,
4274 v, tail = v[:l], v[l:]
4275 sub_offset = offset + tlen + llen
4279 value, v_tail = spec.decode(
4283 decode_path=decode_path + (str(len(_value)),),
4286 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4288 _value.append(value)
4289 obj = self.__class__(
4292 bounds=(self._bound_min, self._bound_max),
4295 default=self.default,
4296 optional=self.optional,
4297 _decoded=(offset, llen, l),
4303 pp_console_row(next(self.pps())),
4304 ", ".join(repr(v) for v in self._value),
4307 def pps(self, decode_path=()):
4309 asn1_type_name=self.asn1_type_name,
4310 obj_name=self.__class__.__name__,
4311 decode_path=decode_path,
4312 optional=self.optional,
4313 default=self == self.default,
4314 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4315 expl=None if self._expl is None else tag_decode(self._expl),
4320 expl_offset=self.expl_offset if self.expled else None,
4321 expl_tlen=self.expl_tlen if self.expled else None,
4322 expl_llen=self.expl_llen if self.expled else None,
4323 expl_vlen=self.expl_vlen if self.expled else None,
4325 for i, value in enumerate(self._value):
4326 yield value.pps(decode_path=decode_path + (str(i),))
4329 class SetOf(SequenceOf):
4330 """``SET OF`` sequence type
4332 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4335 tag_default = tag_encode(form=TagFormConstructed, num=17)
4336 asn1_type_name = "SET OF"
4339 raws = self._encoded_values()
4342 return b"".join((self.tag, len_encode(len(v)), v))
4345 def obj_by_path(pypath): # pragma: no cover
4346 """Import object specified as string Python path
4348 Modules must be separated from classes/functions with ``:``.
4350 >>> obj_by_path("foo.bar:Baz")
4351 <class 'foo.bar.Baz'>
4352 >>> obj_by_path("foo.bar:Baz.boo")
4353 <classmethod 'foo.bar.Baz.boo'>
4355 mod, objs = pypath.rsplit(":", 1)
4356 from importlib import import_module
4357 obj = import_module(mod)
4358 for obj_name in objs.split("."):
4359 obj = getattr(obj, obj_name)
4363 def generic_decoder(): # pragma: no cover
4364 # All of this below is a big hack with self references
4365 choice = PrimitiveTypes()
4366 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4367 choice.specs["SetOf"] = SetOf(schema=choice)
4369 choice.specs["SequenceOf%d" % i] = SequenceOf(
4373 choice.specs["Any"] = Any()
4375 # Class name equals to type name, to omit it from output
4376 class SEQUENCEOF(SequenceOf):
4380 def pprint_any(obj, oids=None):
4381 def _pprint_pps(pps):
4383 if hasattr(pp, "_fields"):
4384 if pp.asn1_type_name == Choice.asn1_type_name:
4386 pp_kwargs = pp._asdict()
4387 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4388 pp = _pp(**pp_kwargs)
4389 yield pp_console_row(
4395 for row in pp_console_blob(pp):
4398 for row in _pprint_pps(pp):
4400 return "\n".join(_pprint_pps(obj.pps()))
4401 return SEQUENCEOF(), pprint_any
4404 def main(): # pragma: no cover
4406 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4407 parser.add_argument(
4411 help="Skip that number of bytes from the beginning",
4413 parser.add_argument(
4415 help="Python path to dictionary with OIDs",
4417 parser.add_argument(
4419 help="Python path to schema definition to use",
4421 parser.add_argument(
4422 "--defines-by-path",
4423 help="Python path to decoder's defines_by_path",
4425 parser.add_argument(
4427 type=argparse.FileType("rb"),
4428 help="Path to DER file you want to decode",
4430 args = parser.parse_args()
4431 args.DERFile.seek(args.skip)
4432 der = memoryview(args.DERFile.read())
4433 args.DERFile.close()
4434 oids = obj_by_path(args.oids) if args.oids else {}
4436 schema = obj_by_path(args.schema)
4437 from functools import partial
4438 pprinter = partial(pprint, big_blobs=True)
4440 schema, pprinter = generic_decoder()
4441 obj, tail = schema().decode(
4444 None if args.defines_by_path is None else
4445 {"defines_by_path": obj_by_path(args.defines_by_path)}
4448 print(pprinter(obj, oids=oids))
4450 print("\nTrailing data: %s" % hexenc(tail))
4453 if __name__ == "__main__":