diff --git a/wolfcrypt/src/chacha20_poly1305.c b/wolfcrypt/src/chacha20_poly1305.c index a30fbf991..d348d2c08 100644 --- a/wolfcrypt/src/chacha20_poly1305.c +++ b/wolfcrypt/src/chacha20_poly1305.c @@ -32,8 +32,6 @@ #include #include #include -#include -#include #ifdef NO_INLINE #include @@ -121,7 +119,6 @@ int wc_ChaCha20Poly1305_Decrypt( byte calculatedAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]; /* Validate function arguments */ - if (!inKey || !inIV || !inCiphertext || !inCiphertextLen || !inAuthTag || @@ -152,11 +149,8 @@ int wc_ChaCha20Poly1305_Decrypt( calculatedAuthTag); /* Compare the calculated auth tag with the received one */ - if (err == 0 && ConstantCompare(inAuthTag, calculatedAuthTag, - CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) != 0) - { - err = MAC_CMP_FAILED_E; - } + if (err == 0) + err = wc_ChaCha20Poly1305_CheckTag(inAuthTag, calculatedAuthTag); /* Decrypt the received ciphertext */ if (err == 0) @@ -167,6 +161,17 @@ int wc_ChaCha20Poly1305_Decrypt( return err; } +int wc_ChaCha20Poly1305_CheckTag( + const byte authTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE], + const byte authTagChk[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]) +{ + int ret = 0; + if (ConstantCompare(authTag, authTagChk, + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) != 0) { + ret = MAC_CMP_FAILED_E; + } + return ret; +} static int calculateAuthTag( const byte inAuthKey[CHACHA20_POLY1305_AEAD_KEYSIZE], @@ -248,4 +253,167 @@ static void word32ToLittle64(const word32 inLittle32, byte outLittle64[8]) } +#ifndef NO_CHACHAPOLY_AEAD_IUF +int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, + const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], + const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], + int isEncrypt) +{ + int ret; + byte authKey[CHACHA20_POLY1305_AEAD_KEYSIZE]; + + /* check arguments */ + if (aead == NULL || inKey == NULL || inIV == NULL) { + return BAD_FUNC_ARG; + } + + /* setup aead context */ + XMEMSET(aead, 0, sizeof(ChaChaPoly_Aead)); + XMEMSET(authKey, 0, sizeof(authKey)); + aead->isEncrypt = isEncrypt; + + /* Initialize the ChaCha20 context (key and iv) */ + ret = wc_Chacha_SetKey(&aead->chacha, inKey, + CHACHA20_POLY1305_AEAD_KEYSIZE); + if (ret == 0) { + ret = wc_Chacha_SetIV(&aead->chacha, inIV, + CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); + } + + /* Create the Poly1305 key */ + if (ret == 0) { + ret = wc_Chacha_Process(&aead->chacha, authKey, authKey, + CHACHA20_POLY1305_AEAD_KEYSIZE); + } + + /* Initialize Poly1305 context */ + if (ret == 0) { + ret = wc_Poly1305SetKey(&aead->poly, authKey, + CHACHA20_POLY1305_AEAD_KEYSIZE); + } + + if (ret == 0) { + aead->state = CHACHA20_POLY1305_STATE_READY; + } + + return ret; +} + +/* optional additional authentication data */ +int wc_ChaCha20Poly1305_UpdateAad(ChaChaPoly_Aead* aead, + const byte* inAAD, word32 inAADLen) +{ + int ret = 0; + + if (aead == NULL || inAAD == NULL) { + return BAD_FUNC_ARG; + } + if (aead->state != CHACHA20_POLY1305_STATE_READY && + aead->state != CHACHA20_POLY1305_STATE_AAD) { + return BAD_STATE_E; + } + + if (inAADLen > 0) { + ret = wc_Poly1305Update(&aead->poly, inAAD, inAADLen); + if (ret == 0) { + aead->aadLen += inAADLen; + aead->state = CHACHA20_POLY1305_STATE_AAD; + } + } + + return ret; +} + +int wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead* aead, + byte* data, word32 dataLen) +{ + int ret = 0; + + if (aead == NULL || data == NULL) { + return BAD_FUNC_ARG; + } + if (aead->state != CHACHA20_POLY1305_STATE_READY && + aead->state != CHACHA20_POLY1305_STATE_AAD && + aead->state != CHACHA20_POLY1305_STATE_DATA) { + return BAD_STATE_E; + } + + if (aead->state == CHACHA20_POLY1305_STATE_AAD) { + /* Pad the AAD to 16 bytes */ + byte padding[CHACHA20_POLY1305_MAC_PADDING_ALIGNMENT - 1]; + word32 paddingLen = -(int)aead->aadLen & + (CHACHA20_POLY1305_MAC_PADDING_ALIGNMENT - 1); + if (paddingLen > 0) { + XMEMSET(padding, 0, paddingLen); + ret = wc_Poly1305Update(&aead->poly, padding, paddingLen); + } + } + aead->state = CHACHA20_POLY1305_STATE_DATA; + + if (ret == 0) { + /* Perform ChaCha20 encrypt or decrypt inline and Poly1305 auth calc */ + if (aead->isEncrypt) { + ret = wc_Chacha_Process(&aead->chacha, data, data, dataLen); + if (ret == 0) + ret = wc_Poly1305Update(&aead->poly, data, dataLen); + } + else { + ret = wc_Poly1305Update(&aead->poly, data, dataLen); + if (ret == 0) + ret = wc_Chacha_Process(&aead->chacha, data, data, dataLen); + } + } + if (ret == 0) { + aead->dataLen += dataLen; + } + return ret; +} + +int wc_ChaCha20Poly1305_Final(ChaChaPoly_Aead* aead, + byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]) +{ + int ret = 0; + byte padding[CHACHA20_POLY1305_MAC_PADDING_ALIGNMENT - 1]; + word32 paddingLen; + byte little64[16]; /* word64 * 2 */ + + if (aead == NULL || outAuthTag == NULL) { + return BAD_FUNC_ARG; + } + if (aead->state != CHACHA20_POLY1305_STATE_DATA) { + return BAD_STATE_E; + } + aead->state = CHACHA20_POLY1305_STATE_FINAL; + + /* Pad the ciphertext to 16 bytes */ + paddingLen = -(int)aead->dataLen & + (CHACHA20_POLY1305_MAC_PADDING_ALIGNMENT - 1); + if (paddingLen > 0) { + XMEMSET(padding, 0, paddingLen); + ret = wc_Poly1305Update(&aead->poly, padding, paddingLen); + } + + /* Add the aad and ciphertext length */ + if (ret == 0) { + /* AAD length as a 64-bit little endian integer */ + word32ToLittle64(aead->aadLen, little64); + /* Ciphertext length as a 64-bit little endian integer */ + word32ToLittle64(aead->dataLen, little64 + 8); + + ret = wc_Poly1305Update(&aead->poly, little64, sizeof(little64)); + } + + /* Finalize the auth tag */ + if (ret == 0) { + ret = wc_Poly1305Final(&aead->poly, outAuthTag); + } + + /* reset and cleanup sensitive context */ + ForceZero(aead, sizeof(ChaChaPoly_Aead)); + + return ret; +} + +#endif /* !NO_CHACHAPOLY_AEAD_IUF */ + #endif /* HAVE_CHACHA && HAVE_POLY1305 */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 7a3cba7cd..b0c3fe99f 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -5034,6 +5034,13 @@ int chacha20_poly1305_aead_test(void) byte generatedPlaintext[272]; byte generatedAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]; int err; +#ifndef NO_CHACHAPOLY_AEAD_IUF + ChaChaPoly_Aead aead; + #define TEST_SMALL_CHACHA_CHUNKS 64 + #ifdef TEST_SMALL_CHACHA_CHUNKS + word32 testLen; + #endif +#endif XMEMSET(generatedCiphertext, 0, sizeof(generatedCiphertext)); XMEMSET(generatedAuthTag, 0, sizeof(generatedAuthTag)); @@ -5092,8 +5099,8 @@ int chacha20_poly1305_aead_test(void) if (err != BAD_FUNC_ARG) return -4511; - /* Test #1 */ + /* Test #1 */ err = wc_ChaCha20Poly1305_Encrypt(key1, iv1, aad1, sizeof(aad1), plaintext1, sizeof(plaintext1), @@ -5103,17 +5110,14 @@ int chacha20_poly1305_aead_test(void) } /* -- Check the ciphertext and authtag */ - if (XMEMCMP(generatedCiphertext, cipher1, sizeof(cipher1))) { return -4512; } - if (XMEMCMP(generatedAuthTag, authTag1, sizeof(authTag1))) { return -4513; } /* -- Verify decryption works */ - err = wc_ChaCha20Poly1305_Decrypt(key1, iv1, aad1, sizeof(aad1), cipher1, sizeof(cipher1), @@ -5121,17 +5125,16 @@ int chacha20_poly1305_aead_test(void) if (err) { return err; } - - if (XMEMCMP(generatedPlaintext, plaintext1, sizeof( plaintext1))) { + if (XMEMCMP(generatedPlaintext, plaintext1, sizeof(plaintext1))) { return -4514; } + XMEMSET(generatedCiphertext, 0, sizeof(generatedCiphertext)); XMEMSET(generatedAuthTag, 0, sizeof(generatedAuthTag)); XMEMSET(generatedPlaintext, 0, sizeof(generatedPlaintext)); /* Test #2 */ - err = wc_ChaCha20Poly1305_Encrypt(key2, iv2, aad2, sizeof(aad2), plaintext2, sizeof(plaintext2), @@ -5141,17 +5144,14 @@ int chacha20_poly1305_aead_test(void) } /* -- Check the ciphertext and authtag */ - if (XMEMCMP(generatedCiphertext, cipher2, sizeof(cipher2))) { return -4515; } - if (XMEMCMP(generatedAuthTag, authTag2, sizeof(authTag2))) { return -4516; } /* -- Verify decryption works */ - err = wc_ChaCha20Poly1305_Decrypt(key2, iv2, aad2, sizeof(aad2), cipher2, sizeof(cipher2), @@ -5164,6 +5164,222 @@ int chacha20_poly1305_aead_test(void) return -4517; } + +#ifndef NO_CHACHAPOLY_AEAD_IUF + /* AEAD init/update/final */ + err = wc_ChaCha20Poly1305_Init(NULL, key1, iv1, 0); + if (err != BAD_FUNC_ARG) + return -4520; + err = wc_ChaCha20Poly1305_Init(&aead, NULL, iv1, 0); + if (err != BAD_FUNC_ARG) + return -4521; + err = wc_ChaCha20Poly1305_Init(&aead, key1, NULL, 0); + if (err != BAD_FUNC_ARG) + return -4522; + err = wc_ChaCha20Poly1305_UpdateAad(NULL, aad1, sizeof(aad1)); + if (err != BAD_FUNC_ARG) + return -4523; + err = wc_ChaCha20Poly1305_UpdateAad(&aead, NULL, sizeof(aad1)); + if (err != BAD_FUNC_ARG) + return -4524; + err = wc_ChaCha20Poly1305_UpdateData(NULL, generatedPlaintext, sizeof(plaintext1)); + if (err != BAD_FUNC_ARG) + return -4525; + err = wc_ChaCha20Poly1305_UpdateData(&aead, NULL, sizeof(plaintext1)); + if (err != BAD_FUNC_ARG) + return -4526; + err = wc_ChaCha20Poly1305_Final(NULL, generatedAuthTag); + if (err != BAD_FUNC_ARG) + return -4527; + err = wc_ChaCha20Poly1305_Final(&aead, NULL); + if (err != BAD_FUNC_ARG) + return -4528; + + /* AEAD init/update/final - state tests */ + aead.state = CHACHA20_POLY1305_STATE_INIT; + err = wc_ChaCha20Poly1305_UpdateAad(&aead, aad1, sizeof(aad1)); + if (err != BAD_STATE_E) + return -4529; + aead.state = CHACHA20_POLY1305_STATE_DATA; + err = wc_ChaCha20Poly1305_UpdateAad(&aead, aad1, sizeof(aad1)); + if (err != BAD_STATE_E) + return -4530; + aead.state = CHACHA20_POLY1305_STATE_FINAL; + err = wc_ChaCha20Poly1305_UpdateAad(&aead, aad1, sizeof(aad1)); + if (err != BAD_STATE_E) + return -4531; + aead.state = CHACHA20_POLY1305_STATE_INIT; + err = wc_ChaCha20Poly1305_UpdateData(&aead, generatedPlaintext, sizeof(plaintext1)); + if (err != BAD_STATE_E) + return -4532; + aead.state = CHACHA20_POLY1305_STATE_FINAL; + err = wc_ChaCha20Poly1305_UpdateData(&aead, generatedPlaintext, sizeof(plaintext1)); + if (err != BAD_STATE_E) + return -4533; + aead.state = CHACHA20_POLY1305_STATE_INIT; + err = wc_ChaCha20Poly1305_Final(&aead, generatedAuthTag); + if (err != BAD_STATE_E) + return -4534; + aead.state = CHACHA20_POLY1305_STATE_READY; + err = wc_ChaCha20Poly1305_Final(&aead, generatedAuthTag); + if (err != BAD_STATE_E) + return -4535; + aead.state = CHACHA20_POLY1305_STATE_AAD; + err = wc_ChaCha20Poly1305_Final(&aead, generatedAuthTag); + if (err != BAD_STATE_E) + return -4536; + + XMEMSET(generatedCiphertext, 0, sizeof(generatedCiphertext)); + XMEMSET(generatedAuthTag, 0, sizeof(generatedAuthTag)); + XMEMSET(generatedPlaintext, 0, sizeof(generatedPlaintext)); + + /* Test 1 - Encrypt */ + err = wc_ChaCha20Poly1305_Init(&aead, key1, iv1, 1); + if (err != 0) + return -4537; + err = wc_ChaCha20Poly1305_UpdateAad(&aead, aad1, sizeof(aad1)); + if (err != 0) + return -4538; +#ifdef TEST_SMALL_CHACHA_CHUNKS + /* test doing data in smaller chunks */ + for (testLen=0; testLen TEST_SMALL_CHACHA_CHUNKS) + dataLen = TEST_SMALL_CHACHA_CHUNKS; + XMEMCPY(&generatedCiphertext[testLen], &plaintext1[testLen], dataLen); + err = wc_ChaCha20Poly1305_UpdateData(&aead, + &generatedCiphertext[testLen], dataLen); + if (err != 0) + return -4539; + testLen += dataLen; + } +#else + XMEMCPY(generatedCiphertext, plaintext1, sizeof(plaintext1)); + err = wc_ChaCha20Poly1305_UpdateData(&aead, generatedCiphertext, + sizeof(plaintext1)); +#endif + err = wc_ChaCha20Poly1305_Final(&aead, generatedAuthTag); + if (err != 0) + return -4540; + err = wc_ChaCha20Poly1305_CheckTag(generatedAuthTag, authTag1); + if (err != 0) + return -4541; + if (XMEMCMP(generatedCiphertext, cipher1, sizeof(cipher1))) { + return -4542; + } + + /* Test 1 - Decrypt */ + err = wc_ChaCha20Poly1305_Init(&aead, key1, iv1, 0); + if (err != 0) + return -4543; + err = wc_ChaCha20Poly1305_UpdateAad(&aead, aad1, sizeof(aad1)); + if (err != 0) + return -4544; +#ifdef TEST_SMALL_CHACHA_CHUNKS + /* test doing data in smaller chunks */ + for (testLen=0; testLen TEST_SMALL_CHACHA_CHUNKS) + dataLen = TEST_SMALL_CHACHA_CHUNKS; + XMEMCPY(&generatedPlaintext[testLen], &generatedCiphertext[testLen], + dataLen); + err = wc_ChaCha20Poly1305_UpdateData(&aead, + &generatedPlaintext[testLen], dataLen); + if (err != 0) + return -4545; + testLen += dataLen; + } +#else + XMEMCPY(generatedPlaintext, generatedCiphertext, sizeof(cipher1)); + err = wc_ChaCha20Poly1305_UpdateData(&aead, generatedPlaintext, + sizeof(cipher1)); +#endif + err = wc_ChaCha20Poly1305_Final(&aead, generatedAuthTag); + if (err != 0) + return -4546; + err = wc_ChaCha20Poly1305_CheckTag(generatedAuthTag, authTag1); + if (err != 0) + return -4547; + if (XMEMCMP(generatedPlaintext, plaintext1, sizeof(plaintext1))) { + return -4548; + } + + XMEMSET(generatedCiphertext, 0, sizeof(generatedCiphertext)); + XMEMSET(generatedAuthTag, 0, sizeof(generatedAuthTag)); + XMEMSET(generatedPlaintext, 0, sizeof(generatedPlaintext)); + + /* Test 2 - Encrypt */ + err = wc_ChaCha20Poly1305_Init(&aead, key2, iv2, 1); + if (err != 0) + return -4550; + err = wc_ChaCha20Poly1305_UpdateAad(&aead, aad2, sizeof(aad2)); + if (err != 0) + return -4551; +#ifdef TEST_SMALL_CHACHA_CHUNKS + /* test doing data in smaller chunks */ + for (testLen=0; testLen TEST_SMALL_CHACHA_CHUNKS) + dataLen = TEST_SMALL_CHACHA_CHUNKS; + XMEMCPY(&generatedCiphertext[testLen], &plaintext2[testLen], dataLen); + err = wc_ChaCha20Poly1305_UpdateData(&aead, + &generatedCiphertext[testLen], dataLen); + if (err != 0) + return -4552; + testLen += dataLen; + } +#else + XMEMCPY(generatedCiphertext, plaintext2, sizeof(plaintext2)); + err = wc_ChaCha20Poly1305_UpdateData(&aead, generatedCiphertext, + sizeof(plaintext2)); +#endif + err = wc_ChaCha20Poly1305_Final(&aead, generatedAuthTag); + if (err != 0) + return -4553; + err = wc_ChaCha20Poly1305_CheckTag(generatedAuthTag, authTag2); + if (err != 0) + return -4554; + if (XMEMCMP(generatedCiphertext, cipher2, sizeof(cipher2))) { + return -4555; + } + + /* Test 2 - Decrypt */ + err = wc_ChaCha20Poly1305_Init(&aead, key2, iv2, 0); + if (err != 0) + return -4556; + err = wc_ChaCha20Poly1305_UpdateAad(&aead, aad2, sizeof(aad2)); + if (err != 0) + return -4557; +#ifdef TEST_SMALL_CHACHA_CHUNKS + /* test doing data in smaller chunks */ + for (testLen=0; testLen TEST_SMALL_CHACHA_CHUNKS) + dataLen = TEST_SMALL_CHACHA_CHUNKS; + XMEMCPY(&generatedPlaintext[testLen], &generatedCiphertext[testLen], + dataLen); + err = wc_ChaCha20Poly1305_UpdateData(&aead, + &generatedPlaintext[testLen], dataLen); + if (err != 0) + return -4558; + testLen += dataLen; + } +#else + XMEMCPY(generatedPlaintext, generatedCiphertext, sizeof(cipher2)); + err = wc_ChaCha20Poly1305_UpdateData(&aead, generatedPlaintext, + sizeof(cipher2)); +#endif + err = wc_ChaCha20Poly1305_Final(&aead, generatedAuthTag); + if (err != 0) + return -4559; + err = wc_ChaCha20Poly1305_CheckTag(generatedAuthTag, authTag2); + if (err != 0) + return -4560; + if (XMEMCMP(generatedPlaintext, plaintext2, sizeof(plaintext2))) { + return -4561; + } +#endif /* !NO_CHACHAPOLY_AEAD_IUF */ + return err; } #endif /* HAVE_CHACHA && HAVE_POLY1305 */ diff --git a/wolfssl/wolfcrypt/chacha20_poly1305.h b/wolfssl/wolfcrypt/chacha20_poly1305.h index aef72540e..4754b17ae 100644 --- a/wolfssl/wolfcrypt/chacha20_poly1305.h +++ b/wolfssl/wolfcrypt/chacha20_poly1305.h @@ -33,6 +33,8 @@ #define WOLF_CRYPT_CHACHA20_POLY1305_H #include +#include +#include #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) @@ -45,18 +47,41 @@ #define CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE 16 enum { - CHACHA20_POLY_1305_ENC_TYPE = 8 /* cipher unique type */ + CHACHA20_POLY_1305_ENC_TYPE = 8, /* cipher unique type */ + + /* AEAD Cipher Direction */ + CHACHA20_POLY1305_AEAD_DECRYPT = 0, + CHACHA20_POLY1305_AEAD_ENCRYPT = 1, + + /* AEAD State */ + CHACHA20_POLY1305_STATE_INIT = 0, + CHACHA20_POLY1305_STATE_READY = 1, + CHACHA20_POLY1305_STATE_AAD = 2, + CHACHA20_POLY1305_STATE_DATA = 3, + CHACHA20_POLY1305_STATE_FINAL = 4, }; - /* - * The IV for this implementation is 96 bits to give the most flexibility. - * - * Some protocols may have unique per-invocation inputs that are not - * 96-bit in length. For example, IPsec may specify a 64-bit nonce. In - * such a case, it is up to the protocol document to define how to - * transform the protocol nonce into a 96-bit nonce, for example by - * concatenating a constant value. - */ +typedef struct ChaChaPoly_Aead { + ChaCha chacha; + Poly1305 poly; + + word32 aadLen; + word32 dataLen; + + byte state; + word32 isEncrypt:1; +} ChaChaPoly_Aead; + + +/* + * The IV for this implementation is 96 bits to give the most flexibility. + * + * Some protocols may have unique per-invocation inputs that are not + * 96-bit in length. For example, IPsec may specify a 64-bit nonce. In + * such a case, it is up to the protocol document to define how to + * transform the protocol nonce into a 96-bit nonce, for example by + * concatenating a constant value. + */ WOLFSSL_API int wc_ChaCha20Poly1305_Encrypt( @@ -76,6 +101,27 @@ int wc_ChaCha20Poly1305_Decrypt( const byte inAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE], byte* outPlaintext); +WOLFSSL_API +int wc_ChaCha20Poly1305_CheckTag( + const byte authTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE], + const byte authTagChk[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]); + + +#ifndef NO_CHACHAPOLY_AEAD_IUF +/* Implementation of AEAD, which includes support for adding + data, then final calculation of authentication tag */ +WOLFSSL_API int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, + const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], + const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], + int isEncrypt); +WOLFSSL_API int wc_ChaCha20Poly1305_UpdateAad(ChaChaPoly_Aead* aead, + const byte* inAAD, word32 inAADLen); +WOLFSSL_API int wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead* aead, + byte* data, word32 dataLen); +WOLFSSL_API int wc_ChaCha20Poly1305_Final(ChaChaPoly_Aead* aead, + byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]); +#endif /* !NO_CHACHAPOLY_AEAD_IUF */ + #ifdef __cplusplus } /* extern "C" */ #endif