Home | History | Annotate | Line # | Download | only in tls
      1 /*	$NetBSD: tls_fprint.c,v 1.5 2025/02/25 19:15:50 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	tls_fprint 3
      6 /* SUMMARY
      7 /*	Digests fingerprints and all that.
      8 /* SYNOPSIS
      9 /*	#include <tls.h>
     10 /*
     11 /*	EVP_MD *tls_digest_byname(const char *mdalg, EVP_MD_CTX **mdctxPtr)
     12 /*	const char *mdalg;
     13 /*	EVP_MD_CTX **mdctxPtr;
     14 /*
     15 /*	char	*tls_serverid_digest(TLScontext, props, ciphers)
     16 /*	TLS_SESS_STATE *TLScontext;
     17 /*	const TLS_CLIENT_START_PROPS *props;
     18 /*	const char *ciphers;
     19 /*
     20 /*	char	*tls_digest_encode(md_buf, md_len)
     21 /*	const unsigned char *md_buf;
     22 /*	const char *md_len;
     23 /*
     24 /*	char	*tls_cert_fprint(peercert, mdalg)
     25 /*	X509	*peercert;
     26 /*	const char *mdalg;
     27 /*
     28 /*	char	*tls_pkey_fprint(peercert, mdalg)
     29 /*	EVP_PKEY *peerpkey;
     30 /*	const char *mdalg;
     31 /* DESCRIPTION
     32 /*	tls_digest_byname() constructs, and optionally returns, an EVP_MD_CTX
     33 /*	handle for performing digest operations with the algorithm named by the
     34 /*	mdalg parameter.  The return value is non-null on success, and holds a
     35 /*	digest algorithm handle.  If the mdctxPtr argument is non-null the
     36 /*	created context is returned to the caller, who is then responsible for
     37 /*	deleting it by calling EVP_MD_ctx_free() once it is no longer needed.
     38 /*
     39 /*	tls_digest_encode() converts a binary message digest to a hex ASCII
     40 /*	format with ':' separators between each pair of hex digits.
     41 /*	The return value is dynamically allocated with mymalloc(),
     42 /*	and the caller must eventually free it with myfree().
     43 /*
     44 /*	tls_cert_fprint() returns a fingerprint of the given
     45 /*	certificate using the requested message digest, formatted
     46 /*	with tls_digest_encode(). Panics if the
     47 /*	(previously verified) digest algorithm is not found. The return
     48 /*	value is dynamically allocated with mymalloc(), and the caller
     49 /*	must eventually free it with myfree().
     50 /*
     51 /*	tls_pkey_fprint() returns a public-key fingerprint; in all
     52 /*	other respects the function behaves as tls_cert_fprint().
     53 /*	The return value is dynamically allocated with mymalloc(),
     54 /*	and the caller must eventually free it with myfree().
     55 /*
     56 /*	tls_serverid_digest() suffixes props->serverid computed by the SMTP
     57 /*	client with "&" plus a digest of additional parameters needed to ensure
     58 /*	that re-used sessions are more likely to be reused and that they will
     59 /*	satisfy all protocol and security requirements.  The return value is
     60 /*	dynamically allocated with mymalloc(), and the caller must eventually
     61 /*	free it with myfree().
     62 /*
     63 /*	Arguments:
     64 /* .IP mdalg
     65 /*	A digest algorithm name, such as "sha256".
     66 /* .IP peercert
     67 /*	Server or client X.509 certificate.
     68 /* .IP md_buf
     69 /*	The raw binary digest.
     70 /* .IP md_len
     71 /*	The digest length in bytes.
     72 /* .IP mdalg
     73 /*	Name of a message digest algorithm suitable for computing secure
     74 /*	(1st pre-image resistant) message digests of certificates. For now,
     75 /*	md5, sha1, or member of SHA-2 family if supported by OpenSSL.
     76 /* .IP mdctxPtr
     77 /*	Pointer to an (EVP_MD_CTX *) handle, or NULL if only probing for
     78 /*	algorithm support without immediate use in mind.
     79 /* .IP buf
     80 /*	Input data for the message digest algorithm mdalg.
     81 /* .IP len
     82 /*	The length of the input data.
     83 /* .IP props
     84 /*	The client start properties for the session, which contains the
     85 /*	initial serverid from the SMTP client and the DANE verification
     86 /*	parameters.
     87 /* .IP protomask
     88 /*	The mask of protocol exclusions.
     89 /* .IP ciphers
     90 /*	The SSL client cipherlist.
     91 /* LICENSE
     92 /* .ad
     93 /* .fi
     94 /*	This software is free. You can do with it whatever you want.
     95 /*	The original author kindly requests that you acknowledge
     96 /*	the use of his software.
     97 /* AUTHOR(S)
     98 /*	Wietse Venema
     99 /*	IBM T.J. Watson Research
    100 /*	P.O. Box 704
    101 /*	Yorktown Heights, NY 10598, USA
    102 /*
    103 /*	Viktor Dukhovni
    104 /*--*/
    105 
    106 /* System library. */
    107 
    108 #include <sys_defs.h>
    109 #include <ctype.h>
    110 
    111 #ifdef USE_TLS
    112 #include <string.h>
    113 
    114 /* Utility library. */
    115 
    116 #include <msg.h>
    117 #include <mymalloc.h>
    118 #include <stringops.h>
    119 
    120 /* Global library. */
    121 
    122 #include <mail_params.h>
    123 
    124 /* TLS library. */
    125 
    126 #define TLS_INTERNAL
    127 #include <tls.h>
    128 
    129 /* Application-specific. */
    130 
    131 static const char hexcodes[] = "0123456789ABCDEF";
    132 
    133 #define CHECK_OK_AND(stillok) (ok = ok && (stillok))
    134 #define CHECK_OK_AND_DIGEST_OBJECT(m, p) \
    135 	CHECK_OK_AND_DIGEST_DATA((m), (unsigned char *)(p), sizeof(*(p)))
    136 #define CHECK_OK_AND_DIGEST_DATA(m, p, l) CHECK_OK_AND(digest_bytes((m), (p), (l)))
    137 #define CHECK_OK_AND_DIGEST_CHARS(m, s) CHECK_OK_AND(digest_chars((m), (s)))
    138 
    139 /* digest_bytes - hash octet string of given length */
    140 
    141 static int digest_bytes(EVP_MD_CTX *ctx, const unsigned char *buf, size_t len)
    142 {
    143     return (EVP_DigestUpdate(ctx, buf, len));
    144 }
    145 
    146 /* digest_chars - hash string including trailing NUL */
    147 
    148 static int digest_chars(EVP_MD_CTX *ctx, const char *s)
    149 {
    150     return (EVP_DigestUpdate(ctx, s, strlen(s) + 1));
    151 }
    152 
    153 /* tlsa_cmp - compare TLSA RRs for sorting to canonical order */
    154 
    155 static int tlsa_cmp(const void *a, const void *b)
    156 {
    157     TLS_TLSA *p = *(TLS_TLSA **) a;
    158     TLS_TLSA *q = *(TLS_TLSA **) b;
    159     int     d;
    160 
    161     if ((d = (int) p->usage - (int) q->usage) != 0)
    162 	return d;
    163     if ((d = (int) p->selector - (int) q->selector) != 0)
    164 	return d;
    165     if ((d = (int) p->mtype - (int) q->mtype) != 0)
    166 	return d;
    167     if ((d = (int) p->length - (int) q->length) != 0)
    168 	return d;
    169     return (memcmp(p->data, q->data, p->length));
    170 }
    171 
    172 /* tls_digest_tlsa - fold in digest of TLSA records */
    173 
    174 static int tls_digest_tlsa(EVP_MD_CTX *mdctx, TLS_TLSA *tlsa)
    175 {
    176     TLS_TLSA *p;
    177     TLS_TLSA **arr;
    178     int     ok = 1;
    179     int     n;
    180     int     i;
    181 
    182     for (n = 0, p = tlsa; p != 0; p = p->next)
    183 	++n;
    184     arr = (TLS_TLSA **) mymalloc(n * sizeof(*arr));
    185     for (i = 0, p = tlsa; p; p = p->next)
    186 	arr[i++] = (void *) p;
    187     qsort(arr, n, sizeof(arr[0]), tlsa_cmp);
    188 
    189     CHECK_OK_AND_DIGEST_OBJECT(mdctx, &n);
    190     for (i = 0; i < n; ++i) {
    191 	CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->usage);
    192 	CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->selector);
    193 	CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->mtype);
    194 	CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->length);
    195 	CHECK_OK_AND_DIGEST_DATA(mdctx, arr[i]->data, arr[i]->length);
    196     }
    197     myfree((void *) arr);
    198     return (ok);
    199 }
    200 
    201 /* tls_digest_byname - test availability or prepare to use digest */
    202 
    203 const EVP_MD *tls_digest_byname(const char *mdalg, EVP_MD_CTX **mdctxPtr)
    204 {
    205     const EVP_MD *md;
    206     EVP_MD_CTX *mdctx = NULL;
    207     int     ok = 1;
    208 
    209     /*
    210      * In OpenSSL 3.0, because of dynamically variable algorithm providers,
    211      * there is a time-of-check/time-of-use issue that means that abstract
    212      * algorithm handles returned by EVP_get_digestbyname() can (and not
    213      * infrequently do) return ultimately unusable algorithms, to check for
    214      * actual availability, one needs to use the new EVP_MD_fetch() API, or
    215      * indirectly check usability by creating a concrete context. We take the
    216      * latter approach here (works for 1.1.1 without #ifdef).
    217      *
    218      * Note that EVP_MD_CTX_{create,destroy} were renamed to, respectively,
    219      * EVP_MD_CTX_{new,free} in OpenSSL 1.1.0.
    220      */
    221     CHECK_OK_AND(md = EVP_get_digestbyname(mdalg));
    222 
    223     /*
    224      * Sanity check: Newer shared libraries could (hypothetical ABI break)
    225      * allow larger digests, we avoid such poison algorithms.
    226      */
    227     CHECK_OK_AND(EVP_MD_size(md) <= EVP_MAX_MD_SIZE);
    228     CHECK_OK_AND(mdctx = EVP_MD_CTX_new());
    229     CHECK_OK_AND(EVP_DigestInit_ex(mdctx, md, NULL));
    230 
    231 
    232     if (ok && mdctxPtr != 0)
    233 	*mdctxPtr = mdctx;
    234     else
    235 	EVP_MD_CTX_free(mdctx);
    236     return (ok ? md : 0);
    237 }
    238 
    239 /* tls_serverid_digest - suffix props->serverid with parameter digest */
    240 
    241 char   *tls_serverid_digest(TLS_SESS_STATE *TLScontext,
    242 			            const TLS_CLIENT_START_PROPS *props,
    243 			            const char *ciphers)
    244 {
    245     EVP_MD_CTX *mdctx;
    246     const char *mdalg;
    247     unsigned char md_buf[EVP_MAX_MD_SIZE];
    248     unsigned int md_len;
    249     int     ok = 1;
    250     int     i;
    251     long    sslversion;
    252     VSTRING *result;
    253 
    254     /*
    255      * Try to use sha256: our serverid choice should be strong enough to
    256      * resist 2nd-preimage attacks with a difficulty comparable to that of
    257      * DANE TLSA digests.  Failing that, we compute serverid digests with the
    258      * default digest, but DANE requires sha256 and sha512, so if we must
    259      * fall back to our default digest, DANE support won't be available.  We
    260      * panic if the fallback algorithm is not available, as it was verified
    261      * available in tls_client_init() and must not simply vanish.  Our
    262      * provider set is not expected to change once the OpenSSL library is
    263      * initialized.
    264      */
    265     if (tls_digest_byname(mdalg = LN_sha256, &mdctx) == 0
    266 	&& tls_digest_byname(mdalg = props->mdalg, &mdctx) == 0)
    267 	msg_panic("digest algorithm \"%s\" not found", props->mdalg);
    268 
    269     /* Salt the session lookup key with the OpenSSL runtime version. */
    270     sslversion = OpenSSL_version_num();
    271 
    272     CHECK_OK_AND_DIGEST_CHARS(mdctx, props->helo ? props->helo : "");
    273     CHECK_OK_AND_DIGEST_OBJECT(mdctx, &sslversion);
    274     CHECK_OK_AND_DIGEST_CHARS(mdctx, props->protocols);
    275     CHECK_OK_AND_DIGEST_CHARS(mdctx, ciphers);
    276 
    277     /* Just in case we make this destination-policy specific */
    278     CHECK_OK_AND_DIGEST_OBJECT(mdctx, &props->enable_rpk);
    279 
    280     /*
    281      * Ensure separation of caches for sessions where DANE trust
    282      * configuration succeeded from those where it did not.  The latter
    283      * should always see a certificate validation failure, both on initial
    284      * handshake and on resumption.
    285      */
    286     CHECK_OK_AND_DIGEST_OBJECT(mdctx, &TLScontext->must_fail);
    287 
    288     /*
    289      * DNS-based or synthetic DANE trust settings are potentially used at all
    290      * levels above "encrypt".
    291      */
    292     if (TLScontext->level > TLS_LEV_ENCRYPT
    293 	&& props->dane && props->dane->tlsa) {
    294 	CHECK_OK_AND(tls_digest_tlsa(mdctx, props->dane->tlsa));
    295     } else {
    296 	int     none = 0;		/* Record a TLSA RR count of zero */
    297 
    298 	CHECK_OK_AND_DIGEST_OBJECT(mdctx, &none);
    299     }
    300 
    301     /*
    302      * Include the chosen SNI name, which can affect server certificate
    303      * selection.
    304      */
    305     if (TLScontext->level > TLS_LEV_ENCRYPT && TLScontext->peer_sni)
    306 	CHECK_OK_AND_DIGEST_CHARS(mdctx, TLScontext->peer_sni);
    307     else
    308 	CHECK_OK_AND_DIGEST_CHARS(mdctx, "");
    309 
    310     CHECK_OK_AND(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
    311     EVP_MD_CTX_destroy(mdctx);
    312     if (!ok)
    313 	msg_fatal("error computing %s message digest", mdalg);
    314 
    315     /* Check for OpenSSL contract violation */
    316     if (md_len > EVP_MAX_MD_SIZE)
    317 	msg_panic("unexpectedly large %s digest size: %u", mdalg, md_len);
    318 
    319     /*
    320      * Append the digest to the serverid.  We don't compare this digest to
    321      * any user-specified fingerprints.  Therefore, we don't need to use a
    322      * colon-separated format, which saves space in the TLS session cache and
    323      * makes logging of session cache lookup keys more readable.
    324      *
    325      * This does however duplicate a few lines of code from the digest encoder
    326      * for colon-separated cert and pkey fingerprints. If that is a
    327      * compelling reason to consolidate, we could use that and append the
    328      * result.
    329      */
    330     result = vstring_alloc(strlen(props->serverid) + 1 + 2 * md_len);
    331     vstring_strcpy(result, props->serverid);
    332     VSTRING_ADDCH(result, '&');
    333     for (i = 0; i < md_len; i++) {
    334 	VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0xf0) >> 4U]);
    335 	VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0x0f)]);
    336     }
    337     VSTRING_TERMINATE(result);
    338     return (vstring_export(result));
    339 }
    340 
    341 /* tls_digest_encode - encode message digest binary blob as xx:xx:... */
    342 
    343 char   *tls_digest_encode(const unsigned char *md_buf, int md_len)
    344 {
    345     int     i;
    346     char   *result = mymalloc(md_len * 3);
    347 
    348     /* Check for contract violation */
    349     if (md_len > EVP_MAX_MD_SIZE || md_len >= INT_MAX / 3)
    350 	msg_panic("unexpectedly large message digest size: %u", md_len);
    351 
    352     /* No risk of overruns, len is bounded by OpenSSL digest length */
    353     for (i = 0; i < md_len; i++) {
    354 	result[i * 3] = hexcodes[(md_buf[i] & 0xf0) >> 4U];
    355 	result[(i * 3) + 1] = hexcodes[(md_buf[i] & 0x0f)];
    356 	result[(i * 3) + 2] = (i + 1 != md_len) ? ':' : '\0';
    357     }
    358     return (result);
    359 }
    360 
    361 /* tls_data_fprint - compute and encode digest of binary object */
    362 
    363 static char *tls_data_fprint(const unsigned char *buf, int len, const char *mdalg)
    364 {
    365     EVP_MD_CTX *mdctx = NULL;
    366     unsigned char md_buf[EVP_MAX_MD_SIZE];
    367     unsigned int md_len;
    368     int     ok = 1;
    369 
    370     /* Previously available in "init" routine. */
    371     if (tls_digest_byname(mdalg, &mdctx) == 0)
    372 	msg_panic("digest algorithm \"%s\" not found", mdalg);
    373 
    374     CHECK_OK_AND_DIGEST_DATA(mdctx, buf, len);
    375     CHECK_OK_AND(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
    376     EVP_MD_CTX_destroy(mdctx);
    377     if (!ok)
    378 	msg_fatal("error computing %s message digest", mdalg);
    379 
    380     return (tls_digest_encode(md_buf, md_len));
    381 }
    382 
    383 /* tls_cert_fprint - extract certificate fingerprint */
    384 
    385 char   *tls_cert_fprint(X509 *peercert, const char *mdalg)
    386 {
    387     int     len;
    388     unsigned char *buf;
    389     unsigned char *buf2;
    390     char   *result;
    391 
    392     len = i2d_X509(peercert, NULL);
    393     buf2 = buf = mymalloc(len);
    394     i2d_X509(peercert, &buf2);
    395     if (buf2 - buf != len)
    396 	msg_panic("i2d_X509 invalid result length");
    397 
    398     result = tls_data_fprint(buf, len, mdalg);
    399     myfree(buf);
    400 
    401     return (result);
    402 }
    403 
    404 /* tls_pkey_fprint - extract public key fingerprint */
    405 
    406 char   *tls_pkey_fprint(EVP_PKEY *peerpkey, const char *mdalg)
    407 {
    408     int     len;
    409     unsigned char *buf;
    410     unsigned char *buf2;
    411     char   *result;
    412 
    413     len = i2d_PUBKEY(peerpkey, NULL);
    414     buf2 = buf = mymalloc(len);
    415     i2d_PUBKEY(peerpkey, &buf2);
    416     if (buf2 - buf != len)
    417 	msg_panic("i2d_PUBKEY invalid result length");
    418 
    419     result = tls_data_fprint(buf, len, mdalg);
    420     myfree(buf);
    421     return (result);
    422 }
    423 
    424 #endif
    425