]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - pyderasn.py
Slightly more dense super calls
[pyderasn.git] / pyderasn.py
index f563afef86964240e57971d85c91fa35d40de0c8..2972db70407fe7ce715f8e1cd47ddf687c360d6b 100755 (executable)
@@ -634,6 +634,7 @@ Various
 .. autoclass:: pyderasn.DecodeError
    :members: __init__
 .. autoclass:: pyderasn.NotEnoughData
+.. autoclass:: pyderasn.ExceedingData
 .. autoclass:: pyderasn.LenIndefForm
 .. autoclass:: pyderasn.TagMismatch
 .. autoclass:: pyderasn.InvalidLength
@@ -654,6 +655,7 @@ from math import ceil
 from os import environ
 from string import ascii_letters
 from string import digits
+from unicodedata import category as unicat
 
 from six import add_metaclass
 from six import binary_type
@@ -677,7 +679,7 @@ except ImportError:  # pragma: no cover
     def colored(what, *args, **kwargs):
         return what
 
-__version__ = "5.5"
+__version__ = "5.6"
 
 __all__ = (
     "Any",
@@ -689,6 +691,7 @@ __all__ = (
     "DecodeError",
     "DecodePathDefBy",
     "Enumerated",
+    "ExceedingData",
     "GeneralizedTime",
     "GeneralString",
     "GraphicString",
@@ -799,6 +802,18 @@ class NotEnoughData(DecodeError):
     pass
 
 
+class ExceedingData(ASN1Error):
+    def __init__(self, nbytes):
+        super(ExceedingData, self).__init__()
+        self.nbytes = nbytes
+
+    def __str__(self):
+        return "%d trailing bytes" % self.nbytes
+
+    def __repr__(self):
+        return "%s(%s)" % (self.__class__.__name__, self)
+
+
 class LenIndefForm(DecodeError):
     pass
 
@@ -1264,6 +1279,26 @@ class Obj(object):
                     )
         return obj, (tail if leavemm else tail.tobytes())
 
+    def decod(self, data, offset=0, decode_path=(), ctx=None):
+        """Decode the data, check that tail is empty
+
+        :raises ExceedingData: if tail is not empty
+
+        This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
+        (decode without tail) that also checks that there is no
+        trailing data left.
+        """
+        obj, tail = self.decode(
+            data,
+            offset=offset,
+            decode_path=decode_path,
+            ctx=ctx,
+            leavemm=True,
+        )
+        if len(tail) > 0:
+            raise ExceedingData(len(tail))
+        return obj
+
     @property
     def expled(self):
         """See :ref:`decoding`
@@ -2437,7 +2472,7 @@ class BitString(Obj):
             octets,
         ))
 
-    def _decode_chunk(self, lv, offset, decode_path, ctx):
+    def _decode_chunk(self, lv, offset, decode_path):
         try:
             l, llen, v = len_decode(lv)
         except DecodeError as err:
@@ -2508,7 +2543,7 @@ class BitString(Obj):
         if t == self.tag:
             if tag_only:  # pragma: no cover
                 return None
-            return self._decode_chunk(lv, offset, decode_path, ctx)
+            return self._decode_chunk(lv, offset, decode_path)
         if t == self.tag_constructed:
             if not ctx.get("bered", False):
                 raise DecodeError(
@@ -2716,13 +2751,7 @@ class OctetString(Obj):
         :param default: set default value. Type same as in ``value``
         :param bool optional: is object ``OPTIONAL`` in sequence
         """
-        super(OctetString, self).__init__(
-            impl,
-            expl,
-            default,
-            optional,
-            _decoded,
-        )
+        super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
         self._value = value
         self._bound_min, self._bound_max = getattr(
             self,
@@ -2827,7 +2856,7 @@ class OctetString(Obj):
             self._value,
         ))
 
-    def _decode_chunk(self, lv, offset, decode_path, ctx):
+    def _decode_chunk(self, lv, offset, decode_path):
         try:
             l, llen, v = len_decode(lv)
         except DecodeError as err:
@@ -2884,7 +2913,7 @@ class OctetString(Obj):
         if t == self.tag:
             if tag_only:
                 return None
-            return self._decode_chunk(lv, offset, decode_path, ctx)
+            return self._decode_chunk(lv, offset, decode_path)
         if t == self.tag_constructed:
             if not ctx.get("bered", False):
                 raise DecodeError(
@@ -3212,13 +3241,7 @@ class ObjectIdentifier(Obj):
         :param default: set default value. Type same as in ``value``
         :param bool optional: is object ``OPTIONAL`` in sequence
         """
-        super(ObjectIdentifier, self).__init__(
-            impl,
-            expl,
-            default,
-            optional,
-            _decoded,
-        )
+        super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
         self._value = value
         if value is not None:
             self._value = self._value_sanitize(value)
@@ -3489,13 +3512,7 @@ class Enumerated(Integer):
             bounds=None,  # dummy argument, workability for Integer.decode
     ):
         super(Enumerated, self).__init__(
-            value=value,
-            impl=impl,
-            expl=expl,
-            default=default,
-            optional=optional,
-            _specs=_specs,
-            _decoded=_decoded,
+            value, bounds, impl, expl,default, optional, _specs, _decoded,
         )
         if len(self.specs) == 0:
             raise ValueError("schema must be specified")
@@ -3556,6 +3573,12 @@ class Enumerated(Integer):
         )
 
 
+def escape_control_unicode(c):
+    if unicat(c).startswith("C"):
+        c = repr(c).lstrip("u").strip("'")
+    return c
+
+
 class CommonString(OctetString):
     """Common class for all strings
 
@@ -3674,7 +3697,10 @@ class CommonString(OctetString):
     def pps(self, decode_path=(), no_unicode=False):
         value = None
         if self.ready:
-            value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
+            value = (
+                hexenc(bytes(self)) if no_unicode else
+                "".join(escape_control_unicode(c) for c in self.__unicode__())
+            )
         yield _pp(
             obj=self,
             asn1_type_name=self.asn1_type_name,
@@ -3834,11 +3860,7 @@ class UTCTime(CommonString):
         :param bool optional: is object ``OPTIONAL`` in sequence
         """
         super(UTCTime, self).__init__(
-            impl=impl,
-            expl=expl,
-            default=default,
-            optional=optional,
-            _decoded=_decoded,
+            None, None, impl, expl, default, optional, _decoded,
         )
         self._value = value
         if value is not None:
@@ -5287,13 +5309,7 @@ class SequenceOf(Obj):
             optional=False,
             _decoded=(0, 0, 0),
     ):
-        super(SequenceOf, self).__init__(
-            impl,
-            expl,
-            default,
-            optional,
-            _decoded,
-        )
+        super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
         if schema is None:
             schema = getattr(self, "schema", None)
         if schema is None:
@@ -5752,7 +5768,7 @@ def main():  # pragma: no cover
     print(pprinter(
         obj,
         oid_maps=oid_maps,
-        with_colours=True if environ.get("NO_COLOR") is None else False,
+        with_colours=environ.get("NO_COLOR") is None,
         with_decode_path=args.print_decode_path,
         decode_path_only=(
             () if args.decode_path_only is None else