0
0
mirror of https://github.com/OpenVPN/openvpn.git synced 2024-09-20 12:02:28 +02:00

Refactor key_state_export_keying_material functions

This refactors the common code between mbed SSL and OpenSSL into
export_user_keying_material and also prepares the backend functions
to export more than one key.

Also fix checking the return value of SSL_export_keying_material
only 1 is a success, -1 is also an error.

Signed-off-by: Arne Schwabe <arne@rfc2549.org>

Patch V2: Cache secrets for mbed TLS instead generating all ekms
          in the call back function

Patch V3: comment is no longer a lie. (fixed doxygen)

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Steffan Karger <steffan@karger.me>
Message-Id: <20200814145153.12895-1-arne@rfc2549.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg20739.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
Arne Schwabe 2020-08-14 16:51:53 +02:00 committed by Gert Doering
parent 62560e2a29
commit 10abd656a3
5 changed files with 114 additions and 70 deletions

View File

@ -2412,6 +2412,40 @@ error:
return false; return false;
} }
static void
export_user_keying_material(struct key_state_ssl *ssl,
struct tls_session *session)
{
if (session->opt->ekm_size > 0)
{
unsigned int size = session->opt->ekm_size;
struct gc_arena gc = gc_new();
unsigned char *ekm;
if ((ekm = key_state_export_keying_material(session,
session->opt->ekm_label,
session->opt->ekm_label_size,
session->opt->ekm_size,
&gc)))
{
unsigned int len = (size * 2) + 2;
const char *key = format_hex_ex(ekm, size, len, 0, NULL, &gc);
setenv_str(session->opt->es, "exported_keying_material", key);
dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s",
__func__, key);
secure_memzero(ekm, size);
}
else
{
msg(M_WARN, "WARNING: Export keying material failed!");
setenv_del(session->opt->es, "exported_keying_material");
}
gc_free(&gc);
}
}
/** /**
* Handle reading key data, peer-info, username/password, OCC * Handle reading key data, peer-info, username/password, OCC
* from the TLS control channel (cleartext). * from the TLS control channel (cleartext).
@ -2541,7 +2575,7 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio
if ((ks->authenticated > KS_AUTH_FALSE) if ((ks->authenticated > KS_AUTH_FALSE)
&& plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL)) && plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL))
{ {
key_state_export_keying_material(&ks->ks_ssl, session); export_user_keying_material(&ks->ks_ssl, session);
if (plugin_call(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS) if (plugin_call(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
{ {

View File

@ -394,13 +394,21 @@ void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
* derived from existing TLS channel. This exported keying material can then be * derived from existing TLS channel. This exported keying material can then be
* used for a variety of purposes. * used for a variety of purposes.
* *
* @param ks_ssl The SSL channel's state info
* @param session The session associated with the given key_state * @param session The session associated with the given key_state
* @param label The label to use when exporting the key
* @param label_size The size of the label to use when exporting the key
* @param ekm_size THe size of the exported/returned key material
* @param gc gc_arena that might be used to allocate the string
* returned
* @returns The exported key material, the caller may zero the
* string but should not free it
*/ */
void unsigned char*
key_state_export_keying_material(struct key_state_ssl *ks_ssl, key_state_export_keying_material(struct tls_session *session,
struct tls_session *session) __attribute__((nonnull)); const char* label, size_t label_size,
size_t ekm_size,
struct gc_arena *gc) __attribute__((nonnull));
/**************************************************************************/ /**************************************************************************/
/** @addtogroup control_tls /** @addtogroup control_tls

View File

@ -206,52 +206,55 @@ mbedtls_ssl_export_keys_cb(void *p_expkey, const unsigned char *ms,
{ {
struct tls_session *session = p_expkey; struct tls_session *session = p_expkey;
struct key_state_ssl *ks_ssl = &session->key[KS_PRIMARY].ks_ssl; struct key_state_ssl *ks_ssl = &session->key[KS_PRIMARY].ks_ssl;
unsigned char client_server_random[64]; struct tls_key_cache *cache = &ks_ssl->tls_key_cache;
ks_ssl->exported_key_material = gc_malloc(session->opt->ekm_size, static_assert(sizeof(ks_ssl->ctx->session->master)
true, NULL); == sizeof(cache->master_secret), "master size mismatch");
memcpy(client_server_random, client_random, 32); memcpy(cache->client_server_random, client_random, 32);
memcpy(client_server_random + 32, server_random, 32); memcpy(cache->client_server_random + 32, server_random, 32);
memcpy(cache->master_secret, ms, sizeof(cache->master_secret));
cache->tls_prf_type = tls_prf_type;
const size_t ms_len = sizeof(ks_ssl->ctx->session->master); return true;
int ret = mbedtls_ssl_tls_prf(tls_prf_type, ms, ms_len, }
session->opt->ekm_label, client_server_random,
sizeof(client_server_random), ks_ssl->exported_key_material,
session->opt->ekm_size);
if (!mbed_ok(ret)) unsigned char *
key_state_export_keying_material(struct tls_session *session,
const char* label, size_t label_size,
size_t ekm_size,
struct gc_arena *gc)
{
ASSERT(strlen(label) == label_size);
struct tls_key_cache *cache = &session->key[KS_PRIMARY].ks_ssl.tls_key_cache;
/* If the type is NONE, we either have no cached secrets or
* there is no PRF, in both cases we cannot generate key material */
if (cache->tls_prf_type == MBEDTLS_SSL_TLS_PRF_NONE)
{ {
secure_memzero(ks_ssl->exported_key_material, session->opt->ekm_size); return NULL;
} }
secure_memzero(client_server_random, sizeof(client_server_random)); unsigned char *ekm = (unsigned char *) gc_malloc(ekm_size, true, gc);
int ret = mbedtls_ssl_tls_prf(cache->tls_prf_type, cache->master_secret,
sizeof(cache->master_secret),
label, cache->client_server_random,
sizeof(cache->client_server_random),
ekm, ekm_size);
return ret; if (mbed_ok(ret))
{
return ekm;
}
else
{
secure_memzero(ekm, session->opt->ekm_size);
return NULL;
}
} }
#endif /* HAVE_EXPORT_KEYING_MATERIAL */ #endif /* HAVE_EXPORT_KEYING_MATERIAL */
void
key_state_export_keying_material(struct key_state_ssl *ssl,
struct tls_session *session)
{
if (ssl->exported_key_material)
{
unsigned int size = session->opt->ekm_size;
struct gc_arena gc = gc_new();
unsigned int len = (size * 2) + 2;
const char *key = format_hex_ex(ssl->exported_key_material,
size, len, 0, NULL, &gc);
setenv_str(session->opt->es, "exported_keying_material", key);
dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s",
__func__, key);
gc_free(&gc);
}
}
bool bool
tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags) tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags)
{ {
@ -1178,7 +1181,7 @@ key_state_ssl_free(struct key_state_ssl *ks_ssl)
{ {
if (ks_ssl) if (ks_ssl)
{ {
free(ks_ssl->exported_key_material); CLEAR(ks_ssl->tls_key_cache);
if (ks_ssl->ctx) if (ks_ssl->ctx)
{ {

View File

@ -82,6 +82,15 @@ struct external_context {
void *sign_ctx; void *sign_ctx;
}; };
/** struct to cache TLS secrets for keying material exporter (RFC 5705).
* The constants (64 and 48) are inherent to TLS version and
* the whole keying material export will likely change when they change */
struct tls_key_cache {
unsigned char client_server_random[64];
mbedtls_tls_prf_types tls_prf_type;
unsigned char master_secret[48];
};
/** /**
* Structure that wraps the TLS context. Contents differ depending on the * Structure that wraps the TLS context. Contents differ depending on the
* SSL library used. * SSL library used.
@ -114,8 +123,7 @@ struct key_state_ssl {
mbedtls_ssl_context *ctx; /**< mbedTLS connection context */ mbedtls_ssl_context *ctx; /**< mbedTLS connection context */
bio_ctx *bio_ctx; bio_ctx *bio_ctx;
/** Keying material exporter cache (RFC 5705). */ struct tls_key_cache tls_key_cache;
uint8_t *exported_key_material;
}; };

View File

@ -158,35 +158,26 @@ tls_ctx_initialised(struct tls_root_ctx *ctx)
return NULL != ctx->ctx; return NULL != ctx->ctx;
} }
void unsigned char*
key_state_export_keying_material(struct key_state_ssl *ssl, key_state_export_keying_material(struct tls_session *session,
struct tls_session *session) const char* label, size_t label_size,
size_t ekm_size,
struct gc_arena *gc)
{ {
if (session->opt->ekm_size > 0) unsigned char *ekm = (unsigned char *) gc_malloc(ekm_size, true, gc);
SSL* ssl = session->key[KS_PRIMARY].ks_ssl.ssl;
if (SSL_export_keying_material(ssl, ekm, ekm_size, label,
label_size, NULL, 0, 0) == 1)
{ {
unsigned int size = session->opt->ekm_size; return ekm;
struct gc_arena gc = gc_new(); }
unsigned char *ekm = (unsigned char *) gc_malloc(size, true, &gc); else
{
if (SSL_export_keying_material(ssl->ssl, ekm, size, secure_memzero(ekm, ekm_size);
session->opt->ekm_label, return NULL;
session->opt->ekm_label_size,
NULL, 0, 0))
{
unsigned int len = (size * 2) + 2;
const char *key = format_hex_ex(ekm, size, len, 0, NULL, &gc);
setenv_str(session->opt->es, "exported_keying_material", key);
dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s",
__func__, key);
}
else
{
msg(M_WARN, "WARNING: Export keying material failed!");
setenv_del(session->opt->es, "exported_keying_material");
}
gc_free(&gc);
} }
} }