Project import generated by Copybara.
PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
new file mode 100644
index 0000000..f225785
--- /dev/null
+++ b/quic/core/crypto/transport_parameters.cc
@@ -0,0 +1,302 @@
+// Copyright (c) 2018 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/crypto/transport_parameters.h"
+
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+
+namespace quic {
+
+namespace {
+
+// Values of the TransportParameterId enum as defined in
+// draft-ietf-quic-transport-08 section 7.4. When parameters are encoded, one of
+// these enum values is used to indicate which parameter is encoded.
+enum TransportParameterId : uint16_t {
+ kInitialMaxStreamDataId = 0,
+ kInitialMaxDataId = 1,
+ kInitialMaxBidiStreamsId = 2,
+ kIdleTimeoutId = 3,
+ kMaxPacketSizeId = 5,
+ kStatelessResetTokenId = 6,
+ kAckDelayExponentId = 7,
+ kInitialMaxUniStreamsId = 8,
+
+ kMaxKnownParameterId = 9,
+};
+
+// Value for the TransportParameterId to use for non-standard Google QUIC params
+// in Transport Parameters.
+const uint16_t kGoogleQuicParamId = 18257;
+
+// The following constants define minimum and maximum allowed values for some of
+// the parameters. These come from draft-ietf-quic-transport-08 section 7.4.1.
+const uint16_t kMaxAllowedIdleTimeout = 600;
+const uint16_t kMinAllowedMaxPacketSize = 1200;
+const uint16_t kMaxAllowedMaxPacketSize = 65527;
+const uint8_t kMaxAllowedAckDelayExponent = 20;
+
+static_assert(kMaxKnownParameterId <= 32, "too many parameters to bit pack");
+
+// The initial_max_stream_data, initial_max_data, and idle_timeout parameters
+// are always required to be present. When parsing the extension, a bitmask is
+// used to keep track of which parameter have been seen so far, and that bitmask
+// will be compared to this mask to check that all of the required parameters
+// were present.
+static constexpr uint16_t kRequiredParamsMask = (1 << kInitialMaxStreamDataId) |
+ (1 << kInitialMaxDataId) |
+ (1 << kIdleTimeoutId);
+
+} // namespace
+
+TransportParameters::TransportParameters() = default;
+
+TransportParameters::~TransportParameters() = default;
+
+bool TransportParameters::is_valid() const {
+ if (perspective == Perspective::IS_CLIENT && !stateless_reset_token.empty()) {
+ return false;
+ }
+ if (perspective == Perspective::IS_SERVER &&
+ stateless_reset_token.size() != 16) {
+ return false;
+ }
+ if (idle_timeout > kMaxAllowedIdleTimeout ||
+ (max_packet_size.present &&
+ (max_packet_size.value > kMaxAllowedMaxPacketSize ||
+ max_packet_size.value < kMinAllowedMaxPacketSize)) ||
+ (ack_delay_exponent.present &&
+ ack_delay_exponent.value > kMaxAllowedAckDelayExponent)) {
+ return false;
+ }
+ return true;
+}
+
+bool SerializeTransportParameters(const TransportParameters& in,
+ std::vector<uint8_t>* out) {
+ if (!in.is_valid()) {
+ return false;
+ }
+ bssl::ScopedCBB cbb;
+ // 28 is the minimum size that the serialized TransportParameters can be,
+ // which is when it is for a client and only the required parameters are
+ // present. The CBB will grow to fit larger serializations.
+ if (!CBB_init(cbb.get(), 28) || !CBB_add_u32(cbb.get(), in.version)) {
+ return false;
+ }
+ CBB versions;
+ if (in.perspective == Perspective::IS_SERVER) {
+ if (!CBB_add_u8_length_prefixed(cbb.get(), &versions)) {
+ return false;
+ }
+ for (QuicVersionLabel version : in.supported_versions) {
+ if (!CBB_add_u32(&versions, version)) {
+ return false;
+ }
+ }
+ }
+
+ CBB params, initial_max_stream_data_param, initial_max_data_param,
+ idle_timeout_param;
+ // required parameters
+ if (!CBB_add_u16_length_prefixed(cbb.get(), ¶ms) ||
+ // initial_max_stream_data
+ !CBB_add_u16(¶ms, kInitialMaxStreamDataId) ||
+ !CBB_add_u16_length_prefixed(¶ms, &initial_max_stream_data_param) ||
+ !CBB_add_u32(&initial_max_stream_data_param,
+ in.initial_max_stream_data) ||
+ // initial_max_data
+ !CBB_add_u16(¶ms, kInitialMaxDataId) ||
+ !CBB_add_u16_length_prefixed(¶ms, &initial_max_data_param) ||
+ !CBB_add_u32(&initial_max_data_param, in.initial_max_data) ||
+ // idle_timeout
+ !CBB_add_u16(¶ms, kIdleTimeoutId) ||
+ !CBB_add_u16_length_prefixed(¶ms, &idle_timeout_param) ||
+ !CBB_add_u16(&idle_timeout_param, in.idle_timeout)) {
+ return false;
+ }
+
+ CBB stateless_reset_token_param;
+ if (!in.stateless_reset_token.empty()) {
+ if (!CBB_add_u16(¶ms, kStatelessResetTokenId) ||
+ !CBB_add_u16_length_prefixed(¶ms, &stateless_reset_token_param) ||
+ !CBB_add_bytes(&stateless_reset_token_param,
+ in.stateless_reset_token.data(),
+ in.stateless_reset_token.size())) {
+ return false;
+ }
+ }
+
+ CBB initial_max_bidi_streams_param;
+ if (in.initial_max_bidi_streams.present) {
+ if (!CBB_add_u16(¶ms, kInitialMaxBidiStreamsId) ||
+ !CBB_add_u16_length_prefixed(¶ms,
+ &initial_max_bidi_streams_param) ||
+ !CBB_add_u16(&initial_max_bidi_streams_param,
+ in.initial_max_bidi_streams.value)) {
+ return false;
+ }
+ }
+ CBB initial_max_uni_streams_param;
+ if (in.initial_max_uni_streams.present) {
+ if (!CBB_add_u16(¶ms, kInitialMaxUniStreamsId) ||
+ !CBB_add_u16_length_prefixed(¶ms, &initial_max_uni_streams_param) ||
+ !CBB_add_u16(&initial_max_uni_streams_param,
+ in.initial_max_uni_streams.value)) {
+ return false;
+ }
+ }
+ CBB max_packet_size_param;
+ if (in.max_packet_size.present) {
+ if (!CBB_add_u16(¶ms, kMaxPacketSizeId) ||
+ !CBB_add_u16_length_prefixed(¶ms, &max_packet_size_param) ||
+ !CBB_add_u16(&max_packet_size_param, in.max_packet_size.value)) {
+ return false;
+ }
+ }
+ CBB ack_delay_exponent_param;
+ if (in.ack_delay_exponent.present) {
+ if (!CBB_add_u16(¶ms, kAckDelayExponentId) ||
+ !CBB_add_u16_length_prefixed(¶ms, &ack_delay_exponent_param) ||
+ !CBB_add_u8(&ack_delay_exponent_param, in.ack_delay_exponent.value)) {
+ return false;
+ }
+ }
+ CBB google_quic_params;
+ if (in.google_quic_params) {
+ const QuicData& serialized_google_quic_params =
+ in.google_quic_params->GetSerialized();
+ if (!CBB_add_u16(¶ms, kGoogleQuicParamId) ||
+ !CBB_add_u16_length_prefixed(¶ms, &google_quic_params) ||
+ !CBB_add_bytes(&google_quic_params,
+ reinterpret_cast<const uint8_t*>(
+ serialized_google_quic_params.data()),
+ serialized_google_quic_params.length())) {
+ return false;
+ }
+ }
+ if (!CBB_flush(cbb.get())) {
+ return false;
+ }
+ out->resize(CBB_len(cbb.get()));
+ memcpy(out->data(), CBB_data(cbb.get()), CBB_len(cbb.get()));
+ return true;
+}
+
+bool ParseTransportParameters(const uint8_t* in,
+ size_t in_len,
+ Perspective perspective,
+ TransportParameters* out) {
+ CBS cbs;
+ CBS_init(&cbs, in, in_len);
+ if (!CBS_get_u32(&cbs, &out->version)) {
+ return false;
+ }
+ if (perspective == Perspective::IS_SERVER) {
+ CBS versions;
+ if (!CBS_get_u8_length_prefixed(&cbs, &versions) ||
+ CBS_len(&versions) % 4 != 0) {
+ return false;
+ }
+ while (CBS_len(&versions) > 0) {
+ QuicVersionLabel version;
+ if (!CBS_get_u32(&versions, &version)) {
+ return false;
+ }
+ out->supported_versions.push_back(version);
+ }
+ }
+ out->perspective = perspective;
+
+ uint32_t present_params = 0;
+ bool has_google_quic_params = false;
+ CBS params;
+ if (!CBS_get_u16_length_prefixed(&cbs, ¶ms)) {
+ return false;
+ }
+ while (CBS_len(¶ms) > 0) {
+ uint16_t param_id;
+ CBS value;
+ if (!CBS_get_u16(¶ms, ¶m_id) ||
+ !CBS_get_u16_length_prefixed(¶ms, &value)) {
+ return false;
+ }
+ if (param_id < kMaxKnownParameterId) {
+ uint16_t mask = 1 << param_id;
+ if (present_params & mask) {
+ return false;
+ }
+ present_params |= mask;
+ }
+ switch (param_id) {
+ case kInitialMaxStreamDataId:
+ if (!CBS_get_u32(&value, &out->initial_max_stream_data) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ break;
+ case kInitialMaxDataId:
+ if (!CBS_get_u32(&value, &out->initial_max_data) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ break;
+ case kInitialMaxBidiStreamsId:
+ if (!CBS_get_u16(&value, &out->initial_max_bidi_streams.value) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ out->initial_max_bidi_streams.present = true;
+ break;
+ case kIdleTimeoutId:
+ if (!CBS_get_u16(&value, &out->idle_timeout) || CBS_len(&value) != 0) {
+ return false;
+ }
+ break;
+ case kMaxPacketSizeId:
+ if (!CBS_get_u16(&value, &out->max_packet_size.value) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ out->max_packet_size.present = true;
+ break;
+ case kStatelessResetTokenId:
+ if (CBS_len(&value) == 0) {
+ return false;
+ }
+ out->stateless_reset_token.assign(CBS_data(&value),
+ CBS_data(&value) + CBS_len(&value));
+ break;
+ case kAckDelayExponentId:
+ if (!CBS_get_u8(&value, &out->ack_delay_exponent.value) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ out->ack_delay_exponent.present = true;
+ break;
+ case kInitialMaxUniStreamsId:
+ if (!CBS_get_u16(&value, &out->initial_max_uni_streams.value) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ out->initial_max_uni_streams.present = true;
+ break;
+ case kGoogleQuicParamId:
+ if (has_google_quic_params) {
+ return false;
+ }
+ has_google_quic_params = true;
+ QuicStringPiece serialized_params(
+ reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value));
+ out->google_quic_params = CryptoFramer::ParseMessage(serialized_params);
+ }
+ }
+ if ((present_params & kRequiredParamsMask) != kRequiredParamsMask) {
+ return false;
+ }
+ return out->is_valid();
+}
+
+} // namespace quic