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 "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"
34
35#if defined(ENABLE_RLZ)
36#include "rlz/lib/machine_id.h"
37#endif
38
39namespace {
40
41using extensions::ExtensionIdSet;
42
43const char kExpireDateKey[] = "expire_date";
44const char kExpiryKey[] = "expiry";
45const char kHashKey[] = "hash";
46const char kIdsKey[] = "ids";
47const char kInvalidIdsKey[] = "invalid_ids";
48const char kProtocolVersionKey[] = "protocol_version";
49const char kSaltKey[] = "salt";
50const char kSignatureKey[] = "signature";
51const char kSignatureFormatVersionKey[] = "signature_format_version";
52const 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.
57const int kSignatureFormatVersion = 2;
58
59const size_t kSaltBytes = 32;
60
61const char kBackendUrl[] =
62    "https://www.googleapis.com/chromewebstore/v1.1/items/verify";
63
64const 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
75GURL GetBackendUrl() {
76  return GURL(kBackendUrl);
77}
78
79// Hashes |salt| with the machine id, base64-encodes it and returns it in
80// |result|.
81bool 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".
104bool 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|.
120void 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.
133bool 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
153namespace extensions {
154
155InstallSignature::InstallSignature() {
156}
157InstallSignature::~InstallSignature() {
158}
159
160void 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
178scoped_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
226InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter,
227                             const ExtensionIdSet& ids)
228    : ids_(ids), context_getter_(context_getter) {
229}
230
231InstallSigner::~InstallSigner() {
232}
233
234// static
235bool 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(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;
264
265  verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()),
266                        signed_data.size());
267  return verifier.VerifyFinal();
268}
269
270
271class InstallSigner::FetcherDelegate : public net::URLFetcherDelegate {
272 public:
273  explicit FetcherDelegate(const base::Closure& callback)
274      : callback_(callback) {
275  }
276
277  virtual ~FetcherDelegate() {
278  }
279
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
290ExtensionIdSet 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
301namespace {
302
303static int g_request_count = 0;
304
305base::LazyInstance<base::TimeTicks> g_last_request_time =
306    LAZY_INSTANCE_INITIALIZER;
307
308base::LazyInstance<base::ThreadChecker> g_single_thread_checker =
309    LAZY_INSTANCE_INITIALIZER;
310
311void 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
339void 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
403void InstallSigner::ReportErrorViaCallback() {
404  InstallSignature* null_signature = NULL;
405  if (!callback_.is_null())
406    callback_.Run(scoped_ptr<InstallSignature>(null_signature));
407}
408
409void 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
482void 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