TLS: really delay key lookup until it is necessary

Even if the certificate has a RSA public key, be sure to lookup the key
only if it is an actual RSA key exchange. Move the hashtable to the
secrets module to enable reuse.

Change-Id: I39010831079d3b65d5d4368ec97d02491c1615a5
Reviewed-on: https://code.wireshark.org/review/30854
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Peter Wu 2018-12-01 03:40:17 +01:00 committed by Anders Broman
parent 0ceead5335
commit 97dbdc3ac9
6 changed files with 76 additions and 60 deletions

View File

@ -46,6 +46,7 @@
#include <epan/exported_pdu.h>
#include <epan/decode_as.h>
#include <epan/proto_data.h>
#include <epan/secrets.h> /* for privkey_hash_table_new */
#include <wsutil/str_util.h>
#include <wsutil/strtoi.h>
#include <wsutil/utf8_entities.h>
@ -247,8 +248,7 @@ dtls_parse_uat(void)
}
/* parse private keys string, load available keys and put them in key hash*/
dtls_key_hash = g_hash_table_new_full(tls_private_key_hash,
tls_private_key_equal, g_free, tls_private_key_free);
dtls_key_hash = privkey_hash_table_new();
ssl_set_debug(dtls_debug_file_name);

View File

@ -30,6 +30,7 @@
#include <epan/asn1.h>
#include <epan/proto_data.h>
#include <epan/oids.h>
#include <epan/secrets.h>
#include <wsutil/filesystem.h>
#include <wsutil/file_util.h>
@ -3033,7 +3034,7 @@ end:
static int
ssl_decrypt_pre_master_secret(SslDecryptSession *ssl_session,
StringInfo *encrypted_pre_master,
gnutls_privkey_t pk);
GHashTable *key_hash);
#endif /* HAVE_LIBGNUTLS */
static gboolean
@ -3163,12 +3164,8 @@ ssl_generate_pre_master_secret(SslDecryptSession *ssl_session,
#ifdef HAVE_LIBGNUTLS
/* Try to lookup an appropriate RSA private key to decrypt the Encrypted Pre-Master Secret. */
gnutls_privkey_t pk = NULL;
if (ssl_session->cert_key_id) {
pk = (gnutls_privkey_t)g_hash_table_lookup(key_hash, ssl_session->cert_key_id);
}
if (pk) {
if (ssl_decrypt_pre_master_secret(ssl_session, &encrypted_pre_master, pk))
if (ssl_decrypt_pre_master_secret(ssl_session, &encrypted_pre_master, key_hash))
return TRUE;
ssl_debug_printf("%s: can't decrypt pre-master secret\n",
@ -3599,8 +3596,10 @@ end:
/* Decrypt RSA pre-master secret using RSA private key. {{{ */
static gboolean
ssl_decrypt_pre_master_secret(SslDecryptSession *ssl_session,
StringInfo *encrypted_pre_master, gnutls_privkey_t pk)
StringInfo *encrypted_pre_master, GHashTable *key_hash)
{
int ret;
if (!encrypted_pre_master)
return FALSE;
@ -3618,12 +3617,18 @@ ssl_decrypt_pre_master_secret(SslDecryptSession *ssl_session,
return FALSE;
}
gnutls_privkey_t pk = (gnutls_privkey_t)g_hash_table_lookup(key_hash, ssl_session->cert_key_id);
ssl_print_string("pre master encrypted", encrypted_pre_master);
ssl_debug_printf("%s: RSA_private_decrypt\n", G_STRFUNC);
const gnutls_datum_t epms = { encrypted_pre_master->data, encrypted_pre_master->data_len };
gnutls_datum_t pms = { 0 };
// uses mechanism CKM_RSA_PKCS (corresponds to RSAES-PKCS1-v1_5)
int ret = gnutls_privkey_decrypt_data(pk, 0, &epms, &pms);
if (pk) {
// Try to decrypt using the RSA keys table from (D)TLS preferences.
ret = gnutls_privkey_decrypt_data(pk, 0, &epms, &pms);
} else {
ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
}
if (ret < 0) {
ssl_debug_printf("%s: decryption failed: %d (%s)\n", G_STRFUNC, ret, gnutls_strerror(ret));
return FALSE;
@ -4267,7 +4272,7 @@ ssl_find_private_key_by_pubkey(SslDecryptSession *ssl,
gnutls_datum_t *subjectPublicKeyInfo)
{
gnutls_pubkey_t pubkey = NULL;
guchar key_id[20];
cert_key_id_t key_id;
size_t key_id_len = sizeof(key_id);
int r;
@ -4296,7 +4301,7 @@ ssl_find_private_key_by_pubkey(SslDecryptSession *ssl,
}
/* Generate a 20-byte SHA-1 hash. */
r = gnutls_pubkey_get_key_id(pubkey, 0, key_id, &key_id_len);
r = gnutls_pubkey_get_key_id(pubkey, 0, key_id.key_id, &key_id_len);
if (r < 0) {
ssl_debug_printf("%s: failed to extract key id from pubkey: %s\n",
G_STRFUNC, gnutls_strerror(r));
@ -4309,8 +4314,9 @@ ssl_find_private_key_by_pubkey(SslDecryptSession *ssl,
goto end;
}
ssl_print_data("Certificate.KeyID", key_id, key_id_len);
ssl->cert_key_id = (guint8 *)wmem_memdup(wmem_file_scope(), key_id, key_id_len);
ssl_print_data("Certificate.KeyID", key_id.key_id, key_id_len);
ssl->cert_key_id = wmem_new(wmem_file_scope(), cert_key_id_t);
*ssl->cert_key_id = key_id;
end:
gnutls_pubkey_deinit(pubkey);
@ -4510,35 +4516,6 @@ ssl_hash (gconstpointer v)
return hash;
}
#ifdef HAVE_LIBGNUTLS
gboolean
tls_private_key_equal (gconstpointer v, gconstpointer v2)
{
/* key ID length (SHA-1 hash, per GNUTLS_KEYID_USE_SHA1) */
return !memcmp(v, v2, 20);
}
guint
tls_private_key_hash (gconstpointer v)
{
guint l, hash = 0;
const guint8 *cur = (const guint8 *)v;
/* The public key' SHA-1 hash (which maps to a private key) has a uniform
* distribution, hence simply xor'ing them should be sufficient. */
for (l = 0; l < 20; l += 4, cur += 4)
hash ^= pntoh32(cur);
return hash;
}
void
tls_private_key_free(gpointer data)
{
gnutls_privkey_t pkey = (gnutls_privkey_t)data;
gnutls_privkey_deinit(pkey);
}
#endif
/* Functions for TLS/DTLS sessions and RSA private keys hashtables. }}} */
/* Handling of association between tls/dtls ports and clear text protocol. {{{ */

View File

@ -408,6 +408,8 @@ typedef struct _SslSession {
/* RFC 5246, section 8.1 says that the master secret is always 48 bytes */
#define SSL_MASTER_SECRET_LENGTH 48
struct cert_key_id; /* defined in epan/secrets.h */
/* This holds state information for a SSL conversation */
typedef struct _SslDecryptSession {
guchar _master_secret[SSL_MASTER_SECRET_LENGTH];
@ -434,7 +436,7 @@ typedef struct _SslDecryptSession {
SslDecoder *server_new;
SslDecoder *client_new;
#if defined(HAVE_LIBGNUTLS)
guint8 *cert_key_id; /**< SHA-1 Key ID of public key in certificate. */
struct cert_key_id *cert_key_id; /**< SHA-1 Key ID of public key in certificate. */
#endif
StringInfo psk;
StringInfo app_data_segment;
@ -611,19 +613,6 @@ tls13_cipher_create(const char *label_prefix, int cipher_algo, int cipher_mode,
/* Common part between TLS and DTLS dissectors */
/* Hash Functions for RSA private keys table */
#ifdef HAVE_LIBGNUTLS
extern gboolean
tls_private_key_equal (gconstpointer v, gconstpointer v2);
extern guint
tls_private_key_hash (gconstpointer v);
extern void
tls_private_key_free(gpointer key);
#endif /* HAVE_LIBGNUTLS */
/* handling of association between tls/dtls ports and clear text protocol */
extern void

View File

@ -336,8 +336,7 @@ ssl_parse_uat(void)
}
}
/* parse private keys string, load available keys and put them in key hash*/
ssl_key_hash = g_hash_table_new_full(tls_private_key_hash,
tls_private_key_equal, g_free, tls_private_key_free);
ssl_key_hash = privkey_hash_table_new();
if (ntlsdecrypt > 0) {

View File

@ -9,9 +9,17 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include "secrets.h"
#include <wiretap/wtap.h>
#include <string.h>
#ifdef HAVE_LIBGNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/abstract.h>
#endif /* HAVE_LIBGNUTLS */
/** Maps guint32 secrets_type -> secrets_block_callback_t. */
static GHashTable *secrets_callbacks;
@ -44,6 +52,34 @@ secrets_wtap_callback(guint32 secrets_type, const void *secrets, guint size)
}
}
#ifdef HAVE_LIBGNUTLS
static guint
key_id_hash(gconstpointer key)
{
const cert_key_id_t *key_id = (const cert_key_id_t *)key;
const guint32 *dw = (const guint32 *)key_id->key_id;
/* The public key' SHA-1 hash (which maps to a private key) has a uniform
* distribution, hence simply xor'ing them should be sufficient. */
return dw[0] ^ dw[1] ^ dw[2] ^ dw[3] ^ dw[4];
}
static gboolean
key_id_equal(gconstpointer a, gconstpointer b)
{
const cert_key_id_t *key_id_a = (const cert_key_id_t *)a;
const cert_key_id_t *key_id_b = (const cert_key_id_t *)b;
return !memcmp(key_id_a, key_id_b, sizeof(*key_id_a));
}
GHashTable *
privkey_hash_table_new(void)
{
return g_hash_table_new_full(key_id_hash, key_id_equal, g_free, (GDestroyNotify)gnutls_privkey_deinit);
}
#endif /* HAVE_LIBGNUTLS */
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*

View File

@ -46,6 +46,15 @@ enum secrets_scope {
};
#endif
#ifdef HAVE_LIBGNUTLS
/** Identifier for a RSA public key (a SHA-1 hash). */
struct cert_key_id {
guint8 key_id[20];
};
typedef struct cert_key_id cert_key_id_t;
#endif /* HAVE_LIBGNUTLS */
/**
* Callback for the wiretap secrets provider (wtap_new_secrets_callback_t).
*/
@ -65,4 +74,10 @@ typedef void (*secrets_block_callback_t)(const void *secrets, guint size);
* @param cb Callback to be invoked for new secrets.
*/
void secrets_register_type(guint32 secrets_type, secrets_block_callback_t cb);
#ifdef HAVE_LIBGNUTLS
/** Returns a new hash table, mapping cert_key_id_t -> gnutls_privkey_t. */
GHashTable *privkey_hash_table_new(void);
#endif /* HAVE_LIBGNUTLS */
#endif /* __SECRETS_H__ */