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 <n...@rtfm.cz>
List-Post: openvpn-devel@lists.sourceforge.net
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(-)

diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index c2907cd..7dc1982 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -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
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 4af2974..8be527d 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -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;
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index ec1d091..8c8aeb3 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -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)))
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index b09e52b..bac1c77 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -2,12 +2,13 @@
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
- *             packet encryption, packet authentication, and
- *             packet compression.
+ *             packet encryption, packet authentication,
+ *             packet compression and channel bindings.
  *
  *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sa...@openvpn.net>
  *  Copyright (C) 2010 Fox Crypto B.V. <open...@fox-it.com>
  *  Copyright (C) 2008-2013 David Sommerseth <d...@users.sourceforge.net>
+ *  Copyright (C) 2014 Daniel Kubec <n...@rtfm.cz>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
@@ -887,6 +888,14 @@ 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 vpn_binding_key and tls_ekm [RFC 5705] */            
+  session->ekm = malloc (session->opt->ekm_size);                              
+  session->ekm_exported = false;                                               
+  session->binding_key = malloc (session->opt->ekm_size);                      
+
+  memset (session->ekm, 0, session->opt->ekm_size);                            
 
+  memset (session->binding_key, 0, session->opt->ekm_size);
+
   /* Initialize control channel authentication parameters */
   session->tls_auth = session->opt->tls_auth;

@@ -1492,45 +1501,143 @@ openvpn_PRF (const uint8_t *secret,
   VALGRIND_MAKE_READABLE ((void *)output, output_len);
 }

+static void                                                                    
+generate_master_secret(struct key_ctx_bi *key,                                 
+                       const struct key_type *key_type,                        
+                       const struct key_source2 *key_src,                      
+                       uint8_t *master,                                        
+                       unsigned int master_size)                               
+{                                                                              
+  /* compute master secret */                                                  
+  openvpn_PRF (key_src->client.pre_master,                                     
+               sizeof(key_src->client.pre_master),                             
+               KEY_EXPANSION_ID " master secret",                              
+               key_src->client.random1,                                        
+               sizeof(key_src->client.random1),                                
+               key_src->server.random1,                                        
+               sizeof(key_src->server.random1),                                
+               NULL,                                                           
+               NULL,                                                           
+               master,                                                         
+               master_size);                                                   
+
+  key_source2_print (key_src);                                                 
+}
+
+/*                                                                             
 
+ * Use the tls1_keying_material_exporter for generating VPN Binding Key        
+ *                                                                             
+ * 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       
+ * tls1_PRF labels.  In order to prevent this, labels SHOULD begin with
+ * "EXPORTER".
+ *
+ * The output is a pseudorandom bit string of length bytes generated
+ * from the master_secret.
+ *
+ * If no context is provided, it then computes:
+ *
+ *     tls1_PRF(SecurityParameters.master_secret, label,
+ *              SecurityParameters.client_random +
+ *              SecurityParameters.server_random)[length]
+ *
+ * If context is provided, it computes:
+ *
+ *     tls1_PRF(SecurityParameters.master_secret, label,
+ *              SecurityParameters.client_random +
+ *              SecurityParameters.server_random +
+ *              context_value_length + context_value)[length]
+ */
+
+static void
+tls1_keying_material_exporter(uint8_t *master, int master_size,
+                               uint8_t *label, int label_len,
+                               const uint8_t *client_random, int 
client_random_len,
+                               const uint8_t *server_random, int 
server_random_len,
+                               const uint8_t *context_value, int 
context_value_len,
+                               uint8_t *out, int out_len)
+{
+  struct buffer seed = alloc_buf (master_size
+                                + label_len
+                                + client_random_len
+                                + server_random_len
+                                + context_value_len);
+
+  ASSERT (buf_write (&seed, label, label_len));
+  ASSERT (buf_write (&seed, client_random, client_random_len));
+  ASSERT (buf_write (&seed, server_random, server_random_len));
+
+  if (context_value)
+    ASSERT (buf_write (&seed, context_value, context_value_len));
+
+  /* compute PRF */
+  tls1_PRF (BPTR(&seed), BLEN(&seed), master, master_size, out, out_len);
+
+  buf_clear (&seed);
+  free_buf (&seed);
+
+  VALGRIND_MAKE_READABLE ((void *)out, out_len);
+}
+
+/*
+ * Generated Binding Key using tls1_keying_material_exporter()
+ */
+static bool
+generate_binding_key(uint8_t *master,
+                     int master_size,
+                     struct key_ctx_bi *key,
+                     const struct key_type *key_type,
+                     const struct key_source2 *key_src,
+                     uint8_t *label, int label_len,
+                     uint8_t *binding_key, int binding_key_len)
+{
+  const char *context_value = KEY_EXPANSION_ID " binding key";
+  struct gc_arena gc = gc_new ();
+
+  tls1_keying_material_exporter(master, master_size,
+                                label, label_len,
+                                key_src->client.random2,
+                                sizeof(key_src->client.random2),
+                                key_src->server.random2,
+                                sizeof(key_src->server.random2),
+                                context_value,
+                                strlen(context_value),
+                                binding_key, binding_key_len);
+
+  dmsg (D_SHOW_KEY_SOURCE, "vpn_binding_key: %s",
+        format_hex (binding_key, binding_key_len, 0, &gc));
+  gc_free (&gc);
+  return true;
+}
+
 /* 
  * Using source entropy from local and remote hosts, mix into
  * master key.
  */
 static bool
-generate_key_expansion (struct key_ctx_bi *key,
+generate_key_expansion (uint8_t *master, 
+                        int master_size,
+                        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)
 {
-  uint8_t master[48];
   struct key2 key2;
   bool ret = false;
   int i;

-  CLEAR (master);
   CLEAR (key2);

   /* debugging print of source key material */
   key_source2_print (key_src);

-  /* compute master secret */
-  openvpn_PRF (key_src->client.pre_master,
-              sizeof(key_src->client.pre_master),
-              KEY_EXPANSION_ID " master secret",
-              key_src->client.random1,
-              sizeof(key_src->client.random1),
-              key_src->server.random1,
-              sizeof(key_src->server.random1),
-              NULL,
-              NULL,
-              master,
-              sizeof(master));
-  
   /* compute key expansion */
   openvpn_PRF (master,
-              sizeof(master),
+              master_size,
               KEY_EXPANSION_ID " key expansion",
               key_src->client.random2,
               sizeof(key_src->client.random2),
@@ -1867,6 +1974,68 @@ push_peer_info(struct buffer *buf, struct tls_session 
*session)
 }

 static bool
+key_method_final(struct tls_session *session, bool server)
+{
+  struct key_state *ks = &session->key[KS_PRIMARY];        /* primary key */
+  const struct session_id *client_sid;
+  const struct session_id *server_sid;
+  uint8_t master[48];
+
+  CLEAR(master);
+
+  generate_master_secret(&ks->key, &session->opt->key_type,
+                         ks->key_src, master, sizeof(master));
+
+  /* VPN binding key identifies TLS Session across (re)negotiations */
+  if (session->opt->ekm_used && session->key_id == 1)
+    generate_binding_key(master, sizeof(master),
+                         &ks->key, &session->opt->key_type, ks->key_src,
+                         session->opt->ekm_label, session->opt->ekm_label_size,
+                         session->binding_key, session->opt->ekm_size);
+  /*
+   * Call OPENVPN_PLUGIN_TLS_FINAL plugin if defined, for final
+   * veto opportunity over authentication decision.
+   */
+  if (ks->authenticated && plugin_defined (session->opt->plugins, 
OPENVPN_PLUGIN_TLS_FINAL))
+    {
+      if (session->opt->ekm_used)
+        {
+          struct gc_arena gc = gc_new ();
+          setenv_str (session->opt->es, "vpn_binding_key", 
+                      format_hex (session->binding_key, 
session->opt->ekm_size, 0, &gc));
+
+          /* Exported Keying Material [RFC 5705] */
+          if (session->ekm_exported == true)
+            setenv_str (session->opt->es, "tls_binding_key",
+                        format_hex (session->ekm, session->opt->ekm_size, 0, 
&gc));
+
+          gc_free (&gc);
+        }
+
+      if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, 
NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+        ks->authenticated = false;
+    }
+
+  client_sid = server ? &ks->session_id_remote : &session->session_id;
+  server_sid = server ? &session->session_id : &ks->session_id_remote;
+
+  if (!generate_key_expansion (master,
+                               sizeof(master),
+                               &ks->key,
+                               &session->opt->key_type,
+                               ks->key_src,
+                               client_sid,
+                               server_sid,
+                               server))
+    {
+      msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed");
+      return false;
+    }
+
+  return true;
+}
+
+static bool
 key_method_2_write (struct buffer *buf, struct tls_session *session)
 {
   struct key_state *ks = &session->key[KS_PRIMARY];       /* primary key */
@@ -1920,25 +2089,19 @@ key_method_2_write (struct buffer *buf, struct 
tls_session *session)
   /*
    * generate tunnel keys if server
    */
-  if (session->opt->server)
+  if (!session->opt->server)
+    goto done;
+
+  if (!ks->authenticated)
     {
-      if (ks->authenticated)
-       {
-         if (!generate_key_expansion (&ks->key,
-                                      &session->opt->key_type,
-                                      ks->key_src,
-                                      &ks->session_id_remote,
-                                      &session->session_id,
-                                      true))
-           {
-             msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion 
failed");
-             goto error;
-           }
-       }
-                     
       CLEAR (*ks->key_src);
+      goto done;
     }

+  if (!key_method_final(session, true))
+    goto error;
+
+ done:
   return true;

  error:
@@ -2122,34 +2285,15 @@ key_method_2_read (struct buffer *buf, struct tls_multi 
*multi, struct tls_sessi
   buf_clear (buf);

   /*
-   * Call OPENVPN_PLUGIN_TLS_FINAL plugin if defined, for final
-   * veto opportunity over authentication decision.
-   */
-  if (ks->authenticated && plugin_defined (session->opt->plugins, 
OPENVPN_PLUGIN_TLS_FINAL))
-    {
-      if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, 
NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
-       ks->authenticated = false;
-    }
-
-  /*
    * Generate tunnel keys if client
    */
-  if (!session->opt->server)
-    {
-      if (!generate_key_expansion (&ks->key,
-                                  &session->opt->key_type,
-                                  ks->key_src,
-                                  &session->session_id,
-                                  &ks->session_id_remote,
-                                  false))
-       {
-         msg (D_TLS_ERRORS, "TLS Error: client generate_key_expansion failed");
-         goto error;
-       }
-                     
-      CLEAR (*ks->key_src);
-    }
+  if (session->opt->server)
+    goto done;

+  if (!key_method_final(session, false))
+    goto error;
+
+ done:
   gc_free (&gc);
   return true;

diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 04ba789..ad4c8b1 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 */
+  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;
diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
index 1923230..e59f9e6 100644
--- a/src/openvpn/ssl_openssl.c
+++ b/src/openvpn/ssl_openssl.c
@@ -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.");
+    }
 }

 /*
-- 
1.7.1

Reply via email to