diff --git a/gtests/ssl_gtest/ssl_0rtt_unittest.cc b/gtests/ssl_gtest/ssl_0rtt_unittest.cc index 42966c19cf..f873d265bd 100644 --- a/gtests/ssl_gtest/ssl_0rtt_unittest.cc +++ b/gtests/ssl_gtest/ssl_0rtt_unittest.cc @@ -211,6 +211,106 @@ TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) { SendReceive(); } +// Make sure that a session ticket sent well after the original handshake +// can be used for 0-RTT. +// Stream because DTLS doesn't support SSL_SendSessionTicket. +TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicket) { + // Use a small-ish anti-replay window. + ResetAntiReplay(100 * PR_USEC_PER_MSEC); + RolloverAntiReplay(); + + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + server_->Set0RttEnabled(true); + Connect(); + CheckKeys(); + + // Now move time forward 30s and send a ticket. + AdvanceTime(30 * PR_USEC_PER_SEC); + EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); + SendReceive(); + Reset(); + StartConnect(); + + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ExpectResumption(RESUME_TICKET); + ZeroRttSendReceive(true, true); + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + +// Check that post-handshake authentication with a long RTT doesn't +// make things worse. +TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicketPha) { + // Use a small-ish anti-replay window. + ResetAntiReplay(100 * PR_USEC_PER_MSEC); + RolloverAntiReplay(); + + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + server_->Set0RttEnabled(true); + client_->SetupClientAuth(); + client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); + Connect(); + CheckKeys(); + + // Add post-handshake authentication, with some added delays. + AdvanceTime(10 * PR_USEC_PER_SEC); + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())); + AdvanceTime(10 * PR_USEC_PER_SEC); + server_->SendData(50); + client_->ReadBytes(50); + client_->SendData(50); + server_->ReadBytes(50); + + AdvanceTime(10 * PR_USEC_PER_SEC); + EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); + server_->SendData(100); + client_->ReadBytes(100); + Reset(); + StartConnect(); + + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ExpectResumption(RESUME_TICKET); + ZeroRttSendReceive(true, true); + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + +// Same, but with client authentication on the first connection. +TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicketClientAuth) { + // Use a small-ish anti-replay window. + ResetAntiReplay(100 * PR_USEC_PER_MSEC); + RolloverAntiReplay(); + + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + server_->Set0RttEnabled(true); + Connect(); + CheckKeys(); + + // Now move time forward 30s and send a ticket. + AdvanceTime(30 * PR_USEC_PER_SEC); + EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); + SendReceive(); + Reset(); + StartConnect(); + + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ExpectResumption(RESUME_TICKET); + ZeroRttSendReceive(true, true); + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) { SetupForZeroRtt(); client_->Set0RttEnabled(true); diff --git a/gtests/ssl_gtest/tls_connect.cc b/gtests/ssl_gtest/tls_connect.cc index 915f3705a4..9b7f9b6d8c 100644 --- a/gtests/ssl_gtest/tls_connect.cc +++ b/gtests/ssl_gtest/tls_connect.cc @@ -107,7 +107,7 @@ std::string VersionString(uint16_t version) { } // The default anti-replay window for tests. Tests that rely on a different -// value call SSL_InitAntiReplay directly. +// value call ResetAntiReplay directly. static PRTime kAntiReplayWindow = 100 * PR_USEC_PER_SEC; TlsConnectTestBase::TlsConnectTestBase(SSLProtocolVariant variant, diff --git a/lib/ssl/ssl3exthandle.c b/lib/ssl/ssl3exthandle.c index 8f23bf88d7..cb46982538 100644 --- a/lib/ssl/ssl3exthandle.c +++ b/lib/ssl/ssl3exthandle.c @@ -796,7 +796,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss, const NewSessionTicket *ticket, * This is compared to the expected time, which should differ only as a * result of clock errors or errors in the RTT estimate. */ - ticketAgeBaseline = (ssl_Time(ss) - ss->ssl3.hs.serverHelloTime) / PR_USEC_PER_MSEC; + ticketAgeBaseline = ss->ssl3.hs.rttEstimate / PR_USEC_PER_MSEC; ticketAgeBaseline -= ticket->ticket_age_add; rv = sslBuffer_AppendNumber(&plaintext, ticketAgeBaseline, 4); if (rv != SECSuccess) diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h index dae2a27031..1a947c8421 100644 --- a/lib/ssl/sslimpl.h +++ b/lib/ssl/sslimpl.h @@ -713,17 +713,23 @@ typedef struct SSL3HandshakeStateStr { PRBool receivedCcs; /* A server received ChangeCipherSpec * before the handshake started. */ PRBool clientCertRequested; /* True if CertificateRequest received. */ + PRBool endOfFlight; /* Processed a full flight (DTLS 1.3). */ ssl3KEADef kea_def_mutable; /* Used to hold the writable kea_def * we use for TLS 1.3 */ - PRTime serverHelloTime; /* Time the ServerHello flight was sent. */ PRUint16 ticketNonce; /* A counter we use for tickets. */ SECItem fakeSid; /* ... (server) the SID the client used. */ - PRBool endOfFlight; /* Processed a full flight (DTLS 1.3). */ + + /* rttEstimate is used to guess the round trip time between server and client. + * When the server sends ServerHello it sets this to the current time. + * Only after it receives a message from the client's second flight does it + * set the value to something resembling an RTT estimate. */ + PRTime rttEstimate; /* The following lists contain DTLSHandshakeRecordEntry */ PRCList dtlsSentHandshake; /* Used to map records to handshake fragments. */ PRCList dtlsRcvdHandshake; /* Handshake records we have received * used to generate ACKs. */ + PRCList psks; /* A list of PSKs, resumption and/or external. */ } SSL3HandshakeState; diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c index 3e8c751172..5d51d3c5cf 100644 --- a/lib/ssl/tls13con.c +++ b/lib/ssl/tls13con.c @@ -2866,7 +2866,9 @@ tls13_SendServerHelloSequence(sslSocket *ss) } } - ss->ssl3.hs.serverHelloTime = ssl_Time(ss); + /* Here we set a baseline value for our RTT estimation. + * This value is updated when we get a response from the client. */ + ss->ssl3.hs.rttEstimate = ssl_Time(ss); return SECSuccess; } @@ -3251,8 +3253,9 @@ tls13_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length) rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE, wait_cert_request, wait_server_cert); } - if (rv != SECSuccess) + if (rv != SECSuccess) { return SECFailure; + } /* We can ignore any other cleartext from the client. */ if (ss->sec.isServer && IS_DTLS(ss)) { @@ -3266,6 +3269,11 @@ tls13_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length) PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } + } else if (ss->sec.isServer) { + /* Our first shot an getting an RTT estimate. If the client took extra + * time to fetch a certificate, this will be bad, but we can't do much + * about that. */ + ss->ssl3.hs.rttEstimate = ssl_Time(ss) - ss->ssl3.hs.rttEstimate; } /* Process the context string */ @@ -4773,6 +4781,11 @@ tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length) if (rv != SECSuccess) { return SECFailure; /* Code already set. */ } + + if (!tls13_IsPostHandshake(ss)) { + /* Finalize the RTT estimate. */ + ss->ssl3.hs.rttEstimate = ssl_Time(ss) - ss->ssl3.hs.rttEstimate; + } } rv = tls13_CommonHandleFinished(ss, diff --git a/lib/ssl/tls13replay.c b/lib/ssl/tls13replay.c index 8deb596d24..7e00785e08 100644 --- a/lib/ssl/tls13replay.c +++ b/lib/ssl/tls13replay.c @@ -56,8 +56,7 @@ tls13_ReleaseAntiReplayContext(SSLAntiReplayContext *ctx) PORT_Free(ctx); } -/* Clear the current state and free any resources we allocated. The signature - * here is odd to allow this to be called during shutdown. */ +/* Clear the current state and free any resources we allocated. */ SECStatus SSLExp_ReleaseAntiReplayContext(SSLAntiReplayContext *ctx) {