182 lines
6.8 KiB
Diff
182 lines
6.8 KiB
Diff
From ae233967cf40b15fe80c70c17438d391158171b3 Mon Sep 17 00:00:00 2001
|
|
From: Nikola Pajkovsky <nikolap@openssl.org>
|
|
Date: Thu, 21 May 2026 11:53:09 +0200
|
|
Subject: [PATCH 1/2] cms: kek_unwrap_key: Fix out-of-bounds read in check-byte
|
|
validation
|
|
|
|
the check-byte test in kek_unwrap_key() reads tmp[1] through tmp[6]
|
|
unconditionally, so the decrypted buffer must hold at least seven
|
|
octets. The pre-decryption size check enforces inlen >= 2 * blocklen,
|
|
which yields the required seven octets only when blocklen >= 4. For
|
|
a KEK cipher with a smaller block size, inlen can be as small as
|
|
2 * blocklen and the check-byte read overruns the inlen-sized tmp
|
|
allocation.
|
|
|
|
Reject blocklen < 4 in the early sanity check. All block ciphers
|
|
appropriate for CMS PasswordRecipientInfo key wrapping have a block
|
|
size of at least 8 octets (DES/3DES = 8, AES = 16), so this only
|
|
forbids ciphers that would not be valid KEK choices anyway, and the
|
|
existing inlen >= 2 * blocklen check then guarantees the seven-octet
|
|
lower bound the check-byte test relies on.
|
|
|
|
Fixes CVE-2026-9076
|
|
Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>
|
|
---
|
|
crypto/cms/cms_pwri.c | 8 ++++----
|
|
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c
|
|
index d62dbbde881..4bb06b406eb 100644
|
|
--- a/crypto/cms/cms_pwri.c
|
|
+++ b/crypto/cms/cms_pwri.c
|
|
@@ -200,18 +200,18 @@ static int kek_unwrap_key(unsigned char *out, size_t *outlen,
|
|
const unsigned char *in, size_t inlen,
|
|
EVP_CIPHER_CTX *ctx)
|
|
{
|
|
- size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
|
|
+ int blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
|
|
unsigned char *tmp;
|
|
int outl, rv = 0;
|
|
|
|
- if (blocklen == 0)
|
|
+ if (blocklen < 4)
|
|
return 0;
|
|
|
|
- if (inlen < 2 * blocklen) {
|
|
+ if (inlen < 2 * (size_t)blocklen) {
|
|
/* too small */
|
|
return 0;
|
|
}
|
|
- if (inlen % blocklen) {
|
|
+ if (inlen > INT_MAX || inlen % blocklen) {
|
|
/* Invalid size */
|
|
return 0;
|
|
}
|
|
|
|
From 4a35e3c194ceb0af30fcbe42d65c95e08d5758a5 Mon Sep 17 00:00:00 2001
|
|
From: Nikola Pajkovsky <nikolap@openssl.org>
|
|
Date: Thu, 21 May 2026 14:18:11 +0200
|
|
Subject: [PATCH 2/2] cms: kek_unwrap_key: test for fix out-of-bounds read in
|
|
check-byte validation
|
|
|
|
added EnvelopedData blob with a PasswordRecipientInfo using
|
|
id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher. CFB's 1-byte
|
|
effective block size let the inlen >= 2 * blocklen guard in
|
|
kek_unwrap_key() accept a wrapped key shorter than the seven octets
|
|
the check-byte test reads from tmp[1..6]; the encryptedKey OCTET
|
|
STRING here is only two bytes.
|
|
|
|
Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>
|
|
---
|
|
test/cmsapitest.c | 48 +++++++++++++++++-
|
|
test/recipes/80-test_cmsapi.t | 3 +-
|
|
.../80-test_cmsapi_data/cms_pwri_kek_oob.der | Bin 0 -> 193 bytes
|
|
3 files changed, 48 insertions(+), 3 deletions(-)
|
|
create mode 100644 test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der
|
|
|
|
diff --git a/test/cmsapitest.c b/test/cmsapitest.c
|
|
index 0752d14df09..d908bc6fc4c 100644
|
|
--- a/test/cmsapitest.c
|
|
+++ b/test/cmsapitest.c
|
|
@@ -21,6 +21,7 @@ static X509 *cert = NULL;
|
|
static EVP_PKEY *privkey = NULL;
|
|
static char *derin = NULL;
|
|
static char *too_long_iv_cms_in = NULL;
|
|
+static char *pwri_kek_oob_der_in = NULL;
|
|
|
|
static int test_encrypt_decrypt(const EVP_CIPHER *cipher)
|
|
{
|
|
@@ -512,7 +513,48 @@ static int test_cms_aesgcm_iv_too_long(void)
|
|
return ret;
|
|
}
|
|
|
|
-OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile\n")
|
|
+/*
|
|
+ * CMS EnvelopedData with a single PasswordRecipientInfo using
|
|
+ * id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher
|
|
+ * (1-byte effective block size). The encryptedKey OCTET STRING is
|
|
+ * only two bytes long, so the wrapped key buffer is shorter than
|
|
+ * the seven octets read by the check-byte test in kek_unwrap_key().
|
|
+ * Prior to CVE-2026-9076 this triggered an out-of-bounds heap read;
|
|
+ * CMS_decrypt() must now fail cleanly.
|
|
+ */
|
|
+static int test_pwri_kek_unwrap_short_encrypted_key(void)
|
|
+{
|
|
+ BIO *in = NULL;
|
|
+ CMS_ContentInfo *cms = NULL;
|
|
+ unsigned long err = 0;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (!TEST_ptr(in = BIO_new_file(pwri_kek_oob_der_in, "rb"))
|
|
+ || !TEST_ptr(cms = d2i_CMS_bio(in, NULL)))
|
|
+ goto end;
|
|
+
|
|
+ /*
|
|
+ * The unwrap is attempted eagerly inside CMS_decrypt_set1_password().
|
|
+ * It must fail cleanly (no OOB read) and report CMS_R_UNWRAP_FAILURE.
|
|
+ */
|
|
+ if (!TEST_false(CMS_decrypt_set1_password(cms,
|
|
+ (unsigned char *)"password", -1)))
|
|
+ goto end;
|
|
+
|
|
+ err = ERR_peek_last_error();
|
|
+ if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS)
|
|
+ || !TEST_int_eq(ERR_GET_REASON(err), CMS_R_UNWRAP_FAILURE))
|
|
+ goto end;
|
|
+
|
|
+ ERR_clear_error();
|
|
+ ret = 1;
|
|
+end:
|
|
+ CMS_ContentInfo_free(cms);
|
|
+ BIO_free(in);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile tooLongIVpem pwriKekOobDer\n")
|
|
|
|
int setup_tests(void)
|
|
{
|
|
@@ -527,7 +569,8 @@ int setup_tests(void)
|
|
if (!TEST_ptr(certin = test_get_argument(0))
|
|
|| !TEST_ptr(privkeyin = test_get_argument(1))
|
|
|| !TEST_ptr(derin = test_get_argument(2))
|
|
- || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3)))
|
|
+ || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3))
|
|
+ || !TEST_ptr(pwri_kek_oob_der_in = test_get_argument(4)))
|
|
return 0;
|
|
|
|
certbio = BIO_new_file(certin, "r");
|
|
@@ -564,6 +607,7 @@ int setup_tests(void)
|
|
ADD_TEST(test_encrypted_data_aead);
|
|
ADD_ALL_TESTS(test_d2i_CMS_decode, 2);
|
|
ADD_TEST(test_cms_aesgcm_iv_too_long);
|
|
+ ADD_TEST(test_pwri_kek_unwrap_short_encrypted_key);
|
|
return 1;
|
|
}
|
|
|
|
diff --git a/test/recipes/80-test_cmsapi.t b/test/recipes/80-test_cmsapi.t
|
|
index 8d9371e005c..3d1dae84646 100644
|
|
--- a/test/recipes/80-test_cmsapi.t
|
|
+++ b/test/recipes/80-test_cmsapi.t
|
|
@@ -19,5 +19,6 @@ plan tests => 1;
|
|
ok(run(test(["cmsapitest", srctop_file("test", "certs", "servercert.pem"),
|
|
srctop_file("test", "certs", "serverkey.pem"),
|
|
srctop_file("test", "recipes", "80-test_cmsapi_data", "encryptedData.der"),
|
|
- srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem")])),
|
|
+ srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem"),
|
|
+ srctop_file("test", "recipes", "80-test_cmsapi_data", "cms_pwri_kek_oob.der")])),
|
|
"running cmsapitest");
|
|
diff --git a/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der b/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c3ef3abd10e6b0a57b1eef985b0c8c43cb057371
|
|
GIT binary patch
|
|
literal 193
|
|
zcmXqL+{ebL)#lOmotKfFc|qd_gT}Q?jLe2!i#?ba85StRC0Th4#8?FEa~A$hV(x0n
|
|
zFrRcQQS~(+6B7r6ffO4z)C5ieW=;ccHqL}L55`nx7Dg5pCI$wB7`P$qj0Um@Stb?%
|
|
z*}W&;-kY#&W8?d>8TmW9IT{Q$uJ-6FX$+dodfRzpO2J!$Gg}?F()_mBI35*0&tMe5
|
|
VHoNtcLX?S)y&B`}phcqR3jwDUJP7~*
|
|
|
|
literal 0
|
|
HcmV?d00001
|
|
|