Skip to contentSkip to conversation

Add control channel encryption (--tls-crypt)

PATCH Superseded Steffan Karger 6 participants 26 messages Nov 8, 2016 – Nov 16, 2016

lore
Share & tools
lore
#1 lore raw

PATCH 1/5 Refactor static/tls-auth key loading

Remove duplicate code, in preparation for adding --tls-crypt, which otherwise would have to duplicate this code again.

This should be equivalent to the old code, except for two things:
* The log lines for static key initialization change slightly, from "Static Encrypt/Decrypt" to "Incoming/Outgoing Static Key Encryption"
* We also 'check and fix highly unlikely key problems' for tls-auth keys (boils down to a sanity-check for an all-zero key).

---
 src/openvpn/crypto.c | 69 +++++++++++++++++++++++++---------------------------
 src/openvpn/crypto.h |  9 +++----
 src/openvpn/init.c   | 47 +++++++----------------------------
 3 files changed, 45 insertions(+), 80 deletions(-)
src/openvpn/crypto.c +33 -36
index 0212450..749b7da 100644
@@ -1103,48 +1103,45 @@ test_crypto (struct crypto_options *co, struct frame* frame)
 }
 
 void
-get_tls_handshake_key (const struct key_type *key_type,
-		       struct key_ctx_bi *ctx,
-		       const char *key_file,
-		       const int key_direction,
-		       const unsigned int flags)
+crypto_read_openvpn_key (const struct key_type *key_type,
+	struct key_ctx_bi *ctx, const char *key_file, const char *key_inline,
+	const int key_direction, const char *key_name, const char *opt_name)
 {
-  if (key_file)
-    {
-      struct key2 key2;
-      struct key_direction_state kds;
-
-      if (flags & GHK_INLINE)
-	{
-	  read_key_file (&key2, key_file, RKF_INLINE|RKF_MUST_SUCCEED);
-	}
-      else
-	{
-	  read_key_file (&key2, key_file, RKF_MUST_SUCCEED);
-	}
-
-	if (key2.n != 2)
-	  {
-	    msg (M_ERR, "Control Channel Authentication: File '%s' does not "
-		"have OpenVPN Static Key format.  Using free-form passphrase "
-		"file is not supported anymore.", key_file);
-	  }
-      /* handle key direction */
-      key_direction_state_init (&kds, key_direction);
-      must_have_n_keys (key_file, "tls-auth", &key2, kds.need_keys);
+  struct key2 key2;
+  struct key_direction_state kds;
+  char log_prefix[128] = { 0 };
 
-      /* initialize key in both directions */
-      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], key_type, OPENVPN_OP_DECRYPT,
-		    "Incoming Control Channel Authentication");
-
-      CLEAR (key2);
+  if (key_inline)
+    {
+      read_key_file (&key2, key_inline, RKF_MUST_SUCCEED|RKF_INLINE);
     }
   else
     {
-      CLEAR (*ctx);
+      read_key_file (&key2, key_file, RKF_MUST_SUCCEED);
+    }
+
+  if (key2.n != 2)
+    {
+      msg (M_ERR, "File '%s' does not have OpenVPN Static Key format.  Using "
+	   "free-form passphrase file is not supported anymore.", key_file);
     }
+
+  /* check for and fix highly unlikely key problems */
+  verify_fix_key2 (&key2, key_type, key_file);
+
+  /* handle key direction */
+  key_direction_state_init (&kds, key_direction);
+  must_have_n_keys (key_file, opt_name, &key2, kds.need_keys);
+
+  /* initialize key in both directions */
+  openvpn_snprintf (log_prefix, sizeof (log_prefix), "Outgoing %s", key_name);
+  init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], key_type,
+		OPENVPN_OP_ENCRYPT, log_prefix);
+  openvpn_snprintf (log_prefix, sizeof (log_prefix), "Incoming %s", key_name);
+  init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], key_type,
+		OPENVPN_OP_DECRYPT, log_prefix);
+
+  CLEAR (key2);
 }
 
 /* header and footer for static key file */
src/openvpn/crypto.h +3 -6
index 4b90c67..bc9630a 100644
@@ -465,12 +465,9 @@ void key2_print (const struct key2* k,
 		 const char* prefix0,
 		 const char* prefix1);
 
-#define GHK_INLINE  (1<<0)
-void get_tls_handshake_key (const struct key_type *key_type,
-			    struct key_ctx_bi *ctx,
-			    const char *passphrase_file,
-			    const int key_direction,
-			    const unsigned int flags);
+void crypto_read_openvpn_key (const struct key_type *key_type,
+	struct key_ctx_bi *ctx, const char *key_file, const char *key_inline,
+	const int key_direction, const char *key_name, const char *opt_name);
 
 /*
  * Inline functions
src/openvpn/init.c +9 -38

65 lines not on this page · raw message

Signature · 1 line
2.7.4
#2 lore raw

Re: [Openvpn-devel] [PATCH 1/5] Refactor static/tls-auth key loading

Am 08.11.16 um 21:18 schrieb Steffan Karger:

Quoted 8 lines

Remove duplicate code, in preparation for adding --tls-crypt, which otherwise would have to duplicate this code again.

This should be equivalent to the old code, except for two things:
* The log lines for static key initialization change slightly, from "Static Encrypt/Decrypt" to "Incoming/Outgoing Static Key Encryption"
* We also 'check and fix highly unlikely key problems' for tls-auth keys (boils down to a sanity-check for an all-zero key).

Looks good. ACK to this one.

Arne

#3 lore raw

[Openvpn-devel] [PATCH applied] Re: Refactor static/tls-auth key loading

In reply to Steffan Karger

Your patch has been applied to the master branch.

commit 28c115e401636432b1da2365b8f144523d9d7c53

Author: Steffan Karger
Date:   Tue Nov 8 21:18:18 2016 +0100
     Refactor static/tls-auth key loading

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg12969.html
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#4 lore raw

PATCH 1/1 Remove unused variables from do_init_crypto_static()

In reply to Steffan Karger

Commit 28c115e4 refactored the key loading, but forgot to remove these variables from do_init_crypto_static(). Fix that.

---
 src/openvpn/init.c | 3 ---
 1 file changed, 3 deletions(-)
src/openvpn/init.c +0 -3
index d6733f0..c42aed2 100644
@@ -2132,9 +2132,6 @@ do_init_crypto_static (struct context *c, const unsigned int flags)
 
   if (!key_ctx_bi_defined (&c->c1.ks.static_key))
     {
-      struct key2 key2;
-      struct key_direction_state kds;
-
       /* Get cipher & hash algorithms */
       init_key_type (&c->c1.ks.key_type, options->ciphername, options->authname,
 		     options->keysize, options->test_crypto, true);
Signature · 1 line
2.7.4
#5 lore raw

Re: [Openvpn-devel] [PATCH applied] Remove unused variables from do_init_crypto_static()

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

ACK.

Your patch has been applied to the master branch

commit 4066f1ce602c2404fa4d80ba237e9d24d79e26cd

Author: Steffan Karger
Date:   Wed Nov 16 14:23:04 2016 +0100
     Remove unused variables from do_init_crypto_static()

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: David Sommerseth <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg13079.html
     Signed-off-by: David Sommerseth <[email protected]...>

- --
kind regards,

David Sommerseth

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJYLHppAAoJEIbPlEyWcf3ykgwP/2uw4SowfmDrrJw/2Beu1hyX u0Uc13669J3wrXRiMz0kvsFEYQe5dqy23pXB/TMJY8lceHzDtn4bJdyOPwaCTMFG exMdU5LSkeK7PuXghszKAa4+UJdEHEswagDXLwP/tR8hGTiRD5k5VnzkmMBny3eN 1VJ6Zxe30FYs96nJh2PV18FljlBr3Lb/Zsd/kzybc8+XQf1qadVPVVeouGK/ZQeW SmFmX9DjcglU3BjNO4VcVMHjqbTSM7xRYpEtVH1HQHGZa675tddGZokEIt0GGKdE r8J5Ti73T9BdVhSuYhS/r9i1p/o4Xe+gcmSBPjzxSLcc9hJd4/e7uns6z7jMgdPd ohPBV3znuOlC7DeI5BpTNioQ8S/Y47ZQdGAjQhons2O+kIMqqdeiyOGaWPyiIxqU X2cxyV/GP3jIiX7uba8wlMHREU3qv2P1gjX1tfNLOuFbBvtL6q2N993LlXkaC3pQ sPhMB/j4HZeHSHxNT5iSx3CsZA1mMgBvZYh7/Hl/Ju5zf4DJI+9qmHWSc62OEEzV RwnH3UEZAYS08iYr4GhgbPCIXF4ExWV1QoZA++RutL5ZOy/QhOvgqzKKMxgrMtt1 hXbymJppYV9wmMboT4xAk9WOgLhCoMz46GpF1FxLwMHB87mN/aHqyje5K+Y9WXwg o5FE6RjqR/DsUrnv+9Yt
=UAhz
-----END PGP SIGNATURE-----

#6 lore raw

PATCH 2/5 Add control channel encryption (--tls-crypt)

In reply to Steffan Karger

This adds a --tls-crypt option, which uses a pre-shared static key (like the --tls-auth key) to encrypt control channel packets.

Encrypting control channel packets has three main advantages:

* It provides more privacy by hiding the certificate used for the TLS connection.
* It is harder to identify OpenVPN traffic as such.
* It provides "poor-man's" post-quantum security, against attackers who will never know the pre-shared key (i.e. no forward secrecy).

Control channel packet encryption
---------------------------------

We propose to use the following encryption method, based on the SIV construction [0], to achieve nonce misuse-resistant authenticated encryption:

   msg      = control channel plaintext
   header   = opcode (1 byte) || session_id (8 bytes) || packet_id (8 bytes)
   Ka       = authentication key (256 bits)
   Ke       = encryption key (256 bits)

(Ka and Ke are pre-shared keys, like with --tls-auth)

   auth_tag = HMAC-SHA256(Ka, header || msg)
   IV       = 128 most-significant bits of auth_tag
   ciph     = AES256-CTR(Ke, IV, msg)

   output   = Header || Tag || Ciph

This boils down to the following on-the-wire packet format:

   -opcode- || -session_id- || -packet_id- || auth_tag || * payload *

Where
- XXX - means authenticated, and
* XXX * means authenticated and encrypted.

Which is very similar to the current tls-auth packet format, and has the same overhead as "--tls-auth" with "--auth SHA256".

The use of a nonce misuse-resistant authenticated encryption scheme allows us to worry less about the risks of nonce collisions. This is important, because in contrast with the data channel in TLS mode, we will not be able to rotate tls-crypt keys often or fully guarantee nonce uniqueness. For non misuse-resistant modes such as GCM [1], [2], the data channel in TLS mode only has to ensure that the packet counter never rolls over, while tls-crypt would have to provide nonce uniqueness over all control channel packets sent by all clients, for the lifetime of the tls-crypt key.

Unlike with tls-auth, no --key-direction has to be specified for tls-crypt. TLS servers always use key direction 1, and TLS clients always use key direction 2, which means that client->server traffic and server->client traffic always use different keys, without requiring configuration.

Using fixed, secure, encryption and authentication algorithms makes both implementation and configuration easier. If we ever want to, we can extend this to support other crypto primitives. Since tls-crypt should provide privacy as well as DoS protection, these should not be made negotiable.

Security considerations:
------------------------

tls-crypt is a best-effort mechanism that aims to provide as much privacy and security as possible, while staying as simple as possible. The following are some security considerations for this scheme.

1. The same tls-crypt key is potentially shared by a lot of peers, so it is quite likely to get compromised. Once an attacker acquires the tls-crypt key, this mechanism no longer provides any security against the attacker.

2. Since many peers potentially use the tls-crypt key for a long time, a lot of data might be encrypted under the tls-crypt key. This leads to two potential problems:

* The "opcode || session id || packet id" combination might collide. This might happen in larger setups, because the session id contains just 64 bits or random. Using the uniqueness requirement from the GCM spec [3] (a collision probability of less than 2^(-32)), uniqueness is achieved when using the tls-crypt key for at most 2^16 (65536) connections per process start. (The packet id includes the daemon start time in the packet ID, which should be different after stopping and (re)starting OpenPVN.)

And if a collision happens, an attacker can *only* learn whether colliding packets contain the same plaintext. Attackers will not be able to learn anything else about the plaintext (unless the attacker knows the plaintext of one of these packets, of course). Since the impact is limited, I consider this an acceptable remaining risk.

* The IVs used in encryption might collide. When two IVs collide, an attacker can learn the xor of the two plaintexts by xorring the ciphertexts. This is a serious loss of confidentiality. The IVs are 128-bit, so when HMAC-SHA256 is a secure PRF (an assumption that must also hold for TLS), and we use the same uniqueness requirement from [3], this limits the total amount of control channel messages for all peers in the setup to 2^48. Assuming a large setup of 2^16 (65536) clients, and a (conservative) number of 2^16 control channel packets per connection on average, this means that clients may set up 2^16 connections on average. I think these numbers are reasonable.

(I have a follow-up proposal to use client-specific tls-auth/tls-crypt keys to partially mitigate these issues, but let's tackle this patch first.)

References:
-----------

[0] Rogaway & Shrimpton, A Provable-Security Treatment of the Key-Wrap

    Problem, 2006
    (https://www.iacr.org/archive/eurocrypt2006/40040377/40040377.pdf)

[1] Ferguson, Authentication weaknesses in GCM, 2005
(http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/CWC-GCM/Ferguson2.pdf)

[2] Joux, Authentication Failures in NIST version of GCM, 2006
(http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/800-38_Series-Drafts/GCM/Joux_comments.pdf)

[3] Dworking, Recommendation for Block Cipher Modes of Operation:
Galois/Counter Mode (GCM) and GMAC, 2007
(http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf)

---
 Changes.rst              |   5 +
 doc/openvpn.8            |  39 +++++++-
 src/openvpn/Makefile.am  |   1 +
 src/openvpn/crypto.c     |  17 +---
 src/openvpn/crypto.h     |  18 ++++
 src/openvpn/init.c       |  30 ++++--
 src/openvpn/openvpn.h    |   4 +-
 src/openvpn/options.c    |  31 +++++-
 src/openvpn/options.h    |   8 +-
 src/openvpn/ssl.c        | 119 +++++++++++++++--------
 src/openvpn/ssl.h        |   2 +-
 src/openvpn/ssl_common.h |  18 +++-
 src/openvpn/tls_crypt.c  | 245 +++++++++++++++++++++++++++++++++++++++++++++++
 src/openvpn/tls_crypt.h  | 144 ++++++++++++++++++++++++++++
 14 files changed, 603 insertions(+), 78 deletions(-)
 create mode 100644 src/openvpn/tls_crypt.c
 create mode 100644 src/openvpn/tls_crypt.h
Changes.rst +5 -0
index c0f14a1..78897f8 100644
@@ -111,6 +111,11 @@ AIX platform support
 	AIX platform support has been added. The support only includes tap
     devices since AIX does not provide tun interface.
 
+Control channel encryption (``--tls-crypt``)
+    Use a pre-shared static key (like the ``--tls-auth`` key) to encrypt control
+    channel packets.  Provides more privacy, some obfuscation and poor-man's
+    post-quantum security.
+
 
 User-visible Changes
 --------------------
doc/openvpn.8 +34 -5
index 863dcf9..b84f4c6 100644
@@ -3471,8 +3471,10 @@ DoS scenario, legitimate connections might also be refused.
 For the best protection against DoS attacks in server mode,
 use
 .B \-\-proto udp
-and
-.B \-\-tls\-auth.
+and either
+.B \-\-tls\-auth
+or
+.B \-\-tls\-crypt\fR.
 .\"*********************************************************
 .TP
 .B \-\-learn\-address cmd
@@ -4935,8 +4937,8 @@ Exit on TLS negotiation failure.
 .\"*********************************************************
 .TP
 .B \-\-tls\-auth file [direction]
-Add an additional layer of HMAC authentication on top of the TLS
-control channel to protect against DoS attacks.
+Add an additional layer of HMAC authentication on top of the TLS control channel
+to mitigate DoS attacks and attacks on the TLS stack.
 
 In a nutshell,
 .B \-\-tls\-auth
@@ -5007,12 +5009,39 @@ option which will keep OpenVPN's replay protection state
 in a file so that it is not lost across restarts.
 
 It should be emphasized that this feature is optional and that the
-passphrase/key file used with
+key file used with
 .B \-\-tls\-auth
 gives a peer nothing more than the power to initiate a TLS
 handshake.  It is not used to encrypt or authenticate any tunnel data.
 .\"*********************************************************
 .TP
+.B \-\-tls\-crypt keyfile
+
+Encrypt and authenticate all control channel packets with the key from
+.B keyfile.
+(See
+.B \-\-tls\-auth
+for more background.)
+
+Encrypting (and authenticating) control channel packets:
+.RS
+.IP \[bu] 2
+provides more privacy by hiding the certificate used for the TLS connection,
+.IP \[bu]
+makes it harder to identify OpenVPN traffic as such,
+.IP \[bu]
+provides "poor-man's" post-quantum security, against attackers who will never
+know the pre-shared key (i.e. no forward secrecy).
+.RE
+
+.IP
+In contrast to
+.B \-\-tls\-auth\fR,
+.B \-\-tls\-crypt
+does *not* require the user to set
+.B \-\-key\-direction\fR.
+.\"*********************************************************
+.TP
 .B \-\-askpass [file]
 Get certificate password from console or
 .B file
src/openvpn/Makefile.am +1 -0
index 12b9ebf..4c18449 100644
@@ -113,6 +113,7 @@ openvpn_SOURCES = \
 	ssl_verify_mbedtls.c ssl_verify_mbedtls.h \
 	status.c status.h \
 	syshead.h \
+	tls_crypt.c tls_crypt.h \
 	tun.c tun.h \
 	win32.h win32.c \
 	cryptoapi.h cryptoapi.c
src/openvpn/crypto.c +2 -15
index 749b7da..ab43005 100644
@@ -6,7 +6,7 @@
  *             packet compression.
  *
  *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <[email protected]...>
- *  Copyright (C) 2010 Fox Crypto B.V. <[email protected]...>
+ *  Copyright (C) 2010-2016 Fox Crypto B.V. <[email protected]...>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
@@ -63,9 +63,6 @@
  * happen unless the frame parameters are wrong.
  */
 
-#define CRYPT_ERROR(format) \
-  do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)
-
 static void
 openvpn_encrypt_aead (struct buffer *buf, struct buffer work,
 	 struct crypto_options *opt) {
@@ -326,17 +323,7 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
     }
 }
 
-/**
- * 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,
+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;
src/openvpn/crypto.h +18 -0
index bc9630a..ff90745 100644
@@ -266,6 +266,9 @@ struct crypto_options
                                  *   security operation functions. */
 };
 
+#define CRYPT_ERROR(format) \
+  do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)
+
 /**
  * Minimal IV length for AEAD mode ciphers (in bytes):
  * 4-byte packet id + 8 bytes implicit IV.
@@ -397,6 +400,21 @@ bool openvpn_decrypt (struct buffer *buf, struct buffer work,
 
 /** @} name Functions for performing security operations on data channel packets */
 
+/**
+ * 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.
+ */
+bool crypto_check_replay(struct crypto_options *opt,
+    const struct packet_id_net *pin, const char *error_prefix,
+    struct gc_arena *gc);
+
+
 /** Calculate crypto overhead and adjust frame to account for that */
 void crypto_adjust_frame_parameters(struct frame *frame,
 				    const struct key_type* kt,
src/openvpn/init.c +24 -6
index 1a9340c..3574d81 100644
@@ -44,6 +44,7 @@
 #include "ping.h"
 #include "mstats.h"
 #include "ssl_verify.h"
+#include "tls_crypt.h"
 #include "forward-inline.h"
 
 #include "memdbg.h"
@@ -2078,7 +2079,7 @@ key_schedule_free (struct key_schedule *ks, bool free_ssl_ctx)
   if (tls_ctx_initialised(&ks->ssl_ctx) && free_ssl_ctx)
     {
       tls_ctx_free (&ks->ssl_ctx);
-      free_key_ctx_bi (&ks->tls_auth_key);
+      free_key_ctx_bi (&ks->tls_wrap_key);
     }
 #endif /* ENABLE_CRYPTO */
   CLEAR (*ks);
@@ -2235,11 +2236,17 @@ do_init_crypto_tls_c1 (struct context *c)
 	    }
 
 	  crypto_read_openvpn_key (&c->c1.ks.tls_auth_key_type,
-	      &c->c1.ks.tls_auth_key, options->tls_auth_file,
+	      &c->c1.ks.tls_wrap_key, options->tls_auth_file,
 	      options->tls_auth_file_inline, options->key_direction,
 	      "Control Channel Authentication", "tls-auth");
 	}
 
+      /* TLS handshake encryption+authentication (--tls-crypt) */
+      if (options->tls_crypt_file) {
+	  tls_crypt_init_key (&c->c1.ks.tls_wrap_key, options->tls_crypt_file,
+	      options->tls_crypt_inline, options->tls_server);
+      }
+
       c->c1.ciphername = options->ciphername;
       c->c1.authname = options->authname;
 
@@ -2418,14 +2425,25 @@ 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_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;
+      to.tls_wrap.mode = TLS_WRAP_AUTH;
+      to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
+      to.tls_wrap.opt.pid_persist = &c->c1.pid_persist;
+      to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM;
       crypto_adjust_frame_parameters (&to.frame,
 				      &c->c1.ks.tls_auth_key_type,
 				      false, true, true);
     }
 
+  /* TLS handshake encryption (--tls-crypt) */
+  if (options->tls_crypt_file)
+    {
+      to.tls_wrap.mode = TLS_WRAP_CRYPT;
+      to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
+      to.tls_wrap.opt.pid_persist = &c->c1.pid_persist;
+      to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM;
+      tls_crypt_adjust_frame_parameters (&to.frame);
+    }
+
   /* If we are running over TCP, allow for
      length prefix */
   socket_adjust_frame_parameters (&to.frame, options->ce.proto);
@@ -3789,7 +3807,7 @@ inherit_context_child (struct context *dest,
   dest->c1.ks.key_type = src->c1.ks.key_type;
   /* 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_wrap_key = src->c1.ks.tls_wrap_key;
   dest->c1.ks.tls_auth_key_type = src->c1.ks.tls_auth_key_type;
 #endif
 
src/openvpn/openvpn.h +2 -2
index 5cda7b4..9e6a5fb 100644
@@ -65,9 +65,9 @@ struct key_schedule
   /* our global SSL context */
   struct tls_root_ctx ssl_ctx;
 
-  /* optional authentication HMAC key for TLS control channel */
+  /* optional TLS control channel wrapping */
   struct key_type tls_auth_key_type;
-  struct key_ctx_bi tls_auth_key;
+  struct key_ctx_bi tls_wrap_key;
 #else				/* ENABLE_CRYPTO */
   int dummy;
 #endif				/* ENABLE_CRYPTO */
src/openvpn/options.c +29 -2
index 5a5e7ef..e9dc17e 100644
@@ -610,10 +610,17 @@ static const char usage_message[] =
   "--single-session: Allow only one session (reset state on restart).\n"
   "--tls-exit      : Exit on TLS negotiation failure.\n"
   "--tls-auth f [d]: Add an additional layer of authentication on top of the TLS\n"
-  "                  control channel to protect against DoS attacks.\n"
-  "                  f (required) is a shared-secret passphrase file.\n"
+  "                  control channel to protect against attacks on the TLS stack\n"
+  "                  and DoS attacks.\n"
+  "                  f (required) is a shared-secret key file.\n"
   "                  The optional d parameter controls key directionality,\n"
   "                  see --secret option for more info.\n"
+  "--tls-crypt key : Add an additional layer of authenticated encryption on top\n"
+  "                  of the TLS control channel to hide the TLS certificate,\n"
+  "                  provide basic post-quantum security and protect against\n"
+  "                  attacks on the TLS stack and DoS attacks.\n"
+  "                  key (required) provides the pre-shared key file.\n"
+  "                  see --secret option for more info.\n"
   "--askpass [file]: Get PEM password from controlling tty before we daemonize.\n"
   "--auth-nocache  : Don't cache --askpass or --auth-user-pass passwords.\n"
   "--crl-verify crl ['dir']: Check peer certificate against a CRL.\n"
@@ -1709,6 +1716,7 @@ show_settings (const struct options *o)
   SHOW_BOOL (tls_exit);
 
   SHOW_STR (tls_auth_file);
+  SHOW_STR (tls_crypt_file);
 #endif /* ENABLE_CRYPTO */
 
 #ifdef ENABLE_PKCS11
@@ -2376,6 +2384,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
 	      notnull (options->priv_key_file, "private key file (--key) or PKCS#12 file (--pkcs12)");
 	    }
 	}
+    if (options->tls_auth_file && options->tls_crypt_file)
+      {
+        msg (M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive");
+      }
     }
   else
     {
@@ -2407,6 +2419,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
       MUST_BE_UNDEF (handshake_window);
       MUST_BE_UNDEF (transition_window);
       MUST_BE_UNDEF (tls_auth_file);
+      MUST_BE_UNDEF (tls_crypt_file);
       MUST_BE_UNDEF (single_session);
 #ifdef ENABLE_PUSH_PEER_INFO
       MUST_BE_UNDEF (push_peer_info);
@@ -2851,6 +2864,8 @@ options_postprocess_filechecks (struct options *options)
 
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->tls_auth_file, R_OK,
                              "--tls-auth");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->tls_crypt_file, R_OK,
+                             "--tls-crypt");
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->shared_secret_file, R_OK,
                              "--secret");
   errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR,
@@ -3192,6 +3207,9 @@ options_string (const struct options *o,
       {
 	if (o->tls_auth_file)
 	  buf_printf (&out, ",tls-auth");
+	/* Not adding tls-crypt here, because we won't reach this code if
+	 * tls-auth/tls-crypt does not match.  Removing tls-auth here would
+	 * break stuff, so leaving that in place. */
 
 	if (o->key_method > 1)
 	  buf_printf (&out, ",key-method %d", o->key_method);
@@ -7214,6 +7232,15 @@ add_option (struct options *options,
 	}
       options->tls_auth_file = p[1];
     }
+  else if (streq (p[0], "tls-crypt") && p[1] && !p[3])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      if (streq (p[1], INLINE_FILE_TAG) && p[2])
+	{
+	  options->tls_crypt_inline = p[2];
+	}
+      options->tls_crypt_file = p[1];
+    }
   else if (streq (p[0], "key-method") && p[1] && !p[2])
     {
       int key_method;
src/openvpn/options.h +6 -2
index 0ebea30..841179e 100644
@@ -558,10 +558,14 @@ struct options
   /* Old key allowed to live n seconds after new key goes active */
   int transition_window;
 
-  /* Special authentication MAC for TLS control channel */
-  const char *tls_auth_file;		/* shared secret */
+  /* Shared secret used for TLS control channel authentication */
+  const char *tls_auth_file;
   const char *tls_auth_file_inline;
 
+  /* Shared secret used for TLS control channel authenticated encryption */
+  const char *tls_crypt_file;
+  const char *tls_crypt_inline;
+
   /* Allow only one session */
   bool single_session;
 
src/openvpn/ssl.c +77 -42

227 lines not on this page · raw message

src/openvpn/ssl.h +1 -1
index e6963a4..777b621 100644
@@ -136,7 +136,7 @@
  */
 struct tls_auth_standalone
 {
-  struct crypto_options tls_auth_options;
+  struct tls_wrap_ctx tls_wrap;
   struct frame frame;
 };
 
src/openvpn/ssl_common.h +15 -3
index b04a24c..28702af 100644
@@ -204,6 +204,18 @@ struct key_state
 #endif
 };
 
+/** Control channel wrapping (--tls-auth/--tls-crypt) context */
+struct tls_wrap_ctx
+{
+  enum {
+    TLS_WRAP_NONE = 0,	/**< No control channel wrapping */
+    TLS_WRAP_AUTH,	/**< Control channel authentication */
+    TLS_WRAP_CRYPT,	/**< Control channel encryption and authentication */
+  } mode;			/**< Control channel wrapping mode */
+  struct crypto_options opt;	/**< Crypto state */
+  struct buffer work;		/**< Work buffer (only for --tls-crypt) */
+};
+
 /*
  * Our const options, obtained directly or derived from
  * command line options.
@@ -278,8 +290,8 @@ struct tls_options
   const char *config_authname;
   bool ncp_enabled;
 
-  /* packet authentication for TLS handshake */
-  struct crypto_options tls_auth;
+  /** TLS handshake wrapping state */
+  struct tls_wrap_ctx tls_wrap;
 
   /* frame parameters for TLS control channel */
   struct frame frame;
@@ -380,7 +392,7 @@ struct tls_session
   bool burst;
 
   /* authenticate control packets */
-  struct crypto_options tls_auth;
+  struct tls_wrap_ctx tls_wrap;
 
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id;	/* our random session ID */
src/openvpn/tls_crypt.c new file +245 -0

245 lines not on this page · raw message

src/openvpn/tls_crypt.h new file +144 -0

144 lines not on this page · raw message

Signature · 1 line
2.7.4
#7 lore raw
Quoted 4 lines

This boils down to the following on-the-wire packet format:

   -opcode- || -session_id- || -packet_id- || auth_tag || * payload *

I am pretty that opcode is *not* authenticated looking the code. Which is probably not a problem but should not be documented as authenticated. (There is a buf_advance(buf ,1) before calling the unwrap function.

Quoted 3 lines

Where
- XXX - means authenticated, and
* XXX * means authenticated and encrypted.

This misalignes the rest of the packet. I am not sure how big the penality actually is but we have DATA_V2 and COMP_V2 fix this misalignment. But then again ignore my comment for control channel traffic.

For the packet format I think it would be very good to add a key id to help transisition or allow more paranoid people to have a a key per client. In the server you would specify all allowed valid keys together with their ids (or somehow emded that id/read that id from keyfile). Not that saying that this should be implement in the first version of the patch but something that we can implement later without having a tls-crypt and a tls-crypt v2 packet format.

Quoted 38 lines

Which is very similar to the current tls-auth packet format, and has the same overhead as "--tls-auth" with "--auth SHA256".

The use of a nonce misuse-resistant authenticated encryption scheme allows us to worry less about the risks of nonce collisions. This is important, because in contrast with the data channel in TLS mode, we will not be able to rotate tls-crypt keys often or fully guarantee nonce uniqueness. For non misuse-resistant modes such as GCM [1], [2], the data channel in TLS mode only has to ensure that the packet counter never rolls over, while tls-crypt would have to provide nonce uniqueness over all control channel packets sent by all clients, for the lifetime of the tls-crypt key.

Unlike with tls-auth, no --key-direction has to be specified for tls-crypt. TLS servers always use key direction 1, and TLS clients always use key direction 2, which means that client->server traffic and server->client traffic always use different keys, without requiring configuration.

Using fixed, secure, encryption and authentication algorithms makes both implementation and configuration easier. If we ever want to, we can extend this to support other crypto primitives. Since tls-crypt should provide privacy as well as DoS protection, these should not be made negotiable.

Security considerations:
------------------------

tls-crypt is a best-effort mechanism that aims to provide as much privacy and security as possible, while staying as simple as possible. The following are some security considerations for this scheme.

1. The same tls-crypt key is potentially shared by a lot of peers, so it is quite likely to get compromised. Once an attacker acquires the tls-crypt key, this mechanism no longer provides any security against the attacker.

Quoted 7 lines

2. Since many peers potentially use the tls-crypt key for a long time, a lot of data might be encrypted under the tls-crypt key. This leads to two potential problems:

* The "opcode || session id || packet id" combination might collide. This might happen in larger setups, because the session id contains just 64 bits or random. Using the uniqueness requirement from the

Typo: of random

Quoted 25 lines

GCM spec [3] (a collision probability of less than 2^(-32)), uniqueness is achieved when using the tls-crypt key for at most 2^16 (65536) connections per process start. (The packet id includes the daemon start time in the packet ID, which should be different after stopping and (re)starting OpenPVN.)

And if a collision happens, an attacker can *only* learn whether colliding packets contain the same plaintext. Attackers will not be able to learn anything else about the plaintext (unless the attacker knows the plaintext of one of these packets, of course). Since the impact is limited, I consider this an acceptable remaining risk.

* The IVs used in encryption might collide. When two IVs collide, an attacker can learn the xor of the two plaintexts by xorring the ciphertexts. This is a serious loss of confidentiality. The IVs are 128-bit, so when HMAC-SHA256 is a secure PRF (an assumption that must also hold for TLS), and we use the same uniqueness requirement from [3], this limits the total amount of control channel messages for all peers in the setup to 2^48. Assuming a large setup of 2^16 (65536) clients, and a (conservative) number of 2^16 control channel packets per connection on average, this means that clients may set up 2^16 connections on average. I think these numbers are reasonable.

Quoted 3 lines

(I have a follow-up proposal to use client-specific tls-auth/tls-crypt keys to partially mitigate these issues, but let's tackle this patch first.)

How does the server know which packet Id to use? Part of opcode, e.g. some bits there or part of the session id.

Quoted 17 lines

+ */

src/openvpn/tls_crypt.c new file +16 -0
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#ifdef ENABLE_CRYPTO
+#include "crypto.h"
+#include "session_id.h"
+
+#include "tls_crypt.h"
+
+int tls_crypt_buf_overhead(void)

src/openvpn/tls_crypt.c line 49

Quoted 10 lines
src/openvpn/tls_crypt.c new file +10 -0
+{
+  return packet_id_size (true) + TLS_CRYPT_TAG_SIZE + TLS_CRYPT_BLOCK_SIZE;
+}
+
+void
+tls_crypt_init_key (struct key_ctx_bi *key, const char *key_file,
+    const char *key_inline, bool tls_server) {
+  struct key_type kt;
+
+  kt.cipher = cipher_kt_get ("AES-256-CTR");

This can fail, if we are on a weird OpenSSL library. We assert later (ctx->cipher). I think you should also add an ASSERT/M_FATAL here that gives gives a clear indication that the crypto library does not support AES-256-CTR.

src/openvpn/tls_crypt.c line 51

Quoted 2 lines
src/openvpn/tls_crypt.c new file +2 -0
+  kt.cipher_length = cipher_kt_key_size (kt.cipher);
+  kt.digest = md_kt_get ("SHA256");

Same here.

Quoted 7 lines
src/openvpn/tls_crypt.c new file +7 -0
+  gc_init (&gc);
+
+  dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP FROM: %s",
+      format_hex (BPTR (src), BLEN (src), 80, &gc));
+
+  /* Get packet ID */
+  {

src/openvpn/tls_crypt.c line 99

Quoted 10 lines
src/openvpn/tls_crypt.c new file +10 -0
+    struct packet_id_net pin;
+    packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true);
+    packet_id_write (&pin, dst, true, false);
+  }
+
+  dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP AD: %s",
+      format_hex (BPTR (dst), BLEN (dst), 0, &gc));
+
+  /* Buffer overflow check */
+  if (!buf_safe (dst, BLEN (src) + TLS_CRYPT_BLOCK_SIZE + TLS_CRYPT_TAG_SIZE))

This does not take the opcode into account

Quoted 18 lines
src/openvpn/tls_crypt.c new file +18 -0
+    {
+      msg (D_CRYPT_ERRORS, "TLS-CRYPT WRAP: buffer size error, "
+	  "sc=%d so=%d sl=%d dc=%d do=%d dl=%d", src->capacity, src->offset,
+	  src->len, dst->capacity, dst->offset, dst->len);
+      goto err;
+    }
+
+  /* Calculate auth tag and synthetic IV */
+  {
+    uint8_t *tag = NULL;
+    hmac_ctx_reset (ctx->hmac);
+    hmac_ctx_update (ctx->hmac, BPTR (dst), BLEN (dst));
+    hmac_ctx_update (ctx->hmac, BPTR (src), BLEN (src));
+
+    ASSERT (tag = buf_write_alloc (dst, TLS_CRYPT_TAG_SIZE));
+    hmac_ctx_final (ctx->hmac, tag);
+
+    dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP TAG: %s",

src/openvpn/tls_crypt.c line 127

Quoted 10 lines
src/openvpn/tls_crypt.c new file +10 -0
+        format_hex (tag, TLS_CRYPT_TAG_SIZE, 0, &gc));
+
+    cipher_ctx_reset (ctx->cipher, tag);
+  }
+
+  /* Encrypt src */
+  {
+    int outlen = 0;
+    ASSERT (cipher_ctx_update (ctx->cipher, BEND (dst), &outlen,
+	BPTR (src), BLEN(src)));

Indentation is wrong here.

src/openvpn/tls_crypt.c line 138

Quoted 11 lines
src/openvpn/tls_crypt.c new file +11 -0
+    ASSERT (buf_inc_len (dst, outlen));
+    ASSERT (cipher_ctx_final (ctx->cipher, BPTR (dst), &outlen));
+    ASSERT (buf_inc_len (dst, outlen));
+  }
+
+  dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP TO: %s",
+      format_hex (BPTR (dst), BLEN (dst), 80, &gc));
+
+  gc_free (&gc);
+  return true;
+

Otherwise all looks. Note that I have not extensively tested it so far but more looked on the sourcecode and crypto stuff.

Arne

#8 lore raw

Hi,

Thanks for reviewing! Replies inline.

Quoted 8 lines · Arne Schwabe
Quoted 4 lines

This boils down to the following on-the-wire packet format:

   -opcode- || -session_id- || -packet_id- || auth_tag || * payload *

I am pretty that opcode is *not* authenticated looking the code. Which is probably not a problem but should not be documented as authenticated. (There is a buf_advance(buf ,1) before calling the unwrap function.

No, the opcode *is* authenticated. See e.g. tls_crypt_wrap(), which receives a dst buffer which is prepared with the opcode and session_id, and computes the HMAC over the contents.

(I only see a buf_advance (buf, SID_SIZE + 1) *after* the tls_crypt_unwrap() call?)

Quoted 7 lines
Quoted 3 lines

Where
- XXX - means authenticated, and
* XXX * means authenticated and encrypted.

This misalignes the rest of the packet. I am not sure how big the penality actually is but we have DATA_V2 and COMP_V2 fix this misalignment. But then again ignore my comment for control channel traffic.

Yes, this is how it's always been. I don't think that's an issue for the (low-bandwidth) control channel traffic.

Quoted 7 lines

For the packet format I think it would be very good to add a key id to help transisition or allow more paranoid people to have a a key per client. In the server you would specify all allowed valid keys together with their ids (or somehow emded that id/read that id from keyfile). Not that saying that this should be implement in the first version of the patch but something that we can implement later without having a tls-crypt and a tls-crypt v2 packet format.

Yeah, I have plans for supporting per-client keys, but those won't need a different packet format. Also, I'd like to support client-specific keys for both tls-auth and tls-crypt. *If* we need a new packet format, I think the client-specific key patch set will be right moment to introduce it. (This stuff is not negotiated, which makes it less painful to change anyway.)

Quoted 71 lines
Quoted 37 lines

Which is very similar to the current tls-auth packet format, and has the same overhead as "--tls-auth" with "--auth SHA256".

The use of a nonce misuse-resistant authenticated encryption scheme allows us to worry less about the risks of nonce collisions. This is important, because in contrast with the data channel in TLS mode, we will not be able to rotate tls-crypt keys often or fully guarantee nonce uniqueness. For non misuse-resistant modes such as GCM [1], [2], the data channel in TLS mode only has to ensure that the packet counter never rolls over, while tls-crypt would have to provide nonce uniqueness over all control channel packets sent by all clients, for the lifetime of the tls-crypt key.

Unlike with tls-auth, no --key-direction has to be specified for tls-crypt. TLS servers always use key direction 1, and TLS clients always use key direction 2, which means that client->server traffic and server->client traffic always use different keys, without requiring configuration.

Using fixed, secure, encryption and authentication algorithms makes both implementation and configuration easier. If we ever want to, we can extend this to support other crypto primitives. Since tls-crypt should provide privacy as well as DoS protection, these should not be made negotiable.

Security considerations:
------------------------

tls-crypt is a best-effort mechanism that aims to provide as much privacy and security as possible, while staying as simple as possible. The following are some security considerations for this scheme.

1. The same tls-crypt key is potentially shared by a lot of peers, so it is quite likely to get compromised. Once an attacker acquires the tls-crypt key, this mechanism no longer provides any security against the attacker.

Quoted 7 lines

2. Since many peers potentially use the tls-crypt key for a long time, a lot of data might be encrypted under the tls-crypt key. This leads to two potential problems:

* The "opcode || session id || packet id" combination might collide. This might happen in larger setups, because the session id contains just 64 bits or random. Using the uniqueness requirement from the

Typo: of random

Quoted 25 lines

GCM spec [3] (a collision probability of less than 2^(-32)), uniqueness is achieved when using the tls-crypt key for at most 2^16 (65536) connections per process start. (The packet id includes the daemon start time in the packet ID, which should be different after stopping and (re)starting OpenPVN.)

And if a collision happens, an attacker can *only* learn whether colliding packets contain the same plaintext. Attackers will not be able to learn anything else about the plaintext (unless the attacker knows the plaintext of one of these packets, of course). Since the impact is limited, I consider this an acceptable remaining risk.

* The IVs used in encryption might collide. When two IVs collide, an attacker can learn the xor of the two plaintexts by xorring the ciphertexts. This is a serious loss of confidentiality. The IVs are 128-bit, so when HMAC-SHA256 is a secure PRF (an assumption that must also hold for TLS), and we use the same uniqueness requirement from [3], this limits the total amount of control channel messages for all peers in the setup to 2^48. Assuming a large setup of 2^16 (65536) clients, and a (conservative) number of 2^16 control channel packets per connection on average, this means that clients may set up 2^16 connections on average. I think these numbers are reasonable.

Quoted 6 lines
Quoted 3 lines

(I have a follow-up proposal to use client-specific tls-auth/tls-crypt keys to partially mitigate these issues, but let's tackle this patch first.)

How does the server know which packet Id to use? Part of opcode, e.g. some bits there or part of the session id.

Each connection starts with a packet id of 'daemon start timestamp' + 0.
This means that the first packet is replayable, but that's how it's
always been.

(Note that even though the first packet is replayable, TLS negotiation will not start until the openvpn reliability layer handshake (TCP-like), has completed. OpenVPN handshake msgs 2 and 3 contain a random session id that must be authenticated with the shared secret, preventing an attacker without the key to replay a TLS handshake.)

Quoted 33 lines
Quoted 17 lines

+ */

src/openvpn/tls_crypt.c new file +16 -0
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#ifdef ENABLE_CRYPTO
+#include "crypto.h"
+#include "session_id.h"
+
+#include "tls_crypt.h"
+
+int tls_crypt_buf_overhead(void)

src/openvpn/tls_crypt.c line 49

Quoted 10 lines
src/openvpn/tls_crypt.c new file +10 -0
+{
+  return packet_id_size (true) + TLS_CRYPT_TAG_SIZE + TLS_CRYPT_BLOCK_SIZE;
+}
+
+void
+tls_crypt_init_key (struct key_ctx_bi *key, const char *key_file,
+    const char *key_inline, bool tls_server) {
+  struct key_type kt;
+
+  kt.cipher = cipher_kt_get ("AES-256-CTR");

This can fail, if we are on a weird OpenSSL library. We assert later (ctx->cipher). I think you should also add an ASSERT/M_FATAL here that gives gives a clear indication that the crypto library does not support AES-256-CTR.

Quoted 3 lines

src/openvpn/tls_crypt.c line 51

Quoted 2 lines
src/openvpn/tls_crypt.c new file +2 -0
+  kt.cipher_length = cipher_kt_key_size (kt.cipher);
+  kt.digest = md_kt_get ("SHA256");

Same here.

Indeed, it makes sense to error out with a clear error message here. I'll add them. (For the record, I believe the chance of finding a library without AES-256-CTR or HMAC-SHA-256 support is very close to zero.)

Quoted 11 lines
Quoted 11 lines
src/openvpn/tls_crypt.c new file +11 -0
+  gc_init (&gc);
+
+  dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP FROM: %s",
+      format_hex (BPTR (src), BLEN (src), 80, &gc));
+
+  /* Get packet ID */
+  {
+    struct packet_id_net pin;
+    packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true);
+    packet_id_write (&pin, dst, true, false);
+  }
Quoted 8 lines

src/openvpn/tls_crypt.c line 99

Quoted 6 lines
src/openvpn/tls_crypt.c new file +6 -0
+
+  dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP AD: %s",
+      format_hex (BPTR (dst), BLEN (dst), 0, &gc));
+
+  /* Buffer overflow check */
+  if (!buf_safe (dst, BLEN (src) + TLS_CRYPT_BLOCK_SIZE + TLS_CRYPT_TAG_SIZE))

This does not take the opcode into account

It does. Because the opcode is already present in dst, and buf_safe() verifies against the 'forward capacity' of dst.

Quoted 22 lines
Quoted 22 lines
src/openvpn/tls_crypt.c new file +22 -0
+    {
+      msg (D_CRYPT_ERRORS, "TLS-CRYPT WRAP: buffer size error, "
+	  "sc=%d so=%d sl=%d dc=%d do=%d dl=%d", src->capacity, src->offset,
+	  src->len, dst->capacity, dst->offset, dst->len);
+      goto err;
+    }
+
+  /* Calculate auth tag and synthetic IV */
+  {
+    uint8_t *tag = NULL;
+    hmac_ctx_reset (ctx->hmac);
+    hmac_ctx_update (ctx->hmac, BPTR (dst), BLEN (dst));
+    hmac_ctx_update (ctx->hmac, BPTR (src), BLEN (src));
+
+    ASSERT (tag = buf_write_alloc (dst, TLS_CRYPT_TAG_SIZE));
+    hmac_ctx_final (ctx->hmac, tag);
+
+    dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP TAG: %s",
+        format_hex (tag, TLS_CRYPT_TAG_SIZE, 0, &gc));
+
+    cipher_ctx_reset (ctx->cipher, tag);
+  }
Quoted 8 lines

src/openvpn/tls_crypt.c line 127

Quoted 6 lines
src/openvpn/tls_crypt.c new file +6 -0
+
+  /* Encrypt src */
+  {
+    int outlen = 0;
+    ASSERT (cipher_ctx_update (ctx->cipher, BEND (dst), &outlen,
+	BPTR (src), BLEN(src)));

Indentation is wrong here.

Looks funny in the MUA, but is actually right. The ASSERT() is preceded by 4 spaces, the BPTR by one tab (=8 spaces).

Quoted 7 lines
Quoted 7 lines
src/openvpn/tls_crypt.c new file +7 -0
+    ASSERT (buf_inc_len (dst, outlen));
+    ASSERT (cipher_ctx_final (ctx->cipher, BPTR (dst), &outlen));
+    ASSERT (buf_inc_len (dst, outlen));
+  }
+
+  dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP TO: %s",
+      format_hex (BPTR (dst), BLEN (dst), 80, &gc));
Quoted 8 lines

src/openvpn/tls_crypt.c line 138

Quoted 4 lines
src/openvpn/tls_crypt.c new file +4 -0
+
+  gc_free (&gc);
+  return true;
+

Otherwise all looks. Note that I have not extensively tested it so far but more looked on the sourcecode and crypto stuff.

Thanks!

I have an automated test for this in the OpenVPN-NL test suite, that now verifies this works in UDP and TCP modes, and also checks that the authentication fails if the wrong keys are used. And then there are of course the unit tests from 5/5. It would be nice to also add a t_client test with --tls-crypt, but I do not maintain that infrastructure.

I'll postpone sending the v2 until you've either confirmed my answers, or told me that I'm wrong ;)

-Steffan

#9 lore raw

Hi,

Quoted 5 lines · Steffan Karger

I have an automated test for this in the OpenVPN-NL test suite, that now verifies this works in UDP and TCP modes, and also checks that the authentication fails if the wrong keys are used. And then there are of course the unit tests from 5/5. It would be nice to also add a t_client test with --tls-crypt, but I do not maintain that infrastructure.

I'm happy to add that, but for pure practical reasons, this is a bit cumbersome until the patches are actually *in*. Please remind me later.

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]...
#10 lore raw
In reply to Steffan Karger

Am 13.11.16 um 14:14 schrieb Steffan Karger:

Quoted 10 lines

Hi,

Thanks for reviewing! Replies inline.

Quoted 5 lines · Arne Schwabe
Quoted 4 lines

This boils down to the following on-the-wire packet format:

   -opcode- || -session_id- || -packet_id- || auth_tag || * payload *
Quoted 7 lines
Quoted 3 lines

I am pretty that opcode is *not* authenticated looking the code. Which is probably not a problem but should not be documented as authenticated. (There is a buf_advance(buf ,1) before calling the unwrap function.

No, the opcode *is* authenticated. See e.g. tls_crypt_wrap(), which receives a dst buffer which is prepared with the opcode and session_id, and computes the HMAC over the contents.

Yeah, I looked harder and see now the buffer pointer magic more clearly. My fault.

I think that

    cipher_ctx_reset (ctx->cipher, tag);

belongs into the encrypt block instead of the auth block.

Arne

#11 lore raw
Quoted 5 lines · Arne Schwabe

I think that

    cipher_ctx_reset (ctx->cipher, tag);

belongs into the encrypt block instead of the auth block.

Yes and no. It's part of the encryption, but SIV uses the auth tag as the IV, so this function needs to be called within the scope where the tag pointer is available. I will add a comment to make this more clear.

I'll send a v2 with your comments addressed in a few minutes.

Thanks again!

-Steffan

#12 lore raw

PATCH v2 2/5 Add control channel encryption (--tls-crypt)

This adds a --tls-crypt option, which uses a pre-shared static key (like the --tls-auth key) to encrypt control channel packets.

Encrypting control channel packets has three main advantages:

* It provides more privacy by hiding the certificate used for the TLS connection.
* It is harder to identify OpenVPN traffic as such.
* It provides "poor-man's" post-quantum security, against attackers who will never know the pre-shared key (i.e. no forward secrecy).

Control channel packet encryption
---------------------------------

We propose to use the following encryption method, based on the SIV construction [0], to achieve nonce misuse-resistant authenticated encryption:

   msg      = control channel plaintext
   header   = opcode (1 byte) || session_id (8 bytes) || packet_id (8 bytes)
   Ka       = authentication key (256 bits)
   Ke       = encryption key (256 bits)

(Ka and Ke are pre-shared keys, like with --tls-auth)

   auth_tag = HMAC-SHA256(Ka, header || msg)
   IV       = 128 most-significant bits of auth_tag
   ciph     = AES256-CTR(Ke, IV, msg)

   output   = Header || Tag || Ciph

This boils down to the following on-the-wire packet format:

   -opcode- || -session_id- || -packet_id- || auth_tag || * payload *

Where
- XXX - means authenticated, and
* XXX * means authenticated and encrypted.

Which is very similar to the current tls-auth packet format, and has the same overhead as "--tls-auth" with "--auth SHA256".

The use of a nonce misuse-resistant authenticated encryption scheme allows us to worry less about the risks of nonce collisions. This is important, because in contrast with the data channel in TLS mode, we will not be able to rotate tls-crypt keys often or fully guarantee nonce uniqueness. For non misuse-resistant modes such as GCM [1], [2], the data channel in TLS mode only has to ensure that the packet counter never rolls over, while tls-crypt would have to provide nonce uniqueness over all control channel packets sent by all clients, for the lifetime of the tls-crypt key.

Unlike with tls-auth, no --key-direction has to be specified for tls-crypt. TLS servers always use key direction 1, and TLS clients always use key direction 2, which means that client->server traffic and server->client traffic always use different keys, without requiring configuration.

Using fixed, secure, encryption and authentication algorithms makes both implementation and configuration easier. If we ever want to, we can extend this to support other crypto primitives. Since tls-crypt should provide privacy as well as DoS protection, these should not be made negotiable.

Security considerations:
------------------------

tls-crypt is a best-effort mechanism that aims to provide as much privacy and security as possible, while staying as simple as possible. The following are some security considerations for this scheme.

1. The same tls-crypt key is potentially shared by a lot of peers, so it is quite likely to get compromised. Once an attacker acquires the tls-crypt key, this mechanism no longer provides any security against the attacker.

2. Since many peers potentially use the tls-crypt key for a long time, a lot of data might be encrypted under the tls-crypt key. This leads to two potential problems:

* The "opcode || session id || packet id" combination might collide. This might happen in larger setups, because the session id contains just 64 bits or random. Using the uniqueness requirement from the GCM spec [3] (a collision probability of less than 2^(-32)), uniqueness is achieved when using the tls-crypt key for at most 2^16 (65536) connections per process start. (The packet id includes the daemon start time in the packet ID, which should be different after stopping and (re)starting OpenPVN.)

And if a collision happens, an attacker can *only* learn whether colliding packets contain the same plaintext. Attackers will not be able to learn anything else about the plaintext (unless the attacker knows the plaintext of one of these packets, of course). Since the impact is limited, I consider this an acceptable remaining risk.

* The IVs used in encryption might collide. When two IVs collide, an attacker can learn the xor of the two plaintexts by xorring the ciphertexts. This is a serious loss of confidentiality. The IVs are 128-bit, so when HMAC-SHA256 is a secure PRF (an assumption that must also hold for TLS), and we use the same uniqueness requirement from [3], this limits the total amount of control channel messages for all peers in the setup to 2^48. Assuming a large setup of 2^16 (65536) clients, and a (conservative) number of 2^16 control channel packets per connection on average, this means that clients may set up 2^16 connections on average. I think these numbers are reasonable.

(I have a follow-up proposal to use client-specific tls-auth/tls-crypt keys to partially mitigate these issues, but let's tackle this patch first.)

References:
-----------

[0] Rogaway & Shrimpton, A Provable-Security Treatment of the Key-Wrap

    Problem, 2006
    (https://www.iacr.org/archive/eurocrypt2006/40040377/40040377.pdf)

[1] Ferguson, Authentication weaknesses in GCM, 2005
(http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/CWC-GCM/Ferguson2.pdf)

[2] Joux, Authentication Failures in NIST version of GCM, 2006
(http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/800-38_Series-Drafts/GCM/Joux_comments.pdf)

[3] Dworking, Recommendation for Block Cipher Modes of Operation:
Galois/Counter Mode (GCM) and GMAC, 2007
(http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf)

Patch history:
--------------

v2 - processed Arne's review comments:

* Error out early with a clear error message when AES-256-CTR or HMAC-SHA-256 are not supported by the crypto library.
* Clarify that cipher_ctx_reset() sets the IV.

---
 Changes.rst              |   5 +
 doc/openvpn.8            |  39 +++++++-
 src/openvpn/Makefile.am  |   1 +
 src/openvpn/crypto.c     |  17 +---
 src/openvpn/crypto.h     |  18 ++++
 src/openvpn/init.c       |  30 ++++--
 src/openvpn/openvpn.h    |   4 +-
 src/openvpn/options.c    |  31 +++++-
 src/openvpn/options.h    |   8 +-
 src/openvpn/ssl.c        | 119 +++++++++++++++--------
 src/openvpn/ssl.h        |   2 +-
 src/openvpn/ssl_common.h |  18 +++-
 src/openvpn/tls_crypt.c  | 246 +++++++++++++++++++++++++++++++++++++++++++++++
 src/openvpn/tls_crypt.h  | 144 +++++++++++++++++++++++++++
 14 files changed, 604 insertions(+), 78 deletions(-)
 create mode 100644 src/openvpn/tls_crypt.c
 create mode 100644 src/openvpn/tls_crypt.h
Changes.rst +5 -0
index 2b99a1d..94fdb47 100644
@@ -111,6 +111,11 @@ AIX platform support
 	AIX platform support has been added. The support only includes tap
     devices since AIX does not provide tun interface.
 
+Control channel encryption (``--tls-crypt``)
+    Use a pre-shared static key (like the ``--tls-auth`` key) to encrypt control
+    channel packets.  Provides more privacy, some obfuscation and poor-man's
+    post-quantum security.
+
 
 Deprecated features
 -------------------
doc/openvpn.8 +34 -5
index 7227d22..4c52cb6 100644
@@ -3471,8 +3471,10 @@ DoS scenario, legitimate connections might also be refused.
 For the best protection against DoS attacks in server mode,
 use
 .B \-\-proto udp
-and
-.B \-\-tls\-auth.
+and either
+.B \-\-tls\-auth
+or
+.B \-\-tls\-crypt\fR.
 .\"*********************************************************
 .TP
 .B \-\-learn\-address cmd
@@ -4938,8 +4940,8 @@ Exit on TLS negotiation failure.
 .\"*********************************************************
 .TP
 .B \-\-tls\-auth file [direction]
-Add an additional layer of HMAC authentication on top of the TLS
-control channel to protect against DoS attacks.
+Add an additional layer of HMAC authentication on top of the TLS control channel
+to mitigate DoS attacks and attacks on the TLS stack.
 
 In a nutshell,
 .B \-\-tls\-auth
@@ -5010,12 +5012,39 @@ option which will keep OpenVPN's replay protection state
 in a file so that it is not lost across restarts.
 
 It should be emphasized that this feature is optional and that the
-passphrase/key file used with
+key file used with
 .B \-\-tls\-auth
 gives a peer nothing more than the power to initiate a TLS
 handshake.  It is not used to encrypt or authenticate any tunnel data.
 .\"*********************************************************
 .TP
+.B \-\-tls\-crypt keyfile
+
+Encrypt and authenticate all control channel packets with the key from
+.B keyfile.
+(See
+.B \-\-tls\-auth
+for more background.)
+
+Encrypting (and authenticating) control channel packets:
+.RS
+.IP \[bu] 2
+provides more privacy by hiding the certificate used for the TLS connection,
+.IP \[bu]
+makes it harder to identify OpenVPN traffic as such,
+.IP \[bu]
+provides "poor-man's" post-quantum security, against attackers who will never
+know the pre-shared key (i.e. no forward secrecy).
+.RE
+
+.IP
+In contrast to
+.B \-\-tls\-auth\fR,
+.B \-\-tls\-crypt
+does *not* require the user to set
+.B \-\-key\-direction\fR.
+.\"*********************************************************
+.TP
 .B \-\-askpass [file]
 Get certificate password from console or
 .B file
src/openvpn/Makefile.am +1 -0
index 12b9ebf..4c18449 100644
@@ -113,6 +113,7 @@ openvpn_SOURCES = \
 	ssl_verify_mbedtls.c ssl_verify_mbedtls.h \
 	status.c status.h \
 	syshead.h \
+	tls_crypt.c tls_crypt.h \
 	tun.c tun.h \
 	win32.h win32.c \
 	cryptoapi.h cryptoapi.c
src/openvpn/crypto.c +2 -15
index 8b2f460..05622ce 100644
@@ -6,7 +6,7 @@
  *             packet compression.
  *
  *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <[email protected]...>
- *  Copyright (C) 2010 Fox Crypto B.V. <[email protected]...>
+ *  Copyright (C) 2010-2016 Fox Crypto B.V. <[email protected]...>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
@@ -63,9 +63,6 @@
  * happen unless the frame parameters are wrong.
  */
 
-#define CRYPT_ERROR(format) \
-  do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)
-
 static void
 openvpn_encrypt_aead (struct buffer *buf, struct buffer work,
 	 struct crypto_options *opt) {
@@ -326,17 +323,7 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
     }
 }
 
-/**
- * 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,
+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;
src/openvpn/crypto.h +18 -0
index bc9630a..ff90745 100644
@@ -266,6 +266,9 @@ struct crypto_options
                                  *   security operation functions. */
 };
 
+#define CRYPT_ERROR(format) \
+  do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)
+
 /**
  * Minimal IV length for AEAD mode ciphers (in bytes):
  * 4-byte packet id + 8 bytes implicit IV.
@@ -397,6 +400,21 @@ bool openvpn_decrypt (struct buffer *buf, struct buffer work,
 
 /** @} name Functions for performing security operations on data channel packets */
 
+/**
+ * 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.
+ */
+bool crypto_check_replay(struct crypto_options *opt,
+    const struct packet_id_net *pin, const char *error_prefix,
+    struct gc_arena *gc);
+
+
 /** Calculate crypto overhead and adjust frame to account for that */
 void crypto_adjust_frame_parameters(struct frame *frame,
 				    const struct key_type* kt,
src/openvpn/init.c +24 -6
index eaa5e79..ec1adc4 100644
@@ -44,6 +44,7 @@
 #include "ping.h"
 #include "mstats.h"
 #include "ssl_verify.h"
+#include "tls_crypt.h"
 #include "forward-inline.h"
 
 #include "memdbg.h"
@@ -2078,7 +2079,7 @@ key_schedule_free (struct key_schedule *ks, bool free_ssl_ctx)
   if (tls_ctx_initialised(&ks->ssl_ctx) && free_ssl_ctx)
     {
       tls_ctx_free (&ks->ssl_ctx);
-      free_key_ctx_bi (&ks->tls_auth_key);
+      free_key_ctx_bi (&ks->tls_wrap_key);
     }
 #endif /* ENABLE_CRYPTO */
   CLEAR (*ks);
@@ -2235,11 +2236,17 @@ do_init_crypto_tls_c1 (struct context *c)
 	    }
 
 	  crypto_read_openvpn_key (&c->c1.ks.tls_auth_key_type,
-	      &c->c1.ks.tls_auth_key, options->tls_auth_file,
+	      &c->c1.ks.tls_wrap_key, options->tls_auth_file,
 	      options->tls_auth_file_inline, options->key_direction,
 	      "Control Channel Authentication", "tls-auth");
 	}
 
+      /* TLS handshake encryption+authentication (--tls-crypt) */
+      if (options->tls_crypt_file) {
+	  tls_crypt_init_key (&c->c1.ks.tls_wrap_key, options->tls_crypt_file,
+	      options->tls_crypt_inline, options->tls_server);
+      }
+
       c->c1.ciphername = options->ciphername;
       c->c1.authname = options->authname;
 
@@ -2418,14 +2425,25 @@ 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_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;
+      to.tls_wrap.mode = TLS_WRAP_AUTH;
+      to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
+      to.tls_wrap.opt.pid_persist = &c->c1.pid_persist;
+      to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM;
       crypto_adjust_frame_parameters (&to.frame,
 				      &c->c1.ks.tls_auth_key_type,
 				      false, true, true);
     }
 
+  /* TLS handshake encryption (--tls-crypt) */
+  if (options->tls_crypt_file)
+    {
+      to.tls_wrap.mode = TLS_WRAP_CRYPT;
+      to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
+      to.tls_wrap.opt.pid_persist = &c->c1.pid_persist;
+      to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM;
+      tls_crypt_adjust_frame_parameters (&to.frame);
+    }
+
   /* If we are running over TCP, allow for
      length prefix */
   socket_adjust_frame_parameters (&to.frame, options->ce.proto);
@@ -3789,7 +3807,7 @@ inherit_context_child (struct context *dest,
   dest->c1.ks.key_type = src->c1.ks.key_type;
   /* 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_wrap_key = src->c1.ks.tls_wrap_key;
   dest->c1.ks.tls_auth_key_type = src->c1.ks.tls_auth_key_type;
 #endif
 
src/openvpn/openvpn.h +2 -2
index 5cda7b4..9e6a5fb 100644
@@ -65,9 +65,9 @@ struct key_schedule
   /* our global SSL context */
   struct tls_root_ctx ssl_ctx;
 
-  /* optional authentication HMAC key for TLS control channel */
+  /* optional TLS control channel wrapping */
   struct key_type tls_auth_key_type;
-  struct key_ctx_bi tls_auth_key;
+  struct key_ctx_bi tls_wrap_key;
 #else				/* ENABLE_CRYPTO */
   int dummy;
 #endif				/* ENABLE_CRYPTO */
src/openvpn/options.c +29 -2
index 443f96b..8b225cd 100644
@@ -610,10 +610,17 @@ static const char usage_message[] =
   "--single-session: Allow only one session (reset state on restart).\n"
   "--tls-exit      : Exit on TLS negotiation failure.\n"
   "--tls-auth f [d]: Add an additional layer of authentication on top of the TLS\n"
-  "                  control channel to protect against DoS attacks.\n"
-  "                  f (required) is a shared-secret passphrase file.\n"
+  "                  control channel to protect against attacks on the TLS stack\n"
+  "                  and DoS attacks.\n"
+  "                  f (required) is a shared-secret key file.\n"
   "                  The optional d parameter controls key directionality,\n"
   "                  see --secret option for more info.\n"
+  "--tls-crypt key : Add an additional layer of authenticated encryption on top\n"
+  "                  of the TLS control channel to hide the TLS certificate,\n"
+  "                  provide basic post-quantum security and protect against\n"
+  "                  attacks on the TLS stack and DoS attacks.\n"
+  "                  key (required) provides the pre-shared key file.\n"
+  "                  see --secret option for more info.\n"
   "--askpass [file]: Get PEM password from controlling tty before we daemonize.\n"
   "--auth-nocache  : Don't cache --askpass or --auth-user-pass passwords.\n"
   "--crl-verify crl ['dir']: Check peer certificate against a CRL.\n"
@@ -1709,6 +1716,7 @@ show_settings (const struct options *o)
   SHOW_BOOL (tls_exit);
 
   SHOW_STR (tls_auth_file);
+  SHOW_STR (tls_crypt_file);
 #endif /* ENABLE_CRYPTO */
 
 #ifdef ENABLE_PKCS11
@@ -2383,6 +2391,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
 	      notnull (options->priv_key_file, "private key file (--key) or PKCS#12 file (--pkcs12)");
 	    }
 	}
+    if (options->tls_auth_file && options->tls_crypt_file)
+      {
+        msg (M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive");
+      }
     }
   else
     {
@@ -2414,6 +2426,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
       MUST_BE_UNDEF (handshake_window);
       MUST_BE_UNDEF (transition_window);
       MUST_BE_UNDEF (tls_auth_file);
+      MUST_BE_UNDEF (tls_crypt_file);
       MUST_BE_UNDEF (single_session);
 #ifdef ENABLE_PUSH_PEER_INFO
       MUST_BE_UNDEF (push_peer_info);
@@ -2892,6 +2905,8 @@ options_postprocess_filechecks (struct options *options)
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
 			     options->tls_auth_file, R_OK, "--tls-auth");
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+			     options->tls_crypt_file, R_OK, "--tls-crypt");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
 			     options->shared_secret_file, R_OK, "--secret");
   errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR,
 			     options->packet_id_file, R_OK|W_OK, "--replay-persist");
@@ -3232,6 +3247,9 @@ options_string (const struct options *o,
       {
 	if (o->tls_auth_file)
 	  buf_printf (&out, ",tls-auth");
+	/* Not adding tls-crypt here, because we won't reach this code if
+	 * tls-auth/tls-crypt does not match.  Removing tls-auth here would
+	 * break stuff, so leaving that in place. */
 
 	if (o->key_method > 1)
 	  buf_printf (&out, ",key-method %d", o->key_method);
@@ -7254,6 +7272,15 @@ add_option (struct options *options,
 	}
       options->tls_auth_file = p[1];
     }
+  else if (streq (p[0], "tls-crypt") && p[1] && !p[3])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      if (streq (p[1], INLINE_FILE_TAG) && p[2])
+	{
+	  options->tls_crypt_inline = p[2];
+	}
+      options->tls_crypt_file = p[1];
+    }
   else if (streq (p[0], "key-method") && p[1] && !p[2])
     {
       int key_method;
src/openvpn/options.h +6 -2
index d51150b..a028556 100644
@@ -558,10 +558,14 @@ struct options
   /* Old key allowed to live n seconds after new key goes active */
   int transition_window;
 
-  /* Special authentication MAC for TLS control channel */
-  const char *tls_auth_file;		/* shared secret */
+  /* Shared secret used for TLS control channel authentication */
+  const char *tls_auth_file;
   const char *tls_auth_file_inline;
 
+  /* Shared secret used for TLS control channel authenticated encryption */
+  const char *tls_crypt_file;
+  const char *tls_crypt_inline;
+
   /* Allow only one session */
   bool single_session;
 
src/openvpn/ssl.c +77 -42

227 lines not on this page · raw message

src/openvpn/ssl.h +1 -1
index e6963a4..777b621 100644
@@ -136,7 +136,7 @@
  */
 struct tls_auth_standalone
 {
-  struct crypto_options tls_auth_options;
+  struct tls_wrap_ctx tls_wrap;
   struct frame frame;
 };
 
src/openvpn/ssl_common.h +15 -3
index b04a24c..28702af 100644
@@ -204,6 +204,18 @@ struct key_state
 #endif
 };
 
+/** Control channel wrapping (--tls-auth/--tls-crypt) context */
+struct tls_wrap_ctx
+{
+  enum {
+    TLS_WRAP_NONE = 0,	/**< No control channel wrapping */
+    TLS_WRAP_AUTH,	/**< Control channel authentication */
+    TLS_WRAP_CRYPT,	/**< Control channel encryption and authentication */
+  } mode;			/**< Control channel wrapping mode */
+  struct crypto_options opt;	/**< Crypto state */
+  struct buffer work;		/**< Work buffer (only for --tls-crypt) */
+};
+
 /*
  * Our const options, obtained directly or derived from
  * command line options.
@@ -278,8 +290,8 @@ struct tls_options
   const char *config_authname;
   bool ncp_enabled;
 
-  /* packet authentication for TLS handshake */
-  struct crypto_options tls_auth;
+  /** TLS handshake wrapping state */
+  struct tls_wrap_ctx tls_wrap;
 
   /* frame parameters for TLS control channel */
   struct frame frame;
@@ -380,7 +392,7 @@ struct tls_session
   bool burst;
 
   /* authenticate control packets */
-  struct crypto_options tls_auth;
+  struct tls_wrap_ctx tls_wrap;
 
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id;	/* our random session ID */
src/openvpn/tls_crypt.c new file +246 -0

246 lines not on this page · raw message

src/openvpn/tls_crypt.h new file +144 -0

144 lines not on this page · raw message

Signature · 1 line
2.7.4
#13 lore raw

PATCH v3 2/5 Add control channel encryption (--tls-crypt)

This adds a --tls-crypt option, which uses a pre-shared static key (like the --tls-auth key) to encrypt control channel packets.

Encrypting control channel packets has three main advantages:

* It provides more privacy by hiding the certificate used for the TLS connection.
* It is harder to identify OpenVPN traffic as such.
* It provides "poor-man's" post-quantum security, against attackers who will never know the pre-shared key (i.e. no forward secrecy).

Control channel packet encryption
---------------------------------

We propose to use the following encryption method, based on the SIV construction [0], to achieve nonce misuse-resistant authenticated encryption:

   msg      = control channel plaintext
   header   = opcode (1 byte) || session_id (8 bytes) || packet_id (8 bytes)
   Ka       = authentication key (256 bits)
   Ke       = encryption key (256 bits)

(Ka and Ke are pre-shared keys, like with --tls-auth)

   auth_tag = HMAC-SHA256(Ka, header || msg)
   IV       = 128 most-significant bits of auth_tag
   ciph     = AES256-CTR(Ke, IV, msg)

   output   = Header || Tag || Ciph

This boils down to the following on-the-wire packet format:

   -opcode- || -session_id- || -packet_id- || auth_tag || * payload *

Where
- XXX - means authenticated, and
* XXX * means authenticated and encrypted.

Which is very similar to the current tls-auth packet format, and has the same overhead as "--tls-auth" with "--auth SHA256".

The use of a nonce misuse-resistant authenticated encryption scheme allows us to worry less about the risks of nonce collisions. This is important, because in contrast with the data channel in TLS mode, we will not be able to rotate tls-crypt keys often or fully guarantee nonce uniqueness. For non misuse-resistant modes such as GCM [1], [2], the data channel in TLS mode only has to ensure that the packet counter never rolls over, while tls-crypt would have to provide nonce uniqueness over all control channel packets sent by all clients, for the lifetime of the tls-crypt key.

Unlike with tls-auth, no --key-direction has to be specified for tls-crypt. TLS servers always use key direction 1, and TLS clients always use key direction 2, which means that client->server traffic and server->client traffic always use different keys, without requiring configuration.

Using fixed, secure, encryption and authentication algorithms makes both implementation and configuration easier. If we ever want to, we can extend this to support other crypto primitives. Since tls-crypt should provide privacy as well as DoS protection, these should not be made negotiable.

Security considerations:
------------------------

tls-crypt is a best-effort mechanism that aims to provide as much privacy and security as possible, while staying as simple as possible. The following are some security considerations for this scheme.

1. The same tls-crypt key is potentially shared by a lot of peers, so it is quite likely to get compromised. Once an attacker acquires the tls-crypt key, this mechanism no longer provides any security against the attacker.

2. Since many peers potentially use the tls-crypt key for a long time, a lot of data might be encrypted under the tls-crypt key. This leads to two potential problems:

* The "opcode || session id || packet id" combination might collide. This might happen in larger setups, because the session id contains just 64 bits or random. Using the uniqueness requirement from the GCM spec [3] (a collision probability of less than 2^(-32)), uniqueness is achieved when using the tls-crypt key for at most 2^16 (65536) connections per process start. (The packet id includes the daemon start time in the packet ID, which should be different after stopping and (re)starting OpenPVN.)

And if a collision happens, an attacker can *only* learn whether colliding packets contain the same plaintext. Attackers will not be able to learn anything else about the plaintext (unless the attacker knows the plaintext of one of these packets, of course). Since the impact is limited, I consider this an acceptable remaining risk.

* The IVs used in encryption might collide. When two IVs collide, an attacker can learn the xor of the two plaintexts by xorring the ciphertexts. This is a serious loss of confidentiality. The IVs are 128-bit, so when HMAC-SHA256 is a secure PRF (an assumption that must also hold for TLS), and we use the same uniqueness requirement from [3], this limits the total amount of control channel messages for all peers in the setup to 2^48. Assuming a large setup of 2^16 (65536) clients, and a (conservative) number of 2^16 control channel packets per connection on average, this means that clients may set up 2^16 connections on average. I think these numbers are reasonable.

(I have a follow-up proposal to use client-specific tls-auth/tls-crypt keys to partially mitigate these issues, but let's tackle this patch first.)

References:
-----------

[0] Rogaway & Shrimpton, A Provable-Security Treatment of the Key-Wrap

    Problem, 2006
    (https://www.iacr.org/archive/eurocrypt2006/40040377/40040377.pdf)

[1] Ferguson, Authentication weaknesses in GCM, 2005
(http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/CWC-GCM/Ferguson2.pdf)

[2] Joux, Authentication Failures in NIST version of GCM, 2006
(http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/800-38_Series-Drafts/GCM/Joux_comments.pdf)

[3] Dworking, Recommendation for Block Cipher Modes of Operation:
Galois/Counter Mode (GCM) and GMAC, 2007
(http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf)

Patch history:
--------------

v2 - processed Arne's review comments:

* Error out early with a clear error message when AES-256-CTR or HMAC-SHA-256 are not supported by the crypto library.
* Clarify that cipher_ctx_reset() sets the IV.

v3 - actually add error messages promised in v2...

---
 Changes.rst              |   5 +
 doc/openvpn.8            |  39 +++++++-
 src/openvpn/Makefile.am  |   1 +
 src/openvpn/crypto.c     |  17 +---
 src/openvpn/crypto.h     |  18 ++++
 src/openvpn/init.c       |  30 ++++--
 src/openvpn/openvpn.h    |   4 +-
 src/openvpn/options.c    |  31 +++++-
 src/openvpn/options.h    |   8 +-
 src/openvpn/ssl.c        | 119 ++++++++++++++--------
 src/openvpn/ssl.h        |   2 +-
 src/openvpn/ssl_common.h |  18 +++-
 src/openvpn/tls_crypt.c  | 254 +++++++++++++++++++++++++++++++++++++++++++++++
 src/openvpn/tls_crypt.h  | 144 +++++++++++++++++++++++++++
 14 files changed, 612 insertions(+), 78 deletions(-)
 create mode 100644 src/openvpn/tls_crypt.c
 create mode 100644 src/openvpn/tls_crypt.h
Changes.rst +5 -0
index 2b99a1d..94fdb47 100644
@@ -111,6 +111,11 @@ AIX platform support
 	AIX platform support has been added. The support only includes tap
     devices since AIX does not provide tun interface.
 
+Control channel encryption (``--tls-crypt``)
+    Use a pre-shared static key (like the ``--tls-auth`` key) to encrypt control
+    channel packets.  Provides more privacy, some obfuscation and poor-man's
+    post-quantum security.
+
 
 Deprecated features
 -------------------
doc/openvpn.8 +34 -5
index 7227d22..4c52cb6 100644
@@ -3471,8 +3471,10 @@ DoS scenario, legitimate connections might also be refused.
 For the best protection against DoS attacks in server mode,
 use
 .B \-\-proto udp
-and
-.B \-\-tls\-auth.
+and either
+.B \-\-tls\-auth
+or
+.B \-\-tls\-crypt\fR.
 .\"*********************************************************
 .TP
 .B \-\-learn\-address cmd
@@ -4938,8 +4940,8 @@ Exit on TLS negotiation failure.
 .\"*********************************************************
 .TP
 .B \-\-tls\-auth file [direction]
-Add an additional layer of HMAC authentication on top of the TLS
-control channel to protect against DoS attacks.
+Add an additional layer of HMAC authentication on top of the TLS control channel
+to mitigate DoS attacks and attacks on the TLS stack.
 
 In a nutshell,
 .B \-\-tls\-auth
@@ -5010,12 +5012,39 @@ option which will keep OpenVPN's replay protection state
 in a file so that it is not lost across restarts.
 
 It should be emphasized that this feature is optional and that the
-passphrase/key file used with
+key file used with
 .B \-\-tls\-auth
 gives a peer nothing more than the power to initiate a TLS
 handshake.  It is not used to encrypt or authenticate any tunnel data.
 .\"*********************************************************
 .TP
+.B \-\-tls\-crypt keyfile
+
+Encrypt and authenticate all control channel packets with the key from
+.B keyfile.
+(See
+.B \-\-tls\-auth
+for more background.)
+
+Encrypting (and authenticating) control channel packets:
+.RS
+.IP \[bu] 2
+provides more privacy by hiding the certificate used for the TLS connection,
+.IP \[bu]
+makes it harder to identify OpenVPN traffic as such,
+.IP \[bu]
+provides "poor-man's" post-quantum security, against attackers who will never
+know the pre-shared key (i.e. no forward secrecy).
+.RE
+
+.IP
+In contrast to
+.B \-\-tls\-auth\fR,
+.B \-\-tls\-crypt
+does *not* require the user to set
+.B \-\-key\-direction\fR.
+.\"*********************************************************
+.TP
 .B \-\-askpass [file]
 Get certificate password from console or
 .B file
src/openvpn/Makefile.am +1 -0
index 12b9ebf..4c18449 100644
@@ -113,6 +113,7 @@ openvpn_SOURCES = \
 	ssl_verify_mbedtls.c ssl_verify_mbedtls.h \
 	status.c status.h \
 	syshead.h \
+	tls_crypt.c tls_crypt.h \
 	tun.c tun.h \
 	win32.h win32.c \
 	cryptoapi.h cryptoapi.c
src/openvpn/crypto.c +2 -15
index 8b2f460..05622ce 100644
@@ -6,7 +6,7 @@
  *             packet compression.
  *
  *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <[email protected]...>
- *  Copyright (C) 2010 Fox Crypto B.V. <[email protected]...>
+ *  Copyright (C) 2010-2016 Fox Crypto B.V. <[email protected]...>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
@@ -63,9 +63,6 @@
  * happen unless the frame parameters are wrong.
  */
 
-#define CRYPT_ERROR(format) \
-  do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)
-
 static void
 openvpn_encrypt_aead (struct buffer *buf, struct buffer work,
 	 struct crypto_options *opt) {
@@ -326,17 +323,7 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
     }
 }
 
-/**
- * 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,
+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;
src/openvpn/crypto.h +18 -0
index bc9630a..ff90745 100644
@@ -266,6 +266,9 @@ struct crypto_options
                                  *   security operation functions. */
 };
 
+#define CRYPT_ERROR(format) \
+  do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)
+
 /**
  * Minimal IV length for AEAD mode ciphers (in bytes):
  * 4-byte packet id + 8 bytes implicit IV.
@@ -397,6 +400,21 @@ bool openvpn_decrypt (struct buffer *buf, struct buffer work,
 
 /** @} name Functions for performing security operations on data channel packets */
 
+/**
+ * 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.
+ */
+bool crypto_check_replay(struct crypto_options *opt,
+    const struct packet_id_net *pin, const char *error_prefix,
+    struct gc_arena *gc);
+
+
 /** Calculate crypto overhead and adjust frame to account for that */
 void crypto_adjust_frame_parameters(struct frame *frame,
 				    const struct key_type* kt,
src/openvpn/init.c +24 -6
index eaa5e79..ec1adc4 100644
@@ -44,6 +44,7 @@
 #include "ping.h"
 #include "mstats.h"
 #include "ssl_verify.h"
+#include "tls_crypt.h"
 #include "forward-inline.h"
 
 #include "memdbg.h"
@@ -2078,7 +2079,7 @@ key_schedule_free (struct key_schedule *ks, bool free_ssl_ctx)
   if (tls_ctx_initialised(&ks->ssl_ctx) && free_ssl_ctx)
     {
       tls_ctx_free (&ks->ssl_ctx);
-      free_key_ctx_bi (&ks->tls_auth_key);
+      free_key_ctx_bi (&ks->tls_wrap_key);
     }
 #endif /* ENABLE_CRYPTO */
   CLEAR (*ks);
@@ -2235,11 +2236,17 @@ do_init_crypto_tls_c1 (struct context *c)
 	    }
 
 	  crypto_read_openvpn_key (&c->c1.ks.tls_auth_key_type,
-	      &c->c1.ks.tls_auth_key, options->tls_auth_file,
+	      &c->c1.ks.tls_wrap_key, options->tls_auth_file,
 	      options->tls_auth_file_inline, options->key_direction,
 	      "Control Channel Authentication", "tls-auth");
 	}
 
+      /* TLS handshake encryption+authentication (--tls-crypt) */
+      if (options->tls_crypt_file) {
+	  tls_crypt_init_key (&c->c1.ks.tls_wrap_key, options->tls_crypt_file,
+	      options->tls_crypt_inline, options->tls_server);
+      }
+
       c->c1.ciphername = options->ciphername;
       c->c1.authname = options->authname;
 
@@ -2418,14 +2425,25 @@ 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_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;
+      to.tls_wrap.mode = TLS_WRAP_AUTH;
+      to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
+      to.tls_wrap.opt.pid_persist = &c->c1.pid_persist;
+      to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM;
       crypto_adjust_frame_parameters (&to.frame,
 				      &c->c1.ks.tls_auth_key_type,
 				      false, true, true);
     }
 
+  /* TLS handshake encryption (--tls-crypt) */
+  if (options->tls_crypt_file)
+    {
+      to.tls_wrap.mode = TLS_WRAP_CRYPT;
+      to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
+      to.tls_wrap.opt.pid_persist = &c->c1.pid_persist;
+      to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM;
+      tls_crypt_adjust_frame_parameters (&to.frame);
+    }
+
   /* If we are running over TCP, allow for
      length prefix */
   socket_adjust_frame_parameters (&to.frame, options->ce.proto);
@@ -3789,7 +3807,7 @@ inherit_context_child (struct context *dest,
   dest->c1.ks.key_type = src->c1.ks.key_type;
   /* 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_wrap_key = src->c1.ks.tls_wrap_key;
   dest->c1.ks.tls_auth_key_type = src->c1.ks.tls_auth_key_type;
 #endif
 
src/openvpn/openvpn.h +2 -2
index 5cda7b4..9e6a5fb 100644
@@ -65,9 +65,9 @@ struct key_schedule
   /* our global SSL context */
   struct tls_root_ctx ssl_ctx;
 
-  /* optional authentication HMAC key for TLS control channel */
+  /* optional TLS control channel wrapping */
   struct key_type tls_auth_key_type;
-  struct key_ctx_bi tls_auth_key;
+  struct key_ctx_bi tls_wrap_key;
 #else				/* ENABLE_CRYPTO */
   int dummy;
 #endif				/* ENABLE_CRYPTO */
src/openvpn/options.c +29 -2
index 443f96b..8b225cd 100644
@@ -610,10 +610,17 @@ static const char usage_message[] =
   "--single-session: Allow only one session (reset state on restart).\n"
   "--tls-exit      : Exit on TLS negotiation failure.\n"
   "--tls-auth f [d]: Add an additional layer of authentication on top of the TLS\n"
-  "                  control channel to protect against DoS attacks.\n"
-  "                  f (required) is a shared-secret passphrase file.\n"
+  "                  control channel to protect against attacks on the TLS stack\n"
+  "                  and DoS attacks.\n"
+  "                  f (required) is a shared-secret key file.\n"
   "                  The optional d parameter controls key directionality,\n"
   "                  see --secret option for more info.\n"
+  "--tls-crypt key : Add an additional layer of authenticated encryption on top\n"
+  "                  of the TLS control channel to hide the TLS certificate,\n"
+  "                  provide basic post-quantum security and protect against\n"
+  "                  attacks on the TLS stack and DoS attacks.\n"
+  "                  key (required) provides the pre-shared key file.\n"
+  "                  see --secret option for more info.\n"
   "--askpass [file]: Get PEM password from controlling tty before we daemonize.\n"
   "--auth-nocache  : Don't cache --askpass or --auth-user-pass passwords.\n"
   "--crl-verify crl ['dir']: Check peer certificate against a CRL.\n"
@@ -1709,6 +1716,7 @@ show_settings (const struct options *o)
   SHOW_BOOL (tls_exit);
 
   SHOW_STR (tls_auth_file);
+  SHOW_STR (tls_crypt_file);
 #endif /* ENABLE_CRYPTO */
 
 #ifdef ENABLE_PKCS11
@@ -2383,6 +2391,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
 	      notnull (options->priv_key_file, "private key file (--key) or PKCS#12 file (--pkcs12)");
 	    }
 	}
+    if (options->tls_auth_file && options->tls_crypt_file)
+      {
+        msg (M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive");
+      }
     }
   else
     {
@@ -2414,6 +2426,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
       MUST_BE_UNDEF (handshake_window);
       MUST_BE_UNDEF (transition_window);
       MUST_BE_UNDEF (tls_auth_file);
+      MUST_BE_UNDEF (tls_crypt_file);
       MUST_BE_UNDEF (single_session);
 #ifdef ENABLE_PUSH_PEER_INFO
       MUST_BE_UNDEF (push_peer_info);
@@ -2892,6 +2905,8 @@ options_postprocess_filechecks (struct options *options)
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
 			     options->tls_auth_file, R_OK, "--tls-auth");
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+			     options->tls_crypt_file, R_OK, "--tls-crypt");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
 			     options->shared_secret_file, R_OK, "--secret");
   errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR,
 			     options->packet_id_file, R_OK|W_OK, "--replay-persist");
@@ -3232,6 +3247,9 @@ options_string (const struct options *o,
       {
 	if (o->tls_auth_file)
 	  buf_printf (&out, ",tls-auth");
+	/* Not adding tls-crypt here, because we won't reach this code if
+	 * tls-auth/tls-crypt does not match.  Removing tls-auth here would
+	 * break stuff, so leaving that in place. */
 
 	if (o->key_method > 1)
 	  buf_printf (&out, ",key-method %d", o->key_method);
@@ -7254,6 +7272,15 @@ add_option (struct options *options,
 	}
       options->tls_auth_file = p[1];
     }
+  else if (streq (p[0], "tls-crypt") && p[1] && !p[3])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      if (streq (p[1], INLINE_FILE_TAG) && p[2])
+	{
+	  options->tls_crypt_inline = p[2];
+	}
+      options->tls_crypt_file = p[1];
+    }
   else if (streq (p[0], "key-method") && p[1] && !p[2])
     {
       int key_method;
src/openvpn/options.h +6 -2
index d51150b..a028556 100644
@@ -558,10 +558,14 @@ struct options
   /* Old key allowed to live n seconds after new key goes active */
   int transition_window;
 
-  /* Special authentication MAC for TLS control channel */
-  const char *tls_auth_file;		/* shared secret */
+  /* Shared secret used for TLS control channel authentication */
+  const char *tls_auth_file;
   const char *tls_auth_file_inline;
 
+  /* Shared secret used for TLS control channel authenticated encryption */
+  const char *tls_crypt_file;
+  const char *tls_crypt_inline;
+
   /* Allow only one session */
   bool single_session;
 
src/openvpn/ssl.c +77 -42

227 lines not on this page · raw message

src/openvpn/ssl.h +1 -1
index e6963a4..777b621 100644
@@ -136,7 +136,7 @@
  */
 struct tls_auth_standalone
 {
-  struct crypto_options tls_auth_options;
+  struct tls_wrap_ctx tls_wrap;
   struct frame frame;
 };
 
src/openvpn/ssl_common.h +15 -3
index b04a24c..28702af 100644
@@ -204,6 +204,18 @@ struct key_state
 #endif
 };
 
+/** Control channel wrapping (--tls-auth/--tls-crypt) context */
+struct tls_wrap_ctx
+{
+  enum {
+    TLS_WRAP_NONE = 0,	/**< No control channel wrapping */
+    TLS_WRAP_AUTH,	/**< Control channel authentication */
+    TLS_WRAP_CRYPT,	/**< Control channel encryption and authentication */
+  } mode;			/**< Control channel wrapping mode */
+  struct crypto_options opt;	/**< Crypto state */
+  struct buffer work;		/**< Work buffer (only for --tls-crypt) */
+};
+
 /*
  * Our const options, obtained directly or derived from
  * command line options.
@@ -278,8 +290,8 @@ struct tls_options
   const char *config_authname;
   bool ncp_enabled;
 
-  /* packet authentication for TLS handshake */
-  struct crypto_options tls_auth;
+  /** TLS handshake wrapping state */
+  struct tls_wrap_ctx tls_wrap;
 
   /* frame parameters for TLS control channel */
   struct frame frame;
@@ -380,7 +392,7 @@ struct tls_session
   bool burst;
 
   /* authenticate control packets */
-  struct crypto_options tls_auth;
+  struct tls_wrap_ctx tls_wrap;
 
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id;	/* our random session ID */
src/openvpn/tls_crypt.c new file +254 -0

254 lines not on this page · raw message

src/openvpn/tls_crypt.h new file +144 -0

144 lines not on this page · raw message

Signature · 1 line
2.7.4
#14 lore raw

Am 15.11.16 um 08:29 schrieb Steffan Karger:

Quoted 9 lines

v2 - processed Arne's review comments:

* Error out early with a clear error message when AES-256-CTR or HMAC-SHA-256 are not supported by the crypto library.
* Clarify that cipher_ctx_reset() sets the IV.

v3 - actually add error messages promised in v2...

Thanks! ACK

Arne

#15 lore raw
In reply to Steffan Karger

Your patch has been applied to the master branch.

commit c6e24fa3e16c32f9b427e360fd07102f613aa5c6

Author: Steffan Karger
Date:   Tue Nov 15 14:29:46 2016 +0100
     Add control channel encryption (--tls-crypt)

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg13069.html
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#16 lore raw

PATCH 3/5 Add missing includes in error.h

In reply to Steffan Karger

error.h depends on these, but is apparently never used by files that do not include them. When implementing the --tls-crypt unit tests, I ran into this.

---
 src/openvpn/error.h | 3 +++
 1 file changed, 3 insertions(+)
src/openvpn/error.h +3 -0
index ced2fdf..1d791bb 100644
@@ -27,6 +27,9 @@
 
 #include "basic.h"
 
+#include <errno.h>
+#include <stdbool.h>
+
 /* #define ABORT_ON_ERROR */
 
 #ifdef ENABLE_PKCS11
Signature · 1 line
2.7.4
#17 lore raw

Re: [Openvpn-devel] [PATCH 3/5] Add missing includes in error.h

Quoted 15 lines · Steffan Karger

error.h depends on these, but is apparently never used by files that do not include them. When implementing the --tls-crypt unit tests, I ran into this.

---
 src/openvpn/error.h | 3 +++
 1 file changed, 3 insertions(+)
src/openvpn/error.h +0 -0
index ced2fdf..1d791bb 100644

src/openvpn/error.h line 35

Quoted 9 lines
src/openvpn/error.h +3 -0
@@ -27,6 +27,9 @@
 
 #include "basic.h"
 
+#include <errno.h>
+#include <stdbool.h>
+
 /* #define ABORT_ON_ERROR */
 
 #ifdef ENABLE_PKCS11

This one is easy! :) Without this patch, the following patch will not build, so ACK.

Signature · 4 lines
kind regards,

David Sommerseth
OpenVPN Technologies, Inc
#18 lore raw

Re: [Openvpn-devel] [PATCH 3/5] Add missing includes in error.h

In reply to Steffan Karger

Am 08.11.16 um 15:18 schrieb Steffan Karger:

Quoted 4 lines

error.h depends on these, but is apparently never used by files that do not include them. When implementing the --tls-crypt unit tests, I ran into this.

ACK

#19 lore raw

[Openvpn-devel] [PATCH applied] Re: Add missing includes in error.h

In reply to Steffan Karger

Your patch has been applied to the master branch.

(I had to massage it a bit because "someone" added an <assert.h> include to the context below this patch, and git am failed - not very hard to do, just pointing this out)

commit b7e51b137929e325e2ac3c14f751c3f642063cd5

Author: Steffan Karger
Date:   Tue Nov 8 21:18:20 2016 +0100
     Add missing includes in error.h

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: David Sommerseth <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg12972.html
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

#20 lore raw

PATCH 4/5 Move private file access checks to options_postprocess_filechecks()

In reply to Steffan Karger

From: Steffan Karger <[email protected]...>

This removes the dependency of crypto.c on misc.c, which makes testing (stuff that needs) crypto.c functionality easier. In particular, this simplifies the --tls-crypt tests in one of the follow-up patches.

Apart from that, testing file access really belongs in options_postprocess_filechecks(), and moving it there enables us to perform the same check for other private files too.

---
 src/openvpn/crypto.c  |  5 +----
 src/openvpn/options.c | 36 ++++++++++++++++++++++--------------
 2 files changed, 23 insertions(+), 18 deletions(-)
src/openvpn/crypto.c +1 -4
index ab43005..05622ce 100644
@@ -36,7 +36,7 @@
 #include "crypto.h"
 #include "error.h"
 #include "integer.h"
-#include "misc.h"
+#include "platform.h"
 
 #include "memdbg.h"
 
@@ -1307,9 +1307,6 @@ read_key_file (struct key2 *key2, const char *file, const unsigned int flags)
   if (!(flags & RKF_INLINE))
     buf_clear (&in);
 
-  if (key2->n)
-    warn_if_group_others_accessible (error_filename);
-
 #if 0
   /* DEBUGGING */
   {
src/openvpn/options.c +22 -14
index e9dc17e..c576e6e 100644
@@ -2694,6 +2694,7 @@ options_postprocess_mutate (struct options *o)
 #define CHKACC_FILEXSTWR (1<<2)  /** If file exists, is it writable? */
 #define CHKACC_INLINE (1<<3)     /** File is present if it's an inline file */
 #define CHKACC_ACPTSTDIN (1<<4)  /** If filename is stdin, it's allowed and "exists" */
+#define CHKACC_PRIVATE (1<<5)	 /** Warn if this (private) file is group/others accessible */
 
 static bool
 check_file_access(const int type, const char *file, const int mode, const char *opt)
@@ -2734,6 +2735,11 @@ check_file_access(const int type, const char *file, const int mode, const char *
     if (platform_access (file, W_OK) != 0)
       errcode = errno;
 
+  if (type & CHKACC_PRIVATE)
+    {
+      warn_if_group_others_accessible (file);
+    }
+
   /* Scream if an error is found */
   if( errcode > 0 )
     msg (M_NOPREFIX|M_OPTERR, "%s fails with '%s': %s",
@@ -2850,10 +2856,12 @@ options_postprocess_filechecks (struct options *options)
 #ifdef MANAGMENT_EXTERNAL_KEY
   if(!(options->management_flags & MF_EXTERNAL_KEY))
 #endif
-     errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->priv_key_file, R_OK,
-                             "--key");
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->pkcs12_file, R_OK,
-                             "--pkcs12");
+    {
+      errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+	  options->priv_key_file, R_OK, "--key");
+    }
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+      options->pkcs12_file, R_OK, "--pkcs12");
 
   if (options->ssl_flags & SSLF_CRL_VERIFY_DIR)
     errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE, options->crl_file, R_OK|X_OK,
@@ -2862,26 +2870,26 @@ options_postprocess_filechecks (struct options *options)
     errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE|CHKACC_INLINE,
                                       options->crl_file, R_OK, "--crl-verify");
 
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->tls_auth_file, R_OK,
-                             "--tls-auth");
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->tls_crypt_file, R_OK,
-                             "--tls-crypt");
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->shared_secret_file, R_OK,
-                             "--secret");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+      options->tls_auth_file, R_OK, "--tls-auth");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+      options->tls_crypt_file, R_OK, "--tls-crypt");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+      options->shared_secret_file, R_OK, "--secret");
   errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR,
-                             options->packet_id_file, R_OK|W_OK, "--replay-persist");
+      options->packet_id_file, R_OK|W_OK, "--replay-persist");
 
   /* ** Password files ** */
-  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN,
+  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE,
                              options->key_pass_file, R_OK, "--askpass");
 #endif /* ENABLE_CRYPTO */
 #ifdef ENABLE_MANAGEMENT
-  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN,
+  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE,
                              options->management_user_pass, R_OK,
                              "--management user/password file");
 #endif /* ENABLE_MANAGEMENT */
 #if P2MP
-  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN,
+  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE,
                              options->auth_user_pass_file, R_OK,
                              "--auth-user-pass");
 #endif /* P2MP */
Signature · 1 line
2.7.4
#21 lore raw

Re: [Openvpn-devel] [PATCH 4/5] Move private file access checks to options_postprocess_filechecks()

Quoted 10 lines · Steffan Karger

From: Steffan Karger <[email protected]...>

This removes the dependency of crypto.c on misc.c, which makes testing (stuff that needs) crypto.c functionality easier. In particular, this simplifies the --tls-crypt tests in one of the follow-up patches.

Apart from that, testing file access really belongs in options_postprocess_filechecks(), and moving it there enables us to perform the same check for other private files too.

Quoted 5 lines
---
 src/openvpn/crypto.c  |  5 +----
 src/openvpn/options.c | 36 ++++++++++++++++++++++--------------
 2 files changed, 23 insertions(+), 18 deletions(-)

These are code paths I know fairly well :)

Quoted 4 lines
src/openvpn/options.c +0 -0
index e9dc17e..c576e6e 100644

[...snip...]

Quoted 4 lines
src/openvpn/options.c +0 -0
@@ -2850,10 +2856,12 @@ options_postprocess_filechecks (struct options *options)
 #ifdef MANAGMENT_EXTERNAL_KEY
   if(!(options->management_flags & MF_EXTERNAL_KEY))
 #endif

src/openvpn/options.c options_postprocess_filechecks (struct options *options) · line 2864

Quoted 10 lines
src/openvpn/options.c +6 -4
-     errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->priv_key_file, R_OK,
-                             "--key");
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->pkcs12_file, R_OK,
-                             "--pkcs12");
+    {
+      errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+	  options->priv_key_file, R_OK, "--key");
+    }
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+      options->pkcs12_file, R_OK, "--pkcs12");

I'd prefer to have the arguments continuation aligned with the CHKACC_FILE, but this is code style bike-shedding. However that is what we do in the rest of this scope.

Quoted 16 lines
src/openvpn/options.c +4 -6
   if (options->ssl_flags & SSLF_CRL_VERIFY_DIR)
     errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE, options->crl_file, R_OK|X_OK,
@@ -2862,26 +2870,26 @@ options_postprocess_filechecks (struct options *options)
     errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE|CHKACC_INLINE,
                                       options->crl_file, R_OK, "--crl-verify");
 
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->tls_auth_file, R_OK,
-                             "--tls-auth");
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->tls_crypt_file, R_OK,
-                             "--tls-crypt");
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->shared_secret_file, R_OK,
-                             "--secret");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+      options->tls_auth_file, R_OK, "--tls-auth");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+      options->tls_crypt_file, R_OK, "--tls-crypt");

src/openvpn/options.c options_postprocess_filechecks (struct options *options) · line 2884

Quoted 10 lines
src/openvpn/options.c +4 -2
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+      options->shared_secret_file, R_OK, "--secret");
   errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR,
-                             options->packet_id_file, R_OK|W_OK, "--replay-persist");
+      options->packet_id_file, R_OK|W_OK, "--replay-persist");
 
   /* ** Password files ** */
-  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN,
+  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE,
                              options->key_pass_file, R_OK, "--askpass");

Here the indent is as expected though :)

Quoted 4 lines
src/openvpn/options.c +0 -1
 #endif /* ENABLE_CRYPTO */
 #ifdef ENABLE_MANAGEMENT
-  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN,

src/openvpn/options.c options_postprocess_filechecks (struct options *options) · line 2895

Quoted 10 lines
src/openvpn/options.c +2 -1
+  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE,
                              options->management_user_pass, R_OK,
                              "--management user/password file");
 #endif /* ENABLE_MANAGEMENT */
 #if P2MP
-  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN,
+  errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE,
                              options->auth_user_pass_file, R_OK,
                              "--auth-user-pass");
 #endif /* P2MP */

Patch looks good to me. Good clean-up moving warn_if_group_others_accessible() to these code paths. But I have one comment.

I see that warn_if_group_others_accessible() is still called from ssl_{mbedtls,openssl}.c ... seems to be used in tls_ctx_load_priv_file() and tls_ctx_load_pkcs12(). Are those checks needed now? If not, maybe move warn_if_group_others_accessible() to options.c as well?

With these issues resolved, it's an ACK from me.

Signature · 4 lines
kind regards,

David Sommerseth
OpenVPN Technologies, Inc
#22 lore raw

PATCH v2 4/5 Move private file access checks to options_postprocess_filechecks()

From: Steffan Karger <[email protected]...>

This removes the dependency of crypto.c on misc.c, which makes testing (stuff that needs) crypto.c functionality easier. In particular, this simplifies the --tls-crypt tests in one of the follow-up patches.

Apart from that, testing file access really belongs in options_postprocess_filechecks(), and moving it there enables us to perform the same check for other private files too.

v2: change indenting, remove remaining warn_if_group_others_accessible()
calls and move function to options.c.

---
 src/openvpn/crypto.c      |  5 +---
 src/openvpn/misc.c        | 27 ---------------------
 src/openvpn/misc.h        |  3 ---
 src/openvpn/options.c     | 61 ++++++++++++++++++++++++++++++++++++-----------
 src/openvpn/ssl_mbedtls.c |  2 --
 src/openvpn/ssl_openssl.c |  2 --
 6 files changed, 48 insertions(+), 52 deletions(-)
src/openvpn/crypto.c +1 -4
index ab43005..05622ce 100644
@@ -36,7 +36,7 @@
 #include "crypto.h"
 #include "error.h"
 #include "integer.h"
-#include "misc.h"
+#include "platform.h"
 
 #include "memdbg.h"
 
@@ -1307,9 +1307,6 @@ read_key_file (struct key2 *key2, const char *file, const unsigned int flags)
   if (!(flags & RKF_INLINE))
     buf_clear (&in);
 
-  if (key2->n)
-    warn_if_group_others_accessible (error_filename);
-
 #if 0
   /* DEBUGGING */
   {
src/openvpn/misc.c +0 -27
index 3d40f0b..ea0d75d 100644
@@ -194,31 +194,6 @@ save_inetd_socket_descriptor (void)
 }
 
 /*
- * Warn if a given file is group/others accessible.
- */
-void
-warn_if_group_others_accessible (const char* filename)
-{
-#ifndef WIN32
-#ifdef HAVE_STAT
-  if (strcmp (filename, INLINE_FILE_TAG))
-    {
-      struct stat st;
-      if (stat (filename, &st))
-	{
-	  msg (M_WARN | M_ERRNO, "WARNING: cannot stat file '%s'", filename);
-	}
-      else
-	{
-	  if (st.st_mode & (S_IRWXG|S_IRWXO))
-	    msg (M_WARN, "WARNING: file '%s' is group or others accessible", filename);
-	}
-    }
-#endif
-#endif
-}
-
-/*
  * Print an error message based on the status code returned by system().
  */
 const char *
@@ -1115,8 +1090,6 @@ get_user_pass_cr (struct user_pass *up,
           FILE *fp;
           char password_buf[USER_PASS_LEN] = { '\0' };
 
-          warn_if_group_others_accessible (auth_file);
-
           fp = platform_fopen (auth_file, "r");
           if (!fp)
             msg (M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file);
src/openvpn/misc.h +0 -3
index ceda323..cc0a474 100644
@@ -71,9 +71,6 @@ void run_up_down (const char *command,
 
 void write_pid (const char *filename);
 
-/* check file protections */
-void warn_if_group_others_accessible(const char* filename);
-
 /* system flags */
 #define S_SCRIPT (1<<0)
 #define S_FATAL  (1<<1)
src/openvpn/options.c +47 -14

100 lines not on this page · raw message

src/openvpn/ssl_mbedtls.c +0 -2
index 4414f01..ea6d4c4 100644
@@ -361,8 +361,6 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file,
       return 1;
     }
 
-  warn_if_group_others_accessible (priv_key_file);
-
   if (!mbed_ok(mbedtls_pk_check_pair(&ctx->crt_chain->pk, ctx->priv_key)))
     {
       msg (M_WARN, "Private key does not match the certificate");
src/openvpn/ssl_openssl.c +0 -2
index 7002907..3064c20 100644
@@ -582,7 +582,6 @@ tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file,
   /* Load Private Key */
   if (!SSL_CTX_use_PrivateKey (ctx->ctx, pkey))
     crypto_msg (M_FATAL, "Cannot use private key");
-  warn_if_group_others_accessible (pkcs12_file);
 
   /* Check Private Key */
   if (!SSL_CTX_check_private_key (ctx->ctx))
@@ -758,7 +757,6 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file,
       crypto_msg (M_WARN, "Cannot load private key file %s", priv_key_file);
       goto end;
     }
-  warn_if_group_others_accessible (priv_key_file);
 
   /* Check Private Key */
   if (!SSL_CTX_check_private_key (ssl_ctx))
Signature · 1 line
2.7.4
#23 lore raw

Re: [Openvpn-devel] [PATCH applied] Move private file access checks to options_postprocess_filechecks()

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

ACK, but I added a slight modification at commit time. I removed any reference to --tls-crypt, to get this nice clean-up into the current tree. When --tls-crypt is added, it needs to be slightly adjusted to add the proper check in options_postprocess_filechecks().

Your patch has been applied to the master branch

commit 825e2ec1f358f2e81b623f2770fbbf76748b0739

Author: Steffan Karger
Date:   Sun Nov 13 15:02:31 2016 +0100
     Move private file access checks to options_postprocess_filechecks()

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: David Sommerseth <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg13019.html
     Signed-off-by: David Sommerseth <[email protected]...>

- --
kind regards,

David Sommerseth

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJYKjXbAAoJEIbPlEyWcf3yVeEP/Rs8Yz2iR9j3gp9ad79AIVKs Hgs+OkAQQaPoUAfVlKaC6fkvgf3p+pFd1yh7eQ45ymnUxlX3ViHs916DUaGn77jA rgXXCDu5kYZu3+BsknTCk+tO0oWE3GUP1DoSffAdVB5AQ7iyAqELODT5FEhDP4rf q8NmGoz3XOpjFduAK5O8oK73ftMEYK+Uw31DtJEWnUIK1FxoVLcFP8eOxMVLPKzq usKM0XO4GeoiFSR7xEKSZ5xvBtxO2edKiQzQpSgu8B12JaPLQ0OZ4SOgTNMSHn9d eUbbk0qbVyPt8mX8ARKb11/pEV7juG8SBRcii7D+OPbcDjl2YBrc8oHZUjndYRlv 3Zk92R3FudmbpOJEA1CFGzeL94CTpNhWjFVIMncuyt7QO1UdzYu1ikSmO9rXYFYH 3RWp6cMpfYZEo9gu2txYdhJgRpMXOg+TAMHLFkiwN1e2KGHpLEnosnDnP1DBgkkr atTNiH5c02kjMwIXeo5boLeBer/v+i+T/07dCoxIUghIhY37Y/3MAxw7uzAzzRPw t7dm/0bTYm0UeGQfMNmHelCKB/b7j1Wn7am1tUZOAkQuzfO/10kMqdOt7G2bW7FJ 22sHlSZ1DtdoUPQUCm2yLqsOnqMyIztCCEapMMW2XAhk6PTG+szyoAat3ri51aDT BwX3yZLM7VNPtOaRmAfU
=emq9
-----END PGP SIGNATURE-----

#24 lore raw

PATCH 5/5 Add --tls-crypt unit tests

In reply to Steffan Karger

These help verify the tls-crypt functionality - they already caught a bug during development. We should however probably also add some t_client tests once this feature is in.

To test --tls-crypt with as few dependencies as possible, this adds a mock implementation of msg() (or actually x_msg()). For debugging purposes, the mock implementation can be made to really log by calling mock_set_debug_level(), but defaults to (almost) no logging.

---
 tests/unit_tests/openvpn/Makefile.am      |  22 ++-
 tests/unit_tests/openvpn/mock_msg.c       |  92 ++++++++++++
 tests/unit_tests/openvpn/mock_msg.h       |  35 +++++
 tests/unit_tests/openvpn/test_argv.c      |  10 --
 tests/unit_tests/openvpn/test_tls_crypt.c | 242 ++++++++++++++++++++++++++++++
 5 files changed, 390 insertions(+), 11 deletions(-)
 create mode 100644 tests/unit_tests/openvpn/mock_msg.c
 create mode 100644 tests/unit_tests/openvpn/mock_msg.h
 create mode 100644 tests/unit_tests/openvpn/test_tls_crypt.c
tests/unit_tests/openvpn/Makefile.am +21 -1
index b706fae..632ff58 100644
@@ -2,8 +2,13 @@ AUTOMAKE_OPTIONS = foreign
 
 check_PROGRAMS = argv_testdriver
 
+if ENABLE_CRYPTO
+check_PROGRAMS += tls_crypt_testdriver
+endif
+
 TESTS = $(check_PROGRAMS)
 
+openvpn_includedir = $(top_srcdir)/include
 openvpn_srcdir = $(top_srcdir)/src/openvpn
 compat_srcdir = $(top_srcdir)/src/compat
 
@@ -11,7 +16,22 @@ argv_testdriver_CFLAGS  = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir) \
 	$(OPTIONAL_CRYPTO_CFLAGS)
 argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line \
 	$(OPTIONAL_CRYPTO_LIBS)
-argv_testdriver_SOURCES = test_argv.c \
+argv_testdriver_SOURCES = test_argv.c mock_msg.c \
 	$(openvpn_srcdir)/platform.c \
 	$(openvpn_srcdir)/buffer.c \
 	$(openvpn_srcdir)/argv.c
+
+tls_crypt_testdriver_CFLAGS  = @TEST_CFLAGS@ \
+	-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
+	$(OPTIONAL_CRYPTO_CFLAGS)
+tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
+	$(OPTIONAL_CRYPTO_LIBS)
+tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c \
+	$(openvpn_srcdir)/buffer.c \
+	$(openvpn_srcdir)/crypto.c \
+	$(openvpn_srcdir)/crypto_mbedtls.c \
+	$(openvpn_srcdir)/crypto_openssl.c \
+	$(openvpn_srcdir)/otime.c \
+	$(openvpn_srcdir)/packet_id.c \
+	$(openvpn_srcdir)/platform.c \
+	$(openvpn_srcdir)/tls_crypt.c
tests/unit_tests/openvpn/mock_msg.c new file +92 -0

92 lines not on this page · raw message

tests/unit_tests/openvpn/mock_msg.h new file +35 -0
new file mode 100644
index 0000000..5d93a1a
@@ -0,0 +1,35 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2016 Fox Crypto B.V. <[email protected]...>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MOCK_MSG_H
+#define MOCK_MSG_H
+
+/**
+ * Mock debug level defaults to 0, which gives clean(-ish) test reports.  Call
+ * this function from your test driver to increase debug output when you
+ * need debug output.
+ */
+void mock_set_debug_level(int level);
+
+#endif /* MOCK_MSG */
tests/unit_tests/openvpn/test_argv.c +0 -10
index f07a5fb..34125d6 100644
@@ -14,16 +14,6 @@
 #include "buffer.h"
 
 /*
- * Dummy symbols that need to be defined due to them being
- * referenced in #include'd header files and their includes
- */
-unsigned int x_debug_level;
-bool dont_mute (unsigned int flags) { return true; }
-void assert_failed (const char *filename, int line, const char *condition) { exit(0); }
-void out_of_memory (void) { }
-void x_msg (const unsigned int flags, const char *format, ...) { }
-
-/*
  * This is defined here to prevent #include'ing misc.h
  * which makes things difficult beyond any recognition
  */
tests/unit_tests/openvpn/test_tls_crypt.c new file +242 -0

242 lines not on this page · raw message

Signature · 1 line
2.7.4
#25 lore raw

[Openvpn-devel] [PATCH applied] Re: Add --tls-crypt unit tests

ACK, based on

- it does not change any "openvpn itself" code parts
- the cmocka stuff looks reasonable (and reasonably complete)
- it passes said unit tests :-)

Your patch has been applied to the master branch.

commit 3b185161de1254af746494007b7e81d17f632d4b

Author: Steffan Karger
Date:   Tue Nov 8 21:18:22 2016 +0100
     Add --tls-crypt unit tests

     Signed-off-by: Steffan Karger <[email protected]...>
     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg12973.html
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering