Eliminate heap allocations from initial obfuscator generation in QUICHE. Each crypter still invokes 16 malloc operations due to OpenSSL HKDF libraries.
When creating initial obfuscators, the Crypters themselves are allocated from the heap, but all operations to populate those crypters are conducted entirely on the stack.
Additional #includes in *_session fix compiler failures in the benchmark test.
Result of benchmark run:
blaze run -c opt --dynamic_mode=off --copt=-gmlt third_party/quic/core/crypto/crypto_utils_benchmark -- --benchmark_filter=all
CPU: Intel Icelake with HyperThreading (24 cores) dL1:48KB dL2:1280KB dL3:54MB
Benchmark Time(ns) CPU(ns) Iterations
-----------------------------------------------------------------------------
BM_InitialObfuscators 6506 6583 7290
BM_InitialObfuscators 6507 6583 7292
BM_InitialObfuscators 6497 6588 7285
BM_InitialObfuscators 6525 6585 7296
BM_InitialObfuscators 6527 6587 7282
BM_InitialObfuscators 6514 6579 7306
BM_InitialObfuscators 6504 6616 7260
BM_InitialObfuscators 6482 6584 7299
BM_InitialObfuscators 6488 6589 7286
BM_InitialObfuscators 6489 6042 7291
BM_InitialObfuscators 6533 6592 7290
BM_InitialObfuscators 6563 6799 10000
BM_InitialObfuscators_mean 6513 6568 90177
BM_InitialObfuscators_stddev 23.5 169 90177
BM_InitialObfuscatorsNoMalloc 6267 6414 10000
BM_InitialObfuscatorsNoMalloc 6316 6405 10000
BM_InitialObfuscatorsNoMalloc 6251 6050 7286
BM_InitialObfuscatorsNoMalloc 6250 6405 10000
BM_InitialObfuscatorsNoMalloc 6266 6045 7294
BM_InitialObfuscatorsNoMalloc 6262 6039 7286
BM_InitialObfuscatorsNoMalloc 6250 6028 7300
BM_InitialObfuscatorsNoMalloc 6232 6040 7292
BM_InitialObfuscatorsNoMalloc 6250 6457 10000
BM_InitialObfuscatorsNoMalloc 6230 6409 10000
BM_InitialObfuscatorsNoMalloc 6243 6405 10000
BM_InitialObfuscatorsNoMalloc 6248 6069 7259
BM_InitialObfuscatorsNoMalloc_mean 6256 6260 103717
BM_InitialObfuscatorsNoMalloc_stddev 22.4 184 103717
Protected by FLAGS_quic_reloadable_flag_quic_heapless_obfuscator.
PiperOrigin-RevId: 751001626
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index 1ae5624..e24c493 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -35,6 +35,7 @@
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_version_rfcv2, false, false, "When true, support RFC9369.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fin_before_completed_http_headers, false, true, "If true, close the connection with error if FIN is received before finish receiving the whole HTTP headers.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fix_timeouts, true, true, "If true, postpone setting handshake timeout to infinite to handshake complete.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_heapless_obfuscator, false, false, "If true, generates QUIC initial obfuscators with no heap allocations.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_heapless_static_parser, false, false, "If true, stops parsing immediately on unknown version, to avoid a potential malloc when parsing the connection ID")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_ignore_gquic_probing, true, true, "If true, QUIC server will not respond to gQUIC probing packet(PING + PADDING) but treat it as a regular packet.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_limit_new_streams_per_loop_2, true, true, "If true, when the peer sends connection options \\\'SLP1\\\', \\\'SLP2\\\' and \\\'SLPF\\\', internet facing GFEs will only allow a limited number of new requests to be processed per event loop, and postpone the rest to the following event loops. Also guard QuicConnection to iterate through all decrypters at each encryption level to get cipher id for a request.")
diff --git a/quiche/quic/core/crypto/crypto_utils.cc b/quiche/quic/core/crypto/crypto_utils.cc
index 2cbdb83..51ee57b 100644
--- a/quiche/quic/core/crypto/crypto_utils.cc
+++ b/quiche/quic/core/crypto/crypto_utils.cc
@@ -4,30 +4,30 @@
#include "quiche/quic/core/crypto/crypto_utils.h"
-#include <algorithm>
#include <cstddef>
+#include <cstdint>
+#include <cstring>
#include <memory>
#include <optional>
#include <string>
-#include <utility>
#include <vector>
#include "absl/base/macros.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
#include "openssl/bytestring.h"
#include "openssl/err.h"
#include "openssl/hkdf.h"
#include "openssl/mem.h"
#include "openssl/sha.h"
-#include "quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h"
-#include "quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
#include "quiche/quic/core/crypto/aes_128_gcm_decrypter.h"
#include "quiche/quic/core/crypto/aes_128_gcm_encrypter.h"
#include "quiche/quic/core/crypto/crypto_handshake.h"
#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/crypto/null_decrypter.h"
#include "quiche/quic/core/crypto/null_encrypter.h"
+#include "quiche/quic/core/crypto/quic_crypter.h"
#include "quiche/quic/core/crypto/quic_decrypter.h"
#include "quiche/quic/core/crypto/quic_encrypter.h"
#include "quiche/quic/core/crypto/quic_hkdf.h"
@@ -36,11 +36,15 @@
#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_data_writer.h"
#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/platform/api/quic_bug_tracker.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"
#include "quiche/common/quiche_endian.h"
+#include "quiche/common/wire_serialization.h"
namespace quic {
@@ -59,6 +63,37 @@
// The implicit PRF is explicitly passed into HkdfExpandLabel as |prf|; the
// Secret, Label, and Length are passed in as |secret|, |label|, and
// |out_len|, respectively. The resulting expanded secret is returned.
+bool HkdfExpandLabel(const EVP_MD* prf, absl::Span<const uint8_t> secret,
+ absl::string_view label, absl::Span<uint8_t> out) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_obfuscator, 2, 7);
+ constexpr absl::string_view kLabelPrefix = "tls13 ";
+ constexpr size_t kMaxLabelLength = 10; // "quicv2 key" is the longest
+ QUICHE_DCHECK_LE(label.length(), kMaxLabelLength);
+ char quic_hkdf_label[sizeof(uint16_t) + sizeof(uint8_t) +
+ kLabelPrefix.length() + kMaxLabelLength +
+ sizeof(uint8_t)];
+ QuicDataWriter writer(ABSL_ARRAYSIZE(quic_hkdf_label), quic_hkdf_label);
+ if (!quiche::SerializeIntoWriter(
+ writer, quiche::WireUint16(static_cast<uint16_t>(out.length())),
+ quiche::WireUint8(
+ static_cast<uint8_t>(kLabelPrefix.length() + label.length())),
+ quiche::WireBytes(kLabelPrefix), quiche::WireBytes(label),
+ quiche::WireUint8(0))
+ .ok()) {
+ QUIC_LOG(ERROR) << "Failed to serialize label";
+ return false;
+ }
+ if (!HKDF_expand(out.data(), out.size(), prf, secret.data(), secret.size(),
+ reinterpret_cast<const uint8_t*>(quic_hkdf_label),
+ writer.length())) {
+ QUIC_LOG(ERROR) << "Running HKDF-Expand-Label failed";
+ return false;
+ }
+ return true;
+}
+// Legacy overloaded version that accepts string and returns vector, which leads
+// to heap allocations.
+// TODO(martinduke): Delete this.
std::vector<uint8_t> HkdfExpandLabel(const EVP_MD* prf,
absl::Span<const uint8_t> secret,
const std::string& label, size_t out_len) {
@@ -95,10 +130,34 @@
return out;
}
+// "quicv2 key" is the longest string for a version label.
+constexpr size_t kMaxVersionLabelLength = 10;
+
} // namespace
-const std::string getLabelForVersion(const ParsedQuicVersion& version,
- const absl::string_view& predicate) {
+// Populates |out| with the label for |version| and |predicate|, returning a
+// string_view of the correct length.
+absl::string_view GetLabelForVersion(const ParsedQuicVersion& version,
+ absl::string_view predicate,
+ absl::Span<char> out) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_obfuscator, 3, 7);
+ static_assert(SupportedVersions().size() == 4u,
+ "Supported versions out of sync with HKDF labels");
+ QuicDataWriter writer(out.size(), out.data());
+ constexpr absl::string_view kV2Prefix = "quicv2 ";
+ constexpr absl::string_view kV1Prefix = "quic ";
+ if (version == ParsedQuicVersion::RFCv2()) {
+ writer.WriteStringPiece(kV2Prefix);
+ } else {
+ writer.WriteStringPiece(kV1Prefix);
+ }
+ writer.WriteStringPiece(predicate);
+ return absl::string_view(out.data(), writer.length());
+}
+// Legacy overloaded version that returns string, leading to a heap allocation.
+// TODO(martinduke): Delete this.
+std::string getLabelForVersion(const ParsedQuicVersion& version,
+ const absl::string_view& predicate) {
static_assert(SupportedVersions().size() == 4u,
"Supported versions out of sync with HKDF labels");
if (version == ParsedQuicVersion::RFCv2()) {
@@ -108,6 +167,25 @@
}
}
+// static
+void CryptoUtils::InitializeCrypterSecrets(const EVP_MD* prf,
+ absl::Span<const uint8_t> pp_secret,
+ const ParsedQuicVersion& version,
+ QuicCrypter* crypter) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_obfuscator, 4, 7);
+ SetKeyAndIVHeapless(prf, pp_secret, version, crypter);
+ uint8_t header_protection_key[16];
+ QUIC_BUG_IF(quic_bug_hp_length_mismatch,
+ crypter->GetKeySize() > sizeof(header_protection_key))
+ << "HP length does not match crypter";
+ GenerateHeaderProtectionKey(
+ prf, pp_secret, version,
+ absl::Span<uint8_t>(header_protection_key, crypter->GetKeySize()));
+ crypter->SetHeaderProtectionKey(absl::string_view(
+ reinterpret_cast<char*>(header_protection_key), crypter->GetKeySize()));
+}
+// Version that uses the heap.
+// TODO(martinduke): Delete this.
void CryptoUtils::InitializeCrypterSecrets(
const EVP_MD* prf, const std::vector<uint8_t>& pp_secret,
const ParsedQuicVersion& version, QuicCrypter* crypter) {
@@ -119,6 +197,38 @@
header_protection_key.size()));
}
+// static
+void CryptoUtils::SetKeyAndIVHeapless(const EVP_MD* prf,
+ absl::Span<const uint8_t> pp_secret,
+ const ParsedQuicVersion& version,
+ QuicCrypter* crypter) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_obfuscator, 5, 7);
+ uint8_t key[16];
+ QUIC_BUG_IF(quic_bug_key_length_mismatch, crypter->GetKeySize() > sizeof(key))
+ << "Key length does not match crypter";
+
+ char version_label_raw[kMaxVersionLabelLength];
+ constexpr absl::string_view kKeyPredicate = "key";
+ absl::string_view version_label = GetLabelForVersion(
+ version, kKeyPredicate,
+ absl::Span<char>(version_label_raw, kMaxVersionLabelLength));
+ HkdfExpandLabel(prf, pp_secret, version_label,
+ absl::Span<uint8_t>(key, crypter->GetKeySize()));
+ uint8_t iv[12];
+ QUIC_BUG_IF(quic_bug_iv_length_mismatch, crypter->GetIVSize() > sizeof(iv))
+ << "IV length does not match crypter";
+ constexpr absl::string_view kIvPredicate = "iv";
+ version_label = GetLabelForVersion(
+ version, kIvPredicate,
+ absl::Span<char>(version_label_raw, kMaxVersionLabelLength));
+ HkdfExpandLabel(prf, pp_secret, version_label,
+ absl::Span<uint8_t>(iv, crypter->GetIVSize()));
+ crypter->SetKey(
+ absl::string_view(reinterpret_cast<char*>(key), crypter->GetKeySize()));
+ crypter->SetIV(
+ absl::string_view(reinterpret_cast<char*>(iv), crypter->GetIVSize()));
+}
+// TODO(martinduke): Delete this.
void CryptoUtils::SetKeyAndIV(const EVP_MD* prf,
absl::Span<const uint8_t> pp_secret,
const ParsedQuicVersion& version,
@@ -134,6 +244,19 @@
absl::string_view(reinterpret_cast<char*>(iv.data()), iv.size()));
}
+// static
+bool CryptoUtils::GenerateHeaderProtectionKey(
+ const EVP_MD* prf, absl::Span<const uint8_t> pp_secret,
+ const ParsedQuicVersion& version, absl::Span<uint8_t> out) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_obfuscator, 6, 7);
+ char version_label_raw[kMaxVersionLabelLength];
+ constexpr absl::string_view kHeaderProtectionPredicate = "hp";
+ absl::string_view version_label = GetLabelForVersion(
+ version, kHeaderProtectionPredicate,
+ absl::Span<char>(version_label_raw, kMaxVersionLabelLength));
+ return HkdfExpandLabel(prf, pp_secret, version_label, out);
+}
+// TODO(martinduke): Delete this.
std::vector<uint8_t> CryptoUtils::GenerateHeaderProtectionKey(
const EVP_MD* prf, absl::Span<const uint8_t> pp_secret,
const ParsedQuicVersion& version, size_t out_len) {
@@ -141,6 +264,19 @@
out_len);
}
+// static
+bool CryptoUtils::GenerateNextKeyPhaseSecret(
+ const EVP_MD* prf, const ParsedQuicVersion& version,
+ const absl::Span<const uint8_t> current_secret, absl::Span<uint8_t> out) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_obfuscator, 7, 7);
+ char version_label_raw[kMaxVersionLabelLength];
+ constexpr absl::string_view kKeyUpdatePredicate = "ku";
+ absl::string_view version_label = GetLabelForVersion(
+ version, kKeyUpdatePredicate,
+ absl::Span<char>(version_label_raw, kMaxVersionLabelLength));
+ return HkdfExpandLabel(prf, current_secret, version_label, out);
+}
+// TODO(martinduke): Delete this.
std::vector<uint8_t> CryptoUtils::GenerateNextKeyPhaseSecret(
const EVP_MD* prf, const ParsedQuicVersion& version,
const std::vector<uint8_t>& current_secret) {
@@ -277,10 +413,89 @@
} // namespace
// static
+void CryptoUtils::PopulateInitialObfuscators(Perspective perspective,
+ const ParsedQuicVersion& version,
+ QuicConnectionId& connection_id,
+ QuicCrypter* encrypter,
+ QuicCrypter* decrypter) {
+ QUIC_BUG_IF(quic_bug_12871_1, !QuicUtils::IsConnectionIdValidForVersion(
+ connection_id, version.transport_version))
+ << "CreateTlsInitialCrypters: attempted to use connection ID "
+ << connection_id << " which is invalid with version " << version;
+ const EVP_MD* hash = EVP_sha256();
+
+ size_t salt_len;
+ const uint8_t* salt = InitialSaltForVersion(version, &salt_len);
+ uint8_t handshake_secret_raw[EVP_MAX_MD_SIZE];
+ size_t handshake_secret_len;
+ const bool hkdf_extract_success =
+ HKDF_extract(handshake_secret_raw, &handshake_secret_len, hash,
+ reinterpret_cast<const uint8_t*>(connection_id.data()),
+ connection_id.length(), salt, salt_len);
+ QUIC_BUG_IF(quic_bug_12871_2, !hkdf_extract_success)
+ << "HKDF_extract failed when creating initial crypters";
+ absl::Span<const uint8_t> handshake_secret(handshake_secret_raw,
+ handshake_secret_len);
+
+ constexpr absl::string_view kClientLabel = "client in";
+ constexpr absl::string_view kServerLabel = "server in";
+ absl::string_view encryption_label, decryption_label;
+ if (perspective == Perspective::IS_CLIENT) {
+ encryption_label = absl::string_view(kClientLabel);
+ decryption_label = absl::string_view(kServerLabel);
+ } else {
+ encryption_label = absl::string_view(kServerLabel);
+ decryption_label = absl::string_view(kClientLabel);
+ }
+ if (encrypter != nullptr) {
+ uint8_t encryption_secret[32];
+ QUIC_BUG_IF(quic_bug_digest_mismatch,
+ EVP_MD_size(hash) != sizeof(encryption_secret))
+ << "EVP_MD_size(hash) != sizeof(encryption_secret)";
+ HkdfExpandLabel(
+ hash, handshake_secret, encryption_label,
+ absl::Span<uint8_t>(encryption_secret, sizeof(encryption_secret)));
+ InitializeCrypterSecrets(hash, encryption_secret, version, encrypter);
+ }
+ if (decrypter != nullptr) {
+ uint8_t decryption_secret[32];
+ QUIC_BUG_IF(quic_bug_digest_mismatch,
+ EVP_MD_size(hash) != sizeof(decryption_secret))
+ << "EVP_MD_size(hash) != sizeof(decryption_secret)";
+ HkdfExpandLabel(
+ hash, handshake_secret, decryption_label,
+ absl::Span<uint8_t>(decryption_secret, sizeof(decryption_secret)));
+ InitializeCrypterSecrets(hash, decryption_secret, version, decrypter);
+ }
+}
+
+// static
+void CryptoUtils::CreateInitialObfuscatorsNew(Perspective perspective,
+ ParsedQuicVersion version,
+ QuicConnectionId connection_id,
+ CrypterPair* crypters) {
+ if (!version.UsesInitialObfuscators()) {
+ crypters->encrypter = std::make_unique<NullEncrypter>(perspective);
+ crypters->decrypter = std::make_unique<NullDecrypter>(perspective);
+ return;
+ }
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_obfuscator, 1, 7);
+ crypters->encrypter = std::make_unique<Aes128GcmEncrypter>();
+ crypters->decrypter = std::make_unique<Aes128GcmDecrypter>();
+
+ PopulateInitialObfuscators(perspective, version, connection_id,
+ crypters->encrypter.get(),
+ crypters->decrypter.get());
+}
+// static
void CryptoUtils::CreateInitialObfuscators(Perspective perspective,
ParsedQuicVersion version,
QuicConnectionId connection_id,
CrypterPair* crypters) {
+ if (GetQuicReloadableFlag(quic_heapless_obfuscator)) {
+ CreateInitialObfuscatorsNew(perspective, version, connection_id, crypters);
+ return;
+ }
QUIC_DLOG(INFO) << "Creating "
<< (perspective == Perspective::IS_CLIENT ? "client"
: "server")
diff --git a/quiche/quic/core/crypto/crypto_utils.h b/quiche/quic/core/crypto/crypto_utils.h
index 31ec380..f1541ab 100644
--- a/quiche/quic/core/crypto/crypto_utils.h
+++ b/quiche/quic/core/crypto/crypto_utils.h
@@ -10,20 +10,20 @@
#include <cstddef>
#include <cstdint>
#include <string>
+#include <vector>
#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
#include "openssl/evp.h"
#include "openssl/ssl.h"
#include "quiche/quic/core/crypto/crypto_handshake.h"
#include "quiche/quic/core/crypto/crypto_handshake_message.h"
-#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/crypto/quic_crypter.h"
#include "quiche/quic/core/crypto/quic_random.h"
#include "quiche/quic/core/quic_connection_id.h"
-#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
-#include "quiche/quic/platform/api/quic_export.h"
namespace quic {
@@ -81,6 +81,12 @@
// as described in draft-ietf-quic-tls-14, section 5.1, or "quicv2 " as
// described in draft-ietf-quic-v2-01.
static void InitializeCrypterSecrets(const EVP_MD* prf,
+ absl::Span<const uint8_t> pp_secret,
+ const ParsedQuicVersion& version,
+ QuicCrypter* crypter);
+ // Overloaded legacy version that takes a vector.
+ // TODO(martinduke): Delete this.
+ static void InitializeCrypterSecrets(const EVP_MD* prf,
const std::vector<uint8_t>& pp_secret,
const ParsedQuicVersion& version,
QuicCrypter* crypter);
@@ -89,21 +95,48 @@
// fields on the given QuicCrypter |*crypter|, but does not set the header
// protection key. GenerateHeaderProtectionKey/SetHeaderProtectionKey must be
// called before using |crypter|.
+ static void SetKeyAndIVHeapless(const EVP_MD* prf,
+ absl::Span<const uint8_t> pp_secret,
+ const ParsedQuicVersion& version,
+ QuicCrypter* crypter);
+ // TODO(martinduke): Delete this legacy version that allocates more from the
+ // heap.
static void SetKeyAndIV(const EVP_MD* prf,
absl::Span<const uint8_t> pp_secret,
const ParsedQuicVersion& version,
QuicCrypter* crypter);
- // Derives the header protection key from the packet protection secret.
+ // Derives the header protection key from the packet protection secret. Writes
+ // the result to |out|, limited by the size of the provided span. Returns true
+ // if the derivation was successful, false otherwise.
+ static bool GenerateHeaderProtectionKey(const EVP_MD* prf,
+ absl::Span<const uint8_t> pp_secret,
+ const ParsedQuicVersion& version,
+ absl::Span<uint8_t> out);
+ // Overloaded legacy version that allocates the vector.
static std::vector<uint8_t> GenerateHeaderProtectionKey(
const EVP_MD* prf, absl::Span<const uint8_t> pp_secret,
const ParsedQuicVersion& version, size_t out_len);
- // Given a secret for key phase n, return the secret for phase n+1.
+ // Given a secret for key phase n, return the secret for phase n+1 in |out|.
+ // Returns true if the derivation was successful, false otherwise.
+ static bool GenerateNextKeyPhaseSecret(
+ const EVP_MD* prf, const ParsedQuicVersion& version,
+ absl::Span<const uint8_t> current_secret, absl::Span<uint8_t> out);
+ // Overloaded legacy version that allocates the vector.
static std::vector<uint8_t> GenerateNextKeyPhaseSecret(
const EVP_MD* prf, const ParsedQuicVersion& version,
const std::vector<uint8_t>& current_secret);
+ // Assumes Initial crypters have already been allocated, to create a path
+ // with heap allocations limited to those inherited from OpenSSL. |encrypter|
+ // or |decrypter| may be nullptr.
+ static void PopulateInitialObfuscators(Perspective perspective,
+ const ParsedQuicVersion& version,
+ QuicConnectionId& connection_id,
+ QuicCrypter* encrypter,
+ QuicCrypter* decrypter);
+
// IETF QUIC encrypts ENCRYPTION_INITIAL messages with a version-specific key
// (to prevent network observers that are not aware of that QUIC version from
// making decisions based on the TLS handshake). This packet protection secret
@@ -114,6 +147,10 @@
// as setting the key and IV on those crypters. For older versions of QUIC
// that do not use the new IETF style ENCRYPTION_INITIAL obfuscators, this
// function puts a NullEncrypter and NullDecrypter in |*crypters|.
+ static void CreateInitialObfuscatorsNew(Perspective perspective,
+ ParsedQuicVersion version,
+ QuicConnectionId connection_id,
+ CrypterPair* crypters);
static void CreateInitialObfuscators(Perspective perspective,
ParsedQuicVersion version,
QuicConnectionId connection_id,
diff --git a/quiche/quic/core/crypto/crypto_utils_test.cc b/quiche/quic/core/crypto/crypto_utils_test.cc
index 6ddc522..e585d57 100644
--- a/quiche/quic/core/crypto/crypto_utils_test.cc
+++ b/quiche/quic/core/crypto/crypto_utils_test.cc
@@ -176,7 +176,97 @@
// Test that the library is using the correct labels for each version, and
// therefore generating correct obfuscators, using the test vectors in appendix
// A of each RFC or internet-draft.
-TEST_F(CryptoUtilsTest, ValidateCryptoLabels) {
+TEST_F(CryptoUtilsTest, ValidateCryptoLabelsHeapless) {
+ SetQuicReloadableFlag(quic_heapless_obfuscator, true);
+ // if the number of HTTP/3 QUIC versions has changed, we need to change the
+ // expected_keys hardcoded into this test. Regrettably, this is not a
+ // compile-time constant.
+ EXPECT_EQ(AllSupportedVersionsWithTls().size(), 3u);
+ const char draft_29_key[] = {// test vector from draft-ietf-quic-tls-29, A.1
+ 0x14,
+ static_cast<char>(0x9d),
+ 0x0b,
+ 0x16,
+ 0x62,
+ static_cast<char>(0xab),
+ static_cast<char>(0x87),
+ 0x1f,
+ static_cast<char>(0xbe),
+ 0x63,
+ static_cast<char>(0xc4),
+ static_cast<char>(0x9b),
+ 0x5e,
+ 0x65,
+ 0x5a,
+ 0x5d};
+ const char v1_key[] = {// test vector from RFC 9001, A.1
+ static_cast<char>(0xcf),
+ 0x3a,
+ 0x53,
+ 0x31,
+ 0x65,
+ 0x3c,
+ 0x36,
+ 0x4c,
+ static_cast<char>(0x88),
+ static_cast<char>(0xf0),
+ static_cast<char>(0xf3),
+ 0x79,
+ static_cast<char>(0xb6),
+ 0x06,
+ 0x7e,
+ 0x37};
+ const char v2_08_key[] = {// test vector from draft-ietf-quic-v2-08
+ static_cast<char>(0x82),
+ static_cast<char>(0xdb),
+ static_cast<char>(0x63),
+ static_cast<char>(0x78),
+ static_cast<char>(0x61),
+ static_cast<char>(0xd5),
+ static_cast<char>(0x5e),
+ 0x1d,
+ static_cast<char>(0x01),
+ static_cast<char>(0x1f),
+ 0x19,
+ static_cast<char>(0xea),
+ 0x71,
+ static_cast<char>(0xd5),
+ static_cast<char>(0xd2),
+ static_cast<char>(0xa7)};
+ const char connection_id[] = // test vector from both docs
+ {static_cast<char>(0x83),
+ static_cast<char>(0x94),
+ static_cast<char>(0xc8),
+ static_cast<char>(0xf0),
+ 0x3e,
+ 0x51,
+ 0x57,
+ 0x08};
+ const QuicConnectionId cid(connection_id, sizeof(connection_id));
+ const char* key_str;
+ size_t key_size;
+ for (const ParsedQuicVersion& version : AllSupportedVersionsWithTls()) {
+ if (version == ParsedQuicVersion::Draft29()) {
+ key_str = draft_29_key;
+ key_size = sizeof(draft_29_key);
+ } else if (version == ParsedQuicVersion::RFCv1()) {
+ key_str = v1_key;
+ key_size = sizeof(v1_key);
+ } else { // draft-ietf-quic-v2-01
+ key_str = v2_08_key;
+ key_size = sizeof(v2_08_key);
+ }
+ const absl::string_view expected_key{key_str, key_size};
+
+ CrypterPair crypters;
+ CryptoUtils::CreateInitialObfuscators(Perspective::IS_SERVER, version, cid,
+ &crypters);
+ EXPECT_EQ(crypters.encrypter->GetKey(), expected_key);
+ }
+}
+
+TEST_F(CryptoUtilsTest, ValidateCryptoLabelsHeap) {
+ SetQuicReloadableFlag(quic_heapless_obfuscator, false);
// if the number of HTTP/3 QUIC versions has changed, we need to change the
// expected_keys hardcoded into this test. Regrettably, this is not a
// compile-time constant.
diff --git a/quiche/quic/core/http/quic_spdy_session.cc b/quiche/quic/core/http/quic_spdy_session.cc
index de560d4..d322e27 100644
--- a/quiche/quic/core/http/quic_spdy_session.cc
+++ b/quiche/quic/core/http/quic_spdy_session.cc
@@ -19,6 +19,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "quiche/http2/core/http2_frame_decoder_adapter.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/http/http_constants.h"
#include "quiche/quic/core/http/http_decoder.h"
#include "quiche/quic/core/http/http_frames.h"
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index 6e79b25..be6aa0a 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -18,6 +18,7 @@
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
+#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/frames/quic_ack_frequency_frame.h"
#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/frames/quic_window_update_frame.h"
@@ -38,7 +39,6 @@
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_server_stats.h"
-#include "quiche/quic/platform/api/quic_stack_trace.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_callbacks.h"
#include "quiche/common/quiche_text_utils.h"