Strict default values existence validation option
authorSergey Matveev <stargrave@stargrave.org>
Tue, 2 Jan 2018 10:04:38 +0000 (13:04 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 2 Jan 2018 10:04:38 +0000 (13:04 +0300)
doc/news.rst
pyderasn.py
tests/test_pyderasn.py

index 914a9f80ebd55065dd603feea02840b7ce335f7c..692f5cc87ab8ec8bb684704acc416f0b62bd0ee6 100644 (file)
@@ -10,6 +10,8 @@ News
   keyword argument to ``decode()`` method
 * :ref:`defines_by_path <defines_by_path_ctx>` option is now
   :ref:`decode context <ctx>` option, not a keyword argument
+* Ability to do :ref:`strict validation <strict_default_existence_ctx>`
+  of defaulted values met in sequence, raising an exception
 
 .. _release1.6:
 
index b4a10d9fc04ba14fcf91a86ce21f2490cf5c41fb..cc44e24c91293ca4ffe85ba7684279e87718925c 100755 (executable)
@@ -207,6 +207,7 @@ decoding process.
 Currently available context options:
 
 * :ref:`defines_by_path <defines_by_path_ctx>`
+* :ref:`strict_default_existence <strict_default_existence_ctx>`
 
 .. _pprinting:
 
@@ -3660,13 +3661,18 @@ class Sequence(Obj):
 
     All defaulted values are always optional.
 
+    .. _strict_default_existence_ctx:
+
     .. warning::
 
        When decoded DER contains defaulted value inside, then
-       technically this is not valid DER encoding. But we allow
-       and pass it. Of course reencoding of that kind of DER will
+       technically this is not valid DER encoding. But we allow and pass
+       it **by default**. Of course reencoding of that kind of DER will
        result in different binary representation (validly without
-       defaulted value inside).
+       defaulted value inside). You can enable strict defaulted values
+       existence validation by setting ``"strict_default_existence":
+       True`` :ref:`context <ctx>` option -- decoding process will raise
+       an exception if defaulted value is met.
 
     Two sequences are equal if they have equal specification (schema),
     implicit/explicit tagging and the same values.
@@ -3900,9 +3906,15 @@ class Sequence(Obj):
             sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
             v = v_tail
             if spec.default is not None and value == spec.default:
-                # Encoded default values are not valid in DER,
-                # but we allow that anyway
-                continue
+                if ctx.get("strict_default_existence", False):
+                    raise DecodeError(
+                        "DEFAULT value met",
+                        klass=self.__class__,
+                        decode_path=sub_decode_path,
+                        offset=sub_offset,
+                    )
+                else:
+                    continue
             values[name] = value
 
             spec_defines = getattr(spec, "defines", ())
index 498b160a23698a7ba37848c3a2cbf87e9f627034..5b98ea60cf1012c829ef6b9d396ac245e858343b 100644 (file)
@@ -5112,3 +5112,26 @@ class TestAbsDecodePath(TestCase):
             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
             decode_path[:-number_of_dots] + rel_path,
         )
+
+
+class TestStrictDefaultExistence(TestCase):
+    @given(data_strategy())
+    def runTest(self, d):
+        count = d.draw(integers(min_value=1, max_value=10))
+        chosen = d.draw(integers(min_value=0, max_value=count - 1))
+        _schema = [
+            ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
+            for i in range(count)
+        ]
+
+        class Seq(Sequence):
+            schema = _schema
+        seq = Seq()
+        for i in range(count):
+            seq["int%d" % i] = Integer(123)
+        raw = seq.encode()
+        chosen = "int%d" % chosen
+        seq.specs[chosen] = seq.specs[chosen](default=123)
+        seq.decode(raw)
+        with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+            seq.decode(raw, ctx={"strict_default_existence": True})