# https://svn.apache.org/r1540727
#
# Backport from 2.2.26: mod_ssl: enable support for ECC keys and ECDH ciphers.
#
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
index b9e3f93..19794f0 100644
--- a/modules/ssl/mod_ssl.c
+++ b/modules/ssl/mod_ssl.c
@@ -441,6 +441,9 @@ int ssl_init_ssl_connection(conn_rec *c)
      */
     SSL_set_tmp_rsa_callback(ssl, ssl_callback_TmpRSA);
     SSL_set_tmp_dh_callback(ssl,  ssl_callback_TmpDH);
+#ifndef OPENSSL_NO_EC
+    SSL_set_tmp_ecdh_callback(ssl, ssl_callback_TmpECDH);
+#endif
 
     SSL_set_verify_result(ssl, X509_V_OK);
 
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index dcae945..d8b4802 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -72,6 +72,9 @@ static void ssl_tmp_keys_free(server_rec *s)
 
     MODSSL_TMP_KEYS_FREE(mc, RSA);
     MODSSL_TMP_KEYS_FREE(mc, DH);
+#ifndef OPENSSL_NO_EC
+    MODSSL_TMP_KEY_FREE(mc, EC_KEY, SSL_TMP_KEY_EC_256);
+#endif
 }
 
 static int ssl_tmp_key_init_rsa(server_rec *s,
@@ -133,6 +136,40 @@ static int ssl_tmp_key_init_dh(server_rec *s,
     return OK;
 }
 
+#ifndef OPENSSL_NO_EC
+static int ssl_tmp_key_init_ec(server_rec *s,
+                               int bits, int idx)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    EC_KEY *ecdh = NULL;
+
+    /* XXX: Are there any FIPS constraints we should enforce? */
+
+    if (bits != 256) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                     "Init: Failed to generate temporary "
+                     "%d bit EC parameters, only 256 bits supported", bits);
+        return !OK;
+    }
+
+    if ((ecdh = EC_KEY_new()) == NULL ||
+        EC_KEY_set_group(ecdh, EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) != 1)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                     "Init: Failed to generate temporary "
+                     "%d bit EC parameters", bits);
+        return !OK;
+    }
+
+    mc->pTmpKeys[idx] = ecdh;
+    return OK;
+}
+
+#define MODSSL_TMP_KEY_INIT_EC(s, bits) \
+    ssl_tmp_key_init_ec(s, bits, SSL_TMP_KEY_EC_##bits)
+
+#endif
+
 #define MODSSL_TMP_KEY_INIT_RSA(s, bits) \
     ssl_tmp_key_init_rsa(s, bits, SSL_TMP_KEY_RSA_##bits)
 
@@ -157,6 +194,15 @@ static int ssl_tmp_keys_init(server_rec *s)
         return !OK;
     }
 
+#ifndef OPENSSL_NO_EC
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
+                 "Init: Generating temporary EC parameters (256 bits)");
+
+    if (MODSSL_TMP_KEY_INIT_EC(s, 256)) {
+        return !OK;
+    }
+#endif
+
     return OK;
 }
 
@@ -399,7 +445,11 @@ static void ssl_init_server_check(server_rec *s,
      *  Check for problematic re-initializations
      */
     if (mctx->pks->certs[SSL_AIDX_RSA] ||
-        mctx->pks->certs[SSL_AIDX_DSA])
+        mctx->pks->certs[SSL_AIDX_DSA]
+#ifndef OPENSSL_NO_EC
+      || mctx->pks->certs[SSL_AIDX_ECC]
+#endif
+        )
     {
         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
                 "Illegal attempt to re-initialise SSL for server "
@@ -599,6 +649,9 @@ static void ssl_init_ctx_callbacks(server_rec *s,
 
     SSL_CTX_set_tmp_rsa_callback(ctx, ssl_callback_TmpRSA);
     SSL_CTX_set_tmp_dh_callback(ctx,  ssl_callback_TmpDH);
+#ifndef OPENSSL_NO_EC
+    SSL_CTX_set_tmp_ecdh_callback(ctx,ssl_callback_TmpECDH);
+#endif
 
     SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
 }
@@ -866,9 +919,16 @@ static int ssl_server_import_key(server_rec *s,
     ssl_asn1_t *asn1;
     MODSSL_D2I_PrivateKey_CONST unsigned char *ptr;
     const char *type = ssl_asn1_keystr(idx);
-    int pkey_type = (idx == SSL_AIDX_RSA) ? EVP_PKEY_RSA : EVP_PKEY_DSA;
+    int pkey_type;
     EVP_PKEY *pkey;
 
+#ifndef OPENSSL_NO_EC
+    if (idx == SSL_AIDX_ECC)
+      pkey_type = EVP_PKEY_EC;
+    else
+#endif
+    pkey_type = (idx == SSL_AIDX_RSA) ? EVP_PKEY_RSA : EVP_PKEY_DSA;
+
     if (!(asn1 = ssl_asn1_table_get(mc->tPrivateKey, id))) {
         return FALSE;
     }
@@ -979,19 +1039,39 @@ static void ssl_init_server_certs(server_rec *s,
                                   modssl_ctx_t *mctx)
 {
     const char *rsa_id, *dsa_id;
+#ifndef OPENSSL_NO_EC
+    const char *ecc_id;
+#endif
     const char *vhost_id = mctx->sc->vhost_id;
     int i;
     int have_rsa, have_dsa;
+#ifndef OPENSSL_NO_EC
+    int have_ecc;
+#endif
 
     rsa_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_RSA);
     dsa_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_DSA);
+#ifndef OPENSSL_NO_EC
+    ecc_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_ECC);
+#endif
 
     have_rsa = ssl_server_import_cert(s, mctx, rsa_id, SSL_AIDX_RSA);
     have_dsa = ssl_server_import_cert(s, mctx, dsa_id, SSL_AIDX_DSA);
+#ifndef OPENSSL_NO_EC
+    have_ecc = ssl_server_import_cert(s, mctx, ecc_id, SSL_AIDX_ECC);
+#endif
 
-    if (!(have_rsa || have_dsa)) {
+    if (!(have_rsa || have_dsa
+#ifndef OPENSSL_NO_EC
+        || have_ecc
+#endif
+)) {
         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+#ifndef OPENSSL_NO_EC
+                "Oops, no RSA, DSA or ECC server certificate found "
+#else
                 "Oops, no RSA or DSA server certificate found "
+#endif
                 "for '%s:%d'?!", s->server_hostname, s->port);
         ssl_die();
     }
@@ -1002,10 +1082,21 @@ static void ssl_init_server_certs(server_rec *s,
 
     have_rsa = ssl_server_import_key(s, mctx, rsa_id, SSL_AIDX_RSA);
     have_dsa = ssl_server_import_key(s, mctx, dsa_id, SSL_AIDX_DSA);
+#ifndef OPENSSL_NO_EC
+    have_ecc = ssl_server_import_key(s, mctx, ecc_id, SSL_AIDX_ECC);
+#endif
 
-    if (!(have_rsa || have_dsa)) {
+    if (!(have_rsa || have_dsa
+#ifndef OPENSSL_NO_EC
+        || have_ecc
+#endif
+          )) {
         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+#ifndef OPENSSL_NO_EC
+                "Oops, no RSA, DSA or ECC server private key found?!");
+#else
                 "Oops, no RSA or DSA server private key found?!");
+#endif
         ssl_die();
     }
 }
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index 6cb2087..28bc47a 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -1267,6 +1267,27 @@ DH *ssl_callback_TmpDH(SSL *ssl, int export, int keylen)
     return (DH *)mc->pTmpKeys[idx];
 }
 
+#ifndef OPENSSL_NO_EC
+EC_KEY *ssl_callback_TmpECDH(SSL *ssl, int export, int keylen)
+{
+    conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+    SSLModConfigRec *mc = myModConfigFromConn(c);
+    int idx;
+
+    /* XXX Uses 256-bit key for now. TODO: support other sizes. */
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+                  "handing out temporary 256 bit ECC key");
+
+    switch (keylen) {
+      case 256:
+      default:
+        idx = SSL_TMP_KEY_EC_256;
+    }
+
+    return (EC_KEY *)mc->pTmpKeys[idx];
+}
+#endif
+
 /*
  * This OpenSSL callback function is called when OpenSSL
  * does client authentication and verifies the certificate chain.
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
index 650043d..79d222e 100644
--- a/modules/ssl/ssl_private.h
+++ b/modules/ssl/ssl_private.h
@@ -191,11 +191,21 @@ typedef int ssl_algo_t;
 #define SSL_ALGO_UNKNOWN (0)
 #define SSL_ALGO_RSA     (1<<0)
 #define SSL_ALGO_DSA     (1<<1)
+#ifndef OPENSSL_NO_EC
+#define SSL_ALGO_ECC     (1<<2)
+#define SSL_ALGO_ALL     (SSL_ALGO_RSA|SSL_ALGO_DSA|SSL_ALGO_ECC)
+#else
 #define SSL_ALGO_ALL     (SSL_ALGO_RSA|SSL_ALGO_DSA)
+#endif
 
 #define SSL_AIDX_RSA     (0)
 #define SSL_AIDX_DSA     (1)
+#ifndef OPENSSL_NO_EC
+#define SSL_AIDX_ECC     (2)
+#define SSL_AIDX_MAX     (3)
+#else
 #define SSL_AIDX_MAX     (2)
+#endif
 
 
 /**
@@ -206,7 +216,12 @@ typedef int ssl_algo_t;
 #define SSL_TMP_KEY_RSA_1024 (1)
 #define SSL_TMP_KEY_DH_512   (2)
 #define SSL_TMP_KEY_DH_1024  (3)
+#ifndef OPENSSL_NO_EC
+#define SSL_TMP_KEY_EC_256   (4)
+#define SSL_TMP_KEY_MAX      (5)
+#else
 #define SSL_TMP_KEY_MAX      (4)
+#endif
 
 /**
  * Define the SSL options
@@ -625,6 +640,9 @@ void         ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s);
 /**  OpenSSL callbacks */
 RSA         *ssl_callback_TmpRSA(SSL *, int, int);
 DH          *ssl_callback_TmpDH(SSL *, int, int);
+#ifndef OPENSSL_NO_EC
+EC_KEY      *ssl_callback_TmpECDH(SSL *, int, int);
+#endif
 int          ssl_callback_SSLVerify(int, X509_STORE_CTX *);
 int          ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, conn_rec *);
 int          ssl_callback_proxy_cert(SSL *ssl, MODSSL_CLIENT_CERT_CB_ARG_TYPE **x509, EVP_PKEY **pkey);
diff --git a/modules/ssl/ssl_toolkit_compat.h b/modules/ssl/ssl_toolkit_compat.h
index bd57a17..6f49f30 100644
--- a/modules/ssl/ssl_toolkit_compat.h
+++ b/modules/ssl/ssl_toolkit_compat.h
@@ -38,6 +38,12 @@
 #include <openssl/evp.h>
 #include <openssl/rand.h>
 #include <openssl/x509v3.h>
+
+/* ECC support came along in OpenSSL 1.0.0 */
+#if (OPENSSL_VERSION_NUMBER < 0x10000000)
+#define OPENSSL_NO_EC
+#endif
+
 /** Avoid tripping over an engine build installed globally and detected
  * when the user points at an explicit non-engine flavor of OpenSSL
  */
diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c
index e4387e6..efac310 100644
--- a/modules/ssl/ssl_util.c
+++ b/modules/ssl/ssl_util.c
@@ -150,6 +150,11 @@ ssl_algo_t ssl_util_algotypeof(X509 *pCert, EVP_PKEY *pKey)
             case EVP_PKEY_DSA:
                 t = SSL_ALGO_DSA;
                 break;
+#ifndef OPENSSL_NO_EC
+            case EVP_PKEY_EC:
+                t = SSL_ALGO_ECC;
+                break;
+#endif 
             default:
                 break;
         }
@@ -174,6 +179,11 @@ char *ssl_util_algotypestr(ssl_algo_t t)
         case SSL_ALGO_DSA:
             cp = "DSA";
             break;
+#ifndef OPENSSL_NO_EC
+        case SSL_ALGO_ECC:
+            cp = "ECC";
+            break;
+#endif
         default:
             break;
     }
@@ -245,7 +255,11 @@ void ssl_asn1_table_unset(apr_hash_t *table,
     apr_hash_set(table, key, klen, NULL);
 }
 
+#ifndef OPENSSL_NO_EC
+static const char *ssl_asn1_key_types[] = {"RSA", "DSA", "ECC"};
+#else
 static const char *ssl_asn1_key_types[] = {"RSA", "DSA"};
+#endif
 
 const char *ssl_asn1_keystr(int keytype)
 {
