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 LenIndefForm(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):
808 :returns: (decoded length, length's length, remaining data)
809 :raises LenIndefForm: if indefinite form encoding is met
812 raise NotEnoughData("no data at all")
813 first_octet = byte2int(data)
814 if first_octet & 0x80 == 0:
815 return first_octet, 1, data[1:]
816 octets_num = first_octet & 0x7F
817 if octets_num + 1 > len(data):
818 raise NotEnoughData("encoded length is longer than data")
821 if byte2int(data[1:]) == 0:
822 raise DecodeError("leading zeros")
824 for v in iterbytes(data[1:1 + octets_num]):
827 raise DecodeError("long form instead of short one")
828 return l, 1 + octets_num, data[1 + octets_num:]
831 ########################################################################
833 ########################################################################
835 class AutoAddSlots(type):
836 def __new__(mcs, name, bases, _dict):
837 _dict["__slots__"] = _dict.get("__slots__", ())
838 return type.__new__(mcs, name, bases, _dict)
841 @add_metaclass(AutoAddSlots)
843 """Common ASN.1 object class
845 All ASN.1 types are inherited from it. It has metaclass that
846 automatically adds ``__slots__`` to all inherited classes.
870 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
871 self._expl = getattr(self, "expl", None) if expl is None else expl
872 if self.tag != self.tag_default and self._expl is not None:
874 "implicit and explicit tags can not be set simultaneously"
876 if default is not None:
878 self.optional = optional
879 self.offset, self.llen, self.vlen = _decoded
881 self.lenindef = False
882 self.expl_lenindef = False
886 def ready(self): # pragma: no cover
887 """Is object ready to be encoded?
889 raise NotImplementedError()
891 def _assert_ready(self):
893 raise ObjNotReady(self.__class__.__name__)
897 """Is object decoded?
899 return (self.llen + self.vlen) > 0
901 def copy(self): # pragma: no cover
902 """Make a copy of object, safe to be mutated
904 raise NotImplementedError()
912 return self.tlen + self.llen + self.vlen
914 def __str__(self): # pragma: no cover
915 return self.__bytes__() if PY2 else self.__unicode__()
917 def __ne__(self, their):
918 return not(self == their)
920 def __gt__(self, their): # pragma: no cover
921 return not(self < their)
923 def __le__(self, their): # pragma: no cover
924 return (self == their) or (self < their)
926 def __ge__(self, their): # pragma: no cover
927 return (self == their) or (self > their)
929 def _encode(self): # pragma: no cover
930 raise NotImplementedError()
932 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
933 raise NotImplementedError()
937 if self._expl is None:
939 return b"".join((self._expl, len_encode(len(raw)), raw))
952 :param data: either binary or memoryview
953 :param int offset: initial data's offset
954 :param bool leavemm: do we need to leave memoryview of remaining
955 data as is, or convert it to bytes otherwise
956 :param ctx: optional :ref:`context <ctx>` governing decoding process.
957 :param tag_only: decode only the tag, without length and contents
958 (used only in Choice and Set structures, trying to
959 determine if tag satisfies the scheme)
960 :returns: (Obj, remaining data)
964 tlv = memoryview(data)
965 if self._expl is None:
966 result = self._decode(
969 decode_path=decode_path,
978 t, tlen, lv = tag_strip(tlv)
979 except DecodeError as err:
982 klass=self.__class__,
983 decode_path=decode_path,
988 klass=self.__class__,
989 decode_path=decode_path,
993 l, llen, v = len_decode(lv)
994 except LenIndefForm as err:
995 if not ctx.get("bered", False):
998 klass=self.__class__,
999 decode_path=decode_path,
1003 offset += tlen + llen
1004 result = self._decode(
1007 decode_path=decode_path,
1014 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1015 if eoc_expected.tobytes() != EOC:
1018 decode_path=decode_path,
1022 obj.expl_lenindef = True
1023 except DecodeError as err:
1024 raise err.__class__(
1026 klass=self.__class__,
1027 decode_path=decode_path,
1032 raise NotEnoughData(
1033 "encoded length is longer than data",
1034 klass=self.__class__,
1035 decode_path=decode_path,
1038 result = self._decode(
1040 offset=offset + tlen + llen,
1041 decode_path=decode_path,
1048 return obj, (tail if leavemm else tail.tobytes())
1052 return self._expl is not None
1059 def expl_tlen(self):
1060 return len(self._expl)
1063 def expl_llen(self):
1064 if self.expl_lenindef:
1066 return len(len_encode(self.tlvlen))
1069 def expl_offset(self):
1070 return self.offset - self.expl_tlen - self.expl_llen
1073 def expl_vlen(self):
1077 def expl_tlvlen(self):
1078 return self.expl_tlen + self.expl_llen + self.expl_vlen
1081 class DecodePathDefBy(object):
1082 """DEFINED BY representation inside decode path
1084 __slots__ = ("defined_by",)
1086 def __init__(self, defined_by):
1087 self.defined_by = defined_by
1089 def __ne__(self, their):
1090 return not(self == their)
1092 def __eq__(self, their):
1093 if not isinstance(their, self.__class__):
1095 return self.defined_by == their.defined_by
1098 return "DEFINED BY " + str(self.defined_by)
1101 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1104 ########################################################################
1106 ########################################################################
1108 PP = namedtuple("PP", (
1130 asn1_type_name="unknown",
1169 def _colorize(what, colour, with_colours, attrs=("bold",)):
1170 return colored(what, colour, attrs=attrs) if with_colours else what
1185 " " if pp.expl_offset is None else
1186 ("-%d" % (pp.offset - pp.expl_offset))
1189 cols.append(_colorize(col, "red", with_colours, ()))
1190 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1191 cols.append(_colorize(col, "green", with_colours, ()))
1192 if len(pp.decode_path) > 0:
1193 cols.append(" ." * (len(pp.decode_path)))
1194 ent = pp.decode_path[-1]
1195 if isinstance(ent, DecodePathDefBy):
1196 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1197 value = str(ent.defined_by)
1199 oids is not None and
1200 ent.defined_by.asn1_type_name ==
1201 ObjectIdentifier.asn1_type_name and
1204 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1206 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1208 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1209 if pp.expl is not None:
1210 klass, _, num = pp.expl
1211 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1212 cols.append(_colorize(col, "blue", with_colours))
1213 if pp.impl is not None:
1214 klass, _, num = pp.impl
1215 col = "[%s%d]" % (TagClassReprs[klass], num)
1216 cols.append(_colorize(col, "blue", with_colours))
1217 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1218 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1219 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1220 if pp.value is not None:
1222 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1224 oids is not None and
1225 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1228 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1230 if isinstance(pp.blob, binary_type):
1231 cols.append(hexenc(pp.blob))
1232 elif isinstance(pp.blob, tuple):
1233 cols.append(", ".join(pp.blob))
1235 cols.append(_colorize("OPTIONAL", "red", with_colours))
1237 cols.append(_colorize("DEFAULT", "red", with_colours))
1238 return " ".join(cols)
1241 def pp_console_blob(pp):
1242 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1243 if len(pp.decode_path) > 0:
1244 cols.append(" ." * (len(pp.decode_path) + 1))
1245 if isinstance(pp.blob, binary_type):
1246 blob = hexenc(pp.blob).upper()
1247 for i in range(0, len(blob), 32):
1248 chunk = blob[i:i + 32]
1249 yield " ".join(cols + [":".join(
1250 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1252 elif isinstance(pp.blob, tuple):
1253 yield " ".join(cols + [", ".join(pp.blob)])
1256 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1257 """Pretty print object
1259 :param Obj obj: object you want to pretty print
1260 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1261 from it is met, then its humand readable form is printed
1262 :param big_blobs: if large binary objects are met (like OctetString
1263 values), do we need to print them too, on separate
1265 :param with_colours: colourize output, if ``termcolor`` library
1268 def _pprint_pps(pps):
1270 if hasattr(pp, "_fields"):
1272 yield pp_console_row(
1277 with_colours=with_colours,
1279 for row in pp_console_blob(pp):
1282 yield pp_console_row(
1287 with_colours=with_colours,
1290 for row in _pprint_pps(pp):
1292 return "\n".join(_pprint_pps(obj.pps()))
1295 ########################################################################
1296 # ASN.1 primitive types
1297 ########################################################################
1300 """``BOOLEAN`` boolean type
1302 >>> b = Boolean(True)
1304 >>> b == Boolean(True)
1310 tag_default = tag_encode(1)
1311 asn1_type_name = "BOOLEAN"
1323 :param value: set the value. Either boolean type, or
1324 :py:class:`pyderasn.Boolean` object
1325 :param bytes impl: override default tag with ``IMPLICIT`` one
1326 :param bytes expl: override default tag with ``EXPLICIT`` one
1327 :param default: set default value. Type same as in ``value``
1328 :param bool optional: is object ``OPTIONAL`` in sequence
1330 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1331 self._value = None if value is None else self._value_sanitize(value)
1332 if default is not None:
1333 default = self._value_sanitize(default)
1334 self.default = self.__class__(
1340 self._value = default
1342 def _value_sanitize(self, value):
1343 if issubclass(value.__class__, Boolean):
1345 if isinstance(value, bool):
1347 raise InvalidValueType((self.__class__, bool))
1351 return self._value is not None
1354 obj = self.__class__()
1355 obj._value = self._value
1357 obj._expl = self._expl
1358 obj.default = self.default
1359 obj.optional = self.optional
1360 obj.offset = self.offset
1361 obj.llen = self.llen
1362 obj.vlen = self.vlen
1365 def __nonzero__(self):
1366 self._assert_ready()
1370 self._assert_ready()
1373 def __eq__(self, their):
1374 if isinstance(their, bool):
1375 return self._value == their
1376 if not issubclass(their.__class__, Boolean):
1379 self._value == their._value and
1380 self.tag == their.tag and
1381 self._expl == their._expl
1392 return self.__class__(
1394 impl=self.tag if impl is None else impl,
1395 expl=self._expl if expl is None else expl,
1396 default=self.default if default is None else default,
1397 optional=self.optional if optional is None else optional,
1401 self._assert_ready()
1405 (b"\xFF" if self._value else b"\x00"),
1408 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1410 t, _, lv = tag_strip(tlv)
1411 except DecodeError as err:
1412 raise err.__class__(
1414 klass=self.__class__,
1415 decode_path=decode_path,
1420 klass=self.__class__,
1421 decode_path=decode_path,
1427 l, _, v = len_decode(lv)
1428 except DecodeError as err:
1429 raise err.__class__(
1431 klass=self.__class__,
1432 decode_path=decode_path,
1436 raise InvalidLength(
1437 "Boolean's length must be equal to 1",
1438 klass=self.__class__,
1439 decode_path=decode_path,
1443 raise NotEnoughData(
1444 "encoded length is longer than data",
1445 klass=self.__class__,
1446 decode_path=decode_path,
1449 first_octet = byte2int(v)
1451 if first_octet == 0:
1453 elif first_octet == 0xFF:
1455 elif ctx.get("bered", False):
1460 "unacceptable Boolean value",
1461 klass=self.__class__,
1462 decode_path=decode_path,
1465 obj = self.__class__(
1469 default=self.default,
1470 optional=self.optional,
1471 _decoded=(offset, 1, 1),
1477 return pp_console_row(next(self.pps()))
1479 def pps(self, decode_path=()):
1481 asn1_type_name=self.asn1_type_name,
1482 obj_name=self.__class__.__name__,
1483 decode_path=decode_path,
1484 value=str(self._value) if self.ready else None,
1485 optional=self.optional,
1486 default=self == self.default,
1487 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1488 expl=None if self._expl is None else tag_decode(self._expl),
1493 expl_offset=self.expl_offset if self.expled else None,
1494 expl_tlen=self.expl_tlen if self.expled else None,
1495 expl_llen=self.expl_llen if self.expled else None,
1496 expl_vlen=self.expl_vlen if self.expled else None,
1501 """``INTEGER`` integer type
1503 >>> b = Integer(-123)
1505 >>> b == Integer(-123)
1510 >>> Integer(2, bounds=(1, 3))
1512 >>> Integer(5, bounds=(1, 3))
1513 Traceback (most recent call last):
1514 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1518 class Version(Integer):
1525 >>> v = Version("v1")
1532 {'v3': 2, 'v1': 0, 'v2': 1}
1534 __slots__ = ("specs", "_bound_min", "_bound_max")
1535 tag_default = tag_encode(2)
1536 asn1_type_name = "INTEGER"
1550 :param value: set the value. Either integer type, named value
1551 (if ``schema`` is specified in the class), or
1552 :py:class:`pyderasn.Integer` object
1553 :param bounds: set ``(MIN, MAX)`` value constraint.
1554 (-inf, +inf) by default
1555 :param bytes impl: override default tag with ``IMPLICIT`` one
1556 :param bytes expl: override default tag with ``EXPLICIT`` one
1557 :param default: set default value. Type same as in ``value``
1558 :param bool optional: is object ``OPTIONAL`` in sequence
1560 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1562 specs = getattr(self, "schema", {}) if _specs is None else _specs
1563 self.specs = specs if isinstance(specs, dict) else dict(specs)
1564 self._bound_min, self._bound_max = getattr(
1567 (float("-inf"), float("+inf")),
1568 ) if bounds is None else bounds
1569 if value is not None:
1570 self._value = self._value_sanitize(value)
1571 if default is not None:
1572 default = self._value_sanitize(default)
1573 self.default = self.__class__(
1579 if self._value is None:
1580 self._value = default
1582 def _value_sanitize(self, value):
1583 if issubclass(value.__class__, Integer):
1584 value = value._value
1585 elif isinstance(value, integer_types):
1587 elif isinstance(value, str):
1588 value = self.specs.get(value)
1590 raise ObjUnknown("integer value: %s" % value)
1592 raise InvalidValueType((self.__class__, int, str))
1593 if not self._bound_min <= value <= self._bound_max:
1594 raise BoundsError(self._bound_min, value, self._bound_max)
1599 return self._value is not None
1602 obj = self.__class__(_specs=self.specs)
1603 obj._value = self._value
1604 obj._bound_min = self._bound_min
1605 obj._bound_max = self._bound_max
1607 obj._expl = self._expl
1608 obj.default = self.default
1609 obj.optional = self.optional
1610 obj.offset = self.offset
1611 obj.llen = self.llen
1612 obj.vlen = self.vlen
1616 self._assert_ready()
1617 return int(self._value)
1620 self._assert_ready()
1623 bytes(self._expl or b"") +
1624 str(self._value).encode("ascii"),
1627 def __eq__(self, their):
1628 if isinstance(their, integer_types):
1629 return self._value == their
1630 if not issubclass(their.__class__, Integer):
1633 self._value == their._value and
1634 self.tag == their.tag and
1635 self._expl == their._expl
1638 def __lt__(self, their):
1639 return self._value < their._value
1643 for name, value in self.specs.items():
1644 if value == self._value:
1656 return self.__class__(
1659 (self._bound_min, self._bound_max)
1660 if bounds is None else bounds
1662 impl=self.tag if impl is None else impl,
1663 expl=self._expl if expl is None else expl,
1664 default=self.default if default is None else default,
1665 optional=self.optional if optional is None else optional,
1670 self._assert_ready()
1674 octets = bytearray([0])
1678 octets = bytearray()
1680 octets.append((value & 0xFF) ^ 0xFF)
1682 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1685 octets = bytearray()
1687 octets.append(value & 0xFF)
1689 if octets[-1] & 0x80 > 0:
1692 octets = bytes(octets)
1694 bytes_len = ceil(value.bit_length() / 8) or 1
1697 octets = value.to_bytes(
1702 except OverflowError:
1706 return b"".join((self.tag, len_encode(len(octets)), octets))
1708 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1710 t, _, lv = tag_strip(tlv)
1711 except DecodeError as err:
1712 raise err.__class__(
1714 klass=self.__class__,
1715 decode_path=decode_path,
1720 klass=self.__class__,
1721 decode_path=decode_path,
1727 l, llen, v = len_decode(lv)
1728 except DecodeError as err:
1729 raise err.__class__(
1731 klass=self.__class__,
1732 decode_path=decode_path,
1736 raise NotEnoughData(
1737 "encoded length is longer than data",
1738 klass=self.__class__,
1739 decode_path=decode_path,
1743 raise NotEnoughData(
1745 klass=self.__class__,
1746 decode_path=decode_path,
1749 v, tail = v[:l], v[l:]
1750 first_octet = byte2int(v)
1752 second_octet = byte2int(v[1:])
1754 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1755 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1758 "non normalized integer",
1759 klass=self.__class__,
1760 decode_path=decode_path,
1765 if first_octet & 0x80 > 0:
1766 octets = bytearray()
1767 for octet in bytearray(v):
1768 octets.append(octet ^ 0xFF)
1769 for octet in octets:
1770 value = (value << 8) | octet
1774 for octet in bytearray(v):
1775 value = (value << 8) | octet
1777 value = int.from_bytes(v, byteorder="big", signed=True)
1779 obj = self.__class__(
1781 bounds=(self._bound_min, self._bound_max),
1784 default=self.default,
1785 optional=self.optional,
1787 _decoded=(offset, llen, l),
1789 except BoundsError as err:
1792 klass=self.__class__,
1793 decode_path=decode_path,
1799 return pp_console_row(next(self.pps()))
1801 def pps(self, decode_path=()):
1803 asn1_type_name=self.asn1_type_name,
1804 obj_name=self.__class__.__name__,
1805 decode_path=decode_path,
1806 value=(self.named or str(self._value)) if self.ready else None,
1807 optional=self.optional,
1808 default=self == self.default,
1809 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1810 expl=None if self._expl is None else tag_decode(self._expl),
1815 expl_offset=self.expl_offset if self.expled else None,
1816 expl_tlen=self.expl_tlen if self.expled else None,
1817 expl_llen=self.expl_llen if self.expled else None,
1818 expl_vlen=self.expl_vlen if self.expled else None,
1822 class BitString(Obj):
1823 """``BIT STRING`` bit string type
1825 >>> BitString(b"hello world")
1826 BIT STRING 88 bits 68656c6c6f20776f726c64
1829 >>> b == b"hello world"
1834 >>> b = BitString("'010110000000'B")
1835 BIT STRING 12 bits 5800
1838 >>> b[0], b[1], b[2], b[3]
1839 (False, True, False, True)
1843 [False, True, False, True, True, False, False, False, False, False, False, False]
1847 class KeyUsage(BitString):
1849 ("digitalSignature", 0),
1850 ("nonRepudiation", 1),
1851 ("keyEncipherment", 2),
1854 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1855 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1857 ['nonRepudiation', 'keyEncipherment']
1859 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1861 __slots__ = ("tag_constructed", "specs", "defined")
1862 tag_default = tag_encode(3)
1863 asn1_type_name = "BIT STRING"
1876 :param value: set the value. Either binary type, tuple of named
1877 values (if ``schema`` is specified in the class),
1878 string in ``'XXX...'B`` form, or
1879 :py:class:`pyderasn.BitString` object
1880 :param bytes impl: override default tag with ``IMPLICIT`` one
1881 :param bytes expl: override default tag with ``EXPLICIT`` one
1882 :param default: set default value. Type same as in ``value``
1883 :param bool optional: is object ``OPTIONAL`` in sequence
1885 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1886 specs = getattr(self, "schema", {}) if _specs is None else _specs
1887 self.specs = specs if isinstance(specs, dict) else dict(specs)
1888 self._value = None if value is None else self._value_sanitize(value)
1889 if default is not None:
1890 default = self._value_sanitize(default)
1891 self.default = self.__class__(
1897 self._value = default
1899 tag_klass, _, tag_num = tag_decode(self.tag)
1900 self.tag_constructed = tag_encode(
1902 form=TagFormConstructed,
1906 def _bits2octets(self, bits):
1907 if len(self.specs) > 0:
1908 bits = bits.rstrip("0")
1910 bits += "0" * ((8 - (bit_len % 8)) % 8)
1911 octets = bytearray(len(bits) // 8)
1912 for i in six_xrange(len(octets)):
1913 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1914 return bit_len, bytes(octets)
1916 def _value_sanitize(self, value):
1917 if issubclass(value.__class__, BitString):
1919 if isinstance(value, (string_types, binary_type)):
1921 isinstance(value, string_types) and
1922 value.startswith("'") and
1923 value.endswith("'B")
1926 if not set(value) <= set(("0", "1")):
1927 raise ValueError("B's coding contains unacceptable chars")
1928 return self._bits2octets(value)
1929 elif isinstance(value, binary_type):
1930 return (len(value) * 8, value)
1932 raise InvalidValueType((
1937 if isinstance(value, tuple):
1940 isinstance(value[0], integer_types) and
1941 isinstance(value[1], binary_type)
1946 bit = self.specs.get(name)
1948 raise ObjUnknown("BitString value: %s" % name)
1951 return self._bits2octets("")
1953 return self._bits2octets("".join(
1954 ("1" if bit in bits else "0")
1955 for bit in six_xrange(max(bits) + 1)
1957 raise InvalidValueType((self.__class__, binary_type, string_types))
1961 return self._value is not None
1964 obj = self.__class__(_specs=self.specs)
1966 if value is not None:
1967 value = (value[0], value[1])
1970 obj._expl = self._expl
1971 obj.default = self.default
1972 obj.optional = self.optional
1973 obj.offset = self.offset
1974 obj.llen = self.llen
1975 obj.vlen = self.vlen
1979 self._assert_ready()
1980 for i in six_xrange(self._value[0]):
1985 self._assert_ready()
1986 return self._value[0]
1988 def __bytes__(self):
1989 self._assert_ready()
1990 return self._value[1]
1992 def __eq__(self, their):
1993 if isinstance(their, bytes):
1994 return self._value[1] == their
1995 if not issubclass(their.__class__, BitString):
1998 self._value == their._value and
1999 self.tag == their.tag and
2000 self._expl == their._expl
2005 return [name for name, bit in self.specs.items() if self[bit]]
2015 return self.__class__(
2017 impl=self.tag if impl is None else impl,
2018 expl=self._expl if expl is None else expl,
2019 default=self.default if default is None else default,
2020 optional=self.optional if optional is None else optional,
2024 def __getitem__(self, key):
2025 if isinstance(key, int):
2026 bit_len, octets = self._value
2030 byte2int(memoryview(octets)[key // 8:]) >>
2033 if isinstance(key, string_types):
2034 value = self.specs.get(key)
2036 raise ObjUnknown("BitString value: %s" % key)
2038 raise InvalidValueType((int, str))
2041 self._assert_ready()
2042 bit_len, octets = self._value
2045 len_encode(len(octets) + 1),
2046 int2byte((8 - bit_len % 8) % 8),
2050 def _decode_chunk(self, lv, offset, decode_path, ctx):
2052 l, llen, v = len_decode(lv)
2053 except DecodeError as err:
2054 raise err.__class__(
2056 klass=self.__class__,
2057 decode_path=decode_path,
2061 raise NotEnoughData(
2062 "encoded length is longer than data",
2063 klass=self.__class__,
2064 decode_path=decode_path,
2068 raise NotEnoughData(
2070 klass=self.__class__,
2071 decode_path=decode_path,
2074 pad_size = byte2int(v)
2075 if l == 1 and pad_size != 0:
2077 "invalid empty value",
2078 klass=self.__class__,
2079 decode_path=decode_path,
2085 klass=self.__class__,
2086 decode_path=decode_path,
2089 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2092 klass=self.__class__,
2093 decode_path=decode_path,
2096 v, tail = v[:l], v[l:]
2097 obj = self.__class__(
2098 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2101 default=self.default,
2102 optional=self.optional,
2104 _decoded=(offset, llen, l),
2108 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2110 t, tlen, lv = tag_strip(tlv)
2111 except DecodeError as err:
2112 raise err.__class__(
2114 klass=self.__class__,
2115 decode_path=decode_path,
2121 return self._decode_chunk(lv, offset, decode_path, ctx)
2122 if t == self.tag_constructed:
2123 if not ctx.get("bered", False):
2125 msg="unallowed BER constructed encoding",
2126 decode_path=decode_path,
2133 l, llen, v = len_decode(lv)
2134 except LenIndefForm:
2135 llen, l, v = 1, 0, lv[1:]
2137 except DecodeError as err:
2138 raise err.__class__(
2140 klass=self.__class__,
2141 decode_path=decode_path,
2144 if l > 0 and l > len(v):
2145 raise NotEnoughData(
2146 "encoded length is longer than data",
2147 klass=self.__class__,
2148 decode_path=decode_path,
2151 if not lenindef and l == 0:
2152 raise NotEnoughData(
2154 klass=self.__class__,
2155 decode_path=decode_path,
2159 sub_offset = offset + tlen + llen
2163 if v[:EOC_LEN].tobytes() == EOC:
2170 msg="chunk out of bounds",
2171 decode_path=len(chunks) - 1,
2172 offset=chunks[-1].offset,
2174 sub_decode_path = decode_path + (str(len(chunks)),)
2176 chunk, v_tail = BitString().decode(
2179 decode_path=sub_decode_path,
2185 msg="expected BitString encoded chunk",
2186 decode_path=sub_decode_path,
2189 chunks.append(chunk)
2190 sub_offset += chunk.tlvlen
2191 vlen += chunk.tlvlen
2193 if len(chunks) == 0:
2196 decode_path=decode_path,
2201 for chunk_i, chunk in enumerate(chunks[:-1]):
2202 if chunk.bit_len % 8 != 0:
2204 msg="BitString chunk is not multiple of 8 bit",
2205 decode_path=decode_path + (str(chunk_i),),
2206 offset=chunk.offset,
2208 values.append(bytes(chunk))
2209 bit_len += chunk.bit_len
2210 chunk_last = chunks[-1]
2211 values.append(bytes(chunk_last))
2212 bit_len += chunk_last.bit_len
2213 obj = self.__class__(
2214 value=(bit_len, b"".join(values)),
2217 default=self.default,
2218 optional=self.optional,
2220 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2222 obj.lenindef = lenindef
2224 return obj, (v[EOC_LEN:] if lenindef else v)
2226 klass=self.__class__,
2227 decode_path=decode_path,
2232 return pp_console_row(next(self.pps()))
2234 def pps(self, decode_path=()):
2238 bit_len, blob = self._value
2239 value = "%d bits" % bit_len
2240 if len(self.specs) > 0:
2241 blob = tuple(self.named)
2243 asn1_type_name=self.asn1_type_name,
2244 obj_name=self.__class__.__name__,
2245 decode_path=decode_path,
2248 optional=self.optional,
2249 default=self == self.default,
2250 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2251 expl=None if self._expl is None else tag_decode(self._expl),
2256 expl_offset=self.expl_offset if self.expled else None,
2257 expl_tlen=self.expl_tlen if self.expled else None,
2258 expl_llen=self.expl_llen if self.expled else None,
2259 expl_vlen=self.expl_vlen if self.expled else None,
2261 defined_by, defined = self.defined or (None, None)
2262 if defined_by is not None:
2264 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2268 class OctetString(Obj):
2269 """``OCTET STRING`` binary string type
2271 >>> s = OctetString(b"hello world")
2272 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2273 >>> s == OctetString(b"hello world")
2278 >>> OctetString(b"hello", bounds=(4, 4))
2279 Traceback (most recent call last):
2280 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2281 >>> OctetString(b"hell", bounds=(4, 4))
2282 OCTET STRING 4 bytes 68656c6c
2284 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2285 tag_default = tag_encode(4)
2286 asn1_type_name = "OCTET STRING"
2299 :param value: set the value. Either binary type, or
2300 :py:class:`pyderasn.OctetString` object
2301 :param bounds: set ``(MIN, MAX)`` value size constraint.
2302 (-inf, +inf) by default
2303 :param bytes impl: override default tag with ``IMPLICIT`` one
2304 :param bytes expl: override default tag with ``EXPLICIT`` one
2305 :param default: set default value. Type same as in ``value``
2306 :param bool optional: is object ``OPTIONAL`` in sequence
2308 super(OctetString, self).__init__(
2316 self._bound_min, self._bound_max = getattr(
2320 ) if bounds is None else bounds
2321 if value is not None:
2322 self._value = self._value_sanitize(value)
2323 if default is not None:
2324 default = self._value_sanitize(default)
2325 self.default = self.__class__(
2330 if self._value is None:
2331 self._value = default
2333 tag_klass, _, tag_num = tag_decode(self.tag)
2334 self.tag_constructed = tag_encode(
2336 form=TagFormConstructed,
2340 def _value_sanitize(self, value):
2341 if issubclass(value.__class__, OctetString):
2342 value = value._value
2343 elif isinstance(value, binary_type):
2346 raise InvalidValueType((self.__class__, bytes))
2347 if not self._bound_min <= len(value) <= self._bound_max:
2348 raise BoundsError(self._bound_min, len(value), self._bound_max)
2353 return self._value is not None
2356 obj = self.__class__()
2357 obj._value = self._value
2358 obj._bound_min = self._bound_min
2359 obj._bound_max = self._bound_max
2361 obj._expl = self._expl
2362 obj.default = self.default
2363 obj.optional = self.optional
2364 obj.offset = self.offset
2365 obj.llen = self.llen
2366 obj.vlen = self.vlen
2369 def __bytes__(self):
2370 self._assert_ready()
2373 def __eq__(self, their):
2374 if isinstance(their, binary_type):
2375 return self._value == their
2376 if not issubclass(their.__class__, OctetString):
2379 self._value == their._value and
2380 self.tag == their.tag and
2381 self._expl == their._expl
2384 def __lt__(self, their):
2385 return self._value < their._value
2396 return self.__class__(
2399 (self._bound_min, self._bound_max)
2400 if bounds is None else bounds
2402 impl=self.tag if impl is None else impl,
2403 expl=self._expl if expl is None else expl,
2404 default=self.default if default is None else default,
2405 optional=self.optional if optional is None else optional,
2409 self._assert_ready()
2412 len_encode(len(self._value)),
2416 def _decode_chunk(self, lv, offset, decode_path, ctx):
2418 l, llen, v = len_decode(lv)
2419 except DecodeError as err:
2420 raise err.__class__(
2422 klass=self.__class__,
2423 decode_path=decode_path,
2427 raise NotEnoughData(
2428 "encoded length is longer than data",
2429 klass=self.__class__,
2430 decode_path=decode_path,
2433 v, tail = v[:l], v[l:]
2435 obj = self.__class__(
2437 bounds=(self._bound_min, self._bound_max),
2440 default=self.default,
2441 optional=self.optional,
2442 _decoded=(offset, llen, l),
2444 except DecodeError as err:
2447 klass=self.__class__,
2448 decode_path=decode_path,
2451 except BoundsError as err:
2454 klass=self.__class__,
2455 decode_path=decode_path,
2460 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2462 t, tlen, lv = tag_strip(tlv)
2463 except DecodeError as err:
2464 raise err.__class__(
2466 klass=self.__class__,
2467 decode_path=decode_path,
2473 return self._decode_chunk(lv, offset, decode_path, ctx)
2474 if t == self.tag_constructed:
2475 if not ctx.get("bered", False):
2477 msg="unallowed BER constructed encoding",
2478 decode_path=decode_path,
2485 l, llen, v = len_decode(lv)
2486 except LenIndefForm:
2487 llen, l, v = 1, 0, lv[1:]
2489 except DecodeError as err:
2490 raise err.__class__(
2492 klass=self.__class__,
2493 decode_path=decode_path,
2496 if l > 0 and l > len(v):
2497 raise NotEnoughData(
2498 "encoded length is longer than data",
2499 klass=self.__class__,
2500 decode_path=decode_path,
2503 if not lenindef and l == 0:
2504 raise NotEnoughData(
2506 klass=self.__class__,
2507 decode_path=decode_path,
2511 sub_offset = offset + tlen + llen
2515 if v[:EOC_LEN].tobytes() == EOC:
2522 msg="chunk out of bounds",
2523 decode_path=len(chunks) - 1,
2524 offset=chunks[-1].offset,
2526 sub_decode_path = decode_path + (str(len(chunks)),)
2528 chunk, v_tail = OctetString().decode(
2531 decode_path=sub_decode_path,
2537 msg="expected OctetString encoded chunk",
2538 decode_path=sub_decode_path,
2541 chunks.append(chunk)
2542 sub_offset += chunk.tlvlen
2543 vlen += chunk.tlvlen
2545 if len(chunks) == 0:
2548 decode_path=decode_path,
2552 obj = self.__class__(
2553 value=b"".join(bytes(chunk) for chunk in chunks),
2554 bounds=(self._bound_min, self._bound_max),
2557 default=self.default,
2558 optional=self.optional,
2559 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2561 except DecodeError as err:
2564 klass=self.__class__,
2565 decode_path=decode_path,
2568 except BoundsError as err:
2571 klass=self.__class__,
2572 decode_path=decode_path,
2575 obj.lenindef = lenindef
2577 return obj, (v[EOC_LEN:] if lenindef else v)
2579 klass=self.__class__,
2580 decode_path=decode_path,
2585 return pp_console_row(next(self.pps()))
2587 def pps(self, decode_path=()):
2589 asn1_type_name=self.asn1_type_name,
2590 obj_name=self.__class__.__name__,
2591 decode_path=decode_path,
2592 value=("%d bytes" % len(self._value)) if self.ready else None,
2593 blob=self._value if self.ready else None,
2594 optional=self.optional,
2595 default=self == self.default,
2596 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2597 expl=None if self._expl is None else tag_decode(self._expl),
2602 expl_offset=self.expl_offset if self.expled else None,
2603 expl_tlen=self.expl_tlen if self.expled else None,
2604 expl_llen=self.expl_llen if self.expled else None,
2605 expl_vlen=self.expl_vlen if self.expled else None,
2607 defined_by, defined = self.defined or (None, None)
2608 if defined_by is not None:
2610 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2615 """``NULL`` null object
2623 tag_default = tag_encode(5)
2624 asn1_type_name = "NULL"
2628 value=None, # unused, but Sequence passes it
2635 :param bytes impl: override default tag with ``IMPLICIT`` one
2636 :param bytes expl: override default tag with ``EXPLICIT`` one
2637 :param bool optional: is object ``OPTIONAL`` in sequence
2639 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2647 obj = self.__class__()
2649 obj._expl = self._expl
2650 obj.default = self.default
2651 obj.optional = self.optional
2652 obj.offset = self.offset
2653 obj.llen = self.llen
2654 obj.vlen = self.vlen
2657 def __eq__(self, their):
2658 if not issubclass(their.__class__, Null):
2661 self.tag == their.tag and
2662 self._expl == their._expl
2672 return self.__class__(
2673 impl=self.tag if impl is None else impl,
2674 expl=self._expl if expl is None else expl,
2675 optional=self.optional if optional is None else optional,
2679 return self.tag + len_encode(0)
2681 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2683 t, _, lv = tag_strip(tlv)
2684 except DecodeError as err:
2685 raise err.__class__(
2687 klass=self.__class__,
2688 decode_path=decode_path,
2693 klass=self.__class__,
2694 decode_path=decode_path,
2700 l, _, v = len_decode(lv)
2701 except DecodeError as err:
2702 raise err.__class__(
2704 klass=self.__class__,
2705 decode_path=decode_path,
2709 raise InvalidLength(
2710 "Null must have zero length",
2711 klass=self.__class__,
2712 decode_path=decode_path,
2715 obj = self.__class__(
2718 optional=self.optional,
2719 _decoded=(offset, 1, 0),
2724 return pp_console_row(next(self.pps()))
2726 def pps(self, decode_path=()):
2728 asn1_type_name=self.asn1_type_name,
2729 obj_name=self.__class__.__name__,
2730 decode_path=decode_path,
2731 optional=self.optional,
2732 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2733 expl=None if self._expl is None else tag_decode(self._expl),
2738 expl_offset=self.expl_offset if self.expled else None,
2739 expl_tlen=self.expl_tlen if self.expled else None,
2740 expl_llen=self.expl_llen if self.expled else None,
2741 expl_vlen=self.expl_vlen if self.expled else None,
2745 class ObjectIdentifier(Obj):
2746 """``OBJECT IDENTIFIER`` OID type
2748 >>> oid = ObjectIdentifier((1, 2, 3))
2749 OBJECT IDENTIFIER 1.2.3
2750 >>> oid == ObjectIdentifier("1.2.3")
2756 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2757 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2759 >>> str(ObjectIdentifier((3, 1)))
2760 Traceback (most recent call last):
2761 pyderasn.InvalidOID: unacceptable first arc value
2763 __slots__ = ("defines",)
2764 tag_default = tag_encode(6)
2765 asn1_type_name = "OBJECT IDENTIFIER"
2778 :param value: set the value. Either tuples of integers,
2779 string of "."-concatenated integers, or
2780 :py:class:`pyderasn.ObjectIdentifier` object
2781 :param defines: sequence of tuples. Each tuple has two elements.
2782 First one is relative to current one decode
2783 path, aiming to the field defined by that OID.
2784 Read about relative path in
2785 :py:func:`pyderasn.abs_decode_path`. Second
2786 tuple element is ``{OID: pyderasn.Obj()}``
2787 dictionary, mapping between current OID value
2788 and structure applied to defined field.
2789 :ref:`Read about DEFINED BY <definedby>`
2790 :param bytes impl: override default tag with ``IMPLICIT`` one
2791 :param bytes expl: override default tag with ``EXPLICIT`` one
2792 :param default: set default value. Type same as in ``value``
2793 :param bool optional: is object ``OPTIONAL`` in sequence
2795 super(ObjectIdentifier, self).__init__(
2803 if value is not None:
2804 self._value = self._value_sanitize(value)
2805 if default is not None:
2806 default = self._value_sanitize(default)
2807 self.default = self.__class__(
2812 if self._value is None:
2813 self._value = default
2814 self.defines = defines
2816 def __add__(self, their):
2817 if isinstance(their, self.__class__):
2818 return self.__class__(self._value + their._value)
2819 if isinstance(their, tuple):
2820 return self.__class__(self._value + their)
2821 raise InvalidValueType((self.__class__, tuple))
2823 def _value_sanitize(self, value):
2824 if issubclass(value.__class__, ObjectIdentifier):
2826 if isinstance(value, string_types):
2828 value = tuple(int(arc) for arc in value.split("."))
2830 raise InvalidOID("unacceptable arcs values")
2831 if isinstance(value, tuple):
2833 raise InvalidOID("less than 2 arcs")
2834 first_arc = value[0]
2835 if first_arc in (0, 1):
2836 if not (0 <= value[1] <= 39):
2837 raise InvalidOID("second arc is too wide")
2838 elif first_arc == 2:
2841 raise InvalidOID("unacceptable first arc value")
2843 raise InvalidValueType((self.__class__, str, tuple))
2847 return self._value is not None
2850 obj = self.__class__()
2851 obj._value = self._value
2852 obj.defines = self.defines
2854 obj._expl = self._expl
2855 obj.default = self.default
2856 obj.optional = self.optional
2857 obj.offset = self.offset
2858 obj.llen = self.llen
2859 obj.vlen = self.vlen
2863 self._assert_ready()
2864 return iter(self._value)
2867 return ".".join(str(arc) for arc in self._value or ())
2870 self._assert_ready()
2873 bytes(self._expl or b"") +
2874 str(self._value).encode("ascii"),
2877 def __eq__(self, their):
2878 if isinstance(their, tuple):
2879 return self._value == their
2880 if not issubclass(their.__class__, ObjectIdentifier):
2883 self.tag == their.tag and
2884 self._expl == their._expl and
2885 self._value == their._value
2888 def __lt__(self, their):
2889 return self._value < their._value
2900 return self.__class__(
2902 defines=self.defines if defines is None else defines,
2903 impl=self.tag if impl is None else impl,
2904 expl=self._expl if expl is None else expl,
2905 default=self.default if default is None else default,
2906 optional=self.optional if optional is None else optional,
2910 self._assert_ready()
2912 first_value = value[1]
2913 first_arc = value[0]
2916 elif first_arc == 1:
2918 elif first_arc == 2:
2920 else: # pragma: no cover
2921 raise RuntimeError("invalid arc is stored")
2922 octets = [zero_ended_encode(first_value)]
2923 for arc in value[2:]:
2924 octets.append(zero_ended_encode(arc))
2925 v = b"".join(octets)
2926 return b"".join((self.tag, len_encode(len(v)), v))
2928 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2930 t, _, lv = tag_strip(tlv)
2931 except DecodeError as err:
2932 raise err.__class__(
2934 klass=self.__class__,
2935 decode_path=decode_path,
2940 klass=self.__class__,
2941 decode_path=decode_path,
2947 l, llen, v = len_decode(lv)
2948 except DecodeError as err:
2949 raise err.__class__(
2951 klass=self.__class__,
2952 decode_path=decode_path,
2956 raise NotEnoughData(
2957 "encoded length is longer than data",
2958 klass=self.__class__,
2959 decode_path=decode_path,
2963 raise NotEnoughData(
2965 klass=self.__class__,
2966 decode_path=decode_path,
2969 v, tail = v[:l], v[l:]
2975 octet = indexbytes(v, i)
2976 arc = (arc << 7) | (octet & 0x7F)
2977 if octet & 0x80 == 0:
2985 klass=self.__class__,
2986 decode_path=decode_path,
2990 second_arc = arcs[0]
2991 if 0 <= second_arc <= 39:
2993 elif 40 <= second_arc <= 79:
2999 obj = self.__class__(
3000 value=tuple([first_arc, second_arc] + arcs[1:]),
3003 default=self.default,
3004 optional=self.optional,
3005 _decoded=(offset, llen, l),
3010 return pp_console_row(next(self.pps()))
3012 def pps(self, decode_path=()):
3014 asn1_type_name=self.asn1_type_name,
3015 obj_name=self.__class__.__name__,
3016 decode_path=decode_path,
3017 value=str(self) if self.ready else None,
3018 optional=self.optional,
3019 default=self == self.default,
3020 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3021 expl=None if self._expl is None else tag_decode(self._expl),
3026 expl_offset=self.expl_offset if self.expled else None,
3027 expl_tlen=self.expl_tlen if self.expled else None,
3028 expl_llen=self.expl_llen if self.expled else None,
3029 expl_vlen=self.expl_vlen if self.expled else None,
3033 class Enumerated(Integer):
3034 """``ENUMERATED`` integer type
3036 This type is identical to :py:class:`pyderasn.Integer`, but requires
3037 schema to be specified and does not accept values missing from it.
3040 tag_default = tag_encode(10)
3041 asn1_type_name = "ENUMERATED"
3052 bounds=None, # dummy argument, workability for Integer.decode
3054 super(Enumerated, self).__init__(
3063 if len(self.specs) == 0:
3064 raise ValueError("schema must be specified")
3066 def _value_sanitize(self, value):
3067 if isinstance(value, self.__class__):
3068 value = value._value
3069 elif isinstance(value, integer_types):
3070 if value not in list(self.specs.values()):
3072 "unknown integer value: %s" % value,
3073 klass=self.__class__,
3075 elif isinstance(value, string_types):
3076 value = self.specs.get(value)
3078 raise ObjUnknown("integer value: %s" % value)
3080 raise InvalidValueType((self.__class__, int, str))
3084 obj = self.__class__(_specs=self.specs)
3085 obj._value = self._value
3086 obj._bound_min = self._bound_min
3087 obj._bound_max = self._bound_max
3089 obj._expl = self._expl
3090 obj.default = self.default
3091 obj.optional = self.optional
3092 obj.offset = self.offset
3093 obj.llen = self.llen
3094 obj.vlen = self.vlen
3106 return self.__class__(
3108 impl=self.tag if impl is None else impl,
3109 expl=self._expl if expl is None else expl,
3110 default=self.default if default is None else default,
3111 optional=self.optional if optional is None else optional,
3116 class CommonString(OctetString):
3117 """Common class for all strings
3119 Everything resembles :py:class:`pyderasn.OctetString`, except
3120 ability to deal with unicode text strings.
3122 >>> hexenc("привет мир".encode("utf-8"))
3123 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3124 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3126 >>> s = UTF8String("привет мир")
3127 UTF8String UTF8String привет мир
3129 'привет мир'
3130 >>> hexenc(bytes(s))
3131 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3133 >>> PrintableString("привет мир")
3134 Traceback (most recent call last):
3135 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3137 >>> BMPString("ада", bounds=(2, 2))
3138 Traceback (most recent call last):
3139 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3140 >>> s = BMPString("ад", bounds=(2, 2))
3143 >>> hexenc(bytes(s))
3151 * - :py:class:`pyderasn.UTF8String`
3153 * - :py:class:`pyderasn.NumericString`
3155 * - :py:class:`pyderasn.PrintableString`
3157 * - :py:class:`pyderasn.TeletexString`
3159 * - :py:class:`pyderasn.T61String`
3161 * - :py:class:`pyderasn.VideotexString`
3163 * - :py:class:`pyderasn.IA5String`
3165 * - :py:class:`pyderasn.GraphicString`
3167 * - :py:class:`pyderasn.VisibleString`
3169 * - :py:class:`pyderasn.ISO646String`
3171 * - :py:class:`pyderasn.GeneralString`
3173 * - :py:class:`pyderasn.UniversalString`
3175 * - :py:class:`pyderasn.BMPString`
3178 __slots__ = ("encoding",)
3180 def _value_sanitize(self, value):
3182 value_decoded = None
3183 if isinstance(value, self.__class__):
3184 value_raw = value._value
3185 elif isinstance(value, text_type):
3186 value_decoded = value
3187 elif isinstance(value, binary_type):
3190 raise InvalidValueType((self.__class__, text_type, binary_type))
3193 value_decoded.encode(self.encoding)
3194 if value_raw is None else value_raw
3197 value_raw.decode(self.encoding)
3198 if value_decoded is None else value_decoded
3200 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3201 raise DecodeError(str(err))
3202 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3210 def __eq__(self, their):
3211 if isinstance(their, binary_type):
3212 return self._value == their
3213 if isinstance(their, text_type):
3214 return self._value == their.encode(self.encoding)
3215 if not isinstance(their, self.__class__):
3218 self._value == their._value and
3219 self.tag == their.tag and
3220 self._expl == their._expl
3223 def __unicode__(self):
3225 return self._value.decode(self.encoding)
3226 return text_type(self._value)
3229 return pp_console_row(next(self.pps(no_unicode=PY2)))
3231 def pps(self, decode_path=(), no_unicode=False):
3234 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3236 asn1_type_name=self.asn1_type_name,
3237 obj_name=self.__class__.__name__,
3238 decode_path=decode_path,
3240 optional=self.optional,
3241 default=self == self.default,
3242 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3243 expl=None if self._expl is None else tag_decode(self._expl),
3251 class UTF8String(CommonString):
3253 tag_default = tag_encode(12)
3255 asn1_type_name = "UTF8String"
3258 class NumericString(CommonString):
3260 tag_default = tag_encode(18)
3262 asn1_type_name = "NumericString"
3263 allowable_chars = set(digits.encode("ascii"))
3265 def _value_sanitize(self, value):
3266 value = super(NumericString, self)._value_sanitize(value)
3267 if not set(value) <= self.allowable_chars:
3268 raise DecodeError("non-numeric value")
3272 class PrintableString(CommonString):
3274 tag_default = tag_encode(19)
3276 asn1_type_name = "PrintableString"
3279 class TeletexString(CommonString):
3281 tag_default = tag_encode(20)
3283 asn1_type_name = "TeletexString"
3286 class T61String(TeletexString):
3288 asn1_type_name = "T61String"
3291 class VideotexString(CommonString):
3293 tag_default = tag_encode(21)
3294 encoding = "iso-8859-1"
3295 asn1_type_name = "VideotexString"
3298 class IA5String(CommonString):
3300 tag_default = tag_encode(22)
3302 asn1_type_name = "IA5"
3305 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3306 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3307 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3310 class UTCTime(CommonString):
3311 """``UTCTime`` datetime type
3313 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3314 UTCTime UTCTime 2017-09-30T22:07:50
3320 datetime.datetime(2017, 9, 30, 22, 7, 50)
3321 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3322 datetime.datetime(1957, 9, 30, 22, 7, 50)
3325 tag_default = tag_encode(23)
3327 asn1_type_name = "UTCTime"
3329 fmt = "%y%m%d%H%M%SZ"
3339 bounds=None, # dummy argument, workability for OctetString.decode
3342 :param value: set the value. Either datetime type, or
3343 :py:class:`pyderasn.UTCTime` object
3344 :param bytes impl: override default tag with ``IMPLICIT`` one
3345 :param bytes expl: override default tag with ``EXPLICIT`` one
3346 :param default: set default value. Type same as in ``value``
3347 :param bool optional: is object ``OPTIONAL`` in sequence
3349 super(UTCTime, self).__init__(
3357 if value is not None:
3358 self._value = self._value_sanitize(value)
3359 if default is not None:
3360 default = self._value_sanitize(default)
3361 self.default = self.__class__(
3366 if self._value is None:
3367 self._value = default
3369 def _value_sanitize(self, value):
3370 if isinstance(value, self.__class__):
3372 if isinstance(value, datetime):
3373 return value.strftime(self.fmt).encode("ascii")
3374 if isinstance(value, binary_type):
3375 value_decoded = value.decode("ascii")
3376 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3378 datetime.strptime(value_decoded, self.fmt)
3380 raise DecodeError("invalid UTCTime format")
3383 raise DecodeError("invalid UTCTime length")
3384 raise InvalidValueType((self.__class__, datetime))
3386 def __eq__(self, their):
3387 if isinstance(their, binary_type):
3388 return self._value == their
3389 if isinstance(their, datetime):
3390 return self.todatetime() == their
3391 if not isinstance(their, self.__class__):
3394 self._value == their._value and
3395 self.tag == their.tag and
3396 self._expl == their._expl
3399 def todatetime(self):
3400 """Convert to datetime
3404 Pay attention that UTCTime can not hold full year, so all years
3405 having < 50 years are treated as 20xx, 19xx otherwise, according
3406 to X.509 recomendation.
3408 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3409 year = value.year % 100
3411 year=(2000 + year) if year < 50 else (1900 + year),
3415 minute=value.minute,
3416 second=value.second,
3420 return pp_console_row(next(self.pps()))
3422 def pps(self, decode_path=()):
3424 asn1_type_name=self.asn1_type_name,
3425 obj_name=self.__class__.__name__,
3426 decode_path=decode_path,
3427 value=self.todatetime().isoformat() if self.ready else None,
3428 optional=self.optional,
3429 default=self == self.default,
3430 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3431 expl=None if self._expl is None else tag_decode(self._expl),
3439 class GeneralizedTime(UTCTime):
3440 """``GeneralizedTime`` datetime type
3442 This type is similar to :py:class:`pyderasn.UTCTime`.
3444 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3445 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3447 '20170930220750.000123Z'
3448 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3449 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3452 tag_default = tag_encode(24)
3453 asn1_type_name = "GeneralizedTime"
3455 fmt = "%Y%m%d%H%M%SZ"
3456 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3458 def _value_sanitize(self, value):
3459 if isinstance(value, self.__class__):
3461 if isinstance(value, datetime):
3462 return value.strftime(
3463 self.fmt_ms if value.microsecond > 0 else self.fmt
3465 if isinstance(value, binary_type):
3466 value_decoded = value.decode("ascii")
3467 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3469 datetime.strptime(value_decoded, self.fmt)
3472 "invalid GeneralizedTime (without ms) format",
3475 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3477 datetime.strptime(value_decoded, self.fmt_ms)
3480 "invalid GeneralizedTime (with ms) format",
3485 "invalid GeneralizedTime length",
3486 klass=self.__class__,
3488 raise InvalidValueType((self.__class__, datetime))
3490 def todatetime(self):
3491 value = self._value.decode("ascii")
3492 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3493 return datetime.strptime(value, self.fmt)
3494 return datetime.strptime(value, self.fmt_ms)
3497 class GraphicString(CommonString):
3499 tag_default = tag_encode(25)
3500 encoding = "iso-8859-1"
3501 asn1_type_name = "GraphicString"
3504 class VisibleString(CommonString):
3506 tag_default = tag_encode(26)
3508 asn1_type_name = "VisibleString"
3511 class ISO646String(VisibleString):
3513 asn1_type_name = "ISO646String"
3516 class GeneralString(CommonString):
3518 tag_default = tag_encode(27)
3519 encoding = "iso-8859-1"
3520 asn1_type_name = "GeneralString"
3523 class UniversalString(CommonString):
3525 tag_default = tag_encode(28)
3526 encoding = "utf-32-be"
3527 asn1_type_name = "UniversalString"
3530 class BMPString(CommonString):
3532 tag_default = tag_encode(30)
3533 encoding = "utf-16-be"
3534 asn1_type_name = "BMPString"
3538 """``CHOICE`` special type
3542 class GeneralName(Choice):
3544 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3545 ("dNSName", IA5String(impl=tag_ctxp(2))),
3548 >>> gn = GeneralName()
3550 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3551 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3552 >>> gn["dNSName"] = IA5String("bar.baz")
3553 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3554 >>> gn["rfc822Name"]
3557 [2] IA5String IA5 bar.baz
3560 >>> gn.value == gn["dNSName"]
3563 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3565 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3566 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3568 __slots__ = ("specs",)
3570 asn1_type_name = "CHOICE"
3583 :param value: set the value. Either ``(choice, value)`` tuple, or
3584 :py:class:`pyderasn.Choice` object
3585 :param bytes impl: can not be set, do **not** use it
3586 :param bytes expl: override default tag with ``EXPLICIT`` one
3587 :param default: set default value. Type same as in ``value``
3588 :param bool optional: is object ``OPTIONAL`` in sequence
3590 if impl is not None:
3591 raise ValueError("no implicit tag allowed for CHOICE")
3592 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3594 schema = getattr(self, "schema", ())
3595 if len(schema) == 0:
3596 raise ValueError("schema must be specified")
3598 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3601 if value is not None:
3602 self._value = self._value_sanitize(value)
3603 if default is not None:
3604 default_value = self._value_sanitize(default)
3605 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3606 default_obj.specs = self.specs
3607 default_obj._value = default_value
3608 self.default = default_obj
3610 self._value = default_obj.copy()._value
3612 def _value_sanitize(self, value):
3613 if isinstance(value, self.__class__):
3615 if isinstance(value, tuple) and len(value) == 2:
3617 spec = self.specs.get(choice)
3619 raise ObjUnknown(choice)
3620 if not isinstance(obj, spec.__class__):
3621 raise InvalidValueType((spec,))
3622 return (choice, spec(obj))
3623 raise InvalidValueType((self.__class__, tuple))
3627 return self._value is not None and self._value[1].ready
3630 obj = self.__class__(schema=self.specs)
3631 obj._expl = self._expl
3632 obj.default = self.default
3633 obj.optional = self.optional
3634 obj.offset = self.offset
3635 obj.llen = self.llen
3636 obj.vlen = self.vlen
3638 if value is not None:
3639 obj._value = (value[0], value[1].copy())
3642 def __eq__(self, their):
3643 if isinstance(their, tuple) and len(their) == 2:
3644 return self._value == their
3645 if not isinstance(their, self.__class__):
3648 self.specs == their.specs and
3649 self._value == their._value
3659 return self.__class__(
3662 expl=self._expl if expl is None else expl,
3663 default=self.default if default is None else default,
3664 optional=self.optional if optional is None else optional,
3669 self._assert_ready()
3670 return self._value[0]
3674 self._assert_ready()
3675 return self._value[1]
3677 def __getitem__(self, key):
3678 if key not in self.specs:
3679 raise ObjUnknown(key)
3680 if self._value is None:
3682 choice, value = self._value
3687 def __setitem__(self, key, value):
3688 spec = self.specs.get(key)
3690 raise ObjUnknown(key)
3691 if not isinstance(value, spec.__class__):
3692 raise InvalidValueType((spec.__class__,))
3693 self._value = (key, spec(value))
3701 return self._value[1].decoded if self.ready else False
3704 self._assert_ready()
3705 return self._value[1].encode()
3707 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3708 for choice, spec in self.specs.items():
3709 sub_decode_path = decode_path + (choice,)
3715 decode_path=sub_decode_path,
3724 klass=self.__class__,
3725 decode_path=decode_path,
3730 value, tail = spec.decode(
3734 decode_path=sub_decode_path,
3737 obj = self.__class__(
3740 default=self.default,
3741 optional=self.optional,
3742 _decoded=(offset, 0, value.tlvlen),
3744 obj._value = (choice, value)
3748 value = pp_console_row(next(self.pps()))
3750 value = "%s[%r]" % (value, self.value)
3753 def pps(self, decode_path=()):
3755 asn1_type_name=self.asn1_type_name,
3756 obj_name=self.__class__.__name__,
3757 decode_path=decode_path,
3758 value=self.choice if self.ready else None,
3759 optional=self.optional,
3760 default=self == self.default,
3761 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3762 expl=None if self._expl is None else tag_decode(self._expl),
3769 yield self.value.pps(decode_path=decode_path + (self.choice,))
3772 class PrimitiveTypes(Choice):
3773 """Predefined ``CHOICE`` for all generic primitive types
3775 It could be useful for general decoding of some unspecified values:
3777 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3778 OCTET STRING 3 bytes 666f6f
3779 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3783 schema = tuple((klass.__name__, klass()) for klass in (
3808 """``ANY`` special type
3810 >>> Any(Integer(-123))
3812 >>> a = Any(OctetString(b"hello world").encode())
3813 ANY 040b68656c6c6f20776f726c64
3814 >>> hexenc(bytes(a))
3815 b'0x040x0bhello world'
3817 __slots__ = ("defined",)
3818 tag_default = tag_encode(0)
3819 asn1_type_name = "ANY"
3829 :param value: set the value. Either any kind of pyderasn's
3830 **ready** object, or bytes. Pay attention that
3831 **no** validation is performed is raw binary value
3833 :param bytes expl: override default tag with ``EXPLICIT`` one
3834 :param bool optional: is object ``OPTIONAL`` in sequence
3836 super(Any, self).__init__(None, expl, None, optional, _decoded)
3837 self._value = None if value is None else self._value_sanitize(value)
3840 def _value_sanitize(self, value):
3841 if isinstance(value, self.__class__):
3843 if isinstance(value, Obj):
3844 return value.encode()
3845 if isinstance(value, binary_type):
3847 raise InvalidValueType((self.__class__, Obj, binary_type))
3851 return self._value is not None
3854 obj = self.__class__()
3855 obj._value = self._value
3857 obj._expl = self._expl
3858 obj.optional = self.optional
3859 obj.offset = self.offset
3860 obj.llen = self.llen
3861 obj.vlen = self.vlen
3864 def __eq__(self, their):
3865 if isinstance(their, binary_type):
3866 return self._value == their
3867 if issubclass(their.__class__, Any):
3868 return self._value == their._value
3877 return self.__class__(
3879 expl=self._expl if expl is None else expl,
3880 optional=self.optional if optional is None else optional,
3883 def __bytes__(self):
3884 self._assert_ready()
3892 self._assert_ready()
3895 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3897 t, tlen, lv = tag_strip(tlv)
3898 except DecodeError as err:
3899 raise err.__class__(
3901 klass=self.__class__,
3902 decode_path=decode_path,
3906 l, llen, v = len_decode(lv)
3907 except LenIndefForm as err:
3908 if not ctx.get("bered", False):
3909 raise err.__class__(
3911 klass=self.__class__,
3912 decode_path=decode_path,
3915 llen, vlen, v = 1, 0, lv[1:]
3916 sub_offset = offset + tlen + llen
3919 if v[:EOC_LEN].tobytes() == EOC:
3920 tlvlen = tlen + llen + vlen + EOC_LEN
3921 obj = self.__class__(
3922 value=tlv[:tlvlen].tobytes(),
3924 optional=self.optional,
3925 _decoded=(offset, 0, tlvlen),
3929 return obj, v[EOC_LEN:]
3931 chunk, v = Any().decode(
3934 decode_path=decode_path + (str(chunk_i),),
3938 vlen += chunk.tlvlen
3939 sub_offset += chunk.tlvlen
3941 except DecodeError as err:
3942 raise err.__class__(
3944 klass=self.__class__,
3945 decode_path=decode_path,
3949 raise NotEnoughData(
3950 "encoded length is longer than data",
3951 klass=self.__class__,
3952 decode_path=decode_path,
3955 tlvlen = tlen + llen + l
3956 v, tail = tlv[:tlvlen], v[l:]
3957 obj = self.__class__(
3960 optional=self.optional,
3961 _decoded=(offset, 0, tlvlen),
3967 return pp_console_row(next(self.pps()))
3969 def pps(self, decode_path=()):
3971 asn1_type_name=self.asn1_type_name,
3972 obj_name=self.__class__.__name__,
3973 decode_path=decode_path,
3974 blob=self._value if self.ready else None,
3975 optional=self.optional,
3976 default=self == self.default,
3977 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3978 expl=None if self._expl is None else tag_decode(self._expl),
3983 expl_offset=self.expl_offset if self.expled else None,
3984 expl_tlen=self.expl_tlen if self.expled else None,
3985 expl_llen=self.expl_llen if self.expled else None,
3986 expl_vlen=self.expl_vlen if self.expled else None,
3988 defined_by, defined = self.defined or (None, None)
3989 if defined_by is not None:
3991 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3995 ########################################################################
3996 # ASN.1 constructed types
3997 ########################################################################
3999 def get_def_by_path(defines_by_path, sub_decode_path):
4000 """Get define by decode path
4002 for path, define in defines_by_path:
4003 if len(path) != len(sub_decode_path):
4005 for p1, p2 in zip(path, sub_decode_path):
4006 if (p1 != any) and (p1 != p2):
4012 def abs_decode_path(decode_path, rel_path):
4013 """Create an absolute decode path from current and relative ones
4015 :param decode_path: current decode path, starting point.
4017 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4018 If first tuple's element is "/", then treat it as
4019 an absolute path, ignoring ``decode_path`` as
4020 starting point. Also this tuple can contain ".."
4021 elements, stripping the leading element from
4024 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4025 ("foo", "bar", "baz", "whatever")
4026 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4028 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4031 if rel_path[0] == "/":
4033 if rel_path[0] == "..":
4034 return abs_decode_path(decode_path[:-1], rel_path[1:])
4035 return decode_path + rel_path
4038 class Sequence(Obj):
4039 """``SEQUENCE`` structure type
4041 You have to make specification of sequence::
4043 class Extension(Sequence):
4045 ("extnID", ObjectIdentifier()),
4046 ("critical", Boolean(default=False)),
4047 ("extnValue", OctetString()),
4050 Then, you can work with it as with dictionary.
4052 >>> ext = Extension()
4053 >>> Extension().specs
4055 ('extnID', OBJECT IDENTIFIER),
4056 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4057 ('extnValue', OCTET STRING),
4059 >>> ext["extnID"] = "1.2.3"
4060 Traceback (most recent call last):
4061 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4062 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4064 You can determine if sequence is ready to be encoded:
4069 Traceback (most recent call last):
4070 pyderasn.ObjNotReady: object is not ready: extnValue
4071 >>> ext["extnValue"] = OctetString(b"foobar")
4075 Value you want to assign, must have the same **type** as in
4076 corresponding specification, but it can have different tags,
4077 optional/default attributes -- they will be taken from specification
4080 class TBSCertificate(Sequence):
4082 ("version", Version(expl=tag_ctxc(0), default="v1")),
4085 >>> tbs = TBSCertificate()
4086 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4088 Assign ``None`` to remove value from sequence.
4090 You can set values in Sequence during its initialization:
4092 >>> AlgorithmIdentifier((
4093 ("algorithm", ObjectIdentifier("1.2.3")),
4094 ("parameters", Any(Null()))
4096 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4098 You can determine if value exists/set in the sequence and take its value:
4100 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4103 OBJECT IDENTIFIER 1.2.3
4105 But pay attention that if value has default, then it won't be (not
4106 in) in the sequence (because ``DEFAULT`` must not be encoded in
4107 DER), but you can read its value:
4109 >>> "critical" in ext, ext["critical"]
4110 (False, BOOLEAN False)
4111 >>> ext["critical"] = Boolean(True)
4112 >>> "critical" in ext, ext["critical"]
4113 (True, BOOLEAN True)
4115 All defaulted values are always optional.
4117 .. _strict_default_existence_ctx:
4121 When decoded DER contains defaulted value inside, then
4122 technically this is not valid DER encoding. But we allow and pass
4123 it **by default**. Of course reencoding of that kind of DER will
4124 result in different binary representation (validly without
4125 defaulted value inside). You can enable strict defaulted values
4126 existence validation by setting ``"strict_default_existence":
4127 True`` :ref:`context <ctx>` option -- decoding process will raise
4128 an exception if defaulted value is met.
4130 Two sequences are equal if they have equal specification (schema),
4131 implicit/explicit tagging and the same values.
4133 __slots__ = ("specs",)
4134 tag_default = tag_encode(form=TagFormConstructed, num=16)
4135 asn1_type_name = "SEQUENCE"
4147 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4149 schema = getattr(self, "schema", ())
4151 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4154 if value is not None:
4155 if issubclass(value.__class__, Sequence):
4156 self._value = value._value
4157 elif hasattr(value, "__iter__"):
4158 for seq_key, seq_value in value:
4159 self[seq_key] = seq_value
4161 raise InvalidValueType((Sequence,))
4162 if default is not None:
4163 if not issubclass(default.__class__, Sequence):
4164 raise InvalidValueType((Sequence,))
4165 default_value = default._value
4166 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4167 default_obj.specs = self.specs
4168 default_obj._value = default_value
4169 self.default = default_obj
4171 self._value = default_obj.copy()._value
4175 for name, spec in self.specs.items():
4176 value = self._value.get(name)
4187 obj = self.__class__(schema=self.specs)
4189 obj._expl = self._expl
4190 obj.default = self.default
4191 obj.optional = self.optional
4192 obj.offset = self.offset
4193 obj.llen = self.llen
4194 obj.vlen = self.vlen
4195 obj._value = {k: v.copy() for k, v in self._value.items()}
4198 def __eq__(self, their):
4199 if not isinstance(their, self.__class__):
4202 self.specs == their.specs and
4203 self.tag == their.tag and
4204 self._expl == their._expl and
4205 self._value == their._value
4216 return self.__class__(
4219 impl=self.tag if impl is None else impl,
4220 expl=self._expl if expl is None else expl,
4221 default=self.default if default is None else default,
4222 optional=self.optional if optional is None else optional,
4225 def __contains__(self, key):
4226 return key in self._value
4228 def __setitem__(self, key, value):
4229 spec = self.specs.get(key)
4231 raise ObjUnknown(key)
4233 self._value.pop(key, None)
4235 if not isinstance(value, spec.__class__):
4236 raise InvalidValueType((spec.__class__,))
4237 value = spec(value=value)
4238 if spec.default is not None and value == spec.default:
4239 self._value.pop(key, None)
4241 self._value[key] = value
4243 def __getitem__(self, key):
4244 value = self._value.get(key)
4245 if value is not None:
4247 spec = self.specs.get(key)
4249 raise ObjUnknown(key)
4250 if spec.default is not None:
4254 def _encoded_values(self):
4256 for name, spec in self.specs.items():
4257 value = self._value.get(name)
4261 raise ObjNotReady(name)
4262 raws.append(value.encode())
4266 v = b"".join(self._encoded_values())
4267 return b"".join((self.tag, len_encode(len(v)), v))
4269 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4271 t, tlen, lv = tag_strip(tlv)
4272 except DecodeError as err:
4273 raise err.__class__(
4275 klass=self.__class__,
4276 decode_path=decode_path,
4281 klass=self.__class__,
4282 decode_path=decode_path,
4289 l, llen, v = len_decode(lv)
4290 except LenIndefForm as err:
4291 if not ctx.get("bered", False):
4292 raise err.__class__(
4294 klass=self.__class__,
4295 decode_path=decode_path,
4298 l, llen, v = 0, 1, lv[1:]
4300 except DecodeError as err:
4301 raise err.__class__(
4303 klass=self.__class__,
4304 decode_path=decode_path,
4308 raise NotEnoughData(
4309 "encoded length is longer than data",
4310 klass=self.__class__,
4311 decode_path=decode_path,
4315 v, tail = v[:l], v[l:]
4317 sub_offset = offset + tlen + llen
4319 for name, spec in self.specs.items():
4320 if spec.optional and (
4321 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4325 sub_decode_path = decode_path + (name,)
4327 value, v_tail = spec.decode(
4331 decode_path=sub_decode_path,
4339 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4340 if defined is not None:
4341 defined_by, defined_spec = defined
4342 if issubclass(value.__class__, SequenceOf):
4343 for i, _value in enumerate(value):
4344 sub_sub_decode_path = sub_decode_path + (
4346 DecodePathDefBy(defined_by),
4348 defined_value, defined_tail = defined_spec.decode(
4349 memoryview(bytes(_value)),
4351 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4352 if value.expled else (value.tlen + value.llen)
4355 decode_path=sub_sub_decode_path,
4358 if len(defined_tail) > 0:
4361 klass=self.__class__,
4362 decode_path=sub_sub_decode_path,
4365 _value.defined = (defined_by, defined_value)
4367 defined_value, defined_tail = defined_spec.decode(
4368 memoryview(bytes(value)),
4370 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4371 if value.expled else (value.tlen + value.llen)
4374 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4377 if len(defined_tail) > 0:
4380 klass=self.__class__,
4381 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4384 value.defined = (defined_by, defined_value)
4386 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4388 sub_offset += value_len
4390 if spec.default is not None and value == spec.default:
4391 if ctx.get("strict_default_existence", False):
4393 "DEFAULT value met",
4394 klass=self.__class__,
4395 decode_path=sub_decode_path,
4400 values[name] = value
4402 spec_defines = getattr(spec, "defines", ())
4403 if len(spec_defines) == 0:
4404 defines_by_path = ctx.get("defines_by_path", ())
4405 if len(defines_by_path) > 0:
4406 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4407 if spec_defines is not None and len(spec_defines) > 0:
4408 for rel_path, schema in spec_defines:
4409 defined = schema.get(value, None)
4410 if defined is not None:
4411 ctx.setdefault("defines", []).append((
4412 abs_decode_path(sub_decode_path[:-1], rel_path),
4416 if v[:EOC_LEN].tobytes() != EOC:
4419 klass=self.__class__,
4420 decode_path=decode_path,
4428 klass=self.__class__,
4429 decode_path=decode_path,
4432 obj = self.__class__(
4436 default=self.default,
4437 optional=self.optional,
4438 _decoded=(offset, llen, vlen),
4441 obj.lenindef = lenindef
4445 value = pp_console_row(next(self.pps()))
4447 for name in self.specs:
4448 _value = self._value.get(name)
4451 cols.append(repr(_value))
4452 return "%s[%s]" % (value, ", ".join(cols))
4454 def pps(self, decode_path=()):
4456 asn1_type_name=self.asn1_type_name,
4457 obj_name=self.__class__.__name__,
4458 decode_path=decode_path,
4459 optional=self.optional,
4460 default=self == self.default,
4461 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4462 expl=None if self._expl is None else tag_decode(self._expl),
4467 expl_offset=self.expl_offset if self.expled else None,
4468 expl_tlen=self.expl_tlen if self.expled else None,
4469 expl_llen=self.expl_llen if self.expled else None,
4470 expl_vlen=self.expl_vlen if self.expled else None,
4472 for name in self.specs:
4473 value = self._value.get(name)
4476 yield value.pps(decode_path=decode_path + (name,))
4479 class Set(Sequence):
4480 """``SET`` structure type
4482 Its usage is identical to :py:class:`pyderasn.Sequence`.
4485 tag_default = tag_encode(form=TagFormConstructed, num=17)
4486 asn1_type_name = "SET"
4489 raws = self._encoded_values()
4492 return b"".join((self.tag, len_encode(len(v)), v))
4494 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4496 t, tlen, lv = tag_strip(tlv)
4497 except DecodeError as err:
4498 raise err.__class__(
4500 klass=self.__class__,
4501 decode_path=decode_path,
4506 klass=self.__class__,
4507 decode_path=decode_path,
4514 l, llen, v = len_decode(lv)
4515 except LenIndefForm as err:
4516 if not ctx.get("bered", False):
4517 raise err.__class__(
4519 klass=self.__class__,
4520 decode_path=decode_path,
4523 l, llen, v = 0, 1, lv[1:]
4525 except DecodeError as err:
4526 raise err.__class__(
4528 klass=self.__class__,
4529 decode_path=decode_path,
4533 raise NotEnoughData(
4534 "encoded length is longer than data",
4535 klass=self.__class__,
4539 v, tail = v[:l], v[l:]
4541 sub_offset = offset + tlen + llen
4543 specs_items = self.specs.items
4545 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4547 for name, spec in specs_items():
4548 sub_decode_path = decode_path + (name,)
4554 decode_path=sub_decode_path,
4563 klass=self.__class__,
4564 decode_path=decode_path,
4567 value, v_tail = spec.decode(
4571 decode_path=sub_decode_path,
4574 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4575 sub_offset += value_len
4578 if spec.default is None or value != spec.default: # pragma: no cover
4579 # SeqMixing.test_encoded_default_accepted covers that place
4580 values[name] = value
4581 obj = self.__class__(
4585 default=self.default,
4586 optional=self.optional,
4587 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4592 msg="not all values are ready",
4593 klass=self.__class__,
4594 decode_path=decode_path,
4597 obj.lenindef = lenindef
4598 return obj, (v[EOC_LEN:] if lenindef else tail)
4601 class SequenceOf(Obj):
4602 """``SEQUENCE OF`` sequence type
4604 For that kind of type you must specify the object it will carry on
4605 (bounds are for example here, not required)::
4607 class Ints(SequenceOf):
4612 >>> ints.append(Integer(123))
4613 >>> ints.append(Integer(234))
4615 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4616 >>> [int(i) for i in ints]
4618 >>> ints.append(Integer(345))
4619 Traceback (most recent call last):
4620 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4623 >>> ints[1] = Integer(345)
4625 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4627 Also you can initialize sequence with preinitialized values:
4629 >>> ints = Ints([Integer(123), Integer(234)])
4631 __slots__ = ("spec", "_bound_min", "_bound_max")
4632 tag_default = tag_encode(form=TagFormConstructed, num=16)
4633 asn1_type_name = "SEQUENCE OF"
4646 super(SequenceOf, self).__init__(
4654 schema = getattr(self, "schema", None)
4656 raise ValueError("schema must be specified")
4658 self._bound_min, self._bound_max = getattr(
4662 ) if bounds is None else bounds
4664 if value is not None:
4665 self._value = self._value_sanitize(value)
4666 if default is not None:
4667 default_value = self._value_sanitize(default)
4668 default_obj = self.__class__(
4673 default_obj._value = default_value
4674 self.default = default_obj
4676 self._value = default_obj.copy()._value
4678 def _value_sanitize(self, value):
4679 if issubclass(value.__class__, SequenceOf):
4680 value = value._value
4681 elif hasattr(value, "__iter__"):
4684 raise InvalidValueType((self.__class__, iter))
4685 if not self._bound_min <= len(value) <= self._bound_max:
4686 raise BoundsError(self._bound_min, len(value), self._bound_max)
4688 if not isinstance(v, self.spec.__class__):
4689 raise InvalidValueType((self.spec.__class__,))
4694 return all(v.ready for v in self._value)
4697 obj = self.__class__(schema=self.spec)
4698 obj._bound_min = self._bound_min
4699 obj._bound_max = self._bound_max
4701 obj._expl = self._expl
4702 obj.default = self.default
4703 obj.optional = self.optional
4704 obj.offset = self.offset
4705 obj.llen = self.llen
4706 obj.vlen = self.vlen
4707 obj._value = [v.copy() for v in self._value]
4710 def __eq__(self, their):
4711 if isinstance(their, self.__class__):
4713 self.spec == their.spec and
4714 self.tag == their.tag and
4715 self._expl == their._expl and
4716 self._value == their._value
4718 if hasattr(their, "__iter__"):
4719 return self._value == list(their)
4731 return self.__class__(
4735 (self._bound_min, self._bound_max)
4736 if bounds is None else bounds
4738 impl=self.tag if impl is None else impl,
4739 expl=self._expl if expl is None else expl,
4740 default=self.default if default is None else default,
4741 optional=self.optional if optional is None else optional,
4744 def __contains__(self, key):
4745 return key in self._value
4747 def append(self, value):
4748 if not isinstance(value, self.spec.__class__):
4749 raise InvalidValueType((self.spec.__class__,))
4750 if len(self._value) + 1 > self._bound_max:
4753 len(self._value) + 1,
4756 self._value.append(value)
4759 self._assert_ready()
4760 return iter(self._value)
4763 self._assert_ready()
4764 return len(self._value)
4766 def __setitem__(self, key, value):
4767 if not isinstance(value, self.spec.__class__):
4768 raise InvalidValueType((self.spec.__class__,))
4769 self._value[key] = self.spec(value=value)
4771 def __getitem__(self, key):
4772 return self._value[key]
4774 def _encoded_values(self):
4775 return [v.encode() for v in self._value]
4778 v = b"".join(self._encoded_values())
4779 return b"".join((self.tag, len_encode(len(v)), v))
4781 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4783 t, tlen, lv = tag_strip(tlv)
4784 except DecodeError as err:
4785 raise err.__class__(
4787 klass=self.__class__,
4788 decode_path=decode_path,
4793 klass=self.__class__,
4794 decode_path=decode_path,
4801 l, llen, v = len_decode(lv)
4802 except LenIndefForm as err:
4803 if not ctx.get("bered", False):
4804 raise err.__class__(
4806 klass=self.__class__,
4807 decode_path=decode_path,
4810 l, llen, v = 0, 1, lv[1:]
4812 except DecodeError as err:
4813 raise err.__class__(
4815 klass=self.__class__,
4816 decode_path=decode_path,
4820 raise NotEnoughData(
4821 "encoded length is longer than data",
4822 klass=self.__class__,
4823 decode_path=decode_path,
4827 v, tail = v[:l], v[l:]
4829 sub_offset = offset + tlen + llen
4833 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4835 value, v_tail = spec.decode(
4839 decode_path=decode_path + (str(len(_value)),),
4842 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4843 sub_offset += value_len
4846 _value.append(value)
4847 obj = self.__class__(
4850 bounds=(self._bound_min, self._bound_max),
4853 default=self.default,
4854 optional=self.optional,
4855 _decoded=(offset, llen, vlen),
4857 obj.lenindef = lenindef
4858 return obj, (v[EOC_LEN:] if lenindef else tail)
4862 pp_console_row(next(self.pps())),
4863 ", ".join(repr(v) for v in self._value),
4866 def pps(self, decode_path=()):
4868 asn1_type_name=self.asn1_type_name,
4869 obj_name=self.__class__.__name__,
4870 decode_path=decode_path,
4871 optional=self.optional,
4872 default=self == self.default,
4873 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4874 expl=None if self._expl is None else tag_decode(self._expl),
4879 expl_offset=self.expl_offset if self.expled else None,
4880 expl_tlen=self.expl_tlen if self.expled else None,
4881 expl_llen=self.expl_llen if self.expled else None,
4882 expl_vlen=self.expl_vlen if self.expled else None,
4884 for i, value in enumerate(self._value):
4885 yield value.pps(decode_path=decode_path + (str(i),))
4888 class SetOf(SequenceOf):
4889 """``SET OF`` sequence type
4891 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4894 tag_default = tag_encode(form=TagFormConstructed, num=17)
4895 asn1_type_name = "SET OF"
4898 raws = self._encoded_values()
4901 return b"".join((self.tag, len_encode(len(v)), v))
4904 def obj_by_path(pypath): # pragma: no cover
4905 """Import object specified as string Python path
4907 Modules must be separated from classes/functions with ``:``.
4909 >>> obj_by_path("foo.bar:Baz")
4910 <class 'foo.bar.Baz'>
4911 >>> obj_by_path("foo.bar:Baz.boo")
4912 <classmethod 'foo.bar.Baz.boo'>
4914 mod, objs = pypath.rsplit(":", 1)
4915 from importlib import import_module
4916 obj = import_module(mod)
4917 for obj_name in objs.split("."):
4918 obj = getattr(obj, obj_name)
4922 def generic_decoder(): # pragma: no cover
4923 # All of this below is a big hack with self references
4924 choice = PrimitiveTypes()
4925 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4926 choice.specs["SetOf"] = SetOf(schema=choice)
4928 choice.specs["SequenceOf%d" % i] = SequenceOf(
4932 choice.specs["Any"] = Any()
4934 # Class name equals to type name, to omit it from output
4935 class SEQUENCEOF(SequenceOf):
4939 def pprint_any(obj, oids=None, with_colours=False):
4940 def _pprint_pps(pps):
4942 if hasattr(pp, "_fields"):
4943 if pp.asn1_type_name == Choice.asn1_type_name:
4945 pp_kwargs = pp._asdict()
4946 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4947 pp = _pp(**pp_kwargs)
4948 yield pp_console_row(
4953 with_colours=with_colours,
4955 for row in pp_console_blob(pp):
4958 for row in _pprint_pps(pp):
4960 return "\n".join(_pprint_pps(obj.pps()))
4961 return SEQUENCEOF(), pprint_any
4964 def main(): # pragma: no cover
4966 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4967 parser.add_argument(
4971 help="Skip that number of bytes from the beginning",
4973 parser.add_argument(
4975 help="Python path to dictionary with OIDs",
4977 parser.add_argument(
4979 help="Python path to schema definition to use",
4981 parser.add_argument(
4982 "--defines-by-path",
4983 help="Python path to decoder's defines_by_path",
4985 parser.add_argument(
4987 type=argparse.FileType("rb"),
4988 help="Path to DER file you want to decode",
4990 args = parser.parse_args()
4991 args.DERFile.seek(args.skip)
4992 der = memoryview(args.DERFile.read())
4993 args.DERFile.close()
4994 oids = obj_by_path(args.oids) if args.oids else {}
4996 schema = obj_by_path(args.schema)
4997 from functools import partial
4998 pprinter = partial(pprint, big_blobs=True)
5000 schema, pprinter = generic_decoder()
5001 ctx = {"bered": True}
5002 if args.defines_by_path is not None:
5003 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5004 obj, tail = schema().decode(der, ctx=ctx)
5008 with_colours=True if environ.get("NO_COLOR") is None else False,
5011 print("\nTrailing data: %s" % hexenc(tail))
5014 if __name__ == "__main__":