mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-08-13 17:41:22 +02:00
Merge pull request #10940 from SparkiDev/chacha20_poly1305_avx512
ChaCha20/Poly1305 ASM: AVX512 and improvements to other Intel x64
This commit is contained in:
@@ -55,6 +55,7 @@ BSP_SDCARD_SDHC_CHANNEL
|
||||
BSP_SDCARD_SPI_CHANNEL
|
||||
CAAM_OUT_INVALIDATE
|
||||
CERT_REL_PREFIX
|
||||
CHACHA20_POLY1305_SHORT_MAX
|
||||
CIOCASYMFEAT
|
||||
CIOCGSESSINFO
|
||||
CMSIS_OS2_H_
|
||||
@@ -845,6 +846,15 @@ WOLFSSL_CAAM_BLACK_KEY_AESCCM
|
||||
WOLFSSL_CAAM_BLACK_KEY_SM
|
||||
WOLFSSL_CAAM_NO_BLACK_KEY
|
||||
WOLFSSL_CALLBACKS
|
||||
WOLFSSL_CHACHA20_AVX512_ALWAYS
|
||||
WOLFSSL_CHACHA20_AVX512_NEVER
|
||||
WOLFSSL_CHACHA20_POLY1305_FUSED
|
||||
WOLFSSL_CHACHA20_POLY1305_FUSED_ALWAYS
|
||||
WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA_ALWAYS
|
||||
WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA_NEVER
|
||||
WOLFSSL_CHACHA20_POLY1305_FUSED_NEVER
|
||||
WOLFSSL_CHACHA20_POLY1305_SHORT
|
||||
WOLFSSL_CHECK_DESKEY
|
||||
WOLFSSL_CHIBIOS
|
||||
WOLFSSL_CLANG_TIDY
|
||||
@@ -941,6 +951,10 @@ WOLFSSL_NONBLOCK_OCSP
|
||||
WOLFSSL_NOSHA3_384
|
||||
WOLFSSL_NOT_WINDOWS_API
|
||||
WOLFSSL_NO_BIO_ADDR_IN
|
||||
WOLFSSL_NO_CHACHA20_POLY1305_FUSED
|
||||
WOLFSSL_NO_CHACHA20_POLY1305_FUSED_IFMA
|
||||
WOLFSSL_NO_CHACHA20_POLY1305_SHORT
|
||||
WOLFSSL_NO_CHACHA20_POLY1305_SMALL_ASM
|
||||
WOLFSSL_NO_CLIENT_CERT_ERROR
|
||||
WOLFSSL_NO_COPY_CERT
|
||||
WOLFSSL_NO_COPY_KEY
|
||||
@@ -994,6 +1008,12 @@ WOLFSSL_PASSTHRU_ERR
|
||||
WOLFSSL_PB
|
||||
WOLFSSL_PEER_ADDRESS_CHANGES
|
||||
WOLFSSL_PKCS11_RW_TOKENS
|
||||
WOLFSSL_POLY1305_AVX512
|
||||
WOLFSSL_POLY1305_AVX512_ALWAYS
|
||||
WOLFSSL_POLY1305_AVX512_NEVER
|
||||
WOLFSSL_POLY1305_IFMA_ALWAYS
|
||||
WOLFSSL_POLY1305_NO_AVX512
|
||||
WOLFSSL_POLY1305_NO_IFMA
|
||||
WOLFSSL_PPC64_ASM_AES_NO_HARDEN
|
||||
WOLFSSL_PRCONNECT_PRO
|
||||
WOLFSSL_PREFIX
|
||||
|
||||
@@ -123,6 +123,126 @@ int wc_ChaCha20Poly1305_Decrypt(
|
||||
const byte inAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],
|
||||
byte* outPlaintext);
|
||||
|
||||
/*!
|
||||
\ingroup ChaCha20Poly1305
|
||||
|
||||
\brief This function performs the same AEAD encryption as
|
||||
wc_ChaCha20Poly1305_Encrypt, but takes a caller-owned ChaCha20 context whose
|
||||
key has already been set (with wc_Chacha_SetKey) plus a Poly1305 context,
|
||||
instead of a raw key. This lets the ChaCha20 key be set once and reused
|
||||
across many records, varying only the per-record nonce - it is intended for
|
||||
protocol record layers such as TLS. A fresh per-record Poly1305 key is
|
||||
derived from the ChaCha20 keystream, so this call re-keys the supplied
|
||||
Poly1305 context; the caller does not key it. The 16 byte authentication tag
|
||||
over the AAD and ciphertext is written to tag.
|
||||
|
||||
\return 0 Returned upon successfully encrypting the message
|
||||
\return BAD_FUNC_ARG Returned if a required pointer argument is NULL (with
|
||||
its matching length nonzero) or otherwise invalid
|
||||
|
||||
\param chacha pointer to a ChaCha20 context already keyed with
|
||||
wc_Chacha_SetKey
|
||||
\param poly pointer to a Poly1305 context used for the per-record MAC; it is
|
||||
re-keyed internally on each call
|
||||
\param out pointer to the buffer in which to store the ciphertext (sz bytes)
|
||||
\param in pointer to the buffer containing the plaintext to encrypt
|
||||
\param sz the length in bytes of the plaintext to encrypt
|
||||
\param nonce pointer to the 12 byte per-record nonce
|
||||
\param tag pointer to a 16 byte buffer in which to store the authentication
|
||||
tag
|
||||
\param aad pointer to the buffer containing arbitrary length additional
|
||||
authenticated data (AAD)
|
||||
\param aadSz length of the input AAD
|
||||
|
||||
_Example_
|
||||
\code
|
||||
ChaCha chacha;
|
||||
Poly1305 poly;
|
||||
byte key[] = { // initialize 32 byte key };
|
||||
byte nonce[] = { // initialize 12 byte per-record nonce };
|
||||
byte aad[] = { // initialize AAD };
|
||||
byte plain[] = { // initialize message to encrypt };
|
||||
byte cipher[sizeof(plain)];
|
||||
byte authTag[16];
|
||||
|
||||
wc_Chacha_SetKey(&chacha, key, sizeof(key)); // once, then reuse
|
||||
int ret = wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, cipher, plain,
|
||||
sizeof(plain), nonce, authTag, aad, sizeof(aad));
|
||||
if (ret != 0) {
|
||||
// error running encrypt
|
||||
}
|
||||
\endcode
|
||||
|
||||
\sa wc_ChaCha20Poly1305_Decrypt_ex
|
||||
\sa wc_ChaCha20Poly1305_Encrypt
|
||||
\sa wc_Chacha_SetKey
|
||||
*/
|
||||
int wc_ChaCha20Poly1305_Encrypt_ex(ChaCha* chacha, Poly1305* poly,
|
||||
byte* out, const byte* in, word32 sz, const byte* nonce, byte* tag,
|
||||
const byte* aad, word32 aadSz);
|
||||
|
||||
/*!
|
||||
\ingroup ChaCha20Poly1305
|
||||
|
||||
\brief This function is the decryption counterpart of
|
||||
wc_ChaCha20Poly1305_Encrypt_ex. It takes a caller-owned ChaCha20 context
|
||||
whose key has already been set (with wc_Chacha_SetKey) plus a Poly1305
|
||||
context, decrypts in to out, and verifies the Poly1305 tag over the AAD and
|
||||
ciphertext. On tag mismatch it returns MAC_CMP_FAILED_E and zeroizes the
|
||||
output buffer, so no unauthenticated plaintext is released. The ChaCha20 key
|
||||
is reused across records, varying only the per-record nonce; the Poly1305
|
||||
context is re-keyed internally on each call. out may alias in (in-place
|
||||
decryption is supported).
|
||||
|
||||
\return 0 Returned upon successfully decrypting and authenticating the
|
||||
message
|
||||
\return MAC_CMP_FAILED_E Returned if the computed authentication tag does not
|
||||
match the supplied tag; out is zeroized in this case
|
||||
\return BAD_FUNC_ARG Returned if a required pointer argument is NULL (with
|
||||
its matching length nonzero) or otherwise invalid
|
||||
|
||||
\param chacha pointer to a ChaCha20 context already keyed with
|
||||
wc_Chacha_SetKey
|
||||
\param poly pointer to a Poly1305 context used for the per-record MAC; it is
|
||||
re-keyed internally on each call
|
||||
\param out pointer to the buffer in which to store the plaintext (sz bytes)
|
||||
\param in pointer to the buffer containing the ciphertext to decrypt
|
||||
\param sz the length in bytes of the ciphertext to decrypt
|
||||
\param nonce pointer to the 12 byte per-record nonce
|
||||
\param tag pointer to the 16 byte authentication tag to verify
|
||||
\param aad pointer to the buffer containing arbitrary length additional
|
||||
authenticated data (AAD)
|
||||
\param aadSz length of the input AAD
|
||||
|
||||
_Example_
|
||||
\code
|
||||
ChaCha chacha;
|
||||
Poly1305 poly;
|
||||
byte key[] = { // initialize 32 byte key };
|
||||
byte nonce[] = { // initialize 12 byte per-record nonce };
|
||||
byte aad[] = { // initialize AAD };
|
||||
byte cipher[] = { // received ciphertext };
|
||||
byte authTag[16] = { // received authentication tag };
|
||||
byte plain[sizeof(cipher)];
|
||||
|
||||
wc_Chacha_SetKey(&chacha, key, sizeof(key)); // once, then reuse
|
||||
int ret = wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, plain, cipher,
|
||||
sizeof(cipher), nonce, authTag, aad, sizeof(aad));
|
||||
if (ret == MAC_CMP_FAILED_E) {
|
||||
// authentication failed; plain has been zeroized
|
||||
} else if (ret != 0) {
|
||||
// error with function arguments
|
||||
}
|
||||
\endcode
|
||||
|
||||
\sa wc_ChaCha20Poly1305_Encrypt_ex
|
||||
\sa wc_ChaCha20Poly1305_Decrypt
|
||||
\sa wc_Chacha_SetKey
|
||||
*/
|
||||
int wc_ChaCha20Poly1305_Decrypt_ex(
|
||||
ChaCha* chacha, Poly1305* poly, byte* out, const byte* in, word32 sz,
|
||||
const byte* nonce, const byte* tag, const byte* aad, word32 aadSz);
|
||||
|
||||
/*!
|
||||
\ingroup ChaCha20Poly1305
|
||||
\brief Compares two authentication tags in constant time to prevent
|
||||
|
||||
+142
-137
@@ -21465,55 +21465,61 @@ int ChachaAEADEncrypt(WOLFSSL* ssl, byte* out, const byte* input,
|
||||
wc_MemZero_Add("ChachaAEADEncrypt nonce", nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
|
||||
/* set the nonce for chacha and get poly1305 key */
|
||||
if ((ret = wc_Chacha_SetIV(ssl->encrypt.chacha, nonce, 0)) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* create Poly1305 key using chacha20 keystream */
|
||||
if ((ret = wc_Chacha_Process(ssl->encrypt.chacha, poly,
|
||||
poly, sizeof(poly))) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Add("ChachaAEADEncrypt poly", poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
|
||||
/* set the counter after getting poly1305 key */
|
||||
if ((ret = wc_Chacha_SetIV(ssl->encrypt.chacha, nonce, 1)) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ); /* done with nonce, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
|
||||
/* encrypt the plain text */
|
||||
if ((ret = wc_Chacha_Process(ssl->encrypt.chacha, out,
|
||||
input, msgLen)) != 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get the poly1305 tag using either old padding scheme or more recent */
|
||||
/* Derive the Poly1305 key, encrypt and authenticate. The legacy oldPoly
|
||||
* draft keeps the manual derivation and old tag layout. RFC 7905 uses the
|
||||
* persistent-key stitched helper - it derives the per-record poly key,
|
||||
* encrypts and MACs in one pass (the IFMA stitch for large records, else
|
||||
* two-pass), matching the split ssl->encrypt.chacha / ssl->auth.poly1305
|
||||
* contexts kept keyed across the connection. */
|
||||
if (ssl->options.oldPoly != 0) {
|
||||
/* set the nonce for chacha and get poly1305 key */
|
||||
if ((ret = wc_Chacha_SetIV(ssl->encrypt.chacha, nonce, 0)) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* create Poly1305 key using chacha20 keystream */
|
||||
if ((ret = wc_Chacha_Process(ssl->encrypt.chacha, poly,
|
||||
poly, sizeof(poly))) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Add("ChachaAEADEncrypt poly", poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
|
||||
/* set the counter after getting poly1305 key */
|
||||
if ((ret = wc_Chacha_SetIV(ssl->encrypt.chacha, nonce, 1)) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ); /* done with nonce, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
|
||||
/* encrypt the plain text */
|
||||
if ((ret = wc_Chacha_Process(ssl->encrypt.chacha, out,
|
||||
input, msgLen)) != 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get the poly1305 tag using the old padding scheme */
|
||||
if ((ret = Poly1305TagOld(ssl, add, addSz, (const byte* )out,
|
||||
poly, sz, tag)) != 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
@@ -21522,29 +21528,22 @@ int ChachaAEADEncrypt(WOLFSSL* ssl, byte* out, const byte* input,
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
if ((ret = wc_Poly1305SetKey(ssl->auth.poly1305, poly,
|
||||
sizeof(poly))) != 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
ret = wc_ChaCha20Poly1305_Encrypt_ex(ssl->encrypt.chacha,
|
||||
ssl->auth.poly1305, out, input, msgLen, nonce, tag, add,
|
||||
(word32)addSz);
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ); /* done with nonce, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
if ((ret = wc_Poly1305_MAC(ssl->auth.poly1305, add, addSz, out, msgLen,
|
||||
tag, sizeof(tag))) != 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
|
||||
/* append tag to ciphertext */
|
||||
XMEMCPY(out + msgLen, tag, sizeof(tag));
|
||||
@@ -21659,45 +21658,49 @@ int ChachaAEADDecrypt(WOLFSSL* ssl, byte* plain, const byte* input,
|
||||
wc_MemZero_Add("ChachaAEADEncrypt nonce", nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
|
||||
/* set nonce and get poly1305 key */
|
||||
if ((ret = wc_Chacha_SetIV(ssl->decrypt.chacha, nonce, 0)) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* use chacha20 keystream to get poly1305 key for tag */
|
||||
if ((ret = wc_Chacha_Process(ssl->decrypt.chacha, poly,
|
||||
poly, sizeof(poly))) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Add("ChachaAEADEncrypt poly", poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
|
||||
/* set counter after getting poly1305 key */
|
||||
if ((ret = wc_Chacha_SetIV(ssl->decrypt.chacha, nonce, 1)) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ); /* done with nonce, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
|
||||
/* get the tag using Poly1305 */
|
||||
/* Verify the tag and decrypt. oldPoly keeps the manual derivation, old tag
|
||||
* layout and verify-then-decrypt. RFC 7905 uses the persistent-key
|
||||
* stitched helper (verify + decrypt in one pass - the IFMA decrypt stitch
|
||||
* for large records, else two-pass; it zeroes plain on tag mismatch). */
|
||||
if (ssl->options.oldPoly != 0) {
|
||||
/* set nonce and get poly1305 key */
|
||||
if ((ret = wc_Chacha_SetIV(ssl->decrypt.chacha, nonce, 0)) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* use chacha20 keystream to get poly1305 key for tag */
|
||||
if ((ret = wc_Chacha_Process(ssl->decrypt.chacha, poly,
|
||||
poly, sizeof(poly))) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Add("ChachaAEADEncrypt poly", poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
|
||||
/* set counter after getting poly1305 key */
|
||||
if ((ret = wc_Chacha_SetIV(ssl->decrypt.chacha, nonce, 1)) != 0) {
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ);
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ); /* done with nonce, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
|
||||
/* get the tag using the old padding scheme */
|
||||
if ((ret = Poly1305TagOld(ssl, add, addSz, input, poly, sz, tag))
|
||||
!= 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
@@ -21706,43 +21709,45 @@ int ChachaAEADDecrypt(WOLFSSL* ssl, byte* plain, const byte* input,
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
|
||||
/* check tag sent along with packet */
|
||||
if (ConstantCompare(input + msgLen, tag,
|
||||
ssl->specs.aead_mac_size) != 0) {
|
||||
WOLFSSL_MSG("MAC did not match");
|
||||
if (!ssl->options.dtls)
|
||||
SendAlert(ssl, alert_fatal, bad_record_mac);
|
||||
WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
|
||||
return VERIFY_MAC_ERROR;
|
||||
}
|
||||
|
||||
/* if the tag was good decrypt message */
|
||||
if ((ret = wc_Chacha_Process(ssl->decrypt.chacha, plain,
|
||||
input, (word32)msgLen)) != 0)
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
if ((ret = wc_Poly1305SetKey(ssl->auth.poly1305, poly,
|
||||
sizeof(poly))) != 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
if ((ret = wc_Poly1305_MAC(ssl->auth.poly1305, add, addSz, input,
|
||||
(word32)msgLen, tag, sizeof(tag))) != 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
ret = wc_ChaCha20Poly1305_Decrypt_ex(ssl->decrypt.chacha,
|
||||
ssl->auth.poly1305, plain, input, (word32)msgLen, nonce,
|
||||
input + msgLen, add, (word32)addSz);
|
||||
ForceZero(nonce, CHACHA20_NONCE_SZ); /* done with nonce, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
if (ret == WC_NO_ERR_TRACE(MAC_CMP_FAILED_E)) {
|
||||
WOLFSSL_MSG("MAC did not match");
|
||||
if (!ssl->options.dtls)
|
||||
SendAlert(ssl, alert_fatal, bad_record_mac);
|
||||
WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
|
||||
return VERIFY_MAC_ERROR;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
|
||||
#endif
|
||||
|
||||
/* check tag sent along with packet */
|
||||
if (ConstantCompare(input + msgLen, tag, ssl->specs.aead_mac_size) != 0) {
|
||||
WOLFSSL_MSG("MAC did not match");
|
||||
if (!ssl->options.dtls)
|
||||
SendAlert(ssl, alert_fatal, bad_record_mac);
|
||||
WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
|
||||
return VERIFY_MAC_ERROR;
|
||||
}
|
||||
|
||||
/* if the tag was good decrypt message */
|
||||
if ((ret = wc_Chacha_Process(ssl->decrypt.chacha, plain,
|
||||
input, (word32)msgLen)) != 0)
|
||||
return ret;
|
||||
|
||||
#ifdef CHACHA_AEAD_TEST
|
||||
printf("plain after decrypt :\n");
|
||||
|
||||
+12
-88
@@ -2543,49 +2543,11 @@ static int ChaCha20Poly1305_Encrypt(WOLFSSL* ssl, byte* output,
|
||||
const byte* input, word16 sz, byte* nonce,
|
||||
const byte* aad, word16 aadSz, byte* tag)
|
||||
{
|
||||
int ret = 0;
|
||||
byte poly[CHACHA20_256_KEY_SIZE];
|
||||
|
||||
/* Poly1305 key is 256 bits of zero encrypted with ChaCha20. */
|
||||
XMEMSET(poly, 0, sizeof(poly));
|
||||
|
||||
/* Set the nonce for ChaCha and get Poly1305 key. */
|
||||
ret = wc_Chacha_SetIV(ssl->encrypt.chacha, nonce, 0);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/* Create Poly1305 key using ChaCha20 keystream. */
|
||||
ret = wc_Chacha_Process(ssl->encrypt.chacha, poly, poly, sizeof(poly));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Add("ChaCha20Poly1305_Encrypt poly", poly, sizeof(poly));
|
||||
#endif
|
||||
ret = wc_Chacha_SetIV(ssl->encrypt.chacha, nonce, 1);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/* Encrypt the plain text. */
|
||||
ret = wc_Chacha_Process(ssl->encrypt.chacha, output, input, sz);
|
||||
if (ret != 0) {
|
||||
ForceZero(poly, sizeof(poly));
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, sizeof(poly));
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set key for Poly1305. */
|
||||
ret = wc_Poly1305SetKey(ssl->auth.poly1305, poly, sizeof(poly));
|
||||
ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, sizeof(poly));
|
||||
#endif
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/* Add authentication code of encrypted data to end. */
|
||||
ret = wc_Poly1305_MAC(ssl->auth.poly1305, aad, aadSz, output, sz, tag,
|
||||
POLY1305_AUTH_SZ);
|
||||
|
||||
return ret;
|
||||
/* Persistent-key stitched helper: derives the per-record Poly1305 key from
|
||||
* the keyed ChaCha, then encrypts and authenticates in one pass (the IFMA
|
||||
* stitch for large records, else two-pass). TLS 1.3 is always RFC 8439. */
|
||||
return wc_ChaCha20Poly1305_Encrypt_ex(ssl->encrypt.chacha,
|
||||
ssl->auth.poly1305, output, input, sz, nonce, tag, aad, aadSz);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2931,55 +2893,17 @@ static int ChaCha20Poly1305_Decrypt(WOLFSSL* ssl, byte* output,
|
||||
const byte* tagIn)
|
||||
{
|
||||
int ret;
|
||||
byte tag[POLY1305_AUTH_SZ];
|
||||
byte poly[CHACHA20_256_KEY_SIZE]; /* generated key for mac */
|
||||
|
||||
/* Poly1305 key is 256 bits of zero encrypted with ChaCha20. */
|
||||
XMEMSET(poly, 0, sizeof(poly));
|
||||
|
||||
/* Set nonce and get Poly1305 key. */
|
||||
ret = wc_Chacha_SetIV(ssl->decrypt.chacha, nonce, 0);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/* Use ChaCha20 keystream to get Poly1305 key for tag. */
|
||||
ret = wc_Chacha_Process(ssl->decrypt.chacha, poly, poly, sizeof(poly));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Add("ChaCha20Poly1305_Decrypt poly", poly, sizeof(poly));
|
||||
#endif
|
||||
ret = wc_Chacha_SetIV(ssl->decrypt.chacha, nonce, 1);
|
||||
if (ret != 0) {
|
||||
ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, sizeof(poly));
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set key for Poly1305. */
|
||||
ret = wc_Poly1305SetKey(ssl->auth.poly1305, poly, sizeof(poly));
|
||||
ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
|
||||
#ifdef WOLFSSL_CHECK_MEM_ZERO
|
||||
wc_MemZero_Check(poly, sizeof(poly));
|
||||
#endif
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/* Generate authentication tag for encrypted data. */
|
||||
if ((ret = wc_Poly1305_MAC(ssl->auth.poly1305, aad, aadSz, input, sz, tag,
|
||||
sizeof(tag))) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check tag sent along with packet. */
|
||||
if (ConstantCompare(tagIn, tag, POLY1305_AUTH_SZ) != 0) {
|
||||
/* Persistent-key stitched helper: verifies the Poly1305 tag over
|
||||
* aad+ciphertext and decrypts in one pass (the IFMA decrypt stitch for
|
||||
* large records, else two-pass); it zeroes output on tag mismatch. */
|
||||
ret = wc_ChaCha20Poly1305_Decrypt_ex(ssl->decrypt.chacha,
|
||||
ssl->auth.poly1305, output, input, sz, nonce, tagIn, aad, aadSz);
|
||||
if (ret == WC_NO_ERR_TRACE(MAC_CMP_FAILED_E)) {
|
||||
WOLFSSL_MSG("MAC did not match");
|
||||
return VERIFY_MAC_ERROR;
|
||||
ret = VERIFY_MAC_ERROR;
|
||||
}
|
||||
|
||||
/* If the tag was good decrypt message. */
|
||||
ret = wc_Chacha_Process(ssl->decrypt.chacha, output, input, sz);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -359,6 +359,8 @@ int test_wc_ChaCha20Poly1305_MonteCarlo(void)
|
||||
byte key[CHACHA20_POLY1305_AEAD_KEYSIZE];
|
||||
byte nonce[CHACHA20_POLY1305_AEAD_IV_SIZE];
|
||||
byte tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
byte aad[32];
|
||||
word32 aadLen = 0;
|
||||
word32 plainLen = 0;
|
||||
int i;
|
||||
WC_DECLARE_VAR(plain, byte, MC_CHACHA20P1305_MAX_SZ, NULL);
|
||||
@@ -386,9 +388,18 @@ int test_wc_ChaCha20Poly1305_MonteCarlo(void)
|
||||
plainLen = (plainLen % MC_CHACHA20P1305_MAX_SZ) + 1;
|
||||
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, plain, plainLen), 0);
|
||||
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, nonce, NULL, 0,
|
||||
/* Random AAD (0..sizeof(aad)) so the AAD fold is exercised alongside
|
||||
* every randomly-chosen message size, including the sz <= 64 small
|
||||
* kernel band. */
|
||||
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, (byte*)&aadLen,
|
||||
sizeof(aadLen)), 0);
|
||||
aadLen = aadLen % (word32)(sizeof(aad) + 1);
|
||||
if (aadLen > 0)
|
||||
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, aad, aadLen), 0);
|
||||
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, nonce, aad, aadLen,
|
||||
plain, plainLen, cipher, tag), 0);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, nonce, NULL, 0,
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, nonce, aad, aadLen,
|
||||
cipher, plainLen, tag, decrypted), 0);
|
||||
ExpectBufEQ(decrypted, plain, plainLen);
|
||||
}
|
||||
@@ -1308,3 +1319,553 @@ int test_wc_XChaCha20Poly1305_LargeBuffer(void)
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_XChaCha20Poly1305_LargeBuffer */
|
||||
|
||||
/*
|
||||
* Large-message coverage for the AVX-512 + IFMA single-pass stitch, which the
|
||||
* one-shot Encrypt, Encrypt_ex, Decrypt_ex and streaming paths all dispatch to
|
||||
* at sz >= CHACHA20_POLY1305_STITCH_MIN (default 4096) on capable CPUs. No
|
||||
* other test reaches 4096 bytes, so on AVX-512/IFMA hardware this is the only
|
||||
* exercise of the stitch kernel, its scalar sub-1024 tail, and the AAD fold.
|
||||
* On CPUs without AVX-512/IFMA every call transparently uses the two-pass path,
|
||||
* so the test still validates (round-trips) but does not reach the stitch.
|
||||
*
|
||||
* Sizes span the sub-bands the stitch splits on: exact 1024-multiples (no
|
||||
* tail), non-multiples (stitch + scalar tail), and both AAD present / absent
|
||||
* (the poly1305_fold_avx512ifma AAD fold). Correctness is cross-checked three
|
||||
* independent ways: the one-shot two-pass Decrypt round-trips the one-shot
|
||||
* (stitch) ciphertext; Encrypt_ex must reproduce the one-shot ciphertext+tag
|
||||
* byte-for-byte; and Decrypt_ex (the decrypt stitch, decrypt-then-verify)
|
||||
* recovers the plaintext and, on a corrupted tag, returns MAC_CMP_FAILED_E with
|
||||
* a zeroized output (no plaintext released though the stitch decrypts first).
|
||||
*/
|
||||
int test_wc_ChaCha20Poly1305_LargeMessage(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
||||
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
||||
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
||||
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
||||
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
||||
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
||||
};
|
||||
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
||||
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
||||
};
|
||||
static const byte aad[12] = {
|
||||
0x50,0x51,0x52,0x53, 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7
|
||||
};
|
||||
/* >= STITCH_MIN: exact 1024-multiples (4096/8192/16384) and tails
|
||||
* (4097 -> 1-byte tail, 5000 -> 904-byte tail). */
|
||||
static const word32 sizes[] = { 4096, 4097, 5000, 8192, 16384 };
|
||||
static const word32 aadLens[] = { 0, 12 };
|
||||
#define BIG_MSG_LEN 16384
|
||||
byte* pt = NULL;
|
||||
byte* ct = NULL;
|
||||
byte* out = NULL;
|
||||
byte tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
byte tag2[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
ChaCha chacha;
|
||||
Poly1305 poly;
|
||||
word32 a;
|
||||
word32 s;
|
||||
|
||||
pt = (byte*)XMALLOC(BIG_MSG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ct = (byte*)XMALLOC(BIG_MSG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
out = (byte*)XMALLOC(BIG_MSG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ExpectNotNull(pt);
|
||||
ExpectNotNull(ct);
|
||||
ExpectNotNull(out);
|
||||
|
||||
for (a = 0; a < (word32)(sizeof(aadLens) / sizeof(aadLens[0])); a++) {
|
||||
word32 aadLen = aadLens[a];
|
||||
for (s = 0; s < (word32)(sizeof(sizes) / sizeof(sizes[0])); s++) {
|
||||
word32 sz = sizes[s];
|
||||
word32 i;
|
||||
|
||||
if (pt == NULL || ct == NULL || out == NULL)
|
||||
break;
|
||||
for (i = 0; i < sz; i++)
|
||||
pt[i] = (byte)(i * 3 + 1);
|
||||
|
||||
/* One-shot Encrypt: IFMA stitch when sz >= STITCH_MIN. */
|
||||
XMEMSET(ct, 0, sz);
|
||||
XMEMSET(tag, 0, sizeof(tag));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, aadLen,
|
||||
pt, sz, ct, tag), 0);
|
||||
|
||||
/* Round-trip via the independent two-pass one-shot Decrypt: proves
|
||||
* the encrypt stitch produced a correct ciphertext AND tag. */
|
||||
XMEMSET(out, 0, sz);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, aadLen,
|
||||
ct, sz, tag, out), 0);
|
||||
ExpectBufEQ(out, pt, sz);
|
||||
|
||||
/* Encrypt_ex must reproduce the one-shot ciphertext + tag. */
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
XMEMSET(out, 0, sz);
|
||||
XMEMSET(tag2, 0, sizeof(tag2));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, out, pt,
|
||||
sz, iv, tag2, aad, aadLen), 0);
|
||||
ExpectBufEQ(out, ct, sz);
|
||||
ExpectBufEQ(tag2, tag, sizeof(tag2));
|
||||
|
||||
/* Decrypt_ex: the decrypt stitch (decrypt-then-verify) must recover
|
||||
* the plaintext with a valid tag. */
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
XMEMSET(out, 0, sz);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, out, ct,
|
||||
sz, iv, tag, aad, aadLen), 0);
|
||||
ExpectBufEQ(out, pt, sz);
|
||||
|
||||
/* Bad tag: the stitch decrypts before checking the tag, so verify
|
||||
* Decrypt_ex both reports the failure and zeroizes the output. */
|
||||
tag[0] ^= 0xff;
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
XMEMSET(out, 0xa5, sz);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, out, ct,
|
||||
sz, iv, tag, aad, aadLen),
|
||||
WC_NO_ERR_TRACE(MAC_CMP_FAILED_E));
|
||||
ExpectIntEQ(out[0], 0);
|
||||
ExpectIntEQ(out[sz / 2], 0);
|
||||
ExpectIntEQ(out[sz - 1], 0);
|
||||
tag[0] ^= 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
XFREE(pt, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(ct, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(out, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
#undef BIG_MSG_LEN
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ChaCha20Poly1305_LargeMessage */
|
||||
|
||||
/*
|
||||
* Small-message coverage WITH a non-empty AAD. The fused single-call asm
|
||||
* kernels (chacha20_poly1305_small_enc / _dec) handle sz <= 64 on AVX2 CPUs and
|
||||
* fold the AAD into Poly1305 inside assembly. No existing test drives that
|
||||
* path with AAD: the KAT vectors are > 64 bytes, and MonteCarlo used NULL/0
|
||||
* AAD - so the in-kernel AAD fold (the exact code the Windows stack-offset
|
||||
* defects corrupt) was never executed. This is deterministic across the whole
|
||||
* small band including the sz == 64 boundary.
|
||||
*
|
||||
* The streaming API does NOT use the short/small path, so it is an independent
|
||||
* reference: small_enc must reproduce its ciphertext + tag byte-for-byte. The
|
||||
* round-trip then recovers the plaintext via small_dec, and a corrupted tag
|
||||
* must return MAC_CMP_FAILED_E with a fully zeroized output (small_dec decrypts
|
||||
* before it verifies). On CPUs without AVX2 the same calls use the C fallback,
|
||||
* so the test still validates but does not reach the asm kernel.
|
||||
*/
|
||||
int test_wc_ChaCha20Poly1305_SmallWithAad(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
||||
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
||||
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
||||
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
||||
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
||||
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
||||
};
|
||||
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
||||
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
||||
};
|
||||
static const byte aad[20] = {
|
||||
0x50,0x51,0x52,0x53, 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
|
||||
0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7
|
||||
};
|
||||
/* small band: the AVX2 kernel is used for sz <= 64 - cover 1, mid, the
|
||||
* boundary at 64, and one just past it as a control. */
|
||||
static const word32 sizes[] = { 1, 16, 32, 63, 64, 65 };
|
||||
static const word32 aadLens[] = { 0, 1, 12, 20 };
|
||||
byte pt[65];
|
||||
byte ct[65];
|
||||
byte ref[65];
|
||||
byte back[65];
|
||||
byte tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
byte tagRef[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
ChaChaPoly_Aead aead;
|
||||
word32 a;
|
||||
word32 s;
|
||||
word32 i;
|
||||
|
||||
XMEMSET(&aead, 0, sizeof(aead));
|
||||
|
||||
for (a = 0; a < (word32)(sizeof(aadLens) / sizeof(aadLens[0])); a++) {
|
||||
word32 aadLen = aadLens[a];
|
||||
for (s = 0; s < (word32)(sizeof(sizes) / sizeof(sizes[0])); s++) {
|
||||
word32 sz = sizes[s];
|
||||
|
||||
for (i = 0; i < sz; i++)
|
||||
pt[i] = (byte)(i * 7 + 2);
|
||||
|
||||
/* One-shot Encrypt: sz <= 64 dispatches to small_enc, which folds
|
||||
* the AAD in asm. */
|
||||
XMEMSET(ct, 0, sizeof(ct));
|
||||
XMEMSET(tag, 0, sizeof(tag));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, aadLen,
|
||||
pt, sz, ct, tag), 0);
|
||||
|
||||
/* Independent reference via the streaming API (never the small
|
||||
* path): validates small_enc + AAD fold against the two-pass. */
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
||||
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
||||
if (aadLen > 0)
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, aadLen),
|
||||
0);
|
||||
XMEMSET(ref, 0, sizeof(ref));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, pt, ref, sz), 0);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, tagRef), 0);
|
||||
ExpectBufEQ(ct, ref, sz);
|
||||
ExpectBufEQ(tag, tagRef, sizeof(tag));
|
||||
|
||||
/* Round-trip: small_dec recovers the plaintext from the reference
|
||||
* ciphertext. */
|
||||
XMEMSET(back, 0, sizeof(back));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, aadLen,
|
||||
ref, sz, tagRef, back), 0);
|
||||
ExpectBufEQ(back, pt, sz);
|
||||
|
||||
/* Bad tag: small_dec decrypts before verifying, so Decrypt must
|
||||
* both report the failure and zeroize the whole output. */
|
||||
tag[0] ^= 0xff;
|
||||
XMEMSET(back, 0xa5, sizeof(back));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, aadLen,
|
||||
ct, sz, tag, back), WC_NO_ERR_TRACE(MAC_CMP_FAILED_E));
|
||||
for (i = 0; i < sz; i++)
|
||||
ExpectIntEQ(back[i], 0);
|
||||
tag[0] ^= 0xff;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ChaCha20Poly1305_SmallWithAad */
|
||||
|
||||
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
||||
/* Streaming AEAD over one message, splitting the data into a first chunk of
|
||||
* 'first' bytes then 'rest'-byte chunks (both clamped to what remains). enc
|
||||
* selects encrypt vs decrypt; the computed/authentication tag is returned in
|
||||
* 'tag' (for decrypt the caller compares it against the received tag). */
|
||||
static int cp_stream(int enc, const byte* key, const byte* iv, const byte* aad,
|
||||
word32 aadLen, const byte* in, word32 sz, word32 first, word32 rest,
|
||||
byte* out, byte* tag)
|
||||
{
|
||||
ChaChaPoly_Aead aead;
|
||||
word32 off;
|
||||
word32 n;
|
||||
int ret;
|
||||
|
||||
XMEMSET(&aead, 0, sizeof(aead));
|
||||
ret = wc_ChaCha20Poly1305_Init(&aead, key, iv, enc ?
|
||||
CHACHA20_POLY1305_AEAD_ENCRYPT : CHACHA20_POLY1305_AEAD_DECRYPT);
|
||||
if (ret == 0 && aadLen > 0)
|
||||
ret = wc_ChaCha20Poly1305_UpdateAad(&aead, aad, aadLen);
|
||||
off = 0;
|
||||
while (ret == 0 && off < sz) {
|
||||
n = (off == 0) ? first : rest;
|
||||
if (n > sz - off)
|
||||
n = sz - off;
|
||||
ret = wc_ChaCha20Poly1305_UpdateData(&aead, in + off, out + off, n);
|
||||
off += n;
|
||||
}
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_Final(&aead, tag);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Large-message coverage of the streaming UpdateData IFMA stitch. UpdateData
|
||||
* grows two AVX-512/IFMA paths that both need dataLen >= STITCH_MIN (4096): the
|
||||
* first-chunk stitch entry - which, when AAD was buffered by the vector path
|
||||
* (< 128 bytes, leftover > 0), COPIES it to a 128-byte stack buffer and
|
||||
* re-hashes it through the scalar path - and the per-chunk stitch gated on
|
||||
* (dataLen & 63) == 0. Every existing streaming test uses <= 64-byte chunks,
|
||||
* so none of this ran. The buffered-AAD re-hash is the highest-value case: a
|
||||
* leftover/pad miscount there yields a wrong tag.
|
||||
*
|
||||
* The streaming API is chunk-invariant by contract, so 256-byte-chunk streaming
|
||||
* (which never reaches STITCH_MIN, hence pure two-pass) is the reference every
|
||||
* stitched chunking must match. Splits exercise: a single large call (entry +
|
||||
* AAD re-hash + full bulk); a 4096 first chunk (2nd chunk 64-aligned -> the
|
||||
* per-chunk stitch fires); and a 4128 first chunk (2nd chunk NOT 64-aligned ->
|
||||
* the guard's false branch, two-pass). AAD lengths cover none, buffered
|
||||
* (12/120 -> re-hash) and >= 128 (200 -> vector-processed, stitch entry
|
||||
* blocked). A two-pass one-shot Decrypt independently round-trips each result.
|
||||
*/
|
||||
int test_wc_ChaCha20Poly1305_StreamLarge(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
||||
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
||||
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
||||
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
||||
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
||||
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
||||
};
|
||||
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
||||
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
||||
};
|
||||
byte aad[200];
|
||||
static const word32 aadLens[] = { 0, 12, 120, 200 };
|
||||
/* { first-chunk, rest-chunk } data splits. */
|
||||
static const word32 splits[][2] = {
|
||||
{ 12288, 12288 }, /* one UpdateData call */
|
||||
{ 4096, 12288 }, /* 64-aligned first chunk -> 2nd chunk stitches */
|
||||
{ 4128, 12288 } /* unaligned first chunk -> 2nd chunk two-pass */
|
||||
};
|
||||
#define SL_LEN 12288
|
||||
byte* pt = NULL;
|
||||
byte* ct = NULL;
|
||||
byte* ref = NULL;
|
||||
byte* back = NULL;
|
||||
byte tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
byte tagRef[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
byte calc[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
word32 a;
|
||||
word32 sp;
|
||||
word32 i;
|
||||
|
||||
for (i = 0; i < sizeof(aad); i++)
|
||||
aad[i] = (byte)(i + 0x30);
|
||||
|
||||
pt = (byte*)XMALLOC(SL_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ct = (byte*)XMALLOC(SL_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ref = (byte*)XMALLOC(SL_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
back = (byte*)XMALLOC(SL_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ExpectNotNull(pt);
|
||||
ExpectNotNull(ct);
|
||||
ExpectNotNull(ref);
|
||||
ExpectNotNull(back);
|
||||
|
||||
if (pt != NULL && ct != NULL && ref != NULL && back != NULL) {
|
||||
for (i = 0; i < SL_LEN; i++)
|
||||
pt[i] = (byte)(i * 5 + 3);
|
||||
|
||||
for (a = 0; a < (word32)(sizeof(aadLens) / sizeof(aadLens[0]))
|
||||
&& EXPECT_SUCCESS(); a++) {
|
||||
word32 aadLen = aadLens[a];
|
||||
|
||||
/* Reference: 256-byte chunks stay below STITCH_MIN -> two-pass. */
|
||||
XMEMSET(ref, 0, SL_LEN);
|
||||
ExpectIntEQ(cp_stream(1, key, iv, aad, aadLen, pt, SL_LEN, 256, 256,
|
||||
ref, tagRef), 0);
|
||||
|
||||
for (sp = 0; sp < (word32)(sizeof(splits) / sizeof(splits[0]))
|
||||
&& EXPECT_SUCCESS(); sp++) {
|
||||
/* Stitched streaming encrypt must match the two-pass ref. */
|
||||
XMEMSET(ct, 0, SL_LEN);
|
||||
ExpectIntEQ(cp_stream(1, key, iv, aad, aadLen, pt, SL_LEN,
|
||||
splits[sp][0], splits[sp][1], ct, tag), 0);
|
||||
ExpectBufEQ(ct, ref, SL_LEN);
|
||||
ExpectBufEQ(tag, tagRef, sizeof(tag));
|
||||
|
||||
/* Streaming decrypt (decrypt stitch) recovers the plaintext and
|
||||
* computes the matching tag. */
|
||||
XMEMSET(back, 0, SL_LEN);
|
||||
ExpectIntEQ(cp_stream(0, key, iv, aad, aadLen, ct, SL_LEN,
|
||||
splits[sp][0], splits[sp][1], back, calc), 0);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_CheckTag(tag, calc), 0);
|
||||
ExpectBufEQ(back, pt, SL_LEN);
|
||||
}
|
||||
|
||||
/* Independent: the two-pass one-shot Decrypt round-trips it. */
|
||||
XMEMSET(back, 0, SL_LEN);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, aadLen,
|
||||
ref, SL_LEN, tagRef, back), 0);
|
||||
ExpectBufEQ(back, pt, SL_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
XFREE(pt, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(ct, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(ref, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(back, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
#undef SL_LEN
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ChaCha20Poly1305_StreamLarge */
|
||||
|
||||
/*
|
||||
* Direct, full-coverage test of the new pre-keyed one-shot APIs
|
||||
* wc_ChaCha20Poly1305_Encrypt_ex / _Decrypt_ex (the TLS-record analogue of
|
||||
* wc_AesGcmEncrypt/Decrypt on a keyed context). Exercises every dispatch band
|
||||
* the _ex path selects on - the sz <= 64 small asm kernel, the 64 < sz <= 192
|
||||
* short C path, the 192 < sz < 4096 two-pass, and the sz >= 4096 IFMA stitch
|
||||
* (+ sub-1024 tail) - each with AAD absent, buffered, and larger, and both
|
||||
* separate and in-place (out == in) buffers. Correctness is anchored to the
|
||||
* trusted one-shot wc_ChaCha20Poly1305_Encrypt, which _ex must reproduce
|
||||
* byte-for-byte regardless of which internal path either takes. Also covers
|
||||
* Decrypt_ex tag verification + output zeroization on a bad tag, the keyed-
|
||||
* context reuse pattern (SetKey once, vary the nonce per record), and the full
|
||||
* argument-validation matrix.
|
||||
*/
|
||||
int test_wc_ChaCha20Poly1305_Ex(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
||||
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
||||
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
||||
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
||||
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
||||
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
||||
};
|
||||
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
||||
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
||||
};
|
||||
byte aad[64];
|
||||
/* one size in each _ex dispatch band, plus the band boundaries. */
|
||||
static const word32 sizes[] =
|
||||
{ 0, 1, 64, 100, 192, 193, 1024, 4096, 5000 };
|
||||
static const word32 aadLens[] = { 0, 12, 64 };
|
||||
#define EX_MAX 5000
|
||||
ChaCha chacha;
|
||||
Poly1305 poly;
|
||||
byte* pt = NULL;
|
||||
byte* ct = NULL;
|
||||
byte* ref = NULL;
|
||||
byte* out = NULL;
|
||||
byte* tmp = NULL;
|
||||
byte tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
byte tagRef[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
word32 a;
|
||||
word32 s;
|
||||
word32 i;
|
||||
|
||||
for (i = 0; i < sizeof(aad); i++)
|
||||
aad[i] = (byte)(i + 0xa0);
|
||||
|
||||
pt = (byte*)XMALLOC(EX_MAX, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ct = (byte*)XMALLOC(EX_MAX, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ref = (byte*)XMALLOC(EX_MAX, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
out = (byte*)XMALLOC(EX_MAX, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
tmp = (byte*)XMALLOC(EX_MAX, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ExpectNotNull(pt);
|
||||
ExpectNotNull(ct);
|
||||
ExpectNotNull(ref);
|
||||
ExpectNotNull(out);
|
||||
ExpectNotNull(tmp);
|
||||
|
||||
if (pt != NULL && ct != NULL && ref != NULL && out != NULL &&
|
||||
tmp != NULL) {
|
||||
for (i = 0; i < EX_MAX; i++)
|
||||
pt[i] = (byte)(i * 11 + 5);
|
||||
|
||||
for (a = 0; a < (word32)(sizeof(aadLens) / sizeof(aadLens[0]))
|
||||
&& EXPECT_SUCCESS(); a++) {
|
||||
word32 aadLen = aadLens[a];
|
||||
|
||||
for (s = 0; s < (word32)(sizeof(sizes) / sizeof(sizes[0]))
|
||||
&& EXPECT_SUCCESS(); s++) {
|
||||
word32 sz = sizes[s];
|
||||
|
||||
/* Encrypt_ex on a pre-keyed context. */
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
XMEMSET(ct, 0, EX_MAX);
|
||||
XMEMSET(tag, 0, sizeof(tag));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, ct,
|
||||
pt, sz, iv, tag, aad, aadLen), 0);
|
||||
|
||||
/* Must match the trusted one-shot Encrypt byte-for-byte. */
|
||||
XMEMSET(ref, 0, EX_MAX);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, aadLen,
|
||||
pt, sz, ref, tagRef), 0);
|
||||
ExpectBufEQ(ct, ref, sz);
|
||||
ExpectBufEQ(tag, tagRef, sizeof(tag));
|
||||
|
||||
/* Encrypt_ex in place (out == in). */
|
||||
XMEMCPY(tmp, pt, sz);
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, tmp,
|
||||
tmp, sz, iv, tag, aad, aadLen), 0);
|
||||
ExpectBufEQ(tmp, ct, sz);
|
||||
|
||||
/* Decrypt_ex round-trip, separate buffers. */
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
XMEMSET(out, 0, EX_MAX);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, out,
|
||||
ct, sz, iv, tag, aad, aadLen), 0);
|
||||
ExpectBufEQ(out, pt, sz);
|
||||
|
||||
/* Decrypt_ex in place (out == in). */
|
||||
XMEMCPY(tmp, ct, sz);
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, tmp,
|
||||
tmp, sz, iv, tag, aad, aadLen), 0);
|
||||
ExpectBufEQ(tmp, pt, sz);
|
||||
|
||||
/* Bad tag: MAC_CMP_FAILED_E and the whole output zeroized. */
|
||||
tag[0] ^= 0xff;
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
XMEMSET(out, 0xa5, EX_MAX);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, out,
|
||||
ct, sz, iv, tag, aad, aadLen),
|
||||
WC_NO_ERR_TRACE(MAC_CMP_FAILED_E));
|
||||
for (i = 0; i < sz; i++)
|
||||
ExpectIntEQ(out[i], 0);
|
||||
tag[0] ^= 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keyed-context reuse: SetKey once, encrypt several records that differ
|
||||
* only by nonce (the intended TLS usage), each decrypting back. */
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
for (i = 0; i < 4 && EXPECT_SUCCESS(); i++) {
|
||||
byte nonce[CHACHA20_POLY1305_AEAD_IV_SIZE];
|
||||
ChaCha chachaDec;
|
||||
word32 sz = 200 + i * 37;
|
||||
|
||||
XMEMCPY(nonce, iv, sizeof(nonce));
|
||||
nonce[0] = (byte)i; /* vary the nonce per record */
|
||||
XMEMSET(ct, 0, EX_MAX);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, ct, pt,
|
||||
sz, nonce, tag, aad, 12), 0);
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chachaDec, key, sizeof(key)), 0);
|
||||
XMEMSET(out, 0, EX_MAX);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chachaDec, &poly, out,
|
||||
ct, sz, nonce, tag, aad, 12), 0);
|
||||
ExpectBufEQ(out, pt, sz);
|
||||
}
|
||||
|
||||
/* Argument validation - Encrypt_ex. */
|
||||
ExpectIntEQ(wc_Chacha_SetKey(&chacha, key, sizeof(key)), 0);
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(NULL, &poly, ct, pt, 64,
|
||||
iv, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, NULL, ct, pt, 64,
|
||||
iv, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, ct, pt, 64,
|
||||
NULL, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, ct, pt, 64,
|
||||
iv, NULL, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, ct, NULL, 64,
|
||||
iv, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, NULL, pt, 64,
|
||||
iv, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, ct, pt, 64,
|
||||
iv, tag, NULL, 12), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
|
||||
/* Argument validation - Decrypt_ex. */
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(NULL, &poly, out, ct, 64,
|
||||
iv, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, NULL, out, ct, 64,
|
||||
iv, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, out, ct, 64,
|
||||
NULL, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, out, ct, 64,
|
||||
iv, NULL, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, out, NULL,
|
||||
64, iv, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, NULL, ct, 64,
|
||||
iv, tag, aad, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, out, ct, 64,
|
||||
iv, tag, NULL, 12), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
}
|
||||
|
||||
XFREE(pt, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(ct, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(ref, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(out, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
#undef EX_MAX
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ChaCha20Poly1305_Ex */
|
||||
|
||||
@@ -36,6 +36,10 @@ int test_wc_ChaCha20Poly1305_InPlace(void);
|
||||
int test_wc_ChaCha20Poly1305_UnalignedBuffers(void);
|
||||
int test_wc_ChaCha20Poly1305_CrossCipher(void);
|
||||
int test_wc_ChaCha20Poly1305_DecisionCoverage(void);
|
||||
int test_wc_ChaCha20Poly1305_LargeMessage(void);
|
||||
int test_wc_ChaCha20Poly1305_SmallWithAad(void);
|
||||
int test_wc_ChaCha20Poly1305_StreamLarge(void);
|
||||
int test_wc_ChaCha20Poly1305_Ex(void);
|
||||
int test_wc_XChaCha20Poly1305_DecisionCoverage(void);
|
||||
int test_wc_XChaCha20Poly1305_LargeBuffer(void);
|
||||
|
||||
@@ -52,6 +56,10 @@ int test_wc_XChaCha20Poly1305_LargeBuffer(void);
|
||||
TEST_DECL_GROUP("chacha20-poly1305", test_wc_ChaCha20Poly1305_UnalignedBuffers), \
|
||||
TEST_DECL_GROUP("chacha20-poly1305", test_wc_ChaCha20Poly1305_CrossCipher), \
|
||||
TEST_DECL_GROUP("chacha20-poly1305", test_wc_ChaCha20Poly1305_DecisionCoverage), \
|
||||
TEST_DECL_GROUP("chacha20-poly1305", test_wc_ChaCha20Poly1305_LargeMessage), \
|
||||
TEST_DECL_GROUP("chacha20-poly1305", test_wc_ChaCha20Poly1305_SmallWithAad), \
|
||||
TEST_DECL_GROUP("chacha20-poly1305", test_wc_ChaCha20Poly1305_StreamLarge), \
|
||||
TEST_DECL_GROUP("chacha20-poly1305", test_wc_ChaCha20Poly1305_Ex), \
|
||||
TEST_DECL_GROUP("xchacha20-poly1305", test_wc_XChaCha20Poly1305_DecisionCoverage), \
|
||||
TEST_DECL_GROUP("xchacha20-poly1305", test_wc_XChaCha20Poly1305_LargeBuffer)
|
||||
|
||||
|
||||
@@ -7732,6 +7732,9 @@ void bench_chacha20_poly1305_aead(void)
|
||||
{
|
||||
double start;
|
||||
int ret = 0, i, count;
|
||||
ChaCha chacha; /* keyed once, reused per record: the TLS-record path */
|
||||
Poly1305 poly;
|
||||
byte nonce[CHACHA20_POLY1305_AEAD_IV_SIZE];
|
||||
DECLARE_MULTI_VALUE_STATS_VARS()
|
||||
|
||||
WC_DECLARE_VAR(bench_additional, byte, AES_AUTH_ADD_SZ, HEAP_HINT);
|
||||
@@ -7743,7 +7746,9 @@ void bench_chacha20_poly1305_aead(void)
|
||||
WC_ALLOC_VAR(authTag, byte, CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE, HEAP_HINT);
|
||||
XMEMSET(bench_additional, 0, AES_AUTH_ADD_SZ);
|
||||
XMEMSET(authTag, 0, CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE);
|
||||
XMEMSET(nonce, 0, sizeof(nonce));
|
||||
|
||||
/* One-shot encrypt (re-keys per call). */
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < numBlocks; i++) {
|
||||
@@ -7763,7 +7768,162 @@ void bench_chacha20_poly1305_aead(void)
|
||||
#endif
|
||||
);
|
||||
|
||||
bench_stats_sym_finish("CHA-POLY", 0, count, bench_size, start, ret);
|
||||
bench_stats_sym_finish("CHA-POLY-enc", 0, count, bench_size, start, ret);
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
RESET_MULTI_VALUE_STATS_VARS();
|
||||
|
||||
/* Produce a valid ciphertext+tag once for the decrypt benchmarks. */
|
||||
ret = wc_ChaCha20Poly1305_Encrypt(bench_key, bench_iv, bench_additional,
|
||||
aesAuthAddSz, bench_plain, bench_size, bench_cipher, authTag);
|
||||
if (ret < 0) {
|
||||
printf("wc_ChaCha20Poly1305_Encrypt error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* One-shot verify+decrypt. */
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < numBlocks; i++) {
|
||||
ret = wc_ChaCha20Poly1305_Decrypt(bench_key, bench_iv,
|
||||
bench_additional, aesAuthAddSz, bench_cipher, bench_size,
|
||||
authTag, bench_plain);
|
||||
if (ret < 0) {
|
||||
printf("wc_ChaCha20Poly1305_Decrypt error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
RECORD_MULTI_VALUE_STATS();
|
||||
}
|
||||
count += i;
|
||||
} while (bench_stats_check(start)
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
|| runs < minimum_runs
|
||||
#endif
|
||||
);
|
||||
|
||||
bench_stats_sym_finish("CHA-POLY-dec", 0, count, bench_size, start, ret);
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
RESET_MULTI_VALUE_STATS_VARS();
|
||||
|
||||
/* TLS-record path: ChaCha keyed once, only the nonce varies per record;
|
||||
* Encrypt_ex/Decrypt_ex use the single-pass stitch (no per-record
|
||||
* re-key). */
|
||||
ret = wc_Chacha_SetKey(&chacha, bench_key,
|
||||
CHACHA20_POLY1305_AEAD_KEYSIZE);
|
||||
if (ret != 0) {
|
||||
printf("wc_Chacha_SetKey error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < numBlocks; i++) {
|
||||
ret = wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, bench_cipher,
|
||||
bench_plain, bench_size, nonce, authTag, bench_additional,
|
||||
aesAuthAddSz);
|
||||
if (ret < 0) {
|
||||
printf("wc_ChaCha20Poly1305_Encrypt_ex error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
RECORD_MULTI_VALUE_STATS();
|
||||
}
|
||||
count += i;
|
||||
} while (bench_stats_check(start)
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
|| runs < minimum_runs
|
||||
#endif
|
||||
);
|
||||
|
||||
bench_stats_sym_finish("CHA-POLY-ex-enc", 0, count, bench_size, start, ret);
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
RESET_MULTI_VALUE_STATS_VARS();
|
||||
|
||||
/* Valid ciphertext+tag for the Decrypt_ex benchmark. */
|
||||
ret = wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, bench_cipher,
|
||||
bench_plain, bench_size, nonce, authTag, bench_additional,
|
||||
aesAuthAddSz);
|
||||
if (ret < 0) {
|
||||
printf("wc_ChaCha20Poly1305_Encrypt_ex error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < numBlocks; i++) {
|
||||
ret = wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, bench_plain,
|
||||
bench_cipher, bench_size, nonce, authTag, bench_additional,
|
||||
aesAuthAddSz);
|
||||
if (ret < 0) {
|
||||
printf("wc_ChaCha20Poly1305_Decrypt_ex error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
RECORD_MULTI_VALUE_STATS();
|
||||
}
|
||||
count += i;
|
||||
} while (bench_stats_check(start)
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
|| runs < minimum_runs
|
||||
#endif
|
||||
);
|
||||
|
||||
bench_stats_sym_finish("CHA-POLY-ex-dec", 0, count, bench_size, start, ret);
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
RESET_MULTI_VALUE_STATS_VARS();
|
||||
|
||||
/* Streaming AEAD interface over the FUSED stitch, "openssl speed -aead"
|
||||
* methodology: Init once (framing amortized), then loop UpdateData with the
|
||||
* whole buffer and NO per-chunk tag/Final. wc_ChaCha20Poly1305_UpdateData
|
||||
* dispatches to the single-pass IFMA stitch for each 1024-aligned chunk
|
||||
* (>= CHACHA20_POLY1305_STITCH_MIN), so this is wolfSSL's fused cipher
|
||||
* throughput measured the same bare-loop way OpenSSL's cipher is - unlike a
|
||||
* two-pass Process+Update, and reachable through the public API. Below the
|
||||
* stitch threshold (or a non-64-aligned bench_size) UpdateData stays
|
||||
* two-pass, which is the interface's real behaviour. Re-Init before the
|
||||
* running dataLen would overflow CHACHA20_POLY1305_MAX. */
|
||||
{
|
||||
ChaChaPoly_Aead sAead;
|
||||
XMEMSET(&sAead, 0, sizeof(sAead));
|
||||
ret = wc_ChaCha20Poly1305_Init(&sAead, bench_key, nonce, 1);
|
||||
if (ret != 0) {
|
||||
printf("chacha20-poly1305 stream Init error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < numBlocks; i++) {
|
||||
if (sAead.dataLen > CHACHA20_POLY1305_MAX - bench_size) {
|
||||
XMEMSET(&sAead, 0, sizeof(sAead));
|
||||
ret = wc_ChaCha20Poly1305_Init(&sAead, bench_key, nonce, 1);
|
||||
if (ret != 0) {
|
||||
printf("chacha20-poly1305 stream Init error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ret = wc_ChaCha20Poly1305_UpdateData(&sAead, bench_plain,
|
||||
bench_cipher, bench_size);
|
||||
if (ret < 0) {
|
||||
printf("chacha20-poly1305 stream error: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
RECORD_MULTI_VALUE_STATS();
|
||||
}
|
||||
count += i;
|
||||
} while (bench_stats_check(start)
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
|| runs < minimum_runs
|
||||
#endif
|
||||
);
|
||||
|
||||
bench_stats_sym_finish("CHA-POLY-stream", 0, count, bench_size, start, ret);
|
||||
}
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
|
||||
@@ -100,6 +100,13 @@ Public domain.
|
||||
#ifndef NO_AVX2_SUPPORT
|
||||
#define HAVE_INTEL_AVX2
|
||||
#endif
|
||||
#if !defined(NO_AVX512_SUPPORT) && !defined(HAVE_INTEL_AVX512)
|
||||
#define HAVE_INTEL_AVX512
|
||||
#endif
|
||||
/* SSSE3 is the baseline SIMD path, used on CPUs that lack AVX. */
|
||||
#ifndef HAVE_INTEL_SSSE3
|
||||
#define HAVE_INTEL_SSSE3
|
||||
#endif
|
||||
|
||||
static cpuid_flags_t cpuidFlags = WC_CPUID_INITIALIZER;
|
||||
#endif
|
||||
@@ -318,11 +325,51 @@ extern void chacha_encrypt_avx1(ChaCha* ctx, const byte* m, byte* c,
|
||||
word32 bytes);
|
||||
extern void chacha_encrypt_avx2(ChaCha* ctx, const byte* m, byte* c,
|
||||
word32 bytes);
|
||||
extern void chacha_encrypt_avx512(ChaCha* ctx, const byte* m, byte* c,
|
||||
word32 bytes);
|
||||
extern void chacha_encrypt_avx512vl(ChaCha* ctx, const byte* m, byte* c,
|
||||
word32 bytes);
|
||||
extern void chacha_encrypt_sse3(ChaCha* ctx, const byte* m, byte* c,
|
||||
word32 bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#if defined(USE_INTEL_CHACHA_SPEEDUP) && defined(HAVE_INTEL_AVX512)
|
||||
/* Decide whether to use the 512-bit (zmm) ChaCha path for this CPU.
|
||||
*
|
||||
* The zmm path processes 16 blocks at a time and is the fastest option on
|
||||
* microarchitectures that run 512-bit code at full clock: AMD Zen 4/5 (no
|
||||
* AVX-512 license) and Intel Ice Lake and later. On Intel Skylake-SP /
|
||||
* Cascade Lake-class parts, sustained 512-bit instructions trip the AVX-512
|
||||
* frequency license and downclock the core - enough that the 256-bit AVX2 path
|
||||
* is faster in practice (this matches OpenSSL, which suppresses its 16x zmm
|
||||
* ChaCha there, and the Linux kernel, which uses only 256-bit AVX-512VL).
|
||||
*
|
||||
* There is no direct "does this core downclock" CPUID bit, so VAES presence is
|
||||
* used as a generational proxy: the throttling parts (Skylake-SP / Skylake-X /
|
||||
* Cascade Lake) predate VAES, whereas every microarchitecture that runs 512-bit
|
||||
* without penalty (AMD Zen 4/5, Intel Ice Lake+) implements it. A missing VAES
|
||||
* only costs a little throughput (fall back to AVX2), never correctness.
|
||||
*
|
||||
* Override the heuristic with:
|
||||
* WOLFSSL_CHACHA20_AVX512_ALWAYS - use zmm whenever AVX-512 is present
|
||||
* WOLFSSL_CHACHA20_AVX512_NEVER - never use zmm (always AVX2 or below)
|
||||
*/
|
||||
static WC_INLINE int chacha_avx512_beneficial(cpuid_flags_t flags)
|
||||
{
|
||||
#if defined(WOLFSSL_CHACHA20_AVX512_NEVER)
|
||||
(void)flags;
|
||||
return 0;
|
||||
#elif defined(WOLFSSL_CHACHA20_AVX512_ALWAYS)
|
||||
return IS_INTEL_AVX512(flags) != 0;
|
||||
#else
|
||||
return (IS_INTEL_AVX512(flags) != 0) && (IS_INTEL_VAES(flags) != 0);
|
||||
#endif
|
||||
}
|
||||
#endif /* USE_INTEL_CHACHA_SPEEDUP && HAVE_INTEL_AVX512 */
|
||||
|
||||
|
||||
#if (!defined(USE_INTEL_CHACHA_SPEEDUP) && !defined(USE_ARM_CHACHA_SPEEDUP) && \
|
||||
!defined(USE_RISCV_CHACHA_SPEEDUP)) || defined(WOLFSSL_ARM_CHACHA_NEED_C)
|
||||
@@ -425,6 +472,73 @@ int wc_Chacha_Process(ChaCha* ctx, byte* output, const byte* input,
|
||||
|
||||
cpuid_get_flags_ex(&cpuidFlags);
|
||||
|
||||
/* One block or less. */
|
||||
#if defined(HAVE_INTEL_AVX1) && !defined(WOLFSSL_LINUXKM)
|
||||
/* In userspace SAVE_VECTOR_REGISTERS is free, so a single AVX block (~285
|
||||
* cyc) beats the scalar block (~435) - e.g. the per-record Poly1305 key
|
||||
* derivation (a 32-byte ChaCha) in the ChaCha20-Poly1305 two-pass path.
|
||||
* The AVX-512VL path already uses SIMD for one block; match that here. */
|
||||
if (msglen <= CHACHA_CHUNK_BYTES && IS_INTEL_AVX512_VL(cpuidFlags) == 0 &&
|
||||
IS_INTEL_AVX1(cpuidFlags)) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
chacha_encrypt_avx1(ctx, input, output, msglen);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/* At most one block: the scalar path avoids the SIMD broadcast/transpose
|
||||
* setup and (in the Linux kernel module) the costly vector-register
|
||||
* save/restore. */
|
||||
if (msglen <= CHACHA_CHUNK_BYTES) {
|
||||
chacha_encrypt_x64(ctx, input, output, msglen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 65..255 bytes without AVX-512VL: use the SSSE3 128-bit exact-block path.
|
||||
* It is ~1.8x the scalar path and beats the 8-block AVX2 kernel (which
|
||||
* always emits a full 512-byte key stream) below 256 bytes - e.g. a
|
||||
* 192-byte key stream is 735 vs 1335 (scalar) vs 836 (AVX2) cycles on
|
||||
* Coffee Lake. This is the ChaCha20-Poly1305 short-record hot path (poly
|
||||
* key + <=2 data blocks). At >=256 bytes the four-block AVX2/AVX1 kernels
|
||||
* take over below. */
|
||||
#ifdef HAVE_INTEL_SSSE3
|
||||
if (IS_INTEL_AVX512_VL(cpuidFlags) == 0 &&
|
||||
msglen < 4 * CHACHA_CHUNK_BYTES &&
|
||||
IS_INTEL_SSSE3(cpuidFlags)) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
chacha_encrypt_sse3(ctx, input, output, msglen);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (IS_INTEL_AVX512_VL(cpuidFlags) == 0 &&
|
||||
msglen < 4 * CHACHA_CHUNK_BYTES) {
|
||||
chacha_encrypt_x64(ctx, input, output, msglen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_INTEL_AVX512
|
||||
/* Below one 16-block chunk (1024 bytes) the zmm path does no work and
|
||||
* just tail-calls AVX2, so dispatch straight to AVX2 for smaller input. */
|
||||
if (chacha_avx512_beneficial(cpuidFlags) &&
|
||||
msglen >= 16 * CHACHA_CHUNK_BYTES) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
chacha_encrypt_avx512(ctx, input, output, msglen);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
return 0;
|
||||
}
|
||||
/* Everything below the AVX2 512-byte minimum (1..511 bytes) is handled by
|
||||
* the AVX-512VL path itself - whole 256-byte four-block chunks plus a
|
||||
* partial four-block tail - using single-instruction vprold rotations on
|
||||
* 128-bit registers (no AVX-512 frequency penalty). It does not fall back
|
||||
* to any other implementation. */
|
||||
if (IS_INTEL_AVX512_VL(cpuidFlags) && msglen < 8 * CHACHA_CHUNK_BYTES) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
chacha_encrypt_avx512vl(ctx, input, output, msglen);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_INTEL_AVX2
|
||||
if (IS_INTEL_AVX2(cpuidFlags)) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
@@ -439,6 +553,14 @@ int wc_Chacha_Process(ChaCha* ctx, byte* output, const byte* input,
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
return 0;
|
||||
}
|
||||
#ifdef HAVE_INTEL_SSSE3
|
||||
else if (IS_INTEL_SSSE3(cpuidFlags)) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
chacha_encrypt_sse3(ctx, input, output, msglen);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
chacha_encrypt_x64(ctx, input, output, msglen);
|
||||
return 0;
|
||||
|
||||
@@ -32,6 +32,7 @@ or Authenticated Encryption with Additional Data (AEAD) algorithm.
|
||||
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
||||
|
||||
#include <wolfssl/wolfcrypt/chacha20_poly1305.h>
|
||||
#include <wolfssl/wolfcrypt/cpuid.h>
|
||||
|
||||
#ifdef NO_INLINE
|
||||
#include <wolfssl/wolfcrypt/misc.h>
|
||||
@@ -41,6 +42,583 @@ or Authenticated Encryption with Additional Data (AEAD) algorithm.
|
||||
#endif
|
||||
|
||||
#define CHACHA20_POLY1305_AEAD_INITIAL_COUNTER 0
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED
|
||||
/* Fused single-pass encrypt kernel (in chacha_asm.S) and the 4-way power
|
||||
* precompute it depends on. */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
WOLFSSL_LOCAL void chacha20_poly1305_avx512(ChaCha* chacha, Poly1305* poly,
|
||||
const byte* m, byte* c, word32 bytes);
|
||||
WOLFSSL_LOCAL void poly1305_calc_powers_avx2(Poly1305* ctx);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The fused kernel uses 4-block ChaCha (256-bit) + 4-way Poly1305, which beats
|
||||
* the wide two-pass only where 512-bit code is throttled - Intel Ice Lake and
|
||||
* later, under the AVX-512 frequency license. On AMD (no throttle, very fast
|
||||
* wide primitives) the two-pass wins, so gate on an Intel vendor. Override:
|
||||
* WOLFSSL_CHACHA20_POLY1305_FUSED_ALWAYS / _NEVER. */
|
||||
static WC_INLINE int chacha20_poly1305_use_fused(void)
|
||||
{
|
||||
#if defined(WOLFSSL_CHACHA20_POLY1305_FUSED_NEVER)
|
||||
return 0;
|
||||
#elif defined(WOLFSSL_CHACHA20_POLY1305_FUSED_ALWAYS)
|
||||
return 1;
|
||||
#else
|
||||
cpuid_flags_t f = cpuid_get_flags();
|
||||
return (IS_CPU_INTEL(f) != 0) && (IS_INTEL_AVX512(f) != 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Encrypt with the fused kernel: no AAD, so Poly1305 starts clean and 256-byte
|
||||
* aligned. Drive Poly1305 4-way (forceAvx2) so the kernel and the tail/final
|
||||
* share the layout; the kernel does the aligned bulk, the tail and length
|
||||
* framing go through the normal 4-way path. */
|
||||
static int chacha20_poly1305_encrypt_fused(ChaChaPoly_Aead* aead,
|
||||
const byte* pt, word32 ptLen, byte* ct, byte* tag)
|
||||
{
|
||||
word32 bulk = ptLen & ~(word32)0xff;
|
||||
int ret;
|
||||
|
||||
aead->poly.forceAvx2 = 1;
|
||||
/* The cpuid setkey may have zeroed a different accumulator; ready the 4-way
|
||||
* hash and let the kernel initialise the lanes. */
|
||||
XMEMSET(aead->poly.hh, 0, sizeof(aead->poly.hh));
|
||||
aead->poly.started = 0;
|
||||
aead->poly.leftover = 0;
|
||||
aead->state = CHACHA20_POLY1305_STATE_DATA;
|
||||
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
poly1305_calc_powers_avx2(&aead->poly);
|
||||
aead->poly.started = 1;
|
||||
/* bulk is a non-zero multiple of 256 here (caller gates on >= 256), but the
|
||||
* kernel has no short-length entry guard - it would run a full 256-byte unit
|
||||
* off the end on a zero length, so never call it with nothing to do. */
|
||||
if (bulk > 0)
|
||||
chacha20_poly1305_avx512(&aead->chacha, &aead->poly, pt, ct, bulk);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
|
||||
aead->dataLen = bulk;
|
||||
ret = 0;
|
||||
if (ptLen > bulk)
|
||||
ret = wc_ChaCha20Poly1305_UpdateData(aead, pt + bulk, ct + bulk,
|
||||
ptLen - bulk);
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_Final(aead, tag);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* WOLFSSL_CHACHA20_POLY1305_FUSED */
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
/* IFMA stitched single-pass encrypt kernel (in chacha_asm.S): full 512-bit
|
||||
* 16-block ChaCha interleaved with an 8-way IFMA (vpmadd52) Poly1305 that
|
||||
* collapses to the scalar hash. Processes 1024-byte units. Depends on the
|
||||
* radix-2^44 powers. */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
WOLFSSL_LOCAL void chacha20_poly1305_ifma(ChaCha* chacha, Poly1305* poly,
|
||||
const byte* m, byte* c, word32 bytes);
|
||||
/* Decrypt counterpart: hashes the ciphertext INPUT (m) as it decrypts to c
|
||||
* (in-place safe - m is hashed before it is overwritten). Same 1024-byte
|
||||
* units and radix-2^44 powers. */
|
||||
WOLFSSL_LOCAL void chacha20_poly1305_ifma_decrypt(ChaCha* chacha,
|
||||
Poly1305* poly, const byte* m, byte* c, word32 bytes);
|
||||
WOLFSSL_LOCAL void poly1305_calc_powers_avx512ifma(Poly1305* ctx);
|
||||
/* ctx->h = ctx->hh * r^nBlocks + ctx->h - advances the running hash (saved by
|
||||
* the kernel to ctx->hh) past a chunk the kernel hashed from zero into ctx->h.
|
||||
* Radix-2^64 scalar, so no 26<->64 conversions. */
|
||||
WOLFSSL_LOCAL void poly1305_fold_avx512ifma(Poly1305* ctx, word32 nBlocks);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Minimum length to stitch. The kernel processes 1024-byte units and a
|
||||
* once-per-op power precompute, and any sub-1024 remainder is authenticated by
|
||||
* the slower scalar Poly1305; below this the two-pass wins (measured crossover
|
||||
* on Zen5). Above it the stitch wins 1.1-1.4x, growing with size.
|
||||
*
|
||||
* HARD LOWER BOUND 1024: callers gate on this then pass bulk = sz & ~0x3ff to
|
||||
* the kernel, and the kernel has NO short-length entry guard - its first length
|
||||
* test runs only AFTER a full 1024-byte chunk. A value below 1024 lets a
|
||||
* sub-1024 message compute bulk == 0, and the kernel then reads/writes a whole
|
||||
* 1024-byte unit off the end of the buffer and underflows its counter into a
|
||||
* multi-million-iteration loop. Enforced at compile time below; stitch_chunk()
|
||||
* also guards bulk == 0 at runtime as defence in depth. */
|
||||
#ifndef CHACHA20_POLY1305_STITCH_MIN
|
||||
#define CHACHA20_POLY1305_STITCH_MIN 4096
|
||||
#endif
|
||||
#if CHACHA20_POLY1305_STITCH_MIN < 1024
|
||||
#error "CHACHA20_POLY1305_STITCH_MIN must be >= 1024 (the kernel unit size)"
|
||||
#endif
|
||||
|
||||
/* Short-message fused path: for messages this small the Poly1305 key block
|
||||
* (ChaCha counter 0) and the whole ciphertext (counter 1+) fit in a single
|
||||
* ChaCha keystream generation - one pass instead of two - and a scalar
|
||||
* Poly1305 avoids the vector power-precompute cost. Needs the forceScalar
|
||||
* flag (same builds as the fused kernels). Measured 1.1-1.5x on Zen5 for
|
||||
* 64-192 byte records; above SHORT_MAX the poly key spills to a second ChaCha
|
||||
* chunk and the saving is gone. */
|
||||
#if defined(WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA) && \
|
||||
!defined(WOLFSSL_NO_CHACHA20_POLY1305_SHORT)
|
||||
#define WOLFSSL_CHACHA20_POLY1305_SHORT
|
||||
#ifndef CHACHA20_POLY1305_SHORT_MAX
|
||||
/* 64 (poly-key block) + 192 = 256 = one AVX-512VL 4-block chunk */
|
||||
#define CHACHA20_POLY1305_SHORT_MAX 192
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* The IFMA stitch runs a full-width 512-bit 16-block ChaCha interleaved with an
|
||||
* 8-way IFMA Poly1305: ChaCha is the bottleneck and Poly hides under it, so it
|
||||
* beats the two-pass (which runs the two passes back to back) by ~1.3-1.4x at
|
||||
* >=16KB - measured on AMD Zen5, and expected wherever AVX-512 + IFMA exist
|
||||
* (both use 512-bit ChaCha, so any frequency throttle hits both equally). Gate
|
||||
* on AVX-512 + IFMA, any vendor. Override: ..._FUSED_IFMA_ALWAYS / _NEVER. */
|
||||
static WC_INLINE int chacha20_poly1305_use_fused_ifma(void)
|
||||
{
|
||||
#if defined(WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA_NEVER)
|
||||
return 0;
|
||||
#elif defined(WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA_ALWAYS)
|
||||
return 1;
|
||||
#else
|
||||
cpuid_flags_t f = cpuid_get_flags();
|
||||
return (IS_INTEL_AVX512(f) != 0) && (IS_INTEL_AVX512_IFMA(f) != 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Stitch one 1024-byte-aligned bulk. (IFMA path) The kernel hashes this
|
||||
* chunk's ciphertext from zero (leaving ctx->h = H_chunk) and advances the
|
||||
* ChaCha counter, saving the running hash (the AAD, or previous chunks) to
|
||||
* ctx->hh; poly1305_fold_avx512ifma then advances that hash past this chunk
|
||||
* (ctx->h = ctx->hh * r^nBlocks + H_chunk). Powers are computed once (started
|
||||
* flag). Caller must have ctx->h = running hash, leftover == 0, forceScalar
|
||||
* and finished set, and the ChaCha counter placed for this chunk. decrypt: in
|
||||
* is ciphertext, out is plaintext (in-place safe - the kernel hashes in before
|
||||
* overwriting it); the hash math is identical. */
|
||||
static int chacha20_poly1305_stitch_chunk(ChaCha* chacha, Poly1305* poly,
|
||||
const byte* in, byte* out, word32 bulk, int decrypt)
|
||||
{
|
||||
int fold;
|
||||
|
||||
/* The kernel has no short-length entry guard and would run a full 1024-byte
|
||||
* unit off the end of the buffer on a zero length. bulk is always a
|
||||
* non-zero multiple of 1024 here (STITCH_MIN >= 1024, enforced at compile
|
||||
* time), but never invoke the kernel with nothing to do. */
|
||||
if (bulk == 0)
|
||||
return 0;
|
||||
|
||||
/* A running hash (AAD or previous chunks) must be folded past this chunk;
|
||||
* detect it before the kernel overwrites poly->h with this chunk's hash. */
|
||||
fold = (poly->h[0] | poly->h[1] | poly->h[2]) != 0;
|
||||
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
if (!poly->started) {
|
||||
poly1305_calc_powers_avx512ifma(poly);
|
||||
poly->started = 1;
|
||||
}
|
||||
if (decrypt)
|
||||
chacha20_poly1305_ifma_decrypt(chacha, poly, in, out, bulk);
|
||||
else
|
||||
chacha20_poly1305_ifma(chacha, poly, in, out, bulk);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
|
||||
/* poly->h = H_chunk, poly->hh = running hash (scalar fold, no vectors). */
|
||||
if (fold)
|
||||
poly1305_fold_avx512ifma(poly, bulk / 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Encrypt the whole message with the IFMA stitch (one-shot path). AAD is
|
||||
* hashed scalar into ctx->h, the 1024-aligned bulk is stitched (folding AAD
|
||||
* through it), the tail + length framing go through the scalar path.
|
||||
* forceScalar/finished: see the AVX2 fused note (setkey_avx2 leaves finished
|
||||
* clear). Both this and the streaming UpdateData path share stitch_chunk(). */
|
||||
static int chacha20_poly1305_encrypt_fused_ifma(ChaChaPoly_Aead* aead,
|
||||
const byte* aad, word32 aadLen, const byte* pt, word32 ptLen, byte* ct,
|
||||
byte* tag)
|
||||
{
|
||||
word32 bulk = ptLen & ~(word32)0x3ff;
|
||||
int ret = 0;
|
||||
|
||||
aead->poly.forceScalar = 1;
|
||||
XMEMSET(aead->poly.h, 0, sizeof(aead->poly.h));
|
||||
aead->poly.finished = 1;
|
||||
aead->poly.leftover = 0;
|
||||
aead->poly.started = 0;
|
||||
|
||||
/* Hash AAD + pad1 (scalar) -> H_aad in ctx->h. */
|
||||
if (aadLen > 0) {
|
||||
ret = wc_Poly1305Update(&aead->poly, aad, aadLen);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305_Pad(&aead->poly, aadLen);
|
||||
}
|
||||
aead->aadLen = aadLen;
|
||||
aead->state = CHACHA20_POLY1305_STATE_DATA;
|
||||
|
||||
if (ret == 0)
|
||||
ret = chacha20_poly1305_stitch_chunk(&aead->chacha, &aead->poly, pt,
|
||||
ct, bulk, 0);
|
||||
if (ret == 0) {
|
||||
aead->dataLen = bulk;
|
||||
if (ptLen > bulk)
|
||||
ret = wc_ChaCha20Poly1305_UpdateData(aead, pt + bulk, ct + bulk,
|
||||
ptLen - bulk);
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_Final(aead, tag);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA */
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_SHORT
|
||||
|
||||
/* small_enc/small_dec are HAVE_INTEL_AVX2 kernels in chacha_asm.S; gate on
|
||||
* NO_AVX2_SUPPORT so a -DNO_AVX2_SUPPORT build does not reference them. */
|
||||
#if defined(USE_INTEL_SPEEDUP) && defined(WOLFSSL_X86_64_BUILD) && \
|
||||
!defined(WOLFSSL_NO_CHACHA20_POLY1305_SMALL_ASM) && \
|
||||
!defined(NO_AVX2_SUPPORT)
|
||||
#define WOLFSSL_CP_SMALL_ASM
|
||||
|
||||
/* Fused single-call ChaCha20-Poly1305 encrypt for a one-block (<=64 byte)
|
||||
* record (in chacha_asm.S): SSSE3 crypt2 produces the Poly1305 key block and
|
||||
* the single data block together; the scalar poly1305_*_avx do the MAC. */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
WOLFSSL_LOCAL void chacha20_poly1305_small_enc(ChaCha* chacha, Poly1305* poly,
|
||||
const byte* m, byte* c, word32 mLen, const byte* aad, word32 aadLen,
|
||||
byte* tag);
|
||||
/* Decrypt twin: decrypts in->out AND verifies the tag in one pass (decrypt-
|
||||
* then-verify). Constant-time-compares the computed tag against the received
|
||||
* tag internally and returns 0 on match, 1 on mismatch; the caller ForceZeros
|
||||
* the output on mismatch, so no plaintext is released on a bad tag. */
|
||||
WOLFSSL_LOCAL int chacha20_poly1305_small_dec(ChaCha* chacha, Poly1305* poly,
|
||||
const byte* in, byte* out, word32 ctLen, const byte* aad, word32 aadLen,
|
||||
const byte* tag);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
static WC_INLINE int chacha20_poly1305_use_small(void)
|
||||
{
|
||||
return IS_INTEL_AVX2(cpuid_get_flags()) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fused short-message (sz <= CHACHA20_POLY1305_SHORT_MAX) encrypt for the
|
||||
* pre-keyed contexts: derive the Poly1305 key (counter 0) and the encryption
|
||||
* keystream (counter 1+) in a SINGLE ChaCha pass, then scalar-hash. Saves the
|
||||
* second ChaCha invocation the two-pass path would make. */
|
||||
static int chacha20_poly1305_encrypt_short(ChaCha* chacha, Poly1305* poly,
|
||||
byte* out, const byte* in, word32 sz, const byte* nonce, byte* tag,
|
||||
const byte* aad, word32 aadSz)
|
||||
{
|
||||
byte ks[64 + CHACHA20_POLY1305_SHORT_MAX];
|
||||
int ret;
|
||||
|
||||
#ifdef WOLFSSL_CP_SMALL_ASM
|
||||
/* One block or less of data: the fused single-call kernel. */
|
||||
if (sz <= 64 && chacha20_poly1305_use_small()) {
|
||||
ret = wc_Chacha_SetIV(chacha, nonce,
|
||||
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
|
||||
if (ret == 0) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
chacha20_poly1305_small_enc(chacha, poly, in, out, sz, aad, aadSz,
|
||||
tag);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
XMEMSET(ks, 0, 64 + sz);
|
||||
ret = wc_Chacha_SetIV(chacha, nonce,
|
||||
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
|
||||
if (ret == 0) /* ctr0 (poly key) .. ctrN, one pass */
|
||||
ret = wc_Chacha_Process(chacha, ks, ks, 64 + sz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305SetKey(poly, ks, CHACHA20_POLY1305_AEAD_KEYSIZE);
|
||||
if (ret == 0) {
|
||||
xorbufout(out, in, ks + 64, sz); /* ct = pt ^ keystream (ctr1+) */
|
||||
poly->forceScalar = 1;
|
||||
poly->finished = 1;
|
||||
ret = wc_Poly1305_MAC(poly, aad, aadSz, out, sz, tag,
|
||||
CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE);
|
||||
}
|
||||
ForceZero(ks, 64 + sz); /* ks[0:32] was the poly key */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Fused short-message decrypt twin: derive key + keystream in one ChaCha pass,
|
||||
* MAC the ciphertext INPUT and verify the tag BEFORE decrypting, so no
|
||||
* plaintext is produced on a bad tag (stronger than the stitch, cheap here
|
||||
* because the message is small). In-place safe. */
|
||||
static int chacha20_poly1305_decrypt_short(ChaCha* chacha, Poly1305* poly,
|
||||
byte* out, const byte* in, word32 sz, const byte* nonce, const byte* tag,
|
||||
const byte* aad, word32 aadSz)
|
||||
{
|
||||
byte ks[64 + CHACHA20_POLY1305_SHORT_MAX];
|
||||
byte calcTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
int ret;
|
||||
|
||||
#ifdef WOLFSSL_CP_SMALL_ASM
|
||||
/* One block or less of data: the fused single-call kernel decrypts in->out
|
||||
* and computes calcTag in one pass (decrypt-then-verify). Zero the output
|
||||
* if the tag is bad, so no plaintext is released. */
|
||||
if (sz <= 64 && chacha20_poly1305_use_small()) {
|
||||
ret = wc_Chacha_SetIV(chacha, nonce,
|
||||
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
|
||||
if (ret == 0) {
|
||||
int bad;
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
bad = chacha20_poly1305_small_dec(chacha, poly, in, out, sz, aad,
|
||||
aadSz, tag);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
if (bad) { /* bad tag: no plaintext */
|
||||
if (sz > 0)
|
||||
ForceZero(out, sz);
|
||||
ret = MAC_CMP_FAILED_E;
|
||||
}
|
||||
}
|
||||
(void)calcTag;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
XMEMSET(ks, 0, 64 + sz);
|
||||
ret = wc_Chacha_SetIV(chacha, nonce,
|
||||
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(chacha, ks, ks, 64 + sz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305SetKey(poly, ks, CHACHA20_POLY1305_AEAD_KEYSIZE);
|
||||
if (ret == 0) {
|
||||
poly->forceScalar = 1;
|
||||
poly->finished = 1;
|
||||
ret = wc_Poly1305_MAC(poly, aad, aadSz, in, sz, calcTag,
|
||||
CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE);
|
||||
}
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_CheckTag(tag, calcTag);
|
||||
if (ret == 0) /* tag good: decrypt pt = ct ^ ks */
|
||||
xorbufout(out, in, ks + 64, sz);
|
||||
else if (sz > 0) /* bad tag/error: no stale output */
|
||||
ForceZero(out, sz);
|
||||
ForceZero(ks, 64 + sz);
|
||||
return ret;
|
||||
}
|
||||
#endif /* WOLFSSL_CHACHA20_POLY1305_SHORT */
|
||||
|
||||
/* Encrypt + authenticate one message with PRE-KEYED ChaCha20 and Poly1305
|
||||
* contexts - the ChaCha20-Poly1305 analogue of wc_AesGcmEncrypt on a keyed Aes.
|
||||
* Intended for the TLS record layer, which keeps the ChaCha context keyed once
|
||||
* (per traffic key) and only varies the nonce per record. The per-record
|
||||
* Poly1305 key is derived here from the ChaCha keystream. Uses the single-pass
|
||||
* IFMA stitch when beneficial, else the two-pass; identical output either way.
|
||||
*
|
||||
* chacha ChaCha20 context with the key already set (wc_Chacha_SetKey)
|
||||
* poly Poly1305 scratch context (re-keyed here every call)
|
||||
* out ciphertext out (may alias in)
|
||||
* in/sz plaintext / length
|
||||
* nonce CHACHA20_POLY1305_AEAD_IV_SIZE (12) byte record nonce
|
||||
* tag CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE (16) byte tag out
|
||||
* aad/aadSz additional authenticated data
|
||||
* returns 0 on success, negative on error.
|
||||
*/
|
||||
WOLFSSL_API int wc_ChaCha20Poly1305_Encrypt_ex(ChaCha* chacha, Poly1305* poly,
|
||||
byte* out, const byte* in, word32 sz, const byte* nonce, byte* tag,
|
||||
const byte* aad, word32 aadSz)
|
||||
{
|
||||
byte polyKey[CHACHA20_POLY1305_AEAD_KEYSIZE];
|
||||
int ret;
|
||||
|
||||
if (chacha == NULL || poly == NULL || nonce == NULL || tag == NULL ||
|
||||
(sz > 0 && (in == NULL || out == NULL)) ||
|
||||
(aadSz > 0 && aad == NULL)) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_SHORT
|
||||
if (sz <= CHACHA20_POLY1305_SHORT_MAX)
|
||||
return chacha20_poly1305_encrypt_short(chacha, poly, out, in, sz,
|
||||
nonce, tag, aad, aadSz);
|
||||
#endif
|
||||
|
||||
/* Per-record Poly1305 key = first 32 bytes of ChaCha20(nonce, ctr 0). */
|
||||
XMEMSET(polyKey, 0, sizeof(polyKey));
|
||||
ret = wc_Chacha_SetIV(chacha, nonce,
|
||||
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(chacha, polyKey, polyKey, sizeof(polyKey));
|
||||
if (ret == 0) /* message data starts at counter 1 */
|
||||
ret = wc_Chacha_SetIV(chacha, nonce,
|
||||
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER + 1);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305SetKey(poly, polyKey, sizeof(polyKey));
|
||||
ForceZero(polyKey, sizeof(polyKey));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
if (sz >= CHACHA20_POLY1305_STITCH_MIN &&
|
||||
chacha20_poly1305_use_fused_ifma()) {
|
||||
word32 bulk = sz & ~(word32)0x3ff;
|
||||
/* Scalar running hash (in poly->h) so stitch and tail chain; poly->h,
|
||||
* leftover and started are all zeroed by wc_Poly1305SetKey. */
|
||||
poly->forceScalar = 1;
|
||||
poly->finished = 1;
|
||||
if (aadSz > 0) { /* H_aad + pad1 (scalar) */
|
||||
ret = wc_Poly1305Update(poly, aad, aadSz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305_Pad(poly, aadSz);
|
||||
}
|
||||
if (ret == 0) /* stitch bulk + fold AAD */
|
||||
ret = chacha20_poly1305_stitch_chunk(chacha, poly, in, out, bulk,
|
||||
0);
|
||||
if (ret == 0 && sz > bulk) { /* scalar tail */
|
||||
ret = wc_Chacha_Process(chacha, out + bulk, in + bulk, sz - bulk);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305Update(poly, out + bulk, sz - bulk);
|
||||
}
|
||||
if (ret == 0) /* pad2 + lengths + tag */
|
||||
ret = wc_Poly1305_Pad(poly, sz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305_EncodeSizes(poly, aadSz, sz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305Final(poly, tag);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Two-pass: fast vector Poly1305 (small msgs, or stitch not beneficial). */
|
||||
ret = wc_Chacha_Process(chacha, out, in, sz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305_MAC(poly, aad, aadSz, out, sz, tag,
|
||||
CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Verify+decrypt one message with pre-keyed ChaCha20 and Poly1305 contexts -
|
||||
* the decrypt counterpart of wc_ChaCha20Poly1305_Encrypt_ex, for the TLS record
|
||||
* layer. Verifies the Poly1305 tag over AAD+ciphertext and decrypts to out
|
||||
* (in-place safe). Uses the single-pass IFMA decrypt stitch when beneficial.
|
||||
* The plaintext is produced while the tag is computed, so on tag mismatch out
|
||||
* is zeroed and MAC_CMP_FAILED_E returned - callers must check the result.
|
||||
*
|
||||
* chacha ChaCha20 context with the key already set (wc_Chacha_SetKey)
|
||||
* poly Poly1305 scratch context (re-keyed here every call)
|
||||
* out plaintext out (may alias in)
|
||||
* in/sz ciphertext / length
|
||||
* nonce CHACHA20_POLY1305_AEAD_IV_SIZE (12) byte record nonce
|
||||
* tag CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE (16) byte tag to verify
|
||||
* aad/aadSz additional authenticated data
|
||||
* returns 0 on success, MAC_CMP_FAILED_E on tag mismatch, else negative.
|
||||
*/
|
||||
WOLFSSL_API int wc_ChaCha20Poly1305_Decrypt_ex(ChaCha* chacha, Poly1305* poly,
|
||||
byte* out, const byte* in, word32 sz, const byte* nonce, const byte* tag,
|
||||
const byte* aad, word32 aadSz)
|
||||
{
|
||||
byte polyKey[CHACHA20_POLY1305_AEAD_KEYSIZE];
|
||||
byte calcTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
int ret;
|
||||
|
||||
if (chacha == NULL || poly == NULL || nonce == NULL || tag == NULL ||
|
||||
(sz > 0 && (in == NULL || out == NULL)) ||
|
||||
(aadSz > 0 && aad == NULL)) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_SHORT
|
||||
if (sz <= CHACHA20_POLY1305_SHORT_MAX)
|
||||
return chacha20_poly1305_decrypt_short(chacha, poly, out, in, sz,
|
||||
nonce, tag, aad, aadSz);
|
||||
#endif
|
||||
|
||||
/* Per-record Poly1305 key = first 32 bytes of ChaCha20(nonce, ctr 0). */
|
||||
XMEMSET(polyKey, 0, sizeof(polyKey));
|
||||
ret = wc_Chacha_SetIV(chacha, nonce,
|
||||
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(chacha, polyKey, polyKey, sizeof(polyKey));
|
||||
if (ret == 0) /* message data starts at counter 1 */
|
||||
ret = wc_Chacha_SetIV(chacha, nonce,
|
||||
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER + 1);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305SetKey(poly, polyKey, sizeof(polyKey));
|
||||
ForceZero(polyKey, sizeof(polyKey));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
if (sz >= CHACHA20_POLY1305_STITCH_MIN &&
|
||||
chacha20_poly1305_use_fused_ifma()) {
|
||||
word32 bulk = sz & ~(word32)0x3ff;
|
||||
/* Scalar running hash (in poly->h) so stitch and tail chain; poly->h,
|
||||
* leftover and started are all zeroed by wc_Poly1305SetKey. */
|
||||
poly->forceScalar = 1;
|
||||
poly->finished = 1;
|
||||
if (aadSz > 0) { /* H_aad + pad1 (scalar) */
|
||||
ret = wc_Poly1305Update(poly, aad, aadSz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305_Pad(poly, aadSz);
|
||||
}
|
||||
if (ret == 0) /* stitch: hash CT + decrypt */
|
||||
ret = chacha20_poly1305_stitch_chunk(chacha, poly, in, out, bulk,
|
||||
1);
|
||||
if (ret == 0 && sz > bulk) { /* scalar tail */
|
||||
/* hash the ciphertext tail before decrypt overwrites it */
|
||||
ret = wc_Poly1305Update(poly, in + bulk, sz - bulk);
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(chacha, out + bulk, in + bulk,
|
||||
sz - bulk);
|
||||
}
|
||||
if (ret == 0) /* pad2 + lengths + tag */
|
||||
ret = wc_Poly1305_Pad(poly, sz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305_EncodeSizes(poly, aadSz, sz);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305Final(poly, calcTag);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Two-pass: MAC the ciphertext (in), then decrypt in -> out. */
|
||||
ret = wc_Poly1305_MAC(poly, aad, aadSz, in, sz, calcTag,
|
||||
CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE);
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(chacha, out, in, sz);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_CheckTag(tag, calcTag);
|
||||
if (ret != 0 && sz > 0)
|
||||
ForceZero(out, sz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clear a temporary ChaChaPoly_Aead. On an AVX-512/IFMA build the Poly1305
|
||||
* state carries ~320 extra bytes (r5..r8 for the 16-way poly, ifma_h for the
|
||||
* IFMA stitch) that are only ever written on AVX-512-capable CPUs; on a CPU
|
||||
* without AVX-512 they are never touched, so zeroing them on every call is pure
|
||||
* overhead - a large fraction of a small AEAD op. Skip them there (they hold
|
||||
* no key material from this call), and clear the full struct otherwise. */
|
||||
static WC_INLINE void chacha20_poly1305_aead_zero(ChaChaPoly_Aead* aead)
|
||||
{
|
||||
#ifdef WOLFSSL_POLY1305_AVX512
|
||||
if (IS_INTEL_AVX512(cpuid_get_flags()) != 0)
|
||||
ForceZero(aead, sizeof(ChaChaPoly_Aead));
|
||||
else
|
||||
ForceZero(aead, (word32)((const byte*)&aead->poly.r5
|
||||
- (const byte*)aead));
|
||||
#else
|
||||
ForceZero(aead, sizeof(ChaChaPoly_Aead));
|
||||
#endif
|
||||
}
|
||||
|
||||
WOLFSSL_ABI
|
||||
int wc_ChaCha20Poly1305_Encrypt(
|
||||
const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
|
||||
@@ -53,9 +631,14 @@ int wc_ChaCha20Poly1305_Encrypt(
|
||||
int ret;
|
||||
WC_DECLARE_VAR(aead, ChaChaPoly_Aead, 1, 0);
|
||||
|
||||
/* Validate function arguments */
|
||||
/* Validate function arguments. A NULL data pointer is rejected even at
|
||||
* zero length: the direct/short paths below bypass UpdateData, whose own
|
||||
* check rejects a NULL data pointer unconditionally - this preserves that
|
||||
* contract. A valid pointer with zero length still succeeds (empty AEAD
|
||||
* message). */
|
||||
if (!inKey || !inIV ||
|
||||
(inPlaintextLen > 0 && inPlaintext == NULL) ||
|
||||
inPlaintext == NULL ||
|
||||
(inAADLen > 0 && inAAD == NULL) ||
|
||||
!outCiphertext ||
|
||||
!outAuthTag)
|
||||
{
|
||||
@@ -65,19 +648,61 @@ int wc_ChaCha20Poly1305_Encrypt(
|
||||
WC_ALLOC_VAR_EX(aead, ChaChaPoly_Aead, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER,
|
||||
return MEMORY_E);
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_SHORT
|
||||
/* Small record: derive the Poly1305 key and the keystream in a SINGLE
|
||||
* ChaCha pass (SSSE3), then scalar-hash - the same short path Encrypt_ex
|
||||
* uses. Avoids wc_ChaCha20Poly1305_Init's separate scalar poly-key block
|
||||
* and the second scalar data block the two-pass fallback would run. */
|
||||
if (inPlaintextLen <= CHACHA20_POLY1305_SHORT_MAX) {
|
||||
ret = wc_Chacha_SetKey(&aead->chacha, inKey,
|
||||
CHACHA20_POLY1305_AEAD_KEYSIZE);
|
||||
if (ret == 0)
|
||||
ret = chacha20_poly1305_encrypt_short(&aead->chacha, &aead->poly,
|
||||
outCiphertext, inPlaintext, inPlaintextLen, inIV, outAuthTag,
|
||||
inAAD, inAADLen);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ret = wc_ChaCha20Poly1305_Init(aead, inKey, inIV,
|
||||
CHACHA20_POLY1305_AEAD_ENCRYPT);
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_UpdateAad(aead, inAAD, inAADLen);
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_UpdateData(aead, inPlaintext, outCiphertext,
|
||||
inPlaintextLen);
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_Final(aead, outAuthTag);
|
||||
/* Prefer the IFMA stitch - full 512-bit ChaCha, beats the others where
|
||||
* AVX-512 + IFMA exist. */
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
if (ret == 0 && inPlaintextLen >= CHACHA20_POLY1305_STITCH_MIN &&
|
||||
chacha20_poly1305_use_fused_ifma()) {
|
||||
ret = chacha20_poly1305_encrypt_fused_ifma(aead, inAAD, inAADLen,
|
||||
inPlaintext, inPlaintextLen, outCiphertext, outAuthTag);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED
|
||||
if (ret == 0 && inAADLen == 0 && inPlaintextLen >= 256 &&
|
||||
chacha20_poly1305_use_fused()) {
|
||||
ret = chacha20_poly1305_encrypt_fused(aead, inPlaintext,
|
||||
inPlaintextLen, outCiphertext, outAuthTag);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Direct two-pass on the contexts Init already keyed (ChaCha counter is
|
||||
* at 1, Poly1305 keyed). Faster than the UpdateAad/UpdateData/Final
|
||||
* state machine for the common non-stitched case - in particular
|
||||
* wc_Poly1305_MAC hashes the AAD inline instead of buffering it through
|
||||
* UpdateAad, which is where the small-message-with-AAD cost was. */
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(&aead->chacha, outCiphertext, inPlaintext,
|
||||
inPlaintextLen);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305_MAC(&aead->poly, inAAD, inAADLen, outCiphertext,
|
||||
inPlaintextLen, outAuthTag,
|
||||
CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE);
|
||||
}
|
||||
}
|
||||
#ifdef WOLFSSL_SMALL_STACK
|
||||
if (aead != NULL)
|
||||
#endif
|
||||
ForceZero(aead, sizeof(ChaChaPoly_Aead));
|
||||
chacha20_poly1305_aead_zero(aead);
|
||||
WC_FREE_VAR_EX(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
return ret;
|
||||
@@ -96,9 +721,14 @@ int wc_ChaCha20Poly1305_Decrypt(
|
||||
WC_DECLARE_VAR(aead, ChaChaPoly_Aead, 1, 0);
|
||||
byte calculatedAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
||||
|
||||
/* Validate function arguments */
|
||||
/* Validate function arguments. A NULL data pointer is rejected even at
|
||||
* zero length: the direct/short paths below bypass UpdateData, whose own
|
||||
* check rejects a NULL data pointer unconditionally - this preserves that
|
||||
* contract. A valid pointer with zero length still succeeds (empty AEAD
|
||||
* message). */
|
||||
if (!inKey || !inIV ||
|
||||
(inCiphertextLen > 0 && inCiphertext == NULL) ||
|
||||
inCiphertext == NULL ||
|
||||
(inAADLen > 0 && inAAD == NULL) ||
|
||||
!inAuthTag ||
|
||||
!outPlaintext)
|
||||
{
|
||||
@@ -110,17 +740,38 @@ int wc_ChaCha20Poly1305_Decrypt(
|
||||
|
||||
XMEMSET(calculatedAuthTag, 0, sizeof(calculatedAuthTag));
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_SHORT
|
||||
/* Small record: single ChaCha pass for poly key + keystream, MAC the
|
||||
* ciphertext and verify BEFORE decrypting (no plaintext on bad tag). Same
|
||||
* short path Decrypt_ex uses; avoids Init's extra scalar poly-key block. */
|
||||
if (inCiphertextLen <= CHACHA20_POLY1305_SHORT_MAX) {
|
||||
ret = wc_Chacha_SetKey(&aead->chacha, inKey,
|
||||
CHACHA20_POLY1305_AEAD_KEYSIZE);
|
||||
if (ret == 0)
|
||||
ret = chacha20_poly1305_decrypt_short(&aead->chacha, &aead->poly,
|
||||
outPlaintext, inCiphertext, inCiphertextLen, inIV, inAuthTag,
|
||||
inAAD, inAADLen);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ret = wc_ChaCha20Poly1305_Init(aead, inKey, inIV,
|
||||
CHACHA20_POLY1305_AEAD_DECRYPT);
|
||||
/* Direct two-pass on the contexts Init already keyed: MAC the ciphertext,
|
||||
* verify the tag, then decrypt - verify-then-decrypt, so no plaintext is
|
||||
* produced on a bad tag. Faster than the UpdateAad/UpdateData/Final state
|
||||
* machine (wc_Poly1305_MAC hashes the AAD inline). In-place safe: the MAC
|
||||
* reads inCiphertext before the decrypt overwrites it. */
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_UpdateAad(aead, inAAD, inAADLen);
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_UpdateData(aead, inCiphertext, outPlaintext,
|
||||
inCiphertextLen);
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_Final(aead, calculatedAuthTag);
|
||||
ret = wc_Poly1305_MAC(&aead->poly, inAAD, inAADLen, inCiphertext,
|
||||
inCiphertextLen, calculatedAuthTag,
|
||||
CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE);
|
||||
if (ret == 0)
|
||||
ret = wc_ChaCha20Poly1305_CheckTag(inAuthTag, calculatedAuthTag);
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(&aead->chacha, outPlaintext, inCiphertext,
|
||||
inCiphertextLen);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
/* zero plaintext on error */
|
||||
@@ -129,7 +780,7 @@ int wc_ChaCha20Poly1305_Decrypt(
|
||||
#ifdef WOLFSSL_SMALL_STACK
|
||||
if (aead != NULL)
|
||||
#endif
|
||||
ForceZero(aead, sizeof(ChaChaPoly_Aead));
|
||||
chacha20_poly1305_aead_zero(aead);
|
||||
WC_FREE_VAR_EX(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
return ret;
|
||||
@@ -175,7 +826,8 @@ int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead,
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* setup aead context */
|
||||
/* setup aead context (full clear: the aadLen/dataLen/state wrapper fields
|
||||
* live after the Poly1305 member and must be initialized) */
|
||||
XMEMSET(aead, 0, sizeof(ChaChaPoly_Aead));
|
||||
XMEMSET(authKey, 0, sizeof(authKey));
|
||||
aead->isEncrypt = isEncrypt ? 1 : 0;
|
||||
@@ -262,8 +914,33 @@ int wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead* aead,
|
||||
if (dataLen > CHACHA20_POLY1305_MAX - aead->dataLen)
|
||||
return CHACHA_POLY_OVERFLOW;
|
||||
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
/* Enter scalar-stitch mode at the first data chunk when it is large enough
|
||||
* to benefit and no vector Poly1305 state exists yet (started==0): any AAD
|
||||
* so far is then fully buffered by the vector path. The vector buffer can
|
||||
* hold >16 bytes, which the scalar path cannot resume, so re-hash the
|
||||
* buffered AAD cleanly through the scalar path; the pad below finishes it.
|
||||
* If the AAD was large enough to be processed by the vector path
|
||||
* (started==1) we cannot switch, so it stays two-pass - no regression.
|
||||
* The IFMA stitch handles both directions (decrypt hashes the ciphertext
|
||||
* input). */
|
||||
if (!aead->poly.forceScalar && aead->poly.started == 0 &&
|
||||
aead->dataLen == 0 && dataLen >= CHACHA20_POLY1305_STITCH_MIN &&
|
||||
chacha20_poly1305_use_fused_ifma()) {
|
||||
word32 aadN = (word32)aead->poly.leftover;
|
||||
byte aadBuf[8 * POLY1305_BLOCK_SIZE];
|
||||
if (aadN > 0)
|
||||
XMEMCPY(aadBuf, aead->poly.buffer, aadN);
|
||||
aead->poly.forceScalar = 1;
|
||||
aead->poly.finished = 1;
|
||||
aead->poly.leftover = 0;
|
||||
if (aadN > 0)
|
||||
ret = wc_Poly1305Update(&aead->poly, aadBuf, aadN);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Pad the AAD */
|
||||
if (aead->state == CHACHA20_POLY1305_STATE_AAD) {
|
||||
if (ret == 0 && aead->state == CHACHA20_POLY1305_STATE_AAD) {
|
||||
ret = wc_Poly1305_Pad(&aead->poly, aead->aadLen);
|
||||
}
|
||||
|
||||
@@ -273,14 +950,58 @@ int wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead* aead,
|
||||
/* Perform ChaCha20 encrypt/decrypt and Poly1305 auth calc */
|
||||
if (ret == 0) {
|
||||
if (aead->isEncrypt) {
|
||||
ret = wc_Chacha_Process(&aead->chacha, outData, inData, dataLen);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305Update(&aead->poly, outData, dataLen);
|
||||
const byte* in = inData;
|
||||
byte* out = outData;
|
||||
word32 len = dataLen;
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
/* Stitch the 1024-aligned bulk (encrypt + auth in one pass) when
|
||||
* the data so far is 64-byte aligned - so BOTH ChaCha (no buffered
|
||||
* partial keystream) and Poly1305 (leftover==0, running hash in
|
||||
* ctx->h) are at a block boundary - and we are in scalar-hash mode
|
||||
* (set at Init for AVX-512+IFMA encrypt). The kernel processes
|
||||
* whole blocks from the ChaCha counter, so a mid-block position
|
||||
* would make it skip the buffered keystream; the 64-alignment check
|
||||
* prevents that. The remainder falls through to the path below. */
|
||||
if (aead->poly.forceScalar && (aead->dataLen & 63) == 0 &&
|
||||
len >= CHACHA20_POLY1305_STITCH_MIN) {
|
||||
word32 bulk = len & ~(word32)0x3ff;
|
||||
ret = chacha20_poly1305_stitch_chunk(&aead->chacha, &aead->poly,
|
||||
in, out, bulk, 0);
|
||||
in += bulk;
|
||||
out += bulk;
|
||||
len -= bulk;
|
||||
}
|
||||
#endif
|
||||
if (ret == 0 && len > 0) {
|
||||
ret = wc_Chacha_Process(&aead->chacha, out, in, len);
|
||||
if (ret == 0)
|
||||
ret = wc_Poly1305Update(&aead->poly, out, len);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = wc_Poly1305Update(&aead->poly, inData, dataLen);
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(&aead->chacha, outData, inData, dataLen);
|
||||
const byte* in = inData;
|
||||
byte* out = outData;
|
||||
word32 len = dataLen;
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
/* Stitch the 1024-aligned bulk (auth + decrypt in one pass) under
|
||||
* the same conditions as encrypt. The kernel hashes the ciphertext
|
||||
* (in) before overwriting it, so in-place decrypt is safe. */
|
||||
if (aead->poly.forceScalar && (aead->dataLen & 63) == 0 &&
|
||||
len >= CHACHA20_POLY1305_STITCH_MIN) {
|
||||
word32 bulk = len & ~(word32)0x3ff;
|
||||
ret = chacha20_poly1305_stitch_chunk(&aead->chacha, &aead->poly,
|
||||
in, out, bulk, 1);
|
||||
in += bulk;
|
||||
out += bulk;
|
||||
len -= bulk;
|
||||
}
|
||||
#endif
|
||||
if (ret == 0 && len > 0) {
|
||||
/* hash the ciphertext before decrypt overwrites it */
|
||||
ret = wc_Poly1305Update(&aead->poly, in, len);
|
||||
if (ret == 0)
|
||||
ret = wc_Chacha_Process(&aead->chacha, out, in, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
|
||||
+9719
-144
File diff suppressed because it is too large
Load Diff
+9712
-144
File diff suppressed because it is too large
Load Diff
@@ -54,9 +54,14 @@
|
||||
new_cpuid_flags |= CPUID_ADX;
|
||||
new_cpuid_flags |= CPUID_MOVBE;
|
||||
new_cpuid_flags |= CPUID_BMI1;
|
||||
new_cpuid_flags |= CPUID_SSSE3;
|
||||
#ifdef WOLFSSL_SGX_CPUID_AVX512_VAES
|
||||
new_cpuid_flags |= CPUID_VAES;
|
||||
new_cpuid_flags |= CPUID_AVX512 | CPUID_AVX512_BW;
|
||||
new_cpuid_flags |= CPUID_AVX512_VL;
|
||||
new_cpuid_flags |= CPUID_AVX512_IFMA;
|
||||
/* SGX is an Intel-only technology. */
|
||||
new_cpuid_flags |= CPUID_INTEL;
|
||||
#endif
|
||||
|
||||
(void)wolfSSL_Atomic_Uint_CompareExchange
|
||||
@@ -166,6 +171,7 @@
|
||||
}
|
||||
if (cpuid_is_intel()) { new_cpuid_flags |= CPUID_INTEL ; }
|
||||
if (cpuid_is_amd()) { new_cpuid_flags |= CPUID_AMD ; }
|
||||
if (cpuid_flag(1, 0, ECX, 9)) { new_cpuid_flags |= CPUID_SSSE3 ; }
|
||||
(void)wolfSSL_Atomic_Uint_CompareExchange
|
||||
(&cpuid_flags, &old_cpuid_flags, new_cpuid_flags);
|
||||
}
|
||||
|
||||
+213
-2
@@ -88,10 +88,80 @@ and Daniel J. Bernstein
|
||||
#ifndef NO_AVX2_SUPPORT
|
||||
#define HAVE_INTEL_AVX2
|
||||
#endif
|
||||
/* 8-way path; the struct carries r^5..r^8 only when this is on. */
|
||||
#if defined(WOLFSSL_POLY1305_AVX512) && !defined(NO_AVX512_SUPPORT)
|
||||
#define HAVE_INTEL_AVX512
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_INTEL_POLY1305_SPEEDUP
|
||||
static cpuid_flags_t intel_flags = WC_CPUID_INITIALIZER;
|
||||
|
||||
/* The fused ChaCha20-Poly1305 kernel drives the 4-way path; when it is active
|
||||
* this flag pins Update/Final to the 4-way path so the state layout matches. */
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED
|
||||
#define POLY1305_FORCE_AVX2(ctx) ((ctx)->forceAvx2 != 0)
|
||||
#else
|
||||
#define POLY1305_FORCE_AVX2(ctx) 0
|
||||
#endif
|
||||
|
||||
/* The fused IFMA stitch drives the scalar path; when it is active this flag
|
||||
* pins Update/Final to the scalar (poly1305_blocks_avx / _final_avx) path so
|
||||
* the state layout matches. */
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
#define POLY1305_FORCE_SCALAR(ctx) ((ctx)->forceScalar != 0)
|
||||
#else
|
||||
#define POLY1305_FORCE_SCALAR(ctx) 0
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INTEL_AVX512
|
||||
/* Whether to use the 8-way (512-bit) Poly1305 over the 4-way AVX2 path.
|
||||
*
|
||||
* The zmm path does eight 16-byte blocks per iteration and is the fastest
|
||||
* option where 512-bit code runs at full clock: AMD Zen 4/5 (no AVX-512
|
||||
* license) and Intel Ice Lake and later. On Intel Skylake-SP / Cascade
|
||||
* Lake-class parts, sustained 512-bit instructions trip the AVX-512 frequency
|
||||
* license and downclock the core; the penalty is milder here than for the
|
||||
* FP-heavy ChaCha, but the 256-bit AVX2 path can still be the safer default on
|
||||
* those parts. As with the ChaCha gate, VAES presence is used as a
|
||||
* generational proxy: the throttling parts predate VAES, whereas every
|
||||
* microarchitecture that runs 512-bit without penalty implements it. A missing
|
||||
* VAES only costs a little throughput (fall back to AVX2), never correctness.
|
||||
*
|
||||
* Override the heuristic with:
|
||||
* WOLFSSL_POLY1305_AVX512_ALWAYS - use zmm whenever AVX-512 is present
|
||||
* WOLFSSL_POLY1305_AVX512_NEVER - never use zmm (always AVX2 or below)
|
||||
*/
|
||||
static WC_INLINE int poly1305_use_avx512(cpuid_flags_t flags)
|
||||
{
|
||||
#if defined(WOLFSSL_POLY1305_AVX512_NEVER)
|
||||
(void)flags;
|
||||
return 0;
|
||||
#elif defined(WOLFSSL_POLY1305_AVX512_ALWAYS)
|
||||
return IS_INTEL_AVX512(flags) != 0;
|
||||
#else
|
||||
return (IS_INTEL_AVX512(flags) != 0) && (IS_INTEL_VAES(flags) != 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* The radix-2^44 IFMA path is preferred on Intel cores with AVX-512 IFMA
|
||||
* (Ice Lake and later), where it beats the 26-bit vpmuludq 8-way. On AMD
|
||||
* Zen 4/5 - which also implement IFMA - the vpmuludq 8-way is measurably faster
|
||||
* (stronger vector-integer throughput), so IFMA is gated to an Intel vendor.
|
||||
* Define WOLFSSL_POLY1305_IFMA_ALWAYS to use it on any IFMA CPU, or
|
||||
* WOLFSSL_POLY1305_NO_IFMA to disable it. */
|
||||
static WC_INLINE int poly1305_use_ifma(cpuid_flags_t flags)
|
||||
{
|
||||
#if defined(WOLFSSL_POLY1305_NO_IFMA)
|
||||
(void)flags;
|
||||
return 0;
|
||||
#elif defined(WOLFSSL_POLY1305_IFMA_ALWAYS)
|
||||
return IS_INTEL_AVX512_IFMA(flags) != 0;
|
||||
#else
|
||||
return (IS_INTEL_AVX512_IFMA(flags) != 0) && (IS_CPU_INTEL(flags) != 0);
|
||||
#endif
|
||||
}
|
||||
#endif /* HAVE_INTEL_AVX512 */
|
||||
#endif
|
||||
|
||||
#if defined(USE_INTEL_POLY1305_SPEEDUP) || defined(POLY130564)
|
||||
@@ -204,6 +274,24 @@ WOLFSSL_LOCAL void poly1305_setkey_avx2(Poly1305* ctx, const byte* key);
|
||||
WOLFSSL_LOCAL void poly1305_final_avx2(Poly1305* ctx, byte* mac);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INTEL_AVX512
|
||||
/* Process multiple 128-byte (8 block) groups of data eight lanes wide. */
|
||||
WOLFSSL_LOCAL void poly1305_blocks_avx512(Poly1305* ctx,
|
||||
const unsigned char* m, size_t bytes);
|
||||
/* Calculate R^1 .. R^8 and store them in the context. */
|
||||
WOLFSSL_LOCAL void poly1305_calc_powers_avx512(Poly1305* ctx);
|
||||
/* Calculate the final result - authentication data. Collapses the eight
|
||||
* lanes and finishes any leftover with the AVX final function. */
|
||||
WOLFSSL_LOCAL void poly1305_final_avx512(Poly1305* ctx, byte* mac);
|
||||
|
||||
/* AVX-512 IFMA (radix 2^44) 8-way variants - used when the CPU has IFMA. */
|
||||
WOLFSSL_LOCAL void poly1305_blocks_avx512ifma(Poly1305* ctx,
|
||||
const unsigned char* m, size_t bytes);
|
||||
WOLFSSL_LOCAL void poly1305_calc_powers_avx512ifma(Poly1305* ctx);
|
||||
WOLFSSL_LOCAL void poly1305_setkey_avx512ifma(Poly1305* ctx, const byte* key);
|
||||
WOLFSSL_LOCAL void poly1305_final_avx512ifma(Poly1305* ctx, byte* mac);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
@@ -854,6 +942,11 @@ int wc_Poly1305SetKey(Poly1305* ctx, const byte* key, word32 keySz)
|
||||
#ifdef USE_INTEL_POLY1305_SPEEDUP
|
||||
cpuid_get_flags_ex(&intel_flags);
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
#ifdef HAVE_INTEL_AVX512
|
||||
if (poly1305_use_ifma(intel_flags))
|
||||
poly1305_setkey_avx512ifma(ctx, key);
|
||||
else
|
||||
#endif
|
||||
#ifdef HAVE_INTEL_AVX2
|
||||
if (IS_INTEL_AVX2(intel_flags))
|
||||
poly1305_setkey_avx2(ctx, key);
|
||||
@@ -862,6 +955,12 @@ int wc_Poly1305SetKey(Poly1305* ctx, const byte* key, word32 keySz)
|
||||
poly1305_setkey_avx(ctx, key);
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
ctx->started = 0;
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED
|
||||
ctx->forceAvx2 = 0;
|
||||
#endif
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
ctx->forceScalar = 0;
|
||||
#endif
|
||||
#elif defined(WOLFSSL_ARMASM)
|
||||
#ifdef __aarch64__
|
||||
poly1305_setkey_aarch64(ctx, key);
|
||||
@@ -885,8 +984,18 @@ int wc_Poly1305Final(Poly1305* ctx, byte* mac)
|
||||
|
||||
#ifdef USE_INTEL_POLY1305_SPEEDUP
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
#ifdef HAVE_INTEL_AVX512
|
||||
if (!POLY1305_FORCE_AVX2(ctx) && !POLY1305_FORCE_SCALAR(ctx) &&
|
||||
poly1305_use_ifma(intel_flags))
|
||||
poly1305_final_avx512ifma(ctx, mac);
|
||||
else
|
||||
if (!POLY1305_FORCE_AVX2(ctx) && !POLY1305_FORCE_SCALAR(ctx) &&
|
||||
poly1305_use_avx512(intel_flags))
|
||||
poly1305_final_avx512(ctx, mac);
|
||||
else
|
||||
#endif
|
||||
#ifdef HAVE_INTEL_AVX2
|
||||
if (IS_INTEL_AVX2(intel_flags))
|
||||
if (!POLY1305_FORCE_SCALAR(ctx) && IS_INTEL_AVX2(intel_flags))
|
||||
poly1305_final_avx2(ctx, mac);
|
||||
else
|
||||
#endif
|
||||
@@ -1008,8 +1117,110 @@ int wc_Poly1305Update(Poly1305* ctx, const byte* m, word32 bytes)
|
||||
}
|
||||
#else
|
||||
#ifdef USE_INTEL_POLY1305_SPEEDUP
|
||||
#ifdef HAVE_INTEL_AVX512
|
||||
if (!POLY1305_FORCE_AVX2(ctx) && !POLY1305_FORCE_SCALAR(ctx) &&
|
||||
poly1305_use_ifma(intel_flags)) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
|
||||
/* handle leftover */
|
||||
if (ctx->leftover) {
|
||||
size_t want = sizeof(ctx->buffer) - ctx->leftover;
|
||||
if (want > bytes)
|
||||
want = bytes;
|
||||
|
||||
for (i = 0; i < want; i++)
|
||||
ctx->buffer[ctx->leftover + i] = m[i];
|
||||
bytes -= (word32)want;
|
||||
m += want;
|
||||
ctx->leftover += want;
|
||||
if (ctx->leftover < sizeof(ctx->buffer)) {
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ctx->started) {
|
||||
poly1305_calc_powers_avx512ifma(ctx);
|
||||
ctx->started = 1;
|
||||
}
|
||||
poly1305_blocks_avx512ifma(ctx, ctx->buffer, sizeof(ctx->buffer));
|
||||
ctx->leftover = 0;
|
||||
}
|
||||
|
||||
/* process full blocks */
|
||||
if (bytes >= sizeof(ctx->buffer)) {
|
||||
size_t want = bytes & ~(sizeof(ctx->buffer) - 1);
|
||||
|
||||
if (!ctx->started) {
|
||||
poly1305_calc_powers_avx512ifma(ctx);
|
||||
ctx->started = 1;
|
||||
}
|
||||
poly1305_blocks_avx512ifma(ctx, m, want);
|
||||
m += want;
|
||||
bytes -= (word32)want;
|
||||
}
|
||||
|
||||
/* store leftover */
|
||||
if (bytes) {
|
||||
for (i = 0; i < bytes; i++)
|
||||
ctx->buffer[ctx->leftover + i] = m[i];
|
||||
ctx->leftover += bytes;
|
||||
}
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
}
|
||||
else
|
||||
if (!POLY1305_FORCE_AVX2(ctx) && !POLY1305_FORCE_SCALAR(ctx) &&
|
||||
poly1305_use_avx512(intel_flags)) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
|
||||
/* handle leftover */
|
||||
if (ctx->leftover) {
|
||||
size_t want = sizeof(ctx->buffer) - ctx->leftover;
|
||||
if (want > bytes)
|
||||
want = bytes;
|
||||
|
||||
for (i = 0; i < want; i++)
|
||||
ctx->buffer[ctx->leftover + i] = m[i];
|
||||
bytes -= (word32)want;
|
||||
m += want;
|
||||
ctx->leftover += want;
|
||||
if (ctx->leftover < sizeof(ctx->buffer)) {
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ctx->started) {
|
||||
poly1305_calc_powers_avx512(ctx);
|
||||
ctx->started = 1;
|
||||
}
|
||||
poly1305_blocks_avx512(ctx, ctx->buffer, sizeof(ctx->buffer));
|
||||
ctx->leftover = 0;
|
||||
}
|
||||
|
||||
/* process full blocks */
|
||||
if (bytes >= sizeof(ctx->buffer)) {
|
||||
size_t want = bytes & ~(sizeof(ctx->buffer) - 1);
|
||||
|
||||
if (!ctx->started) {
|
||||
poly1305_calc_powers_avx512(ctx);
|
||||
ctx->started = 1;
|
||||
}
|
||||
poly1305_blocks_avx512(ctx, m, want);
|
||||
m += want;
|
||||
bytes -= (word32)want;
|
||||
}
|
||||
|
||||
/* store leftover */
|
||||
if (bytes) {
|
||||
for (i = 0; i < bytes; i++)
|
||||
ctx->buffer[ctx->leftover + i] = m[i];
|
||||
ctx->leftover += bytes;
|
||||
}
|
||||
RESTORE_VECTOR_REGISTERS();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef HAVE_INTEL_AVX2
|
||||
if (IS_INTEL_AVX2(intel_flags)) {
|
||||
if (!POLY1305_FORCE_SCALAR(ctx) && IS_INTEL_AVX2(intel_flags)) {
|
||||
SAVE_VECTOR_REGISTERS(return _svr_ret;);
|
||||
|
||||
/* handle leftover */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+2370
-4
File diff suppressed because it is too large
Load Diff
@@ -104,6 +104,20 @@ int wc_ChaCha20Poly1305_Decrypt(
|
||||
const byte inAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],
|
||||
byte* outPlaintext);
|
||||
|
||||
/* Encrypt+authenticate one message with pre-keyed ChaCha20 and Poly1305
|
||||
* contexts (analogue of wc_AesGcmEncrypt on a keyed Aes) - keeps the ChaCha key
|
||||
* across records and only varies the nonce, and uses the single-pass stitch.
|
||||
* Intended for the TLS record layer. */
|
||||
WOLFSSL_API int wc_ChaCha20Poly1305_Encrypt_ex(ChaCha* chacha, Poly1305* poly,
|
||||
byte* out, const byte* in, word32 sz, const byte* nonce, byte* tag,
|
||||
const byte* aad, word32 aadSz);
|
||||
/* Decrypt+verify counterpart of wc_ChaCha20Poly1305_Encrypt_ex. On tag
|
||||
* mismatch returns MAC_CMP_FAILED_E and zeroizes out (no plaintext released);
|
||||
* out may alias in (in-place). */
|
||||
WOLFSSL_API WARN_UNUSED_RESULT int wc_ChaCha20Poly1305_Decrypt_ex(
|
||||
ChaCha* chacha, Poly1305* poly, byte* out, const byte* in, word32 sz,
|
||||
const byte* nonce, const byte* tag, const byte* aad, word32 aadSz);
|
||||
|
||||
WOLFSSL_API WARN_UNUSED_RESULT
|
||||
int wc_ChaCha20Poly1305_CheckTag(
|
||||
const byte authTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],
|
||||
|
||||
@@ -105,6 +105,7 @@ typedef word32 cpuid_flags_t;
|
||||
/* AVX-512 Byte and Word: byte/word instructions at 512-bit width
|
||||
* (vpshufb, vpaddw, vpmulhw, vpackusdw, ... on zmm). */
|
||||
#define CPUID_AVX512_BW 0x80000
|
||||
#define CPUID_SSSE3 0x100000 /* SSSE3 (pshufb etc.) */
|
||||
|
||||
#define IS_INTEL_AVX1(f) (WOLFSSL_ATOMIC_COERCE_UINT(f) & CPUID_AVX1)
|
||||
#define IS_INTEL_AVX2(f) (WOLFSSL_ATOMIC_COERCE_UINT(f) & CPUID_AVX2)
|
||||
@@ -139,6 +140,7 @@ typedef word32 cpuid_flags_t;
|
||||
(IS_INTEL_AVX512(f) && IS_INTEL_AVX512_BW(f))
|
||||
#define IS_CPU_INTEL(f) (WOLFSSL_ATOMIC_COERCE_UINT(f) & CPUID_INTEL)
|
||||
#define IS_CPU_AMD(f) (WOLFSSL_ATOMIC_COERCE_UINT(f) & CPUID_AMD)
|
||||
#define IS_INTEL_SSSE3(f) (WOLFSSL_ATOMIC_COERCE_UINT(f) & CPUID_SSSE3)
|
||||
|
||||
#elif defined(HAVE_CPUID_AARCH64)
|
||||
|
||||
|
||||
@@ -52,6 +52,34 @@
|
||||
#if defined(USE_INTEL_SPEEDUP) && !defined(NO_POLY1305_ASM)
|
||||
#define USE_INTEL_POLY1305_SPEEDUP
|
||||
#define HAVE_INTEL_AVX1
|
||||
/* 8-way AVX-512 path. Enabling it appends r^5..r^8 to the state (see the
|
||||
* struct below); define WOLFSSL_POLY1305_NO_AVX512 to keep the smaller
|
||||
* state and drop the path. Gated on NO_AVX512_SUPPORT to match the
|
||||
* HAVE_INTEL_AVX512 kernels the .S files emit (poly1305_asm.S). */
|
||||
#if !defined(WOLFSSL_POLY1305_NO_AVX512) && !defined(NO_AVX512_SUPPORT)
|
||||
#define WOLFSSL_POLY1305_AVX512
|
||||
#endif
|
||||
/* Fused single-pass ChaCha20-Poly1305 (encrypt). It drives Poly1305 in the
|
||||
* 4-way layout, so the ctx carries a flag forcing that path. The kernel is
|
||||
* an AVX-512 ChaCha (chacha20_poly1305_avx512) feeding an AVX2 4-way power
|
||||
* precompute (poly1305_calc_powers_avx2), so it needs BOTH ISA levels the
|
||||
* .S files emit - gate off if either is disabled. */
|
||||
#if !defined(WOLFSSL_NO_CHACHA20_POLY1305_FUSED) && \
|
||||
!defined(NO_AVX512_SUPPORT) && !defined(NO_AVX2_SUPPORT)
|
||||
#define WOLFSSL_CHACHA20_POLY1305_FUSED
|
||||
#endif
|
||||
/* IFMA stitched single-pass ChaCha20-Poly1305 (AVX-512 + IFMA): a full
|
||||
* 512-bit 16-block ChaCha interleaved with an 8-way IFMA (vpmadd52)
|
||||
* Poly1305 that collapses to the scalar hash. It BEATS the two-pass by
|
||||
* ~1.3-1.4x (>=16KB) - ChaCha is the bottleneck and Poly hides under it -
|
||||
* so it is ON by default and runtime-gated on the AVX-512 + IFMA flags.
|
||||
* Needs the AVX-512 IFMA state (r^1..r^8, ifma_h), so it follows
|
||||
* WOLFSSL_POLY1305_AVX512. Define WOLFSSL_NO_CHACHA20_POLY1305_FUSED_IFMA
|
||||
* to drop it; it drives Poly1305 scalar via the forceScalar flag. */
|
||||
#if defined(WOLFSSL_POLY1305_AVX512) && \
|
||||
!defined(WOLFSSL_NO_CHACHA20_POLY1305_FUSED_IFMA)
|
||||
#define WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -100,6 +128,31 @@ typedef struct Poly1305 {
|
||||
size_t leftover;
|
||||
unsigned char finished;
|
||||
unsigned char started;
|
||||
#ifdef WOLFSSL_POLY1305_AVX512
|
||||
/* r^5..r^8 for the 8-way path, appended so the AVX1/AVX2 field offsets are
|
||||
* unchanged. ALIGN8 keeps each power 8-byte aligned (26-bit limb packing
|
||||
* matches r1..r4). */
|
||||
ALIGN8 word32 r5[8];
|
||||
word32 r6[8];
|
||||
word32 r7[8];
|
||||
word32 r8[8];
|
||||
/* IFMA path (radix 2^44) keeps r^1..r^8 in the r1..r8 fields above (three
|
||||
* 44-bit limbs each) and its eight-lane running hash here: three limbs x
|
||||
* eight lanes x 64-bit. hh (32-bit packed) is too small for 44-bit lanes.
|
||||
*/
|
||||
ALIGN8 word64 ifma_h[24];
|
||||
#endif
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED
|
||||
/* When set, wc_Poly1305Update/Final use the 4-way path so the fused
|
||||
* ChaCha20-Poly1305 kernel (also 4-way) stays layout-consistent. */
|
||||
unsigned char forceAvx2;
|
||||
#endif
|
||||
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
|
||||
/* When set, wc_Poly1305Update/Final use the scalar path so the fused IFMA
|
||||
* ChaCha20-Poly1305 stitch (which finishes in the scalar hash) stays
|
||||
* layout-consistent. */
|
||||
unsigned char forceScalar;
|
||||
#endif
|
||||
#elif defined(WOLFSSL_ARMASM) && defined(__aarch64__)
|
||||
ALIGN8 word64 r64[2];
|
||||
ALIGN8 word32 r4[4];
|
||||
|
||||
Reference in New Issue
Block a user