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.
864 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
865 self._expl = getattr(self, "expl", None) if expl is None else expl
866 if self.tag != self.tag_default and self._expl is not None:
868 "implicit and explicit tags can not be set simultaneously"
870 if default is not None:
872 self.optional = optional
873 self.offset, self.llen, self.vlen = _decoded
876 self.expl_bered = False
879 def ready(self): # pragma: no cover
880 """Is object ready to be encoded?
882 raise NotImplementedError()
884 def _assert_ready(self):
886 raise ObjNotReady(self.__class__.__name__)
890 """Is object decoded?
892 return (self.llen + self.vlen) > 0
894 def copy(self): # pragma: no cover
895 """Make a copy of object, safe to be mutated
897 raise NotImplementedError()
905 return self.tlen + self.llen + self.vlen
907 def __str__(self): # pragma: no cover
908 return self.__bytes__() if PY2 else self.__unicode__()
910 def __ne__(self, their):
911 return not(self == their)
913 def __gt__(self, their): # pragma: no cover
914 return not(self < their)
916 def __le__(self, their): # pragma: no cover
917 return (self == their) or (self < their)
919 def __ge__(self, their): # pragma: no cover
920 return (self == their) or (self > their)
922 def _encode(self): # pragma: no cover
923 raise NotImplementedError()
925 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
926 raise NotImplementedError()
930 if self._expl is None:
932 return b"".join((self._expl, len_encode(len(raw)), raw))
945 :param data: either binary or memoryview
946 :param int offset: initial data's offset
947 :param bool leavemm: do we need to leave memoryview of remaining
948 data as is, or convert it to bytes otherwise
949 :param ctx: optional :ref:`context <ctx>` governing decoding process.
950 :param tag_only: decode only the tag, without length and contents
951 (used only in Choice and Set structures, trying to
952 determine if tag satisfies the scheme)
953 :returns: (Obj, remaining data)
957 tlv = memoryview(data)
958 if self._expl is None:
959 result = self._decode(
962 decode_path=decode_path,
971 t, tlen, lv = tag_strip(tlv)
972 except DecodeError as err:
975 klass=self.__class__,
976 decode_path=decode_path,
981 klass=self.__class__,
982 decode_path=decode_path,
986 l, llen, v = len_decode(lv)
987 except LenIndefiniteForm as err:
988 if not ctx.get("bered", False):
991 klass=self.__class__,
992 decode_path=decode_path,
996 offset += tlen + llen
997 result = self._decode(
1000 decode_path=decode_path,
1007 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1008 if eoc_expected.tobytes() != EOC:
1011 decode_path=decode_path,
1015 obj.expl_bered = True
1016 except DecodeError as err:
1017 raise err.__class__(
1019 klass=self.__class__,
1020 decode_path=decode_path,
1025 raise NotEnoughData(
1026 "encoded length is longer than data",
1027 klass=self.__class__,
1028 decode_path=decode_path,
1031 result = self._decode(
1033 offset=offset + tlen + llen,
1034 decode_path=decode_path,
1041 return obj, (tail if leavemm else tail.tobytes())
1045 return self._expl is not None
1052 def expl_tlen(self):
1053 return len(self._expl)
1056 def expl_llen(self):
1059 return len(len_encode(self.tlvlen))
1062 def expl_offset(self):
1063 return self.offset - self.expl_tlen - self.expl_llen
1066 def expl_vlen(self):
1070 def expl_tlvlen(self):
1071 return self.expl_tlen + self.expl_llen + self.expl_vlen
1074 class DecodePathDefBy(object):
1075 """DEFINED BY representation inside decode path
1077 __slots__ = ("defined_by",)
1079 def __init__(self, defined_by):
1080 self.defined_by = defined_by
1082 def __ne__(self, their):
1083 return not(self == their)
1085 def __eq__(self, their):
1086 if not isinstance(their, self.__class__):
1088 return self.defined_by == their.defined_by
1091 return "DEFINED BY " + str(self.defined_by)
1094 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1097 ########################################################################
1099 ########################################################################
1101 PP = namedtuple("PP", (
1123 asn1_type_name="unknown",
1162 def _colorize(what, colour, with_colours, attrs=("bold",)):
1163 return colored(what, colour, attrs=attrs) if with_colours else what
1178 " " if pp.expl_offset is None else
1179 ("-%d" % (pp.offset - pp.expl_offset))
1182 cols.append(_colorize(col, "red", with_colours, ()))
1183 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1184 cols.append(_colorize(col, "green", with_colours, ()))
1185 if len(pp.decode_path) > 0:
1186 cols.append(" ." * (len(pp.decode_path)))
1187 ent = pp.decode_path[-1]
1188 if isinstance(ent, DecodePathDefBy):
1189 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1190 value = str(ent.defined_by)
1192 oids is not None and
1193 ent.defined_by.asn1_type_name ==
1194 ObjectIdentifier.asn1_type_name and
1197 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1199 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1201 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1202 if pp.expl is not None:
1203 klass, _, num = pp.expl
1204 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1205 cols.append(_colorize(col, "blue", with_colours))
1206 if pp.impl is not None:
1207 klass, _, num = pp.impl
1208 col = "[%s%d]" % (TagClassReprs[klass], num)
1209 cols.append(_colorize(col, "blue", with_colours))
1210 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1211 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1212 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1213 if pp.value is not None:
1215 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1217 oids is not None and
1218 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1221 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1223 if isinstance(pp.blob, binary_type):
1224 cols.append(hexenc(pp.blob))
1225 elif isinstance(pp.blob, tuple):
1226 cols.append(", ".join(pp.blob))
1228 cols.append(_colorize("OPTIONAL", "red", with_colours))
1230 cols.append(_colorize("DEFAULT", "red", with_colours))
1231 return " ".join(cols)
1234 def pp_console_blob(pp):
1235 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1236 if len(pp.decode_path) > 0:
1237 cols.append(" ." * (len(pp.decode_path) + 1))
1238 if isinstance(pp.blob, binary_type):
1239 blob = hexenc(pp.blob).upper()
1240 for i in range(0, len(blob), 32):
1241 chunk = blob[i:i + 32]
1242 yield " ".join(cols + [":".join(
1243 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1245 elif isinstance(pp.blob, tuple):
1246 yield " ".join(cols + [", ".join(pp.blob)])
1249 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1250 """Pretty print object
1252 :param Obj obj: object you want to pretty print
1253 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1254 from it is met, then its humand readable form is printed
1255 :param big_blobs: if large binary objects are met (like OctetString
1256 values), do we need to print them too, on separate
1258 :param with_colours: colourize output, if ``termcolor`` library
1261 def _pprint_pps(pps):
1263 if hasattr(pp, "_fields"):
1265 yield pp_console_row(
1270 with_colours=with_colours,
1272 for row in pp_console_blob(pp):
1275 yield pp_console_row(
1280 with_colours=with_colours,
1283 for row in _pprint_pps(pp):
1285 return "\n".join(_pprint_pps(obj.pps()))
1288 ########################################################################
1289 # ASN.1 primitive types
1290 ########################################################################
1293 """``BOOLEAN`` boolean type
1295 >>> b = Boolean(True)
1297 >>> b == Boolean(True)
1303 tag_default = tag_encode(1)
1304 asn1_type_name = "BOOLEAN"
1316 :param value: set the value. Either boolean type, or
1317 :py:class:`pyderasn.Boolean` object
1318 :param bytes impl: override default tag with ``IMPLICIT`` one
1319 :param bytes expl: override default tag with ``EXPLICIT`` one
1320 :param default: set default value. Type same as in ``value``
1321 :param bool optional: is object ``OPTIONAL`` in sequence
1323 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1324 self._value = None if value is None else self._value_sanitize(value)
1325 if default is not None:
1326 default = self._value_sanitize(default)
1327 self.default = self.__class__(
1333 self._value = default
1335 def _value_sanitize(self, value):
1336 if issubclass(value.__class__, Boolean):
1338 if isinstance(value, bool):
1340 raise InvalidValueType((self.__class__, bool))
1344 return self._value is not None
1347 obj = self.__class__()
1348 obj._value = self._value
1350 obj._expl = self._expl
1351 obj.default = self.default
1352 obj.optional = self.optional
1353 obj.offset = self.offset
1354 obj.llen = self.llen
1355 obj.vlen = self.vlen
1358 def __nonzero__(self):
1359 self._assert_ready()
1363 self._assert_ready()
1366 def __eq__(self, their):
1367 if isinstance(their, bool):
1368 return self._value == their
1369 if not issubclass(their.__class__, Boolean):
1372 self._value == their._value and
1373 self.tag == their.tag and
1374 self._expl == their._expl
1385 return self.__class__(
1387 impl=self.tag if impl is None else impl,
1388 expl=self._expl if expl is None else expl,
1389 default=self.default if default is None else default,
1390 optional=self.optional if optional is None else optional,
1394 self._assert_ready()
1398 (b"\xFF" if self._value else b"\x00"),
1401 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1403 t, _, lv = tag_strip(tlv)
1404 except DecodeError as err:
1405 raise err.__class__(
1407 klass=self.__class__,
1408 decode_path=decode_path,
1413 klass=self.__class__,
1414 decode_path=decode_path,
1420 l, _, v = len_decode(lv)
1421 except DecodeError as err:
1422 raise err.__class__(
1424 klass=self.__class__,
1425 decode_path=decode_path,
1429 raise InvalidLength(
1430 "Boolean's length must be equal to 1",
1431 klass=self.__class__,
1432 decode_path=decode_path,
1436 raise NotEnoughData(
1437 "encoded length is longer than data",
1438 klass=self.__class__,
1439 decode_path=decode_path,
1442 first_octet = byte2int(v)
1444 if first_octet == 0:
1446 elif first_octet == 0xFF:
1448 elif ctx.get("bered", False):
1453 "unacceptable Boolean value",
1454 klass=self.__class__,
1455 decode_path=decode_path,
1458 obj = self.__class__(
1462 default=self.default,
1463 optional=self.optional,
1464 _decoded=(offset, 1, 1),
1470 return pp_console_row(next(self.pps()))
1472 def pps(self, decode_path=()):
1474 asn1_type_name=self.asn1_type_name,
1475 obj_name=self.__class__.__name__,
1476 decode_path=decode_path,
1477 value=str(self._value) if self.ready else None,
1478 optional=self.optional,
1479 default=self == self.default,
1480 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1481 expl=None if self._expl is None else tag_decode(self._expl),
1486 expl_offset=self.expl_offset if self.expled else None,
1487 expl_tlen=self.expl_tlen if self.expled else None,
1488 expl_llen=self.expl_llen if self.expled else None,
1489 expl_vlen=self.expl_vlen if self.expled else None,
1494 """``INTEGER`` integer type
1496 >>> b = Integer(-123)
1498 >>> b == Integer(-123)
1503 >>> Integer(2, bounds=(1, 3))
1505 >>> Integer(5, bounds=(1, 3))
1506 Traceback (most recent call last):
1507 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1511 class Version(Integer):
1518 >>> v = Version("v1")
1525 {'v3': 2, 'v1': 0, 'v2': 1}
1527 __slots__ = ("specs", "_bound_min", "_bound_max")
1528 tag_default = tag_encode(2)
1529 asn1_type_name = "INTEGER"
1543 :param value: set the value. Either integer type, named value
1544 (if ``schema`` is specified in the class), or
1545 :py:class:`pyderasn.Integer` object
1546 :param bounds: set ``(MIN, MAX)`` value constraint.
1547 (-inf, +inf) by default
1548 :param bytes impl: override default tag with ``IMPLICIT`` one
1549 :param bytes expl: override default tag with ``EXPLICIT`` one
1550 :param default: set default value. Type same as in ``value``
1551 :param bool optional: is object ``OPTIONAL`` in sequence
1553 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1555 specs = getattr(self, "schema", {}) if _specs is None else _specs
1556 self.specs = specs if isinstance(specs, dict) else dict(specs)
1557 self._bound_min, self._bound_max = getattr(
1560 (float("-inf"), float("+inf")),
1561 ) if bounds is None else bounds
1562 if value is not None:
1563 self._value = self._value_sanitize(value)
1564 if default is not None:
1565 default = self._value_sanitize(default)
1566 self.default = self.__class__(
1572 if self._value is None:
1573 self._value = default
1575 def _value_sanitize(self, value):
1576 if issubclass(value.__class__, Integer):
1577 value = value._value
1578 elif isinstance(value, integer_types):
1580 elif isinstance(value, str):
1581 value = self.specs.get(value)
1583 raise ObjUnknown("integer value: %s" % value)
1585 raise InvalidValueType((self.__class__, int, str))
1586 if not self._bound_min <= value <= self._bound_max:
1587 raise BoundsError(self._bound_min, value, self._bound_max)
1592 return self._value is not None
1595 obj = self.__class__(_specs=self.specs)
1596 obj._value = self._value
1597 obj._bound_min = self._bound_min
1598 obj._bound_max = self._bound_max
1600 obj._expl = self._expl
1601 obj.default = self.default
1602 obj.optional = self.optional
1603 obj.offset = self.offset
1604 obj.llen = self.llen
1605 obj.vlen = self.vlen
1609 self._assert_ready()
1610 return int(self._value)
1613 self._assert_ready()
1616 bytes(self._expl or b"") +
1617 str(self._value).encode("ascii"),
1620 def __eq__(self, their):
1621 if isinstance(their, integer_types):
1622 return self._value == their
1623 if not issubclass(their.__class__, Integer):
1626 self._value == their._value and
1627 self.tag == their.tag and
1628 self._expl == their._expl
1631 def __lt__(self, their):
1632 return self._value < their._value
1636 for name, value in self.specs.items():
1637 if value == self._value:
1649 return self.__class__(
1652 (self._bound_min, self._bound_max)
1653 if bounds is None else bounds
1655 impl=self.tag if impl is None else impl,
1656 expl=self._expl if expl is None else expl,
1657 default=self.default if default is None else default,
1658 optional=self.optional if optional is None else optional,
1663 self._assert_ready()
1667 octets = bytearray([0])
1671 octets = bytearray()
1673 octets.append((value & 0xFF) ^ 0xFF)
1675 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1678 octets = bytearray()
1680 octets.append(value & 0xFF)
1682 if octets[-1] & 0x80 > 0:
1685 octets = bytes(octets)
1687 bytes_len = ceil(value.bit_length() / 8) or 1
1690 octets = value.to_bytes(
1695 except OverflowError:
1699 return b"".join((self.tag, len_encode(len(octets)), octets))
1701 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1703 t, _, lv = tag_strip(tlv)
1704 except DecodeError as err:
1705 raise err.__class__(
1707 klass=self.__class__,
1708 decode_path=decode_path,
1713 klass=self.__class__,
1714 decode_path=decode_path,
1720 l, llen, v = len_decode(lv)
1721 except DecodeError as err:
1722 raise err.__class__(
1724 klass=self.__class__,
1725 decode_path=decode_path,
1729 raise NotEnoughData(
1730 "encoded length is longer than data",
1731 klass=self.__class__,
1732 decode_path=decode_path,
1736 raise NotEnoughData(
1738 klass=self.__class__,
1739 decode_path=decode_path,
1742 v, tail = v[:l], v[l:]
1743 first_octet = byte2int(v)
1745 second_octet = byte2int(v[1:])
1747 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1748 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1751 "non normalized integer",
1752 klass=self.__class__,
1753 decode_path=decode_path,
1758 if first_octet & 0x80 > 0:
1759 octets = bytearray()
1760 for octet in bytearray(v):
1761 octets.append(octet ^ 0xFF)
1762 for octet in octets:
1763 value = (value << 8) | octet
1767 for octet in bytearray(v):
1768 value = (value << 8) | octet
1770 value = int.from_bytes(v, byteorder="big", signed=True)
1772 obj = self.__class__(
1774 bounds=(self._bound_min, self._bound_max),
1777 default=self.default,
1778 optional=self.optional,
1780 _decoded=(offset, llen, l),
1782 except BoundsError as err:
1785 klass=self.__class__,
1786 decode_path=decode_path,
1792 return pp_console_row(next(self.pps()))
1794 def pps(self, decode_path=()):
1796 asn1_type_name=self.asn1_type_name,
1797 obj_name=self.__class__.__name__,
1798 decode_path=decode_path,
1799 value=(self.named or str(self._value)) if self.ready else None,
1800 optional=self.optional,
1801 default=self == self.default,
1802 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1803 expl=None if self._expl is None else tag_decode(self._expl),
1808 expl_offset=self.expl_offset if self.expled else None,
1809 expl_tlen=self.expl_tlen if self.expled else None,
1810 expl_llen=self.expl_llen if self.expled else None,
1811 expl_vlen=self.expl_vlen if self.expled else None,
1815 class BitString(Obj):
1816 """``BIT STRING`` bit string type
1818 >>> BitString(b"hello world")
1819 BIT STRING 88 bits 68656c6c6f20776f726c64
1822 >>> b == b"hello world"
1827 >>> b = BitString("'010110000000'B")
1828 BIT STRING 12 bits 5800
1831 >>> b[0], b[1], b[2], b[3]
1832 (False, True, False, True)
1836 [False, True, False, True, True, False, False, False, False, False, False, False]
1840 class KeyUsage(BitString):
1842 ("digitalSignature", 0),
1843 ("nonRepudiation", 1),
1844 ("keyEncipherment", 2),
1847 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1848 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1850 ['nonRepudiation', 'keyEncipherment']
1852 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1854 __slots__ = ("tag_constructed", "specs", "defined")
1855 tag_default = tag_encode(3)
1856 asn1_type_name = "BIT STRING"
1869 :param value: set the value. Either binary type, tuple of named
1870 values (if ``schema`` is specified in the class),
1871 string in ``'XXX...'B`` form, or
1872 :py:class:`pyderasn.BitString` object
1873 :param bytes impl: override default tag with ``IMPLICIT`` one
1874 :param bytes expl: override default tag with ``EXPLICIT`` one
1875 :param default: set default value. Type same as in ``value``
1876 :param bool optional: is object ``OPTIONAL`` in sequence
1878 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1879 specs = getattr(self, "schema", {}) if _specs is None else _specs
1880 self.specs = specs if isinstance(specs, dict) else dict(specs)
1881 self._value = None if value is None else self._value_sanitize(value)
1882 if default is not None:
1883 default = self._value_sanitize(default)
1884 self.default = self.__class__(
1890 self._value = default
1892 tag_klass, _, tag_num = tag_decode(self.tag)
1893 self.tag_constructed = tag_encode(
1895 form=TagFormConstructed,
1899 def _bits2octets(self, bits):
1900 if len(self.specs) > 0:
1901 bits = bits.rstrip("0")
1903 bits += "0" * ((8 - (bit_len % 8)) % 8)
1904 octets = bytearray(len(bits) // 8)
1905 for i in six_xrange(len(octets)):
1906 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1907 return bit_len, bytes(octets)
1909 def _value_sanitize(self, value):
1910 if issubclass(value.__class__, BitString):
1912 if isinstance(value, (string_types, binary_type)):
1914 isinstance(value, string_types) and
1915 value.startswith("'") and
1916 value.endswith("'B")
1919 if not set(value) <= set(("0", "1")):
1920 raise ValueError("B's coding contains unacceptable chars")
1921 return self._bits2octets(value)
1922 elif isinstance(value, binary_type):
1923 return (len(value) * 8, value)
1925 raise InvalidValueType((
1930 if isinstance(value, tuple):
1933 isinstance(value[0], integer_types) and
1934 isinstance(value[1], binary_type)
1939 bit = self.specs.get(name)
1941 raise ObjUnknown("BitString value: %s" % name)
1944 return self._bits2octets("")
1946 return self._bits2octets("".join(
1947 ("1" if bit in bits else "0")
1948 for bit in six_xrange(max(bits) + 1)
1950 raise InvalidValueType((self.__class__, binary_type, string_types))
1954 return self._value is not None
1957 obj = self.__class__(_specs=self.specs)
1959 if value is not None:
1960 value = (value[0], value[1])
1963 obj._expl = self._expl
1964 obj.default = self.default
1965 obj.optional = self.optional
1966 obj.offset = self.offset
1967 obj.llen = self.llen
1968 obj.vlen = self.vlen
1972 self._assert_ready()
1973 for i in six_xrange(self._value[0]):
1978 self._assert_ready()
1979 return self._value[0]
1981 def __bytes__(self):
1982 self._assert_ready()
1983 return self._value[1]
1985 def __eq__(self, their):
1986 if isinstance(their, bytes):
1987 return self._value[1] == their
1988 if not issubclass(their.__class__, BitString):
1991 self._value == their._value and
1992 self.tag == their.tag and
1993 self._expl == their._expl
1998 return [name for name, bit in self.specs.items() if self[bit]]
2008 return self.__class__(
2010 impl=self.tag if impl is None else impl,
2011 expl=self._expl if expl is None else expl,
2012 default=self.default if default is None else default,
2013 optional=self.optional if optional is None else optional,
2017 def __getitem__(self, key):
2018 if isinstance(key, int):
2019 bit_len, octets = self._value
2023 byte2int(memoryview(octets)[key // 8:]) >>
2026 if isinstance(key, string_types):
2027 value = self.specs.get(key)
2029 raise ObjUnknown("BitString value: %s" % key)
2031 raise InvalidValueType((int, str))
2034 self._assert_ready()
2035 bit_len, octets = self._value
2038 len_encode(len(octets) + 1),
2039 int2byte((8 - bit_len % 8) % 8),
2043 def _decode_chunk(self, lv, offset, decode_path, ctx):
2045 l, llen, v = len_decode(lv)
2046 except DecodeError as err:
2047 raise err.__class__(
2049 klass=self.__class__,
2050 decode_path=decode_path,
2054 raise NotEnoughData(
2055 "encoded length is longer than data",
2056 klass=self.__class__,
2057 decode_path=decode_path,
2061 raise NotEnoughData(
2063 klass=self.__class__,
2064 decode_path=decode_path,
2067 pad_size = byte2int(v)
2068 if l == 1 and pad_size != 0:
2070 "invalid empty value",
2071 klass=self.__class__,
2072 decode_path=decode_path,
2078 klass=self.__class__,
2079 decode_path=decode_path,
2082 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2085 klass=self.__class__,
2086 decode_path=decode_path,
2089 v, tail = v[:l], v[l:]
2090 obj = self.__class__(
2091 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2094 default=self.default,
2095 optional=self.optional,
2097 _decoded=(offset, llen, l),
2101 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2103 t, tlen, lv = tag_strip(tlv)
2104 except DecodeError as err:
2105 raise err.__class__(
2107 klass=self.__class__,
2108 decode_path=decode_path,
2114 return self._decode_chunk(lv, offset, decode_path, ctx)
2115 if t == self.tag_constructed:
2116 if not ctx.get("bered", False):
2118 msg="unallowed BER constructed encoding",
2119 decode_path=decode_path,
2124 eoc_expected = False
2126 l, llen, v = len_decode(lv)
2127 except LenIndefiniteForm:
2128 llen, l, v = 1, 0, lv[1:]
2130 except DecodeError as err:
2131 raise err.__class__(
2133 klass=self.__class__,
2134 decode_path=decode_path,
2137 if l > 0 and l > len(v):
2138 raise NotEnoughData(
2139 "encoded length is longer than data",
2140 klass=self.__class__,
2141 decode_path=decode_path,
2144 if not eoc_expected and l == 0:
2145 raise NotEnoughData(
2147 klass=self.__class__,
2148 decode_path=decode_path,
2152 sub_offset = offset + tlen + llen
2156 if v[:EOC_LEN].tobytes() == EOC:
2163 msg="chunk out of bounds",
2164 decode_path=len(chunks) - 1,
2165 offset=chunks[-1].offset,
2167 sub_decode_path = decode_path + (str(len(chunks)),)
2169 chunk, v_tail = BitString().decode(
2172 decode_path=sub_decode_path,
2178 msg="expected BitString encoded chunk",
2179 decode_path=sub_decode_path,
2182 chunks.append(chunk)
2183 sub_offset += chunk.tlvlen
2184 vlen += chunk.tlvlen
2186 if len(chunks) == 0:
2189 decode_path=decode_path,
2194 for chunk_i, chunk in enumerate(chunks[:-1]):
2195 if chunk.bit_len % 8 != 0:
2197 msg="BitString chunk is not multiple of 8 bit",
2198 decode_path=decode_path + (str(chunk_i),),
2199 offset=chunk.offset,
2201 values.append(bytes(chunk))
2202 bit_len += chunk.bit_len
2203 chunk_last = chunks[-1]
2204 values.append(bytes(chunk_last))
2205 bit_len += chunk_last.bit_len
2206 obj = self.__class__(
2207 value=(bit_len, b"".join(values)),
2210 default=self.default,
2211 optional=self.optional,
2213 _decoded=(offset, llen, vlen + (EOC_LEN if eoc_expected else 0)),
2216 return obj, v[EOC_LEN if eoc_expected else 0:]
2218 klass=self.__class__,
2219 decode_path=decode_path,
2224 return pp_console_row(next(self.pps()))
2226 def pps(self, decode_path=()):
2230 bit_len, blob = self._value
2231 value = "%d bits" % bit_len
2232 if len(self.specs) > 0:
2233 blob = tuple(self.named)
2235 asn1_type_name=self.asn1_type_name,
2236 obj_name=self.__class__.__name__,
2237 decode_path=decode_path,
2240 optional=self.optional,
2241 default=self == self.default,
2242 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2243 expl=None if self._expl is None else tag_decode(self._expl),
2248 expl_offset=self.expl_offset if self.expled else None,
2249 expl_tlen=self.expl_tlen if self.expled else None,
2250 expl_llen=self.expl_llen if self.expled else None,
2251 expl_vlen=self.expl_vlen if self.expled else None,
2253 defined_by, defined = self.defined or (None, None)
2254 if defined_by is not None:
2256 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2260 class OctetString(Obj):
2261 """``OCTET STRING`` binary string type
2263 >>> s = OctetString(b"hello world")
2264 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2265 >>> s == OctetString(b"hello world")
2270 >>> OctetString(b"hello", bounds=(4, 4))
2271 Traceback (most recent call last):
2272 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2273 >>> OctetString(b"hell", bounds=(4, 4))
2274 OCTET STRING 4 bytes 68656c6c
2276 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2277 tag_default = tag_encode(4)
2278 asn1_type_name = "OCTET STRING"
2291 :param value: set the value. Either binary type, or
2292 :py:class:`pyderasn.OctetString` object
2293 :param bounds: set ``(MIN, MAX)`` value size constraint.
2294 (-inf, +inf) by default
2295 :param bytes impl: override default tag with ``IMPLICIT`` one
2296 :param bytes expl: override default tag with ``EXPLICIT`` one
2297 :param default: set default value. Type same as in ``value``
2298 :param bool optional: is object ``OPTIONAL`` in sequence
2300 super(OctetString, self).__init__(
2308 self._bound_min, self._bound_max = getattr(
2312 ) if bounds is None else bounds
2313 if value is not None:
2314 self._value = self._value_sanitize(value)
2315 if default is not None:
2316 default = self._value_sanitize(default)
2317 self.default = self.__class__(
2322 if self._value is None:
2323 self._value = default
2325 tag_klass, _, tag_num = tag_decode(self.tag)
2326 self.tag_constructed = tag_encode(
2328 form=TagFormConstructed,
2332 def _value_sanitize(self, value):
2333 if issubclass(value.__class__, OctetString):
2334 value = value._value
2335 elif isinstance(value, binary_type):
2338 raise InvalidValueType((self.__class__, bytes))
2339 if not self._bound_min <= len(value) <= self._bound_max:
2340 raise BoundsError(self._bound_min, len(value), self._bound_max)
2345 return self._value is not None
2348 obj = self.__class__()
2349 obj._value = self._value
2350 obj._bound_min = self._bound_min
2351 obj._bound_max = self._bound_max
2353 obj._expl = self._expl
2354 obj.default = self.default
2355 obj.optional = self.optional
2356 obj.offset = self.offset
2357 obj.llen = self.llen
2358 obj.vlen = self.vlen
2361 def __bytes__(self):
2362 self._assert_ready()
2365 def __eq__(self, their):
2366 if isinstance(their, binary_type):
2367 return self._value == their
2368 if not issubclass(their.__class__, OctetString):
2371 self._value == their._value and
2372 self.tag == their.tag and
2373 self._expl == their._expl
2376 def __lt__(self, their):
2377 return self._value < their._value
2388 return self.__class__(
2391 (self._bound_min, self._bound_max)
2392 if bounds is None else bounds
2394 impl=self.tag if impl is None else impl,
2395 expl=self._expl if expl is None else expl,
2396 default=self.default if default is None else default,
2397 optional=self.optional if optional is None else optional,
2401 self._assert_ready()
2404 len_encode(len(self._value)),
2408 def _decode_chunk(self, lv, offset, decode_path, ctx):
2410 l, llen, v = len_decode(lv)
2411 except DecodeError as err:
2412 raise err.__class__(
2414 klass=self.__class__,
2415 decode_path=decode_path,
2419 raise NotEnoughData(
2420 "encoded length is longer than data",
2421 klass=self.__class__,
2422 decode_path=decode_path,
2425 v, tail = v[:l], v[l:]
2427 obj = self.__class__(
2429 bounds=(self._bound_min, self._bound_max),
2432 default=self.default,
2433 optional=self.optional,
2434 _decoded=(offset, llen, l),
2436 except DecodeError as err:
2439 klass=self.__class__,
2440 decode_path=decode_path,
2443 except BoundsError as err:
2446 klass=self.__class__,
2447 decode_path=decode_path,
2452 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2454 t, tlen, lv = tag_strip(tlv)
2455 except DecodeError as err:
2456 raise err.__class__(
2458 klass=self.__class__,
2459 decode_path=decode_path,
2465 return self._decode_chunk(lv, offset, decode_path, ctx)
2466 if t == self.tag_constructed:
2467 if not ctx.get("bered", False):
2469 msg="unallowed BER constructed encoding",
2470 decode_path=decode_path,
2475 eoc_expected = False
2477 l, llen, v = len_decode(lv)
2478 except LenIndefiniteForm:
2479 llen, l, v = 1, 0, lv[1:]
2481 except DecodeError as err:
2482 raise err.__class__(
2484 klass=self.__class__,
2485 decode_path=decode_path,
2488 if l > 0 and l > len(v):
2489 raise NotEnoughData(
2490 "encoded length is longer than data",
2491 klass=self.__class__,
2492 decode_path=decode_path,
2495 if not eoc_expected and l == 0:
2496 raise NotEnoughData(
2498 klass=self.__class__,
2499 decode_path=decode_path,
2503 sub_offset = offset + tlen + llen
2507 if v[:EOC_LEN].tobytes() == EOC:
2514 msg="chunk out of bounds",
2515 decode_path=len(chunks) - 1,
2516 offset=chunks[-1].offset,
2518 sub_decode_path = decode_path + (str(len(chunks)),)
2520 chunk, v_tail = OctetString().decode(
2523 decode_path=sub_decode_path,
2529 msg="expected OctetString encoded chunk",
2530 decode_path=sub_decode_path,
2533 chunks.append(chunk)
2534 sub_offset += chunk.tlvlen
2535 vlen += chunk.tlvlen
2537 if len(chunks) == 0:
2540 decode_path=decode_path,
2544 obj = self.__class__(
2545 value=b"".join(bytes(chunk) for chunk in chunks),
2546 bounds=(self._bound_min, self._bound_max),
2549 default=self.default,
2550 optional=self.optional,
2551 _decoded=(offset, llen, vlen + (EOC_LEN if eoc_expected else 0)),
2553 except DecodeError as err:
2556 klass=self.__class__,
2557 decode_path=decode_path,
2560 except BoundsError as err:
2563 klass=self.__class__,
2564 decode_path=decode_path,
2568 return obj, v[EOC_LEN if eoc_expected else 0:]
2570 klass=self.__class__,
2571 decode_path=decode_path,
2576 return pp_console_row(next(self.pps()))
2578 def pps(self, decode_path=()):
2580 asn1_type_name=self.asn1_type_name,
2581 obj_name=self.__class__.__name__,
2582 decode_path=decode_path,
2583 value=("%d bytes" % len(self._value)) if self.ready else None,
2584 blob=self._value if self.ready else None,
2585 optional=self.optional,
2586 default=self == self.default,
2587 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2588 expl=None if self._expl is None else tag_decode(self._expl),
2593 expl_offset=self.expl_offset if self.expled else None,
2594 expl_tlen=self.expl_tlen if self.expled else None,
2595 expl_llen=self.expl_llen if self.expled else None,
2596 expl_vlen=self.expl_vlen if self.expled else None,
2598 defined_by, defined = self.defined or (None, None)
2599 if defined_by is not None:
2601 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2606 """``NULL`` null object
2614 tag_default = tag_encode(5)
2615 asn1_type_name = "NULL"
2619 value=None, # unused, but Sequence passes it
2626 :param bytes impl: override default tag with ``IMPLICIT`` one
2627 :param bytes expl: override default tag with ``EXPLICIT`` one
2628 :param bool optional: is object ``OPTIONAL`` in sequence
2630 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2638 obj = self.__class__()
2640 obj._expl = self._expl
2641 obj.default = self.default
2642 obj.optional = self.optional
2643 obj.offset = self.offset
2644 obj.llen = self.llen
2645 obj.vlen = self.vlen
2648 def __eq__(self, their):
2649 if not issubclass(their.__class__, Null):
2652 self.tag == their.tag and
2653 self._expl == their._expl
2663 return self.__class__(
2664 impl=self.tag if impl is None else impl,
2665 expl=self._expl if expl is None else expl,
2666 optional=self.optional if optional is None else optional,
2670 return self.tag + len_encode(0)
2672 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2674 t, _, lv = tag_strip(tlv)
2675 except DecodeError as err:
2676 raise err.__class__(
2678 klass=self.__class__,
2679 decode_path=decode_path,
2684 klass=self.__class__,
2685 decode_path=decode_path,
2691 l, _, v = len_decode(lv)
2692 except DecodeError as err:
2693 raise err.__class__(
2695 klass=self.__class__,
2696 decode_path=decode_path,
2700 raise InvalidLength(
2701 "Null must have zero length",
2702 klass=self.__class__,
2703 decode_path=decode_path,
2706 obj = self.__class__(
2709 optional=self.optional,
2710 _decoded=(offset, 1, 0),
2715 return pp_console_row(next(self.pps()))
2717 def pps(self, decode_path=()):
2719 asn1_type_name=self.asn1_type_name,
2720 obj_name=self.__class__.__name__,
2721 decode_path=decode_path,
2722 optional=self.optional,
2723 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2724 expl=None if self._expl is None else tag_decode(self._expl),
2729 expl_offset=self.expl_offset if self.expled else None,
2730 expl_tlen=self.expl_tlen if self.expled else None,
2731 expl_llen=self.expl_llen if self.expled else None,
2732 expl_vlen=self.expl_vlen if self.expled else None,
2736 class ObjectIdentifier(Obj):
2737 """``OBJECT IDENTIFIER`` OID type
2739 >>> oid = ObjectIdentifier((1, 2, 3))
2740 OBJECT IDENTIFIER 1.2.3
2741 >>> oid == ObjectIdentifier("1.2.3")
2747 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2748 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2750 >>> str(ObjectIdentifier((3, 1)))
2751 Traceback (most recent call last):
2752 pyderasn.InvalidOID: unacceptable first arc value
2754 __slots__ = ("defines",)
2755 tag_default = tag_encode(6)
2756 asn1_type_name = "OBJECT IDENTIFIER"
2769 :param value: set the value. Either tuples of integers,
2770 string of "."-concatenated integers, or
2771 :py:class:`pyderasn.ObjectIdentifier` object
2772 :param defines: sequence of tuples. Each tuple has two elements.
2773 First one is relative to current one decode
2774 path, aiming to the field defined by that OID.
2775 Read about relative path in
2776 :py:func:`pyderasn.abs_decode_path`. Second
2777 tuple element is ``{OID: pyderasn.Obj()}``
2778 dictionary, mapping between current OID value
2779 and structure applied to defined field.
2780 :ref:`Read about DEFINED BY <definedby>`
2781 :param bytes impl: override default tag with ``IMPLICIT`` one
2782 :param bytes expl: override default tag with ``EXPLICIT`` one
2783 :param default: set default value. Type same as in ``value``
2784 :param bool optional: is object ``OPTIONAL`` in sequence
2786 super(ObjectIdentifier, self).__init__(
2794 if value is not None:
2795 self._value = self._value_sanitize(value)
2796 if default is not None:
2797 default = self._value_sanitize(default)
2798 self.default = self.__class__(
2803 if self._value is None:
2804 self._value = default
2805 self.defines = defines
2807 def __add__(self, their):
2808 if isinstance(their, self.__class__):
2809 return self.__class__(self._value + their._value)
2810 if isinstance(their, tuple):
2811 return self.__class__(self._value + their)
2812 raise InvalidValueType((self.__class__, tuple))
2814 def _value_sanitize(self, value):
2815 if issubclass(value.__class__, ObjectIdentifier):
2817 if isinstance(value, string_types):
2819 value = tuple(int(arc) for arc in value.split("."))
2821 raise InvalidOID("unacceptable arcs values")
2822 if isinstance(value, tuple):
2824 raise InvalidOID("less than 2 arcs")
2825 first_arc = value[0]
2826 if first_arc in (0, 1):
2827 if not (0 <= value[1] <= 39):
2828 raise InvalidOID("second arc is too wide")
2829 elif first_arc == 2:
2832 raise InvalidOID("unacceptable first arc value")
2834 raise InvalidValueType((self.__class__, str, tuple))
2838 return self._value is not None
2841 obj = self.__class__()
2842 obj._value = self._value
2843 obj.defines = self.defines
2845 obj._expl = self._expl
2846 obj.default = self.default
2847 obj.optional = self.optional
2848 obj.offset = self.offset
2849 obj.llen = self.llen
2850 obj.vlen = self.vlen
2854 self._assert_ready()
2855 return iter(self._value)
2858 return ".".join(str(arc) for arc in self._value or ())
2861 self._assert_ready()
2864 bytes(self._expl or b"") +
2865 str(self._value).encode("ascii"),
2868 def __eq__(self, their):
2869 if isinstance(their, tuple):
2870 return self._value == their
2871 if not issubclass(their.__class__, ObjectIdentifier):
2874 self.tag == their.tag and
2875 self._expl == their._expl and
2876 self._value == their._value
2879 def __lt__(self, their):
2880 return self._value < their._value
2891 return self.__class__(
2893 defines=self.defines if defines is None else defines,
2894 impl=self.tag if impl is None else impl,
2895 expl=self._expl if expl is None else expl,
2896 default=self.default if default is None else default,
2897 optional=self.optional if optional is None else optional,
2901 self._assert_ready()
2903 first_value = value[1]
2904 first_arc = value[0]
2907 elif first_arc == 1:
2909 elif first_arc == 2:
2911 else: # pragma: no cover
2912 raise RuntimeError("invalid arc is stored")
2913 octets = [zero_ended_encode(first_value)]
2914 for arc in value[2:]:
2915 octets.append(zero_ended_encode(arc))
2916 v = b"".join(octets)
2917 return b"".join((self.tag, len_encode(len(v)), v))
2919 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2921 t, _, lv = tag_strip(tlv)
2922 except DecodeError as err:
2923 raise err.__class__(
2925 klass=self.__class__,
2926 decode_path=decode_path,
2931 klass=self.__class__,
2932 decode_path=decode_path,
2938 l, llen, v = len_decode(lv)
2939 except DecodeError as err:
2940 raise err.__class__(
2942 klass=self.__class__,
2943 decode_path=decode_path,
2947 raise NotEnoughData(
2948 "encoded length is longer than data",
2949 klass=self.__class__,
2950 decode_path=decode_path,
2954 raise NotEnoughData(
2956 klass=self.__class__,
2957 decode_path=decode_path,
2960 v, tail = v[:l], v[l:]
2966 octet = indexbytes(v, i)
2967 arc = (arc << 7) | (octet & 0x7F)
2968 if octet & 0x80 == 0:
2976 klass=self.__class__,
2977 decode_path=decode_path,
2981 second_arc = arcs[0]
2982 if 0 <= second_arc <= 39:
2984 elif 40 <= second_arc <= 79:
2990 obj = self.__class__(
2991 value=tuple([first_arc, second_arc] + arcs[1:]),
2994 default=self.default,
2995 optional=self.optional,
2996 _decoded=(offset, llen, l),
3001 return pp_console_row(next(self.pps()))
3003 def pps(self, decode_path=()):
3005 asn1_type_name=self.asn1_type_name,
3006 obj_name=self.__class__.__name__,
3007 decode_path=decode_path,
3008 value=str(self) if self.ready else None,
3009 optional=self.optional,
3010 default=self == self.default,
3011 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3012 expl=None if self._expl is None else tag_decode(self._expl),
3017 expl_offset=self.expl_offset if self.expled else None,
3018 expl_tlen=self.expl_tlen if self.expled else None,
3019 expl_llen=self.expl_llen if self.expled else None,
3020 expl_vlen=self.expl_vlen if self.expled else None,
3024 class Enumerated(Integer):
3025 """``ENUMERATED`` integer type
3027 This type is identical to :py:class:`pyderasn.Integer`, but requires
3028 schema to be specified and does not accept values missing from it.
3031 tag_default = tag_encode(10)
3032 asn1_type_name = "ENUMERATED"
3043 bounds=None, # dummy argument, workability for Integer.decode
3045 super(Enumerated, self).__init__(
3054 if len(self.specs) == 0:
3055 raise ValueError("schema must be specified")
3057 def _value_sanitize(self, value):
3058 if isinstance(value, self.__class__):
3059 value = value._value
3060 elif isinstance(value, integer_types):
3061 if value not in list(self.specs.values()):
3063 "unknown integer value: %s" % value,
3064 klass=self.__class__,
3066 elif isinstance(value, string_types):
3067 value = self.specs.get(value)
3069 raise ObjUnknown("integer value: %s" % value)
3071 raise InvalidValueType((self.__class__, int, str))
3075 obj = self.__class__(_specs=self.specs)
3076 obj._value = self._value
3077 obj._bound_min = self._bound_min
3078 obj._bound_max = self._bound_max
3080 obj._expl = self._expl
3081 obj.default = self.default
3082 obj.optional = self.optional
3083 obj.offset = self.offset
3084 obj.llen = self.llen
3085 obj.vlen = self.vlen
3097 return self.__class__(
3099 impl=self.tag if impl is None else impl,
3100 expl=self._expl if expl is None else expl,
3101 default=self.default if default is None else default,
3102 optional=self.optional if optional is None else optional,
3107 class CommonString(OctetString):
3108 """Common class for all strings
3110 Everything resembles :py:class:`pyderasn.OctetString`, except
3111 ability to deal with unicode text strings.
3113 >>> hexenc("привет мир".encode("utf-8"))
3114 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3115 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3117 >>> s = UTF8String("привет мир")
3118 UTF8String UTF8String привет мир
3120 'привет мир'
3121 >>> hexenc(bytes(s))
3122 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3124 >>> PrintableString("привет мир")
3125 Traceback (most recent call last):
3126 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3128 >>> BMPString("ада", bounds=(2, 2))
3129 Traceback (most recent call last):
3130 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3131 >>> s = BMPString("ад", bounds=(2, 2))
3134 >>> hexenc(bytes(s))
3142 * - :py:class:`pyderasn.UTF8String`
3144 * - :py:class:`pyderasn.NumericString`
3146 * - :py:class:`pyderasn.PrintableString`
3148 * - :py:class:`pyderasn.TeletexString`
3150 * - :py:class:`pyderasn.T61String`
3152 * - :py:class:`pyderasn.VideotexString`
3154 * - :py:class:`pyderasn.IA5String`
3156 * - :py:class:`pyderasn.GraphicString`
3158 * - :py:class:`pyderasn.VisibleString`
3160 * - :py:class:`pyderasn.ISO646String`
3162 * - :py:class:`pyderasn.GeneralString`
3164 * - :py:class:`pyderasn.UniversalString`
3166 * - :py:class:`pyderasn.BMPString`
3169 __slots__ = ("encoding",)
3171 def _value_sanitize(self, value):
3173 value_decoded = None
3174 if isinstance(value, self.__class__):
3175 value_raw = value._value
3176 elif isinstance(value, text_type):
3177 value_decoded = value
3178 elif isinstance(value, binary_type):
3181 raise InvalidValueType((self.__class__, text_type, binary_type))
3184 value_decoded.encode(self.encoding)
3185 if value_raw is None else value_raw
3188 value_raw.decode(self.encoding)
3189 if value_decoded is None else value_decoded
3191 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3192 raise DecodeError(str(err))
3193 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3201 def __eq__(self, their):
3202 if isinstance(their, binary_type):
3203 return self._value == their
3204 if isinstance(their, text_type):
3205 return self._value == their.encode(self.encoding)
3206 if not isinstance(their, self.__class__):
3209 self._value == their._value and
3210 self.tag == their.tag and
3211 self._expl == their._expl
3214 def __unicode__(self):
3216 return self._value.decode(self.encoding)
3217 return text_type(self._value)
3220 return pp_console_row(next(self.pps(no_unicode=PY2)))
3222 def pps(self, decode_path=(), no_unicode=False):
3225 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3227 asn1_type_name=self.asn1_type_name,
3228 obj_name=self.__class__.__name__,
3229 decode_path=decode_path,
3231 optional=self.optional,
3232 default=self == self.default,
3233 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3234 expl=None if self._expl is None else tag_decode(self._expl),
3242 class UTF8String(CommonString):
3244 tag_default = tag_encode(12)
3246 asn1_type_name = "UTF8String"
3249 class NumericString(CommonString):
3251 tag_default = tag_encode(18)
3253 asn1_type_name = "NumericString"
3254 allowable_chars = set(digits.encode("ascii"))
3256 def _value_sanitize(self, value):
3257 value = super(NumericString, self)._value_sanitize(value)
3258 if not set(value) <= self.allowable_chars:
3259 raise DecodeError("non-numeric value")
3263 class PrintableString(CommonString):
3265 tag_default = tag_encode(19)
3267 asn1_type_name = "PrintableString"
3270 class TeletexString(CommonString):
3272 tag_default = tag_encode(20)
3274 asn1_type_name = "TeletexString"
3277 class T61String(TeletexString):
3279 asn1_type_name = "T61String"
3282 class VideotexString(CommonString):
3284 tag_default = tag_encode(21)
3285 encoding = "iso-8859-1"
3286 asn1_type_name = "VideotexString"
3289 class IA5String(CommonString):
3291 tag_default = tag_encode(22)
3293 asn1_type_name = "IA5"
3296 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3297 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3298 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3301 class UTCTime(CommonString):
3302 """``UTCTime`` datetime type
3304 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3305 UTCTime UTCTime 2017-09-30T22:07:50
3311 datetime.datetime(2017, 9, 30, 22, 7, 50)
3312 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3313 datetime.datetime(1957, 9, 30, 22, 7, 50)
3316 tag_default = tag_encode(23)
3318 asn1_type_name = "UTCTime"
3320 fmt = "%y%m%d%H%M%SZ"
3330 bounds=None, # dummy argument, workability for OctetString.decode
3333 :param value: set the value. Either datetime type, or
3334 :py:class:`pyderasn.UTCTime` object
3335 :param bytes impl: override default tag with ``IMPLICIT`` one
3336 :param bytes expl: override default tag with ``EXPLICIT`` one
3337 :param default: set default value. Type same as in ``value``
3338 :param bool optional: is object ``OPTIONAL`` in sequence
3340 super(UTCTime, self).__init__(
3348 if value is not None:
3349 self._value = self._value_sanitize(value)
3350 if default is not None:
3351 default = self._value_sanitize(default)
3352 self.default = self.__class__(
3357 if self._value is None:
3358 self._value = default
3360 def _value_sanitize(self, value):
3361 if isinstance(value, self.__class__):
3363 if isinstance(value, datetime):
3364 return value.strftime(self.fmt).encode("ascii")
3365 if isinstance(value, binary_type):
3366 value_decoded = value.decode("ascii")
3367 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3369 datetime.strptime(value_decoded, self.fmt)
3371 raise DecodeError("invalid UTCTime format")
3374 raise DecodeError("invalid UTCTime length")
3375 raise InvalidValueType((self.__class__, datetime))
3377 def __eq__(self, their):
3378 if isinstance(their, binary_type):
3379 return self._value == their
3380 if isinstance(their, datetime):
3381 return self.todatetime() == their
3382 if not isinstance(their, self.__class__):
3385 self._value == their._value and
3386 self.tag == their.tag and
3387 self._expl == their._expl
3390 def todatetime(self):
3391 """Convert to datetime
3395 Pay attention that UTCTime can not hold full year, so all years
3396 having < 50 years are treated as 20xx, 19xx otherwise, according
3397 to X.509 recomendation.
3399 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3400 year = value.year % 100
3402 year=(2000 + year) if year < 50 else (1900 + year),
3406 minute=value.minute,
3407 second=value.second,
3411 return pp_console_row(next(self.pps()))
3413 def pps(self, decode_path=()):
3415 asn1_type_name=self.asn1_type_name,
3416 obj_name=self.__class__.__name__,
3417 decode_path=decode_path,
3418 value=self.todatetime().isoformat() if self.ready else None,
3419 optional=self.optional,
3420 default=self == self.default,
3421 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3422 expl=None if self._expl is None else tag_decode(self._expl),
3430 class GeneralizedTime(UTCTime):
3431 """``GeneralizedTime`` datetime type
3433 This type is similar to :py:class:`pyderasn.UTCTime`.
3435 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3436 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3438 '20170930220750.000123Z'
3439 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3440 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3443 tag_default = tag_encode(24)
3444 asn1_type_name = "GeneralizedTime"
3446 fmt = "%Y%m%d%H%M%SZ"
3447 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3449 def _value_sanitize(self, value):
3450 if isinstance(value, self.__class__):
3452 if isinstance(value, datetime):
3453 return value.strftime(
3454 self.fmt_ms if value.microsecond > 0 else self.fmt
3456 if isinstance(value, binary_type):
3457 value_decoded = value.decode("ascii")
3458 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3460 datetime.strptime(value_decoded, self.fmt)
3463 "invalid GeneralizedTime (without ms) format",
3466 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3468 datetime.strptime(value_decoded, self.fmt_ms)
3471 "invalid GeneralizedTime (with ms) format",
3476 "invalid GeneralizedTime length",
3477 klass=self.__class__,
3479 raise InvalidValueType((self.__class__, datetime))
3481 def todatetime(self):
3482 value = self._value.decode("ascii")
3483 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3484 return datetime.strptime(value, self.fmt)
3485 return datetime.strptime(value, self.fmt_ms)
3488 class GraphicString(CommonString):
3490 tag_default = tag_encode(25)
3491 encoding = "iso-8859-1"
3492 asn1_type_name = "GraphicString"
3495 class VisibleString(CommonString):
3497 tag_default = tag_encode(26)
3499 asn1_type_name = "VisibleString"
3502 class ISO646String(VisibleString):
3504 asn1_type_name = "ISO646String"
3507 class GeneralString(CommonString):
3509 tag_default = tag_encode(27)
3510 encoding = "iso-8859-1"
3511 asn1_type_name = "GeneralString"
3514 class UniversalString(CommonString):
3516 tag_default = tag_encode(28)
3517 encoding = "utf-32-be"
3518 asn1_type_name = "UniversalString"
3521 class BMPString(CommonString):
3523 tag_default = tag_encode(30)
3524 encoding = "utf-16-be"
3525 asn1_type_name = "BMPString"
3529 """``CHOICE`` special type
3533 class GeneralName(Choice):
3535 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3536 ("dNSName", IA5String(impl=tag_ctxp(2))),
3539 >>> gn = GeneralName()
3541 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3542 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3543 >>> gn["dNSName"] = IA5String("bar.baz")
3544 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3545 >>> gn["rfc822Name"]
3548 [2] IA5String IA5 bar.baz
3551 >>> gn.value == gn["dNSName"]
3554 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3556 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3557 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3559 __slots__ = ("specs",)
3561 asn1_type_name = "CHOICE"
3574 :param value: set the value. Either ``(choice, value)`` tuple, or
3575 :py:class:`pyderasn.Choice` object
3576 :param bytes impl: can not be set, do **not** use it
3577 :param bytes expl: override default tag with ``EXPLICIT`` one
3578 :param default: set default value. Type same as in ``value``
3579 :param bool optional: is object ``OPTIONAL`` in sequence
3581 if impl is not None:
3582 raise ValueError("no implicit tag allowed for CHOICE")
3583 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3585 schema = getattr(self, "schema", ())
3586 if len(schema) == 0:
3587 raise ValueError("schema must be specified")
3589 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3592 if value is not None:
3593 self._value = self._value_sanitize(value)
3594 if default is not None:
3595 default_value = self._value_sanitize(default)
3596 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3597 default_obj.specs = self.specs
3598 default_obj._value = default_value
3599 self.default = default_obj
3601 self._value = default_obj.copy()._value
3603 def _value_sanitize(self, value):
3604 if isinstance(value, self.__class__):
3606 if isinstance(value, tuple) and len(value) == 2:
3608 spec = self.specs.get(choice)
3610 raise ObjUnknown(choice)
3611 if not isinstance(obj, spec.__class__):
3612 raise InvalidValueType((spec,))
3613 return (choice, spec(obj))
3614 raise InvalidValueType((self.__class__, tuple))
3618 return self._value is not None and self._value[1].ready
3621 obj = self.__class__(schema=self.specs)
3622 obj._expl = self._expl
3623 obj.default = self.default
3624 obj.optional = self.optional
3625 obj.offset = self.offset
3626 obj.llen = self.llen
3627 obj.vlen = self.vlen
3629 if value is not None:
3630 obj._value = (value[0], value[1].copy())
3633 def __eq__(self, their):
3634 if isinstance(their, tuple) and len(their) == 2:
3635 return self._value == their
3636 if not isinstance(their, self.__class__):
3639 self.specs == their.specs and
3640 self._value == their._value
3650 return self.__class__(
3653 expl=self._expl if expl is None else expl,
3654 default=self.default if default is None else default,
3655 optional=self.optional if optional is None else optional,
3660 self._assert_ready()
3661 return self._value[0]
3665 self._assert_ready()
3666 return self._value[1]
3668 def __getitem__(self, key):
3669 if key not in self.specs:
3670 raise ObjUnknown(key)
3671 if self._value is None:
3673 choice, value = self._value
3678 def __setitem__(self, key, value):
3679 spec = self.specs.get(key)
3681 raise ObjUnknown(key)
3682 if not isinstance(value, spec.__class__):
3683 raise InvalidValueType((spec.__class__,))
3684 self._value = (key, spec(value))
3692 return self._value[1].decoded if self.ready else False
3695 self._assert_ready()
3696 return self._value[1].encode()
3698 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3699 for choice, spec in self.specs.items():
3700 sub_decode_path = decode_path + (choice,)
3706 decode_path=sub_decode_path,
3715 klass=self.__class__,
3716 decode_path=decode_path,
3721 value, tail = spec.decode(
3725 decode_path=sub_decode_path,
3728 obj = self.__class__(
3731 default=self.default,
3732 optional=self.optional,
3733 _decoded=(offset, 0, value.tlvlen),
3735 obj._value = (choice, value)
3739 value = pp_console_row(next(self.pps()))
3741 value = "%s[%r]" % (value, self.value)
3744 def pps(self, decode_path=()):
3746 asn1_type_name=self.asn1_type_name,
3747 obj_name=self.__class__.__name__,
3748 decode_path=decode_path,
3749 value=self.choice if self.ready else None,
3750 optional=self.optional,
3751 default=self == self.default,
3752 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3753 expl=None if self._expl is None else tag_decode(self._expl),
3760 yield self.value.pps(decode_path=decode_path + (self.choice,))
3763 class PrimitiveTypes(Choice):
3764 """Predefined ``CHOICE`` for all generic primitive types
3766 It could be useful for general decoding of some unspecified values:
3768 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3769 OCTET STRING 3 bytes 666f6f
3770 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3774 schema = tuple((klass.__name__, klass()) for klass in (
3799 """``ANY`` special type
3801 >>> Any(Integer(-123))
3803 >>> a = Any(OctetString(b"hello world").encode())
3804 ANY 040b68656c6c6f20776f726c64
3805 >>> hexenc(bytes(a))
3806 b'0x040x0bhello world'
3808 __slots__ = ("defined",)
3809 tag_default = tag_encode(0)
3810 asn1_type_name = "ANY"
3820 :param value: set the value. Either any kind of pyderasn's
3821 **ready** object, or bytes. Pay attention that
3822 **no** validation is performed is raw binary value
3824 :param bytes expl: override default tag with ``EXPLICIT`` one
3825 :param bool optional: is object ``OPTIONAL`` in sequence
3827 super(Any, self).__init__(None, expl, None, optional, _decoded)
3828 self._value = None if value is None else self._value_sanitize(value)
3831 def _value_sanitize(self, value):
3832 if isinstance(value, self.__class__):
3834 if isinstance(value, Obj):
3835 return value.encode()
3836 if isinstance(value, binary_type):
3838 raise InvalidValueType((self.__class__, Obj, binary_type))
3842 return self._value is not None
3845 obj = self.__class__()
3846 obj._value = self._value
3848 obj._expl = self._expl
3849 obj.optional = self.optional
3850 obj.offset = self.offset
3851 obj.llen = self.llen
3852 obj.vlen = self.vlen
3855 def __eq__(self, their):
3856 if isinstance(their, binary_type):
3857 return self._value == their
3858 if issubclass(their.__class__, Any):
3859 return self._value == their._value
3868 return self.__class__(
3870 expl=self._expl if expl is None else expl,
3871 optional=self.optional if optional is None else optional,
3874 def __bytes__(self):
3875 self._assert_ready()
3883 self._assert_ready()
3886 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3888 t, tlen, lv = tag_strip(tlv)
3889 l, llen, v = len_decode(lv)
3890 except DecodeError as err:
3891 raise err.__class__(
3893 klass=self.__class__,
3894 decode_path=decode_path,
3898 raise NotEnoughData(
3899 "encoded length is longer than data",
3900 klass=self.__class__,
3901 decode_path=decode_path,
3904 tlvlen = tlen + llen + l
3905 v, tail = tlv[:tlvlen], v[l:]
3906 obj = self.__class__(
3909 optional=self.optional,
3910 _decoded=(offset, 0, tlvlen),
3916 return pp_console_row(next(self.pps()))
3918 def pps(self, decode_path=()):
3920 asn1_type_name=self.asn1_type_name,
3921 obj_name=self.__class__.__name__,
3922 decode_path=decode_path,
3923 blob=self._value if self.ready else None,
3924 optional=self.optional,
3925 default=self == self.default,
3926 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3927 expl=None if self._expl is None else tag_decode(self._expl),
3932 expl_offset=self.expl_offset if self.expled else None,
3933 expl_tlen=self.expl_tlen if self.expled else None,
3934 expl_llen=self.expl_llen if self.expled else None,
3935 expl_vlen=self.expl_vlen if self.expled else None,
3937 defined_by, defined = self.defined or (None, None)
3938 if defined_by is not None:
3940 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3944 ########################################################################
3945 # ASN.1 constructed types
3946 ########################################################################
3948 def get_def_by_path(defines_by_path, sub_decode_path):
3949 """Get define by decode path
3951 for path, define in defines_by_path:
3952 if len(path) != len(sub_decode_path):
3954 for p1, p2 in zip(path, sub_decode_path):
3955 if (p1 != any) and (p1 != p2):
3961 def abs_decode_path(decode_path, rel_path):
3962 """Create an absolute decode path from current and relative ones
3964 :param decode_path: current decode path, starting point.
3966 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3967 If first tuple's element is "/", then treat it as
3968 an absolute path, ignoring ``decode_path`` as
3969 starting point. Also this tuple can contain ".."
3970 elements, stripping the leading element from
3973 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3974 ("foo", "bar", "baz", "whatever")
3975 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3977 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3980 if rel_path[0] == "/":
3982 if rel_path[0] == "..":
3983 return abs_decode_path(decode_path[:-1], rel_path[1:])
3984 return decode_path + rel_path
3987 class Sequence(Obj):
3988 """``SEQUENCE`` structure type
3990 You have to make specification of sequence::
3992 class Extension(Sequence):
3994 ("extnID", ObjectIdentifier()),
3995 ("critical", Boolean(default=False)),
3996 ("extnValue", OctetString()),
3999 Then, you can work with it as with dictionary.
4001 >>> ext = Extension()
4002 >>> Extension().specs
4004 ('extnID', OBJECT IDENTIFIER),
4005 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4006 ('extnValue', OCTET STRING),
4008 >>> ext["extnID"] = "1.2.3"
4009 Traceback (most recent call last):
4010 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4011 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4013 You can determine if sequence is ready to be encoded:
4018 Traceback (most recent call last):
4019 pyderasn.ObjNotReady: object is not ready: extnValue
4020 >>> ext["extnValue"] = OctetString(b"foobar")
4024 Value you want to assign, must have the same **type** as in
4025 corresponding specification, but it can have different tags,
4026 optional/default attributes -- they will be taken from specification
4029 class TBSCertificate(Sequence):
4031 ("version", Version(expl=tag_ctxc(0), default="v1")),
4034 >>> tbs = TBSCertificate()
4035 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4037 Assign ``None`` to remove value from sequence.
4039 You can set values in Sequence during its initialization:
4041 >>> AlgorithmIdentifier((
4042 ("algorithm", ObjectIdentifier("1.2.3")),
4043 ("parameters", Any(Null()))
4045 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4047 You can determine if value exists/set in the sequence and take its value:
4049 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4052 OBJECT IDENTIFIER 1.2.3
4054 But pay attention that if value has default, then it won't be (not
4055 in) in the sequence (because ``DEFAULT`` must not be encoded in
4056 DER), but you can read its value:
4058 >>> "critical" in ext, ext["critical"]
4059 (False, BOOLEAN False)
4060 >>> ext["critical"] = Boolean(True)
4061 >>> "critical" in ext, ext["critical"]
4062 (True, BOOLEAN True)
4064 All defaulted values are always optional.
4066 .. _strict_default_existence_ctx:
4070 When decoded DER contains defaulted value inside, then
4071 technically this is not valid DER encoding. But we allow and pass
4072 it **by default**. Of course reencoding of that kind of DER will
4073 result in different binary representation (validly without
4074 defaulted value inside). You can enable strict defaulted values
4075 existence validation by setting ``"strict_default_existence":
4076 True`` :ref:`context <ctx>` option -- decoding process will raise
4077 an exception if defaulted value is met.
4079 Two sequences are equal if they have equal specification (schema),
4080 implicit/explicit tagging and the same values.
4082 __slots__ = ("specs",)
4083 tag_default = tag_encode(form=TagFormConstructed, num=16)
4084 asn1_type_name = "SEQUENCE"
4096 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4098 schema = getattr(self, "schema", ())
4100 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4103 if value is not None:
4104 if issubclass(value.__class__, Sequence):
4105 self._value = value._value
4106 elif hasattr(value, "__iter__"):
4107 for seq_key, seq_value in value:
4108 self[seq_key] = seq_value
4110 raise InvalidValueType((Sequence,))
4111 if default is not None:
4112 if not issubclass(default.__class__, Sequence):
4113 raise InvalidValueType((Sequence,))
4114 default_value = default._value
4115 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4116 default_obj.specs = self.specs
4117 default_obj._value = default_value
4118 self.default = default_obj
4120 self._value = default_obj.copy()._value
4124 for name, spec in self.specs.items():
4125 value = self._value.get(name)
4136 obj = self.__class__(schema=self.specs)
4138 obj._expl = self._expl
4139 obj.default = self.default
4140 obj.optional = self.optional
4141 obj.offset = self.offset
4142 obj.llen = self.llen
4143 obj.vlen = self.vlen
4144 obj._value = {k: v.copy() for k, v in self._value.items()}
4147 def __eq__(self, their):
4148 if not isinstance(their, self.__class__):
4151 self.specs == their.specs and
4152 self.tag == their.tag and
4153 self._expl == their._expl and
4154 self._value == their._value
4165 return self.__class__(
4168 impl=self.tag if impl is None else impl,
4169 expl=self._expl if expl is None else expl,
4170 default=self.default if default is None else default,
4171 optional=self.optional if optional is None else optional,
4174 def __contains__(self, key):
4175 return key in self._value
4177 def __setitem__(self, key, value):
4178 spec = self.specs.get(key)
4180 raise ObjUnknown(key)
4182 self._value.pop(key, None)
4184 if not isinstance(value, spec.__class__):
4185 raise InvalidValueType((spec.__class__,))
4186 value = spec(value=value)
4187 if spec.default is not None and value == spec.default:
4188 self._value.pop(key, None)
4190 self._value[key] = value
4192 def __getitem__(self, key):
4193 value = self._value.get(key)
4194 if value is not None:
4196 spec = self.specs.get(key)
4198 raise ObjUnknown(key)
4199 if spec.default is not None:
4203 def _encoded_values(self):
4205 for name, spec in self.specs.items():
4206 value = self._value.get(name)
4210 raise ObjNotReady(name)
4211 raws.append(value.encode())
4215 v = b"".join(self._encoded_values())
4216 return b"".join((self.tag, len_encode(len(v)), v))
4218 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4220 t, tlen, lv = tag_strip(tlv)
4221 except DecodeError as err:
4222 raise err.__class__(
4224 klass=self.__class__,
4225 decode_path=decode_path,
4230 klass=self.__class__,
4231 decode_path=decode_path,
4237 l, llen, v = len_decode(lv)
4238 except DecodeError as err:
4239 raise err.__class__(
4241 klass=self.__class__,
4242 decode_path=decode_path,
4246 raise NotEnoughData(
4247 "encoded length is longer than data",
4248 klass=self.__class__,
4249 decode_path=decode_path,
4252 v, tail = v[:l], v[l:]
4253 sub_offset = offset + tlen + llen
4255 for name, spec in self.specs.items():
4256 if len(v) == 0 and spec.optional:
4258 sub_decode_path = decode_path + (name,)
4260 value, v_tail = spec.decode(
4264 decode_path=sub_decode_path,
4272 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4273 if defined is not None:
4274 defined_by, defined_spec = defined
4275 if issubclass(value.__class__, SequenceOf):
4276 for i, _value in enumerate(value):
4277 sub_sub_decode_path = sub_decode_path + (
4279 DecodePathDefBy(defined_by),
4281 defined_value, defined_tail = defined_spec.decode(
4282 memoryview(bytes(_value)),
4284 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4285 if value.expled else (value.tlen + value.llen)
4288 decode_path=sub_sub_decode_path,
4291 if len(defined_tail) > 0:
4294 klass=self.__class__,
4295 decode_path=sub_sub_decode_path,
4298 _value.defined = (defined_by, defined_value)
4300 defined_value, defined_tail = defined_spec.decode(
4301 memoryview(bytes(value)),
4303 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4304 if value.expled else (value.tlen + value.llen)
4307 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4310 if len(defined_tail) > 0:
4313 klass=self.__class__,
4314 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4317 value.defined = (defined_by, defined_value)
4319 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4321 if spec.default is not None and value == spec.default:
4322 if ctx.get("strict_default_existence", False):
4324 "DEFAULT value met",
4325 klass=self.__class__,
4326 decode_path=sub_decode_path,
4331 values[name] = value
4333 spec_defines = getattr(spec, "defines", ())
4334 if len(spec_defines) == 0:
4335 defines_by_path = ctx.get("defines_by_path", ())
4336 if len(defines_by_path) > 0:
4337 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4338 if spec_defines is not None and len(spec_defines) > 0:
4339 for rel_path, schema in spec_defines:
4340 defined = schema.get(value, None)
4341 if defined is not None:
4342 ctx.setdefault("defines", []).append((
4343 abs_decode_path(sub_decode_path[:-1], rel_path),
4349 klass=self.__class__,
4350 decode_path=decode_path,
4353 obj = self.__class__(
4357 default=self.default,
4358 optional=self.optional,
4359 _decoded=(offset, llen, l),
4365 value = pp_console_row(next(self.pps()))
4367 for name in self.specs:
4368 _value = self._value.get(name)
4371 cols.append(repr(_value))
4372 return "%s[%s]" % (value, ", ".join(cols))
4374 def pps(self, decode_path=()):
4376 asn1_type_name=self.asn1_type_name,
4377 obj_name=self.__class__.__name__,
4378 decode_path=decode_path,
4379 optional=self.optional,
4380 default=self == self.default,
4381 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4382 expl=None if self._expl is None else tag_decode(self._expl),
4387 expl_offset=self.expl_offset if self.expled else None,
4388 expl_tlen=self.expl_tlen if self.expled else None,
4389 expl_llen=self.expl_llen if self.expled else None,
4390 expl_vlen=self.expl_vlen if self.expled else None,
4392 for name in self.specs:
4393 value = self._value.get(name)
4396 yield value.pps(decode_path=decode_path + (name,))
4399 class Set(Sequence):
4400 """``SET`` structure type
4402 Its usage is identical to :py:class:`pyderasn.Sequence`.
4405 tag_default = tag_encode(form=TagFormConstructed, num=17)
4406 asn1_type_name = "SET"
4409 raws = self._encoded_values()
4412 return b"".join((self.tag, len_encode(len(v)), v))
4414 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4416 t, tlen, lv = tag_strip(tlv)
4417 except DecodeError as err:
4418 raise err.__class__(
4420 klass=self.__class__,
4421 decode_path=decode_path,
4426 klass=self.__class__,
4427 decode_path=decode_path,
4433 l, llen, v = len_decode(lv)
4434 except DecodeError as err:
4435 raise err.__class__(
4437 klass=self.__class__,
4438 decode_path=decode_path,
4442 raise NotEnoughData(
4443 "encoded length is longer than data",
4444 klass=self.__class__,
4447 v, tail = v[:l], v[l:]
4448 sub_offset = offset + tlen + llen
4450 specs_items = self.specs.items
4452 for name, spec in specs_items():
4453 sub_decode_path = decode_path + (name,)
4459 decode_path=sub_decode_path,
4468 klass=self.__class__,
4469 decode_path=decode_path,
4472 value, v_tail = spec.decode(
4476 decode_path=sub_decode_path,
4480 value.expl_tlvlen if value.expled else value.tlvlen
4483 if spec.default is None or value != spec.default: # pragma: no cover
4484 # SeqMixing.test_encoded_default_accepted covers that place
4485 values[name] = value
4486 obj = self.__class__(
4490 default=self.default,
4491 optional=self.optional,
4492 _decoded=(offset, llen, l),
4497 msg="not all values are ready",
4498 klass=self.__class__,
4499 decode_path=decode_path,
4505 class SequenceOf(Obj):
4506 """``SEQUENCE OF`` sequence type
4508 For that kind of type you must specify the object it will carry on
4509 (bounds are for example here, not required)::
4511 class Ints(SequenceOf):
4516 >>> ints.append(Integer(123))
4517 >>> ints.append(Integer(234))
4519 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4520 >>> [int(i) for i in ints]
4522 >>> ints.append(Integer(345))
4523 Traceback (most recent call last):
4524 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4527 >>> ints[1] = Integer(345)
4529 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4531 Also you can initialize sequence with preinitialized values:
4533 >>> ints = Ints([Integer(123), Integer(234)])
4535 __slots__ = ("spec", "_bound_min", "_bound_max")
4536 tag_default = tag_encode(form=TagFormConstructed, num=16)
4537 asn1_type_name = "SEQUENCE OF"
4550 super(SequenceOf, self).__init__(
4558 schema = getattr(self, "schema", None)
4560 raise ValueError("schema must be specified")
4562 self._bound_min, self._bound_max = getattr(
4566 ) if bounds is None else bounds
4568 if value is not None:
4569 self._value = self._value_sanitize(value)
4570 if default is not None:
4571 default_value = self._value_sanitize(default)
4572 default_obj = self.__class__(
4577 default_obj._value = default_value
4578 self.default = default_obj
4580 self._value = default_obj.copy()._value
4582 def _value_sanitize(self, value):
4583 if issubclass(value.__class__, SequenceOf):
4584 value = value._value
4585 elif hasattr(value, "__iter__"):
4588 raise InvalidValueType((self.__class__, iter))
4589 if not self._bound_min <= len(value) <= self._bound_max:
4590 raise BoundsError(self._bound_min, len(value), self._bound_max)
4592 if not isinstance(v, self.spec.__class__):
4593 raise InvalidValueType((self.spec.__class__,))
4598 return all(v.ready for v in self._value)
4601 obj = self.__class__(schema=self.spec)
4602 obj._bound_min = self._bound_min
4603 obj._bound_max = self._bound_max
4605 obj._expl = self._expl
4606 obj.default = self.default
4607 obj.optional = self.optional
4608 obj.offset = self.offset
4609 obj.llen = self.llen
4610 obj.vlen = self.vlen
4611 obj._value = [v.copy() for v in self._value]
4614 def __eq__(self, their):
4615 if isinstance(their, self.__class__):
4617 self.spec == their.spec and
4618 self.tag == their.tag and
4619 self._expl == their._expl and
4620 self._value == their._value
4622 if hasattr(their, "__iter__"):
4623 return self._value == list(their)
4635 return self.__class__(
4639 (self._bound_min, self._bound_max)
4640 if bounds is None else bounds
4642 impl=self.tag if impl is None else impl,
4643 expl=self._expl if expl is None else expl,
4644 default=self.default if default is None else default,
4645 optional=self.optional if optional is None else optional,
4648 def __contains__(self, key):
4649 return key in self._value
4651 def append(self, value):
4652 if not isinstance(value, self.spec.__class__):
4653 raise InvalidValueType((self.spec.__class__,))
4654 if len(self._value) + 1 > self._bound_max:
4657 len(self._value) + 1,
4660 self._value.append(value)
4663 self._assert_ready()
4664 return iter(self._value)
4667 self._assert_ready()
4668 return len(self._value)
4670 def __setitem__(self, key, value):
4671 if not isinstance(value, self.spec.__class__):
4672 raise InvalidValueType((self.spec.__class__,))
4673 self._value[key] = self.spec(value=value)
4675 def __getitem__(self, key):
4676 return self._value[key]
4678 def _encoded_values(self):
4679 return [v.encode() for v in self._value]
4682 v = b"".join(self._encoded_values())
4683 return b"".join((self.tag, len_encode(len(v)), v))
4685 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4687 t, tlen, lv = tag_strip(tlv)
4688 except DecodeError as err:
4689 raise err.__class__(
4691 klass=self.__class__,
4692 decode_path=decode_path,
4697 klass=self.__class__,
4698 decode_path=decode_path,
4704 l, llen, v = len_decode(lv)
4705 except DecodeError as err:
4706 raise err.__class__(
4708 klass=self.__class__,
4709 decode_path=decode_path,
4713 raise NotEnoughData(
4714 "encoded length is longer than data",
4715 klass=self.__class__,
4716 decode_path=decode_path,
4719 v, tail = v[:l], v[l:]
4720 sub_offset = offset + tlen + llen
4724 value, v_tail = spec.decode(
4728 decode_path=decode_path + (str(len(_value)),),
4731 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4733 _value.append(value)
4734 obj = self.__class__(
4737 bounds=(self._bound_min, self._bound_max),
4740 default=self.default,
4741 optional=self.optional,
4742 _decoded=(offset, llen, l),
4748 pp_console_row(next(self.pps())),
4749 ", ".join(repr(v) for v in self._value),
4752 def pps(self, decode_path=()):
4754 asn1_type_name=self.asn1_type_name,
4755 obj_name=self.__class__.__name__,
4756 decode_path=decode_path,
4757 optional=self.optional,
4758 default=self == self.default,
4759 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4760 expl=None if self._expl is None else tag_decode(self._expl),
4765 expl_offset=self.expl_offset if self.expled else None,
4766 expl_tlen=self.expl_tlen if self.expled else None,
4767 expl_llen=self.expl_llen if self.expled else None,
4768 expl_vlen=self.expl_vlen if self.expled else None,
4770 for i, value in enumerate(self._value):
4771 yield value.pps(decode_path=decode_path + (str(i),))
4774 class SetOf(SequenceOf):
4775 """``SET OF`` sequence type
4777 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4780 tag_default = tag_encode(form=TagFormConstructed, num=17)
4781 asn1_type_name = "SET OF"
4784 raws = self._encoded_values()
4787 return b"".join((self.tag, len_encode(len(v)), v))
4790 def obj_by_path(pypath): # pragma: no cover
4791 """Import object specified as string Python path
4793 Modules must be separated from classes/functions with ``:``.
4795 >>> obj_by_path("foo.bar:Baz")
4796 <class 'foo.bar.Baz'>
4797 >>> obj_by_path("foo.bar:Baz.boo")
4798 <classmethod 'foo.bar.Baz.boo'>
4800 mod, objs = pypath.rsplit(":", 1)
4801 from importlib import import_module
4802 obj = import_module(mod)
4803 for obj_name in objs.split("."):
4804 obj = getattr(obj, obj_name)
4808 def generic_decoder(): # pragma: no cover
4809 # All of this below is a big hack with self references
4810 choice = PrimitiveTypes()
4811 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4812 choice.specs["SetOf"] = SetOf(schema=choice)
4814 choice.specs["SequenceOf%d" % i] = SequenceOf(
4818 choice.specs["Any"] = Any()
4820 # Class name equals to type name, to omit it from output
4821 class SEQUENCEOF(SequenceOf):
4825 def pprint_any(obj, oids=None, with_colours=False):
4826 def _pprint_pps(pps):
4828 if hasattr(pp, "_fields"):
4829 if pp.asn1_type_name == Choice.asn1_type_name:
4831 pp_kwargs = pp._asdict()
4832 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4833 pp = _pp(**pp_kwargs)
4834 yield pp_console_row(
4839 with_colours=with_colours,
4841 for row in pp_console_blob(pp):
4844 for row in _pprint_pps(pp):
4846 return "\n".join(_pprint_pps(obj.pps()))
4847 return SEQUENCEOF(), pprint_any
4850 def main(): # pragma: no cover
4852 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4853 parser.add_argument(
4857 help="Skip that number of bytes from the beginning",
4859 parser.add_argument(
4861 help="Python path to dictionary with OIDs",
4863 parser.add_argument(
4865 help="Python path to schema definition to use",
4867 parser.add_argument(
4868 "--defines-by-path",
4869 help="Python path to decoder's defines_by_path",
4871 parser.add_argument(
4873 type=argparse.FileType("rb"),
4874 help="Path to DER file you want to decode",
4876 args = parser.parse_args()
4877 args.DERFile.seek(args.skip)
4878 der = memoryview(args.DERFile.read())
4879 args.DERFile.close()
4880 oids = obj_by_path(args.oids) if args.oids else {}
4882 schema = obj_by_path(args.schema)
4883 from functools import partial
4884 pprinter = partial(pprint, big_blobs=True)
4886 schema, pprinter = generic_decoder()
4887 obj, tail = schema().decode(
4890 None if args.defines_by_path is None else
4891 {"defines_by_path": obj_by_path(args.defines_by_path)}
4897 with_colours=True if environ.get("NO_COLOR") is None else False,
4900 print("\nTrailing data: %s" % hexenc(tail))
4903 if __name__ == "__main__":