Add support to retrieve the fingerprint of a public key from a cert.

Related-to: https://github.com/profanity-im/profanity/issues/2069
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
This commit is contained in:
Steffen Jaeckel
2025-10-31 16:03:26 +01:00
parent d38ecd5453
commit aab9cdd406
5 changed files with 46 additions and 14 deletions

View File

@@ -29,6 +29,17 @@
#include "parser.h" #include "parser.h"
#include "snprintf.h" #include "snprintf.h"
/** Define your own `STROPHE_STATIC_ASSERT` if your compiler doesn't support one
* of the below ones or define as noop if your compiler provides no replacement.
*/
#if !defined(STROPHE_STATIC_ASSERT)
#if (__STDC_VERSION__ >= 202000L)
#define STROPHE_STATIC_ASSERT(cond, msg) static_assert(cond, msg)
#else
#define STROPHE_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
#endif
#endif
/** handlers **/ /** handlers **/
#if (__STDC_VERSION__ >= 202000L) #if (__STDC_VERSION__ >= 202000L)
typedef void *xmpp_void_handler; typedef void *xmpp_void_handler;

View File

@@ -164,9 +164,13 @@ const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt)
"Expires On", "Expires On",
"Public Key Algorithm", "Public Key Algorithm",
"Certificate Signature Algorithm", "Certificate Signature Algorithm",
"Fingerprint SHA-1", "Certificate Fingerprint SHA-1",
"Fingerprint SHA-256", "Certificate Fingerprint SHA-256",
"Public Key Fingerprint SHA-256",
}; };
STROPHE_STATIC_ASSERT(
ARRAY_SIZE(descriptions) == XMPP_CERT_ELEMENT_MAX,
"descriptions don't match the number of enum elements");
if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX) if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
return NULL; return NULL;
return descriptions[elmnt]; return descriptions[elmnt];

View File

@@ -334,6 +334,12 @@ static xmpp_tlscert_t *_x509_to_tlscert(xmpp_ctx_t *ctx, gnutls_x509_crt_t cert)
gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, smallbuf, &size); gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, smallbuf, &size);
hex_encode(buf, smallbuf, size); hex_encode(buf, smallbuf, size);
tlscert->elements[XMPP_CERT_FINGERPRINT_SHA256] = strophe_strdup(ctx, buf); tlscert->elements[XMPP_CERT_FINGERPRINT_SHA256] = strophe_strdup(ctx, buf);
size = sizeof(smallbuf);
gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256, (void *)smallbuf,
&size);
hex_encode(buf, smallbuf, size);
tlscert->elements[XMPP_CERT_PUBKEY_FINGERPRINT_SHA256] =
strophe_strdup(ctx, buf);
strophe_snprintf(buf, sizeof(buf), "%d", gnutls_x509_crt_get_version(cert)); strophe_snprintf(buf, sizeof(buf), "%d", gnutls_x509_crt_get_version(cert));
tlscert->elements[XMPP_CERT_VERSION] = strophe_strdup(ctx, buf); tlscert->elements[XMPP_CERT_VERSION] = strophe_strdup(ctx, buf);

View File

@@ -376,18 +376,26 @@ _get_fingerprint(const xmpp_ctx_t *ctx, X509 *err_cert, xmpp_cert_element_t el)
{ {
unsigned char buf[EVP_MAX_MD_SIZE]; unsigned char buf[EVP_MAX_MD_SIZE];
unsigned int len; unsigned int len;
int (*digest_fn)(const X509 *data, const EVP_MD *type, unsigned char *md,
unsigned int *len);
const EVP_MD *digest; const EVP_MD *digest;
switch (el) { switch (el) {
case XMPP_CERT_FINGERPRINT_SHA1: case XMPP_CERT_FINGERPRINT_SHA1:
digest_fn = X509_digest;
digest = EVP_sha1(); digest = EVP_sha1();
break; break;
case XMPP_CERT_FINGERPRINT_SHA256: case XMPP_CERT_FINGERPRINT_SHA256:
digest_fn = X509_digest;
digest = EVP_sha256();
break;
case XMPP_CERT_PUBKEY_FINGERPRINT_SHA256:
digest_fn = X509_pubkey_digest;
digest = EVP_sha256(); digest = EVP_sha256();
break; break;
default: default:
return NULL; return NULL;
} }
if (X509_digest(err_cert, digest, buf, &len) != 0) { if (digest_fn(err_cert, digest, buf, &len) != 0) {
char fingerprint[4 * EVP_MAX_MD_SIZE]; char fingerprint[4 * EVP_MAX_MD_SIZE];
hex_encode(fingerprint, buf, len); hex_encode(fingerprint, buf, len);
return strophe_strdup(ctx, fingerprint); return strophe_strdup(ctx, fingerprint);
@@ -475,6 +483,8 @@ static xmpp_tlscert_t *_x509_to_tlscert(xmpp_ctx_t *ctx, X509 *cert)
_get_fingerprint(ctx, cert, XMPP_CERT_FINGERPRINT_SHA1); _get_fingerprint(ctx, cert, XMPP_CERT_FINGERPRINT_SHA1);
tlscert->elements[XMPP_CERT_FINGERPRINT_SHA256] = tlscert->elements[XMPP_CERT_FINGERPRINT_SHA256] =
_get_fingerprint(ctx, cert, XMPP_CERT_FINGERPRINT_SHA256); _get_fingerprint(ctx, cert, XMPP_CERT_FINGERPRINT_SHA256);
tlscert->elements[XMPP_CERT_PUBKEY_FINGERPRINT_SHA256] =
_get_fingerprint(ctx, cert, XMPP_CERT_PUBKEY_FINGERPRINT_SHA256);
strophe_snprintf(buf, sizeof(buf), "%ld", X509_get_version(cert) + 1); strophe_snprintf(buf, sizeof(buf), "%ld", X509_get_version(cert) + 1);
tlscert->elements[XMPP_CERT_VERSION] = strophe_strdup(ctx, buf); tlscert->elements[XMPP_CERT_VERSION] = strophe_strdup(ctx, buf);

View File

@@ -251,17 +251,18 @@ typedef enum {
* @ingroup TLS * @ingroup TLS
*/ */
typedef enum { typedef enum {
XMPP_CERT_VERSION, /**< X.509 Version */ XMPP_CERT_VERSION, /**< X.509 Version */
XMPP_CERT_SERIALNUMBER, /**< SerialNumber */ XMPP_CERT_SERIALNUMBER, /**< SerialNumber */
XMPP_CERT_SUBJECT, /**< Subject */ XMPP_CERT_SUBJECT, /**< Subject */
XMPP_CERT_ISSUER, /**< Issuer */ XMPP_CERT_ISSUER, /**< Issuer */
XMPP_CERT_NOTBEFORE, /**< Issued on */ XMPP_CERT_NOTBEFORE, /**< Issued on */
XMPP_CERT_NOTAFTER, /**< Expires on */ XMPP_CERT_NOTAFTER, /**< Expires on */
XMPP_CERT_KEYALG, /**< Public Key Algorithm */ XMPP_CERT_KEYALG, /**< Public Key Algorithm */
XMPP_CERT_SIGALG, /**< Certificate Signature Algorithm */ XMPP_CERT_SIGALG, /**< Certificate Signature Algorithm */
XMPP_CERT_FINGERPRINT_SHA1, /**< Fingerprint SHA-1 */ XMPP_CERT_FINGERPRINT_SHA1, /**< Fingerprint SHA-1 */
XMPP_CERT_FINGERPRINT_SHA256, /**< Fingerprint SHA-256 */ XMPP_CERT_FINGERPRINT_SHA256, /**< Fingerprint SHA-256 */
XMPP_CERT_ELEMENT_MAX /**< Last element of the enum */ XMPP_CERT_PUBKEY_FINGERPRINT_SHA256, /**< SHA-256 fingerprint of pubkey */
XMPP_CERT_ELEMENT_MAX /**< Last element of the enum */
} xmpp_cert_element_t; } xmpp_cert_element_t;
typedef struct { typedef struct {