In 0RTT mode, reverify the server certificate before sending early data.
Bug: chromium:347402
Change-Id: I1442b595ed7296b9d9fe88357565f68e1ab80ffd
Reviewed-on: https://boringssl-review.googlesource.com/c/32644
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 24331ba..a41eb62 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -177,6 +177,7 @@
enum ssl_client_hs_state_t {
state_start_connect = 0,
state_enter_early_data,
+ state_early_reverify_server_certificate,
state_read_hello_verify_request,
state_read_server_hello,
state_tls13,
@@ -466,10 +467,26 @@
// Stash the early data session, so connection properties may be queried out
// of it.
- hs->in_early_data = true;
hs->early_session = UpRef(ssl->session);
- hs->can_early_write = true;
+ hs->state = state_early_reverify_server_certificate;
+ return ssl_hs_ok;
+}
+static enum ssl_hs_wait_t do_early_reverify_server_certificate(SSL_HANDSHAKE *hs) {
+ if (hs->ssl->ctx->reverify_on_resume) {
+ switch (ssl_reverify_peer_cert(hs)) {
+ case ssl_verify_ok:
+ break;
+ case ssl_verify_invalid:
+ return ssl_hs_error;
+ case ssl_verify_retry:
+ hs->state = state_early_reverify_server_certificate;
+ return ssl_hs_certificate_verify;
+ }
+ }
+
+ hs->in_early_data = true;
+ hs->can_early_write = true;
hs->state = state_read_server_hello;
return ssl_hs_early_return;
}
@@ -1692,6 +1709,9 @@
case state_enter_early_data:
ret = do_enter_early_data(hs);
break;
+ case state_early_reverify_server_certificate:
+ ret = do_early_reverify_server_certificate(hs);
+ break;
case state_read_hello_verify_request:
ret = do_read_hello_verify_request(hs);
break;
@@ -1775,6 +1795,8 @@
return "TLS client start_connect";
case state_enter_early_data:
return "TLS client enter_early_data";
+ case state_early_reverify_server_certificate:
+ return "TLS client early_reverify_server_certificate";
case state_read_hello_verify_request:
return "TLS client read_hello_verify_request";
case state_read_server_hello:
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index f453ac7..675a08a 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -704,6 +704,7 @@
// Reset the connection and try again at 1-RTT.
SSL_reset_early_data_reject(ssl.get());
+ GetTestState(ssl.get())->cert_verified = false;
// After reseting, the socket should report it is no longer in an early data
// state.
diff --git a/ssl/test/runner/fuzzer_mode.json b/ssl/test/runner/fuzzer_mode.json
index a840f37..1a154c2 100644
--- a/ssl/test/runner/fuzzer_mode.json
+++ b/ssl/test/runner/fuzzer_mode.json
@@ -44,8 +44,7 @@
"EarlyData-ALPNOmitted1-Server-*": "Trial decryption does not work with the NULL cipher.",
"EarlyData-ALPNOmitted2-Server-*": "Trial decryption does not work with the NULL cipher.",
"*-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
- "EarlyData-Reject-Client-*": "Trial decryption does not work with the NULL cipher.",
- "EarlyData-RejectTicket-Client-*": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-Reject*-Client-*": "Trial decryption does not work with the NULL cipher.",
"CustomExtensions-Server-EarlyDataOffered": "Trial decryption does not work with the NULL cipher.",
"Renegotiate-Client-BadExt*": "Fuzzer mode does not check renegotiation_info.",
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 9631e6e..9d5ba7f 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -908,7 +908,7 @@
for i, v := range buf {
if v != testMessage[i]^0xff {
- return fmt.Errorf("bad reply contents at byte %d", i)
+ return fmt.Errorf("bad reply contents at byte %d; got %q and wanted %q", i, buf, testMessage)
}
}
}
@@ -5016,6 +5016,165 @@
}, flags...),
resumeSession: true,
})
+ if vers.version >= VersionTLS13 {
+ tests = append(tests, testCase{
+ testType: testType,
+ name: "EarlyData-RejectTicket-Client-Reverify" + suffix,
+ config: Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ SessionTicketsDisabled: true,
+ },
+ tls13Variant: vers.tls13Variant,
+ resumeSession: true,
+ expectResumeRejected: true,
+ flags: append([]string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-reverify-on-resume",
+ "-on-resume-shim-writes-first",
+ // Session tickets are disabled, so the runner will not send a ticket.
+ "-on-retry-expect-no-session",
+ "-expect-reject-early-data",
+ }, flags...),
+ })
+ tests = append(tests, testCase{
+ testType: testType,
+ name: "EarlyData-Reject0RTT-Client-Reverify" + suffix,
+ config: Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ AlwaysRejectEarlyData: true,
+ },
+ },
+ tls13Variant: vers.tls13Variant,
+ resumeSession: true,
+ expectResumeRejected: false,
+ flags: append([]string{
+ "-enable-early-data",
+ "-expect-reject-early-data",
+ "-expect-ticket-supports-early-data",
+ "-reverify-on-resume",
+ "-on-resume-shim-writes-first",
+ }, flags...),
+ })
+ tests = append(tests, testCase{
+ testType: testType,
+ name: "EarlyData-RejectTicket-Client-ReverifyFails" + suffix,
+ config: Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ SessionTicketsDisabled: true,
+ },
+ tls13Variant: vers.tls13Variant,
+ resumeSession: true,
+ expectResumeRejected: true,
+ shouldFail: true,
+ expectedError: ":CERTIFICATE_VERIFY_FAILED:",
+ flags: append([]string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-reverify-on-resume",
+ "-on-resume-shim-writes-first",
+ // Session tickets are disabled, so the runner will not send a ticket.
+ "-on-retry-expect-no-session",
+ "-on-retry-verify-fail",
+ "-expect-reject-early-data",
+ }, flags...),
+ })
+ tests = append(tests, testCase{
+ testType: testType,
+ name: "EarlyData-Reject0RTT-Client-ReverifyFails" + suffix,
+ config: Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ AlwaysRejectEarlyData: true,
+ },
+ },
+ tls13Variant: vers.tls13Variant,
+ resumeSession: true,
+ expectResumeRejected: false,
+ shouldFail: true,
+ expectedError: ":CERTIFICATE_VERIFY_FAILED:",
+ flags: append([]string{
+ "-enable-early-data",
+ "-expect-reject-early-data",
+ "-expect-ticket-supports-early-data",
+ "-reverify-on-resume",
+ "-on-resume-shim-writes-first",
+ "-on-retry-verify-fail",
+ }, flags...),
+ })
+ // This tests that we only call the verify callback once.
+ tests = append(tests, testCase{
+ testType: testType,
+ name: "EarlyData-Accept0RTT-Client-Reverify" + suffix,
+ config: Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ ExpectEarlyData: [][]byte{[]byte("hello")},
+ },
+ },
+ tls13Variant: vers.tls13Variant,
+ resumeSession: true,
+ expectResumeRejected: false,
+ flags: append([]string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-reverify-on-resume",
+ "-on-resume-shim-writes-first",
+ }, flags...),
+ })
+ tests = append(tests, testCase{
+ testType: testType,
+ name: "EarlyData-Accept0RTT-Client-ReverifyFails" + suffix,
+ config: Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: vers.version,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ ExpectEarlyData: [][]byte{[]byte("hello")},
+ },
+ },
+ tls13Variant: vers.tls13Variant,
+ resumeSession: true,
+ shouldFail: true,
+ expectedError: ":CERTIFICATE_VERIFY_FAILED:",
+ flags: append([]string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-reverify-on-resume",
+ "-on-resume-verify-fail",
+ "-on-resume-shim-writes-first",
+ }, flags...),
+ })
+ }
}
}
}
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index fb56001..0d3e877 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -465,7 +465,7 @@
SSL *const ssl = hs->ssl;
// CertificateRequest may only be sent in non-resumption handshakes.
if (ssl->s3->session_reused) {
- if (ssl->ctx->reverify_on_resume) {
+ if (ssl->ctx->reverify_on_resume && !ssl->s3->early_data_accepted) {
hs->tls13_state = state_server_certificate_reverify;
return ssl_hs_ok;
}