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>`
210 * :ref:`strict_default_existence <strict_default_existence_ctx>`
217 All objects have ``pps()`` method, that is a generator of
218 :py:class:`pyderasn.PP` namedtuple, holding various raw information
219 about the object. If ``pps`` is called on sequences, then all underlying
220 ``PP`` will be yielded.
222 You can use :py:func:`pyderasn.pp_console_row` function, converting
223 those ``PP`` to human readable string. Actually exactly it is used for
224 all object ``repr``. But it is easy to write custom formatters.
226 >>> from pyderasn import pprint
227 >>> encoded = Integer(-12345).encode()
228 >>> obj, tail = Integer().decode(encoded)
229 >>> print(pprint(obj))
230 0 [1,1, 2] INTEGER -12345
237 ASN.1 structures often have ANY and OCTET STRING fields, that are
238 DEFINED BY some previously met ObjectIdentifier. This library provides
239 ability to specify mapping between some OID and field that must be
240 decoded with specific specification.
245 :py:class:`pyderasn.ObjectIdentifier` field inside
246 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
247 necessary for decoding structures. For example, CMS (:rfc:`5652`)
250 class ContentInfo(Sequence):
252 ("contentType", ContentType(defines=((("content",), {
253 id_digestedData: DigestedData(),
254 id_signedData: SignedData(),
256 ("content", Any(expl=tag_ctxc(0))),
259 ``contentType`` field tells that it defines that ``content`` must be
260 decoded with ``SignedData`` specification, if ``contentType`` equals to
261 ``id-signedData``. The same applies to ``DigestedData``. If
262 ``contentType`` contains unknown OID, then no automatic decoding is
265 You can specify multiple fields, that will be autodecoded -- that is why
266 ``defines`` kwarg is a sequence. You can specify defined field
267 relatively or absolutely to current decode path. For example ``defines``
268 for AlgorithmIdentifier of X.509's
269 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
273 id_ecPublicKey: ECParameters(),
274 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
276 (("..", "subjectPublicKey"), {
277 id_rsaEncryption: RSAPublicKey(),
278 id_GostR3410_2001: OctetString(),
282 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
283 autodecode its parameters inside SPKI's algorithm and its public key
286 Following types can be automatically decoded (DEFINED BY):
288 * :py:class:`pyderasn.Any`
289 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
290 * :py:class:`pyderasn.OctetString`
291 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
292 ``Any``/``BitString``/``OctetString``-s
294 When any of those fields is automatically decoded, then ``.defined``
295 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
296 was defined, ``value`` contains corresponding decoded value. For example
297 above, ``content_info["content"].defined == (id_signedData,
300 .. _defines_by_path_ctx:
302 defines_by_path context option
303 ______________________________
305 Sometimes you either can not or do not want to explicitly set *defines*
306 in the scheme. You can dynamically apply those definitions when calling
307 ``.decode()`` method.
309 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
310 value must be sequence of following tuples::
312 (decode_path, defines)
314 where ``decode_path`` is a tuple holding so-called decode path to the
315 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
316 ``defines``, holding exactly the same value as accepted in its keyword
319 For example, again for CMS, you want to automatically decode
320 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
321 structures it may hold. Also, automatically decode ``controlSequence``
324 content_info, tail = ContentInfo().decode(data, defines_by_path=(
327 ((("content",), {id_signedData: SignedData()}),),
332 DecodePathDefBy(id_signedData),
337 id_cct_PKIData: PKIData(),
338 id_cct_PKIResponse: PKIResponse(),
344 DecodePathDefBy(id_signedData),
347 DecodePathDefBy(id_cct_PKIResponse),
353 id_cmc_recipientNonce: RecipientNonce(),
354 id_cmc_senderNonce: SenderNonce(),
355 id_cmc_statusInfoV2: CMCStatusInfoV2(),
356 id_cmc_transactionId: TransactionId(),
361 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
362 First function is useful for path construction when some automatic
363 decoding is already done. ``any`` means literally any value it meet --
364 useful for SEQUENCE/SET OF-s.
371 .. autoclass:: pyderasn.Boolean
376 .. autoclass:: pyderasn.Integer
381 .. autoclass:: pyderasn.BitString
386 .. autoclass:: pyderasn.OctetString
391 .. autoclass:: pyderasn.Null
396 .. autoclass:: pyderasn.ObjectIdentifier
401 .. autoclass:: pyderasn.Enumerated
405 .. autoclass:: pyderasn.CommonString
409 .. autoclass:: pyderasn.UTCTime
410 :members: __init__, todatetime
414 .. autoclass:: pyderasn.GeneralizedTime
421 .. autoclass:: pyderasn.Choice
426 .. autoclass:: PrimitiveTypes
430 .. autoclass:: pyderasn.Any
438 .. autoclass:: pyderasn.Sequence
443 .. autoclass:: pyderasn.Set
448 .. autoclass:: pyderasn.SequenceOf
453 .. autoclass:: pyderasn.SetOf
459 .. autofunction:: pyderasn.abs_decode_path
460 .. autofunction:: pyderasn.hexenc
461 .. autofunction:: pyderasn.hexdec
462 .. autofunction:: pyderasn.tag_encode
463 .. autofunction:: pyderasn.tag_decode
464 .. autofunction:: pyderasn.tag_ctxp
465 .. autofunction:: pyderasn.tag_ctxc
466 .. autoclass:: pyderasn.Obj
469 from codecs import getdecoder
470 from codecs import getencoder
471 from collections import namedtuple
472 from collections import OrderedDict
473 from datetime import datetime
474 from math import ceil
475 from os import environ
476 from string import digits
478 from six import add_metaclass
479 from six import binary_type
480 from six import byte2int
481 from six import indexbytes
482 from six import int2byte
483 from six import integer_types
484 from six import iterbytes
486 from six import string_types
487 from six import text_type
488 from six.moves import xrange as six_xrange
492 from termcolor import colored
494 def colored(what, *args):
537 "TagClassApplication",
541 "TagFormConstructed",
552 TagClassUniversal = 0
553 TagClassApplication = 1 << 6
554 TagClassContext = 1 << 7
555 TagClassPrivate = 1 << 6 | 1 << 7
557 TagFormConstructed = 1 << 5
560 TagClassApplication: "APPLICATION ",
561 TagClassPrivate: "PRIVATE ",
562 TagClassUniversal: "UNIV ",
568 ########################################################################
570 ########################################################################
572 class DecodeError(Exception):
573 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
575 :param str msg: reason of decode failing
576 :param klass: optional exact DecodeError inherited class (like
577 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
578 :py:exc:`InvalidLength`)
579 :param decode_path: tuple of strings. It contains human
580 readable names of the fields through which
581 decoding process has passed
582 :param int offset: binary offset where failure happened
584 super(DecodeError, self).__init__()
587 self.decode_path = decode_path
593 "" if self.klass is None else self.klass.__name__,
595 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
596 if len(self.decode_path) > 0 else ""
598 ("(at %d)" % self.offset) if self.offset > 0 else "",
604 return "%s(%s)" % (self.__class__.__name__, self)
607 class NotEnoughData(DecodeError):
611 class LenIndefiniteForm(DecodeError):
615 class TagMismatch(DecodeError):
619 class InvalidLength(DecodeError):
623 class InvalidOID(DecodeError):
627 class ObjUnknown(ValueError):
628 def __init__(self, name):
629 super(ObjUnknown, self).__init__()
633 return "object is unknown: %s" % self.name
636 return "%s(%s)" % (self.__class__.__name__, self)
639 class ObjNotReady(ValueError):
640 def __init__(self, name):
641 super(ObjNotReady, self).__init__()
645 return "object is not ready: %s" % self.name
648 return "%s(%s)" % (self.__class__.__name__, self)
651 class InvalidValueType(ValueError):
652 def __init__(self, expected_types):
653 super(InvalidValueType, self).__init__()
654 self.expected_types = expected_types
657 return "invalid value type, expected: %s" % ", ".join(
658 [repr(t) for t in self.expected_types]
662 return "%s(%s)" % (self.__class__.__name__, self)
665 class BoundsError(ValueError):
666 def __init__(self, bound_min, value, bound_max):
667 super(BoundsError, self).__init__()
668 self.bound_min = bound_min
670 self.bound_max = bound_max
673 return "unsatisfied bounds: %s <= %s <= %s" % (
680 return "%s(%s)" % (self.__class__.__name__, self)
683 ########################################################################
685 ########################################################################
687 _hexdecoder = getdecoder("hex")
688 _hexencoder = getencoder("hex")
692 """Binary data to hexadecimal string convert
694 return _hexdecoder(data)[0]
698 """Hexadecimal string to binary data convert
700 return _hexencoder(data)[0].decode("ascii")
703 def int_bytes_len(num, byte_len=8):
706 return int(ceil(float(num.bit_length()) / byte_len))
709 def zero_ended_encode(num):
710 octets = bytearray(int_bytes_len(num, 7))
712 octets[i] = num & 0x7F
716 octets[i] = 0x80 | (num & 0x7F)
722 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
723 """Encode tag to binary form
725 :param int num: tag's number
726 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
727 :py:data:`pyderasn.TagClassContext`,
728 :py:data:`pyderasn.TagClassApplication`,
729 :py:data:`pyderasn.TagClassPrivate`)
730 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
731 :py:data:`pyderasn.TagFormConstructed`)
735 return int2byte(klass | form | num)
736 # [XX|X|11111][1.......][1.......] ... [0.......]
737 return int2byte(klass | form | 31) + zero_ended_encode(num)
741 """Decode tag from binary form
745 No validation is performed, assuming that it has already passed.
747 It returns tuple with three integers, as
748 :py:func:`pyderasn.tag_encode` accepts.
750 first_octet = byte2int(tag)
751 klass = first_octet & 0xC0
752 form = first_octet & 0x20
753 if first_octet & 0x1F < 0x1F:
754 return (klass, form, first_octet & 0x1F)
756 for octet in iterbytes(tag[1:]):
759 return (klass, form, num)
763 """Create CONTEXT PRIMITIVE tag
765 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
769 """Create CONTEXT CONSTRUCTED tag
771 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
775 """Take off tag from the data
777 :returns: (encoded tag, tag length, remaining data)
780 raise NotEnoughData("no data at all")
781 if byte2int(data) & 0x1F < 31:
782 return data[:1], 1, data[1:]
787 raise DecodeError("unfinished tag")
788 if indexbytes(data, i) & 0x80 == 0:
791 return data[:i], i, data[i:]
797 octets = bytearray(int_bytes_len(l) + 1)
798 octets[0] = 0x80 | (len(octets) - 1)
799 for i in six_xrange(len(octets) - 1, 0, -1):
805 def len_decode(data):
807 raise NotEnoughData("no data at all")
808 first_octet = byte2int(data)
809 if first_octet & 0x80 == 0:
810 return first_octet, 1, data[1:]
811 octets_num = first_octet & 0x7F
812 if octets_num + 1 > len(data):
813 raise NotEnoughData("encoded length is longer than data")
815 raise LenIndefiniteForm()
816 if byte2int(data[1:]) == 0:
817 raise DecodeError("leading zeros")
819 for v in iterbytes(data[1:1 + octets_num]):
822 raise DecodeError("long form instead of short one")
823 return l, 1 + octets_num, data[1 + octets_num:]
826 ########################################################################
828 ########################################################################
830 class AutoAddSlots(type):
831 def __new__(mcs, name, bases, _dict):
832 _dict["__slots__"] = _dict.get("__slots__", ())
833 return type.__new__(mcs, name, bases, _dict)
836 @add_metaclass(AutoAddSlots)
838 """Common ASN.1 object class
840 All ASN.1 types are inherited from it. It has metaclass that
841 automatically adds ``__slots__`` to all inherited classes.
865 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
866 self._expl = getattr(self, "expl", None) if expl is None else expl
867 if self.tag != self.tag_default and self._expl is not None:
869 "implicit and explicit tags can not be set simultaneously"
871 if default is not None:
873 self.optional = optional
874 self.offset, self.llen, self.vlen = _decoded
876 self.lenindef = False
877 self.expl_lenindef = False
881 def ready(self): # pragma: no cover
882 """Is object ready to be encoded?
884 raise NotImplementedError()
886 def _assert_ready(self):
888 raise ObjNotReady(self.__class__.__name__)
892 """Is object decoded?
894 return (self.llen + self.vlen) > 0
896 def copy(self): # pragma: no cover
897 """Make a copy of object, safe to be mutated
899 raise NotImplementedError()
907 return self.tlen + self.llen + self.vlen
909 def __str__(self): # pragma: no cover
910 return self.__bytes__() if PY2 else self.__unicode__()
912 def __ne__(self, their):
913 return not(self == their)
915 def __gt__(self, their): # pragma: no cover
916 return not(self < their)
918 def __le__(self, their): # pragma: no cover
919 return (self == their) or (self < their)
921 def __ge__(self, their): # pragma: no cover
922 return (self == their) or (self > their)
924 def _encode(self): # pragma: no cover
925 raise NotImplementedError()
927 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
928 raise NotImplementedError()
932 if self._expl is None:
934 return b"".join((self._expl, len_encode(len(raw)), raw))
947 :param data: either binary or memoryview
948 :param int offset: initial data's offset
949 :param bool leavemm: do we need to leave memoryview of remaining
950 data as is, or convert it to bytes otherwise
951 :param ctx: optional :ref:`context <ctx>` governing decoding process.
952 :param tag_only: decode only the tag, without length and contents
953 (used only in Choice and Set structures, trying to
954 determine if tag satisfies the scheme)
955 :returns: (Obj, remaining data)
959 tlv = memoryview(data)
960 if self._expl is None:
961 result = self._decode(
964 decode_path=decode_path,
973 t, tlen, lv = tag_strip(tlv)
974 except DecodeError as err:
977 klass=self.__class__,
978 decode_path=decode_path,
983 klass=self.__class__,
984 decode_path=decode_path,
988 l, llen, v = len_decode(lv)
989 except LenIndefiniteForm as err:
990 if not ctx.get("bered", False):
993 klass=self.__class__,
994 decode_path=decode_path,
998 offset += tlen + llen
999 result = self._decode(
1002 decode_path=decode_path,
1009 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1010 if eoc_expected.tobytes() != EOC:
1013 decode_path=decode_path,
1017 obj.expl_lenindef = True
1018 except DecodeError as err:
1019 raise err.__class__(
1021 klass=self.__class__,
1022 decode_path=decode_path,
1027 raise NotEnoughData(
1028 "encoded length is longer than data",
1029 klass=self.__class__,
1030 decode_path=decode_path,
1033 result = self._decode(
1035 offset=offset + tlen + llen,
1036 decode_path=decode_path,
1043 return obj, (tail if leavemm else tail.tobytes())
1047 return self._expl is not None
1054 def expl_tlen(self):
1055 return len(self._expl)
1058 def expl_llen(self):
1059 if self.expl_lenindef:
1061 return len(len_encode(self.tlvlen))
1064 def expl_offset(self):
1065 return self.offset - self.expl_tlen - self.expl_llen
1068 def expl_vlen(self):
1072 def expl_tlvlen(self):
1073 return self.expl_tlen + self.expl_llen + self.expl_vlen
1076 class DecodePathDefBy(object):
1077 """DEFINED BY representation inside decode path
1079 __slots__ = ("defined_by",)
1081 def __init__(self, defined_by):
1082 self.defined_by = defined_by
1084 def __ne__(self, their):
1085 return not(self == their)
1087 def __eq__(self, their):
1088 if not isinstance(their, self.__class__):
1090 return self.defined_by == their.defined_by
1093 return "DEFINED BY " + str(self.defined_by)
1096 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1099 ########################################################################
1101 ########################################################################
1103 PP = namedtuple("PP", (
1125 asn1_type_name="unknown",
1164 def _colorize(what, colour, with_colours, attrs=("bold",)):
1165 return colored(what, colour, attrs=attrs) if with_colours else what
1180 " " if pp.expl_offset is None else
1181 ("-%d" % (pp.offset - pp.expl_offset))
1184 cols.append(_colorize(col, "red", with_colours, ()))
1185 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1186 cols.append(_colorize(col, "green", with_colours, ()))
1187 if len(pp.decode_path) > 0:
1188 cols.append(" ." * (len(pp.decode_path)))
1189 ent = pp.decode_path[-1]
1190 if isinstance(ent, DecodePathDefBy):
1191 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1192 value = str(ent.defined_by)
1194 oids is not None and
1195 ent.defined_by.asn1_type_name ==
1196 ObjectIdentifier.asn1_type_name and
1199 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1201 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1203 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1204 if pp.expl is not None:
1205 klass, _, num = pp.expl
1206 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1207 cols.append(_colorize(col, "blue", with_colours))
1208 if pp.impl is not None:
1209 klass, _, num = pp.impl
1210 col = "[%s%d]" % (TagClassReprs[klass], num)
1211 cols.append(_colorize(col, "blue", with_colours))
1212 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1213 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1214 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1215 if pp.value is not None:
1217 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1219 oids is not None and
1220 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1223 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1225 if isinstance(pp.blob, binary_type):
1226 cols.append(hexenc(pp.blob))
1227 elif isinstance(pp.blob, tuple):
1228 cols.append(", ".join(pp.blob))
1230 cols.append(_colorize("OPTIONAL", "red", with_colours))
1232 cols.append(_colorize("DEFAULT", "red", with_colours))
1233 return " ".join(cols)
1236 def pp_console_blob(pp):
1237 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1238 if len(pp.decode_path) > 0:
1239 cols.append(" ." * (len(pp.decode_path) + 1))
1240 if isinstance(pp.blob, binary_type):
1241 blob = hexenc(pp.blob).upper()
1242 for i in range(0, len(blob), 32):
1243 chunk = blob[i:i + 32]
1244 yield " ".join(cols + [":".join(
1245 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1247 elif isinstance(pp.blob, tuple):
1248 yield " ".join(cols + [", ".join(pp.blob)])
1251 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1252 """Pretty print object
1254 :param Obj obj: object you want to pretty print
1255 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1256 from it is met, then its humand readable form is printed
1257 :param big_blobs: if large binary objects are met (like OctetString
1258 values), do we need to print them too, on separate
1260 :param with_colours: colourize output, if ``termcolor`` library
1263 def _pprint_pps(pps):
1265 if hasattr(pp, "_fields"):
1267 yield pp_console_row(
1272 with_colours=with_colours,
1274 for row in pp_console_blob(pp):
1277 yield pp_console_row(
1282 with_colours=with_colours,
1285 for row in _pprint_pps(pp):
1287 return "\n".join(_pprint_pps(obj.pps()))
1290 ########################################################################
1291 # ASN.1 primitive types
1292 ########################################################################
1295 """``BOOLEAN`` boolean type
1297 >>> b = Boolean(True)
1299 >>> b == Boolean(True)
1305 tag_default = tag_encode(1)
1306 asn1_type_name = "BOOLEAN"
1318 :param value: set the value. Either boolean type, or
1319 :py:class:`pyderasn.Boolean` object
1320 :param bytes impl: override default tag with ``IMPLICIT`` one
1321 :param bytes expl: override default tag with ``EXPLICIT`` one
1322 :param default: set default value. Type same as in ``value``
1323 :param bool optional: is object ``OPTIONAL`` in sequence
1325 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1326 self._value = None if value is None else self._value_sanitize(value)
1327 if default is not None:
1328 default = self._value_sanitize(default)
1329 self.default = self.__class__(
1335 self._value = default
1337 def _value_sanitize(self, value):
1338 if issubclass(value.__class__, Boolean):
1340 if isinstance(value, bool):
1342 raise InvalidValueType((self.__class__, bool))
1346 return self._value is not None
1349 obj = self.__class__()
1350 obj._value = self._value
1352 obj._expl = self._expl
1353 obj.default = self.default
1354 obj.optional = self.optional
1355 obj.offset = self.offset
1356 obj.llen = self.llen
1357 obj.vlen = self.vlen
1360 def __nonzero__(self):
1361 self._assert_ready()
1365 self._assert_ready()
1368 def __eq__(self, their):
1369 if isinstance(their, bool):
1370 return self._value == their
1371 if not issubclass(their.__class__, Boolean):
1374 self._value == their._value and
1375 self.tag == their.tag and
1376 self._expl == their._expl
1387 return self.__class__(
1389 impl=self.tag if impl is None else impl,
1390 expl=self._expl if expl is None else expl,
1391 default=self.default if default is None else default,
1392 optional=self.optional if optional is None else optional,
1396 self._assert_ready()
1400 (b"\xFF" if self._value else b"\x00"),
1403 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1405 t, _, lv = tag_strip(tlv)
1406 except DecodeError as err:
1407 raise err.__class__(
1409 klass=self.__class__,
1410 decode_path=decode_path,
1415 klass=self.__class__,
1416 decode_path=decode_path,
1422 l, _, v = len_decode(lv)
1423 except DecodeError as err:
1424 raise err.__class__(
1426 klass=self.__class__,
1427 decode_path=decode_path,
1431 raise InvalidLength(
1432 "Boolean's length must be equal to 1",
1433 klass=self.__class__,
1434 decode_path=decode_path,
1438 raise NotEnoughData(
1439 "encoded length is longer than data",
1440 klass=self.__class__,
1441 decode_path=decode_path,
1444 first_octet = byte2int(v)
1446 if first_octet == 0:
1448 elif first_octet == 0xFF:
1450 elif ctx.get("bered", False):
1455 "unacceptable Boolean value",
1456 klass=self.__class__,
1457 decode_path=decode_path,
1460 obj = self.__class__(
1464 default=self.default,
1465 optional=self.optional,
1466 _decoded=(offset, 1, 1),
1472 return pp_console_row(next(self.pps()))
1474 def pps(self, decode_path=()):
1476 asn1_type_name=self.asn1_type_name,
1477 obj_name=self.__class__.__name__,
1478 decode_path=decode_path,
1479 value=str(self._value) if self.ready else None,
1480 optional=self.optional,
1481 default=self == self.default,
1482 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1483 expl=None if self._expl is None else tag_decode(self._expl),
1488 expl_offset=self.expl_offset if self.expled else None,
1489 expl_tlen=self.expl_tlen if self.expled else None,
1490 expl_llen=self.expl_llen if self.expled else None,
1491 expl_vlen=self.expl_vlen if self.expled else None,
1496 """``INTEGER`` integer type
1498 >>> b = Integer(-123)
1500 >>> b == Integer(-123)
1505 >>> Integer(2, bounds=(1, 3))
1507 >>> Integer(5, bounds=(1, 3))
1508 Traceback (most recent call last):
1509 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1513 class Version(Integer):
1520 >>> v = Version("v1")
1527 {'v3': 2, 'v1': 0, 'v2': 1}
1529 __slots__ = ("specs", "_bound_min", "_bound_max")
1530 tag_default = tag_encode(2)
1531 asn1_type_name = "INTEGER"
1545 :param value: set the value. Either integer type, named value
1546 (if ``schema`` is specified in the class), or
1547 :py:class:`pyderasn.Integer` object
1548 :param bounds: set ``(MIN, MAX)`` value constraint.
1549 (-inf, +inf) by default
1550 :param bytes impl: override default tag with ``IMPLICIT`` one
1551 :param bytes expl: override default tag with ``EXPLICIT`` one
1552 :param default: set default value. Type same as in ``value``
1553 :param bool optional: is object ``OPTIONAL`` in sequence
1555 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1557 specs = getattr(self, "schema", {}) if _specs is None else _specs
1558 self.specs = specs if isinstance(specs, dict) else dict(specs)
1559 self._bound_min, self._bound_max = getattr(
1562 (float("-inf"), float("+inf")),
1563 ) if bounds is None else bounds
1564 if value is not None:
1565 self._value = self._value_sanitize(value)
1566 if default is not None:
1567 default = self._value_sanitize(default)
1568 self.default = self.__class__(
1574 if self._value is None:
1575 self._value = default
1577 def _value_sanitize(self, value):
1578 if issubclass(value.__class__, Integer):
1579 value = value._value
1580 elif isinstance(value, integer_types):
1582 elif isinstance(value, str):
1583 value = self.specs.get(value)
1585 raise ObjUnknown("integer value: %s" % value)
1587 raise InvalidValueType((self.__class__, int, str))
1588 if not self._bound_min <= value <= self._bound_max:
1589 raise BoundsError(self._bound_min, value, self._bound_max)
1594 return self._value is not None
1597 obj = self.__class__(_specs=self.specs)
1598 obj._value = self._value
1599 obj._bound_min = self._bound_min
1600 obj._bound_max = self._bound_max
1602 obj._expl = self._expl
1603 obj.default = self.default
1604 obj.optional = self.optional
1605 obj.offset = self.offset
1606 obj.llen = self.llen
1607 obj.vlen = self.vlen
1611 self._assert_ready()
1612 return int(self._value)
1615 self._assert_ready()
1618 bytes(self._expl or b"") +
1619 str(self._value).encode("ascii"),
1622 def __eq__(self, their):
1623 if isinstance(their, integer_types):
1624 return self._value == their
1625 if not issubclass(their.__class__, Integer):
1628 self._value == their._value and
1629 self.tag == their.tag and
1630 self._expl == their._expl
1633 def __lt__(self, their):
1634 return self._value < their._value
1638 for name, value in self.specs.items():
1639 if value == self._value:
1651 return self.__class__(
1654 (self._bound_min, self._bound_max)
1655 if bounds is None else bounds
1657 impl=self.tag if impl is None else impl,
1658 expl=self._expl if expl is None else expl,
1659 default=self.default if default is None else default,
1660 optional=self.optional if optional is None else optional,
1665 self._assert_ready()
1669 octets = bytearray([0])
1673 octets = bytearray()
1675 octets.append((value & 0xFF) ^ 0xFF)
1677 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1680 octets = bytearray()
1682 octets.append(value & 0xFF)
1684 if octets[-1] & 0x80 > 0:
1687 octets = bytes(octets)
1689 bytes_len = ceil(value.bit_length() / 8) or 1
1692 octets = value.to_bytes(
1697 except OverflowError:
1701 return b"".join((self.tag, len_encode(len(octets)), octets))
1703 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1705 t, _, lv = tag_strip(tlv)
1706 except DecodeError as err:
1707 raise err.__class__(
1709 klass=self.__class__,
1710 decode_path=decode_path,
1715 klass=self.__class__,
1716 decode_path=decode_path,
1722 l, llen, v = len_decode(lv)
1723 except DecodeError as err:
1724 raise err.__class__(
1726 klass=self.__class__,
1727 decode_path=decode_path,
1731 raise NotEnoughData(
1732 "encoded length is longer than data",
1733 klass=self.__class__,
1734 decode_path=decode_path,
1738 raise NotEnoughData(
1740 klass=self.__class__,
1741 decode_path=decode_path,
1744 v, tail = v[:l], v[l:]
1745 first_octet = byte2int(v)
1747 second_octet = byte2int(v[1:])
1749 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1750 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1753 "non normalized integer",
1754 klass=self.__class__,
1755 decode_path=decode_path,
1760 if first_octet & 0x80 > 0:
1761 octets = bytearray()
1762 for octet in bytearray(v):
1763 octets.append(octet ^ 0xFF)
1764 for octet in octets:
1765 value = (value << 8) | octet
1769 for octet in bytearray(v):
1770 value = (value << 8) | octet
1772 value = int.from_bytes(v, byteorder="big", signed=True)
1774 obj = self.__class__(
1776 bounds=(self._bound_min, self._bound_max),
1779 default=self.default,
1780 optional=self.optional,
1782 _decoded=(offset, llen, l),
1784 except BoundsError as err:
1787 klass=self.__class__,
1788 decode_path=decode_path,
1794 return pp_console_row(next(self.pps()))
1796 def pps(self, decode_path=()):
1798 asn1_type_name=self.asn1_type_name,
1799 obj_name=self.__class__.__name__,
1800 decode_path=decode_path,
1801 value=(self.named or str(self._value)) if self.ready else None,
1802 optional=self.optional,
1803 default=self == self.default,
1804 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1805 expl=None if self._expl is None else tag_decode(self._expl),
1810 expl_offset=self.expl_offset if self.expled else None,
1811 expl_tlen=self.expl_tlen if self.expled else None,
1812 expl_llen=self.expl_llen if self.expled else None,
1813 expl_vlen=self.expl_vlen if self.expled else None,
1817 class BitString(Obj):
1818 """``BIT STRING`` bit string type
1820 >>> BitString(b"hello world")
1821 BIT STRING 88 bits 68656c6c6f20776f726c64
1824 >>> b == b"hello world"
1829 >>> b = BitString("'010110000000'B")
1830 BIT STRING 12 bits 5800
1833 >>> b[0], b[1], b[2], b[3]
1834 (False, True, False, True)
1838 [False, True, False, True, True, False, False, False, False, False, False, False]
1842 class KeyUsage(BitString):
1844 ("digitalSignature", 0),
1845 ("nonRepudiation", 1),
1846 ("keyEncipherment", 2),
1849 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1850 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1852 ['nonRepudiation', 'keyEncipherment']
1854 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1856 __slots__ = ("tag_constructed", "specs", "defined")
1857 tag_default = tag_encode(3)
1858 asn1_type_name = "BIT STRING"
1871 :param value: set the value. Either binary type, tuple of named
1872 values (if ``schema`` is specified in the class),
1873 string in ``'XXX...'B`` form, or
1874 :py:class:`pyderasn.BitString` object
1875 :param bytes impl: override default tag with ``IMPLICIT`` one
1876 :param bytes expl: override default tag with ``EXPLICIT`` one
1877 :param default: set default value. Type same as in ``value``
1878 :param bool optional: is object ``OPTIONAL`` in sequence
1880 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1881 specs = getattr(self, "schema", {}) if _specs is None else _specs
1882 self.specs = specs if isinstance(specs, dict) else dict(specs)
1883 self._value = None if value is None else self._value_sanitize(value)
1884 if default is not None:
1885 default = self._value_sanitize(default)
1886 self.default = self.__class__(
1892 self._value = default
1894 tag_klass, _, tag_num = tag_decode(self.tag)
1895 self.tag_constructed = tag_encode(
1897 form=TagFormConstructed,
1901 def _bits2octets(self, bits):
1902 if len(self.specs) > 0:
1903 bits = bits.rstrip("0")
1905 bits += "0" * ((8 - (bit_len % 8)) % 8)
1906 octets = bytearray(len(bits) // 8)
1907 for i in six_xrange(len(octets)):
1908 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1909 return bit_len, bytes(octets)
1911 def _value_sanitize(self, value):
1912 if issubclass(value.__class__, BitString):
1914 if isinstance(value, (string_types, binary_type)):
1916 isinstance(value, string_types) and
1917 value.startswith("'") and
1918 value.endswith("'B")
1921 if not set(value) <= set(("0", "1")):
1922 raise ValueError("B's coding contains unacceptable chars")
1923 return self._bits2octets(value)
1924 elif isinstance(value, binary_type):
1925 return (len(value) * 8, value)
1927 raise InvalidValueType((
1932 if isinstance(value, tuple):
1935 isinstance(value[0], integer_types) and
1936 isinstance(value[1], binary_type)
1941 bit = self.specs.get(name)
1943 raise ObjUnknown("BitString value: %s" % name)
1946 return self._bits2octets("")
1948 return self._bits2octets("".join(
1949 ("1" if bit in bits else "0")
1950 for bit in six_xrange(max(bits) + 1)
1952 raise InvalidValueType((self.__class__, binary_type, string_types))
1956 return self._value is not None
1959 obj = self.__class__(_specs=self.specs)
1961 if value is not None:
1962 value = (value[0], value[1])
1965 obj._expl = self._expl
1966 obj.default = self.default
1967 obj.optional = self.optional
1968 obj.offset = self.offset
1969 obj.llen = self.llen
1970 obj.vlen = self.vlen
1974 self._assert_ready()
1975 for i in six_xrange(self._value[0]):
1980 self._assert_ready()
1981 return self._value[0]
1983 def __bytes__(self):
1984 self._assert_ready()
1985 return self._value[1]
1987 def __eq__(self, their):
1988 if isinstance(their, bytes):
1989 return self._value[1] == their
1990 if not issubclass(their.__class__, BitString):
1993 self._value == their._value and
1994 self.tag == their.tag and
1995 self._expl == their._expl
2000 return [name for name, bit in self.specs.items() if self[bit]]
2010 return self.__class__(
2012 impl=self.tag if impl is None else impl,
2013 expl=self._expl if expl is None else expl,
2014 default=self.default if default is None else default,
2015 optional=self.optional if optional is None else optional,
2019 def __getitem__(self, key):
2020 if isinstance(key, int):
2021 bit_len, octets = self._value
2025 byte2int(memoryview(octets)[key // 8:]) >>
2028 if isinstance(key, string_types):
2029 value = self.specs.get(key)
2031 raise ObjUnknown("BitString value: %s" % key)
2033 raise InvalidValueType((int, str))
2036 self._assert_ready()
2037 bit_len, octets = self._value
2040 len_encode(len(octets) + 1),
2041 int2byte((8 - bit_len % 8) % 8),
2045 def _decode_chunk(self, lv, offset, decode_path, ctx):
2047 l, llen, v = len_decode(lv)
2048 except DecodeError as err:
2049 raise err.__class__(
2051 klass=self.__class__,
2052 decode_path=decode_path,
2056 raise NotEnoughData(
2057 "encoded length is longer than data",
2058 klass=self.__class__,
2059 decode_path=decode_path,
2063 raise NotEnoughData(
2065 klass=self.__class__,
2066 decode_path=decode_path,
2069 pad_size = byte2int(v)
2070 if l == 1 and pad_size != 0:
2072 "invalid empty value",
2073 klass=self.__class__,
2074 decode_path=decode_path,
2080 klass=self.__class__,
2081 decode_path=decode_path,
2084 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2087 klass=self.__class__,
2088 decode_path=decode_path,
2091 v, tail = v[:l], v[l:]
2092 obj = self.__class__(
2093 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2096 default=self.default,
2097 optional=self.optional,
2099 _decoded=(offset, llen, l),
2103 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2105 t, tlen, lv = tag_strip(tlv)
2106 except DecodeError as err:
2107 raise err.__class__(
2109 klass=self.__class__,
2110 decode_path=decode_path,
2116 return self._decode_chunk(lv, offset, decode_path, ctx)
2117 if t == self.tag_constructed:
2118 if not ctx.get("bered", False):
2120 msg="unallowed BER constructed encoding",
2121 decode_path=decode_path,
2128 l, llen, v = len_decode(lv)
2129 except LenIndefiniteForm:
2130 llen, l, v = 1, 0, lv[1:]
2132 except DecodeError as err:
2133 raise err.__class__(
2135 klass=self.__class__,
2136 decode_path=decode_path,
2139 if l > 0 and l > len(v):
2140 raise NotEnoughData(
2141 "encoded length is longer than data",
2142 klass=self.__class__,
2143 decode_path=decode_path,
2146 if not lenindef and l == 0:
2147 raise NotEnoughData(
2149 klass=self.__class__,
2150 decode_path=decode_path,
2154 sub_offset = offset + tlen + llen
2158 if v[:EOC_LEN].tobytes() == EOC:
2165 msg="chunk out of bounds",
2166 decode_path=len(chunks) - 1,
2167 offset=chunks[-1].offset,
2169 sub_decode_path = decode_path + (str(len(chunks)),)
2171 chunk, v_tail = BitString().decode(
2174 decode_path=sub_decode_path,
2180 msg="expected BitString encoded chunk",
2181 decode_path=sub_decode_path,
2184 chunks.append(chunk)
2185 sub_offset += chunk.tlvlen
2186 vlen += chunk.tlvlen
2188 if len(chunks) == 0:
2191 decode_path=decode_path,
2196 for chunk_i, chunk in enumerate(chunks[:-1]):
2197 if chunk.bit_len % 8 != 0:
2199 msg="BitString chunk is not multiple of 8 bit",
2200 decode_path=decode_path + (str(chunk_i),),
2201 offset=chunk.offset,
2203 values.append(bytes(chunk))
2204 bit_len += chunk.bit_len
2205 chunk_last = chunks[-1]
2206 values.append(bytes(chunk_last))
2207 bit_len += chunk_last.bit_len
2208 obj = self.__class__(
2209 value=(bit_len, b"".join(values)),
2212 default=self.default,
2213 optional=self.optional,
2215 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2217 obj.lenindef = lenindef
2219 return obj, (v[EOC_LEN:] if lenindef else v)
2221 klass=self.__class__,
2222 decode_path=decode_path,
2227 return pp_console_row(next(self.pps()))
2229 def pps(self, decode_path=()):
2233 bit_len, blob = self._value
2234 value = "%d bits" % bit_len
2235 if len(self.specs) > 0:
2236 blob = tuple(self.named)
2238 asn1_type_name=self.asn1_type_name,
2239 obj_name=self.__class__.__name__,
2240 decode_path=decode_path,
2243 optional=self.optional,
2244 default=self == self.default,
2245 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2246 expl=None if self._expl is None else tag_decode(self._expl),
2251 expl_offset=self.expl_offset if self.expled else None,
2252 expl_tlen=self.expl_tlen if self.expled else None,
2253 expl_llen=self.expl_llen if self.expled else None,
2254 expl_vlen=self.expl_vlen if self.expled else None,
2256 defined_by, defined = self.defined or (None, None)
2257 if defined_by is not None:
2259 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2263 class OctetString(Obj):
2264 """``OCTET STRING`` binary string type
2266 >>> s = OctetString(b"hello world")
2267 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2268 >>> s == OctetString(b"hello world")
2273 >>> OctetString(b"hello", bounds=(4, 4))
2274 Traceback (most recent call last):
2275 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2276 >>> OctetString(b"hell", bounds=(4, 4))
2277 OCTET STRING 4 bytes 68656c6c
2279 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2280 tag_default = tag_encode(4)
2281 asn1_type_name = "OCTET STRING"
2294 :param value: set the value. Either binary type, or
2295 :py:class:`pyderasn.OctetString` object
2296 :param bounds: set ``(MIN, MAX)`` value size constraint.
2297 (-inf, +inf) by default
2298 :param bytes impl: override default tag with ``IMPLICIT`` one
2299 :param bytes expl: override default tag with ``EXPLICIT`` one
2300 :param default: set default value. Type same as in ``value``
2301 :param bool optional: is object ``OPTIONAL`` in sequence
2303 super(OctetString, self).__init__(
2311 self._bound_min, self._bound_max = getattr(
2315 ) if bounds is None else bounds
2316 if value is not None:
2317 self._value = self._value_sanitize(value)
2318 if default is not None:
2319 default = self._value_sanitize(default)
2320 self.default = self.__class__(
2325 if self._value is None:
2326 self._value = default
2328 tag_klass, _, tag_num = tag_decode(self.tag)
2329 self.tag_constructed = tag_encode(
2331 form=TagFormConstructed,
2335 def _value_sanitize(self, value):
2336 if issubclass(value.__class__, OctetString):
2337 value = value._value
2338 elif isinstance(value, binary_type):
2341 raise InvalidValueType((self.__class__, bytes))
2342 if not self._bound_min <= len(value) <= self._bound_max:
2343 raise BoundsError(self._bound_min, len(value), self._bound_max)
2348 return self._value is not None
2351 obj = self.__class__()
2352 obj._value = self._value
2353 obj._bound_min = self._bound_min
2354 obj._bound_max = self._bound_max
2356 obj._expl = self._expl
2357 obj.default = self.default
2358 obj.optional = self.optional
2359 obj.offset = self.offset
2360 obj.llen = self.llen
2361 obj.vlen = self.vlen
2364 def __bytes__(self):
2365 self._assert_ready()
2368 def __eq__(self, their):
2369 if isinstance(their, binary_type):
2370 return self._value == their
2371 if not issubclass(their.__class__, OctetString):
2374 self._value == their._value and
2375 self.tag == their.tag and
2376 self._expl == their._expl
2379 def __lt__(self, their):
2380 return self._value < their._value
2391 return self.__class__(
2394 (self._bound_min, self._bound_max)
2395 if bounds is None else bounds
2397 impl=self.tag if impl is None else impl,
2398 expl=self._expl if expl is None else expl,
2399 default=self.default if default is None else default,
2400 optional=self.optional if optional is None else optional,
2404 self._assert_ready()
2407 len_encode(len(self._value)),
2411 def _decode_chunk(self, lv, offset, decode_path, ctx):
2413 l, llen, v = len_decode(lv)
2414 except DecodeError as err:
2415 raise err.__class__(
2417 klass=self.__class__,
2418 decode_path=decode_path,
2422 raise NotEnoughData(
2423 "encoded length is longer than data",
2424 klass=self.__class__,
2425 decode_path=decode_path,
2428 v, tail = v[:l], v[l:]
2430 obj = self.__class__(
2432 bounds=(self._bound_min, self._bound_max),
2435 default=self.default,
2436 optional=self.optional,
2437 _decoded=(offset, llen, l),
2439 except DecodeError as err:
2442 klass=self.__class__,
2443 decode_path=decode_path,
2446 except BoundsError as err:
2449 klass=self.__class__,
2450 decode_path=decode_path,
2455 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2457 t, tlen, lv = tag_strip(tlv)
2458 except DecodeError as err:
2459 raise err.__class__(
2461 klass=self.__class__,
2462 decode_path=decode_path,
2468 return self._decode_chunk(lv, offset, decode_path, ctx)
2469 if t == self.tag_constructed:
2470 if not ctx.get("bered", False):
2472 msg="unallowed BER constructed encoding",
2473 decode_path=decode_path,
2480 l, llen, v = len_decode(lv)
2481 except LenIndefiniteForm:
2482 llen, l, v = 1, 0, lv[1:]
2484 except DecodeError as err:
2485 raise err.__class__(
2487 klass=self.__class__,
2488 decode_path=decode_path,
2491 if l > 0 and l > len(v):
2492 raise NotEnoughData(
2493 "encoded length is longer than data",
2494 klass=self.__class__,
2495 decode_path=decode_path,
2498 if not lenindef and l == 0:
2499 raise NotEnoughData(
2501 klass=self.__class__,
2502 decode_path=decode_path,
2506 sub_offset = offset + tlen + llen
2510 if v[:EOC_LEN].tobytes() == EOC:
2517 msg="chunk out of bounds",
2518 decode_path=len(chunks) - 1,
2519 offset=chunks[-1].offset,
2521 sub_decode_path = decode_path + (str(len(chunks)),)
2523 chunk, v_tail = OctetString().decode(
2526 decode_path=sub_decode_path,
2532 msg="expected OctetString encoded chunk",
2533 decode_path=sub_decode_path,
2536 chunks.append(chunk)
2537 sub_offset += chunk.tlvlen
2538 vlen += chunk.tlvlen
2540 if len(chunks) == 0:
2543 decode_path=decode_path,
2547 obj = self.__class__(
2548 value=b"".join(bytes(chunk) for chunk in chunks),
2549 bounds=(self._bound_min, self._bound_max),
2552 default=self.default,
2553 optional=self.optional,
2554 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2556 except DecodeError as err:
2559 klass=self.__class__,
2560 decode_path=decode_path,
2563 except BoundsError as err:
2566 klass=self.__class__,
2567 decode_path=decode_path,
2570 obj.lenindef = lenindef
2572 return obj, (v[EOC_LEN:] if lenindef else v)
2574 klass=self.__class__,
2575 decode_path=decode_path,
2580 return pp_console_row(next(self.pps()))
2582 def pps(self, decode_path=()):
2584 asn1_type_name=self.asn1_type_name,
2585 obj_name=self.__class__.__name__,
2586 decode_path=decode_path,
2587 value=("%d bytes" % len(self._value)) if self.ready else None,
2588 blob=self._value if self.ready else None,
2589 optional=self.optional,
2590 default=self == self.default,
2591 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2592 expl=None if self._expl is None else tag_decode(self._expl),
2597 expl_offset=self.expl_offset if self.expled else None,
2598 expl_tlen=self.expl_tlen if self.expled else None,
2599 expl_llen=self.expl_llen if self.expled else None,
2600 expl_vlen=self.expl_vlen if self.expled else None,
2602 defined_by, defined = self.defined or (None, None)
2603 if defined_by is not None:
2605 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2610 """``NULL`` null object
2618 tag_default = tag_encode(5)
2619 asn1_type_name = "NULL"
2623 value=None, # unused, but Sequence passes it
2630 :param bytes impl: override default tag with ``IMPLICIT`` one
2631 :param bytes expl: override default tag with ``EXPLICIT`` one
2632 :param bool optional: is object ``OPTIONAL`` in sequence
2634 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2642 obj = self.__class__()
2644 obj._expl = self._expl
2645 obj.default = self.default
2646 obj.optional = self.optional
2647 obj.offset = self.offset
2648 obj.llen = self.llen
2649 obj.vlen = self.vlen
2652 def __eq__(self, their):
2653 if not issubclass(their.__class__, Null):
2656 self.tag == their.tag and
2657 self._expl == their._expl
2667 return self.__class__(
2668 impl=self.tag if impl is None else impl,
2669 expl=self._expl if expl is None else expl,
2670 optional=self.optional if optional is None else optional,
2674 return self.tag + len_encode(0)
2676 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2678 t, _, lv = tag_strip(tlv)
2679 except DecodeError as err:
2680 raise err.__class__(
2682 klass=self.__class__,
2683 decode_path=decode_path,
2688 klass=self.__class__,
2689 decode_path=decode_path,
2695 l, _, v = len_decode(lv)
2696 except DecodeError as err:
2697 raise err.__class__(
2699 klass=self.__class__,
2700 decode_path=decode_path,
2704 raise InvalidLength(
2705 "Null must have zero length",
2706 klass=self.__class__,
2707 decode_path=decode_path,
2710 obj = self.__class__(
2713 optional=self.optional,
2714 _decoded=(offset, 1, 0),
2719 return pp_console_row(next(self.pps()))
2721 def pps(self, decode_path=()):
2723 asn1_type_name=self.asn1_type_name,
2724 obj_name=self.__class__.__name__,
2725 decode_path=decode_path,
2726 optional=self.optional,
2727 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2728 expl=None if self._expl is None else tag_decode(self._expl),
2733 expl_offset=self.expl_offset if self.expled else None,
2734 expl_tlen=self.expl_tlen if self.expled else None,
2735 expl_llen=self.expl_llen if self.expled else None,
2736 expl_vlen=self.expl_vlen if self.expled else None,
2740 class ObjectIdentifier(Obj):
2741 """``OBJECT IDENTIFIER`` OID type
2743 >>> oid = ObjectIdentifier((1, 2, 3))
2744 OBJECT IDENTIFIER 1.2.3
2745 >>> oid == ObjectIdentifier("1.2.3")
2751 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2752 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2754 >>> str(ObjectIdentifier((3, 1)))
2755 Traceback (most recent call last):
2756 pyderasn.InvalidOID: unacceptable first arc value
2758 __slots__ = ("defines",)
2759 tag_default = tag_encode(6)
2760 asn1_type_name = "OBJECT IDENTIFIER"
2773 :param value: set the value. Either tuples of integers,
2774 string of "."-concatenated integers, or
2775 :py:class:`pyderasn.ObjectIdentifier` object
2776 :param defines: sequence of tuples. Each tuple has two elements.
2777 First one is relative to current one decode
2778 path, aiming to the field defined by that OID.
2779 Read about relative path in
2780 :py:func:`pyderasn.abs_decode_path`. Second
2781 tuple element is ``{OID: pyderasn.Obj()}``
2782 dictionary, mapping between current OID value
2783 and structure applied to defined field.
2784 :ref:`Read about DEFINED BY <definedby>`
2785 :param bytes impl: override default tag with ``IMPLICIT`` one
2786 :param bytes expl: override default tag with ``EXPLICIT`` one
2787 :param default: set default value. Type same as in ``value``
2788 :param bool optional: is object ``OPTIONAL`` in sequence
2790 super(ObjectIdentifier, self).__init__(
2798 if value is not None:
2799 self._value = self._value_sanitize(value)
2800 if default is not None:
2801 default = self._value_sanitize(default)
2802 self.default = self.__class__(
2807 if self._value is None:
2808 self._value = default
2809 self.defines = defines
2811 def __add__(self, their):
2812 if isinstance(their, self.__class__):
2813 return self.__class__(self._value + their._value)
2814 if isinstance(their, tuple):
2815 return self.__class__(self._value + their)
2816 raise InvalidValueType((self.__class__, tuple))
2818 def _value_sanitize(self, value):
2819 if issubclass(value.__class__, ObjectIdentifier):
2821 if isinstance(value, string_types):
2823 value = tuple(int(arc) for arc in value.split("."))
2825 raise InvalidOID("unacceptable arcs values")
2826 if isinstance(value, tuple):
2828 raise InvalidOID("less than 2 arcs")
2829 first_arc = value[0]
2830 if first_arc in (0, 1):
2831 if not (0 <= value[1] <= 39):
2832 raise InvalidOID("second arc is too wide")
2833 elif first_arc == 2:
2836 raise InvalidOID("unacceptable first arc value")
2838 raise InvalidValueType((self.__class__, str, tuple))
2842 return self._value is not None
2845 obj = self.__class__()
2846 obj._value = self._value
2847 obj.defines = self.defines
2849 obj._expl = self._expl
2850 obj.default = self.default
2851 obj.optional = self.optional
2852 obj.offset = self.offset
2853 obj.llen = self.llen
2854 obj.vlen = self.vlen
2858 self._assert_ready()
2859 return iter(self._value)
2862 return ".".join(str(arc) for arc in self._value or ())
2865 self._assert_ready()
2868 bytes(self._expl or b"") +
2869 str(self._value).encode("ascii"),
2872 def __eq__(self, their):
2873 if isinstance(their, tuple):
2874 return self._value == their
2875 if not issubclass(their.__class__, ObjectIdentifier):
2878 self.tag == their.tag and
2879 self._expl == their._expl and
2880 self._value == their._value
2883 def __lt__(self, their):
2884 return self._value < their._value
2895 return self.__class__(
2897 defines=self.defines if defines is None else defines,
2898 impl=self.tag if impl is None else impl,
2899 expl=self._expl if expl is None else expl,
2900 default=self.default if default is None else default,
2901 optional=self.optional if optional is None else optional,
2905 self._assert_ready()
2907 first_value = value[1]
2908 first_arc = value[0]
2911 elif first_arc == 1:
2913 elif first_arc == 2:
2915 else: # pragma: no cover
2916 raise RuntimeError("invalid arc is stored")
2917 octets = [zero_ended_encode(first_value)]
2918 for arc in value[2:]:
2919 octets.append(zero_ended_encode(arc))
2920 v = b"".join(octets)
2921 return b"".join((self.tag, len_encode(len(v)), v))
2923 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2925 t, _, lv = tag_strip(tlv)
2926 except DecodeError as err:
2927 raise err.__class__(
2929 klass=self.__class__,
2930 decode_path=decode_path,
2935 klass=self.__class__,
2936 decode_path=decode_path,
2942 l, llen, v = len_decode(lv)
2943 except DecodeError as err:
2944 raise err.__class__(
2946 klass=self.__class__,
2947 decode_path=decode_path,
2951 raise NotEnoughData(
2952 "encoded length is longer than data",
2953 klass=self.__class__,
2954 decode_path=decode_path,
2958 raise NotEnoughData(
2960 klass=self.__class__,
2961 decode_path=decode_path,
2964 v, tail = v[:l], v[l:]
2970 octet = indexbytes(v, i)
2971 arc = (arc << 7) | (octet & 0x7F)
2972 if octet & 0x80 == 0:
2980 klass=self.__class__,
2981 decode_path=decode_path,
2985 second_arc = arcs[0]
2986 if 0 <= second_arc <= 39:
2988 elif 40 <= second_arc <= 79:
2994 obj = self.__class__(
2995 value=tuple([first_arc, second_arc] + arcs[1:]),
2998 default=self.default,
2999 optional=self.optional,
3000 _decoded=(offset, llen, l),
3005 return pp_console_row(next(self.pps()))
3007 def pps(self, decode_path=()):
3009 asn1_type_name=self.asn1_type_name,
3010 obj_name=self.__class__.__name__,
3011 decode_path=decode_path,
3012 value=str(self) if self.ready else None,
3013 optional=self.optional,
3014 default=self == self.default,
3015 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3016 expl=None if self._expl is None else tag_decode(self._expl),
3021 expl_offset=self.expl_offset if self.expled else None,
3022 expl_tlen=self.expl_tlen if self.expled else None,
3023 expl_llen=self.expl_llen if self.expled else None,
3024 expl_vlen=self.expl_vlen if self.expled else None,
3028 class Enumerated(Integer):
3029 """``ENUMERATED`` integer type
3031 This type is identical to :py:class:`pyderasn.Integer`, but requires
3032 schema to be specified and does not accept values missing from it.
3035 tag_default = tag_encode(10)
3036 asn1_type_name = "ENUMERATED"
3047 bounds=None, # dummy argument, workability for Integer.decode
3049 super(Enumerated, self).__init__(
3058 if len(self.specs) == 0:
3059 raise ValueError("schema must be specified")
3061 def _value_sanitize(self, value):
3062 if isinstance(value, self.__class__):
3063 value = value._value
3064 elif isinstance(value, integer_types):
3065 if value not in list(self.specs.values()):
3067 "unknown integer value: %s" % value,
3068 klass=self.__class__,
3070 elif isinstance(value, string_types):
3071 value = self.specs.get(value)
3073 raise ObjUnknown("integer value: %s" % value)
3075 raise InvalidValueType((self.__class__, int, str))
3079 obj = self.__class__(_specs=self.specs)
3080 obj._value = self._value
3081 obj._bound_min = self._bound_min
3082 obj._bound_max = self._bound_max
3084 obj._expl = self._expl
3085 obj.default = self.default
3086 obj.optional = self.optional
3087 obj.offset = self.offset
3088 obj.llen = self.llen
3089 obj.vlen = self.vlen
3101 return self.__class__(
3103 impl=self.tag if impl is None else impl,
3104 expl=self._expl if expl is None else expl,
3105 default=self.default if default is None else default,
3106 optional=self.optional if optional is None else optional,
3111 class CommonString(OctetString):
3112 """Common class for all strings
3114 Everything resembles :py:class:`pyderasn.OctetString`, except
3115 ability to deal with unicode text strings.
3117 >>> hexenc("привет мир".encode("utf-8"))
3118 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3119 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3121 >>> s = UTF8String("привет мир")
3122 UTF8String UTF8String привет мир
3124 'привет мир'
3125 >>> hexenc(bytes(s))
3126 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3128 >>> PrintableString("привет мир")
3129 Traceback (most recent call last):
3130 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3132 >>> BMPString("ада", bounds=(2, 2))
3133 Traceback (most recent call last):
3134 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3135 >>> s = BMPString("ад", bounds=(2, 2))
3138 >>> hexenc(bytes(s))
3146 * - :py:class:`pyderasn.UTF8String`
3148 * - :py:class:`pyderasn.NumericString`
3150 * - :py:class:`pyderasn.PrintableString`
3152 * - :py:class:`pyderasn.TeletexString`
3154 * - :py:class:`pyderasn.T61String`
3156 * - :py:class:`pyderasn.VideotexString`
3158 * - :py:class:`pyderasn.IA5String`
3160 * - :py:class:`pyderasn.GraphicString`
3162 * - :py:class:`pyderasn.VisibleString`
3164 * - :py:class:`pyderasn.ISO646String`
3166 * - :py:class:`pyderasn.GeneralString`
3168 * - :py:class:`pyderasn.UniversalString`
3170 * - :py:class:`pyderasn.BMPString`
3173 __slots__ = ("encoding",)
3175 def _value_sanitize(self, value):
3177 value_decoded = None
3178 if isinstance(value, self.__class__):
3179 value_raw = value._value
3180 elif isinstance(value, text_type):
3181 value_decoded = value
3182 elif isinstance(value, binary_type):
3185 raise InvalidValueType((self.__class__, text_type, binary_type))
3188 value_decoded.encode(self.encoding)
3189 if value_raw is None else value_raw
3192 value_raw.decode(self.encoding)
3193 if value_decoded is None else value_decoded
3195 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3196 raise DecodeError(str(err))
3197 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3205 def __eq__(self, their):
3206 if isinstance(their, binary_type):
3207 return self._value == their
3208 if isinstance(their, text_type):
3209 return self._value == their.encode(self.encoding)
3210 if not isinstance(their, self.__class__):
3213 self._value == their._value and
3214 self.tag == their.tag and
3215 self._expl == their._expl
3218 def __unicode__(self):
3220 return self._value.decode(self.encoding)
3221 return text_type(self._value)
3224 return pp_console_row(next(self.pps(no_unicode=PY2)))
3226 def pps(self, decode_path=(), no_unicode=False):
3229 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3231 asn1_type_name=self.asn1_type_name,
3232 obj_name=self.__class__.__name__,
3233 decode_path=decode_path,
3235 optional=self.optional,
3236 default=self == self.default,
3237 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3238 expl=None if self._expl is None else tag_decode(self._expl),
3246 class UTF8String(CommonString):
3248 tag_default = tag_encode(12)
3250 asn1_type_name = "UTF8String"
3253 class NumericString(CommonString):
3255 tag_default = tag_encode(18)
3257 asn1_type_name = "NumericString"
3258 allowable_chars = set(digits.encode("ascii"))
3260 def _value_sanitize(self, value):
3261 value = super(NumericString, self)._value_sanitize(value)
3262 if not set(value) <= self.allowable_chars:
3263 raise DecodeError("non-numeric value")
3267 class PrintableString(CommonString):
3269 tag_default = tag_encode(19)
3271 asn1_type_name = "PrintableString"
3274 class TeletexString(CommonString):
3276 tag_default = tag_encode(20)
3278 asn1_type_name = "TeletexString"
3281 class T61String(TeletexString):
3283 asn1_type_name = "T61String"
3286 class VideotexString(CommonString):
3288 tag_default = tag_encode(21)
3289 encoding = "iso-8859-1"
3290 asn1_type_name = "VideotexString"
3293 class IA5String(CommonString):
3295 tag_default = tag_encode(22)
3297 asn1_type_name = "IA5"
3300 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3301 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3302 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3305 class UTCTime(CommonString):
3306 """``UTCTime`` datetime type
3308 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3309 UTCTime UTCTime 2017-09-30T22:07:50
3315 datetime.datetime(2017, 9, 30, 22, 7, 50)
3316 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3317 datetime.datetime(1957, 9, 30, 22, 7, 50)
3320 tag_default = tag_encode(23)
3322 asn1_type_name = "UTCTime"
3324 fmt = "%y%m%d%H%M%SZ"
3334 bounds=None, # dummy argument, workability for OctetString.decode
3337 :param value: set the value. Either datetime type, or
3338 :py:class:`pyderasn.UTCTime` object
3339 :param bytes impl: override default tag with ``IMPLICIT`` one
3340 :param bytes expl: override default tag with ``EXPLICIT`` one
3341 :param default: set default value. Type same as in ``value``
3342 :param bool optional: is object ``OPTIONAL`` in sequence
3344 super(UTCTime, self).__init__(
3352 if value is not None:
3353 self._value = self._value_sanitize(value)
3354 if default is not None:
3355 default = self._value_sanitize(default)
3356 self.default = self.__class__(
3361 if self._value is None:
3362 self._value = default
3364 def _value_sanitize(self, value):
3365 if isinstance(value, self.__class__):
3367 if isinstance(value, datetime):
3368 return value.strftime(self.fmt).encode("ascii")
3369 if isinstance(value, binary_type):
3370 value_decoded = value.decode("ascii")
3371 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3373 datetime.strptime(value_decoded, self.fmt)
3375 raise DecodeError("invalid UTCTime format")
3378 raise DecodeError("invalid UTCTime length")
3379 raise InvalidValueType((self.__class__, datetime))
3381 def __eq__(self, their):
3382 if isinstance(their, binary_type):
3383 return self._value == their
3384 if isinstance(their, datetime):
3385 return self.todatetime() == their
3386 if not isinstance(their, self.__class__):
3389 self._value == their._value and
3390 self.tag == their.tag and
3391 self._expl == their._expl
3394 def todatetime(self):
3395 """Convert to datetime
3399 Pay attention that UTCTime can not hold full year, so all years
3400 having < 50 years are treated as 20xx, 19xx otherwise, according
3401 to X.509 recomendation.
3403 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3404 year = value.year % 100
3406 year=(2000 + year) if year < 50 else (1900 + year),
3410 minute=value.minute,
3411 second=value.second,
3415 return pp_console_row(next(self.pps()))
3417 def pps(self, decode_path=()):
3419 asn1_type_name=self.asn1_type_name,
3420 obj_name=self.__class__.__name__,
3421 decode_path=decode_path,
3422 value=self.todatetime().isoformat() if self.ready else None,
3423 optional=self.optional,
3424 default=self == self.default,
3425 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3426 expl=None if self._expl is None else tag_decode(self._expl),
3434 class GeneralizedTime(UTCTime):
3435 """``GeneralizedTime`` datetime type
3437 This type is similar to :py:class:`pyderasn.UTCTime`.
3439 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3440 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3442 '20170930220750.000123Z'
3443 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3444 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3447 tag_default = tag_encode(24)
3448 asn1_type_name = "GeneralizedTime"
3450 fmt = "%Y%m%d%H%M%SZ"
3451 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3453 def _value_sanitize(self, value):
3454 if isinstance(value, self.__class__):
3456 if isinstance(value, datetime):
3457 return value.strftime(
3458 self.fmt_ms if value.microsecond > 0 else self.fmt
3460 if isinstance(value, binary_type):
3461 value_decoded = value.decode("ascii")
3462 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3464 datetime.strptime(value_decoded, self.fmt)
3467 "invalid GeneralizedTime (without ms) format",
3470 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3472 datetime.strptime(value_decoded, self.fmt_ms)
3475 "invalid GeneralizedTime (with ms) format",
3480 "invalid GeneralizedTime length",
3481 klass=self.__class__,
3483 raise InvalidValueType((self.__class__, datetime))
3485 def todatetime(self):
3486 value = self._value.decode("ascii")
3487 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3488 return datetime.strptime(value, self.fmt)
3489 return datetime.strptime(value, self.fmt_ms)
3492 class GraphicString(CommonString):
3494 tag_default = tag_encode(25)
3495 encoding = "iso-8859-1"
3496 asn1_type_name = "GraphicString"
3499 class VisibleString(CommonString):
3501 tag_default = tag_encode(26)
3503 asn1_type_name = "VisibleString"
3506 class ISO646String(VisibleString):
3508 asn1_type_name = "ISO646String"
3511 class GeneralString(CommonString):
3513 tag_default = tag_encode(27)
3514 encoding = "iso-8859-1"
3515 asn1_type_name = "GeneralString"
3518 class UniversalString(CommonString):
3520 tag_default = tag_encode(28)
3521 encoding = "utf-32-be"
3522 asn1_type_name = "UniversalString"
3525 class BMPString(CommonString):
3527 tag_default = tag_encode(30)
3528 encoding = "utf-16-be"
3529 asn1_type_name = "BMPString"
3533 """``CHOICE`` special type
3537 class GeneralName(Choice):
3539 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3540 ("dNSName", IA5String(impl=tag_ctxp(2))),
3543 >>> gn = GeneralName()
3545 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3546 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3547 >>> gn["dNSName"] = IA5String("bar.baz")
3548 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3549 >>> gn["rfc822Name"]
3552 [2] IA5String IA5 bar.baz
3555 >>> gn.value == gn["dNSName"]
3558 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3560 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3561 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3563 __slots__ = ("specs",)
3565 asn1_type_name = "CHOICE"
3578 :param value: set the value. Either ``(choice, value)`` tuple, or
3579 :py:class:`pyderasn.Choice` object
3580 :param bytes impl: can not be set, do **not** use it
3581 :param bytes expl: override default tag with ``EXPLICIT`` one
3582 :param default: set default value. Type same as in ``value``
3583 :param bool optional: is object ``OPTIONAL`` in sequence
3585 if impl is not None:
3586 raise ValueError("no implicit tag allowed for CHOICE")
3587 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3589 schema = getattr(self, "schema", ())
3590 if len(schema) == 0:
3591 raise ValueError("schema must be specified")
3593 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3596 if value is not None:
3597 self._value = self._value_sanitize(value)
3598 if default is not None:
3599 default_value = self._value_sanitize(default)
3600 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3601 default_obj.specs = self.specs
3602 default_obj._value = default_value
3603 self.default = default_obj
3605 self._value = default_obj.copy()._value
3607 def _value_sanitize(self, value):
3608 if isinstance(value, self.__class__):
3610 if isinstance(value, tuple) and len(value) == 2:
3612 spec = self.specs.get(choice)
3614 raise ObjUnknown(choice)
3615 if not isinstance(obj, spec.__class__):
3616 raise InvalidValueType((spec,))
3617 return (choice, spec(obj))
3618 raise InvalidValueType((self.__class__, tuple))
3622 return self._value is not None and self._value[1].ready
3625 obj = self.__class__(schema=self.specs)
3626 obj._expl = self._expl
3627 obj.default = self.default
3628 obj.optional = self.optional
3629 obj.offset = self.offset
3630 obj.llen = self.llen
3631 obj.vlen = self.vlen
3633 if value is not None:
3634 obj._value = (value[0], value[1].copy())
3637 def __eq__(self, their):
3638 if isinstance(their, tuple) and len(their) == 2:
3639 return self._value == their
3640 if not isinstance(their, self.__class__):
3643 self.specs == their.specs and
3644 self._value == their._value
3654 return self.__class__(
3657 expl=self._expl if expl is None else expl,
3658 default=self.default if default is None else default,
3659 optional=self.optional if optional is None else optional,
3664 self._assert_ready()
3665 return self._value[0]
3669 self._assert_ready()
3670 return self._value[1]
3672 def __getitem__(self, key):
3673 if key not in self.specs:
3674 raise ObjUnknown(key)
3675 if self._value is None:
3677 choice, value = self._value
3682 def __setitem__(self, key, value):
3683 spec = self.specs.get(key)
3685 raise ObjUnknown(key)
3686 if not isinstance(value, spec.__class__):
3687 raise InvalidValueType((spec.__class__,))
3688 self._value = (key, spec(value))
3696 return self._value[1].decoded if self.ready else False
3699 self._assert_ready()
3700 return self._value[1].encode()
3702 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3703 for choice, spec in self.specs.items():
3704 sub_decode_path = decode_path + (choice,)
3710 decode_path=sub_decode_path,
3719 klass=self.__class__,
3720 decode_path=decode_path,
3725 value, tail = spec.decode(
3729 decode_path=sub_decode_path,
3732 obj = self.__class__(
3735 default=self.default,
3736 optional=self.optional,
3737 _decoded=(offset, 0, value.tlvlen),
3739 obj._value = (choice, value)
3743 value = pp_console_row(next(self.pps()))
3745 value = "%s[%r]" % (value, self.value)
3748 def pps(self, decode_path=()):
3750 asn1_type_name=self.asn1_type_name,
3751 obj_name=self.__class__.__name__,
3752 decode_path=decode_path,
3753 value=self.choice if self.ready else None,
3754 optional=self.optional,
3755 default=self == self.default,
3756 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3757 expl=None if self._expl is None else tag_decode(self._expl),
3764 yield self.value.pps(decode_path=decode_path + (self.choice,))
3767 class PrimitiveTypes(Choice):
3768 """Predefined ``CHOICE`` for all generic primitive types
3770 It could be useful for general decoding of some unspecified values:
3772 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3773 OCTET STRING 3 bytes 666f6f
3774 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3778 schema = tuple((klass.__name__, klass()) for klass in (
3803 """``ANY`` special type
3805 >>> Any(Integer(-123))
3807 >>> a = Any(OctetString(b"hello world").encode())
3808 ANY 040b68656c6c6f20776f726c64
3809 >>> hexenc(bytes(a))
3810 b'0x040x0bhello world'
3812 __slots__ = ("defined",)
3813 tag_default = tag_encode(0)
3814 asn1_type_name = "ANY"
3824 :param value: set the value. Either any kind of pyderasn's
3825 **ready** object, or bytes. Pay attention that
3826 **no** validation is performed is raw binary value
3828 :param bytes expl: override default tag with ``EXPLICIT`` one
3829 :param bool optional: is object ``OPTIONAL`` in sequence
3831 super(Any, self).__init__(None, expl, None, optional, _decoded)
3832 self._value = None if value is None else self._value_sanitize(value)
3835 def _value_sanitize(self, value):
3836 if isinstance(value, self.__class__):
3838 if isinstance(value, Obj):
3839 return value.encode()
3840 if isinstance(value, binary_type):
3842 raise InvalidValueType((self.__class__, Obj, binary_type))
3846 return self._value is not None
3849 obj = self.__class__()
3850 obj._value = self._value
3852 obj._expl = self._expl
3853 obj.optional = self.optional
3854 obj.offset = self.offset
3855 obj.llen = self.llen
3856 obj.vlen = self.vlen
3859 def __eq__(self, their):
3860 if isinstance(their, binary_type):
3861 return self._value == their
3862 if issubclass(their.__class__, Any):
3863 return self._value == their._value
3872 return self.__class__(
3874 expl=self._expl if expl is None else expl,
3875 optional=self.optional if optional is None else optional,
3878 def __bytes__(self):
3879 self._assert_ready()
3887 self._assert_ready()
3890 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3892 t, tlen, lv = tag_strip(tlv)
3893 except DecodeError as err:
3894 raise err.__class__(
3896 klass=self.__class__,
3897 decode_path=decode_path,
3901 l, llen, v = len_decode(lv)
3902 except LenIndefiniteForm as err:
3903 if not ctx.get("bered", False):
3904 raise err.__class__(
3906 klass=self.__class__,
3907 decode_path=decode_path,
3910 llen, vlen, v = 1, 0, lv[1:]
3911 sub_offset = offset + tlen + llen
3914 if v[:EOC_LEN].tobytes() == EOC:
3915 tlvlen = tlen + llen + vlen + EOC_LEN
3916 obj = self.__class__(
3917 value=tlv[:tlvlen].tobytes(),
3919 optional=self.optional,
3920 _decoded=(offset, 0, tlvlen),
3924 return obj, v[EOC_LEN:]
3926 chunk, v = Any().decode(
3929 decode_path=decode_path + (str(chunk_i),),
3933 vlen += chunk.tlvlen
3934 sub_offset += chunk.tlvlen
3936 except DecodeError as err:
3937 raise err.__class__(
3939 klass=self.__class__,
3940 decode_path=decode_path,
3944 raise NotEnoughData(
3945 "encoded length is longer than data",
3946 klass=self.__class__,
3947 decode_path=decode_path,
3950 tlvlen = tlen + llen + l
3951 v, tail = tlv[:tlvlen], v[l:]
3952 obj = self.__class__(
3955 optional=self.optional,
3956 _decoded=(offset, 0, tlvlen),
3962 return pp_console_row(next(self.pps()))
3964 def pps(self, decode_path=()):
3966 asn1_type_name=self.asn1_type_name,
3967 obj_name=self.__class__.__name__,
3968 decode_path=decode_path,
3969 blob=self._value if self.ready else None,
3970 optional=self.optional,
3971 default=self == self.default,
3972 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3973 expl=None if self._expl is None else tag_decode(self._expl),
3978 expl_offset=self.expl_offset if self.expled else None,
3979 expl_tlen=self.expl_tlen if self.expled else None,
3980 expl_llen=self.expl_llen if self.expled else None,
3981 expl_vlen=self.expl_vlen if self.expled else None,
3983 defined_by, defined = self.defined or (None, None)
3984 if defined_by is not None:
3986 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3990 ########################################################################
3991 # ASN.1 constructed types
3992 ########################################################################
3994 def get_def_by_path(defines_by_path, sub_decode_path):
3995 """Get define by decode path
3997 for path, define in defines_by_path:
3998 if len(path) != len(sub_decode_path):
4000 for p1, p2 in zip(path, sub_decode_path):
4001 if (p1 != any) and (p1 != p2):
4007 def abs_decode_path(decode_path, rel_path):
4008 """Create an absolute decode path from current and relative ones
4010 :param decode_path: current decode path, starting point.
4012 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4013 If first tuple's element is "/", then treat it as
4014 an absolute path, ignoring ``decode_path`` as
4015 starting point. Also this tuple can contain ".."
4016 elements, stripping the leading element from
4019 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4020 ("foo", "bar", "baz", "whatever")
4021 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4023 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4026 if rel_path[0] == "/":
4028 if rel_path[0] == "..":
4029 return abs_decode_path(decode_path[:-1], rel_path[1:])
4030 return decode_path + rel_path
4033 class Sequence(Obj):
4034 """``SEQUENCE`` structure type
4036 You have to make specification of sequence::
4038 class Extension(Sequence):
4040 ("extnID", ObjectIdentifier()),
4041 ("critical", Boolean(default=False)),
4042 ("extnValue", OctetString()),
4045 Then, you can work with it as with dictionary.
4047 >>> ext = Extension()
4048 >>> Extension().specs
4050 ('extnID', OBJECT IDENTIFIER),
4051 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4052 ('extnValue', OCTET STRING),
4054 >>> ext["extnID"] = "1.2.3"
4055 Traceback (most recent call last):
4056 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4057 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4059 You can determine if sequence is ready to be encoded:
4064 Traceback (most recent call last):
4065 pyderasn.ObjNotReady: object is not ready: extnValue
4066 >>> ext["extnValue"] = OctetString(b"foobar")
4070 Value you want to assign, must have the same **type** as in
4071 corresponding specification, but it can have different tags,
4072 optional/default attributes -- they will be taken from specification
4075 class TBSCertificate(Sequence):
4077 ("version", Version(expl=tag_ctxc(0), default="v1")),
4080 >>> tbs = TBSCertificate()
4081 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4083 Assign ``None`` to remove value from sequence.
4085 You can set values in Sequence during its initialization:
4087 >>> AlgorithmIdentifier((
4088 ("algorithm", ObjectIdentifier("1.2.3")),
4089 ("parameters", Any(Null()))
4091 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4093 You can determine if value exists/set in the sequence and take its value:
4095 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4098 OBJECT IDENTIFIER 1.2.3
4100 But pay attention that if value has default, then it won't be (not
4101 in) in the sequence (because ``DEFAULT`` must not be encoded in
4102 DER), but you can read its value:
4104 >>> "critical" in ext, ext["critical"]
4105 (False, BOOLEAN False)
4106 >>> ext["critical"] = Boolean(True)
4107 >>> "critical" in ext, ext["critical"]
4108 (True, BOOLEAN True)
4110 All defaulted values are always optional.
4112 .. _strict_default_existence_ctx:
4116 When decoded DER contains defaulted value inside, then
4117 technically this is not valid DER encoding. But we allow and pass
4118 it **by default**. Of course reencoding of that kind of DER will
4119 result in different binary representation (validly without
4120 defaulted value inside). You can enable strict defaulted values
4121 existence validation by setting ``"strict_default_existence":
4122 True`` :ref:`context <ctx>` option -- decoding process will raise
4123 an exception if defaulted value is met.
4125 Two sequences are equal if they have equal specification (schema),
4126 implicit/explicit tagging and the same values.
4128 __slots__ = ("specs",)
4129 tag_default = tag_encode(form=TagFormConstructed, num=16)
4130 asn1_type_name = "SEQUENCE"
4142 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4144 schema = getattr(self, "schema", ())
4146 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4149 if value is not None:
4150 if issubclass(value.__class__, Sequence):
4151 self._value = value._value
4152 elif hasattr(value, "__iter__"):
4153 for seq_key, seq_value in value:
4154 self[seq_key] = seq_value
4156 raise InvalidValueType((Sequence,))
4157 if default is not None:
4158 if not issubclass(default.__class__, Sequence):
4159 raise InvalidValueType((Sequence,))
4160 default_value = default._value
4161 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4162 default_obj.specs = self.specs
4163 default_obj._value = default_value
4164 self.default = default_obj
4166 self._value = default_obj.copy()._value
4170 for name, spec in self.specs.items():
4171 value = self._value.get(name)
4182 obj = self.__class__(schema=self.specs)
4184 obj._expl = self._expl
4185 obj.default = self.default
4186 obj.optional = self.optional
4187 obj.offset = self.offset
4188 obj.llen = self.llen
4189 obj.vlen = self.vlen
4190 obj._value = {k: v.copy() for k, v in self._value.items()}
4193 def __eq__(self, their):
4194 if not isinstance(their, self.__class__):
4197 self.specs == their.specs and
4198 self.tag == their.tag and
4199 self._expl == their._expl and
4200 self._value == their._value
4211 return self.__class__(
4214 impl=self.tag if impl is None else impl,
4215 expl=self._expl if expl is None else expl,
4216 default=self.default if default is None else default,
4217 optional=self.optional if optional is None else optional,
4220 def __contains__(self, key):
4221 return key in self._value
4223 def __setitem__(self, key, value):
4224 spec = self.specs.get(key)
4226 raise ObjUnknown(key)
4228 self._value.pop(key, None)
4230 if not isinstance(value, spec.__class__):
4231 raise InvalidValueType((spec.__class__,))
4232 value = spec(value=value)
4233 if spec.default is not None and value == spec.default:
4234 self._value.pop(key, None)
4236 self._value[key] = value
4238 def __getitem__(self, key):
4239 value = self._value.get(key)
4240 if value is not None:
4242 spec = self.specs.get(key)
4244 raise ObjUnknown(key)
4245 if spec.default is not None:
4249 def _encoded_values(self):
4251 for name, spec in self.specs.items():
4252 value = self._value.get(name)
4256 raise ObjNotReady(name)
4257 raws.append(value.encode())
4261 v = b"".join(self._encoded_values())
4262 return b"".join((self.tag, len_encode(len(v)), v))
4264 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4266 t, tlen, lv = tag_strip(tlv)
4267 except DecodeError as err:
4268 raise err.__class__(
4270 klass=self.__class__,
4271 decode_path=decode_path,
4276 klass=self.__class__,
4277 decode_path=decode_path,
4284 l, llen, v = len_decode(lv)
4285 except LenIndefiniteForm as err:
4286 if not ctx.get("bered", False):
4287 raise err.__class__(
4289 klass=self.__class__,
4290 decode_path=decode_path,
4293 l, llen, v = 0, 1, lv[1:]
4295 except DecodeError as err:
4296 raise err.__class__(
4298 klass=self.__class__,
4299 decode_path=decode_path,
4303 raise NotEnoughData(
4304 "encoded length is longer than data",
4305 klass=self.__class__,
4306 decode_path=decode_path,
4310 v, tail = v[:l], v[l:]
4312 sub_offset = offset + tlen + llen
4314 for name, spec in self.specs.items():
4315 if spec.optional and (
4316 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4320 sub_decode_path = decode_path + (name,)
4322 value, v_tail = spec.decode(
4326 decode_path=sub_decode_path,
4334 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4335 if defined is not None:
4336 defined_by, defined_spec = defined
4337 if issubclass(value.__class__, SequenceOf):
4338 for i, _value in enumerate(value):
4339 sub_sub_decode_path = sub_decode_path + (
4341 DecodePathDefBy(defined_by),
4343 defined_value, defined_tail = defined_spec.decode(
4344 memoryview(bytes(_value)),
4346 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4347 if value.expled else (value.tlen + value.llen)
4350 decode_path=sub_sub_decode_path,
4353 if len(defined_tail) > 0:
4356 klass=self.__class__,
4357 decode_path=sub_sub_decode_path,
4360 _value.defined = (defined_by, defined_value)
4362 defined_value, defined_tail = defined_spec.decode(
4363 memoryview(bytes(value)),
4365 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4366 if value.expled else (value.tlen + value.llen)
4369 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4372 if len(defined_tail) > 0:
4375 klass=self.__class__,
4376 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4379 value.defined = (defined_by, defined_value)
4381 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4383 sub_offset += value_len
4385 if spec.default is not None and value == spec.default:
4386 if ctx.get("strict_default_existence", False):
4388 "DEFAULT value met",
4389 klass=self.__class__,
4390 decode_path=sub_decode_path,
4395 values[name] = value
4397 spec_defines = getattr(spec, "defines", ())
4398 if len(spec_defines) == 0:
4399 defines_by_path = ctx.get("defines_by_path", ())
4400 if len(defines_by_path) > 0:
4401 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4402 if spec_defines is not None and len(spec_defines) > 0:
4403 for rel_path, schema in spec_defines:
4404 defined = schema.get(value, None)
4405 if defined is not None:
4406 ctx.setdefault("defines", []).append((
4407 abs_decode_path(sub_decode_path[:-1], rel_path),
4411 if v[:EOC_LEN].tobytes() != EOC:
4414 klass=self.__class__,
4415 decode_path=decode_path,
4423 klass=self.__class__,
4424 decode_path=decode_path,
4427 obj = self.__class__(
4431 default=self.default,
4432 optional=self.optional,
4433 _decoded=(offset, llen, vlen),
4436 obj.lenindef = lenindef
4440 value = pp_console_row(next(self.pps()))
4442 for name in self.specs:
4443 _value = self._value.get(name)
4446 cols.append(repr(_value))
4447 return "%s[%s]" % (value, ", ".join(cols))
4449 def pps(self, decode_path=()):
4451 asn1_type_name=self.asn1_type_name,
4452 obj_name=self.__class__.__name__,
4453 decode_path=decode_path,
4454 optional=self.optional,
4455 default=self == self.default,
4456 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4457 expl=None if self._expl is None else tag_decode(self._expl),
4462 expl_offset=self.expl_offset if self.expled else None,
4463 expl_tlen=self.expl_tlen if self.expled else None,
4464 expl_llen=self.expl_llen if self.expled else None,
4465 expl_vlen=self.expl_vlen if self.expled else None,
4467 for name in self.specs:
4468 value = self._value.get(name)
4471 yield value.pps(decode_path=decode_path + (name,))
4474 class Set(Sequence):
4475 """``SET`` structure type
4477 Its usage is identical to :py:class:`pyderasn.Sequence`.
4480 tag_default = tag_encode(form=TagFormConstructed, num=17)
4481 asn1_type_name = "SET"
4484 raws = self._encoded_values()
4487 return b"".join((self.tag, len_encode(len(v)), v))
4489 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4491 t, tlen, lv = tag_strip(tlv)
4492 except DecodeError as err:
4493 raise err.__class__(
4495 klass=self.__class__,
4496 decode_path=decode_path,
4501 klass=self.__class__,
4502 decode_path=decode_path,
4509 l, llen, v = len_decode(lv)
4510 except LenIndefiniteForm as err:
4511 if not ctx.get("bered", False):
4512 raise err.__class__(
4514 klass=self.__class__,
4515 decode_path=decode_path,
4518 l, llen, v = 0, 1, lv[1:]
4520 except DecodeError as err:
4521 raise err.__class__(
4523 klass=self.__class__,
4524 decode_path=decode_path,
4528 raise NotEnoughData(
4529 "encoded length is longer than data",
4530 klass=self.__class__,
4534 v, tail = v[:l], v[l:]
4536 sub_offset = offset + tlen + llen
4538 specs_items = self.specs.items
4540 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4542 for name, spec in specs_items():
4543 sub_decode_path = decode_path + (name,)
4549 decode_path=sub_decode_path,
4558 klass=self.__class__,
4559 decode_path=decode_path,
4562 value, v_tail = spec.decode(
4566 decode_path=sub_decode_path,
4569 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4570 sub_offset += value_len
4573 if spec.default is None or value != spec.default: # pragma: no cover
4574 # SeqMixing.test_encoded_default_accepted covers that place
4575 values[name] = value
4576 obj = self.__class__(
4580 default=self.default,
4581 optional=self.optional,
4582 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4587 msg="not all values are ready",
4588 klass=self.__class__,
4589 decode_path=decode_path,
4592 obj.lenindef = lenindef
4593 return obj, (v[EOC_LEN:] if lenindef else tail)
4596 class SequenceOf(Obj):
4597 """``SEQUENCE OF`` sequence type
4599 For that kind of type you must specify the object it will carry on
4600 (bounds are for example here, not required)::
4602 class Ints(SequenceOf):
4607 >>> ints.append(Integer(123))
4608 >>> ints.append(Integer(234))
4610 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4611 >>> [int(i) for i in ints]
4613 >>> ints.append(Integer(345))
4614 Traceback (most recent call last):
4615 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4618 >>> ints[1] = Integer(345)
4620 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4622 Also you can initialize sequence with preinitialized values:
4624 >>> ints = Ints([Integer(123), Integer(234)])
4626 __slots__ = ("spec", "_bound_min", "_bound_max")
4627 tag_default = tag_encode(form=TagFormConstructed, num=16)
4628 asn1_type_name = "SEQUENCE OF"
4641 super(SequenceOf, self).__init__(
4649 schema = getattr(self, "schema", None)
4651 raise ValueError("schema must be specified")
4653 self._bound_min, self._bound_max = getattr(
4657 ) if bounds is None else bounds
4659 if value is not None:
4660 self._value = self._value_sanitize(value)
4661 if default is not None:
4662 default_value = self._value_sanitize(default)
4663 default_obj = self.__class__(
4668 default_obj._value = default_value
4669 self.default = default_obj
4671 self._value = default_obj.copy()._value
4673 def _value_sanitize(self, value):
4674 if issubclass(value.__class__, SequenceOf):
4675 value = value._value
4676 elif hasattr(value, "__iter__"):
4679 raise InvalidValueType((self.__class__, iter))
4680 if not self._bound_min <= len(value) <= self._bound_max:
4681 raise BoundsError(self._bound_min, len(value), self._bound_max)
4683 if not isinstance(v, self.spec.__class__):
4684 raise InvalidValueType((self.spec.__class__,))
4689 return all(v.ready for v in self._value)
4692 obj = self.__class__(schema=self.spec)
4693 obj._bound_min = self._bound_min
4694 obj._bound_max = self._bound_max
4696 obj._expl = self._expl
4697 obj.default = self.default
4698 obj.optional = self.optional
4699 obj.offset = self.offset
4700 obj.llen = self.llen
4701 obj.vlen = self.vlen
4702 obj._value = [v.copy() for v in self._value]
4705 def __eq__(self, their):
4706 if isinstance(their, self.__class__):
4708 self.spec == their.spec and
4709 self.tag == their.tag and
4710 self._expl == their._expl and
4711 self._value == their._value
4713 if hasattr(their, "__iter__"):
4714 return self._value == list(their)
4726 return self.__class__(
4730 (self._bound_min, self._bound_max)
4731 if bounds is None else bounds
4733 impl=self.tag if impl is None else impl,
4734 expl=self._expl if expl is None else expl,
4735 default=self.default if default is None else default,
4736 optional=self.optional if optional is None else optional,
4739 def __contains__(self, key):
4740 return key in self._value
4742 def append(self, value):
4743 if not isinstance(value, self.spec.__class__):
4744 raise InvalidValueType((self.spec.__class__,))
4745 if len(self._value) + 1 > self._bound_max:
4748 len(self._value) + 1,
4751 self._value.append(value)
4754 self._assert_ready()
4755 return iter(self._value)
4758 self._assert_ready()
4759 return len(self._value)
4761 def __setitem__(self, key, value):
4762 if not isinstance(value, self.spec.__class__):
4763 raise InvalidValueType((self.spec.__class__,))
4764 self._value[key] = self.spec(value=value)
4766 def __getitem__(self, key):
4767 return self._value[key]
4769 def _encoded_values(self):
4770 return [v.encode() for v in self._value]
4773 v = b"".join(self._encoded_values())
4774 return b"".join((self.tag, len_encode(len(v)), v))
4776 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4778 t, tlen, lv = tag_strip(tlv)
4779 except DecodeError as err:
4780 raise err.__class__(
4782 klass=self.__class__,
4783 decode_path=decode_path,
4788 klass=self.__class__,
4789 decode_path=decode_path,
4796 l, llen, v = len_decode(lv)
4797 except LenIndefiniteForm as err:
4798 if not ctx.get("bered", False):
4799 raise err.__class__(
4801 klass=self.__class__,
4802 decode_path=decode_path,
4805 l, llen, v = 0, 1, lv[1:]
4807 except DecodeError as err:
4808 raise err.__class__(
4810 klass=self.__class__,
4811 decode_path=decode_path,
4815 raise NotEnoughData(
4816 "encoded length is longer than data",
4817 klass=self.__class__,
4818 decode_path=decode_path,
4822 v, tail = v[:l], v[l:]
4824 sub_offset = offset + tlen + llen
4828 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4830 value, v_tail = spec.decode(
4834 decode_path=decode_path + (str(len(_value)),),
4837 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4838 sub_offset += value_len
4841 _value.append(value)
4842 obj = self.__class__(
4845 bounds=(self._bound_min, self._bound_max),
4848 default=self.default,
4849 optional=self.optional,
4850 _decoded=(offset, llen, vlen),
4852 obj.lenindef = lenindef
4853 return obj, (v[EOC_LEN:] if lenindef else tail)
4857 pp_console_row(next(self.pps())),
4858 ", ".join(repr(v) for v in self._value),
4861 def pps(self, decode_path=()):
4863 asn1_type_name=self.asn1_type_name,
4864 obj_name=self.__class__.__name__,
4865 decode_path=decode_path,
4866 optional=self.optional,
4867 default=self == self.default,
4868 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4869 expl=None if self._expl is None else tag_decode(self._expl),
4874 expl_offset=self.expl_offset if self.expled else None,
4875 expl_tlen=self.expl_tlen if self.expled else None,
4876 expl_llen=self.expl_llen if self.expled else None,
4877 expl_vlen=self.expl_vlen if self.expled else None,
4879 for i, value in enumerate(self._value):
4880 yield value.pps(decode_path=decode_path + (str(i),))
4883 class SetOf(SequenceOf):
4884 """``SET OF`` sequence type
4886 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4889 tag_default = tag_encode(form=TagFormConstructed, num=17)
4890 asn1_type_name = "SET OF"
4893 raws = self._encoded_values()
4896 return b"".join((self.tag, len_encode(len(v)), v))
4899 def obj_by_path(pypath): # pragma: no cover
4900 """Import object specified as string Python path
4902 Modules must be separated from classes/functions with ``:``.
4904 >>> obj_by_path("foo.bar:Baz")
4905 <class 'foo.bar.Baz'>
4906 >>> obj_by_path("foo.bar:Baz.boo")
4907 <classmethod 'foo.bar.Baz.boo'>
4909 mod, objs = pypath.rsplit(":", 1)
4910 from importlib import import_module
4911 obj = import_module(mod)
4912 for obj_name in objs.split("."):
4913 obj = getattr(obj, obj_name)
4917 def generic_decoder(): # pragma: no cover
4918 # All of this below is a big hack with self references
4919 choice = PrimitiveTypes()
4920 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4921 choice.specs["SetOf"] = SetOf(schema=choice)
4923 choice.specs["SequenceOf%d" % i] = SequenceOf(
4927 choice.specs["Any"] = Any()
4929 # Class name equals to type name, to omit it from output
4930 class SEQUENCEOF(SequenceOf):
4934 def pprint_any(obj, oids=None, with_colours=False):
4935 def _pprint_pps(pps):
4937 if hasattr(pp, "_fields"):
4938 if pp.asn1_type_name == Choice.asn1_type_name:
4940 pp_kwargs = pp._asdict()
4941 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4942 pp = _pp(**pp_kwargs)
4943 yield pp_console_row(
4948 with_colours=with_colours,
4950 for row in pp_console_blob(pp):
4953 for row in _pprint_pps(pp):
4955 return "\n".join(_pprint_pps(obj.pps()))
4956 return SEQUENCEOF(), pprint_any
4959 def main(): # pragma: no cover
4961 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4962 parser.add_argument(
4966 help="Skip that number of bytes from the beginning",
4968 parser.add_argument(
4970 help="Python path to dictionary with OIDs",
4972 parser.add_argument(
4974 help="Python path to schema definition to use",
4976 parser.add_argument(
4977 "--defines-by-path",
4978 help="Python path to decoder's defines_by_path",
4980 parser.add_argument(
4982 type=argparse.FileType("rb"),
4983 help="Path to DER file you want to decode",
4985 args = parser.parse_args()
4986 args.DERFile.seek(args.skip)
4987 der = memoryview(args.DERFile.read())
4988 args.DERFile.close()
4989 oids = obj_by_path(args.oids) if args.oids else {}
4991 schema = obj_by_path(args.schema)
4992 from functools import partial
4993 pprinter = partial(pprint, big_blobs=True)
4995 schema, pprinter = generic_decoder()
4996 ctx = {"bered": True}
4997 if args.defines_by_path is not None:
4998 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
4999 obj, tail = schema().decode(der, ctx=ctx)
5003 with_colours=True if environ.get("NO_COLOR") is None else False,
5006 print("\nTrailing data: %s" % hexenc(tail))
5009 if __name__ == "__main__":