Project import generated by Copybara.
PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/test_tools/crypto_test_utils.cc b/quic/test_tools/crypto_test_utils.cc
new file mode 100644
index 0000000..7179288
--- /dev/null
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -0,0 +1,1046 @@
+// 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/test_tools/crypto_test_utils.h"
+
+#include <memory>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/bn.h"
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ecdsa.h"
+#include "third_party/boringssl/src/include/openssl/nid.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+
+namespace quic {
+namespace test {
+
+TestChannelIDKey::TestChannelIDKey(EVP_PKEY* ecdsa_key)
+ : ecdsa_key_(ecdsa_key) {}
+TestChannelIDKey::~TestChannelIDKey() {}
+
+bool TestChannelIDKey::Sign(QuicStringPiece signed_data,
+ QuicString* out_signature) const {
+ bssl::ScopedEVP_MD_CTX md_ctx;
+ if (EVP_DigestSignInit(md_ctx.get(), nullptr, EVP_sha256(), nullptr,
+ ecdsa_key_.get()) != 1) {
+ return false;
+ }
+
+ EVP_DigestUpdate(md_ctx.get(), ChannelIDVerifier::kContextStr,
+ strlen(ChannelIDVerifier::kContextStr) + 1);
+ EVP_DigestUpdate(md_ctx.get(), ChannelIDVerifier::kClientToServerStr,
+ strlen(ChannelIDVerifier::kClientToServerStr) + 1);
+ EVP_DigestUpdate(md_ctx.get(), signed_data.data(), signed_data.size());
+
+ size_t sig_len;
+ if (!EVP_DigestSignFinal(md_ctx.get(), nullptr, &sig_len)) {
+ return false;
+ }
+
+ std::unique_ptr<uint8_t[]> der_sig(new uint8_t[sig_len]);
+ if (!EVP_DigestSignFinal(md_ctx.get(), der_sig.get(), &sig_len)) {
+ return false;
+ }
+
+ uint8_t* derp = der_sig.get();
+ bssl::UniquePtr<ECDSA_SIG> sig(
+ d2i_ECDSA_SIG(nullptr, const_cast<const uint8_t**>(&derp), sig_len));
+ if (sig.get() == nullptr) {
+ return false;
+ }
+
+ // The signature consists of a pair of 32-byte numbers.
+ static const size_t kSignatureLength = 32 * 2;
+ std::unique_ptr<uint8_t[]> signature(new uint8_t[kSignatureLength]);
+ if (!BN_bn2bin_padded(&signature[0], 32, sig->r) ||
+ !BN_bn2bin_padded(&signature[32], 32, sig->s)) {
+ return false;
+ }
+
+ *out_signature =
+ QuicString(reinterpret_cast<char*>(signature.get()), kSignatureLength);
+
+ return true;
+}
+
+QuicString TestChannelIDKey::SerializeKey() const {
+ // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
+ // key, is 0x04 (meaning uncompressed) followed by the x and y field
+ // elements as 32-byte, big-endian numbers.
+ static const int kExpectedKeyLength = 65;
+
+ int len = i2d_PublicKey(ecdsa_key_.get(), nullptr);
+ if (len != kExpectedKeyLength) {
+ return "";
+ }
+
+ uint8_t buf[kExpectedKeyLength];
+ uint8_t* derp = buf;
+ i2d_PublicKey(ecdsa_key_.get(), &derp);
+
+ return QuicString(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1);
+}
+
+TestChannelIDSource::~TestChannelIDSource() {}
+
+QuicAsyncStatus TestChannelIDSource::GetChannelIDKey(
+ const QuicString& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) {
+ *channel_id_key = QuicMakeUnique<TestChannelIDKey>(HostnameToKey(hostname));
+ return QUIC_SUCCESS;
+}
+
+// static
+EVP_PKEY* TestChannelIDSource::HostnameToKey(const QuicString& hostname) {
+ // In order to generate a deterministic key for a given hostname the
+ // hostname is hashed with SHA-256 and the resulting digest is treated as a
+ // big-endian number. The most-significant bit is cleared to ensure that
+ // the resulting value is less than the order of the group and then it's
+ // taken as a private key. Given the private key, the public key is
+ // calculated with a group multiplication.
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ SHA256_Update(&sha256, hostname.data(), hostname.size());
+
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_Final(digest, &sha256);
+
+ // Ensure that the digest is less than the order of the P-256 group by
+ // clearing the most-significant bit.
+ digest[0] &= 0x7f;
+
+ bssl::UniquePtr<BIGNUM> k(BN_new());
+ CHECK(BN_bin2bn(digest, sizeof(digest), k.get()) != nullptr);
+
+ bssl::UniquePtr<EC_GROUP> p256(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ CHECK(p256);
+
+ bssl::UniquePtr<EC_KEY> ecdsa_key(EC_KEY_new());
+ CHECK(ecdsa_key && EC_KEY_set_group(ecdsa_key.get(), p256.get()));
+
+ bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
+ CHECK(EC_POINT_mul(p256.get(), point.get(), k.get(), nullptr, nullptr,
+ nullptr));
+
+ EC_KEY_set_private_key(ecdsa_key.get(), k.get());
+ EC_KEY_set_public_key(ecdsa_key.get(), point.get());
+
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+ // EVP_PKEY_set1_EC_KEY takes a reference so no |release| here.
+ EVP_PKEY_set1_EC_KEY(pkey.get(), ecdsa_key.get());
+
+ return pkey.release();
+}
+
+namespace crypto_test_utils {
+
+namespace {
+
+// CryptoFramerVisitor is a framer visitor that records handshake messages.
+class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
+ public:
+ CryptoFramerVisitor() : error_(false) {}
+
+ void OnError(CryptoFramer* framer) override { error_ = true; }
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ messages_.push_back(message);
+ }
+
+ bool error() const { return error_; }
+
+ const std::vector<CryptoHandshakeMessage>& messages() const {
+ return messages_;
+ }
+
+ private:
+ bool error_;
+ std::vector<CryptoHandshakeMessage> messages_;
+};
+
+// HexChar parses |c| as a hex character. If valid, it sets |*value| to the
+// value of the hex character and returns true. Otherwise it returns false.
+bool HexChar(char c, uint8_t* value) {
+ if (c >= '0' && c <= '9') {
+ *value = c - '0';
+ return true;
+ }
+ if (c >= 'a' && c <= 'f') {
+ *value = c - 'a' + 10;
+ return true;
+ }
+ if (c >= 'A' && c <= 'F') {
+ *value = c - 'A' + 10;
+ return true;
+ }
+ return false;
+}
+
+// A ChannelIDSource that works in asynchronous mode unless the |callback|
+// argument to GetChannelIDKey is nullptr.
+class AsyncTestChannelIDSource : public ChannelIDSource, public CallbackSource {
+ public:
+ // Takes ownership of |sync_source|, a synchronous ChannelIDSource.
+ explicit AsyncTestChannelIDSource(ChannelIDSource* sync_source)
+ : sync_source_(sync_source) {}
+ ~AsyncTestChannelIDSource() override {}
+
+ // ChannelIDSource implementation.
+ QuicAsyncStatus GetChannelIDKey(const QuicString& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) override {
+ // Synchronous mode.
+ if (!callback) {
+ return sync_source_->GetChannelIDKey(hostname, channel_id_key, nullptr);
+ }
+
+ // Asynchronous mode.
+ QuicAsyncStatus status =
+ sync_source_->GetChannelIDKey(hostname, &channel_id_key_, nullptr);
+ if (status != QUIC_SUCCESS) {
+ return QUIC_FAILURE;
+ }
+ callback_.reset(callback);
+ return QUIC_PENDING;
+ }
+
+ // CallbackSource implementation.
+ void RunPendingCallbacks() override {
+ if (callback_) {
+ callback_->Run(&channel_id_key_);
+ callback_.reset();
+ }
+ }
+
+ private:
+ std::unique_ptr<ChannelIDSource> sync_source_;
+ std::unique_ptr<ChannelIDSourceCallback> callback_;
+ std::unique_ptr<ChannelIDKey> channel_id_key_;
+};
+
+} // anonymous namespace
+
+FakeServerOptions::FakeServerOptions() {}
+
+FakeServerOptions::~FakeServerOptions() {}
+
+FakeClientOptions::FakeClientOptions()
+ : channel_id_enabled(false), channel_id_source_async(false) {}
+
+FakeClientOptions::~FakeClientOptions() {}
+
+namespace {
+// This class is used by GenerateFullCHLO() to extract SCID and STK from
+// REJ/SREJ and to construct a full CHLO with these fields and given inchoate
+// CHLO.
+class FullChloGenerator {
+ public:
+ FullChloGenerator(
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ CryptoHandshakeMessage* out)
+ : crypto_config_(crypto_config),
+ server_addr_(server_addr),
+ client_addr_(client_addr),
+ clock_(clock),
+ signed_config_(signed_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ out_(out),
+ params_(new QuicCryptoNegotiatedParameters) {}
+
+ class ValidateClientHelloCallback : public ValidateClientHelloResultCallback {
+ public:
+ explicit ValidateClientHelloCallback(FullChloGenerator* generator)
+ : generator_(generator) {}
+ void Run(QuicReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result> result,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ generator_->ValidateClientHelloDone(std::move(result));
+ }
+
+ private:
+ FullChloGenerator* generator_;
+ };
+
+ std::unique_ptr<ValidateClientHelloCallback>
+ GetValidateClientHelloCallback() {
+ return QuicMakeUnique<ValidateClientHelloCallback>(this);
+ }
+
+ private:
+ void ValidateClientHelloDone(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result) {
+ result_ = result;
+ crypto_config_->ProcessClientHello(
+ result_, /*reject_only=*/false, TestConnectionId(1), server_addr_,
+ client_addr_, AllSupportedVersions().front(), AllSupportedVersions(),
+ /*use_stateless_rejects=*/true,
+ /*server_designated_connection_id=*/TestConnectionId(2), clock_,
+ QuicRandom::GetInstance(), compressed_certs_cache_, params_,
+ signed_config_, /*total_framing_overhead=*/50, kDefaultMaxPacketSize,
+ GetProcessClientHelloCallback());
+ }
+
+ class ProcessClientHelloCallback : public ProcessClientHelloResultCallback {
+ public:
+ explicit ProcessClientHelloCallback(FullChloGenerator* generator)
+ : generator_(generator) {}
+ void Run(
+ QuicErrorCode error,
+ const QuicString& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) override {
+ generator_->ProcessClientHelloDone(std::move(message));
+ }
+
+ private:
+ FullChloGenerator* generator_;
+ };
+
+ std::unique_ptr<ProcessClientHelloCallback> GetProcessClientHelloCallback() {
+ return QuicMakeUnique<ProcessClientHelloCallback>(this);
+ }
+
+ void ProcessClientHelloDone(std::unique_ptr<CryptoHandshakeMessage> rej) {
+ // Verify output is a REJ or SREJ.
+ EXPECT_THAT(rej->tag(),
+ testing::AnyOf(testing::Eq(kSREJ), testing::Eq(kREJ)));
+
+ VLOG(1) << "Extract valid STK and SCID from\n" << rej->DebugString();
+ QuicStringPiece srct;
+ ASSERT_TRUE(rej->GetStringPiece(kSourceAddressTokenTag, &srct));
+
+ QuicStringPiece scfg;
+ ASSERT_TRUE(rej->GetStringPiece(kSCFG, &scfg));
+ std::unique_ptr<CryptoHandshakeMessage> server_config(
+ CryptoFramer::ParseMessage(scfg));
+
+ QuicStringPiece scid;
+ ASSERT_TRUE(server_config->GetStringPiece(kSCID, &scid));
+
+ *out_ = result_->client_hello;
+ out_->SetStringPiece(kSCID, scid);
+ out_->SetStringPiece(kSourceAddressTokenTag, srct);
+ uint64_t xlct = LeafCertHashForTesting();
+ out_->SetValue(kXLCT, xlct);
+ }
+
+ protected:
+ QuicCryptoServerConfig* crypto_config_;
+ QuicSocketAddress server_addr_;
+ QuicSocketAddress client_addr_;
+ const QuicClock* clock_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+ CryptoHandshakeMessage* out_;
+
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result_;
+};
+
+} // namespace
+
+int HandshakeWithFakeServer(QuicConfig* server_quic_config,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* client_conn,
+ QuicCryptoClientStream* client,
+ const FakeServerOptions& options) {
+ PacketSavingConnection* server_conn = new PacketSavingConnection(
+ helper, alarm_factory, Perspective::IS_SERVER,
+ ParsedVersionOfIndex(client_conn->supported_versions(), 0));
+
+ QuicCryptoServerConfig crypto_config(
+ QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
+ ProofSourceForTesting(), KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+ SetupCryptoServerConfigForTest(server_conn->clock(),
+ server_conn->random_generator(),
+ &crypto_config, options);
+
+ TestQuicSpdyServerSession server_session(
+ server_conn, *server_quic_config, client_conn->supported_versions(),
+ &crypto_config, &compressed_certs_cache);
+ server_session.OnSuccessfulVersionNegotiation(
+ client_conn->supported_versions().front());
+ EXPECT_CALL(*server_session.helper(),
+ CanAcceptClientHello(testing::_, testing::_, testing::_,
+ testing::_, testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_session.helper(),
+ GenerateConnectionIdForReject(testing::_, testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_conn, OnCanWrite()).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+
+ // The client's handshake must have been started already.
+ CHECK_NE(0u, client_conn->encrypted_packets_.size());
+
+ CommunicateHandshakeMessages(client_conn, client, server_conn,
+ server_session.GetMutableCryptoStream());
+ CompareClientAndServerKeys(client, server_session.GetMutableCryptoStream());
+
+ return client->num_sent_client_hellos();
+}
+
+int HandshakeWithFakeClient(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* server_conn,
+ QuicCryptoServerStream* server,
+ const QuicServerId& server_id,
+ const FakeClientOptions& options) {
+ ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ if (options.only_tls_versions) {
+ supported_versions.clear();
+ for (QuicTransportVersion transport_version :
+ AllSupportedTransportVersions()) {
+ supported_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, transport_version));
+ }
+ }
+ PacketSavingConnection* client_conn = new PacketSavingConnection(
+ helper, alarm_factory, Perspective::IS_CLIENT, supported_versions);
+ // Advance the time, because timers do not like uninitialized times.
+ client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+
+ QuicCryptoClientConfig crypto_config(ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ AsyncTestChannelIDSource* async_channel_id_source = nullptr;
+ if (options.channel_id_enabled) {
+ ChannelIDSource* source = ChannelIDSourceForTesting();
+ if (options.channel_id_source_async) {
+ async_channel_id_source = new AsyncTestChannelIDSource(source);
+ source = async_channel_id_source;
+ }
+ crypto_config.SetChannelIDSource(source);
+ }
+ if (!options.token_binding_params.empty()) {
+ crypto_config.tb_key_params = options.token_binding_params;
+ }
+ TestQuicSpdyClientSession client_session(client_conn, DefaultQuicConfig(),
+ supported_versions, server_id,
+ &crypto_config);
+
+ EXPECT_CALL(client_session, OnProofValid(testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(client_session, OnProofVerifyDetailsAvailable(testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+ client_session.GetMutableCryptoStream()->CryptoConnect();
+ CHECK_EQ(1u, client_conn->encrypted_packets_.size());
+
+ CommunicateHandshakeMessagesAndRunCallbacks(
+ client_conn, client_session.GetMutableCryptoStream(), server_conn, server,
+ async_channel_id_source);
+
+ if (server->handshake_confirmed() && server->encryption_established()) {
+ CompareClientAndServerKeys(client_session.GetMutableCryptoStream(), server);
+
+ if (options.channel_id_enabled) {
+ std::unique_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ crypto_config.channel_id_source()->GetChannelIDKey(
+ server_id.host(), &channel_id_key, nullptr);
+ EXPECT_EQ(QUIC_SUCCESS, status);
+ EXPECT_EQ(channel_id_key->SerializeKey(),
+ server->crypto_negotiated_params().channel_id);
+ EXPECT_EQ(
+ options.channel_id_source_async,
+ client_session.GetCryptoStream()->WasChannelIDSourceCallbackRun());
+ }
+ }
+
+ return client_session.GetCryptoStream()->num_sent_client_hellos();
+}
+
+void SetupCryptoServerConfigForTest(const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCryptoServerConfig* crypto_config,
+ const FakeServerOptions& fake_options) {
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.channel_id_enabled = true;
+ options.token_binding_params = fake_options.token_binding_params;
+ std::unique_ptr<CryptoHandshakeMessage> scfg(
+ crypto_config->AddDefaultConfig(rand, clock, options));
+}
+
+void SendHandshakeMessageToStream(QuicCryptoStream* stream,
+ const CryptoHandshakeMessage& message,
+ Perspective perspective) {
+ const QuicData& data = message.GetSerialized();
+ QuicSession* session = QuicStreamPeer::session(stream);
+ if (session->connection()->transport_version() < QUIC_VERSION_47) {
+ QuicStreamFrame frame(QuicUtils::GetCryptoStreamId(
+ session->connection()->transport_version()),
+ false, stream->crypto_bytes_read(),
+ data.AsStringPiece());
+ stream->OnStreamFrame(frame);
+ } else {
+ EncryptionLevel level = session->connection()->last_decrypted_level();
+ QuicCryptoFrame frame(level, stream->BytesReadOnLevel(level),
+ data.AsStringPiece());
+ stream->OnCryptoFrame(frame);
+ }
+}
+
+void CommunicateHandshakeMessages(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server) {
+ CommunicateHandshakeMessagesAndRunCallbacks(client_conn, client, server_conn,
+ server, nullptr);
+}
+
+void CommunicateHandshakeMessagesAndRunCallbacks(
+ PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ CallbackSource* callback_source) {
+ size_t client_i = 0, server_i = 0;
+ while (!client->handshake_confirmed() || !server->handshake_confirmed()) {
+ ASSERT_GT(client_conn->encrypted_packets_.size(), client_i);
+ QUIC_LOG(INFO) << "Processing "
+ << client_conn->encrypted_packets_.size() - client_i
+ << " packets client->server";
+ MovePackets(client_conn, &client_i, server, server_conn,
+ Perspective::IS_SERVER);
+ if (callback_source) {
+ callback_source->RunPendingCallbacks();
+ }
+
+ if (client->handshake_confirmed() && server->handshake_confirmed()) {
+ break;
+ }
+ ASSERT_GT(server_conn->encrypted_packets_.size(), server_i);
+ QUIC_LOG(INFO) << "Processing "
+ << server_conn->encrypted_packets_.size() - server_i
+ << " packets server->client";
+ MovePackets(server_conn, &server_i, client, client_conn,
+ Perspective::IS_CLIENT);
+ if (callback_source) {
+ callback_source->RunPendingCallbacks();
+ }
+ }
+}
+
+std::pair<size_t, size_t> AdvanceHandshake(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ size_t client_i,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ size_t server_i) {
+ QUIC_LOG(INFO) << "Processing "
+ << client_conn->encrypted_packets_.size() - client_i
+ << " packets client->server";
+ MovePackets(client_conn, &client_i, server, server_conn,
+ Perspective::IS_SERVER);
+
+ QUIC_LOG(INFO) << "Processing "
+ << server_conn->encrypted_packets_.size() - server_i
+ << " packets server->client";
+ if (server_conn->encrypted_packets_.size() - server_i == 2) {
+ QUIC_LOG(INFO) << "here";
+ }
+ MovePackets(server_conn, &server_i, client, client_conn,
+ Perspective::IS_CLIENT);
+
+ return std::make_pair(client_i, server_i);
+}
+
+QuicString GetValueForTag(const CryptoHandshakeMessage& message, QuicTag tag) {
+ auto it = message.tag_value_map().find(tag);
+ if (it == message.tag_value_map().end()) {
+ return QuicString();
+ }
+ return it->second;
+}
+
+uint64_t LeafCertHashForTesting() {
+ QuicReferenceCountedPointer<ProofSource::Chain> chain;
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 42);
+ QuicCryptoProof proof;
+ std::unique_ptr<ProofSource> proof_source(ProofSourceForTesting());
+
+ class Callback : public ProofSource::Callback {
+ public:
+ Callback(bool* ok, QuicReferenceCountedPointer<ProofSource::Chain>* chain)
+ : ok_(ok), chain_(chain) {}
+
+ void Run(bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& /* proof */,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ *ok_ = ok;
+ *chain_ = chain;
+ }
+
+ private:
+ bool* ok_;
+ QuicReferenceCountedPointer<ProofSource::Chain>* chain_;
+ };
+
+ // Note: relies on the callback being invoked synchronously
+ bool ok = false;
+ proof_source->GetProof(
+ server_address, "", "", AllSupportedTransportVersions().front(), "",
+ std::unique_ptr<ProofSource::Callback>(new Callback(&ok, &chain)));
+ if (!ok || chain->certs.empty()) {
+ DCHECK(false) << "Proof generation failed";
+ return 0;
+ }
+
+ return QuicUtils::FNV1a_64_Hash(chain->certs.at(0));
+}
+
+class MockCommonCertSets : public CommonCertSets {
+ public:
+ MockCommonCertSets(QuicStringPiece cert, uint64_t hash, uint32_t index)
+ : cert_(cert), hash_(hash), index_(index) {}
+
+ QuicStringPiece GetCommonHashes() const override {
+ QUIC_BUG << "not implemented";
+ return QuicStringPiece();
+ }
+
+ QuicStringPiece GetCert(uint64_t hash, uint32_t index) const override {
+ if (hash == hash_ && index == index_) {
+ return cert_;
+ }
+ return QuicStringPiece();
+ }
+
+ bool MatchCert(QuicStringPiece cert,
+ QuicStringPiece common_set_hashes,
+ uint64_t* out_hash,
+ uint32_t* out_index) const override {
+ if (cert != cert_) {
+ return false;
+ }
+
+ if (common_set_hashes.size() % sizeof(uint64_t) != 0) {
+ return false;
+ }
+ bool client_has_set = false;
+ for (size_t i = 0; i < common_set_hashes.size(); i += sizeof(uint64_t)) {
+ uint64_t hash;
+ memcpy(&hash, common_set_hashes.data() + i, sizeof(hash));
+ if (hash == hash_) {
+ client_has_set = true;
+ break;
+ }
+ }
+
+ if (!client_has_set) {
+ return false;
+ }
+
+ *out_hash = hash_;
+ *out_index = index_;
+ return true;
+ }
+
+ private:
+ const QuicString cert_;
+ const uint64_t hash_;
+ const uint32_t index_;
+};
+
+CommonCertSets* MockCommonCertSets(QuicStringPiece cert,
+ uint64_t hash,
+ uint32_t index) {
+ return new class MockCommonCertSets(cert, hash, index);
+}
+
+void FillInDummyReject(CryptoHandshakeMessage* rej, bool reject_is_stateless) {
+ if (reject_is_stateless) {
+ rej->set_tag(kSREJ);
+ } else {
+ rej->set_tag(kREJ);
+ }
+
+ // Minimum SCFG that passes config validation checks.
+ // clang-format off
+ unsigned char scfg[] = {
+ // SCFG
+ 0x53, 0x43, 0x46, 0x47,
+ // num entries
+ 0x01, 0x00,
+ // padding
+ 0x00, 0x00,
+ // EXPY
+ 0x45, 0x58, 0x50, 0x59,
+ // EXPY end offset
+ 0x08, 0x00, 0x00, 0x00,
+ // Value
+ '1', '2', '3', '4',
+ '5', '6', '7', '8'
+ };
+ // clang-format on
+ rej->SetValue(kSCFG, scfg);
+ rej->SetStringPiece(kServerNonceTag, "SERVER_NONCE");
+ int64_t ttl = 2 * 24 * 60 * 60;
+ rej->SetValue(kSTTL, ttl);
+ std::vector<QuicTag> reject_reasons;
+ reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE);
+ rej->SetVector(kRREJ, reject_reasons);
+}
+
+void CompareClientAndServerKeys(QuicCryptoClientStream* client,
+ QuicCryptoServerStream* server) {
+ QuicFramer* client_framer = QuicConnectionPeer::GetFramer(
+ QuicStreamPeer::session(client)->connection());
+ QuicFramer* server_framer = QuicConnectionPeer::GetFramer(
+ QuicStreamPeer::session(server)->connection());
+ const QuicEncrypter* client_encrypter(
+ QuicFramerPeer::GetEncrypter(client_framer, ENCRYPTION_ZERO_RTT));
+ const QuicDecrypter* client_decrypter(
+ QuicStreamPeer::session(client)->connection()->decrypter());
+ const QuicEncrypter* client_forward_secure_encrypter(
+ QuicFramerPeer::GetEncrypter(client_framer, ENCRYPTION_FORWARD_SECURE));
+ const QuicDecrypter* client_forward_secure_decrypter(
+ QuicStreamPeer::session(client)->connection()->alternative_decrypter());
+ const QuicEncrypter* server_encrypter(
+ QuicFramerPeer::GetEncrypter(server_framer, ENCRYPTION_ZERO_RTT));
+ const QuicDecrypter* server_decrypter(
+ QuicStreamPeer::session(server)->connection()->decrypter());
+ const QuicEncrypter* server_forward_secure_encrypter(
+ QuicFramerPeer::GetEncrypter(server_framer, ENCRYPTION_FORWARD_SECURE));
+ const QuicDecrypter* server_forward_secure_decrypter(
+ QuicStreamPeer::session(server)->connection()->alternative_decrypter());
+
+ QuicStringPiece client_encrypter_key = client_encrypter->GetKey();
+ QuicStringPiece client_encrypter_iv = client_encrypter->GetNoncePrefix();
+ QuicStringPiece client_decrypter_key = client_decrypter->GetKey();
+ QuicStringPiece client_decrypter_iv = client_decrypter->GetNoncePrefix();
+ QuicStringPiece client_forward_secure_encrypter_key =
+ client_forward_secure_encrypter->GetKey();
+ QuicStringPiece client_forward_secure_encrypter_iv =
+ client_forward_secure_encrypter->GetNoncePrefix();
+ QuicStringPiece client_forward_secure_decrypter_key =
+ client_forward_secure_decrypter->GetKey();
+ QuicStringPiece client_forward_secure_decrypter_iv =
+ client_forward_secure_decrypter->GetNoncePrefix();
+ QuicStringPiece server_encrypter_key = server_encrypter->GetKey();
+ QuicStringPiece server_encrypter_iv = server_encrypter->GetNoncePrefix();
+ QuicStringPiece server_decrypter_key = server_decrypter->GetKey();
+ QuicStringPiece server_decrypter_iv = server_decrypter->GetNoncePrefix();
+ QuicStringPiece server_forward_secure_encrypter_key =
+ server_forward_secure_encrypter->GetKey();
+ QuicStringPiece server_forward_secure_encrypter_iv =
+ server_forward_secure_encrypter->GetNoncePrefix();
+ QuicStringPiece server_forward_secure_decrypter_key =
+ server_forward_secure_decrypter->GetKey();
+ QuicStringPiece server_forward_secure_decrypter_iv =
+ server_forward_secure_decrypter->GetNoncePrefix();
+
+ QuicStringPiece client_subkey_secret =
+ client->crypto_negotiated_params().subkey_secret;
+ QuicStringPiece server_subkey_secret =
+ server->crypto_negotiated_params().subkey_secret;
+
+ const char kSampleLabel[] = "label";
+ const char kSampleContext[] = "context";
+ const size_t kSampleOutputLength = 32;
+ QuicString client_key_extraction;
+ QuicString server_key_extraction;
+ QuicString client_tb_ekm;
+ QuicString server_tb_ekm;
+ EXPECT_TRUE(client->ExportKeyingMaterial(kSampleLabel, kSampleContext,
+ kSampleOutputLength,
+ &client_key_extraction));
+ EXPECT_TRUE(server->ExportKeyingMaterial(kSampleLabel, kSampleContext,
+ kSampleOutputLength,
+ &server_key_extraction));
+
+ CompareCharArraysWithHexError("client write key", client_encrypter_key.data(),
+ client_encrypter_key.length(),
+ server_decrypter_key.data(),
+ server_decrypter_key.length());
+ CompareCharArraysWithHexError("client write IV", client_encrypter_iv.data(),
+ client_encrypter_iv.length(),
+ server_decrypter_iv.data(),
+ server_decrypter_iv.length());
+ CompareCharArraysWithHexError("server write key", server_encrypter_key.data(),
+ server_encrypter_key.length(),
+ client_decrypter_key.data(),
+ client_decrypter_key.length());
+ CompareCharArraysWithHexError("server write IV", server_encrypter_iv.data(),
+ server_encrypter_iv.length(),
+ client_decrypter_iv.data(),
+ client_decrypter_iv.length());
+ CompareCharArraysWithHexError("client forward secure write key",
+ client_forward_secure_encrypter_key.data(),
+ client_forward_secure_encrypter_key.length(),
+ server_forward_secure_decrypter_key.data(),
+ server_forward_secure_decrypter_key.length());
+ CompareCharArraysWithHexError("client forward secure write IV",
+ client_forward_secure_encrypter_iv.data(),
+ client_forward_secure_encrypter_iv.length(),
+ server_forward_secure_decrypter_iv.data(),
+ server_forward_secure_decrypter_iv.length());
+ CompareCharArraysWithHexError("server forward secure write key",
+ server_forward_secure_encrypter_key.data(),
+ server_forward_secure_encrypter_key.length(),
+ client_forward_secure_decrypter_key.data(),
+ client_forward_secure_decrypter_key.length());
+ CompareCharArraysWithHexError("server forward secure write IV",
+ server_forward_secure_encrypter_iv.data(),
+ server_forward_secure_encrypter_iv.length(),
+ client_forward_secure_decrypter_iv.data(),
+ client_forward_secure_decrypter_iv.length());
+ CompareCharArraysWithHexError("subkey secret", client_subkey_secret.data(),
+ client_subkey_secret.length(),
+ server_subkey_secret.data(),
+ server_subkey_secret.length());
+ CompareCharArraysWithHexError(
+ "sample key extraction", client_key_extraction.data(),
+ client_key_extraction.length(), server_key_extraction.data(),
+ server_key_extraction.length());
+
+ CompareCharArraysWithHexError("token binding key extraction",
+ client_tb_ekm.data(), client_tb_ekm.length(),
+ server_tb_ekm.data(), server_tb_ekm.length());
+}
+
+QuicTag ParseTag(const char* tagstr) {
+ const size_t len = strlen(tagstr);
+ CHECK_NE(0u, len);
+
+ QuicTag tag = 0;
+
+ if (tagstr[0] == '#') {
+ CHECK_EQ(static_cast<size_t>(1 + 2 * 4), len);
+ tagstr++;
+
+ for (size_t i = 0; i < 8; i++) {
+ tag <<= 4;
+
+ uint8_t v = 0;
+ CHECK(HexChar(tagstr[i], &v));
+ tag |= v;
+ }
+
+ return tag;
+ }
+
+ CHECK_LE(len, 4u);
+ for (size_t i = 0; i < 4; i++) {
+ tag >>= 8;
+ if (i < len) {
+ tag |= static_cast<uint32_t>(tagstr[i]) << 24;
+ }
+ }
+
+ return tag;
+}
+
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<QuicString, QuicString>> tags_and_values) {
+ return CreateCHLO(tags_and_values, -1);
+}
+
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<QuicString, QuicString>> tags_and_values,
+ int minimum_size_bytes) {
+ CryptoHandshakeMessage msg;
+ msg.set_tag(MakeQuicTag('C', 'H', 'L', 'O'));
+
+ if (minimum_size_bytes > 0) {
+ msg.set_minimum_size(minimum_size_bytes);
+ }
+
+ for (const auto& tag_and_value : tags_and_values) {
+ const QuicString& tag = tag_and_value.first;
+ const QuicString& value = tag_and_value.second;
+
+ const QuicTag quic_tag = ParseTag(tag.c_str());
+
+ size_t value_len = value.length();
+ if (value_len > 0 && value[0] == '#') {
+ // This is ascii encoded hex.
+ QuicString hex_value =
+ QuicTextUtils::HexDecode(QuicStringPiece(&value[1]));
+ msg.SetStringPiece(quic_tag, hex_value);
+ continue;
+ }
+ msg.SetStringPiece(quic_tag, value);
+ }
+
+ // The CryptoHandshakeMessage needs to be serialized and parsed to ensure
+ // that any padding is included.
+ std::unique_ptr<QuicData> bytes(CryptoFramer::ConstructHandshakeMessage(msg));
+ std::unique_ptr<CryptoHandshakeMessage> parsed(
+ CryptoFramer::ParseMessage(bytes->AsStringPiece()));
+ CHECK(parsed);
+
+ return *parsed;
+}
+
+ChannelIDSource* ChannelIDSourceForTesting() {
+ return new TestChannelIDSource();
+}
+
+void MovePackets(PacketSavingConnection* source_conn,
+ size_t* inout_packet_index,
+ QuicCryptoStream* dest_stream,
+ PacketSavingConnection* dest_conn,
+ Perspective dest_perspective) {
+ SimpleQuicFramer framer(source_conn->supported_versions(), dest_perspective);
+
+ SimpleQuicFramer null_encryption_framer(source_conn->supported_versions(),
+ dest_perspective);
+
+ size_t index = *inout_packet_index;
+ for (; index < source_conn->encrypted_packets_.size(); index++) {
+ // In order to properly test the code we need to perform encryption and
+ // decryption so that the crypters latch when expected. The crypters are in
+ // |dest_conn|, but we don't want to try and use them there. Instead we swap
+ // them into |framer|, perform the decryption with them, and then swap ther
+ // back.
+ QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+ if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) {
+ // The framer will be unable to decrypt forward-secure packets sent after
+ // the handshake is complete. Don't treat them as handshake packets.
+ break;
+ }
+ QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+ dest_conn->OnDecryptedPacket(framer.last_decrypted_level());
+
+ if (dest_stream->handshake_protocol() == PROTOCOL_TLS1_3) {
+ // Try to process the packet with a framer that only has the NullDecrypter
+ // for decryption. If ProcessPacket succeeds, that means the packet was
+ // encrypted with the NullEncrypter. With the TLS handshaker in use, no
+ // packets should ever be encrypted with the NullEncrypter, instead
+ // they're encrypted with an obfuscation cipher based on QUIC version and
+ // connection ID.
+ ASSERT_FALSE(null_encryption_framer.ProcessPacket(
+ *source_conn->encrypted_packets_[index]))
+ << "No TLS packets should be encrypted with the NullEncrypter";
+ }
+
+ // Since we're using QuicFramers separate from the connections to move
+ // packets, the QuicConnection never gets notified about what level the last
+ // packet was decrypted at. This is needed by TLS to know what encryption
+ // level was used for the data it's receiving, so we plumb this information
+ // from the SimpleQuicFramer back into the connection.
+ dest_conn->OnDecryptedPacket(framer.last_decrypted_level());
+
+ QuicConnectionPeer::SetCurrentPacket(
+ dest_conn, source_conn->encrypted_packets_[index]->AsStringPiece());
+ for (const auto& stream_frame : framer.stream_frames()) {
+ dest_stream->OnStreamFrame(*stream_frame);
+ }
+ for (const auto& crypto_frame : framer.crypto_frames()) {
+ dest_stream->OnCryptoFrame(*crypto_frame);
+ }
+ }
+ *inout_packet_index = index;
+
+ QuicConnectionPeer::SetCurrentPacket(dest_conn, QuicStringPiece(nullptr, 0));
+}
+
+CryptoHandshakeMessage GenerateDefaultInchoateCHLO(
+ const QuicClock* clock,
+ QuicTransportVersion version,
+ QuicCryptoServerConfig* crypto_config) {
+ // clang-format off
+ return CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", GenerateClientPublicValuesHex().c_str()},
+ {"NONC", GenerateClientNonceHex(clock, crypto_config).c_str()},
+ {"VER\0", QuicVersionLabelToString(
+ QuicVersionToQuicVersionLabel(version)).c_str()}},
+ kClientHelloMinimumSize);
+ // clang-format on
+}
+
+QuicString GenerateClientNonceHex(const QuicClock* clock,
+ QuicCryptoServerConfig* crypto_config) {
+ QuicCryptoServerConfig::ConfigOptions old_config_options;
+ QuicCryptoServerConfig::ConfigOptions new_config_options;
+ old_config_options.id = "old-config-id";
+ delete crypto_config->AddDefaultConfig(QuicRandom::GetInstance(), clock,
+ old_config_options);
+ std::unique_ptr<QuicServerConfigProtobuf> primary_config(
+ crypto_config->GenerateConfig(QuicRandom::GetInstance(), clock,
+ new_config_options));
+ primary_config->set_primary_time(clock->WallNow().ToUNIXSeconds());
+ std::unique_ptr<CryptoHandshakeMessage> msg(
+ crypto_config->AddConfig(std::move(primary_config), clock->WallNow()));
+ QuicStringPiece orbit;
+ CHECK(msg->GetStringPiece(kORBT, &orbit));
+ QuicString nonce;
+ CryptoUtils::GenerateNonce(clock->WallNow(), QuicRandom::GetInstance(), orbit,
+ &nonce);
+ return ("#" + QuicTextUtils::HexEncode(nonce));
+}
+
+QuicString GenerateClientPublicValuesHex() {
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+ return ("#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value)));
+}
+
+void GenerateFullCHLO(const CryptoHandshakeMessage& inchoate_chlo,
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ QuicTransportVersion version,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> proof,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ CryptoHandshakeMessage* out) {
+ // Pass a inchoate CHLO.
+ FullChloGenerator generator(crypto_config, server_addr, client_addr, clock,
+ proof, compressed_certs_cache, out);
+ crypto_config->ValidateClientHello(
+ inchoate_chlo, client_addr.host(), server_addr, version, clock, proof,
+ generator.GetValidateClientHelloCallback());
+}
+
+} // namespace crypto_test_utils
+} // namespace test
+} // namespace quic