diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index e7916c9379..9a34935dc8 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 c0cbab2830..fc15ca62e8 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,10 +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. + 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. + \param out The byte array for the decrypted data to be stored. Set to NULL + 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 5b0b942372..1179cf3e23 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() 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) +/* 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 e7e4616c0a..28272c59a6 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 affae98d40..0d4e0bc3fb 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 a31ef1b454..eccb3ba343 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 bd1c6b4482..fa130e5bb4 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,377 @@ 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 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; + 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 sigSz = 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(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; + 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 (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; + 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 */ + +/* 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 69081a5cc5..dcb7cf6dd9 100644 --- a/tests/api/test_rsa.h +++ b/tests/api/test_rsa.h @@ -47,6 +47,8 @@ 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); +int test_wc_CryptoCb_RsaPssVerifyRecover(void); #define TEST_RSA_DECLS \ TEST_DECL_GROUP("rsa", test_wc_InitRsaKey), \ @@ -71,6 +73,8 @@ 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), \ + 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 ef714d1cbb..28a8b50e82 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -160,6 +160,8 @@ 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 before the cryptocb early return so wc_CmacFree can clean up. */ + cmac->type = (CmacType)type; #ifndef WOLF_CRYPTO_CB_FIND if (devId != INVALID_DEVID) #endif @@ -376,6 +378,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 cb9c7bc65a..3da458981c 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,45 @@ 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, byte* out, word32 outSz, word32* outLen) +{ + 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; + 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); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} #endif /* WOLF_CRYPTO_CB_RSA_PAD */ #ifdef WOLFSSL_KEY_GEN @@ -1354,6 +1396,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 e8c1a0cd50..3d084b5c75 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 @@ -443,6 +446,31 @@ 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 + /* sanity check on arguments */ + if ((in == NULL) || (out == NULL) || (outLen == NULL) || (key == NULL) || + ((context == NULL) && (contextLen != 0))) { + 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 + 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 + #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. @@ -457,11 +485,6 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, #endif #endif - /* sanity check on arguments */ - if ((in == NULL) || (out == NULL) || (outLen == NULL) || (key == NULL) || - ((context == NULL) && (contextLen != 0))) { - ret = BAD_FUNC_ARG; - } if ((ret == 0) && (!key->pubKeySet)) { ret = BAD_FUNC_ARG; } @@ -469,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; @@ -924,6 +942,21 @@ 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 + { + 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)) + 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 07b12e34f4..a4454a78fd 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. @@ -4644,6 +4704,8 @@ 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: 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, @@ -4679,6 +4741,28 @@ 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) + { + word32 recovered = 0; + + 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) && (out != NULL)) { + if (recovered > 0) { + *out = in; + } + else { + /* Device reported a verdict only; nothing to expose. */ + *out = NULL; + } + } + 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 +4823,23 @@ 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) + { + word32 recovered = 0; + + 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) && (recovered == 0) && (out != NULL)) { + /* Device gave a verdict only; leave no stale data behind. */ + XMEMSET(out, 0, (word32)ret); + } + 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 e9426ab71c..3b7f550bcd 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,52 @@ 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) && \ + !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; + 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 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; + 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 && !WOLF_CRYPTO_CB_ONLY_RSA */ + #ifndef NO_RSA if (info->pk.type == WC_PK_TYPE_RSA) { /* set devId to invalid, so software is used */ @@ -79325,6 +79381,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 +80700,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 +81324,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 +81801,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 bdce20d8b1..ea521ec26f 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,25 @@ typedef struct wc_CryptoInfo { const RsaKey* key; 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; + const byte* digest; + word32 digestSz; + enum wc_HashType hash; + int mgf; + int saltLen; + RsaKey* key; + int* res; + byte* out; + word32 outSz; + word32* outLen; + } rsa_pss_verify; + #endif #endif #ifdef HAVE_ECC #ifdef HAVE_ECC_DHE @@ -360,6 +382,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 +855,10 @@ 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, byte* out, word32 outSz, + word32* outLen); #endif #ifdef WOLFSSL_KEY_GEN @@ -884,6 +933,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 4ef6844d2a..c17195605a 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 };