summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quiche/quic/core/quic_crypto_stream.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/quic/core/quic_crypto_stream.cc')
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/quic_crypto_stream.cc476
1 files changed, 476 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/quic_crypto_stream.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/quic_crypto_stream.cc
new file mode 100644
index 00000000000..a6699820d0b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/quic_crypto_stream.cc
@@ -0,0 +1,476 @@
+// Copyright (c) 2012 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 "quiche/quic/core/quic_crypto_stream.h"
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "quiche/quic/core/crypto/crypto_handshake.h"
+#include "quiche/quic/core/crypto/crypto_utils.h"
+#include "quiche/quic/core/quic_connection.h"
+#include "quiche/quic/core/quic_session.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+#define ENDPOINT \
+ (session()->perspective() == Perspective::IS_SERVER ? "Server: " \
+ : "Client:" \
+ " ")
+
+QuicCryptoStream::QuicCryptoStream(QuicSession* session)
+ : QuicStream(
+ QuicVersionUsesCryptoFrames(session->transport_version())
+ ? QuicUtils::GetInvalidStreamId(session->transport_version())
+ : QuicUtils::GetCryptoStreamId(session->transport_version()),
+ session,
+ /*is_static=*/true,
+ QuicVersionUsesCryptoFrames(session->transport_version())
+ ? CRYPTO
+ : BIDIRECTIONAL),
+ substreams_{{{this, ENCRYPTION_INITIAL},
+ {this, ENCRYPTION_HANDSHAKE},
+ {this, ENCRYPTION_ZERO_RTT},
+ {this, ENCRYPTION_FORWARD_SECURE}}} {
+ // The crypto stream is exempt from connection level flow control.
+ DisableConnectionFlowControlForThisStream();
+}
+
+QuicCryptoStream::~QuicCryptoStream() {}
+
+// static
+QuicByteCount QuicCryptoStream::CryptoMessageFramingOverhead(
+ QuicTransportVersion version, QuicConnectionId connection_id) {
+ QUICHE_DCHECK(
+ QuicUtils::IsConnectionIdValidForVersion(connection_id, version));
+ QuicVariableLengthIntegerLength retry_token_length_length =
+ VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ QuicVariableLengthIntegerLength length_length =
+ VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ if (!QuicVersionHasLongHeaderLengths(version)) {
+ retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ }
+ return QuicPacketCreator::StreamFramePacketOverhead(
+ version, static_cast<QuicConnectionIdLength>(connection_id.length()),
+ PACKET_0BYTE_CONNECTION_ID,
+ /*include_version=*/true,
+ /*include_diversification_nonce=*/true,
+ VersionHasIetfInvariantHeader(version) ? PACKET_4BYTE_PACKET_NUMBER
+ : PACKET_1BYTE_PACKET_NUMBER,
+ retry_token_length_length, length_length,
+ /*offset=*/0);
+}
+
+void QuicCryptoStream::OnCryptoFrame(const QuicCryptoFrame& frame) {
+ QUIC_BUG_IF(quic_bug_12573_1,
+ !QuicVersionUsesCryptoFrames(session()->transport_version()))
+ << "Versions less than 47 shouldn't receive CRYPTO frames";
+ EncryptionLevel level = session()->connection()->last_decrypted_level();
+ substreams_[level].sequencer.OnCryptoFrame(frame);
+ EncryptionLevel frame_level = level;
+ if (substreams_[level].sequencer.NumBytesBuffered() >
+ BufferSizeLimitForLevel(frame_level)) {
+ OnUnrecoverableError(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
+ "Too much crypto data received");
+ }
+}
+
+void QuicCryptoStream::OnStreamFrame(const QuicStreamFrame& frame) {
+ if (QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ QUIC_PEER_BUG(quic_peer_bug_12573_2)
+ << "Crypto data received in stream frame instead of crypto frame";
+ OnUnrecoverableError(QUIC_INVALID_STREAM_DATA, "Unexpected stream frame");
+ }
+ QuicStream::OnStreamFrame(frame);
+}
+
+void QuicCryptoStream::OnDataAvailable() {
+ EncryptionLevel level = session()->connection()->last_decrypted_level();
+ if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ // Versions less than 47 only support QUIC crypto, which ignores the
+ // EncryptionLevel passed into CryptoMessageParser::ProcessInput (and
+ // OnDataAvailableInSequencer).
+ OnDataAvailableInSequencer(sequencer(), level);
+ return;
+ }
+ OnDataAvailableInSequencer(&substreams_[level].sequencer, level);
+}
+
+void QuicCryptoStream::OnDataAvailableInSequencer(
+ QuicStreamSequencer* sequencer, EncryptionLevel level) {
+ struct iovec iov;
+ while (sequencer->GetReadableRegion(&iov)) {
+ absl::string_view data(static_cast<char*>(iov.iov_base), iov.iov_len);
+ if (!crypto_message_parser()->ProcessInput(data, level)) {
+ OnUnrecoverableError(crypto_message_parser()->error(),
+ crypto_message_parser()->error_detail());
+ return;
+ }
+ sequencer->MarkConsumed(iov.iov_len);
+ if (one_rtt_keys_available() &&
+ crypto_message_parser()->InputBytesRemaining() == 0) {
+ // If the handshake is complete and the current message has been fully
+ // processed then no more handshake messages are likely to arrive soon
+ // so release the memory in the stream sequencer.
+ sequencer->ReleaseBufferIfEmpty();
+ }
+ }
+}
+
+void QuicCryptoStream::WriteCryptoData(EncryptionLevel level,
+ absl::string_view data) {
+ if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ WriteOrBufferDataAtLevel(data, /*fin=*/false, level,
+ /*ack_listener=*/nullptr);
+ return;
+ }
+ if (data.empty()) {
+ QUIC_BUG(quic_bug_10322_1) << "Empty crypto data being written";
+ return;
+ }
+ const bool had_buffered_data = HasBufferedCryptoFrames();
+ // Append |data| to the send buffer for this encryption level.
+ QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+ QuicStreamOffset offset = send_buffer->stream_offset();
+ send_buffer->SaveStreamData(data);
+ if (kMaxStreamLength - offset < data.length()) {
+ QUIC_BUG(quic_bug_10322_2) << "Writing too much crypto handshake data";
+ // TODO(nharper): Switch this to an IETF QUIC error code, possibly
+ // INTERNAL_ERROR?
+ OnUnrecoverableError(QUIC_STREAM_LENGTH_OVERFLOW,
+ "Writing too much crypto handshake data");
+ }
+ if (had_buffered_data) {
+ // Do not try to write if there is buffered data.
+ return;
+ }
+
+ size_t bytes_consumed = stream_delegate()->SendCryptoData(
+ level, data.length(), offset, NOT_RETRANSMISSION);
+ send_buffer->OnStreamDataConsumed(bytes_consumed);
+}
+
+size_t QuicCryptoStream::BufferSizeLimitForLevel(EncryptionLevel) const {
+ return GetQuicFlag(FLAGS_quic_max_buffered_crypto_bytes);
+}
+
+bool QuicCryptoStream::OnCryptoFrameAcked(const QuicCryptoFrame& frame,
+ QuicTime::Delta /*ack_delay_time*/) {
+ QuicByteCount newly_acked_length = 0;
+ if (!substreams_[frame.level].send_buffer.OnStreamDataAcked(
+ frame.offset, frame.data_length, &newly_acked_length)) {
+ OnUnrecoverableError(QUIC_INTERNAL_ERROR,
+ "Trying to ack unsent crypto data.");
+ return false;
+ }
+ return newly_acked_length > 0;
+}
+
+void QuicCryptoStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) {
+ stream_delegate()->OnStreamError(QUIC_INVALID_STREAM_ID,
+ "Attempt to reset crypto stream");
+}
+
+void QuicCryptoStream::NeuterUnencryptedStreamData() {
+ NeuterStreamDataOfEncryptionLevel(ENCRYPTION_INITIAL);
+}
+
+void QuicCryptoStream::NeuterStreamDataOfEncryptionLevel(
+ EncryptionLevel level) {
+ if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ for (const auto& interval : bytes_consumed_[level]) {
+ QuicByteCount newly_acked_length = 0;
+ send_buffer().OnStreamDataAcked(
+ interval.min(), interval.max() - interval.min(), &newly_acked_length);
+ }
+ return;
+ }
+ QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+ // TODO(nharper): Consider adding a Clear() method to QuicStreamSendBuffer to
+ // replace the following code.
+ QuicIntervalSet<QuicStreamOffset> to_ack = send_buffer->bytes_acked();
+ to_ack.Complement(0, send_buffer->stream_offset());
+ for (const auto& interval : to_ack) {
+ QuicByteCount newly_acked_length = 0;
+ send_buffer->OnStreamDataAcked(
+ interval.min(), interval.max() - interval.min(), &newly_acked_length);
+ }
+}
+
+void QuicCryptoStream::OnStreamDataConsumed(QuicByteCount bytes_consumed) {
+ if (QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ QUIC_BUG(quic_bug_10322_3)
+ << "Stream data consumed when CRYPTO frames should be in use";
+ }
+ if (bytes_consumed > 0) {
+ bytes_consumed_[session()->connection()->encryption_level()].Add(
+ stream_bytes_written(), stream_bytes_written() + bytes_consumed);
+ }
+ QuicStream::OnStreamDataConsumed(bytes_consumed);
+}
+
+namespace {
+
+constexpr std::array<EncryptionLevel, NUM_ENCRYPTION_LEVELS>
+AllEncryptionLevels() {
+ return {ENCRYPTION_INITIAL, ENCRYPTION_HANDSHAKE, ENCRYPTION_ZERO_RTT,
+ ENCRYPTION_FORWARD_SECURE};
+}
+
+} // namespace
+
+bool QuicCryptoStream::HasPendingCryptoRetransmission() const {
+ if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ return false;
+ }
+ for (EncryptionLevel level : AllEncryptionLevels()) {
+ if (substreams_[level].send_buffer.HasPendingRetransmission()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void QuicCryptoStream::WritePendingCryptoRetransmission() {
+ QUIC_BUG_IF(quic_bug_12573_3,
+ !QuicVersionUsesCryptoFrames(session()->transport_version()))
+ << "Versions less than 47 don't write CRYPTO frames";
+ for (EncryptionLevel level : AllEncryptionLevels()) {
+ QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+ while (send_buffer->HasPendingRetransmission()) {
+ auto pending = send_buffer->NextPendingRetransmission();
+ size_t bytes_consumed = stream_delegate()->SendCryptoData(
+ level, pending.length, pending.offset, HANDSHAKE_RETRANSMISSION);
+ send_buffer->OnStreamDataRetransmitted(pending.offset, bytes_consumed);
+ if (bytes_consumed < pending.length) {
+ return;
+ }
+ }
+ }
+}
+
+void QuicCryptoStream::WritePendingRetransmission() {
+ while (HasPendingRetransmission()) {
+ StreamPendingRetransmission pending =
+ send_buffer().NextPendingRetransmission();
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ pending.offset, pending.offset + pending.length);
+ EncryptionLevel retransmission_encryption_level = ENCRYPTION_INITIAL;
+ // Determine the encryption level to write the retransmission
+ // at. The retransmission should be written at the same encryption level
+ // as the original transmission.
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ if (retransmission.Intersects(bytes_consumed_[i])) {
+ retransmission_encryption_level = static_cast<EncryptionLevel>(i);
+ retransmission.Intersection(bytes_consumed_[i]);
+ break;
+ }
+ }
+ pending.offset = retransmission.begin()->min();
+ pending.length =
+ retransmission.begin()->max() - retransmission.begin()->min();
+ QuicConsumedData consumed = RetransmitStreamDataAtLevel(
+ pending.offset, pending.length, retransmission_encryption_level,
+ HANDSHAKE_RETRANSMISSION);
+ if (consumed.bytes_consumed < pending.length) {
+ // The connection is write blocked.
+ break;
+ }
+ }
+}
+
+bool QuicCryptoStream::RetransmitStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool /*fin*/,
+ TransmissionType type) {
+ QUICHE_DCHECK(type == HANDSHAKE_RETRANSMISSION || type == PTO_RETRANSMISSION);
+ QuicIntervalSet<QuicStreamOffset> retransmission(offset,
+ offset + data_length);
+ // Determine the encryption level to send data. This only needs to be once as
+ // [offset, offset + data_length) is guaranteed to be in the same packet.
+ EncryptionLevel send_encryption_level = ENCRYPTION_INITIAL;
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ if (retransmission.Intersects(bytes_consumed_[i])) {
+ send_encryption_level = static_cast<EncryptionLevel>(i);
+ break;
+ }
+ }
+ retransmission.Difference(bytes_acked());
+ for (const auto& interval : retransmission) {
+ QuicStreamOffset retransmission_offset = interval.min();
+ QuicByteCount retransmission_length = interval.max() - interval.min();
+ QuicConsumedData consumed = RetransmitStreamDataAtLevel(
+ retransmission_offset, retransmission_length, send_encryption_level,
+ type);
+ if (consumed.bytes_consumed < retransmission_length) {
+ // The connection is write blocked.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QuicConsumedData QuicCryptoStream::RetransmitStreamDataAtLevel(
+ QuicStreamOffset retransmission_offset, QuicByteCount retransmission_length,
+ EncryptionLevel encryption_level, TransmissionType type) {
+ QUICHE_DCHECK(type == HANDSHAKE_RETRANSMISSION || type == PTO_RETRANSMISSION);
+ const auto consumed = stream_delegate()->WritevData(
+ id(), retransmission_length, retransmission_offset, NO_FIN, type,
+ encryption_level);
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
+ << " is forced to retransmit stream data ["
+ << retransmission_offset << ", "
+ << retransmission_offset + retransmission_length
+ << "), with encryption level: " << encryption_level
+ << ", consumed: " << consumed;
+ OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed,
+ consumed.fin_consumed);
+
+ return consumed;
+}
+
+uint64_t QuicCryptoStream::crypto_bytes_read() const {
+ if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ return stream_bytes_read();
+ }
+ uint64_t bytes_read = 0;
+ for (EncryptionLevel level : AllEncryptionLevels()) {
+ bytes_read += substreams_[level].sequencer.NumBytesConsumed();
+ }
+ return bytes_read;
+}
+
+uint64_t QuicCryptoStream::BytesReadOnLevel(EncryptionLevel level) const {
+ return substreams_[level].sequencer.NumBytesConsumed();
+}
+
+bool QuicCryptoStream::WriteCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ QUIC_BUG_IF(quic_bug_12573_4,
+ !QuicVersionUsesCryptoFrames(session()->transport_version()))
+ << "Versions less than 47 don't write CRYPTO frames (2)";
+ return substreams_[level].send_buffer.WriteStreamData(offset, data_length,
+ writer);
+}
+
+void QuicCryptoStream::OnCryptoFrameLost(QuicCryptoFrame* crypto_frame) {
+ QUIC_BUG_IF(quic_bug_12573_5,
+ !QuicVersionUsesCryptoFrames(session()->transport_version()))
+ << "Versions less than 47 don't lose CRYPTO frames";
+ substreams_[crypto_frame->level].send_buffer.OnStreamDataLost(
+ crypto_frame->offset, crypto_frame->data_length);
+}
+
+bool QuicCryptoStream::RetransmitData(QuicCryptoFrame* crypto_frame,
+ TransmissionType type) {
+ QUIC_BUG_IF(quic_bug_12573_6,
+ !QuicVersionUsesCryptoFrames(session()->transport_version()))
+ << "Versions less than 47 don't retransmit CRYPTO frames";
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ crypto_frame->offset, crypto_frame->offset + crypto_frame->data_length);
+ QuicStreamSendBuffer* send_buffer =
+ &substreams_[crypto_frame->level].send_buffer;
+ retransmission.Difference(send_buffer->bytes_acked());
+ if (retransmission.Empty()) {
+ return true;
+ }
+ for (const auto& interval : retransmission) {
+ size_t retransmission_offset = interval.min();
+ size_t retransmission_length = interval.max() - interval.min();
+ size_t bytes_consumed = stream_delegate()->SendCryptoData(
+ crypto_frame->level, retransmission_length, retransmission_offset,
+ type);
+ send_buffer->OnStreamDataRetransmitted(retransmission_offset,
+ bytes_consumed);
+ if (bytes_consumed < retransmission_length) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void QuicCryptoStream::WriteBufferedCryptoFrames() {
+ QUIC_BUG_IF(quic_bug_12573_7,
+ !QuicVersionUsesCryptoFrames(session()->transport_version()))
+ << "Versions less than 47 don't use CRYPTO frames";
+ for (EncryptionLevel level : AllEncryptionLevels()) {
+ QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+ const size_t data_length =
+ send_buffer->stream_offset() - send_buffer->stream_bytes_written();
+ if (data_length == 0) {
+ // No buffered data for this encryption level.
+ continue;
+ }
+ size_t bytes_consumed = stream_delegate()->SendCryptoData(
+ level, data_length, send_buffer->stream_bytes_written(),
+ NOT_RETRANSMISSION);
+ send_buffer->OnStreamDataConsumed(bytes_consumed);
+ if (bytes_consumed < data_length) {
+ // Connection is write blocked.
+ break;
+ }
+ }
+}
+
+bool QuicCryptoStream::HasBufferedCryptoFrames() const {
+ QUIC_BUG_IF(quic_bug_12573_8,
+ !QuicVersionUsesCryptoFrames(session()->transport_version()))
+ << "Versions less than 47 don't use CRYPTO frames";
+ for (EncryptionLevel level : AllEncryptionLevels()) {
+ const QuicStreamSendBuffer& send_buffer = substreams_[level].send_buffer;
+ QUICHE_DCHECK_GE(send_buffer.stream_offset(),
+ send_buffer.stream_bytes_written());
+ if (send_buffer.stream_offset() > send_buffer.stream_bytes_written()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuicCryptoStream::IsFrameOutstanding(EncryptionLevel level, size_t offset,
+ size_t length) const {
+ if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ // This only happens if a client was originally configured for a version
+ // greater than 45, but received a version negotiation packet and is
+ // attempting to retransmit for a version less than 47. Outside of tests,
+ // this is a misconfiguration of the client, and this connection will be
+ // doomed. Return false here to avoid trying to retransmit CRYPTO frames on
+ // the wrong transport version.
+ return false;
+ }
+ return substreams_[level].send_buffer.IsStreamDataOutstanding(offset, length);
+}
+
+bool QuicCryptoStream::IsWaitingForAcks() const {
+ if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
+ return QuicStream::IsWaitingForAcks();
+ }
+ for (EncryptionLevel level : AllEncryptionLevels()) {
+ if (substreams_[level].send_buffer.stream_bytes_outstanding()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicCryptoStream::CryptoSubstream::CryptoSubstream(
+ QuicCryptoStream* crypto_stream, EncryptionLevel)
+ : sequencer(crypto_stream),
+ send_buffer(crypto_stream->session()
+ ->connection()
+ ->helper()
+ ->GetStreamSendBufferAllocator()) {}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic