Skip to content

Fix heap buffer overflow in AES key unwrap#133

Open
zandbelt wants to merge 1 commit into
cisco:masterfrom
OpenIDC:cisco-pr-aes-unwrap
Open

Fix heap buffer overflow in AES key unwrap#133
zandbelt wants to merge 1 commit into
cisco:masterfrom
OpenIDC:cisco-pr-aes-unwrap

Conversation

@zandbelt

@zandbelt zandbelt commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

_cjose_jwe_decrypt_ek_aes_kw() passes the attacker-controlled encrypted_key
segment of an imported JWE straight to AES_unwrap_key() without validating its
length. AES_unwrap_key() writes encrypted_key_len - 8 bytes into the
fixed-size jwe->cek buffer (allocated for the expected CEK length of the enc
algorithm), so an oversized encrypted_key overflows that heap buffer.

Impact

This is reachable pre-authentication via cjose_jwe_import() +
cjose_jwe_decrypt() on attacker-supplied input: a JWE using an AES Key Wrap
alg (A128KW, A192KW, A256KW) with an over-long encrypted_key causes a
heap buffer overflow during decryption.

Root cause

The RFC 3394 wrapped key is always exactly the plaintext CEK length plus 8
bytes, but that invariant was never enforced before unwrapping:

// AES unwrap the CEK in to jwe->cek
int len = AES_unwrap_key(&akey, NULL, jwe->cek,
                         recipient->enc_key.raw, recipient->enc_key.raw_len);

Fix

Validate the wrapped-key length before calling AES_unwrap_key():

// the wrapped key (RFC 3394) is always the plaintext CEK length plus 8 bytes;
// enforce this before calling AES_unwrap_key, which would otherwise copy the
// attacker-controlled encrypted_key into the fixed-size jwe->cek buffer
if (recipient->enc_key.raw_len != jwe->cek_len + 8)
{
    CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
    return false;
}

Testing

Adds test_cjose_jwe_decrypt_aes_kw_oversized_ek to test/check_jwe.c: it builds
a valid AES-KW JWE, replaces the encrypted_key segment with an oversized
base64url blob, and asserts that import still succeeds but cjose_jwe_decrypt()
fails with CJOSE_ERR_INVALID_ARG (rather than overflowing). Covered for
A128KW/A192KW/A256KW with the AES-CBC-HMAC enc values and A256GCM.
Full suite passes (Checks: 77, Failures: 0, Errors: 0).

Note: this tree predates OpenSSL 3.x support, so building against OpenSSL 3
with -Werror fails on unrelated deprecation warnings in jwk.c. I verified
the build/tests with CFLAGS=-DOPENSSL_API_COMPAT=0x10000000L; no source
change to that effect is included here.

References

Fixed in the maintained fork (OpenIDC/cjose, v0.6.2.5) and tracked in security
advisory
GHSA-75r7-f5cv-g3wj.

Validate the encrypted_key length before AES_unwrap_key. The RFC 3394
wrapped key is always the plaintext CEK length plus 8 bytes; without this
check an attacker-controlled, oversized encrypted_key is copied into the
fixed-size jwe->cek buffer, overflowing it.

Adds a regression test that imports a JWE whose encrypted_key segment has
been replaced by an oversized blob and asserts that decryption fails with
CJOSE_ERR_INVALID_ARG instead of overflowing.

Signed-off-by: Hans Zandbelt <hans.zandbelt@openidc.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant