[squid-dev] [PATCH] TLS: Add support for EECDH

Paulo Matias matias at ufscar.br
Thu Jun 4 18:51:43 UTC 2015


Hi all,

This patch adds support for Ephemeral Elliptic Curve Diffie-Hellman (EECDH)
key exchange, which allows for forward secrecy with better performance than
traditional ephemeral DH.

This was previously posted to squid-users, but modified since then to implement
Amos's suggestions:

> * the DH parameters I think would be better added as a new option
> "tls-dh=curve:/path/to/params" where the 'curve' part is optional and
> implies EC when present - non-EC when absent.

OK, I have left the "dhparams=" option working as before, but emitting a
deprecation warning message. There is a new "tls-dh=" option which should
replace "dhparams=", and supports the suggested syntax. The following
are also accepted:

 * "tls-dh=:/path/to/params" -> equivalent to "tls-dh=/path/to/params"
 * "tls-dh=curve:" -> allows for enabling only EECDH (no EDH)

> * SINGLE_ECDH_USE needs to be documented in release-4.sgml
>  "New <em>options=SINGLE_ECDH_USE</em> parameter to ..."

OK.

> * The ECDH changes affect both https_port and http_port. They need
> separate listings for each under changed directives, duplicate text on
> the line items is fine.

OK.

> * please implement (duplicate) all this UI parse change using the
> Security::PeerOptions object (src/security/PeerOptions.*)
>  - the src/ssl/* code for UI parsing and config storage is 'legacy' only
> use by http(s)_port directives.
>  - this may require some small changes suitable for use on client contexts
>  - UI options added to Security::PeerOptions get documented in
> release-4.sgml as changes for both cache_peer and tls_outgoing_options.
>  - also in cf.data.pre for those directives

If I understood correctly, Security::PeerOptions is currently only used
for client TLS contexts. Currently, it does not implement e.g. the "dhparams="
option, which is only supposed to be present when configuring a server
TLS context. According to
http://openssl.6102.n7.nabble.com/question-about-ecdh-functions-tp38239p38242.html,
the curve should also be configured only in the server context. Also,
https://github.com/openssl/openssl/blob/e481f9b90b164fd1053015d1c4e0a0d92076d7a8/ssl/ssl_conf.c#L450
shows that the "named_curve" parameter is only allowed in the s_server
OpenSSL tool, therefore not allowed in s_client.

Therefore this is not included in the patch below. Please correct me if I
misunderstood anything.

> * configureSslEECDH() return true in the event that the chosen
> configuration options are not even available.
>  - please make an #else condition that displays an ERROR message at
> level DBG_CRITICAL about the option(s) not being available, then return
> false.
>  - variable 'ok' can then become const (define on assignment) and move
> fully inside the #if case.

OK.

We welcome any additional suggestions or comments.

Best regards,
Paulo Matias


-------------- next part --------------
=== modified file 'doc/release-notes/release-4.sgml'
--- doc/release-notes/release-4.sgml	2015-05-22 09:42:55 +0000
+++ doc/release-notes/release-4.sgml	2015-06-04 14:59:38 +0000
@@ -137,6 +137,12 @@
 	<p>All <em>options=</em> values for SSLv2
 	   configuration or disabling have been removed.
 	<p>Removed <em>version=</em> option. Use <em>options=</em> instead.
+	<p>New <em>options=SINGLE_ECDH_USE</em> parameter to enable ephemeral
+	   ECDH key exchange.
+	<p>Deprecated <em>dhparams=</em> option. Use <em>tls-dh=</em> instead.
+	   The new option allows to optionally specify an elliptic curve for
+	   ephemeral ECDH by adding <em>curve-name:</em> in front of the
+	   parameter file name.
 	<p>Manual squid.conf update may be required on upgrade.
 
 	<tag>sslcrtd_children</tag>

=== modified file 'src/anyp/PortCfg.cc'
--- src/anyp/PortCfg.cc	2015-05-22 09:42:55 +0000
+++ src/anyp/PortCfg.cc	2015-06-04 13:36:42 +0000
@@ -54,6 +54,7 @@
     capath(NULL),
     crlfile(NULL),
     dhfile(NULL),
+    tls_dh(NULL),
     sslflags(NULL),
     sslContextSessionId(NULL),
     generateHostCertificates(false),
@@ -67,6 +68,7 @@
     clientVerifyCrls(),
     clientCA(),
     dhParams(),
+    eecdhCurve(NULL),
     contextMethod(),
     sslContextFlags(0),
     sslOptions(0)
@@ -95,8 +97,10 @@
     safe_free(capath);
     safe_free(crlfile);
     safe_free(dhfile);
+    safe_free(tls_dh);
     safe_free(sslflags);
     safe_free(sslContextSessionId);
+    safe_free(eecdhCurve);
 #endif
 }
 
@@ -140,6 +144,8 @@
         b->crlfile = xstrdup(crlfile);
     if (dhfile)
         b->dhfile = xstrdup(dhfile);
+    if (tls_dh)
+        b->tls_dh = xstrdup(tls_dh);
     if (sslflags)
         b->sslflags = xstrdup(sslflags);
     if (sslContextSessionId)
@@ -227,8 +233,27 @@
     contextMethod = SSLv23_server_method();
 #endif
 
-    if (dhfile)
-        dhParams.reset(Ssl::readDHParams(dhfile));
+    const char *dhFile = dhfile; // backward compatibility for dhparams= configuration
+
+    if (tls_dh && tls_dh[0]) {
+        safe_free(eecdhCurve);
+        eecdhCurve = xstrdup(tls_dh);
+        char *p = strchr(eecdhCurve, ':');
+        if (p) {  // tls-dh=eecdhCurve:dhfile
+            *p = '\0';
+            dhFile = p+1;
+        } else {  // tls-dh=dhfile is equivalent to tls-dh=:dhfile
+            dhFile = tls_dh;
+            eecdhCurve[0] = '\0';
+        }
+    }
+
+    if (dhFile && dhFile[0])
+        dhParams.reset(Ssl::readDHParams(dhFile));
+
+    // an empty eecdhCurve means "do not use EECDH"
+    if (eecdhCurve && !eecdhCurve[0])
+        safe_free(eecdhCurve);
 
     if (sslflags)
         sslContextFlags = Ssl::parse_flags(sslflags);

=== modified file 'src/anyp/PortCfg.h'
--- src/anyp/PortCfg.h	2015-01-13 07:25:36 +0000
+++ src/anyp/PortCfg.h	2015-06-03 19:36:39 +0000
@@ -78,6 +78,7 @@
     char *capath;
     char *crlfile;
     char *dhfile;
+    char *tls_dh;
     char *sslflags;
     char *sslContextSessionId; ///< "session id context" for staticSslContext
     bool generateHostCertificates; ///< dynamically make host cert for sslBump
@@ -93,6 +94,7 @@
     Ssl::X509_CRL_STACK_Pointer clientVerifyCrls; ///< additional CRL lists to use when verifying the client certificate
     Ssl::X509_NAME_STACK_Pointer clientCA; ///< CA certificates to use when verifying client certificates
     Ssl::DH_Pointer dhParams; ///< DH parameters for temporary/ephemeral DH key exchanges
+    char *eecdhCurve; ///< Elliptic curve for ephemeral EC-based DH key exchanges
     Ssl::ContextMethod contextMethod; ///< The context method (SSL_METHOD) to use when creating certificates
     long sslContextFlags; ///< flags modifying the use of SSL
     long sslOptions; ///< SSL engine options

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2015-05-22 09:42:55 +0000
+++ src/cache_cf.cc	2015-06-03 19:13:55 +0000
@@ -3612,8 +3612,13 @@
         safe_free(s->crlfile);
         s->crlfile = xstrdup(token + 8);
     } else if (strncmp(token, "dhparams=", 9) == 0) {
+        debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: '" << token << "' is deprecated " <<
+               "in " << cfg_directive << ". Use 'tls-dh=' instead.");
         safe_free(s->dhfile);
         s->dhfile = xstrdup(token + 9);
+    } else if (strncmp(token, "tls-dh=", 7) == 0) {
+        safe_free(s->tls_dh);
+        s->tls_dh = xstrdup(token + 7);
     } else if (strncmp(token, "sslflags=", 9) == 0) {
         safe_free(s->sslflags);
         s->sslflags = xstrdup(token + 9);
@@ -3834,6 +3839,9 @@
     if (s->dhfile)
         storeAppendPrintf(e, " dhparams=%s", s->dhfile);
 
+    if (s->tls_dh)
+        storeAppendPrintf(e, " tls-dh=%s", s->tls_dh);
+
     if (s->sslflags)
         storeAppendPrintf(e, " sslflags=%s", s->sslflags);
 

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2015-06-02 12:04:00 +0000
+++ src/cf.data.pre	2015-06-03 21:14:04 +0000
@@ -1935,6 +1935,11 @@
 				      Always create a new key when using
 				      temporary/ephemeral DH key exchanges
 
+			    SINGLE_ECDH_USE
+				      Enable ephemeral ECDH key exchange.
+				      The adopted curve should be specified
+				      using the tls-dh option.
+
 			    NO_TICKET
 				      Disable use of RFC5077 session tickets.
 				      Some servers may have problems
@@ -1963,11 +1968,15 @@
 			the client certificate, in addition to CRLs stored in
 			the capath. Implies VERIFY_CRL flag below.
 
-	   dhparams=	File containing DH parameters for temporary/ephemeral
-			DH key exchanges. See OpenSSL documentation for details
-			on how to create this file.
-			WARNING: EDH ciphers will be silently disabled if this
-				 option is not set.
+	   tls-dh=[curve:]file
+			File containing DH parameters for temporary/ephemeral DH key
+			exchanges, optionally prefixed by a curve for ephemeral ECDH
+			key exchanges.
+			See OpenSSL documentation for details on how to create the
+			DH parameter file. Supported curves for ECDH can be listed
+			using the "openssl ecparam -list_curves" command.
+			WARNING: EDH and EECDH ciphers will be silently disabled if
+				 this option is not set.
 
 	   sslflags=	Various flags modifying the use of SSL:
 			    DELAYED_AUTH
@@ -2110,6 +2119,11 @@
 				      Always create a new key when using
 				      temporary/ephemeral DH key exchanges
 
+			    SINGLE_ECDH_USE
+				      Enable ephemeral ECDH key exchange.
+				      The adopted curve should be specified
+				      using the tls-dh option.
+
 			    SSL_OP_NO_TICKET
 				      Disable use of RFC5077 session tickets.
 				      Some servers may have problems
@@ -2138,8 +2152,10 @@
 			the client certificate, in addition to CRLs stored in
 			the capath. Implies VERIFY_CRL flag below.
 
-	   dhparams=	File containing DH parameters for temporary/ephemeral
-			DH key exchanges.
+	   tls-dh=[curve:]file
+			File containing DH parameters for temporary/ephemeral DH key
+			exchanges, optionally prefixed by a curve for ephemeral ECDH
+			key exchanges.
 
 	   sslflags=	Various flags modifying the use of SSL:
 			    DELAYED_AUTH

=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc	2015-06-03 10:42:08 +0000
+++ src/ssl/support.cc	2015-06-04 14:53:55 +0000
@@ -472,6 +472,11 @@
         "NO_TICKET", SSL_OP_NO_TICKET
     },
 #endif
+#if SSL_OP_SINGLE_ECDH_USE
+    {
+        "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
+    },
+#endif
     {
         "", 0
     },
@@ -824,6 +829,29 @@
 }
 
 static bool
+configureSslEECDH(SSL_CTX *sslContext, const char *curve)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
+    int nid = OBJ_sn2nid(curve);
+    if (!nid) {
+        debugs(83, DBG_CRITICAL, "ERROR: Unknown EECDH curve '" << curve << "'");
+        return false;
+    }
+
+    EC_KEY *ecdh = EC_KEY_new_by_curve_name(nid);
+    if (ecdh == NULL)
+        return false;
+
+    const bool ok = SSL_CTX_set_tmp_ecdh(sslContext, ecdh) != 0;
+    EC_KEY_free(ecdh);
+    return ok;
+#else
+    debugs(83, DBG_CRITICAL, "ERROR: EECDH is not available in this build");
+    return false;
+#endif
+}
+
+static bool
 configureSslContext(SSL_CTX *sslContext, AnyP::PortCfg &port)
 {
     int ssl_error;
@@ -855,6 +883,16 @@
     debugs(83, 9, "Setting RSA key generation callback.");
     SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
 
+    if (port.eecdhCurve) {
+        debugs(83, 9, "Setting Ephemeral ECDH curve to " << port.eecdhCurve << ".");
+
+        if (!configureSslEECDH(sslContext, port.eecdhCurve)) {
+            ssl_error = ERR_get_error();
+            debugs(83, DBG_CRITICAL, "ERROR: Unable to configure Ephemeral ECDH: " << ERR_error_string(ssl_error, NULL));
+            return false;
+        }
+    }
+
     debugs(83, 9, "Setting CA certificate locations.");
 
     const char *cafile = port.cafile ? port.cafile : port.clientca;




More information about the squid-dev mailing list