gfe-relnote: In QUIC version T099, implement HANDSHAKE_DONE frame to drive the handshake to confirmation on the client side. Not used in prod yet.
PiperOrigin-RevId: 290948924
Change-Id: Idcbc5c0d573b5db992b65d4971ea12a3d7e87633
diff --git a/quic/core/chlo_extractor.cc b/quic/core/chlo_extractor.cc
index d2a0608..496f201 100644
--- a/quic/core/chlo_extractor.cc
+++ b/quic/core/chlo_extractor.cc
@@ -73,6 +73,7 @@
bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
bool OnMessageFrame(const QuicMessageFrame& frame) override;
+ bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override;
void OnPacketComplete() override {}
bool IsValidStatelessResetToken(QuicUint128 token) const override;
void OnAuthenticatedIetfStatelessResetPacket(
@@ -280,6 +281,11 @@
return true;
}
+bool ChloFramerVisitor::OnHandshakeDoneFrame(
+ const QuicHandshakeDoneFrame& /*frame*/) {
+ return true;
+}
+
bool ChloFramerVisitor::IsValidStatelessResetToken(
QuicUint128 /*token*/) const {
return false;
diff --git a/quic/core/frames/quic_frame.cc b/quic/core/frames/quic_frame.cc
index 44d3cc8..d40202f 100644
--- a/quic/core/frames/quic_frame.cc
+++ b/quic/core/frames/quic_frame.cc
@@ -19,6 +19,9 @@
QuicFrame::QuicFrame(QuicStreamFrame stream_frame)
: stream_frame(stream_frame) {}
+QuicFrame::QuicFrame(QuicHandshakeDoneFrame handshake_done_frame)
+ : handshake_done_frame(handshake_done_frame) {}
+
QuicFrame::QuicFrame(QuicCryptoFrame* crypto_frame)
: type(CRYPTO_FRAME), crypto_frame(crypto_frame) {}
@@ -89,6 +92,7 @@
case STOP_WAITING_FRAME:
case STREAMS_BLOCKED_FRAME:
case STREAM_FRAME:
+ case HANDSHAKE_DONE_FRAME:
break;
case ACK_FRAME:
delete frame->ack_frame;
@@ -159,6 +163,7 @@
case MAX_STREAMS_FRAME:
case PING_FRAME:
case STOP_SENDING_FRAME:
+ case HANDSHAKE_DONE_FRAME:
return true;
default:
return false;
@@ -183,6 +188,8 @@
return frame.ping_frame.control_frame_id;
case STOP_SENDING_FRAME:
return frame.stop_sending_frame->control_frame_id;
+ case HANDSHAKE_DONE_FRAME:
+ return frame.handshake_done_frame.control_frame_id;
default:
return kInvalidControlFrameId;
}
@@ -214,6 +221,9 @@
case STOP_SENDING_FRAME:
frame->stop_sending_frame->control_frame_id = control_frame_id;
return;
+ case HANDSHAKE_DONE_FRAME:
+ frame->handshake_done_frame.control_frame_id = control_frame_id;
+ return;
default:
QUIC_BUG
<< "Try to set control frame id of a frame without control frame id";
@@ -247,6 +257,10 @@
case MAX_STREAMS_FRAME:
copy = QuicFrame(QuicMaxStreamsFrame(frame.max_streams_frame));
break;
+ case HANDSHAKE_DONE_FRAME:
+ copy = QuicFrame(
+ QuicHandshakeDoneFrame(frame.handshake_done_frame.control_frame_id));
+ break;
default:
QUIC_BUG << "Try to copy a non-retransmittable control frame: " << frame;
copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId));
@@ -334,6 +348,10 @@
copy = QuicFrame(
new QuicRetireConnectionIdFrame(*frame.retire_connection_id_frame));
break;
+ case HANDSHAKE_DONE_FRAME:
+ copy = QuicFrame(
+ QuicHandshakeDoneFrame(frame.handshake_done_frame.control_frame_id));
+ break;
default:
QUIC_BUG << "Cannot copy frame: " << frame;
copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId));
@@ -430,6 +448,9 @@
case NEW_TOKEN_FRAME:
os << "type { NEW_TOKEN_FRAME }" << *(frame.new_token_frame);
break;
+ case HANDSHAKE_DONE_FRAME:
+ os << "type { HANDSHAKE_DONE_FRAME } " << frame.handshake_done_frame;
+ break;
default: {
QUIC_LOG(ERROR) << "Unknown frame type: " << frame.type;
break;
diff --git a/quic/core/frames/quic_frame.h b/quic/core/frames/quic_frame.h
index 226cbfb..756b69f 100644
--- a/quic/core/frames/quic_frame.h
+++ b/quic/core/frames/quic_frame.h
@@ -13,6 +13,7 @@
#include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h"
#include "net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h"
#include "net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_handshake_done_frame.h"
#include "net/third_party/quiche/src/quic/core/frames/quic_max_streams_frame.h"
#include "net/third_party/quiche/src/quic/core/frames/quic_message_frame.h"
#include "net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h"
@@ -45,6 +46,7 @@
explicit QuicFrame(QuicStopWaitingFrame frame);
explicit QuicFrame(QuicStreamsBlockedFrame frame);
explicit QuicFrame(QuicStreamFrame stream_frame);
+ explicit QuicFrame(QuicHandshakeDoneFrame handshake_done_frame);
explicit QuicFrame(QuicAckFrame* frame);
explicit QuicFrame(QuicRstStreamFrame* frame);
@@ -76,6 +78,7 @@
QuicStopWaitingFrame stop_waiting_frame;
QuicStreamsBlockedFrame streams_blocked_frame;
QuicStreamFrame stream_frame;
+ QuicHandshakeDoneFrame handshake_done_frame;
// Out of line frames.
struct {
diff --git a/quic/core/frames/quic_frames_test.cc b/quic/core/frames/quic_frames_test.cc
index 1764e17..7450adb 100644
--- a/quic/core/frames/quic_frames_test.cc
+++ b/quic/core/frames/quic_frames_test.cc
@@ -234,6 +234,17 @@
EXPECT_TRUE(IsControlFrame(frame.type));
}
+TEST_F(QuicFramesTest, HandshakeDoneFrameToString) {
+ QuicHandshakeDoneFrame handshake_done;
+ QuicFrame frame(handshake_done);
+ SetControlFrameId(6, &frame);
+ EXPECT_EQ(6u, GetControlFrameId(frame));
+ std::ostringstream stream;
+ stream << frame.handshake_done_frame;
+ EXPECT_EQ("{ control_frame_id: 6 }\n", stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
TEST_F(QuicFramesTest, StreamFrameToString) {
QuicStreamFrame frame;
frame.stream_id = 1;
@@ -546,6 +557,9 @@
case RETIRE_CONNECTION_ID_FRAME:
frames.push_back(QuicFrame(new QuicRetireConnectionIdFrame()));
break;
+ case HANDSHAKE_DONE_FRAME:
+ frames.push_back(QuicFrame(QuicHandshakeDoneFrame()));
+ break;
default:
ASSERT_TRUE(false)
<< "Please fix CopyQuicFrames if a new frame type is added.";
diff --git a/quic/core/frames/quic_handshake_done_frame.cc b/quic/core/frames/quic_handshake_done_frame.cc
new file mode 100644
index 0000000..6f411a5
--- /dev/null
+++ b/quic/core/frames/quic_handshake_done_frame.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_handshake_done_frame.h"
+
+namespace quic {
+
+QuicHandshakeDoneFrame::QuicHandshakeDoneFrame()
+ : QuicInlinedFrame(HANDSHAKE_DONE_FRAME),
+ control_frame_id(kInvalidControlFrameId) {}
+
+QuicHandshakeDoneFrame::QuicHandshakeDoneFrame(
+ QuicControlFrameId control_frame_id)
+ : QuicInlinedFrame(HANDSHAKE_DONE_FRAME),
+ control_frame_id(control_frame_id) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicHandshakeDoneFrame& handshake_done_frame) {
+ os << "{ control_frame_id: " << handshake_done_frame.control_frame_id
+ << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/quic/core/frames/quic_handshake_done_frame.h b/quic/core/frames/quic_handshake_done_frame.h
new file mode 100644
index 0000000..48aa3c7
--- /dev/null
+++ b/quic/core/frames/quic_handshake_done_frame.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_HANDSHAKE_DONE_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_HANDSHAKE_DONE_FRAME_H_
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A HANDSHAKE_DONE frame contains no payload, and it is retransmittable,
+// and ACK'd just like other normal frames.
+struct QUIC_EXPORT_PRIVATE QuicHandshakeDoneFrame
+ : public QuicInlinedFrame<QuicHandshakeDoneFrame> {
+ QuicHandshakeDoneFrame();
+ explicit QuicHandshakeDoneFrame(QuicControlFrameId control_frame_id);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicHandshakeDoneFrame& handshake_done_frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_HANDSHAKE_DONE_FRAME_H_
diff --git a/quic/core/handshaker_delegate_interface.h b/quic/core/handshaker_delegate_interface.h
index 9eae32a..23f2cbd 100644
--- a/quic/core/handshaker_delegate_interface.h
+++ b/quic/core/handshaker_delegate_interface.h
@@ -38,13 +38,10 @@
// Called to neuter ENCRYPTION_INITIAL data (without discarding initial keys).
virtual void NeuterUnencryptedData() = 0;
- // Called to neuter data of HANDSHAKE_DATA packet number space. In QUIC
- // crypto, this is called 1) when a client switches to forward secure
+ // Called to neuter data of HANDSHAKE_DATA packet number space. Only used in
+ // QUIC crypto. This is called 1) when a client switches to forward secure
// encryption level and 2) a server successfully processes a forward secure
- // packet. Temporarily use this method in TLS handshake when both endpoints
- // switch to forward secure encryption level.
- // TODO(fayang): use DiscardOldEncryptionKey instead of this method in TLS
- // handshake when handshake key discarding settles down.
+ // packet.
virtual void NeuterHandshakeData() = 0;
};
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 15ef0f9..080530d 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -679,6 +679,40 @@
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
+class HandshakeDoneObserver : public QuicConnectionDebugVisitor {
+ public:
+ HandshakeDoneObserver() : handshake_done_received_(false) {}
+
+ size_t handshake_done_received() const { return handshake_done_received_; }
+
+ void OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& /*frame*/) override {
+ handshake_done_received_ = true;
+ }
+
+ private:
+ bool handshake_done_received_;
+};
+
+TEST_P(EndToEndTestWithTls, HandshakeDone) {
+ ASSERT_TRUE(Initialize());
+ if (!GetParam().negotiated_version.HasHandshakeDone()) {
+ return;
+ }
+ HandshakeDoneObserver observer;
+ QuicConnection* client_connection = GetClientConnection();
+ client_connection->set_debug_visitor(&observer);
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ // Verify handshake state.
+ EXPECT_EQ(HANDSHAKE_CONFIRMED, GetClientSession()->GetHandshakeState());
+ server_thread_->Pause();
+ EXPECT_EQ(HANDSHAKE_CONFIRMED, GetServerSession()->GetHandshakeState());
+ server_thread_->Resume();
+ client_->Disconnect();
+ // Verify handshake done frame has been receveid by client.
+ EXPECT_TRUE(observer.handshake_done_received());
+}
+
TEST_P(EndToEndTestWithTls, SendAndReceiveCoalescedPackets) {
ASSERT_TRUE(Initialize());
if (!GetClientConnection()->version().CanSendCoalescedPackets()) {
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index a4c1af0..1522118 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -131,6 +131,7 @@
return QuicCryptoHandshaker::crypto_message_parser();
}
void OnPacketDecrypted(EncryptionLevel /*level*/) override {}
+ void OnHandshakeDoneReceived() override {}
MOCK_METHOD0(OnCanWrite, void());
@@ -479,6 +480,9 @@
.WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
}
EXPECT_FALSE(session_.OneRttKeysAvailable());
+ if (connection_->version().HasHandshakeDone()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ }
CryptoHandshakeMessage message;
session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
EXPECT_TRUE(session_.OneRttKeysAvailable());
@@ -742,7 +746,10 @@
.WillRepeatedly(Invoke(
this, &QuicSpdySessionTestServer::ClearMaxStreamsControlFrame));
}
-
+ if (connection_->version().HasHandshakeDone()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ }
// Encryption needs to be established before data can be sent.
CryptoHandshakeMessage msg;
MockPacketWriter* writer = static_cast<MockPacketWriter*>(
@@ -1084,6 +1091,9 @@
}
EXPECT_EQ(kInitialIdleTimeoutSecs + 3,
QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
+ if (connection_->version().HasHandshakeDone()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ }
CryptoHandshakeMessage msg;
session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
EXPECT_EQ(kMaximumIdleTimeoutSecs + 3,
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 6ef2db8..7ba472a 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -1360,6 +1360,28 @@
return connected_;
}
+bool QuicConnection::OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) {
+ DCHECK(connected_ && VersionHasIetfQuicFrames(transport_version()));
+
+ if (perspective_ == Perspective::IS_SERVER) {
+ CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
+ "Server received handshake done frame.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ // Since a handshake done frame was received, this is not a connectivity
+ // probe. A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnHandshakeDoneFrame(frame);
+ }
+ visitor_->OnHandshakeDoneReceived();
+ should_last_packet_instigate_acks_ = true;
+ return connected_;
+}
+
bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) {
DCHECK(connected_);
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index b111f3c..fb38d97 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -89,6 +89,9 @@
// Called when |message| has been received.
virtual void OnMessageReceived(quiche::QuicheStringPiece message) = 0;
+ // Called when a HANDSHAKE_DONE frame has been received.
+ virtual void OnHandshakeDoneReceived() = 0;
+
// Called when a MAX_STREAMS frame has been received from the peer.
virtual bool OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) = 0;
@@ -261,6 +264,9 @@
// Called when a MessageFrame has been parsed.
virtual void OnMessageFrame(const QuicMessageFrame& /*frame*/) {}
+ // Called when a HandshakeDoneFrame has been parsed.
+ virtual void OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& /*frame*/) {}
+
// Called when a public reset packet has been received.
virtual void OnPublicResetPacket(const QuicPublicResetPacket& /*packet*/) {}
@@ -516,6 +522,7 @@
const QuicRetireConnectionIdFrame& frame) override;
bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
bool OnMessageFrame(const QuicMessageFrame& frame) override;
+ bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override;
void OnPacketComplete() override;
bool IsValidStatelessResetToken(QuicUint128 token) const override;
void OnAuthenticatedIetfStatelessResetPacket(
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 8dd8f67..9441e86 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1191,6 +1191,14 @@
size_t ProcessFramePacketAtLevel(uint64_t number,
QuicFrame frame,
EncryptionLevel level) {
+ QuicFrames frames;
+ frames.push_back(frame);
+ return ProcessFramesPacketAtLevel(number, frames, level);
+ }
+
+ size_t ProcessFramesPacketAtLevel(uint64_t number,
+ const QuicFrames& frames,
+ EncryptionLevel level) {
QuicPacketHeader header;
header.destination_connection_id = connection_id_;
header.packet_number_length = packet_number_length_;
@@ -1212,8 +1220,6 @@
header.source_connection_id_included = CONNECTION_ID_PRESENT;
}
header.packet_number = QuicPacketNumber(number);
- QuicFrames frames;
- frames.push_back(frame);
std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
// Set the correct encryption level and encrypter on peer_creator and
// peer_framer, respectively.
@@ -9691,6 +9697,34 @@
EXPECT_NE(nullptr, writer_->coalesced_packet());
}
+TEST_P(QuicConnectionTest, ClientReceivedHandshakeDone) {
+ if (!connection_.version().HasHandshakeDone()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnHandshakeDoneReceived());
+ QuicFrames frames;
+ frames.push_back(QuicFrame(QuicHandshakeDoneFrame()));
+ frames.push_back(QuicFrame(QuicPaddingFrame(-1)));
+ ProcessFramesPacketAtLevel(1, frames, ENCRYPTION_FORWARD_SECURE);
+}
+
+TEST_P(QuicConnectionTest, ServerReceivedHandshakeDone) {
+ if (!connection_.version().HasHandshakeDone()) {
+ return;
+ }
+ set_perspective(Perspective::IS_SERVER);
+ EXPECT_CALL(visitor_, OnHandshakeDoneReceived()).Times(0);
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+ .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
+ QuicFrames frames;
+ frames.push_back(QuicFrame(QuicHandshakeDoneFrame()));
+ frames.push_back(QuicFrame(QuicPaddingFrame(-1)));
+ ProcessFramesPacketAtLevel(1, frames, ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(1, connection_close_frame_count_);
+ EXPECT_THAT(saved_connection_close_frame_.quic_error_code,
+ IsError(IETF_QUIC_PROTOCOL_VIOLATION));
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_control_frame_manager.cc b/quic/core/quic_control_frame_manager.cc
index de49366..5e33368 100644
--- a/quic/core/quic_control_frame_manager.cc
+++ b/quic/core/quic_control_frame_manager.cc
@@ -110,6 +110,12 @@
new QuicStopSendingFrame(++last_control_frame_id_, stream_id, code)));
}
+void QuicControlFrameManager::WriteOrBufferHandshakeDone() {
+ QUIC_DVLOG(1) << "Writing HANDSHAKE_DONE";
+ WriteOrBufferQuicFrame(
+ QuicFrame(QuicHandshakeDoneFrame(++last_control_frame_id_)));
+}
+
void QuicControlFrameManager::WritePing() {
QUIC_DVLOG(1) << "Writing PING_FRAME";
if (HasBufferedFrames()) {
diff --git a/quic/core/quic_control_frame_manager.h b/quic/core/quic_control_frame_manager.h
index 61dfba1..ac31a1e 100644
--- a/quic/core/quic_control_frame_manager.h
+++ b/quic/core/quic_control_frame_manager.h
@@ -67,6 +67,10 @@
// can not be sent immediately.
void WriteOrBufferStopSending(uint16_t code, QuicStreamId stream_id);
+ // Tries to send an HANDSHAKE_DONE frame. The frame is buffered if it can not
+ // be sent immediately.
+ void WriteOrBufferHandshakeDone();
+
// Sends a PING_FRAME. Do not send PING if there is buffered frames.
void WritePing();
diff --git a/quic/core/quic_crypto_client_handshaker.cc b/quic/core/quic_crypto_client_handshaker.cc
index 795f78c..982d070 100644
--- a/quic/core/quic_crypto_client_handshaker.cc
+++ b/quic/core/quic_crypto_client_handshaker.cc
@@ -153,6 +153,10 @@
return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START;
}
+void QuicCryptoClientHandshaker::OnHandshakeDoneReceived() {
+ DCHECK(false);
+}
+
size_t QuicCryptoClientHandshaker::BufferSizeLimitForLevel(
EncryptionLevel level) const {
return QuicCryptoHandshaker::BufferSizeLimitForLevel(level);
diff --git a/quic/core/quic_crypto_client_handshaker.h b/quic/core/quic_crypto_client_handshaker.h
index 9729b1d..dc799fb 100644
--- a/quic/core/quic_crypto_client_handshaker.h
+++ b/quic/core/quic_crypto_client_handshaker.h
@@ -47,6 +47,7 @@
CryptoMessageParser* crypto_message_parser() override;
HandshakeState GetHandshakeState() const override;
size_t BufferSizeLimitForLevel(EncryptionLevel level) const override;
+ void OnHandshakeDoneReceived() override;
// From QuicCryptoHandshaker
void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
diff --git a/quic/core/quic_crypto_client_stream.cc b/quic/core/quic_crypto_client_stream.cc
index d681eb3..92d396f 100644
--- a/quic/core/quic_crypto_client_stream.cc
+++ b/quic/core/quic_crypto_client_stream.cc
@@ -99,4 +99,8 @@
return handshaker_->chlo_hash();
}
+void QuicCryptoClientStream::OnHandshakeDoneReceived() {
+ handshaker_->OnHandshakeDoneReceived();
+}
+
} // namespace quic
diff --git a/quic/core/quic_crypto_client_stream.h b/quic/core/quic_crypto_client_stream.h
index 76d0301..fd81259 100644
--- a/quic/core/quic_crypto_client_stream.h
+++ b/quic/core/quic_crypto_client_stream.h
@@ -120,6 +120,9 @@
// Returns current handshake state.
virtual HandshakeState GetHandshakeState() const = 0;
+
+ // Called when handshake done has been received.
+ virtual void OnHandshakeDoneReceived() = 0;
};
// ProofHandler is an interface that handles callbacks from the crypto
@@ -165,6 +168,7 @@
const override;
CryptoMessageParser* crypto_message_parser() override;
void OnPacketDecrypted(EncryptionLevel /*level*/) override {}
+ void OnHandshakeDoneReceived() override;
HandshakeState GetHandshakeState() const override;
size_t BufferSizeLimitForLevel(EncryptionLevel level) const override;
diff --git a/quic/core/quic_crypto_server_stream.cc b/quic/core/quic_crypto_server_stream.cc
index f4a514d..059317e 100644
--- a/quic/core/quic_crypto_server_stream.cc
+++ b/quic/core/quic_crypto_server_stream.cc
@@ -165,6 +165,10 @@
handshaker_->OnPacketDecrypted(level);
}
+void QuicCryptoServerStream::OnHandshakeDoneReceived() {
+ DCHECK(false);
+}
+
HandshakeState QuicCryptoServerStream::GetHandshakeState() const {
return handshaker_->GetHandshakeState();
}
diff --git a/quic/core/quic_crypto_server_stream.h b/quic/core/quic_crypto_server_stream.h
index db95116..9f8d08d 100644
--- a/quic/core/quic_crypto_server_stream.h
+++ b/quic/core/quic_crypto_server_stream.h
@@ -175,6 +175,7 @@
const override;
CryptoMessageParser* crypto_message_parser() override;
void OnPacketDecrypted(EncryptionLevel level) override;
+ void OnHandshakeDoneReceived() override;
HandshakeState GetHandshakeState() const override;
size_t BufferSizeLimitForLevel(EncryptionLevel level) const override;
void OnSuccessfulVersionNegotiation(
diff --git a/quic/core/quic_crypto_stream.h b/quic/core/quic_crypto_stream.h
index 7b9414c..66bd461 100644
--- a/quic/core/quic_crypto_stream.h
+++ b/quic/core/quic_crypto_stream.h
@@ -85,6 +85,9 @@
// Called when a packet of encryption |level| has been successfully decrypted.
virtual void OnPacketDecrypted(EncryptionLevel level) = 0;
+ // Called when a handshake done frame has been received.
+ virtual void OnHandshakeDoneReceived() = 0;
+
// Returns current handshake state.
virtual HandshakeState GetHandshakeState() const = 0;
diff --git a/quic/core/quic_crypto_stream_test.cc b/quic/core/quic_crypto_stream_test.cc
index 1b391ab..dfbc6ef 100644
--- a/quic/core/quic_crypto_stream_test.cc
+++ b/quic/core/quic_crypto_stream_test.cc
@@ -57,6 +57,7 @@
return QuicCryptoHandshaker::crypto_message_parser();
}
void OnPacketDecrypted(EncryptionLevel /*level*/) override {}
+ void OnHandshakeDoneReceived() override {}
HandshakeState GetHandshakeState() const override { return HANDSHAKE_START; }
private:
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index bb048e1..ab99f05 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -655,6 +655,9 @@
return GetPathChallengeFrameSize(*frame.path_challenge_frame);
case STOP_SENDING_FRAME:
return GetStopSendingFrameSize(*frame.stop_sending_frame);
+ case HANDSHAKE_DONE_FRAME:
+ // HANDSHAKE_DONE has no payload.
+ return kQuicFrameTypeSize;
case STREAM_FRAME:
case ACK_FRAME:
@@ -1158,6 +1161,9 @@
return 0;
}
break;
+ case HANDSHAKE_DONE_FRAME:
+ // HANDSHAKE_DONE has no payload.
+ break;
default:
set_detailed_error("Tried to append unknown frame type.");
RaiseError(QUIC_INVALID_FRAME_DATA);
@@ -3248,6 +3254,19 @@
}
break;
}
+ case IETF_HANDSHAKE_DONE: {
+ // HANDSHAKE_DONE has no payload.
+ QuicHandshakeDoneFrame handshake_done_frame;
+ if (!visitor_->OnHandshakeDoneFrame(handshake_done_frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ QUIC_DVLOG(2) << ENDPOINT << "Processing handshake done frame "
+ << handshake_done_frame;
+ break;
+ }
default:
set_detailed_error("Illegal frame type.");
@@ -4820,6 +4839,9 @@
case CRYPTO_FRAME:
type_byte = IETF_CRYPTO;
break;
+ case HANDSHAKE_DONE_FRAME:
+ type_byte = IETF_HANDSHAKE_DONE;
+ break;
default:
QUIC_BUG << "Attempt to generate a frame type for an unsupported value: "
<< frame.type;
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 768aa33..1a83989 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -204,6 +204,9 @@
// Called when a message frame has been parsed.
virtual bool OnMessageFrame(const QuicMessageFrame& frame) = 0;
+ // Called when a handshake done frame has been parsed.
+ virtual bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) = 0;
+
// Called when a packet has been completely processed.
virtual void OnPacketComplete() = 0;
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index b794cb1..c51c9c2 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -389,6 +389,15 @@
return true;
}
+ bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override {
+ ++frame_count_;
+ handshake_done_frames_.push_back(
+ std::make_unique<QuicHandshakeDoneFrame>(frame));
+ DCHECK(VersionHasIetfQuicFrames(transport_version_));
+ EXPECT_EQ(IETF_HANDSHAKE_DONE, framer_->current_received_frame_type());
+ return true;
+ }
+
void OnPacketComplete() override { ++complete_packets_; }
bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
@@ -553,6 +562,7 @@
std::vector<std::unique_ptr<QuicPaddingFrame>> padding_frames_;
std::vector<std::unique_ptr<QuicPingFrame>> ping_frames_;
std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_;
+ std::vector<std::unique_ptr<QuicHandshakeDoneFrame>> handshake_done_frames_;
std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_;
std::vector<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_;
std::vector<EncryptionLevel> undecryptable_decryption_levels_;
@@ -5104,6 +5114,39 @@
// No need to check the PING frame boundaries because it has no payload.
}
+TEST_P(QuicFramerTest, HandshakeDoneFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (Handshake done frame)
+ 0x1e,
+ };
+ // clang-format on
+
+ if (!VersionHasIetfQuicFrames(framer_.transport_version())) {
+ return;
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(packet), QUICHE_ARRAYSIZE(packet),
+ false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_THAT(framer_.error(), IsQuicNoError());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(1u, visitor_.handshake_done_frames_.size());
+}
+
TEST_P(QuicFramerTest, MessageFrame) {
if (!VersionSupportsMessageFrames(framer_.transport_version())) {
return;
@@ -8497,6 +8540,40 @@
: QUICHE_ARRAYSIZE(packet));
}
+TEST_P(QuicFramerTest, BuildHandshakeDonePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicFrames frames = {QuicFrame(QuicHandshakeDoneFrame())};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (Handshake done frame)
+ 0x1e,
+ };
+ // clang-format on
+ if (!VersionHasIetfQuicFrames(framer_.transport_version())) {
+ return;
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ quiche::test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(packet),
+ QUICHE_ARRAYSIZE(packet));
+}
+
TEST_P(QuicFramerTest, BuildMessagePacket) {
if (!VersionSupportsMessageFrames(framer_.transport_version())) {
return;
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 16ec7b6..74931ad 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -374,6 +374,11 @@
<< ", " << message;
}
+void QuicSession::OnHandshakeDoneReceived() {
+ QUIC_DVLOG(1) << ENDPOINT << "OnHandshakeDoneReceived";
+ GetMutableCryptoStream()->OnHandshakeDoneReceived();
+}
+
// static
void QuicSession::RecordConnectionCloseAtServer(QuicErrorCode error,
ConnectionCloseSource source) {
@@ -1372,6 +1377,12 @@
}
QUIC_BUG_IF(!config_.negotiated())
<< ENDPOINT << "Handshake confirmed without parameter negotiation.";
+ if (connection()->version().HasHandshakeDone() &&
+ perspective_ == Perspective::IS_SERVER) {
+ // Server sends HANDSHAKE_DONE to signal confirmation of the handshake
+ // to the client.
+ control_frame_manager_.WriteOrBufferHandshakeDone();
+ }
break;
default:
QUIC_BUG << "Unknown encryption level: "
@@ -1398,9 +1409,7 @@
NeuterUnencryptedData();
break;
case ENCRYPTION_HANDSHAKE:
- DCHECK(false);
- // TODO(fayang): implement this when handshake keys discarding settles
- // down.
+ NeuterHandshakeData();
break;
case ENCRYPTION_ZERO_RTT:
break;
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 84ba8e1..adeabb1 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -107,6 +107,7 @@
void OnRstStream(const QuicRstStreamFrame& frame) override;
void OnGoAway(const QuicGoAwayFrame& frame) override;
void OnMessageReceived(quiche::QuicheStringPiece message) override;
+ void OnHandshakeDoneReceived() override;
void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
void OnBlockedFrame(const QuicBlockedFrame& frame) override;
void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 007f355..d4bddf2 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -115,6 +115,7 @@
return QuicCryptoHandshaker::crypto_message_parser();
}
void OnPacketDecrypted(EncryptionLevel /*level*/) override {}
+ void OnHandshakeDoneReceived() override {}
HandshakeState GetHandshakeState() const override {
return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START;
}
@@ -573,6 +574,9 @@
TEST_P(QuicSessionTestServer, OneRttKeysAvailable) {
EXPECT_FALSE(session_.OneRttKeysAvailable());
CryptoHandshakeMessage message;
+ if (connection_->version().HasHandshakeDone()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ }
session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
EXPECT_TRUE(session_.OneRttKeysAvailable());
}
@@ -1097,6 +1101,10 @@
TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) {
// Encryption needs to be established before data can be sent.
+ if (connection_->version().HasHandshakeDone()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ }
CryptoHandshakeMessage msg;
MockPacketWriter* writer = static_cast<MockPacketWriter*>(
QuicConnectionPeer::GetWriter(session_.connection()));
@@ -1439,6 +1447,9 @@
TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) {
EXPECT_EQ(kInitialIdleTimeoutSecs + 3,
QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
+ if (connection_->version().HasHandshakeDone()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ }
CryptoHandshakeMessage msg;
session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
EXPECT_EQ(kMaximumIdleTimeoutSecs + 3,
@@ -2354,6 +2365,9 @@
"", &storage)));
// Finish handshake.
+ if (connection_->version().HasHandshakeDone()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ }
CryptoHandshakeMessage handshake_message;
session_.GetMutableCryptoStream()->OnHandshakeMessage(handshake_message);
EXPECT_TRUE(session_.OneRttKeysAvailable());
diff --git a/quic/core/quic_trace_visitor.cc b/quic/core/quic_trace_visitor.cc
index 5097485..9d18843 100644
--- a/quic/core/quic_trace_visitor.cc
+++ b/quic/core/quic_trace_visitor.cc
@@ -62,6 +62,7 @@
case WINDOW_UPDATE_FRAME:
case BLOCKED_FRAME:
case PING_FRAME:
+ case HANDSHAKE_DONE_FRAME:
PopulateFrameInfo(frame, event->add_frames());
break;
@@ -193,6 +194,7 @@
case PING_FRAME:
case MTU_DISCOVERY_FRAME:
+ case HANDSHAKE_DONE_FRAME:
frame_record->set_frame_type(quic_trace::PING);
break;
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index d333fe5..42dc050 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -220,6 +220,7 @@
MESSAGE_FRAME,
NEW_TOKEN_FRAME,
RETIRE_CONNECTION_ID_FRAME,
+ HANDSHAKE_DONE_FRAME,
NUM_FRAME_TYPES
};
@@ -270,6 +271,8 @@
IETF_CONNECTION_CLOSE = 0x1c,
IETF_APPLICATION_CLOSE = 0x1d,
+ IETF_HANDSHAKE_DONE = 0x1e,
+
// The MESSAGE frame type has not yet been fully standardized.
// QUIC versions starting with 46 and before 99 use 0x20-0x21.
// IETF QUIC (v99) uses 0x30-0x31, see draft-pauly-quic-datagram.
@@ -729,13 +732,14 @@
// In QUIC crypto, state proceeds to HANDSHAKE_COMPLETE if client receives
// SHLO or server successfully processes an ENCRYPTION_FORWARD_SECURE
// packet, such that the handshake packets can be neutered. In IETF QUIC
- // with TLS handshake, state proceeds to HANDSHAKE_COMPLETE once the
- // endpoint has both 1-RTT send and receive keys.
+ // with TLS handshake, state proceeds to HANDSHAKE_COMPLETE once the client
+ // has both 1-RTT send and receive keys.
HANDSHAKE_COMPLETE,
// Only used in IETF QUIC with TLS handshake. State proceeds to
- // HANDSHAKE_CONFIRMED if a 1-RTT packet gets acknowledged.
- // TODO(fayang): implement HANDSHAKE_DONE frame to drive handshake to
- // confirmation according to https://github.com/quicwg/base-drafts/pull/3145.
+ // HANDSHAKE_CONFIRMED if a client receives HANDSHAKE_DONE frame or server has
+ // 1-RTT send and receive keys.
+ // TODO(fayang): on the client side, proceed state to HANDSHAKE_CONFIRMED once
+ // 1-RTT packet gets acknowledged..
HANDSHAKE_CONFIRMED,
};
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc
index 72fd964..96f0a82 100644
--- a/quic/core/quic_versions.cc
+++ b/quic/core/quic_versions.cc
@@ -145,6 +145,11 @@
return VersionHasIetfQuicFrames(transport_version);
}
+bool ParsedQuicVersion::HasHandshakeDone() const {
+ DCHECK(IsKnown());
+ return HasIetfQuicFrames() && handshake_protocol == PROTOCOL_TLS1_3;
+}
+
bool VersionHasLengthPrefixedConnectionIds(
QuicTransportVersion transport_version) {
DCHECK(transport_version != QUIC_VERSION_UNSUPPORTED ||
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h
index 5aad73b..606afc3 100644
--- a/quic/core/quic_versions.h
+++ b/quic/core/quic_versions.h
@@ -285,6 +285,9 @@
// Returns whether |transport_version| makes use of IETF QUIC
// frames or not.
bool HasIetfQuicFrames() const;
+
+ // Returns true if this parsed version supports handshake done.
+ bool HasHandshakeDone() const;
};
QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion();
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index da549f3..45d1849 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -240,6 +240,9 @@
}
HandshakeState TlsClientHandshaker::GetHandshakeState() const {
+ if (handshake_confirmed_) {
+ return HANDSHAKE_CONFIRMED;
+ }
if (one_rtt_keys_available_) {
return HANDSHAKE_COMPLETE;
}
@@ -254,6 +257,20 @@
return TlsHandshaker::BufferSizeLimitForLevel(level);
}
+void TlsClientHandshaker::OnHandshakeDoneReceived() {
+ if (!one_rtt_keys_available_) {
+ CloseConnection(QUIC_HANDSHAKE_FAILED,
+ "Unexpected handshake done received");
+ return;
+ }
+ if (handshake_confirmed_) {
+ return;
+ }
+ handshake_confirmed_ = true;
+ delegate()->DiscardOldEncryptionKey(ENCRYPTION_HANDSHAKE);
+ delegate()->DiscardOldDecryptionKey(ENCRYPTION_HANDSHAKE);
+}
+
void TlsClientHandshaker::AdvanceHandshake() {
if (state_ == STATE_CONNECTION_CLOSED) {
QUIC_LOG(INFO)
@@ -359,9 +376,6 @@
SSL_get_peer_signature_algorithm(ssl());
delegate()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
- // TODO(fayang): Replace this with DiscardOldKeys(ENCRYPTION_HANDSHAKE) when
- // handshake key discarding settles down.
- delegate()->NeuterHandshakeData();
}
enum ssl_verify_result_t TlsClientHandshaker::VerifyCert(uint8_t* out_alert) {
diff --git a/quic/core/tls_client_handshaker.h b/quic/core/tls_client_handshaker.h
index c8f58bd..4c7028a 100644
--- a/quic/core/tls_client_handshaker.h
+++ b/quic/core/tls_client_handshaker.h
@@ -51,6 +51,7 @@
CryptoMessageParser* crypto_message_parser() override;
HandshakeState GetHandshakeState() const override;
size_t BufferSizeLimitForLevel(EncryptionLevel level) const override;
+ void OnHandshakeDoneReceived() override;
// Override to drop initial keys if trying to write ENCRYPTION_HANDSHAKE data.
void WriteMessage(EncryptionLevel level,
@@ -138,6 +139,7 @@
bool encryption_established_ = false;
bool one_rtt_keys_available_ = false;
+ bool handshake_confirmed_ = false;
QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters>
crypto_negotiated_params_;
diff --git a/quic/core/tls_handshaker_test.cc b/quic/core/tls_handshaker_test.cc
index 6812549..12b3299 100644
--- a/quic/core/tls_handshaker_test.cc
+++ b/quic/core/tls_handshaker_test.cc
@@ -254,6 +254,7 @@
TlsHandshaker* handshaker() const override { return handshaker_.get(); }
TlsClientHandshaker* client_handshaker() const { return handshaker_.get(); }
const MockProofHandler& proof_handler() { return proof_handler_; }
+ void OnHandshakeDoneReceived() override {}
bool CryptoConnect() { return handshaker_->CryptoConnect(); }
@@ -306,6 +307,7 @@
void OnPacketDecrypted(EncryptionLevel level) override {
handshaker_->OnPacketDecrypted(level);
}
+ void OnHandshakeDoneReceived() override { DCHECK(false); }
TlsHandshaker* handshaker() const override { return handshaker_.get(); }
@@ -369,8 +371,8 @@
EXPECT_TRUE(client_stream_->encryption_established());
EXPECT_TRUE(server_stream_->one_rtt_keys_available());
EXPECT_TRUE(server_stream_->encryption_established());
- EXPECT_TRUE(client_conn_->IsHandshakeComplete());
- EXPECT_TRUE(server_conn_->IsHandshakeComplete());
+ EXPECT_EQ(HANDSHAKE_COMPLETE, client_stream_->GetHandshakeState());
+ EXPECT_EQ(HANDSHAKE_CONFIRMED, server_stream_->GetHandshakeState());
const auto& client_crypto_params =
client_stream_->crypto_negotiated_params();
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index b22d964..1c95d2b 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -120,6 +120,10 @@
}
}
+void TlsServerHandshaker::OnHandshakeDoneReceived() {
+ DCHECK(false);
+}
+
bool TlsServerHandshaker::ShouldSendExpectCTHeader() const {
return false;
}
@@ -143,7 +147,7 @@
HandshakeState TlsServerHandshaker::GetHandshakeState() const {
if (one_rtt_keys_available_) {
- return HANDSHAKE_COMPLETE;
+ return HANDSHAKE_CONFIRMED;
}
if (state_ >= STATE_ENCRYPTION_HANDSHAKE_DATA_PROCESSED) {
return HANDSHAKE_PROCESSED;
@@ -284,9 +288,8 @@
crypto_negotiated_params_->key_exchange_group = SSL_get_curve_id(ssl());
delegate()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
- // TODO(fayang): Replace this with DiscardOldKeys(ENCRYPTION_HANDSHAKE) when
- // handshake key discarding settles down.
- delegate()->NeuterHandshakeData();
+ delegate()->DiscardOldEncryptionKey(ENCRYPTION_HANDSHAKE);
+ delegate()->DiscardOldDecryptionKey(ENCRYPTION_HANDSHAKE);
}
ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign(
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index f2341cf..8215bb7 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -47,6 +47,7 @@
void SetPreviousCachedNetworkParams(
CachedNetworkParameters cached_network_params) override;
void OnPacketDecrypted(EncryptionLevel level) override;
+ void OnHandshakeDoneReceived() override;
bool ShouldSendExpectCTHeader() const override;
// From QuicCryptoServerStreamBase and TlsHandshaker