Commit 5273dc4371 for openssl.org
commit 5273dc4371da16d91700026006ea0a95aef261a1
Author: Bob Beck <beck@openssl.org>
Date: Fri Sep 4 13:57:56 2026 -0600
Verify chains in the X509_STORE_CTX's library context
X509_STORE_CTX_new_ex(3) documents that algorithms fetched while
processing with the store context come from its library context and
property query. The verifier did not do that: X509_verify(),
X509_CRL_verify() and X509_get_signature_info() fetch from the
certificate's or CRL's own context, the one it was decoded in, so a
chain of certificates decoded in the default context and verified
through a store context created in a FIPS context had its signatures
checked by the default provider.
Add internal ossl_x509_verify_ex(), ossl_x509_crl_verify_ex() and
ossl_x509_get_signature_info_ex() that take the library context and
property query explicitly, and use them from the verifier with the
store context's. The public functions are unchanged and still use the
object's own context.
Add a test verifying certificates decoded in the default context
through a store context whose library context has no signature
algorithms; it must fail.
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Merge-date: Mon Oct 26 19:04:41 2026
Merged-from: https://github.com/openssl/openssl/pull/32690
diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c
index 9096c0915d..b1bd486889 100644
--- a/crypto/x509/x509_set.c
+++ b/crypto/x509/x509_set.c
@@ -305,18 +305,25 @@ static int x509_sig_info_init(X509_SIG_INFO *siginf, const X509_ALGOR *alg,
return 1;
}
-int X509_get_signature_info(const X509 *x, int *mdnid, int *pknid, int *secbits,
- uint32_t *flags)
+int ossl_x509_get_signature_info_ex(const X509 *x, int *mdnid, int *pknid,
+ int *secbits, uint32_t *flags, OSSL_LIB_CTX *libctx, const char *propq)
{
X509_SIG_INFO siginf;
- if (x == NULL) {
- ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
- return 0;
- }
ERR_set_mark();
(void)x509_sig_info_init(&siginf, &x->sig_alg, &x->signature,
- X509_PUBKEY_get0(x->cert_info.key), x->libctx, x->propq);
+ X509_PUBKEY_get0(x->cert_info.key), libctx, propq);
ERR_pop_to_mark();
return X509_SIG_INFO_get(&siginf, mdnid, pknid, secbits, flags);
}
+
+int X509_get_signature_info(const X509 *x, int *mdnid, int *pknid, int *secbits,
+ uint32_t *flags)
+{
+ if (x == NULL) {
+ ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ return ossl_x509_get_signature_info_ex(x, mdnid, pknid, secbits, flags,
+ x->libctx, x->propq);
+}
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index 032a86d03a..e97ee74161 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -2190,7 +2190,8 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl)
if (rv != X509_V_OK && !verify_cb_crl(ctx, rv))
return 0;
/* Verify CRL signature */
- if (X509_CRL_verify(crl, ikey) <= 0 && !verify_cb_crl(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE))
+ if (ossl_x509_crl_verify_ex(crl, ikey, ctx->libctx, ctx->propq) <= 0
+ && !verify_cb_crl(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE))
return 0;
}
return 1;
@@ -2510,7 +2511,9 @@ static int internal_verify(X509_STORE_CTX *ctx)
CB_FAIL_IF(1, ctx, xi, issuer_depth,
X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY);
} else {
- CB_FAIL_IF(X509_verify(xs, pkey) <= 0,
+ CB_FAIL_IF(ossl_x509_verify_ex(xs, pkey, ctx->libctx,
+ ctx->propq)
+ <= 0,
ctx, xs, n, X509_V_ERR_CERT_SIGNATURE_FAILURE);
}
}
@@ -3580,7 +3583,10 @@ static int check_dane_pkeys(X509_STORE_CTX *ctx)
for (i = 0; i < recnum; ++i) {
t = sk_danetls_record_value(dane->trecs, i);
- if (t->usage != DANETLS_USAGE_DANE_TA || t->selector != DANETLS_SELECTOR_SPKI || t->mtype != DANETLS_MATCHING_FULL || X509_verify(cert, t->spki) <= 0)
+ if (t->usage != DANETLS_USAGE_DANE_TA
+ || t->selector != DANETLS_SELECTOR_SPKI
+ || t->mtype != DANETLS_MATCHING_FULL
+ || ossl_x509_verify_ex(cert, t->spki, ctx->libctx, ctx->propq) <= 0)
continue;
/* Clear any PKIX-?? matches that failed to extend to a full chain */
@@ -4259,7 +4265,8 @@ static int check_sig_level(X509_STORE_CTX *ctx, X509 *cert)
if (level > NUM_AUTH_LEVELS)
level = NUM_AUTH_LEVELS;
- if (!X509_get_signature_info(cert, NULL, NULL, &secbits, NULL))
+ if (!ossl_x509_get_signature_info_ex(cert, NULL, NULL, &secbits, NULL,
+ ctx->libctx, ctx->propq))
return 0;
return secbits >= minbits_table[level - 1];
diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c
index 6612dd06f9..f358119332 100644
--- a/crypto/x509/x_all.c
+++ b/crypto/x509/x_all.c
@@ -130,14 +130,20 @@ static int i2d_ECPrivateKey_thunk(const void *a, unsigned char **out)
}
#endif
-int X509_verify(const X509 *a, EVP_PKEY *r)
+int ossl_x509_verify_ex(const X509 *a, EVP_PKEY *r, OSSL_LIB_CTX *libctx,
+ const char *propq)
{
if (X509_ALGOR_cmp(&a->sig_alg, &a->cert_info.signature) != 0)
return 0;
return ASN1_item_verify_ex(ASN1_ITEM_rptr(X509_CINF), &a->sig_alg,
&a->signature, &a->cert_info,
- a->distinguishing_id, r, a->libctx, a->propq);
+ a->distinguishing_id, r, libctx, propq);
+}
+
+int X509_verify(const X509 *a, EVP_PKEY *r)
+{
+ return ossl_x509_verify_ex(a, r, a->libctx, a->propq);
}
int X509_REQ_verify_ex(X509_REQ *a, EVP_PKEY *r, OSSL_LIB_CTX *libctx,
diff --git a/crypto/x509/x_crl.c b/crypto/x509/x_crl.c
index 9693ad16a3..3aeee610c3 100644
--- a/crypto/x509/x_crl.c
+++ b/crypto/x509/x_crl.c
@@ -466,7 +466,8 @@ int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, const X509 *x)
return 0;
}
-static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r)
+static int def_crl_verify_ex(X509_CRL *crl, EVP_PKEY *r, OSSL_LIB_CTX *libctx,
+ const char *propq)
{
if (X509_ALGOR_cmp(&crl->sig_alg, &crl->crl.sig_alg) != 0) {
ERR_raise(ERR_LIB_X509, X509_R_CRL_SIGNATURE_ALGORITHM_MISMATCH);
@@ -474,7 +475,21 @@ static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r)
}
return ASN1_item_verify_ex(ASN1_ITEM_rptr(X509_CRL_INFO),
&crl->sig_alg, &crl->signature, &crl->crl, NULL,
- r, crl->libctx, crl->propq);
+ r, libctx, propq);
+}
+
+static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r)
+{
+ return def_crl_verify_ex(crl, r, crl->libctx, crl->propq);
+}
+
+int ossl_x509_crl_verify_ex(X509_CRL *crl, EVP_PKEY *r, OSSL_LIB_CTX *libctx,
+ const char *propq)
+{
+ /* A custom X509_CRL_METHOD has no library context to be told about */
+ if (crl->meth->crl_verify != def_crl_verify)
+ return X509_CRL_verify(crl, r);
+ return def_crl_verify_ex(crl, r, libctx, propq);
}
static int crl_revoked_issuer_match(X509_CRL *crl, const X509_NAME *nm,
diff --git a/include/crypto/x509.h b/include/crypto/x509.h
index 7ab7e79b44..6fd4716769 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -367,6 +367,57 @@ int ossl_x509_internal_fingerprint(const ASN1_ITEM *it, const void *val,
X509_NAME *ossl_dist_point_name_full(const struct DIST_POINT_NAME_st *dpn,
const X509_NAME *iname);
+/**
+ * @brief Verify a certificate signature, fetching algorithms from @p libctx.
+ *
+ * Like X509_verify(), but the signature algorithm is fetched from the given
+ * library context and property query rather than the certificate's own.
+ *
+ * @param a the certificate to verify
+ * @param r the public key of the issuer
+ * @param libctx the library context to fetch from, NULL for the default
+ * @param propq the property query to fetch with, may be NULL
+ * @returns 1 if the signature is valid, 0 if not, -1 on error
+ */
+int ossl_x509_verify_ex(const X509 *a, EVP_PKEY *r, OSSL_LIB_CTX *libctx,
+ const char *propq);
+
+/**
+ * @brief Verify a CRL signature, fetching algorithms from @p libctx.
+ *
+ * Like X509_CRL_verify(), but the signature algorithm is fetched from the
+ * given library context and property query rather than the CRL's own. A
+ * CRL with a custom X509_CRL_METHOD is verified by that method, which is
+ * not told about the library context.
+ *
+ * @param crl the CRL to verify
+ * @param r the public key of the issuer
+ * @param libctx the library context to fetch from, NULL for the default
+ * @param propq the property query to fetch with, may be NULL
+ * @returns 1 if the signature is valid, 0 if not, -1 on error
+ */
+int ossl_x509_crl_verify_ex(X509_CRL *crl, EVP_PKEY *r, OSSL_LIB_CTX *libctx,
+ const char *propq);
+
+/**
+ * @brief Describe a certificate's signature, fetching from @p libctx.
+ *
+ * Like X509_get_signature_info(), but any digest needed to determine the
+ * security bits is fetched from the given library context and property
+ * query rather than the certificate's own.
+ *
+ * @param x the certificate
+ * @param mdnid where to store the digest NID, may be NULL
+ * @param pknid where to store the public key algorithm NID, may be NULL
+ * @param secbits where to store the security bits, may be NULL
+ * @param flags where to store the X509_SIG_INFO flags, may be NULL
+ * @param libctx the library context to fetch from, NULL for the default
+ * @param propq the property query to fetch with, may be NULL
+ * @returns 1 if the information is valid, 0 if it is not available
+ */
+int ossl_x509_get_signature_info_ex(const X509 *x, int *mdnid, int *pknid,
+ int *secbits, uint32_t *flags, OSSL_LIB_CTX *libctx, const char *propq);
+
int ossl_x509_set0_libctx(X509 *x, OSSL_LIB_CTX *libctx, const char *propq);
int ossl_x509_crl_set0_libctx(X509_CRL *x, OSSL_LIB_CTX *libctx,
const char *propq);
diff --git a/test/verify_extra_test.c b/test/verify_extra_test.c
index aa86fda6ee..474f87b4c9 100644
--- a/test/verify_extra_test.c
+++ b/test/verify_extra_test.c
@@ -16,6 +16,7 @@
#include <openssl/pem.h>
#include <openssl/err.h>
#include "internal/nelem.h"
+#include <openssl/provider.h>
#include "testutil.h"
static const char *certs_dir;
@@ -752,6 +753,79 @@ err:
return testresult;
}
+/*
+ * Verify a chain of certificates decoded in the default library context
+ * through a store context created in a library context that has no
+ * signature algorithms. The signatures must be checked in the store
+ * context's library context, so verification must fail there and succeed
+ * with a store context in the default library context.
+ */
+static int do_test_store_ctx_libctx(int use_empty_libctx, int expected,
+ int expected_error)
+{
+ X509 *eecert = load_cert_from_file(ee_cert); /* may result in NULL */
+ X509 *untrcert = load_cert_from_file(ca_cert);
+ X509 *trcert = load_cert_from_file(sroot_cert);
+ STACK_OF(X509) *trusted = sk_X509_new_null();
+ STACK_OF(X509) *untrusted = sk_X509_new_null();
+ OSSL_LIB_CTX *libctx = NULL;
+ OSSL_PROVIDER *nullprov = NULL;
+ X509_STORE_CTX *ctx = NULL;
+ int testresult = 0;
+
+ if (!TEST_ptr(eecert)
+ || !TEST_ptr(untrcert)
+ || !TEST_ptr(trcert)
+ || !TEST_ptr(trusted)
+ || !TEST_ptr(untrusted))
+ goto err;
+
+ if (use_empty_libctx) {
+ if (!TEST_ptr(libctx = OSSL_LIB_CTX_new())
+ || !TEST_ptr(nullprov = OSSL_PROVIDER_load(libctx, "null")))
+ goto err;
+ }
+ if (!TEST_ptr(ctx = X509_STORE_CTX_new_ex(libctx, NULL)))
+ goto err;
+
+ if (!TEST_true(sk_X509_push(trusted, trcert)))
+ goto err;
+ trcert = NULL;
+ if (!TEST_true(sk_X509_push(untrusted, untrcert)))
+ goto err;
+ untrcert = NULL;
+
+ if (!TEST_true(X509_STORE_CTX_init(ctx, NULL, eecert, untrusted)))
+ goto err;
+ X509_STORE_CTX_set0_trusted_stack(ctx, trusted);
+
+ if (!TEST_int_eq(X509_verify_cert(ctx), expected)
+ || !TEST_int_eq(X509_STORE_CTX_get_error(ctx), expected_error))
+ goto err;
+
+ testresult = 1;
+err:
+ OSSL_STACK_OF_X509_free(trusted);
+ OSSL_STACK_OF_X509_free(untrusted);
+ X509_STORE_CTX_free(ctx);
+ X509_free(eecert);
+ X509_free(untrcert);
+ X509_free(trcert);
+ OSSL_PROVIDER_unload(nullprov);
+ OSSL_LIB_CTX_free(libctx);
+ return testresult;
+}
+
+static int test_store_ctx_default_libctx(void)
+{
+ return do_test_store_ctx_libctx(0, 1, X509_V_OK);
+}
+
+static int test_store_ctx_empty_libctx(void)
+{
+ return do_test_store_ctx_libctx(1, 0, X509_V_ERR_CERT_SIGNATURE_FAILURE);
+}
+
static int test_purpose_ssl_client(void)
{
return do_test_purpose(X509_PURPOSE_SSL_CLIENT, 0);
@@ -997,6 +1071,8 @@ int setup_tests(void)
ADD_TEST(test_self_signed_good);
ADD_TEST(test_self_signed_bad);
ADD_TEST(test_self_signed_error);
+ ADD_TEST(test_store_ctx_default_libctx);
+ ADD_TEST(test_store_ctx_empty_libctx);
ADD_TEST(test_purpose_ssl_client);
ADD_TEST(test_purpose_ssl_server);
ADD_TEST(test_purpose_any);