Fix in-place encryption for OCB mode
authorJussi Kivilinna <jussi.kivilinna@iki.fi>
Sat, 28 Feb 2015 16:04:34 +0000 (18:04 +0200)
committerJussi Kivilinna <jussi.kivilinna@iki.fi>
Sat, 28 Feb 2015 16:04:34 +0000 (18:04 +0200)
* cipher/cipher-ocb.c (ocb_checksum): New.
(ocb_crypt): Move checksum calculation outside main crypt loop, do
checksum calculation for encryption before inbuf is overwritten.
* tests/basic.c (check_ocb_cipher): Rename to ...
(do_check_ocb_cipher): ... to this and add argument for testing
in-place encryption/decryption.
(check_ocb_cipher): New.
--

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
cipher/cipher-ocb.c
tests/basic.c

index 25466f0..652683c 100644 (file)
@@ -299,6 +299,21 @@ _gcry_cipher_ocb_authenticate (gcry_cipher_hd_t c, const unsigned char *abuf,
 }
 
 
+/* Checksumming for encrypt and decrypt.  */
+static void ocb_checksum(unsigned char *chksum, const unsigned char *plainbuf,
+                         size_t nblks)
+{
+  while (nblks > 0)
+    {
+      /* Checksum_i = Checksum_{i-1} xor P_i  */
+      buf_xor_1(chksum, plainbuf, OCB_BLOCK_LEN);
+
+      plainbuf += OCB_BLOCK_LEN;
+      nblks--;
+    }
+}
+
+
 /* Common code for encrypt and decrypt.  */
 static gcry_err_code_t
 ocb_crypt (gcry_cipher_hd_t c, int encrypt,
@@ -308,6 +323,7 @@ ocb_crypt (gcry_cipher_hd_t c, int encrypt,
   unsigned char l_tmp[OCB_BLOCK_LEN];
   unsigned int burn = 0;
   unsigned int nburn;
+  size_t nblks = inbuflen / OCB_BLOCK_LEN;
 
   /* Check that a nonce and thus a key has been set and that we are
      not yet in end of data state. */
@@ -324,6 +340,12 @@ ocb_crypt (gcry_cipher_hd_t c, int encrypt,
   else if ((inbuflen % OCB_BLOCK_LEN))
     return GPG_ERR_INV_LENGTH;  /* We support only full blocks for now.  */
 
+  if (encrypt)
+    {
+      /* Checksum_i = Checksum_{i-1} xor P_i  */
+      ocb_checksum (c->u_ctr.ctr, inbuf, nblks);
+    }
+
   /* Encrypt all full blocks.  */
   while (inbuflen >= OCB_BLOCK_LEN)
     {
@@ -341,15 +363,18 @@ ocb_crypt (gcry_cipher_hd_t c, int encrypt,
       burn = nburn > burn ? nburn : burn;
       buf_xor_1 (outbuf, c->u_iv.iv, OCB_BLOCK_LEN);
 
-      /* Checksum_i = Checksum_{i-1} xor P_i  */
-      buf_xor_1 (c->u_ctr.ctr, encrypt? inbuf : outbuf, OCB_BLOCK_LEN);
-
       inbuf += OCB_BLOCK_LEN;
       inbuflen -= OCB_BLOCK_LEN;
       outbuf += OCB_BLOCK_LEN;
       outbuflen =- OCB_BLOCK_LEN;
     }
 
+  if (!encrypt)
+    {
+      /* Checksum_i = Checksum_{i-1} xor P_i  */
+      ocb_checksum (c->u_ctr.ctr, outbuf - nblks * OCB_BLOCK_LEN, nblks);
+     }
+
   /* Encrypt final partial block.  Note that we expect INBUFLEN to be
      shorter than OCB_BLOCK_LEN (see above).  */
   if (inbuflen)
index 869b381..6ebc056 100644 (file)
@@ -2781,7 +2781,7 @@ check_ccm_cipher (void)
 
 
 static void
-check_ocb_cipher (void)
+do_check_ocb_cipher (int inplace)
 {
   /* Note that we use hex strings and not binary strings in TV.  That
      makes it easier to maintain the test vectors.  */
@@ -3028,7 +3028,18 @@ check_ocb_cipher (void)
 
       err = gcry_cipher_final (hde);
       if (!err)
-        err = gcry_cipher_encrypt (hde, out, MAX_DATA_LEN, plain, plainlen);
+        {
+          if (inplace)
+            {
+              memcpy(out, plain, plainlen);
+              err = gcry_cipher_encrypt (hde, out, plainlen, NULL, 0);
+            }
+          else
+            {
+              err = gcry_cipher_encrypt (hde, out, MAX_DATA_LEN,
+                                         plain, plainlen);
+            }
+        }
       if (err)
         {
           fail ("cipher-ocb, gcry_cipher_encrypt failed (tv %d): %s\n",
@@ -3075,7 +3086,19 @@ check_ocb_cipher (void)
       /* Now for the decryption.  */
       err = gcry_cipher_final (hdd);
       if (!err)
-        err = gcry_cipher_decrypt (hdd, out, plainlen, NULL, 0);
+        {
+          if (inplace)
+            {
+              err = gcry_cipher_decrypt (hdd, out, plainlen, NULL, 0);
+            }
+          else
+            {
+              unsigned char tmp[MAX_DATA_LEN];
+
+              memcpy(tmp, out, plainlen);
+              err = gcry_cipher_decrypt (hdd, out, plainlen, tmp, plainlen);
+            }
+        }
       if (err)
         {
           fail ("cipher-ocb, gcry_cipher_decrypt (tv %d) failed: %s\n",
@@ -3130,6 +3153,18 @@ check_ocb_cipher (void)
 
 
 static void
+check_ocb_cipher (void)
+{
+  /* Check OCB cipher with separate destination and source buffers for
+   * encryption/decryption. */
+  do_check_ocb_cipher(0);
+
+  /* Check OCB cipher with inplace encrypt/decrypt. */
+  do_check_ocb_cipher(1);
+}
+
+
+static void
 check_stream_cipher (void)
 {
   static const struct tv