Skip to contentSkip to conversation

Keying Material Exporters [RFC 5705]

PATCH In review Daniel Kubec 2 participants 6 messages Apr 20, 2014 – May 14, 2014

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Keying Material Exporters [RFC 5705]

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

TLS allows client and server to establish keying material for use in the upper layers between the TLS end-points. Channel Bindings is straitforward and well-defined mechanism how to authenticate other layers.

Following two attributes were added primary for the possible plugins extensions.

Attribute

tls_ekm: Exported Keying Material

Configuration

EKM is generated when *.ovpn contains following for each TLS negotiation.

example.ovpn:
{
# TLS Keying Material Exporter [RFC 5705]
#
# Note that exporter labels have the potential to collide with existing PRF

# labels.  In order to prevent this, labels SHOULD begin with "EXPORTER".
keying-material-exporter-label "EXPORTER_OPENVPN"

# Export len bytes of keying material (min. 20)
keying-material-exporter-length 20
}

Use Cases:

1) Authentication of upper layers (like Kerberos etc)

2) Authentication of VPN's TLS channel using QRCODE and device such as smartphones.

(Instead of user/pass dialog TLS VPN client could show for example QRCODE
based on keying material derivate)

3) Authentication of Binding Key in confidental side-channel can be also used and avoid/detect MITM (MITM provides his public key and that's reason why authentication of binding key will fail)

src/openvpn/init.c +8 -0
index c2907cd..7870d10 100644
@@ -2307,6 +2307,13 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
      length prefix */
   socket_adjust_frame_parameters (&to.frame, options->ce.proto);
 
+  /* Checking for required parameters for Channel Bindings */
+  to.ekm_size = options->keying_material_exporter_length;
+
+  to.ekm_label = (uint8_t*)options->keying_material_exporter_label;
+  to.ekm_label_size = to.ekm_label ? strlen(to.ekm_label) : 0;
+  to.ekm_used = (to.ekm_label_size && to.ekm_size >= 20) ? true : false;
+
   /*
    * Initialize OpenVPN's master TLS-mode object.
    */
@@ -2315,6 +2322,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
 
   if (flags & CF_INIT_TLS_AUTH_STANDALONE)
     c->c2.tls_auth_standalone = tls_auth_standalone_init (&to, &c->c2.gc);
+
 }
 
 static void
src/openvpn/options.c +18 -0
index 18cb354..5323cd7 100644
@@ -6966,6 +6966,24 @@ add_option (struct options *options,
       options->persist_mode = 1;
     }
 #endif
+  else if (streq (p[0], "keying-material-exporter-label") && p[1])
+    {
+      if (strncmp(p[1], "EXPORTER", 8))
+        {
+          msg (msglevel, "keying material exporter labels SHOULD begin with \"EXPORTER\"");
+          goto err;
+        }
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_label = p[1];
+    }
+  else if (streq (p[0], "keying-material-exporter-length"))
+    {
+      int len = positive_atoi (p[1]);
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_length = len;
+    }
   else
     {
       int i;
src/openvpn/options.h +13 -7
index ec1d091..f5609f9 100644
@@ -30,26 +30,28 @@
 #ifndef OPTIONS_H
 #define OPTIONS_H
 
+/*
+ * Maximum number of parameters associated with an option,
+ * including the option name itself.
+ */
+
+#define MAX_PARMS 16
+
 #include "basic.h"
 #include "common.h"
 #include "mtu.h"
 #include "route.h"
 #include "tun.h"
 #include "socket.h"
+#include "comp.h"
+#include "ssl_common.h"
 #include "plugin.h"
 #include "manage.h"
 #include "proxy.h"
-#include "comp.h"
 #include "pushlist.h"
 #include "clinat.h"
 
 /*
- * Maximum number of parameters associated with an option,
- * including the option name itself.
- */
-#define MAX_PARMS 16
-
-/*
  * Max size of options line and parameter.
  */
 #define OPTION_PARM_SIZE 256
@@ -600,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)))
src/openvpn/ssl.c +5 -0
index d4acc0f..c23446a 100644
@@ -2126,6 +2126,11 @@ 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))
     {
+
+      /* Exported Keying Material [RFC 5705] */    
+      if (session->opt->ekm_used)                                               
+        setenv_str (session->opt->es, "tls_ekm", format_hex (session->ekm, session->ekm_size, 0, &gc));
+
       if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
 	ks->authenticated = false;
     }
src/openvpn/ssl_common.h +16 -0
index 04ba789..3af5d93 100644
@@ -314,6 +314,12 @@ struct tls_options
 
   /* --gremlin bits */
   int gremlin;
+
+  /* Keying Material Exporter [RFC 5705] parameters */
+  uint8_t *ekm_label;
+  int ekm_label_size;
+  bool ekm_used; /* true when Keying Material should be exported */
+  int ekm_size;
 };
 
 /** @addtogroup control_processor
@@ -359,6 +365,16 @@ struct tls_session
   struct crypto_options tls_auth;
   struct packet_id tls_auth_pid;
 
+  /* number of negotiations for this session */
+  unsigned int negotiations;
+
+  /* channel_id identifies TLS session across (re)negotiations */
+  uint8_t channel_id[32];
+
+  /* Exported Keying Material [RFC 5705] */
+  uint8_t *ekm;
+  int ekm_size;
+
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id;	/* our random session ID */
   int key_id;			/* increments with each soft reset (for key renegotiation) */
src/openvpn/ssl_openssl.c +24 -0
index 0b63e26..4dd588f 100644
@@ -154,6 +154,30 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
 	   SSL_alert_type_string_long (ret),
 	   SSL_alert_desc_string_long (ret));
     }
+  else if (where & SSL_CB_HANDSHAKE_DONE)
+    {
+      struct tls_session *tls;
+      tls = (struct tls_session *)SSL_get_ex_data (s, mydata_index);
+
+      if (tls->opt->ekm_used == false)
+        goto done;
+
+      if (tls->ekm)
+        free(tls->ekm);
+
+      tls->ekm_size = tls->opt->ekm_size;
+      tls->ekm = gc_malloc (tls->ekm_size, true, NULL);
+
+      SSL_export_keying_material((SSL *)s, 
+                                 tls->ekm, 
+                                 tls->ekm_size, 
+                                 tls->opt->ekm_label,
+                                 tls->opt->ekm_label_size, 
+                                 NULL, 0, 0);
+
+done:
+      msg (D_HANDSHAKE_VERBOSE, "SSL Handshake done.");
+    }
 }
 
 /*
#1 lore raw

Re: [Openvpn-devel] [PATCH] Keying Material Exporter [RFC 5705]

Updated patch.

vpn_binding_key:
- keying material derived by openvpn's crypto later (ssl.c:tls1_*)
- life time across negotiations (works a bit like EKM)

tls_ekm: Exported Keying Material [RFC 5705]
- derived when crypto backend support ( currently openssl >= 1.0.2 )

src/openvpn/init.c +8 -0
index c2907cd..7870d10 100644
@@ -2307,6 +2307,13 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
      length prefix */
   socket_adjust_frame_parameters (&to.frame, options->ce.proto);
 
+  /* Checking for required parameters for Channel Bindings */
+  to.ekm_size = options->keying_material_exporter_length;
+
+  to.ekm_label = (uint8_t*)options->keying_material_exporter_label;
+  to.ekm_label_size = to.ekm_label ? strlen(to.ekm_label) : 0;
+  to.ekm_used = (to.ekm_label_size && to.ekm_size >= 20) ? true : false;
+
   /*
    * Initialize OpenVPN's master TLS-mode object.
    */
@@ -2315,6 +2322,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
 
   if (flags & CF_INIT_TLS_AUTH_STANDALONE)
     c->c2.tls_auth_standalone = tls_auth_standalone_init (&to, &c->c2.gc);
+
 }
 
 static void
src/openvpn/options.c +18 -0
index 18cb354..5323cd7 100644
@@ -6966,6 +6966,24 @@ add_option (struct options *options,
       options->persist_mode = 1;
     }
 #endif
+  else if (streq (p[0], "keying-material-exporter-label") && p[1])
+    {
+      if (strncmp(p[1], "EXPORTER", 8))
+        {
+          msg (msglevel, "keying material exporter labels SHOULD begin with \"EXPORTER\"");
+          goto err;
+        }
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_label = p[1];
+    }
+  else if (streq (p[0], "keying-material-exporter-length"))
+    {
+      int len = positive_atoi (p[1]);
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_length = len;
+    }
   else
     {
       int i;
src/openvpn/options.h +4 -0
index ec1d091..8c8aeb3 100644
@@ -600,6 +600,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)))
src/openvpn/ssl.c +252 -50

409 lines not on this page · raw message

src/openvpn/ssl_common.h +15 -0
index 04ba789..4ad82a1 100644
@@ -314,6 +314,12 @@ struct tls_options
 
   /* --gremlin bits */
   int gremlin;
+
+  /* Keying Material Exporter [RFC 5705] parameters */
+  uint8_t *ekm_label;
+  int ekm_label_size;
+  bool ekm_used; /* true when Keying Material should be exported */
+  int ekm_size;
 };
 
 /** @addtogroup control_processor
@@ -359,6 +365,15 @@ struct tls_session
   struct crypto_options tls_auth;
   struct packet_id tls_auth_pid;
 
+  /* number of negotiations for this session */
+  unsigned int negotiations;
+
+  /* Exported Keying Material [RFC 5705] */
+  uint8_t *ekm;                 /* buffer size: session->opt->ekm_size */
+
+  /* VPN's binding key derived by tls1_keying_material_exporter */
+  uint8_t *binding_key;         /* buffer size: session->opt->ekm_size */
+
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id;	/* our random session ID */
   int key_id;			/* increments with each soft reset (for key renegotiation) */
src/openvpn/ssl_openssl.c +21 -0
index 0b63e26..1b44b97 100644
@@ -154,6 +154,27 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
 	   SSL_alert_type_string_long (ret),
 	   SSL_alert_desc_string_long (ret));
     }
+  else if (where & SSL_CB_HANDSHAKE_DONE)
+    {
+#if OPENSSL_VERSION_NUMBER>=0x0010002fL
+      struct tls_session *tls;
+      tls = (struct tls_session *)SSL_get_ex_data (s, mydata_index);
+
+      if (tls->opt->ekm_used == false)
+        goto done;
+
+      memset(tls->ekm, 0, tls->opt->ekm_size);
+
+      SSL_export_keying_material((SSL *)s, 
+                                 tls->ekm, 
+                                 tls->opt->ekm_size, 
+                                 tls->opt->ekm_label,
+                                 tls->opt->ekm_label_size, 
+                                 NULL, 0, 0);
+done:
+#endif
+      msg (D_HANDSHAKE_VERBOSE, "SSL Handshake done.");
+    }
 }
 
 /*
#2 lore raw

Re: [Openvpn-devel] [PATCH] Keying Material Exporter [RFC 5705]

In reply to Daniel Kubec

Updated patch:

- setenv_str() will set only valid tls_ekm attribute

src/openvpn/init.c +8 -0
index c2907cd..7870d10 100644
@@ -2307,6 +2307,13 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
      length prefix */
   socket_adjust_frame_parameters (&to.frame, options->ce.proto);
 
+  /* Checking for required parameters for Channel Bindings */
+  to.ekm_size = options->keying_material_exporter_length;
+
+  to.ekm_label = (uint8_t*)options->keying_material_exporter_label;
+  to.ekm_label_size = to.ekm_label ? strlen(to.ekm_label) : 0;
+  to.ekm_used = (to.ekm_label_size && to.ekm_size >= 20) ? true : false;
+
   /*
    * Initialize OpenVPN's master TLS-mode object.
    */
@@ -2315,6 +2322,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
 
   if (flags & CF_INIT_TLS_AUTH_STANDALONE)
     c->c2.tls_auth_standalone = tls_auth_standalone_init (&to, &c->c2.gc);
+
 }
 
 static void
src/openvpn/options.c +18 -0
index 18cb354..5323cd7 100644
@@ -6966,6 +6966,24 @@ add_option (struct options *options,
       options->persist_mode = 1;
     }
 #endif
+  else if (streq (p[0], "keying-material-exporter-label") && p[1])
+    {
+      if (strncmp(p[1], "EXPORTER", 8))
+        {
+          msg (msglevel, "keying material exporter labels SHOULD begin with \"EXPORTER\"");
+          goto err;
+        }
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_label = p[1];
+    }
+  else if (streq (p[0], "keying-material-exporter-length"))
+    {
+      int len = positive_atoi (p[1]);
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_length = len;
+    }
   else
     {
       int i;
src/openvpn/options.h +4 -0
index ec1d091..8c8aeb3 100644
@@ -600,6 +600,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)))
src/openvpn/ssl.c +262 -50

419 lines not on this page · raw message

src/openvpn/ssl_common.h +16 -0
index 04ba789..40cbdbe 100644
@@ -314,6 +314,12 @@ struct tls_options
 
   /* --gremlin bits */
   int gremlin;
+
+  /* Keying Material Exporter [RFC 5705] parameters */
+  uint8_t *ekm_label;
+  int ekm_label_size;
+  bool ekm_used; /* true when Keying Material should be exported */
+  int ekm_size;
 };
 
 /** @addtogroup control_processor
@@ -359,6 +365,16 @@ struct tls_session
   struct crypto_options tls_auth;
   struct packet_id tls_auth_pid;
 
+  /* number of negotiations for this session */
+  unsigned int negotiations;
+
+  /* Exported Keying Material [RFC 5705] */
+  uint8_t *ekm;                 /* buffer size: session->opt->ekm_size */
+  bool ekm_exported;            /* true when ekm contains updated material */
+
+  /* VPN's binding key derived by tls1_keying_material_exporter */
+  uint8_t *binding_key;         /* buffer size: session->opt->ekm_size */
+
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id;	/* our random session ID */
   int key_id;			/* increments with each soft reset (for key renegotiation) */
src/openvpn/ssl_openssl.c +22 -0
index 0b63e26..873b163 100644
@@ -154,6 +154,28 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
 	   SSL_alert_type_string_long (ret),
 	   SSL_alert_desc_string_long (ret));
     }
+  else if (where & SSL_CB_HANDSHAKE_DONE)
+    {
+      struct tls_session *tls;
+      tls = (struct tls_session *)SSL_get_ex_data (s, mydata_index);
+
+      tls->ekm_exported = false;
+      if (tls->opt->ekm_used == false)
+        goto done;
+
+      memset(tls->ekm, 0, tls->opt->ekm_size);
+
+#if (OPENSSL_VERSION_NUMBER > 0x10002000)
+      tls->ekm_exported = (bool)SSL_export_keying_material((SSL *)s, 
+                                                           tls->ekm, 
+                                                           tls->opt->ekm_size, 
+                                                           tls->opt->ekm_label,
+                                                           tls->opt->ekm_label_size, 
+                                                           NULL, 0, 0);
+#endif
+done:
+      msg (D_HANDSHAKE_VERBOSE, "SSL Handshake done.");
+    }
 }
 
 /*
#3 lore raw
In reply to Daniel Kubec

updated patch:

git format-patch -n HEAD^ --stdout > ./openvpn-channel-bindings.patch

vpn_binding_key:
- keying material derived by openvpn's crypto later (ssl.c:tls1_*)
- life time across negotiations (works a bit like EKM)

tls_binding_key: Exported Keying Material [RFC 5705]
- derived when crypto backend support ( currently openssl >= 1.0.2 )

From b577afe5c076b9f93ff6112c9efb7966f32f86a3 Mon Sep 17 00:00:00 2001
From: Daniel Kubec <[email protected]...>
Date: Thu, 24 Apr 2014 18:17:17 +0200
Subject: [PATCH 1/1] Channel Bindings based on Keying Material Exporters [RFC 5705]

---
 src/openvpn/init.c        |    8 ++
 src/openvpn/options.c     |   17 +++
 src/openvpn/options.h     |    4 +
 src/openvpn/ssl.c         |  262 +++++++++++++++++++++++++++++++++++----------
 src/openvpn/ssl_common.h  |   13 +++
 src/openvpn/ssl_openssl.c |   22 ++++
 6 files changed, 267 insertions(+), 59 deletions(-)
src/openvpn/init.c +8 -0
index c2907cd..7dc1982 100644
@@ -2292,6 +2292,13 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
   to.comp_options = options->comp;
 #endif
 
+  /* Checking for required parameters for Channel Bindings */
+  to.ekm_size = options->keying_material_exporter_length;
+  to.ekm_label = (uint8_t*)options->keying_material_exporter_label;
+  to.ekm_label_size = to.ekm_label ? strlen(to.ekm_label) : 0;
+
+  to.ekm_used = (to.ekm_label_size && to.ekm_size >= 20) ? true : false;
+
   /* TLS handshake authentication (--tls-auth) */
   if (options->tls_auth_file)
     {
@@ -2315,6 +2322,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
 
   if (flags & CF_INIT_TLS_AUTH_STANDALONE)
     c->c2.tls_auth_standalone = tls_auth_standalone_init (&to, &c->c2.gc);
+
 }
 
 static void
src/openvpn/options.c +17 -0
index 4af2974..8be527d 100644
@@ -6984,6 +6984,23 @@ add_option (struct options *options,
       options->persist_mode = 1;
     }
 #endif
+  else if (streq (p[0], "keying-material-exporter-label") && p[1])
+    {
+      if (strncmp(p[1], "EXPORTER", 8))
+        {
+          msg (msglevel, "keying material exporter labels SHOULD begin with \"EXPORTER\"");
+          goto err;
+        }
+        VERIFY_PERMISSION (OPT_P_GENERAL);
+        options->keying_material_exporter_label = p[1];
+    }
+  else if (streq (p[0], "keying-material-exporter-length"))
+    {
+      int len = positive_atoi (p[1]);
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_length = len;
+    }
   else
     {
       int i;
src/openvpn/options.h +4 -0
index ec1d091..8c8aeb3 100644
@@ -600,6 +600,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)))
src/openvpn/ssl.c +203 -59

331 lines not on this page · raw message

src/openvpn/ssl_common.h +13 -0
index 04ba789..ad4c8b1 100644
@@ -314,6 +314,12 @@ struct tls_options
 
   /* --gremlin bits */
   int gremlin;
+
+  /* Keying Material Exporter [RFC 5705] parameters */
+  uint8_t *ekm_label;
+  int ekm_label_size;
+  bool ekm_used; /* true when Keying Material should be exported */
+  int ekm_size;
 };
 
 /** @addtogroup control_processor
@@ -355,6 +361,13 @@ struct tls_session
   /* during hard reset used to control burst retransmit */
   bool burst;
 
+  /* Exported Keying Material [RFC 5705] */
+  uint8_t *ekm;                 /* buffer size: session->opt->ekm_size */
+  bool ekm_exported;            /* true when ekm contains updated material */
+
+  /* VPN's binding key derived by tls1_keying_material_exporter() */
+  uint8_t *binding_key;         /* buffer size: session->opt->ekm_size */
+
   /* authenticate control packets */
   struct crypto_options tls_auth;
   struct packet_id tls_auth_pid;
src/openvpn/ssl_openssl.c +22 -0
index 1923230..e59f9e6 100644
@@ -154,6 +154,28 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
 	   SSL_alert_type_string_long (ret),
 	   SSL_alert_desc_string_long (ret));
     }
+  else if (where & SSL_CB_HANDSHAKE_DONE)
+    {
+      struct tls_session *tls;
+      tls = (struct tls_session *)SSL_get_ex_data (s, mydata_index);
+
+      tls->ekm_exported = false;
+      if (tls->opt->ekm_used == false)
+        goto done;
+
+      memset(tls->ekm, 0, tls->opt->ekm_size);
+
+#if (OPENSSL_VERSION_NUMBER > 0x10002000)
+      tls->ekm_exported = (bool)SSL_export_keying_material((SSL *)s,
+                                                           tls->ekm,
+                                                           tls->opt->ekm_size,
+                                                           tls->opt->ekm_label,
+                                                           tls->opt->ekm_label_size,
+                                                           NULL, 0, 0);
+#endif
+done:
+      msg (D_HANDSHAKE_VERBOSE, "SSL Handshake done.");
+    }
 }
 
 /*
Signature · 1 line
1.7.1
#4 lore raw

Hi Daniel,

Sorry for the delay, I've finally found some spare time to dig a bit into your patch. I've not looked at the implementation details yet, just the big picture for now.

First of all, I can see exported key material being useful. As a matter of fact, OpenVPN does something similar to derive its data channel keys.

Quoted 3 lines · Daniel Kubec

updated patch:

git format-patch -n HEAD^ --stdout > ./openvpn-channel-bindings.patch

It's probably easier if you create commits while doing work, and then use 'git format-patch origin/master' or 'git format-patch HEAD~n' (with n your number of commits). That generates patch files including your commit message, and are easy to apply using 'git am'.

Also, your patch does a number of things to achieve you goal; abstract some functions, add plugin calls, and do the actual export. Patches are more easily reviewed if those separate steps are separated into several commits (with each having a clear commit message describing the goal and the changes of the commit).

Quoted 6 lines

vpn_binding_key:
- keying material derived by openvpn's crypto later (ssl.c:tls1_*)
- life time across negotiations (works a bit like EKM)

tls_binding_key: Exported Keying Material [RFC 5705]
- derived when crypto backend support ( currently openssl >= 1.0.2 )

I'm not sure we should do the vpn_binding_key part, because:
1) I believe we should leave the crypto to the crypto libraries as much as possible. From this perspective I'd vote for just calling SSL_export_keying_material() if it's available (which according tot the OpenSSL ChangeLog / git tree is since 1.0.1, not 1.0.2). PolarSSL does not support EKM yet, so our PolarSSL builds just won't support it either. Just put that into the manpage, and we can add it when(/if) PolarSSL adds support too.
2) I think the exported keying material should *not* survive negotiations; negotiations by design force the endpoints to authenticate again, which should also be the case when using EKM.
3) As a positive side effect, the patch will add a lot less code to OpenVPN if vpn_binding_key is not added.

The new code calls the OPENVPN_PLUGIN_TLS_FINAL plugin twice (also from key_method_2_write() now), why is that? And (partly as a question to our plugin meister David), will that not break stuff for current plugin interface consumers?

Once the big picture is clear, I'll have to look more carefully at the implementation details.

-Steffan

#5 lore raw

PATCH 1/1 Keying Material Exporters [RFC 5705]

Hi Steffan,

Thank you for your comments.

Based on our previous conversation, I'd like to offer you splitted EKM patch without the vpn_binding_key attribute.

Later I'd like to discucess concept of TLS identifier which lives across negotiations.

Daniel

From d6c231cae830e1045c2debdb04a166e4b81f363e Mon Sep 17 00:00:00 2001
From: Daniel Kubec <[email protected]...>
Date: Wed, 14 May 2014 12:37:30 +0200
Subject: [PATCH 1/2] TLS 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.

---
 src/openvpn/init.c        |    8 ++++++++
 src/openvpn/options.c     |   19 +++++++++++++++++++
 src/openvpn/options.h     |    4 ++++
 src/openvpn/ssl.c         |   12 ++++++++++++
 src/openvpn/ssl_common.h  |   10 ++++++++++
 src/openvpn/ssl_openssl.c |   30 ++++++++++++++++++++++++++++++
 6 files changed, 83 insertions(+), 0 deletions(-)
src/openvpn/init.c +8 -0
index 467b98a..90598ef 100644
@@ -2294,6 +2294,14 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
   to.comp_options = options->comp;
 #endif
 
+  /* TLS Exported Keying Material [RFC 5705] */
+  to.ekm_size = options->keying_material_exporter_length;
+
+  to.ekm_label = (uint8_t*)options->keying_material_exporter_label;
+  to.ekm_label_size = to.ekm_label ? strlen(to.ekm_label) : 0;
+
+  to.ekm_used = (to.ekm_label_size && to.ekm_size >= 20) ? true : false;
+
   /* TLS handshake authentication (--tls-auth) */
   if (options->tls_auth_file)
     {
src/openvpn/options.c +19 -0
index fe9b99d..8011c01 100644
@@ -639,6 +639,8 @@ static const char usage_message[] =
   "--remote-cert-tls t: Require that peer certificate was signed with explicit\n"
   "                  key usage and extended key usage based on RFC3280 TLS rules.\n"
   "                  t = 'client' | 'server'.\n"
+  "--keying-material-exporter-label l  : Exported Keying Material Label RFC5705.\n"
+  "--keying-material-exporter-length l : Exported Keying Material Length RFC5705.\n"
 #endif				/* ENABLE_SSL */
 #ifdef ENABLE_PKCS11
   "\n"
@@ -6995,6 +6997,23 @@ add_option (struct options *options,
       options->persist_mode = 1;
     }
 #endif
+  else if (streq (p[0], "keying-material-exporter-label") && p[1])
+    {
+      if (strncmp(p[1], "EXPORTER", 8))
+        {
+          msg (msglevel, "keying material exporter labels SHOULD begin with \"EXPORTER\"");
+          goto err;
+        }
+        VERIFY_PERMISSION (OPT_P_GENERAL);
+        options->keying_material_exporter_label = p[1];
+    }
+  else if (streq (p[0], "keying-material-exporter-length"))
+    {
+      int len = positive_atoi (p[1]);
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_length = len;
+    }
   else
     {
       int i;
src/openvpn/options.h +4 -0
index 092eac4..dcf081e 100644
@@ -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)))
src/openvpn/ssl.c +12 -0
index 9bcb2ac..49bd8c0 100644
@@ -891,6 +891,12 @@ tls_session_init (struct tls_multi *multi, struct tls_session *session)
 	P_CONTROL_HARD_RESET_SERVER_V2 : P_CONTROL_HARD_RESET_CLIENT_V2;
     }
 
+  /* allocate buffers for Exported Keying Material [RFC 5705] */
+  session->ekm = malloc (session->opt->ekm_size);
+  session->ekm_exported = false;
+
+  memset (session->ekm, 0, session->opt->ekm_size);
+
   /* Initialize control channel authentication parameters */
   session->tls_auth = session->opt->tls_auth;
 
@@ -2131,6 +2137,12 @@ 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))
     {
+      const char *tls_binding_key = format_hex (session->ekm, session->opt->ekm_size, 0, &gc);
+      dmsg (M_INFO, "TLS: Exported Keying Material [RFC5705] : length=%d, key=%s",
+                     session->opt->ekm_size, tls_binding_key);
+
+      setenv_str (session->opt->es, "tls_binding_key",  tls_binding_key);
+
       if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
 	ks->authenticated = false;
     }
src/openvpn/ssl_common.h +10 -0
index 04ba789..d12be0e 100644
@@ -314,6 +314,12 @@ struct tls_options
 
   /* --gremlin bits */
   int gremlin;
+
+  /* Keying Material Exporter [RFC 5705] parameters */
+  uint8_t *ekm_label;
+  int ekm_label_size;
+  bool ekm_used; /* true when Keying Material should be exported */
+  int ekm_size;
 };
 
 /** @addtogroup control_processor
@@ -355,6 +361,10 @@ struct tls_session
   /* during hard reset used to control burst retransmit */
   bool burst;
 
+  /* Exported Keying Material [RFC 5705] */
+  uint8_t *ekm;                 /* buffer size: session->opt->ekm_size */
+  bool ekm_exported;            /* true when ekm contains updated material */
+
   /* authenticate control packets */
   struct crypto_options tls_auth;
   struct packet_id tls_auth_pid;
src/openvpn/ssl_openssl.c +30 -0
index 1481850..6ec7bcc 100644
@@ -134,6 +134,30 @@ bool tls_ctx_initialised(struct tls_root_ctx *ctx)
 }
 
 /*
+ * 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.
+ */
+
+static void
+export_keying_material(const SSL *ssl)
+{
+  struct tls_session *tls = SSL_get_ex_data (ssl, mydata_index);
+  ASSERT(tls != NULL);
+
+  tls->ekm_exported = false;
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
+  tls->ekm_exported = (bool)SSL_export_keying_material((SSL *)ssl,
+                                                       tls->ekm,           
+                                                       tls->opt->ekm_size, 
+                                                       tls->opt->ekm_label,
+                                                       tls->opt->ekm_label_size,
+                                                       NULL, 0, 0);
+#endif
+}
+
+/*
  * Print debugging information on SSL/TLS session negotiation.
  */
 
@@ -157,6 +181,12 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
 	   SSL_alert_type_string_long (ret),
 	   SSL_alert_desc_string_long (ret));
     }
+  else if (where & SSL_CB_HANDSHAKE_DONE)
+    {
+      export_keying_material(s);
+
+      dmsg (D_HANDSHAKE_VERBOSE, "SSL handshake done.");
+    }
 }
 
 /*
Signature · 19 lines
1.7.1


From 64bd4ead32cf304537eb006bf2b13ca1680ea5e1 Mon Sep 17 00:00:00 2001
From: Daniel Kubec <[email protected]...>
Date: Wed, 14 May 2014 13:30:24 +0200
Subject: [PATCH 2/2] Updated doc/openvpn.8

Exported Keying Material [RFC5705]

Parameters:
  keying-material-exporter-label
  keying-material-exporter-length

Output:
  tls_binding_key
---
 doc/openvpn.8 |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
doc/openvpn.8 +16 -0
index 34894e5..6f72b4e 100644
@@ -2691,6 +2691,17 @@ 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 label [RFC 5705]
+Labels here have the same definition as in TLS, i.e., an ASCII string with no 
+terminating NULL. Note that exporter labels have the potential to collide with 
+existing PRF labels.  In order to prevent this, labels MUST begin with 
+"EXPORTER".
+.\"*********************************************************
+.TP
+.B \-\-keying-material-exporter-length length [RFC 5705]
+The output is a pseudorandom bit string of length bytes derived from TLS.
+.\"*********************************************************
 .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 +6127,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
Signature · 1 line
1.7.1