Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / install_signer.cc
blobb8f9d58265b422730a394e488987a873d3c849a7
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.
5 #include "chrome/browser/extensions/install_signer.h"
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/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/values.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/extensions/extension_constants.h"
22 #include "crypto/random.h"
23 #include "crypto/secure_hash.h"
24 #include "crypto/sha2.h"
25 #include "crypto/signature_verifier.h"
26 #include "net/url_request/url_fetcher.h"
27 #include "net/url_request/url_fetcher_delegate.h"
28 #include "net/url_request/url_request_context_getter.h"
29 #include "net/url_request/url_request_status.h"
30 #include "url/gurl.h"
32 #if defined(ENABLE_RLZ)
33 #include "rlz/lib/machine_id.h"
34 #endif
36 namespace {
38 using extensions::ExtensionIdSet;
40 const char kExpireDateKey[] = "expire_date";
41 const char kExpiryKey[] = "expiry";
42 const char kHashKey[] = "hash";
43 const char kIdsKey[] = "ids";
44 const char kInvalidIdsKey[] = "invalid_ids";
45 const char kProtocolVersionKey[] = "protocol_version";
46 const char kSaltKey[] = "salt";
47 const char kSignatureKey[] = "signature";
49 const size_t kSaltBytes = 32;
51 const char kBackendUrl[] =
52 "https://www.googleapis.com/chromewebstore/v1.1/items/verify";
54 const char kPublicKeyPEM[] = \
55 "-----BEGIN PUBLIC KEY-----" \
56 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj/u/XDdjlDyw7gHEtaaa" \
57 "sZ9GdG8WOKAyJzXd8HFrDtz2Jcuy7er7MtWvHgNDA0bwpznbI5YdZeV4UfCEsA4S" \
58 "rA5b3MnWTHwA1bgbiDM+L9rrqvcadcKuOlTeN48Q0ijmhHlNFbTzvT9W0zw/GKv8" \
59 "LgXAHggxtmHQ/Z9PP2QNF5O8rUHHSL4AJ6hNcEKSBVSmbbjeVm4gSXDuED5r0nwx" \
60 "vRtupDxGYp8IZpP5KlExqNu1nbkPc+igCTIB6XsqijagzxewUHCdovmkb2JNtskx" \
61 "/PMIEv+TvWIx2BzqGp71gSh/dV7SJ3rClvWd2xj8dtxG8FfAWDTIIi0qZXWn2Qhi" \
62 "zQIDAQAB" \
63 "-----END PUBLIC KEY-----";
65 GURL GetBackendUrl() {
66 return GURL(kBackendUrl);
69 // Hashes |salt| with the machine id, base64-encodes it and returns it in
70 // |result|.
71 bool HashWithMachineId(const std::string& salt, std::string* result) {
72 std::string machine_id;
73 #if defined(ENABLE_RLZ)
74 if (!rlz_lib::GetMachineId(&machine_id))
75 return false;
76 #else
77 machine_id = "unknown";
78 #endif
80 scoped_ptr<crypto::SecureHash> hash(
81 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
83 hash->Update(machine_id.data(), machine_id.size());
84 hash->Update(salt.data(), salt.size());
86 std::string result_bytes(crypto::kSHA256Length, 0);
87 hash->Finish(string_as_array(&result_bytes), result_bytes.size());
89 base::Base64Encode(result_bytes, result);
90 return true;
93 // Validates that |input| is a string of the form "YYYY-MM-DD".
94 bool ValidateExpireDateFormat(const std::string& input) {
95 if (input.length() != 10)
96 return false;
97 for (int i = 0; i < 10; i++) {
98 if (i == 4 || i == 7) {
99 if (input[i] != '-')
100 return false;
101 } else if (!IsAsciiDigit(input[i])) {
102 return false;
105 return true;
108 } // namespace
110 namespace extensions {
112 InstallSignature::InstallSignature() {
114 InstallSignature::~InstallSignature() {
117 void InstallSignature::ToValue(base::DictionaryValue* value) const {
118 CHECK(value);
120 base::ListValue* id_list = new base::ListValue();
121 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end();
122 ++i)
123 id_list->AppendString(*i);
125 value->Set(kIdsKey, id_list);
126 value->SetString(kExpireDateKey, expire_date);
127 std::string salt_base64;
128 std::string signature_base64;
129 base::Base64Encode(salt, &salt_base64);
130 base::Base64Encode(signature, &signature_base64);
131 value->SetString(kSaltKey, salt_base64);
132 value->SetString(kSignatureKey, signature_base64);
135 // static
136 scoped_ptr<InstallSignature> InstallSignature::FromValue(
137 const base::DictionaryValue& value) {
139 scoped_ptr<InstallSignature> result(new InstallSignature);
141 std::string salt_base64;
142 std::string signature_base64;
143 if (!value.GetString(kExpireDateKey, &result->expire_date) ||
144 !value.GetString(kSaltKey, &salt_base64) ||
145 !value.GetString(kSignatureKey, &signature_base64) ||
146 !base::Base64Decode(salt_base64, &result->salt) ||
147 !base::Base64Decode(signature_base64, &result->signature)) {
148 result.reset();
149 return result.Pass();
152 const base::ListValue* ids = NULL;
153 if (!value.GetList(kIdsKey, &ids)) {
154 result.reset();
155 return result.Pass();
158 for (base::ListValue::const_iterator i = ids->begin(); i != ids->end(); ++i) {
159 std::string id;
160 if (!(*i)->GetAsString(&id)) {
161 result.reset();
162 return result.Pass();
164 result->ids.insert(id);
167 return result.Pass();
171 InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter,
172 const ExtensionIdSet& ids)
173 : ids_(ids), context_getter_(context_getter) {
176 InstallSigner::~InstallSigner() {
179 // static
180 bool InstallSigner::VerifySignature(const InstallSignature& signature) {
181 if (signature.ids.empty())
182 return true;
184 std::string signed_data;
185 for (ExtensionIdSet::const_iterator i = signature.ids.begin();
186 i != signature.ids.end(); ++i)
187 signed_data.append(*i);
189 std::string hash_base64;
190 if (!HashWithMachineId(signature.salt, &hash_base64))
191 return false;
192 signed_data.append(hash_base64);
194 signed_data.append(signature.expire_date);
196 std::string public_key;
197 if (!Extension::ParsePEMKeyBytes(kPublicKeyPEM, &public_key))
198 return false;
200 crypto::SignatureVerifier verifier;
201 if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm,
202 sizeof(extension_misc::kSignatureAlgorithm),
203 reinterpret_cast<const uint8*>(
204 signature.signature.data()),
205 signature.signature.size(),
206 reinterpret_cast<const uint8*>(public_key.data()),
207 public_key.size()))
208 return false;
210 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()),
211 signed_data.size());
212 return verifier.VerifyFinal();
216 class InstallSigner::FetcherDelegate : public net::URLFetcherDelegate {
217 public:
218 explicit FetcherDelegate(const base::Closure& callback)
219 : callback_(callback) {
222 virtual ~FetcherDelegate() {
225 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
226 callback_.Run();
229 private:
230 base::Closure callback_;
231 DISALLOW_COPY_AND_ASSIGN(FetcherDelegate);
234 // static
235 ExtensionIdSet InstallSigner::GetForcedNotFromWebstore() {
236 std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
237 switches::kExtensionsNotWebstore);
238 if (value.empty())
239 return ExtensionIdSet();
241 std::vector<std::string> ids;
242 base::SplitString(value, ',', &ids);
243 return ExtensionIdSet(ids.begin(), ids.end());
246 void InstallSigner::GetSignature(const SignatureCallback& callback) {
247 CHECK(!url_fetcher_.get());
248 CHECK(callback_.is_null());
249 CHECK(salt_.empty());
250 callback_ = callback;
252 // If the set of ids is empty, just return an empty signature and skip the
253 // call to the server.
254 if (ids_.empty()) {
255 if (!callback_.is_null())
256 callback_.Run(scoped_ptr<InstallSignature>(new InstallSignature()));
257 return;
260 salt_ = std::string(kSaltBytes, 0);
261 DCHECK_EQ(kSaltBytes, salt_.size());
262 crypto::RandBytes(string_as_array(&salt_), salt_.size());
264 std::string hash_base64;
265 if (!HashWithMachineId(salt_, &hash_base64)) {
266 ReportErrorViaCallback();
267 return;
270 if (!context_getter_) {
271 ReportErrorViaCallback();
272 return;
275 base::Closure closure = base::Bind(&InstallSigner::ParseFetchResponse,
276 base::Unretained(this));
278 delegate_.reset(new FetcherDelegate(closure));
279 url_fetcher_.reset(net::URLFetcher::Create(
280 GetBackendUrl(), net::URLFetcher::POST, delegate_.get()));
281 url_fetcher_->SetRequestContext(context_getter_);
283 // The request protocol is JSON of the form:
284 // {
285 // "protocol_version": "1",
286 // "hash": "<base64-encoded hash value here>",
287 // "ids": [ "<id1>", "id2" ]
288 // }
289 base::DictionaryValue dictionary;
290 dictionary.SetInteger(kProtocolVersionKey, 1);
291 dictionary.SetString(kHashKey, hash_base64);
292 scoped_ptr<base::ListValue> id_list(new base::ListValue);
293 for (ExtensionIdSet::const_iterator i = ids_.begin(); i != ids_.end(); ++i) {
294 id_list->AppendString(*i);
296 dictionary.Set(kIdsKey, id_list.release());
297 std::string json;
298 base::JSONWriter::Write(&dictionary, &json);
299 if (json.empty()) {
300 ReportErrorViaCallback();
301 return;
303 url_fetcher_->SetUploadData("application/json", json);
304 url_fetcher_->Start();
307 void InstallSigner::ReportErrorViaCallback() {
308 InstallSignature* null_signature = NULL;
309 if (!callback_.is_null())
310 callback_.Run(scoped_ptr<InstallSignature>(null_signature));
313 void InstallSigner::ParseFetchResponse() {
314 std::string response;
315 if (!url_fetcher_->GetStatus().is_success() ||
316 !url_fetcher_->GetResponseAsString(&response) ||
317 response.empty()) {
318 ReportErrorViaCallback();
319 return;
322 // The response is JSON of the form:
323 // {
324 // "protocol_version": "1",
325 // "signature": "<base64-encoded signature>",
326 // "expiry": "<date in YYYY-MM-DD form>",
327 // "invalid_ids": [ "<id3>", "<id4>" ]
328 // }
329 // where |invalid_ids| is a list of ids from the original request that
330 // could not be verified to be in the webstore.
332 base::DictionaryValue* dictionary = NULL;
333 scoped_ptr<base::Value> parsed(base::JSONReader::Read(response));
334 if (!parsed.get() || !parsed->GetAsDictionary(&dictionary)) {
335 ReportErrorViaCallback();
336 return;
339 int protocol_version = 0;
340 std::string signature_base64;
341 std::string signature;
342 std::string expire_date;
344 dictionary->GetInteger(kProtocolVersionKey, &protocol_version);
345 dictionary->GetString(kSignatureKey, &signature_base64);
346 dictionary->GetString(kExpiryKey, &expire_date);
348 if (protocol_version != 1 || signature_base64.empty() ||
349 !ValidateExpireDateFormat(expire_date) ||
350 !base::Base64Decode(signature_base64, &signature)) {
351 ReportErrorViaCallback();
352 return;
355 ExtensionIdSet invalid_ids;
356 const base::ListValue* invalid_ids_list = NULL;
357 if (dictionary->GetList(kInvalidIdsKey, &invalid_ids_list)) {
358 for (size_t i = 0; i < invalid_ids_list->GetSize(); i++) {
359 std::string id;
360 if (!invalid_ids_list->GetString(i, &id)) {
361 ReportErrorViaCallback();
362 return;
364 invalid_ids.insert(id);
368 HandleSignatureResult(signature, expire_date, invalid_ids);
371 void InstallSigner::HandleSignatureResult(const std::string& signature,
372 const std::string& expire_date,
373 const ExtensionIdSet& invalid_ids) {
374 ExtensionIdSet valid_ids =
375 base::STLSetDifference<ExtensionIdSet>(ids_, invalid_ids);
377 scoped_ptr<InstallSignature> result;
378 if (!signature.empty()) {
379 result.reset(new InstallSignature);
380 result->ids = valid_ids;
381 result->salt = salt_;
382 result->signature = signature;
383 result->expire_date = expire_date;
384 bool verified = VerifySignature(*result);
385 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ResultWasValid", verified);
386 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.InvalidCount",
387 invalid_ids.size());
388 if (!verified)
389 result.reset();
392 if (!callback_.is_null())
393 callback_.Run(result.Pass());
397 } // namespace extensions