blob: 81995713d612d50e819feeefabfe677812a59d1c [file] [log] [blame]
// 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 "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
#include "net/third_party/quiche/src/quic/core/quic_connection.h"
#include "net/third_party/quiche/src/quic/core/quic_session.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
namespace quic {
#define ENDPOINT \
(session()->perspective() == Perspective::IS_SERVER ? "Server: " \
: "Client:" \
" ")
QuicCryptoStream::QuicCryptoStream(QuicSession* session)
: QuicStream(QuicUtils::GetCryptoStreamId(
session->connection()->transport_version()),
session,
/*is_static=*/true,
BIDIRECTIONAL) {
// The crypto stream is exempt from connection level flow control.
DisableConnectionFlowControlForThisStream();
}
QuicCryptoStream::~QuicCryptoStream() {}
// static
QuicByteCount QuicCryptoStream::CryptoMessageFramingOverhead(
QuicTransportVersion version) {
return QuicPacketCreator::StreamFramePacketOverhead(
version, PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
/*include_version=*/true,
/*include_diversification_nonce=*/true,
version > QUIC_VERSION_43 ? PACKET_4BYTE_PACKET_NUMBER
: PACKET_1BYTE_PACKET_NUMBER,
/*offset=*/0);
}
void QuicCryptoStream::OnDataAvailable() {
struct iovec iov;
// When calling CryptoMessageParser::ProcessInput, an EncryptionLevel needs to
// be provided. Note that in the general case, the following code may be
// incorrect. When a stream frame is added to the sequencer, the encryption
// level provided by the connection will be the encryption level that the
// frame was received under, but stream frames can be received out of order.
// If a later stream frame at a higher encryption level is received before an
// earlier stream frame at a lower encryption level, this code will call
// CryptoMessageParser::Process input with the data from both frames, but
// indicate that they both were received at the higher encryption level.
//
// For QUIC crypto, this is not a problem, because the CryptoFramer (which
// implements CryptoMessageParser) ignores the EncryptionLevel passed into
// ProcessInput.
//
// For the TLS handshake, this does not cause an issue for the transition from
// initial encryption (ClientHello, HelloRetryRequest, and ServerHello) to
// handshake encryption, as all data from the initial encryption level is
// needed to derive the handshake encryption keys. For the transition from
// handshake encryption to 1-RTT application data encryption, all messages at
// the handshake encryption level *except* the client Finished are needed. The
// only place this logic would be broken is if a server receives a crypto
// handshake message that is encrypted under the 1-RTT data keys before
// receiving the client's Finished message (under handshake encryption keys).
// Right now, this implementation of TLS in QUIC does not support doing that,
// but it is possible (although unlikely) that other implementations could.
// Therefore, this needs to be fixed before the TLS handshake is enabled.
//
// TODO(nharper): Use a more robust and correct mechanism to provide the
// EncryptionLevel to CryptoMessageParser::ProcessInput. This must be done
// before enabling the TLS handshake.
EncryptionLevel level = session()->connection()->last_decrypted_level();
while (sequencer()->GetReadableRegion(&iov)) {
QuicStringPiece data(static_cast<char*>(iov.iov_base), iov.iov_len);
if (!crypto_message_parser()->ProcessInput(data, level)) {
CloseConnectionWithDetails(crypto_message_parser()->error(),
crypto_message_parser()->error_detail());
return;
}
sequencer()->MarkConsumed(iov.iov_len);
if (handshake_confirmed() &&
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();
}
}
}
bool QuicCryptoStream::ExportKeyingMaterial(QuicStringPiece label,
QuicStringPiece context,
size_t result_len,
QuicString* result) const {
if (!handshake_confirmed()) {
QUIC_DLOG(ERROR) << "ExportKeyingMaterial was called before forward-secure"
<< "encryption was established.";
return false;
}
return CryptoUtils::ExportKeyingMaterial(
crypto_negotiated_params().subkey_secret, label, context, result_len,
result);
}
void QuicCryptoStream::WriteCryptoData(EncryptionLevel level,
QuicStringPiece data) {
// TODO(nharper): This approach to writing data, by setting the encryption
// level, calling WriteOrBufferData, and then restoring the encryption level,
// is fragile and assumes that the data gets received by the peer when
// WriteOrBufferData is called. There is no guarantee that data will get
// retransmitted at the correct level. This needs to be redone with the
// cleanup for OnDataAvailable by managing the streams/crypto frames for
// encryption levels separately.
EncryptionLevel current_level = session()->connection()->encryption_level();
session()->connection()->SetDefaultEncryptionLevel(level);
WriteOrBufferData(data, /* fin */ false, /* ack_listener */ nullptr);
if (current_level == ENCRYPTION_FORWARD_SECURE && level != current_level) {
session()->connection()->SetDefaultEncryptionLevel(current_level);
}
}
void QuicCryptoStream::OnSuccessfulVersionNegotiation(
const ParsedQuicVersion& version) {}
void QuicCryptoStream::NeuterUnencryptedStreamData() {
for (const auto& interval : bytes_consumed_[ENCRYPTION_NONE]) {
QuicByteCount newly_acked_length = 0;
send_buffer().OnStreamDataAcked(
interval.min(), interval.max() - interval.min(), &newly_acked_length);
}
}
void QuicCryptoStream::OnStreamDataConsumed(size_t bytes_consumed) {
if (bytes_consumed > 0) {
bytes_consumed_[session()->connection()->encryption_level()].Add(
stream_bytes_written(), stream_bytes_written() + bytes_consumed);
}
QuicStream::OnStreamDataConsumed(bytes_consumed);
}
void QuicCryptoStream::WritePendingRetransmission() {
while (HasPendingRetransmission()) {
StreamPendingRetransmission pending =
send_buffer().NextPendingRetransmission();
QuicIntervalSet<QuicStreamOffset> retransmission(
pending.offset, pending.offset + pending.length);
EncryptionLevel retransmission_encryption_level = ENCRYPTION_NONE;
// 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();
EncryptionLevel current_encryption_level =
session()->connection()->encryption_level();
// Set appropriate encryption level.
session()->connection()->SetDefaultEncryptionLevel(
retransmission_encryption_level);
QuicConsumedData consumed = session()->WritevData(
this, id(), pending.length, pending.offset, NO_FIN);
QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
<< " tries to retransmit stream data [" << pending.offset
<< ", " << pending.offset + pending.length
<< ") with encryption level: "
<< retransmission_encryption_level
<< ", consumed: " << consumed;
OnStreamFrameRetransmitted(pending.offset, consumed.bytes_consumed,
consumed.fin_consumed);
// Restore encryption level.
session()->connection()->SetDefaultEncryptionLevel(
current_encryption_level);
if (consumed.bytes_consumed < pending.length) {
// The connection is write blocked.
break;
}
}
}
bool QuicCryptoStream::RetransmitStreamData(QuicStreamOffset offset,
QuicByteCount data_length,
bool /*fin*/) {
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_NONE;
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());
EncryptionLevel current_encryption_level =
session()->connection()->encryption_level();
for (const auto& interval : retransmission) {
QuicStreamOffset retransmission_offset = interval.min();
QuicByteCount retransmission_length = interval.max() - interval.min();
// Set appropriate encryption level.
session()->connection()->SetDefaultEncryptionLevel(send_encryption_level);
QuicConsumedData consumed = session()->WritevData(
this, id(), retransmission_length, retransmission_offset, NO_FIN);
QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
<< " is forced to retransmit stream data ["
<< retransmission_offset << ", "
<< retransmission_offset + retransmission_length
<< "), with encryption level: " << send_encryption_level
<< ", consumed: " << consumed;
OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed,
consumed.fin_consumed);
// Restore encryption level.
session()->connection()->SetDefaultEncryptionLevel(
current_encryption_level);
if (consumed.bytes_consumed < retransmission_length) {
// The connection is write blocked.
return false;
}
}
return true;
}
#undef ENDPOINT // undef for jumbo builds
} // namespace quic