-
Notifications
You must be signed in to change notification settings - Fork 61
QUIC: Add early data support #8
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -258,24 +258,49 @@ int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level) | |
| return 1; | ||
| } | ||
|
|
||
| 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) | ||
| if (level == ssl_encryption_early_data) { | ||
| const SSL_CIPHER *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) { | ||
| if (!ossl_assert(ssl->psksession != NULL | ||
| && ssl->max_early_data | ||
| == ssl->psksession->ext.max_early_data)) { | ||
| SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, | ||
| SSL_F_QUIC_SET_ENCRYPTION_SECRETS, | ||
| ERR_R_INTERNAL_ERROR); | ||
| return 0; | ||
| } | ||
| c = SSL_SESSION_get0_cipher(ssl->psksession); | ||
| } | ||
|
|
||
| if (c == NULL) { | ||
| SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, | ||
| SSL_F_QUIC_SET_ENCRYPTION_SECRETS, ERR_R_INTERNAL_ERROR); | ||
| return 0; | ||
| } | ||
|
|
||
| md = ssl_md(c->algorithm2); | ||
| } else { | ||
| md = ssl_handshake_md(ssl); | ||
| if (md == NULL) { | ||
| /* May not have selected cipher, yet */ | ||
| const SSL_CIPHER *c = NULL; | ||
|
|
||
| if (c != NULL) | ||
| md = SSL_CIPHER_get_handshake_digest(c); | ||
| /* | ||
| * 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 ((len = EVP_MD_size(md)) <= 0) { | ||
|
|
@@ -334,3 +359,25 @@ int SSL_is_quic(SSL* ssl) | |
| { | ||
| return SSL_IS_QUIC(ssl); | ||
| } | ||
|
|
||
| void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled) | ||
| { | ||
| if (!SSL_is_quic(ssl) || !SSL_in_before(ssl)) | ||
| return; | ||
|
|
||
| if (!enabled) { | ||
| ssl->early_data_state = SSL_EARLY_DATA_NONE; | ||
| return; | ||
| } | ||
|
|
||
| if (ssl->server) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we know that |
||
| ssl->early_data_state = SSL_EARLY_DATA_ACCEPTING; | ||
| return; | ||
| } | ||
|
|
||
| if ((ssl->session == NULL || ssl->session->ext.max_early_data == 0) | ||
| && ssl->psk_use_session_cb == NULL) | ||
| return; | ||
|
|
||
| ssl->early_data_state = SSL_EARLY_DATA_CONNECTING; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -468,21 +468,81 @@ static int quic_change_cipher_state(SSL *s, int which) | |
| int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE); | ||
| int is_early = (which & SSL3_CC_EARLY); | ||
|
|
||
| md = ssl_handshake_md(s); | ||
| if (!ssl3_digest_cached_records(s, 1) | ||
| || !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) { | ||
| /* SSLfatal() already called */; | ||
| goto err; | ||
| } | ||
| if (is_early) { | ||
| EVP_MD_CTX *mdctx = NULL; | ||
| long handlen; | ||
| void *hdata; | ||
| unsigned int hashlenui; | ||
| const SSL_CIPHER *sslcipher = SSL_SESSION_get0_cipher(s->session); | ||
|
|
||
| handlen = BIO_get_mem_data(s->s3->handshake_buffer, &hdata); | ||
| if (handlen <= 0) { | ||
| SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, | ||
| SSL_R_BAD_HANDSHAKE_LENGTH); | ||
| goto err; | ||
| } | ||
|
|
||
| /* Ensure cast to size_t is safe */ | ||
| hashleni = EVP_MD_size(md); | ||
| if (!ossl_assert(hashleni >= 0)) { | ||
| SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, | ||
| ERR_R_EVP_LIB); | ||
| goto err; | ||
| if (s->early_data_state == SSL_EARLY_DATA_CONNECTING | ||
| && s->max_early_data > 0 && s->session->ext.max_early_data == 0) { | ||
| /* | ||
| * If we are attempting to send early data, and we've decided to | ||
| * actually do it but max_early_data in s->session is 0 then we | ||
| * must be using an external PSK. | ||
| */ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not have minded if this comment was duplicated in quic_set_encryption_secrets(), but it's not actually needed and it also gave me a chance to independently re-derive the reasoning :) |
||
| if (!ossl_assert(s->psksession != NULL | ||
| && s->max_early_data | ||
| == s->psksession->ext.max_early_data)) { | ||
| SSLfatal(s, SSL_AD_INTERNAL_ERROR, | ||
| SSL_F_QUIC_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR); | ||
| goto err; | ||
| } | ||
| sslcipher = SSL_SESSION_get0_cipher(s->psksession); | ||
| } | ||
| if (sslcipher == NULL) { | ||
| SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, | ||
| SSL_R_BAD_PSK); | ||
| goto err; | ||
| } | ||
|
|
||
| /* | ||
| * We need to calculate the handshake digest using the digest from | ||
| * the session. We haven't yet selected our ciphersuite so we can't | ||
| * use ssl_handshake_md(). | ||
| */ | ||
| mdctx = EVP_MD_CTX_new(); | ||
| if (mdctx == NULL) { | ||
| SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, | ||
| ERR_R_MALLOC_FAILURE); | ||
| goto err; | ||
| } | ||
| md = ssl_md(sslcipher->algorithm2); | ||
| if (md == NULL || !EVP_DigestInit_ex(mdctx, md, NULL) | ||
| || !EVP_DigestUpdate(mdctx, hdata, handlen) | ||
| || !EVP_DigestFinal_ex(mdctx, hash, &hashlenui)) { | ||
| SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, | ||
| ERR_R_INTERNAL_ERROR); | ||
| EVP_MD_CTX_free(mdctx); | ||
| goto err; | ||
| } | ||
| hashlen = hashlenui; | ||
| EVP_MD_CTX_free(mdctx); | ||
| } else { | ||
| md = ssl_handshake_md(s); | ||
| if (!ssl3_digest_cached_records(s, 1) | ||
| || !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) { | ||
| /* SSLfatal() already called */; | ||
| goto err; | ||
| } | ||
|
|
||
| /* Ensure cast to size_t is safe */ | ||
| hashleni = EVP_MD_size(md); | ||
| if (!ossl_assert(hashleni >= 0)) { | ||
| SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, | ||
| ERR_R_EVP_LIB); | ||
| goto err; | ||
| } | ||
| hashlen = (size_t)hashleni; | ||
| } | ||
| hashlen = (size_t)hashleni; | ||
|
|
||
| if (is_client_read || is_server_write) { | ||
| if (is_handshake) { | ||
|
|
@@ -588,10 +648,12 @@ static int quic_change_cipher_state(SSL *s, int which) | |
| } | ||
| } | ||
|
|
||
| if (s->server) | ||
| s->quic_read_level = level; | ||
| else | ||
| s->quic_write_level = level; | ||
| if (level != ssl_encryption_early_data) { | ||
| if (s->server) | ||
| s->quic_read_level = level; | ||
| else | ||
| s->quic_write_level = level; | ||
| } | ||
| } | ||
|
|
||
| ret = 1; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to have it written down somewhere discoverable, why do we go into
SSL_EARLY_DATA_WRITE_RETRYhere rather thanSSL_EARLY_DATA_FINISHED_WRITING?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like
SSL_EARLY_DATA_FINISHED_WRITINGworks fine here.I think
SSL_EARLY_DATA_FINISHED_WRITINGis a better choice.