From 001e01db3e996e13ffc72386fe79d03a6683b5ac Mon Sep 17 00:00:00 2001
From: Nikola Pajkovsky <nikolap@openssl.org>
Date: Thu, 19 Mar 2026 12:16:08 +0100
Subject: [PATCH] rsa_kem: validate RSA_public_encrypt() result in RSASVE
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

RSA_public_encrypt() returns the number of bytes written on success and
-1 on failure. With the existing `if (ret)` check, a provider-side RSA KEM
encapsulation can incorrectly succeed when the underlying RSA public
encrypt operation fails. In that case the code reports success, returns
lengths as if encapsulation completed normally, and leaves the freshly
generated secret available instead of discarding it.

Tighten the success condition so RSASVE only succeeds when
RSA_public_encrypt() returns a positive value equal to the modulus-sized
output expected for RSA_NO_PADDING. Any other return value is treated as
failure, and the generated secret is cleansed before returning.

Fixes CVE-2026-31790
Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>

Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Mon Apr  6 19:51:30 2026
---
 providers/implementations/kem/rsa_kem.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/providers/implementations/kem/rsa_kem.c b/providers/implementations/kem/rsa_kem.c
index f7bf368a0dfc7..74dfafddd9e06 100644
--- a/providers/implementations/kem/rsa_kem.c
+++ b/providers/implementations/kem/rsa_kem.c
@@ -316,17 +316,19 @@ static int rsasve_generate(PROV_RSA_CTX *prsactx,
         return 0;
 
     /* Step(3): out = RSAEP((n,e), z) */
-    ret = RSA_public_encrypt(nlen, secret, out, prsactx->rsa, RSA_NO_PADDING);
-    if (ret) {
-        ret = 1;
-        if (outlen != NULL)
-            *outlen = nlen;
-        if (secretlen != NULL)
-            *secretlen = nlen;
-    } else {
+    ret = RSA_public_encrypt((int)nlen, secret, out, prsactx->rsa,
+        RSA_NO_PADDING);
+    if (ret <= 0 || ret != (int)nlen) {
         OPENSSL_cleanse(secret, nlen);
+        return 0;
     }
-    return ret;
+
+    if (outlen != NULL)
+        *outlen = nlen;
+    if (secretlen != NULL)
+        *secretlen = nlen;
+
+    return 1;
 }
 
 /**
