]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Fix sigalg corner cases
authorViktor Dukhovni <openssl-users@dukhovni.org>
Wed, 26 Mar 2025 10:36:08 +0000 (21:36 +1100)
committerTomas Mraz <tomas@openssl.org>
Mon, 31 Mar 2025 12:07:56 +0000 (14:07 +0200)
- Tolerate RSA PKCS#1 *certificate* signatures when
  the peer sigals include RSA PSS with the same digest.

  Now that we're more strict about not sending sigalgs that are out of
  protocol range, when the client supports TLS 1.3 only, we might refuse
  to return an RSA PKCS#1-signed cert.

- Don't send TLS 1.3 sigalgs when requesting client certs from
  a TLS 1.2 client.

Fixes: #1144
Fixes: #25277
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/27166)

ssl/ssl_local.h
ssl/statem/extensions_clnt.c
ssl/t1_lib.c
test/certs/p256-ee-rsa-ca-cert.pem [new file with mode: 0644]
test/certs/p256-ee-rsa-ca-key.pem [new file with mode: 0644]
test/certs/setup.sh
test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt
test/recipes/75-test_quicapi_data/ssltraceref.txt
test/recipes/95-test_external_tlsfuzzer_data/cert.json.in
test/sslapitest.c

index 3a7c809881768286459ca0545476859a45cedd99..960f0c0f84b3a0cdff7ef2ca0df0cc994c1e2f4e 100644 (file)
  */
 # define SSL_USE_TLS1_2_CIPHERS(s)       \
     (SSL_CONNECTION_GET_SSL(s)->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_TLS1_2_CIPHERS)
-/*
- * Determine if a client can use TLS 1.2 ciphersuites: can't rely on method
- * flags because it may not be set to correct version yet.
- */
-# define SSL_CLIENT_USE_TLS1_2_CIPHERS(s)        \
-    ((!SSL_CONNECTION_IS_DTLS(s) && s->client_version >= TLS1_2_VERSION) || \
-     (SSL_CONNECTION_IS_DTLS(s) && DTLS_VERSION_GE(s->client_version, DTLS1_2_VERSION)))
-/*
- * Determine if a client should send signature algorithms extension:
- * as with TLS1.2 cipher we can't rely on method flags.
- */
-# define SSL_CLIENT_USE_SIGALGS(s)        \
-    SSL_CLIENT_USE_TLS1_2_CIPHERS(s)
 
 # define IS_MAX_FRAGMENT_LENGTH_EXT_VALID(value) \
     (((value) >= TLSEXT_max_fragment_length_512) && \
index 8b6bc60efc54852226f3d86f2b0438675d2c2edf..baa7c47b3cd9fe067f2d7e6e73062aec585c803e 100644 (file)
@@ -329,8 +329,22 @@ EXT_RETURN tls_construct_ctos_sig_algs(SSL_CONNECTION *s, WPACKET *pkt,
     size_t salglen;
     const uint16_t *salg;
 
-    if (!SSL_CLIENT_USE_SIGALGS(s))
+    /*
+     * This used both in the initial hello and as part of renegotiation,
+     * in the latter case, the client version may be already set and may
+     * be lower than that initially offered in `client_version`.
+     */
+    if (!SSL_CONNECTION_IS_DTLS(s)) {
+        if (s->client_version < TLS1_2_VERSION
+            || (s->ssl.method->version != TLS_ANY_VERSION
+                && s->version < TLS1_2_VERSION))
         return EXT_RETURN_NOT_SENT;
+    } else {
+        if (DTLS_VERSION_LT(s->client_version, DTLS1_2_VERSION)
+            || (s->ssl.method->version != DTLS_ANY_VERSION
+                && DTLS_VERSION_LT(s->version, DTLS1_2_VERSION)))
+        return EXT_RETURN_NOT_SENT;
+    }
 
     salglen = tls12_get_psigalgs(s, 1, &salg);
     if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_signature_algorithms)
index 4eadeb4009ecf09c25a029de83cfe775ec0dab5d..8d0c2647b79cfa0e4f59c3092199dacc1b8351a3 100644 (file)
@@ -2057,17 +2057,17 @@ static const SIGALG_LOOKUP sigalg_lookup_tbl[] = {
      "RSA+SHA256", TLSEXT_SIGALG_rsa_pkcs1_sha256,
      NID_sha256, SSL_MD_SHA256_IDX, EVP_PKEY_RSA, SSL_PKEY_RSA,
      NID_sha256WithRSAEncryption, NID_undef, 1, 0,
-     TLS1_2_VERSION, TLS1_2_VERSION, DTLS1_2_VERSION, DTLS1_2_VERSION},
+     TLS1_2_VERSION, 0, DTLS1_2_VERSION, 0},
     {TLSEXT_SIGALG_rsa_pkcs1_sha384_name,
      "RSA+SHA384", TLSEXT_SIGALG_rsa_pkcs1_sha384,
      NID_sha384, SSL_MD_SHA384_IDX, EVP_PKEY_RSA, SSL_PKEY_RSA,
      NID_sha384WithRSAEncryption, NID_undef, 1, 0,
-     TLS1_2_VERSION, TLS1_2_VERSION, DTLS1_2_VERSION, DTLS1_2_VERSION},
+     TLS1_2_VERSION, 0, DTLS1_2_VERSION, 0},
     {TLSEXT_SIGALG_rsa_pkcs1_sha512_name,
      "RSA+SHA512", TLSEXT_SIGALG_rsa_pkcs1_sha512,
      NID_sha512, SSL_MD_SHA512_IDX, EVP_PKEY_RSA, SSL_PKEY_RSA,
      NID_sha512WithRSAEncryption, NID_undef, 1, 0,
-     TLS1_2_VERSION, TLS1_2_VERSION, DTLS1_2_VERSION, DTLS1_2_VERSION},
+     TLS1_2_VERSION, 0, DTLS1_2_VERSION, 0},
 
     {TLSEXT_SIGALG_rsa_pkcs1_sha224_name,
      "RSA+SHA224", TLSEXT_SIGALG_rsa_pkcs1_sha224,
@@ -2642,6 +2642,43 @@ static int sigalg_security_bits(SSL_CTX *ctx, const SIGALG_LOOKUP *lu)
     return secbits;
 }
 
+static int tls_sigalg_compat(SSL_CONNECTION *sc, const SIGALG_LOOKUP *lu)
+{
+    int minversion, maxversion;
+    int minproto, maxproto;
+
+    if (!lu->available)
+        return 0;
+
+    if (SSL_CONNECTION_IS_DTLS(sc)) {
+        if (sc->ssl.method->version == DTLS_ANY_VERSION) {
+            minproto = sc->min_proto_version;
+            maxproto = sc->max_proto_version;
+        } else {
+            maxproto = minproto = sc->version;
+        }
+        minversion = lu->mindtls;
+        maxversion = lu->maxdtls;
+    } else {
+        if (sc->ssl.method->version == TLS_ANY_VERSION) {
+            minproto = sc->min_proto_version;
+            maxproto = sc->max_proto_version;
+        } else {
+            maxproto = minproto = sc->version;
+        }
+        minversion = lu->mintls;
+        maxversion = lu->maxtls;
+    }
+    if (minversion == -1 || maxversion == -1
+        || (minversion != 0 && maxproto != 0
+            && ssl_version_cmp(sc, minversion, maxproto) > 0)
+        || (maxversion != 0 && minproto != 0
+            && ssl_version_cmp(sc, maxversion, minproto) < 0)
+        || !tls12_sigalg_allowed(sc, SSL_SECOP_SIGALG_SUPPORTED, lu))
+        return 0;
+    return 1;
+}
+
 /*
  * Check signature algorithm is consistent with sent supported signature
  * algorithms and if so set relevant digest and signature scheme in
@@ -2656,7 +2693,6 @@ int tls12_check_peer_sigalg(SSL_CONNECTION *s, uint16_t sig, EVP_PKEY *pkey)
     int pkeyid = -1;
     const SIGALG_LOOKUP *lu;
     int secbits = 0;
-    int minversion, maxversion;
 
     pkeyid = EVP_PKEY_get_id(pkey);
 
@@ -2673,18 +2709,7 @@ int tls12_check_peer_sigalg(SSL_CONNECTION *s, uint16_t sig, EVP_PKEY *pkey)
 
     /* Is this code point available and compatible with the protocol */
     lu = tls1_lookup_sigalg(SSL_CONNECTION_GET_CTX(s), sig);
-    if (lu == NULL) {
-        SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_WRONG_SIGNATURE_TYPE);
-        return 0;
-    }
-    minversion = SSL_CONNECTION_IS_DTLS(s) ? lu->mindtls : lu->mintls;
-    maxversion = SSL_CONNECTION_IS_DTLS(s) ? lu->maxdtls : lu->maxtls;
-    if (minversion == -1 || maxversion == -1 || !lu->available
-        || (minversion != 0 && s->max_proto_version != 0
-            && ssl_version_cmp(s, minversion, s->max_proto_version) > 0)
-        || (maxversion != 0 && s->min_proto_version != 0
-            && ssl_version_cmp(s, maxversion, s->min_proto_version) < 0)
-        || !tls12_sigalg_allowed(s, SSL_SECOP_SIGALG_SUPPORTED, lu)) {
+    if (lu == NULL || !tls_sigalg_compat(s, lu)) {
         SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_WRONG_SIGNATURE_TYPE);
         return 0;
     }
@@ -3411,18 +3436,8 @@ int tls12_copy_sigalgs(SSL_CONNECTION *s, WPACKET *pkt,
     for (i = 0; i < psiglen; i++, psig++) {
         const SIGALG_LOOKUP *lu =
             tls1_lookup_sigalg(SSL_CONNECTION_GET_CTX(s), *psig);
-        int minversion, maxversion;
 
-        if (lu == NULL)
-            continue;
-        minversion = SSL_CONNECTION_IS_DTLS(s) ? lu->mindtls : lu->mintls;
-        maxversion = SSL_CONNECTION_IS_DTLS(s) ? lu->maxdtls : lu->maxtls;
-        if (minversion == -1 || maxversion == -1 || !lu->available
-            || (minversion != 0 && s->max_proto_version != 0
-                && ssl_version_cmp(s, minversion, s->max_proto_version) > 0)
-            || (maxversion != 0 && s->min_proto_version != 0
-                && ssl_version_cmp(s, maxversion, s->min_proto_version) < 0)
-            || !tls12_sigalg_allowed(s, SSL_SECOP_SIGALG_SUPPORTED, lu))
+        if (lu == NULL || !tls_sigalg_compat(s, lu))
             continue;
         if (!WPACKET_put_bytes_u16(pkt, *psig))
             return 0;
@@ -3889,7 +3904,17 @@ static int tls1_check_sig_alg(SSL_CONNECTION *s, X509 *x, int default_nid)
     const SIGALG_LOOKUP *sigalg;
     size_t sigalgslen;
 
-    if (default_nid == -1)
+    /*-
+     * RFC 8446, section 4.2.3:
+     *
+     * The signatures on certificates that are self-signed or certificates
+     * that are trust anchors are not validated, since they begin a
+     * certification path (see [RFC5280], Section 3.2).  A certificate that
+     * begins a certification path MAY use a signature algorithm that is not
+     * advertised as being supported in the "signature_algorithms"
+     * extension.
+     */
+    if (default_nid == -1 || X509_self_signed(x, 0))
         return 1;
     sig_nid = X509_get_signature_nid(x);
     if (default_nid)
@@ -3907,11 +3932,33 @@ static int tls1_check_sig_alg(SSL_CONNECTION *s, X509 *x, int default_nid)
         sigalgslen = s->shared_sigalgslen;
     }
     for (i = 0; i < sigalgslen; i++) {
+        int mdnid, pknid;
+
         sigalg = use_pc_sigalgs
                  ? tls1_lookup_sigalg(SSL_CONNECTION_GET_CTX(s),
                                       s->s3.tmp.peer_cert_sigalgs[i])
                  : s->shared_sigalgs[i];
-        if (sigalg != NULL && sig_nid == sigalg->sigandhash)
+        if (sigalg == NULL)
+            continue;
+        if (sig_nid == sigalg->sigandhash)
+            return 1;
+        if (sigalg->sig != EVP_PKEY_RSA_PSS)
+            continue;
+        /*
+         * Accept RSA PKCS#1 signatures in certificates when the signature
+         * algorithms include RSA-PSS with a matching digest algorithm.
+         *
+         * When a TLS 1.3 peer inadvertently omits the legacy RSA PKCS#1 code
+         * points, and we're doing strict checking of the certificate chain (in
+         * a cert_cb via SSL_check_chain()) we may then reject RSA signed
+         * certificates in the chain, but the TLS requirement on PSS should not
+         * extend to certificates.  Though the peer can in fact list the legacy
+         * sigalgs for just this purpose, it is not likely that a better chain
+         * signed with RSA-PSS is available.
+         */
+        if (!OBJ_find_sigid_algs(sig_nid, &mdnid, &pknid))
+            continue;
+        if (pknid == EVP_PKEY_RSA && mdnid == sigalg->hash)
             return 1;
     }
     return 0;
@@ -4529,26 +4576,13 @@ static const SIGALG_LOOKUP *find_sig_alg(SSL_CONNECTION *s, X509 *x,
 
     /* Look for a shared sigalgs matching possible certificates */
     for (i = 0; i < s->shared_sigalgslen; i++) {
-        int dtls, minversion, maxversion;
-
         /* Skip SHA1, SHA224, DSA and RSA if not PSS */
         lu = s->shared_sigalgs[i];
         if (lu->hash == NID_sha1
             || lu->hash == NID_sha224
             || lu->sig == EVP_PKEY_DSA
-            || lu->sig == EVP_PKEY_RSA)
-            continue;
-
-        /*
-         * By this point the protocol version should already be chosen.  Check
-         * the sigalg version bounds.
-         */
-        dtls = SSL_CONNECTION_IS_DTLS(s);
-        minversion = dtls ? lu->mindtls : lu->mintls;
-        maxversion = dtls ? lu->maxdtls : lu->maxtls;
-        if (minversion != 0 && ssl_version_cmp(s, minversion, s->version) > 0)
-            continue;
-        if (maxversion != 0 && ssl_version_cmp(s, maxversion, s->version) < 0)
+            || lu->sig == EVP_PKEY_RSA
+            || !tls_sigalg_compat(s, lu))
             continue;
 
         /* Check that we have a cert, and signature_algorithms_cert */
@@ -4631,20 +4665,10 @@ int tls_choose_sigalg(SSL_CONNECTION *s, int fatalerrs)
                  * cert type
                  */
                 for (i = 0; i < s->shared_sigalgslen; i++) {
-                    int dtls, minversion, maxversion;
-
                     /* Check the sigalg version bounds */
                     lu = s->shared_sigalgs[i];
-                    dtls = SSL_CONNECTION_IS_DTLS(s);
-                    minversion = dtls ? lu->mindtls : lu->mintls;
-                    maxversion = dtls ? lu->maxdtls : lu->maxtls;
-                    if (minversion != 0
-                        && ssl_version_cmp(s, minversion, s->version) > 0)
+                    if (!tls_sigalg_compat(s, lu))
                         continue;
-                    if (maxversion != 0
-                        && ssl_version_cmp(s, maxversion, s->version) < 0)
-                        continue;
-
                     if (s->server) {
                         if ((sig_idx = tls12_get_cert_sigalg_idx(s, lu)) == -1)
                             continue;
diff --git a/test/certs/p256-ee-rsa-ca-cert.pem b/test/certs/p256-ee-rsa-ca-cert.pem
new file mode 100644 (file)
index 0000000..e0a57ed
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICQDCCASigAwIBAgIBAjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDDAJDQTAg
+Fw0yNTAzMjYwNDAwMThaGA8yMTI1MDMyNzA0MDAxOFowHzEdMBsGA1UEAwwUUC0y
+NTYgY2VydCBFRSBpc3N1ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS75MkU
+kSmDEElkBVwaw5WD7XE2wjO4DWyu0ayOZ49RceBzoL0Y/xLcVWDt+89MJ5IbvAsI
+OAdF/7WFpbrz/LgFo2IwYDAdBgNVHQ4EFgQUm0m0pKJWq4YY6Q/2x7BIJhyLyMAw
+HwYDVR0jBBgwFoAUtBEz8dfiXvdTniAiEE+GBr8fyV4wCQYDVR0TBAIwADATBgNV
+HSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEARKppcgmGU7D79Qyd
+gTr5mJVtEWKpkV+WZbOqYRaLeZpRxhFJ0nUlgKgnN7Ck8KY9Zdx5u6bF2N0rXZyl
+J1XnrDtrjadFsVg/j4aFHlSRz9mPxCvl4MAq/cQDyFv+BSel2cuM7+ZPM9BO0256
+0+kVJscMImkEJ3JuAQHz7i/TBK4uk1H9C8IGWWwNK0unWQyZyWtAT7NDa6bjoktf
+FBpcG9D2398TWBZeaO6w31en4SKUqCzJ43MLsJJ4bPP8TEl4vf3skEhwAfeN/yyB
+rO+RInqHuaZ0yGbZNwYotvGaVKToYYt1p4otTy+NSoeXk7SIYW3c6beGRtlaPDzq
+da3+vg==
+-----END CERTIFICATE-----
diff --git a/test/certs/p256-ee-rsa-ca-key.pem b/test/certs/p256-ee-rsa-ca-key.pem
new file mode 100644 (file)
index 0000000..7892d5d
--- /dev/null
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgzekrCs3M+7Mim6iA
+m+Ok4GMMI3YbWEQycJj431k23WWhRANCAAS75MkUkSmDEElkBVwaw5WD7XE2wjO4
+DWyu0ayOZ49RceBzoL0Y/xLcVWDt+89MJ5IbvAsIOAdF/7WFpbrz/LgF
+-----END PRIVATE KEY-----
index 28b8196c7849766280c28da63423547c3cc4e305..5bd87087cc1f303bbdb90872e18ee94fafae9cdf 100755 (executable)
@@ -493,3 +493,8 @@ OPENSSL_SIGALG="sha3-224" ./mkcert.sh genee server.example ee-key-ec-named-named
 OPENSSL_SIGALG="sha3-256" ./mkcert.sh genee server.example ee-key-ec-named-named ee-cert-ec-sha3-256 ca-key-ec-named ca-cert-ec-named
 OPENSSL_SIGALG="sha3-384" ./mkcert.sh genee server.example ee-key-ec-named-named ee-cert-ec-sha3-384 ca-key-ec-named ca-cert-ec-named
 OPENSSL_SIGALG="sha3-512" ./mkcert.sh genee server.example ee-key-ec-named-named ee-cert-ec-sha3-512 ca-key-ec-named ca-cert-ec-named
+
+# EC cert seigned RSA intermediate CA
+OPENSSL_KEYALG=ec OPENSSL_KEYBITS=prime256v1 ./mkcert.sh genee \
+    "P-256 cert EE issuer" p256-ee-rsa-ca-key \
+    p256-ee-rsa-ca-cert ca-key ca-cert
index fabea52ef8b188c430b2835026c78a7cc3e2bf68..44b0aef74b310dec80c8767b8da1a09ac9206eb8 100644 (file)
@@ -55,6 +55,9 @@ Header:
           rsa_pss_rsae_sha256 (0x0804)
           rsa_pss_rsae_sha384 (0x0805)
           rsa_pss_rsae_sha512 (0x0806)
+          rsa_pkcs1_sha256 (0x0401)
+          rsa_pkcs1_sha384 (0x0501)
+          rsa_pkcs1_sha512 (0x0601)
         extension_type=supported_versions(43), length=3
           TLS 1.3 (772)
         extension_type=psk_key_exchange_modes(45), length=2
index 338c628b98cb534b6f5574d5fd619072079aee1f..2a625ae506973ebb448a395196367e945420e601 100644 (file)
@@ -55,6 +55,9 @@ Header:
           rsa_pss_rsae_sha256 (0x0804)
           rsa_pss_rsae_sha384 (0x0805)
           rsa_pss_rsae_sha512 (0x0806)
+          rsa_pkcs1_sha256 (0x0401)
+          rsa_pkcs1_sha384 (0x0501)
+          rsa_pkcs1_sha512 (0x0601)
         extension_type=supported_versions(43), length=3
           TLS 1.3 (772)
         extension_type=psk_key_exchange_modes(45), length=2
index 38e46e4bb929af6958a7666e6d45d36f03d5c802..a14e10e8f20ea822c57d97b22d413192d3d29b4b 100644 (file)
        {"name" : "test-tls13-certificate-verify.py",
         "arguments" : ["-k", "tests/clientX509Key.pem",
                        "-c", "tests/clientX509Cert.pem",
-                       "-s", "9+5 9+6 9+4 ecdsa_secp256r1_sha256 ecdsa_secp384r1_sha384 ecdsa_secp521r1_sha512 ed25519 ed448 8+26 8+27 8+28 rsa_pss_pss_sha256 rsa_pss_pss_sha384 rsa_pss_pss_sha512 rsa_pss_rsae_sha256 rsa_pss_rsae_sha384 rsa_pss_rsae_sha512 rsa_pkcs1_sha256 rsa_pkcs1_sha384 rsa_pkcs1_sha512 ecdsa_sha224 rsa_pkcs1_sha224",
+                       "-s", "9+5 9+6 9+4 ecdsa_secp256r1_sha256 ecdsa_secp384r1_sha384 ecdsa_secp521r1_sha512 ed25519 ed448 8+26 8+27 8+28 rsa_pss_pss_sha256 rsa_pss_pss_sha384 rsa_pss_pss_sha512 rsa_pss_rsae_sha256 rsa_pss_rsae_sha384 rsa_pss_rsae_sha512 rsa_pkcs1_sha256 rsa_pkcs1_sha384 rsa_pkcs1_sha512",
                        "-p", "@PORT@"]},
        {"name" : "test-tls13-ecdsa-in-certificate-verify.py",
           "arguments" : ["-k", "tests/serverECKey.pem",
                          "-c", "tests/serverECCert.pem",
-                         "-s", "9+5 9+6 9+4 ecdsa_secp256r1_sha256 ecdsa_secp384r1_sha384 ecdsa_secp521r1_sha512 ed25519 ed448 8+26 8+27 8+28 rsa_pss_pss_sha256 rsa_pss_pss_sha384 rsa_pss_pss_sha512 rsa_pss_rsae_sha256 rsa_pss_rsae_sha384 rsa_pss_rsae_sha512 rsa_pkcs1_sha256 rsa_pkcs1_sha384 rsa_pkcs1_sha512 ecdsa_sha224 rsa_pkcs1_sha224",
+                         "-s", "9+5 9+6 9+4 ecdsa_secp256r1_sha256 ecdsa_secp384r1_sha384 ecdsa_secp521r1_sha512 ed25519 ed448 8+26 8+27 8+28 rsa_pss_pss_sha256 rsa_pss_pss_sha384 rsa_pss_pss_sha512 rsa_pss_rsae_sha256 rsa_pss_rsae_sha384 rsa_pss_rsae_sha512 rsa_pkcs1_sha256 rsa_pkcs1_sha384 rsa_pkcs1_sha512",
                          "-p", "@PORT@"]}
      ]
     },
index a20d1f720342c2da25b25d292a8c7b68560bb60d..38d58e938743efa69e918c41d50db8e3052c5362 100644 (file)
@@ -8911,14 +8911,47 @@ static int test_async_shutdown(void)
 #if !defined(OPENSSL_NO_TLS1_2) || !defined(OSSL_NO_USABLE_TLS1_3)
 static int cert_cb_cnt;
 
+static int load_chain(const char *file, EVP_PKEY **pkey, X509 **x509,
+                      STACK_OF(X509) *chain)
+{
+    char *path = test_mk_file_path(certsdir, file);
+    BIO *in = NULL;
+    X509 *x = NULL;
+    int ok = 0;
+
+    if (path == NULL)
+        return 0;
+    if ((in = BIO_new(BIO_s_file())) == NULL
+        || BIO_read_filename(in, path) <= 0)
+        goto out;
+    if (pkey == NULL) {
+        if ((x = X509_new_ex(libctx, NULL)) == NULL
+            || PEM_read_bio_X509(in, &x, NULL, NULL) == NULL)
+            goto out;
+        if (chain == NULL)
+            *x509 = x;
+        else if (!sk_X509_push(chain, x))
+            goto out;
+    } else if (PEM_read_bio_PrivateKey_ex(in, pkey, NULL, NULL,
+                                          libctx, NULL) == NULL) {
+        goto out;
+    }
+
+    x = NULL;
+    ok = 1;
+ out:
+    X509_free(x);
+    BIO_free(in);
+    OPENSSL_free(path);
+    return ok;
+}
+
 static int cert_cb(SSL *s, void *arg)
 {
     SSL_CTX *ctx = (SSL_CTX *)arg;
-    BIO *in = NULL;
     EVP_PKEY *pkey = NULL;
-    X509 *x509 = NULL, *rootx = NULL;
+    X509 *x509 = NULL, *x = NULL;
     STACK_OF(X509) *chain = NULL;
-    char *rootfile = NULL, *ecdsacert = NULL, *ecdsakey = NULL;
     int ret = 0;
 
     if (cert_cb_cnt == 0) {
@@ -8943,33 +8976,14 @@ static int cert_cb(SSL *s, void *arg)
     } else if (cert_cb_cnt == 3) {
         int rv;
 
-        rootfile = test_mk_file_path(certsdir, "rootcert.pem");
-        ecdsacert = test_mk_file_path(certsdir, "server-ecdsa-cert.pem");
-        ecdsakey = test_mk_file_path(certsdir, "server-ecdsa-key.pem");
-        if (!TEST_ptr(rootfile) || !TEST_ptr(ecdsacert) || !TEST_ptr(ecdsakey))
-            goto out;
         chain = sk_X509_new_null();
-        if (!TEST_ptr(chain))
-            goto out;
-        if (!TEST_ptr(in = BIO_new(BIO_s_file()))
-                || !TEST_int_gt(BIO_read_filename(in, rootfile), 0)
-                || !TEST_ptr(rootx = X509_new_ex(libctx, NULL))
-                || !TEST_ptr(PEM_read_bio_X509(in, &rootx, NULL, NULL))
-                || !TEST_true(sk_X509_push(chain, rootx)))
-            goto out;
-        rootx = NULL;
-        BIO_free(in);
-        if (!TEST_ptr(in = BIO_new(BIO_s_file()))
-                || !TEST_int_gt(BIO_read_filename(in, ecdsacert), 0)
-                || !TEST_ptr(x509 = X509_new_ex(libctx, NULL))
-                || !TEST_ptr(PEM_read_bio_X509(in, &x509, NULL, NULL)))
-            goto out;
-        BIO_free(in);
-        if (!TEST_ptr(in = BIO_new(BIO_s_file()))
-                || !TEST_int_gt(BIO_read_filename(in, ecdsakey), 0)
-                || !TEST_ptr(pkey = PEM_read_bio_PrivateKey_ex(in, NULL,
-                                                               NULL, NULL,
-                                                               libctx, NULL)))
+        if (!TEST_ptr(chain)
+            || !TEST_true(load_chain("ca-cert.pem", NULL, NULL, chain))
+            || !TEST_true(load_chain("root-cert.pem", NULL, NULL, chain))
+            || !TEST_true(load_chain("p256-ee-rsa-ca-cert.pem", NULL,
+                                     &x509, NULL))
+            || !TEST_true(load_chain("p256-ee-rsa-ca-key.pem", &pkey,
+                                     NULL, NULL)))
             goto out;
         rv = SSL_check_chain(s, x509, pkey, chain);
         /*
@@ -8989,13 +9003,9 @@ static int cert_cb(SSL *s, void *arg)
 
     /* Abort the handshake */
  out:
-    OPENSSL_free(ecdsacert);
-    OPENSSL_free(ecdsakey);
-    OPENSSL_free(rootfile);
-    BIO_free(in);
     EVP_PKEY_free(pkey);
     X509_free(x509);
-    X509_free(rootx);
+    X509_free(x);
     OSSL_STACK_OF_X509_free(chain);
     return ret;
 }
@@ -9024,7 +9034,7 @@ static int test_cert_cb_int(int prot, int tst)
 
     if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
                                        TLS_client_method(),
-                                       TLS1_VERSION,
+                                       prot,
                                        prot,
                                        &sctx, &cctx, NULL, NULL)))
         goto end;