wolfssl: Avoid potential RNG state corruption during RSA/ECDSA operations

The WC_RNG instances are potentially shared between different threads
as private key objects are refcounted.  This may corrupt their internal
state as they are not thread-safe.

For ECDSA, using separate instances for each signing operation has some
performance impact, but for signature operations that should be fine.

The implementation for RSA uses mutexes.  That's due to the weird API.
While RNG instances can be passed for signing and encryption (probably
because they are also required for padding/salt besides blinding), they
can't for verifying and decryption.  The latter use an RNG instance that
has to be set on the key object before calling these operations.  So we
could potentially split the strategy, but to keep this consistent within
the RSA implementation, just continue with the shared RNG but use a
mutex around the API calls.

Fixes: c92eade82c ("wolfssl: Add wolfSSL plugin for cryptographic implementations")
This commit is contained in:
Tobias Brunner
2026-07-23 10:26:07 +02:00
parent 05625acc2a
commit d9cf1b0bff
3 changed files with 84 additions and 24 deletions
@@ -58,11 +58,6 @@ struct private_wolfssl_ec_private_key_t {
*/
ecc_key ec;
/**
* Random number generator
*/
WC_RNG rng;
/**
* Reference count
*/
@@ -79,25 +74,32 @@ static bool build_signature(private_wolfssl_ec_private_key_t *this,
chunk_t hash, chunk_t *signature)
{
bool success = FALSE;
WC_RNG rng;
mp_int r, s;
if (wc_InitRng(&rng) != 0)
{
return FALSE;
}
if (mp_init(&r) != 0)
{
wc_FreeRng(&rng);
return FALSE;
}
if (mp_init(&s) != 0)
{
mp_free(&r);
wc_FreeRng(&rng);
return FALSE;
}
if (wc_ecc_sign_hash_ex(hash.ptr, hash.len, &this->rng, &this->ec, &r,
&s) == 0)
if (wc_ecc_sign_hash_ex(hash.ptr, hash.len, &rng, &this->ec, &r, &s) == 0)
{
success = wolfssl_mp_cat(this->ec.dp->size * 2, &r, &s, signature);
}
mp_free(&s);
mp_free(&r);
wc_FreeRng(&rng);
return success;
}
@@ -135,14 +137,19 @@ static bool build_der_signature(private_wolfssl_ec_private_key_t *this,
{
chunk_t dgst = chunk_empty;
bool success = FALSE;
WC_RNG rng;
word32 len;
if (wc_InitRng(&rng) != 0)
{
return FALSE;
}
if (wolfssl_hash_chunk(hash, data, &dgst))
{
*signature = chunk_alloc(wc_ecc_sig_size(&this->ec));
len = signature->len;
if (wc_ecc_sign_hash(dgst.ptr, dgst.len, signature->ptr, &len,
&this->rng, &this->ec) == 0)
&rng, &this->ec) == 0)
{
signature->len = len;
success = TRUE;
@@ -153,6 +160,7 @@ static bool build_der_signature(private_wolfssl_ec_private_key_t *this,
}
}
chunk_free(&dgst);
wc_FreeRng(&rng);
return success;
}
@@ -306,7 +314,6 @@ METHOD(private_key_t, destroy, void,
if (ref_put(&this->ref))
{
lib->encoding->clear_cache(lib->encoding, &this->ec);
wc_FreeRng(&this->rng);
wc_ecc_free(&this->ec);
free(this);
}
@@ -345,13 +352,6 @@ static private_wolfssl_ec_private_key_t *create_empty(void)
free(this);
return NULL;
}
if (wc_InitRng(&this->rng) != 0)
{
DBG1(DBG_LIB, "RNG initialization for EC private key failed");
wc_ecc_free(&this->ec);
free(this);
return NULL;
}
return this;
}
@@ -364,6 +364,7 @@ wolfssl_ec_private_key_t *wolfssl_ec_private_key_gen(key_type_t type,
private_wolfssl_ec_private_key_t *this;
u_int key_size = 0;
ecc_curve_id curve_id;
WC_RNG rng;
while (TRUE)
{
@@ -407,13 +408,19 @@ wolfssl_ec_private_key_t *wolfssl_ec_private_key_gen(key_type_t type,
return NULL;
}
if (wc_ecc_make_key_ex(&this->rng, (key_size + 7) / 8, &this->ec,
curve_id) < 0)
if (wc_InitRng(&rng) != 0)
{
DBG1(DBG_LIB, "EC private key generation failed");
destroy(this);
return NULL;
}
if (wc_ecc_make_key_ex(&rng, (key_size + 7) / 8, &this->ec, curve_id) < 0)
{
DBG1(DBG_LIB, "EC private key generation failed");
destroy(this);
wc_FreeRng(&rng);
return NULL;
}
wc_FreeRng(&rng);
return &this->public;
}
@@ -57,6 +57,11 @@ struct private_wolfssl_rsa_private_key_t {
*/
WC_RNG rng;
/**
* Mutex to protect access to the RNG during API calls.
*/
wolfSSL_Mutex mutex;
/**
* Reference count
*/
@@ -73,8 +78,12 @@ bool wolfssl_rsa_fingerprint(RsaKey *rsa, cred_encoding_type_t type, chunk_t *fp
static bool build_signature(private_wolfssl_rsa_private_key_t *this,
enum wc_HashType hash, chunk_t data, chunk_t *sig)
{
int ret = wc_RsaSSL_Sign(data.ptr, data.len, sig->ptr, sig->len, &this->rsa,
&this->rng);
int ret;
wc_LockMutex(&this->mutex);
ret = wc_RsaSSL_Sign(data.ptr, data.len, sig->ptr, sig->len, &this->rsa,
&this->rng);
wc_UnLockMutex(&this->mutex);
if (ret > 0)
{
sig->len = ret;
@@ -146,8 +155,10 @@ static bool build_emsa_pss_signature(private_wolfssl_rsa_private_key_t *this,
if (wolfssl_hash_chunk(hash, data, &dgst))
{
wc_LockMutex(&this->mutex);
ret = wc_RsaPSS_Sign_ex(dgst.ptr, dgst.len, sig->ptr, sig->len, hash,
mgf, params->salt_len, &this->rsa, &this->rng);
wc_UnLockMutex(&this->mutex);
if (ret > 0)
{
sig->len = ret;
@@ -305,9 +316,11 @@ METHOD(private_key_t, decrypt, bool,
}
len = wc_RsaEncryptSize(&this->rsa);
*plain = chunk_alloc(len);
wc_LockMutex(&this->mutex);
len = wc_RsaPrivateDecrypt_ex(crypto.ptr, crypto.len, plain->ptr, len,
&this->rsa, padding, hash, mgf,
label.ptr, label.len);
wc_UnLockMutex(&this->mutex);
if (len < 0)
{
DBG1(DBG_LIB, "RSA decryption failed");
@@ -401,6 +414,7 @@ METHOD(private_key_t, destroy, void,
{
lib->encoding->clear_cache(lib->encoding, &this->rsa);
wc_FreeRsaKey(&this->rsa);
wc_FreeMutex(&this->mutex);
wc_FreeRng(&this->rng);
free(this);
}
@@ -439,15 +453,27 @@ static private_wolfssl_rsa_private_key_t *create_empty()
free(this);
return NULL;
}
if (wc_InitMutex(&this->mutex) != 0)
{
DBG1(DBG_LIB, "init mutex failed, rsa private key create failed");
wc_FreeRng(&this->rng);
free(this);
return NULL;
}
if (wc_InitRsaKey(&this->rsa, NULL) != 0)
{
DBG1(DBG_LIB, "init RSA failed, rsa private key create failed");
wc_FreeMutex(&this->mutex);
wc_FreeRng(&this->rng);
free(this);
return NULL;
}
#ifdef WC_RSA_BLINDING
this->rsa.rng = &this->rng;
if (wc_RsaSetRNG(&this->rsa, &this->rng) != 0)
{
destroy(this);
return NULL;
}
#endif
return this;
@@ -57,6 +57,11 @@ struct private_wolfssl_rsa_public_key_t {
*/
WC_RNG rng;
/**
* Mutex to protect access to the RNG via API calls.
*/
wolfSSL_Mutex mutex;
/**
* Reference counter
*/
@@ -81,7 +86,9 @@ static bool verify_signature(private_wolfssl_rsa_public_key_t *this,
padded = chunk_copy_pad(chunk_alloca(len), signature, 0x00);
wc_LockMutex(&this->mutex);
len = wc_RsaSSL_VerifyInline(padded.ptr, len, &p, &this->rsa);
wc_UnLockMutex(&this->mutex);
if (len > 0)
{
success = chunk_equals_const(data, chunk_create(p, len));
@@ -149,8 +156,10 @@ static bool verify_emsa_pss_signature(private_wolfssl_rsa_public_key_t *this,
}
padded = chunk_copy_pad(chunk_alloca(len), signature, 0x00);
wc_LockMutex(&this->mutex);
len = wc_RsaPSS_VerifyInline_ex(padded.ptr, len, &p, hash, mgf,
params->salt_len, &this->rsa);
wc_UnLockMutex(&this->mutex);
if (len > 0)
{
success = wc_RsaPSS_CheckPadding_ex(dgst.ptr, dgst.len, p, len, hash,
@@ -280,9 +289,11 @@ METHOD(public_key_t, encrypt_, bool,
}
len = wc_RsaEncryptSize(&this->rsa);
*crypto = chunk_alloc(len);
wc_LockMutex(&this->mutex);
len = wc_RsaPublicEncrypt_ex(plain.ptr, plain.len, crypto->ptr, len,
&this->rsa, &this->rng, padding, hash, mgf,
label.ptr, label.len);
wc_UnLockMutex(&this->mutex);
if (len < 0)
{
DBG1(DBG_LIB, "RSA encryption failed");
@@ -423,6 +434,7 @@ METHOD(public_key_t, destroy, void,
{
lib->encoding->clear_cache(lib->encoding, &this->rsa);
wc_FreeRsaKey(&this->rsa);
wc_FreeMutex(&this->mutex);
wc_FreeRng(&this->rng);
free(this);
}
@@ -459,13 +471,28 @@ static private_wolfssl_rsa_public_key_t *create_empty()
free(this);
return NULL;
}
if (wc_InitRsaKey(&this->rsa, NULL) != 0)
if (wc_InitMutex(&this->mutex) != 0)
{
DBG1(DBG_LIB, "init RSA failed, rsa public key load failed");
DBG1(DBG_LIB, "init mutex failed, rsa public key load failed");
wc_FreeRng(&this->rng);
free(this);
return NULL;
}
if (wc_InitRsaKey(&this->rsa, NULL) != 0)
{
DBG1(DBG_LIB, "init RSA failed, rsa public key load failed");
wc_FreeMutex(&this->mutex);
wc_FreeRng(&this->rng);
free(this);
return NULL;
}
#ifdef WC_RSA_BLINDING
if (wc_RsaSetRNG(&this->rsa, &this->rng) != 0)
{
destroy(this);
return NULL;
}
#endif
return this;
}