xref: /aosp_15_r20/external/cronet/net/quic/crypto/proof_test_chromium.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <memory>
6 
7 #include "base/files/file_path.h"
8 #include "base/memory/raw_ptr.h"
9 #include "net/base/ip_endpoint.h"
10 #include "net/base/net_errors.h"
11 #include "net/base/test_completion_callback.h"
12 #include "net/cert/cert_status_flags.h"
13 #include "net/cert/cert_verify_result.h"
14 #include "net/cert/x509_certificate.h"
15 #include "net/quic/quic_context.h"
16 #include "net/test/cert_test_util.h"
17 #include "net/test/test_data_directory.h"
18 #include "net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.h"
19 #include "net/third_party/quiche/src/quiche/quic/core/crypto/proof_verifier.h"
20 #include "net/third_party/quiche/src/quiche/quic/test_tools/crypto_test_utils.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "third_party/boringssl/src/include/openssl/ssl.h"
23 
24 using std::string;
25 
26 namespace net::test {
27 namespace {
28 
29 // TestProofVerifierCallback is a simple callback for a quic::ProofVerifier that
30 // signals a TestCompletionCallback when called and stores the results from the
31 // quic::ProofVerifier in pointers passed to the constructor.
32 class TestProofVerifierCallback : public quic::ProofVerifierCallback {
33  public:
TestProofVerifierCallback(TestCompletionCallback * comp_callback,bool * ok,string * error_details)34   TestProofVerifierCallback(TestCompletionCallback* comp_callback,
35                             bool* ok,
36                             string* error_details)
37       : comp_callback_(comp_callback), ok_(ok), error_details_(error_details) {}
38 
Run(bool ok,const string & error_details,std::unique_ptr<quic::ProofVerifyDetails> * details)39   void Run(bool ok,
40            const string& error_details,
41            std::unique_ptr<quic::ProofVerifyDetails>* details) override {
42     *ok_ = ok;
43     *error_details_ = error_details;
44 
45     comp_callback_->callback().Run(0);
46   }
47 
48  private:
49   const raw_ptr<TestCompletionCallback> comp_callback_;
50   const raw_ptr<bool> ok_;
51   const raw_ptr<string> error_details_;
52 };
53 
54 // RunVerification runs |verifier->VerifyProof| and asserts that the result
55 // matches |expected_ok|.
RunVerification(quic::ProofVerifier * verifier,const string & hostname,const uint16_t port,const string & server_config,quic::QuicTransportVersion quic_version,std::string_view chlo_hash,const std::vector<string> & certs,const string & proof,bool expected_ok)56 void RunVerification(quic::ProofVerifier* verifier,
57                      const string& hostname,
58                      const uint16_t port,
59                      const string& server_config,
60                      quic::QuicTransportVersion quic_version,
61                      std::string_view chlo_hash,
62                      const std::vector<string>& certs,
63                      const string& proof,
64                      bool expected_ok) {
65   std::unique_ptr<quic::ProofVerifyDetails> details;
66   TestCompletionCallback comp_callback;
67   bool ok;
68   string error_details;
69   std::unique_ptr<quic::ProofVerifyContext> verify_context(
70       quic::test::crypto_test_utils::ProofVerifyContextForTesting());
71   auto callback = std::make_unique<TestProofVerifierCallback>(
72       &comp_callback, &ok, &error_details);
73 
74   quic::QuicAsyncStatus status = verifier->VerifyProof(
75       hostname, port, server_config, quic_version, chlo_hash, certs, "", proof,
76       verify_context.get(), &error_details, &details, std::move(callback));
77 
78   switch (status) {
79     case quic::QUIC_FAILURE:
80       ASSERT_FALSE(expected_ok);
81       ASSERT_NE("", error_details);
82       return;
83     case quic::QUIC_SUCCESS:
84       ASSERT_TRUE(expected_ok);
85       ASSERT_EQ("", error_details);
86       return;
87     case quic::QUIC_PENDING:
88       comp_callback.WaitForResult();
89       ASSERT_EQ(expected_ok, ok);
90       break;
91   }
92 }
93 
94 class TestCallback : public quic::ProofSource::Callback {
95  public:
TestCallback(bool * called,bool * ok,quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain> * chain,quic::QuicCryptoProof * proof)96   explicit TestCallback(
97       bool* called,
98       bool* ok,
99       quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain>* chain,
100       quic::QuicCryptoProof* proof)
101       : called_(called), ok_(ok), chain_(chain), proof_(proof) {}
102 
Run(bool ok,const quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain> & chain,const quic::QuicCryptoProof & proof,std::unique_ptr<quic::ProofSource::Details>)103   void Run(
104       bool ok,
105       const quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain>&
106           chain,
107       const quic::QuicCryptoProof& proof,
108       std::unique_ptr<quic::ProofSource::Details> /* details */) override {
109     *ok_ = ok;
110     *chain_ = chain;
111     *proof_ = proof;
112     *called_ = true;
113   }
114 
115  private:
116   raw_ptr<bool> called_;
117   raw_ptr<bool> ok_;
118   raw_ptr<quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain>>
119       chain_;
120   raw_ptr<quic::QuicCryptoProof> proof_;
121 };
122 
123 class ProofTest : public ::testing::TestWithParam<quic::ParsedQuicVersion> {};
124 
125 }  // namespace
126 
127 INSTANTIATE_TEST_SUITE_P(QuicTransportVersion,
128                          ProofTest,
129                          ::testing::ValuesIn(AllSupportedQuicVersions()),
130                          ::testing::PrintToStringParamName());
131 
TEST_P(ProofTest,Verify)132 TEST_P(ProofTest, Verify) {
133   std::unique_ptr<quic::ProofSource> source(
134       quic::test::crypto_test_utils::ProofSourceForTesting());
135   std::unique_ptr<quic::ProofVerifier> verifier(
136       quic::test::crypto_test_utils::ProofVerifierForTesting());
137 
138   const string server_config = "server config bytes";
139   const string hostname = "test.example.com";
140   const uint16_t port = 8443;
141   const string first_chlo_hash = "first chlo hash bytes";
142   const string second_chlo_hash = "first chlo hash bytes";
143   const quic::QuicTransportVersion quic_version = GetParam().transport_version;
144 
145   bool called = false;
146   bool first_called = false;
147   bool ok, first_ok;
148   quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain> chain;
149   quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain> first_chain;
150   string error_details;
151   quic::QuicCryptoProof proof, first_proof;
152   quic::QuicSocketAddress server_addr;
153   quic::QuicSocketAddress client_addr;
154 
155   auto cb = std::make_unique<TestCallback>(&called, &ok, &chain, &proof);
156   auto first_cb = std::make_unique<TestCallback>(&first_called, &first_ok,
157                                                  &first_chain, &first_proof);
158 
159   // GetProof here expects the async method to invoke the callback
160   // synchronously.
161   source->GetProof(server_addr, client_addr, hostname, server_config,
162                    quic_version, first_chlo_hash, std::move(first_cb));
163   source->GetProof(server_addr, client_addr, hostname, server_config,
164                    quic_version, second_chlo_hash, std::move(cb));
165   ASSERT_TRUE(called);
166   ASSERT_TRUE(first_called);
167   ASSERT_TRUE(ok);
168   ASSERT_TRUE(first_ok);
169 
170   // Check that the proof source is caching correctly:
171   ASSERT_EQ(first_chain->certs, chain->certs);
172   ASSERT_NE(proof.signature, first_proof.signature);
173   ASSERT_EQ(first_proof.leaf_cert_scts, proof.leaf_cert_scts);
174 
175   RunVerification(verifier.get(), hostname, port, server_config, quic_version,
176                   first_chlo_hash, chain->certs, proof.signature, true);
177 
178   RunVerification(verifier.get(), "foo.com", port, server_config, quic_version,
179                   first_chlo_hash, chain->certs, proof.signature, false);
180 
181   RunVerification(verifier.get(), server_config.substr(1, string::npos), port,
182                   server_config, quic_version, first_chlo_hash, chain->certs,
183                   proof.signature, false);
184 
185   const string corrupt_signature = "1" + proof.signature;
186   RunVerification(verifier.get(), hostname, port, server_config, quic_version,
187                   first_chlo_hash, chain->certs, corrupt_signature, false);
188 
189   std::vector<string> wrong_certs;
190   for (size_t i = 1; i < chain->certs.size(); i++) {
191     wrong_certs.push_back(chain->certs[i]);
192   }
193 
194   RunVerification(verifier.get(), "foo.com", port, server_config, quic_version,
195                   first_chlo_hash, wrong_certs, corrupt_signature, false);
196 }
197 
198 namespace {
199 
200 class TestingSignatureCallback : public quic::ProofSource::SignatureCallback {
201  public:
TestingSignatureCallback(bool * ok_out,std::string * signature_out)202   TestingSignatureCallback(bool* ok_out, std::string* signature_out)
203       : ok_out_(ok_out), signature_out_(signature_out) {}
204 
Run(bool ok,std::string signature,std::unique_ptr<quic::ProofSource::Details>)205   void Run(bool ok,
206            std::string signature,
207            std::unique_ptr<quic::ProofSource::Details> /*details*/) override {
208     *ok_out_ = ok;
209     *signature_out_ = std::move(signature);
210   }
211 
212  private:
213   raw_ptr<bool> ok_out_;
214   raw_ptr<std::string> signature_out_;
215 };
216 
217 }  // namespace
218 
TEST_P(ProofTest,TlsSignature)219 TEST_P(ProofTest, TlsSignature) {
220   std::unique_ptr<quic::ProofSource> source(
221       quic::test::crypto_test_utils::ProofSourceForTesting());
222 
223   quic::QuicSocketAddress server_address;
224   const string hostname = "test.example.com";
225 
226   quic::QuicSocketAddress client_address;
227 
228   bool cert_matched_sni;
229   quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain> chain =
230       source->GetCertChain(server_address, client_address, hostname,
231                            &cert_matched_sni);
232   ASSERT_GT(chain->certs.size(), 0ul);
233 
234   // Generate a value to be signed similar to the example in TLS 1.3 section
235   // 4.4.3. The value to be signed starts with octed 0x20 repeated 64 times,
236   // followed by the context string, followed by a single 0 byte, followed by
237   // the transcript hash. Since there's no TLS stack here, we're using 32 bytes
238   // of 01 as the transcript hash.
239   string to_be_signed(64, ' ');
240   to_be_signed.append("TLS 1.3, server CertificateVerify");
241   to_be_signed.append(1, '\0');
242   to_be_signed.append(32, 1);
243 
244   string sig;
245   bool success;
246   std::unique_ptr<TestingSignatureCallback> callback =
247       std::make_unique<TestingSignatureCallback>(&success, &sig);
248   source->ComputeTlsSignature(server_address, client_address, hostname,
249                               SSL_SIGN_RSA_PSS_SHA256, to_be_signed,
250                               std::move(callback));
251   EXPECT_TRUE(success);
252 
253   // Verify that the signature from ComputeTlsSignature can be verified with the
254   // leaf cert from GetCertChain.
255   const uint8_t* data;
256   const uint8_t* orig_data;
257   orig_data = data = reinterpret_cast<const uint8_t*>(chain->certs[0].data());
258   bssl::UniquePtr<X509> leaf(d2i_X509(nullptr, &data, chain->certs[0].size()));
259   ASSERT_NE(leaf.get(), nullptr);
260   EXPECT_EQ(data - orig_data, static_cast<ptrdiff_t>(chain->certs[0].size()));
261   bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(leaf.get()));
262   bssl::ScopedEVP_MD_CTX md_ctx;
263   EVP_PKEY_CTX* ctx;
264   ASSERT_EQ(EVP_DigestVerifyInit(md_ctx.get(), &ctx, EVP_sha256(), nullptr,
265                                  pkey.get()),
266             1);
267   ASSERT_EQ(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING), 1);
268   ASSERT_EQ(EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1), 1);
269   ASSERT_EQ(EVP_DigestVerifyUpdate(md_ctx.get(), to_be_signed.data(),
270                                    to_be_signed.size()),
271             1);
272   EXPECT_EQ(EVP_DigestVerifyFinal(md_ctx.get(),
273                                   reinterpret_cast<const uint8_t*>(sig.data()),
274                                   sig.size()),
275             1);
276 }
277 
TEST_P(ProofTest,UseAfterFree)278 TEST_P(ProofTest, UseAfterFree) {
279   std::unique_ptr<quic::ProofSource> source(
280       quic::test::crypto_test_utils::ProofSourceForTesting());
281 
282   const string server_config = "server config bytes";
283   const string hostname = "test.example.com";
284   const string chlo_hash = "proof nonce bytes";
285   bool called = false;
286   bool ok;
287   quiche::QuicheReferenceCountedPointer<quic::ProofSource::Chain> chain;
288   string error_details;
289   quic::QuicCryptoProof proof;
290   quic::QuicSocketAddress server_addr;
291   quic::QuicSocketAddress client_addr;
292   auto cb = std::make_unique<TestCallback>(&called, &ok, &chain, &proof);
293 
294   // GetProof here expects the async method to invoke the callback
295   // synchronously.
296   source->GetProof(server_addr, client_addr, hostname, server_config,
297                    GetParam().transport_version, chlo_hash, std::move(cb));
298   ASSERT_TRUE(called);
299   ASSERT_TRUE(ok);
300 
301   // Make sure we can safely access results after deleting where they came from.
302   EXPECT_FALSE(chain->HasOneRef());
303   source = nullptr;
304   EXPECT_TRUE(chain->HasOneRef());
305 
306   EXPECT_FALSE(chain->certs.empty());
307   for (const string& cert : chain->certs) {
308     EXPECT_FALSE(cert.empty());
309   }
310 }
311 
312 }  // namespace net::test
313