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 "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, &timestamp) ||
    164         !base::StringToInt64(timestamp, &timestamp_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