Extensions cleanup: Merge IsSyncableApp+Extension, ShouldSyncApp+Extension
[chromium-blink-merge.git] / chrome / browser / extensions / install_signer.cc
blob8ec39e52f0ff38db1543bda83a09562b6f066d15
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.
5 #include "chrome/browser/extensions/install_signer.h"
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 "components/crx_file/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"
35 #if defined(ENABLE_RLZ)
36 #include "rlz/lib/machine_id.h"
37 #endif
39 namespace {
41 using extensions::ExtensionIdSet;
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 kSignatureFormatVersionKey[] = "signature_format_version";
52 const char kTimestampKey[] = "timestamp";
54 // This allows us to version the format of what we write into the prefs,
55 // allowing for forward migration, as well as detecting forwards/backwards
56 // incompatabilities, etc.
57 const int kSignatureFormatVersion = 2;
59 const size_t kSaltBytes = 32;
61 const char kBackendUrl[] =
62 "https://www.googleapis.com/chromewebstore/v1.1/items/verify";
64 const char kPublicKeyPEM[] = \
65 "-----BEGIN PUBLIC KEY-----" \
66 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj/u/XDdjlDyw7gHEtaaa" \
67 "sZ9GdG8WOKAyJzXd8HFrDtz2Jcuy7er7MtWvHgNDA0bwpznbI5YdZeV4UfCEsA4S" \
68 "rA5b3MnWTHwA1bgbiDM+L9rrqvcadcKuOlTeN48Q0ijmhHlNFbTzvT9W0zw/GKv8" \
69 "LgXAHggxtmHQ/Z9PP2QNF5O8rUHHSL4AJ6hNcEKSBVSmbbjeVm4gSXDuED5r0nwx" \
70 "vRtupDxGYp8IZpP5KlExqNu1nbkPc+igCTIB6XsqijagzxewUHCdovmkb2JNtskx" \
71 "/PMIEv+TvWIx2BzqGp71gSh/dV7SJ3rClvWd2xj8dtxG8FfAWDTIIi0qZXWn2Qhi" \
72 "zQIDAQAB" \
73 "-----END PUBLIC KEY-----";
75 GURL GetBackendUrl() {
76 return GURL(kBackendUrl);
79 // Hashes |salt| with the machine id, base64-encodes it and returns it in
80 // |result|.
81 bool HashWithMachineId(const std::string& salt, std::string* result) {
82 std::string machine_id;
83 #if defined(ENABLE_RLZ)
84 if (!rlz_lib::GetMachineId(&machine_id))
85 return false;
86 #else
87 machine_id = "unknown";
88 #endif
90 scoped_ptr<crypto::SecureHash> hash(
91 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
93 hash->Update(machine_id.data(), machine_id.size());
94 hash->Update(salt.data(), salt.size());
96 std::string result_bytes(crypto::kSHA256Length, 0);
97 hash->Finish(string_as_array(&result_bytes), result_bytes.size());
99 base::Base64Encode(result_bytes, result);
100 return true;
103 // Validates that |input| is a string of the form "YYYY-MM-DD".
104 bool ValidateExpireDateFormat(const std::string& input) {
105 if (input.length() != 10)
106 return false;
107 for (int i = 0; i < 10; i++) {
108 if (i == 4 || i == 7) {
109 if (input[i] != '-')
110 return false;
111 } else if (!base::IsAsciiDigit(input[i])) {
112 return false;
115 return true;
118 // Sets the value of |key| in |dictionary| to be a list with the contents of
119 // |ids|.
120 void SetExtensionIdSet(base::DictionaryValue* dictionary,
121 const char* key,
122 const ExtensionIdSet& ids) {
123 base::ListValue* id_list = new base::ListValue();
124 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i)
125 id_list->AppendString(*i);
126 dictionary->Set(key, id_list);
129 // Tries to fetch a list of strings from |dictionay| for |key|, and inserts
130 // them into |ids|. The return value indicates success/failure. Note: on
131 // failure, |ids| might contain partial results, for instance if some of the
132 // members of the list were not strings.
133 bool GetExtensionIdSet(const base::DictionaryValue& dictionary,
134 const char* key,
135 ExtensionIdSet* ids) {
136 const base::ListValue* id_list = NULL;
137 if (!dictionary.GetList(key, &id_list))
138 return false;
139 for (base::ListValue::const_iterator i = id_list->begin();
140 i != id_list->end();
141 ++i) {
142 std::string id;
143 if (!(*i)->GetAsString(&id)) {
144 return false;
146 ids->insert(id);
148 return true;
151 } // namespace
153 namespace extensions {
155 InstallSignature::InstallSignature() {
157 InstallSignature::~InstallSignature() {
160 void InstallSignature::ToValue(base::DictionaryValue* value) const {
161 CHECK(value);
163 value->SetInteger(kSignatureFormatVersionKey, kSignatureFormatVersion);
164 SetExtensionIdSet(value, kIdsKey, ids);
165 SetExtensionIdSet(value, kInvalidIdsKey, invalid_ids);
166 value->SetString(kExpireDateKey, expire_date);
167 std::string salt_base64;
168 std::string signature_base64;
169 base::Base64Encode(salt, &salt_base64);
170 base::Base64Encode(signature, &signature_base64);
171 value->SetString(kSaltKey, salt_base64);
172 value->SetString(kSignatureKey, signature_base64);
173 value->SetString(kTimestampKey,
174 base::Int64ToString(timestamp.ToInternalValue()));
177 // static
178 scoped_ptr<InstallSignature> InstallSignature::FromValue(
179 const base::DictionaryValue& value) {
181 scoped_ptr<InstallSignature> result(new InstallSignature);
183 // For now we don't want to support any backwards compability, but in the
184 // future if we do, we would want to put the migration code here.
185 int format_version = 0;
186 if (!value.GetInteger(kSignatureFormatVersionKey, &format_version) ||
187 format_version != kSignatureFormatVersion) {
188 result.reset();
189 return result.Pass();
192 std::string salt_base64;
193 std::string signature_base64;
194 if (!value.GetString(kExpireDateKey, &result->expire_date) ||
195 !value.GetString(kSaltKey, &salt_base64) ||
196 !value.GetString(kSignatureKey, &signature_base64) ||
197 !base::Base64Decode(salt_base64, &result->salt) ||
198 !base::Base64Decode(signature_base64, &result->signature)) {
199 result.reset();
200 return result.Pass();
203 // Note: earlier versions of the code did not write out a timestamp value
204 // so older entries will not necessarily have this.
205 if (value.HasKey(kTimestampKey)) {
206 std::string timestamp;
207 int64 timestamp_value = 0;
208 if (!value.GetString(kTimestampKey, &timestamp) ||
209 !base::StringToInt64(timestamp, &timestamp_value)) {
210 result.reset();
211 return result.Pass();
213 result->timestamp = base::Time::FromInternalValue(timestamp_value);
216 if (!GetExtensionIdSet(value, kIdsKey, &result->ids) ||
217 !GetExtensionIdSet(value, kInvalidIdsKey, &result->invalid_ids)) {
218 result.reset();
219 return result.Pass();
222 return result.Pass();
226 InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter,
227 const ExtensionIdSet& ids)
228 : ids_(ids), context_getter_(context_getter) {
231 InstallSigner::~InstallSigner() {
234 // static
235 bool InstallSigner::VerifySignature(const InstallSignature& signature) {
236 if (signature.ids.empty())
237 return true;
239 std::string signed_data;
240 for (ExtensionIdSet::const_iterator i = signature.ids.begin();
241 i != signature.ids.end(); ++i)
242 signed_data.append(*i);
244 std::string hash_base64;
245 if (!HashWithMachineId(signature.salt, &hash_base64))
246 return false;
247 signed_data.append(hash_base64);
249 signed_data.append(signature.expire_date);
251 std::string public_key;
252 if (!Extension::ParsePEMKeyBytes(kPublicKeyPEM, &public_key))
253 return false;
255 crypto::SignatureVerifier verifier;
256 if (!verifier.VerifyInit(crx_file::kSignatureAlgorithm,
257 sizeof(crx_file::kSignatureAlgorithm),
258 reinterpret_cast<const uint8*>(
259 signature.signature.data()),
260 signature.signature.size(),
261 reinterpret_cast<const uint8*>(public_key.data()),
262 public_key.size()))
263 return false;
265 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()),
266 signed_data.size());
267 return verifier.VerifyFinal();
271 class InstallSigner::FetcherDelegate : public net::URLFetcherDelegate {
272 public:
273 explicit FetcherDelegate(const base::Closure& callback)
274 : callback_(callback) {
277 ~FetcherDelegate() override {}
279 void OnURLFetchComplete(const net::URLFetcher* source) override {
280 callback_.Run();
283 private:
284 base::Closure callback_;
285 DISALLOW_COPY_AND_ASSIGN(FetcherDelegate);
288 // static
289 ExtensionIdSet InstallSigner::GetForcedNotFromWebstore() {
290 std::string value =
291 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
292 switches::kExtensionsNotWebstore);
293 if (value.empty())
294 return ExtensionIdSet();
296 std::vector<std::string> ids;
297 base::SplitString(value, ',', &ids);
298 return ExtensionIdSet(ids.begin(), ids.end());
301 namespace {
303 static int g_request_count = 0;
305 base::LazyInstance<base::TimeTicks> g_last_request_time =
306 LAZY_INSTANCE_INITIALIZER;
308 base::LazyInstance<base::ThreadChecker> g_single_thread_checker =
309 LAZY_INSTANCE_INITIALIZER;
311 void LogRequestStartHistograms() {
312 // Make sure we only ever call this from one thread, so that we don't have to
313 // worry about race conditions setting g_last_request_time.
314 DCHECK(g_single_thread_checker.Get().CalledOnValidThread());
316 // CurrentProcessInfo::CreationTime is only defined on some platforms.
317 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
318 const base::Time process_creation_time =
319 base::CurrentProcessInfo::CreationTime();
320 UMA_HISTOGRAM_COUNTS("ExtensionInstallSigner.UptimeAtTimeOfRequest",
321 (base::Time::Now() - process_creation_time).InSeconds());
322 #endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
324 base::TimeDelta delta;
325 base::TimeTicks now = base::TimeTicks::Now();
326 if (!g_last_request_time.Get().is_null())
327 delta = now - g_last_request_time.Get();
328 g_last_request_time.Get() = now;
329 UMA_HISTOGRAM_COUNTS("ExtensionInstallSigner.SecondsSinceLastRequest",
330 delta.InSeconds());
332 g_request_count += 1;
333 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.RequestCount",
334 g_request_count);
337 } // namespace
339 void InstallSigner::GetSignature(const SignatureCallback& callback) {
340 CHECK(!url_fetcher_.get());
341 CHECK(callback_.is_null());
342 CHECK(salt_.empty());
343 callback_ = callback;
345 // If the set of ids is empty, just return an empty signature and skip the
346 // call to the server.
347 if (ids_.empty()) {
348 if (!callback_.is_null())
349 callback_.Run(scoped_ptr<InstallSignature>(new InstallSignature()));
350 return;
353 salt_ = std::string(kSaltBytes, 0);
354 DCHECK_EQ(kSaltBytes, salt_.size());
355 crypto::RandBytes(string_as_array(&salt_), salt_.size());
357 std::string hash_base64;
358 if (!HashWithMachineId(salt_, &hash_base64)) {
359 ReportErrorViaCallback();
360 return;
363 if (!context_getter_) {
364 ReportErrorViaCallback();
365 return;
368 base::Closure closure = base::Bind(&InstallSigner::ParseFetchResponse,
369 base::Unretained(this));
371 delegate_.reset(new FetcherDelegate(closure));
372 url_fetcher_ = net::URLFetcher::Create(GetBackendUrl(), net::URLFetcher::POST,
373 delegate_.get());
374 url_fetcher_->SetRequestContext(context_getter_);
376 // The request protocol is JSON of the form:
377 // {
378 // "protocol_version": "1",
379 // "hash": "<base64-encoded hash value here>",
380 // "ids": [ "<id1>", "id2" ]
381 // }
382 base::DictionaryValue dictionary;
383 dictionary.SetInteger(kProtocolVersionKey, 1);
384 dictionary.SetString(kHashKey, hash_base64);
385 scoped_ptr<base::ListValue> id_list(new base::ListValue);
386 for (ExtensionIdSet::const_iterator i = ids_.begin(); i != ids_.end(); ++i) {
387 id_list->AppendString(*i);
389 dictionary.Set(kIdsKey, id_list.release());
390 std::string json;
391 base::JSONWriter::Write(dictionary, &json);
392 if (json.empty()) {
393 ReportErrorViaCallback();
394 return;
396 url_fetcher_->SetUploadData("application/json", json);
397 LogRequestStartHistograms();
398 request_start_time_ = base::Time::Now();
399 VLOG(1) << "Sending request: " << json;
400 url_fetcher_->Start();
403 void InstallSigner::ReportErrorViaCallback() {
404 InstallSignature* null_signature = NULL;
405 if (!callback_.is_null())
406 callback_.Run(scoped_ptr<InstallSignature>(null_signature));
409 void InstallSigner::ParseFetchResponse() {
410 bool fetch_success = url_fetcher_->GetStatus().is_success();
411 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.FetchSuccess", fetch_success);
413 std::string response;
414 if (fetch_success) {
415 if (!url_fetcher_->GetResponseAsString(&response))
416 response.clear();
418 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.GetResponseSuccess",
419 !response.empty());
420 if (!fetch_success || response.empty()) {
421 ReportErrorViaCallback();
422 return;
424 VLOG(1) << "Got response: " << response;
426 // The response is JSON of the form:
427 // {
428 // "protocol_version": "1",
429 // "signature": "<base64-encoded signature>",
430 // "expiry": "<date in YYYY-MM-DD form>",
431 // "invalid_ids": [ "<id3>", "<id4>" ]
432 // }
433 // where |invalid_ids| is a list of ids from the original request that
434 // could not be verified to be in the webstore.
436 base::DictionaryValue* dictionary = NULL;
437 scoped_ptr<base::Value> parsed = base::JSONReader::Read(response);
438 bool json_success = parsed.get() && parsed->GetAsDictionary(&dictionary);
439 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ParseJsonSuccess",
440 json_success);
441 if (!json_success) {
442 ReportErrorViaCallback();
443 return;
446 int protocol_version = 0;
447 std::string signature_base64;
448 std::string signature;
449 std::string expire_date;
451 dictionary->GetInteger(kProtocolVersionKey, &protocol_version);
452 dictionary->GetString(kSignatureKey, &signature_base64);
453 dictionary->GetString(kExpiryKey, &expire_date);
455 bool fields_success =
456 protocol_version == 1 && !signature_base64.empty() &&
457 ValidateExpireDateFormat(expire_date) &&
458 base::Base64Decode(signature_base64, &signature);
459 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ParseFieldsSuccess",
460 fields_success);
461 if (!fields_success) {
462 ReportErrorViaCallback();
463 return;
466 ExtensionIdSet invalid_ids;
467 const base::ListValue* invalid_ids_list = NULL;
468 if (dictionary->GetList(kInvalidIdsKey, &invalid_ids_list)) {
469 for (size_t i = 0; i < invalid_ids_list->GetSize(); i++) {
470 std::string id;
471 if (!invalid_ids_list->GetString(i, &id)) {
472 ReportErrorViaCallback();
473 return;
475 invalid_ids.insert(id);
479 HandleSignatureResult(signature, expire_date, invalid_ids);
482 void InstallSigner::HandleSignatureResult(const std::string& signature,
483 const std::string& expire_date,
484 const ExtensionIdSet& invalid_ids) {
485 ExtensionIdSet valid_ids =
486 base::STLSetDifference<ExtensionIdSet>(ids_, invalid_ids);
488 scoped_ptr<InstallSignature> result;
489 if (!signature.empty()) {
490 result.reset(new InstallSignature);
491 result->ids = valid_ids;
492 result->invalid_ids = invalid_ids;
493 result->salt = salt_;
494 result->signature = signature;
495 result->expire_date = expire_date;
496 result->timestamp = request_start_time_;
497 bool verified = VerifySignature(*result);
498 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ResultWasValid", verified);
499 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.InvalidCount",
500 invalid_ids.size());
501 if (!verified)
502 result.reset();
505 if (!callback_.is_null())
506 callback_.Run(result.Pass());
510 } // namespace extensions