openssl channel_binding: lookup digest algorithm without NID

Use an alternate OpenSSL API to get the digest algorithm tied
to a certificate signature to compute the channel binding.

Fixes #20590
Reported-by: Rob Crittenden
Closes #20734
This commit is contained in:
Stefan Eissing 2026-02-26 10:21:31 +01:00 committed by Daniel Stenberg
parent 9bc8b078eb
commit b0f6e9a3d7
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2

View File

@ -5255,13 +5255,15 @@ out:
return result;
}
static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
static CURLcode ossl_get_channel_binding(struct Curl_easy *data,
int sockindex,
struct dynbuf *binding)
{
X509 *cert;
int algo_nid;
const EVP_MD *algo_type;
const char *algo_name;
int mdnid;
bool no_digest_acceptable = FALSE;
const EVP_MD *algo_type = NULL;
const char *algo_name = NULL;
unsigned int length;
unsigned char buf[EVP_MAX_MD_SIZE];
@ -5293,43 +5295,108 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
/* No server certificate, do not do channel binding */
return CURLE_OK;
if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) {
#ifdef HAVE_OPENSSL3
{
int pknid, secbits;
uint32_t flags;
EVP_PKEY *pkey = X509_get0_pubkey(cert);
if(!X509_get_signature_info(cert, &mdnid, &pknid, &secbits, &flags)) {
failf(data, "certificate signature algorithm not recognized");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto out;
}
if(mdnid != NID_undef) {
if(mdnid == NID_md5 || mdnid == NID_sha1) {
algo_type = EVP_sha256();
}
else
algo_type = EVP_get_digestbynid(mdnid);
}
else if(pkey && !EVP_PKEY_is_a(pkey, OBJ_nid2sn(pknid))) {
/* The cert's pkey is different from the algorithm used to sign
* the certificate. Since the reported `mdnid` is undefined, there
* is no digest algorithm available here. This happens in PQC
* and is accepted, resulting in no addition to the binding. */
no_digest_acceptable = TRUE;
}
else if(pkey) {
/* cert's pkey type is the same as the cert signer (or same family).
* Ask for the mandatory/advisory digest algorithm for the pkey.
*/
char mdname[128] = "";
int rc = EVP_PKEY_get_default_digest_name(pkey, mdname, sizeof(mdname));
bool md_is_undef = !strcmp(mdname, "UNDEF");
if(rc == 2 && md_is_undef) {
/* OpenSSL declares "undef" the *mandatory* digest for this key.
* This is some PQC shit, accept it, no addition to binding. */
no_digest_acceptable = TRUE;
}
else if(rc > 0 && mdname[0] != '\0' && !md_is_undef) {
infof(data, "Digest algorithm : %s%s (derived from public key)"
", but unavailable",
mdname, rc == 2 ? " [mandatory]" : " [advisory]");
}
}
}
#else /* HAVE_OPENSSL3 */
if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &mdnid, NULL)) {
failf(data,
"Unable to find digest NID for certificate signature algorithm");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto error;
goto out;
}
/* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */
if(algo_nid == NID_md5 || algo_nid == NID_sha1) {
if(mdnid == NID_md5 || mdnid == NID_sha1) {
algo_type = EVP_sha256();
}
else {
algo_type = EVP_get_digestbynid(algo_nid);
algo_type = EVP_get_digestbynid(mdnid);
if(!algo_type) {
algo_name = OBJ_nid2sn(algo_nid);
algo_name = OBJ_nid2sn(mdnid);
failf(data, "Could not find digest algorithm %s (NID %d)",
algo_name ? algo_name : "(null)", algo_nid);
algo_name ? algo_name : "(null)", mdnid);
result = CURLE_SSL_INVALIDCERTSTATUS;
goto error;
goto out;
}
}
if(!X509_digest(cert, algo_type, buf, &length)) {
failf(data, "X509_digest() failed");
#endif /* HAVE_OPENSSL3, else */
if(!algo_type) {
if(no_digest_acceptable) {
infof(data, "certificate exposes no signing digest algorithm, "
"nothing to add to channel binding");
result = CURLE_OK;
goto out;
}
/* unacceptable, something is wrong, fail */
algo_name = OBJ_nid2sn(mdnid);
failf(data, "Unable to find digest algorithm %s (NID %d) "
"for channel binding", algo_name ? algo_name : "(null)", mdnid);
result = CURLE_SSL_INVALIDCERTSTATUS;
goto error;
goto out;
}
if(!X509_digest(cert, algo_type, buf, &length)) {
failf(data, "X509_digest() failed for channel binding");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto out;
}
/* Append "tls-server-end-point:" */
result = curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1);
if(result)
goto error;
goto out;
/* Append digest */
result = curlx_dyn_addn(binding, buf, length);
error:
out:
X509_free(cert);
return result;
}