blob: 0bafdc97d9f617c4072cc5a37f7977b2485852b3 [file] [log] [blame]
// Copyright 2020 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 "services/network/trust_tokens/trust_token_request_issuance_helper.h"
#include <memory>
#include "base/callback.h"
#include "base/test/task_environment.h"
#include "net/base/load_flags.h"
#include "net/base/request_priority.h"
#include "net/http/http_response_headers.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/trust_tokens/proto/public.pb.h"
#include "services/network/trust_tokens/test/trust_token_test_util.h"
#include "services/network/trust_tokens/trust_token_http_headers.h"
#include "services/network/trust_tokens/trust_token_key_commitment_getter.h"
#include "services/network/trust_tokens/trust_token_parameterization.h"
#include "services/network/trust_tokens/trust_token_store.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
namespace network {
namespace {
using ::testing::_;
using ::testing::ByMove;
using ::testing::ElementsAre;
using ::testing::Property;
using ::testing::Return;
using ::testing::ReturnNull;
using TrustTokenRequestIssuanceHelperTest = TrustTokenRequestHelperTest;
using UnblindedTokens =
TrustTokenRequestIssuanceHelper::Cryptographer::UnblindedTokens;
// FixedKeyCommitmentGetter returns the provided commitment result when
// |Get| is called by the tested code.
class FixedKeyCommitmentGetter : public TrustTokenKeyCommitmentGetter {
public:
FixedKeyCommitmentGetter() = default;
explicit FixedKeyCommitmentGetter(
const url::Origin& issuer,
mojom::TrustTokenKeyCommitmentResultPtr result)
: issuer_(issuer), result_(std::move(result)) {}
void Get(const url::Origin& origin,
base::OnceCallback<void(mojom::TrustTokenKeyCommitmentResultPtr)>
on_done) const override {
EXPECT_EQ(origin, issuer_);
std::move(on_done).Run(result_.Clone());
}
private:
url::Origin issuer_;
mojom::TrustTokenKeyCommitmentResultPtr result_;
};
base::NoDestructor<FixedKeyCommitmentGetter> g_fixed_key_commitment_getter{};
// MockCryptographer mocks out the cryptographic operations underlying Trust
// Tokens issuance.
class MockCryptographer
: public TrustTokenRequestIssuanceHelper::Cryptographer {
public:
MOCK_METHOD1(Initialize, bool(int issuer_configured_batch_size));
MOCK_METHOD1(AddKey, bool(base::StringPiece key));
MOCK_METHOD1(BeginIssuance, base::Optional<std::string>(size_t num_tokens));
MOCK_METHOD1(
ConfirmIssuance,
std::unique_ptr<UnblindedTokens>(base::StringPiece response_header));
};
} // namespace
// Check that issuance fails if it would result in too many issuers being
// configured for the issuance top-level origin.
TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfTooManyIssuers) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
auto issuer = *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/"));
// Associate the toplevel with the cap's worth of issuers different from
// |issuer|. (The cap is guaranteed to be quite small because of privacy
// requirements of the Trust Tokens protocol.)
for (int i = 0; i < kTrustTokenPerToplevelMaxNumberOfAssociatedIssuers; ++i) {
ASSERT_TRUE(store->SetAssociation(
*SuitableTrustTokenOrigin::Create(
GURL(base::StringPrintf("https://issuer%d.com/", i))),
toplevel));
}
TrustTokenRequestIssuanceHelper helper(toplevel, store.get(),
g_fixed_key_commitment_getter.get(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kResourceExhausted);
}
// Check that issuance fails if the number of tokens stored for the issuer is
// already at capacity.
TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfAtCapacity) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
auto issuer = *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
// Fill up the store with tokens; issuance should fail the tokens for |issuer|
// are at capacity.
store->AddTokens(issuer,
std::vector<std::string>(kTrustTokenPerIssuerTokenCapacity),
/*issuing_key=*/"");
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), g_fixed_key_commitment_getter.get(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kResourceExhausted);
}
// Check that issuance fails if its key commitment request fails.
TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfKeyCommitmentFails) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
// Have the key commitment getter return nullptr, denoting that the key
// commitment fetch failed.
auto getter = std::make_unique<FixedKeyCommitmentGetter>(issuer, nullptr);
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/")));
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kFailedPrecondition);
}
// Reject if initializing the cryptography delegate fails.
TEST_F(TrustTokenRequestIssuanceHelperTest,
RejectsIfInitializingCryptographerFails) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
auto cryptographer = std::make_unique<MockCryptographer>();
EXPECT_CALL(*cryptographer, Initialize(_)).WillOnce(Return(false));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kInternalError);
}
// Reject if one of the keys in the commitment is malformed.
TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfAddingKeyFails) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
auto cryptographer = std::make_unique<MockCryptographer>();
EXPECT_CALL(*cryptographer, Initialize(_)).WillOnce(Return(true));
EXPECT_CALL(*cryptographer, AddKey(_)).WillOnce(Return(false));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kFailedPrecondition);
}
// Reject if there's an error getting blinded, unsigned tokens from BoringSSL.
TEST_F(TrustTokenRequestIssuanceHelperTest,
RejectsIfGettingBlindedTokensFails) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
auto cryptographer = std::make_unique<MockCryptographer>();
EXPECT_CALL(*cryptographer, Initialize(_)).WillOnce(Return(true));
EXPECT_CALL(*cryptographer, AddKey(_)).WillOnce(Return(true));
// Return nullopt, denoting an error, when the issuance helper requests
// blinded, unsigned tokens.
EXPECT_CALL(*cryptographer, BeginIssuance(_)).WillOnce(Return(base::nullopt));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
// This is an internal error because creating blinded tokens is a
// cryptographic operation not dependent on the inputs provided by the client
// or the protocol state.
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kInternalError);
}
// Check that the issuance helper sets the Sec-Trust-Token header on the
// outgoing request.
TEST_F(TrustTokenRequestIssuanceHelperTest, SetsRequestHeader) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
// The result of providing blinded, unsigned tokens should be the exact value
// of the Sec-Trust-Token header attached to the request.
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")));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
ASSERT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kOk);
std::string attached_header;
EXPECT_TRUE(request->extra_request_headers().GetHeader(
kTrustTokensSecTrustTokenHeader, &attached_header));
EXPECT_EQ(attached_header, "this string contains some blinded tokens");
}
// Check that the issuance helper sets the LOAD_BYPASS_CACHE flag on the
// outgoing request.
TEST_F(TrustTokenRequestIssuanceHelperTest, SetsLoadFlag) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
// The result of providing blinded, unsigned tokens should be the exact value
// of the Sec-Trust-Token header attached to the request.
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")));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
ASSERT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kOk);
EXPECT_TRUE(request->load_flags() & net::LOAD_BYPASS_CACHE);
}
// Check that the issuance helper rejects responses lacking the Sec-Trust-Token
// response header.
TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfResponseOmitsHeader) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
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")));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
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");
EXPECT_EQ(ExecuteFinalizeAndWaitForResult(&helper, response_head.get()),
mojom::TrustTokenOperationStatus::kBadResponse);
}
// Check that the issuance helper handles an issuance response rejected by the
// underlying cryptographic library.
TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsIfResponseIsUnusable) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
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")));
// Fail the "confirm issuance" step of validating the server's response
// within the underlying cryptographic library.
EXPECT_CALL(*cryptographer, ConfirmIssuance(_)).WillOnce(ReturnNull());
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
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::kBadResponse);
// Verify that Finalize correctly stripped the response header.
EXPECT_FALSE(
response_head->headers->HasHeader(kTrustTokensSecTrustTokenHeader));
}
// Check that, when preconditions are met and the underlying cryptographic steps
// successfully complete, the begin/finalize methods succeed.
TEST_F(TrustTokenRequestIssuanceHelperTest, Success) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
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::make_unique<UnblindedTokens>())));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
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);
// Verify that Finalize correctly stripped the response header.
EXPECT_FALSE(
response_head->headers->HasHeader(kTrustTokensSecTrustTokenHeader));
}
// Check that a successful Begin call associates the issuer with the issuance
// toplevel origin.
TEST_F(TrustTokenRequestIssuanceHelperTest, AssociatesIssuerWithToplevel) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New());
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
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")));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
ASSERT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kOk);
// After the operation has successfully begun, the issuer and the toplevel
// should be associated.
EXPECT_TRUE(store->IsAssociated(issuer, *SuitableTrustTokenOrigin::Create(
GURL("https://toplevel.com/"))));
}
// Check that a successful end-to-end Begin/Finalize flow stores the obtained
// trust tokens in the trust token store.
TEST_F(TrustTokenRequestIssuanceHelperTest, StoresObtainedTokens) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New("key", /*expiry=*/base::Time()));
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
// 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 = "key";
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)))));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
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")));
}
TEST_F(TrustTokenRequestIssuanceHelperTest, RejectsUnsuitableInsecureIssuer) {
auto store = TrustTokenStore::CreateForTesting();
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), g_fixed_key_commitment_getter.get(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("http://insecure-issuer.com/");
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kInvalidArgument);
}
TEST_F(TrustTokenRequestIssuanceHelperTest,
RejectsUnsuitableNonHttpNonHttpsIssuer) {
auto store = TrustTokenStore::CreateForTesting();
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), g_fixed_key_commitment_getter.get(),
std::make_unique<MockCryptographer>());
auto request = MakeURLRequest("file:///non-https-issuer.txt");
EXPECT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kInvalidArgument);
}
TEST_F(TrustTokenRequestIssuanceHelperTest, RespectsMaximumBatchsize) {
std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com/"));
auto key_commitment_result = mojom::TrustTokenKeyCommitmentResult::New();
key_commitment_result->keys.push_back(
mojom::TrustTokenVerificationKey::New("key", /*expiry=*/base::Time()));
key_commitment_result->batch_size =
static_cast<int>(kMaximumTrustTokenIssuanceBatchSize + 1);
auto getter = std::make_unique<FixedKeyCommitmentGetter>(
issuer, std::move(key_commitment_result));
auto cryptographer = std::make_unique<MockCryptographer>();
EXPECT_CALL(*cryptographer, Initialize(_)).WillOnce(Return(true));
EXPECT_CALL(*cryptographer, AddKey(_)).WillOnce(Return(true));
// The batch size should be clamped to the configured maximum.
EXPECT_CALL(*cryptographer,
BeginIssuance(kMaximumTrustTokenIssuanceBatchSize))
.WillOnce(
Return(std::string("this string contains some blinded tokens")));
TrustTokenRequestIssuanceHelper helper(
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com/")),
store.get(), getter.get(), std::move(cryptographer));
auto request = MakeURLRequest("https://issuer.com/");
request->set_initiator(issuer);
ASSERT_EQ(ExecuteBeginOperationAndWaitForResult(&helper, request.get()),
mojom::TrustTokenOperationStatus::kOk);
}
} // namespace network