diff --git a/ctaocrypt/benchmark/benchmark.c b/ctaocrypt/benchmark/benchmark.c index f7f53228e..cc02fdf44 100644 --- a/ctaocrypt/benchmark/benchmark.c +++ b/ctaocrypt/benchmark/benchmark.c @@ -55,6 +55,7 @@ void bench_hc128(void); void bench_rabbit(void); void bench_aes(int); void bench_aesgcm(void); +void bench_aesccm(void); void bench_md5(void); void bench_sha(void); @@ -85,6 +86,9 @@ int main(int argc, char** argv) #ifdef HAVE_AESGCM bench_aesgcm(); #endif +#ifdef HAVE_AESCCM + bench_aesccm(); +#endif #ifndef NO_RC4 bench_arc4(); #endif @@ -211,6 +215,29 @@ void bench_aesgcm(void) #endif +#ifdef HAVE_AESCCM +void bench_aesccm(void) +{ + Aes enc; + double start, total, persec; + int i; + + AesCcmSetKey(&enc, key, 16, iv, 12); + start = current_time(); + + for(i = 0; i < megs; i++) + AesCcmEncrypt(&enc, cipher, plain, sizeof(plain), + tag, 16, additional, 13); + + total = current_time() - start; + + persec = 1 / total * megs; + printf("AES-CCM %d megs took %5.3f seconds, %6.2f MB/s\n", megs, total, + persec); +} +#endif + + #ifndef NO_DES3 void bench_des(void) { diff --git a/ctaocrypt/src/aes.c b/ctaocrypt/src/aes.c index b8bfbcb3a..de2bf488c 100644 --- a/ctaocrypt/src/aes.c +++ b/ctaocrypt/src/aes.c @@ -2541,6 +2541,229 @@ int AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* HAVE_AESGCM */ +#ifdef HAVE_AESCCM + +void AesCcmSetKey(Aes* aes, const byte* key, word32 keySz, + const byte* implicitIV, word32 ivSz) +{ + byte fullIV[AES_BLOCK_SIZE]; + + if (!((keySz == 16) || (keySz == 24) || (keySz == 32))) + return; + + if (ivSz > AES_BLOCK_SIZE - 2) { + CYASSL_MSG("AES-CCM IV is too long"); + return; + } + + XMEMSET(fullIV, 0, sizeof(fullIV)); + XMEMCPY(fullIV + 1, implicitIV, ivSz); + + AesSetKeyLocal(aes, key, keySz, fullIV, AES_ENCRYPTION); + aes->lenSz = AES_BLOCK_SIZE - 1 - ivSz; + + XMEMSET(fullIV, 0, sizeof(fullIV)); +} + + +static void roll_x(Aes* aes, const byte* in, word32 inSz, byte* out) +{ + /* process the bulk of the data */ + while (inSz >= AES_BLOCK_SIZE) { + xorbuf(out, in, AES_BLOCK_SIZE); + in += AES_BLOCK_SIZE; + inSz -= AES_BLOCK_SIZE; + + AesEncrypt(aes, out, out); + } + + /* process remainder of the data */ + if (inSz > 0) { + xorbuf(out, in, inSz); + AesEncrypt(aes, out, out); + } +} + + +static void roll_auth(Aes* aes, const byte* in, word32 inSz, byte* out) +{ + word32 authLenSz; + word32 remainder; + + /* encode the length in */ + if (inSz <= 0xFEFF) { + authLenSz = 2; + out[0] ^= ((inSz & 0xFF00) >> 8); + out[1] ^= (inSz & 0x00FF); + } + else if (inSz <= 0xFFFFFFFF) { + authLenSz = 6; + out[0] ^= 0xFF; out[1] ^= 0xFE; + out[2] ^= ((inSz & 0xFF000000) >> 24); + out[3] ^= ((inSz & 0x00FF0000) >> 16); + out[4] ^= ((inSz & 0x0000FF00) >> 8); + out[5] ^= (inSz & 0x000000FF); + } + /* Note, the protocol handles auth data up to 2^64, but we are + * using 32-bit sizes right now, so the bigger data isn't handled + * else if (inSz <= 0xFFFFFFFFFFFFFFFF) {} */ + else + return; + + /* start fill out the rest of the first block */ + remainder = AES_BLOCK_SIZE - authLenSz; + if (inSz >= remainder) { + /* plenty of bulk data to fill the remainder of this block */ + xorbuf(out + authLenSz, in, remainder); + inSz -= remainder; + in += remainder; + } + else { + /* not enough bulk data, copy what is available, and pad zero */ + xorbuf(out + authLenSz, in, inSz); + inSz = 0; + } + AesEncrypt(aes, out, out); + + if (inSz > 0) + roll_x(aes, in, inSz, out); +} + + +static INLINE void AesCcmCtrInc(byte* B, word32 lenSz) +{ + word32 i; + + for (i = 0; i < lenSz; i++) { + if (++B[AES_BLOCK_SIZE - 1 - i] != 0) return; + } +} + + +void AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, + byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz) +{ + byte A[AES_BLOCK_SIZE]; + byte B[AES_BLOCK_SIZE]; + word32 i; + + XMEMCPY(B, aes->reg, AES_BLOCK_SIZE); + B[0] = (authInSz > 0 ? 64 : 0) + + (8 * ((authTagSz - 2) / 2)) + + (aes->lenSz - 1); + for (i = 0; i < aes->lenSz; i++) + B[AES_BLOCK_SIZE - 1 - i] = (inSz >> (8 * i)) & 0xFF; + + AesEncrypt(aes, B, A); + if (authInSz > 0) + roll_auth(aes, authIn, authInSz, A); + if (inSz > 0) + roll_x(aes, in, inSz, A); + XMEMCPY(authTag, A, authTagSz); + + B[0] = (aes->lenSz - 1); + for (i = 0; i < aes->lenSz; i++) + B[AES_BLOCK_SIZE - 1 - i] = 0; + AesEncrypt(aes, B, A); + xorbuf(authTag, A, authTagSz); + + B[15] = 1; + while (inSz >= AES_BLOCK_SIZE) { + AesEncrypt(aes, B, A); + xorbuf(A, in, AES_BLOCK_SIZE); + XMEMCPY(out, A, AES_BLOCK_SIZE); + + AesCcmCtrInc(B, aes->lenSz); + inSz -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (inSz > 0) { + AesEncrypt(aes, B, A); + xorbuf(A, in, inSz); + XMEMCPY(out, A, inSz); + } + + XMEMSET(A, 0, AES_BLOCK_SIZE); + XMEMSET(B, 0, AES_BLOCK_SIZE); +} + + +int AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, + const byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz) +{ + byte A[AES_BLOCK_SIZE]; + byte B[AES_BLOCK_SIZE]; + byte* o; + word32 i, oSz, result = 0; + + o = out; + oSz = inSz; + XMEMCPY(B, aes->reg, AES_BLOCK_SIZE); + B[0] = (aes->lenSz - 1); + for (i = 0; i < aes->lenSz - 1; i++) + B[AES_BLOCK_SIZE - 1 - i] = 0; + B[15] = 1; + while (oSz >= AES_BLOCK_SIZE) { + AesEncrypt(aes, B, A); + xorbuf(A, in, AES_BLOCK_SIZE); + XMEMCPY(o, A, AES_BLOCK_SIZE); + + AesCcmCtrInc(B, aes->lenSz); + oSz -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + o += AES_BLOCK_SIZE; + } + if (inSz > 0) { + AesEncrypt(aes, B, A); + xorbuf(A, in, oSz); + XMEMCPY(o, A, oSz); + } + + for (i = 0; i < aes->lenSz; i++) + B[AES_BLOCK_SIZE - 1 - i] = 0; + AesEncrypt(aes, B, A); + + o = out; + oSz = inSz; + + B[0] = (authInSz > 0 ? 64 : 0) + + (8 * ((authTagSz - 2) / 2)) + + (aes->lenSz - 1); + for (i = 0; i < aes->lenSz; i++) + B[AES_BLOCK_SIZE - 1 - i] = (inSz >> (8 * i)) & 0xFF; + + AesEncrypt(aes, B, A); + if (authInSz > 0) + roll_auth(aes, authIn, authInSz, A); + if (inSz > 0) + roll_x(aes, o, oSz, A); + + B[0] = (aes->lenSz - 1); + for (i = 0; i < aes->lenSz; i++) + B[AES_BLOCK_SIZE - 1 - i] = 0; + AesEncrypt(aes, B, B); + xorbuf(A, B, authTagSz); + + if (XMEMCMP(A, authTag, authTagSz) != 0) { + /* If the authTag check fails, don't keep the decrypted data. + * Unfortunately, you need the decrypted data to calculate the + * check value. */ + XMEMSET(out, 0, inSz); + result = AES_CCM_AUTH_E; + } + + XMEMSET(A, 0, AES_BLOCK_SIZE); + XMEMSET(B, 0, AES_BLOCK_SIZE); + o = NULL; + + return result; +} + +#endif + #endif /* STM32F2_CRYPTO */ int AesSetIV(Aes* aes, const byte* iv) diff --git a/ctaocrypt/src/error.c b/ctaocrypt/src/error.c index 6cfe8e729..b4d1eb258 100644 --- a/ctaocrypt/src/error.c +++ b/ctaocrypt/src/error.c @@ -273,6 +273,10 @@ void CTaoCryptErrorString(int error, char* buffer) XSTRNCPY(buffer, "AES-GCM Authentication check fail", max); break; + case AES_CCM_AUTH_E: + XSTRNCPY(buffer, "AES-CCM Authentication check fail", max); + break; + default: XSTRNCPY(buffer, "unknown error number", max); diff --git a/ctaocrypt/test/test.c b/ctaocrypt/test/test.c index 1f4abd4da..c49c83ab5 100644 --- a/ctaocrypt/test/test.c +++ b/ctaocrypt/test/test.c @@ -112,6 +112,7 @@ int des_test(void); int des3_test(void); int aes_test(void); int aesgcm_test(void); +int aesccm_test(void); int rsa_test(void); int dh_test(void); int dsa_test(void); @@ -292,6 +293,13 @@ void ctaocrypt_test(void* args) else printf( "AES-GCM test passed!\n"); #endif + +#ifdef HAVE_AESCCM + if ( (ret = aesccm_test()) ) + err_sys("AES-CCM test failed!\n", ret); + else + printf( "AES-CCM test passed!\n"); +#endif #endif if ( (ret = random_test()) ) @@ -1580,6 +1588,79 @@ int aesgcm_test(void) } #endif /* HAVE_AESGCM */ +#ifdef HAVE_AESCCM +int aesccm_test(void) +{ + Aes enc; + + /* key */ + const byte k[] = + { + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf + }; + + /* nonce */ + const byte iv[] = + { + 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 + }; + + /* plaintext */ + const byte p[] = + { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e + }; + + const byte a[] = + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + + const byte c[] = + { + 0x58, 0x8c, 0x97, 0x9a, 0x61, 0xc6, 0x63, 0xd2, + 0xf0, 0x66, 0xd0, 0xc2, 0xc0, 0xf9, 0x89, 0x80, + 0x6d, 0x5f, 0x6b, 0x61, 0xda, 0xc3, 0x84 + }; + + const byte t[] = + { + 0x17, 0xe8, 0xd1, 0x2c, 0xfd, 0xf9, 0x26, 0xe0 + }; + + byte t2[sizeof(t)]; + byte p2[sizeof(p)]; + byte c2[sizeof(c)]; + + int result; + + memset(t2, 0, sizeof(t2)); + memset(c2, 0, sizeof(c2)); + memset(p2, 0, sizeof(p2)); + + AesCcmSetKey(&enc, k, sizeof(k), iv, sizeof(iv)); + /* AES-CCM encrypt and decrypt both use AES encrypt internally */ + AesCcmEncrypt(&enc, c2, p, sizeof(c2), t2, sizeof(t2), a, sizeof(a)); + if (memcmp(c, c2, sizeof(c2))) + return -107; + if (memcmp(t, t2, sizeof(t2))) + return -108; + + result = AesCcmDecrypt(&enc, + p2, c2, sizeof(p2), t2, sizeof(t2), a, sizeof(a)); + if (result != 0) + return -109; + if (memcmp(p, p2, sizeof(p2))) + return -110; + + return 0; +} +#endif /* HAVE_AESCCM */ + #endif /* NO_AES */ diff --git a/cyassl/ctaocrypt/aes.h b/cyassl/ctaocrypt/aes.h index 8f1dc327d..1c5e93bf0 100644 --- a/cyassl/ctaocrypt/aes.h +++ b/cyassl/ctaocrypt/aes.h @@ -76,6 +76,9 @@ typedef struct Aes { ALIGN16 byte M0[256][AES_BLOCK_SIZE]; #endif /* GCM_TABLE */ #endif /* HAVE_AESGCM */ +#ifdef HAVE_AESCCM + word32 lenSz; +#endif #ifdef CYASSL_AESNI byte use_aesni; #endif /* CYASSL_AESNI */ @@ -105,6 +108,16 @@ CYASSL_API int AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, const byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz); #endif /* HAVE_AESGCM */ +#ifdef HAVE_AESCCM +CYASSL_API void AesCcmSetKey(Aes* aes, const byte* key, word32 keySz, + const byte* implicitIV, word32 ivSz); +CYASSL_API void AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, + byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz); +CYASSL_API int AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, + const byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz); +#endif /* HAVE_AESCCM */ #ifdef __cplusplus diff --git a/cyassl/ctaocrypt/error.h b/cyassl/ctaocrypt/error.h index 8a0d58628..308fb1d81 100644 --- a/cyassl/ctaocrypt/error.h +++ b/cyassl/ctaocrypt/error.h @@ -99,6 +99,7 @@ enum { ALT_NAME_E = -177, /* alt name size problem, too big */ AES_GCM_AUTH_E = -180, /* AES-GCM Authentication check failure */ + AES_CCM_AUTH_E = -181, /* AES-CCM Authentication check failure */ MIN_CODE_E = -200 /* errors -101 - -199 */ };