[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 1 | // Copyright 2014 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 | |
blundell | 0b2305c | 2015-08-25 15:54:42 | [diff] [blame] | 5 | #ifndef COMPONENTS_VARIATIONS_VARIATIONS_SEED_STORE_H_ |
| 6 | #define COMPONENTS_VARIATIONS_VARIATIONS_SEED_STORE_H_ |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 7 | |
Paul Miller | 49eebb8 | 2018-06-14 04:11:08 | [diff] [blame] | 8 | #include <memory> |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 9 | #include <string> |
| 10 | |
Paul Miller | 33a495e | 2018-09-29 04:35:02 | [diff] [blame] | 11 | #include "base/callback.h" |
Scott Violet | 69a5d8dd | 2021-06-01 23:46:48 | [diff] [blame] | 12 | #include "base/component_export.h" |
[email protected] | c69b6c1 | 2014-03-07 20:32:36 | [diff] [blame] | 13 | #include "base/gtest_prod_util.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 14 | #include "base/memory/raw_ptr.h" |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 15 | #include "base/time/time.h" |
avi | 5dd91f8 | 2015-12-25 22:30:46 | [diff] [blame] | 16 | #include "build/build_config.h" |
isherman | 5f556b51 | 2017-06-14 16:27:23 | [diff] [blame] | 17 | #include "components/variations/metrics.h" |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 18 | #include "components/variations/proto/variations_seed.pb.h" |
Paul Miller | 49eebb8 | 2018-06-14 04:11:08 | [diff] [blame] | 19 | #include "components/variations/seed_response.h" |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 20 | |
| 21 | class PrefService; |
| 22 | class PrefRegistrySimple; |
| 23 | |
[email protected] | 59b6f67 | 2014-07-26 18:35:47 | [diff] [blame] | 24 | namespace variations { |
[email protected] | 59b6f67 | 2014-07-26 18:35:47 | [diff] [blame] | 25 | |
Ilya Sherman | 3231a20 | 2018-01-13 05:15:01 | [diff] [blame] | 26 | struct ClientFilterableState; |
isherman | b1917aabe | 2017-06-13 23:35:14 | [diff] [blame] | 27 | class VariationsSeed; |
[email protected] | 24afce1 | 2014-07-25 21:00:31 | [diff] [blame] | 28 | |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 29 | // A seed that has passed validation. |
| 30 | struct ValidatedSeed { |
| 31 | // The serialized VariationsSeed bytes. |
| 32 | std::string bytes; |
| 33 | // A cryptographic signature on the seed_data. |
| 34 | std::string base64_seed_signature; |
| 35 | // The seed data parsed as a proto. |
| 36 | VariationsSeed parsed; |
| 37 | }; |
| 38 | |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 39 | // VariationsSeedStore is a helper class for reading and writing the variations |
| 40 | // seed from Local State. |
Scott Violet | 69a5d8dd | 2021-06-01 23:46:48 | [diff] [blame] | 41 | class COMPONENT_EXPORT(VARIATIONS) VariationsSeedStore { |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 42 | public: |
Adrian Taylor | 2613db7 | 2020-05-20 20:26:55 | [diff] [blame] | 43 | // Standard constructor. Enables signature verification. |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 44 | explicit VariationsSeedStore(PrefService* local_state); |
Paul Miller | 49eebb8 | 2018-06-14 04:11:08 | [diff] [blame] | 45 | // |initial_seed| may be null. If not null, then it will be stored in this |
| 46 | // seed store. This is used by Android Chrome to supply the first run seed, |
| 47 | // and by Android WebView to supply the seed on every run. |
Adrian Taylor | 2613db7 | 2020-05-20 20:26:55 | [diff] [blame] | 48 | // |signature_verification_enabled| can be used in unit tests to disable |
Nate Fischer | 0d7bb6db | 2020-10-05 21:49:48 | [diff] [blame] | 49 | // signature checks on the seed. If |use_first_run_prefs| is true (default), |
| 50 | // then this VariationsSeedStore may modify the Java SharedPreferences ("first |
| 51 | // run prefs") which are set during first run; otherwise this will not access |
| 52 | // SharedPreferences at all. |
Paul Miller | 49eebb8 | 2018-06-14 04:11:08 | [diff] [blame] | 53 | VariationsSeedStore(PrefService* local_state, |
Paul Miller | 33a495e | 2018-09-29 04:35:02 | [diff] [blame] | 54 | std::unique_ptr<SeedResponse> initial_seed, |
Nate Fischer | 0d7bb6db | 2020-10-05 21:49:48 | [diff] [blame] | 55 | bool signature_verification_enabled, |
| 56 | bool use_first_run_prefs = true); |
Peter Boström | 09c0182 | 2021-09-20 22:43:27 | [diff] [blame] | 57 | |
| 58 | VariationsSeedStore(const VariationsSeedStore&) = delete; |
| 59 | VariationsSeedStore& operator=(const VariationsSeedStore&) = delete; |
| 60 | |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 61 | virtual ~VariationsSeedStore(); |
| 62 | |
Ilya Sherman | 3231a20 | 2018-01-13 05:15:01 | [diff] [blame] | 63 | // Loads the variations seed data from local state into |seed|, as well as the |
| 64 | // raw pref values into |seed_data| and |base64_signature|. If there is a |
| 65 | // problem with loading, clears the seed pref value and returns false. If |
| 66 | // successful, fills the the outparams with the loaded data and returns true. |
Ilya Sherman | 9534f1d | 2018-01-24 04:02:20 | [diff] [blame] | 67 | // Virtual for testing. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 68 | [[nodiscard]] virtual bool LoadSeed(VariationsSeed* seed, |
| 69 | std::string* seed_data, |
| 70 | std::string* base64_seed_signature); |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 71 | |
asvitkine | b24f4559 | 2015-08-07 02:57:25 | [diff] [blame] | 72 | // Stores the given seed |data| (serialized protobuf) to local state, along |
[email protected] | 54af732e | 2014-01-23 22:20:39 | [diff] [blame] | 73 | // with a base64-encoded digital signature for seed and the date when it was |
veranika | f6acefe76 | 2015-10-19 16:37:56 | [diff] [blame] | 74 | // fetched. If |is_gzip_compressed| is true, treats |data| as being gzip |
| 75 | // compressed and decompresses it before any other processing. |
| 76 | // If |is_delta_compressed| is true, treats |data| as being delta |
asvitkine | b24f4559 | 2015-08-07 02:57:25 | [diff] [blame] | 77 | // compressed and attempts to decode it first using the store's seed data. |
| 78 | // The actual seed data will be base64 encoded for storage. If the string |
| 79 | // is invalid, the existing prefs are untouched and false is returned. |
| 80 | // Additionally, stores the |country_code| that was received with the seed in |
| 81 | // a separate pref. On success and if |parsed_seed| is not NULL, |parsed_seed| |
| 82 | // will be filled with the de-serialized decoded protobuf. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 83 | [[nodiscard]] bool StoreSeedData(const std::string& data, |
| 84 | const std::string& base64_seed_signature, |
| 85 | const std::string& country_code, |
| 86 | const base::Time& date_fetched, |
| 87 | bool is_delta_compressed, |
| 88 | bool is_gzip_compressed, |
| 89 | VariationsSeed* parsed_seed); |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 90 | |
Caitlin Fischer | 4dc0babf | 2021-07-23 03:20:57 | [diff] [blame] | 91 | // Loads the safe variations seed data from local state into |seed| and |
Caitlin Fischer | c1a31b1 | 2022-02-08 19:40:32 | [diff] [blame] | 92 | // updates any relevant fields in |client_state|. Returns true iff the safe |
| 93 | // seed was read successfully from prefs. If the safe seed could not be |
| 94 | // loaded, it is guaranteed that no fields in |client_state| are modified. |
Caitlin Fischer | 4700f75 | 2021-06-29 02:20:11 | [diff] [blame] | 95 | // |
| 96 | // Side effect: Upon failing to read or validate the safe seed, clears all |
| 97 | // of the safe seed pref values. |
| 98 | // |
Ilya Sherman | 9534f1d | 2018-01-24 04:02:20 | [diff] [blame] | 99 | // Virtual for testing. |
Caitlin Fischer | c1a31b1 | 2022-02-08 19:40:32 | [diff] [blame] | 100 | [[nodiscard]] virtual bool LoadSafeSeed(VariationsSeed* seed, |
| 101 | ClientFilterableState* client_state); |
Ilya Sherman | e3b22a6 | 2018-01-22 06:10:21 | [diff] [blame] | 102 | |
Ilya Sherman | 3231a20 | 2018-01-13 05:15:01 | [diff] [blame] | 103 | // Stores the given |seed_data| (a serialized protobuf) to local state as a |
| 104 | // safe seed, along with a base64-encoded digital signature for seed and any |
| 105 | // additional client metadata relevant to the safe seed. Returns true on |
| 106 | // success or false on failure; no prefs are updated in case of failure. |
| 107 | // Virtual for testing. |
| 108 | virtual bool StoreSafeSeed(const std::string& seed_data, |
| 109 | const std::string& base64_seed_signature, |
Caitlin Fischer | bf634b6 | 2021-10-30 00:07:22 | [diff] [blame] | 110 | int seed_milestone, |
Ilya Sherman | 279eff3 | 2018-01-31 04:28:32 | [diff] [blame] | 111 | const ClientFilterableState& client_state, |
| 112 | base::Time seed_fetch_time); |
| 113 | |
| 114 | // Loads the last fetch time (for the latest seed) that was persisted to the |
| 115 | // store. |
| 116 | base::Time GetLastFetchTime() const; |
| 117 | |
Caitlin Fischer | 4dc0babf | 2021-07-23 03:20:57 | [diff] [blame] | 118 | // Returns the time at which the safe seed was fetched. |
| 119 | base::Time GetSafeSeedFetchTime() const; |
| 120 | |
Robbie McElrath | 27890f1 | 2019-10-16 17:25:59 | [diff] [blame] | 121 | // Records |fetch_time| as the last time at which a seed was fetched |
Ilya Sherman | 279eff3 | 2018-01-31 04:28:32 | [diff] [blame] | 122 | // successfully. Also updates the safe seed's fetch time if the latest and |
| 123 | // safe seeds are identical. |
Robbie McElrath | 27890f1 | 2019-10-16 17:25:59 | [diff] [blame] | 124 | void RecordLastFetchTime(base::Time fetch_time); |
Ilya Sherman | 3231a20 | 2018-01-13 05:15:01 | [diff] [blame] | 125 | |
[email protected] | 344c623a | 2014-03-11 20:29:39 | [diff] [blame] | 126 | // Updates |kVariationsSeedDate| and logs when previous date was from a |
| 127 | // different day. |
| 128 | void UpdateSeedDateAndLogDayChange(const base::Time& server_date_fetched); |
| 129 | |
Ilya Sherman | c70363ec | 2018-01-17 05:51:57 | [diff] [blame] | 130 | // Returns the serial number of the most recently received seed, or an empty |
| 131 | // string if there is no seed (or if it could not be read). |
| 132 | // Side-effect: If there is a failure while attempting to read the latest seed |
| 133 | // from prefs, clears the prefs associated with the seed. |
| 134 | // Efficiency note: If code will eventually need to load the latest seed, it's |
| 135 | // more efficient to call LoadSeed() prior to calling this method. |
| 136 | const std::string& GetLatestSerialNumber(); |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 137 | |
asvitkine | b24f4559 | 2015-08-07 02:57:25 | [diff] [blame] | 138 | // Registers Local State prefs used by this class. |
| 139 | static void RegisterPrefs(PrefRegistrySimple* registry); |
| 140 | |
Kyle Milka | 3aababd | 2017-07-20 19:01:32 | [diff] [blame] | 141 | PrefService* local_state() { return local_state_; } |
Kyle Milka | 3aababd | 2017-07-20 19:01:32 | [diff] [blame] | 142 | const PrefService* local_state() const { return local_state_; } |
| 143 | |
Roger McFarlane | 3da6db273 | 2021-10-06 13:41:04 | [diff] [blame] | 144 | static VerifySignatureResult VerifySeedSignatureForTesting( |
| 145 | const std::string& seed_bytes, |
| 146 | const std::string& base64_seed_signature); |
| 147 | |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 148 | private: |
[email protected] | c69b6c1 | 2014-03-07 20:32:36 | [diff] [blame] | 149 | FRIEND_TEST_ALL_PREFIXES(VariationsSeedStoreTest, VerifySeedSignature); |
asvitkine | b24f4559 | 2015-08-07 02:57:25 | [diff] [blame] | 150 | FRIEND_TEST_ALL_PREFIXES(VariationsSeedStoreTest, ApplyDeltaPatch); |
[email protected] | c69b6c1 | 2014-03-07 20:32:36 | [diff] [blame] | 151 | |
Ilya Sherman | 3231a20 | 2018-01-13 05:15:01 | [diff] [blame] | 152 | // The seed store contains two distinct seeds: |
| 153 | // (1) The most recently fetched, or "latest", seed; and |
| 154 | // (2) A "safe" seed, which has been observed to keep Chrome in a basically |
| 155 | // functional state. In particular, a safe seed is one that allows |
| 156 | // Chrome to receive new seed updates from the server. |
| 157 | // Note that it's possible for both seeds to be empty, and it's possible for |
| 158 | // the two seeds to be identical in their contents. |
| 159 | enum class SeedType { |
| 160 | LATEST, |
| 161 | SAFE, |
| 162 | }; |
| 163 | |
Ilya Sherman | e3b22a6 | 2018-01-22 06:10:21 | [diff] [blame] | 164 | // Clears all prefs related to variations seed storage for the specified seed |
| 165 | // type. |
| 166 | void ClearPrefs(SeedType seed_type); |
[email protected] | 54af732e | 2014-01-23 22:20:39 | [diff] [blame] | 167 | |
Xiaohan Wang | d30b05ae | 2022-01-14 19:51:28 | [diff] [blame] | 168 | #if BUILDFLAG(IS_ANDROID) |
Paul Miller | 49eebb8 | 2018-06-14 04:11:08 | [diff] [blame] | 169 | // Imports the variations seed from the Java side. Logs UMA on failure. |
| 170 | // Android Chrome uses this on first run; WebView uses this on every startup. |
| 171 | // In Chrome's case, it's important to set the first run seed as soon as |
| 172 | // possible, because some clients query the seed store prefs directly rather |
| 173 | // than accessing them via the seed store API: https://crbug.com/829527 |
| 174 | void ImportInitialSeed(std::unique_ptr<SeedResponse> initial_seed); |
Xiaohan Wang | d30b05ae | 2022-01-14 19:51:28 | [diff] [blame] | 175 | #endif // BUILDFLAG(IS_ANDROID) |
agulenko | 25d2111a | 2015-11-05 05:16:16 | [diff] [blame] | 176 | |
Ilya Sherman | e3b22a6 | 2018-01-22 06:10:21 | [diff] [blame] | 177 | // Loads the variations seed data from local state into |seed|, as well as the |
| 178 | // raw pref values into |seed_data| and |base64_signature|. Loads either the |
| 179 | // safe seed or the latest seed, according to the |seed_type|. Returns whether |
| 180 | // loading the seed was successful. |
| 181 | // Side-effect: Upon any failure to read or validate the safe seed, clears all |
| 182 | // of the pref values for the seed. This occurs iff the method returns false. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 183 | [[nodiscard]] LoadSeedResult LoadSeedImpl(SeedType seed_type, |
| 184 | VariationsSeed* seed, |
| 185 | std::string* seed_data, |
| 186 | std::string* base64_seed_signature); |
Ilya Sherman | e3b22a6 | 2018-01-22 06:10:21 | [diff] [blame] | 187 | |
isherman | 5f556b51 | 2017-06-14 16:27:23 | [diff] [blame] | 188 | // Reads the variations seed data from prefs into |seed_data|, and returns the |
| 189 | // result of the load. The value stored into |seed_data| should only be used |
Ilya Sherman | e3b22a6 | 2018-01-22 06:10:21 | [diff] [blame] | 190 | // if the result is SUCCESS. Reads either the latest or the safe seed, |
| 191 | // according to the specified |seed_type|. |
isherman | 5f556b51 | 2017-06-14 16:27:23 | [diff] [blame] | 192 | // Side-effect: If the read fails, clears the prefs associated with the seed. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 193 | [[nodiscard]] LoadSeedResult ReadSeedData(SeedType seed_type, |
| 194 | std::string* seed_data); |
asvitkine | 5ea32c5 | 2015-02-12 01:23:11 | [diff] [blame] | 195 | |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 196 | // Resolves a |delta_bytes| against the latest seed. |
| 197 | // Returns success or an error, populating |seed_bytes| on success. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 198 | [[nodiscard]] StoreSeedResult ResolveDelta(const std::string& delta_bytes, |
| 199 | std::string* seed_bytes); |
asvitkine | b24f4559 | 2015-08-07 02:57:25 | [diff] [blame] | 200 | |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 201 | // Resolves instance manipulations applied to received data. |
| 202 | // Returns success or an error, populating |seed_bytes| on success. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 203 | [[nodiscard]] StoreSeedResult ResolveInstanceManipulations( |
| 204 | const std::string& data, |
| 205 | const InstanceManipulations& im, |
| 206 | std::string* seed_bytes); |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 207 | |
| 208 | // Validates that |seed_bytes| parses and matches |base64_seed_signature|. |
| 209 | // Signature checking may be disabled via |signature_verification_enabled_|. |
| 210 | // |seed_type| indicates the source of the seed for logging purposes. |
| 211 | // |result| must be non-null, and will be populated on success. |
| 212 | // Returns success or some error value. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 213 | [[nodiscard]] StoreSeedResult ValidateSeedBytes( |
| 214 | const std::string& seed_bytes, |
| 215 | const std::string& base64_seed_signature, |
| 216 | SeedType seed_type, |
| 217 | ValidatedSeed* result); |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 218 | |
| 219 | // Gzip compresses and base64 encodes a validated seed. |
| 220 | // Returns success or error and populates base64_seed_data on success. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 221 | [[nodiscard]] StoreSeedResult CompressSeedBytes( |
| 222 | const ValidatedSeed& validated, |
| 223 | std::string* base64_seed_data); |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 224 | |
| 225 | // Updates the latest seed with validated data. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 226 | [[nodiscard]] StoreSeedResult StoreValidatedSeed( |
| 227 | const ValidatedSeed& seed, |
| 228 | const std::string& country_code, |
| 229 | const base::Time& date_fetched); |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 230 | |
| 231 | // Updates the safe seed with validated data. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 232 | [[nodiscard]] StoreSeedResult StoreValidatedSafeSeed( |
Caitlin Fischer | bf634b6 | 2021-10-30 00:07:22 | [diff] [blame] | 233 | const ValidatedSeed& seed, |
| 234 | int seed_milestone, |
Steven Holte | 75115332e | 2021-08-09 23:55:08 | [diff] [blame] | 235 | const ClientFilterableState& client_state, |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 236 | base::Time seed_fetch_time); |
Ilya Sherman | 3231a20 | 2018-01-13 05:15:01 | [diff] [blame] | 237 | |
asvitkine | b24f4559 | 2015-08-07 02:57:25 | [diff] [blame] | 238 | // Applies a delta-compressed |patch| to |existing_data|, producing the result |
| 239 | // in |output|. Returns whether the operation was successful. |
Daniel Cheng | 5cda1cf3 | 2022-01-13 23:33:43 | [diff] [blame] | 240 | [[nodiscard]] static bool ApplyDeltaPatch(const std::string& existing_data, |
| 241 | const std::string& patch, |
| 242 | std::string* output); |
asvitkine | b24f4559 | 2015-08-07 02:57:25 | [diff] [blame] | 243 | |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 244 | // The pref service used to persist the variations seed. |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 245 | raw_ptr<PrefService> local_state_; |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 246 | |
| 247 | // Cached serial number from the most recently fetched variations seed. |
Ilya Sherman | c70363ec | 2018-01-17 05:51:57 | [diff] [blame] | 248 | std::string latest_serial_number_; |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 249 | |
Caitlin Fischer | 6920a08 | 2021-06-29 05:25:24 | [diff] [blame] | 250 | // Whether to validate signatures on the seed. Always on except in unit tests. |
Nate Fischer | 0d7bb6db | 2020-10-05 21:49:48 | [diff] [blame] | 251 | const bool signature_verification_enabled_; |
| 252 | |
| 253 | // Whether this may read or write to Java "first run" SharedPreferences. |
| 254 | const bool use_first_run_prefs_; |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 255 | }; |
| 256 | |
blundell | 57bcfed | 2015-09-04 08:44:45 | [diff] [blame] | 257 | } // namespace variations |
[email protected] | 1df24d0a | 2014-01-20 21:29:59 | [diff] [blame] | 258 | |
blundell | 0b2305c | 2015-08-25 15:54:42 | [diff] [blame] | 259 | #endif // COMPONENTS_VARIATIONS_VARIATIONS_SEED_STORE_H_ |