--- /dev/null
+Since 2.4.0, OpenVPN has official support for elliptic curve crypto. Elliptic
+curves are an alternative to RSA for asymmetric encryption.
+
+Elliptic curve crypto ('ECC') can be used for the ('TLS') control channel only
+in OpenVPN; the data channel (encrypting the actual network traffic) uses
+symmetric encryption. ECC can be used in TLS for authentication (ECDSA) and key
+exchange (ECDH).
+
+Key exchange (ECDH)
+-------------------
+OpenVPN 2.4.0 and newer automatically initialize ECDH parameters. When ECDSA is
+used for authentication, the curve used for the server certificate will be used
+for ECDH too. When autodetection fails (e.g. when using RSA certificates)
+OpenVPN lets the crypto library decide if possible, or falls back to the
+secp384r1 curve.
+
+An administrator can force an OpenVPN/OpenSSL server to use a specific curve
+using the --ecdh-curve <curvename> option with one of the curves listed as
+available by the --show-curves option. Clients will use the same curve as
+selected by the server.
+
+Note that not all curves listed by --show-curves are available for use with TLS;
+in that case connecting will fail with a 'no shared cipher' TLS error.
+
+Authentication (ECDSA)
+----------------------
+Since OpenVPN 2.4.0, using ECDSA certificates works 'out of the box'. Which
+specific curves and cipher suites are available depends on your version and
+configuration of the crypto library. The crypto library will automatically
+select a cipher suite for the TLS control channel.
+
+Support for generating an ECDSA certificate chain is available in EasyRSA (in
+spite of it's name) since EasyRSA 3.0. The parameters you're looking for are
+'--use-algo=ec' and '--curve=<curve_name>'. See the EasyRSA documentation for
+more details on generating ECDSA certificates.
may be considered public.
.\"*********************************************************
.TP
+.B \-\-ecdh-curve name
+Specify the curve to use for elliptic curve Diffie Hellman. Available
+curves can be listed with
+.B \-\-show-curves
+. The specified curve will only be used for ECDH TLS-ciphers.
+.\"*********************************************************
+.TP
.B \-\-cert file
Local peer's signed certificate in .pem format \-\- must be signed
by a certificate authority whose certificate is in
Show currently available hardware-based crypto acceleration
engines supported by the OpenSSL library.
.\"*********************************************************
+.TP
+.B \-\-show-curves
+(Standalone)
+Show all available elliptic curves to use with the
+.B \-\-ecdh-curve
+option.
+.\"*********************************************************
.SS Generate a random key:
Used only for non-TLS static key encryption mode.
.\"*********************************************************
#ifdef ENABLE_CRYPTO
if (options->show_ciphers || options->show_digests || options->show_engines
#ifdef ENABLE_SSL
- || options->show_tls_ciphers
+ || options->show_tls_ciphers || options->show_curves
#endif
)
{
#ifdef ENABLE_SSL
if (options->show_tls_ciphers)
show_available_tls_ciphers (options->cipher_list);
+ if (options->show_curves)
+ show_available_curves();
#endif
return true;
}
o->renegotiate_seconds = 3600;
o->handshake_window = 60;
o->transition_window = 3600;
+ o->ecdh_curve = NULL;
#ifdef ENABLE_X509ALTUSERNAME
o->x509_username_field = X509_USERNAME_FIELD_DEFAULT;
#endif
VERIFY_PERMISSION (OPT_P_GENERAL);
options->show_tls_ciphers = true;
}
+ else if (streq (p[0], "show-curves"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->show_curves = true;
+ }
+ else if (streq (p[0], "ecdh-curve") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ options->ecdh_curve= p[1];
+ }
else if (streq (p[0], "tls-server"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
bool show_engines;
#ifdef ENABLE_SSL
bool show_tls_ciphers;
+ bool show_curves;
#endif
bool genkey;
#endif
const char *priv_key_file;
const char *pkcs12_file;
const char *cipher_list;
+ const char *ecdh_curve;
const char *tls_verify;
int verify_x509_type;
const char *verify_x509_name;
tls_ctx_load_extra_certs(new_ctx, options->extra_certs_file, options->extra_certs_file_inline);
}
+ /* Once keys and cert are loaded, load ECDH parameters */
+ if (options->tls_server)
+ tls_ctx_load_ecdh_params(new_ctx, options->ecdh_curve);
+
/* Allowable ciphers */
tls_ctx_restrict_ciphers(new_ctx, options->cipher_list);
void tls_ctx_load_dh_params(struct tls_root_ctx *ctx, const char *dh_file,
const char *dh_file_inline);
+/**
+ * Load Elliptic Curve Parameters, and load them into the library-specific
+ * TLS context.
+ *
+ * @param ctx TLS context to use
+ * @param curve_name The name of the elliptic curve to load.
+ */
+void tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name
+ );
+
/**
* Load PKCS #12 file for key, cert and (optionally) CA certs, and add to
* library-specific TLS context.
*/
void show_available_tls_ciphers (const char *tls_ciphers);
+/*
+ * Show the available elliptic curves in the crypto library
+ */
+void show_available_curves (void);
+
/*
* The OpenSSL library has a notion of preference in TLS ciphers. Higher
* preference == more secure. Return the highest preference cipher.
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
#include <openssl/crypto.h>
+#ifndef OPENSSL_NO_EC
+#include <openssl/ec.h>
+#endif
/*
* Allocate space in SSL objects in which to store a struct tls_session
DH_free (dh);
}
+void
+tls_ctx_load_ecdh_params (struct tls_root_ctx *ctx, const char *curve_name
+ )
+{
+#ifndef OPENSSL_NO_EC
+ int nid = NID_undef;
+ EC_KEY *ecdh = NULL;
+ const char *sname = NULL;
+
+ /* Generate a new ECDH key for each SSL session (for non-ephemeral ECDH) */
+ SSL_CTX_set_options(ctx->ctx, SSL_OP_SINGLE_ECDH_USE);
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ /* OpenSSL 1.0.2 and newer can automatically handle ECDH parameter loading */
+ if (NULL == curve_name) {
+ SSL_CTX_set_ecdh_auto(ctx->ctx, 1);
+ return;
+ }
+#endif
+ /* For older OpenSSL, we'll have to do the parameter loading on our own */
+ if (curve_name != NULL)
+ {
+ /* Use user supplied curve if given */
+ msg (D_TLS_DEBUG, "Using user specified ECDH curve (%s)", curve_name);
+ nid = OBJ_sn2nid(curve_name);
+ }
+ else
+ {
+ /* Extract curve from key */
+ EC_KEY *eckey = NULL;
+ const EC_GROUP *ecgrp = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ /* Little hack to get private key ref from SSL_CTX, yay OpenSSL... */
+ SSL ssl;
+ ssl.cert = ctx->ctx->cert;
+ pkey = SSL_get_privatekey(&ssl);
+
+ msg (D_TLS_DEBUG, "Extracting ECDH curve from private key");
+
+ if (pkey != NULL && (eckey = EVP_PKEY_get1_EC_KEY(pkey)) != NULL &&
+ (ecgrp = EC_KEY_get0_group(eckey)) != NULL)
+ nid = EC_GROUP_get_curve_name(ecgrp);
+ }
+
+ /* Translate NID back to name , just for kicks */
+ sname = OBJ_nid2sn(nid);
+ if (sname == NULL) sname = "(Unknown)";
+
+ /* Create new EC key and set as ECDH key */
+ if (NID_undef == nid || NULL == (ecdh = EC_KEY_new_by_curve_name(nid)))
+ {
+ /* Creating key failed, fall back on sane default */
+ ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
+ const char *source = (NULL == curve_name) ?
+ "extract curve from certificate" : "use supplied curve";
+ msg (D_TLS_DEBUG_LOW,
+ "Failed to %s (%s), using secp384r1 instead.", source, sname);
+ sname = OBJ_nid2sn(NID_secp384r1);
+ }
+
+ if (!SSL_CTX_set_tmp_ecdh(ctx->ctx, ecdh))
+ msg (M_SSLERR, "SSL_CTX_set_tmp_ecdh: cannot add curve");
+
+ msg (D_TLS_DEBUG_LOW, "ECDH curve %s added", sname);
+
+ EC_KEY_free(ecdh);
+#else
+ msg (M_DEBUG, "Your OpenSSL library was built without elliptic curve support."
+ " Skipping ECDH parameter loading.");
+#endif /* OPENSSL_NO_EC */
+}
+
int
tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file,
const char *pkcs12_file_inline,
SSL_CTX_free (tls_ctx.ctx);
}
+/*
+ * Show the Elliptic curves that are available for us to use
+ * in the OpenSSL library.
+ */
+void
+show_available_curves()
+{
+#ifndef OPENSSL_NO_EC
+ EC_builtin_curve *curves = NULL;
+ size_t crv_len = 0;
+ size_t n = 0;
+
+ crv_len = EC_get_builtin_curves(NULL, 0);
+
+ curves = OPENSSL_malloc((int)(sizeof(EC_builtin_curve) * crv_len));
+
+ if (curves == NULL)
+ msg (M_SSLERR, "Cannot create EC_builtin_curve object");
+ else
+ {
+ if (EC_get_builtin_curves(curves, crv_len))
+ {
+ printf ("Available Elliptic curves:\n");
+ for (n = 0; n < crv_len; n++)
+ {
+ const char *sname;
+ sname = OBJ_nid2sn(curves[n].nid);
+ if (sname == NULL) sname = "";
+
+ printf("%s\n", sname);
+ }
+ }
+ else
+ {
+ msg (M_SSLERR, "Cannot get list of builtin curves");
+ }
+ OPENSSL_free(curves);
+ }
+#else
+ msg (M_WARN, "Your OpenSSL library was built without elliptic curve support. "
+ "No curves available.");
+#endif
+}
+
void
get_highest_preference_tls_cipher (char *buf, int size)
{
(counter_type) 8 * mpi_size(&ctx->dhm_ctx->P));
}
+void
+tls_ctx_load_ecdh_params (struct tls_root_ctx *ctx, const char *curve_name
+ )
+{
+ if (NULL != curve_name)
+ msg(M_WARN, "WARNING: PolarSSL builds do not support specifying an ECDH "
+ "curve, using default curves.");
+}
+
int
tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file,
const char *pkcs12_file_inline,
tls_ctx_free(&tls_ctx);
}
+void
+show_available_curves (void)
+{
+ const ecp_curve_info *pcurve = ecp_curve_list();
+
+ if (NULL == pcurve)
+ msg (M_FATAL, "Cannot retrieve curve list from PolarSSL");
+
+ /* Print curve list */
+ printf ("Available Elliptic curves, listed in order of preference:\n\n");
+ while (POLARSSL_ECP_DP_NONE != pcurve->grp_id)
+ {
+ printf("%s\n", pcurve->name);
+ pcurve++;
+ }
+}
+
void
get_highest_preference_tls_cipher (char *buf, int size)
{