Commit 4e8a850294 for openssl.org
commit 4e8a850294d3860e39c2e7feda6b552fd06db80b
Author: sftcd <stephen.farrell@cs.tcd.ie>
Date: Fri May 2 12:58:30 2025 +0100
ECH client support for sending multiple key shares
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/27540)
diff --git a/include/internal/ssl.h b/include/internal/ssl.h
index d56ed6e6dd..c9e87d417b 100644
--- a/include/internal/ssl.h
+++ b/include/internal/ssl.h
@@ -23,4 +23,9 @@ int ossl_ssl_get_error(const SSL *s, int i, int check_err);
/* Set if this is our QUIC handshake layer */
#define TLS1_FLAGS_QUIC_INTERNAL 0x4000
+/* We limit the number of key shares sent */
+#ifndef OPENSSL_CLIENT_MAX_KEY_SHARES
+#define OPENSSL_CLIENT_MAX_KEY_SHARES 4
+#endif
+
#endif
diff --git a/ssl/ech/ech_internal.c b/ssl/ech/ech_internal.c
index cac6098621..2f43877a83 100644
--- a/ssl/ech/ech_internal.c
+++ b/ssl/ech/ech_internal.c
@@ -170,6 +170,20 @@ void ossl_ech_ctx_clear(OSSL_ECH_CTX *ce)
return;
}
+static void ech_free_stashed_key_shares(OSSL_ECH_CONN *ec)
+{
+ size_t i;
+
+ if (ec == NULL)
+ return;
+ for (i = 0; i != ec->num_ks_pkey; i++) {
+ EVP_PKEY_free(ec->ks_pkey[i]);
+ ec->ks_pkey[i] = NULL;
+ }
+ ec->num_ks_pkey = 0;
+ return;
+}
+
void ossl_ech_conn_clear(OSSL_ECH_CONN *ec)
{
if (ec == NULL)
@@ -185,8 +199,8 @@ void ossl_ech_conn_clear(OSSL_ECH_CONN *ec)
OPENSSL_free(ec->returned);
OPENSSL_free(ec->pub);
OSSL_HPKE_CTX_free(ec->hpke_ctx);
- EVP_PKEY_free(ec->tmp_pkey);
OPENSSL_free(ec->encoded_inner);
+ ech_free_stashed_key_shares(ec);
return;
}
@@ -826,15 +840,11 @@ int ossl_ech_swaperoo(SSL_CONNECTION *s)
#ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_ptranscript(s, "ech_swaperoo, b4");
#endif
- /* un-stash inner key share */
- if (s->ext.ech.tmp_pkey == NULL) {
+ /* un-stash inner key share(s) */
+ if (ossl_ech_unstash_keyshares(s) != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
- EVP_PKEY_free(s->s3.tmp.pkey);
- s->s3.tmp.pkey = s->ext.ech.tmp_pkey;
- s->s3.group_id = s->ext.ech.group_id;
- s->ext.ech.tmp_pkey = NULL;
/*
* When not doing HRR... fix up the transcript to reflect the inner CH.
* If there's a client hello at the start of the buffer, then that's
@@ -1165,4 +1175,38 @@ int ossl_ech_intbuf_fetch(SSL_CONNECTION *s, unsigned char **buf, size_t *blen)
*blen = s->ext.ech.transbuf_len;
return 1;
}
+
+int ossl_ech_stash_keyshares(SSL_CONNECTION *s)
+{
+ size_t i;
+
+ ech_free_stashed_key_shares(&s->ext.ech);
+ for (i = 0; i != s->s3.tmp.num_ks_pkey; i++) {
+ s->ext.ech.ks_pkey[i] = s->s3.tmp.ks_pkey[i];
+ if (EVP_PKEY_up_ref(s->ext.ech.ks_pkey[i]) != 1)
+ return 0;
+ s->ext.ech.ks_group_id[i] = s->s3.tmp.ks_group_id[i];
+ }
+ s->ext.ech.num_ks_pkey = s->s3.tmp.num_ks_pkey;
+ return 1;
+}
+
+int ossl_ech_unstash_keyshares(SSL_CONNECTION *s)
+{
+ size_t i;
+
+ for (i = 0; i != s->s3.tmp.num_ks_pkey; i++) {
+ EVP_PKEY_free(s->s3.tmp.ks_pkey[i]);
+ s->s3.tmp.ks_pkey[i] = NULL;
+ }
+ for (i = 0; i != s->ext.ech.num_ks_pkey; i++) {
+ s->s3.tmp.ks_pkey[i] = s->ext.ech.ks_pkey[i];
+ if (EVP_PKEY_up_ref(s->s3.tmp.ks_pkey[i]) != 1)
+ return 0;
+ s->s3.tmp.ks_group_id[i] = s->ext.ech.ks_group_id[i];
+ }
+ s->s3.tmp.num_ks_pkey = s->ext.ech.num_ks_pkey;
+ ech_free_stashed_key_shares(&s->ext.ech);
+ return 1;
+}
#endif
diff --git a/ssl/ech/ech_local.h b/ssl/ech/ech_local.h
index 553de8c9c3..426d42e360 100644
--- a/ssl/ech/ech_local.h
+++ b/ssl/ech/ech_local.h
@@ -58,7 +58,6 @@
* defined in a header file I could find.
*/
#define CLIENT_VERSION_LEN 2
-
#endif
/*
@@ -219,8 +218,10 @@ typedef struct ossl_ech_conn_st {
* keep and swap over IFF ECH has succeeded. Same names chosen as are
* used in SSL_CONNECTION
*/
- EVP_PKEY *tmp_pkey; /* client's key share for inner */
- int group_id; /* key share group */
+ EVP_PKEY *ks_pkey[OPENSSL_CLIENT_MAX_KEY_SHARES];
+ /* The IDs of the keyshare keys */
+ uint16_t ks_group_id[OPENSSL_CLIENT_MAX_KEY_SHARES];
+ size_t num_ks_pkey; /* how many keyshares are there */
unsigned char client_random[SSL3_RANDOM_SIZE]; /* CH random */
} OSSL_ECH_CONN;
@@ -306,6 +307,8 @@ int ossl_ech_intbuf_add(SSL_CONNECTION *s, const unsigned char *buf,
int ossl_ech_intbuf_fetch(SSL_CONNECTION *s, unsigned char **buf, size_t *blen);
size_t ossl_ech_calc_padding(SSL_CONNECTION *s, OSSL_ECHSTORE_ENTRY *ee,
size_t encoded_len);
+int ossl_ech_stash_keyshares(SSL_CONNECTION *s);
+int ossl_ech_unstash_keyshares(SSL_CONNECTION *s);
#endif
#endif
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index 00ceab915d..daf195fd03 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -795,11 +795,6 @@ typedef struct {
#define TLS_GROUP_FFDHE_FOR_TLS1_3 (TLS_GROUP_FFDHE | TLS_GROUP_ONLY_FOR_TLS1_3)
-/* We limit the number of key shares sent */
-#ifndef OPENSSL_CLIENT_MAX_KEY_SHARES
-#define OPENSSL_CLIENT_MAX_KEY_SHARES 4
-#endif
-
struct ssl_ctx_st {
OSSL_LIB_CTX *libctx;
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index 52901c3d8a..3b03a71948 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -832,18 +832,6 @@ static int add_key_share(SSL_CONNECTION *s, WPACKET *pkt, unsigned int group_id,
goto err;
}
-#ifndef OPENSSL_NO_ECH
- if (s->ext.ech.ch_depth == 1) { /* stash inner */
- if (EVP_PKEY_up_ref(key_share_key) != 1) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- EVP_PKEY_free(s->ext.ech.tmp_pkey);
- s->ext.ech.tmp_pkey = key_share_key;
- s->ext.ech.group_id = group_id;
- }
-#endif
-
/* For backward compatibility, we use the first valid group to add a key share */
if (loop_num == 0) {
s->s3.tmp.pkey = key_share_key;
@@ -856,7 +844,6 @@ static int add_key_share(SSL_CONNECTION *s, WPACKET *pkt, unsigned int group_id,
s->s3.tmp.num_ks_pkey++;
OPENSSL_free(encoded_pubkey);
-
return 1;
err:
if (key_share_key != s->s3.tmp.ks_pkey[loop_num])
@@ -947,6 +934,14 @@ EXT_RETURN tls_construct_ctos_key_share(SSL_CONNECTION *s, WPACKET *pkt,
return EXT_RETURN_FAIL;
}
+#ifndef OPENSSL_NO_ECH
+ /* stash inner key shares */
+ if (s->ext.ech.ch_depth == 1 && ossl_ech_stash_keyshares(s) != 1) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+#endif
+
if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;