diff --git a/configure.ac b/configure.ac index 3cf5971ad..6d27e8408 100644 --- a/configure.ac +++ b/configure.ac @@ -5438,6 +5438,22 @@ AC_ARG_ENABLE([xts], [ ENABLED_AESXTS=$enableval ] ) +# AES-CTS +AC_ARG_ENABLE([aescts], + [AS_HELP_STRING([--enable-aescts],[Enable AES CTS (default: disabled)])], + [ ENABLED_AESCTS=$enableval ], + [ ENABLED_AESCTS=no ] + ) + +if test "$ENABLED_AESCTS" = "yes" +then + if test "$ENABLED_AESCBC" = "no" + then + AC_MSG_ERROR([AES CTS requires AES CBC.]) + fi + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_AES_CTS" +fi + # Web Server Build AC_ARG_ENABLE([webserver], [AS_HELP_STRING([--enable-webserver],[Enable Web Server (default: disabled)])], diff --git a/tests/api/test_aes.c b/tests/api/test_aes.c index 181a4cb9d..98adc3a03 100644 --- a/tests/api/test_aes.c +++ b/tests/api/test_aes.c @@ -263,6 +263,230 @@ int test_wc_AesCbcEncryptDecrypt(void) return EXPECT_RESULT(); } /* END test_wc_AesCbcEncryptDecrypt */ +/******************************************************************************* + * AES-CTS + ******************************************************************************/ + +int test_wc_AesCtsEncryptDecrypt(void) +{ + EXPECT_DECLS; +#if !defined(NO_AES) && defined(WOLFSSL_AES_CTS) && \ + defined(HAVE_AES_DECRYPT) && defined(WOLFSSL_AES_128) + /* Test vectors taken form RFC3962 Appendix B */ + struct { + const char* input; + const char* output; + size_t inLen; + size_t outLen; + } vects[] = { + { + "\x49\x20\x77\x6f\x75\x6c\x64\x20\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20", + "\xc6\x35\x35\x68\xf2\xbf\x8c\xb4\xd8\xa5\x80\x36\x2d\xa7\xff\x7f" + "\x97", + 17, 17 + }, + { + "\x49\x20\x77\x6f\x75\x6c\x64\x20\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c\x20\x47\x61\x75\x27\x73\x20", + "\xfc\x00\x78\x3e\x0e\xfd\xb2\xc1\xd4\x45\xd4\xc8\xef\xf7\xed\x22" + "\x97\x68\x72\x68\xd6\xec\xcc\xc0\xc0\x7b\x25\xe2\x5e\xcf\xe5", + 31, 31 + }, + { + "\x49\x20\x77\x6f\x75\x6c\x64\x20\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c\x20\x47\x61\x75\x27\x73\x20\x43", + "\x39\x31\x25\x23\xa7\x86\x62\xd5\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" + "\x97\x68\x72\x68\xd6\xec\xcc\xc0\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84", + 32, 32 + }, + { + "\x49\x20\x77\x6f\x75\x6c\x64\x20\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20\x70\x6c\x65\x61\x73\x65\x2c", + "\x97\x68\x72\x68\xd6\xec\xcc\xc0\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\xb3\xff\xfd\x94\x0c\x16\xa1\x8c\x1b\x55\x49\xd2\xf8\x38\x02\x9e" + "\x39\x31\x25\x23\xa7\x86\x62\xd5\xbe\x7f\xcb\xcc\x98\xeb\xf5", + 47, 47 + }, + { + "\x49\x20\x77\x6f\x75\x6c\x64\x20\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20\x70\x6c\x65\x61\x73\x65\x2c\x20", + "\x97\x68\x72\x68\xd6\xec\xcc\xc0\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8" + "\x39\x31\x25\x23\xa7\x86\x62\xd5\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8", + 48, 48 + }, + { + "\x49\x20\x77\x6f\x75\x6c\x64\x20\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c\x20\x47\x61\x75\x27\x73\x20\x43" + "\x68\x69\x63\x6b\x65\x6e\x2c\x20\x70\x6c\x65\x61\x73\x65\x2c\x20" + "\x61\x6e\x64\x20\x77\x6f\x6e\x74\x6f\x6e\x20\x73\x6f\x75\x70\x2e", + "\x97\x68\x72\x68\xd6\xec\xcc\xc0\xc0\x7b\x25\xe2\x5e\xcf\xe5\x84" + "\x39\x31\x25\x23\xa7\x86\x62\xd5\xbe\x7f\xcb\xcc\x98\xeb\xf5\xa8" + "\x48\x07\xef\xe8\x36\xee\x89\xa5\x26\x73\x0d\xbc\x2f\x7b\xc8\x40" + "\x9d\xad\x8b\xbb\x96\xc4\xcd\xc0\x3b\xc1\x03\xe1\xa1\x94\xbb\xd8", + 64, 64 + } + }; + byte keyBytes[AES_128_KEY_SIZE] = { + 0x63, 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x20, + 0x74, 0x65, 0x72, 0x69, 0x79, 0x61, 0x6b, 0x69 + }; + byte tmp[64]; /* Largest vector size */ + size_t i; + byte iv[AES_IV_SIZE]; /* All-zero IV for all cases */ + + XMEMSET(iv, 0, sizeof(iv)); + for (i = 0; i < XELEM_CNT(vects) && EXPECT_SUCCESS(); i++) { + /* One-shot encrypt */ + XMEMSET(tmp, 0, sizeof(tmp)); + ExpectIntEQ(wc_AesCtsEncrypt(keyBytes, sizeof(keyBytes), tmp, + (const byte*)vects[i].input, (word32)vects[i].inLen, iv), 0); + ExpectBufEQ(tmp, vects[i].output, vects[i].outLen); + XMEMSET(tmp, 0, sizeof(tmp)); + ExpectIntEQ(wc_AesCtsDecrypt(keyBytes, sizeof(keyBytes), tmp, + (const byte*)vects[i].output, (word32)vects[i].outLen, iv), 0); + ExpectBufEQ(tmp, vects[i].input, vects[i].inLen); + } + /* Execute all branches */ + { + Aes* aes = NULL; + int result_code = 0; + const byte* in = (const byte*)vects[5].input; + byte* out = tmp; + word32 outSz = (word32)vects[5].outLen; + word32 remSz = (word32)vects[5].outLen; + + XMEMSET(tmp, 0, sizeof(tmp)); + ExpectNotNull(aes = wc_AesNew(NULL, INVALID_DEVID, &result_code)); + ExpectIntEQ(wc_AesSetKey(aes, keyBytes, sizeof(keyBytes), iv, + AES_ENCRYPTION), 0); + ExpectIntEQ(wc_AesCtsEncryptUpdate(aes, out, &outSz, in, 1), 0); + in += 1; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsEncryptUpdate(aes, out, &outSz, in, 31), 0); + in += 31; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsEncryptUpdate(aes, out, &outSz, in, 32), 0); + in += 32; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsEncryptFinal(aes, out, &outSz), 0); + remSz -= outSz; + ExpectIntEQ(remSz, 0); + ExpectBufEQ(tmp, vects[5].output, vects[5].outLen); + ExpectIntEQ(wc_AesDelete(aes, &aes), 0); + } + { + Aes* aes = NULL; + int result_code = 0; + const byte* in = (const byte*)vects[5].input; + byte* out = tmp; + word32 outSz = (word32)vects[5].outLen; + word32 remSz = (word32)vects[5].outLen; + + ExpectNotNull(aes = wc_AesNew(NULL, INVALID_DEVID, &result_code)); + ExpectIntEQ(wc_AesSetKey(aes, keyBytes, sizeof(keyBytes), iv, + AES_ENCRYPTION), 0); + ExpectIntEQ(wc_AesCtsEncryptUpdate(aes, out, &outSz, in, 1), 0); + in += 1; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsEncryptUpdate(aes, out, &outSz, in, 63), 0); + in += 63; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsEncryptFinal(aes, out, &outSz), 0); + remSz -= outSz; + ExpectIntEQ(remSz, 0); + ExpectBufEQ(tmp, vects[5].output, vects[5].outLen); + ExpectIntEQ(wc_AesDelete(aes, &aes), 0); + } + { + Aes* aes = NULL; + int result_code = 0; + const byte* in = (const byte*)vects[2].input; + byte* out = tmp; + word32 outSz = (word32)vects[2].outLen; + word32 remSz = (word32)vects[2].outLen; + + ExpectNotNull(aes = wc_AesNew(NULL, INVALID_DEVID, &result_code)); + ExpectIntEQ(wc_AesSetKey(aes, keyBytes, sizeof(keyBytes), iv, + AES_ENCRYPTION), 0); + ExpectIntEQ(wc_AesCtsEncryptUpdate(aes, out, &outSz, in, 16), 0); + in += 16; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsEncryptUpdate(aes, out, &outSz, in, 16), 0); + in += 16; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsEncryptFinal(aes, out, &outSz), 0); + remSz -= outSz; + ExpectIntEQ(remSz, 0); + ExpectBufEQ(tmp, vects[2].output, vects[2].outLen); + ExpectIntEQ(wc_AesDelete(aes, &aes), 0); + } + { + Aes* aes = NULL; + int result_code = 0; + const byte* in = (const byte*)vects[5].output; + byte* out = tmp; + word32 outSz = (word32)vects[5].inLen; + word32 remSz = (word32)vects[5].inLen; + + XMEMSET(tmp, 0, sizeof(tmp)); + ExpectNotNull(aes = wc_AesNew(NULL, INVALID_DEVID, &result_code)); + ExpectIntEQ(wc_AesSetKey(aes, keyBytes, sizeof(keyBytes), iv, + AES_DECRYPTION), 0); + ExpectIntEQ(wc_AesCtsDecryptUpdate(aes, out, &outSz, in, 1), 0); + in += 1; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsDecryptUpdate(aes, out, &outSz, in, 31), 0); + in += 31; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsDecryptUpdate(aes, out, &outSz, in, 32), 0); + in += 32; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsDecryptFinal(aes, out, &outSz), 0); + remSz -= outSz; + ExpectIntEQ(remSz, 0); + ExpectBufEQ(tmp, vects[5].input, vects[5].inLen); + ExpectIntEQ(wc_AesDelete(aes, &aes), 0); + } + { + Aes* aes = NULL; + int result_code = 0; + const byte* in = (const byte*)vects[5].output; + byte* out = tmp; + word32 outSz = (word32)vects[5].inLen; + word32 remSz = (word32)vects[5].inLen; + + ExpectNotNull(aes = wc_AesNew(NULL, INVALID_DEVID, &result_code)); + ExpectIntEQ(wc_AesSetKey(aes, keyBytes, sizeof(keyBytes), iv, + AES_DECRYPTION), 0); + ExpectIntEQ(wc_AesCtsDecryptUpdate(aes, out, &outSz, in, 1), 0); + in += 1; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsDecryptUpdate(aes, out, &outSz, in, 63), 0); + in += 63; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsDecryptFinal(aes, out, &outSz), 0); + remSz -= outSz; + ExpectIntEQ(remSz, 0); + ExpectBufEQ(tmp, vects[5].input, vects[5].inLen); + ExpectIntEQ(wc_AesDelete(aes, &aes), 0); + } + { + Aes* aes = NULL; + int result_code = 0; + const byte* in = (const byte*)vects[2].output; + byte* out = tmp; + word32 outSz = (word32)vects[2].inLen; + word32 remSz = (word32)vects[2].inLen; + + ExpectNotNull(aes = wc_AesNew(NULL, INVALID_DEVID, &result_code)); + ExpectIntEQ(wc_AesSetKey(aes, keyBytes, sizeof(keyBytes), iv, + AES_DECRYPTION), 0); + ExpectIntEQ(wc_AesCtsDecryptUpdate(aes, out, &outSz, in, 16), 0); + in += 16; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsDecryptUpdate(aes, out, &outSz, in, 16), 0); + in += 16; out += outSz; remSz -= outSz; outSz = remSz; + ExpectIntEQ(wc_AesCtsDecryptFinal(aes, out, &outSz), 0); + remSz -= outSz; + ExpectIntEQ(remSz, 0); + ExpectBufEQ(tmp, vects[2].input, vects[2].inLen); + ExpectIntEQ(wc_AesDelete(aes, &aes), 0); + } +#endif + return EXPECT_RESULT(); +} + /******************************************************************************* * AES-CTR ******************************************************************************/ diff --git a/tests/api/test_aes.h b/tests/api/test_aes.h index 46d13c541..22b24732a 100644 --- a/tests/api/test_aes.h +++ b/tests/api/test_aes.h @@ -27,6 +27,7 @@ int test_wc_AesSetKey(void); int test_wc_AesSetIV(void); int test_wc_AesCbcEncryptDecrypt(void); +int test_wc_AesCtsEncryptDecrypt(void); int test_wc_AesCtrEncryptDecrypt(void); int test_wc_AesGcmSetKey(void); int test_wc_AesGcmEncryptDecrypt(void); @@ -48,6 +49,7 @@ int test_wc_GmacUpdate(void); TEST_DECL_GROUP("aes", test_wc_AesSetKey), \ TEST_DECL_GROUP("aes", test_wc_AesSetIV), \ TEST_DECL_GROUP("aes", test_wc_AesCbcEncryptDecrypt), \ + TEST_DECL_GROUP("aes", test_wc_AesCtsEncryptDecrypt), \ TEST_DECL_GROUP("aes", test_wc_AesCtrEncryptDecrypt), \ TEST_DECL_GROUP("aes", test_wc_AesGcmSetKey), \ TEST_DECL_GROUP("aes", test_wc_AesGcmEncryptDecrypt), \ diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 926e80002..754390d4f 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -3759,7 +3759,8 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt( ByteReverseWords(rk, rk, keylen); #endif #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) aes->left = 0; #endif return wc_AesSetIV(aes, iv); @@ -3840,7 +3841,8 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt( XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE); #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) aes->left = 0; #endif @@ -3871,7 +3873,8 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt( XMEMCPY(aes->key, userKey, keylen); #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) aes->left = 0; #endif @@ -3923,7 +3926,8 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt( return BAD_FUNC_ARG; #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) aes->left = 0; #endif @@ -4004,7 +4008,8 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt( ret = nrf51_aes_set_key(userKey); #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) aes->left = 0; #endif @@ -4061,7 +4066,8 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt( XMEMCPY(aes->key, userKey, keylen); #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) aes->left = 0; #endif return wc_AesSetIV(aes, iv); @@ -4554,7 +4560,8 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) } #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) aes->left = 0; #endif @@ -4836,7 +4843,8 @@ int wc_AesSetIV(Aes* aes, const byte* iv) XMEMSET(aes->reg, 0, WC_AES_BLOCK_SIZE); #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) /* Clear any unused bytes from last cipher op. */ aes->left = 0; #endif @@ -10857,7 +10865,7 @@ int wc_GmacVerify(const byte* key, word32 keySz, #endif /* WC_NO_RNG */ -WOLFSSL_API int wc_GmacSetKey(Gmac* gmac, const byte* key, word32 len) +int wc_GmacSetKey(Gmac* gmac, const byte* key, word32 len) { if (gmac == NULL || key == NULL) { return BAD_FUNC_ARG; @@ -10866,7 +10874,7 @@ WOLFSSL_API int wc_GmacSetKey(Gmac* gmac, const byte* key, word32 len) } -WOLFSSL_API int wc_GmacUpdate(Gmac* gmac, const byte* iv, word32 ivSz, +int wc_GmacUpdate(Gmac* gmac, const byte* iv, word32 ivSz, const byte* authIn, word32 authInSz, byte* authTag, word32 authTagSz) { @@ -14906,4 +14914,278 @@ int wc_AesEaxFree(AesEax* eax) #endif /* WOLFSSL_AES_EAX */ +#ifdef WOLFSSL_AES_CTS + + +/* One-shot API */ +int wc_AesCtsEncrypt(const byte* key, word32 keySz, byte* out, + const byte* in, word32 inSz, + const byte* iv) +{ +#ifdef WOLFSSL_SMALL_STACK + Aes *aes = NULL; +#else + Aes _aes; + Aes *aes = &_aes; +#endif + int ret = 0; + word32 outSz = inSz; + + if (key == NULL || out == NULL || in == NULL || iv == NULL) + return BAD_FUNC_ARG; + +#ifdef WOLFSSL_SMALL_STACK + aes = wc_AesNew(NULL, INVALID_DEVID, &ret); +#else + ret = wc_AesInit(aes, NULL, INVALID_DEVID); +#endif + if (ret == 0) + ret = wc_AesSetKey(aes, key, keySz, iv, AES_ENCRYPTION); + if (ret == 0) + ret = wc_AesCtsEncryptUpdate(aes, out, &outSz, in, inSz); + if (ret == 0) { + out += outSz; + outSz = inSz - outSz; + ret = wc_AesCtsEncryptFinal(aes, out, &outSz); + } + +#ifdef WOLFSSL_SMALL_STACK + wc_AesDelete(aes, NULL); +#else + wc_AesFree(aes); +#endif + return ret; +} + +int wc_AesCtsDecrypt(const byte* key, word32 keySz, byte* out, + const byte* in, word32 inSz, + const byte* iv) +{ +#ifdef WOLFSSL_SMALL_STACK + Aes *aes = NULL; +#else + Aes _aes; + Aes *aes = &_aes; +#endif + int ret = 0; + word32 outSz = inSz; + + if (key == NULL || out == NULL || in == NULL || iv == NULL) { + return BAD_FUNC_ARG; + } + +#ifdef WOLFSSL_SMALL_STACK + aes = wc_AesNew(NULL, INVALID_DEVID, &ret); +#else + ret = wc_AesInit(aes, NULL, INVALID_DEVID); +#endif + if (ret == 0) + ret = wc_AesSetKey(aes, key, keySz, iv, AES_DECRYPTION); + if (ret == 0) + ret = wc_AesCtsDecryptUpdate(aes, out, &outSz, in, inSz); + if (ret == 0) { + out += outSz; + outSz = inSz - outSz; + ret = wc_AesCtsDecryptFinal(aes, out, &outSz); + } + +#ifdef WOLFSSL_SMALL_STACK + wc_AesDelete(aes, NULL); +#else + wc_AesFree(aes); +#endif + return ret; +} + +static int AesCtsUpdate(Aes* aes, byte* out, word32* outSz, + const byte* in, word32 inSz, int enc) +{ + word32 blocks = 0; + int ret = 0; + word32 writtenSz = 0; + word32 tmpOutSz; + + if (aes == NULL || out == NULL || in == NULL || outSz == NULL) + return BAD_FUNC_ARG; + + /* Error out early for easy sanity check */ + if (*outSz < inSz) + return BUFFER_E; + tmpOutSz = *outSz; + + /* We need to store last two blocks of plaintext */ + if (aes->left > 0) { + word32 copySz = min(inSz, (WC_AES_BLOCK_SIZE * 2) - aes->left); + XMEMCPY(aes->ctsBlock + aes->left, in, copySz); + aes->left += copySz; + in += copySz; + inSz -= copySz; + + if (aes->left == WC_AES_BLOCK_SIZE * 2) { + if (inSz > WC_AES_BLOCK_SIZE) { + if (tmpOutSz < WC_AES_BLOCK_SIZE * 2) + return BUFFER_E; + if (enc) { + ret = wc_AesCbcEncrypt(aes, out, aes->ctsBlock, + WC_AES_BLOCK_SIZE * 2); + } + else { + ret = wc_AesCbcDecrypt(aes, out, aes->ctsBlock, + WC_AES_BLOCK_SIZE * 2); + } + if (ret != 0) + return ret; + out += WC_AES_BLOCK_SIZE * 2; + writtenSz += WC_AES_BLOCK_SIZE * 2; + tmpOutSz -= WC_AES_BLOCK_SIZE * 2; + aes->left = 0; + } + else if (inSz > 0) { + if (tmpOutSz < WC_AES_BLOCK_SIZE) + return BUFFER_E; + if (enc) { + ret = wc_AesCbcEncrypt(aes, out, aes->ctsBlock, + WC_AES_BLOCK_SIZE); + } + else { + ret = wc_AesCbcDecrypt(aes, out, aes->ctsBlock, + WC_AES_BLOCK_SIZE); + } + if (ret != 0) + return ret; + out += WC_AES_BLOCK_SIZE; + writtenSz += WC_AES_BLOCK_SIZE; + tmpOutSz -= WC_AES_BLOCK_SIZE; + /* Move the last block in ctsBlock to the beginning for + * next operation */ + XMEMCPY(aes->ctsBlock, aes->ctsBlock + WC_AES_BLOCK_SIZE, + WC_AES_BLOCK_SIZE); + XMEMCPY(aes->ctsBlock + WC_AES_BLOCK_SIZE, in, inSz); + aes->left = WC_AES_BLOCK_SIZE + inSz; + *outSz = writtenSz; + return ret; /* Return the result of encryption */ + } + else { + /* Can't output data as we need > 1 block for Final call */ + *outSz = writtenSz; + return 0; + } + } + else { + /* All input has been absorbed into aes->ctsBlock */ + *outSz = 0; + return 0; + } + } + if (inSz > WC_AES_BLOCK_SIZE) { + /* We need to store the last two full or partial blocks */ + blocks = (inSz + (WC_AES_BLOCK_SIZE - 1)) / WC_AES_BLOCK_SIZE; + blocks -= 2; + } + if (tmpOutSz < blocks * WC_AES_BLOCK_SIZE) + return BUFFER_E; + if (enc) + ret = wc_AesCbcEncrypt(aes, out, in, blocks * WC_AES_BLOCK_SIZE); + else + ret = wc_AesCbcDecrypt(aes, out, in, blocks * WC_AES_BLOCK_SIZE); + in += blocks * WC_AES_BLOCK_SIZE; + inSz -= blocks * WC_AES_BLOCK_SIZE; + XMEMCPY(aes->ctsBlock, in, inSz); + aes->left = inSz; + writtenSz += blocks * WC_AES_BLOCK_SIZE; + *outSz = writtenSz; + return ret; +} + +/* Incremental API */ +int wc_AesCtsEncryptUpdate(Aes* aes, byte* out, word32* outSz, + const byte* in, word32 inSz) +{ + return AesCtsUpdate(aes, out, outSz, in, inSz, 1); +} + +int wc_AesCtsEncryptFinal(Aes* aes, byte* out, word32* outSz) +{ + int ret = 0; + + if (aes == NULL || out == NULL || outSz == NULL) + return BAD_FUNC_ARG; + if (*outSz < aes->left) + return BUFFER_E; + + /* Input must be at least two complete or partial blocks */ + if (aes->left <= WC_AES_BLOCK_SIZE) + return BAD_FUNC_ARG; + + /* Zero padding */ + XMEMSET(aes->ctsBlock + aes->left, 0, (WC_AES_BLOCK_SIZE * 2) - aes->left); + + ret = wc_AesCbcEncrypt(aes, aes->ctsBlock, aes->ctsBlock, + WC_AES_BLOCK_SIZE * 2); + if (ret != 0) + return ret; + + XMEMCPY(out, aes->ctsBlock + WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); + XMEMCPY(out + WC_AES_BLOCK_SIZE, aes->ctsBlock, + aes->left - WC_AES_BLOCK_SIZE); + *outSz = aes->left; + return ret; +} + +int wc_AesCtsDecryptUpdate(Aes* aes, byte* out, word32* outSz, + const byte* in, word32 inSz) +{ + return AesCtsUpdate(aes, out, outSz, in, inSz, 0); +} + +int wc_AesCtsDecryptFinal(Aes* aes, byte* out, word32* outSz) +{ + int ret = 0; + byte iv[WC_AES_BLOCK_SIZE]; + byte tmp[WC_AES_BLOCK_SIZE]; + word32 partialSz; + word32 padSz; + + if (aes == NULL || out == NULL || outSz == NULL) + return BAD_FUNC_ARG; + if (*outSz < aes->left) + return BUFFER_E; + + /* Input must be at least two complete or partial blocks */ + if (aes->left <= WC_AES_BLOCK_SIZE) + return BAD_FUNC_ARG; + + partialSz = aes->left - WC_AES_BLOCK_SIZE; + padSz = 2 * WC_AES_BLOCK_SIZE - aes->left; + /* Zero pad */ + XMEMSET(aes->ctsBlock + aes->left, 0, padSz); + + /* Store IV */ + XMEMCPY(iv, aes->reg, WC_AES_BLOCK_SIZE); + /* Load IV */ + XMEMCPY(aes->reg, aes->ctsBlock + WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); + + ret = wc_AesCbcDecrypt(aes, tmp, aes->ctsBlock, WC_AES_BLOCK_SIZE); + if (ret != 0) + return ret; + + /* Write out partial block */ + XMEMCPY(out + WC_AES_BLOCK_SIZE, tmp, partialSz); + /* Retrieve the padding */ + XMEMCPY(aes->ctsBlock + aes->left, tmp + partialSz, padSz); + /* Restore IV */ + XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE); + + ret = wc_AesCbcDecrypt(aes, out, aes->ctsBlock + WC_AES_BLOCK_SIZE, + WC_AES_BLOCK_SIZE); + if (ret != 0) + return ret; + + *outSz = aes->left; + return ret; +} + +#endif /* WOLFSSL_AES_CTS */ + + #endif /* !NO_AES */ diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 0ce8acc02..128611cc5 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -334,7 +334,8 @@ struct Aes { WC_ASYNC_DEV asyncDev; #endif /* WOLFSSL_ASYNC_CRYPT */ #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ - defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_AES_CTS) word32 left; /* unused bytes left from last call */ #endif #ifdef WOLFSSL_XILINX_CRYPT @@ -425,6 +426,9 @@ struct Aes { * trackable by sanitizers. */ #endif +#ifdef WOLFSSL_AES_CTS + byte ctsBlock[WC_AES_BLOCK_SIZE * 2]; +#endif }; #ifndef WC_AES_TYPE_DEFINED @@ -849,6 +853,28 @@ WOLFSSL_API int wc_AesEaxFree(AesEax* eax); #endif /* WOLFSSL_AES_EAX */ +#ifdef WOLFSSL_AES_CTS +/* Ciphertext stealing encryption compatible with RFC2040 and RFC3962. */ + +/* One-shot API */ +WOLFSSL_API int wc_AesCtsEncrypt(const byte* key, word32 keySz, byte* out, + const byte* in, word32 inSz, + const byte* iv); +WOLFSSL_API int wc_AesCtsDecrypt(const byte* key, word32 keySz, byte* out, + const byte* in, word32 inSz, + const byte* iv); + +/* Incremental API */ +WOLFSSL_API int wc_AesCtsEncryptUpdate(Aes* aes, byte* out, word32* outSz, + const byte* in, word32 inSz); +WOLFSSL_API int wc_AesCtsDecryptUpdate(Aes* aes, byte* out, word32* outSz, + const byte* in, word32 inSz); +WOLFSSL_API int wc_AesCtsEncryptFinal(Aes* aes, byte* out, word32* outSz); +WOLFSSL_API int wc_AesCtsDecryptFinal(Aes* aes, byte* out, word32* outSz); + + +#endif + #if defined(__aarch64__) && defined(WOLFSSL_ARMASM) && \ !defined(WOLFSSL_ARMASM_NO_HW_CRYPTO) diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 12397f182..cbdb35f3d 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -3170,6 +3170,11 @@ extern void uITRON4_free(void *p) ; #define WOLFSSL_AES_DIRECT #endif #endif + #ifdef WOLFSSL_AES_CTS + #if defined(NO_AES_CBC) || !defined(HAVE_AES_CBC) + #error "AES CTS requires AES CBC" + #endif + #endif #endif #if (defined(WOLFSSL_TLS13) && defined(WOLFSSL_NO_TLS12)) || \