Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(252)

Side by Side Diff: chrome/browser/metrics/variations/variations_seed_store.cc

Issue 183003008: Enforce variations signature verification. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/metrics/variations/variations_seed_store.h" 5 #include "chrome/browser/metrics/variations/variations_seed_store.h"
6 6
7 #include "base/base64.h" 7 #include "base/base64.h"
8 #include "base/metrics/histogram.h" 8 #include "base/metrics/histogram.h"
9 #include "base/prefs/pref_registry_simple.h" 9 #include "base/prefs/pref_registry_simple.h"
10 #include "base/prefs/pref_service.h" 10 #include "base/prefs/pref_service.h"
11 #include "base/sha1.h" 11 #include "base/sha1.h"
12 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/common/pref_names.h" 13 #include "chrome/common/pref_names.h"
14 #include "components/variations/proto/variations_seed.pb.h" 14 #include "components/variations/proto/variations_seed.pb.h"
15 #include "crypto/signature_verifier.h" 15 #include "crypto/signature_verifier.h"
16 16
17 namespace chrome_variations { 17 namespace chrome_variations {
18 18
19 namespace { 19 namespace {
20 20
21 // Computes a hash of the serialized variations seed data.
22 // TODO(asvitkine): Remove this once the seed signature is ubiquitous.
23 std::string HashSeed(const std::string& seed_data) {
24 const std::string sha1 = base::SHA1HashString(seed_data);
25 return base::HexEncode(sha1.data(), sha1.size());
26 }
27
28 // Signature verification is disabled on mobile platforms for now, since it 21 // Signature verification is disabled on mobile platforms for now, since it
29 // adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop). 22 // adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop).
30 bool SignatureVerificationEnabled() { 23 bool SignatureVerificationEnabled() {
31 #if defined(OS_IOS) || defined(OS_ANDROID) 24 #if defined(OS_IOS) || defined(OS_ANDROID)
32 return false; 25 return false;
33 #else 26 #else
34 return true; 27 return true;
35 #endif 28 #endif
36 } 29 }
37 30
(...skipping 21 matching lines...) Expand all
59 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 52 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
60 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 53 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
61 0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43, 54 0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43,
62 0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72, 55 0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72,
63 0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b, 56 0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b,
64 0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15, 57 0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15,
65 0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf, 58 0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf,
66 }; 59 };
67 60
68 // Note: UMA histogram enum - don't re-order or remove entries. 61 // Note: UMA histogram enum - don't re-order or remove entries.
69 enum VariationSeedSignatureState {
70 VARIATIONS_SEED_SIGNATURE_MISSING,
71 VARIATIONS_SEED_SIGNATURE_DECODE_FAILED,
72 VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE,
73 VARIATIONS_SEED_SIGNATURE_INVALID_SEED,
74 VARIATIONS_SEED_SIGNATURE_VALID,
75 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE,
76 };
77
78 // Verifies a variations seed (the serialized proto bytes) with the specified
79 // base-64 encoded signate that was received from the server and returns the
80 // result. The signature is assumed to be an "ECDSA with SHA-256" signature
81 // (see kECDSAWithSHA256AlgorithmID above).
82 VariationSeedSignatureState VerifySeedSignature(
83 const std::string& seed_bytes,
84 const std::string& base64_seed_signature) {
85 if (base64_seed_signature.empty())
86 return VARIATIONS_SEED_SIGNATURE_MISSING;
87
88 std::string signature;
89 if (!base::Base64Decode(base64_seed_signature, &signature))
90 return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED;
91
92 crypto::SignatureVerifier verifier;
93 if (!verifier.VerifyInit(
94 kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
95 reinterpret_cast<const uint8*>(signature.data()), signature.size(),
96 kPublicKey, arraysize(kPublicKey))) {
97 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE;
98 }
99
100 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()),
101 seed_bytes.size());
102 if (verifier.VerifyFinal())
103 return VARIATIONS_SEED_SIGNATURE_VALID;
104 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
105 }
106
107 // Note: UMA histogram enum - don't re-order or remove entries.
108 enum VariationSeedEmptyState { 62 enum VariationSeedEmptyState {
109 VARIATIONS_SEED_NOT_EMPTY, 63 VARIATIONS_SEED_NOT_EMPTY,
110 VARIATIONS_SEED_EMPTY, 64 VARIATIONS_SEED_EMPTY,
111 VARIATIONS_SEED_CORRUPT, 65 VARIATIONS_SEED_CORRUPT,
66 VARIATIONS_SEED_INVALID_SIGNATURE,
112 VARIATIONS_SEED_EMPTY_ENUM_SIZE, 67 VARIATIONS_SEED_EMPTY_ENUM_SIZE,
113 }; 68 };
114 69
115 void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state) { 70 void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state) {
116 UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state, 71 UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state,
117 VARIATIONS_SEED_EMPTY_ENUM_SIZE); 72 VARIATIONS_SEED_EMPTY_ENUM_SIZE);
118 } 73 }
119 74
120 } // namespace 75 } // namespace
121 76
122 VariationsSeedStore::VariationsSeedStore(PrefService* local_state) 77 VariationsSeedStore::VariationsSeedStore(PrefService* local_state)
123 : local_state_(local_state) { 78 : local_state_(local_state) {
124 } 79 }
125 80
126 VariationsSeedStore::~VariationsSeedStore() { 81 VariationsSeedStore::~VariationsSeedStore() {
127 } 82 }
128 83
129 bool VariationsSeedStore::LoadSeed(VariationsSeed* seed) { 84 bool VariationsSeedStore::LoadSeed(VariationsSeed* seed) {
130 const std::string base64_seed_data = 85 const std::string base64_seed_data =
131 local_state_->GetString(prefs::kVariationsSeed); 86 local_state_->GetString(prefs::kVariationsSeed);
132 if (base64_seed_data.empty()) { 87 if (base64_seed_data.empty()) {
133 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY); 88 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY);
134 return false; 89 return false;
135 } 90 }
136 91
137 const std::string hash_from_pref =
138 local_state_->GetString(prefs::kVariationsSeedHash);
139 // If the decode process fails, assume the pref value is corrupt and clear it. 92 // If the decode process fails, assume the pref value is corrupt and clear it.
140 std::string seed_data; 93 std::string seed_data;
141 if (!base::Base64Decode(base64_seed_data, &seed_data) || 94 if (!base::Base64Decode(base64_seed_data, &seed_data) ||
142 (!hash_from_pref.empty() && HashSeed(seed_data) != hash_from_pref) ||
143 !seed->ParseFromString(seed_data)) { 95 !seed->ParseFromString(seed_data)) {
144 VLOG(1) << "Variations seed data in local pref is corrupt, clearing the " 96 VLOG(1) << "Variations seed data in local pref is corrupt, clearing the "
145 << "pref."; 97 << "pref.";
146 ClearPrefs(); 98 ClearPrefs();
147 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT); 99 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT);
148 return false; 100 return false;
149 } 101 }
150 102
151 if (SignatureVerificationEnabled()) { 103 const std::string base64_seed_signature =
152 const std::string base64_seed_signature = 104 local_state_->GetString(prefs::kVariationsSeedSignature);
153 local_state_->GetString(prefs::kVariationsSeedSignature); 105 const VerifySignatureResult result =
154 const VariationSeedSignatureState signature_state = 106 VerifySeedSignature(seed_data, base64_seed_signature);
155 VerifySeedSignature(seed_data, base64_seed_signature); 107 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
156 UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", signature_state, 108 UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result,
157 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE); 109 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
110 if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
111 VLOG(1) << "Variations seed signature in local pref missing or invalid "
jwd 2014/03/05 17:56:27 Do these need to be VLOGs?
Alexei Svitkine (slow) 2014/03/05 18:21:43 I've followed the convention that's used in this f
jwd 2014/03/05 18:36:00 Ah, didn't notice the convention. I was thinking D
112 << "with result: " << result << ". Clearing the pref.";
113 ClearPrefs();
114 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_INVALID_SIGNATURE);
115 return false;
116 }
158 } 117 }
159 118
160 variations_serial_number_ = seed->serial_number(); 119 variations_serial_number_ = seed->serial_number();
161 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY); 120 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY);
162 return true; 121 return true;
163 } 122 }
164 123
165 bool VariationsSeedStore::StoreSeedData( 124 bool VariationsSeedStore::StoreSeedData(
166 const std::string& seed_data, 125 const std::string& seed_data,
167 const std::string& base64_seed_signature, 126 const std::string& base64_seed_signature,
168 const base::Time& date_fetched) { 127 const base::Time& date_fetched) {
169 if (seed_data.empty()) { 128 if (seed_data.empty()) {
170 VLOG(1) << "Variations seed data is empty, rejecting the seed."; 129 VLOG(1) << "Variations seed data is empty, rejecting the seed.";
171 return false; 130 return false;
172 } 131 }
173 132
174 // Only store the seed data if it parses correctly. 133 // Only store the seed data if it parses correctly.
175 VariationsSeed seed; 134 VariationsSeed seed;
176 if (!seed.ParseFromString(seed_data)) { 135 if (!seed.ParseFromString(seed_data)) {
177 VLOG(1) << "Variations seed data is not in valid proto format, " 136 VLOG(1) << "Variations seed data is not in valid proto format, "
178 << "rejecting the seed."; 137 << "rejecting the seed.";
179 return false; 138 return false;
180 } 139 }
181 140
182 if (SignatureVerificationEnabled()) { 141 const VerifySignatureResult result =
183 const VariationSeedSignatureState signature_state = 142 VerifySeedSignature(seed_data, base64_seed_signature);
184 VerifySeedSignature(seed_data, base64_seed_signature); 143 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
185 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", signature_state, 144 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result,
186 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE); 145 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
146 if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
147 VLOG(1) << "Variations seed signature missing or invalid with result: "
148 << result << ". Rejecting the seed.";
149 return false;
150 }
187 } 151 }
188 152
189 std::string base64_seed_data; 153 std::string base64_seed_data;
190 base::Base64Encode(seed_data, &base64_seed_data); 154 base::Base64Encode(seed_data, &base64_seed_data);
191 155
156 // TODO(asvitkine): This pref is no longer being used. Remove it completely
157 // in a couple of releases.
158 local_state_->ClearPref(prefs::kVariationsSeedHash);
159
192 local_state_->SetString(prefs::kVariationsSeed, base64_seed_data); 160 local_state_->SetString(prefs::kVariationsSeed, base64_seed_data);
193 local_state_->SetString(prefs::kVariationsSeedHash, HashSeed(seed_data));
194 local_state_->SetInt64(prefs::kVariationsSeedDate, 161 local_state_->SetInt64(prefs::kVariationsSeedDate,
195 date_fetched.ToInternalValue()); 162 date_fetched.ToInternalValue());
196 local_state_->SetString(prefs::kVariationsSeedSignature, 163 local_state_->SetString(prefs::kVariationsSeedSignature,
197 base64_seed_signature); 164 base64_seed_signature);
198 variations_serial_number_ = seed.serial_number(); 165 variations_serial_number_ = seed.serial_number();
199 166
200 return true; 167 return true;
201 } 168 }
202 169
203 // static 170 // static
204 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) { 171 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {
205 registry->RegisterStringPref(prefs::kVariationsSeed, std::string()); 172 registry->RegisterStringPref(prefs::kVariationsSeed, std::string());
206 registry->RegisterStringPref(prefs::kVariationsSeedHash, std::string()); 173 registry->RegisterStringPref(prefs::kVariationsSeedHash, std::string());
207 registry->RegisterInt64Pref(prefs::kVariationsSeedDate, 174 registry->RegisterInt64Pref(prefs::kVariationsSeedDate,
208 base::Time().ToInternalValue()); 175 base::Time().ToInternalValue());
209 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string()); 176 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string());
210 } 177 }
211 178
212 void VariationsSeedStore::ClearPrefs() { 179 void VariationsSeedStore::ClearPrefs() {
213 local_state_->ClearPref(prefs::kVariationsSeed); 180 local_state_->ClearPref(prefs::kVariationsSeed);
214 local_state_->ClearPref(prefs::kVariationsSeedDate); 181 local_state_->ClearPref(prefs::kVariationsSeedDate);
215 local_state_->ClearPref(prefs::kVariationsSeedHash); 182 local_state_->ClearPref(prefs::kVariationsSeedHash);
216 local_state_->ClearPref(prefs::kVariationsSeedSignature); 183 local_state_->ClearPref(prefs::kVariationsSeedSignature);
217 } 184 }
218 185
186 VariationsSeedStore::VerifySignatureResult
187 VariationsSeedStore::VerifySeedSignature(
jwd 2014/03/05 17:56:27 Can this be tested? What about having a testing ke
Alexei Svitkine (slow) 2014/03/05 18:21:43 A testing key pair would require passing the publi
jwd 2014/03/05 18:36:00 Base-64 blob seems fine then.
Alexei Svitkine (slow) 2014/03/06 18:16:07 Done. Added checking for all the possible return v
188 const std::string& seed_bytes,
189 const std::string& base64_seed_signature) {
190 if (!SignatureVerificationEnabled())
191 return VARIATIONS_SEED_SIGNATURE_ENUM_SIZE;
192
193 if (base64_seed_signature.empty())
194 return VARIATIONS_SEED_SIGNATURE_MISSING;
195
196 std::string signature;
197 if (!base::Base64Decode(base64_seed_signature, &signature))
198 return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED;
199
200 crypto::SignatureVerifier verifier;
201 if (!verifier.VerifyInit(
202 kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
203 reinterpret_cast<const uint8*>(signature.data()), signature.size(),
204 kPublicKey, arraysize(kPublicKey))) {
205 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE;
206 }
207
208 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()),
209 seed_bytes.size());
210 if (verifier.VerifyFinal())
211 return VARIATIONS_SEED_SIGNATURE_VALID;
212 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
213 }
214
219 } // namespace chrome_variations 215 } // namespace chrome_variations
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698