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

Commit 4201c4c

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 387bcbb commit 4201c4c

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
@@ -2580,6 +2580,8 @@ __owur int SSL_get_peer_quic_transport_version(const SSL *ssl);
25802580

25812581
int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c);
25822582

2583+
void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled);
2584+
25832585
# endif
25842586

25852587
# ifdef __cplusplus

ssl/ssl_lib.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3998,6 +3998,21 @@ int SSL_do_handshake(SSL *s)
39983998
ret = s->handshake_func(s);
39993999
}
40004000
}
4001+
#ifndef OPENSSL_NO_QUIC
4002+
if (SSL_IS_QUIC(s) && ret == 1) {
4003+
if (s->server) {
4004+
if (s->early_data_state == SSL_EARLY_DATA_ACCEPTING) {
4005+
s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
4006+
s->rwstate = SSL_READING;
4007+
ret = 0;
4008+
}
4009+
} else if (s->early_data_state == SSL_EARLY_DATA_CONNECTING) {
4010+
s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY;
4011+
s->rwstate = SSL_READING;
4012+
ret = 0;
4013+
}
4014+
}
4015+
#endif
40014016
return ret;
40024017
}
40034018

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
@@ -419,20 +419,76 @@ static int quic_change_cipher_state(SSL *s, int which)
419419
int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE);
420420
int is_early = (which & SSL3_CC_EARLY);
421421

422-
md = ssl_handshake_md(s);
423-
if (!ssl3_digest_cached_records(s, 1)
424-
|| !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
425-
/* SSLfatal() already called */;
426-
goto err;
427-
}
422+
if (is_early) {
423+
EVP_MD_CTX *mdctx = NULL;
424+
long handlen;
425+
void *hdata;
426+
unsigned int hashlenui;
427+
const SSL_CIPHER *sslcipher = SSL_SESSION_get0_cipher(s->session);
428+
429+
handlen = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
430+
if (handlen <= 0) {
431+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_HANDSHAKE_LENGTH);
432+
goto err;
433+
}
428434

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

437493
if (is_client_read || is_server_write) {
438494
if (is_handshake) {
@@ -538,10 +594,12 @@ static int quic_change_cipher_state(SSL *s, int which)
538594
}
539595
}
540596

541-
if (s->server)
542-
s->quic_read_level = level;
543-
else
544-
s->quic_write_level = level;
597+
if (level != ssl_encryption_early_data) {
598+
if (s->server)
599+
s->quic_read_level = level;
600+
else
601+
s->quic_write_level = level;
602+
}
545603
}
546604

547605
ret = 1;

0 commit comments

Comments
 (0)