Skip to content

Commit

Permalink
[Trigger Attestation] Add attribution attestation mediator
Browse files Browse the repository at this point in the history
AttributionAttestationMediator handles a single trigger attestation operation
(https://github.com/WICG/attribution-reporting-api/blob/main/trigger_attestation.md):
it generates a blinded unsigned message using an underlying cryptographic
library, asks a token issuer to sign the blinded message, verifies and
unblinds the blind token using the cryptographic library and then returns a
token for redemption.

The attribution request helper
(https://chromium-review.googlesource.com/c/chromium/src/+/4097288) hooked in
a url_loader will own a attribution mediator instance.

The class can be viewed in context in this Trigger Attestation prototype CL:
https://chromium-review.googlesource.com/c/chromium/src/+/4048207

Bug: 1396434 Change-Id: Ieb75bc6f476f1e858db17bb953f50edef7ce07c0
Change-Id: Ieb75bc6f476f1e858db17bb953f50edef7ce07c0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4089468
Reviewed-by: Charlie Harrison <csharrison@chromium.org>
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Commit-Queue: Anthony Garant <anthonygarant@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1091935}
  • Loading branch information
agarant authored and Chromium LUCI CQ committed Jan 12, 2023
1 parent 537a180 commit 974eee9
Show file tree
Hide file tree
Showing 11 changed files with 820 additions and 0 deletions.
3 changes: 3 additions & 0 deletions services/network/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ component("network_service") {
"//mojo/public/cpp/system",
"//net",
"//net:extras",
"//services/network/attribution",
"//services/network/first_party_sets:first_party_sets_manager",
"//services/network/public/cpp",
"//services/network/public/cpp:crash_keys",
Expand Down Expand Up @@ -473,6 +474,8 @@ source_set("tests") {
"//net:simple_quic_tools",
"//net:test_support",
"//net/http:transport_security_state_unittest_data_default",
"//services/network/attribution",
"//services/network/attribution:tests",
"//services/network/first_party_sets:first_party_sets_manager",
"//services/network/first_party_sets:tests",
"//services/network/public/cpp",
Expand Down
47 changes: 47 additions & 0 deletions services/network/attribution/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 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.

source_set("attribution") {
visibility = [
":*",
"//services/network:network_service",
"//services/network:test_support",
"//services/network:tests",
]
defines = [ "IS_NETWORK_SERVICE_IMPL" ]
sources = [
"attribution_attestation_mediator.cc",
"attribution_attestation_mediator.h",
]
deps = [
"//base",
"//net",
"//services/network/public/cpp",
"//services/network/public/mojom",
"//services/network/trust_tokens",
"//url",
]
}

source_set("tests") {
testonly = true

defines = [ "IS_NETWORK_SERVICE_IMPL" ]

sources = [
"attribution_attestation_mediator_unittest.cc",
"attribution_test_utils.cc",
"attribution_test_utils.h",
]

deps = [
":attribution",
"//base",
"//base/test:test_support",
"//services/network/public/mojom",
"//services/network/trust_tokens",
"//testing/gtest",
"//url",
]
}
1 change: 1 addition & 0 deletions services/network/attribution/DIR_METADATA
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mixins: "//content/browser/attribution_reporting/COMMON_METADATA"
4 changes: 4 additions & 0 deletions services/network/attribution/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
anthonygarant@chromium.org
csharrison@chromium.org

file://content/browser/attribution_reporting/OWNERS
13 changes: 13 additions & 0 deletions services/network/attribution/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Attribution

`//services/network/attribution` contains logic supporting the [Attribution
Reporting API](https://github.com/WICG/attribution-reporting-api) in the network
service. Specific use cases are detailed below.

## Attribution Attestation

The directory contains logic that orchestrates [Trigger
Attestation](https://github.com/WICG/attribution-reporting-api/blob/main/trigger_attestation.md).
Hooked in the url_loader when applicable, it adds and parse specific headers
which allow reporting origins to submit a token attesting to the veracity of
a trigger registration.
175 changes: 175 additions & 0 deletions services/network/attribution/attribution_attestation_mediator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// 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 <utility>

#include "base/bind.h"
#include "base/check.h"
#include "base/task/thread_pool.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/trust_token_http_headers.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h"
#include "services/network/trust_tokens/trust_token_key_commitment_getter.h"
#include "services/network/trust_tokens/types.h"
#include "url/origin.h"

namespace network {

using Cryptographer = AttributionAttestationMediator::Cryptographer;

struct AttributionAttestationMediator::CryptographerAndBlindMessage {
std::unique_ptr<Cryptographer> cryptographer;
absl::optional<std::string> blind_message;
};

struct AttributionAttestationMediator::CryptographerAndToken {
std::unique_ptr<Cryptographer> cryptographer;
absl::optional<std::string> token;
};

AttributionAttestationMediator::AttributionAttestationMediator(
const TrustTokenKeyCommitmentGetter* key_commitment_getter,
std::unique_ptr<Cryptographer> cryptographer)
: key_commitment_getter_(std::move(key_commitment_getter)),
cryptographer_(std::move(cryptographer)) {
DCHECK(key_commitment_getter_);
DCHECK(cryptographer_);
}

AttributionAttestationMediator::~AttributionAttestationMediator() = default;

void AttributionAttestationMediator::GetHeadersForAttestation(
const GURL& url,
const std::string& message,
base::OnceCallback<void(net::HttpRequestHeaders)> done) {
DCHECK(!message_);
message_ = message;

absl::optional<SuitableTrustTokenOrigin> issuer =
SuitableTrustTokenOrigin::Create(url);
if (!issuer.has_value()) {
std::move(done).Run(net::HttpRequestHeaders());
return;
}

key_commitment_getter_->Get(
issuer.value(),
base::BindOnce(&AttributionAttestationMediator::OnGotKeyCommitment,
weak_ptr_factory_.GetWeakPtr(), std::move(done)));
}

void AttributionAttestationMediator::OnGotKeyCommitment(
base::OnceCallback<void(net::HttpRequestHeaders)> done,
mojom::TrustTokenKeyCommitmentResultPtr commitment_result) {
if (!commitment_result) {
std::move(done).Run(net::HttpRequestHeaders());
return;
}

if (!cryptographer_->Initialize(commitment_result->protocol_version)) {
std::move(done).Run(net::HttpRequestHeaders());
return;
}

for (const mojom::TrustTokenVerificationKeyPtr& key :
commitment_result->keys) {
if (!cryptographer_->AddKey(key->body)) {
std::move(done).Run(net::HttpRequestHeaders());
return;
}
}

base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<Cryptographer> cryptographer,
std::string message) {
absl::optional<std::string> blind_message =
cryptographer->BeginIssuance(message);
return AttributionAttestationMediator::CryptographerAndBlindMessage{
std::move(cryptographer), std::move(blind_message)};
},
std::move(cryptographer_), message_.value()),
base::BindOnce(&AttributionAttestationMediator::OnDoneBeginIssuance,
weak_ptr_factory_.GetWeakPtr(),
commitment_result->protocol_version, std::move(done)));
}

void AttributionAttestationMediator::OnDoneBeginIssuance(
mojom::TrustTokenProtocolVersion protocol_version,
base::OnceCallback<void(net::HttpRequestHeaders)> done,
AttributionAttestationMediator::CryptographerAndBlindMessage
cryptographer_and_blind_message) {
cryptographer_ = std::move(cryptographer_and_blind_message.cryptographer);

if (!cryptographer_and_blind_message.blind_message.has_value()) {
std::move(done).Run(net::HttpRequestHeaders());
return;
}

net::HttpRequestHeaders request_headers;
request_headers.SetHeader(
kTriggerAttestationHeader,
std::move(cryptographer_and_blind_message.blind_message.value()));
request_headers.SetHeader(
kTrustTokensSecTrustTokenVersionHeader,
internal::ProtocolVersionToString(protocol_version));
std::move(done).Run(std::move(request_headers));
}

void AttributionAttestationMediator::ProcessAttestationToGetToken(
net::HttpResponseHeaders& response_headers,
base::OnceCallback<void(absl::optional<std::string>)> done) {
DCHECK(message_.has_value());

std::string header_value;

// EnumerateHeader(|iter|=nullptr) asks for the first instance of the header,
// if any. At most one `kTriggerAttestationHeader` is expected as only one
// token can attest to a trigger. Subsequent instances of the header are
// ignored.
if (!response_headers.EnumerateHeader(
/*iter=*/nullptr, kTriggerAttestationHeader, &header_value)) {
std::move(done).Run(absl::nullopt);
return;
}
response_headers.RemoveHeader(kTriggerAttestationHeader);

base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<Cryptographer> cryptographer,
std::string blind_token) {
absl::optional<std::string> token =
cryptographer->ConfirmIssuanceAndBeginRedemption(blind_token);

return AttributionAttestationMediator::CryptographerAndToken{
std::move(cryptographer), std::move(token)};
},
std::move(cryptographer_), std::move(header_value)),
base::BindOnce(
&AttributionAttestationMediator::OnDoneProcessingIssuanceResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(done)));
}

void AttributionAttestationMediator::OnDoneProcessingIssuanceResponse(
base::OnceCallback<void(absl::optional<std::string>)> done,
AttributionAttestationMediator::CryptographerAndToken
cryptographer_and_token) {
cryptographer_ = std::move(cryptographer_and_token.cryptographer);

if (!cryptographer_and_token.token.has_value()) {
// The response was rejected by the underlying cryptographic library as
// malformed or otherwise invalid.
std::move(done).Run(absl::nullopt);
return;
}

std::move(done).Run(std::move(cryptographer_and_token.token.value()));
}

} // namespace network
Loading

0 comments on commit 974eee9

Please sign in to comment.