In SSL_reset_early_data_reject, clear out unreported bytes written. This fixes initial content in case 0-RTT gets rejected after trying to send a large amount of data. Change-Id: Ie7be08a9ac050153db43877552923ba26a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/95547 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Rudolf Polzer <rpolzer@google.com>
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index bc319b3..29ac089 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc
@@ -1146,6 +1146,7 @@ // retry. The handshake will transparently flush out the pending record // (discarded by the server) to keep the framing correct. ssl->s3->pending_write = {}; + ssl->s3->unreported_bytes_written = 0; } enum ssl_early_data_reason_t SSL_get_early_data_reason(const SSL *ssl) {
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 284c5ac..0a7c900 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -3847,6 +3847,77 @@ EXPECT_EQ(0u, len); } +TEST(SSLTest, EarlyDataRejectStaleUnreportedBytes) { + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(client_ctx); + bssl::UniquePtr<SSL_CTX> server_ctx = + CreateContextWithTestCertificate(TLS_method()); + ASSERT_TRUE(server_ctx); + + // Setup a 0-RTT session. + SSL_CTX_set_early_data_enabled(client_ctx.get(), 1); + SSL_CTX_set_early_data_enabled(server_ctx.get(), 1); + SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH); + SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH); + SSL_CTX_set_custom_verify(client_ctx.get(), SSL_VERIFY_PEER, + AcceptAnyCertificate); + bssl::UniquePtr<SSL_SESSION> session = + CreateClientSession(client_ctx.get(), server_ctx.get()); + ASSERT_TRUE(session); + ASSERT_TRUE(SSL_SESSION_early_data_capable(session.get())); + + // Use the session, but with 0-RTT disabled on the server. + bssl::UniquePtr<SSL> client, server; + ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get())); + SSL_set_session(client.get(), session.get()); + SSL_set_early_data_enabled(server.get(), 0); + + // The client sends ClientHello. + ASSERT_EQ(1, SSL_do_handshake(client.get())); + ASSERT_TRUE(SSL_in_early_data(client.get())); + + // Try to write a large amount of early data, large enough that one record + // gets through, but a subsequent record is interrupted. (In this case, by the + // early data limit, but the exact interrupt is not important.) + // + // |client| now remembers there is a pending write, and that some prefix of it + // has already been written. + std::vector<uint8_t> early_write(20000, 'A'); + ASSERT_EQ(-1, + SSL_write(client.get(), early_write.data(), early_write.size())); + ASSERT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(client.get(), -1)); + + // The server reads the ClientHello, rejects 0-RTT, and sends + // ServerHello..Finished. + ASSERT_EQ(-1, SSL_do_handshake(server.get())); + ASSERT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(server.get(), -1)); + ASSERT_FALSE(SSL_in_early_data(server.get())); + + // The client retries the write. It now reads the server's flight and + // discovers 0-RTT was rejected. + ASSERT_EQ(-1, + SSL_write(client.get(), early_write.data(), early_write.size())); + ASSERT_EQ(SSL_ERROR_EARLY_DATA_REJECTED, SSL_get_error(client.get(), -1)); + + // Resetting the early data reject should allow the handshake to proceed and + // forget the pending write. + SSL_reset_early_data_reject(client.get()); + ASSERT_TRUE(CompleteHandshakes(client.get(), server.get())); + ASSERT_FALSE(SSL_in_early_data(client.get())); + + // Test the pending write was cleared by attempting to write something else. + static const uint8_t kNewWrite[] = {'h', 'e', 'l', 'l', 'o'}; + ASSERT_EQ(static_cast<int>(sizeof(kNewWrite)), + SSL_write(client.get(), kNewWrite, sizeof(kNewWrite))); + + // Confirm it was written correctly. + uint8_t received[sizeof(kNewWrite)]; + ASSERT_EQ(static_cast<int>(sizeof(received)), + SSL_read(server.get(), received, sizeof(received))); + EXPECT_EQ(Bytes(received), Bytes(kNewWrite)); +} + TEST(SSLTest, SessionDuplication) { bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); bssl::UniquePtr<SSL_CTX> server_ctx =