Commit 0a62876772 for openssl.org

commit 0a62876772a24969f503fbcbf0d5da7d827a3c77
Author: Dmitry Belyavskiy <beldmit@gmail.com>
Date:   Thu Sep 10 16:49:18 2026 +0200

    Provide stricter test for cert order on parse

    Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
    Reviewed-by: Simo Sorce <simo@redhat.com>
    Reviewed-by: Todd Short <todd.short@me.com>
    Merge-date: Tue Sep 15 15:23:49 2026
    Merged-from: https://github.com/openssl/openssl/pull/32773

diff --git a/test/pkcs12_api_test.c b/test/pkcs12_api_test.c
index 2a8105a892..5f4550fe3b 100644
--- a/test/pkcs12_api_test.c
+++ b/test/pkcs12_api_test.c
@@ -63,6 +63,7 @@ static int has_ca = 0;
 static int expected_ca_count = -1;
 static const char *expected_cert_file = NULL;
 static const char *expected_ca_file = NULL;
+static const char *expected_key_file = NULL;
 static int mismatched_key_pass = 0;

 static int changepass(PKCS12 *p12, EVP_PKEY *key, X509 *cert, STACK_OF(X509) *ca)
@@ -393,6 +394,7 @@ typedef enum OPTION_choice {
     OPT_CA_COUNT,
     OPT_EXPECTED_CERT,
     OPT_EXPECTED_CA,
+    OPT_EXPECTED_KEY,
     OPT_MISMATCHED_P12,
     OPT_LEGACY,
     OPT_TEST_ENUM
@@ -410,6 +412,7 @@ const OPTIONS *test_get_options(void)
         { "ca-count", OPT_CA_COUNT, 'n', "Expected number of CA certificates" },
         { "expected-cert", OPT_EXPECTED_CERT, '<', "PEM file of expected main certificate" },
         { "expected-ca", OPT_EXPECTED_CA, '<', "PEM file of expected CA certificates in order" },
+        { "expected-key", OPT_EXPECTED_KEY, '<', "PEM file of expected private key" },
         { "mismatched-key-pass", OPT_MISMATCHED_P12, '-', "Input has key encrypted with a different password" },
         { "legacy", OPT_LEGACY, '-', "Test the legacy APIs" },
         { NULL }
@@ -477,93 +480,91 @@ err:
     return ret;
 }

-static int test_parse_cert_placement(void)
+static int test_parse_cert_placement(int idx)
 {
-    int ret = 0, i;
+    int ret = 0, i, pos = 0;
+    int want_key = (idx >> 2) & 1;
+    int want_cert = (idx >> 1) & 1;
+    int want_ca = idx & 1;
+    int selected = want_key && want_cert ? 1 : -1;
     PKCS12 *p12 = NULL;
-    EVP_PKEY *key = NULL;
-    X509 *cert = NULL, *exp_cert = NULL, *x = NULL;
-    STACK_OF(X509) *ca = NULL, *exp_ca = NULL;
-    BIO *bio = NULL;
+    EVP_PKEY *key = NULL, *exp_key = NULL;
+    X509 *cert = NULL, *exp_cert = NULL;
+    X509 *ordered[3];
+    STACK_OF(X509) *ca = NULL, *exp_ca = NULL, *all = NULL;

-    if (in_file == NULL
-        || (expected_cert_file == NULL && expected_ca_file == NULL))
+    if (expected_key_file == NULL && expected_cert_file == NULL
+        && expected_ca_file == NULL)
         return 1;

-    p12 = PKCS12_load(in_file);
-    if (!TEST_ptr(p12))
-        goto err;
-
-    if (!TEST_true(PKCS12_parse(p12, in_pass, &key, &cert, &ca)))
-        goto err;
-
-    if (has_key && !TEST_ptr(key))
+    if (!TEST_ptr(exp_key = load_pkey_pem(expected_key_file, testctx))
+        || !TEST_ptr(exp_cert = load_cert_pem(expected_cert_file, testctx))
+        || !TEST_ptr(exp_ca = load_certs_pem(expected_ca_file))
+        || !TEST_int_eq(sk_X509_num(exp_ca), 2)
+        || !TEST_int_ne(X509_cmp(exp_cert, sk_X509_value(exp_ca, 0)), 0)
+        || !TEST_ptr(all = sk_X509_new_null()))
         goto err;

-    if (expected_cert_file != NULL) {
-        bio = BIO_new_file(expected_cert_file, "rb");
-        if (!TEST_ptr(bio))
+    /* extra_certs.pem contains the second match, then the unrelated cert. */
+    ordered[0] = sk_X509_value(exp_ca, 1);
+    ordered[1] = exp_cert;
+    ordered[2] = sk_X509_value(exp_ca, 0);
+    for (i = 0; i < (int)OSSL_NELEM(ordered); i++) {
+        if (!TEST_int_gt(sk_X509_push(all, ordered[i]), 0))
             goto err;
-        exp_cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
-        BIO_free(bio);
-        bio = NULL;
-        if (!TEST_ptr(exp_cert))
-            goto err;
-        if (!TEST_ptr(cert))
-            goto err;
-        if (!TEST_int_eq(X509_cmp(cert, exp_cert), 0)) {
-            TEST_info("main cert does not match expected cert");
-            goto err;
-        }
     }

-    if (expected_ca_file != NULL) {
-        int actual_count, expected_count;
+    /* Passing all certificates through ca preserves the chosen order. */
+    p12 = PKCS12_create_ex("", NULL, exp_key, NULL, all,
+        NID_undef, NID_undef, 0, -1, 0, testctx, "provider=default");
+    if (!TEST_ptr(p12)
+        || !TEST_true(PKCS12_parse(p12, "",
+            want_key ? &key : NULL,
+            want_cert ? &cert : NULL,
+            want_ca ? &ca : NULL)))
+        goto err;

-        exp_ca = sk_X509_new_null();
-        if (!TEST_ptr(exp_ca))
-            goto err;
+    if (want_key
+        && (!TEST_ptr(key) || !TEST_int_eq(EVP_PKEY_eq(key, exp_key), 1)))
+        goto err;

-        bio = BIO_new_file(expected_ca_file, "rb");
-        if (!TEST_ptr(bio))
-            goto err;
-        while ((x = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
-            if (!sk_X509_push(exp_ca, x)) {
-                X509_free(x);
-                x = NULL;
+    if (want_cert) {
+        if (selected >= 0) {
+            if (!TEST_ptr(cert)
+                || !TEST_int_eq(X509_cmp(cert, exp_cert), 0))
                 goto err;
-            }
-            x = NULL;
+        } else if (!TEST_ptr_null(cert)) {
+            goto err;
         }
-        ERR_clear_error();
-        BIO_free(bio);
-        bio = NULL;
-
-        actual_count = ca == NULL ? 0 : sk_X509_num(ca);
-        expected_count = sk_X509_num(exp_ca);
+    }

-        if (!TEST_int_eq(actual_count, expected_count))
+    if (want_ca) {
+        if (!TEST_ptr(ca)
+            || !TEST_int_eq(sk_X509_num(ca), selected >= 0 ? 2 : 3))
             goto err;
-
-        for (i = 0; i < expected_count; i++) {
-            if (!TEST_int_eq(X509_cmp(sk_X509_value(ca, i),
-                                 sk_X509_value(exp_ca, i)),
-                    0)) {
-                TEST_info("CA cert mismatch at index %d", i);
+        for (i = 0; i < (int)OSSL_NELEM(ordered); i++) {
+            if (i == selected)
+                continue;
+            if (!TEST_int_eq(X509_cmp(sk_X509_value(ca, pos), ordered[i]), 0)) {
+                TEST_info("CA cert mismatch at index %d", pos);
                 goto err;
             }
+            pos++;
         }
     }

     ret = 1;
 err:
+    if (!ret)
+        TEST_info("failed placement combination %d", idx);
     PKCS12_free(p12);
     EVP_PKEY_free(key);
+    EVP_PKEY_free(exp_key);
     X509_free(cert);
-    OSSL_STACK_OF_X509_free(ca);
     X509_free(exp_cert);
+    OSSL_STACK_OF_X509_free(ca);
     OSSL_STACK_OF_X509_free(exp_ca);
-    BIO_free(bio);
+    sk_X509_free(all);
     return ret;
 }

@@ -642,6 +643,9 @@ int setup_tests(void)
         case OPT_EXPECTED_CA:
             expected_ca_file = opt_arg();
             break;
+        case OPT_EXPECTED_KEY:
+            expected_key_file = opt_arg();
+            break;
         case OPT_MISMATCHED_P12:
             mismatched_key_pass = 1;
             break;
@@ -661,7 +665,7 @@ int setup_tests(void)
     ADD_TEST(test_null_args);
     ADD_TEST(pkcs12_parse_test);
     ADD_ALL_TESTS(test_parse_combinations, 8);
-    ADD_TEST(test_parse_cert_placement);
+    ADD_ALL_TESTS(test_parse_cert_placement, 8);
     ADD_TEST(test_parse_mismatched_key_password);
     ADD_MFAIL_NO_CHECK_TEST(pkcs12_parse_mfail_test);
     ADD_MFAIL_NO_CHECK_TEST(pkcs12_parse_existing_ca_mfail_test);
diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t
index 0b553a7585..e1457639c4 100644
--- a/test/recipes/80-test_pkcs12.t
+++ b/test/recipes/80-test_pkcs12.t
@@ -445,6 +445,7 @@ ok(run(test(["pkcs12_api_test",
                  "-has-ca", 1,
                  "-ca-count", 2,
                  "-expected-cert", srctop_file(@path, "ee-cert.pem"),
+                 "-expected-key", srctop_file(@path, "ee-key.pem"),
                  "-expected-ca", $extra_certs,
                  ])), "Test PKCS12_parse cert placement with shared key");
 }