]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
cms: kek_unwrap_key: test for fix out-of-bounds read in check-byte validation
authorNikola Pajkovsky <nikolap@openssl.org>
Thu, 21 May 2026 12:18:11 +0000 (14:18 +0200)
committerTomas Mraz <tomas@openssl.foundation>
Thu, 11 Jun 2026 15:08:41 +0000 (17:08 +0200)
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>
Reviewed-by: Daniel Kubec <kubec@openssl.foundation>
Reviewed-by: Milan Broz <mbroz@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Mon Jun  8 14:06:38 2026

test/cmsapitest.c
test/recipes/80-test_cmsapi.t
test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der [new file with mode: 0644]

index 4b250624a879b505ab03dc63ec622be9801a781b..eaad075b0d80839d51c6e0c14bd55ec03b346040 100644 (file)
@@ -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 @@ end:
     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;
 }
 
index 8d9371e005c0a6f1ec757ccfa29e1dbd0d95fc26..3d1dae84646467211041874c682f8233f60b107e 100644 (file)
@@ -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 (file)
index 0000000..c3ef3ab
Binary files /dev/null and b/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der differ