Add support for TLS Keying Material Exporters [RFC 5705].

Keying Material Exporter allow additional keying material to be derived from 
existing TLS channel. 
This exported keying material can then be used for a variety of purposes.

>From df9b59db8099ce7cd754cf3dc4adf4c8af81c973 Mon Sep 17 00:00:00 2001
From: Daniel Kubec <n...@rtfm.cz>
List-Post: openvpn-devel@lists.sourceforge.net
Date: Mon, 26 May 2014 14:55:32 +0200
Subject: [PATCH] Added support for TLS Keying Material Exporters [RFC 5705].

Keying Material Exporter allow additional keying material to be derived from
existing TLS channel. This exported keying material can then be used for a
variety of purposes.
---
 doc/openvpn.8             |   15 +++++++++++++++
 src/openvpn/init.c        |   13 +++++++++++++
 src/openvpn/options.c     |   28 ++++++++++++++++++++++++++++
 src/openvpn/options.h     |    4 ++++
 src/openvpn/ssl.c         |    2 ++
 src/openvpn/ssl_backend.h |   12 ++++++++++++
 src/openvpn/ssl_common.h  |    6 ++++++
 src/openvpn/ssl_openssl.c |   33 +++++++++++++++++++++++++++++++++
 8 files changed, 113 insertions(+), 0 deletions(-)

diff --git a/doc/openvpn.8 b/doc/openvpn.8
index 34894e5..d3b3925 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -2691,6 +2691,16 @@ client-connect), then
 every module and script must return success (0) in order for
 the connection to be authenticated.
 .\"*********************************************************
+.TP
+.B \-\-keying-material-exporter label len
+Save Exported Keying Material [RFC5705] of len bytes using label in environment
+for use by plugins.
+
+Note that exporter labels have the potential to collide with existing PRF
+labels. In order to prevent this, labels MUST begin with "EXPORTER".
+
+This option requires OpenSSL 1.0.1 or newer.
+.\"*********************************************************
 .SS Server Mode
 Starting with OpenVPN 2.0, a multi-client TCP/UDP server mode
 is supported, and can be enabled with the
@@ -6116,6 +6126,11 @@ Like
 but in hex form (e.g. "12:34:56:78:9A").
 .\"*********************************************************
 .TP
+.B tls_binding_key
+TLS Binding Key is a pseudorandom hex string based on Exported Keying Material
+[RFC 5705].
+.\"*********************************************************
+.TP
 .B tun_mtu
 The MTU of the TUN/TAP device.
 Set prior to
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 467b98a..715b5ab 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2294,6 +2294,19 @@ do_init_crypto_tls (struct context *c, const unsigned 
int flags)
   to.comp_options = options->comp;
 #endif

+  /* TLS Exported Keying Material [RFC 5705] */
+  if (options->keying_material_exporter_label)
+    {
+      to.ekm_used = true;
+      to.ekm_size = options->keying_material_exporter_length;
+      to.ekm_label = options->keying_material_exporter_label;
+      to.ekm_label_size = strlen(to.ekm_label);
+    }
+  else
+    {
+      to.ekm_used = false;
+    }
+
   /* TLS handshake authentication (--tls-auth) */
   if (options->tls_auth_file)
     {
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index fe9b99d..5f29d79 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -630,6 +630,10 @@ static const char usage_message[] =
   "--x509-track x  : Save peer X509 attribute x in environment for use by\n"
   "                  plugins and management interface.\n"
 #endif
+#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000
+  "--keying-material-exporter label len : Save Exported Keying Material 
(RFC5705)\n"
+  "                  of len bytes using label in environment for use by 
plugins.\n"
+#endif
   "--remote-cert-ku v ... : Require that the peer certificate was signed 
with\n"
   "                  explicit key usage, you can specify more than one 
value.\n"
   "                  value should be given in hex format.\n"
@@ -6995,6 +6999,30 @@ add_option (struct options *options,
       options->persist_mode = 1;
     }
 #endif
+#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000
+  else if (streq (p[0], "keying-material-exporter") && p[1] && p[2])
+    {
+      int ekm_length = positive_atoi (p[2]);
+
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      if (strncmp(p[1], "EXPORTER", 8))
+        {
+          msg (msglevel, "Keying material exporter label must begin with "
+              "\"EXPORTER\"");
+          goto err;
+        }
+
+      if (ekm_length < 1 || ekm_length >= (1 << 16))
+       {
+         msg (msglevel, "Invalid keying material exporter length");
+         goto err;
+       }
+
+      options->keying_material_exporter_label = p[1];
+      options->keying_material_exporter_length = ekm_length;
+    }
+#endif
   else
     {
       int i;
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index 092eac4..dcf081e 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -602,6 +602,10 @@ struct options
   bool show_net_up;
   int route_method;
 #endif
+
+  /* Keying Material Exporters [RFC 5705] */
+  const char *keying_material_exporter_label;
+  int keying_material_exporter_length;
 };

 #define streq(x, y) (!strcmp((x), (y)))
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 9bcb2ac..3c5fdd9 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -2131,6 +2131,8 @@ key_method_2_read (struct buffer *buf, struct tls_multi 
*multi, struct tls_sessi
    */
   if (ks->authenticated && plugin_defined (session->opt->plugins, 
OPENVPN_PLUGIN_TLS_FINAL))
     {
+      key_state_export_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)
        ks->authenticated = false;
     }
diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h
index 37a458c..67b61e7 100644
--- a/src/openvpn/ssl_backend.h
+++ b/src/openvpn/ssl_backend.h
@@ -333,6 +333,18 @@ void key_state_ssl_init(struct key_state_ssl *ks_ssl,
  */
 void key_state_ssl_free(struct key_state_ssl *ks_ssl);

+/**
+ * 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.
+ *
+ * @param ks_ssl       The SSL channel's state info
+ * @param session      The session associated with the given key_state
+ */
+void
+key_state_export_keying_material(struct key_state_ssl *ks_ssl,
+    struct tls_session *session) __attribute__((nonnull));
+
 /**************************************************************************/
 /** @addtogroup control_tls
  *  @{ */
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 04ba789..eefc3c5 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -314,6 +314,12 @@ struct tls_options

   /* --gremlin bits */
   int gremlin;
+
+  /* Keying Material Exporter [RFC 5705] parameters */
+  const char *ekm_label;
+  size_t ekm_label_size;
+  bool ekm_used; /* true when Keying Material should be exported */
+  size_t ekm_size;
 };

 /** @addtogroup control_processor
diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
index 1481850..80743d9 100644
--- a/src/openvpn/ssl_openssl.c
+++ b/src/openvpn/ssl_openssl.c
@@ -1286,6 +1286,39 @@ key_state_read_plaintext (struct key_state_ssl *ks_ssl, 
struct buffer *buf,
   return ret;
 }

+void
+key_state_export_keying_material(struct key_state_ssl *ssl,
+    struct tls_session *session)
+{
+  if (session->opt->ekm_used)
+    {
+#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
+      unsigned char ekm[session->opt->ekm_size];
+
+      if (SSL_export_keying_material(ssl->ssl, ekm, sizeof(ekm),
+         session->opt->ekm_label, session->opt->ekm_label_size, NULL, 0, 0))
+       {
+         const char *ekm_hex = NULL;
+         struct gc_arena gc = gc_new();
+
+         ekm_hex = format_hex (ekm, sizeof(ekm), 0, &gc);
+         setenv_str (session->opt->es, "tls_binding_key", ekm_hex);
+         dmsg(M_DEBUG, "%s: exported keying material: %s", __func__,
+             ekm_hex);
+
+         gc_free(&gc);
+       }
+      else
+       {
+         msg (M_WARN, "WARNING: Export keying material failed!");
+         setenv_del (session->opt->es, "tls_binding_key");
+       }
+#else
+      ASSERT(0);
+#endif
+    }
+}
+
 /* **************************************
  *
  * Information functions
-- 
1.7.1

Reply via email to