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