• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "crypto/random.h"
25 #include "crypto/secure_hash.h"
26 #include "crypto/sha2.h"
27 #include "crypto/signature_verifier.h"
28 #include "extensions/common/constants.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 kSignatureFormatVersionKey[] = "signature_format_version";
52 const char kTimestampKey[] = "timestamp";
53 
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;
58 
59 const size_t kSaltBytes = 32;
60 
61 const char kBackendUrl[] =
62     "https://www.googleapis.com/chromewebstore/v1.1/items/verify";
63 
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-----";
74 
GetBackendUrl()75 GURL GetBackendUrl() {
76   return GURL(kBackendUrl);
77 }
78 
79 // Hashes |salt| with the machine id, base64-encodes it and returns it in
80 // |result|.
HashWithMachineId(const std::string & salt,std::string * 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
89 
90   scoped_ptr<crypto::SecureHash> hash(
91       crypto::SecureHash::Create(crypto::SecureHash::SHA256));
92 
93   hash->Update(machine_id.data(), machine_id.size());
94   hash->Update(salt.data(), salt.size());
95 
96   std::string result_bytes(crypto::kSHA256Length, 0);
97   hash->Finish(string_as_array(&result_bytes), result_bytes.size());
98 
99   base::Base64Encode(result_bytes, result);
100   return true;
101 }
102 
103 // Validates that |input| is a string of the form "YYYY-MM-DD".
ValidateExpireDateFormat(const std::string & input)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 (!IsAsciiDigit(input[i])) {
112       return false;
113     }
114   }
115   return true;
116 }
117 
118 // Sets the value of |key| in |dictionary| to be a list with the contents of
119 // |ids|.
SetExtensionIdSet(base::DictionaryValue * dictionary,const char * key,const ExtensionIdSet & 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);
127 }
128 
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.
GetExtensionIdSet(const base::DictionaryValue & dictionary,const char * key,ExtensionIdSet * ids)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;
145     }
146     ids->insert(id);
147   }
148   return true;
149 }
150 
151 }  // namespace
152 
153 namespace extensions {
154 
InstallSignature()155 InstallSignature::InstallSignature() {
156 }
~InstallSignature()157 InstallSignature::~InstallSignature() {
158 }
159 
ToValue(base::DictionaryValue * value) const160 void InstallSignature::ToValue(base::DictionaryValue* value) const {
161   CHECK(value);
162 
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()));
175 }
176 
177 // static
FromValue(const base::DictionaryValue & value)178 scoped_ptr<InstallSignature> InstallSignature::FromValue(
179     const base::DictionaryValue& value) {
180 
181   scoped_ptr<InstallSignature> result(new InstallSignature);
182 
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();
190   }
191 
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();
201   }
202 
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();
212     }
213     result->timestamp = base::Time::FromInternalValue(timestamp_value);
214   }
215 
216   if (!GetExtensionIdSet(value, kIdsKey, &result->ids) ||
217       !GetExtensionIdSet(value, kInvalidIdsKey, &result->invalid_ids)) {
218     result.reset();
219     return result.Pass();
220   }
221 
222   return result.Pass();
223 }
224 
225 
InstallSigner(net::URLRequestContextGetter * context_getter,const ExtensionIdSet & ids)226 InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter,
227                              const ExtensionIdSet& ids)
228     : ids_(ids), context_getter_(context_getter) {
229 }
230 
~InstallSigner()231 InstallSigner::~InstallSigner() {
232 }
233 
234 // static
VerifySignature(const InstallSignature & signature)235 bool InstallSigner::VerifySignature(const InstallSignature& signature) {
236   if (signature.ids.empty())
237     return true;
238 
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);
243 
244   std::string hash_base64;
245   if (!HashWithMachineId(signature.salt, &hash_base64))
246     return false;
247   signed_data.append(hash_base64);
248 
249   signed_data.append(signature.expire_date);
250 
251   std::string public_key;
252   if (!Extension::ParsePEMKeyBytes(kPublicKeyPEM, &public_key))
253     return false;
254 
255   crypto::SignatureVerifier verifier;
256   if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm,
257                            sizeof(extension_misc::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;
264 
265   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()),
266                         signed_data.size());
267   return verifier.VerifyFinal();
268 }
269 
270 
271 class InstallSigner::FetcherDelegate : public net::URLFetcherDelegate {
272  public:
FetcherDelegate(const base::Closure & callback)273   explicit FetcherDelegate(const base::Closure& callback)
274       : callback_(callback) {
275   }
276 
~FetcherDelegate()277   virtual ~FetcherDelegate() {
278   }
279 
OnURLFetchComplete(const net::URLFetcher * source)280   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
281     callback_.Run();
282   }
283 
284  private:
285   base::Closure callback_;
286   DISALLOW_COPY_AND_ASSIGN(FetcherDelegate);
287 };
288 
289 // static
GetForcedNotFromWebstore()290 ExtensionIdSet InstallSigner::GetForcedNotFromWebstore() {
291   std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
292       switches::kExtensionsNotWebstore);
293   if (value.empty())
294     return ExtensionIdSet();
295 
296   std::vector<std::string> ids;
297   base::SplitString(value, ',', &ids);
298   return ExtensionIdSet(ids.begin(), ids.end());
299 }
300 
301 namespace {
302 
303 static int g_request_count = 0;
304 
305 base::LazyInstance<base::TimeTicks> g_last_request_time =
306     LAZY_INSTANCE_INITIALIZER;
307 
308 base::LazyInstance<base::ThreadChecker> g_single_thread_checker =
309     LAZY_INSTANCE_INITIALIZER;
310 
LogRequestStartHistograms()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());
315 
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)
323 
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());
331 
332   g_request_count += 1;
333   UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.RequestCount",
334                            g_request_count);
335 }
336 
337 }  // namespace
338 
GetSignature(const SignatureCallback & callback)339 void InstallSigner::GetSignature(const SignatureCallback& callback) {
340   CHECK(!url_fetcher_.get());
341   CHECK(callback_.is_null());
342   CHECK(salt_.empty());
343   callback_ = callback;
344 
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;
351   }
352 
353   salt_ = std::string(kSaltBytes, 0);
354   DCHECK_EQ(kSaltBytes, salt_.size());
355   crypto::RandBytes(string_as_array(&salt_), salt_.size());
356 
357   std::string hash_base64;
358   if (!HashWithMachineId(salt_, &hash_base64)) {
359     ReportErrorViaCallback();
360     return;
361   }
362 
363   if (!context_getter_) {
364     ReportErrorViaCallback();
365     return;
366   }
367 
368   base::Closure closure = base::Bind(&InstallSigner::ParseFetchResponse,
369                                      base::Unretained(this));
370 
371   delegate_.reset(new FetcherDelegate(closure));
372   url_fetcher_.reset(net::URLFetcher::Create(
373       GetBackendUrl(), net::URLFetcher::POST, delegate_.get()));
374   url_fetcher_->SetRequestContext(context_getter_);
375 
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);
388   }
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;
395   }
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();
401 }
402 
ReportErrorViaCallback()403 void InstallSigner::ReportErrorViaCallback() {
404   InstallSignature* null_signature = NULL;
405   if (!callback_.is_null())
406     callback_.Run(scoped_ptr<InstallSignature>(null_signature));
407 }
408 
ParseFetchResponse()409 void InstallSigner::ParseFetchResponse() {
410   bool fetch_success = url_fetcher_->GetStatus().is_success();
411   UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.FetchSuccess", fetch_success);
412 
413   std::string response;
414   if (fetch_success) {
415     if (!url_fetcher_->GetResponseAsString(&response))
416       response.clear();
417   }
418   UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.GetResponseSuccess",
419                         !response.empty());
420   if (!fetch_success || response.empty()) {
421     ReportErrorViaCallback();
422     return;
423   }
424   VLOG(1) << "Got response: " << response;
425 
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.
435 
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;
444   }
445 
446   int protocol_version = 0;
447   std::string signature_base64;
448   std::string signature;
449   std::string expire_date;
450 
451   dictionary->GetInteger(kProtocolVersionKey, &protocol_version);
452   dictionary->GetString(kSignatureKey, &signature_base64);
453   dictionary->GetString(kExpiryKey, &expire_date);
454 
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;
464   }
465 
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;
474       }
475       invalid_ids.insert(id);
476     }
477   }
478 
479   HandleSignatureResult(signature, expire_date, invalid_ids);
480 }
481 
HandleSignatureResult(const std::string & signature,const std::string & expire_date,const ExtensionIdSet & 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);
487 
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();
503   }
504 
505   if (!callback_.is_null())
506     callback_.Run(result.Pass());
507 }
508 
509 
510 }  // namespace extensions
511