Use TicketCrypter to enable TLS session resumption in QUIC. gfe-relnote: Adds support for session resumption in TLS-based versions of QUIC. Protected by quic_enable_tls_resumption. PiperOrigin-RevId: 308357681 Change-Id: I3889a8eec65d3903967d6ab1ca7c1b997da79606
diff --git a/quic/test_tools/fake_proof_source.cc b/quic/test_tools/fake_proof_source.cc index 032560e..3763bcc 100644 --- a/quic/test_tools/fake_proof_source.cc +++ b/quic/test_tools/fake_proof_source.cc
@@ -114,9 +114,17 @@ } ProofSource::TicketCrypter* FakeProofSource::SessionTicketCrypter() { + if (ticket_crypter_) { + return ticket_crypter_.get(); + } return delegate_->SessionTicketCrypter(); } +void FakeProofSource::SetTicketCrypter( + std::unique_ptr<TicketCrypter> ticket_crypter) { + ticket_crypter_ = std::move(ticket_crypter); +} + int FakeProofSource::NumPendingCallbacks() const { return pending_ops_.size(); }
diff --git a/quic/test_tools/fake_proof_source.h b/quic/test_tools/fake_proof_source.h index ef7669d..c179f59 100644 --- a/quic/test_tools/fake_proof_source.h +++ b/quic/test_tools/fake_proof_source.h
@@ -15,10 +15,13 @@ namespace quic { namespace test { -// Implementation of ProofSource which delegates to a ProofSourceForTesting, -// except that when the async GetProof is called, it captures the call and -// allows tests to see that a call is pending, which they can then cause to -// complete at a time of their choosing. +// Implementation of ProofSource which delegates to a ProofSourceForTesting, but +// allows for overriding certain functionality. FakeProofSource allows +// intercepting calls to GetProof and ComputeTlsSignature to force them to run +// asynchronously, and allow the caller to see that the call is pending and +// resume the operation at the caller's choosing. FakeProofSource also allows +// the caller to replace the TicketCrypter provided by +// FakeProofSource::SessionTicketCrypter. class FakeProofSource : public ProofSource { public: FakeProofSource(); @@ -46,9 +49,12 @@ uint16_t signature_algorithm, quiche::QuicheStringPiece in, std::unique_ptr<ProofSource::SignatureCallback> callback) override; - TicketCrypter* SessionTicketCrypter() override; + // Sets the TicketCrypter to use. If nullptr, the TicketCrypter from + // ProofSourceForTesting will be returned instead. + void SetTicketCrypter(std::unique_ptr<TicketCrypter> ticket_crypter); + // Get the number of callbacks which are pending int NumPendingCallbacks() const; @@ -58,6 +64,7 @@ private: std::unique_ptr<ProofSource> delegate_; + std::unique_ptr<TicketCrypter> ticket_crypter_; bool active_ = false; class PendingOp {
diff --git a/quic/test_tools/test_ticket_crypter.cc b/quic/test_tools/test_ticket_crypter.cc new file mode 100644 index 0000000..4d0d93e --- /dev/null +++ b/quic/test_tools/test_ticket_crypter.cc
@@ -0,0 +1,74 @@ +// Copyright (c) 2020 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/test_ticket_crypter.h" + +#include <cstring> + +#include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h" + +namespace quic { +namespace test { + +namespace { + +// A TicketCrypter implementation is supposed to encrypt and decrypt session +// tickets. However, the only requirement that is needed of a test +// implementation is that calling Decrypt(Encrypt(input), callback) results in +// callback being called with input. (The output of Encrypt must also not exceed +// the overhead specified by MaxOverhead.) This test implementation encrypts +// tickets by prepending kTicketPrefix to generate the ciphertext. The decrypt +// function checks that the prefix is present and strips it; otherwise it +// returns an empty vector to signal failure. +constexpr char kTicketPrefix[] = "TEST TICKET"; + +} // namespace + +size_t TestTicketCrypter::MaxOverhead() { + return QUICHE_ARRAYSIZE(kTicketPrefix); +} + +std::vector<uint8_t> TestTicketCrypter::Encrypt(quiche::QuicheStringPiece in) { + size_t prefix_len = QUICHE_ARRAYSIZE(kTicketPrefix); + std::vector<uint8_t> out(prefix_len + in.size()); + memcpy(out.data(), kTicketPrefix, prefix_len); + memcpy(out.data() + prefix_len, in.data(), in.size()); + return out; +} + +std::vector<uint8_t> TestTicketCrypter::Decrypt(quiche::QuicheStringPiece in) { + size_t prefix_len = QUICHE_ARRAYSIZE(kTicketPrefix); + if (fail_decrypt_ || in.size() < prefix_len || + memcmp(kTicketPrefix, in.data(), prefix_len) != 0) { + return std::vector<uint8_t>(); + } + return std::vector<uint8_t>(in.begin() + prefix_len, in.end()); +} + +void TestTicketCrypter::Decrypt( + quiche::QuicheStringPiece in, + std::unique_ptr<ProofSource::DecryptCallback> callback) { + auto decrypted_ticket = Decrypt(in); + if (run_async_) { + pending_callbacks_.push_back({std::move(callback), decrypted_ticket}); + } else { + callback->Run(decrypted_ticket); + } +} + +void TestTicketCrypter::SetRunCallbacksAsync(bool run_async) { + run_async_ = run_async; +} + +size_t TestTicketCrypter::NumPendingCallbacks() { + return pending_callbacks_.size(); +} + +void TestTicketCrypter::RunPendingCallback(size_t n) { + const PendingCallback& callback = pending_callbacks_[n]; + callback.callback->Run(callback.decrypted_ticket); +} + +} // namespace test +} // namespace quic
diff --git a/quic/test_tools/test_ticket_crypter.h b/quic/test_tools/test_ticket_crypter.h new file mode 100644 index 0000000..b596348 --- /dev/null +++ b/quic/test_tools/test_ticket_crypter.h
@@ -0,0 +1,49 @@ +// Copyright (c) 2020 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. + +#ifndef QUICHE_QUIC_TEST_TOOLS_TEST_TICKET_CRYPTER_H_ +#define QUICHE_QUIC_TEST_TOOLS_TEST_TICKET_CRYPTER_H_ + +#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h" + +namespace quic { +namespace test { + +// Provides a simple implementation of ProofSource::TicketCrypter for testing. +// THIS IMPLEMENTATION IS NOT SECURE. It is only intended for testing purposes. +class TestTicketCrypter : public ProofSource::TicketCrypter { + public: + ~TestTicketCrypter() override = default; + + // TicketCrypter interface + size_t MaxOverhead() override; + std::vector<uint8_t> Encrypt(quiche::QuicheStringPiece in) override; + void Decrypt(quiche::QuicheStringPiece in, + std::unique_ptr<ProofSource::DecryptCallback> callback) override; + + void SetRunCallbacksAsync(bool run_async); + size_t NumPendingCallbacks(); + void RunPendingCallback(size_t n); + + // Allows configuring this TestTicketCrypter to fail decryption. + void set_fail_decrypt(bool fail_decrypt) { fail_decrypt_ = fail_decrypt; } + + private: + // Performs the Decrypt operation synchronously. + std::vector<uint8_t> Decrypt(quiche::QuicheStringPiece in); + + struct PendingCallback { + std::unique_ptr<ProofSource::DecryptCallback> callback; + std::vector<uint8_t> decrypted_ticket; + }; + + bool fail_decrypt_ = false; + bool run_async_ = false; + std::vector<PendingCallback> pending_callbacks_; +}; + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_TEST_TOOLS_TEST_TICKET_CRYPTER_H_