Skip to content

RSA-OAEP cannot encrypt or decrypt an empty message (C_Encrypt: CKR_FUNCTION_FAILED, C_Decrypt: CKR_ENCRYPTED_DATA_INVALID) #270

Closed

Description

@AlexLaroche

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) calls wc_RsaPublicEncrypt_ex. wolfCrypt's RsaPublicEncryptEx (wolfcrypt/src/rsa.c) starts with if (in == NULL || inLen == 0 || out == NULL || key == NULL) return BAD_FUNC_ARG;. wp11_C_Encrypt (src/crypto.c, line 3610) maps that to CKR_FUNCTION_FAILED.
  • Decrypt. RsaUnPad_OAEP returns 0 both for a valid empty message and for a padding error. RsaPrivateDecryptEx therefore rejects every 0-length result unless wolfSSL is built with WOLFSSL_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 to CKR_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

  1. MarkAtwood commented on Nov 3, 2026

    @MarkAtwood
    Contributor

    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

  2. Frauschi commented on Nov 3, 2026

    @Frauschi
    Member

    Closing as won't fix based on #281.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions