0

Implement custom key commitments for Trust Token.

Bug: 1342255
Change-Id: I323c2ad1330e7a5e982e01fa692b5053630d5be4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3735304
Commit-Queue: Steven Valdez <svaldez@chromium.org>
Auto-Submit: Steven Valdez <svaldez@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Russ Hamilton <behamilton@google.com>
Cr-Commit-Position: refs/heads/main@{#1032702}
This commit is contained in:
Steven Valdez
2022-08-08 20:28:57 +00:00
committed by Chromium LUCI CQ
parent 0bdc18bac0
commit 08de9b9aae
10 changed files with 299 additions and 59 deletions

@ -26,7 +26,7 @@ namespace {
OptionalTrustTokenParams NonemptyTrustTokenParams() {
return mojom::TrustTokenParams(
mojom::TrustTokenOperationType::kRedemption,
mojom::TrustTokenRefreshPolicy::kRefresh,
mojom::TrustTokenRefreshPolicy::kRefresh, "custom_key_commitment",
mojom::TrustTokenSignRequestData::kInclude,
/*include_timestamp_header=*/true,
std::vector<url::Origin>{url::Origin::Create(GURL("https://issuer.com"))},

@ -123,6 +123,10 @@ struct TrustTokenParams {
// redeem a new token, evicting the RR currently stored.
TrustTokenRefreshPolicy refresh_policy = kUseCached;
// "custom_key_commitment" stores a custom key commitment that should be
// used for this operation if set.
string? custom_key_commitment;
// The remaining members are used only when "type" is "kSigning": these
// parameters specify the manner in which the outgoing request should be
// signed, including optionally specifying additional data to add in

@ -150,6 +150,7 @@ void TrustTokenRequestHelperFactory::ConstructHelperUsingStore(
Outcome::kSuccessfullyCreatedAnIssuanceHelper);
auto helper = std::make_unique<TrustTokenRequestIssuanceHelper>(
std::move(top_frame_origin), store, key_commitment_getter_,
params->custom_key_commitment,
std::make_unique<BoringsslTrustTokenIssuanceCryptographer>(),
std::make_unique<LocalTrustTokenOperationDelegateImpl>(
context_client_provider_),
@ -166,7 +167,8 @@ void TrustTokenRequestHelperFactory::ConstructHelperUsingStore(
Outcome::kSuccessfullyCreatedARedemptionHelper);
auto helper = std::make_unique<TrustTokenRequestRedemptionHelper>(
std::move(top_frame_origin), params->refresh_policy, store,
key_commitment_getter_, std::make_unique<EcdsaP256KeyPairGenerator>(),
key_commitment_getter_, params->custom_key_commitment,
std::make_unique<EcdsaP256KeyPairGenerator>(),
std::make_unique<BoringsslTrustTokenRedemptionCryptographer>(),
std::move(net_log));
std::move(done).Run(TrustTokenStatusOrRequestHelper(

@ -23,6 +23,7 @@
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/trust_tokens/proto/public.pb.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h"
#include "services/network/trust_tokens/trust_token_key_commitment_parser.h"
#include "services/network/trust_tokens/trust_token_key_filtering.h"
#include "services/network/trust_tokens/trust_token_parameterization.h"
#include "services/network/trust_tokens/trust_token_store.h"
@ -92,6 +93,7 @@ TrustTokenRequestIssuanceHelper::TrustTokenRequestIssuanceHelper(
SuitableTrustTokenOrigin top_level_origin,
TrustTokenStore* token_store,
const TrustTokenKeyCommitmentGetter* key_commitment_getter,
absl::optional<std::string> custom_key_commitment,
std::unique_ptr<Cryptographer> cryptographer,
std::unique_ptr<LocalTrustTokenOperationDelegate> local_operation_delegate,
base::RepeatingCallback<bool(mojom::TrustTokenKeyCommitmentResult::Os)>
@ -101,6 +103,7 @@ TrustTokenRequestIssuanceHelper::TrustTokenRequestIssuanceHelper(
: top_level_origin_(std::move(top_level_origin)),
token_store_(token_store),
key_commitment_getter_(std::move(key_commitment_getter)),
custom_key_commitment_(custom_key_commitment),
cryptographer_(std::move(cryptographer)),
local_operation_delegate_(std::move(local_operation_delegate)),
is_current_os_callback_(std::move(is_current_os_callback)),
@ -133,6 +136,18 @@ void TrustTokenRequestIssuanceHelper::Begin(
return;
}
if (custom_key_commitment_) {
mojom::TrustTokenKeyCommitmentResultPtr keys =
TrustTokenKeyCommitmentParser().Parse(*custom_key_commitment_);
if (!keys) {
LogOutcome(net_log_, kBegin, "Failed to parse custom keys");
std::move(done).Run(mojom::TrustTokenOperationStatus::kInvalidArgument);
return;
}
OnGotKeyCommitment(request, std::move(done), std::move(keys));
return;
}
if (!token_store_->SetAssociation(*issuer_, top_level_origin_)) {
LogOutcome(net_log_, kBegin, "Couldn't set issuer-toplevel association");
std::move(done).Run(mojom::TrustTokenOperationStatus::kResourceExhausted);

@ -146,6 +146,7 @@ class TrustTokenRequestIssuanceHelper : public TrustTokenRequestHelper {
SuitableTrustTokenOrigin top_level_origin,
TrustTokenStore* token_store,
const TrustTokenKeyCommitmentGetter* key_commitment_getter,
absl::optional<std::string> custom_key_commitment,
std::unique_ptr<Cryptographer> cryptographer,
std::unique_ptr<LocalTrustTokenOperationDelegate>
local_operation_delegate,
@ -270,6 +271,7 @@ class TrustTokenRequestIssuanceHelper : public TrustTokenRequestHelper {
const SuitableTrustTokenOrigin top_level_origin_;
const raw_ptr<TrustTokenStore> token_store_;
const raw_ptr<const TrustTokenKeyCommitmentGetter> key_commitment_getter_;
absl::optional<std::string> custom_key_commitment_;
mojom::TrustTokenProtocolVersion protocol_version_;

@ -162,7 +162,7 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfTooManyIssuers) {
}
TrustTokenRequestIssuanceHelper helper(
toplevel, store.get(), g_fixed_key_commitment_getter.get(),
toplevel, store.get(), g_fixed_key_commitment_getter.get(), absl::nullopt,
std::make_unique<MockCryptographer>(),
std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
@ -188,7 +188,7 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfAtCapacity) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), g_fixed_key_commitment_getter.get(),
store.get(), g_fixed_key_commitment_getter.get(), absl::nullopt,
std::make_unique<MockCryptographer>(),
std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
@ -211,7 +211,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfKeyCommitmentFails) {
auto getter = std::make_unique<FixedKeyCommitmentGetter>(issuer, nullptr);
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::make_unique<MockCryptographer>(),
store.get(), getter.get(), absl::nullopt,
std::make_unique<MockCryptographer>(),
std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
@ -236,8 +237,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -260,8 +261,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfAddingKeyFails) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -288,8 +289,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -321,8 +322,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, SetsRequestHeaders) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -361,8 +362,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, SetsLoadFlag) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -390,8 +391,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfResponseOmitsHeader) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -424,8 +425,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, TreatsEmptyHeaderAsSuccess) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -469,8 +470,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfResponseIsUnusable) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -513,8 +514,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, Success) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -555,8 +556,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, AssociatesIssuerWithToplevel) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -598,8 +599,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, StoresObtainedTokens) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -664,8 +665,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, DiscardDataResponseSuccess) {
// ReasonableKeyCommitmentGetter is for issuer1
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
// request is from issuer1
@ -733,8 +734,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -800,8 +801,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -858,8 +859,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -890,7 +891,7 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsUnsuitableInsecureIssuer) {
auto store = TrustTokenStore::CreateForTesting();
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), g_fixed_key_commitment_getter.get(),
store.get(), g_fixed_key_commitment_getter.get(), absl::nullopt,
std::make_unique<MockCryptographer>(),
std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
@ -906,7 +907,7 @@ TEST_F(TrustTokenRequestIssuanceHelperTest,
auto store = TrustTokenStore::CreateForTesting();
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), g_fixed_key_commitment_getter.get(),
store.get(), g_fixed_key_commitment_getter.get(), absl::nullopt,
std::make_unique<MockCryptographer>(),
std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
@ -935,8 +936,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RespectsMaximumBatchsize) {
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
store.get(), ReasonableKeyCommitmentGetter(), absl::nullopt,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
@ -946,6 +947,90 @@ TEST_F(TrustTokenRequestIssuanceHelperTest, RespectsMaximumBatchsize) {
mojom::TrustTokenOperationStatus::kOk);
}
// Check that attempting to issue with custom key commitments fails if custom
// key commitments are invalid.
TEST_F(TrustTokenRequestIssuanceHelperTest, BadCustomKeys) {
auto store = TrustTokenStore::CreateForTesting();
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/")),
store.get(), g_fixed_key_commitment_getter.get(), "junk keys",
std::make_unique<MockCryptographer>(),
std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kInvalidArgument);
}
// Check that a successful end-to-end Begin/Finalize flow with custom key
// commitments stores the obtained trust tokens in the trust token store.
TEST_F(TrustTokenRequestIssuanceHelperTest, CustomKeysStoresObtainedTokens) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
// Have the Trust Tokens issuance conclude by the underlying cryptographic
// library returning one signed, unblinded token associated with the same
// returned from the key commitment.
auto unblinded_tokens = std::make_unique<UnblindedTokens>();
unblinded_tokens->body_of_verifying_key =
ReasonableKeyCommitmentResult()->keys.front()->body;
unblinded_tokens->tokens.push_back("a signed, unblinded token");
auto cryptographer = std::make_unique<MockCryptographer>();
EXPECT_CALL(*cryptographer, Initialize(_, _)).WillOnce(Return(true));
EXPECT_CALL(*cryptographer, AddKey(_)).WillOnce(Return(true));
EXPECT_CALL(*cryptographer, BeginIssuance(_))
.WillOnce(
Return(std::string("this string contains some blinded tokens")));
EXPECT_CALL(*cryptographer, ConfirmIssuance(_))
.WillOnce(Return(ByMove(std::move((unblinded_tokens)))));
base::Time one_minute_from_now = base::Time::Now() + base::Minutes(1);
int64_t one_minute_from_now_in_micros =
(one_minute_from_now - base::Time::UnixEpoch()).InMicroseconds();
const std::string basic_key = base::StringPrintf(
R"({ "TrustTokenV3PMB": {
"protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
"keys": {"1": { "Y": "akey", "expiry": "%s" }}
}})",
base::NumberToString(one_minute_from_now_in_micros).c_str());
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), ReasonableKeyCommitmentGetter(), basic_key,
std::move(cryptographer), std::make_unique<MockLocalOperationDelegate>(),
base::BindRepeating(&IsCurrentOperatingSystem), g_metrics_delegate.get());
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
ASSERT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kOk);
auto response_head = mojom::URLResponseHead::New();
response_head->headers =
net::HttpResponseHeaders::TryToCreate("HTTP/1.1 200 OK\r\n");
response_head->headers->SetHeader(
kTrustTokensSecTrustTokenHeader,
"response from issuer (this value will be ignored, since "
"Cryptographer::ConfirmResponse is mocked out)");
EXPECT_EQ(ExecuteFinalizeAndWaitForResult(&helper, response_head.get()),
mojom::TrustTokenOperationStatus::kOk);
// After the operation has successfully finished, the trust tokens parsed from
// the server response should be in the store.
auto match_all_keys =
base::BindRepeating([](const std::string&) { return true; });
EXPECT_THAT(
store->RetrieveMatchingTokens(issuer, std::move(match_all_keys)),
ElementsAre(Property(&TrustToken::body, "a signed, unblinded token")));
}
class TrustTokenRequestIssuanceHelperTestWithPlatformIssuance
: public TrustTokenRequestIssuanceHelperTest {
public:
@ -1018,7 +1103,7 @@ TEST_F(TrustTokenRequestIssuanceHelperTestWithPlatformIssuance,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer),
store.get(), getter.get(), absl::nullopt, std::move(cryptographer),
std::move(local_operation_delegate),
base::BindRepeating([](mojom::TrustTokenKeyCommitmentResult::Os os) {
return os == mojom::TrustTokenKeyCommitmentResult::Os::kAndroid;
@ -1088,7 +1173,7 @@ TEST_F(TrustTokenRequestIssuanceHelperTestWithPlatformIssuance,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer),
store.get(), getter.get(), absl::nullopt, std::move(cryptographer),
std::move(local_operation_delegate),
base::BindRepeating([](mojom::TrustTokenKeyCommitmentResult::Os os) {
return os == mojom::TrustTokenKeyCommitmentResult::Os::kAndroid;
@ -1124,7 +1209,8 @@ TEST_F(TrustTokenRequestIssuanceHelperTestWithPlatformIssuance,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::make_unique<MockCryptographer>(),
store.get(), getter.get(), absl::nullopt,
std::make_unique<MockCryptographer>(),
std::make_unique<MockLocalOperationDelegate>(),
// Fail to match to the current OS...
base::BindLambdaForTesting(
@ -1168,7 +1254,7 @@ TEST_F(TrustTokenRequestIssuanceHelperTestWithPlatformIssuance,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer),
store.get(), getter.get(), absl::nullopt, std::move(cryptographer),
std::make_unique<MockLocalOperationDelegate>(),
// Fail to match to the current OS...
base::BindLambdaForTesting(
@ -1235,7 +1321,7 @@ TEST_F(TrustTokenRequestIssuanceHelperTestWithPlatformIssuance,
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer),
store.get(), getter.get(), absl::nullopt, std::move(cryptographer),
std::move(local_operation_delegate),
base::BindRepeating(&IsCurrentOperatingSystem), metrics_delegate.get());

@ -17,6 +17,7 @@
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/trust_tokens/proto/public.pb.h"
#include "services/network/trust_tokens/trust_token_database_owner.h"
#include "services/network/trust_tokens/trust_token_key_commitment_parser.h"
#include "services/network/trust_tokens/trust_token_parameterization.h"
#include "services/network/trust_tokens/trust_token_store.h"
#include "url/url_constants.h"
@ -50,6 +51,7 @@ TrustTokenRequestRedemptionHelper::TrustTokenRequestRedemptionHelper(
mojom::TrustTokenRefreshPolicy refresh_policy,
TrustTokenStore* token_store,
const TrustTokenKeyCommitmentGetter* key_commitment_getter,
absl::optional<std::string> custom_key_commitment,
std::unique_ptr<KeyPairGenerator> key_pair_generator,
std::unique_ptr<Cryptographer> cryptographer,
net::NetLogWithSource net_log)
@ -57,6 +59,7 @@ TrustTokenRequestRedemptionHelper::TrustTokenRequestRedemptionHelper(
refresh_policy_(refresh_policy),
token_store_(token_store),
key_commitment_getter_(std::move(key_commitment_getter)),
custom_key_commitment_(custom_key_commitment),
key_pair_generator_(std::move(key_pair_generator)),
cryptographer_(std::move(cryptographer)),
net_log_(std::move(net_log)) {
@ -84,6 +87,18 @@ void TrustTokenRequestRedemptionHelper::Begin(
return;
}
if (custom_key_commitment_) {
mojom::TrustTokenKeyCommitmentResultPtr keys =
TrustTokenKeyCommitmentParser().Parse(*custom_key_commitment_);
if (!keys) {
LogOutcome(net_log_, kBegin, "Failed to parse custom keys");
std::move(done).Run(mojom::TrustTokenOperationStatus::kInvalidArgument);
return;
}
OnGotKeyCommitment(request, std::move(done), std::move(keys));
return;
}
if (!token_store_->SetAssociation(*issuer_, top_level_origin_)) {
LogOutcome(net_log_, kBegin, "Couldn't set issuer-toplevel association");
std::move(done).Run(mojom::TrustTokenOperationStatus::kResourceExhausted);

@ -129,6 +129,7 @@ class TrustTokenRequestRedemptionHelper : public TrustTokenRequestHelper {
mojom::TrustTokenRefreshPolicy refresh_policy,
TrustTokenStore* token_store,
const TrustTokenKeyCommitmentGetter* key_commitment_getter,
absl::optional<std::string> custom_key_commitment,
std::unique_ptr<KeyPairGenerator> key_pair_generator,
std::unique_ptr<Cryptographer> cryptographer,
net::NetLogWithSource net_log = net::NetLogWithSource());
@ -214,6 +215,7 @@ class TrustTokenRequestRedemptionHelper : public TrustTokenRequestHelper {
const raw_ptr<TrustTokenStore> token_store_;
const raw_ptr<const TrustTokenKeyCommitmentGetter> key_commitment_getter_;
absl::optional<std::string> custom_key_commitment_;
const std::unique_ptr<KeyPairGenerator> key_pair_generator_;
const std::unique_ptr<Cryptographer> cryptographer_;
net::NetLogWithSource net_log_;

@ -134,7 +134,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RejectsIfTooManyIssuers) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(),
&*g_fixed_key_commitment_getter, std::make_unique<FakeKeyPairGenerator>(),
&*g_fixed_key_commitment_getter, absl::nullopt,
std::make_unique<FakeKeyPairGenerator>(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
@ -158,7 +159,7 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RejectsIfKeyCommitmentFails) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &getter,
std::make_unique<FakeKeyPairGenerator>(),
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
@ -186,7 +187,7 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RejectsIfNoTokensToRedeem) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(),
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
@ -236,7 +237,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest,
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(), std::move(cryptographer));
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
@ -286,7 +288,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest,
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(), std::move(cryptographer));
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
@ -331,7 +334,7 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RejectsIfKeyPairGenerationFails) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FailingKeyPairGenerator>(),
absl::nullopt, std::make_unique<FailingKeyPairGenerator>(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
@ -389,7 +392,8 @@ class TrustTokenBeginRedemptionPostconditionsTest
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(), std::move(cryptographer));
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
request_ = MakeURLRequest("https://issuer.com/");
request_->set_initiator(
@ -471,7 +475,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RecordsEmptyRequestHistogram) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(), std::move(cryptographer));
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
@ -522,7 +527,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RejectsIfResponseOmitsHeader) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(), std::move(cryptographer));
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
@ -587,7 +593,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RejectsIfResponseIsUnusable) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(), std::move(cryptographer));
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
@ -655,7 +662,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, Success) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(), std::move(cryptographer));
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
@ -722,7 +730,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, AssociatesIssuerWithToplevel) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
std::make_unique<FakeKeyPairGenerator>(), std::move(cryptographer));
absl::nullopt, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
@ -780,6 +789,7 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, NegativeLifetime) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
absl::nullopt,
std::make_unique<MockKeyPairGenerator>("signing key", "verification key"),
std::move(cryptographer));
@ -850,6 +860,7 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, NonnumericLifetime) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
absl::nullopt,
std::make_unique<MockKeyPairGenerator>("signing key", "verification key"),
std::move(cryptographer));
@ -921,6 +932,7 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, StoresObtainedRedemptionRecord) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
absl::nullopt,
std::make_unique<MockKeyPairGenerator>("signing key", "verification key"),
std::move(cryptographer));
@ -974,7 +986,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RedemptionRecordCacheHit) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(),
&*g_fixed_key_commitment_getter, std::make_unique<FakeKeyPairGenerator>(),
&*g_fixed_key_commitment_getter, absl::nullopt,
std::make_unique<FakeKeyPairGenerator>(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
@ -1033,6 +1046,7 @@ TEST_F(TrustTokenRequestRedemptionHelperTest,
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kRefresh, store.get(), &*getter,
absl::nullopt,
std::make_unique<MockKeyPairGenerator>("signing key", "verification key"),
std::move(cryptographer));
@ -1076,7 +1090,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest, RejectsUnsuitableInsecureIssuer) {
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(),
&*g_fixed_key_commitment_getter, std::make_unique<FakeKeyPairGenerator>(),
&*g_fixed_key_commitment_getter, absl::nullopt,
std::make_unique<FakeKeyPairGenerator>(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("http://insecure-issuer.com/");
@ -1091,7 +1106,8 @@ TEST_F(TrustTokenRequestRedemptionHelperTest,
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(),
&*g_fixed_key_commitment_getter, std::make_unique<FakeKeyPairGenerator>(),
&*g_fixed_key_commitment_getter, absl::nullopt,
std::make_unique<FakeKeyPairGenerator>(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("file:///non-https-issuer.txt");
@ -1100,4 +1116,101 @@ TEST_F(TrustTokenRequestRedemptionHelperTest,
mojom::TrustTokenOperationStatus::kInvalidArgument);
}
TEST_F(TrustTokenRequestRedemptionHelperTest, BadCustomKeys) {
auto store = TrustTokenStore::CreateForTesting();
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(),
&*g_fixed_key_commitment_getter, "junk keys",
std::make_unique<FakeKeyPairGenerator>(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kInvalidArgument);
}
// Check that, when preconditions are met and the underlying cryptographic steps
// successfully complete, the begin/finalize methods with custom key commitments
// succeed.
TEST_F(TrustTokenRequestRedemptionHelperTest, CustomKeysSuccess) {
// Establish the following state:
// * One key commitment returned from the key commitment registry, with one
// key, with body "".
// * One token stored corresponding to the key "" (this will be the token
// that the redemption request redeems; its key needs to match the key
// commitment's key so that it does not get evicted from storage after the key
// commitment is updated to reflect the key commitment result).
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
store->AddTokens(
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/")),
std::vector<std::string>{"a token"},
/*issuing_key=*/"");
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->protocol_version =
mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
key_commitment_result->id = 1;
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
*SuitableTrustTokenOrigin::Create(GURL("https://badissuer.com")),
std::move(key_commitment_result));
// Configure the cryptographer to succeed on both the outbound and inbound
// halves of the operation.
auto cryptographer = std::make_unique<MockCryptographer>();
EXPECT_CALL(*cryptographer, Initialize(_, _)).WillOnce(Return(true));
EXPECT_CALL(*cryptographer, BeginRedemption(_, _, _))
.WillOnce(Return("well-formed redemption request"));
EXPECT_CALL(*cryptographer, ConfirmRedemption(_))
.WillOnce(Return("a successfully-extracted RR"));
base::Time one_minute_from_now = base::Time::Now() + base::Minutes(1);
int64_t one_minute_from_now_in_micros =
(one_minute_from_now - base::Time::UnixEpoch()).InMicroseconds();
const std::string basic_key = base::StringPrintf(
R"({ "TrustTokenV3PMB": {
"protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
"keys": {"1": { "Y": "", "expiry": "%s" }}
}})",
base::NumberToString(one_minute_from_now_in_micros).c_str());
TrustTokenRequestRedemptionHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
mojom::TrustTokenRefreshPolicy::kUseCached, store.get(), &*getter,
basic_key, std::make_unique<FakeKeyPairGenerator>(),
std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/")));
mojom::TrustTokenOperationStatus result =
ExecuteBeginOperationAndWaitForResult(&helper, request.get());
// Since this test is testing the behavior on handling the response after
// successfully constructing a redemption request, sanity check that the setup
// has correctly caused constructing the request so succeed.
ASSERT_EQ(result, mojom::TrustTokenOperationStatus::kOk);
auto response_head = mojom::URLResponseHead::New();
response_head->headers =
net::HttpResponseHeaders::TryToCreate("HTTP/1.1 200 OK\r\n");
response_head->headers->SetHeader(kTrustTokensSecTrustTokenHeader, "");
// After a successfully constructed request, when the response is well-formed
// and the delegate accepts the response, Finalize should succeed.
EXPECT_EQ(ExecuteFinalizeAndWaitForResult(&helper, response_head.get()),
mojom::TrustTokenOperationStatus::kOk);
// Processing the response should have stripped the header.
EXPECT_FALSE(
response_head->headers->HasHeader(kTrustTokensSecTrustTokenHeader));
}
} // namespace network

@ -58,6 +58,7 @@ TEST(FetchRequestDataTest, CheckTrustTokenParamsAreCopiedWithCreate) {
auto trust_token_params = network::mojom::blink::TrustTokenParams::New(
network::mojom::TrustTokenOperationType::kRedemption,
network::mojom::TrustTokenRefreshPolicy::kUseCached,
/* custom_key_commitment=*/"custom_key_commitment",
network::mojom::TrustTokenSignRequestData::kInclude,
/* include_timestamp_header=*/true, issuers, additional_signed_headers,
/* possibly_unsafe_additional_signing_data=*/"ccc");