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