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). 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 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. 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 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 115 VariationsSeedStore::VariationsSeedStore(PrefService* local_state) 116 : local_state_(local_state) { 117 } 118 119 VariationsSeedStore::~VariationsSeedStore() { 120 } 121 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 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 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 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 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 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