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
5 #include "chrome/browser/metrics/variations/variations_seed_store.h"
6
7 #include "base/base64.h"
8 #include "base/metrics/histogram.h"
9 #include "base/prefs/pref_registry_simple.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/sha1.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/common/pref_names.h"
14 #include "components/variations/proto/variations_seed.pb.h"
15 #include "crypto/signature_verifier.h"
16
17 namespace chrome_variations {
18
19 namespace {
20
21 // Signature verification is disabled on mobile platforms for now, since it
22 // adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop).
SignatureVerificationEnabled()23 bool SignatureVerificationEnabled() {
24 #if defined(OS_IOS) || defined(OS_ANDROID)
25 return false;
26 #else
27 return true;
28 #endif
29 }
30
31 // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
32 // RFC 5758:
33 // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
34 // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
35 // ...
36 // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
37 // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
38 // as an AlgorithmIdentifier, the encoding MUST omit the parameters
39 // field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
40 // component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
41 // SHA384, or ecdsa-with-SHA512.
42 // See also RFC 5480, Appendix A.
43 const uint8 kECDSAWithSHA256AlgorithmID[] = {
44 0x30, 0x0a,
45 0x06, 0x08,
46 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
47 };
48
49 // The ECDSA public key of the variations server for verifying variations seed
50 // signatures.
51 const uint8_t kPublicKey[] = {
52 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
53 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
54 0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43,
55 0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72,
56 0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b,
57 0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15,
58 0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf,
59 };
60
61 // Note: UMA histogram enum - don't re-order or remove entries.
62 enum VariationSeedEmptyState {
63 VARIATIONS_SEED_NOT_EMPTY,
64 VARIATIONS_SEED_EMPTY,
65 VARIATIONS_SEED_CORRUPT,
66 VARIATIONS_SEED_INVALID_SIGNATURE,
67 VARIATIONS_SEED_EMPTY_ENUM_SIZE,
68 };
69
RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state)70 void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state) {
71 UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state,
72 VARIATIONS_SEED_EMPTY_ENUM_SIZE);
73 }
74
75 // Note: UMA histogram enum - don't re-order or remove entries.
76 enum VariationsSeedDateChangeState {
77 SEED_DATE_NO_OLD_DATE,
78 SEED_DATE_NEW_DATE_OLDER,
79 SEED_DATE_SAME_DAY,
80 SEED_DATE_NEW_DAY,
81 SEED_DATE_ENUM_SIZE,
82 };
83
84 // Truncates a time to the start of the day in UTC. If given a time representing
85 // 2014-03-11 10:18:03.1 UTC, it will return a time representing
86 // 2014-03-11 00:00:00.0 UTC.
TruncateToUTCDay(const base::Time & time)87 base::Time TruncateToUTCDay(const base::Time& time) {
88 base::Time::Exploded exploded;
89 time.UTCExplode(&exploded);
90 exploded.hour = 0;
91 exploded.minute = 0;
92 exploded.second = 0;
93 exploded.millisecond = 0;
94
95 return base::Time::FromUTCExploded(exploded);
96 }
97
GetSeedDateChangeState(const base::Time & server_seed_date,const base::Time & stored_seed_date)98 VariationsSeedDateChangeState GetSeedDateChangeState(
99 const base::Time& server_seed_date,
100 const base::Time& stored_seed_date) {
101 if (server_seed_date < stored_seed_date)
102 return SEED_DATE_NEW_DATE_OLDER;
103
104 if (TruncateToUTCDay(server_seed_date) !=
105 TruncateToUTCDay(stored_seed_date)) {
106 // The server date is earlier than the stored date, and they are from
107 // different UTC days, so |server_seed_date| is a valid new day.
108 return SEED_DATE_NEW_DAY;
109 }
110 return SEED_DATE_SAME_DAY;
111 }
112
113 } // namespace
114
VariationsSeedStore(PrefService * local_state)115 VariationsSeedStore::VariationsSeedStore(PrefService* local_state)
116 : local_state_(local_state) {
117 }
118
~VariationsSeedStore()119 VariationsSeedStore::~VariationsSeedStore() {
120 }
121
LoadSeed(variations::VariationsSeed * seed)122 bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) {
123 const std::string base64_seed_data =
124 local_state_->GetString(prefs::kVariationsSeed);
125 if (base64_seed_data.empty()) {
126 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY);
127 return false;
128 }
129
130 // If the decode process fails, assume the pref value is corrupt and clear it.
131 std::string seed_data;
132 if (!base::Base64Decode(base64_seed_data, &seed_data) ||
133 !seed->ParseFromString(seed_data)) {
134 VLOG(1) << "Variations seed data in local pref is corrupt, clearing the "
135 << "pref.";
136 ClearPrefs();
137 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT);
138 return false;
139 }
140
141 const std::string base64_seed_signature =
142 local_state_->GetString(prefs::kVariationsSeedSignature);
143 const VerifySignatureResult result =
144 VerifySeedSignature(seed_data, base64_seed_signature);
145 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
146 UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result,
147 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
148 if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
149 VLOG(1) << "Variations seed signature in local pref missing or invalid "
150 << "with result: " << result << ". Clearing the pref.";
151 ClearPrefs();
152 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_INVALID_SIGNATURE);
153 return false;
154 }
155 }
156
157 variations_serial_number_ = seed->serial_number();
158 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY);
159 return true;
160 }
161
StoreSeedData(const std::string & seed_data,const std::string & base64_seed_signature,const base::Time & date_fetched,variations::VariationsSeed * parsed_seed)162 bool VariationsSeedStore::StoreSeedData(
163 const std::string& seed_data,
164 const std::string& base64_seed_signature,
165 const base::Time& date_fetched,
166 variations::VariationsSeed* parsed_seed) {
167 if (seed_data.empty()) {
168 VLOG(1) << "Variations seed data is empty, rejecting the seed.";
169 return false;
170 }
171
172 // Only store the seed data if it parses correctly.
173 variations::VariationsSeed seed;
174 if (!seed.ParseFromString(seed_data)) {
175 VLOG(1) << "Variations seed data is not in valid proto format, "
176 << "rejecting the seed.";
177 return false;
178 }
179
180 const VerifySignatureResult result =
181 VerifySeedSignature(seed_data, base64_seed_signature);
182 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
183 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result,
184 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
185 if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
186 VLOG(1) << "Variations seed signature missing or invalid with result: "
187 << result << ". Rejecting the seed.";
188 return false;
189 }
190 }
191
192 std::string base64_seed_data;
193 base::Base64Encode(seed_data, &base64_seed_data);
194
195 // TODO(asvitkine): This pref is no longer being used. Remove it completely
196 // in a couple of releases.
197 local_state_->ClearPref(prefs::kVariationsSeedHash);
198
199 local_state_->SetString(prefs::kVariationsSeed, base64_seed_data);
200 UpdateSeedDateAndLogDayChange(date_fetched);
201 local_state_->SetString(prefs::kVariationsSeedSignature,
202 base64_seed_signature);
203 variations_serial_number_ = seed.serial_number();
204 if (parsed_seed)
205 seed.Swap(parsed_seed);
206
207 return true;
208 }
209
UpdateSeedDateAndLogDayChange(const base::Time & server_date_fetched)210 void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
211 const base::Time& server_date_fetched) {
212 VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE;
213
214 if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) {
215 const int64 stored_date_value =
216 local_state_->GetInt64(prefs::kVariationsSeedDate);
217 const base::Time stored_date =
218 base::Time::FromInternalValue(stored_date_value);
219
220 date_change = GetSeedDateChangeState(server_date_fetched, stored_date);
221 }
222
223 UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change,
224 SEED_DATE_ENUM_SIZE);
225
226 local_state_->SetInt64(prefs::kVariationsSeedDate,
227 server_date_fetched.ToInternalValue());
228 }
229
230 // static
RegisterPrefs(PrefRegistrySimple * registry)231 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {
232 registry->RegisterStringPref(prefs::kVariationsSeed, std::string());
233 registry->RegisterStringPref(prefs::kVariationsSeedHash, std::string());
234 registry->RegisterInt64Pref(prefs::kVariationsSeedDate,
235 base::Time().ToInternalValue());
236 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string());
237 }
238
ClearPrefs()239 void VariationsSeedStore::ClearPrefs() {
240 local_state_->ClearPref(prefs::kVariationsSeed);
241 local_state_->ClearPref(prefs::kVariationsSeedDate);
242 local_state_->ClearPref(prefs::kVariationsSeedHash);
243 local_state_->ClearPref(prefs::kVariationsSeedSignature);
244 }
245
246 VariationsSeedStore::VerifySignatureResult
VerifySeedSignature(const std::string & seed_bytes,const std::string & base64_seed_signature)247 VariationsSeedStore::VerifySeedSignature(
248 const std::string& seed_bytes,
249 const std::string& base64_seed_signature) {
250 if (!SignatureVerificationEnabled())
251 return VARIATIONS_SEED_SIGNATURE_ENUM_SIZE;
252
253 if (base64_seed_signature.empty())
254 return VARIATIONS_SEED_SIGNATURE_MISSING;
255
256 std::string signature;
257 if (!base::Base64Decode(base64_seed_signature, &signature))
258 return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED;
259
260 crypto::SignatureVerifier verifier;
261 if (!verifier.VerifyInit(
262 kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
263 reinterpret_cast<const uint8*>(signature.data()), signature.size(),
264 kPublicKey, arraysize(kPublicKey))) {
265 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE;
266 }
267
268 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()),
269 seed_bytes.size());
270 if (verifier.VerifyFinal())
271 return VARIATIONS_SEED_SIGNATURE_VALID;
272 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
273 }
274
275 } // namespace chrome_variations
276