Skip to contentSkip to conversation

Add support for AEAD (GCM) cipher mode

PATCH New Steffan Karger 4 participants 43 messages Feb 7, 2016 – Mar 6, 2016 1 message not in the archive

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Add support for AEAD (GCM) cipher mode

Hi,

These patches add support for GCM mode ciphers to OpenVPN. These are originally inspired by the patch from kruton (trac #301, and
http://thread.gmane.org/gmane.network.openvpn.devel/7653), but most of the original code has been rewritten.

As discussed in various IRC meetings and at the hackathons, we used this opportunity to introduce a new - more efficient - packet format. See
http://sourceforge.net/p/openvpn/mailman/message/33210313/ and the commit message of patch 8 for more details.

The first patches (1-7) are refactoring in preparation of adding AEAD modes. Not all of the changes are strictly required, but they made it easier for me to understand what was going on and debug my AEAD code. I think they improve the understandability of the code. These should not change any behaviour (apart from adding better log messages).

Patch 8 actually adds the GCM cipher mode. See it's commit message for more information on the implementation.

Patch 9 provides polarssl/mbedtls and openssl config file interoperability.

Patch 10 adds a (very) preliminary version of cipher negotation. I'm not entirely sure if we should already apply this patch or wait for full cipher negotiation support. I'm also not sure when I will have proper negotiation patches available.

This implementation has been verified to be compatible with openvpn 3 clients and servers. To test this you'll need to pretend to fully support IV_NCP and IV_TCPNL, see e.g. https://github.com/syzzer/openvpn/tree/aead-cipher-modes13.

This has been a spare-time project, and did not yet receive any thorough review or field testing. So both are still very much needed.

-Steffan

#1 lore raw

PATCH 1/10 Allow NULL argument in cipher_ctx_get_cipher_kt()

Since otherwise we'll have to perform the check before each call.

---
 src/openvpn/crypto_backend.h  | 8 ++++----
 src/openvpn/crypto_openssl.c  | 2 +-
 src/openvpn/crypto_polarssl.c | 4 +---
 3 files changed, 6 insertions(+), 8 deletions(-)
src/openvpn/crypto_backend.h +4 -4
index 4c1ce9f..1c23436 100644
@@ -308,12 +308,12 @@ int cipher_ctx_mode (const cipher_ctx_t *ctx);
 /**
  * Returns the static cipher parameters for this context.
  *
- * @param ctx 		Cipher's context. May not be NULL.
+ * @param ctx 		Cipher's context.
  *
- * @return 		Static cipher parameters for the supplied context.
+ * @return 		Static cipher parameters for the supplied context, or
+ * 			NULL if unable to determine cipher parameters.
  */
-const cipher_kt_t *cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx)
-  __attribute__((nonnull));
+const cipher_kt_t *cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx);
 
 /**
  * Resets the given cipher context, setting the IV to the specified value.
src/openvpn/crypto_openssl.c +1 -1
index 1d68662..7dabe5d 100644
@@ -585,7 +585,7 @@ cipher_ctx_mode (const EVP_CIPHER_CTX *ctx)
 const cipher_kt_t *
 cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx)
 {
-  return EVP_CIPHER_CTX_cipher(ctx);
+  return ctx ? EVP_CIPHER_CTX_cipher(ctx) : NULL;
 }
 
 
src/openvpn/crypto_polarssl.c +1 -3
index 407a176..0e4c088 100644
@@ -506,9 +506,7 @@ int cipher_ctx_mode (const cipher_context_t *ctx)
 const cipher_kt_t *
 cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx)
 {
-  ASSERT(NULL != ctx);
-
-  return ctx->cipher_info;
+  return ctx ? ctx->cipher_info : NULL;
 }
 
 int cipher_ctx_reset (cipher_context_t *ctx, uint8_t *iv_buf)
Signature · 1 line
2.5.0
#2 lore raw

Re: [Openvpn-devel] [PATCH 01/10] Allow NULL argument in cipher_ctx_get_cipher_kt()

Am 07.02.16 um 20:47 schrieb Steffan Karger:

Quoted 3 lines

Since otherwise we'll have to perform the check before each call.

ACK. No changes in behaviour.

Arne

#3 lore raw

[Openvpn-devel] [PATCH applied] Re: Allow NULL argument in cipher_ctx_get_cipher_kt()

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit 70fbc5be209635739458267abde31b5cd4f770d0

Author: Steffan Karger
Date:   Sun Feb 7 20:47:09 2016 +0100
     Allow NULL argument in cipher_ctx_get_cipher_kt()

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11079
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#4 lore raw

PATCH 2/10 Remove reuse of key_type during init of data channel auth and tls-auth

In reply to Steffan Karger

Prepare for using AEAD cipher modes + tls-auth, as tls-auth might want to use an HMAC, while the data channel uses e.g. GCM tags. This separates the two initialisations. Also, error out (and give a clear error message) if a user specifies tls-auth but no valid auth algorithm, which makes no sense at all.

---
 src/openvpn/crypto.c  |  9 ++-------
 src/openvpn/init.c    | 25 +++++++++++++++++++------
 src/openvpn/openvpn.h |  1 +
 3 files changed, 22 insertions(+), 13 deletions(-)
src/openvpn/crypto.c +2 -7
index c18d88b..806a995 100644
@@ -751,13 +751,8 @@ get_tls_handshake_key (const struct key_type *key_type,
   if (passphrase_file && key_type->hmac_length)
     {
       struct key2 key2;
-      struct key_type kt = *key_type;
       struct key_direction_state kds;
 
-      /* for control channel we are only authenticating, not encrypting */
-      kt.cipher_length = 0;
-      kt.cipher = NULL;
-
       if (flags & GHK_INLINE)
 	{
 	  /* key was specified inline, key text is in passphrase_file */
@@ -800,9 +795,9 @@ get_tls_handshake_key (const struct key_type *key_type,
 
       /* initialize hmac key in both directions */
 
-      init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], &kt, OPENVPN_OP_ENCRYPT,
+      init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], key_type, OPENVPN_OP_ENCRYPT,
 		    "Outgoing Control Channel Authentication");
-      init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], &kt, OPENVPN_OP_DECRYPT,
+      init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], key_type, OPENVPN_OP_DECRYPT,
 		    "Incoming Control Channel Authentication");
 
       CLEAR (key2);
src/openvpn/init.c +19 -6
index d0020b7..7e6e448 100644
@@ -2206,11 +2206,23 @@ do_init_crypto_tls_c1 (struct context *c)
 	      flags |= GHK_INLINE;
 	      file = options->tls_auth_file_inline;
 	    }
-	  get_tls_handshake_key (&c->c1.ks.key_type,
-				 &c->c1.ks.tls_auth_key,
-				 file,
-				 options->key_direction,
-				 flags);
+
+	  /* Initialize key_type for tls-auth with auth only */
+	  CLEAR (c->c1.ks.tls_auth_key_type);
+	  if (options->authname && options->authname_defined)
+	    {
+	      c->c1.ks.tls_auth_key_type.digest = md_kt_get (options->authname);
+	      c->c1.ks.tls_auth_key_type.hmac_length =
+		  md_kt_size (c->c1.ks.tls_auth_key_type.digest);
+	    }
+	  else
+	    {
+	      msg (M_FATAL, "ERROR: tls-auth enabled, but no valid --auth "
+		  "algorithm specified ('%s')", options->authname);
+	    }
+
+	  get_tls_handshake_key (&c->c1.ks.tls_auth_key_type,
+	      &c->c1.ks.tls_auth_key, file, options->key_direction, flags);
 	}
 
 #if 0 /* was: #if ENABLE_INLINE_FILES --  Note that enabling this code will break restarts */
@@ -2375,7 +2387,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
       to.tls_auth.pid_persist = &c->c1.pid_persist;
       to.tls_auth.flags |= CO_PACKET_ID_LONG_FORM;
       crypto_adjust_frame_parameters (&to.frame,
-				      &c->c1.ks.key_type,
+				      &c->c1.ks.tls_auth_key_type,
 				      false, false, true, true);
     }
 
@@ -3758,6 +3770,7 @@ inherit_context_child (struct context *dest,
   /* inherit SSL context */
   dest->c1.ks.ssl_ctx = src->c1.ks.ssl_ctx;
   dest->c1.ks.tls_auth_key = src->c1.ks.tls_auth_key;
+  dest->c1.ks.tls_auth_key_type = src->c1.ks.tls_auth_key_type;
 #endif
 
   /* options */
src/openvpn/openvpn.h +1 -0
index 3f1df6e..71adf48 100644
@@ -66,6 +66,7 @@ struct key_schedule
   struct tls_root_ctx ssl_ctx;
 
   /* optional authentication HMAC key for TLS control channel */
+  struct key_type tls_auth_key_type;
   struct key_ctx_bi tls_auth_key;
 #else				/* ENABLE_CRYPTO */
   int dummy;
Signature · 1 line
2.5.0
#5 lore raw

Re: [Openvpn-devel] [PATCH 02/10] Remove reuse of key_type during init of data channel auth and tls-auth

Am 07.02.16 um 20:47 schrieb Steffan Karger:

Quoted 6 lines

Prepare for using AEAD cipher modes + tls-auth, as tls-auth might want to use an HMAC, while the data channel uses e.g. GCM tags. This separates the two initialisations. Also, error out (and give a clear error message) if a user specifies tls-auth but no valid auth algorithm, which makes no sense at all.

ACK.

Arne

#6 lore raw

[Openvpn-devel] [PATCH applied] Re: Remove reuse of key_type during init of data channel auth and tls-auth

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit e7d78e407d41d48fbd91a71b2edfedcd2879b778

Author: Steffan Karger
Date:   Sun Feb 7 20:47:10 2016 +0100
     Remove reuse of key_type during init of data channel auth and tls-auth

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11073
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#7 lore raw

PATCH 3/10 Move crypto_options into key_state and stop using context in SSL-mode.

In reply to Steffan Karger

Moving crypto_options into key_state enables us to stop using the global context for each packet encrypt/decrypt operation. Decoupling the crypto from the global context removes the need to copy the relevant parts of crypto_options for each processed packet, but instead enables us to just pass along a pointer to the related crypto_options.

This paves the way for an efficient GCM cipher mode implementation, but is probably fruitful too for threading and/or cipher negotiation.

---
 src/openvpn/forward.c    | 18 ++++++++++++++----
 src/openvpn/init.c       | 21 +++++++++++++--------
 src/openvpn/ssl.c        | 36 +++++++++++++++---------------------
 src/openvpn/ssl.h        | 15 +++++++--------
 src/openvpn/ssl_common.h |  2 ++
 5 files changed, 51 insertions(+), 41 deletions(-)
src/openvpn/forward.c +14 -4
index 36a99e6..75cd21d 100644
@@ -432,6 +432,7 @@ encrypt_sign (struct context *c, bool comp_frag)
 {
   struct context_buffers *b = c->c2.buffers;
   const uint8_t *orig_buf = c->c2.buf.data;
+  struct crypto_options *co = NULL;
 
 #if P2MP_SERVER
   /*
@@ -462,14 +463,18 @@ encrypt_sign (struct context *c, bool comp_frag)
    */
   if (c->c2.tls_multi)
     {
-      tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &c->c2.crypto_options);
+      tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &co);
+    }
+  else
+    {
+      co = &c->c2.crypto_options;
     }
 
   /*
    * Encrypt the packet and write an optional
    * HMAC signature.
    */
-  openvpn_encrypt (&c->c2.buf, b->encrypt_buf, &c->c2.crypto_options, &c->c2.frame);
+  openvpn_encrypt (&c->c2.buf, b->encrypt_buf, co, &c->c2.frame);
 #endif
   /*
    * Get the address we will be sending the packet to.
@@ -774,6 +779,7 @@ process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bo
    */
   if (c->c2.buf.len > 0)
     {
+      struct crypto_options *co = NULL;
       if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from))
 	link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from);
 
@@ -790,7 +796,7 @@ process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bo
 	   * will load crypto_options with the correct encryption key
 	   * and return false.
 	   */
-	  if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options, floated))
+	  if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &co, floated))
 	    {
 	      interval_action (&c->c2.tmp_int);
 
@@ -799,6 +805,10 @@ process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bo
 		event_timeout_reset (&c->c2.ping_rec_interval);
 	    }
 	}
+      else
+	{
+	  co = &c->c2.crypto_options;
+	}
 #if P2MP_SERVER
       /*
        * Drop non-TLS packet if client-connect script/plugin has not
@@ -809,7 +819,7 @@ process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bo
 #endif
 
       /* authenticate and decrypt the incoming packet */
-      decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, &c->c2.crypto_options, &c->c2.frame);
+      decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, co, &c->c2.frame);
 
       if (!decrypt_status && link_socket_connection_oriented (c->c2.link_socket))
 	{
src/openvpn/init.c +13 -8
index 7e6e448..8fc5c5d 100644
@@ -2048,14 +2048,6 @@ init_crypto_pre (struct context *c, const unsigned int flags)
 	packet_id_persist_load (&c->c1.pid_persist, c->options.packet_id_file);
     }
 
-  /* Initialize crypto options */
-
-  if (c->options.use_iv)
-    c->c2.crypto_options.flags |= CO_USE_IV;
-
-  if (c->options.mute_replay_warnings)
-    c->c2.crypto_options.flags |= CO_MUTE_REPLAY_WARNINGS;
-
 #ifdef ENABLE_PREDICTION_RESISTANCE
   if (c->options.use_prediction_resistance)
     rand_ctx_enable_prediction_resistance();
@@ -2074,6 +2066,13 @@ do_init_crypto_static (struct context *c, const unsigned int flags)
 
   init_crypto_pre (c, flags);
 
+  /* Initialize flags */
+  if (c->options.use_iv)
+    c->c2.crypto_options.flags |= CO_USE_IV;
+
+  if (c->options.mute_replay_warnings)
+    c->c2.crypto_options.flags |= CO_MUTE_REPLAY_WARNINGS;
+
   /* Initialize packet ID tracking */
   if (options->replay)
     {
@@ -2277,6 +2276,12 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
   /* Set all command-line TLS-related options */
   CLEAR (to);
 
+  if (options->use_iv)
+    to.crypto_flags |= CO_USE_IV;
+
+  if (options->mute_replay_warnings)
+    to.crypto_flags |= CO_MUTE_REPLAY_WARNINGS;
+
   to.crypto_flags_and = ~(CO_PACKET_ID_LONG_FORM);
   if (packet_id_long_form)
     to.crypto_flags_or = CO_PACKET_ID_LONG_FORM;
src/openvpn/ssl.c +15 -21
index d39f131..6aa9284 100644
@@ -786,6 +786,13 @@ key_state_init (struct tls_session *session, struct key_state *ks)
 		  session->opt->replay_time,
 		  "SSL", ks->key_id);
 
+  ks->crypto_options.key_ctx_bi = &ks->key;
+  ks->crypto_options.packet_id = session->opt->replay ? &ks->packet_id : NULL;
+  ks->crypto_options.pid_persist = NULL;
+  ks->crypto_options.flags = session->opt->crypto_flags;
+  ks->crypto_options.flags &= session->opt->crypto_flags_and;
+  ks->crypto_options.flags |= session->opt->crypto_flags_or;
+
 #ifdef MANAGEMENT_DEF_AUTH
   ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++;
 #endif
@@ -1700,6 +1707,8 @@ key_state_soft_reset (struct tls_session *session)
   ks->must_die = now + session->opt->transition_window; /* remaining lifetime of old key */
   key_state_free (ks_lame, false);
   *ks_lame = *ks;
+  ks_lame->crypto_options.key_ctx_bi = &ks_lame->key;
+  ks_lame->crypto_options.packet_id = &ks_lame->packet_id;
 
   key_state_init (session, ks);
   ks->session_id_remote = ks_lame->session_id_remote;
@@ -2808,7 +2817,7 @@ bool
 tls_pre_decrypt (struct tls_multi *multi,
 		 const struct link_socket_actual *from,
 		 struct buffer *buf,
-		 struct crypto_options *opt,
+		 struct crypto_options **opt,
 		 bool floated)
 {
   struct gc_arena gc = gc_new ();
@@ -2856,12 +2865,7 @@ tls_pre_decrypt (struct tls_multi *multi,
 		  && (floated || link_socket_actual_match (from, &ks->remote_addr)))
 		{
 		  /* return appropriate data channel decrypt key in opt */
-		  opt->key_ctx_bi = &ks->key;
-		  opt->packet_id = multi->opt.replay ? &ks->packet_id : NULL;
-		  opt->pid_persist = NULL;
-		  opt->flags &= multi->opt.crypto_flags_and;
-		  opt->flags |= multi->opt.crypto_flags_or;
-
+		  *opt = &ks->crypto_options;
 		  ASSERT (buf_advance (buf, 1));
 		  if (op == P_DATA_V2)
 		    {
@@ -3251,10 +3255,7 @@ tls_pre_decrypt (struct tls_multi *multi,
 
  done:
   buf->len = 0;
-  opt->key_ctx_bi = NULL;
-  opt->packet_id = NULL;
-  opt->pid_persist = NULL;
-  opt->flags &= multi->opt.crypto_flags_and;
+  *opt = NULL;
   gc_free (&gc);
   return ret;
 
@@ -3380,7 +3381,7 @@ tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
 /* Choose the key with which to encrypt a data packet */
 void
 tls_pre_encrypt (struct tls_multi *multi,
-		 struct buffer *buf, struct crypto_options *opt)
+		 struct buffer *buf, struct crypto_options **opt)
 {
   multi->save_ks = NULL;
   if (buf->len > 0)
@@ -3409,11 +3410,7 @@ tls_pre_encrypt (struct tls_multi *multi,
 
       if (ks_select)
 	{
-	  opt->key_ctx_bi = &ks_select->key;
-	  opt->packet_id = multi->opt.replay ? &ks_select->packet_id : NULL;
-	  opt->pid_persist = NULL;
-	  opt->flags &= multi->opt.crypto_flags_and;
-	  opt->flags |= multi->opt.crypto_flags_or;
+	  *opt = &ks_select->crypto_options;
 	  multi->save_ks = ks_select;
 	  dmsg (D_TLS_KEYSELECT, "TLS: tls_pre_encrypt: key_id=%d", ks_select->key_id);
 	  return;
@@ -3428,10 +3425,7 @@ tls_pre_encrypt (struct tls_multi *multi,
     }
 
   buf->len = 0;
-  opt->key_ctx_bi = NULL;
-  opt->packet_id = NULL;
-  opt->pid_persist = NULL;
-  opt->flags &= multi->opt.crypto_flags_and;
+  *opt = NULL;
 }
 
 /* Prepend the appropriate opcode to encrypted buffer prior to TCP/UDP send */
src/openvpn/ssl.h +7 -8
index 797c3e5..e9d0f28 100644
@@ -293,9 +293,8 @@ int tls_multi_process (struct tls_multi *multi,
  *     of this packet.
  * @param from - The source address of the packet.
  * @param buf - A buffer structure containing the incoming packet.
- * @param opt - A crypto options structure that will be loaded with the
- *     appropriate security parameters to handle the packet if it is a
- *     data channel packet.
+ * @param opt - Returns a crypto options structure with the appropriate security
+ *     parameters to handle the packet if it is a data channel packet.
  *
  * @return
  * @li True if the packet is a control channel packet that has been
@@ -306,7 +305,7 @@ int tls_multi_process (struct tls_multi *multi,
 bool tls_pre_decrypt (struct tls_multi *multi,
 		      const struct link_socket_actual *from,
 		      struct buffer *buf,
-		      struct crypto_options *opt,
+		      struct crypto_options **opt,
 		      bool floated);
 
 
@@ -356,15 +355,15 @@ bool tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
  * @ingroup data_crypto
  *
  * If no appropriate security parameters can be found, or if some other
- * error occurs, then the buffer is set to empty.
+ * error occurs, then the buffer is set to empty, and the parameters to a NULL
+ * pointer.
  *
  * @param multi - The TLS state for this packet's destination VPN tunnel.
  * @param buf - The buffer containing the outgoing packet.
- * @param opt - The crypto options structure into which the appropriate
- *     security parameters should be loaded.
+ * @param opt - Returns a crypto options structure with the security parameters.
  */
 void tls_pre_encrypt (struct tls_multi *multi,
-		      struct buffer *buf, struct crypto_options *opt);
+		      struct buffer *buf, struct crypto_options **opt);
 
 
 /**
src/openvpn/ssl_common.h +2 -0
index e2b0ebf..00d2ce8 100644
@@ -162,6 +162,7 @@ struct key_state
   struct link_socket_actual remote_addr; /* peer's IP addr */
   struct packet_id packet_id;	       /* for data channel, to prevent replay attacks */
 
+  struct crypto_options crypto_options;/* data channel crypto options */
   struct key_ctx_bi key;	       /* data channel keys for encrypt/decrypt/hmac */
 
   struct key_source2 *key_src;         /* source entropy for key expansion */
@@ -259,6 +260,7 @@ struct tls_options
   bool pass_config_info;
 
   /* struct crypto_option flags */
+  unsigned int crypto_flags;
   unsigned int crypto_flags_and;
   unsigned int crypto_flags_or;
 
Signature · 1 line
2.5.0
#8 lore raw

Re: [Openvpn-devel] [PATCH 03/10] Move crypto_options into key_state and stop using context in SSL-mode.

Am 07.02.16 um 20:47 schrieb Steffan Karger:

Quoted 8 lines

Moving crypto_options into key_state enables us to stop using the global context for each packet encrypt/decrypt operation. Decoupling the crypto from the global context removes the need to copy the relevant parts of crypto_options for each processed packet, but instead enables us to just pass along a pointer to the related crypto_options.

This paves the way for an efficient GCM cipher mode implementation, but is probably fruitful too for threading and/or cipher negotiation.

ACK from me.

Arne

#9 lore raw

[Openvpn-devel] [PATCH applied] Re: Move crypto_options into key_state and stop using context in SSL-mode.

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit 2d9c6d20e6e98f852930ea96dae9bd912d34068e

Author: Steffan Karger
Date:   Sun Feb 7 20:47:11 2016 +0100
     Move crypto_options into key_state and stop using context in SSL-mode.

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11075
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#10 lore raw

PATCH 4/10 Move key_ctx_bi into crypto_options

In reply to Steffan Karger

The encrypt and decrypt routines use struct crypto_options as their main information source. A struct crypto_options would have a pointer to a struct key_ctx_bi, which had to be updated at the correct moments to keep them correct. Instead of doing this administration, just put the struct key_ctx_bi inside crypto_options. Makes the code a little simpler too.

---
 src/openvpn/crypto.c     |  8 ++++----
 src/openvpn/crypto.h     |  2 +-
 src/openvpn/init.c       |  4 ++--
 src/openvpn/ssl.c        | 32 ++++++++++++++------------------
 src/openvpn/ssl.h        |  1 -
 src/openvpn/ssl_common.h |  2 --
 6 files changed, 21 insertions(+), 28 deletions(-)
src/openvpn/crypto.c +4 -4
index 806a995..9679fd0 100644
@@ -91,9 +91,9 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
   struct gc_arena gc;
   gc_init (&gc);
 
-  if (buf->len > 0 && opt->key_ctx_bi)
+  if (buf->len > 0 && opt)
     {
-      struct key_ctx *ctx = &opt->key_ctx_bi->encrypt;
+      const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
 
       /* Do Encrypt from buf -> work */
       if (ctx->cipher)
@@ -240,9 +240,9 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
   struct gc_arena gc;
   gc_init (&gc);
 
-  if (buf->len > 0 && opt->key_ctx_bi)
+  if (buf->len > 0 && opt)
     {
-      struct key_ctx *ctx = &opt->key_ctx_bi->decrypt;
+      const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt;
       struct packet_id_net pin;
       bool have_pin = false;
 
src/openvpn/crypto.h +1 -1
index b32a900..1f84284 100644
@@ -207,7 +207,7 @@ struct key_ctx_bi
  */
 struct crypto_options
 {
-  struct key_ctx_bi *key_ctx_bi;
+  struct key_ctx_bi key_ctx_bi;
                                 /**< OpenSSL cipher and HMAC contexts for
                                  *   both sending and receiving
                                  *   directions. */
src/openvpn/init.c +2 -2
index 8fc5c5d..dcc3ccb 100644
@@ -2134,7 +2134,7 @@ do_init_crypto_static (struct context *c, const unsigned int flags)
     }
 
   /* Get key schedule */
-  c->c2.crypto_options.key_ctx_bi = &c->c1.ks.static_key;
+  c->c2.crypto_options.key_ctx_bi = c->c1.ks.static_key;
 
   /* Compute MTU parameters */
   crypto_adjust_frame_parameters (&c->c2.frame,
@@ -2388,7 +2388,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
   /* TLS handshake authentication (--tls-auth) */
   if (options->tls_auth_file)
     {
-      to.tls_auth_key = c->c1.ks.tls_auth_key;
+      to.tls_auth.key_ctx_bi = c->c1.ks.tls_auth_key;
       to.tls_auth.pid_persist = &c->c1.pid_persist;
       to.tls_auth.flags |= CO_PACKET_ID_LONG_FORM;
       crypto_adjust_frame_parameters (&to.frame,
src/openvpn/ssl.c +14 -18
index 6aa9284..e3a745d 100644
@@ -786,7 +786,6 @@ key_state_init (struct tls_session *session, struct key_state *ks)
 		  session->opt->replay_time,
 		  "SSL", ks->key_id);
 
-  ks->crypto_options.key_ctx_bi = &ks->key;
   ks->crypto_options.packet_id = session->opt->replay ? &ks->packet_id : NULL;
   ks->crypto_options.pid_persist = NULL;
   ks->crypto_options.flags = session->opt->crypto_flags;
@@ -819,7 +818,7 @@ key_state_free (struct key_state *ks, bool clear)
 
   key_state_ssl_free(&ks->ks_ssl);
 
-  free_key_ctx_bi (&ks->key);
+  free_key_ctx_bi (&ks->crypto_options.key_ctx_bi);
   free_buf (&ks->plaintext_read_buf);
   free_buf (&ks->plaintext_write_buf);
   free_buf (&ks->ack_write_buf);
@@ -1072,9 +1071,6 @@ tls_multi_init (struct tls_options *tls_options)
   /* get command line derived options */
   ret->opt = *tls_options;
 
-  /* set up pointer to HMAC object for TLS packet authentication */
-  ret->opt.tls_auth.key_ctx_bi = &ret->opt.tls_auth_key;
-
   /* set up list of keys to be scanned by data channel encrypt and decrypt routines */
   ASSERT (SIZE (ret->key_scan) == 3);
   ret->key_scan[0] = &ret->session[TM_ACTIVE].key[KS_PRIMARY];
@@ -1113,8 +1109,7 @@ tls_auth_standalone_init (struct tls_options *tls_options,
   ALLOC_OBJ_CLEAR_GC (tas, struct tls_auth_standalone, gc);
 
   /* set up pointer to HMAC object for TLS packet authentication */
-  tas->tls_auth_key = tls_options->tls_auth_key;
-  tas->tls_auth_options.key_ctx_bi = &tas->tls_auth_key;
+  tas->tls_auth_options.key_ctx_bi = tls_options->tls_auth.key_ctx_bi;
   tas->tls_auth_options.flags |= CO_PACKET_ID_LONG_FORM;
 
   /* get initial frame parms, still need to finalize */
@@ -1197,11 +1192,11 @@ tls_multi_free (struct tls_multi *multi, bool clear)
 static bool
 swap_hmac (struct buffer *buf, const struct crypto_options *co, bool incoming)
 {
-  struct key_ctx *ctx;
+  const struct key_ctx *ctx;
 
   ASSERT (co);
 
-  ctx = (incoming ? &co->key_ctx_bi->decrypt : &co->key_ctx_bi->encrypt);
+  ctx = (incoming ? &co->key_ctx_bi.decrypt : &co->key_ctx_bi.encrypt);
   ASSERT (ctx->hmac);
 
   {
@@ -1265,7 +1260,7 @@ write_control_auth (struct tls_session *session,
   ASSERT (session_id_write_prepend (&session->session_id, buf));
   ASSERT (header = buf_prepend (buf, 1));
   *header = ks->key_id | (opcode << P_OPCODE_SHIFT);
-  if (session->tls_auth.key_ctx_bi->encrypt.hmac)
+  if (session->tls_auth.key_ctx_bi.encrypt.hmac)
     {
       /* no encryption, only write hmac */
       openvpn_encrypt (buf, null, &session->tls_auth, NULL);
@@ -1284,7 +1279,7 @@ read_control_auth (struct buffer *buf,
 {
   struct gc_arena gc = gc_new ();
 
-  if (co->key_ctx_bi->decrypt.hmac)
+  if (co->key_ctx_bi.decrypt.hmac)
     {
       struct buffer null = clear_buf ();
 
@@ -1707,7 +1702,6 @@ key_state_soft_reset (struct tls_session *session)
   ks->must_die = now + session->opt->transition_window; /* remaining lifetime of old key */
   key_state_free (ks_lame, false);
   *ks_lame = *ks;
-  ks_lame->crypto_options.key_ctx_bi = &ks_lame->key;
   ks_lame->crypto_options.packet_id = &ks_lame->packet_id;
 
   key_state_init (session, ks);
@@ -1806,8 +1800,9 @@ key_method_1_write (struct buffer *buf, struct tls_session *session)
       return false;
     }
 
-  init_key_ctx (&ks->key.encrypt, &key, &session->opt->key_type,
-		OPENVPN_OP_ENCRYPT, "Data Channel Encrypt");
+  init_key_ctx (&ks->crypto_options.key_ctx_bi.encrypt, &key,
+		&session->opt->key_type, OPENVPN_OP_ENCRYPT,
+		"Data Channel Encrypt");
   CLEAR (key);
 
   /* send local options string */
@@ -1969,7 +1964,7 @@ key_method_2_write (struct buffer *buf, struct tls_session *session)
     {
       if (ks->authenticated)
 	{
-	  if (!generate_key_expansion (&ks->key,
+	  if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi,
 				       &session->opt->key_type,
 				       ks->key_src,
 				       &ks->session_id_remote,
@@ -2040,8 +2035,9 @@ key_method_1_read (struct buffer *buf, struct tls_session *session)
 
   buf_clear (buf);
 
-  init_key_ctx (&ks->key.decrypt, &key, &session->opt->key_type,
-		OPENVPN_OP_DECRYPT, "Data Channel Decrypt");
+  init_key_ctx (&ks->crypto_options.key_ctx_bi.decrypt, &key,
+		&session->opt->key_type, OPENVPN_OP_DECRYPT,
+		"Data Channel Decrypt");
   CLEAR (key);
   ks->authenticated = true;
   return true;
@@ -2189,7 +2185,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
    */
   if (!session->opt->server)
     {
-      if (!generate_key_expansion (&ks->key,
+      if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi,
 				   &session->opt->key_type,
 				   ks->key_src,
 				   &session->session_id,
src/openvpn/ssl.h +0 -1
index e9d0f28..20991cc 100644
@@ -136,7 +136,6 @@
  */
 struct tls_auth_standalone
 {
-  struct key_ctx_bi tls_auth_key;
   struct crypto_options tls_auth_options;
   struct frame frame;
 };
src/openvpn/ssl_common.h +0 -2
index 00d2ce8..b40aec2 100644
@@ -163,7 +163,6 @@ struct key_state
   struct packet_id packet_id;	       /* for data channel, to prevent replay attacks */
 
   struct crypto_options crypto_options;/* data channel crypto options */
-  struct key_ctx_bi key;	       /* data channel keys for encrypt/decrypt/hmac */
 
   struct key_source2 *key_src;         /* source entropy for key expansion */
 
@@ -270,7 +269,6 @@ struct tls_options
 
   /* packet authentication for TLS handshake */
   struct crypto_options tls_auth;
-  struct key_ctx_bi tls_auth_key;
 
   /* frame parameters for TLS control channel */
   struct frame frame;
Signature · 1 line
2.5.0
#11 lore raw

Re: [Openvpn-devel] [PATCH 04/10] Move key_ctx_bi into crypto_options

Am 07.02.16 um 20:47 schrieb Steffan Karger:

Quoted 5 lines

The encrypt and decrypt routines use struct crypto_options as their main information source. A struct crypto_options would have a pointer to a struct key_ctx_bi, which had to be updated at the correct moments to keep them correct. Instead of doing this administration, just put the struct key_ctx_bi inside crypto_options. Makes the code a little simpler too.

ACK. I like simpler code :)

Arne

#12 lore raw

[Openvpn-devel] [PATCH applied] Re: Move key_ctx_bi into crypto_options

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit 8b1a00ca4be11f03238c27b0f9a54573b707ba89

Author: Steffan Karger
Date:   Sun Feb 7 20:47:12 2016 +0100
     Move key_ctx_bi into crypto_options

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11078
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#13 lore raw

PATCH 5/10 Move packet_id into crypto_options

In reply to Steffan Karger

Decouples struct key_state and struct crypto_options. No longer updating self-referential pointers!

---
 src/openvpn/crypto.c     | 45 ++++++++++++++++++++++++---------------------
 src/openvpn/crypto.h     | 10 ++++------
 src/openvpn/init.c       | 11 +++++------
 src/openvpn/openvpn.h    |  2 --
 src/openvpn/packet_id.h  |  6 ++++++
 src/openvpn/ssl.c        | 38 +++++++++++++-------------------------
 src/openvpn/ssl_common.h |  2 --
 7 files changed, 52 insertions(+), 62 deletions(-)
src/openvpn/crypto.c +24 -21
index 9679fd0..db52182 100644
@@ -85,8 +85,7 @@ memcmp_constant_time (const void *a, const void *b, size_t size) {
 
 void
 openvpn_encrypt (struct buffer *buf, struct buffer work,
-		 const struct crypto_options *opt,
-		 const struct frame* frame)
+		 struct crypto_options *opt, const struct frame* frame)
 {
   struct gc_arena gc;
   gc_init (&gc);
@@ -111,11 +110,11 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
 	      if (opt->flags & CO_USE_IV)
 		prng_bytes (iv_buf, iv_size);
 
-	      /* Put packet ID in plaintext buffer or IV, depending on cipher mode */
-	      if (opt->packet_id)
+	      /* Put packet ID in plaintext buffer */
+	      if (packet_id_initialized(&opt->packet_id))
 		{
 		  struct packet_id_net pin;
-		  packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
+		  packet_id_alloc_outgoing (&opt->packet_id.send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
 		  ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true));
 		}
 	    }
@@ -124,10 +123,11 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
 	      struct packet_id_net pin;
 	      struct buffer b;
 
-	      ASSERT (opt->flags & CO_USE_IV);    /* IV and packet-ID required */
-	      ASSERT (opt->packet_id); /*  for this mode. */
+	      /* IV and packet-ID required for this mode. */
+	      ASSERT (opt->flags & CO_USE_IV);
+	      ASSERT (packet_id_initialized(&opt->packet_id));
 
-	      packet_id_alloc_outgoing (&opt->packet_id->send, &pin, true);
+	      packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true);
 	      memset (iv_buf, 0, iv_size);
 	      buf_set_write (&b, iv_buf, iv_size);
 	      ASSERT (packet_id_write (&pin, &b, true, false));
@@ -189,10 +189,10 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
 	}
       else				/* No Encryption */
 	{
-	  if (opt->packet_id)
+	  if (packet_id_initialized(&opt->packet_id))
 	    {
 	      struct packet_id_net pin;
-	      packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
+	      packet_id_alloc_outgoing (&opt->packet_id.send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
 	      ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true));
 	    }
 	  work = *buf;
@@ -233,8 +233,7 @@ err:
  */
 bool
 openvpn_decrypt (struct buffer *buf, struct buffer work,
-		 const struct crypto_options *opt,
-		 const struct frame* frame)
+		 struct crypto_options *opt, const struct frame* frame)
 {
   static const char error_prefix[] = "Authenticate/Decrypt packet error";
   struct gc_arena gc;
@@ -246,6 +245,9 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
       struct packet_id_net pin;
       bool have_pin = false;
 
+      dmsg (D_PACKET_CONTENT, "DECRYPT FROM: %s",
+	  format_hex (BPTR (buf), BLEN (buf), 80, &gc));
+
       /* Verify the HMAC */
       if (ctx->hmac)
 	{
@@ -325,7 +327,7 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
 	  {
 	    if (cipher_kt_mode_cbc(cipher_kt))
 	      {
-		if (opt->packet_id)
+		if (packet_id_initialized(&opt->packet_id))
 		  {
 		    if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)))
 		      CRYPT_ERROR ("error reading CBC packet-id");
@@ -336,8 +338,9 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
 	      {
 		struct buffer b;
 
-		ASSERT (opt->flags & CO_USE_IV);    /* IV and packet-ID required */
-		ASSERT (opt->packet_id); /*  for this mode. */
+		/* IV and packet-ID required for this mode. */
+		ASSERT (opt->flags & CO_USE_IV);
+		ASSERT (packet_id_initialized(&opt->packet_id));
 
 		buf_set_read (&b, iv_buf, iv_size);
 		if (!packet_id_read (&pin, &b, true))
@@ -353,7 +356,7 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
       else
 	{
 	  work = *buf;
-	  if (opt->packet_id)
+	  if (packet_id_initialized(&opt->packet_id))
 	    {
 	      if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)))
 		CRYPT_ERROR ("error reading packet-id");
@@ -363,12 +366,12 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
       
       if (have_pin)
 	{
-	  packet_id_reap_test (&opt->packet_id->rec);
-	  if (packet_id_test (&opt->packet_id->rec, &pin))
+	  packet_id_reap_test (&opt->packet_id.rec);
+	  if (packet_id_test (&opt->packet_id.rec, &pin))
 	    {
-	      packet_id_add (&opt->packet_id->rec, &pin);
+	      packet_id_add (&opt->packet_id.rec, &pin);
 	      if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM))
-		packet_id_persist_save_obj (opt->pid_persist, opt->packet_id);
+		packet_id_persist_save_obj (opt->pid_persist, &opt->packet_id);
 	    }
 	  else
 	    {
@@ -688,7 +691,7 @@ key2_print (const struct key2* k,
 }
 
 void
-test_crypto (const struct crypto_options *co, struct frame* frame)
+test_crypto (struct crypto_options *co, struct frame* frame)
 {
   int i, j;
   struct gc_arena gc = gc_new ();
src/openvpn/crypto.h +4 -6
index 1f84284..aac50c4 100644
@@ -211,7 +211,7 @@ struct crypto_options
                                 /**< OpenSSL cipher and HMAC contexts for
                                  *   both sending and receiving
                                  *   directions. */
-  struct packet_id *packet_id;  /**< Current packet ID state for both
+  struct packet_id packet_id;   /**< Current packet ID state for both
                                  *   sending and receiving directions. */
   struct packet_id_persist *pid_persist;
                                 /**< Persistent packet ID state for
@@ -311,8 +311,7 @@ void free_key_ctx_bi (struct key_ctx_bi *ctx);
  *     error occurred.
  */
 void openvpn_encrypt (struct buffer *buf, struct buffer work,
-		      const struct crypto_options *opt,
-		      const struct frame* frame);
+		      struct crypto_options *opt, const struct frame* frame);
 
 
 /**
@@ -347,8 +346,7 @@ void openvpn_encrypt (struct buffer *buf, struct buffer work,
  *     an error occurred.
  */
 bool openvpn_decrypt (struct buffer *buf, struct buffer work,
-		      const struct crypto_options *opt,
-		      const struct frame* frame);
+		      struct crypto_options *opt, const struct frame* frame);
 
 /** @} name Functions for performing security operations on data channel packets */
 
@@ -397,7 +395,7 @@ void prng_bytes (uint8_t *output, int len);
 
 void prng_uninit ();
 
-void test_crypto (const struct crypto_options *co, struct frame* f);
+void test_crypto (struct crypto_options *co, struct frame* f);
 
 
 /* key direction functions */
src/openvpn/init.c +5 -6
index dcc3ccb..cb73a3d 100644
@@ -2076,16 +2076,15 @@ do_init_crypto_static (struct context *c, const unsigned int flags)
   /* Initialize packet ID tracking */
   if (options->replay)
     {
-      packet_id_init (&c->c2.packet_id,
+      packet_id_init (&c->c2.crypto_options.packet_id,
 		      link_socket_proto_connection_oriented (options->ce.proto),
 		      options->replay_window,
 		      options->replay_time,
 		      "STATIC", 0);
-      c->c2.crypto_options.packet_id = &c->c2.packet_id;
       c->c2.crypto_options.pid_persist = &c->c1.pid_persist;
       c->c2.crypto_options.flags |= CO_PACKET_ID_LONG_FORM;
       packet_id_persist_load_obj (&c->c1.pid_persist,
-				  c->c2.crypto_options.packet_id);
+				  &c->c2.crypto_options.packet_id);
     }
 
   if (!key_ctx_bi_defined (&c->c1.ks.static_key))
@@ -3007,7 +3006,7 @@ static void
 do_close_packet_id (struct context *c)
 {
 #ifdef ENABLE_CRYPTO
-  packet_id_free (&c->c2.packet_id);
+  packet_id_free (&c->c2.crypto_options.packet_id);
   packet_id_persist_save (&c->c1.pid_persist);
   if (!(c->sig->signal_received == SIGUSR1))
     packet_id_persist_close (&c->c1.pid_persist);
@@ -3923,13 +3922,13 @@ test_crypto_thread (void *arg)
   test_crypto (&c->c2.crypto_options, &c->c2.frame);
 
   key_schedule_free (&c->c1.ks, true);
-  packet_id_free (&c->c2.packet_id);
+  packet_id_free (&c->c2.crypto_options.packet_id);
 
   context_gc_free (c);
   return NULL;
 }
 
-#endif
+#endif /* ENABLE_CRYPTO */
 
 bool
 do_test_crypto (const struct options *o)
src/openvpn/openvpn.h +0 -2
index 71adf48..3281fd7 100644
@@ -361,8 +361,6 @@ struct context_2
                                  *   Channel Crypto module\endlink to
                                  *   process data channel packet. */
 
-  /* used to keep track of data channel packet sequence numbers */
-  struct packet_id packet_id;
   struct event_timeout packet_id_persist_interval;
 
 #endif /* ENABLE_CRYPTO */
src/openvpn/packet_id.h +6 -0
index 3ddaab6..5eb501d 100644
@@ -258,6 +258,12 @@ bool packet_id_write (const struct packet_id_net *pin, struct buffer *buf, bool
  * Inline functions.
  */
 
+/** Is this struct packet_id initialized? */
+static inline bool packet_id_initialized (const struct packet_id *pid)
+{
+  return pid->rec.initialized;
+}
+
 /* are we in enabled state? */
 static inline bool
 packet_id_persist_enabled (const struct packet_id_persist *p)
src/openvpn/ssl.c +13 -25
index e3a745d..7f99ee9 100644
@@ -780,13 +780,13 @@ key_state_init (struct tls_session *session, struct key_state *ks)
   reliable_set_timeout (ks->send_reliable, session->opt->packet_timeout);
 
   /* init packet ID tracker */
-  packet_id_init (&ks->packet_id,
-		  session->opt->tcp_mode,
-		  session->opt->replay_window,
-		  session->opt->replay_time,
-		  "SSL", ks->key_id);
+  if (session->opt->replay)
+    {
+      packet_id_init (&ks->crypto_options.packet_id, session->opt->tcp_mode,
+	  session->opt->replay_window, session->opt->replay_time, "SSL",
+	  ks->key_id);
+    }
 
-  ks->crypto_options.packet_id = session->opt->replay ? &ks->packet_id : NULL;
   ks->crypto_options.pid_persist = NULL;
   ks->crypto_options.flags = session->opt->crypto_flags;
   ks->crypto_options.flags &= session->opt->crypto_flags_and;
@@ -842,7 +842,7 @@ key_state_free (struct key_state *ks, bool clear)
   if (ks->key_src)
     free (ks->key_src);
 
-  packet_id_free (&ks->packet_id);
+  packet_id_free (&ks->crypto_options.packet_id);
 
 #ifdef PLUGIN_DEF_AUTH
   key_state_rm_auth_control_file (ks);
@@ -857,13 +857,6 @@ key_state_free (struct key_state *ks, bool clear)
 /** @} addtogroup control_processor */
 
 
-/*
- * Must be called if we move a tls_session in memory.
- */
-static inline void tls_session_set_self_referential_pointers (struct tls_session* session) {
-  session->tls_auth.packet_id = &session->tls_auth_pid;
-}
-
 /**
  * Returns whether or not the server should check for username/password
  *
@@ -936,18 +929,15 @@ tls_session_init (struct tls_multi *multi, struct tls_session *session)
   /* Initialize control channel authentication parameters */
   session->tls_auth = session->opt->tls_auth;
 
-  /* Set session internal pointers (also called if session object is moved in memory) */
-  tls_session_set_self_referential_pointers (session);
-
   /* initialize packet ID replay window for --tls-auth */
-  packet_id_init (session->tls_auth.packet_id,
+  packet_id_init (&session->tls_auth.packet_id,
 		  session->opt->tcp_mode,
 		  session->opt->replay_window,
 		  session->opt->replay_time,
 		  "TLS_AUTH", session->key_id);
 
   /* load most recent packet-id to replay protect on --tls-auth */
-  packet_id_persist_load_obj (session->tls_auth.pid_persist, session->tls_auth.packet_id);
+  packet_id_persist_load_obj (session->tls_auth.pid_persist, &session->tls_auth.packet_id);
 
   key_state_init (session, &session->key[KS_PRIMARY]);
 
@@ -974,8 +964,8 @@ tls_session_free (struct tls_session *session, bool clear)
 {
   int i;
 
-  if (session->tls_auth.packet_id)
-    packet_id_free (session->tls_auth.packet_id);
+  if (packet_id_initialized(&session->tls_auth.packet_id))
+    packet_id_free (&session->tls_auth.packet_id);
 
   for (i = 0; i < KS_SIZE; ++i)
     key_state_free (&session->key[i], false);
@@ -1006,7 +996,6 @@ move_session (struct tls_multi* multi, int dest, int src, bool reinit_src)
   ASSERT (dest >= 0 && dest < TM_SIZE);
   tls_session_free (&multi->session[dest], false);
   multi->session[dest] = multi->session[src];
-  tls_session_set_self_referential_pointers (&multi->session[dest]);
 
   if (reinit_src)
     tls_session_init (multi, &multi->session[src]);
@@ -1274,7 +1263,7 @@ write_control_auth (struct tls_session *session,
  */
 static bool
 read_control_auth (struct buffer *buf,
-		   const struct crypto_options *co,
+		   struct crypto_options *co,
 		   const struct link_socket_actual *from)
 {
   struct gc_arena gc = gc_new ();
@@ -1702,7 +1691,6 @@ key_state_soft_reset (struct tls_session *session)
   ks->must_die = now + session->opt->transition_window; /* remaining lifetime of old key */
   key_state_free (ks_lame, false);
   *ks_lame = *ks;
-  ks_lame->crypto_options.packet_id = &ks_lame->packet_id;
 
   key_state_init (session, ks);
   ks->session_id_remote = ks_lame->session_id_remote;
@@ -2257,7 +2245,7 @@ tls_process (struct tls_multi *multi,
 	   && ks->n_bytes >= session->opt->renegotiate_bytes)
        || (session->opt->renegotiate_packets
 	   && ks->n_packets >= session->opt->renegotiate_packets)
-       || (packet_id_close_to_wrapping (&ks->packet_id.send))))
+       || (packet_id_close_to_wrapping (&ks->crypto_options.packet_id.send))))
     {
       msg (D_TLS_DEBUG_LOW,
            "TLS: soft reset sec=%d bytes=" counter_format "/%d pkts=" counter_format "/%d",
src/openvpn/ssl_common.h +0 -2
index b40aec2..eaf4a91 100644
@@ -160,7 +160,6 @@ struct key_state
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id_remote;   /* peer's random session ID */
   struct link_socket_actual remote_addr; /* peer's IP addr */
-  struct packet_id packet_id;	       /* for data channel, to prevent replay attacks */
 
   struct crypto_options crypto_options;/* data channel crypto options */
 
@@ -366,7 +365,6 @@ struct tls_session
 
   /* authenticate control packets */
   struct crypto_options tls_auth;
-  struct packet_id tls_auth_pid;
 
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id;	/* our random session ID */
Signature · 1 line
2.5.0
#14 lore raw

Re: [Openvpn-devel] [PATCH 05/10] Move packet_id into crypto_options

Am 07.02.16 um 20:47 schrieb Steffan Karger:

Quoted 3 lines

Decouples struct key_state and struct crypto_options. No longer updating self-referential pointers!

ACK.

Arne

#15 lore raw

[Openvpn-devel] [PATCH applied] Re: Move packet_id into crypto_options

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit 3ebc31f9591ce11b0673dc20e76022c13bdb2c37

Author: Steffan Karger
Date:   Sun Feb 7 20:47:13 2016 +0100
     Move packet_id into crypto_options

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11082
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#16 lore raw

PATCH 6/10 Change openvpn_encrypt() to append to work buffer only

In reply to Steffan Karger

Preparation for AEAD cipher modes, which also have to authenticate the opcode and peer-id of packets. To supply that information to openvpn_encrypt(), I want to simply write those to the work buffer before calling openvpn_encrypt(). That however requires that openvpn_encrypt() never prepends something to the work buffer.

---
 src/openvpn/crypto.c | 55 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 32 insertions(+), 23 deletions(-)
src/openvpn/crypto.c +32 -23
index db52182..e92125e 100644
@@ -93,6 +93,8 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
   if (buf->len > 0 && opt)
     {
       const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
+      uint8_t *mac_out = NULL;
+      const uint8_t *hmac_start = NULL;
 
       /* Do Encrypt from buf -> work */
       if (ctx->cipher)
@@ -102,6 +104,17 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
 	  const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher);
 	  int outlen;
 
+	  /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
+	  ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
+
+	  /* Reserve space for HMAC */
+	  if (ctx->hmac)
+	    {
+	      mac_out = buf_write_alloc (&work, hmac_ctx_size(ctx->hmac));
+	      ASSERT (mac_out);
+	      hmac_start = BEND(&work);
+	    }
+
 	  if (cipher_kt_mode_cbc(cipher_kt))
 	    {
 	      CLEAR (iv_buf);
@@ -137,12 +150,12 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
 	      ASSERT (0);
 	    }
 
-	  /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
-	  ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
-
 	  /* set the IV pseudo-randomly */
 	  if (opt->flags & CO_USE_IV)
-	    dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc));
+            {
+              ASSERT (buf_write(&work, iv_buf, iv_size));
+              dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc));
+            }
 
 	  dmsg (D_PACKET_CONTENT, "ENCRYPT FROM: %s",
 	       format_hex (BPTR (buf), BLEN (buf), 80, &gc));
@@ -165,27 +178,16 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
 	    }
 
 	  /* Encrypt packet ID, payload */
-	  ASSERT (cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf)));
+	  ASSERT (cipher_ctx_update (ctx->cipher, BEND (&work), &outlen, BPTR (buf), BLEN (buf)));
 	  ASSERT (buf_inc_len(&work, outlen));
 
 	  /* Flush the encryption buffer */
-	  ASSERT (cipher_ctx_final(ctx->cipher, BPTR (&work) + outlen, &outlen));
+	  ASSERT (cipher_ctx_final(ctx->cipher, BEND (&work), &outlen));
 	  ASSERT (buf_inc_len(&work, outlen));
 
 	  /* For all CBC mode ciphers, check the last block is complete */
 	  ASSERT (cipher_kt_mode (cipher_kt) != OPENVPN_MODE_CBC ||
 	      outlen == iv_size);
-
-	  /* prepend the IV to the ciphertext */
-	  if (opt->flags & CO_USE_IV)
-	    {
-	      uint8_t *output = buf_prepend (&work, iv_size);
-	      ASSERT (output);
-	      memcpy (output, iv_buf, iv_size);
-	    }
-
-	  dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s",
-	       format_hex (BPTR (&work), BLEN (&work), 80, &gc));
 	}
       else				/* No Encryption */
 	{
@@ -195,22 +197,29 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
 	      packet_id_alloc_outgoing (&opt->packet_id.send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
 	      ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true));
 	    }
+	  if (ctx->hmac)
+	    {
+	      hmac_start = BPTR(buf);
+	      ASSERT (mac_out = buf_prepend (buf, hmac_ctx_size(ctx->hmac)));
+	    }
+	  buf_write_prepend(buf, BPTR(&work), BLEN(&work));
 	  work = *buf;
 	}
 
       /* HMAC the ciphertext (or plaintext if !cipher) */
       if (ctx->hmac)
 	{
-	  uint8_t *output = NULL;
-
 	  hmac_ctx_reset (ctx->hmac);
-	  hmac_ctx_update (ctx->hmac, BPTR(&work), BLEN(&work));
-	  output = buf_prepend (&work, hmac_ctx_size(ctx->hmac));
-	  ASSERT (output);
-	  hmac_ctx_final (ctx->hmac, output);
+	  hmac_ctx_update (ctx->hmac, hmac_start, BEND(&work) - hmac_start);
+	  hmac_ctx_final (ctx->hmac, mac_out);
+	  dmsg (D_PACKET_CONTENT, "ENCRYPT HMAC: %s",
+	      format_hex (mac_out, hmac_ctx_size(ctx->hmac), 80, &gc));
 	}
 
       *buf = work;
+
+      dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s",
+	  format_hex (BPTR (&work), BLEN (&work), 80, &gc));
     }
 
   gc_free (&gc);
Signature · 1 line
2.5.0
#17 lore raw

Re: [Openvpn-devel] [PATCH 06/10] Change openvpn_encrypt() to append to work buffer only

Am 07.02.16 um 20:47 schrieb Steffan Karger:

Quoted 5 lines

Preparation for AEAD cipher modes, which also have to authenticate the opcode and peer-id of packets. To supply that information to openvpn_encrypt(), I want to simply write those to the work buffer before calling openvpn_encrypt(). That however requires that openvpn_encrypt() never prepends something to the work buffer.

ACK.

Arne

#18 lore raw

[Openvpn-devel] [PATCH applied] Re: Change openvpn_encrypt() to append to work buffer only

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit a070f75b7dbb06161b9e2009124ad82277054524

Author: Steffan Karger
Date:   Sun Feb 7 20:47:14 2016 +0100
     Change openvpn_encrypt() to append to work buffer only

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11074
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#19 lore raw

PATCH 7/10 Create separate function for replay check

In reply to Steffan Karger

In preparation for AEAD cipher modes, which will need the same functionality.

Should not change any behaviour.

---
 src/openvpn/crypto.c | 52 +++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 37 insertions(+), 15 deletions(-)
src/openvpn/crypto.c +37 -15
index e92125e..9c0c353 100644
@@ -232,6 +232,41 @@ err:
   return;
 }
 
+/**
+ * Check packet ID for replay, and perform replay administration.
+ *
+ * @param opt	Crypto options for this packet, contains replay state.
+ * @param pin	Packet ID read from packet.
+ * @param error_prefix	Prefix to use when printing error messages.
+ * @param gc	Garbage collector to use.
+ *
+ * @return true if packet ID is validated to be not a replay, false otherwise.
+ */
+static bool crypto_check_replay(struct crypto_options *opt,
+    const struct packet_id_net *pin, const char *error_prefix,
+    struct gc_arena *gc) {
+  bool ret = false;
+  packet_id_reap_test (&opt->packet_id.rec);
+  if (packet_id_test (&opt->packet_id.rec, pin))
+    {
+      packet_id_add (&opt->packet_id.rec, pin);
+      if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM))
+	packet_id_persist_save_obj (opt->pid_persist, &opt->packet_id);
+      ret = true;
+    }
+  else
+    {
+      if (!(opt->flags & CO_MUTE_REPLAY_WARNINGS))
+	{
+	  msg (D_REPLAY_ERRORS, "%s: bad packet ID (may be a replay): %s -- "
+	      "see the man page entry for --no-replay and --replay-window for "
+	      "more info or silence this warning with --mute-replay-warnings",
+	      error_prefix, packet_id_net_print (pin, true, gc));
+	}
+    }
+  return ret;
+}
+
 /*
  * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet.
  *
@@ -373,22 +408,9 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
 	    }
 	}
       
-      if (have_pin)
+      if (have_pin && !crypto_check_replay(opt, &pin, error_prefix, &gc))
 	{
-	  packet_id_reap_test (&opt->packet_id.rec);
-	  if (packet_id_test (&opt->packet_id.rec, &pin))
-	    {
-	      packet_id_add (&opt->packet_id.rec, &pin);
-	      if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM))
-		packet_id_persist_save_obj (opt->pid_persist, &opt->packet_id);
-	    }
-	  else
-	    {
-	      if (!(opt->flags & CO_MUTE_REPLAY_WARNINGS))
-	      msg (D_REPLAY_ERRORS, "%s: bad packet ID (may be a replay): %s -- see the man page entry for --no-replay and --replay-window for more info or silence this warning with --mute-replay-warnings",
-		   error_prefix, packet_id_net_print (&pin, true, &gc));
-	      goto error_exit;
-	    }
+	  goto error_exit;
 	}
       *buf = work;
     }
Signature · 1 line
2.5.0
#20 lore raw

Re: [Openvpn-devel] [PATCH 07/10] Create separate function for replay check

Am 07.02.16 um 20:47 schrieb Steffan Karger:

Quoted 4 lines

In preparation for AEAD cipher modes, which will need the same functionality.

Should not change any behaviour.

ACK. Passing gc instead of using an own gc does not really matter in this case.

Arne

A message that is not in the archive (sent off-list or not yet received): [email protected]...

#22 lore raw

Re: [Openvpn-devel] [PATCH 07/10] Create separate function for replay check

Hi,

Quoted 8 lines · Arne Schwabe

Am 07.02.16 um 20:47 schrieb Steffan Karger:

Quoted 4 lines

In preparation for AEAD cipher modes, which will need the same functionality.

Should not change any behaviour.

ACK. Passing gc instead of using an own gc does not really matter in this case.

I chose to pass the gc for performance reasons. Passing the gc should be faster (even 'free' if the compiler decides to inline). This is in the critical path for packet processing, so I tried to keep overhead to a minimum.

-Steffan

#23 lore raw

[Openvpn-devel] [PATCH applied] Re: Create separate function for replay check

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit de4cbd62b7d120e1ec083eaf7688ef3d18e03288

Author: Steffan Karger
Date:   Sun Feb 7 20:47:15 2016 +0100
     Create separate function for replay check

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11076
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#24 lore raw

PATCH 8/10 Add AEAD cipher support (GCM)

In reply to Steffan Karger

Add Authenticated Encryption with Additional Data (AEAD) support for ciphers, which obviates the need for a separate HMAC step. The MAC is integrated into the cipher and the MAC tag is prepended to the payload.

This patch is inspired by the patch originally submitted by Kenny Root on the openvpn-devel mailinglist, but does a number things differently:
* Don't support XTS (makes no sense for VPN)
* Don't support CCM (needs extra code to make it actually work)
* Don't force the user to specify "auth none" (that would break tls-auth)
* Add support for PolarSSL (and change internal API for this)
* Update openvpn frame size ('link mtu') calculation for AEAD modes
* Use the HMAC key as an implicit part of the IV to save 8 bytes per data channel network packet.
* Also authenticate the opcode/peer-id as AD in P_DATA_V2 packets.

By using the negotiated HMAC key as an implicit part of the IV for AEAD-mode ciphers in TLS mode, we can save (at least) 8 bytes on each packet sent. This is particularly interesting for connections which transfer many small packets, such as remote desktop or voip connections.

The current AEAD-mode ciphers (for now GCM) are based on CTR-mode cipher operation, which requires the IV to be unique (but does not require unpredictability).

IV uniqueness is guaranteed by using a combination of at least 64-bits of the HMAC key (unique per TLS session), and a 32-bit packet counter. The last 32-bit word of the 128-bit cipher block is not part of the IV, but is used as a block counter.

AEAD cipher mode is not available for static key mode, since IV uniqueness is harder the guarantee over sessions, and I believe supporting AEAD in static key mode too is not worth the extra complexity. Modern setups should simply use TLS mode.

---
 configure.ac                  |  30 +++-
 src/openvpn/crypto.c          | 353 +++++++++++++++++++++++++++++++++++++++---
 src/openvpn/crypto.h          |  65 ++++++--
 src/openvpn/crypto_backend.h  |  69 +++++++++
 src/openvpn/crypto_openssl.c  |  85 +++++++++-
 src/openvpn/crypto_openssl.h  |   7 +
 src/openvpn/crypto_polarssl.c | 105 +++++++++++--
 src/openvpn/crypto_polarssl.h |   3 +
 src/openvpn/forward.c         |  49 +++---
 src/openvpn/ssl.c             | 102 +++++++++---
 src/openvpn/ssl.h             |  42 ++++-
 11 files changed, 810 insertions(+), 100 deletions(-)
configure.ac +29 -1
index 7ff2435..3609207 100644
@@ -93,6 +93,13 @@ AC_ARG_ENABLE(
 )
 
 AC_ARG_ENABLE(
+	[aead-modes],
+	[AS_HELP_STRING([--disable-aead-modes], [disable AEAD crypto modes @<:@default=yes@:>@])],
+	,
+	[enable_aead_modes="yes"]
+)
+
+AC_ARG_ENABLE(
 	[x509-alt-username],
 	[AS_HELP_STRING([--enable-x509-alt-username], [enable the --x509-username-field feature @<:@default=no@:>@])],
 	,
@@ -821,6 +828,13 @@ if test "${with_crypto_library}" = "openssl"; then
 		AC_DEFINE([HAVE_OPENSSL_ENGINE], [1], [OpenSSL engine support available])
 	fi
 
+	have_crypto_aead_modes="yes"
+	AC_CHECK_FUNCS(
+		[EVP_aes_256_gcm],
+		,
+		[have_crypto_aead_modes="no"; break]
+	)
+
 	CFLAGS="${saved_CFLAGS}"
 	LIBS="${saved_LIBS}"
 
@@ -897,9 +911,19 @@ elif test "${with_crypto_library}" = "polarssl"; then
 			AC_MSG_ERROR([PolarSSL compiled with PKCS11, while OpenVPN is not])
 		fi
 	fi
+
+	have_crypto_aead_modes="yes"
+	AC_CHECK_FUNCS(
+		[ \
+			cipher_write_tag \
+			cipher_check_tag \
+		],
+		,
+		[have_crypto_aead_modes="no"; break]
+	)
+
 	CFLAGS="${saved_CFLAGS}"
 	LIBS="${saved_LIBS}"
-
 	have_crypto="yes"
 	AC_DEFINE([ENABLE_CRYPTO_POLARSSL], [1], [Use PolarSSL library])
 	CRYPTO_CFLAGS="${POLARSSL_CFLAGS}"
@@ -1054,6 +1078,10 @@ test "${enable_strict_options}" = "yes" && AC_DEFINE([ENABLE_STRICT_OPTIONS_CHEC
 if test "${enable_crypto}" = "yes"; then
 	test "${have_crypto}" != "yes" && AC_MSG_ERROR([${with_crypto_library} crypto is required but missing])
 	test "${enable_crypto_ofb_cfb}" = "yes" && AC_DEFINE([ENABLE_OFB_CFB_MODE], [1], [Enable OFB and CFB cipher modes])
+	if test "${enable_aead_modes}" = "yes"; then
+		test "${have_crypto_aead_modes}" = "yes" && AC_DEFINE([HAVE_AEAD_CIPHER_MODES], [1], [Use crypto library])
+		test "${have_crypto_aead_modes}" != "yes" && AC_MSG_ERROR([AEAD modes required but missing])
+	fi
 	OPTIONAL_CRYPTO_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${CRYPTO_CFLAGS}"
 	OPTIONAL_CRYPTO_LIBS="${OPTIONAL_CRYPTO_LIBS} ${CRYPTO_LIBS}"
 	AC_DEFINE([ENABLE_CRYPTO], [1], [Enable crypto library])
src/openvpn/crypto.c +330 -23

444 lines not on this page · raw message

src/openvpn/crypto.h +54 -11

125 lines not on this page · raw message

src/openvpn/crypto_backend.h +69 -0

105 lines not on this page · raw message

src/openvpn/crypto_openssl.c +77 -8

132 lines not on this page · raw message

src/openvpn/crypto_openssl.h +7 -0
index 42c7e9a..f157041 100644
@@ -61,6 +61,13 @@ typedef HMAC_CTX hmac_ctx_t;
 /** Cipher is in CFB mode */
 #define OPENVPN_MODE_CFB 	EVP_CIPH_CFB_MODE
 
+#ifdef HAVE_AEAD_CIPHER_MODES
+
+/** Cipher is in GCM mode */
+#define OPENVPN_MODE_GCM 	EVP_CIPH_GCM_MODE
+
+#endif /* HAVE_AEAD_CIPHER_MODES */
+
 /** Cipher should encrypt */
 #define OPENVPN_OP_ENCRYPT 	1
 
src/openvpn/crypto_polarssl.c +95 -10

146 lines not on this page · raw message

src/openvpn/crypto_polarssl.h +3 -0
index 94306ed..7be0862 100644
@@ -61,6 +61,9 @@ typedef md_context_t hmac_ctx_t;
 /** Cipher is in CFB mode */
 #define OPENVPN_MODE_CFB 	POLARSSL_MODE_CFB
 
+/** Cipher is in GCM mode */
+#define OPENVPN_MODE_GCM	POLARSSL_MODE_GCM
+
 /** Cipher should encrypt */
 #define OPENVPN_OP_ENCRYPT 	POLARSSL_ENCRYPT
 
src/openvpn/forward.c +25 -24

88 lines not on this page · raw message

src/openvpn/ssl.c +82 -20

167 lines not on this page · raw message

src/openvpn/ssl.h +39 -3

60 lines not on this page · raw message

Signature · 1 line
2.5.0
#25 lore raw

Re: [Openvpn-devel] [PATCH 08/10] Add AEAD cipher support (GCM)

Overall the code looks good and works well in my tests. A few remarks (but ACK otherwise):

Quoted 7 lines

AC_ARG_ENABLE(
[aead-modes],
[AS_HELP_STRING([--disable-aead-modes], [disable AEAD crypto modes
@<:@default=yes@:>@])],

    ,
    [enable_aead_modes="yes"]

)

I would not make this user configurable. If it wasn't for RHEL5 I even would throw out 0.9.8 support and always compile it in.

     {
+      /* Get the key we will use to encrypt the packet. */
       tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &co);

+ /* If using P_DATA_V2, prepend the 1-byte opcode and 3-byte peer-id to the
+ * packet before openvpn_encrypt(), so we can authenticate the opcode too.
+ */
+ if (c->c2.buf.len > 0 && !c->c2.tls_multi->opt.server && c->c2.tls_multi->use_peer_id)

The latter part of that if might be turned into a small inline fuction to make it clearer. Or alternatively a local boolean variable to be used in both if places.

Quoted 7 lines
  /* Prepare IV */
  {
    struct buffer iv_buffer;
    struct packet_id_net pin;
    const int iv_len = cipher_ctx_iv_length (ctx->cipher);
    ASSERT (iv_len >= 12 && iv_len <= OPENVPN_MAX_IV_LENGTH);

A comment about the 12 would be nice here. (=> 96 bits ....)

Quoted 1 line
    /* set_tag() call required for older OpenSSL versions only */

What does older mean in this context? Add an idef or atleast specify the version in the file.

Quoted 1 line
    const int ad_size = BPTR (buf) - ad_start - tag_size;

ad_size can be 0. Did you check what hte PolarSSL/OpenSSL function do on a 0 byte call?

Also add for the manpage:

doc/openvpn.8 +3 -0
index 6b97fc9..30faff3 100644
@@ -3991,6 +3991,10 @@ Set
 .B alg=none
 to disable authentication.
 
+If a AEAD
+.B \-\-cipher
+is selected like AES-128-GCM is used the authentication of that cipher

is used for the data channel packets.
+
For more information on HMAC see
.I http://www.cs.ucsd.edu/users/mihir/papers/hmac.html
.\"*********************************************************

Arne

#26 lore raw

Re: [Openvpn-devel] [PATCH 08/10] Add AEAD cipher support (GCM)

Hi Arne,

Thanks for reviewing. Attached a v2, and below inline responses to your remarks.

Quoted 10 lines · Arne Schwabe
Quoted 7 lines

AC_ARG_ENABLE(
[aead-modes],
[AS_HELP_STRING([--disable-aead-modes], [disable AEAD crypto modes
@<:@default=yes@:>@])],

    ,
    [enable_aead_modes="yes"]

)

I would not make this user configurable. If it wasn't for RHEL5 I even would throw out 0.9.8 support and always compile it in.

I agree. I removed the configurability.

Now that OpenSSL has officially stopped supporting 0.9.8 and 1.0.0 (as of 31st Dec 2015), we might want to revisit our decision to support those in 2.4/master, but let's not do that as a part of this patch set :)

Quoted 11 lines
     {
(quoted diff) +5 -0
+      /* Get the key we will use to encrypt the packet. */
       tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &co);
+      /* If using P_DATA_V2, prepend the 1-byte opcode and 3-byte peer-id to the
+       * packet before openvpn_encrypt(), so we can authenticate the opcode too.
+       */
+      if (c->c2.buf.len > 0 && !c->c2.tls_multi->opt.server && c->c2.tls_multi->use_peer_id)

The latter part of that if might be turned into a small inline fuction to make it clearer. Or alternatively a local boolean variable to be used in both if places.

Hm, I tried that but don't really like it. I'd rather simplify this by making server->client traffic use P_DATA_V2 too (but didn't want to do that in this patch set).

Quoted 8 lines
Quoted 7 lines
  /* Prepare IV */
  {
    struct buffer iv_buffer;
    struct packet_id_net pin;
    const int iv_len = cipher_ctx_iv_length (ctx->cipher);
    ASSERT (iv_len >= 12 && iv_len <= OPENVPN_MAX_IV_LENGTH);

A comment about the 12 would be nice here. (=> 96 bits ....)

Replaced with OPENVPN_AEAD_MIN_IV_LEN.

Quoted 3 lines
Quoted 1 line
    /* set_tag() call required for older OpenSSL versions only */

What does older mean in this context? Add an idef or atleast specify the version in the file.

OpenSSL versions 1.0.1-1.0.1c have a bug that makes a cipher update call fail is no AEAD tag was set, while there is no need to set such a tag at that point. As of 1.0.1d (released Feb 2013), this has been resolved. In the end (after short discussion on IRC), I decided to remove this extra code, which was there just for this few buggy openssl versions.

Quoted 4 lines
Quoted 1 line
    const int ad_size = BPTR (buf) - ad_start - tag_size;

ad_size can be 0. Did you check what hte PolarSSL/OpenSSL function do on a 0 byte call?

Yes, both become a no-op.

Quoted 13 lines

Also add for the manpage:

doc/openvpn.8 +3 -0
index 6b97fc9..30faff3 100644
@@ -3991,6 +3991,10 @@ Set
 .B alg=none
 to disable authentication.
 
+If a AEAD
+.B \-\-cipher
+is selected like AES-128-GCM is used the authentication of that cipher
Quoted 5 lines

is used for the data channel packets.
+
For more information on HMAC see
.I http://www.cs.ucsd.edu/users/mihir/papers/hmac.html
.\"*********************************************************

Good point. I took the opportunity to also explain a bit more about the relation between --auth and --tls-auth.

-Steffan

From 4b20fa7444d7b6c9a1c9f28c89d35202b03b6637 Mon Sep 17 00:00:00 2001
From: Steffan Karger <[email protected]>
Date: Sat, 24 Oct 2015 16:44:09 +0200
Subject: [PATCH 8/10 v2] Add AEAD cipher support (GCM)

Add Authenticated Encryption with Additional Data (AEAD) support for ciphers, which removes the need for a separate HMAC step. The MAC is integrated into the cipher and the MAC tag is prepended to the payload.

This patch is inspired by the patch originally submitted by Kenny Root on the openvpn-devel mailinglist, but does a number things differently:
* Don't support XTS (makes no sense for VPN)
* Don't support CCM (needs extra code to make it actually work)
* Don't force the user to specify "auth none" (that would break tls-auth)
* Add support for PolarSSL (and change internal API for this)
* Update openvpn frame size ('link mtu') calculation for AEAD modes
* Use the HMAC key as an implicit part of the IV to save 8 bytes per data channel network packet.
* Also authenticate the opcode/peer-id as AD in P_DATA_V2 packets.

By using the negotiated HMAC key as an implicit part of the IV for AEAD-mode ciphers in TLS mode, we can save (at least) 8 bytes on each packet sent. This is particularly interesting for connections which transfer many small packets, such as remote desktop or voip connections.

The current AEAD-mode ciphers (for now GCM) are based on CTR-mode cipher operation, which requires the IV to be unique (but does not require unpredictability).

IV uniqueness is guaranteed by using a combination of at least 64-bits of the HMAC key (unique per TLS session), and a 32-bit packet counter. The last 32-bit word of the 128-bit cipher block is not part of the IV, but is used as a block counter.

AEAD cipher mode is not available for static key mode, since IV uniqueness is harder the guarantee over sessions, and I believe supporting AEAD in static key mode too is not worth the extra complexity. Modern setups should simply use TLS mode.

OpenSSL 1.0.1-1.0.1c will not work with AEAD mode, because those versions have an unnecessary check that fails to update the cipher if the tag was not already set. 1.0.1d, which fixes that, was released in February 2013. People should have updated, and distros should have backported the fix by now.

Changes in v2:
* Remove extra code that was just for making OpenSSL 1.0.1-1.0.1c work in AEAD mode.
* Do not make AEAD support configurable in ./configure.
* Get rid of '12' magic constant in openvpn_encrypt_aead().
* Update manpage to explain that --auth is ignored for the data channel when using an AEAD cipher.
* Move setting the IV in AEAD cipher modes to the IV generation code. This is a more natural place and now we can pull iv[] into the IV generation scope.
* Read packet ID directly from packet buffer instead of from iv buffer, to remove the need for an extra buffer.

---
 configure.ac                  |  20 ++-
 doc/openvpn.8                 |  17 ++-
 src/openvpn/crypto.c          | 347 +++++++++++++++++++++++++++++++++++++++---
 src/openvpn/crypto.h          |  68 +++++++--
 src/openvpn/crypto_backend.h  |  59 +++++++
 src/openvpn/crypto_openssl.c  |  74 ++++++++-
 src/openvpn/crypto_openssl.h  |   7 +
 src/openvpn/crypto_polarssl.c |  98 ++++++++++--
 src/openvpn/crypto_polarssl.h |   3 +
 src/openvpn/forward.c         |  49 +++---
 src/openvpn/ssl.c             | 101 +++++++++---
 src/openvpn/ssl.h             |  42 ++++-
 12 files changed, 782 insertions(+), 103 deletions(-)
configure.ac +19 -1
index 7ff2435..b75d51f 100644
@@ -821,6 +821,13 @@ if test "${with_crypto_library}" = "openssl"; then
 		AC_DEFINE([HAVE_OPENSSL_ENGINE], [1], [OpenSSL engine support available])
 	fi
 
+	have_crypto_aead_modes="yes"
+	AC_CHECK_FUNCS(
+		[EVP_aes_256_gcm],
+		,
+		[have_crypto_aead_modes="no"; break]
+	)
+
 	CFLAGS="${saved_CFLAGS}"
 	LIBS="${saved_LIBS}"
 
@@ -897,9 +904,19 @@ elif test "${with_crypto_library}" = "polarssl"; then
 			AC_MSG_ERROR([PolarSSL compiled with PKCS11, while OpenVPN is not])
 		fi
 	fi
+
+	have_crypto_aead_modes="yes"
+	AC_CHECK_FUNCS(
+		[ \
+			cipher_write_tag \
+			cipher_check_tag \
+		],
+		,
+		[have_crypto_aead_modes="no"; break]
+	)
+
 	CFLAGS="${saved_CFLAGS}"
 	LIBS="${saved_LIBS}"
-
 	have_crypto="yes"
 	AC_DEFINE([ENABLE_CRYPTO_POLARSSL], [1], [Use PolarSSL library])
 	CRYPTO_CFLAGS="${POLARSSL_CFLAGS}"
@@ -1054,6 +1071,7 @@ test "${enable_strict_options}" = "yes" && AC_DEFINE([ENABLE_STRICT_OPTIONS_CHEC
 if test "${enable_crypto}" = "yes"; then
 	test "${have_crypto}" != "yes" && AC_MSG_ERROR([${with_crypto_library} crypto is required but missing])
 	test "${enable_crypto_ofb_cfb}" = "yes" && AC_DEFINE([ENABLE_OFB_CFB_MODE], [1], [Enable OFB and CFB cipher modes])
+	test "${have_crypto_aead_modes}" = "yes" && AC_DEFINE([HAVE_AEAD_CIPHER_MODES], [1], [Use crypto library])
 	OPTIONAL_CRYPTO_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${CRYPTO_CFLAGS}"
 	OPTIONAL_CRYPTO_LIBS="${OPTIONAL_CRYPTO_LIBS} ${CRYPTO_LIBS}"
 	AC_DEFINE([ENABLE_CRYPTO], [1], [Enable crypto library])
doc/openvpn.8 +14 -3
index 76650e9..628d877 100644
@@ -3979,8 +3979,9 @@ options. Useful when using inline files (See section on inline files).
 .\"*********************************************************
 .TP
 .B \-\-auth alg
-Authenticate packets with HMAC using message
-digest algorithm
+Authenticate data channel packets and (if enabled)
+.B tls-auth
+control channel packets with HMAC using message digest algorithm
 .B alg.
 (The default is
 .B SHA1
@@ -3989,7 +3990,17 @@ HMAC is a commonly used message authentication algorithm (MAC) that uses
 a data string, a secure hash algorithm, and a key, to produce
 a digital signature.
 
-OpenVPN's usage of HMAC is to first encrypt a packet, then HMAC the resulting ciphertext.
+The OpenVPN data channel protocol uses encrypt-then-mac (i.e. first encrypt a
+packet, then HMAC the resulting ciphertext), which prevents padding oracle
+attacks.
+
+If an AEAD cipher mode (e.g. GCM) is chosen, the specified
+.B \-\-auth
+algorithm is ignored for the data channel, and the authentication method of the
+AEAD cipher is used instead.  Note that
+.B alg
+still specifies the digest used for
+.B tls-auth\fR.
 
 In static-key encryption mode, the HMAC key
 is included in the key file generated by
src/openvpn/crypto.c +324 -23

438 lines not on this page · raw message

src/openvpn/crypto.h +57 -11

128 lines not on this page · raw message

src/openvpn/crypto_backend.h +59 -0

95 lines not on this page · raw message

src/openvpn/crypto_openssl.c +66 -8

121 lines not on this page · raw message

src/openvpn/crypto_openssl.h +7 -0
index 42c7e9a..f157041 100644
@@ -61,6 +61,13 @@ typedef HMAC_CTX hmac_ctx_t;
 /** Cipher is in CFB mode */
 #define OPENVPN_MODE_CFB 	EVP_CIPH_CFB_MODE
 
+#ifdef HAVE_AEAD_CIPHER_MODES
+
+/** Cipher is in GCM mode */
+#define OPENVPN_MODE_GCM 	EVP_CIPH_GCM_MODE
+
+#endif /* HAVE_AEAD_CIPHER_MODES */
+
 /** Cipher should encrypt */
 #define OPENVPN_OP_ENCRYPT 	1
 
src/openvpn/crypto_polarssl.c +88 -10

139 lines not on this page · raw message

src/openvpn/crypto_polarssl.h +3 -0
index 94306ed..7be0862 100644
@@ -61,6 +61,9 @@ typedef md_context_t hmac_ctx_t;
 /** Cipher is in CFB mode */
 #define OPENVPN_MODE_CFB 	POLARSSL_MODE_CFB
 
+/** Cipher is in GCM mode */
+#define OPENVPN_MODE_GCM	POLARSSL_MODE_GCM
+
 /** Cipher should encrypt */
 #define OPENVPN_OP_ENCRYPT 	POLARSSL_ENCRYPT
 
src/openvpn/forward.c +25 -24

88 lines not on this page · raw message

src/openvpn/ssl.c +81 -20

166 lines not on this page · raw message

src/openvpn/ssl.h +39 -3

60 lines not on this page · raw message

Signature · 1 line
2.5.0
#27 lore raw

Re: [Openvpn-devel] [PATCH 08/10] Add AEAD cipher support (GCM)

Am 14.02.16 um 21:59 schrieb Steffan Karger:

Quoted 5 lines

Hi Arne,

Thanks for reviewing. Attached a v2, and below inline responses to your remarks.

ACK. I looked through the changes and it is now good to go.

Quoted 27 lines · Arne Schwabe
Quoted 10 lines
Quoted 7 lines

AC_ARG_ENABLE(
[aead-modes],
[AS_HELP_STRING([--disable-aead-modes], [disable AEAD crypto modes
@<:@default=yes@:>@])],

    ,
    [enable_aead_modes="yes"]

)

I would not make this user configurable. If it wasn't for RHEL5 I even would throw out 0.9.8 support and always compile it in.

I agree. I removed the configurability.

Now that OpenSSL has officially stopped supporting 0.9.8 and 1.0.0 (as of 31st Dec 2015), we might want to revisit our decision to support those in 2.4/master, but let's not do that as a part of this patch set :)

Quoted 8 lines
     {
src/openvpn/forward.c +5 -0
+      /* Get the key we will use to encrypt the packet. */
       tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &co);
+      /* If using P_DATA_V2, prepend the 1-byte opcode and 3-byte peer-id to the
+       * packet before openvpn_encrypt(), so we can authenticate the opcode too.
+       */
+      if (c->c2.buf.len > 0 && !c->c2.tls_multi->opt.server && c->c2.tls_multi->use_peer_id)
Quoted 7 lines
Quoted 3 lines

The latter part of that if might be turned into a small inline fuction to make it clearer. Or alternatively a local boolean variable to be used in both if places.

Hm, I tried that but don't really like it. I'd rather simplify this by making server->client traffic use P_DATA_V2 too (but didn't want to do that in this patch set).

Yes. Also better alignment for those packets.

Quoted 2 lines

Good point. I took the opportunity to also explain a bit more about the relation between --auth and --tls-auth.

Good idea.

And we may need another patch for the Changes.rst

Arne

#28 lore raw

Re: [Openvpn-devel] [PATCH 08/10] Add AEAD cipher support (GCM)

Quoted 1 line · Arne Schwabe

And we may need another patch for the Changes.rst

Woops, forgot this *again*... Attached a patch for Changes.rst. Feel free to squash this into 8/10 when committing.

-Steffan

From d264096273b8213ac7d65fab000810b27dcae565 Mon Sep 17 00:00:00 2001
From: Steffan Karger <[email protected]>
Date: Mon, 15 Feb 2016 08:08:41 +0100
Subject: [PATCH] Add Changes.rst entry for AEAD cipher modes

---
 Changes.rst | 7 +++++++
 1 file changed, 7 insertions(+)
Changes.rst +7 -0
index dd9b9b2..af70d14 100644
@@ -37,6 +37,13 @@ Windows version
     Windows version is detected, logged and possibly signalled to server
     (IV_PLAT_VER=<nn> if --push-peer-info is set on client)
 
+AEAD (GCM) data channel cipher support
+    The data channel now supports AEAD ciphers (currently only GCM).  The AEAD
+    packet format has a smaller overhead than the CBC packet format, (e.g. 20
+    bytes per packet for AES-128-GCM instead of 36 bytes per packet for
+    AES-128-CBC + HMAC-SHA1).
+
+
 User-visible Changes
 --------------------
 - For certificate DNs with duplicate fields, e.g. "OU=one,OU=two", both fields
Signature · 1 line
2.5.0
#29 lore raw

[Openvpn-devel] [PATCH applied] Re: Add AEAD cipher support (GCM)

In reply to Steffan Karger

Your patch has been applied to the master branch.

I have added the "Changes.rst" patch to the commit, as requested.

commit 66407e11c4746e564bd4285e9c1a1805ecfd82bd (master)

Author: Steffan Karger
Date:   Sat Oct 24 16:44:09 2015 +0200
     Add AEAD cipher support (GCM)

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11162
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#30 lore raw

PATCH 1/1 Make AEAD modes work with OpenSSL 1.0.1-1.0.1c

The 'nobody uses OpenSSL 1.0.1-1.0.1c'-gamble in commit 66407e11 (add AEAD support) did not turn out well; apparently Ubuntu 12.04 LTS ships with a broken OpenSSL 1.0.1. Since this is still a popular platform, re-add the fixup code, now with a clear version check so it's easy to remove once we drop support for OpenSSL 1.0.1.

---
 src/openvpn/crypto.c | 7 +++++++
 1 file changed, 7 insertions(+)
src/openvpn/crypto.c +7 -0
index bd86679..269ec4b 100644
@@ -450,6 +450,13 @@ openvpn_decrypt_aead (struct buffer *buf, struct buffer work,
   tag_ptr = BPTR(buf);
   ASSERT (buf_advance (buf, tag_size));
   dmsg (D_PACKET_CONTENT, "DECRYPT MAC: %s", format_hex (tag_ptr, tag_size, 0, &gc));
+#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x10001040L
+  /* OpenSSL <= 1.0.1c bug requires set tag before processing ciphertext */
+  if (!EVP_CIPHER_CTX_ctrl (ctx->cipher, EVP_CTRL_GCM_SET_TAG, tag_size, tag_ptr))
+    {
+      CRYPT_ERROR ("setting tag failed");
+    }
+#endif
 
   if (buf->len < 1)
     {
Signature · 1 line
2.5.0
#31 lore raw

[Openvpn-devel] [PATCH applied] Re: Make AEAD modes work with OpenSSL 1.0.1-1.0.1c

ACK.

Tested on Ubuntu 12.04, makes "make check" succeed for AES*GCM modes (and has no effect on systems with older/newer OpenSSL versions).

Your patch has been applied to the master branch.

commit 13de0103ea361e2be24ab8b16f5be269c6ab7496

Author: Steffan Karger
Date:   Sun Mar 6 10:31:55 2016 +0100
     Make AEAD modes work with OpenSSL 1.0.1-1.0.1c

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11322
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#32 lore raw

PATCH 1/1 Only include aead encrypt/decrypt functions if AEAD modes are supported

This fixes the build for OpenSSL < 1.0.1 (broken by commit 3654d953), which has no AEAD support.

---
 src/openvpn/crypto.c | 9 +++++++++
 1 file changed, 9 insertions(+)
src/openvpn/crypto.c +9 -0
index 269ec4b..f15ac35 100644
@@ -86,6 +86,7 @@ memcmp_constant_time (const void *a, const void *b, size_t size) {
 static void
 openvpn_encrypt_aead (struct buffer *buf, struct buffer work,
 	 struct crypto_options *opt) {
+#ifdef HAVE_AEAD_CIPHER_MODES
   struct gc_arena gc;
   int outlen = 0;
   const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
@@ -173,6 +174,9 @@ err:
   crypto_clear_error();
   buf->len = 0;
   goto cleanup;
+#else /* HAVE_AEAD_CIPHER_MODES */
+  ASSERT (0);
+#endif
 }
 
 static void
@@ -385,6 +389,7 @@ openvpn_decrypt_aead (struct buffer *buf, struct buffer work,
     struct crypto_options *opt, const struct frame* frame,
     const uint8_t *ad_start)
 {
+#ifdef HAVE_AEAD_CIPHER_MODES
   static const char error_prefix[] = "AEAD Decrypt error";
   struct packet_id_net pin = { 0 };
   const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt;
@@ -511,6 +516,10 @@ openvpn_decrypt_aead (struct buffer *buf, struct buffer work,
   buf->len = 0;
   gc_free (&gc);
   return false;
+#else /* HAVE_AEAD_CIPHER_MODES */
+  ASSERT (0);
+  return false;
+#endif
 }
 
 /*
Signature · 1 line
2.5.0
#33 lore raw

[Openvpn-devel] [PATCH applied] Re: Only include aead encrypt/decrypt functions if AEAD modes are supported

ACK, thanks for the quick followup. As discussed on IRC.

Your patch has been applied to the master branch.

commit 71d89065ad56dda19996deeeffeddcea632b8349

Author: Steffan Karger
Date:   Sun Mar 6 13:09:50 2016 +0100
     Only include aead encrypt/decrypt functions if AEAD modes are supported

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11325
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#34 lore raw

PATCH 9/10 Add cipher name translation for OpenSSL.

In reply to Steffan Karger

This keeps naming consistent. For example, instead of id-aes128-GCM use AES-128-GCM, which is more like AES-128-CBC.

---
 src/openvpn/crypto.c          | 40 +++++++++++++++++++++++++++++++++++++++-
 src/openvpn/crypto_backend.h  | 30 ++++++++++++++++++++++++++++++
 src/openvpn/crypto_openssl.c  | 23 ++++++++++-------------
 src/openvpn/crypto_polarssl.c | 43 +++----------------------------------------
 src/openvpn/options.c         |  3 ++-
 5 files changed, 84 insertions(+), 55 deletions(-)
src/openvpn/crypto.c +39 -1
index 21790a2..9620f72 100644
@@ -797,7 +797,7 @@ init_key_ctx (struct key_ctx *ctx, struct key *key,
 
       msg (D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key",
           prefix,
-          cipher_kt_name(kt->cipher),
+          translate_cipher_name_to_openvpn(cipher_kt_name(kt->cipher)),
           kt->cipher_length *8);
 
       dmsg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix,
@@ -1670,4 +1670,42 @@ get_random()
   return l;
 }
 
+static const cipher_name_pair *
+get_cipher_name_pair(const char *cipher_name) {
+  const cipher_name_pair *pair;
+  size_t i = 0;
+
+  /* Search for a cipher name translation */
+  for (; i < cipher_name_translation_table_count; i++)
+    {
+      pair = &cipher_name_translation_table[i];
+      if (0 == strcmp (cipher_name, pair->openvpn_name) ||
+	  0 == strcmp (cipher_name, pair->lib_name))
+	  return pair;
+    }
+
+  /* Nothing found, return null */
+  return NULL;
+}
+
+const char *
+translate_cipher_name_from_openvpn (const char *cipher_name) {
+  const cipher_name_pair *pair = get_cipher_name_pair(cipher_name);
+
+  if (NULL == pair)
+    return cipher_name;
+
+  return pair->lib_name;
+}
+
+const char *
+translate_cipher_name_to_openvpn (const char *cipher_name) {
+  const cipher_name_pair *pair = get_cipher_name_pair(cipher_name);
+
+  if (NULL == pair)
+    return cipher_name;
+
+  return pair->openvpn_name;
+}
+
 #endif /* ENABLE_CRYPTO */
src/openvpn/crypto_backend.h +30 -0
index 3e030d1..d389ba4 100644
@@ -41,6 +41,16 @@
 /* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */
 #define OPENVPN_AEAD_TAG_LENGTH 16
 
+/** Struct used in cipher name translation table */
+typedef struct {
+  const char *openvpn_name;	/**< Cipher name used by OpenVPN */
+  const char *lib_name;		/**< Cipher name used by crypto library */
+} cipher_name_pair;
+
+/** Cipher name translation table */
+extern const cipher_name_pair cipher_name_translation_table[];
+extern const size_t cipher_name_translation_table_count;
+
 /*
  * This routine should have additional OpenSSL crypto library initialisations
  * used by both crypto and ssl components of OpenVPN.
@@ -594,4 +604,24 @@ void hmac_ctx_update (hmac_ctx_t *ctx, const uint8_t *src, int src_len);
  */
 void hmac_ctx_final (hmac_ctx_t *ctx, uint8_t *dst);
 
+/**
+ * Translate an OpenVPN cipher name to a crypto library cipher name.
+ *
+ * @param cipher_name	An OpenVPN cipher name
+ *
+ * @return		The corresponding crypto library cipher name, or NULL
+ * 			if no matching cipher name was found.
+ */
+const char * translate_cipher_name_from_openvpn (const char *cipher_name);
+
+/**
+ * Translate a crypto library cipher name to an OpenVPN cipher name.
+ *
+ * @param cipher_name	A crypto library cipher name
+ *
+ * @return		The corresponding OpenVPN cipher name, or NULL if no
+ * 			matching cipher name was found.
+ */
+const char * translate_cipher_name_to_openvpn (const char *cipher_name);
+
 #endif /* CRYPTO_BACKEND_H_ */
src/openvpn/crypto_openssl.c +10 -13
index 6fdff4f..d73634c 100644
@@ -240,17 +240,14 @@ crypto_init_dmalloc (void)
 }
 #endif /* DMALLOC */
 
-const char *
-translate_cipher_name_from_openvpn (const char *cipher_name) {
-  // OpenSSL doesn't require any translation
-  return cipher_name;
-}
+const cipher_name_pair cipher_name_translation_table[] = {
+    { "AES-128-GCM", "id-aes128-GCM" },
+    { "AES-192-GCM", "id-aes192-GCM" },
+    { "AES-256-GCM", "id-aes256-GCM" },
+};
+const size_t cipher_name_translation_table_count =
+    sizeof (cipher_name_translation_table) / sizeof (*cipher_name_translation_table);
 
-const char *
-translate_cipher_name_to_openvpn (const char *cipher_name) {
-  // OpenSSL doesn't require any translation
-  return cipher_name;
-}
 
 void
 show_available_ciphers ()
@@ -286,9 +283,9 @@ show_available_ciphers ()
 	      const char *ssl_only = cipher_kt_mode_cbc(cipher) ?
 		  "" : " (TLS client/server mode)";
 
-	      printf ("%s %d bit default key (%s)%s\n", OBJ_nid2sn (nid),
-		      EVP_CIPHER_key_length (cipher) * 8, var_key_size,
-		      ssl_only);
+	      printf ("%s %d bit default key (%s)%s\n",
+		  translate_cipher_name_to_openvpn(OBJ_nid2sn (nid)),
+		  EVP_CIPHER_key_length (cipher) * 8, var_key_size, ssl_only);
 	    }
 	}
     }
src/openvpn/crypto_polarssl.c +3 -40

55 lines not on this page · raw message

src/openvpn/options.c +2 -1
index 6d97b4f..02def3a 100644
@@ -3039,7 +3039,8 @@ options_string (const struct options *o,
 		       o->authname, o->authname_defined,
 		       o->keysize, true, false);
 
-	buf_printf (&out, ",cipher %s", cipher_kt_name (kt.cipher));
+	buf_printf (&out, ",cipher %s",
+	    translate_cipher_name_to_openvpn(cipher_kt_name (kt.cipher)));
 	buf_printf (&out, ",auth %s", md_kt_name (kt.digest));
 	buf_printf (&out, ",keysize %d", kt.cipher_length * 8);
 	if (o->shared_secret_file)
Signature · 1 line
2.5.0
#35 lore raw

Re: [Openvpn-devel] [PATCH 09/10] Add cipher name translation for OpenSSL.

Am 07.02.16 um 20:47 schrieb Steffan Karger:

src/openvpn/crypto.c get_random() · line 1697

Quoted 7 lines

This keeps naming consistent. For example, instead of id-aes128-GCM use AES-128-GCM, which is more like AES-128-CBC.

src/openvpn/crypto.c +4 -0
+
+  if (NULL == pair)
+    return cipher_name;
+

I like the pair == NULL style more.

Otherwise ACK from me.

arne

#36 lore raw

Re: [Openvpn-devel] [PATCH 09/10] Add cipher name translation for OpenSSL.

Quoted 11 lines · Arne Schwabe

Am 07.02.16 um 20:47 schrieb Steffan Karger:

src/openvpn/crypto.c get_random() · line 1697

Quoted 7 lines

This keeps naming consistent. For example, instead of id-aes128-GCM use AES-128-GCM, which is more like AES-128-CBC.

src/openvpn/crypto.c +4 -0
+
+  if (NULL == pair)
+    return cipher_name;
+

I like the pair == NULL style more.

I "kind of" agree, as that's what we've grown up with. However, from a defensive programming style, this is a far better approach. A mistyping using '=' will in 'NULL = pair' case cause a compile failure. The other way around will compile just fine with a nice ugly bug attached with it ... So I'm trying to get used to his "flipped around" style.

Especially for OpenVPN, I believe a defensive style is very much appropriate.

Signature · 3 lines
kind regards,

David Sommerseth
#37 lore raw

Re: [Openvpn-devel] [PATCH 09/10] Add cipher name translation for OpenSSL.

Quoted 11 lines · David Sommerseth
Quoted 9 lines · Arne Schwabe

Am 07.02.16 um 20:47 schrieb Steffan Karger:

src/openvpn/crypto.c get_random() · line 1697

Quoted 7 lines

This keeps naming consistent. For example, instead of id-aes128-GCM use AES-128-GCM, which is more like AES-128-CBC.

src/openvpn/crypto.c +4 -0
+
+  if (NULL == pair)
+    return cipher_name;
+

I like the pair == NULL style more.

Quoted 5 lines

I "kind of" agree, as that's what we've grown up with. However, from a defensive programming style, this is a far better approach. A mistyping using '=' will in 'NULL = pair' case cause a compile failure. The other way around will compile just fine with a nice ugly bug attached with it ... So I'm trying to get used to his "flipped around" style.

This indeed the motivation is.

But, modern compilers do detect this (given the correct flags):

gcc 5.2.1:

$ clang -o no-yoda no-yoda.c
no-yoda.c:5:9: warning: using the result of an assignment as a condition without
      parentheses [-Wparentheses]
  if (a = NULL) {
      ~~^~~~~~
no-yoda.c:5:9: note: place parentheses around the assignment to silence this
      warning
  if (a = NULL) {
        ^
      (       )
no-yoda.c:5:9: note: use '==' to turn this assignment into an equality
      comparison
  if (a = NULL) {
        ^
        ==

clang 3.6.2:

$ clang -o no-yoda no-yoda.c
no-yoda.c:5:9: warning: using the result of an assignment as a condition without
      parentheses [-Wparentheses]
  if (a = NULL) {
      ~~^~~~~~

So, I do not have any strong preference.

-Steffan

#38 lore raw

Re: [Openvpn-devel] [PATCH 09/10] Add cipher name translation for OpenSSL.

Hi,

Quoted 6 lines · Steffan Karger

gcc 5.2.1:

$ clang -o no-yoda no-yoda.c
no-yoda.c:5:9: warning: using the result of an assignment as a condition without
      parentheses [-Wparentheses]
  if (a = NULL) {
      ~~^~~~~~

I noticed this as well, and find it really useful (without needing -Wall).

Quoted 1 line

So, I do not have any strong preference.

In that case, my preference always is "match existing coding style" (which would mean "it depends on the module" - the ssl_*.c / crypto*.c code tends to "if ( NULL == ...)" while most of the rest uses "if ( ... == NULL )"
- so much for "existing coding style"...)

gert

Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
                                                           //www.muc.de/~gert/
Gert Doering - Munich, Germany                             [email protected]...
fax: +49-89-35655025                        [email protected]...
#39 lore raw

[Openvpn-devel] [PATCH applied] Re: Add cipher name translation for OpenSSL.

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit 44dc5d309cf04ebd9fc35b5f97be631fd99e22d6

Author: Steffan Karger
Date:   Sun Feb 7 20:47:17 2016 +0100
     Add cipher name translation for OpenSSL.

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11081
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#40 lore raw

PATCH 10/10 Add preliminary server-side support for negotiable crypto parameters

In reply to Steffan Karger

Add preliminary support for Negotiable Crypto Parameters 'level 2' (IV_NCP=2), as proposed by James Yonan on the openvpn-devel mailinglist:
http://comments.gmane.org/gmane.network.openvpn.devel/9385

This patch:
* Makes the server advertise "IV_NCP=2", if --push-peer-info 2 is enabled.
* Pushes a 'cipher XXX' directive to the client, if the client advertises "IV_NCP=2", where XXX is the cipher set in the server config file.

This enables clients that have support for IV_NCP to connect to a server, even when the client does not have the correct cipher specified in it's config file.

Since pushing the cipher directive is quite similar to pushing peer-id, I moved peer-id pushing to same prepare_push_reply() function I created for pushing cipher. Adding these directives as regular push options allows us to use the existing 'push-continuation' infrastructure. Note that we should not reduce safe_cap in send_push_reply, because it was never increased to account for peer-id.

This is a preliminary patch, which will be followed by more patches to add client support, and configurability.

---
 src/openvpn/push.c | 97 +++++++++++++++++++++++++++++++++++++++++++++---------
 src/openvpn/push.h |  2 --
 src/openvpn/ssl.c  |  4 +++
 3 files changed, 86 insertions(+), 17 deletions(-)
src/openvpn/push.c +82 -15
index d4f3cb6..051aef6 100644
@@ -40,6 +40,30 @@
 
 #if P2MP
 
+/**
+ * Add an option to the push list by providing a format string.
+ *
+ * The string added to the push options is allocated in o->gc, so fmt may be
+ * free'd by the caller before the option is sent to the peer.
+ *
+ * @param o		The current connection's options
+ * @param msglevel	The message level to use when printing errors
+ * @param fmt		Format string for the option
+ * @param ...		Format string arguments
+ *
+ * @return true on success, false on failure.
+ */
+static bool push_option_fmt(struct options *o, int msglevel,
+    const char *fmt, ...)
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+    __attribute__ ((format (gnu_printf, 3, 4)))
+#else
+    __attribute__ ((format (__printf__, 3, 4)))
+#endif
+#endif
+    ;
+
 /*
  * Auth username/password
  *
@@ -239,7 +263,47 @@ send_push_request (struct context *c)
 
 #if P2MP_SERVER
 
-bool
+/**
+ * Prepare push options, based on local options and available peer info.
+ *
+ * @param options	Connection options
+ * @param tls_multi	TLS state structure for the current tunnel
+ *
+ * @return true on success, false on failure.
+ */
+static bool
+prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
+{
+  const char *optstr = NULL;
+  const char * const peer_info = tls_multi->peer_info;
+
+  /* Send peer-id if client supports it */
+  optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
+  if (optstr)
+    {
+      int proto = 0;
+      int r = sscanf(optstr, "IV_PROTO=%d", &proto);
+      if ((r == 1) && (proto >= 2))
+	{
+	  push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
+	}
+    }
+
+  /* Push cipher if client supports Negotiable Crypto Parameters */
+  optstr = peer_info ? strstr(peer_info, "IV_NCP=") : NULL;
+  if (optstr)
+    {
+      int ncp = 0;
+      int r = sscanf(optstr, "IV_NCP=%d", &ncp);
+      if ((r == 1) && (ncp == 2))
+	{
+	  push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
+	}
+    }
+  return true;
+}
+
+static bool
 send_push_reply (struct context *c)
 {
   struct gc_arena gc = gc_new ();
@@ -309,19 +373,6 @@ send_push_reply (struct context *c)
   if (multi_push)
     buf_printf (&buf, ",push-continuation 1");
 
-  /* Send peer-id if client supports it */
-  if (c->c2.tls_multi->peer_info)
-    {
-      const char* proto_str = strstr(c->c2.tls_multi->peer_info, "IV_PROTO=");
-      if (proto_str)
-	{
-	  int proto = 0;
-	  int r = sscanf(proto_str, "IV_PROTO=%d", &proto);
-	  if ((r == 1) && (proto >= 2))
-	    buf_printf(&buf, ",peer-id %d", c->c2.tls_multi->peer_id);
-	}
-  }
-
   if (BLEN (&buf) > sizeof(cmd)-1)
     {
       const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
@@ -409,6 +460,21 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
   push_option (o, opt, msglevel);
 }
 
+static bool push_option_fmt(struct options *o, int msglevel,
+    const char *format, ...)
+{
+  va_list arglist;
+  char tmp[256] = {0};
+  int len = -1;
+  va_start (arglist, format);
+  len = vsnprintf (tmp, sizeof(tmp), format, arglist);
+  va_end (arglist);
+  if (len > sizeof(tmp)-1)
+    return false;
+  push_option (o, string_alloc (tmp, &o->gc), msglevel);
+  return true;
+}
+
 void
 push_reset (struct options *o)
 {
@@ -442,7 +508,8 @@ process_incoming_push_request (struct context *c)
 	}
       else
 	{
-	  if (send_push_reply (c))
+	  if (prepare_push_reply(&c->options, c->c2.tls_multi) &&
+	      send_push_reply (c))
 	    {
 	      ret = PUSH_MSG_REQUEST;
 	      c->c2.sent_push_reply_expiry = now + 30;
src/openvpn/push.h +0 -2
index fa06e08..19bbf5e 100644
@@ -62,8 +62,6 @@ void push_options (struct options *o, char **p, int msglevel, struct gc_arena *g
 
 void push_reset (struct options *o);
 
-bool send_push_reply (struct context *c);
-
 void remove_iroutes_from_push_route_list (struct options *o);
 
 void send_auth_failed (struct context *c, const char *client_reason);
src/openvpn/ssl.c +4 -0
index 7156030..1d811b1 100644
@@ -1883,6 +1883,10 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
       /* support for P_DATA_V2 */
       buf_printf(&out, "IV_PROTO=2\n");
 
+      /* support for Negotiable Crypto Paramters */
+      if (session->opt->server)
+	buf_printf(&out, "IV_NCP=2\n");
+
       /* push compression status */
 #ifdef USE_COMP
       comp_generate_peer_info_string(&session->opt->comp_options, &out);
Signature · 1 line
2.5.0
#41 lore raw

Re: [Openvpn-devel] [PATCH 10/10] Add preliminary server-side support for negotiable crypto parameters

Hiya,

Quoted 2 lines · Steffan Karger

This patch:
* Makes the server advertise "IV_NCP=2", if --push-peer-info 2 is enabled.

I'd rather not do that - it won't do harm, but technically, it's not correct (we don't support "negotiable" ciphers in that direction either) - and then, nothing at the client ever *looks* at the peer-info variables, so it's somewhat moot. And that makes it "unneccessary extra code"...

Quoted 14 lines

* Pushes a 'cipher XXX' directive to the client, if the client advertises "IV_NCP=2", where XXX is the cipher set in the server config file.

This enables clients that have support for IV_NCP to connect to a server, even when the client does not have the correct cipher specified in it's config file.

Since pushing the cipher directive is quite similar to pushing peer-id, I moved peer-id pushing to same prepare_push_reply() function I created for pushing cipher. Adding these directives as regular push options allows us to use the existing 'push-continuation' infrastructure. Note that we should not reduce safe_cap in send_push_reply, because it was never increased to account for peer-id.

Quoted 2 lines

This is a preliminary patch, which will be followed by more patches to add client support, and configurability.

For the rest, this looks good to me. Especially introducing the intermediate step with push_option_fmt() simplifies the extra buffer handling and fixes the potential overflow if "peer-id" happens to be put at the exactly wrong spot...

So, feature- and code-ACK, except for pushing "IV_NCP=2"...

As a side note: I was wondering if o->gc is save to use here (when is it cleaned up? is it ever cleaned up?) and under the assumption that the existing code in push_option_ex() is correct, the new code is correct as well - the existing code uses o->gc too.

A minor comment comment:

Quoted 7 lines

#if P2MP

+/**
+ * Add an option to the push list by providing a format string.
+ *
+ * The string added to the push options is allocated in o->gc, so fmt may be
+ * free'd by the caller before the option is sent to the peer.

Since nobody ever free()s *fmt*, maybe word that slightly differently, like:

* The resulting string added to the push options is allocated in o->gc,
* so the caller does not have to preserve anything

or so

gert

Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
                                                           //www.muc.de/~gert/
Gert Doering - Munich, Germany                             [email protected]...
fax: +49-89-35655025                        [email protected]...
#42 lore raw

Re: [Openvpn-devel] [PATCH 10/10] Add preliminary server-side support for negotiable crypto parameters

Hi,

Thanks for the review. Attached a v2 of the patch, and responses inline below.

Quoted 8 lines · Gert Doering
Quoted 2 lines · Steffan Karger

This patch:
* Makes the server advertise "IV_NCP=2", if --push-peer-info 2 is enabled.

I'd rather not do that - it won't do harm, but technically, it's not correct (we don't support "negotiable" ciphers in that direction either) - and then, nothing at the client ever *looks* at the peer-info variables, so it's somewhat moot. And that makes it "unneccessary extra code"...

Ok, removed from the patch.

Quoted 4 lines

As a side note: I was wondering if o->gc is save to use here (when is it cleaned up? is it ever cleaned up?) and under the assumption that the existing code in push_option_ex() is correct, the new code is correct as well - the existing code uses o->gc too.

Yes, o->gc is the right thing here. It is freshly initialized by init_instance() when a new 'child' (connection/session) instance is created, and cleaned up by multi_close_instance() when the child is instance is destroyed.

Quoted 10 lines

A minor comment comment:

Quoted 7 lines

#if P2MP

+/**
+ * Add an option to the push list by providing a format string.
+ *
+ * The string added to the push options is allocated in o->gc, so fmt may be
+ * free'd by the caller before the option is sent to the peer.

Quoted 7 lines

Since nobody ever free()s *fmt*, maybe word that slightly differently, like:

* The resulting string added to the push options is allocated in o->gc,
* so the caller does not have to preserve anything

or so

Yes, much better. Updated.

-Steffan

From b0a9c3f22b39c7b658aa2f59786cad217256c9d6 Mon Sep 17 00:00:00 2001
From: Steffan Karger <[email protected]>
Date: Mon, 15 Feb 2016 21:07:11 +0100
Subject: [PATCH 10/10 v2] Add preliminary server-side support for negotiable
crypto parameters

Add preliminary support for Negotiable Crypto Parameters 'level 2' (IV_NCP=2), as proposed by James Yonan on the openvpn-devel mailinglist:
http://comments.gmane.org/gmane.network.openvpn.devel/9385

This patch makes a server push a 'cipher XXX' directive to the client, if the client advertises "IV_NCP=2", where XXX is the cipher set in the server config file.

This enables clients that have support for IV_NCP to connect to a server, even when the client does not have the correct cipher specified in it's config file.

Since pushing the cipher directive is quite similar to pushing peer-id, I moved peer-id pushing to the same prepare_push_reply() function I created for pushing cipher. Adding these directives as regular push options allows us to use the existing 'push-continuation' infrastructure. Note that we should not reduce safe_cap in send_push_reply, because it was never increased to account for peer-id.

This is a preliminary patch, which will be followed by more patches to add client support, and configurability.

v2:
* Reword doxygen of push_options_fmt()
* No longer push IV_NCP as a server

---
 src/openvpn/push.c | 97 +++++++++++++++++++++++++++++++++++++++++++++---------
 src/openvpn/push.h |  2 --
 2 files changed, 82 insertions(+), 17 deletions(-)
src/openvpn/push.c +82 -15
index d4f3cb6..c29093b 100644
@@ -40,6 +40,30 @@
 
 #if P2MP
 
+/**
+ * Add an option to the push list by providing a format string.
+ *
+ * The string added to the push options is allocated in o->gc, so the caller
+ * does not have to preserve anything.
+ *
+ * @param o		The current connection's options
+ * @param msglevel	The message level to use when printing errors
+ * @param fmt		Format string for the option
+ * @param ...		Format string arguments
+ *
+ * @return true on success, false on failure.
+ */
+static bool push_option_fmt(struct options *o, int msglevel,
+    const char *fmt, ...)
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+    __attribute__ ((format (gnu_printf, 3, 4)))
+#else
+    __attribute__ ((format (__printf__, 3, 4)))
+#endif
+#endif
+    ;
+
 /*
  * Auth username/password
  *
@@ -239,7 +263,47 @@ send_push_request (struct context *c)
 
 #if P2MP_SERVER
 
-bool
+/**
+ * Prepare push options, based on local options and available peer info.
+ *
+ * @param options	Connection options
+ * @param tls_multi	TLS state structure for the current tunnel
+ *
+ * @return true on success, false on failure.
+ */
+static bool
+prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
+{
+  const char *optstr = NULL;
+  const char * const peer_info = tls_multi->peer_info;
+
+  /* Send peer-id if client supports it */
+  optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
+  if (optstr)
+    {
+      int proto = 0;
+      int r = sscanf(optstr, "IV_PROTO=%d", &proto);
+      if ((r == 1) && (proto >= 2))
+	{
+	  push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
+	}
+    }
+
+  /* Push cipher if client supports Negotiable Crypto Parameters */
+  optstr = peer_info ? strstr(peer_info, "IV_NCP=") : NULL;
+  if (optstr)
+    {
+      int ncp = 0;
+      int r = sscanf(optstr, "IV_NCP=%d", &ncp);
+      if ((r == 1) && (ncp == 2))
+	{
+	  push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
+	}
+    }
+  return true;
+}
+
+static bool
 send_push_reply (struct context *c)
 {
   struct gc_arena gc = gc_new ();
@@ -309,19 +373,6 @@ send_push_reply (struct context *c)
   if (multi_push)
     buf_printf (&buf, ",push-continuation 1");
 
-  /* Send peer-id if client supports it */
-  if (c->c2.tls_multi->peer_info)
-    {
-      const char* proto_str = strstr(c->c2.tls_multi->peer_info, "IV_PROTO=");
-      if (proto_str)
-	{
-	  int proto = 0;
-	  int r = sscanf(proto_str, "IV_PROTO=%d", &proto);
-	  if ((r == 1) && (proto >= 2))
-	    buf_printf(&buf, ",peer-id %d", c->c2.tls_multi->peer_id);
-	}
-  }
-
   if (BLEN (&buf) > sizeof(cmd)-1)
     {
       const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
@@ -409,6 +460,21 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
   push_option (o, opt, msglevel);
 }
 
+static bool push_option_fmt(struct options *o, int msglevel,
+    const char *format, ...)
+{
+  va_list arglist;
+  char tmp[256] = {0};
+  int len = -1;
+  va_start (arglist, format);
+  len = vsnprintf (tmp, sizeof(tmp), format, arglist);
+  va_end (arglist);
+  if (len > sizeof(tmp)-1)
+    return false;
+  push_option (o, string_alloc (tmp, &o->gc), msglevel);
+  return true;
+}
+
 void
 push_reset (struct options *o)
 {
@@ -442,7 +508,8 @@ process_incoming_push_request (struct context *c)
 	}
       else
 	{
-	  if (send_push_reply (c))
+	  if (prepare_push_reply(&c->options, c->c2.tls_multi) &&
+	      send_push_reply (c))
 	    {
 	      ret = PUSH_MSG_REQUEST;
 	      c->c2.sent_push_reply_expiry = now + 30;
src/openvpn/push.h +0 -2
index fa06e08..19bbf5e 100644
@@ -62,8 +62,6 @@ void push_options (struct options *o, char **p, int msglevel, struct gc_arena *g
 
 void push_reset (struct options *o);
 
-bool send_push_reply (struct context *c);
-
 void remove_iroutes_from_push_route_list (struct options *o);
 
 void send_auth_failed (struct context *c, const char *client_reason);
Signature · 1 line
2.5.0
#43 lore raw

[Openvpn-devel] [PATCH applied] Re: Add preliminary server-side support for negotiable crypto parameters

ACK!

Your patch has been applied to the master branch.

commit 3a5a46cf2b7f6a8b8520c2513a8054deb48bfcbe

Author: Steffan Karger
Date:   Mon Feb 15 21:07:11 2016 +0100
     Add preliminary server-side support for negotiable crypto parameters

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/11170
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering