TLS: Split tls_connection_prf() into two functions

Most protocols extracting keys from TLS use RFC 5705 exporters which is
commonly implemented in TLS libraries. This is the mechanism used by
EAP-TLS. (EAP-TLS actually predates RFC 5705, but RFC 5705 was defined
to be compatible with it.)

EAP-FAST, however, uses a legacy mechanism. It reuses the TLS internal
key block derivation and derives key material after the key block. This
is uncommon and a misuse of TLS internals, so not all TLS libraries
support this. Instead, we reimplement the PRF for the OpenSSL backend
and don't support it at all in the GnuTLS one.

Since these two are very different operations, split
tls_connection_prf() in two. tls_connection_export_key() implements the
standard RFC 5705 mechanism that we expect most TLS libraries to
support. tls_connection_get_eap_fast_key() implements the
EAP-FAST-specific legacy mechanism which may not be implemented on all
backends but is only used by EAP-FAST.

Signed-Off-By: David Benjamin <davidben@google.com>
This commit is contained in:
David Benjamin 2016-05-16 11:47:37 -04:00 committed by Jouni Malinen
parent f150db6c83
commit 7358170787
11 changed files with 98 additions and 85 deletions

View file

@ -3146,9 +3146,19 @@ static int openssl_get_keyblock_size(SSL *ssl)
#endif /* CONFIG_FIPS */
static int openssl_tls_prf(struct tls_connection *conn,
const char *label, int server_random_first,
int skip_keyblock, u8 *out, size_t out_len)
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
const char *label, u8 *out, size_t out_len)
{
if (!conn ||
SSL_export_keying_material(conn->ssl, out, out_len, label,
os_strlen(label), NULL, 0, 0) != 1)
return -1;
return 0;
}
int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
u8 *out, size_t out_len)
{
#ifdef CONFIG_FIPS
wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
@ -3169,9 +3179,9 @@ static int openssl_tls_prf(struct tls_connection *conn,
const char *ver;
/*
* TLS library did not support key generation, so get the needed TLS
* session parameters and use an internal implementation of TLS PRF to
* derive the key.
* TLS library did not support EAP-FAST key generation, so get the
* needed TLS session parameters and use an internal implementation of
* TLS PRF to derive the key.
*/
if (conn == NULL)
@ -3184,15 +3194,13 @@ static int openssl_tls_prf(struct tls_connection *conn,
if (!ver || !sess)
return -1;
if (skip_keyblock) {
skip = openssl_get_keyblock_size(ssl);
if (skip < 0)
return -1;
tmp_out = os_malloc(skip + out_len);
if (!tmp_out)
return -1;
_out = tmp_out;
}
skip = openssl_get_keyblock_size(ssl);
if (skip < 0)
return -1;
tmp_out = os_malloc(skip + out_len);
if (!tmp_out)
return -1;
_out = tmp_out;
rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
if (!rnd) {
@ -3205,29 +3213,22 @@ static int openssl_tls_prf(struct tls_connection *conn,
master_key_len = SSL_SESSION_get_master_key(sess, master_key,
sizeof(master_key));
if (server_random_first) {
os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
SSL3_RANDOM_SIZE);
} else {
os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
SSL3_RANDOM_SIZE);
}
os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE);
if (os_strcmp(ver, "TLSv1.2") == 0) {
tls_prf_sha256(master_key, master_key_len,
label, rnd, 2 * SSL3_RANDOM_SIZE,
"key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
_out, skip + out_len);
ret = 0;
} else if (tls_prf_sha1_md5(master_key, master_key_len,
label, rnd, 2 * SSL3_RANDOM_SIZE,
"key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
_out, skip + out_len) == 0) {
ret = 0;
}
os_memset(master_key, 0, sizeof(master_key));
os_free(rnd);
if (ret == 0 && skip_keyblock)
if (ret == 0)
os_memcpy(out, _out + skip, out_len);
bin_clear_free(tmp_out, skip);
@ -3236,26 +3237,6 @@ static int openssl_tls_prf(struct tls_connection *conn,
}
int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
const char *label, int server_random_first,
int skip_keyblock, u8 *out, size_t out_len)
{
if (conn == NULL)
return -1;
if (server_random_first || skip_keyblock)
return openssl_tls_prf(conn, label,
server_random_first, skip_keyblock,
out, out_len);
if (SSL_export_keying_material(conn->ssl, out, out_len, label,
os_strlen(label), NULL, 0, 0) == 1) {
wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
return 0;
}
return openssl_tls_prf(conn, label, server_random_first,
skip_keyblock, out, out_len);
}
static struct wpabuf *
openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
int server)