From e8e7cd19b45099fceab8df1e3c91ff248066ca28 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) Found via Wycheproof test vectors. --- 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 4fb4393f0b4..d442466f2c2 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -3354,7 +3354,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; } @@ -3752,8 +3760,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)