1 // Copyright 2013 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/extensions/install_signer.h" 6 7 #include "base/base64.h" 8 #include "base/bind.h" 9 #include "base/command_line.h" 10 #include "base/json/json_reader.h" 11 #include "base/json/json_writer.h" 12 #include "base/lazy_instance.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/metrics/histogram.h" 15 #include "base/process/process_info.h" 16 #include "base/stl_util.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_split.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/time/time.h" 22 #include "base/values.h" 23 #include "chrome/common/chrome_switches.h" 24 #include "chrome/common/extensions/extension_constants.h" 25 #include "crypto/random.h" 26 #include "crypto/secure_hash.h" 27 #include "crypto/sha2.h" 28 #include "crypto/signature_verifier.h" 29 #include "net/url_request/url_fetcher.h" 30 #include "net/url_request/url_fetcher_delegate.h" 31 #include "net/url_request/url_request_context_getter.h" 32 #include "net/url_request/url_request_status.h" 33 #include "url/gurl.h" 34 35 #if defined(ENABLE_RLZ) 36 #include "rlz/lib/machine_id.h" 37 #endif 38 39 namespace { 40 41 using extensions::ExtensionIdSet; 42 43 const char kExpireDateKey[] = "expire_date"; 44 const char kExpiryKey[] = "expiry"; 45 const char kHashKey[] = "hash"; 46 const char kIdsKey[] = "ids"; 47 const char kInvalidIdsKey[] = "invalid_ids"; 48 const char kProtocolVersionKey[] = "protocol_version"; 49 const char kSaltKey[] = "salt"; 50 const char kSignatureKey[] = "signature"; 51 const char kTimestampKey[] = "timestamp"; 52 53 const size_t kSaltBytes = 32; 54 55 const char kBackendUrl[] = 56 "https://www.googleapis.com/chromewebstore/v1.1/items/verify"; 57 58 const char kPublicKeyPEM[] = \ 59 "-----BEGIN PUBLIC KEY-----" \ 60 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj/u/XDdjlDyw7gHEtaaa" \ 61 "sZ9GdG8WOKAyJzXd8HFrDtz2Jcuy7er7MtWvHgNDA0bwpznbI5YdZeV4UfCEsA4S" \ 62 "rA5b3MnWTHwA1bgbiDM+L9rrqvcadcKuOlTeN48Q0ijmhHlNFbTzvT9W0zw/GKv8" \ 63 "LgXAHggxtmHQ/Z9PP2QNF5O8rUHHSL4AJ6hNcEKSBVSmbbjeVm4gSXDuED5r0nwx" \ 64 "vRtupDxGYp8IZpP5KlExqNu1nbkPc+igCTIB6XsqijagzxewUHCdovmkb2JNtskx" \ 65 "/PMIEv+TvWIx2BzqGp71gSh/dV7SJ3rClvWd2xj8dtxG8FfAWDTIIi0qZXWn2Qhi" \ 66 "zQIDAQAB" \ 67 "-----END PUBLIC KEY-----"; 68 69 GURL GetBackendUrl() { 70 return GURL(kBackendUrl); 71 } 72 73 // Hashes |salt| with the machine id, base64-encodes it and returns it in 74 // |result|. 75 bool HashWithMachineId(const std::string& salt, std::string* result) { 76 std::string machine_id; 77 #if defined(ENABLE_RLZ) 78 if (!rlz_lib::GetMachineId(&machine_id)) 79 return false; 80 #else 81 machine_id = "unknown"; 82 #endif 83 84 scoped_ptr<crypto::SecureHash> hash( 85 crypto::SecureHash::Create(crypto::SecureHash::SHA256)); 86 87 hash->Update(machine_id.data(), machine_id.size()); 88 hash->Update(salt.data(), salt.size()); 89 90 std::string result_bytes(crypto::kSHA256Length, 0); 91 hash->Finish(string_as_array(&result_bytes), result_bytes.size()); 92 93 base::Base64Encode(result_bytes, result); 94 return true; 95 } 96 97 // Validates that |input| is a string of the form "YYYY-MM-DD". 98 bool ValidateExpireDateFormat(const std::string& input) { 99 if (input.length() != 10) 100 return false; 101 for (int i = 0; i < 10; i++) { 102 if (i == 4 || i == 7) { 103 if (input[i] != '-') 104 return false; 105 } else if (!IsAsciiDigit(input[i])) { 106 return false; 107 } 108 } 109 return true; 110 } 111 112 } // namespace 113 114 namespace extensions { 115 116 InstallSignature::InstallSignature() { 117 } 118 InstallSignature::~InstallSignature() { 119 } 120 121 void InstallSignature::ToValue(base::DictionaryValue* value) const { 122 CHECK(value); 123 124 base::ListValue* id_list = new base::ListValue(); 125 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); 126 ++i) 127 id_list->AppendString(*i); 128 129 value->Set(kIdsKey, id_list); 130 value->SetString(kExpireDateKey, expire_date); 131 std::string salt_base64; 132 std::string signature_base64; 133 base::Base64Encode(salt, &salt_base64); 134 base::Base64Encode(signature, &signature_base64); 135 value->SetString(kSaltKey, salt_base64); 136 value->SetString(kSignatureKey, signature_base64); 137 value->SetString(kTimestampKey, 138 base::Int64ToString(timestamp.ToInternalValue())); 139 } 140 141 // static 142 scoped_ptr<InstallSignature> InstallSignature::FromValue( 143 const base::DictionaryValue& value) { 144 145 scoped_ptr<InstallSignature> result(new InstallSignature); 146 147 std::string salt_base64; 148 std::string signature_base64; 149 if (!value.GetString(kExpireDateKey, &result->expire_date) || 150 !value.GetString(kSaltKey, &salt_base64) || 151 !value.GetString(kSignatureKey, &signature_base64) || 152 !base::Base64Decode(salt_base64, &result->salt) || 153 !base::Base64Decode(signature_base64, &result->signature)) { 154 result.reset(); 155 return result.Pass(); 156 } 157 158 // Note: earlier versions of the code did not write out a timestamp value 159 // so older entries will not necessarily have this. 160 if (value.HasKey(kTimestampKey)) { 161 std::string timestamp; 162 int64 timestamp_value = 0; 163 if (!value.GetString(kTimestampKey, ×tamp) || 164 !base::StringToInt64(timestamp, ×tamp_value)) { 165 result.reset(); 166 return result.Pass(); 167 } 168 result->timestamp = base::Time::FromInternalValue(timestamp_value); 169 } 170 171 const base::ListValue* ids = NULL; 172 if (!value.GetList(kIdsKey, &ids)) { 173 result.reset(); 174 return result.Pass(); 175 } 176 177 for (ListValue::const_iterator i = ids->begin(); i != ids->end(); ++i) { 178 std::string id; 179 if (!(*i)->GetAsString(&id)) { 180 result.reset(); 181 return result.Pass(); 182 } 183 result->ids.insert(id); 184 } 185 186 return result.Pass(); 187 } 188 189 190 InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter, 191 const ExtensionIdSet& ids) 192 : ids_(ids), context_getter_(context_getter) { 193 } 194 195 InstallSigner::~InstallSigner() { 196 } 197 198 // static 199 bool InstallSigner::VerifySignature(const InstallSignature& signature) { 200 if (signature.ids.empty()) 201 return true; 202 203 std::string signed_data; 204 for (ExtensionIdSet::const_iterator i = signature.ids.begin(); 205 i != signature.ids.end(); ++i) 206 signed_data.append(*i); 207 208 std::string hash_base64; 209 if (!HashWithMachineId(signature.salt, &hash_base64)) 210 return false; 211 signed_data.append(hash_base64); 212 213 signed_data.append(signature.expire_date); 214 215 std::string public_key; 216 if (!Extension::ParsePEMKeyBytes(kPublicKeyPEM, &public_key)) 217 return false; 218 219 crypto::SignatureVerifier verifier; 220 if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm, 221 sizeof(extension_misc::kSignatureAlgorithm), 222 reinterpret_cast<const uint8*>( 223 signature.signature.data()), 224 signature.signature.size(), 225 reinterpret_cast<const uint8*>(public_key.data()), 226 public_key.size())) 227 return false; 228 229 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()), 230 signed_data.size()); 231 return verifier.VerifyFinal(); 232 } 233 234 235 class InstallSigner::FetcherDelegate : public net::URLFetcherDelegate { 236 public: 237 explicit FetcherDelegate(const base::Closure& callback) 238 : callback_(callback) { 239 } 240 241 virtual ~FetcherDelegate() { 242 } 243 244 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { 245 callback_.Run(); 246 } 247 248 private: 249 base::Closure callback_; 250 DISALLOW_COPY_AND_ASSIGN(FetcherDelegate); 251 }; 252 253 // static 254 ExtensionIdSet InstallSigner::GetForcedNotFromWebstore() { 255 std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 256 switches::kExtensionsNotWebstore); 257 if (value.empty()) 258 return ExtensionIdSet(); 259 260 std::vector<std::string> ids; 261 base::SplitString(value, ',', &ids); 262 return ExtensionIdSet(ids.begin(), ids.end()); 263 } 264 265 namespace { 266 267 static int g_request_count = 0; 268 269 base::LazyInstance<base::TimeTicks> g_last_request_time = 270 LAZY_INSTANCE_INITIALIZER; 271 272 base::LazyInstance<base::ThreadChecker> g_single_thread_checker = 273 LAZY_INSTANCE_INITIALIZER; 274 275 void LogRequestStartHistograms() { 276 // Make sure we only ever call this from one thread, so that we don't have to 277 // worry about race conditions setting g_last_request_time. 278 DCHECK(g_single_thread_checker.Get().CalledOnValidThread()); 279 280 // CurrentProcessInfo::CreationTime is only defined on some platforms. 281 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) 282 const base::Time process_creation_time = 283 base::CurrentProcessInfo::CreationTime(); 284 UMA_HISTOGRAM_COUNTS("ExtensionInstallSigner.UptimeAtTimeOfRequest", 285 (base::Time::Now() - process_creation_time).InSeconds()); 286 #endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) 287 288 base::TimeDelta delta; 289 base::TimeTicks now = base::TimeTicks::Now(); 290 if (!g_last_request_time.Get().is_null()) 291 delta = now - g_last_request_time.Get(); 292 g_last_request_time.Get() = now; 293 UMA_HISTOGRAM_COUNTS("ExtensionInstallSigner.SecondsSinceLastRequest", 294 delta.InSeconds()); 295 296 g_request_count += 1; 297 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.RequestCount", 298 g_request_count); 299 } 300 301 } // namespace 302 303 void InstallSigner::GetSignature(const SignatureCallback& callback) { 304 CHECK(!url_fetcher_.get()); 305 CHECK(callback_.is_null()); 306 CHECK(salt_.empty()); 307 callback_ = callback; 308 309 // If the set of ids is empty, just return an empty signature and skip the 310 // call to the server. 311 if (ids_.empty()) { 312 if (!callback_.is_null()) 313 callback_.Run(scoped_ptr<InstallSignature>(new InstallSignature())); 314 return; 315 } 316 317 salt_ = std::string(kSaltBytes, 0); 318 DCHECK_EQ(kSaltBytes, salt_.size()); 319 crypto::RandBytes(string_as_array(&salt_), salt_.size()); 320 321 std::string hash_base64; 322 if (!HashWithMachineId(salt_, &hash_base64)) { 323 ReportErrorViaCallback(); 324 return; 325 } 326 327 if (!context_getter_) { 328 ReportErrorViaCallback(); 329 return; 330 } 331 332 base::Closure closure = base::Bind(&InstallSigner::ParseFetchResponse, 333 base::Unretained(this)); 334 335 delegate_.reset(new FetcherDelegate(closure)); 336 url_fetcher_.reset(net::URLFetcher::Create( 337 GetBackendUrl(), net::URLFetcher::POST, delegate_.get())); 338 url_fetcher_->SetRequestContext(context_getter_); 339 340 // The request protocol is JSON of the form: 341 // { 342 // "protocol_version": "1", 343 // "hash": "<base64-encoded hash value here>", 344 // "ids": [ "<id1>", "id2" ] 345 // } 346 base::DictionaryValue dictionary; 347 dictionary.SetInteger(kProtocolVersionKey, 1); 348 dictionary.SetString(kHashKey, hash_base64); 349 scoped_ptr<ListValue> id_list(new ListValue); 350 for (ExtensionIdSet::const_iterator i = ids_.begin(); i != ids_.end(); ++i) { 351 id_list->AppendString(*i); 352 } 353 dictionary.Set(kIdsKey, id_list.release()); 354 std::string json; 355 base::JSONWriter::Write(&dictionary, &json); 356 if (json.empty()) { 357 ReportErrorViaCallback(); 358 return; 359 } 360 url_fetcher_->SetUploadData("application/json", json); 361 LogRequestStartHistograms(); 362 request_start_time_ = base::Time::Now(); 363 url_fetcher_->Start(); 364 } 365 366 void InstallSigner::ReportErrorViaCallback() { 367 InstallSignature* null_signature = NULL; 368 if (!callback_.is_null()) 369 callback_.Run(scoped_ptr<InstallSignature>(null_signature)); 370 } 371 372 void InstallSigner::ParseFetchResponse() { 373 bool fetch_success = url_fetcher_->GetStatus().is_success(); 374 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.FetchSuccess", fetch_success); 375 376 std::string response; 377 if (fetch_success) { 378 if (!url_fetcher_->GetResponseAsString(&response)) 379 response.clear(); 380 } 381 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.GetResponseSuccess", 382 !response.empty()); 383 if (!fetch_success || response.empty()) { 384 ReportErrorViaCallback(); 385 return; 386 } 387 388 // The response is JSON of the form: 389 // { 390 // "protocol_version": "1", 391 // "signature": "<base64-encoded signature>", 392 // "expiry": "<date in YYYY-MM-DD form>", 393 // "invalid_ids": [ "<id3>", "<id4>" ] 394 // } 395 // where |invalid_ids| is a list of ids from the original request that 396 // could not be verified to be in the webstore. 397 398 base::DictionaryValue* dictionary = NULL; 399 scoped_ptr<base::Value> parsed(base::JSONReader::Read(response)); 400 bool json_success = parsed.get() && parsed->GetAsDictionary(&dictionary); 401 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ParseJsonSuccess", 402 json_success); 403 if (!json_success) { 404 ReportErrorViaCallback(); 405 return; 406 } 407 408 int protocol_version = 0; 409 std::string signature_base64; 410 std::string signature; 411 std::string expire_date; 412 413 dictionary->GetInteger(kProtocolVersionKey, &protocol_version); 414 dictionary->GetString(kSignatureKey, &signature_base64); 415 dictionary->GetString(kExpiryKey, &expire_date); 416 417 bool fields_success = 418 protocol_version == 1 && !signature_base64.empty() && 419 ValidateExpireDateFormat(expire_date) && 420 base::Base64Decode(signature_base64, &signature); 421 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ParseFieldsSuccess", 422 fields_success); 423 if (!fields_success) { 424 ReportErrorViaCallback(); 425 return; 426 } 427 428 ExtensionIdSet invalid_ids; 429 const base::ListValue* invalid_ids_list = NULL; 430 if (dictionary->GetList(kInvalidIdsKey, &invalid_ids_list)) { 431 for (size_t i = 0; i < invalid_ids_list->GetSize(); i++) { 432 std::string id; 433 if (!invalid_ids_list->GetString(i, &id)) { 434 ReportErrorViaCallback(); 435 return; 436 } 437 invalid_ids.insert(id); 438 } 439 } 440 441 HandleSignatureResult(signature, expire_date, invalid_ids); 442 } 443 444 void InstallSigner::HandleSignatureResult(const std::string& signature, 445 const std::string& expire_date, 446 const ExtensionIdSet& invalid_ids) { 447 ExtensionIdSet valid_ids = 448 base::STLSetDifference<ExtensionIdSet>(ids_, invalid_ids); 449 450 scoped_ptr<InstallSignature> result; 451 if (!signature.empty()) { 452 result.reset(new InstallSignature); 453 result->ids = valid_ids; 454 result->salt = salt_; 455 result->signature = signature; 456 result->expire_date = expire_date; 457 result->timestamp = request_start_time_; 458 bool verified = VerifySignature(*result); 459 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ResultWasValid", verified); 460 if (!verified) 461 result.reset(); 462 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.InvalidCount", 463 invalid_ids.size()); 464 } 465 466 if (!callback_.is_null()) 467 callback_.Run(result.Pass()); 468 } 469 470 471 } // namespace extensions 472