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.expl_lenindef = False
882 self.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", (
1133 asn1_type_name="unknown",
1150 expl_lenindef=False,
1178 def _colorize(what, colour, with_colours, attrs=("bold",)):
1179 return colored(what, colour, attrs=attrs) if with_colours else what
1194 " " if pp.expl_offset is None else
1195 ("-%d" % (pp.offset - pp.expl_offset))
1198 cols.append(_colorize(col, "red", with_colours, ()))
1199 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1200 col = _colorize(col, "green", with_colours, ())
1202 if pp.expl_lenindef:
1207 " " if ber_deoffset == 0 else
1208 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1211 if len(pp.decode_path) > 0:
1212 cols.append(" ." * (len(pp.decode_path)))
1213 ent = pp.decode_path[-1]
1214 if isinstance(ent, DecodePathDefBy):
1215 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1216 value = str(ent.defined_by)
1218 oids is not None and
1219 ent.defined_by.asn1_type_name ==
1220 ObjectIdentifier.asn1_type_name and
1223 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1225 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1227 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1228 if pp.expl is not None:
1229 klass, _, num = pp.expl
1230 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1231 cols.append(_colorize(col, "blue", with_colours))
1232 if pp.impl is not None:
1233 klass, _, num = pp.impl
1234 col = "[%s%d]" % (TagClassReprs[klass], num)
1235 cols.append(_colorize(col, "blue", with_colours))
1236 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1237 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1239 cols.append(_colorize("BER", "red", with_colours))
1240 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1241 if pp.value is not None:
1243 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1245 oids is not None and
1246 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1249 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1251 if isinstance(pp.blob, binary_type):
1252 cols.append(hexenc(pp.blob))
1253 elif isinstance(pp.blob, tuple):
1254 cols.append(", ".join(pp.blob))
1256 cols.append(_colorize("OPTIONAL", "red", with_colours))
1258 cols.append(_colorize("DEFAULT", "red", with_colours))
1259 return " ".join(cols)
1262 def pp_console_blob(pp):
1263 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1264 if len(pp.decode_path) > 0:
1265 cols.append(" ." * (len(pp.decode_path) + 1))
1266 if isinstance(pp.blob, binary_type):
1267 blob = hexenc(pp.blob).upper()
1268 for i in range(0, len(blob), 32):
1269 chunk = blob[i:i + 32]
1270 yield " ".join(cols + [":".join(
1271 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1273 elif isinstance(pp.blob, tuple):
1274 yield " ".join(cols + [", ".join(pp.blob)])
1277 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1278 """Pretty print object
1280 :param Obj obj: object you want to pretty print
1281 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1282 from it is met, then its humand readable form is printed
1283 :param big_blobs: if large binary objects are met (like OctetString
1284 values), do we need to print them too, on separate
1286 :param with_colours: colourize output, if ``termcolor`` library
1289 def _pprint_pps(pps):
1291 if hasattr(pp, "_fields"):
1293 yield pp_console_row(
1298 with_colours=with_colours,
1300 for row in pp_console_blob(pp):
1303 yield pp_console_row(
1308 with_colours=with_colours,
1311 for row in _pprint_pps(pp):
1313 return "\n".join(_pprint_pps(obj.pps()))
1316 ########################################################################
1317 # ASN.1 primitive types
1318 ########################################################################
1321 """``BOOLEAN`` boolean type
1323 >>> b = Boolean(True)
1325 >>> b == Boolean(True)
1331 tag_default = tag_encode(1)
1332 asn1_type_name = "BOOLEAN"
1344 :param value: set the value. Either boolean type, or
1345 :py:class:`pyderasn.Boolean` object
1346 :param bytes impl: override default tag with ``IMPLICIT`` one
1347 :param bytes expl: override default tag with ``EXPLICIT`` one
1348 :param default: set default value. Type same as in ``value``
1349 :param bool optional: is object ``OPTIONAL`` in sequence
1351 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1352 self._value = None if value is None else self._value_sanitize(value)
1353 if default is not None:
1354 default = self._value_sanitize(default)
1355 self.default = self.__class__(
1361 self._value = default
1363 def _value_sanitize(self, value):
1364 if issubclass(value.__class__, Boolean):
1366 if isinstance(value, bool):
1368 raise InvalidValueType((self.__class__, bool))
1372 return self._value is not None
1375 obj = self.__class__()
1376 obj._value = self._value
1378 obj._expl = self._expl
1379 obj.default = self.default
1380 obj.optional = self.optional
1381 obj.offset = self.offset
1382 obj.llen = self.llen
1383 obj.vlen = self.vlen
1386 def __nonzero__(self):
1387 self._assert_ready()
1391 self._assert_ready()
1394 def __eq__(self, their):
1395 if isinstance(their, bool):
1396 return self._value == their
1397 if not issubclass(their.__class__, Boolean):
1400 self._value == their._value and
1401 self.tag == their.tag and
1402 self._expl == their._expl
1413 return self.__class__(
1415 impl=self.tag if impl is None else impl,
1416 expl=self._expl if expl is None else expl,
1417 default=self.default if default is None else default,
1418 optional=self.optional if optional is None else optional,
1422 self._assert_ready()
1426 (b"\xFF" if self._value else b"\x00"),
1429 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1431 t, _, lv = tag_strip(tlv)
1432 except DecodeError as err:
1433 raise err.__class__(
1435 klass=self.__class__,
1436 decode_path=decode_path,
1441 klass=self.__class__,
1442 decode_path=decode_path,
1448 l, _, v = len_decode(lv)
1449 except DecodeError as err:
1450 raise err.__class__(
1452 klass=self.__class__,
1453 decode_path=decode_path,
1457 raise InvalidLength(
1458 "Boolean's length must be equal to 1",
1459 klass=self.__class__,
1460 decode_path=decode_path,
1464 raise NotEnoughData(
1465 "encoded length is longer than data",
1466 klass=self.__class__,
1467 decode_path=decode_path,
1470 first_octet = byte2int(v)
1472 if first_octet == 0:
1474 elif first_octet == 0xFF:
1476 elif ctx.get("bered", False):
1481 "unacceptable Boolean value",
1482 klass=self.__class__,
1483 decode_path=decode_path,
1486 obj = self.__class__(
1490 default=self.default,
1491 optional=self.optional,
1492 _decoded=(offset, 1, 1),
1498 return pp_console_row(next(self.pps()))
1500 def pps(self, decode_path=()):
1502 asn1_type_name=self.asn1_type_name,
1503 obj_name=self.__class__.__name__,
1504 decode_path=decode_path,
1505 value=str(self._value) if self.ready else None,
1506 optional=self.optional,
1507 default=self == self.default,
1508 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1509 expl=None if self._expl is None else tag_decode(self._expl),
1514 expl_offset=self.expl_offset if self.expled else None,
1515 expl_tlen=self.expl_tlen if self.expled else None,
1516 expl_llen=self.expl_llen if self.expled else None,
1517 expl_vlen=self.expl_vlen if self.expled else None,
1518 expl_lenindef=self.expl_lenindef,
1524 """``INTEGER`` integer type
1526 >>> b = Integer(-123)
1528 >>> b == Integer(-123)
1533 >>> Integer(2, bounds=(1, 3))
1535 >>> Integer(5, bounds=(1, 3))
1536 Traceback (most recent call last):
1537 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1541 class Version(Integer):
1548 >>> v = Version("v1")
1555 {'v3': 2, 'v1': 0, 'v2': 1}
1557 __slots__ = ("specs", "_bound_min", "_bound_max")
1558 tag_default = tag_encode(2)
1559 asn1_type_name = "INTEGER"
1573 :param value: set the value. Either integer type, named value
1574 (if ``schema`` is specified in the class), or
1575 :py:class:`pyderasn.Integer` object
1576 :param bounds: set ``(MIN, MAX)`` value constraint.
1577 (-inf, +inf) by default
1578 :param bytes impl: override default tag with ``IMPLICIT`` one
1579 :param bytes expl: override default tag with ``EXPLICIT`` one
1580 :param default: set default value. Type same as in ``value``
1581 :param bool optional: is object ``OPTIONAL`` in sequence
1583 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1585 specs = getattr(self, "schema", {}) if _specs is None else _specs
1586 self.specs = specs if isinstance(specs, dict) else dict(specs)
1587 self._bound_min, self._bound_max = getattr(
1590 (float("-inf"), float("+inf")),
1591 ) if bounds is None else bounds
1592 if value is not None:
1593 self._value = self._value_sanitize(value)
1594 if default is not None:
1595 default = self._value_sanitize(default)
1596 self.default = self.__class__(
1602 if self._value is None:
1603 self._value = default
1605 def _value_sanitize(self, value):
1606 if issubclass(value.__class__, Integer):
1607 value = value._value
1608 elif isinstance(value, integer_types):
1610 elif isinstance(value, str):
1611 value = self.specs.get(value)
1613 raise ObjUnknown("integer value: %s" % value)
1615 raise InvalidValueType((self.__class__, int, str))
1616 if not self._bound_min <= value <= self._bound_max:
1617 raise BoundsError(self._bound_min, value, self._bound_max)
1622 return self._value is not None
1625 obj = self.__class__(_specs=self.specs)
1626 obj._value = self._value
1627 obj._bound_min = self._bound_min
1628 obj._bound_max = self._bound_max
1630 obj._expl = self._expl
1631 obj.default = self.default
1632 obj.optional = self.optional
1633 obj.offset = self.offset
1634 obj.llen = self.llen
1635 obj.vlen = self.vlen
1639 self._assert_ready()
1640 return int(self._value)
1643 self._assert_ready()
1646 bytes(self._expl or b"") +
1647 str(self._value).encode("ascii"),
1650 def __eq__(self, their):
1651 if isinstance(their, integer_types):
1652 return self._value == their
1653 if not issubclass(their.__class__, Integer):
1656 self._value == their._value and
1657 self.tag == their.tag and
1658 self._expl == their._expl
1661 def __lt__(self, their):
1662 return self._value < their._value
1666 for name, value in self.specs.items():
1667 if value == self._value:
1679 return self.__class__(
1682 (self._bound_min, self._bound_max)
1683 if bounds is None else bounds
1685 impl=self.tag if impl is None else impl,
1686 expl=self._expl if expl is None else expl,
1687 default=self.default if default is None else default,
1688 optional=self.optional if optional is None else optional,
1693 self._assert_ready()
1697 octets = bytearray([0])
1701 octets = bytearray()
1703 octets.append((value & 0xFF) ^ 0xFF)
1705 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1708 octets = bytearray()
1710 octets.append(value & 0xFF)
1712 if octets[-1] & 0x80 > 0:
1715 octets = bytes(octets)
1717 bytes_len = ceil(value.bit_length() / 8) or 1
1720 octets = value.to_bytes(
1725 except OverflowError:
1729 return b"".join((self.tag, len_encode(len(octets)), octets))
1731 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1733 t, _, lv = tag_strip(tlv)
1734 except DecodeError as err:
1735 raise err.__class__(
1737 klass=self.__class__,
1738 decode_path=decode_path,
1743 klass=self.__class__,
1744 decode_path=decode_path,
1750 l, llen, v = len_decode(lv)
1751 except DecodeError as err:
1752 raise err.__class__(
1754 klass=self.__class__,
1755 decode_path=decode_path,
1759 raise NotEnoughData(
1760 "encoded length is longer than data",
1761 klass=self.__class__,
1762 decode_path=decode_path,
1766 raise NotEnoughData(
1768 klass=self.__class__,
1769 decode_path=decode_path,
1772 v, tail = v[:l], v[l:]
1773 first_octet = byte2int(v)
1775 second_octet = byte2int(v[1:])
1777 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1778 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1781 "non normalized integer",
1782 klass=self.__class__,
1783 decode_path=decode_path,
1788 if first_octet & 0x80 > 0:
1789 octets = bytearray()
1790 for octet in bytearray(v):
1791 octets.append(octet ^ 0xFF)
1792 for octet in octets:
1793 value = (value << 8) | octet
1797 for octet in bytearray(v):
1798 value = (value << 8) | octet
1800 value = int.from_bytes(v, byteorder="big", signed=True)
1802 obj = self.__class__(
1804 bounds=(self._bound_min, self._bound_max),
1807 default=self.default,
1808 optional=self.optional,
1810 _decoded=(offset, llen, l),
1812 except BoundsError as err:
1815 klass=self.__class__,
1816 decode_path=decode_path,
1822 return pp_console_row(next(self.pps()))
1824 def pps(self, decode_path=()):
1826 asn1_type_name=self.asn1_type_name,
1827 obj_name=self.__class__.__name__,
1828 decode_path=decode_path,
1829 value=(self.named or str(self._value)) if self.ready else None,
1830 optional=self.optional,
1831 default=self == self.default,
1832 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1833 expl=None if self._expl is None else tag_decode(self._expl),
1838 expl_offset=self.expl_offset if self.expled else None,
1839 expl_tlen=self.expl_tlen if self.expled else None,
1840 expl_llen=self.expl_llen if self.expled else None,
1841 expl_vlen=self.expl_vlen if self.expled else None,
1842 expl_lenindef=self.expl_lenindef,
1846 class BitString(Obj):
1847 """``BIT STRING`` bit string type
1849 >>> BitString(b"hello world")
1850 BIT STRING 88 bits 68656c6c6f20776f726c64
1853 >>> b == b"hello world"
1858 >>> BitString("'0A3B5F291CD'H")
1859 BIT STRING 44 bits 0a3b5f291cd0
1860 >>> b = BitString("'010110000000'B")
1861 BIT STRING 12 bits 5800
1864 >>> b[0], b[1], b[2], b[3]
1865 (False, True, False, True)
1869 [False, True, False, True, True, False, False, False, False, False, False, False]
1873 class KeyUsage(BitString):
1875 ("digitalSignature", 0),
1876 ("nonRepudiation", 1),
1877 ("keyEncipherment", 2),
1880 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1881 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1883 ['nonRepudiation', 'keyEncipherment']
1885 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1887 __slots__ = ("tag_constructed", "specs", "defined")
1888 tag_default = tag_encode(3)
1889 asn1_type_name = "BIT STRING"
1902 :param value: set the value. Either binary type, tuple of named
1903 values (if ``schema`` is specified in the class),
1904 string in ``'XXX...'B`` form, or
1905 :py:class:`pyderasn.BitString` object
1906 :param bytes impl: override default tag with ``IMPLICIT`` one
1907 :param bytes expl: override default tag with ``EXPLICIT`` one
1908 :param default: set default value. Type same as in ``value``
1909 :param bool optional: is object ``OPTIONAL`` in sequence
1911 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1912 specs = getattr(self, "schema", {}) if _specs is None else _specs
1913 self.specs = specs if isinstance(specs, dict) else dict(specs)
1914 self._value = None if value is None else self._value_sanitize(value)
1915 if default is not None:
1916 default = self._value_sanitize(default)
1917 self.default = self.__class__(
1923 self._value = default
1925 tag_klass, _, tag_num = tag_decode(self.tag)
1926 self.tag_constructed = tag_encode(
1928 form=TagFormConstructed,
1932 def _bits2octets(self, bits):
1933 if len(self.specs) > 0:
1934 bits = bits.rstrip("0")
1936 bits += "0" * ((8 - (bit_len % 8)) % 8)
1937 octets = bytearray(len(bits) // 8)
1938 for i in six_xrange(len(octets)):
1939 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1940 return bit_len, bytes(octets)
1942 def _value_sanitize(self, value):
1943 if issubclass(value.__class__, BitString):
1945 if isinstance(value, (string_types, binary_type)):
1947 isinstance(value, string_types) and
1948 value.startswith("'")
1950 if value.endswith("'B"):
1952 if not set(value) <= set(("0", "1")):
1953 raise ValueError("B's coding contains unacceptable chars")
1954 return self._bits2octets(value)
1955 elif value.endswith("'H"):
1959 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
1962 raise InvalidValueType((self.__class__, string_types, binary_type))
1963 elif isinstance(value, binary_type):
1964 return (len(value) * 8, value)
1966 raise InvalidValueType((self.__class__, string_types, binary_type))
1967 if isinstance(value, tuple):
1970 isinstance(value[0], integer_types) and
1971 isinstance(value[1], binary_type)
1976 bit = self.specs.get(name)
1978 raise ObjUnknown("BitString value: %s" % name)
1981 return self._bits2octets("")
1983 return self._bits2octets("".join(
1984 ("1" if bit in bits else "0")
1985 for bit in six_xrange(max(bits) + 1)
1987 raise InvalidValueType((self.__class__, binary_type, string_types))
1991 return self._value is not None
1994 obj = self.__class__(_specs=self.specs)
1996 if value is not None:
1997 value = (value[0], value[1])
2000 obj._expl = self._expl
2001 obj.default = self.default
2002 obj.optional = self.optional
2003 obj.offset = self.offset
2004 obj.llen = self.llen
2005 obj.vlen = self.vlen
2009 self._assert_ready()
2010 for i in six_xrange(self._value[0]):
2015 self._assert_ready()
2016 return self._value[0]
2018 def __bytes__(self):
2019 self._assert_ready()
2020 return self._value[1]
2022 def __eq__(self, their):
2023 if isinstance(their, bytes):
2024 return self._value[1] == their
2025 if not issubclass(their.__class__, BitString):
2028 self._value == their._value and
2029 self.tag == their.tag and
2030 self._expl == their._expl
2035 return [name for name, bit in self.specs.items() if self[bit]]
2045 return self.__class__(
2047 impl=self.tag if impl is None else impl,
2048 expl=self._expl if expl is None else expl,
2049 default=self.default if default is None else default,
2050 optional=self.optional if optional is None else optional,
2054 def __getitem__(self, key):
2055 if isinstance(key, int):
2056 bit_len, octets = self._value
2060 byte2int(memoryview(octets)[key // 8:]) >>
2063 if isinstance(key, string_types):
2064 value = self.specs.get(key)
2066 raise ObjUnknown("BitString value: %s" % key)
2068 raise InvalidValueType((int, str))
2071 self._assert_ready()
2072 bit_len, octets = self._value
2075 len_encode(len(octets) + 1),
2076 int2byte((8 - bit_len % 8) % 8),
2080 def _decode_chunk(self, lv, offset, decode_path, ctx):
2082 l, llen, v = len_decode(lv)
2083 except DecodeError as err:
2084 raise err.__class__(
2086 klass=self.__class__,
2087 decode_path=decode_path,
2091 raise NotEnoughData(
2092 "encoded length is longer than data",
2093 klass=self.__class__,
2094 decode_path=decode_path,
2098 raise NotEnoughData(
2100 klass=self.__class__,
2101 decode_path=decode_path,
2104 pad_size = byte2int(v)
2105 if l == 1 and pad_size != 0:
2107 "invalid empty value",
2108 klass=self.__class__,
2109 decode_path=decode_path,
2115 klass=self.__class__,
2116 decode_path=decode_path,
2119 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2122 klass=self.__class__,
2123 decode_path=decode_path,
2126 v, tail = v[:l], v[l:]
2127 obj = self.__class__(
2128 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2131 default=self.default,
2132 optional=self.optional,
2134 _decoded=(offset, llen, l),
2138 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2140 t, tlen, lv = tag_strip(tlv)
2141 except DecodeError as err:
2142 raise err.__class__(
2144 klass=self.__class__,
2145 decode_path=decode_path,
2151 return self._decode_chunk(lv, offset, decode_path, ctx)
2152 if t == self.tag_constructed:
2153 if not ctx.get("bered", False):
2155 msg="unallowed BER constructed encoding",
2156 decode_path=decode_path,
2163 l, llen, v = len_decode(lv)
2164 except LenIndefForm:
2165 llen, l, v = 1, 0, lv[1:]
2167 except DecodeError as err:
2168 raise err.__class__(
2170 klass=self.__class__,
2171 decode_path=decode_path,
2174 if l > 0 and l > len(v):
2175 raise NotEnoughData(
2176 "encoded length is longer than data",
2177 klass=self.__class__,
2178 decode_path=decode_path,
2181 if not lenindef and l == 0:
2182 raise NotEnoughData(
2184 klass=self.__class__,
2185 decode_path=decode_path,
2189 sub_offset = offset + tlen + llen
2193 if v[:EOC_LEN].tobytes() == EOC:
2200 msg="chunk out of bounds",
2201 decode_path=len(chunks) - 1,
2202 offset=chunks[-1].offset,
2204 sub_decode_path = decode_path + (str(len(chunks)),)
2206 chunk, v_tail = BitString().decode(
2209 decode_path=sub_decode_path,
2215 msg="expected BitString encoded chunk",
2216 decode_path=sub_decode_path,
2219 chunks.append(chunk)
2220 sub_offset += chunk.tlvlen
2221 vlen += chunk.tlvlen
2223 if len(chunks) == 0:
2226 decode_path=decode_path,
2231 for chunk_i, chunk in enumerate(chunks[:-1]):
2232 if chunk.bit_len % 8 != 0:
2234 msg="BitString chunk is not multiple of 8 bit",
2235 decode_path=decode_path + (str(chunk_i),),
2236 offset=chunk.offset,
2238 values.append(bytes(chunk))
2239 bit_len += chunk.bit_len
2240 chunk_last = chunks[-1]
2241 values.append(bytes(chunk_last))
2242 bit_len += chunk_last.bit_len
2243 obj = self.__class__(
2244 value=(bit_len, b"".join(values)),
2247 default=self.default,
2248 optional=self.optional,
2250 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2252 obj.lenindef = lenindef
2254 return obj, (v[EOC_LEN:] if lenindef else v)
2256 klass=self.__class__,
2257 decode_path=decode_path,
2262 return pp_console_row(next(self.pps()))
2264 def pps(self, decode_path=()):
2268 bit_len, blob = self._value
2269 value = "%d bits" % bit_len
2270 if len(self.specs) > 0:
2271 blob = tuple(self.named)
2273 asn1_type_name=self.asn1_type_name,
2274 obj_name=self.__class__.__name__,
2275 decode_path=decode_path,
2278 optional=self.optional,
2279 default=self == self.default,
2280 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2281 expl=None if self._expl is None else tag_decode(self._expl),
2286 expl_offset=self.expl_offset if self.expled else None,
2287 expl_tlen=self.expl_tlen if self.expled else None,
2288 expl_llen=self.expl_llen if self.expled else None,
2289 expl_vlen=self.expl_vlen if self.expled else None,
2290 expl_lenindef=self.expl_lenindef,
2291 lenindef=self.lenindef,
2294 defined_by, defined = self.defined or (None, None)
2295 if defined_by is not None:
2297 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2301 class OctetString(Obj):
2302 """``OCTET STRING`` binary string type
2304 >>> s = OctetString(b"hello world")
2305 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2306 >>> s == OctetString(b"hello world")
2311 >>> OctetString(b"hello", bounds=(4, 4))
2312 Traceback (most recent call last):
2313 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2314 >>> OctetString(b"hell", bounds=(4, 4))
2315 OCTET STRING 4 bytes 68656c6c
2317 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2318 tag_default = tag_encode(4)
2319 asn1_type_name = "OCTET STRING"
2332 :param value: set the value. Either binary type, or
2333 :py:class:`pyderasn.OctetString` object
2334 :param bounds: set ``(MIN, MAX)`` value size constraint.
2335 (-inf, +inf) by default
2336 :param bytes impl: override default tag with ``IMPLICIT`` one
2337 :param bytes expl: override default tag with ``EXPLICIT`` one
2338 :param default: set default value. Type same as in ``value``
2339 :param bool optional: is object ``OPTIONAL`` in sequence
2341 super(OctetString, self).__init__(
2349 self._bound_min, self._bound_max = getattr(
2353 ) if bounds is None else bounds
2354 if value is not None:
2355 self._value = self._value_sanitize(value)
2356 if default is not None:
2357 default = self._value_sanitize(default)
2358 self.default = self.__class__(
2363 if self._value is None:
2364 self._value = default
2366 tag_klass, _, tag_num = tag_decode(self.tag)
2367 self.tag_constructed = tag_encode(
2369 form=TagFormConstructed,
2373 def _value_sanitize(self, value):
2374 if issubclass(value.__class__, OctetString):
2375 value = value._value
2376 elif isinstance(value, binary_type):
2379 raise InvalidValueType((self.__class__, bytes))
2380 if not self._bound_min <= len(value) <= self._bound_max:
2381 raise BoundsError(self._bound_min, len(value), self._bound_max)
2386 return self._value is not None
2389 obj = self.__class__()
2390 obj._value = self._value
2391 obj._bound_min = self._bound_min
2392 obj._bound_max = self._bound_max
2394 obj._expl = self._expl
2395 obj.default = self.default
2396 obj.optional = self.optional
2397 obj.offset = self.offset
2398 obj.llen = self.llen
2399 obj.vlen = self.vlen
2402 def __bytes__(self):
2403 self._assert_ready()
2406 def __eq__(self, their):
2407 if isinstance(their, binary_type):
2408 return self._value == their
2409 if not issubclass(their.__class__, OctetString):
2412 self._value == their._value and
2413 self.tag == their.tag and
2414 self._expl == their._expl
2417 def __lt__(self, their):
2418 return self._value < their._value
2429 return self.__class__(
2432 (self._bound_min, self._bound_max)
2433 if bounds is None else bounds
2435 impl=self.tag if impl is None else impl,
2436 expl=self._expl if expl is None else expl,
2437 default=self.default if default is None else default,
2438 optional=self.optional if optional is None else optional,
2442 self._assert_ready()
2445 len_encode(len(self._value)),
2449 def _decode_chunk(self, lv, offset, decode_path, ctx):
2451 l, llen, v = len_decode(lv)
2452 except DecodeError as err:
2453 raise err.__class__(
2455 klass=self.__class__,
2456 decode_path=decode_path,
2460 raise NotEnoughData(
2461 "encoded length is longer than data",
2462 klass=self.__class__,
2463 decode_path=decode_path,
2466 v, tail = v[:l], v[l:]
2468 obj = self.__class__(
2470 bounds=(self._bound_min, self._bound_max),
2473 default=self.default,
2474 optional=self.optional,
2475 _decoded=(offset, llen, l),
2477 except DecodeError as err:
2480 klass=self.__class__,
2481 decode_path=decode_path,
2484 except BoundsError as err:
2487 klass=self.__class__,
2488 decode_path=decode_path,
2493 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2495 t, tlen, lv = tag_strip(tlv)
2496 except DecodeError as err:
2497 raise err.__class__(
2499 klass=self.__class__,
2500 decode_path=decode_path,
2506 return self._decode_chunk(lv, offset, decode_path, ctx)
2507 if t == self.tag_constructed:
2508 if not ctx.get("bered", False):
2510 msg="unallowed BER constructed encoding",
2511 decode_path=decode_path,
2518 l, llen, v = len_decode(lv)
2519 except LenIndefForm:
2520 llen, l, v = 1, 0, lv[1:]
2522 except DecodeError as err:
2523 raise err.__class__(
2525 klass=self.__class__,
2526 decode_path=decode_path,
2529 if l > 0 and l > len(v):
2530 raise NotEnoughData(
2531 "encoded length is longer than data",
2532 klass=self.__class__,
2533 decode_path=decode_path,
2536 if not lenindef and l == 0:
2537 raise NotEnoughData(
2539 klass=self.__class__,
2540 decode_path=decode_path,
2544 sub_offset = offset + tlen + llen
2548 if v[:EOC_LEN].tobytes() == EOC:
2555 msg="chunk out of bounds",
2556 decode_path=len(chunks) - 1,
2557 offset=chunks[-1].offset,
2559 sub_decode_path = decode_path + (str(len(chunks)),)
2561 chunk, v_tail = OctetString().decode(
2564 decode_path=sub_decode_path,
2570 msg="expected OctetString encoded chunk",
2571 decode_path=sub_decode_path,
2574 chunks.append(chunk)
2575 sub_offset += chunk.tlvlen
2576 vlen += chunk.tlvlen
2578 if len(chunks) == 0:
2581 decode_path=decode_path,
2585 obj = self.__class__(
2586 value=b"".join(bytes(chunk) for chunk in chunks),
2587 bounds=(self._bound_min, self._bound_max),
2590 default=self.default,
2591 optional=self.optional,
2592 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2594 except DecodeError as err:
2597 klass=self.__class__,
2598 decode_path=decode_path,
2601 except BoundsError as err:
2604 klass=self.__class__,
2605 decode_path=decode_path,
2608 obj.lenindef = lenindef
2610 return obj, (v[EOC_LEN:] if lenindef else v)
2612 klass=self.__class__,
2613 decode_path=decode_path,
2618 return pp_console_row(next(self.pps()))
2620 def pps(self, decode_path=()):
2622 asn1_type_name=self.asn1_type_name,
2623 obj_name=self.__class__.__name__,
2624 decode_path=decode_path,
2625 value=("%d bytes" % len(self._value)) if self.ready else None,
2626 blob=self._value if self.ready else None,
2627 optional=self.optional,
2628 default=self == self.default,
2629 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2630 expl=None if self._expl is None else tag_decode(self._expl),
2635 expl_offset=self.expl_offset if self.expled else None,
2636 expl_tlen=self.expl_tlen if self.expled else None,
2637 expl_llen=self.expl_llen if self.expled else None,
2638 expl_vlen=self.expl_vlen if self.expled else None,
2639 expl_lenindef=self.expl_lenindef,
2640 lenindef=self.lenindef,
2643 defined_by, defined = self.defined or (None, None)
2644 if defined_by is not None:
2646 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2651 """``NULL`` null object
2659 tag_default = tag_encode(5)
2660 asn1_type_name = "NULL"
2664 value=None, # unused, but Sequence passes it
2671 :param bytes impl: override default tag with ``IMPLICIT`` one
2672 :param bytes expl: override default tag with ``EXPLICIT`` one
2673 :param bool optional: is object ``OPTIONAL`` in sequence
2675 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2683 obj = self.__class__()
2685 obj._expl = self._expl
2686 obj.default = self.default
2687 obj.optional = self.optional
2688 obj.offset = self.offset
2689 obj.llen = self.llen
2690 obj.vlen = self.vlen
2693 def __eq__(self, their):
2694 if not issubclass(their.__class__, Null):
2697 self.tag == their.tag and
2698 self._expl == their._expl
2708 return self.__class__(
2709 impl=self.tag if impl is None else impl,
2710 expl=self._expl if expl is None else expl,
2711 optional=self.optional if optional is None else optional,
2715 return self.tag + len_encode(0)
2717 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2719 t, _, lv = tag_strip(tlv)
2720 except DecodeError as err:
2721 raise err.__class__(
2723 klass=self.__class__,
2724 decode_path=decode_path,
2729 klass=self.__class__,
2730 decode_path=decode_path,
2736 l, _, v = len_decode(lv)
2737 except DecodeError as err:
2738 raise err.__class__(
2740 klass=self.__class__,
2741 decode_path=decode_path,
2745 raise InvalidLength(
2746 "Null must have zero length",
2747 klass=self.__class__,
2748 decode_path=decode_path,
2751 obj = self.__class__(
2754 optional=self.optional,
2755 _decoded=(offset, 1, 0),
2760 return pp_console_row(next(self.pps()))
2762 def pps(self, decode_path=()):
2764 asn1_type_name=self.asn1_type_name,
2765 obj_name=self.__class__.__name__,
2766 decode_path=decode_path,
2767 optional=self.optional,
2768 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2769 expl=None if self._expl is None else tag_decode(self._expl),
2774 expl_offset=self.expl_offset if self.expled else None,
2775 expl_tlen=self.expl_tlen if self.expled else None,
2776 expl_llen=self.expl_llen if self.expled else None,
2777 expl_vlen=self.expl_vlen if self.expled else None,
2778 expl_lenindef=self.expl_lenindef,
2782 class ObjectIdentifier(Obj):
2783 """``OBJECT IDENTIFIER`` OID type
2785 >>> oid = ObjectIdentifier((1, 2, 3))
2786 OBJECT IDENTIFIER 1.2.3
2787 >>> oid == ObjectIdentifier("1.2.3")
2793 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2794 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2796 >>> str(ObjectIdentifier((3, 1)))
2797 Traceback (most recent call last):
2798 pyderasn.InvalidOID: unacceptable first arc value
2800 __slots__ = ("defines",)
2801 tag_default = tag_encode(6)
2802 asn1_type_name = "OBJECT IDENTIFIER"
2815 :param value: set the value. Either tuples of integers,
2816 string of "."-concatenated integers, or
2817 :py:class:`pyderasn.ObjectIdentifier` object
2818 :param defines: sequence of tuples. Each tuple has two elements.
2819 First one is relative to current one decode
2820 path, aiming to the field defined by that OID.
2821 Read about relative path in
2822 :py:func:`pyderasn.abs_decode_path`. Second
2823 tuple element is ``{OID: pyderasn.Obj()}``
2824 dictionary, mapping between current OID value
2825 and structure applied to defined field.
2826 :ref:`Read about DEFINED BY <definedby>`
2827 :param bytes impl: override default tag with ``IMPLICIT`` one
2828 :param bytes expl: override default tag with ``EXPLICIT`` one
2829 :param default: set default value. Type same as in ``value``
2830 :param bool optional: is object ``OPTIONAL`` in sequence
2832 super(ObjectIdentifier, self).__init__(
2840 if value is not None:
2841 self._value = self._value_sanitize(value)
2842 if default is not None:
2843 default = self._value_sanitize(default)
2844 self.default = self.__class__(
2849 if self._value is None:
2850 self._value = default
2851 self.defines = defines
2853 def __add__(self, their):
2854 if isinstance(their, self.__class__):
2855 return self.__class__(self._value + their._value)
2856 if isinstance(their, tuple):
2857 return self.__class__(self._value + their)
2858 raise InvalidValueType((self.__class__, tuple))
2860 def _value_sanitize(self, value):
2861 if issubclass(value.__class__, ObjectIdentifier):
2863 if isinstance(value, string_types):
2865 value = tuple(int(arc) for arc in value.split("."))
2867 raise InvalidOID("unacceptable arcs values")
2868 if isinstance(value, tuple):
2870 raise InvalidOID("less than 2 arcs")
2871 first_arc = value[0]
2872 if first_arc in (0, 1):
2873 if not (0 <= value[1] <= 39):
2874 raise InvalidOID("second arc is too wide")
2875 elif first_arc == 2:
2878 raise InvalidOID("unacceptable first arc value")
2880 raise InvalidValueType((self.__class__, str, tuple))
2884 return self._value is not None
2887 obj = self.__class__()
2888 obj._value = self._value
2889 obj.defines = self.defines
2891 obj._expl = self._expl
2892 obj.default = self.default
2893 obj.optional = self.optional
2894 obj.offset = self.offset
2895 obj.llen = self.llen
2896 obj.vlen = self.vlen
2900 self._assert_ready()
2901 return iter(self._value)
2904 return ".".join(str(arc) for arc in self._value or ())
2907 self._assert_ready()
2910 bytes(self._expl or b"") +
2911 str(self._value).encode("ascii"),
2914 def __eq__(self, their):
2915 if isinstance(their, tuple):
2916 return self._value == their
2917 if not issubclass(their.__class__, ObjectIdentifier):
2920 self.tag == their.tag and
2921 self._expl == their._expl and
2922 self._value == their._value
2925 def __lt__(self, their):
2926 return self._value < their._value
2937 return self.__class__(
2939 defines=self.defines if defines is None else defines,
2940 impl=self.tag if impl is None else impl,
2941 expl=self._expl if expl is None else expl,
2942 default=self.default if default is None else default,
2943 optional=self.optional if optional is None else optional,
2947 self._assert_ready()
2949 first_value = value[1]
2950 first_arc = value[0]
2953 elif first_arc == 1:
2955 elif first_arc == 2:
2957 else: # pragma: no cover
2958 raise RuntimeError("invalid arc is stored")
2959 octets = [zero_ended_encode(first_value)]
2960 for arc in value[2:]:
2961 octets.append(zero_ended_encode(arc))
2962 v = b"".join(octets)
2963 return b"".join((self.tag, len_encode(len(v)), v))
2965 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2967 t, _, lv = tag_strip(tlv)
2968 except DecodeError as err:
2969 raise err.__class__(
2971 klass=self.__class__,
2972 decode_path=decode_path,
2977 klass=self.__class__,
2978 decode_path=decode_path,
2984 l, llen, v = len_decode(lv)
2985 except DecodeError as err:
2986 raise err.__class__(
2988 klass=self.__class__,
2989 decode_path=decode_path,
2993 raise NotEnoughData(
2994 "encoded length is longer than data",
2995 klass=self.__class__,
2996 decode_path=decode_path,
3000 raise NotEnoughData(
3002 klass=self.__class__,
3003 decode_path=decode_path,
3006 v, tail = v[:l], v[l:]
3012 octet = indexbytes(v, i)
3013 arc = (arc << 7) | (octet & 0x7F)
3014 if octet & 0x80 == 0:
3022 klass=self.__class__,
3023 decode_path=decode_path,
3027 second_arc = arcs[0]
3028 if 0 <= second_arc <= 39:
3030 elif 40 <= second_arc <= 79:
3036 obj = self.__class__(
3037 value=tuple([first_arc, second_arc] + arcs[1:]),
3040 default=self.default,
3041 optional=self.optional,
3042 _decoded=(offset, llen, l),
3047 return pp_console_row(next(self.pps()))
3049 def pps(self, decode_path=()):
3051 asn1_type_name=self.asn1_type_name,
3052 obj_name=self.__class__.__name__,
3053 decode_path=decode_path,
3054 value=str(self) if self.ready else None,
3055 optional=self.optional,
3056 default=self == self.default,
3057 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3058 expl=None if self._expl is None else tag_decode(self._expl),
3063 expl_offset=self.expl_offset if self.expled else None,
3064 expl_tlen=self.expl_tlen if self.expled else None,
3065 expl_llen=self.expl_llen if self.expled else None,
3066 expl_vlen=self.expl_vlen if self.expled else None,
3067 expl_lenindef=self.expl_lenindef,
3071 class Enumerated(Integer):
3072 """``ENUMERATED`` integer type
3074 This type is identical to :py:class:`pyderasn.Integer`, but requires
3075 schema to be specified and does not accept values missing from it.
3078 tag_default = tag_encode(10)
3079 asn1_type_name = "ENUMERATED"
3090 bounds=None, # dummy argument, workability for Integer.decode
3092 super(Enumerated, self).__init__(
3101 if len(self.specs) == 0:
3102 raise ValueError("schema must be specified")
3104 def _value_sanitize(self, value):
3105 if isinstance(value, self.__class__):
3106 value = value._value
3107 elif isinstance(value, integer_types):
3108 if value not in list(self.specs.values()):
3110 "unknown integer value: %s" % value,
3111 klass=self.__class__,
3113 elif isinstance(value, string_types):
3114 value = self.specs.get(value)
3116 raise ObjUnknown("integer value: %s" % value)
3118 raise InvalidValueType((self.__class__, int, str))
3122 obj = self.__class__(_specs=self.specs)
3123 obj._value = self._value
3124 obj._bound_min = self._bound_min
3125 obj._bound_max = self._bound_max
3127 obj._expl = self._expl
3128 obj.default = self.default
3129 obj.optional = self.optional
3130 obj.offset = self.offset
3131 obj.llen = self.llen
3132 obj.vlen = self.vlen
3144 return self.__class__(
3146 impl=self.tag if impl is None else impl,
3147 expl=self._expl if expl is None else expl,
3148 default=self.default if default is None else default,
3149 optional=self.optional if optional is None else optional,
3154 class CommonString(OctetString):
3155 """Common class for all strings
3157 Everything resembles :py:class:`pyderasn.OctetString`, except
3158 ability to deal with unicode text strings.
3160 >>> hexenc("привет мир".encode("utf-8"))
3161 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3162 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3164 >>> s = UTF8String("привет мир")
3165 UTF8String UTF8String привет мир
3167 'привет мир'
3168 >>> hexenc(bytes(s))
3169 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3171 >>> PrintableString("привет мир")
3172 Traceback (most recent call last):
3173 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3175 >>> BMPString("ада", bounds=(2, 2))
3176 Traceback (most recent call last):
3177 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3178 >>> s = BMPString("ад", bounds=(2, 2))
3181 >>> hexenc(bytes(s))
3189 * - :py:class:`pyderasn.UTF8String`
3191 * - :py:class:`pyderasn.NumericString`
3193 * - :py:class:`pyderasn.PrintableString`
3195 * - :py:class:`pyderasn.TeletexString`
3197 * - :py:class:`pyderasn.T61String`
3199 * - :py:class:`pyderasn.VideotexString`
3201 * - :py:class:`pyderasn.IA5String`
3203 * - :py:class:`pyderasn.GraphicString`
3205 * - :py:class:`pyderasn.VisibleString`
3207 * - :py:class:`pyderasn.ISO646String`
3209 * - :py:class:`pyderasn.GeneralString`
3211 * - :py:class:`pyderasn.UniversalString`
3213 * - :py:class:`pyderasn.BMPString`
3216 __slots__ = ("encoding",)
3218 def _value_sanitize(self, value):
3220 value_decoded = None
3221 if isinstance(value, self.__class__):
3222 value_raw = value._value
3223 elif isinstance(value, text_type):
3224 value_decoded = value
3225 elif isinstance(value, binary_type):
3228 raise InvalidValueType((self.__class__, text_type, binary_type))
3231 value_decoded.encode(self.encoding)
3232 if value_raw is None else value_raw
3235 value_raw.decode(self.encoding)
3236 if value_decoded is None else value_decoded
3238 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3239 raise DecodeError(str(err))
3240 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3248 def __eq__(self, their):
3249 if isinstance(their, binary_type):
3250 return self._value == their
3251 if isinstance(their, text_type):
3252 return self._value == their.encode(self.encoding)
3253 if not isinstance(their, self.__class__):
3256 self._value == their._value and
3257 self.tag == their.tag and
3258 self._expl == their._expl
3261 def __unicode__(self):
3263 return self._value.decode(self.encoding)
3264 return text_type(self._value)
3267 return pp_console_row(next(self.pps(no_unicode=PY2)))
3269 def pps(self, decode_path=(), no_unicode=False):
3272 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3274 asn1_type_name=self.asn1_type_name,
3275 obj_name=self.__class__.__name__,
3276 decode_path=decode_path,
3278 optional=self.optional,
3279 default=self == self.default,
3280 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3281 expl=None if self._expl is None else tag_decode(self._expl),
3286 expl_offset=self.expl_offset if self.expled else None,
3287 expl_tlen=self.expl_tlen if self.expled else None,
3288 expl_llen=self.expl_llen if self.expled else None,
3289 expl_vlen=self.expl_vlen if self.expled else None,
3290 expl_lenindef=self.expl_lenindef,
3294 class UTF8String(CommonString):
3296 tag_default = tag_encode(12)
3298 asn1_type_name = "UTF8String"
3301 class NumericString(CommonString):
3303 tag_default = tag_encode(18)
3305 asn1_type_name = "NumericString"
3306 allowable_chars = set(digits.encode("ascii"))
3308 def _value_sanitize(self, value):
3309 value = super(NumericString, self)._value_sanitize(value)
3310 if not set(value) <= self.allowable_chars:
3311 raise DecodeError("non-numeric value")
3315 class PrintableString(CommonString):
3317 tag_default = tag_encode(19)
3319 asn1_type_name = "PrintableString"
3322 class TeletexString(CommonString):
3324 tag_default = tag_encode(20)
3326 asn1_type_name = "TeletexString"
3329 class T61String(TeletexString):
3331 asn1_type_name = "T61String"
3334 class VideotexString(CommonString):
3336 tag_default = tag_encode(21)
3337 encoding = "iso-8859-1"
3338 asn1_type_name = "VideotexString"
3341 class IA5String(CommonString):
3343 tag_default = tag_encode(22)
3345 asn1_type_name = "IA5"
3348 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3349 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3350 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3353 class UTCTime(CommonString):
3354 """``UTCTime`` datetime type
3356 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3357 UTCTime UTCTime 2017-09-30T22:07:50
3363 datetime.datetime(2017, 9, 30, 22, 7, 50)
3364 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3365 datetime.datetime(1957, 9, 30, 22, 7, 50)
3368 tag_default = tag_encode(23)
3370 asn1_type_name = "UTCTime"
3372 fmt = "%y%m%d%H%M%SZ"
3382 bounds=None, # dummy argument, workability for OctetString.decode
3385 :param value: set the value. Either datetime type, or
3386 :py:class:`pyderasn.UTCTime` object
3387 :param bytes impl: override default tag with ``IMPLICIT`` one
3388 :param bytes expl: override default tag with ``EXPLICIT`` one
3389 :param default: set default value. Type same as in ``value``
3390 :param bool optional: is object ``OPTIONAL`` in sequence
3392 super(UTCTime, self).__init__(
3400 if value is not None:
3401 self._value = self._value_sanitize(value)
3402 if default is not None:
3403 default = self._value_sanitize(default)
3404 self.default = self.__class__(
3409 if self._value is None:
3410 self._value = default
3412 def _value_sanitize(self, value):
3413 if isinstance(value, self.__class__):
3415 if isinstance(value, datetime):
3416 return value.strftime(self.fmt).encode("ascii")
3417 if isinstance(value, binary_type):
3418 value_decoded = value.decode("ascii")
3419 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3421 datetime.strptime(value_decoded, self.fmt)
3423 raise DecodeError("invalid UTCTime format")
3426 raise DecodeError("invalid UTCTime length")
3427 raise InvalidValueType((self.__class__, datetime))
3429 def __eq__(self, their):
3430 if isinstance(their, binary_type):
3431 return self._value == their
3432 if isinstance(their, datetime):
3433 return self.todatetime() == their
3434 if not isinstance(their, self.__class__):
3437 self._value == their._value and
3438 self.tag == their.tag and
3439 self._expl == their._expl
3442 def todatetime(self):
3443 """Convert to datetime
3447 Pay attention that UTCTime can not hold full year, so all years
3448 having < 50 years are treated as 20xx, 19xx otherwise, according
3449 to X.509 recomendation.
3451 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3452 year = value.year % 100
3454 year=(2000 + year) if year < 50 else (1900 + year),
3458 minute=value.minute,
3459 second=value.second,
3463 return pp_console_row(next(self.pps()))
3465 def pps(self, decode_path=()):
3467 asn1_type_name=self.asn1_type_name,
3468 obj_name=self.__class__.__name__,
3469 decode_path=decode_path,
3470 value=self.todatetime().isoformat() if self.ready else None,
3471 optional=self.optional,
3472 default=self == self.default,
3473 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3474 expl=None if self._expl is None else tag_decode(self._expl),
3479 expl_offset=self.expl_offset if self.expled else None,
3480 expl_tlen=self.expl_tlen if self.expled else None,
3481 expl_llen=self.expl_llen if self.expled else None,
3482 expl_vlen=self.expl_vlen if self.expled else None,
3483 expl_lenindef=self.expl_lenindef,
3487 class GeneralizedTime(UTCTime):
3488 """``GeneralizedTime`` datetime type
3490 This type is similar to :py:class:`pyderasn.UTCTime`.
3492 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3493 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3495 '20170930220750.000123Z'
3496 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3497 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3500 tag_default = tag_encode(24)
3501 asn1_type_name = "GeneralizedTime"
3503 fmt = "%Y%m%d%H%M%SZ"
3504 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3506 def _value_sanitize(self, value):
3507 if isinstance(value, self.__class__):
3509 if isinstance(value, datetime):
3510 return value.strftime(
3511 self.fmt_ms if value.microsecond > 0 else self.fmt
3513 if isinstance(value, binary_type):
3514 value_decoded = value.decode("ascii")
3515 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3517 datetime.strptime(value_decoded, self.fmt)
3520 "invalid GeneralizedTime (without ms) format",
3523 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3525 datetime.strptime(value_decoded, self.fmt_ms)
3528 "invalid GeneralizedTime (with ms) format",
3533 "invalid GeneralizedTime length",
3534 klass=self.__class__,
3536 raise InvalidValueType((self.__class__, datetime))
3538 def todatetime(self):
3539 value = self._value.decode("ascii")
3540 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3541 return datetime.strptime(value, self.fmt)
3542 return datetime.strptime(value, self.fmt_ms)
3545 class GraphicString(CommonString):
3547 tag_default = tag_encode(25)
3548 encoding = "iso-8859-1"
3549 asn1_type_name = "GraphicString"
3552 class VisibleString(CommonString):
3554 tag_default = tag_encode(26)
3556 asn1_type_name = "VisibleString"
3559 class ISO646String(VisibleString):
3561 asn1_type_name = "ISO646String"
3564 class GeneralString(CommonString):
3566 tag_default = tag_encode(27)
3567 encoding = "iso-8859-1"
3568 asn1_type_name = "GeneralString"
3571 class UniversalString(CommonString):
3573 tag_default = tag_encode(28)
3574 encoding = "utf-32-be"
3575 asn1_type_name = "UniversalString"
3578 class BMPString(CommonString):
3580 tag_default = tag_encode(30)
3581 encoding = "utf-16-be"
3582 asn1_type_name = "BMPString"
3586 """``CHOICE`` special type
3590 class GeneralName(Choice):
3592 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3593 ("dNSName", IA5String(impl=tag_ctxp(2))),
3596 >>> gn = GeneralName()
3598 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3599 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3600 >>> gn["dNSName"] = IA5String("bar.baz")
3601 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3602 >>> gn["rfc822Name"]
3605 [2] IA5String IA5 bar.baz
3608 >>> gn.value == gn["dNSName"]
3611 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3613 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3614 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3616 __slots__ = ("specs",)
3618 asn1_type_name = "CHOICE"
3631 :param value: set the value. Either ``(choice, value)`` tuple, or
3632 :py:class:`pyderasn.Choice` object
3633 :param bytes impl: can not be set, do **not** use it
3634 :param bytes expl: override default tag with ``EXPLICIT`` one
3635 :param default: set default value. Type same as in ``value``
3636 :param bool optional: is object ``OPTIONAL`` in sequence
3638 if impl is not None:
3639 raise ValueError("no implicit tag allowed for CHOICE")
3640 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3642 schema = getattr(self, "schema", ())
3643 if len(schema) == 0:
3644 raise ValueError("schema must be specified")
3646 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3649 if value is not None:
3650 self._value = self._value_sanitize(value)
3651 if default is not None:
3652 default_value = self._value_sanitize(default)
3653 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3654 default_obj.specs = self.specs
3655 default_obj._value = default_value
3656 self.default = default_obj
3658 self._value = default_obj.copy()._value
3660 def _value_sanitize(self, value):
3661 if isinstance(value, self.__class__):
3663 if isinstance(value, tuple) and len(value) == 2:
3665 spec = self.specs.get(choice)
3667 raise ObjUnknown(choice)
3668 if not isinstance(obj, spec.__class__):
3669 raise InvalidValueType((spec,))
3670 return (choice, spec(obj))
3671 raise InvalidValueType((self.__class__, tuple))
3675 return self._value is not None and self._value[1].ready
3678 obj = self.__class__(schema=self.specs)
3679 obj._expl = self._expl
3680 obj.default = self.default
3681 obj.optional = self.optional
3682 obj.offset = self.offset
3683 obj.llen = self.llen
3684 obj.vlen = self.vlen
3686 if value is not None:
3687 obj._value = (value[0], value[1].copy())
3690 def __eq__(self, their):
3691 if isinstance(their, tuple) and len(their) == 2:
3692 return self._value == their
3693 if not isinstance(their, self.__class__):
3696 self.specs == their.specs and
3697 self._value == their._value
3707 return self.__class__(
3710 expl=self._expl if expl is None else expl,
3711 default=self.default if default is None else default,
3712 optional=self.optional if optional is None else optional,
3717 self._assert_ready()
3718 return self._value[0]
3722 self._assert_ready()
3723 return self._value[1]
3725 def __getitem__(self, key):
3726 if key not in self.specs:
3727 raise ObjUnknown(key)
3728 if self._value is None:
3730 choice, value = self._value
3735 def __setitem__(self, key, value):
3736 spec = self.specs.get(key)
3738 raise ObjUnknown(key)
3739 if not isinstance(value, spec.__class__):
3740 raise InvalidValueType((spec.__class__,))
3741 self._value = (key, spec(value))
3749 return self._value[1].decoded if self.ready else False
3752 self._assert_ready()
3753 return self._value[1].encode()
3755 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3756 for choice, spec in self.specs.items():
3757 sub_decode_path = decode_path + (choice,)
3763 decode_path=sub_decode_path,
3772 klass=self.__class__,
3773 decode_path=decode_path,
3778 value, tail = spec.decode(
3782 decode_path=sub_decode_path,
3785 obj = self.__class__(
3788 default=self.default,
3789 optional=self.optional,
3790 _decoded=(offset, 0, value.tlvlen),
3792 obj._value = (choice, value)
3796 value = pp_console_row(next(self.pps()))
3798 value = "%s[%r]" % (value, self.value)
3801 def pps(self, decode_path=()):
3803 asn1_type_name=self.asn1_type_name,
3804 obj_name=self.__class__.__name__,
3805 decode_path=decode_path,
3806 value=self.choice if self.ready else None,
3807 optional=self.optional,
3808 default=self == self.default,
3809 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3810 expl=None if self._expl is None else tag_decode(self._expl),
3815 expl_lenindef=self.expl_lenindef,
3818 yield self.value.pps(decode_path=decode_path + (self.choice,))
3821 class PrimitiveTypes(Choice):
3822 """Predefined ``CHOICE`` for all generic primitive types
3824 It could be useful for general decoding of some unspecified values:
3826 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3827 OCTET STRING 3 bytes 666f6f
3828 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3832 schema = tuple((klass.__name__, klass()) for klass in (
3857 """``ANY`` special type
3859 >>> Any(Integer(-123))
3861 >>> a = Any(OctetString(b"hello world").encode())
3862 ANY 040b68656c6c6f20776f726c64
3863 >>> hexenc(bytes(a))
3864 b'0x040x0bhello world'
3866 __slots__ = ("defined",)
3867 tag_default = tag_encode(0)
3868 asn1_type_name = "ANY"
3878 :param value: set the value. Either any kind of pyderasn's
3879 **ready** object, or bytes. Pay attention that
3880 **no** validation is performed is raw binary value
3882 :param bytes expl: override default tag with ``EXPLICIT`` one
3883 :param bool optional: is object ``OPTIONAL`` in sequence
3885 super(Any, self).__init__(None, expl, None, optional, _decoded)
3886 self._value = None if value is None else self._value_sanitize(value)
3889 def _value_sanitize(self, value):
3890 if isinstance(value, self.__class__):
3892 if isinstance(value, Obj):
3893 return value.encode()
3894 if isinstance(value, binary_type):
3896 raise InvalidValueType((self.__class__, Obj, binary_type))
3900 return self._value is not None
3903 obj = self.__class__()
3904 obj._value = self._value
3906 obj._expl = self._expl
3907 obj.optional = self.optional
3908 obj.offset = self.offset
3909 obj.llen = self.llen
3910 obj.vlen = self.vlen
3913 def __eq__(self, their):
3914 if isinstance(their, binary_type):
3915 return self._value == their
3916 if issubclass(their.__class__, Any):
3917 return self._value == their._value
3926 return self.__class__(
3928 expl=self._expl if expl is None else expl,
3929 optional=self.optional if optional is None else optional,
3932 def __bytes__(self):
3933 self._assert_ready()
3941 self._assert_ready()
3944 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3946 t, tlen, lv = tag_strip(tlv)
3947 except DecodeError as err:
3948 raise err.__class__(
3950 klass=self.__class__,
3951 decode_path=decode_path,
3955 l, llen, v = len_decode(lv)
3956 except LenIndefForm as err:
3957 if not ctx.get("bered", False):
3958 raise err.__class__(
3960 klass=self.__class__,
3961 decode_path=decode_path,
3964 llen, vlen, v = 1, 0, lv[1:]
3965 sub_offset = offset + tlen + llen
3968 if v[:EOC_LEN].tobytes() == EOC:
3969 tlvlen = tlen + llen + vlen + EOC_LEN
3970 obj = self.__class__(
3971 value=tlv[:tlvlen].tobytes(),
3973 optional=self.optional,
3974 _decoded=(offset, 0, tlvlen),
3978 return obj, v[EOC_LEN:]
3980 chunk, v = Any().decode(
3983 decode_path=decode_path + (str(chunk_i),),
3987 vlen += chunk.tlvlen
3988 sub_offset += chunk.tlvlen
3990 except DecodeError as err:
3991 raise err.__class__(
3993 klass=self.__class__,
3994 decode_path=decode_path,
3998 raise NotEnoughData(
3999 "encoded length is longer than data",
4000 klass=self.__class__,
4001 decode_path=decode_path,
4004 tlvlen = tlen + llen + l
4005 v, tail = tlv[:tlvlen], v[l:]
4006 obj = self.__class__(
4009 optional=self.optional,
4010 _decoded=(offset, 0, tlvlen),
4016 return pp_console_row(next(self.pps()))
4018 def pps(self, decode_path=()):
4020 asn1_type_name=self.asn1_type_name,
4021 obj_name=self.__class__.__name__,
4022 decode_path=decode_path,
4023 blob=self._value if self.ready else None,
4024 optional=self.optional,
4025 default=self == self.default,
4026 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4027 expl=None if self._expl is None else tag_decode(self._expl),
4032 expl_offset=self.expl_offset if self.expled else None,
4033 expl_tlen=self.expl_tlen if self.expled else None,
4034 expl_llen=self.expl_llen if self.expled else None,
4035 expl_vlen=self.expl_vlen if self.expled else None,
4036 expl_lenindef=self.expl_lenindef,
4037 lenindef=self.lenindef,
4039 defined_by, defined = self.defined or (None, None)
4040 if defined_by is not None:
4042 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4046 ########################################################################
4047 # ASN.1 constructed types
4048 ########################################################################
4050 def get_def_by_path(defines_by_path, sub_decode_path):
4051 """Get define by decode path
4053 for path, define in defines_by_path:
4054 if len(path) != len(sub_decode_path):
4056 for p1, p2 in zip(path, sub_decode_path):
4057 if (p1 != any) and (p1 != p2):
4063 def abs_decode_path(decode_path, rel_path):
4064 """Create an absolute decode path from current and relative ones
4066 :param decode_path: current decode path, starting point.
4068 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4069 If first tuple's element is "/", then treat it as
4070 an absolute path, ignoring ``decode_path`` as
4071 starting point. Also this tuple can contain ".."
4072 elements, stripping the leading element from
4075 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4076 ("foo", "bar", "baz", "whatever")
4077 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4079 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4082 if rel_path[0] == "/":
4084 if rel_path[0] == "..":
4085 return abs_decode_path(decode_path[:-1], rel_path[1:])
4086 return decode_path + rel_path
4089 class Sequence(Obj):
4090 """``SEQUENCE`` structure type
4092 You have to make specification of sequence::
4094 class Extension(Sequence):
4096 ("extnID", ObjectIdentifier()),
4097 ("critical", Boolean(default=False)),
4098 ("extnValue", OctetString()),
4101 Then, you can work with it as with dictionary.
4103 >>> ext = Extension()
4104 >>> Extension().specs
4106 ('extnID', OBJECT IDENTIFIER),
4107 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4108 ('extnValue', OCTET STRING),
4110 >>> ext["extnID"] = "1.2.3"
4111 Traceback (most recent call last):
4112 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4113 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4115 You can determine if sequence is ready to be encoded:
4120 Traceback (most recent call last):
4121 pyderasn.ObjNotReady: object is not ready: extnValue
4122 >>> ext["extnValue"] = OctetString(b"foobar")
4126 Value you want to assign, must have the same **type** as in
4127 corresponding specification, but it can have different tags,
4128 optional/default attributes -- they will be taken from specification
4131 class TBSCertificate(Sequence):
4133 ("version", Version(expl=tag_ctxc(0), default="v1")),
4136 >>> tbs = TBSCertificate()
4137 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4139 Assign ``None`` to remove value from sequence.
4141 You can set values in Sequence during its initialization:
4143 >>> AlgorithmIdentifier((
4144 ("algorithm", ObjectIdentifier("1.2.3")),
4145 ("parameters", Any(Null()))
4147 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4149 You can determine if value exists/set in the sequence and take its value:
4151 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4154 OBJECT IDENTIFIER 1.2.3
4156 But pay attention that if value has default, then it won't be (not
4157 in) in the sequence (because ``DEFAULT`` must not be encoded in
4158 DER), but you can read its value:
4160 >>> "critical" in ext, ext["critical"]
4161 (False, BOOLEAN False)
4162 >>> ext["critical"] = Boolean(True)
4163 >>> "critical" in ext, ext["critical"]
4164 (True, BOOLEAN True)
4166 All defaulted values are always optional.
4168 .. _strict_default_existence_ctx:
4172 When decoded DER contains defaulted value inside, then
4173 technically this is not valid DER encoding. But we allow and pass
4174 it **by default**. Of course reencoding of that kind of DER will
4175 result in different binary representation (validly without
4176 defaulted value inside). You can enable strict defaulted values
4177 existence validation by setting ``"strict_default_existence":
4178 True`` :ref:`context <ctx>` option -- decoding process will raise
4179 an exception if defaulted value is met.
4181 Two sequences are equal if they have equal specification (schema),
4182 implicit/explicit tagging and the same values.
4184 __slots__ = ("specs",)
4185 tag_default = tag_encode(form=TagFormConstructed, num=16)
4186 asn1_type_name = "SEQUENCE"
4198 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4200 schema = getattr(self, "schema", ())
4202 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4205 if value is not None:
4206 if issubclass(value.__class__, Sequence):
4207 self._value = value._value
4208 elif hasattr(value, "__iter__"):
4209 for seq_key, seq_value in value:
4210 self[seq_key] = seq_value
4212 raise InvalidValueType((Sequence,))
4213 if default is not None:
4214 if not issubclass(default.__class__, Sequence):
4215 raise InvalidValueType((Sequence,))
4216 default_value = default._value
4217 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4218 default_obj.specs = self.specs
4219 default_obj._value = default_value
4220 self.default = default_obj
4222 self._value = default_obj.copy()._value
4226 for name, spec in self.specs.items():
4227 value = self._value.get(name)
4238 obj = self.__class__(schema=self.specs)
4240 obj._expl = self._expl
4241 obj.default = self.default
4242 obj.optional = self.optional
4243 obj.offset = self.offset
4244 obj.llen = self.llen
4245 obj.vlen = self.vlen
4246 obj._value = {k: v.copy() for k, v in self._value.items()}
4249 def __eq__(self, their):
4250 if not isinstance(their, self.__class__):
4253 self.specs == their.specs and
4254 self.tag == their.tag and
4255 self._expl == their._expl and
4256 self._value == their._value
4267 return self.__class__(
4270 impl=self.tag if impl is None else impl,
4271 expl=self._expl if expl is None else expl,
4272 default=self.default if default is None else default,
4273 optional=self.optional if optional is None else optional,
4276 def __contains__(self, key):
4277 return key in self._value
4279 def __setitem__(self, key, value):
4280 spec = self.specs.get(key)
4282 raise ObjUnknown(key)
4284 self._value.pop(key, None)
4286 if not isinstance(value, spec.__class__):
4287 raise InvalidValueType((spec.__class__,))
4288 value = spec(value=value)
4289 if spec.default is not None and value == spec.default:
4290 self._value.pop(key, None)
4292 self._value[key] = value
4294 def __getitem__(self, key):
4295 value = self._value.get(key)
4296 if value is not None:
4298 spec = self.specs.get(key)
4300 raise ObjUnknown(key)
4301 if spec.default is not None:
4305 def _encoded_values(self):
4307 for name, spec in self.specs.items():
4308 value = self._value.get(name)
4312 raise ObjNotReady(name)
4313 raws.append(value.encode())
4317 v = b"".join(self._encoded_values())
4318 return b"".join((self.tag, len_encode(len(v)), v))
4320 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4322 t, tlen, lv = tag_strip(tlv)
4323 except DecodeError as err:
4324 raise err.__class__(
4326 klass=self.__class__,
4327 decode_path=decode_path,
4332 klass=self.__class__,
4333 decode_path=decode_path,
4340 l, llen, v = len_decode(lv)
4341 except LenIndefForm as err:
4342 if not ctx.get("bered", False):
4343 raise err.__class__(
4345 klass=self.__class__,
4346 decode_path=decode_path,
4349 l, llen, v = 0, 1, lv[1:]
4351 except DecodeError as err:
4352 raise err.__class__(
4354 klass=self.__class__,
4355 decode_path=decode_path,
4359 raise NotEnoughData(
4360 "encoded length is longer than data",
4361 klass=self.__class__,
4362 decode_path=decode_path,
4366 v, tail = v[:l], v[l:]
4368 sub_offset = offset + tlen + llen
4370 for name, spec in self.specs.items():
4371 if spec.optional and (
4372 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4376 sub_decode_path = decode_path + (name,)
4378 value, v_tail = spec.decode(
4382 decode_path=sub_decode_path,
4390 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4391 if defined is not None:
4392 defined_by, defined_spec = defined
4393 if issubclass(value.__class__, SequenceOf):
4394 for i, _value in enumerate(value):
4395 sub_sub_decode_path = sub_decode_path + (
4397 DecodePathDefBy(defined_by),
4399 defined_value, defined_tail = defined_spec.decode(
4400 memoryview(bytes(_value)),
4402 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4403 if value.expled else (value.tlen + value.llen)
4406 decode_path=sub_sub_decode_path,
4409 if len(defined_tail) > 0:
4412 klass=self.__class__,
4413 decode_path=sub_sub_decode_path,
4416 _value.defined = (defined_by, defined_value)
4418 defined_value, defined_tail = defined_spec.decode(
4419 memoryview(bytes(value)),
4421 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4422 if value.expled else (value.tlen + value.llen)
4425 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4428 if len(defined_tail) > 0:
4431 klass=self.__class__,
4432 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4435 value.defined = (defined_by, defined_value)
4437 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4439 sub_offset += value_len
4441 if spec.default is not None and value == spec.default:
4442 if ctx.get("strict_default_existence", False):
4444 "DEFAULT value met",
4445 klass=self.__class__,
4446 decode_path=sub_decode_path,
4451 values[name] = value
4453 spec_defines = getattr(spec, "defines", ())
4454 if len(spec_defines) == 0:
4455 defines_by_path = ctx.get("defines_by_path", ())
4456 if len(defines_by_path) > 0:
4457 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4458 if spec_defines is not None and len(spec_defines) > 0:
4459 for rel_path, schema in spec_defines:
4460 defined = schema.get(value, None)
4461 if defined is not None:
4462 ctx.setdefault("defines", []).append((
4463 abs_decode_path(sub_decode_path[:-1], rel_path),
4467 if v[:EOC_LEN].tobytes() != EOC:
4470 klass=self.__class__,
4471 decode_path=decode_path,
4479 klass=self.__class__,
4480 decode_path=decode_path,
4483 obj = self.__class__(
4487 default=self.default,
4488 optional=self.optional,
4489 _decoded=(offset, llen, vlen),
4492 obj.lenindef = lenindef
4496 value = pp_console_row(next(self.pps()))
4498 for name in self.specs:
4499 _value = self._value.get(name)
4502 cols.append(repr(_value))
4503 return "%s[%s]" % (value, ", ".join(cols))
4505 def pps(self, decode_path=()):
4507 asn1_type_name=self.asn1_type_name,
4508 obj_name=self.__class__.__name__,
4509 decode_path=decode_path,
4510 optional=self.optional,
4511 default=self == self.default,
4512 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4513 expl=None if self._expl is None else tag_decode(self._expl),
4518 expl_offset=self.expl_offset if self.expled else None,
4519 expl_tlen=self.expl_tlen if self.expled else None,
4520 expl_llen=self.expl_llen if self.expled else None,
4521 expl_vlen=self.expl_vlen if self.expled else None,
4522 expl_lenindef=self.expl_lenindef,
4523 lenindef=self.lenindef,
4525 for name in self.specs:
4526 value = self._value.get(name)
4529 yield value.pps(decode_path=decode_path + (name,))
4532 class Set(Sequence):
4533 """``SET`` structure type
4535 Its usage is identical to :py:class:`pyderasn.Sequence`.
4538 tag_default = tag_encode(form=TagFormConstructed, num=17)
4539 asn1_type_name = "SET"
4542 raws = self._encoded_values()
4545 return b"".join((self.tag, len_encode(len(v)), v))
4547 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4549 t, tlen, lv = tag_strip(tlv)
4550 except DecodeError as err:
4551 raise err.__class__(
4553 klass=self.__class__,
4554 decode_path=decode_path,
4559 klass=self.__class__,
4560 decode_path=decode_path,
4567 l, llen, v = len_decode(lv)
4568 except LenIndefForm as err:
4569 if not ctx.get("bered", False):
4570 raise err.__class__(
4572 klass=self.__class__,
4573 decode_path=decode_path,
4576 l, llen, v = 0, 1, lv[1:]
4578 except DecodeError as err:
4579 raise err.__class__(
4581 klass=self.__class__,
4582 decode_path=decode_path,
4586 raise NotEnoughData(
4587 "encoded length is longer than data",
4588 klass=self.__class__,
4592 v, tail = v[:l], v[l:]
4594 sub_offset = offset + tlen + llen
4596 specs_items = self.specs.items
4598 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4600 for name, spec in specs_items():
4601 sub_decode_path = decode_path + (name,)
4607 decode_path=sub_decode_path,
4616 klass=self.__class__,
4617 decode_path=decode_path,
4620 value, v_tail = spec.decode(
4624 decode_path=sub_decode_path,
4627 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4628 sub_offset += value_len
4631 if spec.default is None or value != spec.default: # pragma: no cover
4632 # SeqMixing.test_encoded_default_accepted covers that place
4633 values[name] = value
4634 obj = self.__class__(
4638 default=self.default,
4639 optional=self.optional,
4640 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4645 msg="not all values are ready",
4646 klass=self.__class__,
4647 decode_path=decode_path,
4650 obj.lenindef = lenindef
4651 return obj, (v[EOC_LEN:] if lenindef else tail)
4654 class SequenceOf(Obj):
4655 """``SEQUENCE OF`` sequence type
4657 For that kind of type you must specify the object it will carry on
4658 (bounds are for example here, not required)::
4660 class Ints(SequenceOf):
4665 >>> ints.append(Integer(123))
4666 >>> ints.append(Integer(234))
4668 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4669 >>> [int(i) for i in ints]
4671 >>> ints.append(Integer(345))
4672 Traceback (most recent call last):
4673 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4676 >>> ints[1] = Integer(345)
4678 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4680 Also you can initialize sequence with preinitialized values:
4682 >>> ints = Ints([Integer(123), Integer(234)])
4684 __slots__ = ("spec", "_bound_min", "_bound_max")
4685 tag_default = tag_encode(form=TagFormConstructed, num=16)
4686 asn1_type_name = "SEQUENCE OF"
4699 super(SequenceOf, self).__init__(
4707 schema = getattr(self, "schema", None)
4709 raise ValueError("schema must be specified")
4711 self._bound_min, self._bound_max = getattr(
4715 ) if bounds is None else bounds
4717 if value is not None:
4718 self._value = self._value_sanitize(value)
4719 if default is not None:
4720 default_value = self._value_sanitize(default)
4721 default_obj = self.__class__(
4726 default_obj._value = default_value
4727 self.default = default_obj
4729 self._value = default_obj.copy()._value
4731 def _value_sanitize(self, value):
4732 if issubclass(value.__class__, SequenceOf):
4733 value = value._value
4734 elif hasattr(value, "__iter__"):
4737 raise InvalidValueType((self.__class__, iter))
4738 if not self._bound_min <= len(value) <= self._bound_max:
4739 raise BoundsError(self._bound_min, len(value), self._bound_max)
4741 if not isinstance(v, self.spec.__class__):
4742 raise InvalidValueType((self.spec.__class__,))
4747 return all(v.ready for v in self._value)
4750 obj = self.__class__(schema=self.spec)
4751 obj._bound_min = self._bound_min
4752 obj._bound_max = self._bound_max
4754 obj._expl = self._expl
4755 obj.default = self.default
4756 obj.optional = self.optional
4757 obj.offset = self.offset
4758 obj.llen = self.llen
4759 obj.vlen = self.vlen
4760 obj._value = [v.copy() for v in self._value]
4763 def __eq__(self, their):
4764 if isinstance(their, self.__class__):
4766 self.spec == their.spec and
4767 self.tag == their.tag and
4768 self._expl == their._expl and
4769 self._value == their._value
4771 if hasattr(their, "__iter__"):
4772 return self._value == list(their)
4784 return self.__class__(
4788 (self._bound_min, self._bound_max)
4789 if bounds is None else bounds
4791 impl=self.tag if impl is None else impl,
4792 expl=self._expl if expl is None else expl,
4793 default=self.default if default is None else default,
4794 optional=self.optional if optional is None else optional,
4797 def __contains__(self, key):
4798 return key in self._value
4800 def append(self, value):
4801 if not isinstance(value, self.spec.__class__):
4802 raise InvalidValueType((self.spec.__class__,))
4803 if len(self._value) + 1 > self._bound_max:
4806 len(self._value) + 1,
4809 self._value.append(value)
4812 self._assert_ready()
4813 return iter(self._value)
4816 self._assert_ready()
4817 return len(self._value)
4819 def __setitem__(self, key, value):
4820 if not isinstance(value, self.spec.__class__):
4821 raise InvalidValueType((self.spec.__class__,))
4822 self._value[key] = self.spec(value=value)
4824 def __getitem__(self, key):
4825 return self._value[key]
4827 def _encoded_values(self):
4828 return [v.encode() for v in self._value]
4831 v = b"".join(self._encoded_values())
4832 return b"".join((self.tag, len_encode(len(v)), v))
4834 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4836 t, tlen, lv = tag_strip(tlv)
4837 except DecodeError as err:
4838 raise err.__class__(
4840 klass=self.__class__,
4841 decode_path=decode_path,
4846 klass=self.__class__,
4847 decode_path=decode_path,
4854 l, llen, v = len_decode(lv)
4855 except LenIndefForm as err:
4856 if not ctx.get("bered", False):
4857 raise err.__class__(
4859 klass=self.__class__,
4860 decode_path=decode_path,
4863 l, llen, v = 0, 1, lv[1:]
4865 except DecodeError as err:
4866 raise err.__class__(
4868 klass=self.__class__,
4869 decode_path=decode_path,
4873 raise NotEnoughData(
4874 "encoded length is longer than data",
4875 klass=self.__class__,
4876 decode_path=decode_path,
4880 v, tail = v[:l], v[l:]
4882 sub_offset = offset + tlen + llen
4886 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4888 value, v_tail = spec.decode(
4892 decode_path=decode_path + (str(len(_value)),),
4895 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4896 sub_offset += value_len
4899 _value.append(value)
4900 obj = self.__class__(
4903 bounds=(self._bound_min, self._bound_max),
4906 default=self.default,
4907 optional=self.optional,
4908 _decoded=(offset, llen, vlen),
4910 obj.lenindef = lenindef
4911 return obj, (v[EOC_LEN:] if lenindef else tail)
4915 pp_console_row(next(self.pps())),
4916 ", ".join(repr(v) for v in self._value),
4919 def pps(self, decode_path=()):
4921 asn1_type_name=self.asn1_type_name,
4922 obj_name=self.__class__.__name__,
4923 decode_path=decode_path,
4924 optional=self.optional,
4925 default=self == self.default,
4926 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4927 expl=None if self._expl is None else tag_decode(self._expl),
4932 expl_offset=self.expl_offset if self.expled else None,
4933 expl_tlen=self.expl_tlen if self.expled else None,
4934 expl_llen=self.expl_llen if self.expled else None,
4935 expl_vlen=self.expl_vlen if self.expled else None,
4936 expl_lenindef=self.expl_lenindef,
4937 lenindef=self.lenindef,
4939 for i, value in enumerate(self._value):
4940 yield value.pps(decode_path=decode_path + (str(i),))
4943 class SetOf(SequenceOf):
4944 """``SET OF`` sequence type
4946 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4949 tag_default = tag_encode(form=TagFormConstructed, num=17)
4950 asn1_type_name = "SET OF"
4953 raws = self._encoded_values()
4956 return b"".join((self.tag, len_encode(len(v)), v))
4959 def obj_by_path(pypath): # pragma: no cover
4960 """Import object specified as string Python path
4962 Modules must be separated from classes/functions with ``:``.
4964 >>> obj_by_path("foo.bar:Baz")
4965 <class 'foo.bar.Baz'>
4966 >>> obj_by_path("foo.bar:Baz.boo")
4967 <classmethod 'foo.bar.Baz.boo'>
4969 mod, objs = pypath.rsplit(":", 1)
4970 from importlib import import_module
4971 obj = import_module(mod)
4972 for obj_name in objs.split("."):
4973 obj = getattr(obj, obj_name)
4977 def generic_decoder(): # pragma: no cover
4978 # All of this below is a big hack with self references
4979 choice = PrimitiveTypes()
4980 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4981 choice.specs["SetOf"] = SetOf(schema=choice)
4983 choice.specs["SequenceOf%d" % i] = SequenceOf(
4987 choice.specs["Any"] = Any()
4989 # Class name equals to type name, to omit it from output
4990 class SEQUENCEOF(SequenceOf):
4994 def pprint_any(obj, oids=None, with_colours=False):
4995 def _pprint_pps(pps):
4997 if hasattr(pp, "_fields"):
4998 if pp.asn1_type_name == Choice.asn1_type_name:
5000 pp_kwargs = pp._asdict()
5001 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5002 pp = _pp(**pp_kwargs)
5003 yield pp_console_row(
5008 with_colours=with_colours,
5010 for row in pp_console_blob(pp):
5013 for row in _pprint_pps(pp):
5015 return "\n".join(_pprint_pps(obj.pps()))
5016 return SEQUENCEOF(), pprint_any
5019 def main(): # pragma: no cover
5021 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
5022 parser.add_argument(
5026 help="Skip that number of bytes from the beginning",
5028 parser.add_argument(
5030 help="Python path to dictionary with OIDs",
5032 parser.add_argument(
5034 help="Python path to schema definition to use",
5036 parser.add_argument(
5037 "--defines-by-path",
5038 help="Python path to decoder's defines_by_path",
5040 parser.add_argument(
5042 type=argparse.FileType("rb"),
5043 help="Path to DER file you want to decode",
5045 args = parser.parse_args()
5046 args.DERFile.seek(args.skip)
5047 der = memoryview(args.DERFile.read())
5048 args.DERFile.close()
5049 oids = obj_by_path(args.oids) if args.oids else {}
5051 schema = obj_by_path(args.schema)
5052 from functools import partial
5053 pprinter = partial(pprint, big_blobs=True)
5055 schema, pprinter = generic_decoder()
5056 ctx = {"bered": True}
5057 if args.defines_by_path is not None:
5058 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5059 obj, tail = schema().decode(der, ctx=ctx)
5063 with_colours=True if environ.get("NO_COLOR") is None else False,
5066 print("\nTrailing data: %s" % hexenc(tail))
5069 if __name__ == "__main__":