Quickfix for issue #73
The API of the function is changed (for decryption, tag is now an input parameter). With the old API it is impossible to confirm to the NIST specification and a timing sidechannel leak is inevitable.
This commit is contained in:
parent
38bfef2996
commit
25af184cd5
@ -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,7 +48,7 @@ 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;
|
||||||
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;
|
||||||
@ -203,13 +203,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 +252,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,11 +326,34 @@ 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);
|
||||||
|
|
||||||
|
/* TODO: pt should not be revealed when the tag is invalid. However, resetting the
|
||||||
|
* memory should be done in constant time, which is not the case in the
|
||||||
|
* (commented) code below.
|
||||||
|
if (err != CRYPT_OK) {
|
||||||
|
zeromem(pt, ptlen);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef LTC_CLEAN_STACK
|
#ifdef LTC_CLEAN_STACK
|
||||||
zeromem(skey, sizeof(*skey));
|
zeromem(skey, sizeof(*skey));
|
||||||
|
@ -195,7 +195,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) {
|
tests[x].tag, &taglen, 1 )) != CRYPT_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -224,6 +224,9 @@ int ccm_test(void)
|
|||||||
#endif
|
#endif
|
||||||
return CRYPT_FAIL_TESTVECTOR;
|
return CRYPT_FAIL_TESTVECTOR;
|
||||||
}
|
}
|
||||||
|
/* Only check the tag if ccm_memory was not called: ccm_memory already
|
||||||
|
validates the tag */
|
||||||
|
if (y != 0) {
|
||||||
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);
|
||||||
@ -232,6 +235,7 @@ int ccm_test(void)
|
|||||||
#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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user