// Copyright (c) 2013 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 #include "base/files/file_path.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" #include "net/cert/cert_status_flags.h" #include "net/cert/cert_verify_result.h" #include "net/cert/x509_certificate.h" #include "net/test/cert_test_util.h" #include "net/test/test_data_directory.h" #include "net/third_party/quic/core/crypto/proof_source.h" #include "net/third_party/quic/core/crypto/proof_verifier.h" #include "net/third_party/quic/test_tools/crypto_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/boringssl/src/include/openssl/ssl.h" using std::string; namespace net { namespace test { namespace { // TestProofVerifierCallback is a simple callback for a ProofVerifier that // signals a TestCompletionCallback when called and stores the results from the // ProofVerifier in pointers passed to the constructor. class TestProofVerifierCallback : public ProofVerifierCallback { public: TestProofVerifierCallback(TestCompletionCallback* comp_callback, bool* ok, string* error_details) : comp_callback_(comp_callback), ok_(ok), error_details_(error_details) {} void Run(bool ok, const string& error_details, std::unique_ptr* details) override { *ok_ = ok; *error_details_ = error_details; comp_callback_->callback().Run(0); } private: TestCompletionCallback* const comp_callback_; bool* const ok_; string* const error_details_; }; // RunVerification runs |verifier->VerifyProof| and asserts that the result // matches |expected_ok|. void RunVerification(ProofVerifier* verifier, const string& hostname, const uint16_t port, const string& server_config, QuicTransportVersion quic_version, QuicStringPiece chlo_hash, const std::vector& certs, const string& proof, bool expected_ok) { std::unique_ptr details; TestCompletionCallback comp_callback; bool ok; string error_details; std::unique_ptr verify_context( crypto_test_utils::ProofVerifyContextForTesting()); std::unique_ptr callback( new TestProofVerifierCallback(&comp_callback, &ok, &error_details)); QuicAsyncStatus status = verifier->VerifyProof( hostname, port, server_config, quic_version, chlo_hash, certs, "", proof, verify_context.get(), &error_details, &details, std::move(callback)); switch (status) { case QUIC_FAILURE: ASSERT_FALSE(expected_ok); ASSERT_NE("", error_details); return; case QUIC_SUCCESS: ASSERT_TRUE(expected_ok); ASSERT_EQ("", error_details); return; case QUIC_PENDING: comp_callback.WaitForResult(); ASSERT_EQ(expected_ok, ok); break; } } class TestCallback : public ProofSource::Callback { public: explicit TestCallback(bool* called, bool* ok, QuicReferenceCountedPointer* chain, QuicCryptoProof* proof) : called_(called), ok_(ok), chain_(chain), proof_(proof) {} void Run(bool ok, const QuicReferenceCountedPointer& chain, const QuicCryptoProof& proof, std::unique_ptr /* details */) override { *ok_ = ok; *chain_ = chain; *proof_ = proof; *called_ = true; } private: bool* called_; bool* ok_; QuicReferenceCountedPointer* chain_; QuicCryptoProof* proof_; }; class ProofTest : public ::testing::TestWithParam {}; } // namespace INSTANTIATE_TEST_CASE_P(QuicTransportVersion, ProofTest, ::testing::ValuesIn(AllSupportedTransportVersions())); // TODO(rtenneti): Enable testing of ProofVerifier. See http://crbug.com/514468. TEST_P(ProofTest, DISABLED_Verify) { std::unique_ptr source( crypto_test_utils::ProofSourceForTesting()); std::unique_ptr verifier( crypto_test_utils::ProofVerifierForTesting()); const string server_config = "server config bytes"; const string hostname = "test.example.com"; const uint16_t port = 8443; const string first_chlo_hash = "first chlo hash bytes"; const string second_chlo_hash = "first chlo hash bytes"; const QuicTransportVersion quic_version = GetParam(); bool called = false; bool first_called = false; bool ok, first_ok; QuicReferenceCountedPointer chain; QuicReferenceCountedPointer first_chain; string error_details; QuicCryptoProof proof, first_proof; QuicSocketAddress server_addr; std::unique_ptr cb( new TestCallback(&called, &ok, &chain, &proof)); std::unique_ptr first_cb( new TestCallback(&first_called, &first_ok, &first_chain, &first_proof)); // GetProof here expects the async method to invoke the callback // synchronously. source->GetProof(server_addr, hostname, server_config, quic_version, first_chlo_hash, std::move(first_cb)); source->GetProof(server_addr, hostname, server_config, quic_version, second_chlo_hash, std::move(cb)); ASSERT_TRUE(called); ASSERT_TRUE(first_called); ASSERT_TRUE(ok); ASSERT_TRUE(first_ok); // Check that the proof source is caching correctly: ASSERT_EQ(first_chain->certs, chain->certs); ASSERT_NE(proof.signature, first_proof.signature); ASSERT_EQ(first_proof.leaf_cert_scts, proof.leaf_cert_scts); RunVerification(verifier.get(), hostname, port, server_config, quic_version, first_chlo_hash, chain->certs, proof.signature, true); RunVerification(verifier.get(), "foo.com", port, server_config, quic_version, first_chlo_hash, chain->certs, proof.signature, false); RunVerification(verifier.get(), server_config.substr(1, string::npos), port, server_config, quic_version, first_chlo_hash, chain->certs, proof.signature, false); const string corrupt_signature = "1" + proof.signature; RunVerification(verifier.get(), hostname, port, server_config, quic_version, first_chlo_hash, chain->certs, corrupt_signature, false); std::vector wrong_certs; for (size_t i = 1; i < chain->certs.size(); i++) { wrong_certs.push_back(chain->certs[i]); } RunVerification(verifier.get(), "foo.com", port, server_config, quic_version, first_chlo_hash, wrong_certs, corrupt_signature, false); } namespace { class TestingSignatureCallback : public ProofSource::SignatureCallback { public: TestingSignatureCallback(bool* ok_out, std::string* signature_out) : ok_out_(ok_out), signature_out_(signature_out) {} void Run(bool ok, std::string signature) override { *ok_out_ = ok; *signature_out_ = std::move(signature); } private: bool* ok_out_; std::string* signature_out_; }; } // namespace TEST_P(ProofTest, TlsSignature) { std::unique_ptr source( crypto_test_utils::ProofSourceForTesting()); QuicSocketAddress server_address; const string hostname = "test.example.com"; QuicReferenceCountedPointer chain = source->GetCertChain(server_address, hostname); ASSERT_GT(chain->certs.size(), 0ul); // Generate a value to be signed similar to the example in TLS 1.3 section // 4.4.3. The value to be signed starts with octed 0x20 repeated 64 times, // followed by the context string, followed by a single 0 byte, followed by // the transcript hash. Since there's no TLS stack here, we're using 32 bytes // of 01 as the transcript hash. string to_be_signed(64, ' '); to_be_signed.append("TLS 1.3, server CertificateVerify"); to_be_signed.append(1, '\0'); to_be_signed.append(32, 1); string sig; bool success; std::unique_ptr callback = QuicMakeUnique(&success, &sig); source->ComputeTlsSignature(server_address, hostname, SSL_SIGN_RSA_PSS_SHA256, to_be_signed, std::move(callback)); EXPECT_TRUE(success); // Verify that the signature from ComputeTlsSignature can be verified with the // leaf cert from GetCertChain. const uint8_t* data; const uint8_t* orig_data; orig_data = data = reinterpret_cast(chain->certs[0].data()); bssl::UniquePtr leaf(d2i_X509(nullptr, &data, chain->certs[0].size())); ASSERT_NE(leaf.get(), nullptr); EXPECT_EQ(data - orig_data, static_cast(chain->certs[0].size())); bssl::UniquePtr pkey(X509_get_pubkey(leaf.get())); bssl::ScopedEVP_MD_CTX md_ctx; EVP_PKEY_CTX* ctx; ASSERT_EQ(EVP_DigestVerifyInit(md_ctx.get(), &ctx, EVP_sha256(), nullptr, pkey.get()), 1); ASSERT_EQ(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING), 1); ASSERT_EQ(EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1), 1); ASSERT_EQ(EVP_DigestVerifyUpdate(md_ctx.get(), to_be_signed.data(), to_be_signed.size()), 1); EXPECT_EQ(EVP_DigestVerifyFinal(md_ctx.get(), reinterpret_cast(sig.data()), sig.size()), 1); } TEST_P(ProofTest, UseAfterFree) { std::unique_ptr source( crypto_test_utils::ProofSourceForTesting()); const string server_config = "server config bytes"; const string hostname = "test.example.com"; const string chlo_hash = "proof nonce bytes"; bool called = false; bool ok; QuicReferenceCountedPointer chain; string error_details; QuicCryptoProof proof; QuicSocketAddress server_addr; std::unique_ptr cb( new TestCallback(&called, &ok, &chain, &proof)); // GetProof here expects the async method to invoke the callback // synchronously. source->GetProof(server_addr, hostname, server_config, GetParam(), chlo_hash, std::move(cb)); ASSERT_TRUE(called); ASSERT_TRUE(ok); // Make sure we can safely access results after deleting where they came from. EXPECT_FALSE(chain->HasOneRef()); source = nullptr; EXPECT_TRUE(chain->HasOneRef()); EXPECT_FALSE(chain->certs.empty()); for (const string& cert : chain->certs) { EXPECT_FALSE(cert.empty()); } } } // namespace test } // namespace net