From eefa39a9b787294ce5d9ba6985b7ef1143214741 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Wed, 18 Mar 2026 19:08:49 -0700 Subject: [PATCH] Fix RSA-OAEP to allow zero-length plaintext per RFC 8017 RsaPublicEncryptEx() rejected inLen==0 unconditionally with BAD_FUNC_ARG. RFC 8017 Section 7.1.1 (RSAES-OAEP-ENCRYPT) permits zero-length messages: the only length constraint is mLen <= k - 2*hLen - 2, which mLen=0 always satisfies. RsaPrivateDecryptEx() converted a zero-length decryption result to RSA_BUFFER_E (unless WOLFSSL_RSA_DECRYPT_TO_0_LEN was defined). RFC 8017 Section 7.1.2 (RSAES-OAEP-DECRYPT) produces the original message M which may be empty. The fix uses constant-time masking to allow ret==0 when pad_type is WC_RSA_OAEP_PAD, preserving the existing timing-safe behavior for other padding types. Both OpenSSL and BoringSSL accept empty OAEP plaintexts. Co-Authored-By: Claude Opus 4.6 (1M context) --- wolfcrypt/src/rsa.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 3a32f915b72..389ff03d13b 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -3318,7 +3318,15 @@ static int RsaPublicEncryptEx(const byte* in, word32 inLen, byte* out, RsaPadding padding; #endif - if (in == NULL || inLen == 0 || out == NULL || key == NULL) { + if (out == NULL || key == NULL) { + return BAD_FUNC_ARG; + } + + /* For OAEP padding (RFC 8017, Section 7.1.1), zero-length messages are + * permitted: the spec requires mLen <= k - 2*hLen - 2, and mLen = 0 + * satisfies this for all supported key sizes. For other padding types, + * a zero-length input is invalid. */ + if (in == NULL || (inLen == 0 && pad_type != WC_RSA_OAEP_PAD)) { return BAD_FUNC_ARG; } @@ -3706,8 +3714,13 @@ static int RsaPrivateDecryptEx(const byte* in, word32 inLen, byte* out, ret = ctMaskSelInt(ctMaskLTE(ret, (int)outLen), ret, WC_NO_ERR_TRACE(RSA_BUFFER_E)); #ifndef WOLFSSL_RSA_DECRYPT_TO_0_LEN - ret = ctMaskSelInt(ctMaskNotEq(ret, 0), ret, - WC_NO_ERR_TRACE(RSA_BUFFER_E)); + /* RFC 8017 Section 7.1.2: OAEP decryption may produce a valid + * zero-length message. Only reject ret==0 for non-OAEP types. */ + { + int zeroOk = ctMaskEq(pad_type, WC_RSA_OAEP_PAD); + ret = ctMaskSelInt(ctMaskNotEq(ret, 0) | zeroOk, ret, + WC_NO_ERR_TRACE(RSA_BUFFER_E)); + } #endif #else if (outLen < (word32)ret)