Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / install_signer.cc
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 (base::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<base::ListValue> id_list(new base::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     UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.InvalidCount",
461                              invalid_ids.size());
462     if (!verified)
463       result.reset();
464   }
465
466   if (!callback_.is_null())
467     callback_.Run(result.Pass());
468 }
469
470
471 }  // namespace extensions