]> Cypherpunks.ru repositories - pyderasn.git/commitdiff
Convenient decod() helper method
authorSergey Matveev <stargrave@stargrave.org>
Thu, 23 Jan 2020 13:58:56 +0000 (16:58 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 23 Jan 2020 16:01:20 +0000 (19:01 +0300)
VERSION
doc/install.rst
doc/news.rst
pyderasn.py
tests/test_pyderasn.py

diff --git a/VERSION b/VERSION
index 9ad974f6109e4bf8a9c47b6d1870e2b4990d9841..2df33d76977064e686bccc1b213c1c9186354aba 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.5
+5.6
index ee3d0fc5860c7359354915fe1998c314320a0baf..40d93837c72543652b071232dc5c69951394eaff 100644 (file)
@@ -4,11 +4,11 @@ Install
 Preferable way is to :ref:`download <download>` tarball with the
 signature from `official website <http://pyderasn.cypherpunks.ru/>`__::
 
-    $ [fetch|wget] http://pyderasn.cypherpunks.ru/pyderasn-5.5.tar.xz
-    $ [fetch|wget] http://pyderasn.cypherpunks.ru/pyderasn-5.5.tar.xz.sig
-    $ gpg --verify pyderasn-5.5.tar.xz.sig pyderasn-5.5.tar.xz
-    $ xz --decompress --stdout pyderasn-5.5.tar.xz | tar xf -
-    $ cd pyderasn-5.5
+    $ [fetch|wget] http://pyderasn.cypherpunks.ru/pyderasn-5.6.tar.xz
+    $ [fetch|wget] http://pyderasn.cypherpunks.ru/pyderasn-5.6.tar.xz.sig
+    $ gpg --verify pyderasn-5.6.tar.xz.sig pyderasn-5.6.tar.xz
+    $ xz --decompress --stdout pyderasn-5.6.tar.xz | tar xf -
+    $ cd pyderasn-5.6
     $ python setup.py install
     # or copy pyderasn.py (+six.py, possibly termcolor.py) to your PYTHONPATH
 
@@ -19,7 +19,7 @@ You can also find it mirrored on :ref:`download <download>` page.
 You could use pip (**no** OpenPGP authentication is performed!) with PyPI::
 
     $ cat > requirements.txt <<EOF
-    pyderasn==5.5 --hash=sha256:TODO
+    pyderasn==5.6 --hash=sha256:TODO
     six==1.13.0 --hash=sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66
     EOF
     $ pip install --requirement requirements.txt
index 94dca18d222605d853470ad8e95bd3b09bca8d6e..3fbddd0325b56aa6c2c7fb8fcaa3f8d605fa6d6e 100644 (file)
@@ -1,6 +1,12 @@
 News
 ====
 
+.. _release5.6:
+
+5.6
+---
+* Convenient ``.decod()`` method, that raises if tail is not empty
+
 .. _release5.5:
 
 5.5
index 87bb05665a08659cc36e48a7db270cafde75b2b0..5f0c0343ccd0d4153d29e6328fde4a73882229ec 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
@@ -677,7 +678,7 @@ except ImportError:  # pragma: no cover
     def colored(what, *args, **kwargs):
         return what
 
-__version__ = "5.5"
+__version__ = "5.6"
 
 __all__ = (
     "Any",
@@ -689,6 +690,7 @@ __all__ = (
     "DecodeError",
     "DecodePathDefBy",
     "Enumerated",
+    "ExceedingData",
     "GeneralizedTime",
     "GeneralString",
     "GraphicString",
@@ -799,6 +801,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 +1278,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`
index edf6a7b440f0a6989836d01c0e2a2e2f1344e01d..9fbb33eb39f2d75231721cee73de95a32d66d717 100644 (file)
@@ -65,6 +65,7 @@ from pyderasn import DecodePathDefBy
 from pyderasn import Enumerated
 from pyderasn import EOC
 from pyderasn import EOC_LEN
+from pyderasn import ExceedingData
 from pyderasn import GeneralizedTime
 from pyderasn import GeneralString
 from pyderasn import GraphicString
@@ -135,6 +136,12 @@ decode_path_strat = lists(integers(), max_size=3).map(
 ctx_dummy = dictionaries(integers(), integers(), min_size=2, max_size=4).example()
 
 
+def assert_exceeding_data(self, call, junk):
+    if len(junk) > 0:
+        with assertRaisesRegex(self, ExceedingData, "%d trailing bytes" % len(junk)):
+            call()
+
+
 class TestHex(TestCase):
     @given(binary())
     def test_symmetric(self, data):
@@ -591,6 +598,11 @@ class TestBoolean(CommonMixin, TestCase):
                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
             )
             self.assertEqual(obj_decoded.expl_offset, offset)
+            assert_exceeding_data(
+                self,
+                lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+                tail_junk,
+            )
 
     @given(integers(min_value=2))
     def test_invalid_len(self, l):
@@ -1083,6 +1095,11 @@ class TestInteger(CommonMixin, TestCase):
                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
             )
             self.assertEqual(obj_decoded.expl_offset, offset)
+            assert_exceeding_data(
+                self,
+                lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+                tail_junk,
+            )
 
     def test_go_vectors_valid(self):
         for data, expect in ((
@@ -1473,6 +1490,11 @@ class TestBitString(CommonMixin, TestCase):
                 self.assertSetEqual(set(value), set(obj_decoded.named))
                 for name in value:
                     obj_decoded[name]
+            assert_exceeding_data(
+                self,
+                lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+                tail_junk,
+            )
 
     @given(integers(min_value=1, max_value=255))
     def test_bad_zero_value(self, pad_size):
@@ -2058,6 +2080,11 @@ class TestOctetString(CommonMixin, TestCase):
                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
             )
             self.assertEqual(obj_decoded.expl_offset, offset)
+            assert_exceeding_data(
+                self,
+                lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+                tail_junk,
+            )
 
     @given(
         integers(min_value=1, max_value=30),
@@ -2360,6 +2387,11 @@ class TestNull(CommonMixin, TestCase):
                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
             )
             self.assertEqual(obj_decoded.expl_offset, offset)
+            assert_exceeding_data(
+                self,
+                lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+                tail_junk,
+            )
 
     @given(integers(min_value=1))
     def test_invalid_len(self, l):
@@ -2698,6 +2730,11 @@ class TestObjectIdentifier(CommonMixin, TestCase):
                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
             )
             self.assertEqual(obj_decoded.expl_offset, offset)
+            assert_exceeding_data(
+                self,
+                lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+                tail_junk,
+            )
 
     @given(
         oid_strategy().map(ObjectIdentifier),
@@ -3041,6 +3078,11 @@ class TestEnumerated(CommonMixin, TestCase):
             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
         )
         self.assertEqual(obj_decoded.expl_offset, offset)
+        assert_exceeding_data(
+            self,
+            lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+            tail_junk,
+        )
 
 
 @composite
@@ -3371,6 +3413,11 @@ class StringMixin(object):
             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
         )
         self.assertEqual(obj_decoded.expl_offset, offset)
+        assert_exceeding_data(
+            self,
+            lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+            tail_junk,
+        )
 
 
 class TestUTF8String(StringMixin, CommonMixin, TestCase):
@@ -3806,6 +3853,11 @@ class TimeMixin(object):
             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
         )
         self.assertEqual(obj_decoded.expl_offset, offset)
+        assert_exceeding_data(
+            self,
+            lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+            tail_junk,
+        )
 
 
 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
@@ -4246,6 +4298,11 @@ class TestAny(CommonMixin, TestCase):
             self.assertEqual(obj_decoded.tlen, 0)
             self.assertEqual(obj_decoded.llen, 0)
             self.assertEqual(obj_decoded.vlen, len(value))
+            assert_exceeding_data(
+                self,
+                lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+                tail_junk,
+            )
 
     @given(
         integers(min_value=1).map(tag_ctxc),
@@ -4610,6 +4667,11 @@ class TestChoice(CommonMixin, TestCase):
             ],
             obj_encoded,
         )
+        assert_exceeding_data(
+            self,
+            lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+            tail_junk,
+        )
 
     @given(integers())
     def test_set_get(self, value):
@@ -5128,6 +5190,12 @@ class SeqMixing(object):
                     obj.encode(),
                 )
 
+        assert_exceeding_data(
+            self,
+            lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
+            tail_junk,
+        )
+
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(data_strategy())
     def test_symmetric_with_seq(self, d):
@@ -5814,6 +5882,12 @@ class SeqOfMixing(object):
         with self.assertRaises(DecodeError):
             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
 
+        assert_exceeding_data(
+            self,
+            lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+            tail_junk,
+        )
+
     def test_bered(self):
         class SeqOf(self.base_klass):
             schema = Boolean()