Skip to content
This repository was archived by the owner on Apr 10, 2025. It is now read-only.

Commit 2cdcec4

Browse files
tatsuhiro-ttmshort
authored andcommitted
QUIC: Add early data support (#8)
* QUIC: Add early data support This commit adds SSL_set_quic_early_data_enabled to add early data support to QUIC. * fixup! QUIC: Add early data support * fixup! QUIC: Add early data support
1 parent e2c7041 commit 2cdcec4

File tree

8 files changed

+331
-34
lines changed

8 files changed

+331
-34
lines changed

doc/man3/SSL_CTX_set_quic_method.pod

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ SSL_is_quic,
1717
SSL_get_peer_quic_transport_version,
1818
SSL_get_quic_transport_version,
1919
SSL_set_quic_transport_version,
20-
SSL_set_quic_use_legacy_codepoint
20+
SSL_set_quic_use_legacy_codepoint,
21+
SSL_set_quic_early_data_enabled
2122
- QUIC support
2223

2324
=head1 SYNOPSIS
@@ -47,6 +48,7 @@ SSL_set_quic_use_legacy_codepoint
4748
void SSL_set_quic_transport_version(SSL *ssl, int version);
4849
int SSL_get_quic_transport_version(const SSL *ssl);
4950
int SSL_get_peer_quic_transport_version(const SSL *ssl);
51+
void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled);
5052

5153
=head1 DESCRIPTION
5254

@@ -106,6 +108,12 @@ SSL_set_quic_transport_version().
106108
SSL_get_peer_quic_transport_version() returns the version the that was
107109
negotiated.
108110

111+
SSL_set_quic_early_data_enabled() enables QUIC early data if a nonzero
112+
value is passed. Client must set a resumed session before calling
113+
this function. Server must set 0xffffffffu to
114+
SSL_CTX_set_max_early_data() or SSL_set_max_early_data() so that a
115+
session ticket indicates that server is able to accept early data.
116+
109117
=head1 NOTES
110118

111119
These APIs are implementations of BoringSSL's QUIC APIs.

include/openssl/ssl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2488,6 +2488,8 @@ __owur int SSL_get_peer_quic_transport_version(const SSL *ssl);
24882488

24892489
int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c);
24902490

2491+
void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled);
2492+
24912493
# endif
24922494

24932495
# ifdef __cplusplus

ssl/ssl_lib.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3757,6 +3757,21 @@ int SSL_do_handshake(SSL *s)
37573757
ret = s->handshake_func(s);
37583758
}
37593759
}
3760+
#ifndef OPENSSL_NO_QUIC
3761+
if (SSL_IS_QUIC(s) && ret == 1) {
3762+
if (s->server) {
3763+
if (s->early_data_state == SSL_EARLY_DATA_ACCEPTING) {
3764+
s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
3765+
s->rwstate = SSL_READING;
3766+
ret = 0;
3767+
}
3768+
} else if (s->early_data_state == SSL_EARLY_DATA_CONNECTING) {
3769+
s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY;
3770+
s->rwstate = SSL_READING;
3771+
ret = 0;
3772+
}
3773+
}
3774+
#endif
37603775
return ret;
37613776
}
37623777

ssl/ssl_quic.c

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -258,24 +258,49 @@ int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level)
258258
return 1;
259259
}
260260

261-
md = ssl_handshake_md(ssl);
262-
if (md == NULL) {
263-
/* May not have selected cipher, yet */
264-
const SSL_CIPHER *c = NULL;
265-
266-
/*
267-
* It probably doesn't make sense to use an (external) PSK session,
268-
* but in theory some kinds of external session caches could be
269-
* implemented using it, so allow psksession to be used as well as
270-
* the regular session.
271-
*/
272-
if (ssl->session != NULL)
273-
c = SSL_SESSION_get0_cipher(ssl->session);
274-
else if (ssl->psksession != NULL)
261+
if (level == ssl_encryption_early_data) {
262+
const SSL_CIPHER *c = SSL_SESSION_get0_cipher(ssl->session);
263+
if (ssl->early_data_state == SSL_EARLY_DATA_CONNECTING
264+
&& ssl->max_early_data > 0
265+
&& ssl->session->ext.max_early_data == 0) {
266+
if (!ossl_assert(ssl->psksession != NULL
267+
&& ssl->max_early_data
268+
== ssl->psksession->ext.max_early_data)) {
269+
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR,
270+
SSL_F_QUIC_SET_ENCRYPTION_SECRETS,
271+
ERR_R_INTERNAL_ERROR);
272+
return 0;
273+
}
275274
c = SSL_SESSION_get0_cipher(ssl->psksession);
275+
}
276+
277+
if (c == NULL) {
278+
SSLfatal(ssl, SSL_AD_INTERNAL_ERROR,
279+
SSL_F_QUIC_SET_ENCRYPTION_SECRETS, ERR_R_INTERNAL_ERROR);
280+
return 0;
281+
}
282+
283+
md = ssl_md(c->algorithm2);
284+
} else {
285+
md = ssl_handshake_md(ssl);
286+
if (md == NULL) {
287+
/* May not have selected cipher, yet */
288+
const SSL_CIPHER *c = NULL;
276289

277-
if (c != NULL)
278-
md = SSL_CIPHER_get_handshake_digest(c);
290+
/*
291+
* It probably doesn't make sense to use an (external) PSK session,
292+
* but in theory some kinds of external session caches could be
293+
* implemented using it, so allow psksession to be used as well as
294+
* the regular session.
295+
*/
296+
if (ssl->session != NULL)
297+
c = SSL_SESSION_get0_cipher(ssl->session);
298+
else if (ssl->psksession != NULL)
299+
c = SSL_SESSION_get0_cipher(ssl->psksession);
300+
301+
if (c != NULL)
302+
md = SSL_CIPHER_get_handshake_digest(c);
303+
}
279304
}
280305

281306
if ((len = EVP_MD_size(md)) <= 0) {
@@ -334,3 +359,25 @@ int SSL_is_quic(SSL* ssl)
334359
{
335360
return SSL_IS_QUIC(ssl);
336361
}
362+
363+
void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled)
364+
{
365+
if (!SSL_is_quic(ssl) || !SSL_in_before(ssl))
366+
return;
367+
368+
if (!enabled) {
369+
ssl->early_data_state = SSL_EARLY_DATA_NONE;
370+
return;
371+
}
372+
373+
if (ssl->server) {
374+
ssl->early_data_state = SSL_EARLY_DATA_ACCEPTING;
375+
return;
376+
}
377+
378+
if ((ssl->session == NULL || ssl->session->ext.max_early_data == 0)
379+
&& ssl->psk_use_session_cb == NULL)
380+
return;
381+
382+
ssl->early_data_state = SSL_EARLY_DATA_CONNECTING;
383+
}

ssl/statem/statem_srvr.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,16 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
965965
SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_WRITE))
966966
/* SSLfatal() already called */
967967
return WORK_ERROR;
968+
969+
#ifndef OPENSSL_NO_QUIC
970+
if (SSL_IS_QUIC(s) && s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) {
971+
s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
972+
if (!s->method->ssl3_enc->change_cipher_state(
973+
s, SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_READ))
974+
/* SSLfatal() already called */
975+
return WORK_ERROR;
976+
}
977+
#endif
968978
}
969979
break;
970980

ssl/tls13_enc.c

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -468,21 +468,81 @@ static int quic_change_cipher_state(SSL *s, int which)
468468
int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE);
469469
int is_early = (which & SSL3_CC_EARLY);
470470

471-
md = ssl_handshake_md(s);
472-
if (!ssl3_digest_cached_records(s, 1)
473-
|| !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
474-
/* SSLfatal() already called */;
475-
goto err;
476-
}
471+
if (is_early) {
472+
EVP_MD_CTX *mdctx = NULL;
473+
long handlen;
474+
void *hdata;
475+
unsigned int hashlenui;
476+
const SSL_CIPHER *sslcipher = SSL_SESSION_get0_cipher(s->session);
477+
478+
handlen = BIO_get_mem_data(s->s3->handshake_buffer, &hdata);
479+
if (handlen <= 0) {
480+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
481+
SSL_R_BAD_HANDSHAKE_LENGTH);
482+
goto err;
483+
}
477484

478-
/* Ensure cast to size_t is safe */
479-
hashleni = EVP_MD_size(md);
480-
if (!ossl_assert(hashleni >= 0)) {
481-
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
482-
ERR_R_EVP_LIB);
483-
goto err;
485+
if (s->early_data_state == SSL_EARLY_DATA_CONNECTING
486+
&& s->max_early_data > 0 && s->session->ext.max_early_data == 0) {
487+
/*
488+
* If we are attempting to send early data, and we've decided to
489+
* actually do it but max_early_data in s->session is 0 then we
490+
* must be using an external PSK.
491+
*/
492+
if (!ossl_assert(s->psksession != NULL
493+
&& s->max_early_data
494+
== s->psksession->ext.max_early_data)) {
495+
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
496+
SSL_F_QUIC_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
497+
goto err;
498+
}
499+
sslcipher = SSL_SESSION_get0_cipher(s->psksession);
500+
}
501+
if (sslcipher == NULL) {
502+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
503+
SSL_R_BAD_PSK);
504+
goto err;
505+
}
506+
507+
/*
508+
* We need to calculate the handshake digest using the digest from
509+
* the session. We haven't yet selected our ciphersuite so we can't
510+
* use ssl_handshake_md().
511+
*/
512+
mdctx = EVP_MD_CTX_new();
513+
if (mdctx == NULL) {
514+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
515+
ERR_R_MALLOC_FAILURE);
516+
goto err;
517+
}
518+
md = ssl_md(sslcipher->algorithm2);
519+
if (md == NULL || !EVP_DigestInit_ex(mdctx, md, NULL)
520+
|| !EVP_DigestUpdate(mdctx, hdata, handlen)
521+
|| !EVP_DigestFinal_ex(mdctx, hash, &hashlenui)) {
522+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
523+
ERR_R_INTERNAL_ERROR);
524+
EVP_MD_CTX_free(mdctx);
525+
goto err;
526+
}
527+
hashlen = hashlenui;
528+
EVP_MD_CTX_free(mdctx);
529+
} else {
530+
md = ssl_handshake_md(s);
531+
if (!ssl3_digest_cached_records(s, 1)
532+
|| !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
533+
/* SSLfatal() already called */;
534+
goto err;
535+
}
536+
537+
/* Ensure cast to size_t is safe */
538+
hashleni = EVP_MD_size(md);
539+
if (!ossl_assert(hashleni >= 0)) {
540+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
541+
ERR_R_EVP_LIB);
542+
goto err;
543+
}
544+
hashlen = (size_t)hashleni;
484545
}
485-
hashlen = (size_t)hashleni;
486546

487547
if (is_client_read || is_server_write) {
488548
if (is_handshake) {
@@ -588,10 +648,12 @@ static int quic_change_cipher_state(SSL *s, int which)
588648
}
589649
}
590650

591-
if (s->server)
592-
s->quic_read_level = level;
593-
else
594-
s->quic_write_level = level;
651+
if (level != ssl_encryption_early_data) {
652+
if (s->server)
653+
s->quic_read_level = level;
654+
else
655+
s->quic_write_level = level;
656+
}
595657
}
596658

597659
ret = 1;

0 commit comments

Comments
 (0)