1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "services/network/trust_tokens/trust_token_store.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/optional.h"
11 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
12 #include "services/network/public/cpp/is_potentially_trustworthy.h"
13 #include "services/network/public/cpp/trust_token_parameterization.h"
14 #include "services/network/public/mojom/trust_tokens.mojom-forward.h"
15 #include "services/network/trust_tokens/in_memory_trust_token_persister.h"
16 #include "services/network/trust_tokens/proto/public.pb.h"
17 #include "services/network/trust_tokens/proto/storage.pb.h"
18 #include "services/network/trust_tokens/suitable_trust_token_origin.h"
19 #include "services/network/trust_tokens/trust_token_key_commitment_getter.h"
20 #include "services/network/trust_tokens/trust_token_parameterization.h"
21 #include "services/network/trust_tokens/types.h"
22 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
23 #include "url/origin.h"
24
25 namespace network {
26
27 namespace {
28 class NeverExpiringExpiryDelegate
29 : public TrustTokenStore::RecordExpiryDelegate {
30 public:
IsRecordExpired(const TrustTokenRedemptionRecord & record,const SuitableTrustTokenOrigin & issuer)31 bool IsRecordExpired(const TrustTokenRedemptionRecord& record,
32 const SuitableTrustTokenOrigin& issuer) override {
33 return false;
34 }
35 };
36
37 } // namespace
38
TrustTokenStore(std::unique_ptr<TrustTokenPersister> persister,std::unique_ptr<RecordExpiryDelegate> expiry_delegate)39 TrustTokenStore::TrustTokenStore(
40 std::unique_ptr<TrustTokenPersister> persister,
41 std::unique_ptr<RecordExpiryDelegate> expiry_delegate)
42 : persister_(std::move(persister)),
43 record_expiry_delegate_(std::move(expiry_delegate)) {
44 DCHECK(persister_);
45 }
46
47 TrustTokenStore::~TrustTokenStore() = default;
48
CreateForTesting(std::unique_ptr<TrustTokenPersister> persister,std::unique_ptr<RecordExpiryDelegate> expiry_delegate)49 std::unique_ptr<TrustTokenStore> TrustTokenStore::CreateForTesting(
50 std::unique_ptr<TrustTokenPersister> persister,
51 std::unique_ptr<RecordExpiryDelegate> expiry_delegate) {
52 if (!persister)
53 persister = std::make_unique<InMemoryTrustTokenPersister>();
54 if (!expiry_delegate)
55 expiry_delegate = std::make_unique<NeverExpiringExpiryDelegate>();
56 return std::make_unique<TrustTokenStore>(std::move(persister),
57 std::move(expiry_delegate));
58 }
59
RecordIssuance(const SuitableTrustTokenOrigin & issuer)60 void TrustTokenStore::RecordIssuance(const SuitableTrustTokenOrigin& issuer) {
61 SuitableTrustTokenOrigin issuer_origin = issuer;
62 std::unique_ptr<TrustTokenIssuerConfig> config =
63 persister_->GetIssuerConfig(issuer);
64 if (!config)
65 config = std::make_unique<TrustTokenIssuerConfig>();
66 config->set_last_issuance(internal::TimeToString(base::Time::Now()));
67 persister_->SetIssuerConfig(issuer, std::move(config));
68 }
69
TimeSinceLastIssuance(const SuitableTrustTokenOrigin & issuer)70 base::Optional<base::TimeDelta> TrustTokenStore::TimeSinceLastIssuance(
71 const SuitableTrustTokenOrigin& issuer) {
72 std::unique_ptr<TrustTokenIssuerConfig> config =
73 persister_->GetIssuerConfig(issuer);
74 if (!config)
75 return base::nullopt;
76 if (!config->has_last_issuance())
77 return base::nullopt;
78 base::Optional<base::Time> maybe_last_issuance =
79 internal::StringToTime(config->last_issuance());
80 if (!maybe_last_issuance)
81 return base::nullopt;
82
83 base::TimeDelta ret = base::Time::Now() - *maybe_last_issuance;
84 if (ret < base::TimeDelta())
85 return base::nullopt;
86
87 return ret;
88 }
89
RecordRedemption(const SuitableTrustTokenOrigin & issuer,const SuitableTrustTokenOrigin & top_level)90 void TrustTokenStore::RecordRedemption(
91 const SuitableTrustTokenOrigin& issuer,
92 const SuitableTrustTokenOrigin& top_level) {
93 std::unique_ptr<TrustTokenIssuerToplevelPairConfig> config =
94 persister_->GetIssuerToplevelPairConfig(issuer, top_level);
95 if (!config)
96 config = std::make_unique<TrustTokenIssuerToplevelPairConfig>();
97 config->set_last_redemption(internal::TimeToString(base::Time::Now()));
98 persister_->SetIssuerToplevelPairConfig(issuer, top_level, std::move(config));
99 }
100
TimeSinceLastRedemption(const SuitableTrustTokenOrigin & issuer,const SuitableTrustTokenOrigin & top_level)101 base::Optional<base::TimeDelta> TrustTokenStore::TimeSinceLastRedemption(
102 const SuitableTrustTokenOrigin& issuer,
103 const SuitableTrustTokenOrigin& top_level) {
104 auto config = persister_->GetIssuerToplevelPairConfig(issuer, top_level);
105 if (!config)
106 return base::nullopt;
107 if (!config->has_last_redemption())
108 return base::nullopt;
109 base::Optional<base::Time> maybe_last_redemption =
110 internal::StringToTime(config->last_redemption());
111 // internal::StringToTime can fail in the case of data corruption (or writer
112 // error).
113 if (!maybe_last_redemption)
114 return base::nullopt;
115
116 base::TimeDelta ret = base::Time::Now() - *maybe_last_redemption;
117 if (ret < base::TimeDelta())
118 return base::nullopt;
119 return ret;
120 }
121
IsAssociated(const SuitableTrustTokenOrigin & issuer,const SuitableTrustTokenOrigin & top_level)122 bool TrustTokenStore::IsAssociated(const SuitableTrustTokenOrigin& issuer,
123 const SuitableTrustTokenOrigin& top_level) {
124 std::unique_ptr<TrustTokenToplevelConfig> config =
125 persister_->GetToplevelConfig(top_level);
126 if (!config)
127 return false;
128 return base::Contains(config->associated_issuers(), issuer.Serialize());
129 }
130
SetAssociation(const SuitableTrustTokenOrigin & issuer,const SuitableTrustTokenOrigin & top_level)131 bool TrustTokenStore::SetAssociation(
132 const SuitableTrustTokenOrigin& issuer,
133 const SuitableTrustTokenOrigin& top_level) {
134 std::unique_ptr<TrustTokenToplevelConfig> config =
135 persister_->GetToplevelConfig(top_level);
136 if (!config)
137 config = std::make_unique<TrustTokenToplevelConfig>();
138 auto string_issuer = issuer.Serialize();
139
140 if (base::Contains(config->associated_issuers(), string_issuer))
141 return true;
142
143 if (config->associated_issuers_size() >=
144 kTrustTokenPerToplevelMaxNumberOfAssociatedIssuers) {
145 return false;
146 }
147
148 config->add_associated_issuers(std::move(string_issuer));
149 persister_->SetToplevelConfig(top_level, std::move(config));
150
151 return true;
152 }
153
PruneStaleIssuerState(const SuitableTrustTokenOrigin & issuer,const std::vector<mojom::TrustTokenVerificationKeyPtr> & keys)154 void TrustTokenStore::PruneStaleIssuerState(
155 const SuitableTrustTokenOrigin& issuer,
156 const std::vector<mojom::TrustTokenVerificationKeyPtr>& keys) {
157 DCHECK([&keys]() {
158 std::set<base::StringPiece> unique_keys;
159 for (const auto& key : keys)
160 unique_keys.insert(base::StringPiece(key->body));
161 return unique_keys.size() == keys.size();
162 }());
163
164 std::unique_ptr<TrustTokenIssuerConfig> config =
165 persister_->GetIssuerConfig(issuer);
166 if (!config)
167 config = std::make_unique<TrustTokenIssuerConfig>();
168
169 google::protobuf::RepeatedPtrField<TrustToken> filtered_tokens;
170 for (auto& token : *config->mutable_tokens()) {
171 if (std::any_of(keys.begin(), keys.end(),
172 [&token](const mojom::TrustTokenVerificationKeyPtr& key) {
173 return key->body == token.signing_key();
174 }))
175 *filtered_tokens.Add() = std::move(token);
176 }
177
178 config->mutable_tokens()->Swap(&filtered_tokens);
179
180 persister_->SetIssuerConfig(issuer, std::move(config));
181 }
182
AddTokens(const SuitableTrustTokenOrigin & issuer,base::span<const std::string> token_bodies,base::StringPiece issuing_key)183 void TrustTokenStore::AddTokens(const SuitableTrustTokenOrigin& issuer,
184 base::span<const std::string> token_bodies,
185 base::StringPiece issuing_key) {
186 auto config = persister_->GetIssuerConfig(issuer);
187 if (!config)
188 config = std::make_unique<TrustTokenIssuerConfig>();
189
190 for (auto it = token_bodies.begin();
191 it != token_bodies.end() &&
192 config->tokens_size() < kTrustTokenPerIssuerTokenCapacity;
193 ++it) {
194 TrustToken* entry = config->add_tokens();
195 entry->set_body(*it);
196 entry->set_signing_key(std::string(issuing_key));
197 }
198
199 persister_->SetIssuerConfig(issuer, std::move(config));
200 }
201
CountTokens(const SuitableTrustTokenOrigin & issuer)202 int TrustTokenStore::CountTokens(const SuitableTrustTokenOrigin& issuer) {
203 auto config = persister_->GetIssuerConfig(issuer);
204 if (!config)
205 return 0;
206 return config->tokens_size();
207 }
208
RetrieveMatchingTokens(const SuitableTrustTokenOrigin & issuer,base::RepeatingCallback<bool (const std::string &)> key_matcher)209 std::vector<TrustToken> TrustTokenStore::RetrieveMatchingTokens(
210 const SuitableTrustTokenOrigin& issuer,
211 base::RepeatingCallback<bool(const std::string&)> key_matcher) {
212 auto config = persister_->GetIssuerConfig(issuer);
213 std::vector<TrustToken> matching_tokens;
214 if (!config)
215 return matching_tokens;
216
217 std::copy_if(config->tokens().begin(), config->tokens().end(),
218 std::back_inserter(matching_tokens),
219 [&key_matcher](const TrustToken& token) {
220 return token.has_signing_key() &&
221 key_matcher.Run(token.signing_key());
222 });
223
224 return matching_tokens;
225 }
226
DeleteToken(const SuitableTrustTokenOrigin & issuer,const TrustToken & to_delete)227 void TrustTokenStore::DeleteToken(const SuitableTrustTokenOrigin& issuer,
228 const TrustToken& to_delete) {
229 auto config = persister_->GetIssuerConfig(issuer);
230 if (!config)
231 return;
232
233 for (auto it = config->mutable_tokens()->begin();
234 it != config->mutable_tokens()->end(); ++it) {
235 if (it->body() == to_delete.body()) {
236 config->mutable_tokens()->erase(it);
237 break;
238 }
239 }
240
241 persister_->SetIssuerConfig(issuer, std::move(config));
242 }
243
SetRedemptionRecord(const SuitableTrustTokenOrigin & issuer,const SuitableTrustTokenOrigin & top_level,const TrustTokenRedemptionRecord & record)244 void TrustTokenStore::SetRedemptionRecord(
245 const SuitableTrustTokenOrigin& issuer,
246 const SuitableTrustTokenOrigin& top_level,
247 const TrustTokenRedemptionRecord& record) {
248 auto config = persister_->GetIssuerToplevelPairConfig(issuer, top_level);
249 if (!config)
250 config = std::make_unique<TrustTokenIssuerToplevelPairConfig>();
251 *config->mutable_redemption_record() = record;
252 persister_->SetIssuerToplevelPairConfig(issuer, top_level, std::move(config));
253 }
254
255 base::Optional<TrustTokenRedemptionRecord>
RetrieveNonstaleRedemptionRecord(const SuitableTrustTokenOrigin & issuer,const SuitableTrustTokenOrigin & top_level)256 TrustTokenStore::RetrieveNonstaleRedemptionRecord(
257 const SuitableTrustTokenOrigin& issuer,
258 const SuitableTrustTokenOrigin& top_level) {
259 auto config = persister_->GetIssuerToplevelPairConfig(issuer, top_level);
260 if (!config)
261 return base::nullopt;
262
263 if (!config->has_redemption_record())
264 return base::nullopt;
265
266 if (record_expiry_delegate_->IsRecordExpired(config->redemption_record(),
267 issuer))
268 return base::nullopt;
269
270 return config->redemption_record();
271 }
272
ClearDataForFilter(mojom::ClearDataFilterPtr filter)273 bool TrustTokenStore::ClearDataForFilter(mojom::ClearDataFilterPtr filter) {
274 if (!filter) {
275 return persister_->DeleteForOrigins(base::BindRepeating(
276 [](const SuitableTrustTokenOrigin&) { return true; }));
277 }
278
279 // Returns whether |storage_key|'s data should be deleted, based on the logic
280 // |filter| specifies. (Default to deleting everything, because a null
281 // |filter| is a wildcard.)
282 auto matcher = base::BindRepeating(
283 [](const mojom::ClearDataFilter& filter,
284 const SuitableTrustTokenOrigin& storage_key) -> bool {
285 // Match an origin if
286 // - it is an eTLD+1 (aka "domain and registry") match with anything
287 // on |filter|'s domain list, or
288 // - it is an origin match with anything on |filter|'s origin list.
289 bool is_match = base::Contains(filter.origins, storage_key.origin());
290
291 // Computing the domain might be a little expensive, so
292 // skip it if we know for sure the origin is a match because it
293 // matches the origin list.
294 if (!is_match) {
295 std::string etld1_for_origin =
296 net::registry_controlled_domains::GetDomainAndRegistry(
297 storage_key.origin(),
298 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
299 is_match = base::Contains(filter.domains, etld1_for_origin);
300 }
301
302 switch (filter.type) {
303 case mojom::ClearDataFilter::Type::KEEP_MATCHES:
304 return !is_match;
305 case mojom::ClearDataFilter::Type::DELETE_MATCHES:
306 return is_match;
307 }
308 },
309 *filter);
310
311 return persister_->DeleteForOrigins(std::move(matcher));
312 }
313
314 } // namespace network
315