Fix changing of the cipher state when dealing with early data
Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2737)
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index a774452..2f62f4b 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1657,6 +1657,7 @@
{
if (s->early_data_state == SSL_EARLY_DATA_READING) {
s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
+ ossl_statem_finish_early_data(s);
return 1;
}
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 3175009..f849278 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1027,6 +1027,7 @@
unsigned char client_finished_secret[EVP_MAX_MD_SIZE];
unsigned char server_finished_secret[EVP_MAX_MD_SIZE];
unsigned char server_finished_hash[EVP_MAX_MD_SIZE];
+ unsigned char handshake_traffic_hash[EVP_MAX_MD_SIZE];
unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE];
unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE];
EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h
index 5bb74d0..c0c9cab 100644
--- a/ssl/statem/statem.h
+++ b/ssl/statem/statem.h
@@ -129,3 +129,4 @@
void ossl_statem_set_sctp_read_sock(SSL *s, int read_sock);
__owur int ossl_statem_in_sctp_read_sock(SSL *s);
#endif
+int ossl_statem_finish_early_data(SSL *s);
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 3d2d39b..0f68ddf 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -787,10 +787,13 @@
if (SSL_IS_TLS13(s)) {
if (!s->method->ssl3_enc->setup_key_block(s)
|| !s->method->ssl3_enc->change_cipher_state(s,
- SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_WRITE)
- || !s->method->ssl3_enc->change_cipher_state(s,
+ SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_WRITE))
+ return WORK_ERROR;
+
+ if (s->ext.early_data != SSL_EARLY_DATA_ACCEPTED
+ && !s->method->ssl3_enc->change_cipher_state(s,
SSL3_CC_HANDSHAKE |SSL3_CHANGE_CIPHER_SERVER_READ))
- return WORK_ERROR;
+ return WORK_ERROR;
}
break;
@@ -1104,6 +1107,15 @@
return WORK_FINISHED_CONTINUE;
}
+int ossl_statem_finish_early_data(SSL *s)
+{
+ if (!s->method->ssl3_enc->change_cipher_state(s,
+ SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_READ))
+ return 0;
+
+ return 1;
+}
+
#ifndef OPENSSL_NO_SRP
static int ssl_check_srp_ext_ClientHello(SSL *s, int *al)
{
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index 2dc7dad..d42be60 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -422,6 +422,16 @@
label = client_handshake_traffic;
labellen = sizeof(client_handshake_traffic) - 1;
log_label = CLIENT_HANDSHAKE_LABEL;
+ /*
+ * The hanshake hash used for the server read handshake traffic
+ * secret is the same as the hash for the server write handshake
+ * traffic secret. However, if we processed early data then we delay
+ * changing the server read cipher state until later, and the
+ * handshake hashes have moved on. Therefore we use the value saved
+ * earlier when we did the server write change cipher state.
+ */
+ if (s->server)
+ hash = s->handshake_traffic_hash;
} else {
insecret = s->master_secret;
label = client_application_traffic;
@@ -469,6 +479,9 @@
if (label == server_application_traffic)
memcpy(s->server_finished_hash, hashval, hashlen);
+ if (s->server && label == server_handshake_traffic)
+ memcpy(s->handshake_traffic_hash, hashval, hashlen);
+
if (label == client_application_traffic) {
/*
* We also create the resumption master secret, but this time use the