From a768492683be14822fc6574d0bcb0c6ac43922fc Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Fri, 6 Mar 2026 15:49:00 +0000 Subject: [PATCH] Fix SE050 RSA-PSS signing, key cleanup, and mutex leaks RSA-PSS fix: Skip SE050 hardware path for RSA-PSS sign and verify operations in RsaPublicEncryptEx() and RsaPrivateDecryptEx(). The SE050's PSS sign API (Se05x_API_RSASign) is a hash-then-sign operation, which double-hashes when wolfSSL passes a pre-computed digest (as done during TLS CertificateVerify). PSS operations now fall through to the software RSA path. PKCS#1 v1.5 signing continues to use SE050 hardware. Key object leak fix: Add se050_rsa_free_key() called from wc_FreeRsaKey() to erase wolfSSL-allocated RSA key objects from SE050 persistent storage on free. Without this, persistent key slots on the SE050 are never reclaimed and eventually exhaust secure storage. Add matching sss_key_store_erase_key() calls to se050_ecc_free_key(), se050_ed25519_free_key(), and se050_curve25519_free_key(). Only keys with keyId >= SE050_KEYID_START are erased (pre-provisioned keys are left intact). Mutex leak fix: Add missing wolfSSL_CryptHwMutexUnLock() calls before early returns in se050_rsa_sign(), se050_rsa_verify(), se050_rsa_public_encrypt(), and se050_rsa_private_decrypt() when the algorithm lookup fails after the mutex has already been acquired. ZD 21212 --- wolfcrypt/src/port/nxp/se050_port.c | 125 ++++++++++++++++++++++++++-- wolfcrypt/src/rsa.c | 18 +++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 2532dd5fb2..e3e9814e13 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -1017,8 +1017,59 @@ int wc_se050_rsa_insert_public_key(word32 keyId, const byte* rsaDer, } /** - * Return sss_algorithm_t type for RSA sign/verify based on wolfCrypt pad type, - * hash value, and mask generation function (mgf). + * Free an RSA key object from the SE050. Erases key from persistent storage + * if it was allocated by wolfSSL (not pre-provisioned). + * + * key Pointer to initialized RsaKey structure + */ +void se050_rsa_free_key(struct RsaKey* key) +{ + sss_status_t status = kStatus_SSS_Success; + sss_object_t keyObject; + sss_key_store_t host_keystore; + +#ifdef SE050_DEBUG + printf("se050_rsa_free_key: key %p, keyId %d\n", key, key->keyId); +#endif + + if (cfg_se050_i2c_pi == NULL) { + return; + } + if (key->keyIdSet == 0) { + return; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return; + } + + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_RSA); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&keyObject, &host_keystore); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_get_handle(&keyObject, key->keyId); + } + + if (status == kStatus_SSS_Success) { + /* Erase key from SE050 persistent storage if it was allocated + * by wolfSSL (not a pre-provisioned key). Without this, persistent + * key objects leak on the SE050 and can exhaust secure storage. */ + if (key->keyId >= SE050_KEYID_START) { + sss_key_store_erase_key(&host_keystore, &keyObject); + } + sss_key_object_free(&keyObject); + key->keyId = 0; + key->keyIdSet = 0; + } + wolfSSL_CryptHwMutexUnLock(); +} + +/** + * Get SSS algorithm type for RSA signature operations. * * padType padding type * hash hash function @@ -1172,15 +1223,29 @@ int se050_rsa_sign(const byte* in, word32 inLen, byte* out, algorithm = se050_get_rsa_signature_type(pad_type, hash, mgf); if (algorithm == kAlgorithm_None) { WOLFSSL_MSG("Unsupported padding/hash/mgf combination for SE050"); + wolfSSL_CryptHwMutexUnLock(); return BAD_FUNC_ARG; } +#ifdef SE050_DEBUG + printf("se050_rsa_sign: algorithm = %d, keySz = %d, keyIdSet = %d\n", + algorithm, keySz, key->keyIdSet); +#endif status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_key_store_context_init status = %d\n", status); +#endif if (status == kStatus_SSS_Success) { status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_RSA); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_key_store_allocate status = %d\n", status); +#endif } if (status == kStatus_SSS_Success) { status = sss_key_object_init(&newKey, &host_keystore); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_key_object_init status = %d\n", status); +#endif } if (status == kStatus_SSS_Success) { keyId = key->keyId; @@ -1188,6 +1253,9 @@ int se050_rsa_sign(const byte* in, word32 inLen, byte* out, /* key was not generated in SE050, export RsaKey to DER * and use that to store into SE050 keystore */ derSz = wc_RsaKeyToDer(key, NULL, 0); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: wc_RsaKeyToDer size query = %d\n", derSz); +#endif if (derSz < 0) { status = kStatus_SSS_Fail; ret = derSz; @@ -1203,6 +1271,9 @@ int se050_rsa_sign(const byte* in, word32 inLen, byte* out, } if (status == kStatus_SSS_Success) { derSz = wc_RsaKeyToDer(key, derBuf, derSz); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: wc_RsaKeyToDer export = %d\n", derSz); +#endif if (derSz < 0) { status = kStatus_SSS_Fail; ret = derSz; @@ -1213,31 +1284,60 @@ int se050_rsa_sign(const byte* in, word32 inLen, byte* out, status = sss_key_object_allocate_handle(&newKey, keyId, kSSS_KeyPart_Pair, kSSS_CipherType_RSA, keySz, kKeyObject_Mode_Persistent); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_key_object_allocate_handle " + "status = %d, keyId = %d\n", status, keyId); +#endif } if (status == kStatus_SSS_Success) { /* Try to delete existing key first, ignore return since will * fail if no key exists yet */ - sss_key_store_erase_key(&host_keystore, &newKey); + status = sss_key_store_erase_key(&host_keystore, &newKey); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_key_store_erase_key " + "status = %d\n", status); +#endif + /* Reset status - erase failing is expected if key doesn't + * exist yet */ + status = kStatus_SSS_Success; keyCreated = 1; status = sss_key_store_set_key(&host_keystore, &newKey, derBuf, derSz, (keySz * 8), NULL, 0); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_key_store_set_key " + "status = %d, derSz = %d, keyBits = %d\n", + status, derSz, (keySz * 8)); +#endif } XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); } else { status = sss_key_object_get_handle(&newKey, keyId); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_key_object_get_handle " + "status = %d, keyId = %d\n", status, keyId); +#endif } } if (status == kStatus_SSS_Success) { status = sss_asymmetric_context_init(&ctx_asymm, cfg_se050_i2c_pi, &newKey, algorithm, kMode_SSS_Sign); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_asymmetric_context_init " + "status = %d, algorithm = %d\n", status, algorithm); +#endif if (status == kStatus_SSS_Success) { sigSz = outLen; - status = sss_asymmetric_sign_digest(&ctx_asymm, (uint8_t*)in, inLen, - out, &sigSz); + status = sss_asymmetric_sign_digest(&ctx_asymm, (uint8_t*)in, + inLen, out, &sigSz); +#ifdef SE050_DEBUG + printf("se050_rsa_sign: sss_asymmetric_sign_digest " + "status = %d, inLen = %d, sigSz = %d\n", + status, inLen, (int)sigSz); +#endif } sss_asymmetric_context_free(&ctx_asymm); } @@ -1327,6 +1427,7 @@ int se050_rsa_verify(const byte* in, word32 inLen, byte* out, word32 outLen, algorithm = se050_get_rsa_signature_type(pad_type, hash, mgf); if (algorithm == kAlgorithm_None) { WOLFSSL_MSG("Unsupported padding/hash/mgf combination for SE050"); + wolfSSL_CryptHwMutexUnLock(); return BAD_FUNC_ARG; } @@ -1515,6 +1616,7 @@ int se050_rsa_public_encrypt(const byte* in, word32 inLen, byte* out, algorithm = se050_get_rsa_encrypt_type(pad_type, hash); if (algorithm == kAlgorithm_None) { WOLFSSL_MSG("Unsupported padding/hash/mgf combination for SE050"); + wolfSSL_CryptHwMutexUnLock(); return BAD_FUNC_ARG; } @@ -1673,6 +1775,7 @@ int se050_rsa_private_decrypt(const byte* in, word32 inLen, byte* out, algorithm = se050_get_rsa_encrypt_type(pad_type, hash); if (algorithm == kAlgorithm_None) { WOLFSSL_MSG("Unsupported padding/hash/mgf combination for SE050"); + wolfSSL_CryptHwMutexUnLock(); return BAD_FUNC_ARG; } @@ -2334,6 +2437,12 @@ void se050_ecc_free_key(struct ecc_key* key) } if (status == kStatus_SSS_Success) { + /* Erase key from SE050 persistent storage if it was allocated + * by wolfSSL (not a pre-provisioned key). Without this, persistent + * key objects leak on the SE050 and can exhaust secure storage. */ + if (key->keyId >= SE050_KEYID_START) { + sss_key_store_erase_key(&host_keystore, &keyObject); + } sss_key_object_free(&keyObject); key->keyId = 0; key->keyIdSet = 0; @@ -2795,6 +2904,9 @@ void se050_ed25519_free_key(ed25519_key* key) status = sss_key_object_get_handle(&newKey, key->keyId); } if (status == kStatus_SSS_Success) { + if (key->keyId >= SE050_KEYID_START) { + sss_key_store_erase_key(&host_keystore, &newKey); + } sss_key_object_free(&newKey); key->keyId = 0; key->keyIdSet = 0; @@ -3268,6 +3380,9 @@ void se050_curve25519_free_key(struct curve25519_key* key) status = sss_key_object_get_handle(&newKey, key->keyId); } if (status == kStatus_SSS_Success) { + if (key->keyId >= SE050_KEYID_START) { + sss_key_store_erase_key(&host_keystore, &newKey); + } sss_key_object_free(&newKey); key->keyId = 0; key->keyIdSet = 0; diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 8d1fb10385..41248d2e1d 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -557,6 +557,10 @@ int wc_FreeRsaKey(RsaKey* key) return BAD_FUNC_ARG; } +#if defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NO_RSA) + se050_rsa_free_key(key); +#endif + wc_RsaCleanup(key); #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_RSA) @@ -3395,7 +3399,12 @@ static int RsaPublicEncryptEx(const byte* in, word32 inLen, byte* out, mgf, label, labelSz, sz); } else if (rsa_type == RSA_PRIVATE_ENCRYPT && - pad_value == RSA_BLOCK_TYPE_1) { + pad_value == RSA_BLOCK_TYPE_1 && + pad_type != WC_RSA_PSS_PAD) { + /* SE050 handles PKCS#1 v1.5 signing directly. PSS signing falls + * through to software path because the SE050 PSS sign API + * (Se05x_API_RSASign) is hash-then-sign and does not support + * signing a pre-computed digest without double-hashing. */ return se050_rsa_sign(in, inLen, out, outLen, key, rsa_type, pad_value, pad_type, hash, mgf, label, labelSz, sz); @@ -3561,7 +3570,12 @@ static int RsaPrivateDecryptEx(const byte* in, word32 inLen, byte* out, return ret; } else if (rsa_type == RSA_PUBLIC_DECRYPT && - pad_value == RSA_BLOCK_TYPE_1) { + pad_value == RSA_BLOCK_TYPE_1 && + pad_type != WC_RSA_PSS_PAD) { + /* SE050 handles PKCS#1 v1.5 verification directly. PSS + * verification falls through to software path to match the + * software PSS signing path (SE050 PSS sign uses hash-then-sign + * which double-hashes a pre-computed digest). */ ret = se050_rsa_verify(in, inLen, out, outLen, key, rsa_type, pad_value, pad_type, hash, mgf, label, labelSz);