From 8040cefc41ac470b7c8a62a01bf4946d9573b417 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Fri, 4 Sep 2026 09:51:14 +0900 Subject: [PATCH] rsa: hold the key mutex on every shared-key wolfCrypt call - The five verify helpers, wp_rsaa_encrypt(), wp_rsasve_generate(), wp_rsasve_recover() and wp_rsa_validate() take the per-key mutex around their wc_* calls; wp_rsaa_decrypt() takes it regardless of WC_RSA_BLINDING, which now guards only wc_RsaSetRNG() and its NULL reset. - wp_rsaa_encrypt() and wp_rsaa_decrypt() skip their padding ladders when the lock is not held; wp_rsa_verify_pss() releases on a locked flag; wp_rsasve_generate() cleanses the secret on lock failure. - rc in the verify and RSASVE helpers and in wp_rsa_validate(), and saltLen in wp_rsa_verify_pss(), are initialized at declaration. - The wp_Rsa mutex comment says it is held while refCnt changes and while a wolfCrypt call uses key. - test_rsa_concurrent_ops() runs four threads over one shared EVP_PKEY doing PKCS#1 sign/verify/verify-recover, PSS digest sign/verify, PKCS#1 and OAEP encrypt/decrypt, RSASVE encapsulate/decapsulate, X9.31 sign/verify and a pairwise check. It is built only with HAVE_PTHREAD and without WP_SINGLE_THREADED. Issue: F-8702 --- src/wp_rsa_asym.c | 32 ++++-- src/wp_rsa_kem.c | 41 ++++--- src/wp_rsa_kmgmt.c | 17 ++- src/wp_rsa_sig.c | 71 +++++++++--- test/test_rsa.c | 266 +++++++++++++++++++++++++++++++++++++++++++++ test/unit.c | 1 + test/unit.h | 1 + 7 files changed, 383 insertions(+), 46 deletions(-) diff --git a/src/wp_rsa_asym.c b/src/wp_rsa_asym.c index 9fd1a035..318e16b8 100644 --- a/src/wp_rsa_asym.c +++ b/src/wp_rsa_asym.c @@ -311,12 +311,22 @@ static int wp_rsaa_encrypt(wp_RsaAsymCtx* ctx, unsigned char* out, } else { int rc = 0; + int locked = 0; if (outSize == (size_t)-1) { outSize = *outLen; } - if ((ctx->padMode == RSA_PKCS1_PADDING) || - (ctx->padMode == RSA_PKCS1_WITH_TLS_PADDING)) { + if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { + ok = 0; + } + else { + locked = 1; + } + if (!ok) { + /* Mutex not held - skip rather than run the operation unlocked. */ + } + else if ((ctx->padMode == RSA_PKCS1_PADDING) || + (ctx->padMode == RSA_PKCS1_WITH_TLS_PADDING)) { rc = wc_RsaPublicEncrypt(in, (word32)inLen, out, (word32)outSize, wp_rsa_get_key(ctx->rsa), &ctx->rng); if (rc < 0) { @@ -354,6 +364,9 @@ static int wp_rsaa_encrypt(wp_RsaAsymCtx* ctx, unsigned char* out, else { ok = 0; } + if (locked) { + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); + } if (ok) { *outLen = rc; } @@ -433,31 +446,28 @@ static int wp_rsaa_decrypt(wp_RsaAsymCtx* ctx, unsigned char* out, } else { int rc = 0; -#ifdef WC_RSA_BLINDING int locked = 0; -#endif if (outSize == (size_t)-1) { outSize = *outLen; } -#ifdef WC_RSA_BLINDING - /* Fail closed if the key mutex can't be held for the shared-key RNG. */ if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { ok = 0; } else { locked = 1; +#ifdef WC_RSA_BLINDING rc = wc_RsaSetRNG(wp_rsa_get_key(ctx->rsa), &ctx->rng); if (rc != 0) { WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaSetRNG", rc); ok = 0; } +#endif /* WC_RSA_BLINDING */ } if (!ok) { + /* Lock or RNG setup failed - skip the operation. */ } - else -#endif /* WC_RSA_BLINDING */ - if (ctx->padMode == RSA_PKCS1_PADDING) { + else if (ctx->padMode == RSA_PKCS1_PADDING) { PRIVATE_KEY_UNLOCK(); rc = wc_RsaPrivateDecrypt(in, (word32)inLen, out, (word32)outSize, wp_rsa_get_key(ctx->rsa)); @@ -546,12 +556,12 @@ static int wp_rsaa_decrypt(wp_RsaAsymCtx* ctx, unsigned char* out, else { ok = 0; } -#ifdef WC_RSA_BLINDING if (locked) { +#ifdef WC_RSA_BLINDING wc_RsaSetRNG(wp_rsa_get_key(ctx->rsa), NULL); +#endif wp_unlock(wp_rsa_get_mutex(ctx->rsa)); } -#endif if (ok) { *outLen = rc; } diff --git a/src/wp_rsa_kem.c b/src/wp_rsa_kem.c index 31dbfc8b..dd7f7957 100644 --- a/src/wp_rsa_kem.c +++ b/src/wp_rsa_kem.c @@ -344,16 +344,24 @@ static int wp_rsasve_generate(wp_RsaKemCtx* ctx, unsigned char* out, } if (ok && (out != NULL)) { /* Step 3: out = RSAEP((n,e), z) */ - int rc; + int rc = 0; oLen = nLen; - rc = wc_RsaDirect(secret, nLen, out, &oLen, rsa, RSA_PUBLIC_ENCRYPT, - &ctx->rng); - if (rc < 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaDirect", rc); + if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { OPENSSL_cleanse(secret, nLen); ok = 0; } + if (ok) { + rc = wc_RsaDirect(secret, nLen, out, &oLen, rsa, + RSA_PUBLIC_ENCRYPT, &ctx->rng); + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); + if (rc < 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaDirect", + rc); + OPENSSL_cleanse(secret, nLen); + ok = 0; + } + } /* Front pad output with zeros if required. */ if (ok && (oLen < nLen)) { word32 padLen = nLen - oLen; @@ -450,16 +458,23 @@ static int wp_rsasve_recover(wp_RsaKemCtx* ctx, unsigned char* out, /* Step 3: out = RSADP((n,d), in) */ if (ok && (out != NULL)) { word32 oLen = nLen; - int rc; - - PRIVATE_KEY_UNLOCK(); - rc = wc_RsaDirect((byte*)in, (word32)inLen, out, &oLen, rsa, - RSA_PRIVATE_DECRYPT, &ctx->rng); - PRIVATE_KEY_LOCK(); - if (rc < 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaDirect decrypt", rc); + int rc = 0; + + if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { ok = 0; } + if (ok) { + PRIVATE_KEY_UNLOCK(); + rc = wc_RsaDirect((byte*)in, (word32)inLen, out, &oLen, rsa, + RSA_PRIVATE_DECRYPT, &ctx->rng); + PRIVATE_KEY_LOCK(); + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); + if (rc < 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, + "wc_RsaDirect decrypt", rc); + ok = 0; + } + } /* Front pad output with zeros if required. */ if (ok && (oLen < nLen)) { word32 padLen = nLen - oLen; diff --git a/src/wp_rsa_kmgmt.c b/src/wp_rsa_kmgmt.c index 5163b97f..aca573a9 100644 --- a/src/wp_rsa_kmgmt.c +++ b/src/wp_rsa_kmgmt.c @@ -260,7 +260,7 @@ struct wp_Rsa { RsaKey key; #ifndef WP_SINGLE_THREADED - /** Mutex for reference count updating. */ + /** Held while refCnt changes and while a wolfCrypt call uses key. */ wolfSSL_Mutex mutex; #endif /** Count of references to this object. */ @@ -1280,11 +1280,20 @@ static int wp_rsa_validate(const wp_Rsa* rsa, int selection, int checkType) #ifdef WOLFSSL_RSA_KEY_CHECK if (checkPub && checkPriv) { - int rc = wc_CheckRsaKey((RsaKey*)&rsa->key); - if (rc != 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_CheckRsaKey", rc); + int rc = 0; + + if (wp_lock(wp_rsa_get_mutex((wp_Rsa*)rsa)) != 1) { ok = 0; } + if (ok) { + rc = wc_CheckRsaKey((RsaKey*)&rsa->key); + wp_unlock(wp_rsa_get_mutex((wp_Rsa*)rsa)); + if (rc != 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_CheckRsaKey", + rc); + ok = 0; + } + } /* wc_CheckRsaKey runs the private key operation and only bounds-checks * d (d < n). Check d*e = 1 mod lcm(p-1,q-1) to match OSSL */ diff --git a/src/wp_rsa_sig.c b/src/wp_rsa_sig.c index 4568eb92..90916b9e 100644 --- a/src/wp_rsa_sig.c +++ b/src/wp_rsa_sig.c @@ -1084,7 +1084,7 @@ static int wp_rsa_verify_pkcs1(wp_RsaSigCtx* ctx, const unsigned char* sig, unsigned char* decryptedSig) { int ok = 1; - int rc; + int rc = 0; unsigned char* encodedDigest = NULL; int encodedDigestLen = 0; @@ -1094,12 +1094,19 @@ static int wp_rsa_verify_pkcs1(wp_RsaSigCtx* ctx, const unsigned char* sig, return 0; } - rc = wc_RsaSSL_Verify(sig, (word32)sigLen, decryptedSig, (word32)sigLen, - wp_rsa_get_key(ctx->rsa)); - if (rc < 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaSSL_Verify", rc); + if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { ok = 0; } + if (ok) { + rc = wc_RsaSSL_Verify(sig, (word32)sigLen, decryptedSig, (word32)sigLen, + wp_rsa_get_key(ctx->rsa)); + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); + if (rc < 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaSSL_Verify", + rc); + ok = 0; + } + } if (ok && ((size_t)rc > tbsLen)) { encodedDigest = (unsigned char*)OPENSSL_malloc(MAX_DER_DIGEST_SZ); @@ -1152,8 +1159,9 @@ static int wp_rsa_verify_pss(wp_RsaSigCtx* ctx, const unsigned char* sig, unsigned char* decryptedSig) { int ok = 1; - int rc; - int saltLen; + int rc = 0; + int saltLen = 0; + int locked = 0; WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_verify_pss"); @@ -1178,6 +1186,14 @@ static int wp_rsa_verify_pss(wp_RsaSigCtx* ctx, const unsigned char* sig, #endif wp_rsa_get_key(ctx->rsa), EVP_PKEY_OP_VERIFY); + if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { + ok = 0; + } + else { + locked = 1; + } + } + if (ok) { rc = wc_RsaPSS_Verify_ex((byte*)sig, (word32)sigLen, decryptedSig, (word32)sigLen, #if LIBWOLFSSL_VERSION_HEX >= 0x05007004 @@ -1192,6 +1208,9 @@ static int wp_rsa_verify_pss(wp_RsaSigCtx* ctx, const unsigned char* sig, ok = 0; } } + if (locked) { + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); + } if (ok) { rc = wc_RsaPSS_CheckPadding_ex(tbs, (word32)tbsLen, decryptedSig, rc, #if LIBWOLFSSL_VERSION_HEX >= 0x05007004 @@ -1227,17 +1246,23 @@ static int wp_rsa_verify_no_pad(wp_RsaSigCtx* ctx, const unsigned char* sig, unsigned char* decryptedSig) { int ok = 1; - int rc; + int rc = 0; word32 len = (word32)sigLen; WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_verify_no_pad"); - rc = wc_RsaDirect((byte*)sig, (word32)sigLen, decryptedSig, &len, - wp_rsa_get_key(ctx->rsa), RSA_PUBLIC_DECRYPT, &ctx->rng); - if (rc < 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaDirect", rc); + if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { ok = 0; } + if (ok) { + rc = wc_RsaDirect((byte*)sig, (word32)sigLen, decryptedSig, &len, + wp_rsa_get_key(ctx->rsa), RSA_PUBLIC_DECRYPT, &ctx->rng); + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); + if (rc < 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaDirect", rc); + ok = 0; + } + } if (ok && (((size_t)rc != tbsLen) || ((XMEMCMP(tbs, decryptedSig, tbsLen) != 0)))) { ok = 0; @@ -1329,7 +1354,7 @@ static int wp_rsa_verify_x931(wp_RsaSigCtx* ctx, const unsigned char* sig, unsigned char* decryptedSig) { int ok = 1; - int rc; + int rc = 0; word32 len = (word32)sigLen; unsigned char* unpadded = NULL; mp_int toMp; @@ -1337,12 +1362,18 @@ static int wp_rsa_verify_x931(wp_RsaSigCtx* ctx, const unsigned char* sig, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_verify_x931"); - rc = wc_RsaDirect((byte*)sig, (word32)sigLen, decryptedSig, &len, - wp_rsa_get_key(ctx->rsa), RSA_PUBLIC_DECRYPT, &ctx->rng); - if (rc < 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaDirect", rc); + if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { ok = 0; } + if (ok) { + rc = wc_RsaDirect((byte*)sig, (word32)sigLen, decryptedSig, &len, + wp_rsa_get_key(ctx->rsa), RSA_PUBLIC_DECRYPT, &ctx->rng); + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); + if (rc < 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaDirect", rc); + ok = 0; + } + } if (ok) { /* * X9.31 specifies, "If e is odd, then @@ -1516,7 +1547,7 @@ static int wp_rsa_verify_recover_init(wp_RsaSigCtx* ctx, wp_Rsa* rsa, static int wp_rsa_verify_recover(wp_RsaSigCtx* ctx, unsigned char* rout, size_t* routlen, size_t routsize, const unsigned char* sig, size_t sigLen) { - int rc; + int rc = 0; int ok = 1; WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_verify_recover"); @@ -1535,9 +1566,13 @@ static int wp_rsa_verify_recover(wp_RsaSigCtx* ctx, unsigned char* rout, if (ok && ((!WP_FITS_WORD32(sigLen)) || (!WP_FITS_WORD32(routsize)))) { ok = 0; } + if (ok && (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1)) { + ok = 0; + } if (ok) { rc = wc_RsaSSL_Verify(sig, (word32)sigLen, rout, (word32)routsize, wp_rsa_get_key(ctx->rsa)); + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); if (rc < 0) { WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaSSL_Verify", rc); ok = 0; diff --git a/test/test_rsa.c b/test/test_rsa.c index 20c36a23..b0ee1c31 100644 --- a/test/test_rsa.c +++ b/test/test_rsa.c @@ -3615,4 +3615,270 @@ int test_rsa_sha512_256_dupctx(void *data) } #endif /* WP_HAVE_SHA512_256 */ +/* + * Concurrency: many threads driving one shared EVP_PKEY. Exercises the + * per-key mutex on the verify, encrypt, decrypt and KEM paths. Run this + * under ThreadSanitizer - a plain pass proves little on its own. + */ +#if defined(HAVE_PTHREAD) && !defined(WP_SINGLE_THREADED) + +#include + +#define WP_CONC_THREADS 4 +#define WP_CONC_ITERS 30 +#define WP_CONC_RSA_SZ 256 + +/* Per-thread state; pkey is deliberately shared by every worker. */ +typedef struct { + EVP_PKEY *pkey; + int id; + int err; +} WP_CONC_ARG; + +/* RSASVE encapsulate then decapsulate, checking the secret round-trips. */ +static int test_rsa_conc_kem(EVP_PKEY *pkey) +{ + int err; + EVP_PKEY_CTX *encCtx = NULL; + EVP_PKEY_CTX *decCtx = NULL; + unsigned char ct[WP_CONC_RSA_SZ]; + unsigned char secret[WP_CONC_RSA_SZ]; + unsigned char recovered[WP_CONC_RSA_SZ]; + size_t ctLen = sizeof(ct); + size_t secretLen = sizeof(secret); + size_t recoveredLen = sizeof(recovered); + OSSL_PARAM params[2]; + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION, + (char*)"RSASVE", 0); + params[1] = OSSL_PARAM_construct_end(); + + err = (encCtx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, pkey, NULL)) == NULL; + if (err == 0) { + err = EVP_PKEY_encapsulate_init(encCtx, NULL) <= 0; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_params(encCtx, params) != 1; + } + if (err == 0) { + err = EVP_PKEY_encapsulate(encCtx, ct, &ctLen, secret, &secretLen) <= 0; + } + if (err == 0) { + err = (decCtx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, pkey, + NULL)) == NULL; + } + if (err == 0) { + err = EVP_PKEY_decapsulate_init(decCtx, NULL) <= 0; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_params(decCtx, params) != 1; + } + if (err == 0) { + err = EVP_PKEY_decapsulate(decCtx, recovered, &recoveredLen, ct, + ctLen) <= 0; + } + if (err == 0) { + err = (recoveredLen != secretLen) || + (memcmp(recovered, secret, secretLen) != 0); + } + + EVP_PKEY_CTX_free(decCtx); + EVP_PKEY_CTX_free(encCtx); + + return err; +} + +#ifdef WOLFSSL_RSA_KEY_CHECK +/* Pairwise check runs wc_CheckRsaKey() through wp_rsa_validate(). */ +static int test_rsa_conc_check(EVP_PKEY *pkey) +{ + int err; + EVP_PKEY_CTX *ctx = NULL; + + err = (ctx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, pkey, NULL)) == NULL; + if (err == 0) { + err = EVP_PKEY_pairwise_check(ctx) != 1; + } + + EVP_PKEY_CTX_free(ctx); + + return err; +} +#endif /* WOLFSSL_RSA_KEY_CHECK */ + +static void* test_rsa_conc_worker(void *arg) +{ + WP_CONC_ARG *a = (WP_CONC_ARG *)arg; + unsigned char hash[32]; + unsigned char msg[32]; + unsigned char sig[WP_CONC_RSA_SZ]; + unsigned char ct[WP_CONC_RSA_SZ]; + unsigned char nopad[WP_CONC_RSA_SZ]; + size_t sigLen; + int err = 0; + int i; + + /* Distinct data per thread so a crossed result is a mismatch. */ + memset(hash, (unsigned char)(0x5A + a->id), sizeof(hash)); + memset(msg, (unsigned char)(0xA5 - a->id), sizeof(msg)); + /* Leading zero keeps the raw value below the modulus. */ + memset(nopad, (unsigned char)(0x33 + a->id), sizeof(nopad)); + nopad[0] = 0; + + for (i = 0; (err == 0) && (i < WP_CONC_ITERS); i++) { + sigLen = sizeof(sig); + err = test_pkey_sign(a->pkey, wpLibCtx, hash, sizeof(hash), sig, + &sigLen, RSA_PKCS1_PADDING, NULL, NULL); + if (err == 0) { + err = test_pkey_verify(a->pkey, wpLibCtx, hash, sizeof(hash), sig, + sigLen, RSA_PKCS1_PADDING, NULL, NULL); + } + if (err == 0) { + err = test_pkey_verify_recover(a->pkey, wpLibCtx, hash, + sizeof(hash), sig, sigLen, RSA_PKCS1_PADDING); + } +#ifdef WP_HAVE_SHA256 + if (err == 0) { + sigLen = sizeof(sig); + err = test_digest_sign(a->pkey, wpLibCtx, msg, sizeof(msg), + "SHA256", EVP_sha256(), sig, &sigLen, RSA_PKCS1_PSS_PADDING, 0); + } + if (err == 0) { + err = test_digest_verify(a->pkey, wpLibCtx, msg, sizeof(msg), + "SHA256", EVP_sha256(), sig, sigLen, RSA_PKCS1_PSS_PADDING, 0); + } +#endif /* WP_HAVE_SHA256 */ + if (err == 0) { + err = test_pkey_enc(a->pkey, wpLibCtx, msg, sizeof(msg), ct, + sizeof(ct), RSA_PKCS1_PADDING, NULL, NULL); + } + if (err == 0) { + err = test_pkey_dec(a->pkey, wpLibCtx, msg, sizeof(msg), ct, + sizeof(ct), RSA_PKCS1_PADDING, NULL, NULL); + } +#ifdef WP_HAVE_SHA256 + if (err == 0) { + err = test_pkey_enc(a->pkey, wpLibCtx, msg, sizeof(msg), ct, + sizeof(ct), RSA_PKCS1_OAEP_PADDING, EVP_sha256(), + EVP_sha256()); + } + if (err == 0) { + err = test_pkey_dec(a->pkey, wpLibCtx, msg, sizeof(msg), ct, + sizeof(ct), RSA_PKCS1_OAEP_PADDING, EVP_sha256(), + EVP_sha256()); + } +#endif /* WP_HAVE_SHA256 */ + if (err == 0) { + err = test_rsa_conc_kem(a->pkey); + } +#ifdef WP_HAVE_SHA256 + if (err == 0) { + sigLen = sizeof(sig); + err = test_pkey_sign(a->pkey, wpLibCtx, hash, sizeof(hash), sig, + &sigLen, RSA_X931_PADDING, EVP_sha256(), EVP_sha256()); + } + if (err == 0) { + err = test_pkey_verify(a->pkey, wpLibCtx, hash, sizeof(hash), sig, + sigLen, RSA_X931_PADDING, EVP_sha256(), EVP_sha256()); + } +#endif /* WP_HAVE_SHA256 */ +#ifdef WOLFSSL_RSA_KEY_CHECK + if (err == 0) { + err = test_rsa_conc_check(a->pkey); + } +#endif /* WOLFSSL_RSA_KEY_CHECK */ + if (err == 0) { + sigLen = sizeof(sig); + err = test_pkey_sign(a->pkey, wpLibCtx, nopad, sizeof(nopad), sig, + &sigLen, RSA_NO_PADDING, NULL, NULL); + } + if (err == 0) { + err = test_pkey_verify(a->pkey, wpLibCtx, nopad, sizeof(nopad), sig, + sigLen, RSA_NO_PADDING, NULL, NULL); + } + } + + a->err = err; + + return NULL; +} + +int test_rsa_concurrent_ops(void *data) +{ + int err = 0; + int i; + int created = 0; + EVP_PKEY *pkey = NULL; + const unsigned char *p = rsa_key_der_2048; + WP_CONC_ARG args[WP_CONC_THREADS]; + pthread_t threads[WP_CONC_THREADS]; + unsigned char warm[32]; + unsigned char warmSig[WP_CONC_RSA_SZ]; + size_t warmLen; + + (void)data; + + PRINT_MSG("Load one RSA key to be shared by every thread"); + pkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &p, sizeof(rsa_key_der_2048)); + err = pkey == NULL; + + if (err == 0) { + /* Buffers are fixed at WP_CONC_RSA_SZ and RSA_NO_PADDING needs + * an input of exactly the modulus size. */ + err = EVP_PKEY_get_size(pkey) != WP_CONC_RSA_SZ; + } + + if (err == 0) { + /* Populate the key's provider-side export cache before threading; + * OpenSSL's lazy legacy export is not what this test covers. */ + memset(warm, 0x11, sizeof(warm)); + warmLen = sizeof(warmSig); + err = test_pkey_sign(pkey, wpLibCtx, warm, sizeof(warm), warmSig, + &warmLen, RSA_PKCS1_PADDING, NULL, NULL); + } + + if (err == 0) { + PRINT_MSG("Run concurrent sign/verify/recover/encrypt/decrypt/KEM"); + for (i = 0; i < WP_CONC_THREADS; i++) { + args[i].pkey = pkey; + args[i].id = i; + args[i].err = 0; + } + for (i = 0; i < WP_CONC_THREADS; i++) { + if (pthread_create(&threads[i], NULL, test_rsa_conc_worker, + &args[i]) != 0) { + err = 1; + break; + } + created++; + } + for (i = 0; i < created; i++) { + pthread_join(threads[i], NULL); + } + for (i = 0; i < created; i++) { + if (args[i].err != 0) { + PRINT_ERR_MSG("Concurrent thread %d failed", i); + err = args[i].err; + } + } + } + + EVP_PKEY_free(pkey); + + return err; +} + +#else + +int test_rsa_concurrent_ops(void *data) +{ + (void)data; + + PRINT_MSG("No pthreads in this build - RSA concurrency test not run"); + + return 0; +} + +#endif /* HAVE_PTHREAD && !WP_SINGLE_THREADED */ + #endif /* WP_HAVE_RSA */ diff --git a/test/unit.c b/test/unit.c index c5543dd0..cde194c3 100644 --- a/test/unit.c +++ b/test/unit.c @@ -407,6 +407,7 @@ TEST_CASE test_case[] = { TEST_DECL(test_rsa_pss_mgf1_get_params, NULL), TEST_DECL(test_rsa_kem, NULL), TEST_DECL(test_rsa_key_integrity, NULL), + TEST_DECL(test_rsa_concurrent_ops, NULL), #endif /* WP_HAVE_RSA */ #ifdef WP_HAVE_EC_P192 #ifdef WP_HAVE_ECKEYGEN diff --git a/test/unit.h b/test/unit.h index f7910ba1..d308654e 100644 --- a/test/unit.h +++ b/test/unit.h @@ -397,6 +397,7 @@ int test_rsa_kem_prefix_match(void* data); int test_rsa_pss_mgf1_get_params(void *data); int test_rsa_kem(void *data); int test_rsa_key_integrity(void* data); +int test_rsa_concurrent_ops(void *data); #endif /* WP_HAVE_RSA */ #ifdef WP_HAVE_DH