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_