]> Cypherpunks.ru repositories - pyderasn.git/commitdiff
Strict DEFAULT check in evgen mode 7.3
authorSergey Matveev <stargrave@stargrave.org>
Tue, 18 Feb 2020 07:04:48 +0000 (10:04 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 18 Feb 2020 07:25:53 +0000 (10:25 +0300)
VERSION
doc/install.rst
doc/news.rst
pyderasn.py
tests/test_pyderasn.py

diff --git a/VERSION b/VERSION
index 5904f7adec1a0d8e5e10d8e72fcff747645ef796..f99416236e9fc148fb5b694705c04492d60793dd 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-7.2
+7.3
index e57faadedc7a443c1262a0a5a838970b6fb8378e..3d380eaa04c7b7cd047e03306c49a4ff291ee7fd 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-7.2.tar.xz
-    $ [fetch|wget] http://pyderasn.cypherpunks.ru/pyderasn-7.2.tar.xz.sig
-    $ gpg --verify pyderasn-7.2.tar.xz.sig pyderasn-7.2.tar.xz
-    $ xz --decompress --stdout pyderasn-7.2.tar.xz | tar xf -
-    $ cd pyderasn-7.2
+    $ [fetch|wget] http://pyderasn.cypherpunks.ru/pyderasn-7.3.tar.xz
+    $ [fetch|wget] http://pyderasn.cypherpunks.ru/pyderasn-7.3.tar.xz.sig
+    $ gpg --verify pyderasn-7.3.tar.xz.sig pyderasn-7.3.tar.xz
+    $ xz --decompress --stdout pyderasn-7.3.tar.xz | tar xf -
+    $ cd pyderasn-7.3
     $ 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==7.2 --hash=sha256:TO-BE-FILLED
+    pyderasn==7.3 --hash=sha256:TO-BE-FILLED
     six==1.14.0 --hash=sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a
     EOF
     $ pip install --requirement requirements.txt
index 919944a05005a9ed61426f317be2c511d9ea5036..aaf834ce90c8baf59f690a9291d52857a3c76237 100644 (file)
@@ -1,6 +1,16 @@
 News
 ====
 
+.. _release7.3:
+
+7.3
+---
+
+* SEQUENCE/SET fully performs default value existence validation in
+  evgen mode, by the cost of DEFAULTed value full decoding. That should
+  not be the problem, as DEFAULTs are relatively small in practice. Now
+  evgen mode has the same DER validation strictness as an ordinary one
+
 .. _release7.2:
 
 7.2
index 62e884ba9357bfc49f21f77c19c7cd90fe21f954..60d9cf0513f89503afeb749cab4e43c932c00993 100755 (executable)
@@ -665,6 +665,11 @@ parse the CRL above with fully assembled ``RevokedCertificate``::
         ):
             print("serial number:", int(obj["userCertificate"]))
 
+.. note::
+
+   SEQUENCE/SET values with DEFAULT specified are automatically decoded
+   without evgen mode.
+
 .. _mmap:
 
 mmap-ed file
@@ -1180,7 +1185,7 @@ except ImportError:  # pragma: no cover
     def colored(what, *args, **kwargs):
         return what
 
-__version__ = "7.2"
+__version__ = "7.3"
 
 __all__ = (
     "agg_octet_string",
@@ -6195,11 +6200,10 @@ class Sequence(SequenceEncode1stMixing, Obj):
     defaulted values existence validation by setting
     ``"allow_default_values": True`` :ref:`context <ctx>` option.
 
-    .. warning::
-
-       Check for default value existence is not performed in
-       ``evgen_mode``, because previously decoded values are not stored
-       in memory, to be able to compare them.
+    All values with DEFAULT specified are decoded atomically in
+    :ref:`evgen mode <evgen_mode>`. If DEFAULT value is some kind of
+    SEQUENCE, then it will be yielded as a single element, not
+    disassembled. That is required for DEFAULT existence check.
 
     Two sequences are equal if they have equal specification (schema),
     implicit/explicit tagging and the same values.
@@ -6425,9 +6429,10 @@ class Sequence(SequenceEncode1stMixing, Obj):
                     len(v) == 0
             ):
                 continue
+            spec_defaulted = spec.default is not None
             sub_decode_path = decode_path + (name,)
             try:
-                if evgen_mode:
+                if evgen_mode and not spec_defaulted:
                     for _decode_path, value, v_tail in spec.decode_evgen(
                             v,
                             sub_offset,
@@ -6505,9 +6510,10 @@ class Sequence(SequenceEncode1stMixing, Obj):
             vlen += value_len
             sub_offset += value_len
             v = v_tail
-            if not evgen_mode:
-                if spec.default is not None and value == spec.default:
-                    # This will not work in evgen_mode
+            if spec_defaulted:
+                if evgen_mode:
+                    yield sub_decode_path, value, v_tail
+                if value == spec.default:
                     if ctx_bered or ctx_allow_default_values:
                         ber_encoded = True
                     else:
@@ -6517,6 +6523,7 @@ class Sequence(SequenceEncode1stMixing, Obj):
                             decode_path=sub_decode_path,
                             offset=sub_offset,
                         )
+            if not evgen_mode:
                 values[name] = value
                 spec_defines = getattr(spec, "defines", ())
                 if len(spec_defines) == 0:
@@ -6716,7 +6723,8 @@ class Set(Sequence, SequenceEncode1stMixing):
                     decode_path=decode_path,
                     offset=offset,
                 )
-            if evgen_mode:
+            spec_defaulted = spec.default is not None
+            if evgen_mode and not spec_defaulted:
                 for _decode_path, value, v_tail in spec.decode_evgen(
                         v,
                         sub_offset,
@@ -6748,17 +6756,20 @@ class Set(Sequence, SequenceEncode1stMixing):
                         decode_path=sub_decode_path,
                         offset=sub_offset,
                     )
-            if spec.default is None or value != spec.default:
-                pass
-            elif ctx_bered or ctx_allow_default_values:
-                ber_encoded = True
-            else:
-                raise DecodeError(
-                    "DEFAULT value met",
-                    klass=self.__class__,
-                    decode_path=sub_decode_path,
-                    offset=sub_offset,
-                )
+            if spec_defaulted:
+                if evgen_mode:
+                    yield sub_decode_path, value, v_tail
+                if value != spec.default:
+                    pass
+                elif ctx_bered or ctx_allow_default_values:
+                    ber_encoded = True
+                else:
+                    raise DecodeError(
+                        "DEFAULT value met",
+                        klass=self.__class__,
+                        decode_path=sub_decode_path,
+                        offset=sub_offset,
+                    )
             values[name] = value
             del _specs_items[name]
             tag_order_prev = value_tag_order
index 46e10e53abdbb12848d8be51d21d58f43ff0a11b..b16c6f6cc7fd318c6534599d3887ef1415688dbc 100644 (file)
@@ -6274,25 +6274,34 @@ class SeqMixing(object):
             min_size=len(_schema),
             max_size=len(_schema),
         ))]
+        class Wahl(Choice):
+            schema = (("int", Integer()),)
 
         class SeqWithoutDefault(self.base_klass):
             schema = [
-                (n, Integer(impl=t))
+                (n, Wahl(expl=t))
                 for (n, _), t in zip(_schema, tags)
             ]
         seq_without_default = SeqWithoutDefault()
         for name, value in _schema:
-            seq_without_default[name] = Integer(value)
+            seq_without_default[name] = Wahl(("int", Integer(value)))
         seq_encoded = seq_without_default.encode()
+        seq_without_default.decode(seq_encoded)
+        self.assertEqual(
+            len(list(seq_without_default.decode_evgen(seq_encoded))),
+            len(_schema) * 2 + 1,
+        )
 
         class SeqWithDefault(self.base_klass):
             schema = [
-                (n, Integer(default=v, impl=t))
+                (n, Wahl(default=Wahl(("int", Integer(v))), expl=t))
                 for (n, v), t in zip(_schema, tags)
             ]
         seq_with_default = SeqWithDefault()
         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
             seq_with_default.decode(seq_encoded)
+        with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+            list(seq_with_default.decode_evgen(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.ber_encoded)
@@ -6302,7 +6311,21 @@ class SeqMixing(object):
             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)
+                self.assertEqual(seq_decoded[name].value, value)
+            self.assertEqual(
+                len(list(seq_with_default.decode_evgen(seq_encoded, ctx=ctx))),
+                len(_schema) + 1,
+            )
+
+        seq_without_default = SeqWithoutDefault()
+        for name, value in _schema:
+            seq_without_default[name] = Wahl(("int", Integer(value + 1)))
+        seq_encoded = seq_without_default.encode()
+        seq_with_default.decode(seq_encoded)
+        self.assertEqual(
+            len(list(seq_with_default.decode_evgen(seq_encoded))),
+            len(_schema) + 1,
+        )
 
     @given(data_strategy())
     def test_missing_from_spec(self, d):
@@ -6327,6 +6350,8 @@ class SeqMixing(object):
         seq_missing = SeqMissing()
         with self.assertRaises(TagMismatch):
             seq_missing.decode(seq_encoded)
+        with self.assertRaises(TagMismatch):
+            list(seq_missing.decode_evgen(seq_encoded))
 
     def test_bered(self):
         class Seq(self.base_klass):
@@ -6353,6 +6378,9 @@ class SeqMixing(object):
         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
         with self.assertRaises(DecodeError):
             Seq().decode(encoded)
+        with self.assertRaises(DecodeError):
+            list(Seq().decode_evgen(encoded))
+        list(Seq().decode_evgen(encoded, ctx={"bered": True}))
         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
         self.assertFalse(decoded.ber_encoded)
         self.assertFalse(decoded.lenindef)