Merge pull request #11204 from padelsbach/falcon-cb-free

Add CB_FREE mode for Falcon
pull/11090/head
philljj 2026-08-24 16:30:18 -05:00 committed by GitHub
commit f8b140f511
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 498 additions and 5 deletions

View File

@ -71,6 +71,15 @@
"--enable-dilithium=yes,no-ctx", "--enable-dual-alg-certs",
"--disable-qt",
"CPPFLAGS=-pedantic -Wdeclaration-after-statement -Wnull-dereference -DWOLFCRYPT_TEST_LINT -DNO_WOLFSSL_CIPHER_SUITE_TEST -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"]},
{"name": "pq-cryptocbutils", "minutes": 3,
"comment": "The only config pairing --enable-cryptocbutils with the PQC algorithms. --enable-all already turns cryptocb on, but the utility callbacks (copy, free, setkey, export) default to off, so without this entry WOLF_CRYPTO_CB_FREE is never compiled alongside a PQC algorithm and the free-callback tests for ML-DSA, SLH-DSA and ML-KEM all skip. Falcon and FrodoKEM are named explicitly: experimental only auto-enables them under --enable-all-quantum-crypto, which nothing here sets.",
"configure": ["--enable-intelasm", "--enable-sp-asm",
"--enable-all", "--enable-testcert", "--enable-experimental",
"--enable-mlkem=yes,kyber,ml-kem", "--enable-slhdsa",
"--enable-dilithium", "--enable-falcon", "--enable-frodokem",
"--enable-cryptocb", "--enable-cryptocbutils",
"--disable-qt",
"CPPFLAGS=-pedantic -Wdeclaration-after-statement -Wnull-dereference -DWOLFCRYPT_TEST_LINT -DNO_WOLFSSL_CIPHER_SUITE_TEST -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"]},
{"name": "pq-asynccrypt-dual-alg", "minutes": 2.5,
"comment": "Async crypto with dual-algorithm certs and PQC signatures. WOLFSSL_ASYNC_CRYPT turns off the streaming CertificateVerify path, so this is the only config that compiles the in-place fragmented send and the async args holder that Scv13Args must fit.",
"configure": ["--enable-asynccrypt-sw", "--enable-dual-alg-certs",

View File

@ -36,6 +36,7 @@
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/cryptocb.h>
#include <tests/api/api.h>
#include <tests/api/test_falcon.h>
@ -1035,3 +1036,105 @@ int test_wc_FalconDecisionCoverage(void)
#endif /* HAVE_FALCON */
return EXPECT_RESULT();
}
#if defined(HAVE_FALCON) && defined(WOLF_CRYPTO_CB) && \
defined(WOLF_CRYPTO_CB_FREE)
#define TEST_FALCON_CB_FREE
#define TEST_FALCON_CB_FREE_DEVID 0x46414C43
#endif
#ifdef TEST_FALCON_CB_FREE
/* What the free callback saw, so the test can check the contract rather than
* just that something fired. */
typedef struct {
int frees; /* matching free callbacks seen */
int badObj; /* callback was handed the wrong object */
int wiped; /* callback saw a key already cleaned up */
int ret; /* what the callback returns */
const void* obj; /* object the free is expected to name */
} FalconCbFreeCtx;
/* Stands in for a device holding state for the key. Counting the call proves
* wc_falcon_free told the device rather than only cleaning up in software,
* which would leave the device side of the key behind. */
static int falcon_cb_free_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
{
FalconCbFreeCtx* seen = (FalconCbFreeCtx*)ctx;
(void)devIdArg;
if ((seen != NULL) && (info != NULL) &&
(info->algo_type == WC_ALGO_TYPE_FREE) &&
(info->free.algo == WC_ALGO_TYPE_PK) &&
(info->free.type == WC_PK_TYPE_PQC_SIG_KEYGEN) &&
(info->free.subType == WC_PQC_SIG_TYPE_FALCON)) {
const falcon_key* fk = (const falcon_key*)info->free.obj;
seen->frees++;
if ((fk == NULL) || ((const void*)fk != seen->obj)) {
seen->badObj++;
}
/* The device gets the key while it is still whole: it may need to
* read it to release the right resource, so the software wipe has
* to come after this call, not before. */
else if (fk->devId != TEST_FALCON_CB_FREE_DEVID) {
seen->wiped++;
}
return seen->ret;
}
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
}
#endif /* TEST_FALCON_CB_FREE */
/* Freeing a key that names a device has to tell that device, so it can
* release what it holds. A key with no device must not, and neither must a
* second free of a key already freed: a freed key names no device. A device
* that reports an error does not stop the software cleanup. */
int test_falcon_cb_free(void)
{
EXPECT_DECLS;
#ifdef TEST_FALCON_CB_FREE
falcon_key key;
FalconCbFreeCtx seen;
XMEMSET(&key, 0, sizeof(key));
XMEMSET(&seen, 0, sizeof(seen));
seen.obj = &key;
/* No key to free, and nothing to tell a device about. */
wc_falcon_free(NULL);
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_FALCON_CB_FREE_DEVID,
falcon_cb_free_cb, &seen), 0);
ExpectIntEQ(wc_falcon_init_ex(&key, NULL, TEST_FALCON_CB_FREE_DEVID), 0);
wc_falcon_free(&key);
ExpectIntEQ(seen.frees, 1);
ExpectIntEQ(seen.badObj, 0);
ExpectIntEQ(seen.wiped, 0);
ExpectIntEQ(key.devId, INVALID_DEVID);
wc_falcon_free(&key);
ExpectIntEQ(seen.frees, 1);
/* A device that fails still leaves the key cleaned up locally. */
seen.ret = WC_NO_ERR_TRACE(WC_HW_E);
XMEMSET(&key, 0, sizeof(key));
ExpectIntEQ(wc_falcon_init_ex(&key, NULL, TEST_FALCON_CB_FREE_DEVID), 0);
ExpectIntEQ(wc_falcon_set_level(&key, 1), 0);
wc_falcon_free(&key);
ExpectIntEQ(seen.frees, 2);
ExpectIntEQ(key.devId, INVALID_DEVID);
ExpectIntEQ(key.level, 0);
seen.ret = 0;
XMEMSET(&key, 0, sizeof(key));
ExpectIntEQ(wc_falcon_init_ex(&key, NULL, INVALID_DEVID), 0);
wc_falcon_free(&key);
ExpectIntEQ(seen.frees, 2);
wc_CryptoCb_UnRegisterDevice(TEST_FALCON_CB_FREE_DEVID);
#endif
return EXPECT_RESULT();
}

View File

@ -32,6 +32,7 @@ int test_wc_falcon_check_key(void);
int test_wc_falcon_der(void);
int test_wc_falcon_error_paths(void);
int test_wc_FalconDecisionCoverage(void);
int test_falcon_cb_free(void);
#define TEST_FALCON_DECLS \
TEST_DECL_GROUP("falcon", test_wc_falcon_sizes), \
@ -41,6 +42,7 @@ int test_wc_FalconDecisionCoverage(void);
TEST_DECL_GROUP("falcon", test_wc_falcon_check_key), \
TEST_DECL_GROUP("falcon", test_wc_falcon_der), \
TEST_DECL_GROUP("falcon", test_wc_falcon_error_paths), \
TEST_DECL_GROUP("falcon", test_wc_FalconDecisionCoverage)
TEST_DECL_GROUP("falcon", test_wc_FalconDecisionCoverage), \
TEST_DECL_GROUP("falcon", test_falcon_cb_free)
#endif /* WOLFCRYPT_TEST_FALCON_H */

View File

@ -43,6 +43,7 @@
#endif
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/cryptocb.h>
#ifdef WOLFSSL_HAVE_MLDSA
#include <wolfssl/wolfcrypt/wc_mldsa.h>
#endif
@ -31401,3 +31402,105 @@ int test_wc_MldsaDerDecisionCoverage(void)
#endif /* WOLFSSL_HAVE_MLDSA && WOLFSSL_MLDSA_NO_ASN1 && ... */
return EXPECT_RESULT();
}
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLF_CRYPTO_CB) && \
defined(WOLF_CRYPTO_CB_FREE)
#define TEST_MLDSA_CB_FREE
#define TEST_MLDSA_CB_FREE_DEVID 0x4D4C4453
#endif
#ifdef TEST_MLDSA_CB_FREE
/* What the free callback saw, so the test can check the contract rather than
* just that something fired. */
typedef struct {
int frees; /* matching free callbacks seen */
int badObj; /* callback was handed the wrong object */
int wiped; /* callback saw a key already cleaned up */
int ret; /* what the callback returns */
const void* obj; /* object the free is expected to name */
} MlDsaCbFreeCtx;
/* Stands in for a device holding state for the key. Counting the call proves
* wc_MlDsaKey_Free told the device rather than only cleaning up in software,
* which would leave the device side of the key behind. */
static int mldsa_cb_free_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
{
MlDsaCbFreeCtx* seen = (MlDsaCbFreeCtx*)ctx;
(void)devIdArg;
if ((seen != NULL) && (info != NULL) &&
(info->algo_type == WC_ALGO_TYPE_FREE) &&
(info->free.algo == WC_ALGO_TYPE_PK) &&
(info->free.type == WC_PK_TYPE_PQC_SIG_KEYGEN) &&
(info->free.subType == WC_PQC_SIG_TYPE_MLDSA)) {
const wc_MlDsaKey* dil = (const wc_MlDsaKey*)info->free.obj;
seen->frees++;
if ((dil == NULL) || ((const void*)dil != seen->obj)) {
seen->badObj++;
}
/* The device gets the key while it is still whole: it may need to
* read it to release the right resource, so the software wipe has
* to come after this call, not before. */
else if (dil->devId != TEST_MLDSA_CB_FREE_DEVID) {
seen->wiped++;
}
return seen->ret;
}
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
}
#endif /* TEST_MLDSA_CB_FREE */
/* Freeing a key that names a device has to tell that device, so it can
* release what it holds. A key with no device must not, and neither must a
* second free of a key already freed: a freed key names no device. A device
* that reports an error does not stop the software cleanup. */
int test_mldsa_cb_free(void)
{
EXPECT_DECLS;
#ifdef TEST_MLDSA_CB_FREE
wc_MlDsaKey* key = NULL;
MlDsaCbFreeCtx seen;
XMEMSET(&seen, 0, sizeof(seen));
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_MLDSA_CB_FREE_DEVID,
mldsa_cb_free_cb, &seen), 0);
ExpectNotNull(key = (wc_MlDsaKey*)XMALLOC(sizeof(wc_MlDsaKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER));
seen.obj = key;
ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, TEST_MLDSA_CB_FREE_DEVID), 0);
wc_MlDsaKey_Free(key);
ExpectIntEQ(seen.frees, 1);
ExpectIntEQ(seen.badObj, 0);
ExpectIntEQ(seen.wiped, 0);
if (key != NULL) {
ExpectIntEQ(key->devId, INVALID_DEVID);
ExpectIntEQ(key->shake.devId, INVALID_DEVID);
}
wc_MlDsaKey_Free(key);
ExpectIntEQ(seen.frees, 1);
/* A device that fails still leaves the key cleaned up locally. */
seen.ret = WC_NO_ERR_TRACE(WC_HW_E);
ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, TEST_MLDSA_CB_FREE_DEVID), 0);
wc_MlDsaKey_Free(key);
ExpectIntEQ(seen.frees, 2);
if (key != NULL) {
ExpectIntEQ(key->devId, INVALID_DEVID);
}
seen.ret = 0;
ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, INVALID_DEVID), 0);
wc_MlDsaKey_Free(key);
ExpectIntEQ(seen.frees, 2);
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wc_CryptoCb_UnRegisterDevice(TEST_MLDSA_CB_FREE_DEVID);
#endif
return EXPECT_RESULT();
}

View File

@ -68,6 +68,7 @@ int test_wc_MldsaFeatureCoverage(void);
int test_mldsa_legacy_shim(void);
int test_wc_MldsaDecisionCoverage2(void);
int test_wc_MldsaDerDecisionCoverage(void);
int test_mldsa_cb_free(void);
#define TEST_MLDSA_DECLS \
TEST_DECL_GROUP("mldsa", test_mldsa), \
@ -99,6 +100,7 @@ int test_wc_MldsaDerDecisionCoverage(void);
TEST_DECL_GROUP("mldsa", test_wc_MldsaFeatureCoverage), \
TEST_DECL_GROUP("mldsa", test_mldsa_legacy_shim), \
TEST_DECL_GROUP("mldsa", test_wc_MldsaDecisionCoverage2), \
TEST_DECL_GROUP("mldsa", test_wc_MldsaDerDecisionCoverage)
TEST_DECL_GROUP("mldsa", test_wc_MldsaDerDecisionCoverage), \
TEST_DECL_GROUP("mldsa", test_mldsa_cb_free)
#endif /* WOLFCRYPT_TEST_MLDSA_H */

View File

@ -32,6 +32,7 @@
#include <wolfssl/wolfcrypt/wc_mlkem.h>
#endif
#include <wolfssl/wolfcrypt/types.h>
#include <wolfssl/wolfcrypt/cryptocb.h>
#include <tests/api/api.h>
#include <tests/api/test_mlkem.h>
@ -4648,3 +4649,129 @@ int test_wc_mlkem_encode_key_len_decision(void)
#endif
return EXPECT_RESULT();
} /* END test_wc_mlkem_encode_key_len_decision */
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) && \
defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE)
#define TEST_MLKEM_CB_FREE
#define TEST_MLKEM_CB_FREE_DEVID 0x4D4C4B4D
#ifndef WOLFSSL_NO_ML_KEM_512
#define TEST_MLKEM_CB_FREE_TYPE WC_ML_KEM_512
#elif !defined(WOLFSSL_NO_ML_KEM_768)
#define TEST_MLKEM_CB_FREE_TYPE WC_ML_KEM_768
#elif !defined(WOLFSSL_NO_ML_KEM_1024)
#define TEST_MLKEM_CB_FREE_TYPE WC_ML_KEM_1024
#else
#undef TEST_MLKEM_CB_FREE
#endif
#endif
#ifdef TEST_MLKEM_CB_FREE
/* What the free callback saw, so the test can check the contract rather than
* just that something fired. */
typedef struct {
int frees; /* matching free callbacks seen */
int badObj; /* callback was handed the wrong object */
int wiped; /* callback saw a key already cleaned up */
int ret; /* what the callback returns */
const void* obj; /* object the free is expected to name */
} MlKemCbFreeCtx;
/* Stands in for a device holding state for the key. Counting the call proves
* wc_MlKemKey_Free told the device rather than only cleaning up in software,
* which would leave the device side of the key behind. */
static int mlkem_cb_free_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
{
MlKemCbFreeCtx* seen = (MlKemCbFreeCtx*)ctx;
(void)devIdArg;
if ((seen != NULL) && (info != NULL) &&
(info->algo_type == WC_ALGO_TYPE_FREE) &&
(info->free.algo == WC_ALGO_TYPE_PK) &&
(info->free.type == WC_PK_TYPE_PQC_KEM_KEYGEN) &&
(info->free.subType == WC_PQC_KEM_TYPE_MLKEM)) {
const MlKemKey* mk = (const MlKemKey*)info->free.obj;
seen->frees++;
if ((mk == NULL) || ((const void*)mk != seen->obj)) {
seen->badObj++;
}
/* The device gets the key while it is still whole: it may need to
* read it to release the right resource, so the software wipe has
* to come after this call, not before. */
else if (mk->devId != TEST_MLKEM_CB_FREE_DEVID) {
seen->wiped++;
}
return seen->ret;
}
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
}
#endif /* TEST_MLKEM_CB_FREE */
/* Freeing a key that names a device has to tell that device, so it can
* release what it holds. A key with no device must not, and neither must a
* second free of a key already freed: neither the key nor the hash and PRF
* objects inside it name a device once freed. A device that reports an error
* does not stop the software cleanup. */
int test_wc_mlkem_cb_free(void)
{
EXPECT_DECLS;
#ifdef TEST_MLKEM_CB_FREE
MlKemKey* key = NULL;
MlKemCbFreeCtx seen;
int freeRet;
XMEMSET(&seen, 0, sizeof(seen));
/* No key to free, and nothing to tell a device about. */
ExpectIntEQ(wc_MlKemKey_Free(NULL), 0);
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_MLKEM_CB_FREE_DEVID,
mlkem_cb_free_cb, &seen), 0);
ExpectNotNull(key = (MlKemKey*)XMALLOC(sizeof(MlKemKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER));
seen.obj = key;
ExpectIntEQ(wc_MlKemKey_Init(key, TEST_MLKEM_CB_FREE_TYPE, NULL,
TEST_MLKEM_CB_FREE_DEVID), 0);
/* Freed outside the assertion so the hash and PRF objects are always
* disposed of, even once an earlier check has failed. */
freeRet = wc_MlKemKey_Free(key);
ExpectIntEQ(freeRet, 0);
ExpectIntEQ(seen.frees, 1);
ExpectIntEQ(seen.badObj, 0);
ExpectIntEQ(seen.wiped, 0);
if (key != NULL) {
ExpectIntEQ(key->devId, INVALID_DEVID);
ExpectIntEQ(key->hash.devId, INVALID_DEVID);
ExpectIntEQ(key->prf.devId, INVALID_DEVID);
}
freeRet = wc_MlKemKey_Free(key);
ExpectIntEQ(freeRet, 0);
ExpectIntEQ(seen.frees, 1);
/* A device that fails still leaves the key cleaned up locally. */
seen.ret = WC_NO_ERR_TRACE(WC_HW_E);
ExpectIntEQ(wc_MlKemKey_Init(key, TEST_MLKEM_CB_FREE_TYPE, NULL,
TEST_MLKEM_CB_FREE_DEVID), 0);
freeRet = wc_MlKemKey_Free(key);
ExpectIntEQ(freeRet, 0);
ExpectIntEQ(seen.frees, 2);
if (key != NULL) {
ExpectIntEQ(key->devId, INVALID_DEVID);
}
seen.ret = 0;
ExpectIntEQ(wc_MlKemKey_Init(key, TEST_MLKEM_CB_FREE_TYPE, NULL,
INVALID_DEVID), 0);
freeRet = wc_MlKemKey_Free(key);
ExpectIntEQ(freeRet, 0);
ExpectIntEQ(seen.frees, 2);
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wc_CryptoCb_UnRegisterDevice(TEST_MLKEM_CB_FREE_DEVID);
#endif
return EXPECT_RESULT();
}

View File

@ -36,6 +36,7 @@ int test_wc_mlkem_init_id_decision(void);
int test_wc_mlkem_init_label_decision(void);
int test_wc_mlkem_encapsulate_pubkey_unset_decision(void);
int test_wc_mlkem_encode_key_len_decision(void);
int test_wc_mlkem_cb_free(void);
#define TEST_MLKEM_DECLS \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_make_key_kats), \
@ -49,6 +50,7 @@ int test_wc_mlkem_encode_key_len_decision(void);
TEST_DECL_GROUP("mlkem", test_wc_mlkem_init_id_decision), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_init_label_decision), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_encapsulate_pubkey_unset_decision), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_encode_key_len_decision)
TEST_DECL_GROUP("mlkem", test_wc_mlkem_encode_key_len_decision), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_cb_free)
#endif /* WOLFCRYPT_TEST_MLKEM_H */

View File

@ -3871,3 +3871,106 @@ int test_slhdsa_get_sigalg_info(void)
#endif /* WOLFSSL_HAVE_SLHDSA && OPENSSL_EXTRA */
return EXPECT_RESULT();
}
#if defined(WOLFSSL_HAVE_SLHDSA) && defined(WOLF_CRYPTO_CB) && \
defined(WOLF_CRYPTO_CB_FREE)
#define TEST_SLHDSA_CB_FREE
#define TEST_SLHDSA_CB_FREE_DEVID 0x534C4844
#endif
#ifdef TEST_SLHDSA_CB_FREE
/* What the free callback saw, so the test can check the contract rather than
* just that something fired. */
typedef struct {
int frees; /* matching free callbacks seen */
int badObj; /* callback was handed the wrong object */
int wiped; /* callback saw a key already cleaned up */
int ret; /* what the callback returns */
const void* obj; /* object the free is expected to name */
} SlhDsaCbFreeCtx;
/* Stands in for a device holding state for the key. Counting the call proves
* wc_SlhDsaKey_Free told the device rather than only cleaning up in software,
* which would leave the device side of the key behind. */
static int slhdsa_cb_free_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
{
SlhDsaCbFreeCtx* seen = (SlhDsaCbFreeCtx*)ctx;
(void)devIdArg;
if ((seen != NULL) && (info != NULL) &&
(info->algo_type == WC_ALGO_TYPE_FREE) &&
(info->free.algo == WC_ALGO_TYPE_PK) &&
(info->free.type == WC_PK_TYPE_PQC_SIG_KEYGEN) &&
(info->free.subType == WC_PQC_SIG_TYPE_SLHDSA)) {
const SlhDsaKey* slh = (const SlhDsaKey*)info->free.obj;
seen->frees++;
if ((slh == NULL) || ((const void*)slh != seen->obj)) {
seen->badObj++;
}
/* The device gets the key while it is still whole: it may need to
* read it to release the right resource, so the software wipe has
* to come after this call, not before. */
else if ((slh->devId != TEST_SLHDSA_CB_FREE_DEVID) ||
(slh->params == NULL)) {
seen->wiped++;
}
return seen->ret;
}
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
}
#endif /* TEST_SLHDSA_CB_FREE */
/* Freeing a key that names a device has to tell that device, so it can
* release what it holds. A key with no device must not, and neither must a
* second free of a key already freed: a freed key names no device. A device
* that reports an error does not stop the software cleanup. */
int test_slhdsa_cb_free(void)
{
EXPECT_DECLS;
#ifdef TEST_SLHDSA_CB_FREE
SlhDsaKey key;
SlhDsaCbFreeCtx seen;
XMEMSET(&key, 0, sizeof(key));
XMEMSET(&seen, 0, sizeof(seen));
seen.obj = &key;
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_SLHDSA_CB_FREE_DEVID,
slhdsa_cb_free_cb, &seen), 0);
ExpectIntEQ(wc_SlhDsaKey_Init(&key, WC_SLHDSA_DEFAULT_PARAM, NULL,
TEST_SLHDSA_CB_FREE_DEVID), 0);
wc_SlhDsaKey_Free(&key);
ExpectIntEQ(seen.frees, 1);
ExpectIntEQ(seen.badObj, 0);
ExpectIntEQ(seen.wiped, 0);
ExpectIntEQ(key.devId, INVALID_DEVID);
ExpectNull(key.params);
wc_SlhDsaKey_Free(&key);
ExpectIntEQ(seen.frees, 1);
/* A device that fails still leaves the key cleaned up locally. */
seen.ret = WC_NO_ERR_TRACE(WC_HW_E);
XMEMSET(&key, 0, sizeof(key));
ExpectIntEQ(wc_SlhDsaKey_Init(&key, WC_SLHDSA_DEFAULT_PARAM, NULL,
TEST_SLHDSA_CB_FREE_DEVID), 0);
wc_SlhDsaKey_Free(&key);
ExpectIntEQ(seen.frees, 2);
ExpectIntEQ(key.devId, INVALID_DEVID);
ExpectNull(key.params);
seen.ret = 0;
XMEMSET(&key, 0, sizeof(key));
ExpectIntEQ(wc_SlhDsaKey_Init(&key, WC_SLHDSA_DEFAULT_PARAM, NULL,
INVALID_DEVID), 0);
wc_SlhDsaKey_Free(&key);
ExpectIntEQ(seen.frees, 2);
wc_CryptoCb_UnRegisterDevice(TEST_SLHDSA_CB_FREE_DEVID);
#endif
return EXPECT_RESULT();
}

View File

@ -49,6 +49,7 @@ int test_slhdsa_tls13_certverify_multi_stall(void);
int test_mldsa_tls13_certverify_maxfrag_stream(void);
int test_slhdsa_dev_private_key(void);
int test_slhdsa_tls13_certverify_bad_signature(void);
int test_slhdsa_cb_free(void);
#define TEST_SLHDSA_DECLS \
TEST_DECL_GROUP("slhdsa", test_wc_slhdsa), \
@ -75,6 +76,7 @@ int test_slhdsa_tls13_certverify_bad_signature(void);
TEST_DECL_GROUP("slhdsa", test_slhdsa_tls13_certverify_multi_stall), \
TEST_DECL_GROUP("slhdsa", test_mldsa_tls13_certverify_maxfrag_stream), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_dev_private_key), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_tls13_certverify_bad_signature)
TEST_DECL_GROUP("slhdsa", test_slhdsa_tls13_certverify_bad_signature), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_cb_free)
#endif /* WOLFCRYPT_TEST_SLHDSA_H */

View File

@ -9046,7 +9046,21 @@ int wc_falcon_get_level(falcon_key* key, byte* level)
void wc_falcon_free(falcon_key* key)
{
if (key != NULL) {
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE)
if (key->devId != INVALID_DEVID) {
(void)wc_CryptoCb_Free(key->devId, WC_ALGO_TYPE_PK,
WC_PK_TYPE_PQC_SIG_KEYGEN,
WC_PQC_SIG_TYPE_FALCON,
(void*)key);
/* always continue to software cleanup */
}
#endif
ForceZero(key, sizeof(*key));
#ifdef WOLF_CRYPTO_CB
/* Zeroing leaves devId at 0, which is a usable device id. Mark the
* key as having no device so a second free does not call out again. */
key->devId = INVALID_DEVID;
#endif
}
}

View File

@ -12185,6 +12185,13 @@ void wc_MlDsaKey_Free(wc_MlDsaKey* key)
#endif
/* Ensure all private data is zeroized. */
ForceZero(key, sizeof(*key));
#ifdef WOLF_CRYPTO_CB
/* Zeroing leaves devId at 0, which is a usable device id. Mark the
* key and the SHAKE object inside it as having no device: a second
* free runs the SHAKE free again and would otherwise call out. */
key->devId = INVALID_DEVID;
key->shake.devId = INVALID_DEVID;
#endif
}
}

View File

@ -591,6 +591,10 @@ int wc_MlKemKey_Free(MlKemKey* key)
/* Ensure all private data is zeroed. */
ForceZero(&key->hash, sizeof(key->hash));
ForceZero(&key->prf, sizeof(key->prf));
#ifdef WOLF_CRYPTO_CB
key->hash.devId = INVALID_DEVID;
key->prf.devId = INVALID_DEVID;
#endif
#ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS
if (key->priv != NULL) {
ForceZero(key->priv, key->privAllocSz);
@ -615,6 +619,12 @@ int wc_MlKemKey_Free(MlKemKey* key)
/* Clear flags as values are no longer set. */
key->flags = 0;
#ifdef WOLF_CRYPTO_CB
/* Mark the key as having no device so a second free does not call
* out to it again. */
key->devCtx = NULL;
key->devId = INVALID_DEVID;
#endif
}
return 0;

View File

@ -82685,7 +82685,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
break;
}
#endif
#if defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA)
#if defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA) || \
defined(HAVE_FALCON)
case WC_PK_TYPE_PQC_SIG_KEYGEN:
{
#ifdef WOLFSSL_HAVE_MLDSA
@ -82703,6 +82704,14 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
wc_SlhDsaKey_Free(slh);
ret = 0;
}
#endif
#ifdef HAVE_FALCON
if (info->free.subType == WC_PQC_SIG_TYPE_FALCON) {
falcon_key* fk = (falcon_key*)info->free.obj;
fk->devId = INVALID_DEVID;
wc_falcon_free(fk);
ret = 0;
}
#endif
break;
}