Home | History | Annotate | Download | only in extensions
      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 
     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 
     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|.
     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".
    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|.
    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.
    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 
    155 InstallSignature::InstallSignature() {
    156 }
    157 InstallSignature::~InstallSignature() {
    158 }
    159 
    160 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
    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 
    226 InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter,
    227                              const ExtensionIdSet& ids)
    228     : ids_(ids), context_getter_(context_getter) {
    229 }
    230 
    231 InstallSigner::~InstallSigner() {
    232 }
    233 
    234 // static
    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(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 
    271 class 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
    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 
    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 
    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 
    403 void InstallSigner::ReportErrorViaCallback() {
    404   InstallSignature* null_signature = NULL;
    405   if (!callback_.is_null())
    406     callback_.Run(scoped_ptr<InstallSignature>(null_signature));
    407 }
    408 
    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 
    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