diff --git a/src/encauth/ccm/ccm_memory.c b/src/encauth/ccm/ccm_memory.c index 1b4328d..5750f34 100644 --- a/src/encauth/ccm/ccm_memory.c +++ b/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); } diff --git a/src/encauth/ccm/ccm_test.c b/src/encauth/ccm/ccm_test.c index f8eb3de..7384151 100644 --- a/src/encauth/ccm/ccm_test.c +++ b/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 }