@@ -5284,6 +5284,11 @@ static int test_CryptoCb_Aes_Cb(int devId, wc_CryptoInfo* info, void* ctx)
52845284 /* Store handle in aes->devCtx - this is what wolfSSL will use */
52855285 aes -> devCtx = cryptoCbAesMockHandle ;
52865286
5287+ /* CRITICAL: Declare full AES-GCM offload capability.
5288+ * This tells wolfSSL to skip GCM table generation and expect
5289+ * all GCM operations to go through CryptoCB. */
5290+ info -> cipher .aessetkey .capabilities = WC_CRYPTOCB_AES_GCM ;
5291+
52875292 cryptoCbAesSetKeyCalled ++ ;
52885293
52895294 return 0 ;
@@ -5486,6 +5491,9 @@ int test_wc_CryptoCb_AesSetKey(void)
54865491 /* Verify callback was invoked */
54875492 ExpectIntEQ (cryptoCbAesSetKeyCalled , 1 );
54885493
5494+ /* Verify GCM offload flag was set (callback declared capability) */
5495+ ExpectIntEQ (aes -> gcmCryptoCbOffload , 1 );
5496+
54895497 /* Verify handle stored in devCtx */
54905498 ExpectPtrEq (aes -> devCtx , cryptoCbAesMockHandle );
54915499
@@ -5628,6 +5636,11 @@ static int test_CryptoCb_AesGcm_Offload_Cb(int devId, wc_CryptoInfo* info, void*
56285636 /* Store handle in aes->devCtx - this is what wolfSSL will use */
56295637 aes -> devCtx = cryptoCbAesGcmMockHandle ;
56305638
5639+ /* CRITICAL: Declare full AES-GCM offload capability.
5640+ * This tells wolfSSL to skip GCM table generation and expect
5641+ * all GCM operations to go through CryptoCB. */
5642+ info -> cipher .aessetkey .capabilities = WC_CRYPTOCB_AES_GCM ;
5643+
56315644 cryptoCbAesGcmSetKeyCalled ++ ;
56325645
56335646 return 0 ;
@@ -5863,6 +5876,9 @@ int test_wc_CryptoCb_AesGcm_EncryptDecrypt(void)
58635876 /* Verify SetKey callback was invoked */
58645877 ExpectIntEQ (cryptoCbAesGcmSetKeyCalled , 1 );
58655878
5879+ /* Verify GCM offload flag was set (callback declared capability) */
5880+ ExpectIntEQ (aes -> gcmCryptoCbOffload , 1 );
5881+
58665882 /* Verify handle stored in devCtx */
58675883 ExpectPtrEq (aes -> devCtx , cryptoCbAesGcmMockHandle );
58685884
@@ -5950,5 +5966,201 @@ int test_wc_CryptoCb_AesGcm_EncryptDecrypt(void)
59505966 return EXPECT_RESULT ();
59515967}
59525968
5969+ /*
5970+ * Test: Key import only callback - verifies software fallback works
5971+ * when callback does NOT declare WC_CRYPTOCB_AES_GCM capability
5972+ */
5973+ #define TEST_CRYPTOCB_AES_KEYIMPORT_ONLY_DEVID 9
5974+
5975+ /* Test state tracking for key-import-only test */
5976+ static int cryptoCbKeyImportOnlySetKeyCalled = 0 ;
5977+ static void * cryptoCbKeyImportOnlyMockHandle = (void * )0xDEADBEEF ;
5978+
5979+ /* Mock SE key storage for key-import-only test */
5980+ static struct {
5981+ byte key [AES_256_KEY_SIZE ];
5982+ word32 keySz ;
5983+ int valid ;
5984+ } mockSeKeyImportOnly ;
5985+
5986+ /* Callback that handles key import but NOT GCM operations */
5987+ static int test_CryptoCb_KeyImportOnly_Cb (int devId , wc_CryptoInfo * info , void * ctx )
5988+ {
5989+ (void )ctx ;
5990+
5991+ if (devId != TEST_CRYPTOCB_AES_KEYIMPORT_ONLY_DEVID )
5992+ return CRYPTOCB_UNAVAILABLE ;
5993+
5994+ /* AES SetKey operation - simulate SE key import */
5995+ if (info -> algo_type == WC_ALGO_TYPE_CIPHER &&
5996+ info -> cipher .type == WC_CIPHER_AES &&
5997+ info -> cipher .aessetkey .aes != NULL ) {
5998+
5999+ Aes * aes = info -> cipher .aessetkey .aes ;
6000+ const byte * key = info -> cipher .aessetkey .key ;
6001+ word32 keySz = info -> cipher .aessetkey .keySz ;
6002+
6003+ /* Validate key */
6004+ if (key == NULL || keySz == 0 || keySz > AES_256_KEY_SIZE ) {
6005+ return BAD_FUNC_ARG ;
6006+ }
6007+
6008+ /* "Import" key to simulated SE storage */
6009+ XMEMCPY (mockSeKeyImportOnly .key , key , keySz );
6010+ mockSeKeyImportOnly .keySz = keySz ;
6011+ mockSeKeyImportOnly .valid = 1 ;
6012+
6013+ /* Store handle in aes->devCtx */
6014+ aes -> devCtx = cryptoCbKeyImportOnlyMockHandle ;
6015+
6016+ /* CRITICAL: DO NOT set WC_CRYPTOCB_AES_GCM capability.
6017+ * This simulates a device that can import keys but cannot
6018+ * perform GCM operations. wolfSSL should generate GCM tables
6019+ * and use software fallback for GCM operations. */
6020+ /* info->cipher.aessetkey.capabilities stays 0 (default) */
6021+
6022+ cryptoCbKeyImportOnlySetKeyCalled ++ ;
6023+
6024+ return 0 ;
6025+ }
6026+
6027+ /* GCM operations not supported - return UNAVAILABLE to trigger software fallback */
6028+ if (info -> algo_type == WC_ALGO_TYPE_CIPHER &&
6029+ info -> cipher .type == WC_CIPHER_AES_GCM ) {
6030+ return CRYPTOCB_UNAVAILABLE ;
6031+ }
6032+
6033+ return CRYPTOCB_UNAVAILABLE ;
6034+ }
6035+
6036+ int test_wc_CryptoCb_AesKeyImportOnly (void )
6037+ {
6038+ EXPECT_DECLS ;
6039+ #ifdef WOLFSSL_SMALL_STACK
6040+ Aes * aes = NULL ;
6041+ byte * key = NULL ;
6042+ byte * iv = NULL ;
6043+ byte * plaintext = NULL ;
6044+ byte * ciphertext = NULL ;
6045+ byte * decrypted = NULL ;
6046+ byte * authTag = NULL ;
6047+ byte * aad = NULL ;
6048+ #else
6049+ Aes aes [1 ];
6050+ byte key [AES_128_KEY_SIZE ];
6051+ byte iv [GCM_NONCE_MID_SZ ];
6052+ byte plaintext [32 ];
6053+ byte ciphertext [32 ];
6054+ byte decrypted [32 ];
6055+ byte authTag [AES_BLOCK_SIZE ];
6056+ byte aad [16 ];
6057+ #endif
6058+ int ret ;
6059+
6060+ #ifdef WOLFSSL_SMALL_STACK
6061+ aes = (Aes * )XMALLOC (sizeof (Aes ), NULL , DYNAMIC_TYPE_TMP_BUFFER );
6062+ key = (byte * )XMALLOC (AES_128_KEY_SIZE , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6063+ iv = (byte * )XMALLOC (GCM_NONCE_MID_SZ , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6064+ plaintext = (byte * )XMALLOC (32 , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6065+ ciphertext = (byte * )XMALLOC (32 , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6066+ decrypted = (byte * )XMALLOC (32 , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6067+ authTag = (byte * )XMALLOC (AES_BLOCK_SIZE , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6068+ aad = (byte * )XMALLOC (16 , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6069+
6070+ if (aes == NULL || key == NULL || iv == NULL || plaintext == NULL ||
6071+ ciphertext == NULL || decrypted == NULL || authTag == NULL || aad == NULL ) {
6072+ ret = MEMORY_E ;
6073+ goto out ;
6074+ }
6075+ #endif
6076+
6077+ /* Initialize test data */
6078+ XMEMSET (key , 0x01 , AES_128_KEY_SIZE );
6079+ XMEMSET (iv , 0x02 , GCM_NONCE_MID_SZ );
6080+ XMEMSET (plaintext , 0x03 , 32 );
6081+ XMEMSET (aad , 0x04 , 16 );
6082+ XMEMSET (aes , 0 , sizeof (Aes ));
6083+ XMEMSET (& mockSeKeyImportOnly , 0 , sizeof (mockSeKeyImportOnly ));
6084+ cryptoCbKeyImportOnlySetKeyCalled = 0 ;
6085+
6086+ /* Register callback for key-import-only device */
6087+ ret = wc_CryptoCb_RegisterDevice (TEST_CRYPTOCB_AES_KEYIMPORT_ONLY_DEVID ,
6088+ test_CryptoCb_KeyImportOnly_Cb , NULL );
6089+ ExpectIntEQ (ret , 0 );
6090+
6091+ /* Initialize Aes with device ID */
6092+ ret = wc_AesInit (aes , NULL , TEST_CRYPTOCB_AES_KEYIMPORT_ONLY_DEVID );
6093+ ExpectIntEQ (ret , 0 );
6094+ ExpectIntEQ (aes -> devId , TEST_CRYPTOCB_AES_KEYIMPORT_ONLY_DEVID );
6095+
6096+ /* Set key - should trigger CryptoCB and "import" to mock SE */
6097+ ret = wc_AesGcmSetKey (aes , key , sizeof (key ));
6098+ ExpectIntEQ (ret , 0 );
6099+
6100+ /* Verify SetKey callback was invoked (callback is called but then we fall through) */
6101+ ExpectIntEQ (cryptoCbKeyImportOnlySetKeyCalled , 1 );
6102+
6103+ /* CRITICAL: Verify GCM offload flag is 0 (capability NOT declared) */
6104+ ExpectIntEQ (aes -> gcmCryptoCbOffload , 0 );
6105+
6106+ /* Verify keylen is set (software path sets this after falling through) */
6107+ ExpectIntEQ (aes -> keylen , (int )sizeof (key ));
6108+
6109+ /* Note: The callback sets devCtx, but since capability is not set, we fall through
6110+ * to software path. The software path will set up the key in aes->key for GCM
6111+ * table generation. devCtx may or may not be cleared by software path. */
6112+
6113+ /* CRITICAL: Verify GCM tables were generated (H table should be non-zero) */
6114+ /* If gcmCryptoCbOffload is 0, tables should be generated for software fallback */
6115+ {
6116+ int hTableNonZero = 0 ;
6117+ int i ;
6118+ for (i = 0 ; i < WC_AES_BLOCK_SIZE ; i ++ ) {
6119+ if (aes -> gcm .H [i ] != 0 ) {
6120+ hTableNonZero = 1 ;
6121+ break ;
6122+ }
6123+ }
6124+ ExpectIntEQ (hTableNonZero , 1 ); /* H table should be generated */
6125+ }
6126+
6127+ /* Encrypt via wolfCrypt API - should fall back to software */
6128+ /* Callback will return CRYPTOCB_UNAVAILABLE, triggering software path */
6129+ ret = wc_AesGcmEncrypt (aes , ciphertext , plaintext , 32 ,
6130+ iv , sizeof (iv ), authTag , sizeof (authTag ),
6131+ aad , 16 );
6132+ ExpectIntEQ (ret , 0 );
6133+
6134+ /* Decrypt via wolfCrypt API - should fall back to software */
6135+ ret = wc_AesGcmDecrypt (aes , decrypted , ciphertext , 32 ,
6136+ iv , sizeof (iv ), authTag , sizeof (authTag ),
6137+ aad , 16 );
6138+ ExpectIntEQ (ret , 0 );
6139+
6140+ /* Verify decrypted plaintext matches original */
6141+ ExpectIntEQ (XMEMCMP (plaintext , decrypted , 32 ), 0 );
6142+
6143+ /* Cleanup */
6144+ wc_AesFree (aes );
6145+ wc_CryptoCb_UnRegisterDevice (TEST_CRYPTOCB_AES_KEYIMPORT_ONLY_DEVID );
6146+
6147+ /* Note: mockSeKeyImportOnly.valid may still be 1 since callback was called,
6148+ * but the key is not actually used (software path is taken) */
6149+
6150+ #ifdef WOLFSSL_SMALL_STACK
6151+ out :
6152+ XFREE (aes , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6153+ XFREE (key , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6154+ XFREE (iv , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6155+ XFREE (aad , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6156+ XFREE (plaintext , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6157+ XFREE (ciphertext , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6158+ XFREE (decrypted , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6159+ XFREE (authTag , NULL , DYNAMIC_TYPE_TMP_BUFFER );
6160+ #endif
6161+
6162+ return EXPECT_RESULT ();
6163+ }
6164+
59536165#endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_AES_SETKEY && !NO_AES && HAVE_AESGCM */
59546166
0 commit comments