mirror of
https://github.com/OpenVPN/openvpn.git
synced 2025-05-10 22:11:27 +08:00
Implement generating data channel keys via EKM/RFC 5705
OpenVPN currently uses its own (based on TLS 1.0) key derivation mechanism to generate the 256 bytes key data in key2 struct that are then used used to generate encryption/hmac/iv vectors. While this mechanism is still secure, it is not state of the art. Instead of modernising our own approach, this commit implements key derivation using the Keying Material Exporters API introduced by RFC 5705. We also use an opportunistic approach of negotiating the use of EKM (exported key material) through an IV_PROTO flag and prefer EKM to our own PRF if both client and server support it. The use of EKM is pushed to the client as part of NCP as key-derivation tls-ekm. We still exchange the random data (112 bytes from client to server and 64 byte from server to client) for the OpenVPN PRF but do not use it. Removing that exchange would break the handshake and make a key-method 3 or similar necessary. As a side effect, this makes a little bit easier to have a FIPS compatible version of OpenVPN since we do not rely on calling MD5 anymore. Side note: this commit breaks the (not yet merged) WolfSSL support as it claims to support EKM in the OpenSSL compat API but always returns an error if you try to use it. Patch v2: rebase/change to V2 of EKM refactoring Patch v3: add Changes.rst Patch v4: Rebase on master. Patch v5: Refuse internal label to be used with --keying-material-exporter, polishing/fixes suggested by Steffan integrated Signed-off-by: Arne Schwabe <arne@rfc2549.org> Acked-by: Steffan Karger <steffan.karger@foxcrypto.com> Message-Id: <20201009115453.4279-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg21187.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
parent
1e6e083e88
commit
6dc09d0d45
11
Changes.rst
11
Changes.rst
@ -1,3 +1,14 @@
|
|||||||
|
Overview of changes in 2.6
|
||||||
|
==========================
|
||||||
|
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
Keying Material Exporters (RFC 5705) based key generation
|
||||||
|
As part of the cipher negotiation OpenVPN will automatically prefer
|
||||||
|
the RFC5705 based key material generation to the current custom
|
||||||
|
OpenVPN PRF. This feature requires OpenSSL or mbed TLS 2.18+.
|
||||||
|
|
||||||
Overview of changes in 2.5
|
Overview of changes in 2.5
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
@ -58,6 +58,12 @@
|
|||||||
*
|
*
|
||||||
* @subsection key_generation_method_2 Key method 2
|
* @subsection key_generation_method_2 Key method 2
|
||||||
*
|
*
|
||||||
|
* There are two methods for generating key data when using key method 2
|
||||||
|
* the first is OpenVPN's traditional approach that exchanges random
|
||||||
|
* data and uses a PRF and the other is using the RFC5705 keying material
|
||||||
|
* exporter to generate the key material. For both methods the random
|
||||||
|
* data is exchange but only used in the traditional method.
|
||||||
|
*
|
||||||
* -# The client generates random material in the following amounts:
|
* -# The client generates random material in the following amounts:
|
||||||
* - Pre-master secret: 48 bytes
|
* - Pre-master secret: 48 bytes
|
||||||
* - Client's PRF seed for master secret: 32 bytes
|
* - Client's PRF seed for master secret: 32 bytes
|
||||||
@ -73,8 +79,12 @@
|
|||||||
* server's random material.
|
* server's random material.
|
||||||
*
|
*
|
||||||
* %Key method 2 %key expansion is performed by the \c
|
* %Key method 2 %key expansion is performed by the \c
|
||||||
* generate_key_expansion() function. Please refer to its source code for
|
* generate_key_expansion_openvpn_prf() function. Please refer to its source
|
||||||
* details of the %key expansion process.
|
* code for details of the %key expansion process.
|
||||||
|
*
|
||||||
|
* When the client sends the IV_PROTO_TLS_KEY_EXPORT flag and the server replies
|
||||||
|
* with `key-derivation tls-ekm` the RFC5705 key material exporter with the
|
||||||
|
* label EXPORTER-OpenVPN-datakeys is used for the key data.
|
||||||
*
|
*
|
||||||
* @subsection key_generation_random Source of random material
|
* @subsection key_generation_random Source of random material
|
||||||
*
|
*
|
||||||
|
@ -254,6 +254,10 @@ struct crypto_options
|
|||||||
#define CO_MUTE_REPLAY_WARNINGS (1<<2)
|
#define CO_MUTE_REPLAY_WARNINGS (1<<2)
|
||||||
/**< Bit-flag indicating not to display
|
/**< Bit-flag indicating not to display
|
||||||
* replay warnings. */
|
* replay warnings. */
|
||||||
|
#define CO_USE_TLS_KEY_MATERIAL_EXPORT (1<<3)
|
||||||
|
/**< Bit-flag indicating that data channel key derivation
|
||||||
|
* is done using TLS keying material export [RFC5705]
|
||||||
|
*/
|
||||||
unsigned int flags; /**< Bit-flags determining behavior of
|
unsigned int flags; /**< Bit-flags determining behavior of
|
||||||
* security operation functions. */
|
* security operation functions. */
|
||||||
};
|
};
|
||||||
|
@ -687,6 +687,7 @@ restore_ncp_options(struct context *c)
|
|||||||
c->options.ciphername = c->c1.ciphername;
|
c->options.ciphername = c->c1.ciphername;
|
||||||
c->options.authname = c->c1.authname;
|
c->options.authname = c->c1.authname;
|
||||||
c->options.keysize = c->c1.keysize;
|
c->options.keysize = c->c1.keysize;
|
||||||
|
c->options.data_channel_use_ekm = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1817,6 +1817,10 @@ multi_client_set_protocol_options(struct context *c)
|
|||||||
c->c2.push_request_received = true;
|
c->c2.push_request_received = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_EXPORT_KEYING_MATERIAL
|
||||||
|
o->data_channel_use_ekm = (proto & IV_PROTO_TLS_KEY_EXPORT);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Select cipher if client supports Negotiable Crypto Parameters */
|
/* Select cipher if client supports Negotiable Crypto Parameters */
|
||||||
if (!o->ncp_enabled)
|
if (!o->ncp_enabled)
|
||||||
{
|
{
|
||||||
|
@ -7979,6 +7979,20 @@ add_option(struct options *options,
|
|||||||
}
|
}
|
||||||
options->ncp_ciphers = p[1];
|
options->ncp_ciphers = p[1];
|
||||||
}
|
}
|
||||||
|
else if (streq(p[0], "key-derivation") && p[1])
|
||||||
|
{
|
||||||
|
VERIFY_PERMISSION(OPT_P_NCP)
|
||||||
|
#ifdef HAVE_EXPORT_KEYING_MATERIAL
|
||||||
|
if (streq(p[1], "tls-ekm"))
|
||||||
|
{
|
||||||
|
options->data_channel_use_ekm = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
msg(msglevel, "Unknown key-derivation method %s", p[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (streq(p[0], "ncp-disable") && !p[1])
|
else if (streq(p[0], "ncp-disable") && !p[1])
|
||||||
{
|
{
|
||||||
VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INSTANCE);
|
VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INSTANCE);
|
||||||
@ -8707,6 +8721,11 @@ add_option(struct options *options,
|
|||||||
"\"EXPORTER\"");
|
"\"EXPORTER\"");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
if (streq(p[1], EXPORT_KEY_DATA_LABEL))
|
||||||
|
{
|
||||||
|
msg(msglevel, "Keying material exporter label must not be '"
|
||||||
|
EXPORT_KEY_DATA_LABEL "'.");
|
||||||
|
}
|
||||||
if (ekm_length < 16 || ekm_length > 4095)
|
if (ekm_length < 16 || ekm_length > 4095)
|
||||||
{
|
{
|
||||||
msg(msglevel, "Invalid keying material exporter length");
|
msg(msglevel, "Invalid keying material exporter length");
|
||||||
|
@ -648,6 +648,9 @@ struct options
|
|||||||
/* Useful when packets sent by openvpn itself are not subject
|
/* Useful when packets sent by openvpn itself are not subject
|
||||||
* to the routing tables that would move packets into the tunnel. */
|
* to the routing tables that would move packets into the tunnel. */
|
||||||
bool allow_recursive_routing;
|
bool allow_recursive_routing;
|
||||||
|
|
||||||
|
/* Use RFC5705 key export to generate data channel keys */
|
||||||
|
bool data_channel_use_ekm;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define streq(x, y) (!strcmp((x), (y)))
|
#define streq(x, y) (!strcmp((x), (y)))
|
||||||
|
@ -479,7 +479,10 @@ prepare_push_reply(struct context *c, struct gc_arena *gc,
|
|||||||
{
|
{
|
||||||
push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
|
push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
|
||||||
}
|
}
|
||||||
|
if (o->data_channel_use_ekm)
|
||||||
|
{
|
||||||
|
push_option_fmt(gc, push_list, M_USAGE, "key-derivation tls-ekm");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1783,6 +1783,29 @@ init_key_contexts(struct key_ctx_bi *key,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
generate_key_expansion_tls_export(struct tls_session *session, struct key2 *key2)
|
||||||
|
{
|
||||||
|
struct gc_arena gc = gc_new();
|
||||||
|
unsigned char *key2data;
|
||||||
|
|
||||||
|
key2data = key_state_export_keying_material(session,
|
||||||
|
EXPORT_KEY_DATA_LABEL,
|
||||||
|
strlen(EXPORT_KEY_DATA_LABEL),
|
||||||
|
sizeof(key2->keys),
|
||||||
|
&gc);
|
||||||
|
if (!key2data)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(key2->keys, key2data, sizeof(key2->keys));
|
||||||
|
secure_memzero(key2data, sizeof(key2->keys));
|
||||||
|
key2->n = 2;
|
||||||
|
|
||||||
|
gc_free(&gc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static struct key2
|
static struct key2
|
||||||
generate_key_expansion_openvpn_prf(const struct tls_session *session)
|
generate_key_expansion_openvpn_prf(const struct tls_session *session)
|
||||||
{
|
{
|
||||||
@ -1854,7 +1877,7 @@ generate_key_expansion_openvpn_prf(const struct tls_session *session)
|
|||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
generate_key_expansion(struct key_ctx_bi *key,
|
generate_key_expansion(struct key_ctx_bi *key,
|
||||||
const struct tls_session *session)
|
struct tls_session *session)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
struct key2 key2;
|
struct key2 key2;
|
||||||
@ -1865,10 +1888,20 @@ generate_key_expansion(struct key_ctx_bi *key,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool server = session->opt->server;
|
bool server = session->opt->server;
|
||||||
|
|
||||||
key2 = generate_key_expansion_openvpn_prf(session);
|
if (session->opt->crypto_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT)
|
||||||
|
{
|
||||||
|
if (!generate_key_expansion_tls_export(session, &key2))
|
||||||
|
{
|
||||||
|
msg(D_TLS_ERRORS, "TLS Error: Keying material export failed");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
key2 = generate_key_expansion_openvpn_prf(session);
|
||||||
|
}
|
||||||
|
|
||||||
key2_print(&key2, &session->opt->key_type,
|
key2_print(&key2, &session->opt->key_type,
|
||||||
"Master Encrypt", "Master Decrypt");
|
"Master Encrypt", "Master Decrypt");
|
||||||
@ -1967,6 +2000,11 @@ tls_session_update_crypto_params(struct tls_session *session,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options->data_channel_use_ekm)
|
||||||
|
{
|
||||||
|
session->opt->crypto_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(options->ciphername, session->opt->config_ciphername))
|
if (strcmp(options->ciphername, session->opt->config_ciphername))
|
||||||
{
|
{
|
||||||
msg(D_HANDSHAKE, "Data Channel: using negotiated cipher '%s'",
|
msg(D_HANDSHAKE, "Data Channel: using negotiated cipher '%s'",
|
||||||
@ -2251,13 +2289,11 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
|
|||||||
* push request, also signal that the client wants
|
* push request, also signal that the client wants
|
||||||
* to get push-reply messages without without requiring a round
|
* to get push-reply messages without without requiring a round
|
||||||
* trip for a push request message*/
|
* trip for a push request message*/
|
||||||
if(session->opt->pull)
|
if (session->opt->pull)
|
||||||
{
|
{
|
||||||
iv_proto |= IV_PROTO_REQUEST_PUSH;
|
iv_proto |= IV_PROTO_REQUEST_PUSH;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf_printf(&out, "IV_PROTO=%d\n", iv_proto);
|
|
||||||
|
|
||||||
/* support for Negotiable Crypto Parameters */
|
/* support for Negotiable Crypto Parameters */
|
||||||
if (session->opt->ncp_enabled
|
if (session->opt->ncp_enabled
|
||||||
&& (session->opt->mode == MODE_SERVER || session->opt->pull))
|
&& (session->opt->mode == MODE_SERVER || session->opt->pull))
|
||||||
@ -2269,8 +2305,14 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
|
|||||||
buf_printf(&out, "IV_NCP=2\n");
|
buf_printf(&out, "IV_NCP=2\n");
|
||||||
}
|
}
|
||||||
buf_printf(&out, "IV_CIPHERS=%s\n", session->opt->config_ncp_ciphers);
|
buf_printf(&out, "IV_CIPHERS=%s\n", session->opt->config_ncp_ciphers);
|
||||||
|
|
||||||
|
#ifdef HAVE_EXPORT_KEYING_MATERIAL
|
||||||
|
iv_proto |= IV_PROTO_TLS_KEY_EXPORT;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf_printf(&out, "IV_PROTO=%d\n", iv_proto);
|
||||||
|
|
||||||
/* push compression status */
|
/* push compression status */
|
||||||
#ifdef USE_COMP
|
#ifdef USE_COMP
|
||||||
comp_generate_peer_info_string(&session->opt->comp_options, &out);
|
comp_generate_peer_info_string(&session->opt->comp_options, &out);
|
||||||
|
@ -116,6 +116,8 @@
|
|||||||
* to wait for a push-request to send a push-reply */
|
* to wait for a push-request to send a push-reply */
|
||||||
#define IV_PROTO_REQUEST_PUSH (1<<2)
|
#define IV_PROTO_REQUEST_PUSH (1<<2)
|
||||||
|
|
||||||
|
/** Supports key derivation via TLS key material exporter [RFC5705] */
|
||||||
|
#define IV_PROTO_TLS_KEY_EXPORT (1<<3)
|
||||||
|
|
||||||
/* Default field in X509 to be username */
|
/* Default field in X509 to be username */
|
||||||
#define X509_USERNAME_FIELD_DEFAULT "CN"
|
#define X509_USERNAME_FIELD_DEFAULT "CN"
|
||||||
|
@ -389,6 +389,7 @@ void key_state_ssl_free(struct key_state_ssl *ks_ssl);
|
|||||||
void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
|
void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
|
||||||
const char *crl_file, bool crl_inline);
|
const char *crl_file, bool crl_inline);
|
||||||
|
|
||||||
|
#define EXPORT_KEY_DATA_LABEL "EXPORTER-OpenVPN-datakeys"
|
||||||
/**
|
/**
|
||||||
* Keying Material Exporters [RFC 5705] allows additional keying material to be
|
* Keying Material Exporters [RFC 5705] allows additional keying material to be
|
||||||
* derived from existing TLS channel. This exported keying material can then be
|
* derived from existing TLS channel. This exported keying material can then be
|
||||||
|
@ -1168,11 +1168,8 @@ key_state_ssl_init(struct key_state_ssl *ks_ssl,
|
|||||||
|
|
||||||
#ifdef HAVE_EXPORT_KEYING_MATERIAL
|
#ifdef HAVE_EXPORT_KEYING_MATERIAL
|
||||||
/* Initialize keying material exporter */
|
/* Initialize keying material exporter */
|
||||||
if (session->opt->ekm_size)
|
mbedtls_ssl_conf_export_keys_ext_cb(ks_ssl->ssl_config,
|
||||||
{
|
mbedtls_ssl_export_keys_cb, session);
|
||||||
mbedtls_ssl_conf_export_keys_ext_cb(ks_ssl->ssl_config,
|
|
||||||
mbedtls_ssl_export_keys_cb, session);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Initialise SSL context */
|
/* Initialise SSL context */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user