[Privoxy-commits] [privoxy] 03/07: Add elliptic-curve-keys directive and enable it by default

User Git git at git.privoxy.org
Mon Feb 23 12:55:30 CET 2026


This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository privoxy.

commit 1ae617e11cd0bea2797af384f7ccc963f6052502
Author: Fabian Keil <fk at fabiankeil.de>
AuthorDate: Wed Feb 4 14:15:29 2026 +0100

    Add elliptic-curve-keys directive and enable it by default
    
    It lets Privoxy use the SN_X9_62_prime256v1 group instead of
    RSA when generating website keys and certificates.
    
    This is expected to be faster but may not be supported by older clients.
    
    The OpenSSL-specific code is based on on a patch by Steven Smith
    submitted in SF#933.
---
 loadcfg.c |  15 +++-
 openssl.c | 120 +++++++++++++++++++++--------
 project.h |   2 +
 ssl.c     |  46 ++++++++---
 wolfssl.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 5 files changed, 375 insertions(+), 68 deletions(-)

diff --git a/loadcfg.c b/loadcfg.c
index 1e9fe2d3..580643ef 100644
--- a/loadcfg.c
+++ b/loadcfg.c
@@ -7,7 +7,7 @@
  *                routine to load the configuration and the global
  *                variables it writes to.
  *
- * Copyright   :  Written by and Copyright (C) 2001-2022 the
+ * Copyright   :  Written by and Copyright (C) 2001-2026 the
  *                Privoxy team. https://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -148,6 +148,7 @@ static struct file_list *current_configfile = NULL;
 #define hash_debug                            78263U /* "debug" */
 #define hash_default_server_timeout      2530089913U /* "default-server-timeout" */
 #define hash_deny_access                 1227333715U /* "deny-access" */
+#define hash_elliptic_curve_keys          258906537U /* "elliptic-curve-keys" */
 #define hash_enable_accept_filter        2909040407U /* "enable-accept-filter" */
 #define hash_enable_edit_actions         2517097536U /* "enable-edit-actions" */
 #define hash_enable_compression          3943696946U /* "enable-compression" */
@@ -678,6 +679,9 @@ struct configuration_spec * load_config(void)
 #endif
    config->feature_flags            &= ~RUNTIME_FEATURE_TOLERATE_PIPELINING;
    config->cors_allowed_origin       = NULL;
+#ifdef FEATURE_HTTPS_INSPECTION
+   config->elliptic_curve_keys = 1;
+#endif
 
    configfp = fopen(configfile, "r");
    if (NULL == configfp)
@@ -1027,6 +1031,15 @@ struct configuration_spec * load_config(void)
             break;
 #endif /* def FEATURE_ACL */
 
+#ifdef FEATURE_HTTPS_INSPECTION
+/* *************************************************************************
+ * elliptic-curve-keys 0|1
+ * *************************************************************************/
+         case hash_elliptic_curve_keys :
+            config->elliptic_curve_keys = parse_toggle_state(cmd, arg);
+            break;
+#endif /* def FEATURE_HTTPS_INSPECTION */
+
 #if defined(FEATURE_ACCEPT_FILTER) && defined(SO_ACCEPTFILTER)
 /* *************************************************************************
  * enable-accept-filter 0|1
diff --git a/openssl.c b/openssl.c
index bc6d6e53..3a5bf1e5 100644
--- a/openssl.c
+++ b/openssl.c
@@ -8,7 +8,7 @@
  *
  * Copyright   :  Written by and Copyright (c) 2020 Maxim Antonov <mantonov at gmail.com>
  *                Copyright (C) 2017 Vaclav Svec. FIT CVUT.
- *                Copyright (C) 2018-2024 by Fabian Keil <fk at fabiankeil.de>
+ *                Copyright (C) 2018-2026 by Fabian Keil <fk at fabiankeil.de>
  *
  *                This program is free software; you can redistribute it
  *                and/or modify it under the terms of the GNU General
@@ -1448,6 +1448,7 @@ static int generate_key(struct client_state *csp, char **key_buf)
 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
    BIGNUM *exp;
    RSA *rsa;
+   EC_KEY *ec_key;
 #endif
    EVP_PKEY *key;
 
@@ -1468,45 +1469,90 @@ static int generate_key(struct client_state *csp, char **key_buf)
    }
 
 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
-   exp = BN_new();
-   rsa = RSA_new();
    key = EVP_PKEY_new();
-   if (exp == NULL || rsa == NULL || key == NULL)
+   if (key == NULL)
    {
-      log_ssl_errors(LOG_LEVEL_ERROR, "RSA key memory allocation failure");
+      log_ssl_errors(LOG_LEVEL_ERROR, "RSA/EC key memory allocation failure.");
       ret = -1;
       goto exit;
    }
-
-   if (BN_set_word(exp, RSA_KEY_PUBLIC_EXPONENT) != 1)
+   if (csp->config->elliptic_curve_keys)
    {
-      log_ssl_errors(LOG_LEVEL_ERROR, "Setting RSA key exponent failed");
-      ret = -1;
-      goto exit;
+      ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+      if (ec_key == NULL)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR, "EC key creation failed.");
+         ret = -1;
+         goto exit;
+      }
+      if (!EC_KEY_generate_key(ec_key))
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR, "EC key generation failed.");
+         ret = -1;
+         goto exit;
+      }
+      if (!EVP_PKEY_set1_EC_KEY(key, ec_key))
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "Error assigning EC key pair to PKEY structure");
+         ret = -1;
+         goto exit;
+      }
    }
-
-   ret = RSA_generate_key_ex(rsa, RSA_KEYSIZE, exp, NULL);
-   if (ret == 0)
+   else
    {
-      log_ssl_errors(LOG_LEVEL_ERROR, "RSA key generation failure");
-      ret = -1;
-      goto exit;
-   }
+      exp = BN_new();
+      rsa = RSA_new();
+      if (exp == NULL || rsa == NULL)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR, "RSA key memory allocation failure");
+         ret = -1;
+         goto exit;
+      }
 
-   if (!EVP_PKEY_set1_RSA(key, rsa))
-   {
-      log_ssl_errors(LOG_LEVEL_ERROR,
-         "Error assigning RSA key pair to PKEY structure");
-      ret = -1;
-      goto exit;
+      if (BN_set_word(exp, RSA_KEY_PUBLIC_EXPONENT) != 1)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR, "Setting RSA key exponent failed");
+         ret = -1;
+         goto exit;
+      }
+
+      ret = RSA_generate_key_ex(rsa, RSA_KEYSIZE, exp, NULL);
+      if (ret == 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR, "RSA key generation failure");
+         ret = -1;
+         goto exit;
+      }
+
+      if (!EVP_PKEY_set1_RSA(key, rsa))
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "Error assigning RSA key pair to PKEY structure");
+         ret = -1;
+         goto exit;
+      }
    }
 #else
-   key = EVP_RSA_gen(RSA_KEYSIZE);
-   if (key == NULL)
+   if (csp->config->elliptic_curve_keys)
    {
-      log_error(LOG_LEVEL_ERROR, "EVP_RSA_gen() failed");
-      ret = -1;
-      goto exit;
+      key = EVP_EC_gen(SN_X9_62_prime256v1);
+      if (key == NULL)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR, "EC key generation error");
+         ret = -1;
+         goto exit;
+      }
+   }
+   else
+   {
+      key = EVP_RSA_gen(RSA_KEYSIZE);
+      if (key == NULL)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR, "EVP_RSA_gen() failed");
+         ret = -1;
+         goto exit;
+      }
    }
 #endif
 
@@ -1526,13 +1572,23 @@ exit:
     * Freeing used variables
     */
 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
-   if (exp)
+   if (csp->config->elliptic_curve_keys)
    {
-      BN_free(exp);
+      if (ec_key)
+      {
+         EC_KEY_free(ec_key);
+      }
    }
-   if (rsa)
+   else
    {
-      RSA_free(rsa);
+      if (exp)
+      {
+         BN_free(exp);
+      }
+      if (rsa)
+      {
+         RSA_free(rsa);
+      }
    }
 #endif
    if (key)
diff --git a/project.h b/project.h
index 3c516602..9f3c36ef 100644
--- a/project.h
+++ b/project.h
@@ -1616,6 +1616,8 @@ struct configuration_spec
 
    /** Filename of trusted CAs certificates **/
    char *trusted_cas_file;
+
+   int elliptic_curve_keys;
 #endif
 };
 
diff --git a/ssl.c b/ssl.c
index 37d7756c..6950caae 100644
--- a/ssl.c
+++ b/ssl.c
@@ -7,7 +7,7 @@
  *                using mbedTLS.
  *
  * Copyright   :  Written by and Copyright (c) 2017-2020 Vaclav Svec. FIT CVUT.
- *                Copyright (C) 2018-2024 by Fabian Keil <fk at fabiankeil.de>
+ *                Copyright (C) 2018-2026 by Fabian Keil <fk at fabiankeil.de>
  *
  *                This program is free software; you can redistribute it
  *                and/or modify it under the terms of the GNU General
@@ -75,7 +75,7 @@
  */
 typedef struct {
    mbedtls_pk_type_t type;   /* type of key to generate  */
-   int  rsa_keysize;         /* length of key in bits    */
+   int  keysize;             /* length of key in bits    */
    char *key_file_path;      /* filename of the key file */
 } key_options;
 
@@ -1002,8 +1002,16 @@ static int generate_key(struct client_state *csp, unsigned char **key_buf)
    /*
     * Preparing path for key file and other properties for generating key
     */
-   key_opt.type        = MBEDTLS_PK_RSA;
-   key_opt.rsa_keysize = RSA_KEYSIZE;
+   if (csp->config->elliptic_curve_keys)
+   {
+      key_opt.type    = MBEDTLS_PK_ECKEY;
+      key_opt.keysize = 32;
+   }
+   else
+   {
+      key_opt.type    = MBEDTLS_PK_RSA;
+      key_opt.keysize = RSA_KEYSIZE;
+   }
 
    key_opt.key_file_path = make_certs_path(csp->config->certificate_directory,
       (char *)csp->http->hash_of_host_hex, KEY_FILE_TYPE);
@@ -1044,16 +1052,30 @@ static int generate_key(struct client_state *csp, unsigned char **key_buf)
       goto exit;
    }
 
-   ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random,
-      &ctr_drbg, (unsigned)key_opt.rsa_keysize, RSA_KEY_PUBLIC_EXPONENT);
-   if (ret != 0)
+   if (csp->config->elliptic_curve_keys)
    {
-      mbedtls_strerror(ret, err_buf, sizeof(err_buf));
-      log_error(LOG_LEVEL_ERROR, "Key generating failed: %s", err_buf);
-      ret = -1;
-      goto exit;
+      ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1,
+         mbedtls_pk_ec(key), mbedtls_ctr_drbg_random, &ctr_drbg);
+      if (ret != 0)
+      {
+         mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+         log_error(LOG_LEVEL_ERROR, "ECC Key generation failed: %s", err_buf);
+         ret = -1;
+         goto exit;
+      }
+   }
+   else
+   {
+      ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random,
+         &ctr_drbg, (unsigned)key_opt.keysize, RSA_KEY_PUBLIC_EXPONENT);
+      if (ret != 0)
+      {
+         mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+         log_error(LOG_LEVEL_ERROR, "Key generating failed: %s", err_buf);
+         ret = -1;
+         goto exit;
+      }
    }
-
    /*
     * Exporting private key into file
     */
diff --git a/wolfssl.c b/wolfssl.c
index 40a1a46f..12b4c441 100644
--- a/wolfssl.c
+++ b/wolfssl.c
@@ -6,7 +6,7 @@
  *                creating, using and closing TLS/SSL connections
  *                using wolfSSL.
  *
- * Copyright   :  Copyright (C) 2018-2025 by Fabian Keil <fk at fabiankeil.de>
+ * Copyright   :  Copyright (C) 2018-2026 by Fabian Keil <fk at fabiankeil.de>
  *                Copyright (C) 2020 Maxim Antonov <mantonov at gmail.com>
  *                Copyright (C) 2017 Vaclav Svec. FIT CVUT.
  *
@@ -1512,6 +1512,89 @@ exit:
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  generate_ecc_key
+ *
+ * Description : Generates a new ECC key and saves it in a file.
+ *
+ * Parameters  :
+ *          1  :  ecc_key_path = Path to the key that should be written.
+ *
+ * Returns     :  -1 => Error while generating private key
+ *                 0 => Success.
+ *
+ *********************************************************************/
+static int generate_ecc_key(const char *ecc_key_path)
+{
+   ecc_key ecc_key;
+   byte ecc_key_der[4096];
+   int ret;
+   byte key_pem[4096];
+   int der_key_size;
+   int pem_key_size;
+   FILE *f = NULL;
+
+   assert(file_exists(ecc_key_path) != 1);
+
+   wc_ecc_init(&ecc_key);
+
+   ret = wc_ecc_make_key(&wolfssl_rng, 32, &ecc_key);
+   if (ret != 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "ECC key generation failed");
+      ret = -1;
+      goto exit;
+   }
+
+   der_key_size = wc_EccKeyToDer(&ecc_key, ecc_key_der, sizeof(ecc_key_der));
+   wc_ecc_free(&ecc_key);
+   if (der_key_size < 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "ECC key conversion to DER format failed");
+      ret = -1;
+      goto exit;
+   }
+   pem_key_size = wc_DerToPem(ecc_key_der, (word32)der_key_size,
+      key_pem, sizeof(key_pem), ECC_PRIVATEKEY_TYPE);
+   if (pem_key_size < 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "ECC key conversion to PEM format failed");
+      ret = -1;
+      goto exit;
+   }
+
+   /*
+    * Saving key into file
+    */
+   if ((f = fopen(ecc_key_path, "wb")) == NULL)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Opening file %s to save private key failed: %E",
+         ecc_key_path);
+      ret = -1;
+      goto exit;
+   }
+
+   if (fwrite(key_pem, 1, (size_t)pem_key_size, f) != pem_key_size)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Writing private key into file %s failed",
+         ecc_key_path);
+      close_file_stream(f, ecc_key_path);
+      ret = -1;
+      goto exit;
+   }
+
+   close_file_stream(f, ecc_key_path);
+
+exit:
+
+   return ret;
+
+}
+
+
 /*********************************************************************
  *
  * Function    :  ssl_certificate_load
@@ -1714,6 +1797,106 @@ static int load_rsa_key(const char *rsa_key_path, const char *password, RsaKey *
    return 1;
 }
 
+
+/*********************************************************************
+ *
+ * Function    :  load_ecc_key
+ *
+ * Description :  Load a PEM-encoded ECC file into memory.
+ *
+ * Parameters  :
+ *          1  :  ecc_key_path = Path to the file that holds the key.
+ *          2  :  ecc_key = Initialized ECC key storage.
+ *
+ * Returns     :   0 => Error while creating the key.
+ *                 1 => It worked
+ *
+ *********************************************************************/
+static int load_ecc_key(const char *ecc_key_path, ecc_key *ecc_key)
+{
+   FILE *fp;
+   size_t length;
+   long ret;
+   unsigned char *key_pem;
+   DerBuffer *der_buffer;
+   word32 der_index = 0;
+
+   fp = fopen(ecc_key_path, "rb");
+   if (NULL == fp)
+   {
+      log_error(LOG_LEVEL_ERROR, "Failed to open %s: %E", ecc_key_path);
+      return 0;
+   }
+
+   /* Get file length */
+   if (fseek(fp, 0, SEEK_END))
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Unexpected error while fseek()ing to the end of %s: %E",
+         ecc_key_path);
+      fclose(fp);
+      return 0;
+   }
+   ret = ftell(fp);
+   if (-1 == ret)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Unexpected ftell() error while loading %s: %E",
+         ecc_key_path);
+      fclose(fp);
+      return 0;
+   }
+   length = (size_t)ret;
+
+   /* Go back to the beginning. */
+   if (fseek(fp, 0, SEEK_SET))
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Unexpected error while fseek()ing to the beginning of %s: %E",
+         ecc_key_path);
+      fclose(fp);
+      return 0;
+   }
+
+   key_pem = malloc_or_die(length);
+
+   if (1 != fread(key_pem, length, 1, fp))
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Couldn't completely read file %s.", ecc_key_path);
+      fclose(fp);
+      freez(key_pem);
+      return 0;
+   }
+
+   fclose(fp);
+
+   ret = wc_PemToDer(key_pem, (long)length, ECC_PRIVATEKEY_TYPE,
+     &der_buffer, NULL, NULL, NULL);
+   freez(key_pem);
+   if (ret < 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to convert buffer into DER format for file %s. Error = %ld",
+         ecc_key_path, ret);
+      return 0;
+   }
+
+   ret = wc_EccPrivateKeyDecode(der_buffer->buffer, &der_index, ecc_key,
+      der_buffer->length);
+   freez(der_buffer);
+   if (ret < 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to decode DER buffer into ECC key structure for %s",
+         ecc_key_path);
+      return 0;
+   }
+
+   return 1;
+}
+
+
 #ifndef WOLFSSL_ALT_NAMES
 #error wolfSSL lacks Subject Alternative Name support (WOLFSSL_ALT_NAMES) which is mandatory
 #endif
@@ -1773,7 +1956,7 @@ static int set_subject_alternative_name(struct Cert *certificate, const char *ho
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
  *          2  :  certificate_path = Path to the certificate to generate.
- *          3  :  rsa_key_path = Path to the key to generate for the
+ *          3  :  key_path = Path to the key to generate for the
  *                               certificate.
  *
  * Returns     :  -1 => Error while creating certificate.
@@ -1782,11 +1965,12 @@ static int set_subject_alternative_name(struct Cert *certificate, const char *ho
  *
  *********************************************************************/
 static int generate_host_certificate(struct client_state *csp,
-   const char *certificate_path, const char *rsa_key_path)
+   const char *certificate_path, const char *key_path)
 {
    struct Cert certificate;
    RsaKey ca_key;
    RsaKey rsa_key;
+   ecc_key ecc_key;
    int ret;
    byte certificate_der[4096];
    int der_certificate_length;
@@ -1807,10 +1991,10 @@ static int generate_host_certificate(struct client_state *csp,
                certificate_path);
             return -1;
          }
-         if (unlink(rsa_key_path))
+         if (unlink(key_path))
          {
             log_error(LOG_LEVEL_ERROR, "Failed to unlink %s: %E",
-               rsa_key_path);
+               key_path);
             return -1;
          }
       }
@@ -1824,18 +2008,28 @@ static int generate_host_certificate(struct client_state *csp,
       log_error(LOG_LEVEL_CONNECT, "Creating new certificate %s",
          certificate_path);
    }
-   if (enforce_sane_certificate_state(certificate_path, rsa_key_path))
+   if (enforce_sane_certificate_state(certificate_path, key_path))
    {
       return -1;
    }
 
-   wc_InitRsaKey(&rsa_key, NULL);
-   wc_InitRsaKey(&ca_key, NULL);
-
-   if (generate_rsa_key(rsa_key_path) == -1)
+   if (csp->config->elliptic_curve_keys)
    {
-      return -1;
+      wc_ecc_init(&ecc_key);
+      if (generate_ecc_key(key_path) == -1)
+      {
+         return -1;
+      }
    }
+   else
+   {
+      wc_InitRsaKey(&rsa_key, NULL);
+      if (generate_rsa_key(key_path) == -1)
+      {
+         return -1;
+      }
+   }
+   wc_InitRsaKey(&ca_key, NULL);
 
    wc_InitCert(&certificate);
 
@@ -1862,22 +2056,35 @@ static int generate_host_certificate(struct client_state *csp,
       goto exit;
    }
 
-   if (load_rsa_key(rsa_key_path, NULL, &rsa_key) != 1)
+   if (csp->config->elliptic_curve_keys)
    {
-      log_error(LOG_LEVEL_ERROR,
-         "Failed to load RSA key %s", rsa_key_path);
-      ret = -1;
-      goto exit;
+      if (load_ecc_key(key_path, &ecc_key) != 1)
+      {
+         log_error(LOG_LEVEL_ERROR,
+            "Failed to load ECC key %s", key_path);
+         ret = -1;
+         goto exit;
+      }
+      der_certificate_length = wc_MakeCert(&certificate, certificate_der,
+         sizeof(certificate_der), NULL, &ecc_key, &wolfssl_rng);
+   }
+   else
+   {
+      if (load_rsa_key(key_path, NULL, &rsa_key) != 1)
+      {
+         log_error(LOG_LEVEL_ERROR,
+            "Failed to load RSA key %s", key_path);
+         ret = -1;
+         goto exit;
+      }
+      der_certificate_length = wc_MakeCert(&certificate, certificate_der,
+         sizeof(certificate_der), &rsa_key, NULL, &wolfssl_rng);
    }
-
-   /* wolfSSL_Debugging_ON(); */
-   der_certificate_length = wc_MakeCert(&certificate, certificate_der,
-      sizeof(certificate_der), &rsa_key, NULL, &wolfssl_rng);
-   /* wolfSSL_Debugging_OFF(); */
 
    if (der_certificate_length < 0)
    {
-      log_error(LOG_LEVEL_ERROR, "Failed to make certificate");
+      log_error(LOG_LEVEL_ERROR, "Failed to make certificate. "
+         "wc_MakeCert() return code: %d", der_certificate_length);
       ret = -1;
       goto exit;
    }
@@ -1922,7 +2129,14 @@ static int generate_host_certificate(struct client_state *csp,
    ret = 1;
 
 exit:
-   wc_FreeRsaKey(&rsa_key);
+   if (csp->config->elliptic_curve_keys)
+   {
+      wc_ecc_free(&ecc_key);
+   }
+   else
+   {
+      wc_FreeRsaKey(&rsa_key);
+   }
    wc_FreeRsaKey(&ca_key);
 
    return 1;

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Privoxy-commits mailing list