Commit 3f7640a6c0 for openssl.org

commit 3f7640a6c015496b6c02ba7d5e5ff89df0bae603
Author: Mounir IDRASSI <mounir.idrassi@idrix.fr>
Date:   Sun Jun 7 15:33:43 2026 +0900

    Fix CMS Ed448 signer NULL digest handling

    Reject ossl_cms_adjust_md() failures unconditionally in
    CMS_add1_signer(), so unsupported Ed448 signing with signed attributes
    fails cleanly instead of passing a NULL digest to X509_ALGOR_set_md().

    Add a defensive NULL digest check to X509_ALGOR_set_md(), and cover
    the CMS Ed448 signed-attributes/no-attributes cases plus the low-level
    NULL digest path in regression tests.

    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Reviewed-by: Jakub Zelenka <jakub.zelenka@openssl.foundation>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    MergeDate: Wed Jul 29 16:49:57 2026
    (Merged from https://github.com/openssl/openssl/pull/31409)

diff --git a/crypto/asn1/x_algor.c b/crypto/asn1/x_algor.c
index 5050adb8f1..4d7e5f6a0a 100644
--- a/crypto/asn1/x_algor.c
+++ b/crypto/asn1/x_algor.c
@@ -87,12 +87,25 @@ void X509_ALGOR_get0(const ASN1_OBJECT **paobj, int *pptype,
 /* Set up an X509_ALGOR DigestAlgorithmIdentifier from an EVP_MD */
 int X509_ALGOR_set_md(X509_ALGOR *alg, const EVP_MD *md)
 {
-    int type = md->flags & EVP_MD_FLAG_DIGALGID_ABSENT ? V_ASN1_UNDEF
-                                                       : V_ASN1_NULL;
-    int md_type = EVP_MD_type(md);
+    int type, md_type;
+    ASN1_OBJECT *obj;

-    ASN1_OBJECT *obj = (md_type == NID_undef) ? OBJ_txt2obj(EVP_MD_get0_name(md), 0) : OBJ_nid2obj(md_type);
-    return X509_ALGOR_set0(alg, obj, type, NULL);
+    if (alg == NULL || md == NULL)
+        return 0;
+
+    type = md->flags & EVP_MD_FLAG_DIGALGID_ABSENT ? V_ASN1_UNDEF
+                                                   : V_ASN1_NULL;
+    md_type = EVP_MD_type(md);
+
+    obj = (md_type == NID_undef) ? OBJ_txt2obj(EVP_MD_get0_name(md), 0)
+                                 : OBJ_nid2obj(md_type);
+    if (obj == NULL)
+        return 0;
+    if (!X509_ALGOR_set0(alg, obj, type, NULL)) {
+        ASN1_OBJECT_free(obj);
+        return 0;
+    }
+    return 1;
 }

 int X509_ALGOR_cmp(const X509_ALGOR *a, const X509_ALGOR *b)
diff --git a/crypto/cms/cms_sd.c b/crypto/cms/cms_sd.c
index 352f75a45c..e53a523207 100644
--- a/crypto/cms/cms_sd.c
+++ b/crypto/cms/cms_sd.c
@@ -631,7 +631,7 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms,
     if (!ossl_cms_set1_SignerIdentifier(si->sid, signer, type, ctx))
         goto err;

-    if (ossl_cms_adjust_md(ctx, pk, &md, &local_md, flags) != 1 && local_md != md)
+    if (ossl_cms_adjust_md(ctx, pk, &md, &local_md, flags) != 1)
         goto err;

     if (!X509_ALGOR_set_md(si->digestAlgorithm, md))
diff --git a/test/cmsapitest.c b/test/cmsapitest.c
index e583b33f2c..977095b3f8 100644
--- a/test/cmsapitest.c
+++ b/test/cmsapitest.c
@@ -19,6 +19,8 @@

 static X509 *cert = NULL;
 static EVP_PKEY *privkey = NULL;
+static X509 *ed448_cert = NULL;
+static EVP_PKEY *ed448_privkey = NULL;
 static char *derin = NULL;
 static char *too_long_iv_cms_in = NULL;
 static char *pwri_kek_oob_der_in = NULL;
@@ -288,6 +290,47 @@ static int test_CMS_add1_cert(void)
     return ret;
 }

+static int test_CMS_add1_signer_ed448(const EVP_MD *md, unsigned int flags,
+    int expect_success)
+{
+    CMS_ContentInfo *cms = NULL;
+    CMS_SignerInfo *si = NULL;
+    int ret = 0;
+
+    if (!TEST_ptr(cms = CMS_ContentInfo_new()))
+        goto end;
+
+    si = CMS_add1_signer(cms, ed448_cert, ed448_privkey, md, flags);
+    if (expect_success) {
+        if (!TEST_ptr(si))
+            goto end;
+    } else if (!TEST_ptr_null(si)) {
+        goto end;
+    }
+
+    ret = 1;
+end:
+    if (!expect_success && ret)
+        ERR_clear_error();
+    CMS_ContentInfo_free(cms);
+    return ret;
+}
+
+static int test_CMS_add1_signer_ed448_signed_attrs(void)
+{
+    return test_CMS_add1_signer_ed448(NULL, 0, 0);
+}
+
+static int test_CMS_add1_signer_ed448_signed_attrs_md(void)
+{
+    return test_CMS_add1_signer_ed448(EVP_shake256(), 0, 0);
+}
+
+static int test_CMS_add1_signer_ed448_noattr(void)
+{
+    return test_CMS_add1_signer_ed448(NULL, CMS_NOATTR, 1);
+}
+
 static int test_d2i_CMS_bio_NULL(void)
 {
     BIO *bio, *content = NULL;
@@ -739,12 +782,12 @@ end:
     return ret;
 }

-OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile tooLongIVpem pwriKekOobDer\n")
+OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile tooLongIVpem pwriKekOobDer [ed448certfile ed448privkeyfile]\n")

 int setup_tests(void)
 {
     char *certin = NULL, *privkeyin = NULL;
-    BIO *certbio = NULL, *privkeybio = NULL;
+    char *ed448_certin = NULL, *ed448_privkeyin = NULL;

     if (!test_skip_common_options()) {
         TEST_error("Error parsing test options\n");
@@ -758,28 +801,28 @@ int setup_tests(void)
         || !TEST_ptr(pwri_kek_oob_der_in = test_get_argument(4)))
         return 0;

-    certbio = BIO_new_file(certin, "r");
-    if (!TEST_ptr(certbio))
-        return 0;
-    if (!TEST_true(PEM_read_bio_X509(certbio, &cert, NULL, NULL))) {
-        BIO_free(certbio);
-        return 0;
-    }
-    BIO_free(certbio);
-
-    privkeybio = BIO_new_file(privkeyin, "r");
-    if (!TEST_ptr(privkeybio)) {
+    if (!TEST_ptr(cert = load_cert_pem(certin, NULL))
+        || !TEST_ptr(privkey = load_pkey_pem(privkeyin, NULL))) {
         X509_free(cert);
         cert = NULL;
+        EVP_PKEY_free(privkey);
+        privkey = NULL;
         return 0;
     }
-    if (!TEST_true(PEM_read_bio_PrivateKey(privkeybio, &privkey, NULL, NULL))) {
-        BIO_free(privkeybio);
-        X509_free(cert);
-        cert = NULL;
-        return 0;
+
+    if (test_get_argument_count() >= 7) {
+        ed448_certin = test_get_argument(5);
+        ed448_privkeyin = test_get_argument(6);
+
+        if (!TEST_ptr(ed448_cert = load_cert_pem(ed448_certin, NULL))
+            || !TEST_ptr(ed448_privkey = load_pkey_pem(ed448_privkeyin, NULL))) {
+            X509_free(ed448_cert);
+            ed448_cert = NULL;
+            EVP_PKEY_free(ed448_privkey);
+            ed448_privkey = NULL;
+            return 0;
+        }
     }
-    BIO_free(privkeybio);

     ADD_TEST(test_encrypt_decrypt_aes_cbc);
     ADD_TEST(test_encrypt_decrypt_aes_128_gcm);
@@ -796,6 +839,11 @@ int setup_tests(void)
     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);
+    if (ed448_cert != NULL && ed448_privkey != NULL) {
+        ADD_TEST(test_CMS_add1_signer_ed448_signed_attrs);
+        ADD_TEST(test_CMS_add1_signer_ed448_signed_attrs_md);
+        ADD_TEST(test_CMS_add1_signer_ed448_noattr);
+    }
     return 1;
 }

@@ -803,4 +851,6 @@ void cleanup_tests(void)
 {
     X509_free(cert);
     EVP_PKEY_free(privkey);
+    X509_free(ed448_cert);
+    EVP_PKEY_free(ed448_privkey);
 }
diff --git a/test/recipes/80-test_cmsapi.t b/test/recipes/80-test_cmsapi.t
index 3d1dae8464..2813e46b26 100644
--- a/test/recipes/80-test_cmsapi.t
+++ b/test/recipes/80-test_cmsapi.t
@@ -16,9 +16,14 @@ plan skip_all => "CMS is disabled in this build" if disabled("cms");

 plan tests => 1;

+my @ed448_args = disabled("ecx") ? () : (
+    srctop_file("test", "certs", "server-ed448-cert.pem"),
+    srctop_file("test", "certs", "server-ed448-key.pem"));
+
 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", "cms_pwri_kek_oob.der")])),
+             srctop_file("test", "recipes", "80-test_cmsapi_data", "cms_pwri_kek_oob.der"),
+             @ed448_args])),
              "running cmsapitest");
diff --git a/test/x509_internal_test.c b/test/x509_internal_test.c
index 1176ef49ef..85ef13036f 100644
--- a/test/x509_internal_test.c
+++ b/test/x509_internal_test.c
@@ -688,6 +688,24 @@ err:
     return test;
 }

+static int test_X509_ALGOR_set_md_null(void)
+{
+    X509_ALGOR *alg = NULL;
+    int ret = 0;
+
+    if (!TEST_ptr(alg = X509_ALGOR_new()))
+        goto err;
+
+    if (!TEST_false(X509_ALGOR_set_md(alg, NULL)))
+        goto err;
+
+    ret = 1;
+
+err:
+    X509_ALGOR_free(alg);
+    return ret;
+}
+
 /* https://github.com/openssl/openssl/issues/26325 */
 static const char *kRootExtensionDuplicity[] = {
     "-----BEGIN CERTIFICATE-----\n",
@@ -948,6 +966,7 @@ int setup_tests(void)
     ADD_TEST(tests_X509_check_crypto);
     ADD_TEST(tests_x509_check_dpn);
     ADD_TEST(tests_x509_check_akid);
+    ADD_TEST(test_X509_ALGOR_set_md_null);
     ADD_TEST(tests_x509_check_ext_duplicity);
     ADD_TEST(tests_x509_check_ext_duplicity_nid_undef);
     ADD_TEST(tests_x509_check_ext_duplicity_nid_dynamic);