Merge pull request #8594 from julek-wolfssl/nss

Implement AES-CTS in wolfCrypt
This commit is contained in:
Daniel Pouzzner
2025-04-15 18:35:52 -05:00
committed by GitHub
7 changed files with 803 additions and 11 deletions

View File

@@ -5444,6 +5444,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)])],

View File

@@ -1733,5 +1733,244 @@ WOLFSSL_API int wc_AesEaxDecryptFinal(AesEax* eax,
*/
WOLFSSL_API int wc_AesEaxFree(AesEax* eax);
/*!
\ingroup AES
\brief This function performs AES encryption using Ciphertext Stealing (CTS)
mode. It is a one-shot API that handles all operations in a single call.
\return 0 on successful encryption.
\return BAD_FUNC_ARG if input arguments are invalid.
\return other negative error codes for encryption failures.
\param key pointer to the AES key used for encryption.
\param keySz size of the AES key in bytes (16, 24, or 32 bytes).
\param[out] out buffer to hold the encrypted ciphertext. Must be at least
the size of the input.
\param in pointer to the plaintext input data to encrypt.
\param inSz size of the plaintext input data in bytes.
\param iv pointer to the initialization vector (IV) used for encryption.
Must be 16 bytes.
_Example_
\code
byte key[16] = { 0 };
byte iv[16] = { 0 };
byte plaintext[] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
byte ciphertext[sizeof(plaintext)];
int ret = wc_AesCtsEncrypt(key, sizeof(key), ciphertext, plaintext,
sizeof(plaintext), iv);
if (ret != 0) {
// handle encryption error
}
\endcode
\sa wc_AesCtsDecrypt
*/
int wc_AesCtsEncrypt(const byte* key, word32 keySz, byte* out,
const byte* in, word32 inSz,
const byte* iv);
/*!
\ingroup AES
\brief This function performs AES encryption using Ciphertext Stealing (CTS)
mode. It is a one-shot API that handles all operations in a single call.
\return 0 on successful encryption.
\return BAD_FUNC_ARG if input arguments are invalid.
\return other negative error codes for encryption failures.
\param key pointer to the AES key used for encryption.
\param keySz size of the AES key in bytes (16, 24, or 32 bytes).
\param[out] out buffer to hold the encrypted ciphertext. Must be at least
the same size as the input plaintext.
\param in pointer to the plaintext input data to encrypt.
\param inSz size of the plaintext input data in bytes.
\param iv pointer to the initialization vector (IV) used for encryption.
Must be 16 bytes.
_Example_
\code
byte key[16] = { 0 };
byte iv[16] = { 0 };
byte plaintext[] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
byte ciphertext[sizeof(plaintext)];
int ret = wc_AesCtsEncrypt(key, sizeof(key), ciphertext, plaintext,
sizeof(plaintext), iv);
if (ret != 0) {
// handle encryption error
}
\endcode
\sa wc_AesCtsDecrypt
*/
int wc_AesCtsEncrypt(const byte* key, word32 keySz, byte* out,
const byte* in, word32 inSz,
const byte* iv);
/*!
\ingroup AES
\brief This function performs AES decryption using Ciphertext Stealing (CTS) mode.
It is a one-shot API that handles all operations in a single call.
\return 0 on successful decryption.
\return BAD_FUNC_ARG if input arguments are invalid.
\return other negative error codes for decryption failures.
\param key pointer to the AES key used for decryption.
\param keySz size of the AES key in bytes (16, 24, or 32 bytes).
\param[out] out buffer to hold the decrypted plaintext. Must be at least
the same size as the input ciphertext.
\param in pointer to the ciphertext input data to decrypt.
\param inSz size of the ciphertext input data in bytes.
\param iv pointer to the initialization vector (IV) used for decryption.
Must be 16 bytes.
_Example_
\code
byte key[16] = { 0 };
byte iv[16] = { 0 };
byte ciphertext[] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
byte plaintext[sizeof(ciphertext)];
int ret = wc_AesCtsDecrypt(key, sizeof(key), plaintext, ciphertext,
sizeof(ciphertext), iv);
if (ret != 0) {
// handle decryption error
}
\endcode
\sa wc_AesCtsEncrypt
*/
int wc_AesCtsDecrypt(const byte* key, word32 keySz, byte* out,
const byte* in, word32 inSz,
const byte* iv);
/*!
\ingroup AES
\brief This function performs an update step of the AES CTS encryption.
It processes a chunk of plaintext and stores intermediate data.
\return 0 on successful processing.
\return BAD_FUNC_ARG if input arguments are invalid.
\param aes pointer to the Aes structure holding the context of the operation.
\param[out] out buffer to hold the encrypted ciphertext. Must be large enough
to store the output from this update step.
\param[out] outSz size in bytes of the output data written to the \c out buffer.
On input, it should contain the maximum number of bytes that can
be written to the \c out buffer.
\param in pointer to the plaintext input data to encrypt.
\param inSz size of the plaintext input data in bytes.
_Example_
\code
Aes aes;
wc_AesInit(&aes, NULL, INVALID_DEVID);
byte key[16] = { 0 };
byte iv[16] = { 0 };
byte plaintext[] = { ... };
byte ciphertext[sizeof(plaintext)];
word32 outSz = sizeof(ciphertext);
wc_AesSetKey(&aes, key, sizeof(key), iv, AES_ENCRYPTION);
int ret = wc_AesCtsEncryptUpdate(&aes, ciphertext, &outSz, plaintext, sizeof(plaintext));
if (ret != 0) {
// handle error
}
wc_AesFree(&aes);
\endcode
\sa wc_AesCtsDecryptUpdate
*/
int wc_AesCtsEncryptUpdate(Aes* aes, byte* out, word32* outSz,
const byte* in, word32 inSz);
/*!
\ingroup AES
\brief This function finalizes the AES CTS encryption operation.
It processes any remaining plaintext and completes the encryption.
\return 0 on successful encryption completion.
\return BAD_FUNC_ARG if input arguments are invalid.
\param aes pointer to the Aes structure holding the context of the operation.
\param[out] out buffer to hold the final encrypted ciphertext. Must be large
enough to store any remaining ciphertext from this final step.
\param[out] outSz size in bytes of the output data written to the \c out buffer.
On input, it should contain the maximum number of bytes that can
be written to the \c out buffer.
_Example_
\code
Aes aes;
wc_AesInit(&aes, NULL, INVALID_DEVID);
byte key[16] = { 0 };
byte iv[16] = { 0 };
byte plaintext[] = { ... };
byte ciphertext[sizeof(plaintext)];
word32 outSz = sizeof(ciphertext);
wc_AesSetKey(&aes, key, sizeof(key), iv, AES_ENCRYPTION);
// Perform any required update steps using wc_AesCtsEncryptUpdate
int ret = wc_AesCtsEncryptFinal(&aes, ciphertext, &outSz);
if (ret != 0) {
// handle error
}
wc_AesFree(&aes);
\endcode
\sa wc_AesCtsDecryptFinal
*/
int wc_AesCtsEncryptFinal(Aes* aes, byte* out, word32* outSz);
/*!
\ingroup AES
\brief This function performs an update step of the AES CTS decryption.
It processes a chunk of ciphertext and stores intermediate data.
\return 0 on successful processing.
\return BAD_FUNC_ARG if input arguments are invalid.
\param aes pointer to the Aes structure holding the context of the operation.
\param[out] out buffer to hold the decrypted plaintext. Must be large enough
to store the output from this update step.
\param[out] outSz size in bytes of the output data written to the \c out buffer.
On input, it should contain the maximum number of bytes that can
be written to the \c out buffer.
\param in pointer to the ciphertext input data to decrypt.
\param inSz size of the ciphertext input data in bytes.
_Example_
\code
Aes aes;
wc_AesInit(&aes, NULL, INVALID_DEVID);
byte key[16] = { 0 };
byte iv[16] = { 0 };
byte ciphertext[] = { ... };
byte plaintext[sizeof(ciphertext)];
word32 outSz = sizeof(plaintext);
wc_AesSetKey(&aes, key, sizeof(key), iv, AES_DECRYPTION);
int ret = wc_AesCtsDecryptUpdate(&aes, plaintext, &outSz, ciphertext, sizeof(ciphertext));
if (ret != 0) {
// handle error
}
wc_AesFree(&aes);
\endcode
\sa wc_AesCtsEncryptUpdate
*/
int wc_AesCtsDecryptUpdate(Aes* aes, byte* out, word32* outSz,
const byte* in, word32 inSz);
/*!
\ingroup AES
\brief This function finalizes the AES CTS decryption operation.
It processes any remaining ciphertext and completes the decryption.
\return 0 on successful decryption completion.
\return BAD_FUNC_ARG if input arguments are invalid.
\param aes pointer to the Aes structure holding the context of the operation.
\param[out] out buffer to hold the final decrypted plaintext. Must be large
enough to store any remaining plaintext from this final step.
\param[out] outSz size in bytes of the output data written to the \c out buffer.
On input, it should contain the maximum number of bytes that can
be written to the \c out buffer.
_Example_
\code
Aes aes;
wc_AesInit(&aes, NULL, INVALID_DEVID);
byte key[16] = { 0 };
byte iv[16] = { 0 };
byte ciphertext[] = { ... };
byte plaintext[sizeof(ciphertext)];
word32 outSz = sizeof(plaintext);
wc_AesSetKey(&aes, key, sizeof(key), iv, AES_DECRYPTION);
// Perform any required update steps using wc_AesCtsDecryptUpdate
int ret = wc_AesCtsDecryptFinal(&aes, plaintext, &outSz);
if (ret != 0) {
// handle error
}
wc_AesFree(&aes);
\endcode
\sa wc_AesCtsEncryptFinal
*/
int wc_AesCtsDecryptFinal(Aes* aes, byte* out, word32* outSz);

View File

@@ -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 */
const 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
}
};
const 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
******************************************************************************/

View File

@@ -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), \

View File

@@ -3756,7 +3756,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);
@@ -3837,7 +3838,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
@@ -3868,7 +3870,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
@@ -3920,7 +3923,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
@@ -4001,7 +4005,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
@@ -4058,7 +4063,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);
@@ -4551,7 +4557,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
@@ -4833,7 +4840,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
@@ -10854,7 +10862,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;
@@ -10863,7 +10871,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)
{
@@ -14903,4 +14911,276 @@ 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[1];
#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[1];
#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 */

View File

@@ -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)

View File

@@ -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)) || \