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 except DecodeError as err:
3890 raise err.__class__(
3892 klass=self.__class__,
3893 decode_path=decode_path,
3897 l, llen, v = len_decode(lv)
3898 except LenIndefiniteForm as err:
3899 if not ctx.get("bered", False):
3900 raise err.__class__(
3902 klass=self.__class__,
3903 decode_path=decode_path,
3906 llen, vlen, v = 1, 0, lv[1:]
3907 sub_offset = offset + tlen + llen
3910 if v[:EOC_LEN].tobytes() == EOC:
3911 tlvlen = tlen + llen + vlen + EOC_LEN
3912 obj = self.__class__(
3913 value=tlv[:tlvlen].tobytes(),
3915 optional=self.optional,
3916 _decoded=(offset, 0, tlvlen),
3920 return obj, v[EOC_LEN:]
3922 chunk, v = Any().decode(
3925 decode_path=decode_path + (str(chunk_i),),
3929 vlen += chunk.tlvlen
3930 sub_offset += chunk.tlvlen
3932 except DecodeError as err:
3933 raise err.__class__(
3935 klass=self.__class__,
3936 decode_path=decode_path,
3940 raise NotEnoughData(
3941 "encoded length is longer than data",
3942 klass=self.__class__,
3943 decode_path=decode_path,
3946 tlvlen = tlen + llen + l
3947 v, tail = tlv[:tlvlen], v[l:]
3948 obj = self.__class__(
3951 optional=self.optional,
3952 _decoded=(offset, 0, tlvlen),
3958 return pp_console_row(next(self.pps()))
3960 def pps(self, decode_path=()):
3962 asn1_type_name=self.asn1_type_name,
3963 obj_name=self.__class__.__name__,
3964 decode_path=decode_path,
3965 blob=self._value if self.ready else None,
3966 optional=self.optional,
3967 default=self == self.default,
3968 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3969 expl=None if self._expl is None else tag_decode(self._expl),
3974 expl_offset=self.expl_offset if self.expled else None,
3975 expl_tlen=self.expl_tlen if self.expled else None,
3976 expl_llen=self.expl_llen if self.expled else None,
3977 expl_vlen=self.expl_vlen if self.expled else None,
3979 defined_by, defined = self.defined or (None, None)
3980 if defined_by is not None:
3982 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3986 ########################################################################
3987 # ASN.1 constructed types
3988 ########################################################################
3990 def get_def_by_path(defines_by_path, sub_decode_path):
3991 """Get define by decode path
3993 for path, define in defines_by_path:
3994 if len(path) != len(sub_decode_path):
3996 for p1, p2 in zip(path, sub_decode_path):
3997 if (p1 != any) and (p1 != p2):
4003 def abs_decode_path(decode_path, rel_path):
4004 """Create an absolute decode path from current and relative ones
4006 :param decode_path: current decode path, starting point.
4008 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4009 If first tuple's element is "/", then treat it as
4010 an absolute path, ignoring ``decode_path`` as
4011 starting point. Also this tuple can contain ".."
4012 elements, stripping the leading element from
4015 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4016 ("foo", "bar", "baz", "whatever")
4017 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4019 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4022 if rel_path[0] == "/":
4024 if rel_path[0] == "..":
4025 return abs_decode_path(decode_path[:-1], rel_path[1:])
4026 return decode_path + rel_path
4029 class Sequence(Obj):
4030 """``SEQUENCE`` structure type
4032 You have to make specification of sequence::
4034 class Extension(Sequence):
4036 ("extnID", ObjectIdentifier()),
4037 ("critical", Boolean(default=False)),
4038 ("extnValue", OctetString()),
4041 Then, you can work with it as with dictionary.
4043 >>> ext = Extension()
4044 >>> Extension().specs
4046 ('extnID', OBJECT IDENTIFIER),
4047 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4048 ('extnValue', OCTET STRING),
4050 >>> ext["extnID"] = "1.2.3"
4051 Traceback (most recent call last):
4052 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4053 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4055 You can determine if sequence is ready to be encoded:
4060 Traceback (most recent call last):
4061 pyderasn.ObjNotReady: object is not ready: extnValue
4062 >>> ext["extnValue"] = OctetString(b"foobar")
4066 Value you want to assign, must have the same **type** as in
4067 corresponding specification, but it can have different tags,
4068 optional/default attributes -- they will be taken from specification
4071 class TBSCertificate(Sequence):
4073 ("version", Version(expl=tag_ctxc(0), default="v1")),
4076 >>> tbs = TBSCertificate()
4077 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4079 Assign ``None`` to remove value from sequence.
4081 You can set values in Sequence during its initialization:
4083 >>> AlgorithmIdentifier((
4084 ("algorithm", ObjectIdentifier("1.2.3")),
4085 ("parameters", Any(Null()))
4087 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4089 You can determine if value exists/set in the sequence and take its value:
4091 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4094 OBJECT IDENTIFIER 1.2.3
4096 But pay attention that if value has default, then it won't be (not
4097 in) in the sequence (because ``DEFAULT`` must not be encoded in
4098 DER), but you can read its value:
4100 >>> "critical" in ext, ext["critical"]
4101 (False, BOOLEAN False)
4102 >>> ext["critical"] = Boolean(True)
4103 >>> "critical" in ext, ext["critical"]
4104 (True, BOOLEAN True)
4106 All defaulted values are always optional.
4108 .. _strict_default_existence_ctx:
4112 When decoded DER contains defaulted value inside, then
4113 technically this is not valid DER encoding. But we allow and pass
4114 it **by default**. Of course reencoding of that kind of DER will
4115 result in different binary representation (validly without
4116 defaulted value inside). You can enable strict defaulted values
4117 existence validation by setting ``"strict_default_existence":
4118 True`` :ref:`context <ctx>` option -- decoding process will raise
4119 an exception if defaulted value is met.
4121 Two sequences are equal if they have equal specification (schema),
4122 implicit/explicit tagging and the same values.
4124 __slots__ = ("specs",)
4125 tag_default = tag_encode(form=TagFormConstructed, num=16)
4126 asn1_type_name = "SEQUENCE"
4138 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4140 schema = getattr(self, "schema", ())
4142 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4145 if value is not None:
4146 if issubclass(value.__class__, Sequence):
4147 self._value = value._value
4148 elif hasattr(value, "__iter__"):
4149 for seq_key, seq_value in value:
4150 self[seq_key] = seq_value
4152 raise InvalidValueType((Sequence,))
4153 if default is not None:
4154 if not issubclass(default.__class__, Sequence):
4155 raise InvalidValueType((Sequence,))
4156 default_value = default._value
4157 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4158 default_obj.specs = self.specs
4159 default_obj._value = default_value
4160 self.default = default_obj
4162 self._value = default_obj.copy()._value
4166 for name, spec in self.specs.items():
4167 value = self._value.get(name)
4178 obj = self.__class__(schema=self.specs)
4180 obj._expl = self._expl
4181 obj.default = self.default
4182 obj.optional = self.optional
4183 obj.offset = self.offset
4184 obj.llen = self.llen
4185 obj.vlen = self.vlen
4186 obj._value = {k: v.copy() for k, v in self._value.items()}
4189 def __eq__(self, their):
4190 if not isinstance(their, self.__class__):
4193 self.specs == their.specs and
4194 self.tag == their.tag and
4195 self._expl == their._expl and
4196 self._value == their._value
4207 return self.__class__(
4210 impl=self.tag if impl is None else impl,
4211 expl=self._expl if expl is None else expl,
4212 default=self.default if default is None else default,
4213 optional=self.optional if optional is None else optional,
4216 def __contains__(self, key):
4217 return key in self._value
4219 def __setitem__(self, key, value):
4220 spec = self.specs.get(key)
4222 raise ObjUnknown(key)
4224 self._value.pop(key, None)
4226 if not isinstance(value, spec.__class__):
4227 raise InvalidValueType((spec.__class__,))
4228 value = spec(value=value)
4229 if spec.default is not None and value == spec.default:
4230 self._value.pop(key, None)
4232 self._value[key] = value
4234 def __getitem__(self, key):
4235 value = self._value.get(key)
4236 if value is not None:
4238 spec = self.specs.get(key)
4240 raise ObjUnknown(key)
4241 if spec.default is not None:
4245 def _encoded_values(self):
4247 for name, spec in self.specs.items():
4248 value = self._value.get(name)
4252 raise ObjNotReady(name)
4253 raws.append(value.encode())
4257 v = b"".join(self._encoded_values())
4258 return b"".join((self.tag, len_encode(len(v)), v))
4260 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4262 t, tlen, lv = tag_strip(tlv)
4263 except DecodeError as err:
4264 raise err.__class__(
4266 klass=self.__class__,
4267 decode_path=decode_path,
4272 klass=self.__class__,
4273 decode_path=decode_path,
4278 eoc_expected = False
4280 l, llen, v = len_decode(lv)
4281 except LenIndefiniteForm as err:
4282 if not ctx.get("bered", False):
4283 raise err.__class__(
4285 klass=self.__class__,
4286 decode_path=decode_path,
4289 l, llen, v = 0, 1, lv[1:]
4291 except DecodeError as err:
4292 raise err.__class__(
4294 klass=self.__class__,
4295 decode_path=decode_path,
4299 raise NotEnoughData(
4300 "encoded length is longer than data",
4301 klass=self.__class__,
4302 decode_path=decode_path,
4305 if not eoc_expected:
4306 v, tail = v[:l], v[l:]
4308 sub_offset = offset + tlen + llen
4310 for name, spec in self.specs.items():
4311 if spec.optional and (
4312 (eoc_expected and v[:EOC_LEN].tobytes() == EOC) or
4316 sub_decode_path = decode_path + (name,)
4318 value, v_tail = spec.decode(
4322 decode_path=sub_decode_path,
4330 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4331 if defined is not None:
4332 defined_by, defined_spec = defined
4333 if issubclass(value.__class__, SequenceOf):
4334 for i, _value in enumerate(value):
4335 sub_sub_decode_path = sub_decode_path + (
4337 DecodePathDefBy(defined_by),
4339 defined_value, defined_tail = defined_spec.decode(
4340 memoryview(bytes(_value)),
4342 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4343 if value.expled else (value.tlen + value.llen)
4346 decode_path=sub_sub_decode_path,
4349 if len(defined_tail) > 0:
4352 klass=self.__class__,
4353 decode_path=sub_sub_decode_path,
4356 _value.defined = (defined_by, defined_value)
4358 defined_value, defined_tail = defined_spec.decode(
4359 memoryview(bytes(value)),
4361 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4362 if value.expled else (value.tlen + value.llen)
4365 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4368 if len(defined_tail) > 0:
4371 klass=self.__class__,
4372 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4375 value.defined = (defined_by, defined_value)
4377 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4379 sub_offset += value_len
4381 if spec.default is not None and value == spec.default:
4382 if ctx.get("strict_default_existence", False):
4384 "DEFAULT value met",
4385 klass=self.__class__,
4386 decode_path=sub_decode_path,
4391 values[name] = value
4393 spec_defines = getattr(spec, "defines", ())
4394 if len(spec_defines) == 0:
4395 defines_by_path = ctx.get("defines_by_path", ())
4396 if len(defines_by_path) > 0:
4397 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4398 if spec_defines is not None and len(spec_defines) > 0:
4399 for rel_path, schema in spec_defines:
4400 defined = schema.get(value, None)
4401 if defined is not None:
4402 ctx.setdefault("defines", []).append((
4403 abs_decode_path(sub_decode_path[:-1], rel_path),
4407 if v[:EOC_LEN].tobytes() != EOC:
4410 klass=self.__class__,
4411 decode_path=decode_path,
4419 klass=self.__class__,
4420 decode_path=decode_path,
4423 obj = self.__class__(
4427 default=self.default,
4428 optional=self.optional,
4429 _decoded=(offset, llen, vlen),
4437 value = pp_console_row(next(self.pps()))
4439 for name in self.specs:
4440 _value = self._value.get(name)
4443 cols.append(repr(_value))
4444 return "%s[%s]" % (value, ", ".join(cols))
4446 def pps(self, decode_path=()):
4448 asn1_type_name=self.asn1_type_name,
4449 obj_name=self.__class__.__name__,
4450 decode_path=decode_path,
4451 optional=self.optional,
4452 default=self == self.default,
4453 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4454 expl=None if self._expl is None else tag_decode(self._expl),
4459 expl_offset=self.expl_offset if self.expled else None,
4460 expl_tlen=self.expl_tlen if self.expled else None,
4461 expl_llen=self.expl_llen if self.expled else None,
4462 expl_vlen=self.expl_vlen if self.expled else None,
4464 for name in self.specs:
4465 value = self._value.get(name)
4468 yield value.pps(decode_path=decode_path + (name,))
4471 class Set(Sequence):
4472 """``SET`` structure type
4474 Its usage is identical to :py:class:`pyderasn.Sequence`.
4477 tag_default = tag_encode(form=TagFormConstructed, num=17)
4478 asn1_type_name = "SET"
4481 raws = self._encoded_values()
4484 return b"".join((self.tag, len_encode(len(v)), v))
4486 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4488 t, tlen, lv = tag_strip(tlv)
4489 except DecodeError as err:
4490 raise err.__class__(
4492 klass=self.__class__,
4493 decode_path=decode_path,
4498 klass=self.__class__,
4499 decode_path=decode_path,
4504 eoc_expected = False
4506 l, llen, v = len_decode(lv)
4507 except LenIndefiniteForm as err:
4508 if not ctx.get("bered", False):
4509 raise err.__class__(
4511 klass=self.__class__,
4512 decode_path=decode_path,
4515 l, llen, v = 0, 1, lv[1:]
4517 except DecodeError as err:
4518 raise err.__class__(
4520 klass=self.__class__,
4521 decode_path=decode_path,
4525 raise NotEnoughData(
4526 "encoded length is longer than data",
4527 klass=self.__class__,
4530 if not eoc_expected:
4531 v, tail = v[:l], v[l:]
4533 sub_offset = offset + tlen + llen
4535 specs_items = self.specs.items
4537 if eoc_expected and v[:EOC_LEN].tobytes() == EOC:
4539 for name, spec in specs_items():
4540 sub_decode_path = decode_path + (name,)
4546 decode_path=sub_decode_path,
4555 klass=self.__class__,
4556 decode_path=decode_path,
4559 value, v_tail = spec.decode(
4563 decode_path=sub_decode_path,
4566 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4567 sub_offset += value_len
4570 if spec.default is None or value != spec.default: # pragma: no cover
4571 # SeqMixing.test_encoded_default_accepted covers that place
4572 values[name] = value
4573 obj = self.__class__(
4577 default=self.default,
4578 optional=self.optional,
4579 _decoded=(offset, llen, vlen + (EOC_LEN if eoc_expected else 0)),
4584 msg="not all values are ready",
4585 klass=self.__class__,
4586 decode_path=decode_path,
4595 class SequenceOf(Obj):
4596 """``SEQUENCE OF`` sequence type
4598 For that kind of type you must specify the object it will carry on
4599 (bounds are for example here, not required)::
4601 class Ints(SequenceOf):
4606 >>> ints.append(Integer(123))
4607 >>> ints.append(Integer(234))
4609 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4610 >>> [int(i) for i in ints]
4612 >>> ints.append(Integer(345))
4613 Traceback (most recent call last):
4614 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4617 >>> ints[1] = Integer(345)
4619 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4621 Also you can initialize sequence with preinitialized values:
4623 >>> ints = Ints([Integer(123), Integer(234)])
4625 __slots__ = ("spec", "_bound_min", "_bound_max")
4626 tag_default = tag_encode(form=TagFormConstructed, num=16)
4627 asn1_type_name = "SEQUENCE OF"
4640 super(SequenceOf, self).__init__(
4648 schema = getattr(self, "schema", None)
4650 raise ValueError("schema must be specified")
4652 self._bound_min, self._bound_max = getattr(
4656 ) if bounds is None else bounds
4658 if value is not None:
4659 self._value = self._value_sanitize(value)
4660 if default is not None:
4661 default_value = self._value_sanitize(default)
4662 default_obj = self.__class__(
4667 default_obj._value = default_value
4668 self.default = default_obj
4670 self._value = default_obj.copy()._value
4672 def _value_sanitize(self, value):
4673 if issubclass(value.__class__, SequenceOf):
4674 value = value._value
4675 elif hasattr(value, "__iter__"):
4678 raise InvalidValueType((self.__class__, iter))
4679 if not self._bound_min <= len(value) <= self._bound_max:
4680 raise BoundsError(self._bound_min, len(value), self._bound_max)
4682 if not isinstance(v, self.spec.__class__):
4683 raise InvalidValueType((self.spec.__class__,))
4688 return all(v.ready for v in self._value)
4691 obj = self.__class__(schema=self.spec)
4692 obj._bound_min = self._bound_min
4693 obj._bound_max = self._bound_max
4695 obj._expl = self._expl
4696 obj.default = self.default
4697 obj.optional = self.optional
4698 obj.offset = self.offset
4699 obj.llen = self.llen
4700 obj.vlen = self.vlen
4701 obj._value = [v.copy() for v in self._value]
4704 def __eq__(self, their):
4705 if isinstance(their, self.__class__):
4707 self.spec == their.spec and
4708 self.tag == their.tag and
4709 self._expl == their._expl and
4710 self._value == their._value
4712 if hasattr(their, "__iter__"):
4713 return self._value == list(their)
4725 return self.__class__(
4729 (self._bound_min, self._bound_max)
4730 if bounds is None else bounds
4732 impl=self.tag if impl is None else impl,
4733 expl=self._expl if expl is None else expl,
4734 default=self.default if default is None else default,
4735 optional=self.optional if optional is None else optional,
4738 def __contains__(self, key):
4739 return key in self._value
4741 def append(self, value):
4742 if not isinstance(value, self.spec.__class__):
4743 raise InvalidValueType((self.spec.__class__,))
4744 if len(self._value) + 1 > self._bound_max:
4747 len(self._value) + 1,
4750 self._value.append(value)
4753 self._assert_ready()
4754 return iter(self._value)
4757 self._assert_ready()
4758 return len(self._value)
4760 def __setitem__(self, key, value):
4761 if not isinstance(value, self.spec.__class__):
4762 raise InvalidValueType((self.spec.__class__,))
4763 self._value[key] = self.spec(value=value)
4765 def __getitem__(self, key):
4766 return self._value[key]
4768 def _encoded_values(self):
4769 return [v.encode() for v in self._value]
4772 v = b"".join(self._encoded_values())
4773 return b"".join((self.tag, len_encode(len(v)), v))
4775 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4777 t, tlen, lv = tag_strip(tlv)
4778 except DecodeError as err:
4779 raise err.__class__(
4781 klass=self.__class__,
4782 decode_path=decode_path,
4787 klass=self.__class__,
4788 decode_path=decode_path,
4793 eoc_expected = False
4795 l, llen, v = len_decode(lv)
4796 except LenIndefiniteForm as err:
4797 if not ctx.get("bered", False):
4798 raise err.__class__(
4800 klass=self.__class__,
4801 decode_path=decode_path,
4804 l, llen, v = 0, 1, lv[1:]
4806 except DecodeError as err:
4807 raise err.__class__(
4809 klass=self.__class__,
4810 decode_path=decode_path,
4814 raise NotEnoughData(
4815 "encoded length is longer than data",
4816 klass=self.__class__,
4817 decode_path=decode_path,
4820 if not eoc_expected:
4821 v, tail = v[:l], v[l:]
4823 sub_offset = offset + tlen + llen
4827 if eoc_expected and v[:EOC_LEN].tobytes() == EOC:
4829 value, v_tail = spec.decode(
4833 decode_path=decode_path + (str(len(_value)),),
4836 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4837 sub_offset += value_len
4840 _value.append(value)
4841 obj = self.__class__(
4844 bounds=(self._bound_min, self._bound_max),
4847 default=self.default,
4848 optional=self.optional,
4849 _decoded=(offset, llen, vlen),
4858 pp_console_row(next(self.pps())),
4859 ", ".join(repr(v) for v in self._value),
4862 def pps(self, decode_path=()):
4864 asn1_type_name=self.asn1_type_name,
4865 obj_name=self.__class__.__name__,
4866 decode_path=decode_path,
4867 optional=self.optional,
4868 default=self == self.default,
4869 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4870 expl=None if self._expl is None else tag_decode(self._expl),
4875 expl_offset=self.expl_offset if self.expled else None,
4876 expl_tlen=self.expl_tlen if self.expled else None,
4877 expl_llen=self.expl_llen if self.expled else None,
4878 expl_vlen=self.expl_vlen if self.expled else None,
4880 for i, value in enumerate(self._value):
4881 yield value.pps(decode_path=decode_path + (str(i),))
4884 class SetOf(SequenceOf):
4885 """``SET OF`` sequence type
4887 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4890 tag_default = tag_encode(form=TagFormConstructed, num=17)
4891 asn1_type_name = "SET OF"
4894 raws = self._encoded_values()
4897 return b"".join((self.tag, len_encode(len(v)), v))
4900 def obj_by_path(pypath): # pragma: no cover
4901 """Import object specified as string Python path
4903 Modules must be separated from classes/functions with ``:``.
4905 >>> obj_by_path("foo.bar:Baz")
4906 <class 'foo.bar.Baz'>
4907 >>> obj_by_path("foo.bar:Baz.boo")
4908 <classmethod 'foo.bar.Baz.boo'>
4910 mod, objs = pypath.rsplit(":", 1)
4911 from importlib import import_module
4912 obj = import_module(mod)
4913 for obj_name in objs.split("."):
4914 obj = getattr(obj, obj_name)
4918 def generic_decoder(): # pragma: no cover
4919 # All of this below is a big hack with self references
4920 choice = PrimitiveTypes()
4921 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4922 choice.specs["SetOf"] = SetOf(schema=choice)
4924 choice.specs["SequenceOf%d" % i] = SequenceOf(
4928 choice.specs["Any"] = Any()
4930 # Class name equals to type name, to omit it from output
4931 class SEQUENCEOF(SequenceOf):
4935 def pprint_any(obj, oids=None, with_colours=False):
4936 def _pprint_pps(pps):
4938 if hasattr(pp, "_fields"):
4939 if pp.asn1_type_name == Choice.asn1_type_name:
4941 pp_kwargs = pp._asdict()
4942 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4943 pp = _pp(**pp_kwargs)
4944 yield pp_console_row(
4949 with_colours=with_colours,
4951 for row in pp_console_blob(pp):
4954 for row in _pprint_pps(pp):
4956 return "\n".join(_pprint_pps(obj.pps()))
4957 return SEQUENCEOF(), pprint_any
4960 def main(): # pragma: no cover
4962 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4963 parser.add_argument(
4967 help="Skip that number of bytes from the beginning",
4969 parser.add_argument(
4971 help="Python path to dictionary with OIDs",
4973 parser.add_argument(
4975 help="Python path to schema definition to use",
4977 parser.add_argument(
4978 "--defines-by-path",
4979 help="Python path to decoder's defines_by_path",
4981 parser.add_argument(
4983 type=argparse.FileType("rb"),
4984 help="Path to DER file you want to decode",
4986 args = parser.parse_args()
4987 args.DERFile.seek(args.skip)
4988 der = memoryview(args.DERFile.read())
4989 args.DERFile.close()
4990 oids = obj_by_path(args.oids) if args.oids else {}
4992 schema = obj_by_path(args.schema)
4993 from functools import partial
4994 pprinter = partial(pprint, big_blobs=True)
4996 schema, pprinter = generic_decoder()
4997 ctx = {"bered": True}
4998 if args.defines_by_path is not None:
4999 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5000 obj, tail = schema().decode(der, ctx=ctx)
5004 with_colours=True if environ.get("NO_COLOR") is None else False,
5007 print("\nTrailing data: %s" % hexenc(tail))
5010 if __name__ == "__main__":