From 62b07d88062cf9a313289da638ee8ec3412ea0fd Mon Sep 17 00:00:00 2001 From: Hayden Roche Date: Fri, 14 Jan 2022 16:52:03 -0800 Subject: [PATCH] Add AES-SIV (RFC 5297). This commit adds functions to encrypt and decrypt data using AES in SIV mode, as described in RFC 5297. This was added in the process of porting chrony to wolfSSL. chrony is an NTP implementation that can use NTS (network time security), which requires AES-SIV. --- CMakeLists.txt | 48 +++++- configure.ac | 15 +- doc/dox_comments/header_files/aes.h | 94 ++++++++++++ wolfcrypt/benchmark/benchmark.c | 66 +++++++- wolfcrypt/benchmark/benchmark.h | 1 + wolfcrypt/src/aes.c | 230 ++++++++++++++++++++++++++++ wolfcrypt/src/cmac.c | 7 +- wolfcrypt/src/error.c | 3 + wolfcrypt/test/test.c | 151 ++++++++++++++++++ wolfssl/wolfcrypt/aes.h | 11 ++ wolfssl/wolfcrypt/cmac.h | 5 + wolfssl/wolfcrypt/error-crypt.h | 3 +- 12 files changed, 624 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59dc978a5..d0f897ce5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -358,9 +358,32 @@ if(WOLFSSL_AESGCM) list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AESGCM") endif() +# AES-SIV +set(WOLFSSL_AESSIV_HELP_STRING "Enable wolfSSL AES-SIV support (default: disabled)") +add_option("WOLFSSL_AESSIV" ${WOLFSSL_AESSIV_HELP_STRING} "no" "yes;no") + +if(WOLFSSL_AESSIV) + list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_AES_SIV") +endif() + +# AES-CTR +set(WOLFSSL_AESCTR_HELP_STRING "Enable wolfSSL AES-CTR support (default: disabled)") +add_option("WOLFSSL_AESCTR" ${WOLFSSL_AESCTR_HELP_STRING} "no" "yes;no") + +if(WOLFSSL_OPENVPN OR + WOLFSSL_LIBSSH2 OR + WOLFSSL_AESSIV) + override_cache(WOLFSSL_AESCTR "yes") +endif() + +if(WOLFSSL_AESCTR AND NOT WOLFSSL_FORTRESS) + list(APPEND WOLFSSL_DEFINITIONS + "-DWOLFSSL_AES_COUNTER" + "-DWOLFSSL_AES_DIRECT") +endif() + # TODO: - AES-CCM # - AES-GCM stream -# - AES-CTR # - AES-OFB # - AES-CFB # - AES-ARM @@ -623,7 +646,7 @@ add_option("WOLFSSL_ERROR_STRINGS" ${WOLFSSL_ERROR_STRINGS_HELP_STRING} "yes" "y if(NOT WOLFSSL_ERROR_STRINGS) list(APPEND WOLFSSL_DEFINITIONS "-DNO_ERROR_STRINGS") else() - # turn off error strings if leanpsk or leantls o + # turn off error strings if leanpsk or leantls on if(WOLFSSL_LEAN_PSK OR WOLFSSL_LEAN_TLS) list(APPEND WOLFSSL_DEFINITIONS "-DNO_ERROR_STRINGS") override_cache(WOLFSSL_ERROR_STRINGS "no") @@ -897,10 +920,27 @@ else() endif() endif() -# TODO: - CMAC -# - AES-XTS +# TODO: - AES-XTS # - Web server # - Web client +set(WOLFSSL_CMAC_HELP_STRING "Enable CMAC (default: disabled)") +add_option("WOLFSSL_CMAC" ${WOLFSSL_CMAC_HELP_STRING} "no" "yes;no") + +if(WOLFSSL_WPAS OR + WOLFSSL_NTP OR + WOLFSSL_AESSIV) + override_cache(WOLFSSL_CMAC "yes") +endif() + +if(WOLFSSL_CMAC) + if (NOT WOLFSSL_AES) + message(FATAL_ERROR "Cannot use CMAC without AES.") + else() + list(APPEND WOLFSSL_DEFINITIONS + "-DWOLFSSL_CMAC" + "-DWOLFSSL_AES_DIRECT") + endif() +endif() # RABBIT if(NOT WOLFSSL_RABBIT) diff --git a/configure.ac b/configure.ac index e3ffbbc87..7a1aabd20 100644 --- a/configure.ac +++ b/configure.ac @@ -498,6 +498,7 @@ then test "$enable_aesctr" = "" && enable_aesctr=yes test "$enable_aesofb" = "" && enable_aesofb=yes test "$enable_aescfb" = "" && enable_aescfb=yes + test "$enable_aessiv" = "" && enable_aessiv=yes test "$enable_aescbc_length_checks" = "" && enable_aescbc_length_checks=yes test "$enable_camellia" = "" && enable_camellia=yes test "$enable_ripemd" = "" && enable_ripemd=yes @@ -664,6 +665,7 @@ then test "$enable_aesctr" = "" && enable_aesctr=yes test "$enable_aesofb" = "" && enable_aesofb=yes test "$enable_aescfb" = "" && enable_aescfb=yes + test "$enable_aessiv" = "" && enable_aessiv=yes test "$enable_aescbc_length_checks" = "" && enable_aescbc_length_checks=yes test "$enable_camellia" = "" && enable_camellia=yes test "$enable_ripemd" = "" && enable_ripemd=yes @@ -1588,6 +1590,12 @@ then AM_CFLAGS="$AM_CFLAGS -DHAVE_AESCCM" fi +# AES-SIV (RFC 5297) +AC_ARG_ENABLE([aessiv], + [AS_HELP_STRING([--enable-aessiv],[Enable AES-SIV (RFC 5297) (default: disabled)])], + [ ENABLED_AESSIV=$enableval ], + [ ENABLED_AESSIV=no ] + ) # AES-CTR AC_ARG_ENABLE([aesctr], @@ -1595,7 +1603,7 @@ AC_ARG_ENABLE([aesctr], [ ENABLED_AESCTR=$enableval ], [ ENABLED_AESCTR=no ] ) -if test "$ENABLED_OPENVPN" = "yes" || test "$ENABLED_LIBSSH2" = "yes" +if test "$ENABLED_OPENVPN" = "yes" || test "$ENABLED_LIBSSH2" = "yes" || test "$ENABLED_AESSIV" = "yes" then ENABLED_AESCTR=yes fi @@ -3359,7 +3367,7 @@ AC_ARG_ENABLE([cmac], [ ENABLED_CMAC=no ] ) -if test "$ENABLED_WPAS" != "no" || test "$ENABLED_NTP" = "yes" +if test "$ENABLED_WPAS" != "no" || test "$ENABLED_NTP" = "yes" || test "$ENABLED_AESSIV" = "yes" then ENABLED_CMAC=yes fi @@ -6948,6 +6956,8 @@ fi AS_IF([test "x$ENABLED_OPENSSLALL" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -DOPENSSL_ALL -DWOLFSSL_EITHER_SIDE -DWC_RSA_NO_PADDING -DWC_RSA_PSS -DWOLFSSL_PSS_LONG_SALT"]) +AS_IF([test "x$ENABLED_AESSIV" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_AES_SIV"]) + AS_IF([test "x$ENABLED_AESCTR" = "xyes" && test "x$ENABLED_FORTRESS" != "xyes"], [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_AES_COUNTER -DWOLFSSL_AES_DIRECT"]) @@ -7679,6 +7689,7 @@ echo " * AES-CCM: $ENABLED_AESCCM" echo " * AES-CTR: $ENABLED_AESCTR" echo " * AES-CFB: $ENABLED_AESCFB" echo " * AES-OFB: $ENABLED_AESOFB" +echo " * AES-SIV: $ENABLED_AESSIV" echo " * DES3: $ENABLED_DES3" echo " * IDEA: $ENABLED_IDEA" echo " * Camellia: $ENABLED_CAMELLIA" diff --git a/doc/dox_comments/header_files/aes.h b/doc/dox_comments/header_files/aes.h index baa997e80..f28d650e4 100644 --- a/doc/dox_comments/header_files/aes.h +++ b/doc/dox_comments/header_files/aes.h @@ -934,3 +934,97 @@ WOLFSSL_API int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) \sa wc_AesSetKey */ WOLFSSL_API int wc_AesCfbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz); + +/*! + \ingroup AES + + \brief This function performs SIV (synthetic initialization vector) + encryption as described in RFC 5297. + + \return 0 On successful encryption. + \return BAD_FUNC_ARG If key, SIV, or output buffer are NULL. Also returned + if the key size isn't 32, 48, or 64 bytes. + \return Other Other negative error values returned if AES or CMAC operations + fail. + + \param key Byte buffer containing the key to use. + \param keySz Length of the key buffer in bytes. + \param assoc Additional, authenticated associated data (AD). + \param assocSz Length of AD buffer in bytes. + \param nonce A number used once. Used by the algorithm in the same manner as + the AD. + \param nonceSz Length of nonce buffer in bytes. + \param in Plaintext buffer to encrypt. + \param inSz Length of plaintext buffer. + \param siv The SIV output by S2V (see RFC 5297 2.4). + \param out Buffer to hold the ciphertext. Should be the same length as the + plaintext buffer. + + _Example_ + \code + byte key[] = { some 32, 48, or 64 byte key }; + byte assoc[] = {0x01, 0x2, 0x3}; + byte nonce[] = {0x04, 0x5, 0x6}; + byte plainText[] = {0xDE, 0xAD, 0xBE, 0xEF}; + byte siv[AES_BLOCK_SIZE]; + byte cipherText[sizeof(plainText)]; + if (wc_AesSivEncrypt(key, sizeof(key), assoc, sizeof(assoc), nonce, + sizeof(nonce), plainText, sizeof(plainText), siv, cipherText) != 0) { + // failed to encrypt + } + \endcode + + \sa wc_AesSivDecrypt +*/ + +WOLFSSL_API +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); + +/*! + \ingroup AES + \brief This function performs SIV (synthetic initialization vector) + decryption as described in RFC 5297. + + \return 0 On successful decryption. + \return BAD_FUNC_ARG If key, SIV, or output buffer are NULL. Also returned + if the key size isn't 32, 48, or 64 bytes. + \return AES_SIV_AUTH_E If the SIV derived by S2V doesn't match the input + SIV (see RFC 5297 2.7). + \return Other Other negative error values returned if AES or CMAC operations + fail. + + \param key Byte buffer containing the key to use. + \param keySz Length of the key buffer in bytes. + \param assoc Additional, authenticated associated data (AD). + \param assocSz Length of AD buffer in bytes. + \param nonce A number used once. Used by the underlying algorithm in the + same manner as the AD. + \param nonceSz Length of nonce buffer in bytes. + \param in Ciphertext buffer to decrypt. + \param inSz Length of ciphertext buffer. + \param siv The SIV that accompanies the ciphertext (see RFC 5297 2.4). + \param out Buffer to hold the decrypted plaintext. Should be the same length + as the ciphertext buffer. + + _Example_ + \code + byte key[] = { some 32, 48, or 64 byte key }; + byte assoc[] = {0x01, 0x2, 0x3}; + byte nonce[] = {0x04, 0x5, 0x6}; + byte cipherText[] = {0xDE, 0xAD, 0xBE, 0xEF}; + byte siv[AES_BLOCK_SIZE] = { the SIV that came with the ciphertext }; + byte plainText[sizeof(cipherText)]; + if (wc_AesSivDecrypt(key, sizeof(key), assoc, sizeof(assoc), nonce, + sizeof(nonce), cipherText, sizeof(cipherText), siv, plainText) != 0) { + // failed to decrypt + } + \endcode + + \sa wc_AesSivEncrypt +*/ +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); diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 8560e00a0..2c927ce26 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -282,6 +282,7 @@ #define BENCH_IDEA 0x00008000 #define BENCH_AES_CFB 0x00010000 #define BENCH_AES_OFB 0x00020000 +#define BENCH_AES_SIV 0x00040000 /* Digest algorithms. */ #define BENCH_MD5 0x00000001 #define BENCH_POLY1305 0x00000002 @@ -434,6 +435,9 @@ static const bench_alg bench_cipher_opt[] = { #ifdef HAVE_AESCCM { "-aes-ccm", BENCH_AES_CCM }, #endif +#ifdef WOLFSSL_AES_SIV + { "-aes-siv", BENCH_AES_SIV }, +#endif #ifdef HAVE_CAMELLIA { "-camellia", BENCH_CAMELLIA }, #endif @@ -1135,7 +1139,11 @@ static const XGEN_ALIGN byte bench_key_buf[] = 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, 0xfe,0xde,0xba,0x98,0x76,0x54,0x32,0x10, 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67, - 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, + 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, }; static const XGEN_ALIGN byte bench_iv_buf[] = @@ -1730,6 +1738,10 @@ static void* benchmarks_do(void* args) if (bench_all || (bench_cipher_algs & BENCH_AES_CCM)) bench_aesccm(); #endif +#ifdef WOLFSSL_AES_SIV + if (bench_all || (bench_cipher_algs & BENCH_AES_SIV)) + bench_aessiv(); +#endif #endif /* !NO_AES */ #ifdef HAVE_CAMELLIA @@ -3162,6 +3174,58 @@ void bench_aesccm(void) WC_FREE_VAR(bench_tag, HEAP_HINT); } #endif /* HAVE_AESCCM */ + + +#ifdef WOLFSSL_AES_SIV +static void bench_aessiv_internal(const byte* key, word32 keySz, const char* + encLabel, const char* decLabel) +{ + int i; + int ret = 0; + byte assoc[AES_BLOCK_SIZE]; + byte nonce[AES_BLOCK_SIZE]; + byte siv[AES_BLOCK_SIZE]; + int count = 0; + double start; + + bench_stats_start(&count, &start); + do { + for (i = 0; i < numBlocks; i++) { + ret = wc_AesSivEncrypt(key, keySz, assoc, AES_BLOCK_SIZE, nonce, + AES_BLOCK_SIZE, bench_plain, bench_size, + siv, bench_cipher); + if (ret != 0) { + printf("wc_AesSivEncrypt failed (%d)\n", ret); + return; + } + } + count += i; + } while (bench_stats_sym_check(start)); + bench_stats_sym_finish(encLabel, 0, count, bench_size, start, ret); + + bench_stats_start(&count, &start); + do { + for (i = 0; i < numBlocks; i++) { + ret = wc_AesSivDecrypt(key, keySz, assoc, AES_BLOCK_SIZE, nonce, + AES_BLOCK_SIZE, bench_cipher, bench_size, + siv, bench_plain); + if (ret != 0) { + printf("wc_AesSivDecrypt failed (%d)\n", ret); + return; + } + } + count += i; + } while (bench_stats_sym_check(start)); + bench_stats_sym_finish(decLabel, 0, count, bench_size, start, ret); +} + +void bench_aessiv(void) +{ + bench_aessiv_internal(bench_key, 32, "AES-256-SIV-enc", "AES-256-SIV-dec"); + bench_aessiv_internal(bench_key, 48, "AES-384-SIV-enc", "AES-384-SIV-dec"); + bench_aessiv_internal(bench_key, 64, "AES-512-SIV-enc", "AES-512-SIV-dec"); +} +#endif /* WOLFSSL_AES_SIV */ #endif /* !NO_AES */ diff --git a/wolfcrypt/benchmark/benchmark.h b/wolfcrypt/benchmark/benchmark.h index 5b0d1c922..f65094e59 100644 --- a/wolfcrypt/benchmark/benchmark.h +++ b/wolfcrypt/benchmark/benchmark.h @@ -55,6 +55,7 @@ void bench_aesxts(void); void bench_aesctr(void); void bench_aescfb(void); void bench_aesofb(void); +void bench_aessiv(void); void bench_poly1305(void); void bench_camellia(void); void bench_md5(int); diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 748e90ee0..07b81b72c 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -72,6 +72,10 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits #include #endif +#ifdef WOLFSSL_AES_SIV + #include +#endif + /* fips wrapper calls, user can call direct */ #if defined(HAVE_FIPS) && \ (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) @@ -11684,5 +11688,231 @@ int wc_AesXtsDecrypt(XtsAes* xaes, byte* out, const byte* in, word32 sz, #endif /* WOLFSSL_AES_XTS */ +#ifdef WOLFSSL_AES_SIV + +/* + * See RFC 5297 Section 2.4. + */ +static int S2V(const byte* key, word32 keySz, const byte* assoc, word32 assocSz, + const byte* nonce, word32 nonceSz, const byte* data, + word32 dataSz, byte* out) +{ +#ifdef WOLFSSL_SMALL_STACK + byte* tmp[3] = {NULL, NULL, NULL}; + int i; + Cmac* cmac; +#else + byte tmp[3][AES_BLOCK_SIZE]; + Cmac cmac[1]; +#endif + word32 macSz = AES_BLOCK_SIZE; + int ret = 0; + word32 zeroBytes; + +#ifdef WOLFSSL_SMALL_STACK + for (i = 0; i < 3; ++i) { + tmp[i] = (byte*)XMALLOC(AES_BLOCK_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (tmp[i] == NULL) { + ret = MEMORY_E; + break; + } + } + if (ret == 0) +#endif + { + 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) { + ShiftAndXorRb(tmp[0], tmp[1]); + ret = wc_AesCmacGenerate(tmp[1], &macSz, nonce, nonceSz, key, keySz); + if (ret == 0) { + xorbuf(tmp[0], tmp[1], AES_BLOCK_SIZE); + } + } + + if (ret == 0) { + if (dataSz >= AES_BLOCK_SIZE) { + + #ifdef WOLFSSL_SMALL_STACK + cmac = (Cmac*)XMALLOC(sizeof(Cmac), NULL, DYNAMIC_TYPE_CMAC); + if (cmac == NULL) { + ret = MEMORY_E; + } + if (ret == 0) + #endif + { + xorbuf(tmp[0], data + (dataSz - AES_BLOCK_SIZE), + AES_BLOCK_SIZE); + ret = wc_InitCmac(cmac, key, keySz, WC_CMAC_AES, NULL); + if (ret == 0) { + ret = wc_CmacUpdate(cmac, data, dataSz - AES_BLOCK_SIZE); + } + if (ret == 0) { + ret = wc_CmacUpdate(cmac, tmp[0], AES_BLOCK_SIZE); + } + if (ret == 0) { + ret = wc_CmacFinal(cmac, out, &macSz); + } + } + #ifdef WOLFSSL_SMALL_STACK + if (cmac != NULL) { + XFREE(cmac, NULL, DYNAMIC_TYPE_CMAC); + } + #endif + } + else { + XMEMCPY(tmp[2], data, dataSz); + tmp[2][dataSz] |= 0x80; + zeroBytes = AES_BLOCK_SIZE - (dataSz + 1); + if (zeroBytes != 0) { + XMEMSET(tmp[2] + dataSz + 1, 0, zeroBytes); + } + ShiftAndXorRb(tmp[1], tmp[0]); + xorbuf(tmp[1], tmp[2], AES_BLOCK_SIZE); + ret = wc_AesCmacGenerate(out, &macSz, tmp[1], AES_BLOCK_SIZE, key, + keySz); + } + } + +#ifdef WOLFSSL_SMALL_STACK + for (i = 0; i < 3; ++i) { + if (tmp[i] != NULL) { + XFREE(tmp[i], NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + } +#endif + + return ret; +} + +static int AesSivCipher(const byte* key, word32 keySz, const byte* assoc, + word32 assocSz, const byte* nonce, word32 nonceSz, + const byte* data, word32 dataSz, byte* siv, byte* out, + int enc) +{ + int ret = 0; +#ifdef WOLFSSL_SMALL_STACK + Aes* aes = NULL; +#else + Aes aes[1]; +#endif + byte sivTmp[AES_BLOCK_SIZE]; + + if (key == NULL || siv == NULL || out == NULL) { + WOLFSSL_MSG("Bad parameter"); + ret = BAD_FUNC_ARG; + } + + if (ret == 0 && keySz != 32 && keySz != 48 && keySz != 64) { + WOLFSSL_MSG("Bad key size. Must be 256, 384, or 512 bits."); + ret = BAD_FUNC_ARG; + } + +#ifdef WOLFSSL_SMALL_STACK + if (ret == 0) { + aes = (Aes*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_AES); + if (aes == NULL) { + ret = MEMORY_E; + } + } +#endif + + if (ret == 0) { + if (enc == 1) { + ret = S2V(key, keySz / 2, assoc, assocSz, nonce, nonceSz, data, + dataSz, sivTmp); + if (ret != 0) { + WOLFSSL_MSG("S2V failed."); + } + else { + XMEMCPY(siv, sivTmp, AES_BLOCK_SIZE); + } + } + else { + XMEMCPY(sivTmp, siv, AES_BLOCK_SIZE); + } + } + + if (ret == 0) { + ret = wc_AesInit(aes, NULL, INVALID_DEVID); + if (ret != 0) { + WOLFSSL_MSG("Failed to initialized AES object."); + } + } + + if (ret == 0 && dataSz > 0) { + sivTmp[12] &= 0x7f; + sivTmp[8] &= 0x7f; + ret = wc_AesSetKey(aes, key + keySz / 2, keySz / 2, sivTmp, + AES_ENCRYPTION); + if (ret != 0) { + WOLFSSL_MSG("Failed to set key for AES-CTR."); + } + else { + ret = wc_AesCtrEncrypt(aes, out, data, dataSz); + if (ret != 0) { + WOLFSSL_MSG("AES-CTR encryption failed."); + } + } + } + + if (ret == 0 && enc == 0) { + ret = S2V(key, keySz / 2, assoc, assocSz, nonce, nonceSz, out, dataSz, + sivTmp); + if (ret != 0) { + WOLFSSL_MSG("S2V failed."); + } + + if (XMEMCMP(siv, sivTmp, AES_BLOCK_SIZE) != 0) { + WOLFSSL_MSG("Computed SIV doesn't match received SIV."); + ret = AES_SIV_AUTH_E; + } + } + + wc_AesFree(aes); +#ifdef WOLFSSL_SMALL_STACK + XFREE(aes, NULL, DYNAMIC_TYPE_AES); +#endif + + return ret; +} + +/* + * See RFC 5297 Section 2.6. + */ +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, + siv, out, 1); +} + +/* + * See RFC 5297 Section 2.7. + */ +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, + siv, out, 0); +} + +#endif /* WOLFSSL_AES_SIV */ + #endif /* HAVE_FIPS */ #endif /* !NO_AES */ diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index 2abcdb741..3fe933adb 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -57,7 +57,12 @@ #endif +#ifdef HAVE_FIPS static void ShiftAndXorRb(byte* out, byte* in) +#else +/* Used by AES-SIV. See aes.c. */ +void ShiftAndXorRb(byte* out, byte* in) +#endif { int i, j, xorRb; int mask = 0, last = 0; @@ -76,8 +81,6 @@ static void ShiftAndXorRb(byte* out, byte* in) } } - - /* returns 0 on success */ int wc_InitCmac_ex(Cmac* cmac, const byte* key, word32 keySz, int type, void* unused, void* heap, int devId) diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c index 717ee8559..d690e3b86 100644 --- a/wolfcrypt/src/error.c +++ b/wolfcrypt/src/error.c @@ -230,6 +230,9 @@ const char* wc_GetErrorString(int error) case AES_CCM_AUTH_E: return "AES-CCM Authentication check fail"; + case AES_SIV_AUTH_E: + return "AES-SIV authentication failure"; + case ASYNC_INIT_E: return "Async Init error"; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 03dc9e5ff..0b8142005 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -556,6 +556,9 @@ WOLFSSL_TEST_SUBROUTINE int cryptocb_test(void); #ifdef WOLFSSL_CERT_PIV WOLFSSL_TEST_SUBROUTINE int certpiv_test(void); #endif +#ifdef WOLFSSL_AES_SIV +WOLFSSL_TEST_SUBROUTINE int aes_siv_test(void); +#endif /* General big buffer size for many tests. */ #define FOURK_BUF 4096 @@ -1142,6 +1145,12 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ else TEST_PASS("AES Key Wrap test passed!\n"); #endif +#ifdef WOLFSSL_AES_SIV + if ( (ret = aes_siv_test()) != 0) + return err_sys("AES-SIV test failed!\n", ret); + else + TEST_PASS("AES-SIV test passed!\n"); +#endif #endif #ifdef HAVE_CAMELLIA @@ -38526,6 +38535,148 @@ WOLFSSL_TEST_SUBROUTINE int time_test(void) } #endif +#ifdef WOLFSSL_AES_SIV + +typedef struct { + const byte key[32]; + word32 keySz; + const byte nonce[48]; + word32 nonceSz; + const byte assoc[80]; + word32 assocSz; + const byte plaintext[82]; + word32 plaintextSz; + const byte siv[AES_BLOCK_SIZE]; + const byte ciphertext[82]; + word32 ciphertextSz; +} AesSivTestVector; + +#define AES_SIV_TEST_VECTORS 7 + +WOLFSSL_TEST_SUBROUTINE int 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] = { + { "\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, + "", 0, + "", 0, + "\x22\x3e\xb5\x94\xe0\xe0\x25\x4b\x00\x25\x8e\x21\x9a\x1c\xa4\x21", + "", 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, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, + "", 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, + "", 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", + "\x14\xff\xb3\x97\x34\x5c\xcb\xe4\x4a\xa4\xde\xac\xd9\x36\x90\x46", 16 + }, + { "\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, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c", 15, + "\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 + }, + { "\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, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, + "\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 + }, + { "\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" + "\xd5", 17, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b" + "\xa0", 17, + "\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", + "\xa5\x11\xcd\xb6\x08\xf3\x76\xa0\xb6\xfa\x15\x82\xf3\x95\xe1\xeb" + "\xbd", 17 + }, + { "\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, + "\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, + "\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, + "\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" + "\xae\x81\xa1\xca\x5b\x5f\x45\xa6\x6f\x0c\x8a\xf3\xd4\x67\x40\x81" + "\x26\xe2\x01\x86\xe8\x5a\xd5\xf8\x58\x80\x9f\x56\xaa\x76\x96\xbf" + "\x31", 81, + "\x9a\x06\x33\xe0\xee\x00\x6a\x9b\xc8\x20\xd5\xe2\xc2\xed\xb5\x75", + "\xfa\x9e\x42\x2a\x31\x6b\xda\xca\xaa\x7d\x31\x8b\x84\x7a\xb8\xd7" + "\x8a\x81\x25\x64\xed\x41\x9b\xa9\x77\x10\xbd\x05\x0c\x4e\xc5\x31" + "\x0c\xa2\x86\xec\x8a\x94\xc8\x24\x23\x3c\x13\xee\xa5\x51\xc9\xdf" + "\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 + }}; + int i; + byte computedCiphertext[82]; + byte computedPlaintext[82]; + byte siv[AES_BLOCK_SIZE]; + int rc = 0; + + for (i = 0; i < AES_SIV_TEST_VECTORS; ++i) { + rc = wc_AesSivEncrypt(testVectors[i].key, testVectors[i].keySz, + testVectors[i].assoc, testVectors[i].assocSz, + testVectors[i].nonce, testVectors[i].nonceSz, + testVectors[i].plaintext, + testVectors[i].plaintextSz, siv, + computedCiphertext); + if (rc != 0) { + return -16000; + } + rc = XMEMCMP(siv, testVectors[i].siv, AES_BLOCK_SIZE); + if (rc != 0) { + return -16001; + } + rc = XMEMCMP(computedCiphertext, testVectors[i].ciphertext, + testVectors[i].ciphertextSz); + if (rc != 0) { + return -16002; + } + rc = wc_AesSivDecrypt(testVectors[i].key, testVectors[i].keySz, + testVectors[i].assoc, testVectors[i].assocSz, + testVectors[i].nonce, testVectors[i].nonceSz, + computedCiphertext, testVectors[i].plaintextSz, + siv, computedPlaintext); + if (rc != 0) { + return -16003; + } + rc = XMEMCMP(computedPlaintext, testVectors[i].plaintext, + testVectors[i].plaintextSz); + if (rc != 0) { + return -16004; + } + } + + return 0; +} +#endif + #undef ERROR_OUT #else diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 639e13157..b15ed6e8b 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -523,6 +523,17 @@ WOLFSSL_API int wc_AesInit_Label(Aes* aes, const char* label, void* heap, #endif WOLFSSL_API void wc_AesFree(Aes* aes); +#ifdef WOLFSSL_AES_SIV +WOLFSSL_API +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); +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); +#endif + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfssl/wolfcrypt/cmac.h b/wolfssl/wolfcrypt/cmac.h index 9519af9f3..ee99f0a66 100644 --- a/wolfssl/wolfcrypt/cmac.h +++ b/wolfssl/wolfcrypt/cmac.h @@ -103,6 +103,11 @@ int wc_AesCmacVerify(const byte* check, word32 checkSz, const byte* in, word32 inSz, const byte* key, word32 keySz); +#ifndef HAVE_FIPS +WOLFSSL_LOCAL +void ShiftAndXorRb(byte* out, byte* in); +#endif + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index 68318c228..b876a516f 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -247,8 +247,9 @@ enum { ECC_PCT_E = -286, /* ECDHE Pairwise Consistency Test failure */ FIPS_PRIVATE_KEY_LOCKED_E = -287, /* Cannot export private key. */ PROTOCOLCB_UNAVAILABLE = -288, /* Protocol callback unavailable */ + AES_SIV_AUTH_E = -289, /* AES-SIV authentication failed */ - WC_LAST_E = -288, /* Update this to indicate last error */ + WC_LAST_E = -289, /* Update this to indicate last error */ MIN_CODE_E = -300 /* errors -101 - -299 */ /* add new companion error id strings for any new error codes