Commit 647a96676a for openssl.org
commit 647a96676a01b223b500fc14d7dcd93489576ee8
Author: Bob Beck <beck@openssl.org>
Date: Thu Sep 3 16:48:44 2026 -0600
Make the cached SHA-1 fingerprint internal-only
X509::sha1_hash exists only so X509_cmp() can compare certificates by
identity, yet X509_digest() handed it to callers when asked for SHA-1,
and it was computed under the certificate's libctx/propq, tying an
internal value to the context the certificate was parsed in.
Compute it with the built-in SHA-1 over the DER encoding instead, via
the new ossl_x509_internal_fingerprint(), so it is the same in every
library context; the only remaining failure mode is an allocation
failure. Drop the X509_digest() shortcut so the cached value is never
visible to callers, and rename the field to fingerprint in X509 and
X509_CRL. The CRL side gets the same treatment in a following commit.
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Merge-date: Thu Sep 17 16:44:33 2026
Merged-from: https://github.com/openssl/openssl/pull/32686
diff --git a/crypto/x509/v3_purp.c b/crypto/x509/v3_purp.c
index 462e2f12c8..8e685103c5 100644
--- a/crypto/x509/v3_purp.c
+++ b/crypto/x509/v3_purp.c
@@ -518,7 +518,7 @@ static void scan_ext_flags(const X509 *x509, uint32_t *flags)
/*
* Cache info on various X.509v3 extensions and further derived information,
* e.g., if cert 'x' is self-issued, in x->ex_flags and other internal fields.
- * x->sha1_hash is filled in, or else EXFLAG_NO_FINGERPRINT is set in x->flags.
+ * x->fingerprint is filled in, or else EXFLAG_NO_FINGERPRINT is set in x->flags.
* X509_SIG_INFO_VALID is set in x->flags if x->siginf was filled successfully.
* Set EXFLAG_INVALID and return 0 in case the certificate is invalid.
*
@@ -537,7 +537,7 @@ int ossl_x509v3_cache_extensions(const X509 *const_x)
int i;
int res;
uint32_t tmp_ex_flags;
- unsigned char tmp_sha1_hash[SHA_DIGEST_LENGTH];
+ unsigned char tmp_fingerprint[SHA_DIGEST_LENGTH];
long tmp_ex_pathlen;
long tmp_ex_pcpathlen;
uint32_t tmp_ex_kusage;
@@ -570,8 +570,8 @@ int ossl_x509v3_cache_extensions(const X509 *const_x)
ERR_set_mark();
- /* Cache the SHA1 digest of the cert */
- if (!X509_digest(const_x, EVP_sha1(), tmp_sha1_hash, NULL))
+ if (!ossl_x509_internal_fingerprint(ASN1_ITEM_rptr(X509), const_x,
+ tmp_fingerprint, sizeof(tmp_fingerprint)))
tmp_ex_flags |= EXFLAG_NO_FINGERPRINT;
/* V1 should mean no extensions ... */
@@ -768,7 +768,7 @@ int ossl_x509v3_cache_extensions(const X509 *const_x)
((X509 *)const_x)->ex_pathlen = tmp_ex_pathlen;
((X509 *)const_x)->ex_pcpathlen = tmp_ex_pcpathlen;
if (!(tmp_ex_flags & EXFLAG_NO_FINGERPRINT))
- memcpy(((X509 *)const_x)->sha1_hash, tmp_sha1_hash, SHA_DIGEST_LENGTH);
+ memcpy(((X509 *)const_x)->fingerprint, tmp_fingerprint, SHA_DIGEST_LENGTH);
if (tmp_ex_flags & EXFLAG_KUSAGE)
((X509 *)const_x)->ex_kusage = tmp_ex_kusage;
((X509 *)const_x)->ex_xkusage = tmp_ex_xkusage;
diff --git a/crypto/x509/x509_cmp.c b/crypto/x509/x509_cmp.c
index 04d508ddc3..fb7e84f011 100644
--- a/crypto/x509/x509_cmp.c
+++ b/crypto/x509/x509_cmp.c
@@ -97,7 +97,7 @@ int X509_CRL_match(const X509_CRL *a, const X509_CRL *b)
if ((a->flags & EXFLAG_NO_FINGERPRINT) == 0
&& (b->flags & EXFLAG_NO_FINGERPRINT) == 0)
- rv = memcmp(a->sha1_hash, b->sha1_hash, SHA_DIGEST_LENGTH);
+ rv = memcmp(a->fingerprint, b->fingerprint, SHA_DIGEST_LENGTH);
if (rv != 0)
return rv < 0 ? -1 : 1;
@@ -189,7 +189,7 @@ int X509_cmp(const X509 *a, const X509 *b)
if ((a->ex_flags & EXFLAG_NO_FINGERPRINT) == 0
&& (b->ex_flags & EXFLAG_NO_FINGERPRINT) == 0)
- rv = memcmp(a->sha1_hash, b->sha1_hash, SHA_DIGEST_LENGTH);
+ rv = memcmp(a->fingerprint, b->fingerprint, SHA_DIGEST_LENGTH);
if (rv != 0)
return rv < 0 ? -1 : 1;
diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c
index ef16a7fc88..e394bfe0d5 100644
--- a/crypto/x509/x_all.c
+++ b/crypto/x509/x_all.c
@@ -30,6 +30,7 @@
#include "crypto/x509.h"
#include "crypto/x509_acert.h"
#include "crypto/rsa.h"
+#include "crypto/sha.h"
#include "x509_local.h"
static void *RSA_new_thunk(void)
@@ -643,17 +644,27 @@ int X509_pubkey_digest(const X509 *data, const EVP_MD *type,
return EVP_Digest(key->data, key->length, md, len, type, NULL);
}
+int ossl_x509_internal_fingerprint(const ASN1_ITEM *it, const void *val,
+ unsigned char *hash, size_t hash_size)
+{
+ unsigned char *der = NULL;
+ int der_len;
+
+ if (!ossl_assert(hash_size >= SHA_DIGEST_LENGTH)) {
+ ERR_raise(ERR_LIB_X509, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ der_len = ASN1_item_i2d((const ASN1_VALUE *)val, &der, it);
+ if (der_len < 0)
+ return 0;
+ ossl_sha1(der, (size_t)der_len, hash);
+ OPENSSL_free(der);
+ return 1;
+}
+
int X509_digest(const X509 *cert, const EVP_MD *md, unsigned char *data,
unsigned int *len)
{
- if (EVP_MD_is_a(md, SN_sha1) && (cert->ex_flags & EXFLAG_SET) != 0
- && (cert->ex_flags & EXFLAG_NO_FINGERPRINT) == 0) {
- /* Asking for SHA1 and we already computed it. */
- if (len != NULL)
- *len = sizeof(cert->sha1_hash);
- memcpy(data, cert->sha1_hash, sizeof(cert->sha1_hash));
- return 1;
- }
return ossl_asn1_item_digest_ex(ASN1_ITEM_rptr(X509), md, (char *)cert,
data, len, cert->libctx, cert->propq);
}
@@ -764,8 +775,8 @@ int X509_CRL_digest(const X509_CRL *data, const EVP_MD *type,
&& (data->flags & EXFLAG_NO_FINGERPRINT) == 0) {
/* Asking for SHA1; always computed in CRL d2i. */
if (len != NULL)
- *len = sizeof(data->sha1_hash);
- memcpy(md, data->sha1_hash, sizeof(data->sha1_hash));
+ *len = sizeof(data->fingerprint);
+ memcpy(md, data->fingerprint, sizeof(data->fingerprint));
return 1;
}
return ossl_asn1_item_digest_ex(ASN1_ITEM_rptr(X509_CRL), type, (char *)data,
diff --git a/crypto/x509/x_crl.c b/crypto/x509/x_crl.c
index 33c18147aa..6a1a3778fb 100644
--- a/crypto/x509/x_crl.c
+++ b/crypto/x509/x_crl.c
@@ -245,7 +245,7 @@ static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
break;
case ASN1_OP_D2I_POST:
- if (!X509_CRL_digest(crl, EVP_sha1(), crl->sha1_hash, NULL))
+ if (!X509_CRL_digest(crl, EVP_sha1(), crl->fingerprint, NULL))
crl->flags |= EXFLAG_NO_FINGERPRINT;
crl->idp = X509_CRL_get_ext_d2i(crl, NID_issuing_distribution_point, &i, NULL);
if (crl->idp == NULL && i != -1) {
diff --git a/doc/internal/man3/x509v3_cache_extensions.pod b/doc/internal/man3/x509v3_cache_extensions.pod
index cc0aeb6079..8f3dffc859 100644
--- a/doc/internal/man3/x509v3_cache_extensions.pod
+++ b/doc/internal/man3/x509v3_cache_extensions.pod
@@ -16,9 +16,13 @@ x509v3_cache_extensions
This function processes any X509v3 extensions present in an X509 object I<x>
and caches the result of that processing as well as further derived info,
for instance whether the certificate is self-issued or has version X.509v1.
-It computes the SHA1 digest of the certificate using the default library context
-and property query string and stores the result in x->sha1_hash,
-or on failure sets B<EXFLAG_NO_FINGERPRINT> in x->flags.
+It computes an internal-use SHA1 fingerprint of the certificate, used only by
+L<X509_cmp(3)> and never exposed to callers, with the built-in SHA1
+implementation, independent of any library context or property query string,
+and stores the result in x->fingerprint;
+on failure (the certificate cannot be DER encoded, for instance because it is
+still under construction, or memory allocation failed) it sets
+B<EXFLAG_NO_FINGERPRINT> in x->flags instead.
It sets B<X509_SIG_INFO_VALID> in x->flags if x->siginf was filled successfully,
which may not be possible if a referenced algorithm is unknown or not available.
Many OpenSSL functions that use an X509 object call this function implicitly.
diff --git a/doc/man3/X509_get_extension_flags.pod b/doc/man3/X509_get_extension_flags.pod
index bc2ec51ecc..21bf952e75 100644
--- a/doc/man3/X509_get_extension_flags.pod
+++ b/doc/man3/X509_get_extension_flags.pod
@@ -86,8 +86,9 @@ ASN1 object itself.
=item B<EXFLAG_NO_FINGERPRINT>
-Failed to compute the internal SHA1 hash value of the certificate or CRL.
-This may be due to malloc failure or because no SHA1 implementation was found.
+Failed to compute the internal fingerprint of the certificate or CRL,
+because it could not be DER encoded (for instance a certificate still under
+construction) or on memory allocation failure.
=item B<EXFLAG_INVALID_POLICY>
diff --git a/include/crypto/x509.h b/include/crypto/x509.h
index 4925131180..bb575b57a5 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -119,8 +119,8 @@ struct X509_crl_st {
ASN1_INTEGER *crl_number;
ASN1_INTEGER *base_crl_number;
STACK_OF(GENERAL_NAMES) *issuers;
- /* hash of CRL */
- unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+ /* internal-use fingerprint for X509_CRL_match(), see ossl_x509_internal_fingerprint() */
+ unsigned char fingerprint[SHA_DIGEST_LENGTH];
/* alternative method to handle this CRL */
const X509_CRL_METHOD *meth;
void *meth_data;
@@ -198,7 +198,8 @@ struct x509_st {
STACK_OF(IPAddressFamily) *rfc3779_addr;
struct ASIdentifiers_st *rfc3779_asid;
#endif
- unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+ /* internal-use fingerprint for X509_cmp(), see ossl_x509_internal_fingerprint() */
+ unsigned char fingerprint[SHA_DIGEST_LENGTH];
X509_CERT_AUX *aux;
CRYPTO_RWLOCK *lock;
volatile int ex_cached;
@@ -319,6 +320,29 @@ int ossl_x509_print_ex_brief(BIO *bio, const X509 *cert, unsigned long neg_cflag
int ossl_x509v3_cache_extensions(const X509 *x);
int ossl_x509_init_sig_info(const X509 *x, X509_SIG_INFO *info);
+/**
+ * @brief Compute the internal-use fingerprint of a DER-encodable object.
+ *
+ * The fingerprint is cached in X509 / X509_CRL fingerprint and used only for
+ * internal identity comparison (X509_cmp(), X509_CRL_match()); it is never
+ * returned to callers, so the algorithm is an implementation detail
+ * (currently SHA-1). It uses the built-in implementation directly rather
+ * than fetching one, so the result depends on neither the library context
+ * nor the property query string of the object and stays valid if the object
+ * is moved to another library context. It fails only if the object cannot be
+ * DER encoded, for instance a certificate still under construction, or on
+ * an allocation failure in the encoder.
+ *
+ * @param it the ASN1_ITEM describing @p val
+ * @param val the object to encode and hash
+ * @param hash output buffer for the fingerprint
+ * @param hash_size size in bytes of @p hash, must be at least
+ * SHA_DIGEST_LENGTH
+ * @returns 1 on success, 0 on failure
+ */
+int ossl_x509_internal_fingerprint(const ASN1_ITEM *it, const void *val,
+ unsigned char *hash, size_t hash_size);
+
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);