add unit test for IV reuse
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Fri, 5 May 2017 15:52:51 +0000 (17:52 +0200)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Mon, 2 Apr 2018 11:34:08 +0000 (13:34 +0200)
testing/test_crypto.py

index 4b4a016..cfb332c 100644 (file)
@@ -37,11 +37,12 @@ def faux_hdr (ctsize=1337, iv=None):
         }
 
 
-def fill_mod (n):
+def fill_mod (n, off=0):
     buf = bytearray (n)
     bufv = memoryview (buf)
     for i in range (n):
-        c = i % 64 + 32
+        off += 1
+        c = off % 64 + 32
         struct.pack_into ("c", bufv, i, chr(c).encode("UTF-8"))
     return bytes (buf)
 
@@ -232,6 +233,67 @@ class AESGCMTest (CryptoLayerTest):
             pass
 
 
+    def test_crypto_aes_gcm_dec_iv_reuse (self):
+        """
+        Meddle with encrypted content: extract the IV from one object
+        and inject it into the header of another. This must be rejected
+        by the decryptor.
+        """
+        cnksiz         = 1 << 10
+        orig_pt_1      = fill_mod (1 << 10)
+        orig_pt_2      = fill_mod (1 << 10, 42)
+        password       = str (os.urandom (42))
+        encryptor      = crypto.Encrypt (password, TEST_VERSION,
+                                         TEST_PARAMVERSION,
+                                         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))
+                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)
+        ct_2, hdr_2, fixed = enc (orig_pt_2)
+
+        mut_hdr_2    = bytearray (hdr_2)
+        mut_hdr_2_vw = memoryview (mut_hdr_2)
+        # get IV
+        iv_lo        = crypto.HDR_OFF_IV
+        iv_hi        = crypto.HDR_OFF_IV + crypto.PDTCRYPT_HDR_SIZE_IV
+        iv_1         = hdr_1 [iv_lo : iv_hi]
+        # transplant into other header
+        mut_hdr_2_vw [iv_lo : iv_hi] = iv_1
+        hdr_2_mod    = bytes (mut_hdr_2)
+        decryptor    = crypto.Decrypt (password, fixedparts=fixed,
+                                       strict_ivs=True)
+
+        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, different IV
+        try:
+            decr_pt_2 = dec (hdr_2_mod, ct_2)
+        except crypto.DuplicateIV: # bad header, reuse detected
+            pass
+
+
 class HeaderTest (CryptoLayerTest):
 
     def test_crypto_fmt_hdr_make (self):