Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 |
| OLD | NEW |