From 9e2a7b36537ce6bc713405f4f08f8f50843bca87 Mon Sep 17 00:00:00 2001 From: Siert Wieringa <168631986+ptsiewie@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:55:29 +0200 Subject: [PATCH] Feature/multiple aes siv ads (#7911) * Proposed new interface for AesSivEncrypt with number of ADs != 1. * Implement AES SIV S2V computation with a number of ADs not equal to 1. * Add Example A.1 from RFC5297 to AES SIV test vectors. * Add tests for new AES SIV interface, and add test vectors for examples given in RFC5297. * Include the nonce in count of maximum number of ADs. * Addressing review comments. * Addressing review comments: Use uppercase 'U' suffix on unsigned constant. * Rename local variables named 'ad0' to 'ad', since the zero makes no sense, especially since in the RFC 5297 document they're actually counting the ADs from 1. --- wolfcrypt/src/aes.c | 90 +++++++++++++++++++++++++-------- wolfcrypt/test/test.c | 108 +++++++++++++++++++++++++++++++++++++--- wolfssl/wolfcrypt/aes.h | 14 ++++++ 3 files changed, 183 insertions(+), 29 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 177e289b23..aaafbf4019 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -13609,7 +13609,7 @@ int wc_AesXtsDecryptConsecutiveSectors(XtsAes* aes, byte* out, const byte* in, * See RFC 5297 Section 2.4. */ static WARN_UNUSED_RESULT int S2V( - const byte* key, word32 keySz, const byte* assoc, word32 assocSz, + const byte* key, word32 keySz, const AesSivAssoc* assoc, word32 numAssoc, const byte* nonce, word32 nonceSz, const byte* data, word32 dataSz, byte* out) { @@ -13623,6 +13623,8 @@ static WARN_UNUSED_RESULT int S2V( #endif word32 macSz = AES_BLOCK_SIZE; int ret = 0; + byte tmpi = 0; + word32 ai; word32 zeroBytes; #ifdef WOLFSSL_SMALL_STACK @@ -13635,32 +13637,48 @@ static WARN_UNUSED_RESULT int S2V( } if (ret == 0) #endif - { + + if ((numAssoc > 126) || ((nonceSz > 0) && (numAssoc > 125))) { + /* See RFC 5297 Section 7. */ + WOLFSSL_MSG("Maximum number of ADs (including the nonce) for AES SIV is" + " 126."); + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { XMEMSET(tmp[1], 0, AES_BLOCK_SIZE); XMEMSET(tmp[2], 0, AES_BLOCK_SIZE); ret = wc_AesCmacGenerate(tmp[0], &macSz, tmp[1], AES_BLOCK_SIZE, key, keySz); - if (ret == 0) { - ShiftAndXorRb(tmp[1], tmp[0]); - ret = wc_AesCmacGenerate(tmp[0], &macSz, assoc, assocSz, key, - keySz); - if (ret == 0) { - xorbuf(tmp[1], tmp[0], AES_BLOCK_SIZE); - } - } } if (ret == 0) { - if (nonceSz > 0) { - ShiftAndXorRb(tmp[0], tmp[1]); - ret = wc_AesCmacGenerate(tmp[1], &macSz, nonce, nonceSz, key, - keySz); + /* Loop over authenticated associated data AD1..ADn */ + for (ai = 0; ai < numAssoc; ++ai) { + ShiftAndXorRb(tmp[1-tmpi], tmp[tmpi]); + ret = wc_AesCmacGenerate(tmp[tmpi], &macSz, assoc[ai].assoc, + assoc[ai].assocSz, key, keySz); + if (ret != 0) + break; + xorbuf(tmp[1-tmpi], tmp[tmpi], AES_BLOCK_SIZE); + tmpi = 1 - tmpi; + } + + /* Add nonce as final AD. See RFC 5297 Section 3. */ + if ((ret == 0) && (nonceSz > 0)) { + ShiftAndXorRb(tmp[1-tmpi], tmp[tmpi]); + ret = wc_AesCmacGenerate(tmp[tmpi], &macSz, nonce, + nonceSz, key, keySz); if (ret == 0) { - xorbuf(tmp[0], tmp[1], AES_BLOCK_SIZE); + xorbuf(tmp[1-tmpi], tmp[tmpi], AES_BLOCK_SIZE); } + tmpi = 1 - tmpi; } - else { + + /* For simplicity of the remaining code, make sure the "final" result + is always in tmp[0]. */ + if (tmpi == 1) { XMEMCPY(tmp[0], tmp[1], AES_BLOCK_SIZE); } } @@ -13727,8 +13745,8 @@ static WARN_UNUSED_RESULT int S2V( } static WARN_UNUSED_RESULT int AesSivCipher( - const byte* key, word32 keySz, const byte* assoc, - word32 assocSz, const byte* nonce, word32 nonceSz, + const byte* key, word32 keySz, const AesSivAssoc* assoc, + word32 numAssoc, const byte* nonce, word32 nonceSz, const byte* data, word32 dataSz, byte* siv, byte* out, int enc) { @@ -13752,7 +13770,7 @@ static WARN_UNUSED_RESULT int AesSivCipher( if (ret == 0) { if (enc == 1) { - ret = S2V(key, keySz / 2, assoc, assocSz, nonce, nonceSz, data, + ret = S2V(key, keySz / 2, assoc, numAssoc, nonce, nonceSz, data, dataSz, sivTmp); if (ret != 0) { WOLFSSL_MSG("S2V failed."); @@ -13799,7 +13817,7 @@ static WARN_UNUSED_RESULT int AesSivCipher( } if (ret == 0 && enc == 0) { - ret = S2V(key, keySz / 2, assoc, assocSz, nonce, nonceSz, out, dataSz, + ret = S2V(key, keySz / 2, assoc, numAssoc, nonce, nonceSz, out, dataSz, sivTmp); if (ret != 0) { WOLFSSL_MSG("S2V failed."); @@ -13826,7 +13844,10 @@ int wc_AesSivEncrypt(const byte* key, word32 keySz, const byte* assoc, word32 assocSz, const byte* nonce, word32 nonceSz, const byte* in, word32 inSz, byte* siv, byte* out) { - return AesSivCipher(key, keySz, assoc, assocSz, nonce, nonceSz, in, inSz, + AesSivAssoc ad; + ad.assoc = assoc; + ad.assocSz = assocSz; + return AesSivCipher(key, keySz, &ad, 1U, nonce, nonceSz, in, inSz, siv, out, 1); } @@ -13837,7 +13858,32 @@ int wc_AesSivDecrypt(const byte* key, word32 keySz, const byte* assoc, word32 assocSz, const byte* nonce, word32 nonceSz, const byte* in, word32 inSz, byte* siv, byte* out) { - return AesSivCipher(key, keySz, assoc, assocSz, nonce, nonceSz, in, inSz, + AesSivAssoc ad; + ad.assoc = assoc; + ad.assocSz = assocSz; + return AesSivCipher(key, keySz, &ad, 1U, nonce, nonceSz, in, inSz, + siv, out, 0); +} + +/* + * See RFC 5297 Section 2.6. + */ +int wc_AesSivEncrypt_ex(const byte* key, word32 keySz, const AesSivAssoc* assoc, + word32 numAssoc, const byte* nonce, word32 nonceSz, + const byte* in, word32 inSz, byte* siv, byte* out) +{ + return AesSivCipher(key, keySz, assoc, numAssoc, nonce, nonceSz, in, inSz, + siv, out, 1); +} + +/* + * See RFC 5297 Section 2.7. + */ +int wc_AesSivDecrypt_ex(const byte* key, word32 keySz, const AesSivAssoc* assoc, + word32 numAssoc, const byte* nonce, word32 nonceSz, + const byte* in, word32 inSz, byte* siv, byte* out) +{ + return AesSivCipher(key, keySz, assoc, numAssoc, nonce, nonceSz, in, inSz, siv, out, 0); } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 9371810649..7af9893ff4 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -58619,8 +58619,11 @@ typedef struct { word32 keySz; const byte nonce[49]; word32 nonceSz; - const byte assoc[81]; - word32 assocSz; + byte numAssoc; + const byte assoc1[81]; + word32 assoc1Sz; + const byte assoc2[11]; + word32 assoc2Sz; const byte plaintext[83]; word32 plaintextSz; const byte siv[AES_BLOCK_SIZE+1]; @@ -58628,15 +58631,17 @@ typedef struct { word32 ciphertextSz; } AesSivTestVector; -#define AES_SIV_TEST_VECTORS 7 +#define AES_SIV_TEST_VECTORS 9 WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) { - /* These test vectors come from chrony 4.1's SIV unit tests. */ WOLFSSL_SMALL_STACK_STATIC const AesSivTestVector testVectors[AES_SIV_TEST_VECTORS] = { + /* These test vectors come from chrony 4.1's SIV unit tests. */ { "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16, + 1, + "", 0, "", 0, "", 0, "\x22\x3e\xb5\x94\xe0\xe0\x25\x4b\x00\x25\x8e\x21\x9a\x1c\xa4\x21", @@ -58645,14 +58650,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) { "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16, + 1, "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, "", 0, + "", 0, "\xd7\x20\x19\x89\xc6\xdb\xc6\xd6\x61\xfc\x62\xbc\x86\x5e\xee\xef", "", 0 }, { "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16, + 1, + "", 0, "", 0, "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, "\xb6\xc1\x60\xe9\xc2\xfd\x2a\xe8\xde\xc5\x36\x8b\x2a\x33\xed\xe1", @@ -58661,7 +58670,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) { "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e", 15, + 1, "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c", 15, + "", 0, "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4", 15, "\x03\x8c\x41\x51\xba\x7a\x8f\x77\x6e\x56\x31\x99\x42\x0b\xc7\x03", "\xe7\x6c\x67\xc9\xda\xb7\x0d\x5b\x44\x06\x26\x5a\xd0\xd2\x3b", 15 @@ -58669,7 +58680,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) { "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16, + 1, "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, + "", 0, "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7", 16, "\x5c\x05\x23\x65\xf4\x57\x0a\xa0\xfb\x38\x3e\xce\x9b\x75\x85\xeb", "\x68\x85\x19\x36\x0c\x7c\x48\x11\x40\xcb\x9b\x57\x9a\x0e\x65\x32", 16 @@ -58678,8 +58691,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" "\xd5", 17, + 1, "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b" "\xa0", 17, + "", 0, "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7" "\x08", 17, "\xaf\x58\x4b\xe7\x82\x1e\x96\x19\x29\x91\x25\xe0\xdd\x80\x3b\x49", @@ -58691,11 +58706,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) "\xb0\x5a\x1b\xc7\x56\xe7\xb6\x2c\xb4\x85\xe5\x56\xa5\x28\xc0\x6c" "\x2f\x3b\x0b\x9d\x1a\x0c\xdf\x69\x47\xe0\xcc\xc0\x87\xaa\x5c\x09" "\x98\x48\x8d\x6a\x8e\x1e\x05\xd7\x8b\x68\x74\x83\xb5\x1d\xf1\x2c", 48, + 1, "\xe5\x8b\xd2\x6a\x30\xc5\xc5\x61\xcc\xbd\x7c\x27\xbf\xfe\xf9\x06" "\x00\x5b\xd7\xfc\x11\x0b\xcf\x16\x61\xef\xac\x05\xa7\xaf\xec\x27" "\x41\xc8\x5e\x9e\x0d\xf9\x2f\xaf\x20\x79\x17\xe5\x17\x91\x2a\x27" "\x34\x1c\xbc\xaf\xeb\xef\x7f\x52\xe7\x1e\x4c\x2a\xca\xbd\x2b\xbe" "\x34\xd6\xfb\x69\xd3\x3e\x49\x59\x60\xb4\x26\xc9\xb8\xce\xba", 79, + "", 0, "\x6c\xe7\xcf\x7e\xab\x7b\xa0\xe1\xa7\x22\xcb\x88\xde\x5e\x42\xd2" "\xec\x79\xe0\xa2\xcf\x5f\x0f\x6f\x6b\x89\x57\xcd\xae\x17\xd4\xc2" "\xf3\x1b\xa2\xa8\x13\x78\x23\x2f\x83\xa8\xd4\x0c\xc0\xd2\xf3\x99" @@ -58709,7 +58726,39 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) "\x48\xc9\x55\xc5\x2f\x40\x73\x3f\x98\xbb\x8d\x69\x78\x46\x64\x17" "\x8d\x49\x2f\x14\x62\xa4\x7c\x2a\x57\x38\x87\xce\xc6\x72\xd3\x5c" "\xa1", 81 - }}; + }, + /* Example A.1 from RFC5297 */ + { + "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", 32, + "", 0, + 1, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27", 24, + "", 0, + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee", 14, + "\x85\x63\x2d\x07\xc6\xe8\xf3\x7f\x95\x0a\xcd\x32\x0a\x2e\xcc\x93", + "\x40\xc0\x2b\x96\x90\xc4\xdc\x04\xda\xef\x7f\x6a\xfe\x5c", 14 + }, + /* Example A.2 from RFC5297 */ + { + "\x7f\x7e\x7d\x7c\x7b\x7a\x79\x78\x77\x76\x75\x74\x73\x72\x71\x70" + "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f", 32, + "\x09\xf9\x11\x02\x9d\x74\xe3\x5b\xd8\x41\x56\xc5\x63\x56\x88\xc0", 16, + 2, + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff" + "\xde\xad\xda\xda\xde\xad\xda\xda\xff\xee\xdd\xcc\xbb\xaa\x99\x88" + "\x77\x66\x55\x44\x33\x22\x11\x00", 40, + "\x10\x20\x30\x40\x50\x60\x70\x80\x90\xa0", 10, + "\x74\x68\x69\x73\x20\x69\x73\x20\x73\x6f\x6d\x65\x20\x70\x6c\x61" + "\x69\x6e\x74\x65\x78\x74\x20\x74\x6f\x20\x65\x6e\x63\x72\x79\x70" + "\x74\x20\x75\x73\x69\x6e\x67\x20\x53\x49\x56\x2d\x41\x45\x53", 47, + "\x7b\xdb\x6e\x3b\x43\x26\x67\xeb\x06\xf4\xd1\x4b\xff\x2f\xbd\x0f", + "\xcb\x90\x0f\x2f\xdd\xbe\x40\x43\x26\x60\x19\x65\xc8\x89\xbf\x17" + "\xdb\xa7\x7c\xeb\x09\x4f\xa6\x63\xb7\xa3\xf7\x48\xba\x8a\xf8\x29" + "\xea\x64\xad\x54\x4a\x27\x2e\x9c\x48\x5b\x62\xa3\xfd\x5c\x0d", 47 + } + }; int i; byte computedCiphertext[82]; byte computedPlaintext[82]; @@ -58717,9 +58766,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) wc_test_ret_t ret = 0; WOLFSSL_ENTER("aes_siv_test"); + /* First test legacy "exactly one Assoc" interface. */ for (i = 0; i < AES_SIV_TEST_VECTORS; ++i) { + if (testVectors[i].numAssoc != 1) + continue; + ret = wc_AesSivEncrypt(testVectors[i].key, testVectors[i].keySz, - testVectors[i].assoc, testVectors[i].assocSz, + testVectors[i].assoc1, testVectors[i].assoc1Sz, testVectors[i].nonce, testVectors[i].nonceSz, testVectors[i].plaintext, testVectors[i].plaintextSz, siv, @@ -58737,7 +58790,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) return WC_TEST_RET_ENC_NC; } ret = wc_AesSivDecrypt(testVectors[i].key, testVectors[i].keySz, - testVectors[i].assoc, testVectors[i].assocSz, + testVectors[i].assoc1, testVectors[i].assoc1Sz, testVectors[i].nonce, testVectors[i].nonceSz, computedCiphertext, testVectors[i].plaintextSz, siv, computedPlaintext); @@ -58751,6 +58804,47 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) } } + /* Then test "multiple Assoc" interface. */ + for (i = 0; i < AES_SIV_TEST_VECTORS; ++i) { + const struct AesSivAssoc assoc[2] = { + { testVectors[i].assoc1, testVectors[i].assoc1Sz }, + { testVectors[i].assoc2, testVectors[i].assoc2Sz } + }; + + ret = wc_AesSivEncrypt_ex(testVectors[i].key, testVectors[i].keySz, + assoc, testVectors[i].numAssoc, + testVectors[i].nonce, testVectors[i].nonceSz, + testVectors[i].plaintext, + testVectors[i].plaintextSz, siv, + computedCiphertext); + if (ret != 0) { + return WC_TEST_RET_ENC_EC(ret); + } + ret = XMEMCMP(siv, testVectors[i].siv, AES_BLOCK_SIZE); + if (ret != 0) { + return WC_TEST_RET_ENC_NC; + } + ret = XMEMCMP(computedCiphertext, testVectors[i].ciphertext, + testVectors[i].ciphertextSz); + if (ret != 0) { + return WC_TEST_RET_ENC_NC; + } + ret = wc_AesSivDecrypt_ex(testVectors[i].key, testVectors[i].keySz, + assoc, testVectors[i].numAssoc, + testVectors[i].nonce, testVectors[i].nonceSz, + computedCiphertext, + testVectors[i].plaintextSz, siv, + computedPlaintext); + if (ret != 0) { + return WC_TEST_RET_ENC_EC(ret); + } + ret = XMEMCMP(computedPlaintext, testVectors[i].plaintext, + testVectors[i].plaintextSz); + if (ret != 0) { + return WC_TEST_RET_ENC_NC; + } + } + return 0; } #endif diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 3038882a7d..cf08ec3a5c 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -728,6 +728,11 @@ WOLFSSL_API int wc_AesInit_Label(Aes* aes, const char* label, void* heap, WOLFSSL_API void wc_AesFree(Aes* aes); #ifdef WOLFSSL_AES_SIV +typedef struct AesSivAssoc { + const byte* assoc; + word32 assocSz; +} AesSivAssoc; + WOLFSSL_API int wc_AesSivEncrypt(const byte* key, word32 keySz, const byte* assoc, word32 assocSz, const byte* nonce, word32 nonceSz, @@ -736,6 +741,15 @@ WOLFSSL_API int wc_AesSivDecrypt(const byte* key, word32 keySz, const byte* assoc, word32 assocSz, const byte* nonce, word32 nonceSz, const byte* in, word32 inSz, byte* siv, byte* out); + +WOLFSSL_API +int wc_AesSivEncrypt_ex(const byte* key, word32 keySz, const AesSivAssoc* assoc, + word32 numAssoc, const byte* nonce, word32 nonceSz, + const byte* in, word32 inSz, byte* siv, byte* out); +WOLFSSL_API +int wc_AesSivDecrypt_ex(const byte* key, word32 keySz, const AesSivAssoc* assoc, + word32 numAssoc, const byte* nonce, word32 nonceSz, + const byte* in, word32 inSz, byte* siv, byte* out); #endif #ifdef WOLFSSL_AES_EAX