Implement QUIC Header Protection
gfe-relnote: Protected by QUIC_VERSION_99
PiperOrigin-RevId: 247137283
Change-Id: I1deb08d304b7739c3c8fa6b995e55fbd8652dc1e
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index f3390f0..8199307 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -113,6 +113,9 @@
max_packet_length_ = length;
max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_);
+ QUIC_BUG_IF(max_plaintext_size_ - PacketHeaderSize() <
+ MinPlaintextPacketSize())
+ << "Attempted to set max packet length too small";
}
// Stops serializing version of the protocol in packets sent after this call.
@@ -439,6 +442,7 @@
max_plaintext_size_ - writer.length() - min_frame_size;
const size_t bytes_consumed =
std::min<size_t>(available_size, remaining_data_size);
+ const size_t plaintext_bytes_written = min_frame_size + bytes_consumed;
const bool set_fin = fin && (bytes_consumed == remaining_data_size);
QuicStreamFrame frame(id, set_fin, stream_offset, bytes_consumed);
@@ -459,6 +463,12 @@
QUIC_BUG << "AppendStreamFrame failed";
return;
}
+ if (plaintext_bytes_written < MinPlaintextPacketSize() &&
+ !writer.WritePaddingBytes(MinPlaintextPacketSize() -
+ plaintext_bytes_written)) {
+ QUIC_BUG << "Unable to add padding bytes";
+ return;
+ }
if (!framer_->WriteIetfLongHeaderLength(header, &writer, length_field_offset,
packet_.encryption_level)) {
@@ -537,11 +547,7 @@
if (!queued_frames_.empty()) {
return packet_size_;
}
- packet_size_ = GetPacketHeaderSize(
- framer_->transport_version(), GetDestinationConnectionIdLength(),
- GetSourceConnectionIdLength(), IncludeVersionInHeader(),
- IncludeNonceInPublicHeader(), GetPacketNumberLength(),
- GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength());
+ packet_size_ = PacketHeaderSize();
return packet_size_;
}
@@ -771,6 +777,14 @@
return packet_.packet_number_length;
}
+size_t QuicPacketCreator::PacketHeaderSize() const {
+ return GetPacketHeaderSize(
+ framer_->transport_version(), GetDestinationConnectionIdLength(),
+ GetSourceConnectionIdLength(), IncludeVersionInHeader(),
+ IncludeNonceInPublicHeader(), GetPacketNumberLength(),
+ GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength());
+}
+
QuicVariableLengthIntegerLength QuicPacketCreator::GetRetryTokenLengthLength()
const {
if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) &&
@@ -909,11 +923,24 @@
needs_full_padding_ = true;
}
- if (!needs_full_padding_ && pending_padding_bytes_ == 0) {
+ // Header protection requires a minimum plaintext packet size.
+ size_t extra_padding_bytes = 0;
+ if (framer_->version().HasHeaderProtection()) {
+ size_t frame_bytes = PacketSize() - PacketHeaderSize();
+
+ if (frame_bytes + pending_padding_bytes_ < MinPlaintextPacketSize() &&
+ !needs_full_padding_) {
+ extra_padding_bytes = MinPlaintextPacketSize() - frame_bytes;
+ }
+ }
+
+ if (!needs_full_padding_ && pending_padding_bytes_ == 0 &&
+ extra_padding_bytes == 0) {
// Do not need padding.
return;
}
+ int padding_bytes = -1;
if (needs_full_padding_) {
// Full padding does not consume pending padding bytes.
packet_.num_padding_bytes = -1;
@@ -921,11 +948,12 @@
packet_.num_padding_bytes =
std::min<int16_t>(pending_padding_bytes_, BytesFree());
pending_padding_bytes_ -= packet_.num_padding_bytes;
+ padding_bytes =
+ std::max<int16_t>(packet_.num_padding_bytes, extra_padding_bytes);
}
- bool success =
- AddFrame(QuicFrame(QuicPaddingFrame(packet_.num_padding_bytes)), false,
- packet_.transmission_type);
+ bool success = AddFrame(QuicFrame(QuicPaddingFrame(padding_bytes)), false,
+ packet_.transmission_type);
DCHECK(success);
}
@@ -1032,5 +1060,31 @@
packet_.encryption_level < ENCRYPTION_FORWARD_SECURE;
}
+size_t QuicPacketCreator::MinPlaintextPacketSize() const {
+ if (!framer_->version().HasHeaderProtection()) {
+ return 0;
+ }
+ // Header protection samples 16 bytes of ciphertext starting 4 bytes after the
+ // packet number. In IETF QUIC, all AEAD algorithms have a 16-byte auth tag
+ // (i.e. the ciphertext is 16 bytes larger than the plaintext). Since packet
+ // numbers could be as small as 1 byte, but the sample starts 4 bytes after
+ // the packet number, at least 3 bytes of plaintext are needed to make sure
+ // that there is enough ciphertext to sample.
+ //
+ // Google QUIC crypto uses different AEAD algorithms - in particular the auth
+ // tags are only 12 bytes instead of 16 bytes. Since the auth tag is 4 bytes
+ // shorter, 4 more bytes of plaintext are needed to guarantee there is enough
+ // ciphertext to sample.
+ //
+ // This method could check for PROTOCOL_TLS1_3 vs PROTOCOL_QUIC_CRYPTO and
+ // return 3 when TLS 1.3 is in use (the use of IETF vs Google QUIC crypters is
+ // determined based on the handshake protocol used). However, even when TLS
+ // 1.3 is used, unittests still use NullEncrypter/NullDecrypter (and other
+ // test crypters) which also only use 12 byte tags.
+ //
+ // TODO(nharper): Set this based on the handshake protocol in use.
+ return 7;
+}
+
#undef ENDPOINT // undef for jumbo builds
} // namespace quic