Re: [Openvpn-devel] [PATCH v2 1/3] Refactor key_state_export_keying_material functions

2020-08-12 Thread Steffan Karger
Hi,

On 12-08-2020 16:01, Arne Schwabe wrote:
> 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 sucess, -1 is also an error.
> 
> Signed-off-by: Arne Schwabe 
> 
> Patch V2: Cache secrets for mbed TLS instead generating all ekms
>   in the call back function
> 
> Signed-off-by: Arne Schwabe 
> ---
>
>  [...]
> 
> --- a/src/openvpn/ssl_backend.h
> +++ b/src/openvpn/ssl_backend.h
> @@ -394,13 +394,23 @@ void backend_tls_ctx_reload_crl(struct tls_root_ctx 
> *ssl_ctx,
>   * derived from existing TLS channel. This exported keying material can then 
> be
>   * used for a variety of purposes.
>   *
> + *

This extra newline seems unneeded.

>   * @param ks_ssl   The SSL channel's state info
>   * @param session  The session associated with the given key_state
> + * @param labelThe label to use when exporting the key
> + * @param label_size   The size of the label to use when exporting the key
> + *
> + * @param gc   gc_arena that might be used to allocate the string
> + * returned
> + * @returnsThe exported key material, the caller may zero the
> + * string but should not free it
>   */
>  
> -void
> -key_state_export_keying_material(struct key_state_ssl *ks_ssl,
> - struct tls_session *session) 
> __attribute__((nonnull));
> +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) 
> __attribute__((nonnull));

(Quoting a former colleague:) comment is a lie! It does not match the
actual function prototype.

All the code itself looks good to me. I *think* it could be made
slightly better even, but these changes make sense for the intended
goal, and look correct.

Tested both openssl and mbedtls builds (with -fsanitize=address), and
verified that this indeed produces the same exported values for both
builds and a build without this patch.

-Steffan


___
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel


Re: [Openvpn-devel] [PATCH v2 3/3] Implement generating data channel keys via EKM/RFC 5705

2020-08-12 Thread tincanteksup

typos/grammar

On 12/08/2020 15:01, Arne Schwabe wrote:

OpenVPN currently uses its own (based on TLS 1.0) key derivation
mechansim to generate the 256 bytes key data in key2 struct that


mechansim -> mechanism



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 modernisating our own approach, this commit implements


modernisating - > modernising



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) that for the OpenVPN PRF but


client) that for - unnecessary 'that'


do not use it. Removing that exchange would break the handshake
and make a key-method 3 or similar necessary.

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.

Signed-off-by: Arne Schwabe 

Patch V2: rebase/change to V2 of EKM refactoring
---
  Changes.rst  | 11 +++
  doc/doxygen/doc_key_generation.h | 15 --
  src/openvpn/crypto.h |  4 +++
  src/openvpn/init.c   |  1 +
  src/openvpn/multi.c  |  4 +++
  src/openvpn/options.c| 14 +
  src/openvpn/options.h|  3 ++
  src/openvpn/push.c   |  5 +++-
  src/openvpn/ssl.c| 50 ++--
  src/openvpn/ssl.h|  2 ++
  src/openvpn/ssl_backend.h|  2 ++
  src/openvpn/ssl_mbedtls.c|  7 ++---
  12 files changed, 107 insertions(+), 11 deletions(-)

diff --git a/Changes.rst b/Changes.rst
index bacc98cd..2c28a399 100644
--- a/Changes.rst
+++ b/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
  ==
  
diff --git a/doc/doxygen/doc_key_generation.h b/doc/doxygen/doc_key_generation.h

index 4bb9c708..bbd6c0c5 100644
--- a/doc/doxygen/doc_key_generation.h
+++ b/doc/doxygen/doc_key_generation.h
@@ -58,6 +58,12 @@
   *
   * @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:
   *- Pre-master secret: 48 bytes
   *- Client's PRF seed for master secret: 32 bytes
@@ -73,8 +79,13 @@
   *server's random material.
   *
   * %Key method 2 %key expansion is performed by the \c
- * generate_key_expansion() function.  Please refer to its source code for
- * details of the %key expansion process.
+ * generate_key_expansion_oepnvpn_prf() function.  Please refer to its source
+ * code for details of the %key expansion process.
+ *
+ * When the client sends the IV_PROTO_TLS_KEY_EXPORT and the server replies
+ * with `key-derivation tls-ekm` RFC5705 key material exporter with the label
+ * EXPORTER-OpenVPN-datakeys is used for the key data.


Grammar: split the sentence by a 'then'.

> + * When the client sends the IV_PROTO_TLS_KEY_EXPORT and the server 
replies
> + * with `key-derivation tls-ekm` then RFC5705 key material exporter 
with the label

> + * EXPORTER-OpenVPN-datakeys is used for the key data.



___
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel


Re: [Openvpn-devel] [PATCH v2 1/3] Refactor key_state_export_keying_material functions

2020-08-12 Thread tincanteksup

typo

On 12/08/2020 15:01, Arne Schwabe wrote:

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 sucess, -1 is also an error.


sucess -> success



___
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel


[Openvpn-devel] [PATCH v2 3/3] Implement generating data channel keys via EKM/RFC 5705

2020-08-12 Thread Arne Schwabe
OpenVPN currently uses its own (based on TLS 1.0) key derivation
mechansim 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 modernisating 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) that 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.

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.

Signed-off-by: Arne Schwabe 

Patch V2: rebase/change to V2 of EKM refactoring
---
 Changes.rst  | 11 +++
 doc/doxygen/doc_key_generation.h | 15 --
 src/openvpn/crypto.h |  4 +++
 src/openvpn/init.c   |  1 +
 src/openvpn/multi.c  |  4 +++
 src/openvpn/options.c| 14 +
 src/openvpn/options.h|  3 ++
 src/openvpn/push.c   |  5 +++-
 src/openvpn/ssl.c| 50 ++--
 src/openvpn/ssl.h|  2 ++
 src/openvpn/ssl_backend.h|  2 ++
 src/openvpn/ssl_mbedtls.c|  7 ++---
 12 files changed, 107 insertions(+), 11 deletions(-)

diff --git a/Changes.rst b/Changes.rst
index bacc98cd..2c28a399 100644
--- a/Changes.rst
+++ b/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
 ==
 
diff --git a/doc/doxygen/doc_key_generation.h b/doc/doxygen/doc_key_generation.h
index 4bb9c708..bbd6c0c5 100644
--- a/doc/doxygen/doc_key_generation.h
+++ b/doc/doxygen/doc_key_generation.h
@@ -58,6 +58,12 @@
  *
  * @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:
  *- Pre-master secret: 48 bytes
  *- Client's PRF seed for master secret: 32 bytes
@@ -73,8 +79,13 @@
  *server's random material.
  *
  * %Key method 2 %key expansion is performed by the \c
- * generate_key_expansion() function.  Please refer to its source code for
- * details of the %key expansion process.
+ * generate_key_expansion_oepnvpn_prf() function.  Please refer to its source
+ * code for details of the %key expansion process.
+ *
+ * When the client sends the IV_PROTO_TLS_KEY_EXPORT and the server replies
+ * with `key-derivation tls-ekm` RFC5705 key material exporter with the label
+ * EXPORTER-OpenVPN-datakeys is used for the key data.
+ *
  *
  * @subsection key_generation_random Source of random material
  *
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 999f643e..ec935ca5 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -254,6 +254,10 @@ struct crypto_options
 #define CO_MUTE_REPLAY_WARNINGS (1<<2)
 /**< Bit-flag indicating not to display
  *   replay warnings. */
+#define CO_USE_TLS_KEY_MATERIAL_EXPORT  (1<<3)
+/**< Bit-flag indicating that key derivation
+ * is done using TLS keying material export [RFC5705]
+ */
 unsigned int flags; /**< Bit-flags determining behavior of
  *   security operation functions. */
 };
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index dfa045b0..34a7313e 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -676,6 +676,7 @@ restore_ncp_options(struct context *c)
 c->options.ciphername = c->c1.ciphername;
 c->options.authname = c->c1.authname;
 c->options.keysize = c->c1.keysize;
+c->options.data_channel_use_ekm = false;
 }
 
 void
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 13738180..a5862020 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1817,6 +1817,10 @@ multi_client_set_protocol_options(struct context *c)
 c->c2.push_requ

[Openvpn-devel] [PATCH v2 2/3] Move openvpn specific key expansion into its own function

2020-08-12 Thread Arne Schwabe
This moves the OpenVPN specific PRF into its own function also
simplifies the code a bit by passing tls_session directly instead of
5 of its fields.

Signed-off-by: Arne Schwabe 

Patch V2: Rebase
---
 src/openvpn/ssl.c | 109 +-
 1 file changed, 69 insertions(+), 40 deletions(-)

diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 3fcaa25f..06cc4c0b 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -1765,27 +1765,38 @@ openvpn_PRF(const uint8_t *secret,
 VALGRIND_MAKE_READABLE((void *)output, output_len);
 }
 
-/*
- * Using source entropy from local and remote hosts, mix into
- * master key.
- */
-static bool
-generate_key_expansion(struct key_ctx_bi *key,
-   const struct key_type *key_type,
-   const struct key_source2 *key_src,
-   const struct session_id *client_sid,
-   const struct session_id *server_sid,
-   bool server)
+static void
+init_key_contexts(struct key_ctx_bi *key,
+  const struct key_type *key_type,
+  bool server,
+  struct key2 *key2)
+{
+/* Initialize OpenSSL key contexts */
+int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL;
+init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
+
+/* Initialize implicit IVs */
+key_ctx_update_implicit_iv(&key->encrypt, (*key2).keys[(int)server].hmac,
+   MAX_HMAC_KEY_LENGTH);
+key_ctx_update_implicit_iv(&key->decrypt, (*key2).keys[1-(int)server].hmac,
+   MAX_HMAC_KEY_LENGTH);
+
+}
+
+
+static struct key2
+generate_key_expansion_oepnvpn_prf(const struct tls_session *session)
 {
+
 uint8_t master[48] = { 0 };
-struct key2 key2 = { 0 };
-bool ret = false;
 
-if (key->initialized)
-{
-msg(D_TLS_ERRORS, "TLS Error: key already initialized");
-goto exit;
-}
+const struct key_state *ks = &session->key[KS_PRIMARY];
+const struct key_source2 *key_src = ks->key_src;
+
+const struct session_id *client_sid = session->opt->server ?
+  &ks->session_id_remote : 
&session->session_id;
+const struct session_id *server_sid = !session->opt->server ?
+  &ks->session_id_remote : 
&session->session_id;
 
 /* debugging print of source key material */
 key_source2_print(key_src);
@@ -1803,6 +1814,7 @@ generate_key_expansion(struct key_ctx_bi *key,
 master,
 sizeof(master));
 
+struct key2 key2;
 /* compute key expansion */
 openvpn_PRF(master,
 sizeof(master),
@@ -1815,41 +1827,62 @@ generate_key_expansion(struct key_ctx_bi *key,
 server_sid,
 (uint8_t *)key2.keys,
 sizeof(key2.keys));
+secure_memzero(&master, sizeof(master));
 
+/* We use the DES fixup here so we can drop it once we
+ * drop DES support and non RFC5705 key derivation */
+for (int i = 0; i < 2; ++i)
+{
+fixup_key(&key2.keys[i], &session->opt->key_type);
+}
 key2.n = 2;
 
-key2_print(&key2, key_type, "Master Encrypt", "Master Decrypt");
+return key2;
+}
+
+/*
+ * Using source entropy from local and remote hosts, mix into
+ * master key.
+ */
+static bool
+generate_key_expansion(struct key_ctx_bi *key,
+   const struct tls_session *session)
+{
+bool ret = false;
+
+if (key->initialized)
+{
+msg(D_TLS_ERRORS, "TLS Error: key already initialized");
+goto exit;
+}
+
+
+bool server = session->opt->server;
+
+struct key2 key2 = generate_key_expansion_oepnvpn_prf(session);
+
+key2_print(&key2, &session->opt->key_type,
+   "Master Encrypt", "Master Decrypt");
 
 /* check for weak keys */
 for (int i = 0; i < 2; ++i)
 {
-fixup_key(&key2.keys[i], key_type);
-if (!check_key(&key2.keys[i], key_type))
+if (!check_key(&key2.keys[i], &session->opt->key_type))
 {
 msg(D_TLS_ERRORS, "TLS Error: Bad dynamic key generated");
 goto exit;
 }
 }
-
-/* Initialize OpenSSL key contexts */
-int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL;
-init_key_ctx_bi(key, &key2, key_direction, key_type, "Data Channel");
-
-/* Initialize implicit IVs */
-key_ctx_update_implicit_iv(&key->encrypt, key2.keys[(int)server].hmac,
-   MAX_HMAC_KEY_LENGTH);
-key_ctx_update_implicit_iv(&key->decrypt, key2.keys[1-(int)server].hmac,
-   MAX_HMAC_KEY_LENGTH);
-
+init_key_contexts(key, &session->opt->key_type, server, &key2);
 ret = true;
 
 exit:
-secure_memzero(&master, sizeof(master));
 secure_memzero(&key2, sizeof(key2));
 
 return ret;
 }
 
+
 static void
 key_ctx

[Openvpn-devel] [PATCH v2 1/3] Refactor key_state_export_keying_material functions

2020-08-12 Thread Arne Schwabe
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 sucess, -1 is also an error.

Signed-off-by: Arne Schwabe 

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

Signed-off-by: Arne Schwabe 
---
 src/openvpn/ssl.c | 36 ++-
 src/openvpn/ssl_backend.h | 16 +++--
 src/openvpn/ssl_mbedtls.c | 73 ---
 src/openvpn/ssl_mbedtls.h | 12 +--
 src/openvpn/ssl_openssl.c | 43 +--
 5 files changed, 113 insertions(+), 67 deletions(-)

diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index f16114c2..3fcaa25f 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -2412,6 +2412,40 @@ error:
 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
  * 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)
 && 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)
 {
diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h
index 7f52ab1e..eb7d541d 100644
--- a/src/openvpn/ssl_backend.h
+++ b/src/openvpn/ssl_backend.h
@@ -394,13 +394,23 @@ void backend_tls_ctx_reload_crl(struct tls_root_ctx 
*ssl_ctx,
  * derived from existing TLS channel. This exported keying material can then be
  * 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 labelThe label to use when exporting the key
+ * @param label_size   The size of the label to use when exporting the key
+ *
+ * @param gc   gc_arena that might be used to allocate the string
+ * returned
+ * @returnsThe exported key material, the caller may zero the
+ * string but should not free it
  */
 
-void
-key_state_export_keying_material(struct key_state_ssl *ks_ssl,
- struct tls_session *session) 
__attribute__((nonnull));
+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) __attribute__((nonnull));
 
 /**/
 /** @addtogroup control_tls
diff --git a/src/openvpn/ssl_mbedtls.c b/src/openvpn/ssl_mbedtls.c
index 9c874788..4287b59e 100644
--- a/src/openvpn/ssl_mbedtls.c
+++ b/src/openvpn/ssl_mbedtls.c
@@ -206,51 +206,54 @@ mbedtls_ssl_export_keys_cb(void *p_expkey, const unsigned 
char *ms,
 {
 struct tls_session *session = p_expkey;
 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,
-  true, NULL);
+static_assert(sizeof(ks_ssl->ctx->session->master)
+== sizeof(cache->master_secret), "master size mismatch");
 
-memcpy(clie

Re: [Openvpn-devel] [PATCH 1/3] Refactor key_state_export_keying_material functions

2020-08-12 Thread Steffan Karger
Hi,

Couldn't resist giving this a quick look.

Feature-ACK on the patch set, but some comments on the approach:

On 12-08-2020 10:55, Arne Schwabe wrote:
> 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 sucess, -1 is also an error.
> 
> Signed-off-by: Arne Schwabe 
> ---
>  src/openvpn/ssl.c | 33 -
>  src/openvpn/ssl_backend.h | 18 --
>  src/openvpn/ssl_mbedtls.c | 22 ++---
>  src/openvpn/ssl_openssl.c | 51 +--
>  4 files changed, 83 insertions(+), 41 deletions(-)
> 
> diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
> index f16114c2..390114e1 100644
> --- a/src/openvpn/ssl.c
> +++ b/src/openvpn/ssl.c
> @@ -2412,6 +2412,37 @@ error:
>  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(ssl, session,
> +EXPORT_KEY_USER, &gc)))
> +{

Hmm, I don't think these abstractions are right. I would argue that the
crypto backends should know as few about OpenVPN specifics as possible.
So things like "EXPORT_KEY_USER" should probably not be passed to the
backend, and instead be handled in ssl.c.

Why not give the backend a prototype like

  key_state_export_keying_material(label, label_len, ekm, ekm_len)

I understand that this would mean an extra memcpy() for the mbedtls
code, but this is not performance-critical at all, right?

(In the follow-up patch, just cache the master secret and client/server
random, instead of the derived key material in the mbedtls backend, so
you can generate the export at will. For OpenSSL, this API should be
trivial.)

> +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
>   * from the TLS control channel (cleartext).
> @@ -2541,7 +2572,7 @@ key_method_2_read(struct buffer *buf, struct tls_multi 
> *multi, struct tls_sessio
>  if ((ks->authenticated > KS_AUTH_FALSE)
>  && 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)
>  {
> diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h
> index 7f52ab1e..8faaefd5 100644
> --- a/src/openvpn/ssl_backend.h
> +++ b/src/openvpn/ssl_backend.h
> @@ -389,18 +389,32 @@ void key_state_ssl_free(struct key_state_ssl *ks_ssl);
>  void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
>  const char *crl_file, bool crl_inline);
>  
> +
> +/* defines the different RFC5705 that are used in OpenVPN */

Make this /** to have it turn up nicely in the doxygen.

> +enum export_key_identifier {
> +EXPORT_KEY_USER
> +};
> +
>  /**
>   * Keying Material Exporters [RFC 5705] allows additional keying material to 
> be
>   * derived from existing TLS channel. This exported keying material can then 
> be
>   * used for a variety of purposes.
>   *
> + * Note
> + *

Note ... what?

>   * @param ks_ssl   The SSL channel's state info
>   * @param session  The session associated with the given key_state
> + * @param key  The key to export.
> + * @param gc   gc_arena that might be used to allocate a string

Where "a string" is "the keying material", right? Maybe just say that :)

> + * @returnsThe 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,
> - struct tls_session *session) 
> __attribute__((nonnull));
> + struct tls_session *session,
> +   

[Openvpn-devel] [PATCH applied] Re: Improve sections about older OpenVPN clients in cipher-negotiation.rst

2020-08-12 Thread Gert Doering
Acked-by: Gert Doering 

Good work, Richard and Arne, thanks!

Your patch has been applied to the master branch.

commit 9262f1454d78157226f20b15a374f3c750e19cdd
Author: Arne Schwabe
Date:   Wed Aug 12 10:54:12 2020 +0200

 Improve sections about older OpenVPN clients in cipher-negotiation.rst

 Signed-off-by: Arne Schwabe 
 Acked-by: Gert Doering 
 Message-Id: <20200812085412.19178-1-a...@rfc2549.org>
 URL: 
https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg20714.html
 Signed-off-by: Gert Doering 


--
kind regards,

Gert Doering



___
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel


Re: [Openvpn-devel] [PATCH] Workaround FreeBSD 12+ race condition on tun/tap open with IPv6.

2020-08-12 Thread Arne Schwabe
Am 23.07.20 um 14:19 schrieb Gert Doering:
> On FreeBSD 12 (tested and verified on 12.1-RELEASE-p2), after "ifconfig
> inet6" for a tun/tap interface, there sometimes is a race condition
> where the "IFDISABLED" flag shows up after a short time frame, under
> a second, and never clears itself.  This disables use of the configured
> IPv6 address on the interface, breaking IPv6 over tun/tap operation.
> 
> This only happens if ipv6_activate_all_interfaces="YES" is not
> set in /etc/rc.conf - but there might be reasons why this is not so.
> 
> As a workaround until this can be fixed on the FreeBSD side (or a
> better workaround is found), sleep(1) after ifconfig, then call
> "ifconfig $dev inet6 -ifdisable".
> 
> Yes, this is massively ugly but makes the problem completely go
> away for my test systems.
> 
> (The same effect can be achieved with an --up script that does this,
> but it's even less pretty - see trac ticket)
> 
> FreeBSD: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=248172
> 
> v2: reword text, refer to FreeBSD bug with much more details

Acked-By: Arne Schwabe 

This looks like a ugly workaround but if it is the best we can, so be it.

Arne



signature.asc
Description: OpenPGP digital signature
___
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel


[Openvpn-devel] [PATCH] Improve sections about older OpenVPN clients in cipher-negotiation.rst

2020-08-12 Thread Arne Schwabe
 - Explain the IV_NCP=2 client situation in 2.4 a bit better.
 - Make more clear what exact versions are meant in the old client section
 - add a missing - in a heading

Thanks to Richard Bohnhomme for initial proof reading.

Signed-off-by: Arne Schwabe 
---
 doc/man-sections/cipher-negotiation.rst | 34 ++---
 1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/doc/man-sections/cipher-negotiation.rst 
b/doc/man-sections/cipher-negotiation.rst
index 46c9d7cf..b2c20241 100644
--- a/doc/man-sections/cipher-negotiation.rst
+++ b/doc/man-sections/cipher-negotiation.rst
@@ -22,14 +22,18 @@ it is automatically added to this list. If both options are 
unset the default is
 
 OpenVPN 2.4 clients
 ---
-The negotiation support in OpenVPN 2.4 was a first implementation and still 
had some
-quirks. Its main goal was "upgrade to AES-256-GCM when possible".
+The negotiation support in OpenVPN 2.4 was the first iteration of the 
implementation
+and still had some quirks. Its main goal was "upgrade to AES-256-GCM when 
possible".
 An OpenVPN 2.4 client that is built against a crypto library that supports AES 
in GCM
 mode and does not have ``--ncp-disable`` will always announce support for
-`AES-256-GCM` and `AES-128-GCM` even if the ``--ncp-ciphers`` option does not 
include
-those two ciphers. It is therefore recommended to add `AES-256-GCM` and 
`AES-128-GCM`
-to the ``--ncp-ciphers`` options to workaround this bug.
+`AES-256-GCM` and `AES-128-GCM` to a server by sending :code:`IV_NCP=2`.
 
+This only causes a problem if ``--ncp-ciphers`` option has been changed from 
the
+default of :code:`AES-256-GCM:AES-128-GCM` to a value that does not include
+these two ciphers. When a OpenVPN servers try to use `AES-256-GCM` or
+`AES-128-GCM` the connection will then fail. It is therefore recommended to
+always have the `AES-256-GCM` and `AES-128-GCM` ciphers to the 
``--ncp-ciphers``
+options to avoid this behaviour.
 
 OpenVPN 3 clients
 -
@@ -42,7 +46,7 @@ To support OpenVPN 3.x based clients at least one of these 
ciphers needs to be
 included in the server's ``--data-ciphers`` option.
 
 
-OpenVPN 2.3 clients and older (and clients with ``--ncp-disable``)
+OpenVPN 2.3 and older clients (and clients with ``--ncp-disable``)
 --
 When a client without cipher negotiation support connects to a server the
 cipher specified with the ``--cipher`` option in the client configuration
@@ -50,10 +54,10 @@ must be included in the ``--data-ciphers`` option of the 
server to allow
 the client to connect. Otherwise the client will be sent the ``AUTH_FAILED``
 message that indicates no shared cipher.
 
-If the client has been configured with the ``--enable-small``
-:code:``./configure`` argument, using ``data-ciphers-fallback cipher``
-in the server config file with the explicit cipher used by the client
-is necessary.
+If the client is 2.3 or older and has been configured with the
+``--enable-small``  :code:`./configure` argument, using
+``data-ciphers-fallback cipher`` in the server config file with the explicit
+cipher used by the client is necessary.
 
 OpenVPN 2.4 server
 --
@@ -66,7 +70,7 @@ adding  `AES-128-GCM` and `AES-256-GCM` to the client's 
``--data-ciphers``
 option is required. OpenVPN 2.5+ will only announce the ``IV_NCP=2`` flag if
 those ciphers are present.
 
-OpenVPN 2.3 and older servers (and servers with ``-ncp-disable``)
+OpenVPN 2.3 and older servers (and servers with ``--ncp-disable``)
 -
 The cipher used by the server must be included in ``--data-ciphers`` to
 allow the client connecting to a server without cipher negotiation
@@ -74,10 +78,10 @@ support.
 (For compatibility OpenVPN 2.5 will also accept the cipher set with
 ``--cipher``)
 
-If the server has been configured with the ``--enable-small``
-:code:``./configure` argument, adding ``data-ciphers-fallback cipher``
-to the client config with the explicit cipher used by the server
-is necessary.
+If the server is 2.3 or older and  has been configured with the
+``--enable-small`` :code:`./configure` argument, adding
+``data-ciphers-fallback cipher`` to the client config with the explicit
+cipher used by the server is necessary.
 
 Blowfish in CBC mode (BF-CBC) deprecation
 --
-- 
2.26.2



___
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel


[Openvpn-devel] [PATCH 2/3] Move openvpn specific key expansion into its own function

2020-08-12 Thread Arne Schwabe
This moves the OpenVPN specific PRF into its own function also
simplifies the code a bit by passing tls_session directly instead of
5 of its fields.

Signed-off-by: Arne Schwabe 
---
 src/openvpn/ssl.c | 109 +-
 1 file changed, 69 insertions(+), 40 deletions(-)

diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 390114e1..e44017c6 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -1765,27 +1765,38 @@ openvpn_PRF(const uint8_t *secret,
 VALGRIND_MAKE_READABLE((void *)output, output_len);
 }
 
-/*
- * Using source entropy from local and remote hosts, mix into
- * master key.
- */
-static bool
-generate_key_expansion(struct key_ctx_bi *key,
-   const struct key_type *key_type,
-   const struct key_source2 *key_src,
-   const struct session_id *client_sid,
-   const struct session_id *server_sid,
-   bool server)
+static void
+init_key_contexts(struct key_ctx_bi *key,
+  const struct key_type *key_type,
+  bool server,
+  struct key2 *key2)
+{
+/* Initialize OpenSSL key contexts */
+int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL;
+init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
+
+/* Initialize implicit IVs */
+key_ctx_update_implicit_iv(&key->encrypt, (*key2).keys[(int)server].hmac,
+   MAX_HMAC_KEY_LENGTH);
+key_ctx_update_implicit_iv(&key->decrypt, (*key2).keys[1-(int)server].hmac,
+   MAX_HMAC_KEY_LENGTH);
+
+}
+
+
+static struct key2
+generate_key_expansion_oepnvpn_prf(const struct tls_session *session)
 {
+
 uint8_t master[48] = { 0 };
-struct key2 key2 = { 0 };
-bool ret = false;
 
-if (key->initialized)
-{
-msg(D_TLS_ERRORS, "TLS Error: key already initialized");
-goto exit;
-}
+const struct key_state *ks = &session->key[KS_PRIMARY];
+const struct key_source2 *key_src = ks->key_src;
+
+const struct session_id *client_sid = session->opt->server ?
+  &ks->session_id_remote : 
&session->session_id;
+const struct session_id *server_sid = !session->opt->server ?
+  &ks->session_id_remote : 
&session->session_id;
 
 /* debugging print of source key material */
 key_source2_print(key_src);
@@ -1803,6 +1814,7 @@ generate_key_expansion(struct key_ctx_bi *key,
 master,
 sizeof(master));
 
+struct key2 key2;
 /* compute key expansion */
 openvpn_PRF(master,
 sizeof(master),
@@ -1815,41 +1827,62 @@ generate_key_expansion(struct key_ctx_bi *key,
 server_sid,
 (uint8_t *)key2.keys,
 sizeof(key2.keys));
+secure_memzero(&master, sizeof(master));
 
+/* We use the DES fixup here so we can drop it once we
+ * drop DES support and non RFC5705 key derivation */
+for (int i = 0; i < 2; ++i)
+{
+fixup_key(&key2.keys[i], &session->opt->key_type);
+}
 key2.n = 2;
 
-key2_print(&key2, key_type, "Master Encrypt", "Master Decrypt");
+return key2;
+}
+
+/*
+ * Using source entropy from local and remote hosts, mix into
+ * master key.
+ */
+static bool
+generate_key_expansion(struct key_ctx_bi *key,
+   const struct tls_session *session)
+{
+bool ret = false;
+
+if (key->initialized)
+{
+msg(D_TLS_ERRORS, "TLS Error: key already initialized");
+goto exit;
+}
+
+
+bool server = session->opt->server;
+
+struct key2 key2 = generate_key_expansion_oepnvpn_prf(session);
+
+key2_print(&key2, &session->opt->key_type,
+   "Master Encrypt", "Master Decrypt");
 
 /* check for weak keys */
 for (int i = 0; i < 2; ++i)
 {
-fixup_key(&key2.keys[i], key_type);
-if (!check_key(&key2.keys[i], key_type))
+if (!check_key(&key2.keys[i], &session->opt->key_type))
 {
 msg(D_TLS_ERRORS, "TLS Error: Bad dynamic key generated");
 goto exit;
 }
 }
-
-/* Initialize OpenSSL key contexts */
-int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL;
-init_key_ctx_bi(key, &key2, key_direction, key_type, "Data Channel");
-
-/* Initialize implicit IVs */
-key_ctx_update_implicit_iv(&key->encrypt, key2.keys[(int)server].hmac,
-   MAX_HMAC_KEY_LENGTH);
-key_ctx_update_implicit_iv(&key->decrypt, key2.keys[1-(int)server].hmac,
-   MAX_HMAC_KEY_LENGTH);
-
+init_key_contexts(key, &session->opt->key_type, server, &key2);
 ret = true;
 
 exit:
-secure_memzero(&master, sizeof(master));
 secure_memzero(&key2, sizeof(key2));
 
 return ret;
 }
 
+
 static void
 key_ctx_update_implicit_i

[Openvpn-devel] [PATCH 3/3] Implement generating data channel keys via EKM/RFC 5705

2020-08-12 Thread Arne Schwabe
OpenVPN currently uses its own (based on TLS 1.0) key derivation
mechansim 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 modernisating 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) that 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.

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.

Signed-off-by: Arne Schwabe 
---
 doc/doxygen/doc_key_generation.h | 15 +++--
 src/openvpn/crypto.h |  4 +++
 src/openvpn/init.c   |  1 +
 src/openvpn/multi.c  |  4 +++
 src/openvpn/options.c| 14 +
 src/openvpn/options.h|  3 ++
 src/openvpn/push.c   |  5 ++-
 src/openvpn/ssl.c| 49 --
 src/openvpn/ssl.h|  2 ++
 src/openvpn/ssl_backend.h|  8 +++--
 src/openvpn/ssl_mbedtls.c| 52 ++--
 src/openvpn/ssl_mbedtls.h|  3 +-
 src/openvpn/ssl_openssl.c|  6 
 13 files changed, 141 insertions(+), 25 deletions(-)

diff --git a/doc/doxygen/doc_key_generation.h b/doc/doxygen/doc_key_generation.h
index 4bb9c708..bbd6c0c5 100644
--- a/doc/doxygen/doc_key_generation.h
+++ b/doc/doxygen/doc_key_generation.h
@@ -58,6 +58,12 @@
  *
  * @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:
  *- Pre-master secret: 48 bytes
  *- Client's PRF seed for master secret: 32 bytes
@@ -73,8 +79,13 @@
  *server's random material.
  *
  * %Key method 2 %key expansion is performed by the \c
- * generate_key_expansion() function.  Please refer to its source code for
- * details of the %key expansion process.
+ * generate_key_expansion_oepnvpn_prf() function.  Please refer to its source
+ * code for details of the %key expansion process.
+ *
+ * When the client sends the IV_PROTO_TLS_KEY_EXPORT and the server replies
+ * with `key-derivation tls-ekm` RFC5705 key material exporter with the label
+ * EXPORTER-OpenVPN-datakeys is used for the key data.
+ *
  *
  * @subsection key_generation_random Source of random material
  *
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 999f643e..ec935ca5 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -254,6 +254,10 @@ struct crypto_options
 #define CO_MUTE_REPLAY_WARNINGS (1<<2)
 /**< Bit-flag indicating not to display
  *   replay warnings. */
+#define CO_USE_TLS_KEY_MATERIAL_EXPORT  (1<<3)
+/**< Bit-flag indicating that key derivation
+ * is done using TLS keying material export [RFC5705]
+ */
 unsigned int flags; /**< Bit-flags determining behavior of
  *   security operation functions. */
 };
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index dfa045b0..34a7313e 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -676,6 +676,7 @@ restore_ncp_options(struct context *c)
 c->options.ciphername = c->c1.ciphername;
 c->options.authname = c->c1.authname;
 c->options.keysize = c->c1.keysize;
+c->options.data_channel_use_ekm = false;
 }
 
 void
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 13738180..a5862020 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1817,6 +1817,10 @@ multi_client_set_protocol_options(struct context *c)
 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 */
 if (!o->ncp_enabled)
 {
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 8bf82c57..90e78a7b 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -7947,6 +7947,20 @@ add_option(struct options *options,
 }
 options->ncp_ciphers = p[1];
 }
+else if (s

[Openvpn-devel] [PATCH 1/3] Refactor key_state_export_keying_material functions

2020-08-12 Thread Arne Schwabe
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 sucess, -1 is also an error.

Signed-off-by: Arne Schwabe 
---
 src/openvpn/ssl.c | 33 -
 src/openvpn/ssl_backend.h | 18 --
 src/openvpn/ssl_mbedtls.c | 22 ++---
 src/openvpn/ssl_openssl.c | 51 +--
 4 files changed, 83 insertions(+), 41 deletions(-)

diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index f16114c2..390114e1 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -2412,6 +2412,37 @@ error:
 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(ssl, session,
+EXPORT_KEY_USER, &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
  * from the TLS control channel (cleartext).
@@ -2541,7 +2572,7 @@ key_method_2_read(struct buffer *buf, struct tls_multi 
*multi, struct tls_sessio
 if ((ks->authenticated > KS_AUTH_FALSE)
 && 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)
 {
diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h
index 7f52ab1e..8faaefd5 100644
--- a/src/openvpn/ssl_backend.h
+++ b/src/openvpn/ssl_backend.h
@@ -389,18 +389,32 @@ void key_state_ssl_free(struct key_state_ssl *ks_ssl);
 void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
 const char *crl_file, bool crl_inline);
 
+
+/* defines the different RFC5705 that are used in OpenVPN */
+enum export_key_identifier {
+EXPORT_KEY_USER
+};
+
 /**
  * Keying Material Exporters [RFC 5705] allows additional keying material to be
  * derived from existing TLS channel. This exported keying material can then be
  * used for a variety of purposes.
  *
+ * Note
+ *
  * @param ks_ssl   The SSL channel's state info
  * @param session  The session associated with the given key_state
+ * @param key  The key to export.
+ * @param gc   gc_arena that might be used to allocate a string
+ * @returnsThe 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,
- struct tls_session *session) 
__attribute__((nonnull));
+ struct tls_session *session,
+ enum export_key_identifier export_key,
+ struct gc_arena *gc) __attribute__((nonnull));
 
 /**/
 /** @addtogroup control_tls
diff --git a/src/openvpn/ssl_mbedtls.c b/src/openvpn/ssl_mbedtls.c
index 9c874788..8ae6ec7b 100644
--- a/src/openvpn/ssl_mbedtls.c
+++ b/src/openvpn/ssl_mbedtls.c
@@ -231,24 +231,18 @@ mbedtls_ssl_export_keys_cb(void *p_expkey, const unsigned 
char *ms,
 }
 #endif /* HAVE_EXPORT_KEYING_MATERIAL */
 
-void
+unsigned char *
 key_state_export_keying_material(struct key_state_ssl *ssl,
- struct tls_session *session)
+ struct tls_session *session,
+ enum export_key_identifier key_id,
+ struct gc_arena *gc)
 {
-if (ssl->exported_key_material)
+if (key_id == EXPORT_KEY_USER)
 {
-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,