Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: QUIC: Add set_read_secret/set_write_secret support #60

Open
wants to merge 1 commit into
base: openssl-3.0.0+quic
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions doc/man3/SSL_CTX_set_quic_method.pod
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ The B<SSL_QUIC_METHOD> (B<struct ssl_quic_method_st>) describes the
QUIC methods.

struct ssl_quic_method_st {
int (*set_read_secret)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
const SSL_CIPHER *cipher, const uint8_t *secret,
int (*set_write_secret)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
const SSL_CIPHER *cipher, const uint8_t *secret,
int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
const uint8_t *read_secret,
const uint8_t *write_secret, size_t secret_len);
Expand All @@ -196,11 +200,25 @@ QUIC methods.
};
typedef struct ssl_quic_method_st SSL_QUIC_METHOD;

set_write_secret() configures the write secret for the given encryption level.
This function will always be called before an encryption level other than
B<ssl_encryption_initial> is used. It is an error to set both set_write_secret()
and set_encryption_secrets().

set_read_secret() configures the read secret for the given encryption level.
This function will always be called before an encryption level other than
B<ssl_encryption_initial> is used. It is an error to set both set_read_secret()
and set_encryption_secrets().

set_encryption_secrets() configures the read and write secrets for the given
encryption level. This function will always be called before an encryption
level other than B<ssl_encryption_initial> is used. Note, however, that
secrets for a level may be configured before TLS is ready to send or accept
data at that level.
level other than B<ssl_encryption_initial> is used.

The use of set_write_secret() and set_read_secret() is preferred to
set_encryption_secrets().

Note, however, that secrets for a level may be configured before TLS is ready
to send or accept data at that level.

When reading packets at a given level, the QUIC implementation must send
ACKs at the same level, so this function provides read and write secrets
Expand Down
6 changes: 6 additions & 0 deletions include/openssl/ssl.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -2531,6 +2531,12 @@ typedef enum ssl_encryption_level_t {
} OSSL_ENCRYPTION_LEVEL;

struct ssl_quic_method_st {
int (*set_read_secret)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secret_len);
int (*set_write_secret)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secret_len);
int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
const uint8_t *read_secret,
const uint8_t *write_secret, size_t secret_len);
Expand Down
96 changes: 68 additions & 28 deletions ssl/ssl_quic.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,24 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
return 1;
}

int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method)
int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *qm)
{
if (ctx->method->version != TLS_ANY_VERSION)
return 0;
ctx->quic_method = quic_method;

/*
* Ensure that both set_read_secret and set_write_secrt are both
* set to something or set to NULL.
* Also ensure that set_encryption_secret is NULL when read/write are
* non-NULL, and vice-versa
*/
if ((qm->set_read_secret == NULL && qm->set_write_secret != NULL)
|| (qm->set_read_secret != NULL && qm->set_write_secret == NULL)
|| (qm->set_read_secret != NULL && qm->set_encryption_secrets != NULL)
|| (qm->set_read_secret == NULL && qm->set_encryption_secrets == NULL))
return 0;

ctx->quic_method = qm;
ctx->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT;
return 1;
}
Expand All @@ -239,6 +252,7 @@ int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level)
uint8_t *s2c_secret = NULL;
size_t len;
const EVP_MD *md;
const SSL_CIPHER *c = NULL;

if (!SSL_IS_QUIC(ssl))
return 1;
Expand All @@ -261,7 +275,7 @@ int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level)
}

if (level == ssl_encryption_early_data) {
const SSL_CIPHER *c = SSL_SESSION_get0_cipher(ssl->session);
c = SSL_SESSION_get0_cipher(ssl->session);
if (ssl->early_data_state == SSL_EARLY_DATA_CONNECTING
&& ssl->max_early_data > 0
&& ssl->session->ext.max_early_data == 0) {
Expand All @@ -281,25 +295,25 @@ int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level)

md = ssl_md(ssl->ctx, c->algorithm2);
} else {
md = ssl_handshake_md(ssl);
if (md == NULL) {
/* May not have selected cipher, yet */
const SSL_CIPHER *c = NULL;

/*
* It probably doesn't make sense to use an (external) PSK session,
* but in theory some kinds of external session caches could be
* implemented using it, so allow psksession to be used as well as
* the regular session.
*/
if (ssl->session != NULL)
c = SSL_SESSION_get0_cipher(ssl->session);
else if (ssl->psksession != NULL)
c = SSL_SESSION_get0_cipher(ssl->psksession);
/*
* It probably doesn't make sense to use an (external) PSK session,
* but in theory some kinds of external session caches could be
* implemented using it, so allow psksession to be used as well as
* the regular session.
*/
if (ssl->session != NULL)
c = SSL_SESSION_get0_cipher(ssl->session);
else if (ssl->psksession != NULL)
c = SSL_SESSION_get0_cipher(ssl->psksession);

if (c != NULL)
md = SSL_CIPHER_get_handshake_digest(c);
if (c == NULL) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}

md = ssl_handshake_md(ssl);
if (md == NULL)
md = SSL_CIPHER_get_handshake_digest(c);
}

if ((len = EVP_MD_size(md)) <= 0) {
Expand All @@ -308,16 +322,42 @@ int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level)
}

if (ssl->server) {
if (!ssl->quic_method->set_encryption_secrets(ssl, level, c2s_secret,
s2c_secret, len)) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
if (ssl->quic_method->set_encryption_secrets == NULL) {
if (s2c_secret != NULL
&& !ssl->quic_method->set_write_secret(ssl, level, c, s2c_secret, len)) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if (c2s_secret != NULL
&& !ssl->quic_method->set_read_secret(ssl, level, c, c2s_secret, len)) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
} else {
if (!ssl->quic_method->set_encryption_secrets(ssl, level, c2s_secret,
s2c_secret, len)) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
} else {
if (!ssl->quic_method->set_encryption_secrets(ssl, level, s2c_secret,
c2s_secret, len)) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
if (ssl->quic_method->set_encryption_secrets == NULL) {
if (c2s_secret != NULL
&& !ssl->quic_method->set_write_secret(ssl, level, c, c2s_secret, len)) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if (s2c_secret != NULL
&& !ssl->quic_method->set_read_secret(ssl, level, c, s2c_secret, len)) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
} else {
if (!ssl->quic_method->set_encryption_secrets(ssl, level, s2c_secret,
c2s_secret, len)) {
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions test/sslapitest.c
Original file line number Diff line number Diff line change
Expand Up @@ -9488,6 +9488,8 @@ static int test_quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level, uin
}

static SSL_QUIC_METHOD quic_method = {
NULL,
NULL,
test_quic_set_encryption_secrets,
test_quic_add_handshake_data,
test_quic_flush_flight,
Expand Down