Persist QUIC transport parameters in SSL_SESSION

Adds storage for currently understood remote QUIC transport parameters in the SSL_SESSION struct, including serialization and deserialization support. Sets defaults for these values on SSL_SESSION creation. This enables clients to remember and reuse required QUIC transport parameters for 0-RTT.

Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/28301)
This commit is contained in:
Andrew Dinh
2025-08-19 17:01:59 +07:00
parent 045a158e61
commit 093924d8c5
5 changed files with 144 additions and 5 deletions

View File

@@ -108,6 +108,8 @@ int ossl_quic_gen_rand_conn_id(OSSL_LIB_CTX *libctx, size_t len,
# define QUIC_STATELESS_RESET_TOKEN_LEN 16
# define QUIC_DEFAULT_MAX_STREAM_DATA (512 * 1024)
typedef struct {
unsigned char token[QUIC_STATELESS_RESET_TOKEN_LEN];
} QUIC_STATELESS_RESET_TOKEN;

View File

@@ -151,7 +151,6 @@ static QLOG *ch_get_qlog_cb(void *arg)
#define DEFAULT_INIT_CONN_RXFC_WND (768 * 1024)
#define DEFAULT_CONN_RXFC_MAX_WND_MUL 20
#define DEFAULT_INIT_STREAM_RXFC_WND (512 * 1024)
#define DEFAULT_STREAM_RXFC_MAX_WND_MUL 12
#define DEFAULT_INIT_CONN_MAX_STREAMS 100
@@ -206,9 +205,9 @@ static int ch_init(QUIC_CHANNEL *ch)
* Note: The TP we transmit governs what the peer can transmit and thus
* applies to the RXFC.
*/
ch->tx_init_max_stream_data_bidi_local = DEFAULT_INIT_STREAM_RXFC_WND;
ch->tx_init_max_stream_data_bidi_remote = DEFAULT_INIT_STREAM_RXFC_WND;
ch->tx_init_max_stream_data_uni = DEFAULT_INIT_STREAM_RXFC_WND;
ch->tx_init_max_stream_data_bidi_local = QUIC_DEFAULT_MAX_STREAM_DATA;
ch->tx_init_max_stream_data_bidi_remote = QUIC_DEFAULT_MAX_STREAM_DATA;
ch->tx_init_max_stream_data_uni = QUIC_DEFAULT_MAX_STREAM_DATA;
if (!ossl_quic_rxfc_init(&ch->conn_rxfc, NULL,
DEFAULT_INIT_CONN_RXFC_WND,
@@ -1812,6 +1811,53 @@ static int ch_on_transport_params(const unsigned char *params,
reason = TP_REASON_REQUIRED("RETRY_SCID");
goto malformed;
}
/* Store params in SSL_SESSION struct */
if (got_initial_max_data)
sc->session->quic_params.init_max_data = ch->conn_txfc.cwm;
if (got_initial_max_stream_data_bidi_local)
/*
* This is correct; the BIDI_LOCAL TP governs streams created by
* the endpoint which sends the TP, i.e., our peer.
*/
sc->session->quic_params.init_max_stream_data_bidi_local =
ch->rx_init_max_stream_data_bidi_remote;
if (got_initial_max_stream_data_bidi_remote)
/*
* This is correct; the BIDI_REMOTE TP governs streams created
* by the endpoint which receives the TP, i.e., us.
*/
sc->session->quic_params.init_max_stream_data_bidi_remote =
ch->rx_init_max_stream_data_bidi_local;
if (got_initial_max_stream_data_uni)
sc->session->quic_params.init_max_stream_data_uni = ch->rx_init_max_stream_data_uni;
if (got_initial_max_streams_bidi)
sc->session->quic_params.max_local_streams_bidi = ch->max_local_streams_bidi;
if (got_initial_max_streams_uni)
sc->session->quic_params.max_local_streams_uni = ch->max_local_streams_uni;
if (got_max_idle_timeout)
/* Use value for max idle timeout that server requested */
sc->session->quic_params.max_idle_timeout = ch->max_idle_timeout_remote_req;
if (got_max_udp_payload_size)
sc->session->quic_params.max_udp_payload_size = ch->rx_max_udp_payload_size;
if (got_active_conn_id_limit)
sc->session->quic_params.active_conn_id_limit = ch->rx_active_conn_id_limit;
/*
* RFC 9000 7.4.1: A client MUST NOT use remembered values for the
* following parameters: ack_delay_exponent, max_ack_delay,
* initial_source_connection_id, original_destination_connection_id,
* preferred_address, retry_source_connection_id, and
* stateless_reset_token.
*/
}
ch->got_remote_transport_params = 1;

View File

@@ -46,6 +46,17 @@ typedef struct {
ASN1_OCTET_STRING *ticket_appdata;
uint32_t kex_group;
ASN1_OCTET_STRING *peer_rpk;
#ifndef OPENSSL_NO_QUIC
uint64_t init_max_data;
uint64_t init_max_stream_data_bidi_local;
uint64_t init_max_stream_data_bidi_remote;
uint64_t init_max_stream_data_uni;
uint64_t max_local_streams_bidi;
uint64_t max_local_streams_uni;
uint64_t max_idle_timeout;
uint64_t max_udp_payload_size;
uint64_t active_conn_id_limit;
#endif
} SSL_SESSION_ASN1;
ASN1_SEQUENCE(SSL_SESSION_ASN1) = {
@@ -78,7 +89,18 @@ ASN1_SEQUENCE(SSL_SESSION_ASN1) = {
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, tlsext_max_fragment_len_mode, ZUINT32, 17),
ASN1_EXP_OPT(SSL_SESSION_ASN1, ticket_appdata, ASN1_OCTET_STRING, 18),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, kex_group, UINT32, 19),
ASN1_EXP_OPT(SSL_SESSION_ASN1, peer_rpk, ASN1_OCTET_STRING, 20)
ASN1_EXP_OPT(SSL_SESSION_ASN1, peer_rpk, ASN1_OCTET_STRING, 20),
#ifndef OPENSSL_NO_QUIC
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, init_max_data, ZUINT64, 21),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, init_max_stream_data_bidi_local, ZUINT64, 22),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, init_max_stream_data_bidi_remote, ZUINT64, 23),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, init_max_stream_data_uni, ZUINT64, 24),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, max_local_streams_bidi, ZUINT64, 25),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, max_local_streams_uni, ZUINT64, 26),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, max_idle_timeout, ZUINT64, 27),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, max_udp_payload_size, ZUINT64, 28),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, active_conn_id_limit, ZUINT64, 29),
#endif
} static_ASN1_SEQUENCE_END(SSL_SESSION_ASN1)
IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(SSL_SESSION_ASN1)
@@ -217,6 +239,18 @@ int i2d_SSL_SESSION(const SSL_SESSION *in, unsigned char **pp)
ssl_session_oinit(&as.ticket_appdata, &ticket_appdata,
in->ticket_appdata, in->ticket_appdata_len);
#ifndef OPENSSL_NO_QUIC
as.init_max_data = in->quic_params.init_max_data;
as.init_max_stream_data_bidi_local = in->quic_params.init_max_stream_data_bidi_local;
as.init_max_stream_data_bidi_remote = in->quic_params.init_max_stream_data_bidi_remote;
as.init_max_stream_data_uni = in->quic_params.init_max_stream_data_uni;
as.max_local_streams_bidi = in->quic_params.max_local_streams_bidi;
as.max_local_streams_uni = in->quic_params.max_local_streams_uni;
as.max_idle_timeout = in->quic_params.max_idle_timeout;
as.max_udp_payload_size = in->quic_params.max_udp_payload_size;
as.active_conn_id_limit = in->quic_params.active_conn_id_limit;
#endif
ret = i2d_SSL_SESSION_ASN1(&as, pp);
OPENSSL_free(peer_rpk.data);
return ret;
@@ -418,6 +452,18 @@ SSL_SESSION *d2i_SSL_SESSION_ex(SSL_SESSION **a, const unsigned char **pp,
ret->ticket_appdata_len = 0;
}
#ifndef OPENSSL_NO_QUIC
ret->quic_params.init_max_data = as->init_max_data;
ret->quic_params.init_max_stream_data_bidi_local = as->init_max_stream_data_bidi_local;
ret->quic_params.init_max_stream_data_bidi_remote = as->init_max_stream_data_bidi_remote;
ret->quic_params.init_max_stream_data_uni = as->init_max_stream_data_uni;
ret->quic_params.max_local_streams_bidi = as->max_local_streams_bidi;
ret->quic_params.max_local_streams_uni = as->max_local_streams_uni;
ret->quic_params.max_idle_timeout = as->max_idle_timeout;
ret->quic_params.max_udp_payload_size = as->max_udp_payload_size;
ret->quic_params.active_conn_id_limit = as->active_conn_id_limit;
#endif
M_ASN1_free_of(as, SSL_SESSION_ASN1);
if ((a != NULL) && (*a == NULL))

View File

@@ -556,6 +556,21 @@ struct ssl_session_st {
uint32_t flags;
SSL_CTX *owner;
# ifndef OPENSSL_NO_QUIC
/* Remote QUIC transport parameters for 0-RTT, for client only */
struct {
uint64_t init_max_data;
uint64_t init_max_stream_data_bidi_local;
uint64_t init_max_stream_data_bidi_remote;
uint64_t init_max_stream_data_uni;
uint64_t max_local_streams_bidi;
uint64_t max_local_streams_uni;
uint64_t max_idle_timeout;
uint64_t max_udp_payload_size;
uint64_t active_conn_id_limit;
} quic_params;
# endif
/*
* These are used to make removal of session-ids more efficient and to
* implement a maximum cache size. Access requires protection of ctx->lock.

View File

@@ -20,6 +20,9 @@
#include "internal/ssl_unwrap.h"
#include "ssl_local.h"
#include "statem/statem_local.h"
#ifndef OPENSSL_NO_QUIC
# include "internal/quic_types.h"
#endif
static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s);
static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s);
@@ -121,6 +124,19 @@ SSL_SESSION *SSL_SESSION_new(void)
return NULL;
}
#ifndef OPENSSL_NO_QUIC
/* Set QUIC params to default values */
ss->quic_params.init_max_data = 0;
ss->quic_params.init_max_stream_data_bidi_local = QUIC_DEFAULT_MAX_STREAM_DATA;
ss->quic_params.init_max_stream_data_bidi_remote = QUIC_DEFAULT_MAX_STREAM_DATA;
ss->quic_params.init_max_stream_data_uni = QUIC_DEFAULT_MAX_STREAM_DATA;
ss->quic_params.max_local_streams_bidi = 0;
ss->quic_params.max_local_streams_uni = 0;
ss->quic_params.max_idle_timeout = QUIC_DEFAULT_IDLE_TIMEOUT;
ss->quic_params.max_udp_payload_size = QUIC_MIN_INITIAL_DGRAM_LEN;
ss->quic_params.active_conn_id_limit = QUIC_MIN_ACTIVE_CONN_ID_LIMIT;
#endif
if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data)) {
CRYPTO_FREE_REF(&ss->references);
OPENSSL_free(ss);
@@ -261,6 +277,20 @@ static SSL_SESSION *ssl_session_dup_intern(const SSL_SESSION *src, int ticket)
goto err;
}
#ifndef OPENSSL_NO_QUIC
dest->quic_params.init_max_data = src->quic_params.init_max_data;
dest->quic_params.init_max_stream_data_bidi_local =
src->quic_params.init_max_stream_data_bidi_local;
dest->quic_params.init_max_stream_data_bidi_remote =
src->quic_params.init_max_stream_data_bidi_remote;
dest->quic_params.init_max_stream_data_uni = src->quic_params.init_max_stream_data_uni;
dest->quic_params.max_local_streams_bidi = src->quic_params.max_local_streams_bidi;
dest->quic_params.max_local_streams_uni = src->quic_params.max_local_streams_uni;
dest->quic_params.max_idle_timeout = src->quic_params.max_idle_timeout;
dest->quic_params.max_udp_payload_size = src->quic_params.max_udp_payload_size;
dest->quic_params.active_conn_id_limit = src->quic_params.active_conn_id_limit;
#endif
return dest;
err:
SSL_SESSION_free(dest);