From debc59f70b7e0e1ef92250f2e95ea5993d59ca8d Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 1 Jul 2026 07:38:23 +0200 Subject: [PATCH] Falcon: crypto callback (WOLF_CRYPTO_CB) interface + CB_ONLY Wire Falcon into the crypto callback framework like the other algorithms: - wc_falcon_make_key now dispatches to wc_CryptoCb_MakePqcSignatureKey (WC_PQC_SIG_TYPE_FALCON); wc_falcon_sign_msg / wc_falcon_verify_msg already dispatched to wc_CryptoCb_PqcSign / PqcVerify. All three fall through to the software implementation when the callback is unavailable. - Add WOLF_CRYPTO_CB_ONLY_FALCON (mirrors WOLF_CRYPTO_CB_ONLY_RSA/ECC): the callback becomes authoritative (no software fallback; returns NO_VALID_DEVID when no device is registered) and the native core (wc_falcon*.c) is compiled out entirely. WC_FALCON_HAVE_NATIVE_SIGN and the falcon_native_* prototypes are gated off in that build. Tests (test.c): - myCryptoDevCb gains a Falcon branch for PQC keygen/sign/verify. - falcon_test / falcon_verify_kat now use the global test devId, so cryptocb_test drives every Falcon operation through the callback and asserts (via the exampleVar hit counter) that the cb path was actually taken. - Under WOLF_CRYPTO_CB_ONLY_FALCON, falcon_test instead confirms the API returns NO_VALID_DEVID with no device registered, and the KAT data/verifier (software-only) are compiled out. Verified: default (no cryptocb), --enable-cryptocb, and -DWOLF_CRYPTO_CB_ONLY_FALCON all build and pass testwolfcrypt (falcon_test + crypto callback test); the CB_ONLY library contains no falcon_native_* symbols. --- wolfcrypt/src/falcon.c | 39 +++++++++++-- wolfcrypt/src/wc_falcon.c | 2 +- wolfcrypt/src/wc_falcon_bigint.c | 2 +- wolfcrypt/src/wc_falcon_codec.c | 2 +- wolfcrypt/src/wc_falcon_fft.c | 2 +- wolfcrypt/src/wc_falcon_fft_avx2.c | 2 +- wolfcrypt/src/wc_falcon_fpr.c | 2 +- wolfcrypt/src/wc_falcon_keygen.c | 2 +- wolfcrypt/src/wc_falcon_poly.c | 2 +- wolfcrypt/src/wc_falcon_sampler.c | 2 +- wolfcrypt/src/wc_falcon_sign.c | 2 +- wolfcrypt/test/test.c | 89 +++++++++++++++++++++++++++++- wolfssl/wolfcrypt/falcon.h | 6 +- 13 files changed, 134 insertions(+), 20 deletions(-) diff --git a/wolfcrypt/src/falcon.c b/wolfcrypt/src/falcon.c index 307c3e856a..ec32f829ca 100644 --- a/wolfcrypt/src/falcon.c +++ b/wolfcrypt/src/falcon.c @@ -67,7 +67,7 @@ static void falcon_store_pub_behind_priv(falcon_key* key) */ int wc_falcon_make_key(falcon_key* key, WC_RNG* rng) { - int ret; + int ret = 0; if ((key == NULL) || (rng == NULL)) { return BAD_FUNC_ARG; @@ -76,10 +76,29 @@ int wc_falcon_make_key(falcon_key* key, WC_RNG* rng) return BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_MakePqcSignatureKey(rng, WC_PQC_SIG_TYPE_FALCON, + key->level, key); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + ret = 0; + } +#endif /* WOLF_CRYPTO_CB */ + +#ifdef WOLF_CRYPTO_CB_ONLY_FALCON + /* No software fallback: only a crypto callback can service the request. */ + ret = NO_VALID_DEVID; +#else ret = falcon_native_make_key(key, rng); if (ret == 0) { falcon_store_pub_behind_priv(key); } +#endif return ret; } #endif /* !WOLFSSL_FALCON_VERIFY_ONLY */ @@ -119,9 +138,14 @@ int wc_falcon_sign_msg(const byte* in, word32 inLen, /* fall-through when unavailable */ ret = 0; } -#endif +#endif /* WOLF_CRYPTO_CB */ -#ifndef WOLFSSL_FALCON_VERIFY_ONLY +#ifdef WOLF_CRYPTO_CB_ONLY_FALCON + /* No software fallback: only a crypto callback can service the request. */ + ret = NO_VALID_DEVID; +#elif defined(WOLFSSL_FALCON_VERIFY_ONLY) + ret = NOT_COMPILED_IN; +#else if ((ret == 0) && (!key->prvKeySet)) { ret = BAD_FUNC_ARG; } @@ -129,8 +153,6 @@ int wc_falcon_sign_msg(const byte* in, word32 inLen, if (ret == 0) { ret = falcon_native_sign_msg(in, inLen, out, outLen, key, rng); } -#else - ret = NOT_COMPILED_IN; #endif return ret; } @@ -168,8 +190,12 @@ int wc_falcon_verify_msg(const byte* sig, word32 sigLen, const byte* msg, /* fall-through when unavailable */ ret = 0; } -#endif +#endif /* WOLF_CRYPTO_CB */ +#ifdef WOLF_CRYPTO_CB_ONLY_FALCON + /* No software fallback: only a crypto callback can service the request. */ + ret = NO_VALID_DEVID; +#else if ((ret == 0) && (!key->pubKeySet)) { ret = BAD_FUNC_ARG; } @@ -177,6 +203,7 @@ int wc_falcon_verify_msg(const byte* sig, word32 sigLen, const byte* msg, if (ret == 0) { ret = falcon_native_verify_msg(sig, sigLen, msg, msgLen, res, key); } +#endif return ret; } diff --git a/wolfcrypt/src/wc_falcon.c b/wolfcrypt/src/wc_falcon.c index e0336d3591..03ca9ca757 100644 --- a/wolfcrypt/src/wc_falcon.c +++ b/wolfcrypt/src/wc_falcon.c @@ -27,7 +27,7 @@ #include -#if defined(HAVE_FALCON) +#if defined(HAVE_FALCON) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include #include diff --git a/wolfcrypt/src/wc_falcon_bigint.c b/wolfcrypt/src/wc_falcon_bigint.c index 226cc3aecc..45cac84a99 100644 --- a/wolfcrypt/src/wc_falcon_bigint.c +++ b/wolfcrypt/src/wc_falcon_bigint.c @@ -30,7 +30,7 @@ #include -#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) +#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include diff --git a/wolfcrypt/src/wc_falcon_codec.c b/wolfcrypt/src/wc_falcon_codec.c index 3e9c28c09d..e4398430da 100644 --- a/wolfcrypt/src/wc_falcon_codec.c +++ b/wolfcrypt/src/wc_falcon_codec.c @@ -29,7 +29,7 @@ #include -#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) +#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include #include diff --git a/wolfcrypt/src/wc_falcon_fft.c b/wolfcrypt/src/wc_falcon_fft.c index 7af93e022e..2f6d38b992 100644 --- a/wolfcrypt/src/wc_falcon_fft.c +++ b/wolfcrypt/src/wc_falcon_fft.c @@ -25,7 +25,7 @@ #include -#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) +#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include diff --git a/wolfcrypt/src/wc_falcon_fft_avx2.c b/wolfcrypt/src/wc_falcon_fft_avx2.c index 912627f08e..e8257c65e6 100644 --- a/wolfcrypt/src/wc_falcon_fft_avx2.c +++ b/wolfcrypt/src/wc_falcon_fft_avx2.c @@ -53,7 +53,7 @@ #include -#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && \ +#if defined(HAVE_FALCON) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && \ defined(WOLFSSL_FALCON_FFT_AVX2) #include diff --git a/wolfcrypt/src/wc_falcon_fpr.c b/wolfcrypt/src/wc_falcon_fpr.c index 733664607b..32942990c9 100644 --- a/wolfcrypt/src/wc_falcon_fpr.c +++ b/wolfcrypt/src/wc_falcon_fpr.c @@ -36,7 +36,7 @@ #include -#if defined(HAVE_FALCON) +#if defined(HAVE_FALCON) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include diff --git a/wolfcrypt/src/wc_falcon_keygen.c b/wolfcrypt/src/wc_falcon_keygen.c index d97d8c02c3..fe2c179a8b 100644 --- a/wolfcrypt/src/wc_falcon_keygen.c +++ b/wolfcrypt/src/wc_falcon_keygen.c @@ -31,7 +31,7 @@ #include -#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) +#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include #include diff --git a/wolfcrypt/src/wc_falcon_poly.c b/wolfcrypt/src/wc_falcon_poly.c index 98ebf1a675..2415a95963 100644 --- a/wolfcrypt/src/wc_falcon_poly.c +++ b/wolfcrypt/src/wc_falcon_poly.c @@ -25,7 +25,7 @@ #include -#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) +#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include #include /* falcon_gm_tab */ diff --git a/wolfcrypt/src/wc_falcon_sampler.c b/wolfcrypt/src/wc_falcon_sampler.c index 476fe8a2bc..d9bcc81867 100644 --- a/wolfcrypt/src/wc_falcon_sampler.c +++ b/wolfcrypt/src/wc_falcon_sampler.c @@ -57,7 +57,7 @@ #include -#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) +#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include #include diff --git a/wolfcrypt/src/wc_falcon_sign.c b/wolfcrypt/src/wc_falcon_sign.c index b0b5cc90b6..45f227ff1a 100644 --- a/wolfcrypt/src/wc_falcon_sign.c +++ b/wolfcrypt/src/wc_falcon_sign.c @@ -31,7 +31,7 @@ #include -#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) +#if defined(HAVE_FALCON) && !defined(WOLFSSL_FALCON_VERIFY_ONLY) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) #include #include diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 8985dd7a2b..1051491e3b 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -57713,6 +57713,9 @@ static wc_test_ret_t mldsa_decode_test(void) * then drop pk / sig / siglen into the arrays below. (liboqs >= 0.11 API.) */ +/* The KAT vectors and their verifier exercise the software verify path, which + * is not present in a WOLF_CRYPTO_CB_ONLY_FALCON build. */ +#ifndef WOLF_CRYPTO_CB_ONLY_FALCON #define FALCON_KAT_MSG "wolfSSL FN-DSA differential KAT" static const byte FALCON512_pk[] = { @@ -58125,7 +58128,9 @@ static wc_test_ret_t falcon_verify_kat(byte level, const byte* pk, word32 pkLen, const byte* msg = (const byte*)FALCON_KAT_MSG; word32 msgLen = (word32)XSTRLEN(FALCON_KAT_MSG); - ret = wc_falcon_init(&key); + /* Use the global test devId so that, when cryptocb_test() has registered a + * device, verification is routed through the crypto callback. */ + ret = wc_falcon_init_ex(&key, HEAP_HINT, devId); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); @@ -58135,7 +58140,7 @@ static wc_test_ret_t falcon_verify_kat(byte level, const byte* pk, word32 pkLen, ret = wc_falcon_import_public(pk, pkLen, &key); if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto out; } - /* A genuine liboqs-produced signature must verify (res == 1). */ + /* A genuine reference-produced signature must verify (res == 1). */ res = 0; ret = wc_falcon_verify_msg(sig, sigLen, msg, msgLen, &res, &key); if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto out; } @@ -58157,11 +58162,13 @@ out: wc_falcon_free(&key); return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_FALCON */ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t falcon_test(void) { wc_test_ret_t ret; +#ifndef WOLF_CRYPTO_CB_ONLY_FALCON ret = falcon_verify_kat(FALCON_LEVEL1, FALCON512_pk, (word32)sizeof(FALCON512_pk), FALCON512_sig, FALCON512_SIGLEN); if (ret != 0) @@ -58190,7 +58197,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t falcon_test(void) word32 siglen = (word32)sizeof(sig); int res = 0; - ret = wc_falcon_init(&k); + ret = wc_falcon_init_ex(&k, HEAP_HINT, devId); if (ret == 0) ret = wc_falcon_set_level(&k, falconLvls[li]); if (ret == 0) @@ -58220,6 +58227,29 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t falcon_test(void) wc_FreeRng(&rng); } #endif /* WC_FALCON_HAVE_NATIVE_SIGN */ +#else /* WOLF_CRYPTO_CB_ONLY_FALCON */ + /* Software Falcon is compiled out. Confirm the public API refuses an + * operation when no crypto-callback device is available (INVALID_DEVID), + * rather than silently doing nothing. */ + { + falcon_key k; + int res = 0; + int r; + + ret = wc_falcon_init(&k); + if (ret == 0) + ret = wc_falcon_set_level(&k, FALCON_LEVEL1); + if (ret == 0) { + r = wc_falcon_verify_msg((const byte*)"m", 1, (const byte*)"m", 1, + &res, &k); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + wc_falcon_free(&k); + if (ret != 0) + return ret; + } +#endif /* !WOLF_CRYPTO_CB_ONLY_FALCON */ return 0; } @@ -77679,6 +77709,45 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } } #endif /* WOLFSSL_HAVE_SLHDSA */ + #if defined(HAVE_FALCON) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) + #ifndef WOLFSSL_FALCON_VERIFY_ONLY + if (info->pk.type == WC_PK_TYPE_PQC_SIG_KEYGEN && + info->pk.pqc_sig_kg.type == WC_PQC_SIG_TYPE_FALCON) { + falcon_key* fk = (falcon_key*)info->pk.pqc_sig_kg.key; + /* set devId invalid so the software path is used (no recursion) */ + fk->devId = INVALID_DEVID; + ret = wc_falcon_make_key(fk, info->pk.pqc_sig_kg.rng); + fk->devId = devIdArg; + myCtx->exampleVar++; + } + else if (info->pk.type == WC_PK_TYPE_PQC_SIG_SIGN && + info->pk.pqc_sign.type == WC_PQC_SIG_TYPE_FALCON) { + falcon_key* fk = (falcon_key*)info->pk.pqc_sign.key; + fk->devId = INVALID_DEVID; + ret = wc_falcon_sign_msg(info->pk.pqc_sign.in, + info->pk.pqc_sign.inlen, info->pk.pqc_sign.out, + info->pk.pqc_sign.outlen, fk, info->pk.pqc_sign.rng); + fk->devId = devIdArg; + myCtx->exampleVar++; + } + else + #endif /* !WOLFSSL_FALCON_VERIFY_ONLY */ + if (info->pk.type == WC_PK_TYPE_PQC_SIG_VERIFY && + info->pk.pqc_verify.type == WC_PQC_SIG_TYPE_FALCON) { + falcon_key* fk = (falcon_key*)info->pk.pqc_verify.key; + int verifyRet; + fk->devId = INVALID_DEVID; + verifyRet = wc_falcon_verify_msg(info->pk.pqc_verify.sig, + info->pk.pqc_verify.siglen, info->pk.pqc_verify.msg, + info->pk.pqc_verify.msglen, info->pk.pqc_verify.res, fk); + fk->devId = devIdArg; + /* SIG_VERIFY_E is a validity signal, not a crypto error. */ + if (verifyRet == WC_NO_ERR_TRACE(SIG_VERIFY_E)) + verifyRet = 0; + ret = verifyRet; + myCtx->exampleVar++; + } + #endif /* HAVE_FALCON && !WOLF_CRYPTO_CB_ONLY_FALCON */ #ifdef WOLFSSL_HAVE_MLKEM if (info->pk.type == WC_PK_TYPE_PQC_KEM_KEYGEN) { if ((info->pk.pqc_kem_kg.type == WC_PQC_KEM_TYPE_MLKEM) && @@ -79639,6 +79708,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) myCtx.exampleVar = baseline; } #endif +#if defined(HAVE_FALCON) && !defined(WOLF_CRYPTO_CB_ONLY_FALCON) + if (ret == 0) { + /* Route Falcon through the crypto callback (global devId is set) and + * confirm the cb path was actually exercised via the hit counter, so a + * silent software fallback can't mask a dispatch regression. Both the + * KAT verify and (when built) the native keygen/sign/verify round-trip + * use the test devId. */ + int baseline = myCtx.exampleVar; + ret = falcon_test(); + if ((ret == 0) && (myCtx.exampleVar == baseline)) + ret = WC_TEST_RET_ENC_NC; + myCtx.exampleVar = baseline; + } +#endif #if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) if (ret == 0) ret = xmss_test(); diff --git a/wolfssl/wolfcrypt/falcon.h b/wolfssl/wolfcrypt/falcon.h index dc08ca9344..c2e94513f9 100644 --- a/wolfssl/wolfcrypt/falcon.h +++ b/wolfssl/wolfcrypt/falcon.h @@ -213,7 +213,10 @@ WOLFSSL_API int wc_Falcon_PublicKeyToDer(falcon_key* key, byte* output, word32 inLen, int withAlg); /* Native implementation core (internal). The public wc_falcon_* functions in - * falcon.c wrap these with cryptocb dispatch and argument checking. */ + * falcon.c wrap these with cryptocb dispatch and argument checking. With + * WOLF_CRYPTO_CB_ONLY_FALCON the native core is not compiled: all operations go + * through the crypto callback. */ +#ifndef WOLF_CRYPTO_CB_ONLY_FALCON #ifndef WOLFSSL_FALCON_VERIFY_ONLY /* Signals that native signing and key generation are available. */ #define WC_FALCON_HAVE_NATIVE_SIGN @@ -223,6 +226,7 @@ WOLFSSL_LOCAL int falcon_native_sign_msg(const byte* in, word32 inLen, #endif WOLFSSL_LOCAL int falcon_native_verify_msg(const byte* sig, word32 sigLen, const byte* msg, word32 msgLen, int* res, falcon_key* key); +#endif /* !WOLF_CRYPTO_CB_ONLY_FALCON */ #ifdef __cplusplus } /* extern "C" */