unit test detection of non-consecutive ivs
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Thu, 30 Jan 2020 13:28:06 +0000 (14:28 +0100)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Sat, 1 Feb 2020 13:42:43 +0000 (14:42 +0100)
The detection works as expected except for a bug in the exception
formatting.

deltatar/crypto.py
testing/test_crypto.py

index 53150f1..d18d406 100755 (executable)
@@ -1367,7 +1367,7 @@ class Decrypt (Crypto):
                 and self.last_iv [1] != cnt - 1:
             raise NonConsecutiveIV ("iv %s counter not successor of "
                                     "last object (expected %d, found %d)"
-                                    % (fixed, iv_fmt (self.last_iv [1]), cnt))
+                                    % (iv_fmt (iv), self.last_iv [1], cnt))
         self.last_iv = (fixed, cnt)
 
 
index dc652e2..41b38d7 100644 (file)
@@ -618,6 +618,73 @@ class AESGCMTest (CryptoLayerTest):
             _ = decryptor.done ()
 
 
+    def test_crypto_aes_gcm_dec_iv_gap (self):
+        """
+        Encrypt multiple objects using non-consecutive IVs and verify that the
+        decryptor errors out with an exception in strict mode but keeps quiet
+        otherwise.
+        """
+        cnksiz         = 1 << 10
+        orig_pt_1      = fill_mod (1 << 10)
+        orig_pt_2      = fill_mod (1 << 10, 23)
+        orig_pt_3      = fill_mod (1 << 10, 42)
+        password       = str (os.urandom (42))
+        encryptor      = crypto.Encrypt (TEST_VERSION,
+                                         TEST_PARAMVERSION,
+                                         password=password,
+                                         nacl=TEST_STATIC_NACL)
+
+        def enc (pt):
+            header_dummy = encryptor.next (TEST_DUMMY_FILENAME)
+
+            off = 0
+            ct = b""
+            while off < len (pt):
+                upto = min (off + cnksiz, len (pt))
+                _n, cnk = encryptor.process (pt [off:upto])
+                ct += cnk
+                off += cnksiz
+            cnk, header, fixed = encryptor.done (header_dummy)
+            return ct + cnk, header, fixed
+
+        ct_1, hdr_1, _____ = enc (orig_pt_1)
+
+        ## Here we bump the iv of the encryptor, breaking the series.
+        encryptor.set_object_counter (encryptor.cnt + 1)
+        ct_2, hdr_2, fixed = enc (orig_pt_2)
+
+        ## IV of final object is again in-sequence.
+        ct_3, hdr_3, fixed = enc (orig_pt_3)
+
+        def decrypt (strict_ivs):
+            decryptor = crypto.Decrypt (password=password, fixedparts=fixed,
+                                        strict_ivs=strict_ivs)
+
+            def dec (hdr, ct):
+                decryptor.next (hdr)
+                off = 0
+                pt  = b""
+                while off < len (ct):
+                    upto = min (off + cnksiz, len (ct))
+                    cnk = decryptor.process (ct [off:upto])
+                    pt += cnk
+                    off += cnksiz
+                return pt + decryptor.done ()
+
+            decr_pt_1 = dec (hdr_1, ct_1)
+            decr_pt_2 = dec (hdr_2, ct_2) ## ← good header, non-consecutive IV
+            decr_pt_3 = dec (hdr_3, ct_3)
+
+            assert decr_pt_1 == orig_pt_1
+            assert decr_pt_2 == orig_pt_2
+            assert decr_pt_3 == orig_pt_3
+
+        with self.assertRaises (crypto.NonConsecutiveIV):
+            decrypt (True)
+
+        decrypt (False) # Sequence passes
+
+
     def test_crypto_aes_gcm_dec_iv_reuse (self):
         """
         Meddle with encrypted content: extract the IV from one object