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

Commit d491a95

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 1be4a0d commit d491a95

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

25882588
int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c);
25892589

2590+
void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled);
2591+
25902592
# endif
25912593

25922594
# ifdef __cplusplus

ssl/ssl_lib.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3930,6 +3930,21 @@ int SSL_do_handshake(SSL *s)
39303930
ret = s->handshake_func(s);
39313931
}
39323932
}
3933+
#ifndef OPENSSL_NO_QUIC
3934+
if (SSL_IS_QUIC(s) && ret == 1) {
3935+
if (s->server) {
3936+
if (s->early_data_state == SSL_EARLY_DATA_ACCEPTING) {
3937+
s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
3938+
s->rwstate = SSL_READING;
3939+
ret = 0;
3940+
}
3941+
} else if (s->early_data_state == SSL_EARLY_DATA_CONNECTING) {
3942+
s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY;
3943+
s->rwstate = SSL_READING;
3944+
ret = 0;
3945+
}
3946+
}
3947+
#endif
39333948
return ret;
39343949
}
39353950

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
@@ -493,20 +493,76 @@ static int quic_change_cipher_state(SSL *s, int which)
493493
int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE);
494494
int is_early = (which & SSL3_CC_EARLY);
495495

496-
md = ssl_handshake_md(s);
497-
if (!ssl3_digest_cached_records(s, 1)
498-
|| !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
499-
/* SSLfatal() already called */;
500-
goto err;
501-
}
496+
if (is_early) {
497+
EVP_MD_CTX *mdctx = NULL;
498+
long handlen;
499+
void *hdata;
500+
unsigned int hashlenui;
501+
const SSL_CIPHER *sslcipher = SSL_SESSION_get0_cipher(s->session);
502+
503+
handlen = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
504+
if (handlen <= 0) {
505+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_HANDSHAKE_LENGTH);
506+
goto err;
507+
}
502508

503-
/* Ensure cast to size_t is safe */
504-
hashleni = EVP_MD_size(md);
505-
if (!ossl_assert(hashleni >= 0)) {
506-
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
507-
goto err;
509+
if (s->early_data_state == SSL_EARLY_DATA_CONNECTING
510+
&& s->max_early_data > 0
511+
&& s->session->ext.max_early_data == 0) {
512+
/*
513+
* If we are attempting to send early data, and we've decided to
514+
* actually do it but max_early_data in s->session is 0 then we
515+
* must be using an external PSK.
516+
*/
517+
if (!ossl_assert(s->psksession != NULL
518+
&& s->max_early_data ==
519+
s->psksession->ext.max_early_data)) {
520+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
521+
goto err;
522+
}
523+
sslcipher = SSL_SESSION_get0_cipher(s->psksession);
524+
}
525+
if (sslcipher == NULL) {
526+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_PSK);
527+
goto err;
528+
}
529+
530+
/*
531+
* We need to calculate the handshake digest using the digest from
532+
* the session. We haven't yet selected our ciphersuite so we can't
533+
* use ssl_handshake_md().
534+
*/
535+
mdctx = EVP_MD_CTX_new();
536+
if (mdctx == NULL) {
537+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
538+
goto err;
539+
}
540+
md = ssl_md(s->ctx, sslcipher->algorithm2);
541+
if (md == NULL || !EVP_DigestInit_ex(mdctx, md, NULL)
542+
|| !EVP_DigestUpdate(mdctx, hdata, handlen)
543+
|| !EVP_DigestFinal_ex(mdctx, hash, &hashlenui)) {
544+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
545+
EVP_MD_CTX_free(mdctx);
546+
goto err;
547+
}
548+
hashlen = hashlenui;
549+
EVP_MD_CTX_free(mdctx);
550+
} else {
551+
md = ssl_handshake_md(s);
552+
if (!ssl3_digest_cached_records(s, 1)
553+
|| !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
554+
/* SSLfatal() already called */;
555+
goto err;
556+
}
557+
558+
/* Ensure cast to size_t is safe */
559+
hashleni = EVP_MD_size(md);
560+
if (!ossl_assert(hashleni >= 0)) {
561+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
562+
goto err;
563+
}
564+
hashlen = (size_t)hashleni;
508565
}
509-
hashlen = (size_t)hashleni;
510566

511567
if (is_client_read || is_server_write) {
512568
if (is_handshake) {
@@ -612,10 +668,12 @@ static int quic_change_cipher_state(SSL *s, int which)
612668
}
613669
}
614670

615-
if (s->server)
616-
s->quic_read_level = level;
617-
else
618-
s->quic_write_level = level;
671+
if (level != ssl_encryption_early_data) {
672+
if (s->server)
673+
s->quic_read_level = level;
674+
else
675+
s->quic_write_level = level;
676+
}
619677
}
620678

621679
ret = 1;

0 commit comments

Comments
 (0)