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.
863 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
864 self._expl = getattr(self, "expl", None) if expl is None else expl
865 if self.tag != self.tag_default and self._expl is not None:
867 "implicit and explicit tags can not be set simultaneously"
869 if default is not None:
871 self.optional = optional
872 self.offset, self.llen, self.vlen = _decoded
877 def ready(self): # pragma: no cover
878 """Is object ready to be encoded?
880 raise NotImplementedError()
882 def _assert_ready(self):
884 raise ObjNotReady(self.__class__.__name__)
888 """Is object decoded?
890 return (self.llen + self.vlen) > 0
892 def copy(self): # pragma: no cover
893 """Make a copy of object, safe to be mutated
895 raise NotImplementedError()
903 return self.tlen + self.llen + self.vlen
905 def __str__(self): # pragma: no cover
906 return self.__bytes__() if PY2 else self.__unicode__()
908 def __ne__(self, their):
909 return not(self == their)
911 def __gt__(self, their): # pragma: no cover
912 return not(self < their)
914 def __le__(self, their): # pragma: no cover
915 return (self == their) or (self < their)
917 def __ge__(self, their): # pragma: no cover
918 return (self == their) or (self > their)
920 def _encode(self): # pragma: no cover
921 raise NotImplementedError()
923 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
924 raise NotImplementedError()
928 if self._expl is None:
930 return b"".join((self._expl, len_encode(len(raw)), raw))
943 :param data: either binary or memoryview
944 :param int offset: initial data's offset
945 :param bool leavemm: do we need to leave memoryview of remaining
946 data as is, or convert it to bytes otherwise
947 :param ctx: optional :ref:`context <ctx>` governing decoding process.
948 :param tag_only: decode only the tag, without length and contents
949 (used only in Choice and Set structures, trying to
950 determine if tag satisfies the scheme)
951 :returns: (Obj, remaining data)
955 tlv = memoryview(data)
956 if self._expl is None:
957 result = self._decode(
960 decode_path=decode_path,
969 t, tlen, lv = tag_strip(tlv)
970 except DecodeError as err:
973 klass=self.__class__,
974 decode_path=decode_path,
979 klass=self.__class__,
980 decode_path=decode_path,
984 l, llen, v = len_decode(lv)
985 except DecodeError as err:
988 klass=self.__class__,
989 decode_path=decode_path,
994 "encoded length is longer than data",
995 klass=self.__class__,
996 decode_path=decode_path,
999 result = self._decode(
1001 offset=offset + tlen + llen,
1002 decode_path=decode_path,
1009 return obj, (tail if leavemm else tail.tobytes())
1013 return self._expl is not None
1020 def expl_tlen(self):
1021 return len(self._expl)
1024 def expl_llen(self):
1025 return len(len_encode(self.tlvlen))
1028 def expl_offset(self):
1029 return self.offset - self.expl_tlen - self.expl_llen
1032 def expl_vlen(self):
1036 def expl_tlvlen(self):
1037 return self.expl_tlen + self.expl_llen + self.expl_vlen
1040 class DecodePathDefBy(object):
1041 """DEFINED BY representation inside decode path
1043 __slots__ = ("defined_by",)
1045 def __init__(self, defined_by):
1046 self.defined_by = defined_by
1048 def __ne__(self, their):
1049 return not(self == their)
1051 def __eq__(self, their):
1052 if not isinstance(their, self.__class__):
1054 return self.defined_by == their.defined_by
1057 return "DEFINED BY " + str(self.defined_by)
1060 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1063 ########################################################################
1065 ########################################################################
1067 PP = namedtuple("PP", (
1089 asn1_type_name="unknown",
1128 def _colorize(what, colour, with_colours, attrs=("bold",)):
1129 return colored(what, colour, attrs=attrs) if with_colours else what
1144 " " if pp.expl_offset is None else
1145 ("-%d" % (pp.offset - pp.expl_offset))
1148 cols.append(_colorize(col, "red", with_colours, ()))
1149 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1150 cols.append(_colorize(col, "green", with_colours, ()))
1151 if len(pp.decode_path) > 0:
1152 cols.append(" ." * (len(pp.decode_path)))
1153 ent = pp.decode_path[-1]
1154 if isinstance(ent, DecodePathDefBy):
1155 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1156 value = str(ent.defined_by)
1158 oids is not None and
1159 ent.defined_by.asn1_type_name ==
1160 ObjectIdentifier.asn1_type_name and
1163 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1165 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1167 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1168 if pp.expl is not None:
1169 klass, _, num = pp.expl
1170 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1171 cols.append(_colorize(col, "blue", with_colours))
1172 if pp.impl is not None:
1173 klass, _, num = pp.impl
1174 col = "[%s%d]" % (TagClassReprs[klass], num)
1175 cols.append(_colorize(col, "blue", with_colours))
1176 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1177 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1178 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1179 if pp.value is not None:
1181 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1183 oids is not None and
1184 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1187 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1189 if isinstance(pp.blob, binary_type):
1190 cols.append(hexenc(pp.blob))
1191 elif isinstance(pp.blob, tuple):
1192 cols.append(", ".join(pp.blob))
1194 cols.append(_colorize("OPTIONAL", "red", with_colours))
1196 cols.append(_colorize("DEFAULT", "red", with_colours))
1197 return " ".join(cols)
1200 def pp_console_blob(pp):
1201 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1202 if len(pp.decode_path) > 0:
1203 cols.append(" ." * (len(pp.decode_path) + 1))
1204 if isinstance(pp.blob, binary_type):
1205 blob = hexenc(pp.blob).upper()
1206 for i in range(0, len(blob), 32):
1207 chunk = blob[i:i + 32]
1208 yield " ".join(cols + [":".join(
1209 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1211 elif isinstance(pp.blob, tuple):
1212 yield " ".join(cols + [", ".join(pp.blob)])
1215 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1216 """Pretty print object
1218 :param Obj obj: object you want to pretty print
1219 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1220 from it is met, then its humand readable form is printed
1221 :param big_blobs: if large binary objects are met (like OctetString
1222 values), do we need to print them too, on separate
1224 :param with_colours: colourize output, if ``termcolor`` library
1227 def _pprint_pps(pps):
1229 if hasattr(pp, "_fields"):
1231 yield pp_console_row(
1236 with_colours=with_colours,
1238 for row in pp_console_blob(pp):
1241 yield pp_console_row(
1246 with_colours=with_colours,
1249 for row in _pprint_pps(pp):
1251 return "\n".join(_pprint_pps(obj.pps()))
1254 ########################################################################
1255 # ASN.1 primitive types
1256 ########################################################################
1259 """``BOOLEAN`` boolean type
1261 >>> b = Boolean(True)
1263 >>> b == Boolean(True)
1269 tag_default = tag_encode(1)
1270 asn1_type_name = "BOOLEAN"
1282 :param value: set the value. Either boolean type, or
1283 :py:class:`pyderasn.Boolean` object
1284 :param bytes impl: override default tag with ``IMPLICIT`` one
1285 :param bytes expl: override default tag with ``EXPLICIT`` one
1286 :param default: set default value. Type same as in ``value``
1287 :param bool optional: is object ``OPTIONAL`` in sequence
1289 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1290 self._value = None if value is None else self._value_sanitize(value)
1291 if default is not None:
1292 default = self._value_sanitize(default)
1293 self.default = self.__class__(
1299 self._value = default
1301 def _value_sanitize(self, value):
1302 if issubclass(value.__class__, Boolean):
1304 if isinstance(value, bool):
1306 raise InvalidValueType((self.__class__, bool))
1310 return self._value is not None
1313 obj = self.__class__()
1314 obj._value = self._value
1316 obj._expl = self._expl
1317 obj.default = self.default
1318 obj.optional = self.optional
1319 obj.offset = self.offset
1320 obj.llen = self.llen
1321 obj.vlen = self.vlen
1324 def __nonzero__(self):
1325 self._assert_ready()
1329 self._assert_ready()
1332 def __eq__(self, their):
1333 if isinstance(their, bool):
1334 return self._value == their
1335 if not issubclass(their.__class__, Boolean):
1338 self._value == their._value and
1339 self.tag == their.tag and
1340 self._expl == their._expl
1351 return self.__class__(
1353 impl=self.tag if impl is None else impl,
1354 expl=self._expl if expl is None else expl,
1355 default=self.default if default is None else default,
1356 optional=self.optional if optional is None else optional,
1360 self._assert_ready()
1364 (b"\xFF" if self._value else b"\x00"),
1367 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1369 t, _, lv = tag_strip(tlv)
1370 except DecodeError as err:
1371 raise err.__class__(
1373 klass=self.__class__,
1374 decode_path=decode_path,
1379 klass=self.__class__,
1380 decode_path=decode_path,
1386 l, _, v = len_decode(lv)
1387 except DecodeError as err:
1388 raise err.__class__(
1390 klass=self.__class__,
1391 decode_path=decode_path,
1395 raise InvalidLength(
1396 "Boolean's length must be equal to 1",
1397 klass=self.__class__,
1398 decode_path=decode_path,
1402 raise NotEnoughData(
1403 "encoded length is longer than data",
1404 klass=self.__class__,
1405 decode_path=decode_path,
1408 first_octet = byte2int(v)
1410 if first_octet == 0:
1412 elif first_octet == 0xFF:
1414 elif ctx.get("bered", False):
1419 "unacceptable Boolean value",
1420 klass=self.__class__,
1421 decode_path=decode_path,
1424 obj = self.__class__(
1428 default=self.default,
1429 optional=self.optional,
1430 _decoded=(offset, 1, 1),
1436 return pp_console_row(next(self.pps()))
1438 def pps(self, decode_path=()):
1440 asn1_type_name=self.asn1_type_name,
1441 obj_name=self.__class__.__name__,
1442 decode_path=decode_path,
1443 value=str(self._value) if self.ready else None,
1444 optional=self.optional,
1445 default=self == self.default,
1446 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1447 expl=None if self._expl is None else tag_decode(self._expl),
1452 expl_offset=self.expl_offset if self.expled else None,
1453 expl_tlen=self.expl_tlen if self.expled else None,
1454 expl_llen=self.expl_llen if self.expled else None,
1455 expl_vlen=self.expl_vlen if self.expled else None,
1460 """``INTEGER`` integer type
1462 >>> b = Integer(-123)
1464 >>> b == Integer(-123)
1469 >>> Integer(2, bounds=(1, 3))
1471 >>> Integer(5, bounds=(1, 3))
1472 Traceback (most recent call last):
1473 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1477 class Version(Integer):
1484 >>> v = Version("v1")
1491 {'v3': 2, 'v1': 0, 'v2': 1}
1493 __slots__ = ("specs", "_bound_min", "_bound_max")
1494 tag_default = tag_encode(2)
1495 asn1_type_name = "INTEGER"
1509 :param value: set the value. Either integer type, named value
1510 (if ``schema`` is specified in the class), or
1511 :py:class:`pyderasn.Integer` object
1512 :param bounds: set ``(MIN, MAX)`` value constraint.
1513 (-inf, +inf) by default
1514 :param bytes impl: override default tag with ``IMPLICIT`` one
1515 :param bytes expl: override default tag with ``EXPLICIT`` one
1516 :param default: set default value. Type same as in ``value``
1517 :param bool optional: is object ``OPTIONAL`` in sequence
1519 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1521 specs = getattr(self, "schema", {}) if _specs is None else _specs
1522 self.specs = specs if isinstance(specs, dict) else dict(specs)
1523 self._bound_min, self._bound_max = getattr(
1526 (float("-inf"), float("+inf")),
1527 ) if bounds is None else bounds
1528 if value is not None:
1529 self._value = self._value_sanitize(value)
1530 if default is not None:
1531 default = self._value_sanitize(default)
1532 self.default = self.__class__(
1538 if self._value is None:
1539 self._value = default
1541 def _value_sanitize(self, value):
1542 if issubclass(value.__class__, Integer):
1543 value = value._value
1544 elif isinstance(value, integer_types):
1546 elif isinstance(value, str):
1547 value = self.specs.get(value)
1549 raise ObjUnknown("integer value: %s" % value)
1551 raise InvalidValueType((self.__class__, int, str))
1552 if not self._bound_min <= value <= self._bound_max:
1553 raise BoundsError(self._bound_min, value, self._bound_max)
1558 return self._value is not None
1561 obj = self.__class__(_specs=self.specs)
1562 obj._value = self._value
1563 obj._bound_min = self._bound_min
1564 obj._bound_max = self._bound_max
1566 obj._expl = self._expl
1567 obj.default = self.default
1568 obj.optional = self.optional
1569 obj.offset = self.offset
1570 obj.llen = self.llen
1571 obj.vlen = self.vlen
1575 self._assert_ready()
1576 return int(self._value)
1579 self._assert_ready()
1582 bytes(self._expl or b"") +
1583 str(self._value).encode("ascii"),
1586 def __eq__(self, their):
1587 if isinstance(their, integer_types):
1588 return self._value == their
1589 if not issubclass(their.__class__, Integer):
1592 self._value == their._value and
1593 self.tag == their.tag and
1594 self._expl == their._expl
1597 def __lt__(self, their):
1598 return self._value < their._value
1602 for name, value in self.specs.items():
1603 if value == self._value:
1615 return self.__class__(
1618 (self._bound_min, self._bound_max)
1619 if bounds is None else bounds
1621 impl=self.tag if impl is None else impl,
1622 expl=self._expl if expl is None else expl,
1623 default=self.default if default is None else default,
1624 optional=self.optional if optional is None else optional,
1629 self._assert_ready()
1633 octets = bytearray([0])
1637 octets = bytearray()
1639 octets.append((value & 0xFF) ^ 0xFF)
1641 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1644 octets = bytearray()
1646 octets.append(value & 0xFF)
1648 if octets[-1] & 0x80 > 0:
1651 octets = bytes(octets)
1653 bytes_len = ceil(value.bit_length() / 8) or 1
1656 octets = value.to_bytes(
1661 except OverflowError:
1665 return b"".join((self.tag, len_encode(len(octets)), octets))
1667 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1669 t, _, lv = tag_strip(tlv)
1670 except DecodeError as err:
1671 raise err.__class__(
1673 klass=self.__class__,
1674 decode_path=decode_path,
1679 klass=self.__class__,
1680 decode_path=decode_path,
1686 l, llen, v = len_decode(lv)
1687 except DecodeError as err:
1688 raise err.__class__(
1690 klass=self.__class__,
1691 decode_path=decode_path,
1695 raise NotEnoughData(
1696 "encoded length is longer than data",
1697 klass=self.__class__,
1698 decode_path=decode_path,
1702 raise NotEnoughData(
1704 klass=self.__class__,
1705 decode_path=decode_path,
1708 v, tail = v[:l], v[l:]
1709 first_octet = byte2int(v)
1711 second_octet = byte2int(v[1:])
1713 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1714 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1717 "non normalized integer",
1718 klass=self.__class__,
1719 decode_path=decode_path,
1724 if first_octet & 0x80 > 0:
1725 octets = bytearray()
1726 for octet in bytearray(v):
1727 octets.append(octet ^ 0xFF)
1728 for octet in octets:
1729 value = (value << 8) | octet
1733 for octet in bytearray(v):
1734 value = (value << 8) | octet
1736 value = int.from_bytes(v, byteorder="big", signed=True)
1738 obj = self.__class__(
1740 bounds=(self._bound_min, self._bound_max),
1743 default=self.default,
1744 optional=self.optional,
1746 _decoded=(offset, llen, l),
1748 except BoundsError as err:
1751 klass=self.__class__,
1752 decode_path=decode_path,
1758 return pp_console_row(next(self.pps()))
1760 def pps(self, decode_path=()):
1762 asn1_type_name=self.asn1_type_name,
1763 obj_name=self.__class__.__name__,
1764 decode_path=decode_path,
1765 value=(self.named or str(self._value)) if self.ready else None,
1766 optional=self.optional,
1767 default=self == self.default,
1768 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1769 expl=None if self._expl is None else tag_decode(self._expl),
1774 expl_offset=self.expl_offset if self.expled else None,
1775 expl_tlen=self.expl_tlen if self.expled else None,
1776 expl_llen=self.expl_llen if self.expled else None,
1777 expl_vlen=self.expl_vlen if self.expled else None,
1781 class BitString(Obj):
1782 """``BIT STRING`` bit string type
1784 >>> BitString(b"hello world")
1785 BIT STRING 88 bits 68656c6c6f20776f726c64
1788 >>> b == b"hello world"
1793 >>> b = BitString("'010110000000'B")
1794 BIT STRING 12 bits 5800
1797 >>> b[0], b[1], b[2], b[3]
1798 (False, True, False, True)
1802 [False, True, False, True, True, False, False, False, False, False, False, False]
1806 class KeyUsage(BitString):
1808 ("digitalSignature", 0),
1809 ("nonRepudiation", 1),
1810 ("keyEncipherment", 2),
1813 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1814 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1816 ['nonRepudiation', 'keyEncipherment']
1818 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1820 __slots__ = ("tag_constructed", "specs", "defined")
1821 tag_default = tag_encode(3)
1822 asn1_type_name = "BIT STRING"
1835 :param value: set the value. Either binary type, tuple of named
1836 values (if ``schema`` is specified in the class),
1837 string in ``'XXX...'B`` form, or
1838 :py:class:`pyderasn.BitString` object
1839 :param bytes impl: override default tag with ``IMPLICIT`` one
1840 :param bytes expl: override default tag with ``EXPLICIT`` one
1841 :param default: set default value. Type same as in ``value``
1842 :param bool optional: is object ``OPTIONAL`` in sequence
1844 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1845 specs = getattr(self, "schema", {}) if _specs is None else _specs
1846 self.specs = specs if isinstance(specs, dict) else dict(specs)
1847 self._value = None if value is None else self._value_sanitize(value)
1848 if default is not None:
1849 default = self._value_sanitize(default)
1850 self.default = self.__class__(
1856 self._value = default
1858 tag_klass, _, tag_num = tag_decode(self.tag)
1859 self.tag_constructed = tag_encode(
1861 form=TagFormConstructed,
1865 def _bits2octets(self, bits):
1866 if len(self.specs) > 0:
1867 bits = bits.rstrip("0")
1869 bits += "0" * ((8 - (bit_len % 8)) % 8)
1870 octets = bytearray(len(bits) // 8)
1871 for i in six_xrange(len(octets)):
1872 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1873 return bit_len, bytes(octets)
1875 def _value_sanitize(self, value):
1876 if issubclass(value.__class__, BitString):
1878 if isinstance(value, (string_types, binary_type)):
1880 isinstance(value, string_types) and
1881 value.startswith("'") and
1882 value.endswith("'B")
1885 if not set(value) <= set(("0", "1")):
1886 raise ValueError("B's coding contains unacceptable chars")
1887 return self._bits2octets(value)
1888 elif isinstance(value, binary_type):
1889 return (len(value) * 8, value)
1891 raise InvalidValueType((
1896 if isinstance(value, tuple):
1899 isinstance(value[0], integer_types) and
1900 isinstance(value[1], binary_type)
1905 bit = self.specs.get(name)
1907 raise ObjUnknown("BitString value: %s" % name)
1910 return self._bits2octets("")
1912 return self._bits2octets("".join(
1913 ("1" if bit in bits else "0")
1914 for bit in six_xrange(max(bits) + 1)
1916 raise InvalidValueType((self.__class__, binary_type, string_types))
1920 return self._value is not None
1923 obj = self.__class__(_specs=self.specs)
1925 if value is not None:
1926 value = (value[0], value[1])
1929 obj._expl = self._expl
1930 obj.default = self.default
1931 obj.optional = self.optional
1932 obj.offset = self.offset
1933 obj.llen = self.llen
1934 obj.vlen = self.vlen
1938 self._assert_ready()
1939 for i in six_xrange(self._value[0]):
1944 self._assert_ready()
1945 return self._value[0]
1947 def __bytes__(self):
1948 self._assert_ready()
1949 return self._value[1]
1951 def __eq__(self, their):
1952 if isinstance(their, bytes):
1953 return self._value[1] == their
1954 if not issubclass(their.__class__, BitString):
1957 self._value == their._value and
1958 self.tag == their.tag and
1959 self._expl == their._expl
1964 return [name for name, bit in self.specs.items() if self[bit]]
1974 return self.__class__(
1976 impl=self.tag if impl is None else impl,
1977 expl=self._expl if expl is None else expl,
1978 default=self.default if default is None else default,
1979 optional=self.optional if optional is None else optional,
1983 def __getitem__(self, key):
1984 if isinstance(key, int):
1985 bit_len, octets = self._value
1989 byte2int(memoryview(octets)[key // 8:]) >>
1992 if isinstance(key, string_types):
1993 value = self.specs.get(key)
1995 raise ObjUnknown("BitString value: %s" % key)
1997 raise InvalidValueType((int, str))
2000 self._assert_ready()
2001 bit_len, octets = self._value
2004 len_encode(len(octets) + 1),
2005 int2byte((8 - bit_len % 8) % 8),
2009 def _decode_chunk(self, lv, offset, decode_path, ctx):
2011 l, llen, v = len_decode(lv)
2012 except DecodeError as err:
2013 raise err.__class__(
2015 klass=self.__class__,
2016 decode_path=decode_path,
2020 raise NotEnoughData(
2021 "encoded length is longer than data",
2022 klass=self.__class__,
2023 decode_path=decode_path,
2027 raise NotEnoughData(
2029 klass=self.__class__,
2030 decode_path=decode_path,
2033 pad_size = byte2int(v)
2034 if l == 1 and pad_size != 0:
2036 "invalid empty value",
2037 klass=self.__class__,
2038 decode_path=decode_path,
2044 klass=self.__class__,
2045 decode_path=decode_path,
2048 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2051 klass=self.__class__,
2052 decode_path=decode_path,
2055 v, tail = v[:l], v[l:]
2056 obj = self.__class__(
2057 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2060 default=self.default,
2061 optional=self.optional,
2063 _decoded=(offset, llen, l),
2067 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2069 t, tlen, lv = tag_strip(tlv)
2070 except DecodeError as err:
2071 raise err.__class__(
2073 klass=self.__class__,
2074 decode_path=decode_path,
2080 return self._decode_chunk(lv, offset, decode_path, ctx)
2081 if t == self.tag_constructed:
2082 if not ctx.get("bered", False):
2084 msg="unallowed BER constructed encoding",
2085 decode_path=decode_path,
2090 eoc_expected = False
2092 l, llen, v = len_decode(lv)
2093 except LenIndefiniteForm:
2094 llen, l, v = 1, 0, lv[1:]
2096 except DecodeError as err:
2097 raise err.__class__(
2099 klass=self.__class__,
2100 decode_path=decode_path,
2103 if l > 0 and l > len(v):
2104 raise NotEnoughData(
2105 "encoded length is longer than data",
2106 klass=self.__class__,
2107 decode_path=decode_path,
2110 if not eoc_expected and l == 0:
2111 raise NotEnoughData(
2113 klass=self.__class__,
2114 decode_path=decode_path,
2118 sub_offset = offset + tlen + llen
2122 if v[:EOC_LEN].tobytes() == EOC:
2129 msg="chunk out of bounds",
2130 decode_path=len(chunks) - 1,
2131 offset=chunks[-1].offset,
2133 sub_decode_path = decode_path + (str(len(chunks)),)
2135 chunk, v_tail = BitString().decode(
2138 decode_path=sub_decode_path,
2144 msg="expected BitString encoded chunk",
2145 decode_path=sub_decode_path,
2148 chunks.append(chunk)
2149 sub_offset += chunk.tlvlen
2150 vlen += chunk.tlvlen
2152 if len(chunks) == 0:
2155 decode_path=decode_path,
2160 for chunk_i, chunk in enumerate(chunks[:-1]):
2161 if chunk.bit_len % 8 != 0:
2163 msg="BitString chunk is not multiple of 8 bit",
2164 decode_path=decode_path + (str(chunk_i),),
2165 offset=chunk.offset,
2167 values.append(bytes(chunk))
2168 bit_len += chunk.bit_len
2169 chunk_last = chunks[-1]
2170 values.append(bytes(chunk_last))
2171 bit_len += chunk_last.bit_len
2172 obj = self.__class__(
2173 value=(bit_len, b"".join(values)),
2176 default=self.default,
2177 optional=self.optional,
2179 _decoded=(offset, llen, vlen + (EOC_LEN if eoc_expected else 0)),
2182 return obj, v[EOC_LEN if eoc_expected else 0:]
2184 klass=self.__class__,
2185 decode_path=decode_path,
2190 return pp_console_row(next(self.pps()))
2192 def pps(self, decode_path=()):
2196 bit_len, blob = self._value
2197 value = "%d bits" % bit_len
2198 if len(self.specs) > 0:
2199 blob = tuple(self.named)
2201 asn1_type_name=self.asn1_type_name,
2202 obj_name=self.__class__.__name__,
2203 decode_path=decode_path,
2206 optional=self.optional,
2207 default=self == self.default,
2208 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2209 expl=None if self._expl is None else tag_decode(self._expl),
2214 expl_offset=self.expl_offset if self.expled else None,
2215 expl_tlen=self.expl_tlen if self.expled else None,
2216 expl_llen=self.expl_llen if self.expled else None,
2217 expl_vlen=self.expl_vlen if self.expled else None,
2219 defined_by, defined = self.defined or (None, None)
2220 if defined_by is not None:
2222 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2226 class OctetString(Obj):
2227 """``OCTET STRING`` binary string type
2229 >>> s = OctetString(b"hello world")
2230 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2231 >>> s == OctetString(b"hello world")
2236 >>> OctetString(b"hello", bounds=(4, 4))
2237 Traceback (most recent call last):
2238 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2239 >>> OctetString(b"hell", bounds=(4, 4))
2240 OCTET STRING 4 bytes 68656c6c
2242 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2243 tag_default = tag_encode(4)
2244 asn1_type_name = "OCTET STRING"
2257 :param value: set the value. Either binary type, or
2258 :py:class:`pyderasn.OctetString` object
2259 :param bounds: set ``(MIN, MAX)`` value size constraint.
2260 (-inf, +inf) by default
2261 :param bytes impl: override default tag with ``IMPLICIT`` one
2262 :param bytes expl: override default tag with ``EXPLICIT`` one
2263 :param default: set default value. Type same as in ``value``
2264 :param bool optional: is object ``OPTIONAL`` in sequence
2266 super(OctetString, self).__init__(
2274 self._bound_min, self._bound_max = getattr(
2278 ) if bounds is None else bounds
2279 if value is not None:
2280 self._value = self._value_sanitize(value)
2281 if default is not None:
2282 default = self._value_sanitize(default)
2283 self.default = self.__class__(
2288 if self._value is None:
2289 self._value = default
2291 tag_klass, _, tag_num = tag_decode(self.tag)
2292 self.tag_constructed = tag_encode(
2294 form=TagFormConstructed,
2298 def _value_sanitize(self, value):
2299 if issubclass(value.__class__, OctetString):
2300 value = value._value
2301 elif isinstance(value, binary_type):
2304 raise InvalidValueType((self.__class__, bytes))
2305 if not self._bound_min <= len(value) <= self._bound_max:
2306 raise BoundsError(self._bound_min, len(value), self._bound_max)
2311 return self._value is not None
2314 obj = self.__class__()
2315 obj._value = self._value
2316 obj._bound_min = self._bound_min
2317 obj._bound_max = self._bound_max
2319 obj._expl = self._expl
2320 obj.default = self.default
2321 obj.optional = self.optional
2322 obj.offset = self.offset
2323 obj.llen = self.llen
2324 obj.vlen = self.vlen
2327 def __bytes__(self):
2328 self._assert_ready()
2331 def __eq__(self, their):
2332 if isinstance(their, binary_type):
2333 return self._value == their
2334 if not issubclass(their.__class__, OctetString):
2337 self._value == their._value and
2338 self.tag == their.tag and
2339 self._expl == their._expl
2342 def __lt__(self, their):
2343 return self._value < their._value
2354 return self.__class__(
2357 (self._bound_min, self._bound_max)
2358 if bounds is None else bounds
2360 impl=self.tag if impl is None else impl,
2361 expl=self._expl if expl is None else expl,
2362 default=self.default if default is None else default,
2363 optional=self.optional if optional is None else optional,
2367 self._assert_ready()
2370 len_encode(len(self._value)),
2374 def _decode_chunk(self, lv, offset, decode_path, ctx):
2376 l, llen, v = len_decode(lv)
2377 except DecodeError as err:
2378 raise err.__class__(
2380 klass=self.__class__,
2381 decode_path=decode_path,
2385 raise NotEnoughData(
2386 "encoded length is longer than data",
2387 klass=self.__class__,
2388 decode_path=decode_path,
2391 v, tail = v[:l], v[l:]
2393 obj = self.__class__(
2395 bounds=(self._bound_min, self._bound_max),
2398 default=self.default,
2399 optional=self.optional,
2400 _decoded=(offset, llen, l),
2402 except DecodeError as err:
2405 klass=self.__class__,
2406 decode_path=decode_path,
2409 except BoundsError as err:
2412 klass=self.__class__,
2413 decode_path=decode_path,
2418 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2420 t, tlen, lv = tag_strip(tlv)
2421 except DecodeError as err:
2422 raise err.__class__(
2424 klass=self.__class__,
2425 decode_path=decode_path,
2431 return self._decode_chunk(lv, offset, decode_path, ctx)
2432 if t == self.tag_constructed:
2433 if not ctx.get("bered", False):
2435 msg="unallowed BER constructed encoding",
2436 decode_path=decode_path,
2441 eoc_expected = False
2443 l, llen, v = len_decode(lv)
2444 except LenIndefiniteForm:
2445 llen, l, v = 1, 0, lv[1:]
2447 except DecodeError as err:
2448 raise err.__class__(
2450 klass=self.__class__,
2451 decode_path=decode_path,
2454 if l > 0 and l > len(v):
2455 raise NotEnoughData(
2456 "encoded length is longer than data",
2457 klass=self.__class__,
2458 decode_path=decode_path,
2461 if not eoc_expected and l == 0:
2462 raise NotEnoughData(
2464 klass=self.__class__,
2465 decode_path=decode_path,
2469 sub_offset = offset + tlen + llen
2473 if v[:EOC_LEN].tobytes() == EOC:
2480 msg="chunk out of bounds",
2481 decode_path=len(chunks) - 1,
2482 offset=chunks[-1].offset,
2484 sub_decode_path = decode_path + (str(len(chunks)),)
2486 chunk, v_tail = OctetString().decode(
2489 decode_path=sub_decode_path,
2495 msg="expected OctetString encoded chunk",
2496 decode_path=sub_decode_path,
2499 chunks.append(chunk)
2500 sub_offset += chunk.tlvlen
2501 vlen += chunk.tlvlen
2503 if len(chunks) == 0:
2506 decode_path=decode_path,
2510 obj = self.__class__(
2511 value=b"".join(bytes(chunk) for chunk in chunks),
2512 bounds=(self._bound_min, self._bound_max),
2515 default=self.default,
2516 optional=self.optional,
2517 _decoded=(offset, llen, vlen + (EOC_LEN if eoc_expected else 0)),
2519 except DecodeError as err:
2522 klass=self.__class__,
2523 decode_path=decode_path,
2526 except BoundsError as err:
2529 klass=self.__class__,
2530 decode_path=decode_path,
2534 return obj, v[EOC_LEN if eoc_expected else 0:]
2536 klass=self.__class__,
2537 decode_path=decode_path,
2542 return pp_console_row(next(self.pps()))
2544 def pps(self, decode_path=()):
2546 asn1_type_name=self.asn1_type_name,
2547 obj_name=self.__class__.__name__,
2548 decode_path=decode_path,
2549 value=("%d bytes" % len(self._value)) if self.ready else None,
2550 blob=self._value if self.ready else None,
2551 optional=self.optional,
2552 default=self == self.default,
2553 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2554 expl=None if self._expl is None else tag_decode(self._expl),
2559 expl_offset=self.expl_offset if self.expled else None,
2560 expl_tlen=self.expl_tlen if self.expled else None,
2561 expl_llen=self.expl_llen if self.expled else None,
2562 expl_vlen=self.expl_vlen if self.expled else None,
2564 defined_by, defined = self.defined or (None, None)
2565 if defined_by is not None:
2567 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2572 """``NULL`` null object
2580 tag_default = tag_encode(5)
2581 asn1_type_name = "NULL"
2585 value=None, # unused, but Sequence passes it
2592 :param bytes impl: override default tag with ``IMPLICIT`` one
2593 :param bytes expl: override default tag with ``EXPLICIT`` one
2594 :param bool optional: is object ``OPTIONAL`` in sequence
2596 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2604 obj = self.__class__()
2606 obj._expl = self._expl
2607 obj.default = self.default
2608 obj.optional = self.optional
2609 obj.offset = self.offset
2610 obj.llen = self.llen
2611 obj.vlen = self.vlen
2614 def __eq__(self, their):
2615 if not issubclass(their.__class__, Null):
2618 self.tag == their.tag and
2619 self._expl == their._expl
2629 return self.__class__(
2630 impl=self.tag if impl is None else impl,
2631 expl=self._expl if expl is None else expl,
2632 optional=self.optional if optional is None else optional,
2636 return self.tag + len_encode(0)
2638 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2640 t, _, lv = tag_strip(tlv)
2641 except DecodeError as err:
2642 raise err.__class__(
2644 klass=self.__class__,
2645 decode_path=decode_path,
2650 klass=self.__class__,
2651 decode_path=decode_path,
2657 l, _, v = len_decode(lv)
2658 except DecodeError as err:
2659 raise err.__class__(
2661 klass=self.__class__,
2662 decode_path=decode_path,
2666 raise InvalidLength(
2667 "Null must have zero length",
2668 klass=self.__class__,
2669 decode_path=decode_path,
2672 obj = self.__class__(
2675 optional=self.optional,
2676 _decoded=(offset, 1, 0),
2681 return pp_console_row(next(self.pps()))
2683 def pps(self, decode_path=()):
2685 asn1_type_name=self.asn1_type_name,
2686 obj_name=self.__class__.__name__,
2687 decode_path=decode_path,
2688 optional=self.optional,
2689 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2690 expl=None if self._expl is None else tag_decode(self._expl),
2695 expl_offset=self.expl_offset if self.expled else None,
2696 expl_tlen=self.expl_tlen if self.expled else None,
2697 expl_llen=self.expl_llen if self.expled else None,
2698 expl_vlen=self.expl_vlen if self.expled else None,
2702 class ObjectIdentifier(Obj):
2703 """``OBJECT IDENTIFIER`` OID type
2705 >>> oid = ObjectIdentifier((1, 2, 3))
2706 OBJECT IDENTIFIER 1.2.3
2707 >>> oid == ObjectIdentifier("1.2.3")
2713 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2714 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2716 >>> str(ObjectIdentifier((3, 1)))
2717 Traceback (most recent call last):
2718 pyderasn.InvalidOID: unacceptable first arc value
2720 __slots__ = ("defines",)
2721 tag_default = tag_encode(6)
2722 asn1_type_name = "OBJECT IDENTIFIER"
2735 :param value: set the value. Either tuples of integers,
2736 string of "."-concatenated integers, or
2737 :py:class:`pyderasn.ObjectIdentifier` object
2738 :param defines: sequence of tuples. Each tuple has two elements.
2739 First one is relative to current one decode
2740 path, aiming to the field defined by that OID.
2741 Read about relative path in
2742 :py:func:`pyderasn.abs_decode_path`. Second
2743 tuple element is ``{OID: pyderasn.Obj()}``
2744 dictionary, mapping between current OID value
2745 and structure applied to defined field.
2746 :ref:`Read about DEFINED BY <definedby>`
2747 :param bytes impl: override default tag with ``IMPLICIT`` one
2748 :param bytes expl: override default tag with ``EXPLICIT`` one
2749 :param default: set default value. Type same as in ``value``
2750 :param bool optional: is object ``OPTIONAL`` in sequence
2752 super(ObjectIdentifier, self).__init__(
2760 if value is not None:
2761 self._value = self._value_sanitize(value)
2762 if default is not None:
2763 default = self._value_sanitize(default)
2764 self.default = self.__class__(
2769 if self._value is None:
2770 self._value = default
2771 self.defines = defines
2773 def __add__(self, their):
2774 if isinstance(their, self.__class__):
2775 return self.__class__(self._value + their._value)
2776 if isinstance(their, tuple):
2777 return self.__class__(self._value + their)
2778 raise InvalidValueType((self.__class__, tuple))
2780 def _value_sanitize(self, value):
2781 if issubclass(value.__class__, ObjectIdentifier):
2783 if isinstance(value, string_types):
2785 value = tuple(int(arc) for arc in value.split("."))
2787 raise InvalidOID("unacceptable arcs values")
2788 if isinstance(value, tuple):
2790 raise InvalidOID("less than 2 arcs")
2791 first_arc = value[0]
2792 if first_arc in (0, 1):
2793 if not (0 <= value[1] <= 39):
2794 raise InvalidOID("second arc is too wide")
2795 elif first_arc == 2:
2798 raise InvalidOID("unacceptable first arc value")
2800 raise InvalidValueType((self.__class__, str, tuple))
2804 return self._value is not None
2807 obj = self.__class__()
2808 obj._value = self._value
2809 obj.defines = self.defines
2811 obj._expl = self._expl
2812 obj.default = self.default
2813 obj.optional = self.optional
2814 obj.offset = self.offset
2815 obj.llen = self.llen
2816 obj.vlen = self.vlen
2820 self._assert_ready()
2821 return iter(self._value)
2824 return ".".join(str(arc) for arc in self._value or ())
2827 self._assert_ready()
2830 bytes(self._expl or b"") +
2831 str(self._value).encode("ascii"),
2834 def __eq__(self, their):
2835 if isinstance(their, tuple):
2836 return self._value == their
2837 if not issubclass(their.__class__, ObjectIdentifier):
2840 self.tag == their.tag and
2841 self._expl == their._expl and
2842 self._value == their._value
2845 def __lt__(self, their):
2846 return self._value < their._value
2857 return self.__class__(
2859 defines=self.defines if defines is None else defines,
2860 impl=self.tag if impl is None else impl,
2861 expl=self._expl if expl is None else expl,
2862 default=self.default if default is None else default,
2863 optional=self.optional if optional is None else optional,
2867 self._assert_ready()
2869 first_value = value[1]
2870 first_arc = value[0]
2873 elif first_arc == 1:
2875 elif first_arc == 2:
2877 else: # pragma: no cover
2878 raise RuntimeError("invalid arc is stored")
2879 octets = [zero_ended_encode(first_value)]
2880 for arc in value[2:]:
2881 octets.append(zero_ended_encode(arc))
2882 v = b"".join(octets)
2883 return b"".join((self.tag, len_encode(len(v)), v))
2885 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2887 t, _, lv = tag_strip(tlv)
2888 except DecodeError as err:
2889 raise err.__class__(
2891 klass=self.__class__,
2892 decode_path=decode_path,
2897 klass=self.__class__,
2898 decode_path=decode_path,
2904 l, llen, v = len_decode(lv)
2905 except DecodeError as err:
2906 raise err.__class__(
2908 klass=self.__class__,
2909 decode_path=decode_path,
2913 raise NotEnoughData(
2914 "encoded length is longer than data",
2915 klass=self.__class__,
2916 decode_path=decode_path,
2920 raise NotEnoughData(
2922 klass=self.__class__,
2923 decode_path=decode_path,
2926 v, tail = v[:l], v[l:]
2932 octet = indexbytes(v, i)
2933 arc = (arc << 7) | (octet & 0x7F)
2934 if octet & 0x80 == 0:
2942 klass=self.__class__,
2943 decode_path=decode_path,
2947 second_arc = arcs[0]
2948 if 0 <= second_arc <= 39:
2950 elif 40 <= second_arc <= 79:
2956 obj = self.__class__(
2957 value=tuple([first_arc, second_arc] + arcs[1:]),
2960 default=self.default,
2961 optional=self.optional,
2962 _decoded=(offset, llen, l),
2967 return pp_console_row(next(self.pps()))
2969 def pps(self, decode_path=()):
2971 asn1_type_name=self.asn1_type_name,
2972 obj_name=self.__class__.__name__,
2973 decode_path=decode_path,
2974 value=str(self) if self.ready else None,
2975 optional=self.optional,
2976 default=self == self.default,
2977 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2978 expl=None if self._expl is None else tag_decode(self._expl),
2983 expl_offset=self.expl_offset if self.expled else None,
2984 expl_tlen=self.expl_tlen if self.expled else None,
2985 expl_llen=self.expl_llen if self.expled else None,
2986 expl_vlen=self.expl_vlen if self.expled else None,
2990 class Enumerated(Integer):
2991 """``ENUMERATED`` integer type
2993 This type is identical to :py:class:`pyderasn.Integer`, but requires
2994 schema to be specified and does not accept values missing from it.
2997 tag_default = tag_encode(10)
2998 asn1_type_name = "ENUMERATED"
3009 bounds=None, # dummy argument, workability for Integer.decode
3011 super(Enumerated, self).__init__(
3020 if len(self.specs) == 0:
3021 raise ValueError("schema must be specified")
3023 def _value_sanitize(self, value):
3024 if isinstance(value, self.__class__):
3025 value = value._value
3026 elif isinstance(value, integer_types):
3027 if value not in list(self.specs.values()):
3029 "unknown integer value: %s" % value,
3030 klass=self.__class__,
3032 elif isinstance(value, string_types):
3033 value = self.specs.get(value)
3035 raise ObjUnknown("integer value: %s" % value)
3037 raise InvalidValueType((self.__class__, int, str))
3041 obj = self.__class__(_specs=self.specs)
3042 obj._value = self._value
3043 obj._bound_min = self._bound_min
3044 obj._bound_max = self._bound_max
3046 obj._expl = self._expl
3047 obj.default = self.default
3048 obj.optional = self.optional
3049 obj.offset = self.offset
3050 obj.llen = self.llen
3051 obj.vlen = self.vlen
3063 return self.__class__(
3065 impl=self.tag if impl is None else impl,
3066 expl=self._expl if expl is None else expl,
3067 default=self.default if default is None else default,
3068 optional=self.optional if optional is None else optional,
3073 class CommonString(OctetString):
3074 """Common class for all strings
3076 Everything resembles :py:class:`pyderasn.OctetString`, except
3077 ability to deal with unicode text strings.
3079 >>> hexenc("привет мир".encode("utf-8"))
3080 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3081 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3083 >>> s = UTF8String("привет мир")
3084 UTF8String UTF8String привет мир
3086 'привет мир'
3087 >>> hexenc(bytes(s))
3088 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3090 >>> PrintableString("привет мир")
3091 Traceback (most recent call last):
3092 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3094 >>> BMPString("ада", bounds=(2, 2))
3095 Traceback (most recent call last):
3096 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3097 >>> s = BMPString("ад", bounds=(2, 2))
3100 >>> hexenc(bytes(s))
3108 * - :py:class:`pyderasn.UTF8String`
3110 * - :py:class:`pyderasn.NumericString`
3112 * - :py:class:`pyderasn.PrintableString`
3114 * - :py:class:`pyderasn.TeletexString`
3116 * - :py:class:`pyderasn.T61String`
3118 * - :py:class:`pyderasn.VideotexString`
3120 * - :py:class:`pyderasn.IA5String`
3122 * - :py:class:`pyderasn.GraphicString`
3124 * - :py:class:`pyderasn.VisibleString`
3126 * - :py:class:`pyderasn.ISO646String`
3128 * - :py:class:`pyderasn.GeneralString`
3130 * - :py:class:`pyderasn.UniversalString`
3132 * - :py:class:`pyderasn.BMPString`
3135 __slots__ = ("encoding",)
3137 def _value_sanitize(self, value):
3139 value_decoded = None
3140 if isinstance(value, self.__class__):
3141 value_raw = value._value
3142 elif isinstance(value, text_type):
3143 value_decoded = value
3144 elif isinstance(value, binary_type):
3147 raise InvalidValueType((self.__class__, text_type, binary_type))
3150 value_decoded.encode(self.encoding)
3151 if value_raw is None else value_raw
3154 value_raw.decode(self.encoding)
3155 if value_decoded is None else value_decoded
3157 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3158 raise DecodeError(str(err))
3159 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3167 def __eq__(self, their):
3168 if isinstance(their, binary_type):
3169 return self._value == their
3170 if isinstance(their, text_type):
3171 return self._value == their.encode(self.encoding)
3172 if not isinstance(their, self.__class__):
3175 self._value == their._value and
3176 self.tag == their.tag and
3177 self._expl == their._expl
3180 def __unicode__(self):
3182 return self._value.decode(self.encoding)
3183 return text_type(self._value)
3186 return pp_console_row(next(self.pps(no_unicode=PY2)))
3188 def pps(self, decode_path=(), no_unicode=False):
3191 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3193 asn1_type_name=self.asn1_type_name,
3194 obj_name=self.__class__.__name__,
3195 decode_path=decode_path,
3197 optional=self.optional,
3198 default=self == self.default,
3199 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3200 expl=None if self._expl is None else tag_decode(self._expl),
3208 class UTF8String(CommonString):
3210 tag_default = tag_encode(12)
3212 asn1_type_name = "UTF8String"
3215 class NumericString(CommonString):
3217 tag_default = tag_encode(18)
3219 asn1_type_name = "NumericString"
3220 allowable_chars = set(digits.encode("ascii"))
3222 def _value_sanitize(self, value):
3223 value = super(NumericString, self)._value_sanitize(value)
3224 if not set(value) <= self.allowable_chars:
3225 raise DecodeError("non-numeric value")
3229 class PrintableString(CommonString):
3231 tag_default = tag_encode(19)
3233 asn1_type_name = "PrintableString"
3236 class TeletexString(CommonString):
3238 tag_default = tag_encode(20)
3240 asn1_type_name = "TeletexString"
3243 class T61String(TeletexString):
3245 asn1_type_name = "T61String"
3248 class VideotexString(CommonString):
3250 tag_default = tag_encode(21)
3251 encoding = "iso-8859-1"
3252 asn1_type_name = "VideotexString"
3255 class IA5String(CommonString):
3257 tag_default = tag_encode(22)
3259 asn1_type_name = "IA5"
3262 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3263 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3264 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3267 class UTCTime(CommonString):
3268 """``UTCTime`` datetime type
3270 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3271 UTCTime UTCTime 2017-09-30T22:07:50
3277 datetime.datetime(2017, 9, 30, 22, 7, 50)
3278 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3279 datetime.datetime(1957, 9, 30, 22, 7, 50)
3282 tag_default = tag_encode(23)
3284 asn1_type_name = "UTCTime"
3286 fmt = "%y%m%d%H%M%SZ"
3296 bounds=None, # dummy argument, workability for OctetString.decode
3299 :param value: set the value. Either datetime type, or
3300 :py:class:`pyderasn.UTCTime` object
3301 :param bytes impl: override default tag with ``IMPLICIT`` one
3302 :param bytes expl: override default tag with ``EXPLICIT`` one
3303 :param default: set default value. Type same as in ``value``
3304 :param bool optional: is object ``OPTIONAL`` in sequence
3306 super(UTCTime, self).__init__(
3314 if value is not None:
3315 self._value = self._value_sanitize(value)
3316 if default is not None:
3317 default = self._value_sanitize(default)
3318 self.default = self.__class__(
3323 if self._value is None:
3324 self._value = default
3326 def _value_sanitize(self, value):
3327 if isinstance(value, self.__class__):
3329 if isinstance(value, datetime):
3330 return value.strftime(self.fmt).encode("ascii")
3331 if isinstance(value, binary_type):
3332 value_decoded = value.decode("ascii")
3333 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3335 datetime.strptime(value_decoded, self.fmt)
3337 raise DecodeError("invalid UTCTime format")
3340 raise DecodeError("invalid UTCTime length")
3341 raise InvalidValueType((self.__class__, datetime))
3343 def __eq__(self, their):
3344 if isinstance(their, binary_type):
3345 return self._value == their
3346 if isinstance(their, datetime):
3347 return self.todatetime() == their
3348 if not isinstance(their, self.__class__):
3351 self._value == their._value and
3352 self.tag == their.tag and
3353 self._expl == their._expl
3356 def todatetime(self):
3357 """Convert to datetime
3361 Pay attention that UTCTime can not hold full year, so all years
3362 having < 50 years are treated as 20xx, 19xx otherwise, according
3363 to X.509 recomendation.
3365 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3366 year = value.year % 100
3368 year=(2000 + year) if year < 50 else (1900 + year),
3372 minute=value.minute,
3373 second=value.second,
3377 return pp_console_row(next(self.pps()))
3379 def pps(self, decode_path=()):
3381 asn1_type_name=self.asn1_type_name,
3382 obj_name=self.__class__.__name__,
3383 decode_path=decode_path,
3384 value=self.todatetime().isoformat() if self.ready else None,
3385 optional=self.optional,
3386 default=self == self.default,
3387 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3388 expl=None if self._expl is None else tag_decode(self._expl),
3396 class GeneralizedTime(UTCTime):
3397 """``GeneralizedTime`` datetime type
3399 This type is similar to :py:class:`pyderasn.UTCTime`.
3401 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3402 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3404 '20170930220750.000123Z'
3405 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3406 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3409 tag_default = tag_encode(24)
3410 asn1_type_name = "GeneralizedTime"
3412 fmt = "%Y%m%d%H%M%SZ"
3413 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3415 def _value_sanitize(self, value):
3416 if isinstance(value, self.__class__):
3418 if isinstance(value, datetime):
3419 return value.strftime(
3420 self.fmt_ms if value.microsecond > 0 else self.fmt
3422 if isinstance(value, binary_type):
3423 value_decoded = value.decode("ascii")
3424 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3426 datetime.strptime(value_decoded, self.fmt)
3429 "invalid GeneralizedTime (without ms) format",
3432 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3434 datetime.strptime(value_decoded, self.fmt_ms)
3437 "invalid GeneralizedTime (with ms) format",
3442 "invalid GeneralizedTime length",
3443 klass=self.__class__,
3445 raise InvalidValueType((self.__class__, datetime))
3447 def todatetime(self):
3448 value = self._value.decode("ascii")
3449 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3450 return datetime.strptime(value, self.fmt)
3451 return datetime.strptime(value, self.fmt_ms)
3454 class GraphicString(CommonString):
3456 tag_default = tag_encode(25)
3457 encoding = "iso-8859-1"
3458 asn1_type_name = "GraphicString"
3461 class VisibleString(CommonString):
3463 tag_default = tag_encode(26)
3465 asn1_type_name = "VisibleString"
3468 class ISO646String(VisibleString):
3470 asn1_type_name = "ISO646String"
3473 class GeneralString(CommonString):
3475 tag_default = tag_encode(27)
3476 encoding = "iso-8859-1"
3477 asn1_type_name = "GeneralString"
3480 class UniversalString(CommonString):
3482 tag_default = tag_encode(28)
3483 encoding = "utf-32-be"
3484 asn1_type_name = "UniversalString"
3487 class BMPString(CommonString):
3489 tag_default = tag_encode(30)
3490 encoding = "utf-16-be"
3491 asn1_type_name = "BMPString"
3495 """``CHOICE`` special type
3499 class GeneralName(Choice):
3501 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3502 ("dNSName", IA5String(impl=tag_ctxp(2))),
3505 >>> gn = GeneralName()
3507 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3508 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3509 >>> gn["dNSName"] = IA5String("bar.baz")
3510 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3511 >>> gn["rfc822Name"]
3514 [2] IA5String IA5 bar.baz
3517 >>> gn.value == gn["dNSName"]
3520 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3522 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3523 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3525 __slots__ = ("specs",)
3527 asn1_type_name = "CHOICE"
3540 :param value: set the value. Either ``(choice, value)`` tuple, or
3541 :py:class:`pyderasn.Choice` object
3542 :param bytes impl: can not be set, do **not** use it
3543 :param bytes expl: override default tag with ``EXPLICIT`` one
3544 :param default: set default value. Type same as in ``value``
3545 :param bool optional: is object ``OPTIONAL`` in sequence
3547 if impl is not None:
3548 raise ValueError("no implicit tag allowed for CHOICE")
3549 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3551 schema = getattr(self, "schema", ())
3552 if len(schema) == 0:
3553 raise ValueError("schema must be specified")
3555 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3558 if value is not None:
3559 self._value = self._value_sanitize(value)
3560 if default is not None:
3561 default_value = self._value_sanitize(default)
3562 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3563 default_obj.specs = self.specs
3564 default_obj._value = default_value
3565 self.default = default_obj
3567 self._value = default_obj.copy()._value
3569 def _value_sanitize(self, value):
3570 if isinstance(value, self.__class__):
3572 if isinstance(value, tuple) and len(value) == 2:
3574 spec = self.specs.get(choice)
3576 raise ObjUnknown(choice)
3577 if not isinstance(obj, spec.__class__):
3578 raise InvalidValueType((spec,))
3579 return (choice, spec(obj))
3580 raise InvalidValueType((self.__class__, tuple))
3584 return self._value is not None and self._value[1].ready
3587 obj = self.__class__(schema=self.specs)
3588 obj._expl = self._expl
3589 obj.default = self.default
3590 obj.optional = self.optional
3591 obj.offset = self.offset
3592 obj.llen = self.llen
3593 obj.vlen = self.vlen
3595 if value is not None:
3596 obj._value = (value[0], value[1].copy())
3599 def __eq__(self, their):
3600 if isinstance(their, tuple) and len(their) == 2:
3601 return self._value == their
3602 if not isinstance(their, self.__class__):
3605 self.specs == their.specs and
3606 self._value == their._value
3616 return self.__class__(
3619 expl=self._expl if expl is None else expl,
3620 default=self.default if default is None else default,
3621 optional=self.optional if optional is None else optional,
3626 self._assert_ready()
3627 return self._value[0]
3631 self._assert_ready()
3632 return self._value[1]
3634 def __getitem__(self, key):
3635 if key not in self.specs:
3636 raise ObjUnknown(key)
3637 if self._value is None:
3639 choice, value = self._value
3644 def __setitem__(self, key, value):
3645 spec = self.specs.get(key)
3647 raise ObjUnknown(key)
3648 if not isinstance(value, spec.__class__):
3649 raise InvalidValueType((spec.__class__,))
3650 self._value = (key, spec(value))
3658 return self._value[1].decoded if self.ready else False
3661 self._assert_ready()
3662 return self._value[1].encode()
3664 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3665 for choice, spec in self.specs.items():
3666 sub_decode_path = decode_path + (choice,)
3672 decode_path=sub_decode_path,
3681 klass=self.__class__,
3682 decode_path=decode_path,
3687 value, tail = spec.decode(
3691 decode_path=sub_decode_path,
3694 obj = self.__class__(
3697 default=self.default,
3698 optional=self.optional,
3699 _decoded=(offset, 0, value.tlvlen),
3701 obj._value = (choice, value)
3705 value = pp_console_row(next(self.pps()))
3707 value = "%s[%r]" % (value, self.value)
3710 def pps(self, decode_path=()):
3712 asn1_type_name=self.asn1_type_name,
3713 obj_name=self.__class__.__name__,
3714 decode_path=decode_path,
3715 value=self.choice if self.ready else None,
3716 optional=self.optional,
3717 default=self == self.default,
3718 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3719 expl=None if self._expl is None else tag_decode(self._expl),
3726 yield self.value.pps(decode_path=decode_path + (self.choice,))
3729 class PrimitiveTypes(Choice):
3730 """Predefined ``CHOICE`` for all generic primitive types
3732 It could be useful for general decoding of some unspecified values:
3734 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3735 OCTET STRING 3 bytes 666f6f
3736 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3740 schema = tuple((klass.__name__, klass()) for klass in (
3765 """``ANY`` special type
3767 >>> Any(Integer(-123))
3769 >>> a = Any(OctetString(b"hello world").encode())
3770 ANY 040b68656c6c6f20776f726c64
3771 >>> hexenc(bytes(a))
3772 b'0x040x0bhello world'
3774 __slots__ = ("defined",)
3775 tag_default = tag_encode(0)
3776 asn1_type_name = "ANY"
3786 :param value: set the value. Either any kind of pyderasn's
3787 **ready** object, or bytes. Pay attention that
3788 **no** validation is performed is raw binary value
3790 :param bytes expl: override default tag with ``EXPLICIT`` one
3791 :param bool optional: is object ``OPTIONAL`` in sequence
3793 super(Any, self).__init__(None, expl, None, optional, _decoded)
3794 self._value = None if value is None else self._value_sanitize(value)
3797 def _value_sanitize(self, value):
3798 if isinstance(value, self.__class__):
3800 if isinstance(value, Obj):
3801 return value.encode()
3802 if isinstance(value, binary_type):
3804 raise InvalidValueType((self.__class__, Obj, binary_type))
3808 return self._value is not None
3811 obj = self.__class__()
3812 obj._value = self._value
3814 obj._expl = self._expl
3815 obj.optional = self.optional
3816 obj.offset = self.offset
3817 obj.llen = self.llen
3818 obj.vlen = self.vlen
3821 def __eq__(self, their):
3822 if isinstance(their, binary_type):
3823 return self._value == their
3824 if issubclass(their.__class__, Any):
3825 return self._value == their._value
3834 return self.__class__(
3836 expl=self._expl if expl is None else expl,
3837 optional=self.optional if optional is None else optional,
3840 def __bytes__(self):
3841 self._assert_ready()
3849 self._assert_ready()
3852 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3854 t, tlen, lv = tag_strip(tlv)
3855 l, llen, v = len_decode(lv)
3856 except DecodeError as err:
3857 raise err.__class__(
3859 klass=self.__class__,
3860 decode_path=decode_path,
3864 raise NotEnoughData(
3865 "encoded length is longer than data",
3866 klass=self.__class__,
3867 decode_path=decode_path,
3870 tlvlen = tlen + llen + l
3871 v, tail = tlv[:tlvlen], v[l:]
3872 obj = self.__class__(
3875 optional=self.optional,
3876 _decoded=(offset, 0, tlvlen),
3882 return pp_console_row(next(self.pps()))
3884 def pps(self, decode_path=()):
3886 asn1_type_name=self.asn1_type_name,
3887 obj_name=self.__class__.__name__,
3888 decode_path=decode_path,
3889 blob=self._value if self.ready else None,
3890 optional=self.optional,
3891 default=self == self.default,
3892 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3893 expl=None if self._expl is None else tag_decode(self._expl),
3898 expl_offset=self.expl_offset if self.expled else None,
3899 expl_tlen=self.expl_tlen if self.expled else None,
3900 expl_llen=self.expl_llen if self.expled else None,
3901 expl_vlen=self.expl_vlen if self.expled else None,
3903 defined_by, defined = self.defined or (None, None)
3904 if defined_by is not None:
3906 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3910 ########################################################################
3911 # ASN.1 constructed types
3912 ########################################################################
3914 def get_def_by_path(defines_by_path, sub_decode_path):
3915 """Get define by decode path
3917 for path, define in defines_by_path:
3918 if len(path) != len(sub_decode_path):
3920 for p1, p2 in zip(path, sub_decode_path):
3921 if (p1 != any) and (p1 != p2):
3927 def abs_decode_path(decode_path, rel_path):
3928 """Create an absolute decode path from current and relative ones
3930 :param decode_path: current decode path, starting point.
3932 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3933 If first tuple's element is "/", then treat it as
3934 an absolute path, ignoring ``decode_path`` as
3935 starting point. Also this tuple can contain ".."
3936 elements, stripping the leading element from
3939 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3940 ("foo", "bar", "baz", "whatever")
3941 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3943 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3946 if rel_path[0] == "/":
3948 if rel_path[0] == "..":
3949 return abs_decode_path(decode_path[:-1], rel_path[1:])
3950 return decode_path + rel_path
3953 class Sequence(Obj):
3954 """``SEQUENCE`` structure type
3956 You have to make specification of sequence::
3958 class Extension(Sequence):
3960 ("extnID", ObjectIdentifier()),
3961 ("critical", Boolean(default=False)),
3962 ("extnValue", OctetString()),
3965 Then, you can work with it as with dictionary.
3967 >>> ext = Extension()
3968 >>> Extension().specs
3970 ('extnID', OBJECT IDENTIFIER),
3971 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3972 ('extnValue', OCTET STRING),
3974 >>> ext["extnID"] = "1.2.3"
3975 Traceback (most recent call last):
3976 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3977 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3979 You can determine if sequence is ready to be encoded:
3984 Traceback (most recent call last):
3985 pyderasn.ObjNotReady: object is not ready: extnValue
3986 >>> ext["extnValue"] = OctetString(b"foobar")
3990 Value you want to assign, must have the same **type** as in
3991 corresponding specification, but it can have different tags,
3992 optional/default attributes -- they will be taken from specification
3995 class TBSCertificate(Sequence):
3997 ("version", Version(expl=tag_ctxc(0), default="v1")),
4000 >>> tbs = TBSCertificate()
4001 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4003 Assign ``None`` to remove value from sequence.
4005 You can set values in Sequence during its initialization:
4007 >>> AlgorithmIdentifier((
4008 ("algorithm", ObjectIdentifier("1.2.3")),
4009 ("parameters", Any(Null()))
4011 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4013 You can determine if value exists/set in the sequence and take its value:
4015 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4018 OBJECT IDENTIFIER 1.2.3
4020 But pay attention that if value has default, then it won't be (not
4021 in) in the sequence (because ``DEFAULT`` must not be encoded in
4022 DER), but you can read its value:
4024 >>> "critical" in ext, ext["critical"]
4025 (False, BOOLEAN False)
4026 >>> ext["critical"] = Boolean(True)
4027 >>> "critical" in ext, ext["critical"]
4028 (True, BOOLEAN True)
4030 All defaulted values are always optional.
4032 .. _strict_default_existence_ctx:
4036 When decoded DER contains defaulted value inside, then
4037 technically this is not valid DER encoding. But we allow and pass
4038 it **by default**. Of course reencoding of that kind of DER will
4039 result in different binary representation (validly without
4040 defaulted value inside). You can enable strict defaulted values
4041 existence validation by setting ``"strict_default_existence":
4042 True`` :ref:`context <ctx>` option -- decoding process will raise
4043 an exception if defaulted value is met.
4045 Two sequences are equal if they have equal specification (schema),
4046 implicit/explicit tagging and the same values.
4048 __slots__ = ("specs",)
4049 tag_default = tag_encode(form=TagFormConstructed, num=16)
4050 asn1_type_name = "SEQUENCE"
4062 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4064 schema = getattr(self, "schema", ())
4066 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4069 if value is not None:
4070 if issubclass(value.__class__, Sequence):
4071 self._value = value._value
4072 elif hasattr(value, "__iter__"):
4073 for seq_key, seq_value in value:
4074 self[seq_key] = seq_value
4076 raise InvalidValueType((Sequence,))
4077 if default is not None:
4078 if not issubclass(default.__class__, Sequence):
4079 raise InvalidValueType((Sequence,))
4080 default_value = default._value
4081 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4082 default_obj.specs = self.specs
4083 default_obj._value = default_value
4084 self.default = default_obj
4086 self._value = default_obj.copy()._value
4090 for name, spec in self.specs.items():
4091 value = self._value.get(name)
4102 obj = self.__class__(schema=self.specs)
4104 obj._expl = self._expl
4105 obj.default = self.default
4106 obj.optional = self.optional
4107 obj.offset = self.offset
4108 obj.llen = self.llen
4109 obj.vlen = self.vlen
4110 obj._value = {k: v.copy() for k, v in self._value.items()}
4113 def __eq__(self, their):
4114 if not isinstance(their, self.__class__):
4117 self.specs == their.specs and
4118 self.tag == their.tag and
4119 self._expl == their._expl and
4120 self._value == their._value
4131 return self.__class__(
4134 impl=self.tag if impl is None else impl,
4135 expl=self._expl if expl is None else expl,
4136 default=self.default if default is None else default,
4137 optional=self.optional if optional is None else optional,
4140 def __contains__(self, key):
4141 return key in self._value
4143 def __setitem__(self, key, value):
4144 spec = self.specs.get(key)
4146 raise ObjUnknown(key)
4148 self._value.pop(key, None)
4150 if not isinstance(value, spec.__class__):
4151 raise InvalidValueType((spec.__class__,))
4152 value = spec(value=value)
4153 if spec.default is not None and value == spec.default:
4154 self._value.pop(key, None)
4156 self._value[key] = value
4158 def __getitem__(self, key):
4159 value = self._value.get(key)
4160 if value is not None:
4162 spec = self.specs.get(key)
4164 raise ObjUnknown(key)
4165 if spec.default is not None:
4169 def _encoded_values(self):
4171 for name, spec in self.specs.items():
4172 value = self._value.get(name)
4176 raise ObjNotReady(name)
4177 raws.append(value.encode())
4181 v = b"".join(self._encoded_values())
4182 return b"".join((self.tag, len_encode(len(v)), v))
4184 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4186 t, tlen, lv = tag_strip(tlv)
4187 except DecodeError as err:
4188 raise err.__class__(
4190 klass=self.__class__,
4191 decode_path=decode_path,
4196 klass=self.__class__,
4197 decode_path=decode_path,
4203 l, llen, v = len_decode(lv)
4204 except DecodeError as err:
4205 raise err.__class__(
4207 klass=self.__class__,
4208 decode_path=decode_path,
4212 raise NotEnoughData(
4213 "encoded length is longer than data",
4214 klass=self.__class__,
4215 decode_path=decode_path,
4218 v, tail = v[:l], v[l:]
4219 sub_offset = offset + tlen + llen
4221 for name, spec in self.specs.items():
4222 if len(v) == 0 and spec.optional:
4224 sub_decode_path = decode_path + (name,)
4226 value, v_tail = spec.decode(
4230 decode_path=sub_decode_path,
4238 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4239 if defined is not None:
4240 defined_by, defined_spec = defined
4241 if issubclass(value.__class__, SequenceOf):
4242 for i, _value in enumerate(value):
4243 sub_sub_decode_path = sub_decode_path + (
4245 DecodePathDefBy(defined_by),
4247 defined_value, defined_tail = defined_spec.decode(
4248 memoryview(bytes(_value)),
4250 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4251 if value.expled else (value.tlen + value.llen)
4254 decode_path=sub_sub_decode_path,
4257 if len(defined_tail) > 0:
4260 klass=self.__class__,
4261 decode_path=sub_sub_decode_path,
4264 _value.defined = (defined_by, defined_value)
4266 defined_value, defined_tail = defined_spec.decode(
4267 memoryview(bytes(value)),
4269 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4270 if value.expled else (value.tlen + value.llen)
4273 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4276 if len(defined_tail) > 0:
4279 klass=self.__class__,
4280 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4283 value.defined = (defined_by, defined_value)
4285 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4287 if spec.default is not None and value == spec.default:
4288 if ctx.get("strict_default_existence", False):
4290 "DEFAULT value met",
4291 klass=self.__class__,
4292 decode_path=sub_decode_path,
4297 values[name] = value
4299 spec_defines = getattr(spec, "defines", ())
4300 if len(spec_defines) == 0:
4301 defines_by_path = ctx.get("defines_by_path", ())
4302 if len(defines_by_path) > 0:
4303 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4304 if spec_defines is not None and len(spec_defines) > 0:
4305 for rel_path, schema in spec_defines:
4306 defined = schema.get(value, None)
4307 if defined is not None:
4308 ctx.setdefault("defines", []).append((
4309 abs_decode_path(sub_decode_path[:-1], rel_path),
4315 klass=self.__class__,
4316 decode_path=decode_path,
4319 obj = self.__class__(
4323 default=self.default,
4324 optional=self.optional,
4325 _decoded=(offset, llen, l),
4331 value = pp_console_row(next(self.pps()))
4333 for name in self.specs:
4334 _value = self._value.get(name)
4337 cols.append(repr(_value))
4338 return "%s[%s]" % (value, ", ".join(cols))
4340 def pps(self, decode_path=()):
4342 asn1_type_name=self.asn1_type_name,
4343 obj_name=self.__class__.__name__,
4344 decode_path=decode_path,
4345 optional=self.optional,
4346 default=self == self.default,
4347 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4348 expl=None if self._expl is None else tag_decode(self._expl),
4353 expl_offset=self.expl_offset if self.expled else None,
4354 expl_tlen=self.expl_tlen if self.expled else None,
4355 expl_llen=self.expl_llen if self.expled else None,
4356 expl_vlen=self.expl_vlen if self.expled else None,
4358 for name in self.specs:
4359 value = self._value.get(name)
4362 yield value.pps(decode_path=decode_path + (name,))
4365 class Set(Sequence):
4366 """``SET`` structure type
4368 Its usage is identical to :py:class:`pyderasn.Sequence`.
4371 tag_default = tag_encode(form=TagFormConstructed, num=17)
4372 asn1_type_name = "SET"
4375 raws = self._encoded_values()
4378 return b"".join((self.tag, len_encode(len(v)), v))
4380 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4382 t, tlen, lv = tag_strip(tlv)
4383 except DecodeError as err:
4384 raise err.__class__(
4386 klass=self.__class__,
4387 decode_path=decode_path,
4392 klass=self.__class__,
4393 decode_path=decode_path,
4399 l, llen, v = len_decode(lv)
4400 except DecodeError as err:
4401 raise err.__class__(
4403 klass=self.__class__,
4404 decode_path=decode_path,
4408 raise NotEnoughData(
4409 "encoded length is longer than data",
4410 klass=self.__class__,
4413 v, tail = v[:l], v[l:]
4414 sub_offset = offset + tlen + llen
4416 specs_items = self.specs.items
4418 for name, spec in specs_items():
4419 sub_decode_path = decode_path + (name,)
4425 decode_path=sub_decode_path,
4434 klass=self.__class__,
4435 decode_path=decode_path,
4438 value, v_tail = spec.decode(
4442 decode_path=sub_decode_path,
4446 value.expl_tlvlen if value.expled else value.tlvlen
4449 if spec.default is None or value != spec.default: # pragma: no cover
4450 # SeqMixing.test_encoded_default_accepted covers that place
4451 values[name] = value
4452 obj = self.__class__(
4456 default=self.default,
4457 optional=self.optional,
4458 _decoded=(offset, llen, l),
4464 class SequenceOf(Obj):
4465 """``SEQUENCE OF`` sequence type
4467 For that kind of type you must specify the object it will carry on
4468 (bounds are for example here, not required)::
4470 class Ints(SequenceOf):
4475 >>> ints.append(Integer(123))
4476 >>> ints.append(Integer(234))
4478 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4479 >>> [int(i) for i in ints]
4481 >>> ints.append(Integer(345))
4482 Traceback (most recent call last):
4483 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4486 >>> ints[1] = Integer(345)
4488 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4490 Also you can initialize sequence with preinitialized values:
4492 >>> ints = Ints([Integer(123), Integer(234)])
4494 __slots__ = ("spec", "_bound_min", "_bound_max")
4495 tag_default = tag_encode(form=TagFormConstructed, num=16)
4496 asn1_type_name = "SEQUENCE OF"
4509 super(SequenceOf, self).__init__(
4517 schema = getattr(self, "schema", None)
4519 raise ValueError("schema must be specified")
4521 self._bound_min, self._bound_max = getattr(
4525 ) if bounds is None else bounds
4527 if value is not None:
4528 self._value = self._value_sanitize(value)
4529 if default is not None:
4530 default_value = self._value_sanitize(default)
4531 default_obj = self.__class__(
4536 default_obj._value = default_value
4537 self.default = default_obj
4539 self._value = default_obj.copy()._value
4541 def _value_sanitize(self, value):
4542 if issubclass(value.__class__, SequenceOf):
4543 value = value._value
4544 elif hasattr(value, "__iter__"):
4547 raise InvalidValueType((self.__class__, iter))
4548 if not self._bound_min <= len(value) <= self._bound_max:
4549 raise BoundsError(self._bound_min, len(value), self._bound_max)
4551 if not isinstance(v, self.spec.__class__):
4552 raise InvalidValueType((self.spec.__class__,))
4557 return all(v.ready for v in self._value)
4560 obj = self.__class__(schema=self.spec)
4561 obj._bound_min = self._bound_min
4562 obj._bound_max = self._bound_max
4564 obj._expl = self._expl
4565 obj.default = self.default
4566 obj.optional = self.optional
4567 obj.offset = self.offset
4568 obj.llen = self.llen
4569 obj.vlen = self.vlen
4570 obj._value = [v.copy() for v in self._value]
4573 def __eq__(self, their):
4574 if isinstance(their, self.__class__):
4576 self.spec == their.spec and
4577 self.tag == their.tag and
4578 self._expl == their._expl and
4579 self._value == their._value
4581 if hasattr(their, "__iter__"):
4582 return self._value == list(their)
4594 return self.__class__(
4598 (self._bound_min, self._bound_max)
4599 if bounds is None else bounds
4601 impl=self.tag if impl is None else impl,
4602 expl=self._expl if expl is None else expl,
4603 default=self.default if default is None else default,
4604 optional=self.optional if optional is None else optional,
4607 def __contains__(self, key):
4608 return key in self._value
4610 def append(self, value):
4611 if not isinstance(value, self.spec.__class__):
4612 raise InvalidValueType((self.spec.__class__,))
4613 if len(self._value) + 1 > self._bound_max:
4616 len(self._value) + 1,
4619 self._value.append(value)
4622 self._assert_ready()
4623 return iter(self._value)
4626 self._assert_ready()
4627 return len(self._value)
4629 def __setitem__(self, key, value):
4630 if not isinstance(value, self.spec.__class__):
4631 raise InvalidValueType((self.spec.__class__,))
4632 self._value[key] = self.spec(value=value)
4634 def __getitem__(self, key):
4635 return self._value[key]
4637 def _encoded_values(self):
4638 return [v.encode() for v in self._value]
4641 v = b"".join(self._encoded_values())
4642 return b"".join((self.tag, len_encode(len(v)), v))
4644 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4646 t, tlen, lv = tag_strip(tlv)
4647 except DecodeError as err:
4648 raise err.__class__(
4650 klass=self.__class__,
4651 decode_path=decode_path,
4656 klass=self.__class__,
4657 decode_path=decode_path,
4663 l, llen, v = len_decode(lv)
4664 except DecodeError as err:
4665 raise err.__class__(
4667 klass=self.__class__,
4668 decode_path=decode_path,
4672 raise NotEnoughData(
4673 "encoded length is longer than data",
4674 klass=self.__class__,
4675 decode_path=decode_path,
4678 v, tail = v[:l], v[l:]
4679 sub_offset = offset + tlen + llen
4683 value, v_tail = spec.decode(
4687 decode_path=decode_path + (str(len(_value)),),
4690 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4692 _value.append(value)
4693 obj = self.__class__(
4696 bounds=(self._bound_min, self._bound_max),
4699 default=self.default,
4700 optional=self.optional,
4701 _decoded=(offset, llen, l),
4707 pp_console_row(next(self.pps())),
4708 ", ".join(repr(v) for v in self._value),
4711 def pps(self, decode_path=()):
4713 asn1_type_name=self.asn1_type_name,
4714 obj_name=self.__class__.__name__,
4715 decode_path=decode_path,
4716 optional=self.optional,
4717 default=self == self.default,
4718 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4719 expl=None if self._expl is None else tag_decode(self._expl),
4724 expl_offset=self.expl_offset if self.expled else None,
4725 expl_tlen=self.expl_tlen if self.expled else None,
4726 expl_llen=self.expl_llen if self.expled else None,
4727 expl_vlen=self.expl_vlen if self.expled else None,
4729 for i, value in enumerate(self._value):
4730 yield value.pps(decode_path=decode_path + (str(i),))
4733 class SetOf(SequenceOf):
4734 """``SET OF`` sequence type
4736 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4739 tag_default = tag_encode(form=TagFormConstructed, num=17)
4740 asn1_type_name = "SET OF"
4743 raws = self._encoded_values()
4746 return b"".join((self.tag, len_encode(len(v)), v))
4749 def obj_by_path(pypath): # pragma: no cover
4750 """Import object specified as string Python path
4752 Modules must be separated from classes/functions with ``:``.
4754 >>> obj_by_path("foo.bar:Baz")
4755 <class 'foo.bar.Baz'>
4756 >>> obj_by_path("foo.bar:Baz.boo")
4757 <classmethod 'foo.bar.Baz.boo'>
4759 mod, objs = pypath.rsplit(":", 1)
4760 from importlib import import_module
4761 obj = import_module(mod)
4762 for obj_name in objs.split("."):
4763 obj = getattr(obj, obj_name)
4767 def generic_decoder(): # pragma: no cover
4768 # All of this below is a big hack with self references
4769 choice = PrimitiveTypes()
4770 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4771 choice.specs["SetOf"] = SetOf(schema=choice)
4773 choice.specs["SequenceOf%d" % i] = SequenceOf(
4777 choice.specs["Any"] = Any()
4779 # Class name equals to type name, to omit it from output
4780 class SEQUENCEOF(SequenceOf):
4784 def pprint_any(obj, oids=None, with_colours=False):
4785 def _pprint_pps(pps):
4787 if hasattr(pp, "_fields"):
4788 if pp.asn1_type_name == Choice.asn1_type_name:
4790 pp_kwargs = pp._asdict()
4791 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4792 pp = _pp(**pp_kwargs)
4793 yield pp_console_row(
4798 with_colours=with_colours,
4800 for row in pp_console_blob(pp):
4803 for row in _pprint_pps(pp):
4805 return "\n".join(_pprint_pps(obj.pps()))
4806 return SEQUENCEOF(), pprint_any
4809 def main(): # pragma: no cover
4811 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4812 parser.add_argument(
4816 help="Skip that number of bytes from the beginning",
4818 parser.add_argument(
4820 help="Python path to dictionary with OIDs",
4822 parser.add_argument(
4824 help="Python path to schema definition to use",
4826 parser.add_argument(
4827 "--defines-by-path",
4828 help="Python path to decoder's defines_by_path",
4830 parser.add_argument(
4832 type=argparse.FileType("rb"),
4833 help="Path to DER file you want to decode",
4835 args = parser.parse_args()
4836 args.DERFile.seek(args.skip)
4837 der = memoryview(args.DERFile.read())
4838 args.DERFile.close()
4839 oids = obj_by_path(args.oids) if args.oids else {}
4841 schema = obj_by_path(args.schema)
4842 from functools import partial
4843 pprinter = partial(pprint, big_blobs=True)
4845 schema, pprinter = generic_decoder()
4846 obj, tail = schema().decode(
4849 None if args.defines_by_path is None else
4850 {"defines_by_path": obj_by_path(args.defines_by_path)}
4856 with_colours=True if environ.get("NO_COLOR") is None else False,
4859 print("\nTrailing data: %s" % hexenc(tail))
4862 if __name__ == "__main__":