@@ -311,6 +311,114 @@ static int whTest_CertServerVerifyCache(whServerConfig* serverCfg)
311311 WH_TEST_PRINT ("Server cert verify-cache test PASSED\n" );
312312 return WH_ERROR_OK ;
313313}
314+
315+ /* Counts verify-callback invocations so we can tell a cold verify (callback
316+ * fires for every cert) from a warm verify (callback skipped on cache hits).
317+ * Used by the SetEnabled test below to confirm that disabling actually
318+ * suppresses cache hits. */
319+ static int s_setEnabledCb_count = 0 ;
320+ static int whTest_setEnabledVerifyCb (int preverify ,
321+ WOLFSSL_X509_STORE_CTX * store )
322+ {
323+ (void )store ;
324+ s_setEnabledCb_count ++ ;
325+ return preverify ;
326+ }
327+
328+ /* Exercises wh_Server_CertVerifyCache_SetEnabled:
329+ * - disable flushes existing entries and suppresses subsequent cache hits
330+ * (a re-verify after disable runs cold, callback fires the full count)
331+ * - re-enable resumes caching (a verify after re-enable populates, the next
332+ * re-verify warms — callback count drops back down)
333+ * - default state is enabled (covered implicitly by the cold/warm counts) */
334+ static int whTest_CertServerVerifyCacheSetEnabled (whServerConfig * serverCfg )
335+ {
336+ whServerContext server [1 ] = {0 };
337+ whServerCertConfig certCfg = {.verifyCb = whTest_setEnabledVerifyCb };
338+ whServerCertConfig * savedCertConfig ;
339+ const whNvmId rootCertA = 1 ;
340+ int coldCount ;
341+ int warmCount ;
342+ int afterDisableCount ;
343+
344+ WH_TEST_PRINT ("=== Server cert verify-cache set-enabled test ===\n" );
345+
346+ /* Inject the counting callback so we can detect cache hits by absence of
347+ * callback fires. Restore on exit. */
348+ savedCertConfig = serverCfg -> certConfig ;
349+ serverCfg -> certConfig = & certCfg ;
350+
351+ WH_TEST_RETURN_ON_FAIL (wh_Server_Init (server , serverCfg ));
352+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertInit (server ));
353+
354+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertAddTrusted (
355+ server , rootCertA , WH_NVM_ACCESS_ANY , WH_NVM_FLAGS_NONMODIFIABLE , NULL ,
356+ 0 , ROOT_A_CERT , ROOT_A_CERT_len ));
357+
358+ /* Start cold under the global-shared cache mode where prior tests may
359+ * have populated entries. Per-client mode is already clean. */
360+ wh_Server_CertVerifyCache_Clear (server );
361+
362+ /* 1. Cold verify under default-enabled cache: callback fires for every
363+ * cert in the chain. */
364+ s_setEnabledCb_count = 0 ;
365+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertVerify (
366+ server , RAW_CERT_CHAIN_A , RAW_CERT_CHAIN_A_len , rootCertA ,
367+ WH_CERT_FLAGS_NONE , WH_NVM_FLAGS_USAGE_ANY , NULL ));
368+ coldCount = s_setEnabledCb_count ;
369+ WH_TEST_ASSERT_RETURN (coldCount > 0 );
370+
371+ /* 2. Re-verify with cache still enabled: CA cache hits skip the callback,
372+ * so the count is strictly less than cold. */
373+ s_setEnabledCb_count = 0 ;
374+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertVerify (
375+ server , RAW_CERT_CHAIN_A , RAW_CERT_CHAIN_A_len , rootCertA ,
376+ WH_CERT_FLAGS_NONE , WH_NVM_FLAGS_USAGE_ANY , NULL ));
377+ warmCount = s_setEnabledCb_count ;
378+ WH_TEST_ASSERT_RETURN (warmCount > 0 );
379+ WH_TEST_ASSERT_RETURN (warmCount < coldCount );
380+
381+ /* 3. Disable the cache. Entries from steps 1-2 must be flushed and new
382+ * inserts suppressed: the next verify should be cold again (count back
383+ * up to coldCount). */
384+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertVerifyCache_SetEnabled (server , 0 ));
385+ s_setEnabledCb_count = 0 ;
386+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertVerify (
387+ server , RAW_CERT_CHAIN_A , RAW_CERT_CHAIN_A_len , rootCertA ,
388+ WH_CERT_FLAGS_NONE , WH_NVM_FLAGS_USAGE_ANY , NULL ));
389+ afterDisableCount = s_setEnabledCb_count ;
390+ WH_TEST_ASSERT_RETURN (afterDisableCount == coldCount );
391+
392+ /* 4. Second verify while still disabled — also cold (no caching). */
393+ s_setEnabledCb_count = 0 ;
394+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertVerify (
395+ server , RAW_CERT_CHAIN_A , RAW_CERT_CHAIN_A_len , rootCertA ,
396+ WH_CERT_FLAGS_NONE , WH_NVM_FLAGS_USAGE_ANY , NULL ));
397+ WH_TEST_ASSERT_RETURN (s_setEnabledCb_count == coldCount );
398+
399+ /* 5. Re-enable. The post-disable verify did not populate the cache, so
400+ * the first re-enabled verify is still cold (and populates). */
401+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertVerifyCache_SetEnabled (server , 1 ));
402+ s_setEnabledCb_count = 0 ;
403+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertVerify (
404+ server , RAW_CERT_CHAIN_A , RAW_CERT_CHAIN_A_len , rootCertA ,
405+ WH_CERT_FLAGS_NONE , WH_NVM_FLAGS_USAGE_ANY , NULL ));
406+ WH_TEST_ASSERT_RETURN (s_setEnabledCb_count == coldCount );
407+
408+ /* 6. Subsequent verify warms, matching step 2's warmCount. */
409+ s_setEnabledCb_count = 0 ;
410+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertVerify (
411+ server , RAW_CERT_CHAIN_A , RAW_CERT_CHAIN_A_len , rootCertA ,
412+ WH_CERT_FLAGS_NONE , WH_NVM_FLAGS_USAGE_ANY , NULL ));
413+ WH_TEST_ASSERT_RETURN (s_setEnabledCb_count == warmCount );
414+
415+ /* Clean up: leave the cache empty for downstream tests. */
416+ wh_Server_CertVerifyCache_Clear (server );
417+ WH_TEST_RETURN_ON_FAIL (wh_Server_CertEraseTrusted (server , rootCertA ));
418+ serverCfg -> certConfig = savedCertConfig ;
419+ WH_TEST_PRINT ("Server cert verify-cache set-enabled test PASSED\n" );
420+ return WH_ERROR_OK ;
421+ }
314422#endif /* WOLFHSM_CFG_CERTIFICATE_VERIFY_CACHE */
315423
316424#if defined(WOLFHSM_CFG_CERTIFICATE_VERIFY_CACHE ) && \
@@ -873,6 +981,32 @@ int whTest_CertClient(whClientContext* client)
873981 rootCertB_id_c , & out_rc ));
874982 WH_TEST_ASSERT_RETURN (out_rc == WH_ERROR_CERT_VERIFY );
875983
984+ /* Exercise the SetEnabled RPC path. The cross-root-must-fail
985+ * invariant has to hold regardless of cache state, so re-run it
986+ * after disable and after re-enable to confirm the RPC neither
987+ * crashes nor breaks correctness. Behavioral observation (cache
988+ * hits vs misses) is covered by whTest_CertServerVerifyCacheSetEnabled;
989+ * here we just confirm the wire path round-trips. */
990+ WH_TEST_RETURN_ON_FAIL (
991+ wh_Client_CertVerifyCacheSetEnabled (client , 0 , & out_rc ));
992+ WH_TEST_ASSERT_RETURN (out_rc == WH_ERROR_OK );
993+ WH_TEST_RETURN_ON_FAIL (wh_Client_CertVerify (client , RAW_CERT_CHAIN_A ,
994+ RAW_CERT_CHAIN_A_len ,
995+ rootCertA_id_c , & out_rc ));
996+ WH_TEST_ASSERT_RETURN (out_rc == WH_ERROR_OK );
997+ WH_TEST_RETURN_ON_FAIL (wh_Client_CertVerify (client , RAW_CERT_CHAIN_A ,
998+ RAW_CERT_CHAIN_A_len ,
999+ rootCertB_id_c , & out_rc ));
1000+ WH_TEST_ASSERT_RETURN (out_rc == WH_ERROR_CERT_VERIFY );
1001+
1002+ WH_TEST_RETURN_ON_FAIL (
1003+ wh_Client_CertVerifyCacheSetEnabled (client , 1 , & out_rc ));
1004+ WH_TEST_ASSERT_RETURN (out_rc == WH_ERROR_OK );
1005+ WH_TEST_RETURN_ON_FAIL (wh_Client_CertVerify (client , RAW_CERT_CHAIN_A ,
1006+ RAW_CERT_CHAIN_A_len ,
1007+ rootCertA_id_c , & out_rc ));
1008+ WH_TEST_ASSERT_RETURN (out_rc == WH_ERROR_OK );
1009+
8761010 /* Cleanup */
8771011 WH_TEST_RETURN_ON_FAIL (
8781012 wh_Client_CertEraseTrusted (client , rootCertA_id_c , & out_rc ));
@@ -1394,6 +1528,13 @@ int whTest_CertRamSim(whTestNvmBackendType nvmType)
13941528 WH_ERROR_PRINT ("Cert verify-cache tests failed: %d\n" , rc );
13951529 }
13961530 }
1531+ if (rc == WH_ERROR_OK ) {
1532+ rc = whTest_CertServerVerifyCacheSetEnabled (s_conf );
1533+ if (rc != WH_ERROR_OK ) {
1534+ WH_ERROR_PRINT ("Cert verify-cache set-enabled tests failed: %d\n" ,
1535+ rc );
1536+ }
1537+ }
13971538#ifdef WOLFHSM_CFG_CERTIFICATE_VERIFY_CACHE_GLOBAL
13981539 if (rc == WH_ERROR_OK ) {
13991540 rc = whTest_CertServerVerifyCacheGlobalShared (s_conf );
0 commit comments