[go: nahoru, domu]

blob: a9684dd59f691306e2e44a658164121de725ad57 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/attribution/attribution_attestation_mediator.h"
#include <memory>
#include <set>
#include <string>
#include "base/run_loop.h"
#include "base/strings/string_piece_forward.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "services/network/attribution/attribution_attestation_mediator_metrics_recorder.h"
#include "services/network/attribution/attribution_test_utils.h"
#include "services/network/public/mojom/trust_tokens.mojom-shared.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/trust_tokens/trust_token_key_commitments.h"
#include "services/network/trust_tokens/types.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace network {
class AttributionAttestationMediatorTest : public testing::Test {
protected:
void SetUp() override {
key_commitment_getter_ = CreateTestTrustTokenKeyCommitments(
/*key=*/example_verification_key_,
/*protocol_version=*/example_protocol_version_,
/*issuer_url=*/example_valid_request_url_);
auto fake_cryptographer = std::make_unique<FakeCryptographer>();
fake_cryptographer_ = fake_cryptographer.get();
mediator_ = std::make_unique<AttributionAttestationMediator>(
key_commitment_getter_.get(), std::move(fake_cryptographer),
std::make_unique<AttributionAttestationMediatorMetricsRecorder>());
}
net::HttpRequestHeaders RunGetHeadersForAttestationWith(
const GURL& url,
const std::string& message) {
base::RunLoop run_loop;
net::HttpRequestHeaders headers;
mediator_->GetHeadersForAttestation(
url, message,
base::BindLambdaForTesting(
[&run_loop, &headers](net::HttpRequestHeaders h) {
headers = h;
run_loop.Quit();
}));
run_loop.Run();
return headers;
}
void RunGetHeadersForAttestationWithValidParams() {
RunGetHeadersForAttestationWith(
/*url=*/example_valid_request_url_, /*message=*/"message");
}
absl::optional<std::string> RunProcessAttestationToGetTokenWith(
net::HttpResponseHeaders& response_headers) {
base::RunLoop run_loop;
absl::optional<std::string> maybe_token;
mediator_->ProcessAttestationToGetToken(
response_headers,
base::BindLambdaForTesting(
[&run_loop, &maybe_token](absl::optional<std::string> m) {
maybe_token = m;
run_loop.Quit();
}));
run_loop.Run();
return maybe_token;
}
GURL example_valid_request_url_ =
GURL("https://reporting-origin.example/test/path/#123");
mojom::TrustTokenProtocolVersion example_protocol_version_ =
mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
std::string example_verification_key_ = "example-key";
base::test::TaskEnvironment task_environment_;
std::unique_ptr<TrustTokenKeyCommitments> key_commitment_getter_;
// We hold onto a raw ptr to configure the call expectations, the helper owns
// the unique_ptr.
base::raw_ptr<FakeCryptographer> fake_cryptographer_;
std::unique_ptr<AttributionAttestationMediator> mediator_;
base::HistogramTester histograms_;
};
TEST_F(AttributionAttestationMediatorTest,
GetHeadersForAttestation_HeadersReturned) {
net::HttpRequestHeaders headers = RunGetHeadersForAttestationWith(
/*url=*/example_valid_request_url_, /*message=*/"message");
std::string attestation_header;
headers.GetHeader("Sec-Attribution-Reporting-Private-State-Token",
&attestation_header);
// Check that the message was blinded by the Cryptographer before being added
// as an attestation header.
EXPECT_TRUE(FakeCryptographer::IsBlindMessage(attestation_header, "message"));
std::string version_header;
headers.GetHeader("Sec-Private-State-Token-Crypto-Version", &version_header);
EXPECT_EQ(version_header,
internal::ProtocolVersionToString(example_protocol_version_));
EXPECT_TRUE(
base::Contains(fake_cryptographer_->keys, example_verification_key_));
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.GetHeadersStatus",
AttributionAttestationMediator::GetHeadersStatus::kSuccess,
/*expected_bucket_count=*/1);
}
TEST_F(AttributionAttestationMediatorTest,
GetHeadersForAttestation_NonSuitableIssuer) {
net::HttpRequestHeaders headers = RunGetHeadersForAttestationWith(
/*url=*/GURL("http://not-https-url.example/path"),
/*message=*/"does-not-matter");
EXPECT_TRUE(headers.IsEmpty());
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.GetHeadersStatus",
AttributionAttestationMediator::GetHeadersStatus::
kIssuerOriginNotSuitable,
/*expected_bucket_count=*/1);
}
TEST_F(AttributionAttestationMediatorTest,
GetHeadersForAttestation_NoIssuerReturned) {
net::HttpRequestHeaders headers = RunGetHeadersForAttestationWith(
/*url=*/GURL("https://not-registered-origin-url.example/path"),
/*message=*/"does-not-matter");
EXPECT_TRUE(headers.IsEmpty());
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.GetHeadersStatus",
AttributionAttestationMediator::GetHeadersStatus::kIssuerNotRegistered,
/*expected_bucket_count=*/1);
}
TEST_F(AttributionAttestationMediatorTest,
GetHeadersForAttestation_CryptographerInitializationFails) {
fake_cryptographer_->set_should_fail_initialize(true);
net::HttpRequestHeaders headers = RunGetHeadersForAttestationWith(
/*url=*/example_valid_request_url_, /*message=*/"does-not-matter");
EXPECT_TRUE(headers.IsEmpty());
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.GetHeadersStatus",
AttributionAttestationMediator::GetHeadersStatus::
kUnableToInitializeCryptographer,
/*expected_bucket_count=*/1);
}
TEST_F(AttributionAttestationMediatorTest,
GetHeadersForAttestation_CryprographerAddKeyFails) {
fake_cryptographer_->set_should_fail_add_key(true);
net::HttpRequestHeaders headers = RunGetHeadersForAttestationWith(
/*url=*/example_valid_request_url_, /*message=*/"does-not-matter");
EXPECT_TRUE(headers.IsEmpty());
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.GetHeadersStatus",
AttributionAttestationMediator::GetHeadersStatus::
kUnableToAddKeysOnCryptographer,
/*expected_bucket_count=*/1);
}
TEST_F(AttributionAttestationMediatorTest,
GetHeadersForAttestation_CryptographerReturnsNoBlindMessage) {
fake_cryptographer_->set_should_fail_begin_issuance(true);
net::HttpRequestHeaders headers = RunGetHeadersForAttestationWith(
/*url=*/example_valid_request_url_, /*message=*/"does-not-matter");
EXPECT_TRUE(headers.IsEmpty());
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.GetHeadersStatus",
AttributionAttestationMediator::GetHeadersStatus::kUnableToBlindMessage,
/*expected_bucket_count=*/1);
}
TEST_F(AttributionAttestationMediatorTest,
ProcessAttestationToGetToken_HeaderValueReturned) {
RunGetHeadersForAttestationWithValidParams();
auto response_head = mojom::URLResponseHead::New();
response_head->headers = net::HttpResponseHeaders::TryToCreate("");
response_head->headers->AddHeader(
"Sec-Attribution-Reporting-Private-State-Token", "blind-token");
absl::optional<std::string> maybe_token =
RunProcessAttestationToGetTokenWith(*response_head->headers.get());
// Check that that the blind-token returned by the issuer has been formed in
// a token by the Cryptographer.
EXPECT_TRUE(FakeCryptographer::IsToken(maybe_token.value(), "blind-token"));
// Check that the header has been removed after beein processed.
EXPECT_FALSE(response_head->headers->HasHeader(
"Sec-Attribution-Reporting-Private-State-Token"));
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.ProcessAttestationStatus",
AttributionAttestationMediator::ProcessAttestationStatus::kSuccess,
/*expected_bucket_count=*/1);
}
TEST_F(AttributionAttestationMediatorTest,
ProcessAttestationToGetToken_ResponseHeaderIsMissing) {
RunGetHeadersForAttestationWithValidParams();
auto response_head = mojom::URLResponseHead::New();
response_head->headers = net::HttpResponseHeaders::TryToCreate("");
absl::optional<std::string> maybe_token =
RunProcessAttestationToGetTokenWith(*response_head->headers.get());
EXPECT_FALSE(maybe_token.has_value());
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.ProcessAttestationStatus",
AttributionAttestationMediator::ProcessAttestationStatus::
kNoSignatureReceivedFromIssuer,
/*expected_bucket_count=*/1);
}
TEST_F(AttributionAttestationMediatorTest,
ProcessAttestationToGetToken_CryptographerReturnsNoToken) {
RunGetHeadersForAttestationWithValidParams();
auto response_head = mojom::URLResponseHead::New();
response_head->headers = net::HttpResponseHeaders::TryToCreate("");
response_head->headers->AddHeader(
"Sec-Attribution-Reporting-Private-State-Token", "blind-token");
fake_cryptographer_->set_should_fail_confirm_issuance(true);
absl::optional<std::string> maybe_token =
RunProcessAttestationToGetTokenWith(*response_head->headers.get());
EXPECT_FALSE(maybe_token.has_value());
// The header should have been removed even if not able to get a
// token from it.
EXPECT_FALSE(response_head->headers->HasHeader(
"Sec-Attribution-Reporting-Private-State-Token"));
histograms_.ExpectUniqueSample(
"Conversions.TriggerAttestation.ProcessAttestationStatus",
AttributionAttestationMediator::ProcessAttestationStatus::
kUnableToUnblindSignature,
/*expected_bucket_count=*/1);
}
} // namespace network