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.
5 #include "chrome/browser/metrics/variations/variations_seed_store.h"
7 #include "base/base64.h"
8 #include "base/metrics/histogram_macros.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/metrics/compression_utils.h"
15 #include "components/variations/proto/variations_seed.pb.h"
16 #include "crypto/signature_verifier.h"
18 namespace chrome_variations
{
22 // Signature verification is disabled on mobile platforms for now, since it
23 // adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop).
24 bool SignatureVerificationEnabled() {
25 #if defined(OS_IOS) || defined(OS_ANDROID)
32 // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
34 // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
35 // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
37 // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
38 // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
39 // as an AlgorithmIdentifier, the encoding MUST omit the parameters
40 // field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
41 // component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
42 // SHA384, or ecdsa-with-SHA512.
43 // See also RFC 5480, Appendix A.
44 const uint8 kECDSAWithSHA256AlgorithmID
[] = {
47 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
50 // The ECDSA public key of the variations server for verifying variations seed
52 const uint8_t kPublicKey
[] = {
53 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
54 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
55 0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43,
56 0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72,
57 0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b,
58 0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15,
59 0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf,
62 // Note: UMA histogram enum - don't re-order or remove entries.
63 enum VariationSeedEmptyState
{
64 VARIATIONS_SEED_NOT_EMPTY
,
65 VARIATIONS_SEED_EMPTY
,
66 VARIATIONS_SEED_CORRUPT
,
67 VARIATIONS_SEED_INVALID_SIGNATURE
,
68 VARIATIONS_SEED_CORRUPT_BASE64
,
69 VARIATIONS_SEED_CORRUPT_PROTOBUF
,
70 VARIATIONS_SEED_CORRUPT_GZIP
,
71 VARIATIONS_SEED_EMPTY_ENUM_SIZE
,
74 void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state
) {
75 UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state
,
76 VARIATIONS_SEED_EMPTY_ENUM_SIZE
);
79 enum VariationsSeedStoreResult
{
80 VARIATIONS_SEED_STORE_SUCCESS
,
81 VARIATIONS_SEED_STORE_FAILED_EMPTY
,
82 VARIATIONS_SEED_STORE_FAILED_PARSE
,
83 VARIATIONS_SEED_STORE_FAILED_SIGNATURE
,
84 VARIATIONS_SEED_STORE_FAILED_GZIP
,
85 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE
,
88 void RecordVariationsSeedStoreHistogram(VariationsSeedStoreResult result
) {
89 UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result
,
90 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE
);
93 // Note: UMA histogram enum - don't re-order or remove entries.
94 enum VariationsSeedDateChangeState
{
95 SEED_DATE_NO_OLD_DATE
,
96 SEED_DATE_NEW_DATE_OLDER
,
102 // Truncates a time to the start of the day in UTC. If given a time representing
103 // 2014-03-11 10:18:03.1 UTC, it will return a time representing
104 // 2014-03-11 00:00:00.0 UTC.
105 base::Time
TruncateToUTCDay(const base::Time
& time
) {
106 base::Time::Exploded exploded
;
107 time
.UTCExplode(&exploded
);
111 exploded
.millisecond
= 0;
113 return base::Time::FromUTCExploded(exploded
);
116 VariationsSeedDateChangeState
GetSeedDateChangeState(
117 const base::Time
& server_seed_date
,
118 const base::Time
& stored_seed_date
) {
119 if (server_seed_date
< stored_seed_date
)
120 return SEED_DATE_NEW_DATE_OLDER
;
122 if (TruncateToUTCDay(server_seed_date
) !=
123 TruncateToUTCDay(stored_seed_date
)) {
124 // The server date is earlier than the stored date, and they are from
125 // different UTC days, so |server_seed_date| is a valid new day.
126 return SEED_DATE_NEW_DAY
;
128 return SEED_DATE_SAME_DAY
;
133 VariationsSeedStore::VariationsSeedStore(PrefService
* local_state
)
134 : local_state_(local_state
) {
137 VariationsSeedStore::~VariationsSeedStore() {
140 bool VariationsSeedStore::LoadSeed(variations::VariationsSeed
* seed
) {
141 invalid_base64_signature_
.clear();
143 std::string seed_data
;
144 if (!ReadSeedData(&seed_data
))
147 const std::string base64_seed_signature
=
148 local_state_
->GetString(prefs::kVariationsSeedSignature
);
149 const VerifySignatureResult result
=
150 VerifySeedSignature(seed_data
, base64_seed_signature
);
151 if (result
!= VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
) {
152 UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result
,
153 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
);
154 if (result
!= VARIATIONS_SEED_SIGNATURE_VALID
) {
156 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_INVALID_SIGNATURE
);
157 // Record the invalid signature.
158 invalid_base64_signature_
= base64_seed_signature
;
163 if (!seed
->ParseFromString(seed_data
)) {
165 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_PROTOBUF
);
169 variations_serial_number_
= seed
->serial_number();
170 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY
);
174 bool VariationsSeedStore::StoreSeedData(
175 const std::string
& seed_data
,
176 const std::string
& base64_seed_signature
,
177 const base::Time
& date_fetched
,
178 variations::VariationsSeed
* parsed_seed
) {
179 if (seed_data
.empty()) {
180 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY
);
184 // Only store the seed data if it parses correctly.
185 variations::VariationsSeed seed
;
186 if (!seed
.ParseFromString(seed_data
)) {
187 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE
);
191 const VerifySignatureResult result
=
192 VerifySeedSignature(seed_data
, base64_seed_signature
);
193 if (result
!= VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
) {
194 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result
,
195 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
);
196 if (result
!= VARIATIONS_SEED_SIGNATURE_VALID
) {
197 RecordVariationsSeedStoreHistogram(
198 VARIATIONS_SEED_STORE_FAILED_SIGNATURE
);
203 // Compress the seed before base64-encoding and storing.
204 std::string compressed_seed_data
;
205 if (!metrics::GzipCompress(seed_data
, &compressed_seed_data
)) {
206 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP
);
210 std::string base64_seed_data
;
211 base::Base64Encode(compressed_seed_data
, &base64_seed_data
);
213 // TODO(asvitkine): This pref is no longer being used. Remove it completely
215 local_state_
->ClearPref(prefs::kVariationsSeed
);
217 local_state_
->SetString(prefs::kVariationsCompressedSeed
, base64_seed_data
);
218 UpdateSeedDateAndLogDayChange(date_fetched
);
219 local_state_
->SetString(prefs::kVariationsSeedSignature
,
220 base64_seed_signature
);
221 variations_serial_number_
= seed
.serial_number();
223 seed
.Swap(parsed_seed
);
225 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS
);
229 void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
230 const base::Time
& server_date_fetched
) {
231 VariationsSeedDateChangeState date_change
= SEED_DATE_NO_OLD_DATE
;
233 if (local_state_
->HasPrefPath(prefs::kVariationsSeedDate
)) {
234 const int64 stored_date_value
=
235 local_state_
->GetInt64(prefs::kVariationsSeedDate
);
236 const base::Time stored_date
=
237 base::Time::FromInternalValue(stored_date_value
);
239 date_change
= GetSeedDateChangeState(server_date_fetched
, stored_date
);
242 UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change
,
243 SEED_DATE_ENUM_SIZE
);
245 local_state_
->SetInt64(prefs::kVariationsSeedDate
,
246 server_date_fetched
.ToInternalValue());
250 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple
* registry
) {
251 registry
->RegisterStringPref(prefs::kVariationsCompressedSeed
, std::string());
252 registry
->RegisterStringPref(prefs::kVariationsSeed
, std::string());
253 registry
->RegisterInt64Pref(prefs::kVariationsSeedDate
,
254 base::Time().ToInternalValue());
255 registry
->RegisterStringPref(prefs::kVariationsSeedSignature
, std::string());
258 void VariationsSeedStore::ClearPrefs() {
259 local_state_
->ClearPref(prefs::kVariationsCompressedSeed
);
260 local_state_
->ClearPref(prefs::kVariationsSeed
);
261 local_state_
->ClearPref(prefs::kVariationsSeedDate
);
262 local_state_
->ClearPref(prefs::kVariationsSeedSignature
);
265 bool VariationsSeedStore::ReadSeedData(std::string
* seed_data
) {
266 std::string base64_seed_data
=
267 local_state_
->GetString(prefs::kVariationsCompressedSeed
);
268 const bool is_compressed
= !base64_seed_data
.empty();
269 // If there's no compressed seed, fall back to the uncompressed one.
271 base64_seed_data
= local_state_
->GetString(prefs::kVariationsSeed
);
273 if (base64_seed_data
.empty()) {
274 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY
);
278 // If the decode process fails, assume the pref value is corrupt and clear it.
279 std::string decoded_data
;
280 if (!base::Base64Decode(base64_seed_data
, &decoded_data
)) {
282 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_BASE64
);
286 if (!is_compressed
) {
287 seed_data
->swap(decoded_data
);
288 } else if (!metrics::GzipUncompress(decoded_data
, seed_data
)) {
290 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_GZIP
);
297 VariationsSeedStore::VerifySignatureResult
298 VariationsSeedStore::VerifySeedSignature(
299 const std::string
& seed_bytes
,
300 const std::string
& base64_seed_signature
) {
301 if (!SignatureVerificationEnabled())
302 return VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
;
304 if (base64_seed_signature
.empty())
305 return VARIATIONS_SEED_SIGNATURE_MISSING
;
307 std::string signature
;
308 if (!base::Base64Decode(base64_seed_signature
, &signature
))
309 return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED
;
311 crypto::SignatureVerifier verifier
;
312 if (!verifier
.VerifyInit(
313 kECDSAWithSHA256AlgorithmID
, sizeof(kECDSAWithSHA256AlgorithmID
),
314 reinterpret_cast<const uint8
*>(signature
.data()), signature
.size(),
315 kPublicKey
, arraysize(kPublicKey
))) {
316 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE
;
319 verifier
.VerifyUpdate(reinterpret_cast<const uint8
*>(seed_bytes
.data()),
321 if (verifier
.VerifyFinal())
322 return VARIATIONS_SEED_SIGNATURE_VALID
;
323 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED
;
326 std::string
VariationsSeedStore::GetInvalidSignature() const {
327 return invalid_base64_signature_
;
330 } // namespace chrome_variations