Simplify server-side ECDH curve selection.
There's multiple sets of APIs for selecting the curve. Fold away
SSL_OP_SINGLE_ECDH_USE as failing to set it is either a no-op or a bug. With
that gone, the consumer only needs to control the selection of a curve, with
key generation from then on being uniform. Also clean up the interaction
between the three API modes in s3_srvr.c; they were already mutually exclusive
due to tls1_check_ec_tmp_key.
This also removes all callers of EC_KEY_dup (and thus CRYPTO_dup_ex_data)
within the library.
Change-Id: I477b13bd9e77eb03d944ef631dd521639968dc8c
Reviewed-on: https://boringssl-review.googlesource.com/4200
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index d67d381..fe0e760 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -753,26 +753,14 @@
return ret;
case SSL_CTRL_SET_TMP_ECDH: {
- EC_KEY *ecdh = NULL;
-
- if (parg == NULL) {
+ /* For historical reasons, this API expects an |EC_KEY|, but only the
+ * group is used. */
+ EC_KEY *ec_key = (EC_KEY *)parg;
+ if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) {
OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_PASSED_NULL_PARAMETER);
return ret;
}
- if (!EC_KEY_up_ref((EC_KEY *)parg)) {
- OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_ECDH_LIB);
- return ret;
- }
- ecdh = (EC_KEY *)parg;
- if (!(s->options & SSL_OP_SINGLE_ECDH_USE) && !EC_KEY_generate_key(ecdh)) {
- EC_KEY_free(ecdh);
- OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_ECDH_LIB);
- return ret;
- }
- if (s->cert->ecdh_tmp != NULL) {
- EC_KEY_free(s->cert->ecdh_tmp);
- }
- s->cert->ecdh_tmp = ecdh;
+ s->cert->ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
ret = 1;
break;
}
@@ -1034,28 +1022,14 @@
return 0;
case SSL_CTRL_SET_TMP_ECDH: {
- EC_KEY *ecdh = NULL;
-
- if (parg == NULL) {
- OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_ECDH_LIB);
+ /* For historical reasons, this API expects an |EC_KEY|, but only the
+ * group is used. */
+ EC_KEY *ec_key = (EC_KEY *)parg;
+ if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
- ecdh = EC_KEY_dup((EC_KEY *)parg);
- if (ecdh == NULL) {
- OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_EC_LIB);
- return 0;
- }
- if (!(ctx->options & SSL_OP_SINGLE_ECDH_USE) &&
- !EC_KEY_generate_key(ecdh)) {
- EC_KEY_free(ecdh);
- OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_ECDH_LIB);
- return 0;
- }
-
- if (cert->ecdh_tmp != NULL) {
- EC_KEY_free(cert->ecdh_tmp);
- }
- cert->ecdh_tmp = ecdh;
+ ctx->cert->ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
return 1;
}
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index a3256d7..25482a2 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -1331,7 +1331,7 @@
int ssl3_send_server_key_exchange(SSL *s) {
DH *dh = NULL, *dhp;
- EC_KEY *ecdh = NULL, *ecdhp;
+ EC_KEY *ecdh = NULL;
uint8_t *encodedPoint = NULL;
int encodedlen = 0;
uint16_t curve_id = 0;
@@ -1415,19 +1415,21 @@
r[1] = dh->g;
r[2] = dh->pub_key;
} else if (alg_k & SSL_kECDHE) {
- const EC_GROUP *group;
-
- ecdhp = cert->ecdh_tmp;
- if (s->cert->ecdh_tmp_auto) {
- /* Get NID of appropriate shared curve */
- int nid = tls1_get_shared_curve(s);
- if (nid != NID_undef) {
- ecdhp = EC_KEY_new_by_curve_name(nid);
+ /* Determine the curve to use. */
+ int nid = NID_undef;
+ if (cert->ecdh_tmp_auto) {
+ nid = tls1_get_shared_curve(s);
+ } else if (cert->ecdh_nid != NID_undef) {
+ nid = cert->ecdh_nid;
+ } else if (cert->ecdh_tmp_cb != NULL) {
+ /* Note: |ecdh_tmp_cb| does NOT pass ownership of the result
+ * to the caller. */
+ EC_KEY *template = s->cert->ecdh_tmp_cb(s, 0, 1024);
+ if (template != NULL && EC_KEY_get0_group(template) != NULL) {
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(template));
}
- } else if (ecdhp == NULL && s->cert->ecdh_tmp_cb) {
- ecdhp = s->cert->ecdh_tmp_cb(s, 0, 1024);
}
- if (ecdhp == NULL) {
+ if (nid == NID_undef) {
al = SSL_AD_HANDSHAKE_FAILURE;
OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange,
SSL_R_MISSING_TMP_ECDH_KEY);
@@ -1439,42 +1441,19 @@
ERR_R_INTERNAL_ERROR);
goto err;
}
-
- /* Duplicate the ECDH structure. */
- if (ecdhp == NULL) {
- OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB);
+ ecdh = EC_KEY_new_by_curve_name(nid);
+ if (ecdh == NULL) {
goto err;
}
-
- if (s->cert->ecdh_tmp_auto) {
- ecdh = ecdhp;
- } else {
- ecdh = EC_KEY_dup(ecdhp);
- if (ecdh == NULL) {
- OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB);
- goto err;
- }
- }
-
s->s3->tmp.ecdh = ecdh;
- if (EC_KEY_get0_public_key(ecdh) == NULL ||
- EC_KEY_get0_private_key(ecdh) == NULL ||
- (s->options & SSL_OP_SINGLE_ECDH_USE)) {
- if (!EC_KEY_generate_key(ecdh)) {
- OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB);
- goto err;
- }
- }
- group = EC_KEY_get0_group(ecdh);
- if (group == NULL ||
- EC_KEY_get0_public_key(ecdh) == NULL ||
- EC_KEY_get0_private_key(ecdh) == NULL) {
+ if (!EC_KEY_generate_key(ecdh)) {
OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB);
goto err;
}
/* We only support ephemeral ECDH keys over named (not generic) curves. */
+ const EC_GROUP *group = EC_KEY_get0_group(ecdh);
if (!tls1_ec_nid2curve_id(&curve_id, EC_GROUP_get_curve_name(group))) {
OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange,
SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index fbe38ec..185c12b 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -212,13 +212,7 @@
}
ret->dh_tmp_cb = cert->dh_tmp_cb;
- if (cert->ecdh_tmp) {
- ret->ecdh_tmp = EC_KEY_dup(cert->ecdh_tmp);
- if (ret->ecdh_tmp == NULL) {
- OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_EC_LIB);
- goto err;
- }
- }
+ ret->ecdh_nid = cert->ecdh_nid;
ret->ecdh_tmp_cb = cert->ecdh_tmp_cb;
ret->ecdh_tmp_auto = cert->ecdh_tmp_auto;
@@ -324,9 +318,6 @@
if (c->dh_tmp) {
DH_free(c->dh_tmp);
}
- if (c->ecdh_tmp) {
- EC_KEY_free(c->ecdh_tmp);
- }
ssl_cert_clear_certs(c);
if (c->peer_sigalgs) {
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index c81e061..b21b521 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2011,7 +2011,8 @@
dh_tmp = (c->dh_tmp != NULL || c->dh_tmp_cb != NULL);
- have_ecdh_tmp = (c->ecdh_tmp || c->ecdh_tmp_cb || c->ecdh_tmp_auto);
+ have_ecdh_tmp = (c->ecdh_nid != NID_undef || c->ecdh_tmp_cb != NULL ||
+ c->ecdh_tmp_auto);
rsa_enc = ssl_has_key(s, SSL_PKEY_RSA_ENC);
rsa_sign = ssl_has_key(s, SSL_PKEY_RSA_SIGN);
have_ecc_cert = ssl_has_key(s, SSL_PKEY_ECC);
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 13fdd91..ed62fab 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -434,7 +434,9 @@
DH *dh_tmp;
DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
- EC_KEY *ecdh_tmp;
+ /* ecdh_nid, if not |NID_undef|, is the NID of the curve to use for ephemeral
+ * ECDH keys. */
+ int ecdh_nid;
/* Callback for generating ephemeral ECDH keys */
EC_KEY *(*ecdh_tmp_cb)(SSL *ssl, int is_export, int keysize);
/* Select ECDH parameters automatically */
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 01d9875..0009d13 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -606,23 +606,22 @@
}
int tls1_check_ec_tmp_key(SSL *s) {
- uint16_t curve_id;
- EC_KEY *ec = s->cert->ecdh_tmp;
-
if (s->cert->ecdh_tmp_auto) {
- /* Need a shared curve */
+ /* If using |ecdh_tmp_auto|, ECDH is acceptable if there is a shared
+ * curve. */
return tls1_get_shared_curve(s) != NID_undef;
}
- if (!ec) {
- if (s->cert->ecdh_tmp_cb) {
- return 1;
- }
- return 0;
+ if (s->cert->ecdh_nid != NID_undef) {
+ /* If the curve is preconfigured, ECDH is acceptable if the peer supports
+ * the curve. */
+ uint16_t curve_id;
+ return tls1_ec_nid2curve_id(&curve_id, s->cert->ecdh_nid) &&
+ tls1_check_curve_id(s, curve_id);
}
- return tls1_curve_params_from_ec_key(&curve_id, NULL, ec) &&
- tls1_check_curve_id(s, curve_id);
+ /* Otherwise, assume the callback will provide an acceptable curve. */
+ return s->cert->ecdh_tmp_cb != NULL;
}
/* List of supported signature algorithms and hashes. Should make this