blob: 0a58cc8b2a6fde826ef3ee4b508f0ebef3c12423 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/web_package/signed_exchange_cert_fetcher.h"
#include <optional>
#include <string_view>
#include "base/base64.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/test/task_environment.h"
#include "components/cbor/values.h"
#include "components/cbor/writer.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "net/base/isolation_info.h"
#include "net/base/load_flags.h"
#include "net/cert/x509_util.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
#include "url/origin.h"
namespace content {
namespace {
class DeferringURLLoaderThrottle final : public blink::URLLoaderThrottle {
public:
DeferringURLLoaderThrottle() = default;
DeferringURLLoaderThrottle(const DeferringURLLoaderThrottle&) = delete;
DeferringURLLoaderThrottle& operator=(const DeferringURLLoaderThrottle&) =
delete;
~DeferringURLLoaderThrottle() override = default;
void WillStartRequest(network::ResourceRequest* request,
bool* defer) override {
will_start_request_called_ = true;
*defer = true;
}
void WillRedirectRequest(
net::RedirectInfo* redirect_info,
const network::mojom::URLResponseHead& /* response_head */,
bool* defer,
std::vector<std::string>* /* to_be_removed_headers */,
net::HttpRequestHeaders* /* modified_headers */,
net::HttpRequestHeaders* /* modified_cors_exempt_headers */) override {
will_redirect_request_called_ = true;
*defer = true;
}
void WillProcessResponse(const GURL& response_url_,
network::mojom::URLResponseHead* response_head,
bool* defer) override {
will_process_response_called_ = true;
*defer = true;
}
bool will_start_request_called() const { return will_start_request_called_; }
bool will_redirect_request_called() const {
return will_redirect_request_called_;
}
bool will_process_response_called() const {
return will_process_response_called_;
}
Delegate* delegate() { return delegate_; }
private:
bool will_start_request_called_ = false;
bool will_redirect_request_called_ = false;
bool will_process_response_called_ = false;
};
class MockURLLoader final : public network::mojom::URLLoader {
public:
MockURLLoader(
mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver)
: receiver_(this, std::move(url_loader_receiver)) {}
MockURLLoader(const MockURLLoader&) = delete;
MockURLLoader& operator=(const MockURLLoader&) = delete;
~MockURLLoader() override = default;
MOCK_METHOD4(FollowRedirect,
void(const std::vector<std::string>&,
const net::HttpRequestHeaders&,
const net::HttpRequestHeaders&,
const std::optional<GURL>&));
MOCK_METHOD2(SetPriority,
void(net::RequestPriority priority,
int32_t intra_priority_value));
private:
mojo::Receiver<network::mojom::URLLoader> receiver_;
};
class URLLoaderFactoryForMockLoader final
: public network::mojom::URLLoaderFactory {
public:
URLLoaderFactoryForMockLoader() = default;
URLLoaderFactoryForMockLoader(const URLLoaderFactoryForMockLoader&) = delete;
URLLoaderFactoryForMockLoader& operator=(
const URLLoaderFactoryForMockLoader&) = delete;
~URLLoaderFactoryForMockLoader() override = default;
// network::mojom::URLLoaderFactory implementation.
void CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& url_request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
override {
loader_ = std::make_unique<MockURLLoader>(std::move(url_loader_receiver));
url_request_ = url_request;
client_remote_.Bind(std::move(client));
}
void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory)
override {
NOTREACHED();
}
mojo::Remote<network::mojom::URLLoaderClient>& client_remote() {
return client_remote_;
}
void CloseClientPipe() { client_remote_.reset(); }
std::optional<network::ResourceRequest> url_request() const {
return url_request_;
}
private:
std::unique_ptr<MockURLLoader> loader_;
mojo::Remote<network::mojom::URLLoaderClient> client_remote_;
std::optional<network::ResourceRequest> url_request_;
};
void ForwardCertificateCallback(
bool* called,
SignedExchangeLoadResult* out_result,
std::unique_ptr<SignedExchangeCertificateChain>* out_cert,
SignedExchangeLoadResult result,
std::unique_ptr<SignedExchangeCertificateChain> cert_chain,
net::IPAddress cert_server_ip_address) {
*out_result = result;
*called = true;
*out_cert = std::move(cert_chain);
}
class SignedExchangeCertFetcherTest : public testing::Test {
public:
SignedExchangeCertFetcherTest()
: url_(GURL("https://www.example.com/cert")),
origin_(url::Origin::Create(GURL("https://www.example.com/"))) {}
SignedExchangeCertFetcherTest(const SignedExchangeCertFetcherTest&) = delete;
SignedExchangeCertFetcherTest& operator=(
const SignedExchangeCertFetcherTest&) = delete;
~SignedExchangeCertFetcherTest() override {}
protected:
static scoped_refptr<net::X509Certificate> ImportTestCert() {
return net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
}
static std::string CreateCertMessage(std::string_view cert_data) {
cbor::Value::MapValue cbor_map;
cbor_map[cbor::Value("sct")] =
cbor::Value("SCT", cbor::Value::Type::BYTE_STRING);
cbor_map[cbor::Value("cert")] =
cbor::Value(cert_data, cbor::Value::Type::BYTE_STRING);
cbor_map[cbor::Value("ocsp")] =
cbor::Value("OCSP", cbor::Value::Type::BYTE_STRING);
cbor::Value::ArrayValue cbor_array;
cbor_array.push_back(cbor::Value("\U0001F4DC\u26D3"));
cbor_array.push_back(cbor::Value(std::move(cbor_map)));
std::optional<std::vector<uint8_t>> serialized =
cbor::Writer::Write(cbor::Value(std::move(cbor_array)));
if (!serialized)
return std::string();
return std::string(reinterpret_cast<char*>(serialized->data()),
serialized->size());
}
static std::string_view CreateCertMessageFromCert(
const net::X509Certificate& cert) {
return net::x509_util::CryptoBufferAsStringPiece(cert.cert_buffer());
}
static std::string CreateTestData() {
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
return CreateCertMessage(CreateCertMessageFromCert(*certificate));
}
static mojo::ScopedDataPipeConsumerHandle CreateTestDataFilledDataPipe() {
auto message = CreateTestData();
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
EXPECT_EQ(
mojo::CreateDataPipe(message.size(), producer_handle, consumer_handle),
MOJO_RESULT_OK);
CHECK(mojo::BlockingCopyFromString(message, producer_handle));
return consumer_handle;
}
static net::SHA256HashValue GetTestDataCertFingerprint256() {
return ImportTestCert()->CalculateChainFingerprint256();
}
void RunUntilIdle() { task_environment_.RunUntilIdle(); }
std::unique_ptr<SignedExchangeCertFetcher> CreateFetcherAndStart(
const GURL& url,
bool force_fetch) {
SignedExchangeCertFetcher::CertificateCallback callback = base::BindOnce(
&ForwardCertificateCallback, base::Unretained(&callback_called_),
base::Unretained(&result_), base::Unretained(&cert_result_));
auto isolation_info = net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther, origin_, origin_,
net::SiteForCookies::FromOrigin(origin_));
return SignedExchangeCertFetcher::CreateAndStart(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&mock_loader_factory_),
std::move(throttles_), url, force_fetch, std::move(callback),
/*devtools_proxy=*/nullptr, /*throttling_profile_id=*/std::nullopt,
isolation_info, origin_);
}
void CallOnReceiveResponse(
mojo::ScopedDataPipeConsumerHandle consumer_handle) {
auto response_head = network::mojom::URLResponseHead::New();
response_head->headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
response_head->headers->SetHeader("Content-Type",
"application/cert-chain+cbor");
response_head->mime_type = "application/cert-chain+cbor";
mock_loader_factory_.client_remote()->OnReceiveResponse(
std::move(response_head), std::move(consumer_handle), std::nullopt);
}
DeferringURLLoaderThrottle* InitializeDeferringURLLoaderThrottle() {
std::unique_ptr<DeferringURLLoaderThrottle> throttle =
std::make_unique<DeferringURLLoaderThrottle>();
DeferringURLLoaderThrottle* ptr = throttle.get();
throttles_.push_back(std::move(throttle));
return ptr;
}
void CloseClientPipe() { mock_loader_factory_.CloseClientPipe(); }
const GURL url_;
const url::Origin origin_;
bool callback_called_ = false;
SignedExchangeLoadResult result_;
std::unique_ptr<SignedExchangeCertificateChain> cert_result_;
URLLoaderFactoryForMockLoader mock_loader_factory_;
std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles_;
base::test::TaskEnvironment task_environment_;
};
} // namespace
TEST_F(SignedExchangeCertFetcherTest, Simple) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, /*force_fetch=*/false);
ASSERT_TRUE(mock_loader_factory_.client_remote());
ASSERT_TRUE(mock_loader_factory_.url_request());
EXPECT_EQ(url_, mock_loader_factory_.url_request()->url);
EXPECT_EQ(network::mojom::RequestDestination::kEmpty,
mock_loader_factory_.url_request()->destination);
EXPECT_EQ(mock_loader_factory_.url_request()->credentials_mode,
network::mojom::CredentialsMode::kOmit);
EXPECT_EQ(*mock_loader_factory_.url_request()->request_initiator, origin_);
EXPECT_THAT(mock_loader_factory_.url_request()->headers.GetHeader("Accept"),
testing::Optional(std::string("application/cert-chain+cbor")));
CallOnReceiveResponse(CreateTestDataFilledDataPipe());
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kSuccess, result_);
ASSERT_TRUE(cert_result_);
EXPECT_EQ(GetTestDataCertFingerprint256(),
cert_result_->cert()->CalculateChainFingerprint256());
}
TEST_F(SignedExchangeCertFetcherTest, MultipleChunked) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(mojo::CreateDataPipe(message.size() / 2 + 1, producer_handle,
consumer_handle),
MOJO_RESULT_OK);
ASSERT_TRUE(mojo::BlockingCopyFromString(
message.substr(0, message.size() / 2), producer_handle));
CallOnReceiveResponse(std::move(consumer_handle));
RunUntilIdle();
ASSERT_TRUE(mojo::BlockingCopyFromString(message.substr(message.size() / 2),
producer_handle));
producer_handle.reset();
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kSuccess, result_);
ASSERT_TRUE(cert_result_);
EXPECT_EQ(certificate->CalculateChainFingerprint256(),
cert_result_->cert()->CalculateChainFingerprint256());
}
TEST_F(SignedExchangeCertFetcherTest, ForceFetchAndFail) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, true /* force_fetch */);
ASSERT_TRUE(mock_loader_factory_.url_request());
EXPECT_EQ(url_, mock_loader_factory_.url_request()->url);
EXPECT_EQ(network::mojom::RequestDestination::kEmpty,
mock_loader_factory_.url_request()->destination);
EXPECT_EQ(net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_CACHE,
mock_loader_factory_.url_request()->load_flags);
EXPECT_EQ(mock_loader_factory_.url_request()->credentials_mode,
network::mojom::CredentialsMode::kOmit);
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::ERR_INVALID_SIGNED_EXCHANGE));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, MaxCertSize_Exceeds) {
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
base::ScopedClosureRunner reset_max =
SignedExchangeCertFetcher::SetMaxCertSizeForTest(message.size() - 1);
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(
mojo::CreateDataPipe(message.size(), producer_handle, consumer_handle),
MOJO_RESULT_OK);
CHECK(mojo::BlockingCopyFromString(message, producer_handle));
producer_handle.reset();
CallOnReceiveResponse(std::move(consumer_handle));
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, MaxCertSize_SameSize) {
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
base::ScopedClosureRunner reset_max =
SignedExchangeCertFetcher::SetMaxCertSizeForTest(message.size());
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(
mojo::CreateDataPipe(message.size(), producer_handle, consumer_handle),
MOJO_RESULT_OK);
CHECK(mojo::BlockingCopyFromString(message, producer_handle));
producer_handle.reset();
CallOnReceiveResponse(std::move(consumer_handle));
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kSuccess, result_);
EXPECT_TRUE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, MaxCertSize_MultipleChunked) {
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
base::ScopedClosureRunner reset_max =
SignedExchangeCertFetcher::SetMaxCertSizeForTest(message.size() - 1);
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(mojo::CreateDataPipe(message.size() / 2 + 1, producer_handle,
consumer_handle),
MOJO_RESULT_OK);
ASSERT_TRUE(mojo::BlockingCopyFromString(
message.substr(0, message.size() / 2), producer_handle));
CallOnReceiveResponse(std::move(consumer_handle));
RunUntilIdle();
ASSERT_TRUE(mojo::BlockingCopyFromString(message.substr(message.size() / 2),
producer_handle));
producer_handle.reset();
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, MaxCertSize_ContentLengthCheck) {
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
base::ScopedClosureRunner reset_max =
SignedExchangeCertFetcher::SetMaxCertSizeForTest(message.size() - 1);
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
auto response_head = network::mojom::URLResponseHead::New();
response_head->headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
response_head->content_length = message.size();
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(
mojo::CreateDataPipe(message.size(), producer_handle, consumer_handle),
MOJO_RESULT_OK);
CHECK(mojo::BlockingCopyFromString(message, producer_handle));
producer_handle.reset();
mock_loader_factory_.client_remote()->OnReceiveResponse(
std::move(response_head), std::move(consumer_handle), std::nullopt);
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, Abort_Redirect) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
net::RedirectInfo redirect_info;
mock_loader_factory_.client_remote()->OnReceiveRedirect(
redirect_info, network::mojom::URLResponseHead::New());
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, Abort_404) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
auto response_head = network::mojom::URLResponseHead::New();
response_head->headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 404 Not Found");
mock_loader_factory_.client_remote()->OnReceiveResponse(
std::move(response_head), mojo::ScopedDataPipeConsumerHandle(),
std::nullopt);
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, WrongMimeType) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
auto response_head = network::mojom::URLResponseHead::New();
response_head->headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
response_head->headers->SetHeader("Content-Type", "application/octet-stream");
response_head->mime_type = "application/octet-stream";
mock_loader_factory_.client_remote()->OnReceiveResponse(
std::move(response_head), mojo::ScopedDataPipeConsumerHandle(),
std::nullopt);
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, Invalid_CertData) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
const std::string message = CreateCertMessage("Invalid Cert Data");
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(
mojo::CreateDataPipe(message.size(), producer_handle, consumer_handle),
MOJO_RESULT_OK);
CHECK(mojo::BlockingCopyFromString(message, producer_handle));
producer_handle.reset();
CallOnReceiveResponse(std::move(consumer_handle));
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertParseError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, Invalid_CertMessage) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
const std::string message = "Invalid cert message";
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(
mojo::CreateDataPipe(message.size(), producer_handle, consumer_handle),
MOJO_RESULT_OK);
CHECK(mojo::BlockingCopyFromString(message, producer_handle));
producer_handle.reset();
CallOnReceiveResponse(std::move(consumer_handle));
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertParseError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, Throttle_Simple) {
DeferringURLLoaderThrottle* throttle = InitializeDeferringURLLoaderThrottle();
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
RunUntilIdle();
EXPECT_TRUE(throttle->will_start_request_called());
EXPECT_FALSE(mock_loader_factory_.url_request());
EXPECT_FALSE(mock_loader_factory_.client_remote());
throttle->delegate()->Resume();
RunUntilIdle();
CallOnReceiveResponse(CreateTestDataFilledDataPipe());
RunUntilIdle();
EXPECT_TRUE(throttle->will_process_response_called());
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_FALSE(callback_called_);
throttle->delegate()->Resume();
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kSuccess, result_);
ASSERT_TRUE(cert_result_);
EXPECT_EQ(GetTestDataCertFingerprint256(),
cert_result_->cert()->CalculateChainFingerprint256());
}
TEST_F(SignedExchangeCertFetcherTest, Throttle_AbortsOnRequest) {
DeferringURLLoaderThrottle* throttle = InitializeDeferringURLLoaderThrottle();
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
RunUntilIdle();
throttle->delegate()->CancelWithError(net::ERR_INVALID_SIGNED_EXCHANGE);
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, Throttle_AbortsOnRedirect) {
DeferringURLLoaderThrottle* throttle = InitializeDeferringURLLoaderThrottle();
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
RunUntilIdle();
throttle->delegate()->Resume();
RunUntilIdle();
net::RedirectInfo redirect_info;
mock_loader_factory_.client_remote()->OnReceiveRedirect(
redirect_info, network::mojom::URLResponseHead::New());
RunUntilIdle();
EXPECT_TRUE(throttle->will_redirect_request_called());
throttle->delegate()->CancelWithError(net::ERR_INVALID_SIGNED_EXCHANGE);
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, Throttle_AbortsOnResponse) {
DeferringURLLoaderThrottle* throttle = InitializeDeferringURLLoaderThrottle();
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
RunUntilIdle();
throttle->delegate()->Resume();
RunUntilIdle();
CallOnReceiveResponse(CreateTestDataFilledDataPipe());
RunUntilIdle();
EXPECT_TRUE(throttle->will_process_response_called());
mock_loader_factory_.client_remote()->OnComplete(
network::URLLoaderCompletionStatus(net::OK));
RunUntilIdle();
EXPECT_FALSE(callback_called_);
throttle->delegate()->CancelWithError(net::ERR_INVALID_SIGNED_EXCHANGE);
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, DeleteFetcher_BeforeReceiveResponse) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
RunUntilIdle();
fetcher.reset();
RunUntilIdle();
EXPECT_FALSE(callback_called_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, DeleteFetcher_BeforeResponseBody) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
fetcher.reset();
RunUntilIdle();
EXPECT_FALSE(callback_called_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, DeleteFetcher_WhileReceivingBody) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(mojo::CreateDataPipe(message.size() / 2 + 1, producer_handle,
consumer_handle),
MOJO_RESULT_OK);
ASSERT_TRUE(mojo::BlockingCopyFromString(
message.substr(0, message.size() / 2), producer_handle));
CallOnReceiveResponse(std::move(consumer_handle));
RunUntilIdle();
fetcher.reset();
RunUntilIdle();
ASSERT_TRUE(mojo::BlockingCopyFromString(message.substr(message.size() / 2),
producer_handle));
RunUntilIdle();
EXPECT_FALSE(callback_called_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, DeleteFetcher_AfterReceivingBody) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(
mojo::CreateDataPipe(message.size(), producer_handle, consumer_handle),
MOJO_RESULT_OK);
CHECK(mojo::BlockingCopyFromString(message, producer_handle));
CallOnReceiveResponse(std::move(consumer_handle));
RunUntilIdle();
CloseClientPipe();
RunUntilIdle();
producer_handle.reset();
fetcher.reset();
RunUntilIdle();
EXPECT_FALSE(callback_called_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, CloseClientPipe_BeforeReceiveResponse) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
RunUntilIdle();
CloseClientPipe();
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, CloseClientPipe_BeforeResponseBody) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
CloseClientPipe();
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, CloseClientPipe_WhileReceivingBody) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(mojo::CreateDataPipe(message.size() / 2 + 1, producer_handle,
consumer_handle),
MOJO_RESULT_OK);
ASSERT_TRUE(mojo::BlockingCopyFromString(
message.substr(0, message.size() / 2), producer_handle));
CallOnReceiveResponse(std::move(consumer_handle));
RunUntilIdle();
CloseClientPipe();
RunUntilIdle();
producer_handle.reset();
RunUntilIdle();
EXPECT_TRUE(callback_called_);
// SignedExchangeCertFetcher receives a truncated cert cbor.
EXPECT_EQ(SignedExchangeLoadResult::kCertParseError, result_);
EXPECT_FALSE(cert_result_);
}
TEST_F(SignedExchangeCertFetcherTest, CloseClientPipe_AfterReceivingBody) {
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(url_, false /* force_fetch */);
scoped_refptr<net::X509Certificate> certificate = ImportTestCert();
const std::string message =
CreateCertMessage(CreateCertMessageFromCert(*certificate));
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(
mojo::CreateDataPipe(message.size(), producer_handle, consumer_handle),
MOJO_RESULT_OK);
CHECK(mojo::BlockingCopyFromString(message, producer_handle));
CallOnReceiveResponse(std::move(consumer_handle));
RunUntilIdle();
CloseClientPipe();
RunUntilIdle();
producer_handle.reset();
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kSuccess, result_);
ASSERT_TRUE(cert_result_);
EXPECT_EQ(certificate->CalculateChainFingerprint256(),
cert_result_->cert()->CalculateChainFingerprint256());
}
TEST_F(SignedExchangeCertFetcherTest, DataURL) {
std::string data_url_string = "data:application/cert-chain+cbor";
std::string output = base::Base64Encode(CreateTestData());
data_url_string += ";base64," + output;
const GURL data_url = GURL(data_url_string);
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(data_url, false /* force_fetch */);
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kSuccess, result_);
ASSERT_TRUE(cert_result_);
EXPECT_EQ(GetTestDataCertFingerprint256(),
cert_result_->cert()->CalculateChainFingerprint256());
}
TEST_F(SignedExchangeCertFetcherTest, DataURLWithWrongMimeType) {
const GURL data_url = GURL("data:application/octet-stream,foobar");
std::unique_ptr<SignedExchangeCertFetcher> fetcher =
CreateFetcherAndStart(data_url, false /* force_fetch */);
RunUntilIdle();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(SignedExchangeLoadResult::kCertFetchError, result_);
}
} // namespace content