Add support for draft-27

This CL updates v99 from draft 26 to draft 27. The main change is a rewrite of how transport parameters are read and written. This CL also replaces quic_enable_version_draft_25_v2 with quic_enable_version_draft_25_v3 to ensure that we only enable draft25 with the new code.

gfe-relnote: refactor transport parameter encoding code, protected by disabled flag quic_enable_version_draft_25_v3
PiperOrigin-RevId: 298427763
Change-Id: I2417b25584d914d38a55155ffddbac6e073fcd80
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
index a7df751..0f184d8 100644
--- a/quic/core/crypto/transport_parameters.cc
+++ b/quic/core/crypto/transport_parameters.cc
@@ -9,7 +9,6 @@
 #include <forward_list>
 #include <utility>
 
-#include "third_party/boringssl/src/include/openssl/bytestring.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
 #include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
 #include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
@@ -18,6 +17,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
 
@@ -28,7 +28,7 @@
 // When parameters are encoded, one of these enum values is used to indicate
 // which parameter is encoded. The supported draft version is noted in
 // transport_parameters.h.
-enum TransportParameters::TransportParameterId : uint16_t {
+enum TransportParameters::TransportParameterId : uint64_t {
   kOriginalConnectionId = 0,
   kIdleTimeout = 1,
   kStatelessResetToken = 2,
@@ -107,6 +107,89 @@
   return "Unknown(" + quiche::QuicheTextUtils::Uint64ToString(param_id) + ")";
 }
 
+bool WriteTransportParameterId(
+    QuicDataWriter* writer,
+    TransportParameters::TransportParameterId param_id,
+    ParsedQuicVersion version) {
+  if (version.HasVarIntTransportParams()) {
+    if (!writer->WriteVarInt62(param_id)) {
+      QUIC_BUG << "Failed to write param_id for "
+               << TransportParameterIdToString(param_id);
+      return false;
+    }
+  } else {
+    if (param_id > std::numeric_limits<uint16_t>::max()) {
+      QUIC_BUG << "Cannot serialize transport parameter "
+               << TransportParameterIdToString(param_id) << " with version "
+               << version;
+      return false;
+    }
+    if (!writer->WriteUInt16(param_id)) {
+      QUIC_BUG << "Failed to write param_id16 for "
+               << TransportParameterIdToString(param_id);
+      return false;
+    }
+  }
+  return true;
+}
+
+bool WriteTransportParameterLength(QuicDataWriter* writer,
+                                   uint64_t length,
+                                   ParsedQuicVersion version) {
+  if (version.HasVarIntTransportParams()) {
+    return writer->WriteVarInt62(length);
+  }
+  if (length > std::numeric_limits<uint16_t>::max()) {
+    QUIC_BUG << "Cannot serialize transport parameter length " << length
+             << " with version " << version;
+    return false;
+  }
+  return writer->WriteUInt16(length);
+}
+
+bool WriteTransportParameterStringPiece(QuicDataWriter* writer,
+                                        quiche::QuicheStringPiece value,
+                                        ParsedQuicVersion version) {
+  if (version.HasVarIntTransportParams()) {
+    return writer->WriteStringPieceVarInt62(value);
+  }
+  return writer->WriteStringPiece16(value);
+}
+
+bool ReadTransportParameterId(
+    QuicDataReader* reader,
+    ParsedQuicVersion version,
+    TransportParameters::TransportParameterId* out_param_id) {
+  if (version.HasVarIntTransportParams()) {
+    uint64_t param_id64;
+    if (!reader->ReadVarInt62(&param_id64)) {
+      QUIC_BUG << "Failed to read param_id";
+      return false;
+    }
+    *out_param_id =
+        static_cast<TransportParameters::TransportParameterId>(param_id64);
+  } else {
+    uint16_t param_id16;
+    if (!reader->ReadUInt16(&param_id16)) {
+      QUIC_BUG << "Failed to read param_id16";
+      return false;
+    }
+    *out_param_id =
+        static_cast<TransportParameters::TransportParameterId>(param_id16);
+  }
+  return true;
+}
+
+bool ReadTransportParameterLengthAndValue(
+    QuicDataReader* reader,
+    ParsedQuicVersion version,
+    quiche::QuicheStringPiece* out_value) {
+  if (version.HasVarIntTransportParams()) {
+    return reader->ReadStringPieceVarInt62(out_value);
+  }
+  return reader->ReadStringPiece16(out_value);
+}
+
 }  // namespace
 
 TransportParameters::IntegerParameter::IntegerParameter(
@@ -119,7 +202,7 @@
       default_value_(default_value),
       min_value_(min_value),
       max_value_(max_value),
-      has_been_read_from_cbs_(false) {
+      has_been_read_(false) {
   DCHECK_LE(min_value, default_value);
   DCHECK_LE(default_value, max_value);
   DCHECK_LE(max_value, kVarInt62MaxValue);
@@ -145,47 +228,56 @@
   return min_value_ <= value_ && value_ <= max_value_;
 }
 
-bool TransportParameters::IntegerParameter::WriteToCbb(CBB* parent_cbb) const {
+bool TransportParameters::IntegerParameter::Write(
+    QuicDataWriter* writer,
+    ParsedQuicVersion version) const {
   DCHECK(IsValid());
   if (value_ == default_value_) {
     // Do not write if the value is default.
     return true;
   }
-  uint8_t encoded_data[sizeof(uint64_t)] = {};
-  QuicDataWriter writer(sizeof(encoded_data),
-                        reinterpret_cast<char*>(encoded_data));
-  writer.WriteVarInt62(value_);
-  const uint16_t value_length = writer.length();
-  DCHECK_LE(value_length, sizeof(encoded_data));
-  const bool ok = CBB_add_u16(parent_cbb, param_id_) &&
-                  CBB_add_u16(parent_cbb, value_length) &&
-                  CBB_add_bytes(parent_cbb, encoded_data, value_length);
-  QUIC_BUG_IF(!ok) << "Failed to write " << this;
-  return ok;
+  if (!WriteTransportParameterId(writer, param_id_, version)) {
+    QUIC_BUG << "Failed to write param_id for " << *this;
+    return false;
+  }
+  const QuicVariableLengthIntegerLength value_length =
+      QuicDataWriter::GetVarInt62Len(value_);
+  if (version.HasVarIntTransportParams()) {
+    if (!writer->WriteVarInt62(value_length)) {
+      QUIC_BUG << "Failed to write value_length for " << *this;
+      return false;
+    }
+  } else {
+    if (!writer->WriteUInt16(value_length)) {
+      QUIC_BUG << "Failed to write value_length16 for " << *this;
+      return false;
+    }
+  }
+  if (!writer->WriteVarInt62(value_, value_length)) {
+    QUIC_BUG << "Failed to write value for " << *this;
+    return false;
+  }
+  return true;
 }
 
-bool TransportParameters::IntegerParameter::ReadFromCbs(CBS* const value_cbs) {
-  if (has_been_read_from_cbs_) {
-    QUIC_DLOG(ERROR) << "Received a second "
-                     << TransportParameterIdToString(param_id_);
+bool TransportParameters::IntegerParameter::Read(QuicDataReader* reader,
+                                                 std::string* error_details) {
+  if (has_been_read_) {
+    *error_details =
+        "Received a second " + TransportParameterIdToString(param_id_);
     return false;
   }
-  has_been_read_from_cbs_ = true;
-  QuicDataReader reader(reinterpret_cast<const char*>(CBS_data(value_cbs)),
-                        CBS_len(value_cbs));
-  QuicVariableLengthIntegerLength value_length = reader.PeekVarInt62Length();
-  if (value_length == 0 || !reader.ReadVarInt62(&value_)) {
-    QUIC_DLOG(ERROR) << "Failed to parse value for "
-                     << TransportParameterIdToString(param_id_);
+  has_been_read_ = true;
+
+  if (!reader->ReadVarInt62(&value_)) {
+    *error_details =
+        "Failed to parse value for " + TransportParameterIdToString(param_id_);
     return false;
   }
-  if (!reader.IsDoneReading()) {
-    QUIC_DLOG(ERROR) << "Received unexpected " << reader.BytesRemaining()
-                     << " bytes after parsing " << this;
-    return false;
-  }
-  if (!CBS_skip(value_cbs, value_length)) {
-    QUIC_BUG << "Failed to advance CBS past value for " << this;
+  if (!reader->IsDoneReading()) {
+    *error_details =
+        quiche::QuicheStrCat("Received unexpected ", reader->BytesRemaining(),
+                             " bytes after parsing ", this->ToString(false));
     return false;
   }
   return true;
@@ -325,39 +417,40 @@
 // ParseTransportParameters.
 {}
 
-bool TransportParameters::AreValid() const {
+bool TransportParameters::AreValid(std::string* error_details) const {
   DCHECK(perspective == Perspective::IS_CLIENT ||
          perspective == Perspective::IS_SERVER);
   if (perspective == Perspective::IS_CLIENT && !stateless_reset_token.empty()) {
-    QUIC_DLOG(ERROR) << "Client cannot send stateless reset token";
+    *error_details = "Client cannot send stateless reset token";
     return false;
   }
   if (perspective == Perspective::IS_CLIENT &&
       !original_connection_id.IsEmpty()) {
-    QUIC_DLOG(ERROR) << "Client cannot send original connection ID";
+    *error_details = "Client cannot send original connection ID";
     return false;
   }
   if (!stateless_reset_token.empty() &&
       stateless_reset_token.size() != kStatelessResetTokenLength) {
-    QUIC_DLOG(ERROR) << "Stateless reset token has bad length "
-                     << stateless_reset_token.size();
+    *error_details = quiche::QuicheStrCat(
+        "Stateless reset token has bad length ", stateless_reset_token.size());
     return false;
   }
   if (perspective == Perspective::IS_CLIENT && preferred_address) {
-    QUIC_DLOG(ERROR) << "Client cannot send preferred address";
+    *error_details = "Client cannot send preferred address";
     return false;
   }
   if (preferred_address && preferred_address->stateless_reset_token.size() !=
                                kStatelessResetTokenLength) {
-    QUIC_DLOG(ERROR)
-        << "Preferred address stateless reset token has bad length "
-        << preferred_address->stateless_reset_token.size();
+    *error_details = quiche::QuicheStrCat(
+        "Preferred address stateless reset token has bad length ",
+        preferred_address->stateless_reset_token.size());
     return false;
   }
   if (preferred_address &&
       (!preferred_address->ipv4_socket_address.host().IsIPv4() ||
        !preferred_address->ipv6_socket_address.host().IsIPv6())) {
     QUIC_BUG << "Preferred address family failure";
+    *error_details = "Internal preferred address family failure";
     return false;
   }
   const bool ok =
@@ -370,104 +463,112 @@
       ack_delay_exponent.IsValid() && max_ack_delay.IsValid() &&
       active_connection_id_limit.IsValid() && max_datagram_frame_size.IsValid();
   if (!ok) {
-    QUIC_DLOG(ERROR) << "Invalid transport parameters " << *this;
+    *error_details = "Invalid transport parameters " + this->ToString();
   }
   return ok;
 }
 
 TransportParameters::~TransportParameters() = default;
 
-bool SerializeTransportParameters(ParsedQuicVersion /*version*/,
+bool SerializeTransportParameters(ParsedQuicVersion version,
                                   const TransportParameters& in,
                                   std::vector<uint8_t>* out) {
-  if (!in.AreValid()) {
-    QUIC_DLOG(ERROR) << "Not serializing invalid transport parameters " << in;
+  std::string error_details;
+  if (!in.AreValid(&error_details)) {
+    QUIC_BUG << "Not serializing invalid transport parameters: "
+             << error_details;
     return false;
   }
   if (in.version == 0 || (in.perspective == Perspective::IS_SERVER &&
                           in.supported_versions.empty())) {
-    QUIC_DLOG(ERROR) << "Refusing to serialize without versions";
+    QUIC_BUG << "Refusing to serialize without versions";
     return false;
   }
 
-  bssl::ScopedCBB cbb;
   // Empirically transport parameters generally fit within 128 bytes.
-  // The CBB will grow to fit larger serializations if required.
-  if (!CBB_init(cbb.get(), 128)) {
-    QUIC_BUG << "Failed to initialize CBB for " << in;
-    return false;
-  }
+  // For now we hope this will be enough.
+  // TODO(dschinazi) make this grow if needed.
+  static const size_t kMaxTransportParametersLength = 4096;
+  out->resize(kMaxTransportParametersLength);
+  QuicDataWriter writer(out->size(), reinterpret_cast<char*>(out->data()));
 
-  CBB params;
-  // Add length of the transport parameters list.
-  if (!CBB_add_u16_length_prefixed(cbb.get(), &params)) {
-    QUIC_BUG << "Failed to write parameter length for " << in;
-    return false;
+  if (!version.HasVarIntTransportParams()) {
+    // Versions that do not use variable integer transport parameters carry
+    // a 16-bit length of the remaining transport parameters. We write 0 here
+    // to reserve 16 bits, and we fill it in at the end of this function.
+    // TODO(b/150465921) add support for doing this in QuicDataWriter.
+    if (!writer.WriteUInt16(0)) {
+      QUIC_BUG << "Failed to write transport parameter fake length prefix for "
+               << in;
+      return false;
+    }
   }
 
   // original_connection_id
-  CBB original_connection_id_param;
   if (!in.original_connection_id.IsEmpty()) {
     DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
-    if (!CBB_add_u16(&params, TransportParameters::kOriginalConnectionId) ||
-        !CBB_add_u16_length_prefixed(&params, &original_connection_id_param) ||
-        !CBB_add_bytes(
-            &original_connection_id_param,
-            reinterpret_cast<const uint8_t*>(in.original_connection_id.data()),
-            in.original_connection_id.length())) {
+    if (!WriteTransportParameterId(
+            &writer, TransportParameters::kOriginalConnectionId, version) ||
+        !WriteTransportParameterStringPiece(
+            &writer,
+            quiche::QuicheStringPiece(in.original_connection_id.data(),
+                                      in.original_connection_id.length()),
+            version)) {
       QUIC_BUG << "Failed to write original_connection_id "
                << in.original_connection_id << " for " << in;
       return false;
     }
   }
 
-  if (!in.idle_timeout_milliseconds.WriteToCbb(&params)) {
+  if (!in.idle_timeout_milliseconds.Write(&writer, version)) {
     QUIC_BUG << "Failed to write idle_timeout for " << in;
     return false;
   }
 
   // stateless_reset_token
-  CBB stateless_reset_token_param;
   if (!in.stateless_reset_token.empty()) {
     DCHECK_EQ(kStatelessResetTokenLength, in.stateless_reset_token.size());
     DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
-    if (!CBB_add_u16(&params, TransportParameters::kStatelessResetToken) ||
-        !CBB_add_u16_length_prefixed(&params, &stateless_reset_token_param) ||
-        !CBB_add_bytes(&stateless_reset_token_param,
-                       in.stateless_reset_token.data(),
-                       in.stateless_reset_token.size())) {
+    if (!WriteTransportParameterId(
+            &writer, TransportParameters::kStatelessResetToken, version) ||
+        !WriteTransportParameterStringPiece(
+            &writer,
+            quiche::QuicheStringPiece(
+                reinterpret_cast<const char*>(in.stateless_reset_token.data()),
+                in.stateless_reset_token.size()),
+            version)) {
       QUIC_BUG << "Failed to write stateless_reset_token of length "
                << in.stateless_reset_token.size() << " for " << in;
       return false;
     }
   }
 
-  if (!in.max_packet_size.WriteToCbb(&params) ||
-      !in.initial_max_data.WriteToCbb(&params) ||
-      !in.initial_max_stream_data_bidi_local.WriteToCbb(&params) ||
-      !in.initial_max_stream_data_bidi_remote.WriteToCbb(&params) ||
-      !in.initial_max_stream_data_uni.WriteToCbb(&params) ||
-      !in.initial_max_streams_bidi.WriteToCbb(&params) ||
-      !in.initial_max_streams_uni.WriteToCbb(&params) ||
-      !in.ack_delay_exponent.WriteToCbb(&params) ||
-      !in.max_ack_delay.WriteToCbb(&params) ||
-      !in.active_connection_id_limit.WriteToCbb(&params) ||
-      !in.max_datagram_frame_size.WriteToCbb(&params)) {
+  if (!in.max_packet_size.Write(&writer, version) ||
+      !in.initial_max_data.Write(&writer, version) ||
+      !in.initial_max_stream_data_bidi_local.Write(&writer, version) ||
+      !in.initial_max_stream_data_bidi_remote.Write(&writer, version) ||
+      !in.initial_max_stream_data_uni.Write(&writer, version) ||
+      !in.initial_max_streams_bidi.Write(&writer, version) ||
+      !in.initial_max_streams_uni.Write(&writer, version) ||
+      !in.ack_delay_exponent.Write(&writer, version) ||
+      !in.max_ack_delay.Write(&writer, version) ||
+      !in.active_connection_id_limit.Write(&writer, version) ||
+      !in.max_datagram_frame_size.Write(&writer, version)) {
     QUIC_BUG << "Failed to write integers for " << in;
     return false;
   }
 
   // disable_migration
   if (in.disable_migration) {
-    if (!CBB_add_u16(&params, TransportParameters::kDisableMigration) ||
-        !CBB_add_u16(&params, 0u)) {  // 0 is the length of this parameter.
+    if (!WriteTransportParameterId(
+            &writer, TransportParameters::kDisableMigration, version) ||
+        !WriteTransportParameterLength(&writer, /*length=*/0, version)) {
       QUIC_BUG << "Failed to write disable_migration for " << in;
       return false;
     }
   }
 
   // preferred_address
-  CBB preferred_address_params, preferred_address_connection_id_param;
   if (in.preferred_address) {
     std::string v4_address_bytes =
         in.preferred_address->ipv4_socket_address.host().ToPackedString();
@@ -479,45 +580,39 @@
       QUIC_BUG << "Bad lengths " << *in.preferred_address;
       return false;
     }
-    if (!CBB_add_u16(&params, TransportParameters::kPreferredAddress) ||
-        !CBB_add_u16_length_prefixed(&params, &preferred_address_params) ||
-        !CBB_add_bytes(
-            &preferred_address_params,
-            reinterpret_cast<const uint8_t*>(v4_address_bytes.data()),
-            v4_address_bytes.length()) ||
-        !CBB_add_u16(&preferred_address_params,
-                     in.preferred_address->ipv4_socket_address.port()) ||
-        !CBB_add_bytes(
-            &preferred_address_params,
-            reinterpret_cast<const uint8_t*>(v6_address_bytes.data()),
-            v6_address_bytes.length()) ||
-        !CBB_add_u16(&preferred_address_params,
-                     in.preferred_address->ipv6_socket_address.port()) ||
-        !CBB_add_u8_length_prefixed(&preferred_address_params,
-                                    &preferred_address_connection_id_param) ||
-        !CBB_add_bytes(&preferred_address_connection_id_param,
-                       reinterpret_cast<const uint8_t*>(
-                           in.preferred_address->connection_id.data()),
-                       in.preferred_address->connection_id.length()) ||
-        !CBB_add_bytes(&preferred_address_params,
-                       in.preferred_address->stateless_reset_token.data(),
-                       in.preferred_address->stateless_reset_token.size())) {
+    const uint64_t preferred_address_length =
+        v4_address_bytes.length() + /* IPv4 port */ sizeof(uint16_t) +
+        v6_address_bytes.length() + /* IPv6 port */ sizeof(uint16_t) +
+        /* connection ID length byte */ sizeof(uint8_t) +
+        in.preferred_address->connection_id.length() +
+        in.preferred_address->stateless_reset_token.size();
+    if (!WriteTransportParameterId(
+            &writer, TransportParameters::kPreferredAddress, version) ||
+        !WriteTransportParameterLength(&writer, preferred_address_length,
+                                       version) ||
+        !writer.WriteStringPiece(v4_address_bytes) ||
+        !writer.WriteUInt16(in.preferred_address->ipv4_socket_address.port()) ||
+        !writer.WriteStringPiece(v6_address_bytes) ||
+        !writer.WriteUInt16(in.preferred_address->ipv6_socket_address.port()) ||
+        !writer.WriteUInt8(in.preferred_address->connection_id.length()) ||
+        !writer.WriteBytes(in.preferred_address->connection_id.data(),
+                           in.preferred_address->connection_id.length()) ||
+        !writer.WriteBytes(
+            in.preferred_address->stateless_reset_token.data(),
+            in.preferred_address->stateless_reset_token.size())) {
       QUIC_BUG << "Failed to write preferred_address for " << in;
       return false;
     }
   }
 
   // Google-specific non-standard parameter.
-  CBB google_quic_params;
   if (in.google_quic_params) {
     const QuicData& serialized_google_quic_params =
         in.google_quic_params->GetSerialized();
-    if (!CBB_add_u16(&params, TransportParameters::kGoogleQuicParam) ||
-        !CBB_add_u16_length_prefixed(&params, &google_quic_params) ||
-        !CBB_add_bytes(&google_quic_params,
-                       reinterpret_cast<const uint8_t*>(
-                           serialized_google_quic_params.data()),
-                       serialized_google_quic_params.length())) {
+    if (!WriteTransportParameterId(
+            &writer, TransportParameters::kGoogleQuicParam, version) ||
+        !WriteTransportParameterStringPiece(
+            &writer, serialized_google_quic_params.AsStringPiece(), version)) {
       QUIC_BUG << "Failed to write Google params of length "
                << serialized_google_quic_params.length() << " for " << in;
       return false;
@@ -525,51 +620,64 @@
   }
 
   // Google-specific version extension.
-  CBB google_version_params;
-  if (!CBB_add_u16(&params, TransportParameters::kGoogleQuicVersion) ||
-      !CBB_add_u16_length_prefixed(&params, &google_version_params) ||
-      !CBB_add_u32(&google_version_params, in.version)) {
+  static_assert(sizeof(QuicVersionLabel) == sizeof(uint32_t), "bad length");
+  uint64_t google_version_length = sizeof(in.version);
+  if (in.perspective == Perspective::IS_SERVER) {
+    google_version_length +=
+        /* versions length */ sizeof(uint8_t) +
+        sizeof(QuicVersionLabel) * in.supported_versions.size();
+  }
+  if (!WriteTransportParameterId(
+          &writer, TransportParameters::kGoogleQuicVersion, version) ||
+      !WriteTransportParameterLength(&writer, google_version_length, version) ||
+      !writer.WriteUInt32(in.version)) {
     QUIC_BUG << "Failed to write Google version extension for " << in;
     return false;
   }
-  CBB versions;
   if (in.perspective == Perspective::IS_SERVER) {
-    if (!CBB_add_u8_length_prefixed(&google_version_params, &versions)) {
+    if (!writer.WriteUInt8(sizeof(QuicVersionLabel) *
+                           in.supported_versions.size())) {
       QUIC_BUG << "Failed to write versions length for " << in;
       return false;
     }
-    for (QuicVersionLabel version : in.supported_versions) {
-      if (!CBB_add_u32(&versions, version)) {
+    for (QuicVersionLabel version_label : in.supported_versions) {
+      if (!writer.WriteUInt32(version_label)) {
         QUIC_BUG << "Failed to write supported version for " << in;
         return false;
       }
     }
   }
 
-  auto custom_parameters = std::make_unique<CBB[]>(in.custom_parameters.size());
-  int i = 0;
   for (const auto& kv : in.custom_parameters) {
-    CBB* custom_parameter = &custom_parameters[i++];
     QUIC_BUG_IF(kv.first < 0xff00) << "custom_parameters should not be used "
                                       "for non-private use parameters";
-    if (!CBB_add_u16(&params, kv.first) ||
-        !CBB_add_u16_length_prefixed(&params, custom_parameter) ||
-        !CBB_add_bytes(custom_parameter,
-                       reinterpret_cast<const uint8_t*>(kv.second.data()),
-                       kv.second.size())) {
-      QUIC_BUG << "Failed to write custom parameter "
-               << static_cast<int>(kv.first);
+    if (!WriteTransportParameterId(&writer, kv.first, version) ||
+        !WriteTransportParameterStringPiece(&writer, kv.second, version)) {
+      QUIC_BUG << "Failed to write custom parameter " << kv.first;
       return false;
     }
   }
 
-  if (!CBB_flush(cbb.get())) {
-    QUIC_BUG << "Failed to flush CBB for " << in;
-    return false;
+  if (!version.HasVarIntTransportParams()) {
+    // Fill in the length prefix at the start of the transport parameters.
+    if (writer.length() < sizeof(uint16_t) ||
+        writer.length() - sizeof(uint16_t) >
+            std::numeric_limits<uint16_t>::max()) {
+      QUIC_BUG << "Cannot write length " << writer.length() << " for " << in;
+      return false;
+    }
+    const uint16_t length_prefix = writer.length() - sizeof(uint16_t);
+    QuicDataWriter length_prefix_writer(out->size(),
+                                        reinterpret_cast<char*>(out->data()));
+    if (!length_prefix_writer.WriteUInt16(length_prefix)) {
+      QUIC_BUG << "Failed to write length prefix for " << in;
+      return false;
+    }
   }
-  out->resize(CBB_len(cbb.get()));
-  memcpy(out->data(), CBB_data(cbb.get()), CBB_len(cbb.get()));
-  QUIC_DLOG(INFO) << "Serialized " << in << " as " << CBB_len(cbb.get())
+
+  out->resize(writer.length());
+
+  QUIC_DLOG(INFO) << "Serialized " << in << " as " << writer.length()
                   << " bytes";
   return true;
 }
@@ -578,198 +686,192 @@
                               Perspective perspective,
                               const uint8_t* in,
                               size_t in_len,
-                              TransportParameters* out) {
+                              TransportParameters* out,
+                              std::string* error_details) {
   out->perspective = perspective;
-  CBS cbs;
-  CBS_init(&cbs, in, in_len);
+  QuicDataReader reader(reinterpret_cast<const char*>(in), in_len);
 
-  CBS params;
-  if (!CBS_get_u16_length_prefixed(&cbs, &params)) {
-    QUIC_DLOG(ERROR) << "Failed to parse the number of transport parameters";
-    return false;
+  if (!version.HasVarIntTransportParams()) {
+    uint16_t full_length;
+    if (!reader.ReadUInt16(&full_length)) {
+      *error_details = "Failed to parse the transport parameter full length";
+      return false;
+    }
+    if (full_length != reader.BytesRemaining()) {
+      *error_details =
+          quiche::QuicheStrCat("Invalid transport parameter full length ",
+                               full_length, " != ", reader.BytesRemaining());
+      return false;
+    }
   }
 
-  while (CBS_len(&params) > 0) {
+  while (!reader.IsDoneReading()) {
     TransportParameters::TransportParameterId param_id;
-    CBS value;
-    static_assert(sizeof(param_id) == sizeof(uint16_t), "bad size");
-    if (!CBS_get_u16(&params, reinterpret_cast<uint16_t*>(&param_id))) {
-      QUIC_DLOG(ERROR) << "Failed to parse transport parameter ID";
+    if (!ReadTransportParameterId(&reader, version, &param_id)) {
+      *error_details = "Failed to parse transport parameter ID";
       return false;
     }
-    if (!CBS_get_u16_length_prefixed(&params, &value)) {
-      QUIC_DLOG(ERROR) << "Failed to parse length of transport parameter "
-                       << TransportParameterIdToString(param_id);
+    quiche::QuicheStringPiece value;
+    if (!ReadTransportParameterLengthAndValue(&reader, version, &value)) {
+      *error_details =
+          "Failed to read length and value of transport parameter " +
+          TransportParameterIdToString(param_id);
       return false;
     }
+    QuicDataReader value_reader(value);
     bool parse_success = true;
     switch (param_id) {
       case TransportParameters::kOriginalConnectionId: {
         if (!out->original_connection_id.IsEmpty()) {
-          QUIC_DLOG(ERROR) << "Received a second original connection ID";
+          *error_details = "Received a second original connection ID";
           return false;
         }
-        const size_t connection_id_length = CBS_len(&value);
+        const size_t connection_id_length = value_reader.BytesRemaining();
         if (!QuicUtils::IsConnectionIdLengthValidForVersion(
                 connection_id_length, version.transport_version)) {
-          QUIC_DLOG(ERROR) << "Received original connection ID of "
-                           << "invalid length " << connection_id_length;
+          *error_details = quiche::QuicheStrCat(
+              "Received original connection ID of invalid length ",
+              connection_id_length);
           return false;
         }
-        out->original_connection_id.set_length(
-            static_cast<uint8_t>(connection_id_length));
-        if (out->original_connection_id.length() != 0) {
-          memcpy(out->original_connection_id.mutable_data(), CBS_data(&value),
-                 out->original_connection_id.length());
+        if (!value_reader.ReadConnectionId(&out->original_connection_id,
+                                           connection_id_length)) {
+          *error_details = "Failed to read original connection ID";
+          return false;
         }
       } break;
       case TransportParameters::kIdleTimeout:
-        parse_success = out->idle_timeout_milliseconds.ReadFromCbs(&value);
+        parse_success =
+            out->idle_timeout_milliseconds.Read(&value_reader, error_details);
         break;
-      case TransportParameters::kStatelessResetToken:
+      case TransportParameters::kStatelessResetToken: {
         if (!out->stateless_reset_token.empty()) {
-          QUIC_DLOG(ERROR) << "Received a second stateless reset token";
+          *error_details = "Received a second stateless reset token";
           return false;
         }
-        if (CBS_len(&value) != kStatelessResetTokenLength) {
-          QUIC_DLOG(ERROR) << "Received stateless reset token of "
-                           << "invalid length " << CBS_len(&value);
+        quiche::QuicheStringPiece stateless_reset_token =
+            value_reader.ReadRemainingPayload();
+        if (stateless_reset_token.length() != kStatelessResetTokenLength) {
+          *error_details = quiche::QuicheStrCat(
+              "Received stateless reset token of invalid length ",
+              stateless_reset_token.length());
           return false;
         }
-        out->stateless_reset_token.assign(CBS_data(&value),
-                                          CBS_data(&value) + CBS_len(&value));
-        break;
+        out->stateless_reset_token.assign(
+            stateless_reset_token.data(),
+            stateless_reset_token.data() + stateless_reset_token.length());
+      } break;
       case TransportParameters::kMaxPacketSize:
-        parse_success = out->max_packet_size.ReadFromCbs(&value);
+        parse_success = out->max_packet_size.Read(&value_reader, error_details);
         break;
       case TransportParameters::kInitialMaxData:
-        parse_success = out->initial_max_data.ReadFromCbs(&value);
+        parse_success =
+            out->initial_max_data.Read(&value_reader, error_details);
         break;
       case TransportParameters::kInitialMaxStreamDataBidiLocal:
-        parse_success =
-            out->initial_max_stream_data_bidi_local.ReadFromCbs(&value);
+        parse_success = out->initial_max_stream_data_bidi_local.Read(
+            &value_reader, error_details);
         break;
       case TransportParameters::kInitialMaxStreamDataBidiRemote:
-        parse_success =
-            out->initial_max_stream_data_bidi_remote.ReadFromCbs(&value);
+        parse_success = out->initial_max_stream_data_bidi_remote.Read(
+            &value_reader, error_details);
         break;
       case TransportParameters::kInitialMaxStreamDataUni:
-        parse_success = out->initial_max_stream_data_uni.ReadFromCbs(&value);
+        parse_success =
+            out->initial_max_stream_data_uni.Read(&value_reader, error_details);
         break;
       case TransportParameters::kInitialMaxStreamsBidi:
-        parse_success = out->initial_max_streams_bidi.ReadFromCbs(&value);
+        parse_success =
+            out->initial_max_streams_bidi.Read(&value_reader, error_details);
         break;
       case TransportParameters::kInitialMaxStreamsUni:
-        parse_success = out->initial_max_streams_uni.ReadFromCbs(&value);
+        parse_success =
+            out->initial_max_streams_uni.Read(&value_reader, error_details);
         break;
       case TransportParameters::kAckDelayExponent:
-        parse_success = out->ack_delay_exponent.ReadFromCbs(&value);
+        parse_success =
+            out->ack_delay_exponent.Read(&value_reader, error_details);
         break;
       case TransportParameters::kMaxAckDelay:
-        parse_success = out->max_ack_delay.ReadFromCbs(&value);
+        parse_success = out->max_ack_delay.Read(&value_reader, error_details);
         break;
       case TransportParameters::kDisableMigration:
         if (out->disable_migration) {
-          QUIC_DLOG(ERROR) << "Received a second disable migration";
-          return false;
-        }
-        if (CBS_len(&value) != 0) {
-          QUIC_DLOG(ERROR) << "Received disable migration of invalid length "
-                           << CBS_len(&value);
+          *error_details = "Received a second disable migration";
           return false;
         }
         out->disable_migration = true;
         break;
       case TransportParameters::kPreferredAddress: {
+        TransportParameters::PreferredAddress preferred_address;
         uint16_t ipv4_port, ipv6_port;
         in_addr ipv4_address;
         in6_addr ipv6_address;
-        if (!CBS_copy_bytes(&value, reinterpret_cast<uint8_t*>(&ipv4_address),
-                            sizeof(ipv4_address)) ||
-            !CBS_get_u16(&value, &ipv4_port) ||
-            !CBS_copy_bytes(&value, reinterpret_cast<uint8_t*>(&ipv6_address),
-                            sizeof(ipv6_address)) ||
-            !CBS_get_u16(&value, &ipv6_port)) {
-          QUIC_DLOG(ERROR) << "Failed to parse preferred address IPs and ports";
+        preferred_address.stateless_reset_token.resize(
+            kStatelessResetTokenLength);
+        if (!value_reader.ReadBytes(&ipv4_address, sizeof(ipv4_address)) ||
+            !value_reader.ReadUInt16(&ipv4_port) ||
+            !value_reader.ReadBytes(&ipv6_address, sizeof(ipv6_address)) ||
+            !value_reader.ReadUInt16(&ipv6_port) ||
+            !value_reader.ReadLengthPrefixedConnectionId(
+                &preferred_address.connection_id) ||
+            !value_reader.ReadBytes(&preferred_address.stateless_reset_token[0],
+                                    kStatelessResetTokenLength)) {
+          *error_details = "Failed to read preferred address";
           return false;
         }
-        TransportParameters::PreferredAddress preferred_address;
         preferred_address.ipv4_socket_address =
             QuicSocketAddress(QuicIpAddress(ipv4_address), ipv4_port);
         preferred_address.ipv6_socket_address =
             QuicSocketAddress(QuicIpAddress(ipv6_address), ipv6_port);
         if (!preferred_address.ipv4_socket_address.host().IsIPv4() ||
             !preferred_address.ipv6_socket_address.host().IsIPv6()) {
-          QUIC_DLOG(ERROR) << "Received preferred addresses of bad families "
-                           << preferred_address;
+          *error_details = "Received preferred addresses of bad families " +
+                           preferred_address.ToString();
           return false;
         }
-        CBS connection_id_cbs;
-        if (!CBS_get_u8_length_prefixed(&value, &connection_id_cbs)) {
-          QUIC_DLOG(ERROR)
-              << "Failed to parse length of preferred address connection ID";
+        if (!QuicUtils::IsConnectionIdValidForVersion(
+                preferred_address.connection_id, version.transport_version)) {
+          *error_details = "Received invalid preferred address connection ID " +
+                           preferred_address.ToString();
           return false;
         }
-        const size_t connection_id_length = CBS_len(&connection_id_cbs);
-        if (!QuicUtils::IsConnectionIdLengthValidForVersion(
-                connection_id_length, version.transport_version)) {
-          QUIC_DLOG(ERROR) << "Received preferred address connection ID of "
-                           << "invalid length " << connection_id_length;
-          return false;
-        }
-        preferred_address.connection_id.set_length(
-            static_cast<uint8_t>(connection_id_length));
-        if (preferred_address.connection_id.length() > 0 &&
-            !CBS_copy_bytes(&connection_id_cbs,
-                            reinterpret_cast<uint8_t*>(
-                                preferred_address.connection_id.mutable_data()),
-                            preferred_address.connection_id.length())) {
-          QUIC_DLOG(ERROR) << "Failed to read preferred address connection ID";
-          return false;
-        }
-        if (CBS_len(&value) != kStatelessResetTokenLength) {
-          QUIC_DLOG(ERROR) << "Received preferred address with "
-                           << "invalid remaining length " << CBS_len(&value);
-          return false;
-        }
-        preferred_address.stateless_reset_token.assign(
-            CBS_data(&value), CBS_data(&value) + CBS_len(&value));
         out->preferred_address =
             std::make_unique<TransportParameters::PreferredAddress>(
                 preferred_address);
       } break;
       case TransportParameters::kActiveConnectionIdLimit:
-        parse_success = out->active_connection_id_limit.ReadFromCbs(&value);
+        parse_success =
+            out->active_connection_id_limit.Read(&value_reader, error_details);
         break;
       case TransportParameters::kMaxDatagramFrameSize:
-        parse_success = out->max_datagram_frame_size.ReadFromCbs(&value);
+        parse_success =
+            out->max_datagram_frame_size.Read(&value_reader, error_details);
         break;
       case TransportParameters::kGoogleQuicParam: {
         if (out->google_quic_params) {
-          QUIC_DLOG(ERROR) << "Received a second Google parameter";
+          *error_details = "Received a second Google parameter";
           return false;
         }
-        quiche::QuicheStringPiece serialized_params(
-            reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value));
-        out->google_quic_params = CryptoFramer::ParseMessage(serialized_params);
+        out->google_quic_params =
+            CryptoFramer::ParseMessage(value_reader.ReadRemainingPayload());
       } break;
       case TransportParameters::kGoogleQuicVersion: {
-        if (!CBS_get_u32(&value, &out->version)) {
-          QUIC_DLOG(ERROR) << "Failed to parse Google version extension";
+        if (!value_reader.ReadUInt32(&out->version)) {
+          *error_details = "Failed to read Google version extension version";
           return false;
         }
         if (perspective == Perspective::IS_SERVER) {
-          CBS versions;
-          if (!CBS_get_u8_length_prefixed(&value, &versions) ||
-              CBS_len(&versions) % 4 != 0) {
-            QUIC_DLOG(ERROR)
-                << "Failed to parse Google supported versions length";
+          uint8_t versions_length;
+          if (!value_reader.ReadUInt8(&versions_length)) {
+            *error_details = "Failed to parse Google supported versions length";
             return false;
           }
-          while (CBS_len(&versions) > 0) {
+          const uint8_t num_versions = versions_length / sizeof(uint32_t);
+          for (uint8_t i = 0; i < num_versions; ++i) {
             QuicVersionLabel version;
-            if (!CBS_get_u32(&versions, &version)) {
-              QUIC_DLOG(ERROR) << "Failed to parse Google supported version";
+            if (!value_reader.ReadUInt32(&version)) {
+              *error_details = "Failed to parse Google supported version";
               return false;
             }
             out->supported_versions.push_back(version);
@@ -777,24 +879,37 @@
         }
       } break;
       default:
-        out->custom_parameters[param_id] = std::string(
-            reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value));
+        if (out->custom_parameters.find(param_id) !=
+            out->custom_parameters.end()) {
+          *error_details = "Received a second unknown parameter" +
+                           TransportParameterIdToString(param_id);
+          return false;
+        }
+        out->custom_parameters[param_id] =
+            std::string(value_reader.ReadRemainingPayload());
         break;
     }
     if (!parse_success) {
+      DCHECK(!error_details->empty());
+      return false;
+    }
+    if (!value_reader.IsDoneReading()) {
+      *error_details = quiche::QuicheStrCat(
+          "Received unexpected ", value_reader.BytesRemaining(),
+          " bytes after parsing ", TransportParameterIdToString(param_id));
       return false;
     }
   }
 
-  const bool ok = out->AreValid();
-  if (ok) {
-    QUIC_DLOG(INFO) << "Parsed transport parameters " << *out << " from "
-                    << in_len << " bytes";
-  } else {
-    QUIC_DLOG(ERROR) << "Transport parameter validity check failed " << *out
-                     << " from " << in_len << " bytes";
+  if (!out->AreValid(error_details)) {
+    DCHECK(!error_details->empty());
+    return false;
   }
-  return ok;
+
+  QUIC_DLOG(INFO) << "Parsed transport parameters " << *out << " from "
+                  << in_len << " bytes";
+
+  return true;
 }
 
 }  // namespace quic