]> Cypherpunks.ru repositories - pyderasn.git/commitdiff
Defaulted values are checked by default
authorSergey Matveev <stargrave@stargrave.org>
Mon, 10 Sep 2018 14:45:55 +0000 (17:45 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 10 Sep 2018 18:27:45 +0000 (21:27 +0300)
VERSION
doc/news.rst
pyderasn.py
tests/test_pyderasn.py

diff --git a/VERSION b/VERSION
index 93a848f9688291c7b2172cf99f133675225ad282..5186d07068cfed4c3f4705df79c73e115dd35c43 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.15
+4.0
index 4224b3d17338e55f209efa792f3de5e3439dcdb2..d0f156f560e6bd21c663c4a859191cb192083cd8 100644 (file)
@@ -1,11 +1,14 @@
 News
 ====
 
-.. _release3.15:
+.. _release4.0:
 
-3.15
-----
-* DEFAULT-encoded value is checked also for Set-s, not for Sequences only
+4.0
+---
+* Default value is checked also for Sets, not for Sequences only
+* **Incompatible** change: defaulted values in Sequence/Set are always
+  strictly checked, unless ``allow_default_values`` context option is
+  set. ``strict_default_existence`` option disappeared
 
 .. _release3.14:
 
index 7edfc8064c2c0571404e2449a7906ea745ffd264..a7732964dedcc8befbd31da6f4778cfb1cb23dc6 100755 (executable)
@@ -213,10 +213,10 @@ decoding process.
 
 Currently available context options:
 
+* :ref:`allow_default_values <allow_default_values_ctx>`
 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
 * :ref:`bered <bered_ctx>`
 * :ref:`defines_by_path <defines_by_path_ctx>`
-* :ref:`strict_default_existence <strict_default_existence_ctx>`
 
 .. _pprinting:
 
@@ -383,7 +383,7 @@ constructed primitive types should be parsed successfully.
 
 * If object is encoded in BER form (not the DER one), then ``bered``
   attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
-  STRING`` can contain it.
+  STRING``, ``SEQUENCE``, ``SET`` can contain it.
 * If object has an indefinite length encoding, then its ``lenindef``
   attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
   ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
@@ -4338,18 +4338,14 @@ class Sequence(Obj):
 
     All defaulted values are always optional.
 
-    .. _strict_default_existence_ctx:
+    .. _allow_default_values_ctx:
 
-    .. warning::
-
-       When decoded DER contains defaulted value inside, then
-       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). 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.
+    DER prohibits default value encoding and will raise an error if
+    default value is unexpectedly met during decode.
+    If :ref:`bered <bered_ctx>` context option is set, then no error
+    will be raised, but ``bered`` attribute set. You can disable strict
+    defaulted values existence validation by setting
+    ``"allow_default_values": True`` :ref:`context <ctx>` option.
 
     Two sequences are equal if they have equal specification (schema),
     implicit/explicit tagging and the same values.
@@ -4509,10 +4505,11 @@ class Sequence(Obj):
         if tag_only:
             return
         lenindef = False
+        ctx_bered = ctx.get("bered", False)
         try:
             l, llen, v = len_decode(lv)
         except LenIndefForm as err:
-            if not ctx.get("bered", False):
+            if not ctx_bered:
                 raise err.__class__(
                     msg=err.msg,
                     klass=self.__class__,
@@ -4540,6 +4537,8 @@ class Sequence(Obj):
         vlen = 0
         sub_offset = offset + tlen + llen
         values = {}
+        bered = False
+        ctx_allow_default_values = ctx.get("allow_default_values", False)
         for name, spec in self.specs.items():
             if spec.optional and (
                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
@@ -4612,15 +4611,15 @@ class Sequence(Obj):
             sub_offset += value_len
             v = v_tail
             if spec.default is not None and value == spec.default:
-                if ctx.get("strict_default_existence", False):
+                if ctx_bered or ctx_allow_default_values:
+                    bered = True
+                else:
                     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", ())
@@ -4663,6 +4662,7 @@ class Sequence(Obj):
         )
         obj._value = values
         obj.lenindef = lenindef
+        obj.bered = bered
         return obj, tail
 
     def __repr__(self):
@@ -4738,10 +4738,11 @@ class Set(Sequence):
         if tag_only:
             return
         lenindef = False
+        ctx_bered = ctx.get("bered", False)
         try:
             l, llen, v = len_decode(lv)
         except LenIndefForm as err:
-            if not ctx.get("bered", False):
+            if not ctx_bered:
                 raise err.__class__(
                     msg=err.msg,
                     klass=self.__class__,
@@ -4768,6 +4769,8 @@ class Set(Sequence):
         vlen = 0
         sub_offset = offset + tlen + llen
         values = {}
+        bered = False
+        ctx_allow_default_values = ctx.get("allow_default_values", False)
         specs_items = self.specs.items
         while len(v) > 0:
             if lenindef and v[:EOC_LEN].tobytes() == EOC:
@@ -4803,18 +4806,18 @@ class Set(Sequence):
             sub_offset += value_len
             vlen += value_len
             v = v_tail
-            if spec.default is None:
-                values[name] = value
+            if spec.default is None or value != spec.default:
+                pass
+            elif ctx_bered or ctx_allow_default_values:
+                bered = True
             else:
-                if value != spec.default:
-                    values[name] = value
-                if ctx.get("strict_default_existence", False):
-                    raise DecodeError(
-                        "DEFAULT value met",
-                        klass=self.__class__,
-                        decode_path=sub_decode_path,
-                        offset=sub_offset,
-                    )
+                raise DecodeError(
+                    "DEFAULT value met",
+                    klass=self.__class__,
+                    decode_path=sub_decode_path,
+                    offset=sub_offset,
+                )
+            values[name] = value
         obj = self.__class__(
             schema=self.specs,
             impl=self.tag,
@@ -4824,6 +4827,7 @@ class Set(Sequence):
             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
         )
         obj._value = values
+        obj.bered = bered
         if lenindef:
             if v[:EOC_LEN].tobytes() != EOC:
                 raise DecodeError(
index 7b397f2686d6cbd174db9864de7ea593035d48b2..c8321bb0d25116a2ea4320c2ff187bed8df54499 100644 (file)
@@ -4749,6 +4749,7 @@ class SeqMixing(object):
         seq_encoded = seq.encode()
         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
         self.assertFalse(seq_decoded.lenindef)
+        self.assertFalse(seq_decoded.bered)
 
         t, _, lv = tag_strip(seq_encoded)
         _, _, v = len_decode(lv)
@@ -4840,7 +4841,7 @@ class SeqMixing(object):
         self.assertSequenceEqual(seq.encode(), empty_seq)
 
     @given(data_strategy())
-    def test_encoded_default_accepted(self, d):
+    def test_encoded_default_not_accepted(self, d):
         _schema = list(d.draw(dictionaries(
             text_letters(),
             integers(),
@@ -4868,10 +4869,14 @@ class SeqMixing(object):
                 for (n, v), t in zip(_schema, tags)
             ]
         seq_with_default = SeqWithDefault()
-        seq_decoded, _ = seq_with_default.decode(seq_encoded)
-        for name, value in _schema:
-            self.assertEqual(seq_decoded[name], seq_with_default[name])
-            self.assertEqual(seq_decoded[name], value)
+        with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+            seq_with_default.decode(seq_encoded)
+        for ctx in ({"bered": True}, {"allow_default_values": True}):
+            seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
+            self.assertTrue(seq_decoded.bered)
+            for name, value in _schema:
+                self.assertEqual(seq_decoded[name], seq_with_default[name])
+                self.assertEqual(seq_decoded[name], value)
 
     @given(data_strategy())
     def test_missing_from_spec(self, d):
@@ -5880,9 +5885,12 @@ class TestStrictDefaultExistence(TestCase):
             raw = seq.encode()
             chosen_choice = "int%d" % chosen
             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
-            seq.decode(raw)
             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
-                seq.decode(raw, ctx={"strict_default_existence": True})
+                seq.decode(raw)
+            decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
+            self.assertTrue(decoded.bered)
+            decoded, _ = seq.decode(raw, ctx={"bered": True})
+            self.assertTrue(decoded.bered)
 
 
 class TestX690PrefixedType(TestCase):