From 772a62628ec8855fd153679a760bcae01b9d38d6 Mon Sep 17 00:00:00 2001 From: night1rider Date: Thu, 6 Aug 2026 15:18:03 -0600 Subject: [PATCH 1/9] Add crypto callback hooks for Ed448, CMAC free, and RSA-PSS verify Add WOLF_CRYPTO_CB dispatch hooks so a device can service: * Ed448 sign and verify, mirroring the existing Ed25519 hooks. * CMAC context free on wc_CmacFree (WOLF_CRYPTO_CB_FREE), letting a device release offload state. * RSA-PSS verify with the digest (WOLF_CRYPTO_CB_RSA_PAD) so the device does the full signature and padding check. On that path *out is set to NULL with a positive return, documented in rsa.h. Includes testwolfcrypt and API unit test coverage for each hook. --- doc/dox_comments/header_files/rsa.h | 5 +- tests/api/test_cmac.c | 77 +++++++ tests/api/test_cmac.h | 4 +- tests/api/test_ed448.c | 126 ++++++++++++ tests/api/test_ed448.h | 4 +- tests/api/test_rsa.c | 154 ++++++++++++++ tests/api/test_rsa.h | 4 +- wolfcrypt/src/cmac.c | 11 + wolfcrypt/src/cryptocb.c | 106 ++++++++++ wolfcrypt/src/ed448.c | 32 +++ wolfcrypt/src/rsa.c | 50 +++++ wolfcrypt/test/test.c | 299 ++++++++++++++++++++++++++++ wolfssl/wolfcrypt/cryptocb.h | 51 +++++ wolfssl/wolfcrypt/types.h | 5 +- 14 files changed, 923 insertions(+), 5 deletions(-) diff --git a/doc/dox_comments/header_files/rsa.h b/doc/dox_comments/header_files/rsa.h index c0cbab28308..8d67d591ce2 100644 --- a/doc/dox_comments/header_files/rsa.h +++ b/doc/dox_comments/header_files/rsa.h @@ -742,10 +742,13 @@ int wc_RsaPSS_VerifyCheck_ex(byte* in, word32 inLen, The key has to be associated with RNG by wc_RsaSetRNG when WC_RSA_BLINDING is enabled. \return the length of the PSS data on success and negative indicates failure. + On the crypto callback path *out is set to NULL though the return stays + positive, so callers must not dereference *out. \param in The byte array to be decrypted. \param inLen The length of in. - \param out The byte array for the decrypted data to be stored. + \param out The byte array for the decrypted data to be stored. Set to NULL + when a crypto callback device performed the verify (see \return). \param digest Hash of the data that is being verified. \param digestLen Length of hash. \param hash The hash type to be in message diff --git a/tests/api/test_cmac.c b/tests/api/test_cmac.c index 5b0b9423724..9659935b153 100644 --- a/tests/api/test_cmac.c +++ b/tests/api/test_cmac.c @@ -720,3 +720,80 @@ int test_wc_AesCmacVerify_CryptoCb_LenMismatch(void) return EXPECT_RESULT(); } /* END test_wc_AesCmacVerify_CryptoCb_LenMismatch */ +/* Test that wc_CmacFree() dispatches a WC_ALGO_TYPE_FREE / WC_ALGO_TYPE_CMAC + * request to a registered crypto callback (CryptoCb) device. */ +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) && \ + defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) +/* Spy device: declines every request (software handles the work) but counts + * CMAC free dispatches. */ +static int cmac_free_test_crypto_cb(int devIdArg, wc_CryptoInfo* info, void* ctx) +{ + int* freeSeen = (int*)ctx; + + (void)devIdArg; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + if (info->algo_type == WC_ALGO_TYPE_FREE && + info->free.algo == WC_ALGO_TYPE_CMAC) { + if (freeSeen != NULL) { + (*freeSeen)++; + } + } + + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); +} +#endif + +int test_wc_CryptoCb_CmacFree(void) +{ + EXPECT_DECLS; +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) && \ + defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) + int devId = 4460; + int freeSeen = 0; + byte key16[WC_AES_BLOCK_SIZE] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 + }; + byte in[WC_AES_BLOCK_SIZE] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00 + }; + byte tag[WC_AES_BLOCK_SIZE]; + word32 tagSz = (word32)sizeof(tag); + WC_DECLARE_VAR(cmac, Cmac, 1, HEAP_HINT); + + WC_ALLOC_VAR(cmac, Cmac, 1, HEAP_HINT); +#ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC + ExpectNotNull(cmac); +#endif + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(devId, cmac_free_test_crypto_cb, + &freeSeen), 0); + + /* wc_CmacFinal() internally frees the Cmac -> free dispatched to device. */ + ExpectIntEQ(wc_InitCmac_ex(cmac, key16, (word32)sizeof(key16), WC_CMAC_AES, + NULL, HEAP_HINT, devId), 0); + ExpectIntEQ(wc_CmacUpdate(cmac, in, (word32)sizeof(in)), 0); + freeSeen = 0; + ExpectIntEQ(wc_CmacFinal(cmac, tag, &tagSz), 0); + ExpectIntGE(freeSeen, 1); + + /* Explicit wc_CmacFree() also dispatches to the device. */ + tagSz = (word32)sizeof(tag); + ExpectIntEQ(wc_InitCmac_ex(cmac, key16, (word32)sizeof(key16), WC_CMAC_AES, + NULL, HEAP_HINT, devId), 0); + ExpectIntEQ(wc_CmacUpdate(cmac, in, (word32)sizeof(in)), 0); + freeSeen = 0; + ExpectIntEQ(wc_CmacFree(cmac), 0); + ExpectIntGE(freeSeen, 1); + + wc_CryptoCb_UnRegisterDevice(devId); + + WC_FREE_VAR(cmac, HEAP_HINT); +#endif + return EXPECT_RESULT(); +} /* END test_wc_CryptoCb_CmacFree */ diff --git a/tests/api/test_cmac.h b/tests/api/test_cmac.h index e7e4616c0a7..28272c59a64 100644 --- a/tests/api/test_cmac.h +++ b/tests/api/test_cmac.h @@ -34,6 +34,7 @@ int test_wc_InitCmac_Label(void); int test_wc_AesCmacGenerateExDecisionCoverage(void); int test_wc_AesCmacVerifyExDecisionCoverage(void); int test_wc_AesCmacVerify_CryptoCb_LenMismatch(void); +int test_wc_CryptoCb_CmacFree(void); #define TEST_CMAC_DECLS \ TEST_DECL_GROUP("cmac", test_wc_InitCmac), \ @@ -45,6 +46,7 @@ int test_wc_AesCmacVerify_CryptoCb_LenMismatch(void); TEST_DECL_GROUP("cmac", test_wc_InitCmac_Label), \ TEST_DECL_GROUP("cmac", test_wc_AesCmacGenerateExDecisionCoverage), \ TEST_DECL_GROUP("cmac", test_wc_AesCmacVerifyExDecisionCoverage), \ - TEST_DECL_GROUP("cmac", test_wc_AesCmacVerify_CryptoCb_LenMismatch) + TEST_DECL_GROUP("cmac", test_wc_AesCmacVerify_CryptoCb_LenMismatch), \ + TEST_DECL_GROUP("cmac", test_wc_CryptoCb_CmacFree) #endif /* WOLFCRYPT_TEST_CMAC_H */ diff --git a/tests/api/test_ed448.c b/tests/api/test_ed448.c index affae98d404..0d4e0bc3fbb 100644 --- a/tests/api/test_ed448.c +++ b/tests/api/test_ed448.c @@ -30,6 +30,9 @@ #include #include +#ifdef WOLF_CRYPTO_CB + #include +#endif #include #include @@ -1556,3 +1559,126 @@ int test_wc_ed448_check_key_decisions(void) return EXPECT_RESULT(); } /* END test_wc_ed448_check_key_decisions */ +/* Test Ed448 sign/verify routed through a crypto callback (CryptoCb) device. */ +#if defined(WOLF_CRYPTO_CB) && defined(HAVE_ED448) && \ + defined(HAVE_ED448_SIGN) && defined(HAVE_ED448_VERIFY) && \ + !defined(WC_NO_RNG) +typedef struct ed448SpyCtx { + int signSeen; + int verifySeen; +} ed448SpyCtx; + +/* Spy device: services Ed448 sign/verify in software (devId cleared) and counts + * each; declines everything else so make_key runs in software. */ +static int ed448_test_crypto_cb(int devIdArg, wc_CryptoInfo* info, void* ctx) +{ + ed448SpyCtx* spy = (ed448SpyCtx*)ctx; + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + + (void)devIdArg; + + if (info == NULL || spy == NULL) { + return BAD_FUNC_ARG; + } + + if (info->algo_type == WC_ALGO_TYPE_PK) { + #ifdef HAVE_ED448_SIGN + if (info->pk.type == WC_PK_TYPE_ED448) { + int save = info->pk.ed448sign.key->devId; + info->pk.ed448sign.key->devId = INVALID_DEVID; + ret = wc_ed448_sign_msg_ex( + info->pk.ed448sign.in, info->pk.ed448sign.inLen, + info->pk.ed448sign.out, info->pk.ed448sign.outLen, + info->pk.ed448sign.key, info->pk.ed448sign.type, + info->pk.ed448sign.context, info->pk.ed448sign.contextLen); + info->pk.ed448sign.key->devId = save; + spy->signSeen++; + } + #endif + #ifdef HAVE_ED448_VERIFY + if (info->pk.type == WC_PK_TYPE_ED448_VERIFY) { + int save = info->pk.ed448verify.key->devId; + info->pk.ed448verify.key->devId = INVALID_DEVID; + ret = wc_ed448_verify_msg_ex( + info->pk.ed448verify.sig, info->pk.ed448verify.sigLen, + info->pk.ed448verify.msg, info->pk.ed448verify.msgLen, + info->pk.ed448verify.res, info->pk.ed448verify.key, + info->pk.ed448verify.type, info->pk.ed448verify.context, + info->pk.ed448verify.contextLen); + info->pk.ed448verify.key->devId = save; + spy->verifySeen++; + } + #endif + } + + return ret; +} +#endif + +int test_wc_ed448_cryptocb(void) +{ + EXPECT_DECLS; +#if defined(WOLF_CRYPTO_CB) && defined(HAVE_ED448) && \ + defined(HAVE_ED448_SIGN) && defined(HAVE_ED448_VERIFY) && \ + !defined(WC_NO_RNG) + int devId = 4448; + ed448SpyCtx spy; + WC_RNG rng; + byte msg[32]; + word32 sigLen = ED448_SIG_SIZE; + int verify = 0; + WC_DECLARE_VAR(key, ed448_key, 1, HEAP_HINT); + WC_DECLARE_VAR(sig, byte, ED448_SIG_SIZE, HEAP_HINT); + + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(&spy, 0, sizeof(spy)); + XMEMSET(msg, 0x5a, sizeof(msg)); + + WC_ALLOC_VAR(key, ed448_key, 1, HEAP_HINT); + WC_ALLOC_VAR(sig, byte, ED448_SIG_SIZE, HEAP_HINT); +#ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC + ExpectNotNull(key); + ExpectNotNull(sig); +#endif + if (WC_VAR_OK(sig)) { + XMEMSET(sig, 0, ED448_SIG_SIZE); + } + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(devId, ed448_test_crypto_cb, &spy), + 0); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ed448_init_ex(key, HEAP_HINT, devId), 0); + ExpectIntEQ(wc_ed448_make_key(&rng, ED448_KEY_SIZE, key), 0); + + /* Sign routes through the device callback. */ + ExpectIntEQ(wc_ed448_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen, key, + NULL, 0), 0); + ExpectIntGE(spy.signSeen, 1); + + /* Verify routes through the device callback. */ + ExpectIntEQ(wc_ed448_verify_msg(sig, sigLen, msg, (word32)sizeof(msg), + &verify, key, NULL, 0), 0); + ExpectIntGE(spy.verifySeen, 1); + ExpectIntEQ(verify, 1); + + /* Negative: corrupt the signature. Ed448 reports a bad signature as + * SIG_VERIFY_E, exercising the device verify error path (verify == 0). */ + if (WC_VAR_OK(sig)) { + sig[0] ^= 0xFF; + } + verify = 1; + ExpectIntEQ(wc_ed448_verify_msg(sig, sigLen, msg, (word32)sizeof(msg), + &verify, key, NULL, 0), + WC_NO_ERR_TRACE(SIG_VERIFY_E)); + ExpectIntGE(spy.verifySeen, 2); + ExpectIntEQ(verify, 0); + + wc_ed448_free(key); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + wc_CryptoCb_UnRegisterDevice(devId); + + WC_FREE_VAR(sig, HEAP_HINT); + WC_FREE_VAR(key, HEAP_HINT); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ed448.h b/tests/api/test_ed448.h index a31ef1b454a..eccb3ba343b 100644 --- a/tests/api/test_ed448.h +++ b/tests/api/test_ed448.h @@ -44,6 +44,7 @@ int test_wc_Ed448DecisionCoverage(void); int test_wc_Ed448FeatureCoverage(void); int test_wc_ed448_import_private_only(void); int test_wc_ed448_check_key_decisions(void); +int test_wc_ed448_cryptocb(void); #define TEST_ED448_DECLS \ TEST_DECL_GROUP("ed448", test_wc_ed448_make_key), \ @@ -65,6 +66,7 @@ int test_wc_ed448_check_key_decisions(void); TEST_DECL_GROUP("ed448", test_wc_Ed448FeatureCoverage), \ TEST_DECL_GROUP("ed448", test_wc_ed448_import_private_only), \ TEST_DECL_GROUP("ed448", test_wc_ed448_check_key_decisions), \ - TEST_DECL_GROUP("ed448", test_wc_Ed448PrivateKeyDecode_ex) \ + TEST_DECL_GROUP("ed448", test_wc_Ed448PrivateKeyDecode_ex), \ + TEST_DECL_GROUP("ed448", test_wc_ed448_cryptocb) #endif /* WOLFCRYPT_TEST_ED448_H */ diff --git a/tests/api/test_rsa.c b/tests/api/test_rsa.c index bd1c6b44827..4bf190f67e5 100644 --- a/tests/api/test_rsa.c +++ b/tests/api/test_rsa.c @@ -36,6 +36,9 @@ #ifdef WOLFSSL_SHA384 #include #endif +#ifdef WOLF_CRYPTO_CB + #include +#endif #include #include @@ -1999,3 +2002,154 @@ int test_wc_RsaFeatureCoverage(void) #endif return EXPECT_RESULT(); } /* END test_wc_RsaFeatureCoverage */ + +/* Test that wc_RsaPSS_VerifyCheck() routes RSA-PSS verification through a + * registered crypto callback (CryptoCb) device. */ +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ + defined(WC_RSA_PSS) && !defined(NO_RSA) && !defined(WC_NO_RNG) && \ + defined(WOLFSSL_KEY_GEN) && !defined(NO_SHA256) +/* Spy device: on RSA-PSS verify counts the request, verifies in software and + * reports via res; declines other pk types so sign / keygen run in software. */ +static int rsa_pss_test_crypto_cb(int devIdArg, wc_CryptoInfo* info, void* ctx) +{ + int* pssVerifySeen = (int*)ctx; + + (void)devIdArg; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + if (info->algo_type == WC_ALGO_TYPE_PK && + info->pk.type == WC_PK_TYPE_RSA_PSS_VERIFY) { + RsaKey* key = info->pk.rsa_pss_verify.key; + int save; + int v; + byte* outbuf; + word32 outbufSz = 512; + + outbuf = (byte*)XMALLOC(outbufSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (outbuf == NULL) { + return MEMORY_E; + } + + if (pssVerifySeen != NULL) { + (*pssVerifySeen)++; + } + + save = key->devId; + key->devId = INVALID_DEVID; + v = wc_RsaPSS_VerifyCheck( + info->pk.rsa_pss_verify.sig, info->pk.rsa_pss_verify.sigSz, + outbuf, outbufSz, + info->pk.rsa_pss_verify.digest, info->pk.rsa_pss_verify.digestSz, + info->pk.rsa_pss_verify.hash, info->pk.rsa_pss_verify.mgf, + key); + key->devId = save; + + XFREE(outbuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + + /* Only a real verdict maps to res; a genuine internal error (e.g. + * MEMORY_E) is propagated so it is not masked as a bad signature. */ + if (v > 0) { + if (info->pk.rsa_pss_verify.res != NULL) + *info->pk.rsa_pss_verify.res = 1; + return 0; + } + if (v == WC_NO_ERR_TRACE(BAD_PADDING_E) || + v == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + if (info->pk.rsa_pss_verify.res != NULL) + *info->pk.rsa_pss_verify.res = 0; + return 0; + } + return v; + } + + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); +} +#endif + +int test_wc_CryptoCb_RsaPssVerify(void) +{ + EXPECT_DECLS; +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ + defined(WC_RSA_PSS) && !defined(NO_RSA) && !defined(WC_NO_RNG) && \ + defined(WOLFSSL_KEY_GEN) && !defined(NO_SHA256) + int devId = 4470; + int pssVerifySeen = 0; + WC_RNG rng; + byte digest[WC_SHA256_DIGEST_SIZE]; + word32 sigLen = 0; + int r; + WC_DECLARE_VAR(key, RsaKey, 1, HEAP_HINT); + WC_DECLARE_VAR(sig, byte, 512, HEAP_HINT); + WC_DECLARE_VAR(rec, byte, 512, HEAP_HINT); + + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(digest, 0x2b, sizeof(digest)); + + WC_ALLOC_VAR(key, RsaKey, 1, HEAP_HINT); + WC_ALLOC_VAR(sig, byte, 512, HEAP_HINT); + WC_ALLOC_VAR(rec, byte, 512, HEAP_HINT); +#ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC + ExpectNotNull(key); + ExpectNotNull(sig); + ExpectNotNull(rec); +#endif + if (WC_VAR_OK(sig)) { + XMEMSET(sig, 0, 512); + } + if (WC_VAR_OK(rec)) { + XMEMSET(rec, 0, 512); + } + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(devId, rsa_pss_test_crypto_cb, + &pssVerifySeen), 0); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_InitRsaKey_ex(key, HEAP_HINT, devId), 0); + ExpectIntEQ(wc_MakeRsaKey(key, 2048, WC_RSA_EXPONENT, &rng), 0); + ExpectIntEQ(wc_RsaSetRNG(key, &rng), 0); + + /* PSS sign runs in software (device declines). */ + ExpectIntGT(sigLen = (word32)wc_RsaPSS_Sign(digest, + (word32)sizeof(digest), sig, 512, WC_HASH_TYPE_SHA256, WC_MGF1SHA256, + key, &rng), 0); + + /* Positive: verify routes through the device and succeeds. */ + pssVerifySeen = 0; + ExpectIntGT(r = wc_RsaPSS_VerifyCheck(sig, sigLen, rec, 512, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key), 0); + ExpectIntGE(pssVerifySeen, 1); + + /* Positive: the inline variant also routes through the device; *out is NULL + * on that path. Use a copy of the sig since inline 'in' is reused as out. */ + if (WC_VAR_OK(sig) && WC_VAR_OK(rec)) { + byte* inlineOut = rec; /* non-NULL sentinel, must be cleared to NULL */ + XMEMCPY(rec, sig, sigLen); + pssVerifySeen = 0; + ExpectIntGT(wc_RsaPSS_VerifyCheckInline(rec, sigLen, &inlineOut, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key), 0); + ExpectIntGE(pssVerifySeen, 1); + ExpectNull(inlineOut); + } + + /* Negative: corrupt the signature; the device sets res=0 so VerifyCheck + * returns SIG_VERIFY_E (<= 0). */ + if (WC_VAR_OK(sig)) { + sig[0] ^= 0xFF; + } + pssVerifySeen = 0; + ExpectIntLE(wc_RsaPSS_VerifyCheck(sig, sigLen, rec, 512, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key), 0); + ExpectIntGE(pssVerifySeen, 1); + + DoExpectIntEQ(wc_FreeRsaKey(key), 0); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + wc_CryptoCb_UnRegisterDevice(devId); + + WC_FREE_VAR(rec, HEAP_HINT); + WC_FREE_VAR(sig, HEAP_HINT); + WC_FREE_VAR(key, HEAP_HINT); +#endif + return EXPECT_RESULT(); +} /* END test_wc_CryptoCb_RsaPssVerify */ diff --git a/tests/api/test_rsa.h b/tests/api/test_rsa.h index 69081a5cc53..13d503101e1 100644 --- a/tests/api/test_rsa.h +++ b/tests/api/test_rsa.h @@ -47,6 +47,7 @@ int test_wc_RsaFunctionCheckIn_OversizedModulus(void); int test_wc_RsaKeyToDer_SizeOverflow(void); int test_wc_RsaDecisionCoverage(void); int test_wc_RsaFeatureCoverage(void); +int test_wc_CryptoCb_RsaPssVerify(void); #define TEST_RSA_DECLS \ TEST_DECL_GROUP("rsa", test_wc_InitRsaKey), \ @@ -71,6 +72,7 @@ int test_wc_RsaFeatureCoverage(void); TEST_DECL_GROUP("rsa", test_wc_RsaFunctionCheckIn_OversizedModulus), \ TEST_DECL_GROUP("rsa", test_wc_RsaKeyToDer_SizeOverflow), \ TEST_DECL_GROUP("rsa", test_wc_RsaDecisionCoverage), \ - TEST_DECL_GROUP("rsa", test_wc_RsaFeatureCoverage) + TEST_DECL_GROUP("rsa", test_wc_RsaFeatureCoverage), \ + TEST_DECL_GROUP("rsa", test_wc_CryptoCb_RsaPssVerify) #endif /* WOLFCRYPT_TEST_RSA_H */ diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index ef714d1cbb5..f8ba6542f51 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -376,6 +376,17 @@ int wc_CmacFree(Cmac* cmac) { if (cmac == NULL) return BAD_FUNC_ARG; +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) + /* Let the device release any per-context state it hung off cmac->devCtx + * before the struct is zeroed (e.g. an offload context never finalized). */ + #ifndef WOLF_CRYPTO_CB_FIND + if (cmac->devId != INVALID_DEVID) + #endif + { + (void)wc_CryptoCb_Free(cmac->devId, WC_ALGO_TYPE_CMAC, (int)cmac->type, + 0, cmac); + } +#endif #if defined(WOLFSSL_HASH_KEEP) /* TODO: msg is leaked if wc_CmacFinal() is not called * e.g. when multiple calls to wc_CmacUpdate() and one fails but diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index cb9c7bc65ae..dc27ffef155 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -145,12 +145,15 @@ static const char* GetPkTypeStr(int pk) { switch (pk) { case WC_PK_TYPE_RSA: return "RSA"; + case WC_PK_TYPE_RSA_PSS_VERIFY: return "RSA-PSS-Verify"; case WC_PK_TYPE_DH: return "DH"; case WC_PK_TYPE_ECDH: return "ECDH"; case WC_PK_TYPE_ECDSA_SIGN: return "ECDSA-Sign"; case WC_PK_TYPE_ECDSA_VERIFY: return "ECDSA-Verify"; case WC_PK_TYPE_ED25519_SIGN: return "ED25519-Sign"; case WC_PK_TYPE_ED25519_VERIFY: return "ED25519-Verify"; + case WC_PK_TYPE_ED448: return "ED448-Sign"; + case WC_PK_TYPE_ED448_VERIFY: return "ED448-Verify"; case WC_PK_TYPE_CURVE25519: return "CURVE25519"; case WC_PK_TYPE_RSA_KEYGEN: return "RSA KeyGen"; case WC_PK_TYPE_EC_KEYGEN: return "ECC KeyGen"; @@ -629,6 +632,42 @@ int wc_CryptoCb_RsaPad(const byte* in, word32 inLen, byte* out, return wc_CryptoCb_TranslateErrorCode(ret); } + +/* Verify an RSA-PSS signature on the device (padding included). Passes both the + * signature and digest so the device does the whole verify and returns a verdict. */ +int wc_CryptoCb_RsaPssVerify(const byte* sig, word32 sigSz, const byte* digest, + word32 digestSz, enum wc_HashType hash, int mgf, int saltLen, RsaKey* key, + int* res) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + if (key == NULL) + return ret; + + /* locate registered callback */ + dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK); + + if (dev && dev->cb) { + wc_CryptoInfo cryptoInfo; + XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); + cryptoInfo.algo_type = WC_ALGO_TYPE_PK; + cryptoInfo.pk.type = WC_PK_TYPE_RSA_PSS_VERIFY; + cryptoInfo.pk.rsa_pss_verify.sig = sig; + cryptoInfo.pk.rsa_pss_verify.sigSz = sigSz; + cryptoInfo.pk.rsa_pss_verify.digest = digest; + cryptoInfo.pk.rsa_pss_verify.digestSz = digestSz; + cryptoInfo.pk.rsa_pss_verify.hash = hash; + cryptoInfo.pk.rsa_pss_verify.mgf = mgf; + cryptoInfo.pk.rsa_pss_verify.saltLen = saltLen; + cryptoInfo.pk.rsa_pss_verify.key = key; + cryptoInfo.pk.rsa_pss_verify.res = res; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} #endif /* WOLF_CRYPTO_CB_RSA_PAD */ #ifdef WOLFSSL_KEY_GEN @@ -1354,6 +1393,73 @@ int wc_CryptoCb_Ed25519CheckKey(ed25519_key* key) } #endif /* HAVE_ED25519 */ +#ifdef HAVE_ED448 +int wc_CryptoCb_Ed448Sign(const byte* in, word32 inLen, byte* out, + word32 *outLen, ed448_key* key, byte type, const byte* context, + byte contextLen) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + if (key == NULL) + return ret; + + /* locate registered callback */ + dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK); + if (dev && dev->cb) { + wc_CryptoInfo cryptoInfo; + XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); + cryptoInfo.algo_type = WC_ALGO_TYPE_PK; + cryptoInfo.pk.type = WC_PK_TYPE_ED448; + cryptoInfo.pk.ed448sign.in = in; + cryptoInfo.pk.ed448sign.inLen = inLen; + cryptoInfo.pk.ed448sign.out = out; + cryptoInfo.pk.ed448sign.outLen = outLen; + cryptoInfo.pk.ed448sign.key = key; + cryptoInfo.pk.ed448sign.type = type; + cryptoInfo.pk.ed448sign.context = context; + cryptoInfo.pk.ed448sign.contextLen = contextLen; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} + +int wc_CryptoCb_Ed448Verify(const byte* sig, word32 sigLen, + const byte* msg, word32 msgLen, int* res, ed448_key* key, byte type, + const byte* context, byte contextLen) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + if (key == NULL) + return ret; + + /* locate registered callback */ + dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK); + if (dev && dev->cb) { + wc_CryptoInfo cryptoInfo; + XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); + cryptoInfo.algo_type = WC_ALGO_TYPE_PK; + cryptoInfo.pk.type = WC_PK_TYPE_ED448_VERIFY; + cryptoInfo.pk.ed448verify.sig = sig; + cryptoInfo.pk.ed448verify.sigLen = sigLen; + cryptoInfo.pk.ed448verify.msg = msg; + cryptoInfo.pk.ed448verify.msgLen = msgLen; + cryptoInfo.pk.ed448verify.res = res; + cryptoInfo.pk.ed448verify.key = key; + cryptoInfo.pk.ed448verify.type = type; + cryptoInfo.pk.ed448verify.context = context; + cryptoInfo.pk.ed448verify.contextLen = contextLen; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} +#endif /* HAVE_ED448 */ + #if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) int wc_CryptoCb_PqcStatefulSigGetDevId(int type, void* key) { diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index e8c1a0cd500..b7f45a27bc4 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -45,6 +45,9 @@ #include #include +#ifdef WOLF_CRYPTO_CB + #include +#endif #ifdef NO_INLINE #include #else @@ -462,6 +465,22 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, ((context == NULL) && (contextLen != 0))) { ret = BAD_FUNC_ARG; } + +#ifdef WOLF_CRYPTO_CB + if (ret == 0) { + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_Ed448Sign(in, inLen, out, outLen, key, type, + context, contextLen); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + ret = 0; /* fall-through when unavailable */ + } + } +#endif + if ((ret == 0) && (!key->pubKeySet)) { ret = BAD_FUNC_ARG; } @@ -924,6 +943,19 @@ int wc_ed448_verify_msg_ex(const byte* sig, word32 sigLen, const byte* msg, return BAD_LENGTH_E; } +#ifdef WOLF_CRYPTO_CB + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_Ed448Verify(sig, sigLen, msg, msgLen, res, key, type, + context, contextLen); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + } +#endif + #ifdef WOLFSSL_ED448_PERSISTENT_SHA sha = &key->sha; #else diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 07b12e34f41..871364efd38 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -4644,6 +4644,9 @@ int wc_RsaPSS_CheckPadding_ex(const byte* in, word32 inSz, const byte* sig, * mgf Mask generation function. * key Public RSA key. * returns the length of the PSS data on success and negative indicates failure. + * + * Note: when a crypto callback device performs the verify, *out is set to NULL + * even though a positive length is returned; callers must not dereference *out. */ int wc_RsaPSS_VerifyCheckInline(byte* in, word32 inLen, byte** out, const byte* digest, word32 digestLen, @@ -4679,6 +4682,32 @@ int wc_RsaPSS_VerifyCheckInline(byte* in, word32 inLen, byte** out, saltLen = RSA_PSS_SALT_MAX_SZ; #endif +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) + /* Let a device verify signature + padding in one shot (it gets the digest, + * which the RsaPad path does not). Fall through to software if unavailable. */ + #ifndef WOLF_CRYPTO_CB_FIND + if (key != NULL && key->devId != INVALID_DEVID) + #else + if (key != NULL) + #endif + { + int res = 0; + ret = wc_CryptoCb_RsaPssVerify(in, inLen, digest, digestLen, hash, mgf, + saltLen, key, &res); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + if (ret == 0) { + /* Device verified internally; no recovered PSS block to expose, + * so report no inline output rather than a misleading pointer. */ + if (out != NULL) + *out = NULL; + ret = (res != 0) ? (int)inLen : SIG_VERIFY_E; + } + return ret; + } + ret = 0; + } +#endif + verify = wc_RsaPSS_VerifyInline_ex(in, inLen, out, hash, mgf, saltLen, key); if (verify > 0) ret = wc_RsaPSS_CheckPadding_ex(digest, digestLen, *out, (word32)verify, @@ -4739,6 +4768,27 @@ int wc_RsaPSS_VerifyCheck(const byte* in, word32 inLen, byte* out, word32 outLen saltLen = RSA_PSS_SALT_MAX_SZ; #endif +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) + /* Let a device verify signature + padding in one shot (it gets the digest, + * which the RsaPad path does not). Fall through to software if unavailable. */ + #ifndef WOLF_CRYPTO_CB_FIND + if (key != NULL && key->devId != INVALID_DEVID) + #else + if (key != NULL) + #endif + { + int res = 0; + ret = wc_CryptoCb_RsaPssVerify(in, inLen, digest, digestLen, hash, mgf, + saltLen, key, &res); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + if (ret == 0) + ret = (res != 0) ? (int)inLen : SIG_VERIFY_E; + return ret; + } + ret = 0; + } +#endif + verify = wc_RsaPSS_Verify_ex(in, inLen, out, outLen, hash, mgf, saltLen, key); if (verify > 0) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index e9426ab71cb..7a84311ef0a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -77878,6 +77878,16 @@ typedef struct { * public point, simulating a private scalar * resident in the device (input key->k empty) */ #endif +#ifdef HAVE_ED448 + int ed448SignCount; /* Ed448 sign callback invocations */ + int ed448VerifyCount; /* Ed448 verify callback invocations */ +#endif +#if defined(WOLFSSL_CMAC) && defined(WOLF_CRYPTO_CB_FREE) + int cmacFreeCount; /* CMAC free callback invocations */ +#endif +#if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) + int rsaPssVerifyCount; /* RSA-PSS verify callback invocations */ +#endif } myCryptoDevCtx; #ifdef WOLF_CRYPTO_CB_ONLY_RSA @@ -78838,6 +78848,51 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) WOLFSSL_MSG_EX("CryptoDevCb: Pk Type %d\n", info->pk.type); #endif + #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) + if (info->pk.type == WC_PK_TYPE_RSA_PSS_VERIFY) { + RsaKey* pssKey = info->pk.rsa_pss_verify.key; + int pssSaveDevId = pssKey->devId; + int pssVer = 0; + byte* pssOut; + word32 pssOutSz = 512; + + pssOut = (byte*)XMALLOC(pssOutSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + if (pssOut == NULL) + return MEMORY_E; + + myCtx->rsaPssVerifyCount++; + + /* perform software based RSA-PSS verify */ + pssKey->devId = INVALID_DEVID; + pssVer = wc_RsaPSS_VerifyCheck( + info->pk.rsa_pss_verify.sig, info->pk.rsa_pss_verify.sigSz, + pssOut, pssOutSz, + info->pk.rsa_pss_verify.digest, + info->pk.rsa_pss_verify.digestSz, + info->pk.rsa_pss_verify.hash, info->pk.rsa_pss_verify.mgf, + pssKey); + pssKey->devId = pssSaveDevId; + + XFREE(pssOut, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + + /* Only a real verdict maps to res; a genuine internal error (e.g. + * MEMORY_E) is propagated so it is not masked as a bad signature. */ + if (pssVer > 0) { + if (info->pk.rsa_pss_verify.res != NULL) + *info->pk.rsa_pss_verify.res = 1; + return 0; + } + if (pssVer == WC_NO_ERR_TRACE(BAD_PADDING_E) || + pssVer == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + if (info->pk.rsa_pss_verify.res != NULL) + *info->pk.rsa_pss_verify.res = 0; + return 0; + } + return pssVer; + } + #endif /* WC_RSA_PSS && WOLF_CRYPTO_CB_RSA_PAD */ + #ifndef NO_RSA if (info->pk.type == WC_PK_TYPE_RSA) { /* set devId to invalid, so software is used */ @@ -79325,6 +79380,43 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) info->pk.ed25519checkkey.key->devId = devIdArg; } #endif /* HAVE_ED25519 */ + #ifdef HAVE_ED448 + #ifdef HAVE_ED448_SIGN + if (info->pk.type == WC_PK_TYPE_ED448) { + /* set devId to invalid, so software is used */ + info->pk.ed448sign.key->devId = INVALID_DEVID; + + ret = wc_ed448_sign_msg_ex( + info->pk.ed448sign.in, info->pk.ed448sign.inLen, + info->pk.ed448sign.out, info->pk.ed448sign.outLen, + info->pk.ed448sign.key, info->pk.ed448sign.type, + info->pk.ed448sign.context, info->pk.ed448sign.contextLen); + + /* reset devId */ + info->pk.ed448sign.key->devId = devIdArg; + + myCtx->ed448SignCount++; + } + #endif + #ifdef HAVE_ED448_VERIFY + if (info->pk.type == WC_PK_TYPE_ED448_VERIFY) { + /* set devId to invalid, so software is used */ + info->pk.ed448verify.key->devId = INVALID_DEVID; + + ret = wc_ed448_verify_msg_ex( + info->pk.ed448verify.sig, info->pk.ed448verify.sigLen, + info->pk.ed448verify.msg, info->pk.ed448verify.msgLen, + info->pk.ed448verify.res, info->pk.ed448verify.key, + info->pk.ed448verify.type, info->pk.ed448verify.context, + info->pk.ed448verify.contextLen); + + /* reset devId */ + info->pk.ed448verify.key->devId = devIdArg; + + myCtx->ed448VerifyCount++; + } + #endif + #endif /* HAVE_ED448 */ #if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) if (info->pk.type == WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN) { int pqcType = info->pk.pqc_stateful_sig_kg.type; @@ -80607,6 +80699,13 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); #endif } +#if defined(WOLFSSL_CMAC) + else if (info->free.algo == WC_ALGO_TYPE_CMAC) { + /* count the CMAC free dispatch and let software do the free */ + myCtx->cmacFreeCount++; + ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } +#endif else { ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); } @@ -81224,6 +81323,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) myCtx.eccCheckPubSawZeroPoint = 0; myCtx.eccResidentKey = NULL; #endif +#ifdef HAVE_ED448 + myCtx.ed448SignCount = 0; + myCtx.ed448VerifyCount = 0; +#endif +#if defined(WOLFSSL_CMAC) && defined(WOLF_CRYPTO_CB_FREE) + myCtx.cmacFreeCount = 0; +#endif +#if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) + myCtx.rsaPssVerifyCount = 0; +#endif /* set devId to something other than INVALID_DEVID */ devId = 1; @@ -81691,6 +81800,196 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) ret = cmac_test(); #endif + /* Driver coverage for the new CryptoCb hooks: confirm each op is routed + * through myCryptoDevCb (counter bumped) and the round-trip is correct. */ +#if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) && \ + defined(HAVE_ED448_VERIFY) && !defined(WC_NO_RNG) + if (ret == 0) { + WC_RNG ed448Rng; + int ed448RngInit = 0; + byte ed448Msg[32]; + word32 ed448SigLen = ED448_SIG_SIZE; + int ed448Verify = 0; + WC_DECLARE_VAR(ed448Key, ed448_key, 1, HEAP_HINT); + WC_DECLARE_VAR(ed448Sig, byte, ED448_SIG_SIZE, HEAP_HINT); + + WC_ALLOC_VAR_EX(ed448Key, ed448_key, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, ret = WC_TEST_RET_ENC_EC(MEMORY_E)); + if (ret == 0) + WC_ALLOC_VAR_EX(ed448Sig, byte, ED448_SIG_SIZE, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, ret = WC_TEST_RET_ENC_EC(MEMORY_E)); + + XMEMSET(ed448Msg, 0x5a, sizeof(ed448Msg)); + myCtx.ed448SignCount = 0; + myCtx.ed448VerifyCount = 0; + + if (ret == 0) { + ret = wc_InitRng_ex(&ed448Rng, HEAP_HINT, devId); + if (ret == 0) + ed448RngInit = 1; + else + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_ed448_init_ex(ed448Key, HEAP_HINT, devId); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_ed448_make_key(&ed448Rng, ED448_KEY_SIZE, ed448Key); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_ed448_sign_msg(ed448Msg, (word32)sizeof(ed448Msg), + ed448Sig, &ed448SigLen, ed448Key, NULL, 0); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0 && myCtx.ed448SignCount == 0) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0) { + ret = wc_ed448_verify_msg(ed448Sig, ed448SigLen, ed448Msg, + (word32)sizeof(ed448Msg), &ed448Verify, ed448Key, NULL, 0); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0 && (myCtx.ed448VerifyCount == 0 || ed448Verify != 1)) + ret = WC_TEST_RET_ENC_NC; + + if (WC_VAR_OK(ed448Key)) + wc_ed448_free(ed448Key); + if (ed448RngInit) + wc_FreeRng(&ed448Rng); + WC_FREE_VAR_EX(ed448Sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + WC_FREE_VAR_EX(ed448Key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif /* HAVE_ED448 */ + +#if defined(WOLFSSL_CMAC) && defined(WOLF_CRYPTO_CB_FREE) && \ + !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) + if (ret == 0) { + byte cmacKey[WC_AES_BLOCK_SIZE] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 + }; + byte cmacIn[WC_AES_BLOCK_SIZE] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00 + }; + byte cmacTag[WC_AES_BLOCK_SIZE]; + word32 cmacTagSz = (word32)sizeof(cmacTag); + WC_DECLARE_VAR(cmac, Cmac, 1, HEAP_HINT); + + WC_ALLOC_VAR_EX(cmac, Cmac, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, ret = WC_TEST_RET_ENC_EC(MEMORY_E)); + + if (ret == 0) { + ret = wc_InitCmac_ex(cmac, cmacKey, (word32)sizeof(cmacKey), + WC_CMAC_AES, NULL, HEAP_HINT, devId); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_CmacUpdate(cmac, cmacIn, (word32)sizeof(cmacIn)); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + /* wc_CmacFinal() frees the Cmac -> free dispatched to device */ + myCtx.cmacFreeCount = 0; + ret = wc_CmacFinal(cmac, cmacTag, &cmacTagSz); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0 && myCtx.cmacFreeCount == 0) + ret = WC_TEST_RET_ENC_NC; + + WC_FREE_VAR_EX(cmac, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif /* WOLFSSL_CMAC && WOLF_CRYPTO_CB_FREE */ + +#if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ + !defined(NO_RSA) && !defined(WC_NO_RNG) && defined(WOLFSSL_KEY_GEN) && \ + !defined(NO_SHA256) + if (ret == 0) { + WC_RNG rsaRng; + int rsaRngInit = 0; + int rsaKeyInit = 0; + byte rsaDigest[WC_SHA256_DIGEST_SIZE]; + word32 rsaSigLen = 0; + int rsaVer; + WC_DECLARE_VAR(rsaKey, RsaKey, 1, HEAP_HINT); + WC_DECLARE_VAR(rsaSig, byte, 512, HEAP_HINT); + WC_DECLARE_VAR(rsaRec, byte, 512, HEAP_HINT); + + WC_ALLOC_VAR_EX(rsaKey, RsaKey, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, ret = WC_TEST_RET_ENC_EC(MEMORY_E)); + if (ret == 0) + WC_ALLOC_VAR_EX(rsaSig, byte, 512, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, ret = WC_TEST_RET_ENC_EC(MEMORY_E)); + if (ret == 0) + WC_ALLOC_VAR_EX(rsaRec, byte, 512, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, ret = WC_TEST_RET_ENC_EC(MEMORY_E)); + + XMEMSET(rsaDigest, 0x2b, sizeof(rsaDigest)); + myCtx.rsaPssVerifyCount = 0; + + if (ret == 0) { + ret = wc_InitRng_ex(&rsaRng, HEAP_HINT, devId); + if (ret == 0) + rsaRngInit = 1; + else + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_InitRsaKey_ex(rsaKey, HEAP_HINT, devId); + if (ret == 0) + rsaKeyInit = 1; + else + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_MakeRsaKey(rsaKey, 2048, WC_RSA_EXPONENT, &rsaRng); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_RsaSetRNG(rsaKey, &rsaRng); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_RsaPSS_Sign(rsaDigest, (word32)sizeof(rsaDigest), rsaSig, + 512, WC_HASH_TYPE_SHA256, WC_MGF1SHA256, rsaKey, &rsaRng); + if (ret > 0) { + rsaSigLen = (word32)ret; + ret = 0; + } + else { + ret = WC_TEST_RET_ENC_EC(ret); + } + } + if (ret == 0) { + rsaVer = wc_RsaPSS_VerifyCheck(rsaSig, rsaSigLen, rsaRec, 512, + rsaDigest, (word32)sizeof(rsaDigest), WC_HASH_TYPE_SHA256, + WC_MGF1SHA256, rsaKey); + if (rsaVer <= 0) + ret = WC_TEST_RET_ENC_EC(rsaVer); + } + if (ret == 0 && myCtx.rsaPssVerifyCount == 0) + ret = WC_TEST_RET_ENC_NC; + + if (rsaKeyInit) + wc_FreeRsaKey(rsaKey); + if (rsaRngInit) + wc_FreeRng(&rsaRng); + WC_FREE_VAR_EX(rsaRec, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + WC_FREE_VAR_EX(rsaSig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + WC_FREE_VAR_EX(rsaKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif /* WC_RSA_PSS && WOLF_CRYPTO_CB_RSA_PAD */ + wc_CryptoCb_UnRegisterDevice(devId); /* restore devId */ diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index bdce20d8b1b..e8bb2f3b872 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -71,6 +71,9 @@ #ifdef HAVE_ED25519 #include #endif +#ifdef HAVE_ED448 + #include +#endif #ifdef HAVE_CURVE25519 #include #endif @@ -197,6 +200,19 @@ typedef struct wc_CryptoInfo { const RsaKey* key; int* keySize; } rsa_get_size; + #ifdef WOLF_CRYPTO_CB_RSA_PAD + struct { + const byte* sig; + word32 sigSz; + const byte* digest; + word32 digestSz; + enum wc_HashType hash; + int mgf; + int saltLen; + RsaKey* key; + int* res; + } rsa_pss_verify; + #endif #endif #ifdef HAVE_ECC #ifdef HAVE_ECC_DHE @@ -360,6 +376,29 @@ typedef struct wc_CryptoInfo { * priv/pub consistency */ } ed25519checkkey; #endif + #ifdef HAVE_ED448 + struct { + const byte* in; + word32 inLen; + byte* out; + word32* outLen; + ed448_key* key; + byte type; + const byte* context; + byte contextLen; + } ed448sign; + struct { + const byte* sig; + word32 sigLen; + const byte* msg; + word32 msgLen; + int* res; + ed448_key* key; + byte type; + const byte* context; + byte contextLen; + } ed448verify; + #endif #if defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_HAVE_FRODOKEM) struct { WC_RNG* rng; @@ -810,6 +849,9 @@ WOLFSSL_LOCAL int wc_CryptoCb_Rsa(const byte* in, word32 inLen, byte* out, #ifdef WOLF_CRYPTO_CB_RSA_PAD WOLFSSL_LOCAL int wc_CryptoCb_RsaPad(const byte* in, word32 inLen, byte* out, word32* outLen, int type, RsaKey* key, WC_RNG* rng, RsaPadding *padding); +WOLFSSL_LOCAL int wc_CryptoCb_RsaPssVerify(const byte* sig, word32 sigSz, + const byte* digest, word32 digestSz, enum wc_HashType hash, int mgf, + int saltLen, RsaKey* key, int* res); #endif #ifdef WOLFSSL_KEY_GEN @@ -884,6 +926,15 @@ WOLFSSL_LOCAL int wc_CryptoCb_Ed25519MakePub(ed25519_key* key, byte* pubKey, WOLFSSL_LOCAL int wc_CryptoCb_Ed25519CheckKey(ed25519_key* key); #endif /* HAVE_ED25519 */ +#ifdef HAVE_ED448 +WOLFSSL_LOCAL int wc_CryptoCb_Ed448Sign(const byte* in, word32 inLen, + byte* out, word32 *outLen, ed448_key* key, byte type, const byte* context, + byte contextLen); +WOLFSSL_LOCAL int wc_CryptoCb_Ed448Verify(const byte* sig, word32 sigLen, + const byte* msg, word32 msgLen, int* res, ed448_key* key, byte type, + const byte* context, byte contextLen); +#endif /* HAVE_ED448 */ + #if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigGetDevId(int type, void* key); diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 4ef6844d2a9..c17195605a6 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -1633,8 +1633,11 @@ enum wc_PkType { #endif WC_PK_TYPE_CURVE25519_MAKE_PUB = 40, WC_PK_TYPE_CURVE25519_GENERIC = 41, + WC_PK_TYPE_RSA_PSS_VERIFY = 42, + /* Ed448 sign reuses WC_PK_TYPE_ED448 (12); verify needs its own type. */ + WC_PK_TYPE_ED448_VERIFY = 43, #undef _WC_PK_TYPE_MAX - #define _WC_PK_TYPE_MAX WC_PK_TYPE_CURVE25519_GENERIC + #define _WC_PK_TYPE_MAX WC_PK_TYPE_ED448_VERIFY WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX }; From b35f60eef3d1802f5f24921953dd46c908103ae9 Mon Sep 17 00:00:00 2001 From: night1rider Date: Fri, 7 Aug 2026 11:30:50 -0600 Subject: [PATCH 2/9] Set cmac->type before cryptocb init so wc_CmacFree can clean up --- wolfcrypt/src/cmac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index f8ba6542f51..6f2b35355ad 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -160,6 +160,10 @@ static int _InitCmac_common(Cmac* cmac, const byte* key, word32 keySz, #ifdef WOLF_CRYPTO_CB /* Set devId regardless of value (invalid or not) */ cmac->devId = devId; + /* Set type up front so wc_CmacFree can clean up properly when the + * callback handles the init and returns before the software path + * below has a chance to set it. */ + cmac->type = type; #ifndef WOLF_CRYPTO_CB_FIND if (devId != INVALID_DEVID) #endif From 7952b070e61e963dc86b78add5fb4bf5c9c45178 Mon Sep 17 00:00:00 2001 From: night1rider Date: Fri, 7 Aug 2026 11:30:50 -0600 Subject: [PATCH 3/9] Validate Ed448ph prehash length before the sign crypto callback --- wolfcrypt/src/ed448.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index b7f45a27bc4..04822b8bbdf 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -466,6 +466,10 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, ret = BAD_FUNC_ARG; } + if ((ret == 0) && (type == Ed448ph) && (inLen != ED448_PREHASH_SIZE)) { + ret = BAD_LENGTH_E; + } + #ifdef WOLF_CRYPTO_CB if (ret == 0) { #ifndef WOLF_CRYPTO_CB_FIND @@ -488,11 +492,6 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, ret = BAD_FUNC_ARG; } - if ((ret == 0) && (type == Ed448ph) && (inLen != ED448_PREHASH_SIZE)) - { - ret = BAD_LENGTH_E; - } - /* check and set up out length */ if ((ret == 0) && (*outLen < ED448_SIG_SIZE)) { *outLen = ED448_SIG_SIZE; From 3ade5f62b9b67e78afd0b77b825715ea71253253 Mon Sep 17 00:00:00 2001 From: night1rider Date: Fri, 7 Aug 2026 11:30:50 -0600 Subject: [PATCH 4/9] Clear *res before invoking the Ed448 verify crypto callback --- wolfcrypt/src/ed448.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index 04822b8bbdf..bc4928e528d 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -947,6 +947,8 @@ int wc_ed448_verify_msg_ex(const byte* sig, word32 sigLen, const byte* msg, if (key->devId != INVALID_DEVID) #endif { + if (res != NULL) + *res = 0; ret = wc_CryptoCb_Ed448Verify(sig, sigLen, msg, msgLen, res, key, type, context, contextLen); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) From 4d19228ca08ce827496374fe0099a3082fd1e263 Mon Sep 17 00:00:00 2001 From: night1rider Date: Fri, 7 Aug 2026 13:59:55 -0600 Subject: [PATCH 5/9] Cast type to CmacType so cmac.c builds as C++ --- wolfcrypt/src/cmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index 6f2b35355ad..6b17c8f8da9 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -163,7 +163,7 @@ static int _InitCmac_common(Cmac* cmac, const byte* key, word32 keySz, /* Set type up front so wc_CmacFree can clean up properly when the * callback handles the init and returns before the software path * below has a chance to set it. */ - cmac->type = type; + cmac->type = (CmacType)type; #ifndef WOLF_CRYPTO_CB_FIND if (devId != INVALID_DEVID) #endif From 33e03a5ce4e44a2b9b85cfd7d6b0574bb7422a80 Mon Sep 17 00:00:00 2001 From: night1rider Date: Fri, 7 Aug 2026 13:59:55 -0600 Subject: [PATCH 6/9] Release Ed448 sign MemZero registrations on the crypto callback exit --- wolfcrypt/src/ed448.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index bc4928e528d..350b2cc4b8e 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -478,8 +478,19 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, { ret = wc_CryptoCb_Ed448Sign(in, inLen, out, outLen, key, type, context, contextLen); - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + #ifdef WOLFSSL_CHECK_MEM_ZERO + /* The device signed, so this returns without reaching the + * ForceZero below. Release the registrations made above or + * they outlive the stack frame and trip a later check. */ + #ifdef WOLFSSL_EDDSA_CHECK_PRIV_ON_SIGN + wc_MemZero_Check(orig_k, sizeof(orig_k)); + #endif + wc_MemZero_Check(nonce, sizeof(nonce)); + wc_MemZero_Check(az, sizeof(az)); + #endif return ret; + } ret = 0; /* fall-through when unavailable */ } } From 375febc9442fd19d61c125897edbde1ed505c0c3 Mon Sep 17 00:00:00 2001 From: night1rider Date: Sat, 8 Aug 2026 15:10:06 -0600 Subject: [PATCH 7/9] Extend the RSA-PSS verify callback and harden the Ed448/CMAC hooks The PSS hook can hand back the recovered block through out/outSz/outLen. A device that reports only a verdict leaves outLen at 0; wolfSSL then zeroes the buffer and returns saltLen + hLen, and rejects a buffer smaller than that with RSA_BUFFER_E. A reported length is clamped to the buffer size, and any positive handler return maps to SIG_VERIFY_E. Move the Ed448 sign WOLFSSL_CHECK_MEM_ZERO registration below the crypto callback hook so the device path no longer returns past it, and guard the RSA-PSS test callback against WOLF_CRYPTO_CB_ONLY_RSA. Adds tests for the recovered-data, over-claimed-length and undersized buffer paths, and an os-check config that builds the hooks under WOLFSSL_CHECK_MEM_ZERO. --- .github/configs/os-check-linux.json | 5 + doc/dox_comments/header_files/rsa.h | 15 +- tests/api/test_cmac.c | 2 +- tests/api/test_rsa.c | 231 +++++++++++++++++++++++++++- tests/api/test_rsa.h | 4 +- wolfcrypt/src/cmac.c | 4 +- wolfcrypt/src/cryptocb.c | 5 +- wolfcrypt/src/ed448.c | 41 ++--- wolfcrypt/src/rsa.c | 73 +++++++-- wolfcrypt/test/test.c | 9 +- wolfssl/wolfcrypt/cryptocb.h | 6 +- 11 files changed, 337 insertions(+), 58 deletions(-) diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index e7916c9379d..9a34935dc85 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -130,6 +130,11 @@ "comment": "Exercises the AES-CFB/OFB crypto callback wiring (wc_CryptoCb_AesCfb/Ofb Encrypt/Decrypt, the aes.c hooks, and the dedicated offload unit tests). A normal (non-ONLY) cryptocb build keeps the host software AES present as the callbacks' offload fallback; WOLF_CRYPTO_CB_ONLY_AES (no software fallback) is covered separately by cryptocb-only.yml via swdev.", "configure": ["--enable-cryptocb", "--enable-aescfb", "--enable-aesofb", "--enable-aesctr"]}, +{"name": "cryptocb-hooks-check-mem-zero", "minutes": 2.2, + "comment": "Ed448/CMAC/RSA-PSS crypto callback hooks under WOLFSSL_CHECK_MEM_ZERO. The Ed448 sign hook returns early past the secret-buffer registration, which a cryptocb build without this define does not catch.", + "configure": ["--enable-cryptocb", "--enable-ed448", "--enable-cmac", + "--enable-rsapss", "--enable-keygen", + "CPPFLAGS=-DWOLFSSL_CHECK_MEM_ZERO -DWOLF_CRYPTO_CB_FREE -DWOLF_CRYPTO_CB_RSA_PAD"]}, {"name": "opensslall-rng-seed-cb-no-getpid", "minutes": 2.1, "configure": ["--enable-opensslall", "--enable-opensslextra", "CPPFLAGS=-DWC_RNG_SEED_CB -DWOLFSSL_NO_GETPID"]}, diff --git a/doc/dox_comments/header_files/rsa.h b/doc/dox_comments/header_files/rsa.h index 8d67d591ce2..fc15ca62e81 100644 --- a/doc/dox_comments/header_files/rsa.h +++ b/doc/dox_comments/header_files/rsa.h @@ -597,11 +597,16 @@ int wc_RsaPSS_VerifyInline(byte* in, word32 inLen, byte** out, The key has to be associated with RNG by wc_RsaSetRNG when WC_RSA_BLINDING is enabled. \return the length of the PSS data on success and negative indicates failure. + A crypto callback device that returns recovered data fills out and the + return is that length. A device that reports only a verdict recovers + nothing: out is zeroed and the return is the length the data would have + been, so callers must not read out on that path. \return MEMORY_E memory exception. \param in The byte array to be decrypted. \param inLen The length of in. - \param out Pointer to address containing the PSS data. + \param out Pointer to address containing the PSS data. Zeroed when a + crypto callback device reported only a verdict (see \return). \param outLen The length of out. \param digest Hash of the data that is being verified. \param digestLen Length of hash. @@ -742,13 +747,15 @@ int wc_RsaPSS_VerifyCheck_ex(byte* in, word32 inLen, The key has to be associated with RNG by wc_RsaSetRNG when WC_RSA_BLINDING is enabled. \return the length of the PSS data on success and negative indicates failure. - On the crypto callback path *out is set to NULL though the return stays - positive, so callers must not dereference *out. + A crypto callback device that returns recovered data points *out into in and + the return is that length. A device that reports only a verdict recovers + nothing: *out is set to NULL though the return stays positive, so callers + must check *out before dereferencing it. \param in The byte array to be decrypted. \param inLen The length of in. \param out The byte array for the decrypted data to be stored. Set to NULL - when a crypto callback device performed the verify (see \return). + when a crypto callback device reported only a verdict (see \return). \param digest Hash of the data that is being verified. \param digestLen Length of hash. \param hash The hash type to be in message diff --git a/tests/api/test_cmac.c b/tests/api/test_cmac.c index 9659935b153..1179cf3e238 100644 --- a/tests/api/test_cmac.c +++ b/tests/api/test_cmac.c @@ -720,7 +720,7 @@ int test_wc_AesCmacVerify_CryptoCb_LenMismatch(void) return EXPECT_RESULT(); } /* END test_wc_AesCmacVerify_CryptoCb_LenMismatch */ -/* Test that wc_CmacFree() dispatches a WC_ALGO_TYPE_FREE / WC_ALGO_TYPE_CMAC +/* Test that wc_CmacFree() sends a WC_ALGO_TYPE_FREE / WC_ALGO_TYPE_CMAC * request to a registered crypto callback (CryptoCb) device. */ #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) && \ defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) diff --git a/tests/api/test_rsa.c b/tests/api/test_rsa.c index 4bf190f67e5..fa130e5bb47 100644 --- a/tests/api/test_rsa.c +++ b/tests/api/test_rsa.c @@ -2049,8 +2049,8 @@ static int rsa_pss_test_crypto_cb(int devIdArg, wc_CryptoInfo* info, void* ctx) XFREE(outbuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); - /* Only a real verdict maps to res; a genuine internal error (e.g. - * MEMORY_E) is propagated so it is not masked as a bad signature. */ + /* Only a pass or fail sets res. A real error such as MEMORY_E is + * returned as-is, so it is not mistaken for a bad signature. */ if (v > 0) { if (info->pk.rsa_pss_verify.res != NULL) *info->pk.rsa_pss_verify.res = 1; @@ -2080,6 +2080,7 @@ int test_wc_CryptoCb_RsaPssVerify(void) WC_RNG rng; byte digest[WC_SHA256_DIGEST_SIZE]; word32 sigLen = 0; + int sigSz = 0; int r; WC_DECLARE_VAR(key, RsaKey, 1, HEAP_HINT); WC_DECLARE_VAR(sig, byte, 512, HEAP_HINT); @@ -2111,9 +2112,12 @@ int test_wc_CryptoCb_RsaPssVerify(void) ExpectIntEQ(wc_RsaSetRNG(key, &rng), 0); /* PSS sign runs in software (device declines). */ - ExpectIntGT(sigLen = (word32)wc_RsaPSS_Sign(digest, + ExpectIntGT(sigSz = wc_RsaPSS_Sign(digest, (word32)sizeof(digest), sig, 512, WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key, &rng), 0); + if (sigSz > 0) { + sigLen = (word32)sigSz; + } /* Positive: verify routes through the device and succeeds. */ pssVerifySeen = 0; @@ -2123,7 +2127,7 @@ int test_wc_CryptoCb_RsaPssVerify(void) /* Positive: the inline variant also routes through the device; *out is NULL * on that path. Use a copy of the sig since inline 'in' is reused as out. */ - if (WC_VAR_OK(sig) && WC_VAR_OK(rec)) { + if (EXPECT_SUCCESS() && WC_VAR_OK(sig) && WC_VAR_OK(rec)) { byte* inlineOut = rec; /* non-NULL sentinel, must be cleared to NULL */ XMEMCPY(rec, sig, sigLen); pssVerifySeen = 0; @@ -2153,3 +2157,222 @@ int test_wc_CryptoCb_RsaPssVerify(void) #endif return EXPECT_RESULT(); } /* END test_wc_CryptoCb_RsaPssVerify */ + +/* Test that a crypto callback device which returns recovered PSS data is + * reported to the caller, and that an over-claimed length is ignored. */ +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ + defined(WC_RSA_PSS) && !defined(NO_RSA) && !defined(WC_NO_RNG) && \ + defined(WOLFSSL_KEY_GEN) && !defined(NO_SHA256) + +#define PSS_CB_RECOVER 0 +#define PSS_CB_OVERCLAIM 1 + +typedef struct { + int seen; + int mode; +} rsaPssRecoverCtx; + +/* Spy device that verifies in software and, depending on mode, hands the + * recovered block back through out/outLen or over-claims the length. */ +static int rsa_pss_recover_crypto_cb(int devIdArg, wc_CryptoInfo* info, + void* ctx) +{ + rsaPssRecoverCtx* c = (rsaPssRecoverCtx*)ctx; + + (void)devIdArg; + + if (info == NULL || c == NULL) { + return BAD_FUNC_ARG; + } + + if (info->algo_type == WC_ALGO_TYPE_PK && + info->pk.type == WC_PK_TYPE_RSA_PSS_VERIFY) { + RsaKey* key = info->pk.rsa_pss_verify.key; + int save; + int v; + byte* outbuf; + word32 outbufSz = 512; + + outbuf = (byte*)XMALLOC(outbufSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (outbuf == NULL) { + return MEMORY_E; + } + + c->seen++; + + save = key->devId; + key->devId = INVALID_DEVID; + v = wc_RsaPSS_VerifyCheck( + info->pk.rsa_pss_verify.sig, info->pk.rsa_pss_verify.sigSz, + outbuf, outbufSz, + info->pk.rsa_pss_verify.digest, + info->pk.rsa_pss_verify.digestSz, + info->pk.rsa_pss_verify.hash, info->pk.rsa_pss_verify.mgf, + key); + key->devId = save; + + if (v > 0) { + if (info->pk.rsa_pss_verify.res != NULL) { + *info->pk.rsa_pss_verify.res = 1; + } + if (c->mode == PSS_CB_RECOVER) { + if ((info->pk.rsa_pss_verify.out != NULL) && + ((word32)v <= info->pk.rsa_pss_verify.outSz)) { + XMEMCPY(info->pk.rsa_pss_verify.out, outbuf, (word32)v); + if (info->pk.rsa_pss_verify.outLen != NULL) { + *info->pk.rsa_pss_verify.outLen = (word32)v; + } + } + } + else { + /* Claim more than the buffer holds; must be ignored. */ + if (info->pk.rsa_pss_verify.outLen != NULL) { + *info->pk.rsa_pss_verify.outLen = + info->pk.rsa_pss_verify.outSz + 1; + } + } + XFREE(outbuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + return 0; + } + + XFREE(outbuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (v == WC_NO_ERR_TRACE(BAD_PADDING_E) || + v == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + if (info->pk.rsa_pss_verify.res != NULL) { + *info->pk.rsa_pss_verify.res = 0; + } + return 0; + } + return v; + } + + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); +} +#endif + +int test_wc_CryptoCb_RsaPssVerifyRecover(void) +{ + EXPECT_DECLS; +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ + defined(WC_RSA_PSS) && !defined(NO_RSA) && !defined(WC_NO_RNG) && \ + defined(WOLFSSL_KEY_GEN) && !defined(NO_SHA256) + int devId = 4471; + rsaPssRecoverCtx cbCtx; + WC_RNG rng; + byte digest[WC_SHA256_DIGEST_SIZE]; + word32 sigLen = 0; + int sigSz = 0; + int swRet = 0; + int i; + int allZero; + WC_DECLARE_VAR(key, RsaKey, 1, HEAP_HINT); + WC_DECLARE_VAR(sig, byte, 512, HEAP_HINT); + WC_DECLARE_VAR(rec, byte, 512, HEAP_HINT); + WC_DECLARE_VAR(swRec, byte, 512, HEAP_HINT); + + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(&cbCtx, 0, sizeof(cbCtx)); + XMEMSET(digest, 0x3c, sizeof(digest)); + + WC_ALLOC_VAR(key, RsaKey, 1, HEAP_HINT); + WC_ALLOC_VAR(sig, byte, 512, HEAP_HINT); + WC_ALLOC_VAR(rec, byte, 512, HEAP_HINT); + WC_ALLOC_VAR(swRec, byte, 512, HEAP_HINT); +#ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC + ExpectNotNull(key); + ExpectNotNull(sig); + ExpectNotNull(rec); + ExpectNotNull(swRec); +#endif + if (WC_VAR_OK(sig)) { + XMEMSET(sig, 0, 512); + } + if (WC_VAR_OK(rec)) { + XMEMSET(rec, 0, 512); + } + if (WC_VAR_OK(swRec)) { + XMEMSET(swRec, 0, 512); + } + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_InitRsaKey_ex(key, HEAP_HINT, INVALID_DEVID), 0); + ExpectIntEQ(wc_MakeRsaKey(key, 2048, WC_RSA_EXPONENT, &rng), 0); + ExpectIntEQ(wc_RsaSetRNG(key, &rng), 0); + + ExpectIntGT(sigSz = wc_RsaPSS_Sign(digest, (word32)sizeof(digest), sig, + 512, WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key, &rng), 0); + if (sigSz > 0) { + sigLen = (word32)sigSz; + } + + /* Software baseline: no device registered on the key yet. */ + ExpectIntGT(swRet = wc_RsaPSS_VerifyCheck(sig, sigLen, swRec, 512, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key), 0); + + /* Device that recovers: same return as software, same bytes in out. */ + ExpectIntEQ(wc_CryptoCb_RegisterDevice(devId, rsa_pss_recover_crypto_cb, + &cbCtx), 0); + if (EXPECT_SUCCESS()) { + key->devId = devId; + cbCtx.mode = PSS_CB_RECOVER; + cbCtx.seen = 0; + XMEMSET(rec, 0, 512); + ExpectIntEQ(wc_RsaPSS_VerifyCheck(sig, sigLen, rec, 512, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key), + swRet); + ExpectIntGE(cbCtx.seen, 1); + ExpectIntEQ(XMEMCMP(rec, swRec, (word32)swRet), 0); + } + + /* Device that over-claims the length: treated as verdict only, so out is + * zeroed and the return is still the software length. */ + if (EXPECT_SUCCESS()) { + cbCtx.mode = PSS_CB_OVERCLAIM; + cbCtx.seen = 0; + XMEMSET(rec, 0xA5, 512); + ExpectIntEQ(wc_RsaPSS_VerifyCheck(sig, sigLen, rec, 512, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key), + swRet); + ExpectIntGE(cbCtx.seen, 1); + allZero = 1; + if (WC_VAR_OK(rec)) { + for (i = 0; i < swRet; i++) { + if (rec[i] != 0) { + allZero = 0; + break; + } + } + } + ExpectIntEQ(allZero, 1); + } + + /* Inline variant with a recovering device: *out points into in. */ + if (EXPECT_SUCCESS() && WC_VAR_OK(sig) && WC_VAR_OK(rec)) { + byte* inlineOut = NULL; + + cbCtx.mode = PSS_CB_RECOVER; + cbCtx.seen = 0; + XMEMCPY(rec, sig, sigLen); + ExpectIntEQ(wc_RsaPSS_VerifyCheckInline(rec, sigLen, &inlineOut, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, WC_MGF1SHA256, key), + swRet); + ExpectIntGE(cbCtx.seen, 1); + ExpectPtrEq(inlineOut, rec); + ExpectIntEQ(XMEMCMP(rec, swRec, (word32)swRet), 0); + } + + if (WC_VAR_OK(key)) { + key->devId = INVALID_DEVID; + } + DoExpectIntEQ(wc_FreeRsaKey(key), 0); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + wc_CryptoCb_UnRegisterDevice(devId); + + WC_FREE_VAR(swRec, HEAP_HINT); + WC_FREE_VAR(rec, HEAP_HINT); + WC_FREE_VAR(sig, HEAP_HINT); + WC_FREE_VAR(key, HEAP_HINT); +#endif + return EXPECT_RESULT(); +} /* END test_wc_CryptoCb_RsaPssVerifyRecover */ + diff --git a/tests/api/test_rsa.h b/tests/api/test_rsa.h index 13d503101e1..dcb7cf6dd92 100644 --- a/tests/api/test_rsa.h +++ b/tests/api/test_rsa.h @@ -48,6 +48,7 @@ int test_wc_RsaKeyToDer_SizeOverflow(void); int test_wc_RsaDecisionCoverage(void); int test_wc_RsaFeatureCoverage(void); int test_wc_CryptoCb_RsaPssVerify(void); +int test_wc_CryptoCb_RsaPssVerifyRecover(void); #define TEST_RSA_DECLS \ TEST_DECL_GROUP("rsa", test_wc_InitRsaKey), \ @@ -73,6 +74,7 @@ int test_wc_CryptoCb_RsaPssVerify(void); TEST_DECL_GROUP("rsa", test_wc_RsaKeyToDer_SizeOverflow), \ TEST_DECL_GROUP("rsa", test_wc_RsaDecisionCoverage), \ TEST_DECL_GROUP("rsa", test_wc_RsaFeatureCoverage), \ - TEST_DECL_GROUP("rsa", test_wc_CryptoCb_RsaPssVerify) + TEST_DECL_GROUP("rsa", test_wc_CryptoCb_RsaPssVerify), \ + TEST_DECL_GROUP("rsa", test_wc_CryptoCb_RsaPssVerifyRecover) #endif /* WOLFCRYPT_TEST_RSA_H */ diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index 6b17c8f8da9..28a8b50e824 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -160,9 +160,7 @@ static int _InitCmac_common(Cmac* cmac, const byte* key, word32 keySz, #ifdef WOLF_CRYPTO_CB /* Set devId regardless of value (invalid or not) */ cmac->devId = devId; - /* Set type up front so wc_CmacFree can clean up properly when the - * callback handles the init and returns before the software path - * below has a chance to set it. */ + /* Set before the cryptocb early return so wc_CmacFree can clean up. */ cmac->type = (CmacType)type; #ifndef WOLF_CRYPTO_CB_FIND if (devId != INVALID_DEVID) diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index dc27ffef155..3da458981c5 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -637,7 +637,7 @@ int wc_CryptoCb_RsaPad(const byte* in, word32 inLen, byte* out, * signature and digest so the device does the whole verify and returns a verdict. */ int wc_CryptoCb_RsaPssVerify(const byte* sig, word32 sigSz, const byte* digest, word32 digestSz, enum wc_HashType hash, int mgf, int saltLen, RsaKey* key, - int* res) + int* res, byte* out, word32 outSz, word32* outLen) { int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); CryptoCb* dev; @@ -662,6 +662,9 @@ int wc_CryptoCb_RsaPssVerify(const byte* sig, word32 sigSz, const byte* digest, cryptoInfo.pk.rsa_pss_verify.saltLen = saltLen; cryptoInfo.pk.rsa_pss_verify.key = key; cryptoInfo.pk.rsa_pss_verify.res = res; + cryptoInfo.pk.rsa_pss_verify.out = out; + cryptoInfo.pk.rsa_pss_verify.outSz = outSz; + cryptoInfo.pk.rsa_pss_verify.outLen = outLen; ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); } diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index 350b2cc4b8e..3d084b5c75d 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -446,20 +446,6 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, WC_DECLARE_VAR(sha, wc_Shake, 1, key ? key->heap : NULL); #endif -#ifdef WOLFSSL_CHECK_MEM_ZERO - /* Register the secret nonce/expanded-key buffers up front so that any exit - * path from here to the ForceZero below is checked for proper zeroization. - * XMEMSET gives them a defined value before the hash steps fill them. */ - XMEMSET(az, 0, sizeof(az)); - XMEMSET(nonce, 0, sizeof(nonce)); - wc_MemZero_Add("wc_ed448_sign_msg_ex az", az, sizeof(az)); - wc_MemZero_Add("wc_ed448_sign_msg_ex nonce", nonce, sizeof(nonce)); -#ifdef WOLFSSL_EDDSA_CHECK_PRIV_ON_SIGN - XMEMSET(orig_k, 0, sizeof(orig_k)); - wc_MemZero_Add("wc_ed448_sign_msg_ex orig_k", orig_k, sizeof(orig_k)); -#endif -#endif - /* sanity check on arguments */ if ((in == NULL) || (out == NULL) || (outLen == NULL) || (key == NULL) || ((context == NULL) && (contextLen != 0))) { @@ -478,24 +464,27 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, { ret = wc_CryptoCb_Ed448Sign(in, inLen, out, outLen, key, type, context, contextLen); - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - #ifdef WOLFSSL_CHECK_MEM_ZERO - /* The device signed, so this returns without reaching the - * ForceZero below. Release the registrations made above or - * they outlive the stack frame and trip a later check. */ - #ifdef WOLFSSL_EDDSA_CHECK_PRIV_ON_SIGN - wc_MemZero_Check(orig_k, sizeof(orig_k)); - #endif - wc_MemZero_Check(nonce, sizeof(nonce)); - wc_MemZero_Check(az, sizeof(az)); - #endif + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; - } ret = 0; /* fall-through when unavailable */ } } #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Register the secret nonce/expanded-key buffers up front so that any exit + * path from here to the ForceZero below is checked for proper zeroization. + * XMEMSET gives them a defined value before the hash steps fill them. */ + XMEMSET(az, 0, sizeof(az)); + XMEMSET(nonce, 0, sizeof(nonce)); + wc_MemZero_Add("wc_ed448_sign_msg_ex az", az, sizeof(az)); + wc_MemZero_Add("wc_ed448_sign_msg_ex nonce", nonce, sizeof(nonce)); +#ifdef WOLFSSL_EDDSA_CHECK_PRIV_ON_SIGN + XMEMSET(orig_k, 0, sizeof(orig_k)); + wc_MemZero_Add("wc_ed448_sign_msg_ex orig_k", orig_k, sizeof(orig_k)); +#endif +#endif + if ((ret == 0) && (!key->pubKeySet)) { ret = BAD_FUNC_ARG; } diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 871364efd38..794dbd908ce 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -4645,8 +4645,7 @@ int wc_RsaPSS_CheckPadding_ex(const byte* in, word32 inSz, const byte* sig, * key Public RSA key. * returns the length of the PSS data on success and negative indicates failure. * - * Note: when a crypto callback device performs the verify, *out is set to NULL - * even though a positive length is returned; callers must not dereference *out. + * Note: a device that recovers nothing sets *out to NULL, so check *out first. */ int wc_RsaPSS_VerifyCheckInline(byte* in, word32 inLen, byte** out, const byte* digest, word32 digestLen, @@ -4691,16 +4690,39 @@ int wc_RsaPSS_VerifyCheckInline(byte* in, word32 inLen, byte** out, if (key != NULL) #endif { - int res = 0; + int res = 0; + word32 recovered = 0; + ret = wc_CryptoCb_RsaPssVerify(in, inLen, digest, digestLen, hash, mgf, - saltLen, key, &res); + saltLen, key, &res, in, inLen, + &recovered); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { if (ret == 0) { - /* Device verified internally; no recovered PSS block to expose, - * so report no inline output rather than a misleading pointer. */ - if (out != NULL) - *out = NULL; - ret = (res != 0) ? (int)inLen : SIG_VERIFY_E; + if (recovered > inLen) { + recovered = 0; + } + if (res == 0) { + ret = SIG_VERIFY_E; + } + else if (recovered > 0) { + if (out != NULL) { + *out = in; + } + ret = (int)recovered; + } + else if (inLen < (word32)(saltLen + hLen)) { + ret = RSA_BUFFER_E; + } + else { + /* Device gave a verdict only; nothing to expose. */ + if (out != NULL) { + *out = NULL; + } + ret = saltLen + hLen; + } + } + else if (ret > 0) { + ret = SIG_VERIFY_E; } return ret; } @@ -4777,12 +4799,37 @@ int wc_RsaPSS_VerifyCheck(const byte* in, word32 inLen, byte* out, word32 outLen if (key != NULL) #endif { - int res = 0; + int res = 0; + word32 recovered = 0; + ret = wc_CryptoCb_RsaPssVerify(in, inLen, digest, digestLen, hash, mgf, - saltLen, key, &res); + saltLen, key, &res, out, outLen, + &recovered); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - if (ret == 0) - ret = (res != 0) ? (int)inLen : SIG_VERIFY_E; + if (ret == 0) { + if (recovered > outLen) { + recovered = 0; + } + if (res == 0) { + ret = SIG_VERIFY_E; + } + else if (recovered > 0) { + ret = (int)recovered; + } + else if (outLen < (word32)(saltLen + hLen)) { + ret = RSA_BUFFER_E; + } + else { + /* Device gave a verdict only; leave no stale data behind. */ + if (out != NULL) { + XMEMSET(out, 0, (word32)(saltLen + hLen)); + } + ret = saltLen + hLen; + } + } + else if (ret > 0) { + ret = SIG_VERIFY_E; + } return ret; } ret = 0; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 7a84311ef0a..3b7f550bcda 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -78848,7 +78848,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) WOLFSSL_MSG_EX("CryptoDevCb: Pk Type %d\n", info->pk.type); #endif - #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) + #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ + !defined(WOLF_CRYPTO_CB_ONLY_RSA) if (info->pk.type == WC_PK_TYPE_RSA_PSS_VERIFY) { RsaKey* pssKey = info->pk.rsa_pss_verify.key; int pssSaveDevId = pssKey->devId; @@ -78876,8 +78877,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) XFREE(pssOut, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); - /* Only a real verdict maps to res; a genuine internal error (e.g. - * MEMORY_E) is propagated so it is not masked as a bad signature. */ + /* Only a pass or fail sets res. A real error such as MEMORY_E is + * returned as-is, so it is not mistaken for a bad signature. */ if (pssVer > 0) { if (info->pk.rsa_pss_verify.res != NULL) *info->pk.rsa_pss_verify.res = 1; @@ -78891,7 +78892,7 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } return pssVer; } - #endif /* WC_RSA_PSS && WOLF_CRYPTO_CB_RSA_PAD */ + #endif /* WC_RSA_PSS && WOLF_CRYPTO_CB_RSA_PAD && !WOLF_CRYPTO_CB_ONLY_RSA */ #ifndef NO_RSA if (info->pk.type == WC_PK_TYPE_RSA) { diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index e8bb2f3b872..3070addc6a9 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -211,6 +211,9 @@ typedef struct wc_CryptoInfo { int saltLen; RsaKey* key; int* res; + byte* out; + word32 outSz; + word32* outLen; } rsa_pss_verify; #endif #endif @@ -851,7 +854,8 @@ WOLFSSL_LOCAL int wc_CryptoCb_RsaPad(const byte* in, word32 inLen, byte* out, word32* outLen, int type, RsaKey* key, WC_RNG* rng, RsaPadding *padding); WOLFSSL_LOCAL int wc_CryptoCb_RsaPssVerify(const byte* sig, word32 sigSz, const byte* digest, word32 digestSz, enum wc_HashType hash, int mgf, - int saltLen, RsaKey* key, int* res); + int saltLen, RsaKey* key, int* res, byte* out, word32 outSz, + word32* outLen); #endif #ifdef WOLFSSL_KEY_GEN From a39e0bdba0516fef5c5b424692464bdb3c6cc08e Mon Sep 17 00:00:00 2001 From: night1rider Date: Mon, 10 Aug 2026 12:56:29 -0600 Subject: [PATCH 8/9] Document the WC_PK_TYPE_RSA_PSS_VERIFY handler return contract in cryptocb.h --- wolfssl/wolfcrypt/cryptocb.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index 3070addc6a9..ea521ec26f7 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -201,6 +201,9 @@ typedef struct wc_CryptoInfo { int* keySize; } rsa_get_size; #ifdef WOLF_CRYPTO_CB_RSA_PAD + /* WC_PK_TYPE_RSA_PSS_VERIFY handler: return 0 with *res set + * (1 good, 0 bad), or a negative error. Anything positive is + * counted as a failed verify. Filling out is optional. */ struct { const byte* sig; word32 sigSz; From 73298a7f6375b3fa21907546abaf08dcc2d98410 Mon Sep 17 00:00:00 2001 From: night1rider Date: Mon, 10 Aug 2026 13:46:01 -0600 Subject: [PATCH 9/9] Share one helper between the RSA-PSS verify crypto callback paths --- wolfcrypt/src/rsa.c | 140 +++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 68 deletions(-) diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 794dbd908ce..a4454a78fdd 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -4631,6 +4631,66 @@ int wc_RsaPSS_CheckPadding_ex(const byte* in, word32 inSz, const byte* sig, } +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) +/* Let a device verify an RSA-PSS signature and its padding in one shot (it gets + * the digest, which the RsaPad path does not). Shared by the two verify and + * check entry points below. + * + * out Buffer the device may write the recovered PSS block into. + * outSz Size of that buffer. + * recovered Set to the number of bytes the device wrote, 0 for a verdict only. + * returns the length the caller should report, a negative error, or + * CRYPTOCB_UNAVAILABLE when no device handled it. + */ +static int RsaPssVerifyDevice(const byte* in, word32 inLen, const byte* digest, + word32 digestLen, enum wc_HashType hash, int mgf, int saltLen, int hLen, + RsaKey* key, byte* out, word32 outSz, word32* recovered) +{ + int ret; + int res = 0; + word32 recSz = 0; + + *recovered = 0; + +#ifndef WOLF_CRYPTO_CB_FIND + if (key == NULL || key->devId == INVALID_DEVID) +#else + if (key == NULL) +#endif + { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + ret = wc_CryptoCb_RsaPssVerify(in, inLen, digest, digestLen, hash, mgf, + saltLen, key, &res, out, outSz, &recSz); + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + return ret; + } + if (ret > 0) { + /* A handler returns 0 with res set, or a negative error. */ + return SIG_VERIFY_E; + } + if (ret != 0) { + return ret; + } + if (recSz > outSz) { + recSz = 0; + } + if (res == 0) { + return SIG_VERIFY_E; + } + if (recSz > 0) { + *recovered = recSz; + return (int)recSz; + } + if (outSz < (word32)(saltLen + hLen)) { + return RSA_BUFFER_E; + } + return saltLen + hLen; +} +#endif + + /* Verify the message signed with RSA-PSS. * The input buffer is reused for the output buffer. * Salt length is equal to hash length. @@ -4682,48 +4742,21 @@ int wc_RsaPSS_VerifyCheckInline(byte* in, word32 inLen, byte** out, #endif #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) - /* Let a device verify signature + padding in one shot (it gets the digest, - * which the RsaPad path does not). Fall through to software if unavailable. */ - #ifndef WOLF_CRYPTO_CB_FIND - if (key != NULL && key->devId != INVALID_DEVID) - #else - if (key != NULL) - #endif { - int res = 0; word32 recovered = 0; - ret = wc_CryptoCb_RsaPssVerify(in, inLen, digest, digestLen, hash, mgf, - saltLen, key, &res, in, inLen, - &recovered); + ret = RsaPssVerifyDevice(in, inLen, digest, digestLen, hash, mgf, + saltLen, hLen, key, in, inLen, &recovered); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - if (ret == 0) { - if (recovered > inLen) { - recovered = 0; - } - if (res == 0) { - ret = SIG_VERIFY_E; - } - else if (recovered > 0) { - if (out != NULL) { - *out = in; - } - ret = (int)recovered; - } - else if (inLen < (word32)(saltLen + hLen)) { - ret = RSA_BUFFER_E; + if ((ret > 0) && (out != NULL)) { + if (recovered > 0) { + *out = in; } else { - /* Device gave a verdict only; nothing to expose. */ - if (out != NULL) { - *out = NULL; - } - ret = saltLen + hLen; + /* Device reported a verdict only; nothing to expose. */ + *out = NULL; } } - else if (ret > 0) { - ret = SIG_VERIFY_E; - } return ret; } ret = 0; @@ -4791,44 +4824,15 @@ int wc_RsaPSS_VerifyCheck(const byte* in, word32 inLen, byte* out, word32 outLen #endif #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_RSA_PAD) - /* Let a device verify signature + padding in one shot (it gets the digest, - * which the RsaPad path does not). Fall through to software if unavailable. */ - #ifndef WOLF_CRYPTO_CB_FIND - if (key != NULL && key->devId != INVALID_DEVID) - #else - if (key != NULL) - #endif { - int res = 0; word32 recovered = 0; - ret = wc_CryptoCb_RsaPssVerify(in, inLen, digest, digestLen, hash, mgf, - saltLen, key, &res, out, outLen, - &recovered); + ret = RsaPssVerifyDevice(in, inLen, digest, digestLen, hash, mgf, + saltLen, hLen, key, out, outLen, &recovered); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - if (ret == 0) { - if (recovered > outLen) { - recovered = 0; - } - if (res == 0) { - ret = SIG_VERIFY_E; - } - else if (recovered > 0) { - ret = (int)recovered; - } - else if (outLen < (word32)(saltLen + hLen)) { - ret = RSA_BUFFER_E; - } - else { - /* Device gave a verdict only; leave no stale data behind. */ - if (out != NULL) { - XMEMSET(out, 0, (word32)(saltLen + hLen)); - } - ret = saltLen + hLen; - } - } - else if (ret > 0) { - ret = SIG_VERIFY_E; + if ((ret > 0) && (recovered == 0) && (out != NULL)) { + /* Device gave a verdict only; leave no stale data behind. */ + XMEMSET(out, 0, (word32)ret); } return ret; }