Merge pull request #9658 from sameehj/aes-offload

cryptocb: add AES CryptoCB key import support and tests
This commit is contained in:
Daniel Pouzzner
2026-02-03 12:23:42 -06:00
committed by GitHub
10 changed files with 987 additions and 27 deletions
+3
View File
@@ -70,6 +70,9 @@ jobs:
'--enable-all --enable-certgencache',
'--enable-sessionexport --enable-dtls --enable-dtls13',
'--enable-sessionexport',
'--enable-cryptocb --enable-aesgcm CPPFLAGS="-DWOLF_CRYPTO_CB_AES_SETKEY -DWOLF_CRYPTO_CB_FREE"',
'--disable-tls --enable-cryptocb --enable-aesgcm CPPFLAGS="-DWOLF_CRYPTO_CB_AES_SETKEY -DWOLF_CRYPTO_CB_FREE"',
'--enable-cryptocb --enable-aesgcm CPPFLAGS="-DWOLF_CRYPTO_CB_AES_SETKEY"',
'--disable-examples CPPFLAGS=-DWOLFSSL_NO_MALLOC',
'CPPFLAGS=-DNO_WOLFSSL_CLIENT',
'CPPFLAGS=-DNO_WOLFSSL_SERVER',
+23
View File
@@ -57,6 +57,29 @@ suites are available. You can remove this error by defining
`WOLFSSL_ALLOW_NO_SUITES` in the event that you desire that, i.e., you're
not using TLS cipher suites.
### AES CryptoCB Key Import Support
wolfSSL supports hardware-accelerated AES operations via CryptoCB.
When `WOLF_CRYPTO_CB_AES_SETKEY` is defined, wolfSSL invokes a CryptoCB
callback during AES key setup. The callback behavior determines the mode:
**If callback returns 0 (success):**
- Key is imported to Secure Element/HSM
- Key is NOT copied to wolfSSL RAM (true key isolation)
- GCM tables are NOT generated (full hardware offload)
- All subsequent AES operations route through CryptoCB
**If callback returns CRYPTOCB_UNAVAILABLE:**
- SE doesn't support key import
- Normal software AES path is used
- Key is copied to devKey for CryptoCB encrypt/decrypt acceleration
This feature enables TLS 1.3 traffic key protection on embedded platforms
where symmetric keys must never exist in main RAM.
Enable with: `CPPFLAGS="-DWOLF_CRYPTO_CB_AES_SETKEY -DWOLF_CRYPTO_CB_FREE"`
### Note 2
wolfSSL takes a different approach to certificate verification than OpenSSL
does. The default policy for the client is to verify the server, this means
+60
View File
@@ -180,3 +180,63 @@ void wc_CryptoCb_SetDeviceFindCb(CryptoDevCallbackFind cb);
\sa wc_CryptoCb_RegisterDevice
*/
void wc_CryptoCb_InfoString(wc_CryptoInfo* info);
/*!
\ingroup CryptoCb
\brief Import an AES key into a CryptoCB device for hardware offload.
This function allows AES keys to be handled by an external device
(e.g. Secure Element or HSM). When supported, the device callback stores
the key internally and sets an opaque handle in aes->devCtx.
When CryptoCB AES SetKey support is enabled
(WOLF_CRYPTO_CB_AES_SETKEY), wolfCrypt routes AES-GCM operations
through the CryptoCB interface.
**TLS Builds (Default):**
- Key bytes ARE stored in wolfCrypt memory (devKey) for fallback
- GCM tables ARE generated for software fallback
- Provides hardware acceleration with automatic fallback
**Crypto-Only Builds (--disable-tls):**
- Key bytes NOT stored in wolfCrypt memory (true key isolation)
- GCM tables skipped (true hardware offload)
- Callback must handle all GCM operations (SetKey, Encrypt, Decrypt, Free)
If the callback returns success (0), full AES-GCM offload is assumed.
The callback must handle SetKey, Encrypt, Decrypt, and Free operations.
\param aes AES context
\param key Pointer to raw AES key material
\param keySz Size of key in bytes
\return 0 on success
\return CRYPTOCB_UNAVAILABLE if device does not support this operation
\return BAD_FUNC_ARG on invalid parameters
_Example_
\code
#include <wolfssl/wolfcrypt/cryptocb.h>
#include <wolfssl/wolfcrypt/aes.h>
Aes aes;
byte key[32] = { /* 256-bit key */ };
int devId = 1;
/* Register your CryptoCB callback first */
wc_CryptoCb_RegisterDevice(devId, myCryptoCallback, NULL);
wc_AesInit(&aes, NULL, devId);
/* wc_AesGcmSetKey internally calls wc_CryptoCb_AesSetKey */
if (wc_CryptoCb_AesSetKey(&aes, key, sizeof(key)) == 0) {
/* Key successfully imported to device via callback */
/* aes.devCtx now contains device handle */
/* Full GCM offload is assumed - callback must handle all operations */
}
\endcode
\sa wc_CryptoCb_RegisterDevice
\sa wc_AesInit
*/
int wc_CryptoCb_AesSetKey(Aes* aes, const byte* key, word32 keySz);
@@ -74,4 +74,27 @@
- \ref SAKKE_RSK
- \ref SAKKE_Operations
*/
/*!
\page AES_CryptoCB_KeyImport AES CryptoCB Key Import
When enabled via WOLF_CRYPTO_CB_AES_SETKEY, wolfSSL invokes a CryptoCB
callback during AES key setup. The callback behavior determines the mode:
**If callback returns 0 (success):**
- Key is imported to Secure Element/HSM
- Key is NOT copied to wolfSSL RAM (true key isolation)
- GCM tables are NOT generated (full hardware offload)
- All subsequent AES operations route through CryptoCB
**If callback returns CRYPTOCB_UNAVAILABLE:**
- SE doesn't support key import
- Normal software AES path is used
- Key is copied to devKey for CryptoCB encrypt/decrypt acceleration
This mode is compatible with Secure Elements and hardware-backed
key storage and is intended for protecting TLS traffic keys.
\sa wc_CryptoCb_AesSetKey
\sa \ref Crypto Callbacks
*/
+739
View File
@@ -5222,3 +5222,742 @@ int test_wc_AesEaxDecryptAuth(void)
* (!HAVE_FIPS || FIPS_VERSION_GE(5, 3)) && !HAVE_SELFTEST
*/
/*----------------------------------------------------------------------------*
| CryptoCB AES SetKey Test
*----------------------------------------------------------------------------*/
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_AES_SETKEY) && \
!defined(NO_AES) && defined(HAVE_AESGCM)
#include <wolfssl/wolfcrypt/cryptocb.h>
#define TEST_CRYPTOCB_AES_DEVID 7
/* Test state tracking */
static int cryptoCbAesSetKeyCalled = 0;
static int cryptoCbAesFreeCalled = 0;
/* Simulated SE key storage - in real SE this would be in secure hardware */
typedef struct {
byte key[AES_256_KEY_SIZE];
word32 keySz;
int valid;
} MockSeKeySlot;
static MockSeKeySlot mockSeKey = {0};
/* Mock handle pointing to our key slot */
static void* cryptoCbAesMockHandle = (void*)&mockSeKey;
/* Test CryptoCB callback for AES key import operations
* This emulates a Secure Element by:
* - Storing the key on SetKey (simulating SE key import)
* - Using stored key for encrypt/decrypt (simulating SE crypto)
* - Clearing key on Free (simulating SE key deletion)
*/
static int test_CryptoCb_Aes_Cb(int devId, wc_CryptoInfo* info, void* ctx)
{
(void)ctx;
if (devId != TEST_CRYPTOCB_AES_DEVID)
return CRYPTOCB_UNAVAILABLE;
/* AES SetKey operation - simulate SE key import */
if (info->algo_type == WC_ALGO_TYPE_CIPHER &&
info->cipher.type == WC_CIPHER_AES &&
info->cipher.aessetkey.aes != NULL) {
Aes* aes = info->cipher.aessetkey.aes;
const byte* key = info->cipher.aessetkey.key;
word32 keySz = info->cipher.aessetkey.keySz;
/* Validate key */
if (key == NULL || keySz == 0 || keySz > AES_256_KEY_SIZE) {
return BAD_FUNC_ARG;
}
/* "Import" key to simulated SE storage */
XMEMCPY(mockSeKey.key, key, keySz);
mockSeKey.keySz = keySz;
mockSeKey.valid = 1;
/* Store handle in aes->devCtx - this is what wolfSSL will use */
aes->devCtx = cryptoCbAesMockHandle;
cryptoCbAesSetKeyCalled++;
return 0;
}
/* AES-GCM Encrypt - simulate SE encryption using stored key */
if (info->algo_type == WC_ALGO_TYPE_CIPHER &&
info->cipher.type == WC_CIPHER_AES_GCM &&
info->cipher.enc) {
Aes* aes = info->cipher.aesgcm_enc.aes;
MockSeKeySlot* slot;
Aes tempAes;
int ret;
/* Verify handle points to our key slot */
if (aes == NULL || aes->devCtx != cryptoCbAesMockHandle) {
return BAD_FUNC_ARG;
}
slot = (MockSeKeySlot*)aes->devCtx;
if (!slot->valid) {
return BAD_STATE_E;
}
/* Initialize a temporary Aes for software crypto (simulating SE internal operation) */
XMEMSET(&tempAes, 0, sizeof(tempAes));
ret = wc_AesInit(&tempAes, NULL, INVALID_DEVID); /* No CryptoCB for internal use */
if (ret != 0) return ret;
ret = wc_AesGcmSetKey(&tempAes, slot->key, slot->keySz);
if (ret != 0) {
wc_AesFree(&tempAes);
return ret;
}
/* Perform the actual encryption */
ret = wc_AesGcmEncrypt(&tempAes,
info->cipher.aesgcm_enc.out,
info->cipher.aesgcm_enc.in,
info->cipher.aesgcm_enc.sz,
info->cipher.aesgcm_enc.iv,
info->cipher.aesgcm_enc.ivSz,
info->cipher.aesgcm_enc.authTag,
info->cipher.aesgcm_enc.authTagSz,
info->cipher.aesgcm_enc.authIn,
info->cipher.aesgcm_enc.authInSz);
wc_AesFree(&tempAes);
return ret;
}
/* AES-GCM Decrypt - simulate SE decryption using stored key */
if (info->algo_type == WC_ALGO_TYPE_CIPHER &&
info->cipher.type == WC_CIPHER_AES_GCM &&
!info->cipher.enc) {
Aes* aes = info->cipher.aesgcm_dec.aes;
MockSeKeySlot* slot;
Aes tempAes;
int ret;
/* Verify handle points to our key slot */
if (aes == NULL || aes->devCtx != cryptoCbAesMockHandle) {
return BAD_FUNC_ARG;
}
slot = (MockSeKeySlot*)aes->devCtx;
if (!slot->valid) {
return BAD_STATE_E;
}
/* Initialize a temporary Aes for software crypto (simulating SE internal operation) */
XMEMSET(&tempAes, 0, sizeof(tempAes));
ret = wc_AesInit(&tempAes, NULL, INVALID_DEVID);
if (ret != 0) return ret;
ret = wc_AesGcmSetKey(&tempAes, slot->key, slot->keySz);
if (ret != 0) {
wc_AesFree(&tempAes);
return ret;
}
/* Perform the actual decryption */
ret = wc_AesGcmDecrypt(&tempAes,
info->cipher.aesgcm_dec.out,
info->cipher.aesgcm_dec.in,
info->cipher.aesgcm_dec.sz,
info->cipher.aesgcm_dec.iv,
info->cipher.aesgcm_dec.ivSz,
info->cipher.aesgcm_dec.authTag,
info->cipher.aesgcm_dec.authTagSz,
info->cipher.aesgcm_dec.authIn,
info->cipher.aesgcm_dec.authInSz);
wc_AesFree(&tempAes);
return ret;
}
#ifdef WOLF_CRYPTO_CB_FREE
/* Free operation - simulate SE key deletion */
if (info->algo_type == WC_ALGO_TYPE_FREE &&
info->free.algo == WC_ALGO_TYPE_CIPHER &&
info->free.type == WC_CIPHER_AES) {
Aes* aes = (Aes*)info->free.obj;
if (aes != NULL && aes->devCtx == cryptoCbAesMockHandle) {
/* "Delete" key from simulated SE */
ForceZero(&mockSeKey, sizeof(mockSeKey));
cryptoCbAesFreeCalled++;
}
return 0;
}
#endif
return CRYPTOCB_UNAVAILABLE;
}
/*
* Test: CryptoCB AES SetKey hook for key import / secure element support
*/
int test_wc_CryptoCb_AesSetKey(void)
{
EXPECT_DECLS;
#ifdef WOLFSSL_SMALL_STACK
Aes* aes = NULL;
byte* key = NULL;
byte* iv = NULL;
byte* plain = NULL;
byte* cipher = NULL;
byte* decrypted = NULL;
byte* authTag = NULL;
#else
Aes aes[1];
byte key[AES_128_KEY_SIZE];
byte iv[GCM_NONCE_MID_SZ];
byte plain[16];
byte cipher[16];
byte decrypted[16];
byte authTag[AES_BLOCK_SIZE];
#endif
int ret;
#ifdef WOLFSSL_SMALL_STACK
aes = (Aes*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER);
key = (byte*)XMALLOC(AES_128_KEY_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
iv = (byte*)XMALLOC(GCM_NONCE_MID_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER);
plain = (byte*)XMALLOC(16, NULL, DYNAMIC_TYPE_TMP_BUFFER);
cipher = (byte*)XMALLOC(16, NULL, DYNAMIC_TYPE_TMP_BUFFER);
decrypted = (byte*)XMALLOC(16, NULL, DYNAMIC_TYPE_TMP_BUFFER);
authTag = (byte*)XMALLOC(AES_BLOCK_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (aes == NULL || key == NULL || iv == NULL || plain == NULL ||
cipher == NULL || decrypted == NULL || authTag == NULL) {
ret = MEMORY_E;
goto out;
}
#endif
/* Initialize key, iv, plain arrays */
{
static const byte keyData[AES_128_KEY_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
static const byte plainData[16] = {
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77,
0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x21, 0x00
};
XMEMCPY(key, keyData, AES_128_KEY_SIZE);
XMEMSET(iv, 0, GCM_NONCE_MID_SZ);
XMEMCPY(plain, plainData, 16);
}
XMEMSET(aes, 0, sizeof(Aes));
XMEMSET(&mockSeKey, 0, sizeof(mockSeKey));
/* Reset test state */
cryptoCbAesSetKeyCalled = 0;
cryptoCbAesFreeCalled = 0;
/* Register test callback */
ret = wc_CryptoCb_RegisterDevice(TEST_CRYPTOCB_AES_DEVID,
test_CryptoCb_Aes_Cb, NULL);
ExpectIntEQ(ret, 0);
/* Initialize Aes with device ID */
ret = wc_AesInit(aes, NULL, TEST_CRYPTOCB_AES_DEVID);
ExpectIntEQ(ret, 0);
ExpectIntEQ(aes->devId, TEST_CRYPTOCB_AES_DEVID);
/* Set key - should trigger CryptoCB and "import" to mock SE */
ret = wc_AesGcmSetKey(aes, key, sizeof(key));
ExpectIntEQ(ret, 0);
/* Verify callback was invoked */
ExpectIntEQ(cryptoCbAesSetKeyCalled, 1);
/* Verify handle stored in devCtx */
ExpectPtrEq(aes->devCtx, cryptoCbAesMockHandle);
/* Verify key was "imported" to mock SE */
ExpectIntEQ(mockSeKey.valid, 1);
ExpectIntEQ(mockSeKey.keySz, (int)sizeof(key));
/* Verify keylen metadata stored in Aes struct */
ExpectIntEQ(aes->keylen, (int)sizeof(key));
/* After SetKey succeeds via CryptoCB, verify key NOT in devKey */
{
byte zeroKey[AES_128_KEY_SIZE] = {0};
/* Key should NOT be copied to devKey - SE owns it */
ExpectIntEQ(XMEMCMP(aes->devKey, zeroKey, sizeof(key)), 0);
}
/* Test encrypt - callback performs crypto using stored key */
ret = wc_AesGcmEncrypt(aes, cipher, plain, sizeof(plain),
iv, sizeof(iv), authTag, sizeof(authTag),
NULL, 0);
ExpectIntEQ(ret, 0);
/* Test decrypt - callback performs crypto using stored key */
ret = wc_AesGcmDecrypt(aes, decrypted, cipher, sizeof(cipher),
iv, sizeof(iv), authTag, sizeof(authTag),
NULL, 0);
ExpectIntEQ(ret, 0);
/* Verify round-trip */
ExpectIntEQ(XMEMCMP(plain, decrypted, sizeof(plain)), 0);
#ifdef WOLF_CRYPTO_CB_FREE
/* Free should trigger callback and "delete" key from mock SE */
cryptoCbAesFreeCalled = 0;
wc_AesFree(aes);
/* Verify free callback invoked */
ExpectIntEQ(cryptoCbAesFreeCalled, 1);
/* Verify devCtx cleared */
ExpectPtrEq(aes->devCtx, NULL);
/* Verify key was "deleted" from mock SE */
ExpectIntEQ(mockSeKey.valid, 0);
#else
wc_AesFree(aes);
#endif
/* Cleanup */
wc_CryptoCb_UnRegisterDevice(TEST_CRYPTOCB_AES_DEVID);
/* Test software path (no devId) still works */
XMEMSET(aes, 0, sizeof(Aes));
cryptoCbAesSetKeyCalled = 0;
ret = wc_AesInit(aes, NULL, INVALID_DEVID);
ExpectIntEQ(ret, 0);
ret = wc_AesGcmSetKey(aes, key, sizeof(key));
ExpectIntEQ(ret, 0);
/* Callback should NOT have been invoked */
ExpectIntEQ(cryptoCbAesSetKeyCalled, 0);
/* devCtx should be NULL */
ExpectPtrEq(aes->devCtx, NULL);
wc_AesFree(aes);
#ifdef WOLFSSL_SMALL_STACK
out:
XFREE(aes, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(iv, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(cipher, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(decrypted, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(authTag, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}
#endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_AES_SETKEY && !NO_AES && HAVE_AESGCM */
/*----------------------------------------------------------------------------*
| CryptoCB AES-GCM End-to-End Offload Test
*----------------------------------------------------------------------------*/
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_AES_SETKEY) && \
!defined(NO_AES) && defined(HAVE_AESGCM)
#define TEST_CRYPTOCB_AESGCM_OFFLOAD_DEVID 8
/* Test state tracking for end-to-end offload test */
static int cryptoCbAesGcmSetKeyCalled = 0;
static int cryptoCbAesGcmEncryptCalled = 0;
static int cryptoCbAesGcmDecryptCalled = 0;
static int cryptoCbAesGcmFreeCalled = 0;
/* Mock SE key storage for offload test */
typedef struct {
byte key[AES_256_KEY_SIZE];
word32 keySz;
int valid;
} MockSeKeySlotOffload;
static MockSeKeySlotOffload mockSeKeyOffload = {0};
/* Mock handle pointing to our key slot */
static void* cryptoCbAesGcmMockHandle = (void*)&mockSeKeyOffload;
/* Mock CryptoCB callback for end-to-end AES-GCM offload test
* This emulates a Secure Element that:
* - Stores the key on SetKey (simulating SE key import)
* - Performs encryption/decryption using stored key (simulating SE crypto)
* - Tracks all callback invocations to verify offload is working
* - Uses software AES internally (simulating SE internal operation)
*/
static int test_CryptoCb_AesGcm_Offload_Cb(int devId, wc_CryptoInfo* info, void* ctx)
{
(void)ctx;
if (devId != TEST_CRYPTOCB_AESGCM_OFFLOAD_DEVID)
return CRYPTOCB_UNAVAILABLE;
/* AES SetKey operation - simulate SE key import */
if (info->algo_type == WC_ALGO_TYPE_CIPHER &&
info->cipher.type == WC_CIPHER_AES &&
info->cipher.aessetkey.aes != NULL) {
Aes* aes = info->cipher.aessetkey.aes;
const byte* key = info->cipher.aessetkey.key;
word32 keySz = info->cipher.aessetkey.keySz;
/* Validate key */
if (key == NULL || keySz == 0 || keySz > AES_256_KEY_SIZE) {
return BAD_FUNC_ARG;
}
/* "Import" key to simulated SE storage */
XMEMCPY(mockSeKeyOffload.key, key, keySz);
mockSeKeyOffload.keySz = keySz;
mockSeKeyOffload.valid = 1;
/* Store handle in aes->devCtx - this is what wolfSSL will use */
aes->devCtx = cryptoCbAesGcmMockHandle;
cryptoCbAesGcmSetKeyCalled++;
return 0;
}
/* AES-GCM Encrypt - simulate SE encryption using stored key */
if (info->algo_type == WC_ALGO_TYPE_CIPHER &&
info->cipher.type == WC_CIPHER_AES_GCM &&
info->cipher.enc) {
Aes* aes = info->cipher.aesgcm_enc.aes;
MockSeKeySlotOffload* slot;
Aes tempAes;
int ret;
/* Verify handle points to our key slot */
if (aes == NULL || aes->devCtx != cryptoCbAesGcmMockHandle) {
return BAD_FUNC_ARG;
}
slot = (MockSeKeySlotOffload*)aes->devCtx;
if (!slot->valid) {
return BAD_STATE_E;
}
/* Track that encrypt callback was invoked */
cryptoCbAesGcmEncryptCalled++;
/* Initialize a temporary Aes for software crypto (simulating SE internal operation) */
XMEMSET(&tempAes, 0, sizeof(tempAes));
ret = wc_AesInit(&tempAes, NULL, INVALID_DEVID); /* No CryptoCB for internal use */
if (ret != 0) return ret;
ret = wc_AesGcmSetKey(&tempAes, slot->key, slot->keySz);
if (ret != 0) {
wc_AesFree(&tempAes);
return ret;
}
/* Perform the actual encryption using software AES (simulating SE internal operation) */
ret = wc_AesGcmEncrypt(&tempAes,
info->cipher.aesgcm_enc.out,
info->cipher.aesgcm_enc.in,
info->cipher.aesgcm_enc.sz,
info->cipher.aesgcm_enc.iv,
info->cipher.aesgcm_enc.ivSz,
info->cipher.aesgcm_enc.authTag,
info->cipher.aesgcm_enc.authTagSz,
info->cipher.aesgcm_enc.authIn,
info->cipher.aesgcm_enc.authInSz);
wc_AesFree(&tempAes);
return ret;
}
/* AES-GCM Decrypt - simulate SE decryption using stored key */
if (info->algo_type == WC_ALGO_TYPE_CIPHER &&
info->cipher.type == WC_CIPHER_AES_GCM &&
!info->cipher.enc) {
Aes* aes = info->cipher.aesgcm_dec.aes;
MockSeKeySlotOffload* slot;
Aes tempAes;
int ret;
/* Verify handle points to our key slot */
if (aes == NULL || aes->devCtx != cryptoCbAesGcmMockHandle) {
return BAD_FUNC_ARG;
}
slot = (MockSeKeySlotOffload*)aes->devCtx;
if (!slot->valid) {
return BAD_STATE_E;
}
/* Track that decrypt callback was invoked */
cryptoCbAesGcmDecryptCalled++;
/* Initialize a temporary Aes for software crypto (simulating SE internal operation) */
XMEMSET(&tempAes, 0, sizeof(tempAes));
ret = wc_AesInit(&tempAes, NULL, INVALID_DEVID);
if (ret != 0) return ret;
ret = wc_AesGcmSetKey(&tempAes, slot->key, slot->keySz);
if (ret != 0) {
wc_AesFree(&tempAes);
return ret;
}
/* Perform the actual decryption using software AES (simulating SE internal operation) */
ret = wc_AesGcmDecrypt(&tempAes,
info->cipher.aesgcm_dec.out,
info->cipher.aesgcm_dec.in,
info->cipher.aesgcm_dec.sz,
info->cipher.aesgcm_dec.iv,
info->cipher.aesgcm_dec.ivSz,
info->cipher.aesgcm_dec.authTag,
info->cipher.aesgcm_dec.authTagSz,
info->cipher.aesgcm_dec.authIn,
info->cipher.aesgcm_dec.authInSz);
wc_AesFree(&tempAes);
return ret;
}
#ifdef WOLF_CRYPTO_CB_FREE
/* Free operation - simulate SE key deletion */
if (info->algo_type == WC_ALGO_TYPE_FREE &&
info->free.algo == WC_ALGO_TYPE_CIPHER &&
info->free.type == WC_CIPHER_AES) {
Aes* aes = (Aes*)info->free.obj;
if (aes != NULL && aes->devCtx == cryptoCbAesGcmMockHandle) {
/* "Delete" key from simulated SE */
ForceZero(&mockSeKeyOffload, sizeof(mockSeKeyOffload));
cryptoCbAesGcmFreeCalled++;
}
return 0;
}
#endif
return CRYPTOCB_UNAVAILABLE;
}
/*
* Test: End-to-End AES-GCM Offload via CryptoCB
* This test verifies that:
* - AES-GCM encryption/decryption operations are routed through CryptoCb
* - Software AES is bypassed when offload is enabled
* - Encrypted output and auth tag are correct
* - Decryption via CryptoCb restores the original plaintext
*/
int test_wc_CryptoCb_AesGcm_EncryptDecrypt(void)
{
EXPECT_DECLS;
#ifdef WOLFSSL_SMALL_STACK
Aes* aes = NULL;
byte* key = NULL;
byte* iv = NULL;
byte* aad = NULL;
byte* plaintext = NULL;
byte* ciphertext = NULL;
byte* decrypted = NULL;
byte* authTag = NULL;
#else
Aes aes[1];
byte key[AES_128_KEY_SIZE];
byte iv[GCM_NONCE_MID_SZ];
byte aad[16];
byte plaintext[32];
byte ciphertext[32];
byte decrypted[32];
byte authTag[AES_BLOCK_SIZE];
#endif
int ret;
int i;
int hasNonZero = 0;
#ifdef WOLFSSL_SMALL_STACK
aes = (Aes*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER);
key = (byte*)XMALLOC(AES_128_KEY_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
iv = (byte*)XMALLOC(GCM_NONCE_MID_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER);
aad = (byte*)XMALLOC(16, NULL, DYNAMIC_TYPE_TMP_BUFFER);
plaintext = (byte*)XMALLOC(32, NULL, DYNAMIC_TYPE_TMP_BUFFER);
ciphertext = (byte*)XMALLOC(32, NULL, DYNAMIC_TYPE_TMP_BUFFER);
decrypted = (byte*)XMALLOC(32, NULL, DYNAMIC_TYPE_TMP_BUFFER);
authTag = (byte*)XMALLOC(AES_BLOCK_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (aes == NULL || key == NULL || iv == NULL || aad == NULL ||
plaintext == NULL || ciphertext == NULL || decrypted == NULL ||
authTag == NULL) {
ret = MEMORY_E;
goto out;
}
#endif
/* Initialize key, iv, aad, plaintext arrays */
{
static const byte keyData[AES_128_KEY_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
static const byte ivData[GCM_NONCE_MID_SZ] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b
};
static const byte aadData[16] = {
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};
static const byte plaintextData[32] = {
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77,
0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x21, 0x00,
0x54, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x20, 0x32, 0x21, 0x00
};
XMEMCPY(key, keyData, AES_128_KEY_SIZE);
XMEMCPY(iv, ivData, GCM_NONCE_MID_SZ);
XMEMCPY(aad, aadData, 16);
XMEMCPY(plaintext, plaintextData, 32);
}
XMEMSET(aes, 0, sizeof(Aes));
XMEMSET(&mockSeKeyOffload, 0, sizeof(mockSeKeyOffload));
XMEMSET(ciphertext, 0, 32);
XMEMSET(decrypted, 0, 32);
XMEMSET(authTag, 0, AES_BLOCK_SIZE);
/* Reset test state */
cryptoCbAesGcmSetKeyCalled = 0;
cryptoCbAesGcmEncryptCalled = 0;
cryptoCbAesGcmDecryptCalled = 0;
cryptoCbAesGcmFreeCalled = 0;
/* Register test callback */
ret = wc_CryptoCb_RegisterDevice(TEST_CRYPTOCB_AESGCM_OFFLOAD_DEVID,
test_CryptoCb_AesGcm_Offload_Cb, NULL);
ExpectIntEQ(ret, 0);
/* Initialize Aes with device ID */
ret = wc_AesInit(aes, NULL, TEST_CRYPTOCB_AESGCM_OFFLOAD_DEVID);
ExpectIntEQ(ret, 0);
ExpectIntEQ(aes->devId, TEST_CRYPTOCB_AESGCM_OFFLOAD_DEVID);
/* Set key - should trigger CryptoCB and "import" to mock SE */
ret = wc_AesGcmSetKey(aes, key, sizeof(key));
ExpectIntEQ(ret, 0);
/* Verify SetKey callback was invoked */
ExpectIntEQ(cryptoCbAesGcmSetKeyCalled, 1);
/* Verify handle stored in devCtx */
ExpectPtrEq(aes->devCtx, cryptoCbAesGcmMockHandle);
/* Verify key was "imported" to mock SE */
ExpectIntEQ(mockSeKeyOffload.valid, 1);
ExpectIntEQ(mockSeKeyOffload.keySz, (int)sizeof(key));
/* Verify keylen metadata stored in Aes struct */
ExpectIntEQ(aes->keylen, (int)sizeof(key));
/* Encrypt via wolfCrypt API - should route through CryptoCb */
ret = wc_AesGcmEncrypt(aes, ciphertext, plaintext, 32,
iv, sizeof(iv), authTag, sizeof(authTag),
aad, 16);
ExpectIntEQ(ret, 0);
/* Assert: Encrypt callback was invoked */
ExpectIntEQ(cryptoCbAesGcmEncryptCalled, 1);
/* Assert: Ciphertext is different from plaintext */
ExpectIntNE(XMEMCMP(plaintext, ciphertext, 32), 0);
/* Assert: Auth tag is non-zero */
hasNonZero = 0;
for (i = 0; i < (int)sizeof(authTag); i++) {
if (authTag[i] != 0) {
hasNonZero = 1;
break;
}
}
ExpectIntEQ(hasNonZero, 1);
/* Decrypt via wolfCrypt API - should route through CryptoCb */
ret = wc_AesGcmDecrypt(aes, decrypted, ciphertext, 32,
iv, sizeof(iv), authTag, sizeof(authTag),
aad, 16);
ExpectIntEQ(ret, 0);
/* Assert: Decrypt callback was invoked */
ExpectIntEQ(cryptoCbAesGcmDecryptCalled, 1);
/* Assert: Decrypted plaintext matches original */
ExpectIntEQ(XMEMCMP(plaintext, decrypted, 32), 0);
#ifdef WOLF_CRYPTO_CB_FREE
/* Free should trigger callback and "delete" key from mock SE */
cryptoCbAesGcmFreeCalled = 0;
wc_AesFree(aes);
/* Verify free callback invoked */
ExpectIntEQ(cryptoCbAesGcmFreeCalled, 1);
/* Verify devCtx cleared */
ExpectPtrEq(aes->devCtx, NULL);
/* Verify key was "deleted" from mock SE */
ExpectIntEQ(mockSeKeyOffload.valid, 0);
#else
wc_AesFree(aes);
#endif
/* Cleanup */
wc_CryptoCb_UnRegisterDevice(TEST_CRYPTOCB_AESGCM_OFFLOAD_DEVID);
/* Verify lifecycle: SetKey -> Encrypt -> Decrypt -> Free */
ExpectIntEQ(cryptoCbAesGcmSetKeyCalled, 1);
ExpectIntEQ(cryptoCbAesGcmEncryptCalled, 1);
ExpectIntEQ(cryptoCbAesGcmDecryptCalled, 1);
#ifdef WOLF_CRYPTO_CB_FREE
ExpectIntEQ(cryptoCbAesGcmFreeCalled, 1);
#endif
#ifdef WOLFSSL_SMALL_STACK
out:
XFREE(aes, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(iv, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(aad, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(plaintext, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(ciphertext, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(decrypted, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(authTag, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}
#endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_AES_SETKEY && !NO_AES && HAVE_AESGCM */
+15 -1
View File
@@ -53,6 +53,19 @@ int test_wc_AesEaxDecryptAuth(void);
int test_wc_GmacSetKey(void);
int test_wc_GmacUpdate(void);
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_AES_SETKEY) && \
!defined(NO_AES) && defined(HAVE_AESGCM)
int test_wc_CryptoCb_AesSetKey(void);
int test_wc_CryptoCb_AesGcm_EncryptDecrypt(void);
#endif
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_AES_SETKEY) && \
!defined(NO_AES) && defined(HAVE_AESGCM)
#define TEST_CRYPTOCB_AES_SETKEY_DECL , TEST_DECL_GROUP("aes", test_wc_CryptoCb_AesSetKey), \
TEST_DECL_GROUP("aes", test_wc_CryptoCb_AesGcm_EncryptDecrypt)
#else
#define TEST_CRYPTOCB_AES_SETKEY_DECL
#endif
#define TEST_AES_DECLS \
TEST_DECL_GROUP("aes", test_wc_AesSetKey), \
@@ -74,7 +87,8 @@ int test_wc_GmacUpdate(void);
TEST_DECL_GROUP("aes", test_wc_AesCcmEncryptDecrypt), \
TEST_DECL_GROUP("aes", test_wc_AesXtsSetKey), \
TEST_DECL_GROUP("aes", test_wc_AesXtsEncryptDecrypt_Sizes), \
TEST_DECL_GROUP("aes", test_wc_AesXtsEncryptDecrypt)
TEST_DECL_GROUP("aes", test_wc_AesXtsEncryptDecrypt) \
TEST_CRYPTOCB_AES_SETKEY_DECL
#if defined(WOLFSSL_AES_EAX) && defined(WOLFSSL_AES_256) && \
(!defined(HAVE_FIPS) || FIPS_VERSION_GE(5, 3)) && !defined(HAVE_SELFTEST)
+83 -25
View File
@@ -4341,6 +4341,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(
int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen,
const byte* iv, int dir)
{
int ret;
if ((aes == NULL) || (userKey == NULL)) {
return BAD_FUNC_ARG;
}
@@ -4365,6 +4366,24 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(
#ifdef WOLF_CRYPTO_CB
if (aes->devId != INVALID_DEVID) {
#ifdef WOLF_CRYPTO_CB_AES_SETKEY
ret = wc_CryptoCb_AesSetKey(aes, userKey, keylen);
if (ret == 0) {
/* Callback succeeded - SE owns the key */
aes->keylen = (int)keylen;
if (iv != NULL)
XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE);
else
XMEMSET(aes->reg, 0, WC_AES_BLOCK_SIZE);
return 0;
}
else if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
aes->devCtx = NULL;
return ret;
}
/* CRYPTOCB_UNAVAILABLE: continue to software setup */
#endif
/* Standard CryptoCB path - copy key to devKey for encrypt/decrypt offload */
if (keylen > sizeof(aes->devKey)) {
return BAD_FUNC_ARG;
}
@@ -4791,6 +4810,33 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir)
return BAD_FUNC_ARG;
}
#ifdef WOLF_CRYPTO_CB
if (aes->devId != INVALID_DEVID) {
#ifdef WOLF_CRYPTO_CB_AES_SETKEY
ret = wc_CryptoCb_AesSetKey(aes, userKey, keylen);
if (ret == 0) {
/* Callback succeeded - SE owns the key */
aes->keylen = (int)keylen;
if (iv != NULL)
XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE);
else
XMEMSET(aes->reg, 0, WC_AES_BLOCK_SIZE);
return 0;
}
else if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
aes->devCtx = NULL;
return ret;
}
/* CRYPTOCB_UNAVAILABLE: continue to software setup */
#endif
/* Standard CryptoCB path - copy key to devKey */
if (keylen > sizeof(aes->devKey)) {
return BAD_FUNC_ARG;
}
XMEMCPY(aes->devKey, userKey, keylen);
}
#endif
#ifdef WOLFSSL_MAXQ10XX_CRYPTO
if (wc_MAXQ10XX_AesSetKey(aes, userKey, keylen) != 0) {
return WC_HW_E;
@@ -7454,46 +7500,55 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len)
}
#else
#if !defined(FREESCALE_LTC_AES_GCM) && !defined(WOLFSSL_PSOC6_CRYPTO)
#ifdef WOLF_CRYPTO_CB_AES_SETKEY
if ((ret == 0) && (aes->devId != INVALID_DEVID && aes->devCtx != NULL)) {
/* SE owns key - skip H and M table generation */
}
else
#endif
if (ret == 0) {
VECTOR_REGISTERS_PUSH;
/* AES-NI code generates its own H value, but generate it here too, to
* assure pure-C fallback is always usable.
*/
/* Generate H = AES_Encrypt(key, 0^128) */
ret = wc_AesEncrypt(aes, iv, aes->gcm.H);
if (ret == 0) {
#if defined(GCM_TABLE) || defined(GCM_TABLE_4BIT)
#if defined(GCM_TABLE) || defined(GCM_TABLE_4BIT)
#if defined(WOLFSSL_AESNI) && defined(GCM_TABLE_4BIT)
if (aes->use_aesni) {
#if defined(WC_C_DYNAMIC_FALLBACK)
#ifdef HAVE_INTEL_AVX2
if (IS_INTEL_AVX2(intel_flags)) {
GCM_generate_m0_avx2(aes->gcm.H, (byte*)aes->gcm.M0);
GCM_generate_m0_avx2(aes->gcm.H,
(byte*)aes->gcm.M0);
}
else
#endif
#if defined(HAVE_INTEL_AVX1)
if (IS_INTEL_AVX1(intel_flags)) {
GCM_generate_m0_avx1(aes->gcm.H, (byte*)aes->gcm.M0);
GCM_generate_m0_avx1(aes->gcm.H,
(byte*)aes->gcm.M0);
}
else
#endif
{
GCM_generate_m0_aesni(aes->gcm.H, (byte*)aes->gcm.M0);
GCM_generate_m0_aesni(aes->gcm.H,
(byte*)aes->gcm.M0);
}
#endif
#endif /* WC_C_DYNAMIC_FALLBACK */
}
else
#endif
#endif /* AESNI */
{
GenerateM0(&aes->gcm);
}
#endif /* GCM_TABLE || GCM_TABLE_4BIT */
#endif /* GCM_TABLE || GCM_TABLE_4BIT */
}
VECTOR_REGISTERS_POP;
}
#endif /* !FREESCALE_LTC_AES_GCM && !WOLFSSL_PSOC6_CRYPTO */
#endif
@@ -7503,7 +7558,15 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len)
#ifdef WOLF_CRYPTO_CB
if (aes->devId != INVALID_DEVID) {
XMEMCPY(aes->devKey, key, len);
#ifdef WOLF_CRYPTO_CB_AES_SETKEY
if (aes->devCtx != NULL) {
/* SE owns key - don't copy to devKey */
}
else
#endif
{
XMEMCPY(aes->devKey, key, len);
}
}
#endif
@@ -13302,6 +13365,7 @@ int wc_AesInit(Aes* aes, void* heap, int devId)
#if defined(WOLF_CRYPTO_CB) || defined(WOLFSSL_STM32U5_DHUK)
aes->devId = devId;
aes->devCtx = NULL;
#else
(void)devId;
#endif
@@ -13383,10 +13447,6 @@ int wc_AesInit_Label(Aes* aes, const char* label, void* heap, int devId)
/* Free Aes resources */
void wc_AesFree(Aes* aes)
{
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE)
int ret = 0;
#endif
if (aes == NULL) {
return;
}
@@ -13396,19 +13456,17 @@ void wc_AesFree(Aes* aes)
if (aes->devId != INVALID_DEVID)
#endif
{
ret = wc_CryptoCb_Free(aes->devId, WC_ALGO_TYPE_CIPHER,
WC_CIPHER_AES, (void*)aes);
/* If they want the standard free, they can call it themselves */
/* via their callback setting devId to INVALID_DEVID */
/* otherwise assume the callback handled it */
int ret = wc_CryptoCb_Free(aes->devId, WC_ALGO_TYPE_CIPHER,
WC_CIPHER_AES, aes);
#ifdef WOLF_CRYPTO_CB_AES_SETKEY
aes->devCtx = NULL; /* Clear device context handle */
#endif
/* If callback wants standard free, it can set devId to INVALID_DEVID.
* Otherwise assume the callback handled cleanup. */
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
return;
/* fall-through when unavailable */
}
/* silence compiler warning */
(void)ret;
#endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_FREE */
#ifdef WC_DEBUG_CIPHER_LIFECYCLE
+30
View File
@@ -1537,6 +1537,36 @@ int wc_CryptoCb_AesEcbDecrypt(Aes* aes, byte* out,
return wc_CryptoCb_TranslateErrorCode(ret);
}
#endif /* HAVE_AES_ECB */
#ifdef WOLF_CRYPTO_CB_AES_SETKEY
int wc_CryptoCb_AesSetKey(Aes* aes, const byte* key, word32 keySz)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
CryptoCb* dev;
if (aes == NULL || key == NULL)
return BAD_FUNC_ARG;
if (aes->devId == INVALID_DEVID)
return CRYPTOCB_UNAVAILABLE;
/* locate registered callback */
dev = wc_CryptoCb_FindDevice(aes->devId, WC_ALGO_TYPE_CIPHER);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_CIPHER;
cryptoInfo.cipher.type = WC_CIPHER_AES;
cryptoInfo.cipher.aessetkey.aes = aes;
cryptoInfo.cipher.aessetkey.key = key;
cryptoInfo.cipher.aessetkey.keySz = keySz;
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
}
return wc_CryptoCb_TranslateErrorCode(ret);
}
#endif /* WOLF_CRYPTO_CB_AES_SETKEY */
#endif /* !NO_AES */
#ifndef NO_DES3
+1 -1
View File
@@ -334,7 +334,7 @@ struct Aes {
#endif /* __aarch64__ && WOLFSSL_ARMASM && !WOLFSSL_ARMASM_NO_HW_CRYPTO */
#if defined(WOLF_CRYPTO_CB) || defined(WOLFSSL_STM32U5_DHUK)
int devId;
void* devCtx;
void* devCtx; /* Opaque handle for CryptoCB device */
#endif
#ifdef WOLF_PRIVATE_KEY_ID
byte id[AES_MAX_ID_LEN];
+10
View File
@@ -376,6 +376,13 @@ typedef struct wc_CryptoInfo {
const byte* in;
word32 sz;
} des3;
#endif
#if !defined(NO_AES) && defined(WOLF_CRYPTO_CB_AES_SETKEY)
struct {
Aes* aes;
const byte* key;
word32 keySz;
} aessetkey;
#endif
void* ctx;
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
@@ -678,6 +685,9 @@ WOLFSSL_LOCAL int wc_CryptoCb_AesEcbEncrypt(Aes* aes, byte* out,
WOLFSSL_LOCAL int wc_CryptoCb_AesEcbDecrypt(Aes* aes, byte* out,
const byte* in, word32 sz);
#endif /* HAVE_AES_ECB */
#ifdef WOLF_CRYPTO_CB_AES_SETKEY
WOLFSSL_API int wc_CryptoCb_AesSetKey(Aes* aes, const byte* key, word32 keySz);
#endif /* WOLF_CRYPTO_CB_AES_SETKEY */
#endif /* !NO_AES */
#ifndef NO_DES3