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

Commit 0eb2b42

Browse files
tatsuhiro-ttmshort
authored andcommitted
QUIC: Add early data support (#11)
* QUIC: Add early data support This commit adds SSL_set_quic_early_data_enabled to add early data support to QUIC.
1 parent 47e8943 commit 0eb2b42

File tree

8 files changed

+327
-33
lines changed

8 files changed

+327
-33
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.in

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

25642564
int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c);
25652565

2566+
void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled);
2567+
25662568
# endif
25672569

25682570
# ifdef __cplusplus

ssl/ssl_lib.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3936,6 +3936,21 @@ int SSL_do_handshake(SSL *s)
39363936
ret = s->handshake_func(s);
39373937
}
39383938
}
3939+
#ifndef OPENSSL_NO_QUIC
3940+
if (SSL_IS_QUIC(s) && ret == 1) {
3941+
if (s->server) {
3942+
if (s->early_data_state == SSL_EARLY_DATA_ACCEPTING) {
3943+
s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
3944+
s->rwstate = SSL_READING;
3945+
ret = 0;
3946+
}
3947+
} else if (s->early_data_state == SSL_EARLY_DATA_CONNECTING) {
3948+
s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY;
3949+
s->rwstate = SSL_READING;
3950+
ret = 0;
3951+
}
3952+
}
3953+
#endif
39393954
return ret;
39403955
}
39413956

ssl/ssl_quic.c

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

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

276-
if (c != NULL)
277-
md = SSL_CIPHER_get_handshake_digest(c);
279+
md = ssl_md(ssl->ctx, c->algorithm2);
280+
} else {
281+
md = ssl_handshake_md(ssl);
282+
if (md == NULL) {
283+
/* May not have selected cipher, yet */
284+
const SSL_CIPHER *c = NULL;
285+
286+
/*
287+
* It probably doesn't make sense to use an (external) PSK session,
288+
* but in theory some kinds of external session caches could be
289+
* implemented using it, so allow psksession to be used as well as
290+
* the regular session.
291+
*/
292+
if (ssl->session != NULL)
293+
c = SSL_SESSION_get0_cipher(ssl->session);
294+
else if (ssl->psksession != NULL)
295+
c = SSL_SESSION_get0_cipher(ssl->psksession);
296+
297+
if (c != NULL)
298+
md = SSL_CIPHER_get_handshake_digest(c);
299+
}
278300
}
279301

280302
if ((len = EVP_MD_size(md)) <= 0) {
@@ -330,3 +352,25 @@ int SSL_is_quic(SSL* ssl)
330352
{
331353
return SSL_IS_QUIC(ssl);
332354
}
355+
356+
void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled)
357+
{
358+
if (!SSL_is_quic(ssl) || !SSL_in_before(ssl))
359+
return;
360+
361+
if (!enabled) {
362+
ssl->early_data_state = SSL_EARLY_DATA_NONE;
363+
return;
364+
}
365+
366+
if (ssl->server) {
367+
ssl->early_data_state = SSL_EARLY_DATA_ACCEPTING;
368+
return;
369+
}
370+
371+
if ((ssl->session == NULL || ssl->session->ext.max_early_data == 0)
372+
&& ssl->psk_use_session_cb == NULL)
373+
return;
374+
375+
ssl->early_data_state = SSL_EARLY_DATA_CONNECTING;
376+
}

ssl/statem/statem_srvr.c

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

ssl/tls13_enc.c

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -430,20 +430,76 @@ static int quic_change_cipher_state(SSL *s, int which)
430430
int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE);
431431
int is_early = (which & SSL3_CC_EARLY);
432432

433-
md = ssl_handshake_md(s);
434-
if (!ssl3_digest_cached_records(s, 1)
435-
|| !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
436-
/* SSLfatal() already called */;
437-
goto err;
438-
}
433+
if (is_early) {
434+
EVP_MD_CTX *mdctx = NULL;
435+
long handlen;
436+
void *hdata;
437+
unsigned int hashlenui;
438+
const SSL_CIPHER *sslcipher = SSL_SESSION_get0_cipher(s->session);
439+
440+
handlen = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
441+
if (handlen <= 0) {
442+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_HANDSHAKE_LENGTH);
443+
goto err;
444+
}
439445

440-
/* Ensure cast to size_t is safe */
441-
hashleni = EVP_MD_size(md);
442-
if (!ossl_assert(hashleni >= 0)) {
443-
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
444-
goto err;
446+
if (s->early_data_state == SSL_EARLY_DATA_CONNECTING
447+
&& s->max_early_data > 0
448+
&& s->session->ext.max_early_data == 0) {
449+
/*
450+
* If we are attempting to send early data, and we've decided to
451+
* actually do it but max_early_data in s->session is 0 then we
452+
* must be using an external PSK.
453+
*/
454+
if (!ossl_assert(s->psksession != NULL
455+
&& s->max_early_data ==
456+
s->psksession->ext.max_early_data)) {
457+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
458+
goto err;
459+
}
460+
sslcipher = SSL_SESSION_get0_cipher(s->psksession);
461+
}
462+
if (sslcipher == NULL) {
463+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_PSK);
464+
goto err;
465+
}
466+
467+
/*
468+
* We need to calculate the handshake digest using the digest from
469+
* the session. We haven't yet selected our ciphersuite so we can't
470+
* use ssl_handshake_md().
471+
*/
472+
mdctx = EVP_MD_CTX_new();
473+
if (mdctx == NULL) {
474+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
475+
goto err;
476+
}
477+
md = ssl_md(s->ctx, sslcipher->algorithm2);
478+
if (md == NULL || !EVP_DigestInit_ex(mdctx, md, NULL)
479+
|| !EVP_DigestUpdate(mdctx, hdata, handlen)
480+
|| !EVP_DigestFinal_ex(mdctx, hash, &hashlenui)) {
481+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
482+
EVP_MD_CTX_free(mdctx);
483+
goto err;
484+
}
485+
hashlen = hashlenui;
486+
EVP_MD_CTX_free(mdctx);
487+
} else {
488+
md = ssl_handshake_md(s);
489+
if (!ssl3_digest_cached_records(s, 1)
490+
|| !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
491+
/* SSLfatal() already called */;
492+
goto err;
493+
}
494+
495+
/* Ensure cast to size_t is safe */
496+
hashleni = EVP_MD_size(md);
497+
if (!ossl_assert(hashleni >= 0)) {
498+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
499+
goto err;
500+
}
501+
hashlen = (size_t)hashleni;
445502
}
446-
hashlen = (size_t)hashleni;
447503

448504
if (is_client_read || is_server_write) {
449505
if (is_handshake) {
@@ -549,10 +605,12 @@ static int quic_change_cipher_state(SSL *s, int which)
549605
}
550606
}
551607

552-
if (s->server)
553-
s->quic_read_level = level;
554-
else
555-
s->quic_write_level = level;
608+
if (level != ssl_encryption_early_data) {
609+
if (s->server)
610+
s->quic_read_level = level;
611+
else
612+
s->quic_write_level = level;
613+
}
556614
}
557615

558616
ret = 1;

0 commit comments

Comments
 (0)