Browse Source

Merge branch 'fix/ccm_constant_time' into develop

This closes #73 and closes #76
Steffen Jaeckel 10 years ago
parent
commit
eb26b7efd4
2 changed files with 118 additions and 26 deletions
  1. 70 14
      src/encauth/ccm/ccm_memory.c
  2. 48 12
      src/encauth/ccm/ccm_test.c

+ 70 - 14
src/encauth/ccm/ccm_memory.c

@@ -20,7 +20,7 @@
 /**
    CCM encrypt/decrypt and produce an authentication tag
 
-     *1 'pt' and 'ct' can both be 'in' or 'out', depending on 'direction'
+     *1 'pt', 'ct' and 'tag' can both be 'in' or 'out', depending on 'direction'
 
    @param cipher     The index of the cipher desired
    @param key        The secret key to use
@@ -33,8 +33,8 @@
    @param pt         [*1] The plaintext
    @param ptlen      The length of the plaintext (octets)
    @param ct         [*1] The ciphertext
-   @param tag        [out] The destination tag
-   @param taglen     [in/out] The max size and resulting size of the authentication tag
+   @param tag        [*1] The destination tag
+   @param taglen     The max size and resulting size of the authentication tag
    @param direction  Encrypt or Decrypt direction (0 or 1)
    @return CRYPT_OK if successful
 */
@@ -48,10 +48,15 @@ int ccm_memory(int cipher,
           unsigned char *tag,    unsigned long *taglen,
                     int  direction)
 {
-   unsigned char  PAD[16], ctr[16], CTRPAD[16], b;
+   unsigned char  PAD[16], ctr[16], CTRPAD[16], ptTag[16], b, *pt_real;
+   unsigned char *pt_work = NULL;
    symmetric_key *skey;
    int            err;
    unsigned long  len, L, x, y, z, CTRlen;
+#ifdef LTC_FAST
+   LTC_FAST_TYPE fastMask = -1; /* initialize fastMask at all zeroes */
+#endif
+   unsigned char mask = 0xff; /* initialize mask at all zeroes */
 
    if (uskey == NULL) {
       LTC_ARGCHK(key    != NULL);
@@ -65,6 +70,8 @@ int ccm_memory(int cipher,
    LTC_ARGCHK(tag    != NULL);
    LTC_ARGCHK(taglen != NULL);
 
+   pt_real = pt;
+
 #ifdef LTC_FAST
    if (16 % sizeof(LTC_FAST_TYPE)) {
       return CRYPT_INVALID_ARG;
@@ -140,6 +147,15 @@ int ccm_memory(int cipher,
    } else {
       skey = uskey;
    }
+   
+   /* initialize buffer for pt */
+   if (direction == CCM_DECRYPT) {
+      pt_work = XMALLOC(ptlen);
+      if (pt_work == NULL) {
+         goto error;
+      }
+      pt = pt_work;
+   }
 
    /* form B_0 == flags | Nonce N | l(m) */
    x = 0;
@@ -203,11 +219,9 @@ int ccm_memory(int cipher,
           PAD[x++] ^= header[y];
       }
 
-      /* remainder? */
-      if (x != 0) {
-         if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
-            goto error;
-         }
+      /* remainder */
+      if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
+         goto error;
       }
    }
 
@@ -254,7 +268,7 @@ int ccm_memory(int cipher,
                    goto error;
                 }
              }
-         } else {
+          } else { /* direction == CCM_DECRYPT */
              for (; y < (ptlen & ~15); y += 16) {
                 /* increment the ctr? */
                 for (z = 15; z > 15-L; z--) {
@@ -328,18 +342,60 @@ int ccm_memory(int cipher,
       cipher_descriptor[cipher].done(skey);
    }
 
-   /* store the TAG */
-   for (x = 0; x < 16 && x < *taglen; x++) {
-       tag[x] = PAD[x] ^ CTRPAD[x];
+   if (direction == CCM_ENCRYPT) {
+      /* store the TAG */
+      for (x = 0; x < 16 && x < *taglen; x++) {
+          tag[x] = PAD[x] ^ CTRPAD[x];
+      }
+      *taglen = x;
+   } else { /* direction == CCM_DECRYPT */
+      /* decrypt the tag */
+      for (x = 0; x < 16 && x < *taglen; x++) {
+         ptTag[x] = tag[x] ^ CTRPAD[x];
+      }
+      *taglen = x;
+
+      /* check validity of the decrypted tag against the computed PAD (in constant time) */
+      /* HACK: the boolean value of XMEM_NEQ becomes either 0 (CRYPT_OK) or 1 (CRYPT_ERR).
+       *       there should be a better way of setting the correct error code in constant
+       *       time.
+       */
+      err = XMEM_NEQ(ptTag, PAD, *taglen);
+
+      /* Zero the plaintext if the tag was invalid (in constant time) */
+      if (ptlen > 0) {
+         y = 0;
+         mask *= 1 - err; /* mask = ( err ? 0 : 0xff ) */
+#ifdef LTC_FAST
+         fastMask *= 1 - err;
+         if (ptlen & ~15) {
+            for (; y < (ptlen & ~15); y += 16) {
+              for (z = 0; z < 16; z += sizeof(LTC_FAST_TYPE)) {
+                *((LTC_FAST_TYPE*)(&pt_real[y+z])) = *((LTC_FAST_TYPE*)(&pt[y+z])) & fastMask;
+              }
+            }
+         }
+#endif
+         for (; y < ptlen; y++) {
+            pt_real[y] = pt[y] & mask;
+         }
+      }
    }
-   *taglen = x;
 
 #ifdef LTC_CLEAN_STACK
+   fastMask = 0;
+   mask = 0;
    zeromem(skey,   sizeof(*skey));
    zeromem(PAD,    sizeof(PAD));
    zeromem(CTRPAD, sizeof(CTRPAD));
+   if (pt_work != NULL) {
+     zeromem(pt_work, ptlen);
+   }
 #endif
 error:
+   if (pt_work) {
+      XFREE(pt_work);
+   }
    if (skey != uskey) {
       XFREE(skey);
    }

+ 48 - 12
src/encauth/ccm/ccm_test.c

@@ -114,10 +114,12 @@ int ccm_test(void)
 
 };
   unsigned long taglen, x, y;
-  unsigned char buf[64], buf2[64], tag2[16], tag[16];
+  unsigned char buf[64], buf2[64], tag[16], tag2[16], tag3[16], zero[64];
   int           err, idx;
   symmetric_key skey;
   ccm_state ccm;
+  
+  zeromem(zero, 64);
 
   idx = find_cipher("aes");
   if (idx == -1) {
@@ -166,8 +168,8 @@ int ccm_test(void)
       if (XMEMCMP(buf, tests[x].ct, tests[x].ptlen)) {
 #if defined(LTC_TEST_DBG)
          printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y);
-         print_hex("ct is    ", tag, taglen);
-         print_hex("ct should", tests[x].tag, taglen);
+         print_hex("ct is    ", buf, tests[x].ptlen);
+         print_hex("ct should", tests[x].ct, tests[x].ptlen);
 #endif
          return CRYPT_FAIL_TESTVECTOR;
       }
@@ -188,14 +190,16 @@ int ccm_test(void)
       }
 
       if (y == 0) {
-         if ((err = ccm_memory(idx,
+          XMEMCPY(tag3, tests[x].tag, tests[x].taglen);
+          taglen = tests[x].taglen;
+          if ((err = ccm_memory(idx,
                                tests[x].key, 16,
                                NULL,
                                tests[x].nonce, tests[x].noncelen,
                                tests[x].header, tests[x].headerlen,
                                buf2, tests[x].ptlen,
                                buf,
-                               tag2, &taglen, 1   )) != CRYPT_OK) {
+                               tag3, &taglen, 1   )) != CRYPT_OK) {
             return err;
          }
       } else {
@@ -219,24 +223,56 @@ int ccm_test(void)
       if (XMEMCMP(buf2, tests[x].pt, tests[x].ptlen)) {
 #if defined(LTC_TEST_DBG)
          printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y);
-         print_hex("pt is    ", tag, taglen);
-         print_hex("pt should", tests[x].tag, taglen);
+         print_hex("pt is    ", buf2, tests[x].ptlen);
+         print_hex("pt should", tests[x].pt, tests[x].ptlen);
 #endif
          return CRYPT_FAIL_TESTVECTOR;
       }
-      if (XMEMCMP(tag2, tests[x].tag, tests[x].taglen)) {
+      if (y == 0) {
+        /* check if decryption with the wrong tag does not reveal the plaintext */
+        XMEMCPY(tag3, tests[x].tag, tests[x].taglen);
+        tag3[0] ^= 0xff; /* set the tag to the wrong value */
+        taglen = tests[x].taglen;
+        if ((err = ccm_memory(idx,
+                              tests[x].key, 16,
+                              NULL,
+                              tests[x].nonce, tests[x].noncelen,
+                              tests[x].header, tests[x].headerlen,
+                              buf2, tests[x].ptlen,
+                              buf,
+                              tag3, &taglen, 1   )) != CRYPT_ERROR) {
+          return CRYPT_FAIL_TESTVECTOR;
+        }
+        if (XMEMCMP(buf2, zero, tests[x].ptlen)) {
+#if defined(LTC_CCM_TEST_DBG)
+          printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y);
+          print_hex("pt is    ", buf2, tests[x].ptlen);
+          print_hex("pt should", zero, tests[x].ptlen);
+#endif
+          return CRYPT_FAIL_TESTVECTOR;
+        }
+      } else {
+        /* FIXME: Only check the tag if ccm_memory was not called: ccm_memory already
+           validates the tag. ccm_process and ccm_done should somehow do the same,
+           although with current setup it is impossible to keep the plaintext hidden
+           if the tag is incorrect.
+        */
+        if (XMEMCMP(tag2, tests[x].tag, tests[x].taglen)) {
 #if defined(LTC_TEST_DBG)
-         printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y);
-         print_hex("tag is    ", tag, tests[x].taglen);
-         print_hex("tag should", tests[x].tag, tests[x].taglen);
+          printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y);
+          print_hex("tag is    ", tag2, tests[x].taglen);
+          print_hex("tag should", tests[x].tag, tests[x].taglen);
 #endif
-         return CRYPT_FAIL_TESTVECTOR;
+          return CRYPT_FAIL_TESTVECTOR;
+        }
       }
+
       if (y == 0) {
          cipher_descriptor[idx].done(&skey);
       }
     }
   }
+
   return CRYPT_OK;
 #endif
 }