Repository navigation
Description
Summary
CKM_RSA_PKCS_OAEP refuses a zero-length message on both sides. C_Encrypt of 0 bytes returns CKR_FUNCTION_FAILED. C_Decrypt of a valid OAEP ciphertext of the empty message (made by OpenSSL) returns CKR_ENCRYPTED_DATA_INVALID. Every other length from 1 to k - 2hLen - 2 works, for SHA-256, SHA-384 and SHA-512. A token that cannot decrypt what another RSA-OAEP implementation produces breaks interoperability, and the decrypt side reports a valid ciphertext as invalid.
The limit is in wolfCrypt, not in wolfPKCS11's own code. wolfSSL/wolfssl#10012 tried to lift it there and was closed without being merged (2026-09-29). I am filing it here because this is where PKCS#11 users meet it. If it belongs in wolfSSL instead, please move it.
Found with wolfPKCS11 master 979895f (and v2.2.0, dbf5bd4) on static wolfSSL v5.9.4-stable (3c5eead). The two wolfCrypt checks below are unchanged on wolfSSL master (816e922).
Expected (PKCS#11 v3.2 / RFC 8017)
§6.1.8, Table 42 (PKCS #1 RSA OAEP: Key And Data Length): C_Encrypt input "≤ k-2-2hLen". RFC 8017 §7.1.1 (RSAES-OAEP-ENCRYPT) requires only mLen ≤ k - 2hLen - 2, so mLen = 0 is a valid message, and §7.1.2 decrypts it back to an empty string.
Results
RSA-2048 key pair generated by the token, OAEP with SHA-256 and MGF1-SHA-256, no label.
| Call | Input | Expected | Actual |
|---|---|---|---|
C_Encrypt(CKM_RSA_PKCS_OAEP) |
1-byte message (control) | CKR_OK, 256 bytes |
CKR_OK; C_Decrypt gives the byte back |
C_Encrypt(CKM_RSA_PKCS_OAEP) |
0-byte message | CKR_OK, 256 bytes |
CKR_FUNCTION_FAILED |
C_Decrypt(CKM_RSA_PKCS_OAEP) |
OpenSSL EVP_PKEY_encrypt (OAEP, SHA-256) of a 1-byte message (control) |
CKR_OK, 1 byte |
CKR_OK |
C_Decrypt(CKM_RSA_PKCS_OAEP) |
OpenSSL EVP_PKEY_encrypt of the 0-byte message |
CKR_OK, 0 bytes |
CKR_ENCRYPTED_DATA_INVALID |
C_Decrypt(CKM_RSA_PKCS_OAEP), SHA-256 / SHA-384 / SHA-512 |
OpenSSL ciphertexts of every length 0..190 / 0..158 / 0..126 | all CKR_OK |
only length 0 fails, with CKR_ENCRYPTED_DATA_INVALID |
CK_RSA_PKCS_OAEP_PARAMS p = { CKM_SHA256, CKG_MGF1_SHA256, CKZ_DATA_SPECIFIED, NULL, 0 };
CK_MECHANISM m = { CKM_RSA_PKCS_OAEP, &p, sizeof p };
CK_BYTE in[1], out[256];
CK_ULONG outLen = sizeof out;
fn->C_EncryptInit(hSession, &m, hPub);
fn->C_Encrypt(hSession, in, 0, out, &outLen); /* CKR_FUNCTION_FAILED */Cause
- Encrypt.
WP11_RsaOaep_PublicEncrypt(src/internal.c) callswc_RsaPublicEncrypt_ex. wolfCrypt'sRsaPublicEncryptEx(wolfcrypt/src/rsa.c) starts withif (in == NULL || inLen == 0 || out == NULL || key == NULL) return BAD_FUNC_ARG;.wp11_C_Encrypt(src/crypto.c, line 3610) maps that toCKR_FUNCTION_FAILED. - Decrypt.
RsaUnPad_OAEPreturns 0 both for a valid empty message and for a padding error.RsaPrivateDecryptExtherefore rejects every 0-length result unless wolfSSL is built withWOLFSSL_RSA_DECRYPT_TO_0_LEN(default off):ret = ctMaskSelInt(ctMaskNotEq(ret, 0), ret, RSA_BUFFER_E);.wp11_C_Decrypt(src/crypto.c, line 4826) maps every error toCKR_ENCRYPTED_DATA_INVALID.
Suggested fix
The clean fix is in wolfCrypt, along the lines of wolfssl#10012: allow inLen == 0 for WC_RSA_OAEP_PAD on encrypt, and give RsaUnPad_OAEP an error value that is distinct from a valid empty message on decrypt, in constant time. Defining WOLFSSL_RSA_DECRYPT_TO_0_LEN alone is not enough, because a padding error and an empty message would then both decrypt to 0 bytes.
Until then, a note in wolfPKCS11's documentation that RSA-OAEP needs a non-empty message would help users who meet the CKR_FUNCTION_FAILED.
Found while running the KerckhoffsLabs.Security.Cryptography.Pkcs11 conformance suite.
Activity
MarkAtwood commentedon Nov 3, 2026
declining this one, wolfcrypt keeps rejecting empty oaep messages by default. you're right that rfc 8017 and pkcs#11 allow it, but making 0 a successful decrypt changes the return for every wc_RsaPrivateDecrypt_ex caller, wolfcrypt-jni for one would hand back a wrong result, and nobody's asked us for it. it fails with an error so i'd rather document it, readme note in #281. happy to look again if someone has a real use. and like you said don't define WOLFSSL_RSA_DECRYPT_TO_0_LEN to get around it, padding errors come back as 0 bytes too. ty for the report
Frauschi commentedon Nov 3, 2026
Closing as won't fix based on #281.