Merge branch 'fix/ccm_constant_time' into develop

This closes #73 and closes #76
This commit is contained in:
Steffen Jaeckel 2015-08-26 00:16:09 +02:00
commit eb26b7efd4
2 changed files with 118 additions and 26 deletions

View File

@ -20,7 +20,7 @@
/** /**
CCM encrypt/decrypt and produce an authentication tag 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 cipher The index of the cipher desired
@param key The secret key to use @param key The secret key to use
@ -33,8 +33,8 @@
@param pt [*1] The plaintext @param pt [*1] The plaintext
@param ptlen The length of the plaintext (octets) @param ptlen The length of the plaintext (octets)
@param ct [*1] The ciphertext @param ct [*1] The ciphertext
@param tag [out] The destination tag @param tag [*1] The destination tag
@param taglen [in/out] The max size and resulting size of the authentication tag @param taglen The max size and resulting size of the authentication tag
@param direction Encrypt or Decrypt direction (0 or 1) @param direction Encrypt or Decrypt direction (0 or 1)
@return CRYPT_OK if successful @return CRYPT_OK if successful
*/ */
@ -48,10 +48,15 @@ int ccm_memory(int cipher,
unsigned char *tag, unsigned long *taglen, unsigned char *tag, unsigned long *taglen,
int direction) 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; symmetric_key *skey;
int err; int err;
unsigned long len, L, x, y, z, CTRlen; 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) { if (uskey == NULL) {
LTC_ARGCHK(key != NULL); LTC_ARGCHK(key != NULL);
@ -65,6 +70,8 @@ int ccm_memory(int cipher,
LTC_ARGCHK(tag != NULL); LTC_ARGCHK(tag != NULL);
LTC_ARGCHK(taglen != NULL); LTC_ARGCHK(taglen != NULL);
pt_real = pt;
#ifdef LTC_FAST #ifdef LTC_FAST
if (16 % sizeof(LTC_FAST_TYPE)) { if (16 % sizeof(LTC_FAST_TYPE)) {
return CRYPT_INVALID_ARG; return CRYPT_INVALID_ARG;
@ -141,6 +148,15 @@ int ccm_memory(int cipher,
skey = uskey; 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) */ /* form B_0 == flags | Nonce N | l(m) */
x = 0; x = 0;
PAD[x++] = (unsigned char)(((headerlen > 0) ? (1<<6) : 0) | PAD[x++] = (unsigned char)(((headerlen > 0) ? (1<<6) : 0) |
@ -203,13 +219,11 @@ int ccm_memory(int cipher,
PAD[x++] ^= header[y]; PAD[x++] ^= header[y];
} }
/* remainder? */ /* remainder */
if (x != 0) {
if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) { if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
goto error; goto error;
} }
} }
}
/* setup the ctr counter */ /* setup the ctr counter */
x = 0; x = 0;
@ -254,7 +268,7 @@ int ccm_memory(int cipher,
goto error; goto error;
} }
} }
} else { } else { /* direction == CCM_DECRYPT */
for (; y < (ptlen & ~15); y += 16) { for (; y < (ptlen & ~15); y += 16) {
/* increment the ctr? */ /* increment the ctr? */
for (z = 15; z > 15-L; z--) { for (z = 15; z > 15-L; z--) {
@ -328,18 +342,60 @@ int ccm_memory(int cipher,
cipher_descriptor[cipher].done(skey); cipher_descriptor[cipher].done(skey);
} }
if (direction == CCM_ENCRYPT) {
/* store the TAG */ /* store the TAG */
for (x = 0; x < 16 && x < *taglen; x++) { for (x = 0; x < 16 && x < *taglen; x++) {
tag[x] = PAD[x] ^ CTRPAD[x]; tag[x] = PAD[x] ^ CTRPAD[x];
} }
*taglen = 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;
}
}
}
#ifdef LTC_CLEAN_STACK #ifdef LTC_CLEAN_STACK
fastMask = 0;
mask = 0;
zeromem(skey, sizeof(*skey)); zeromem(skey, sizeof(*skey));
zeromem(PAD, sizeof(PAD)); zeromem(PAD, sizeof(PAD));
zeromem(CTRPAD, sizeof(CTRPAD)); zeromem(CTRPAD, sizeof(CTRPAD));
if (pt_work != NULL) {
zeromem(pt_work, ptlen);
}
#endif #endif
error: error:
if (pt_work) {
XFREE(pt_work);
}
if (skey != uskey) { if (skey != uskey) {
XFREE(skey); XFREE(skey);
} }

View File

@ -114,11 +114,13 @@ int ccm_test(void)
}; };
unsigned long taglen, x, y; 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; int err, idx;
symmetric_key skey; symmetric_key skey;
ccm_state ccm; ccm_state ccm;
zeromem(zero, 64);
idx = find_cipher("aes"); idx = find_cipher("aes");
if (idx == -1) { if (idx == -1) {
idx = find_cipher("rijndael"); idx = find_cipher("rijndael");
@ -166,8 +168,8 @@ int ccm_test(void)
if (XMEMCMP(buf, tests[x].ct, tests[x].ptlen)) { if (XMEMCMP(buf, tests[x].ct, tests[x].ptlen)) {
#if defined(LTC_TEST_DBG) #if defined(LTC_TEST_DBG)
printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y); printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y);
print_hex("ct is ", tag, taglen); print_hex("ct is ", buf, tests[x].ptlen);
print_hex("ct should", tests[x].tag, taglen); print_hex("ct should", tests[x].ct, tests[x].ptlen);
#endif #endif
return CRYPT_FAIL_TESTVECTOR; return CRYPT_FAIL_TESTVECTOR;
} }
@ -188,6 +190,8 @@ int ccm_test(void)
} }
if (y == 0) { if (y == 0) {
XMEMCPY(tag3, tests[x].tag, tests[x].taglen);
taglen = tests[x].taglen;
if ((err = ccm_memory(idx, if ((err = ccm_memory(idx,
tests[x].key, 16, tests[x].key, 16,
NULL, NULL,
@ -195,7 +199,7 @@ int ccm_test(void)
tests[x].header, tests[x].headerlen, tests[x].header, tests[x].headerlen,
buf2, tests[x].ptlen, buf2, tests[x].ptlen,
buf, buf,
tag2, &taglen, 1 )) != CRYPT_OK) { tag3, &taglen, 1 )) != CRYPT_OK) {
return err; return err;
} }
} else { } else {
@ -219,24 +223,56 @@ int ccm_test(void)
if (XMEMCMP(buf2, tests[x].pt, tests[x].ptlen)) { if (XMEMCMP(buf2, tests[x].pt, tests[x].ptlen)) {
#if defined(LTC_TEST_DBG) #if defined(LTC_TEST_DBG)
printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y); printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y);
print_hex("pt is ", tag, taglen); print_hex("pt is ", buf2, tests[x].ptlen);
print_hex("pt should", tests[x].tag, taglen); print_hex("pt should", tests[x].pt, tests[x].ptlen);
#endif #endif
return CRYPT_FAIL_TESTVECTOR; return CRYPT_FAIL_TESTVECTOR;
} }
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 (XMEMCMP(tag2, tests[x].tag, tests[x].taglen)) {
#if defined(LTC_TEST_DBG) #if defined(LTC_TEST_DBG)
printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y); printf("\n%d: x=%lu y=%lu\n", __LINE__, x, y);
print_hex("tag is ", tag, tests[x].taglen); print_hex("tag is ", tag2, tests[x].taglen);
print_hex("tag should", tests[x].tag, tests[x].taglen); print_hex("tag should", tests[x].tag, tests[x].taglen);
#endif #endif
return CRYPT_FAIL_TESTVECTOR; return CRYPT_FAIL_TESTVECTOR;
} }
}
if (y == 0) { if (y == 0) {
cipher_descriptor[idx].done(&skey); cipher_descriptor[idx].done(&skey);
} }
} }
} }
return CRYPT_OK; return CRYPT_OK;
#endif #endif
} }