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"
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"
32 #if defined(ENABLE_RLZ)
33 #include "rlz/lib/machine_id.h"
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" \
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
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
))
77 machine_id
= "unknown";
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
);
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)
97 for (int i
= 0; i
< 10; i
++) {
98 if (i
== 4 || i
== 7) {
101 } else if (!IsAsciiDigit(input
[i
])) {
110 namespace extensions
{
112 InstallSignature::InstallSignature() {
114 InstallSignature::~InstallSignature() {
117 void InstallSignature::ToValue(base::DictionaryValue
* value
) const {
120 base::ListValue
* id_list
= new base::ListValue();
121 for (ExtensionIdSet::const_iterator i
= ids
.begin(); i
!= ids
.end();
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
);
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
)) {
149 return result
.Pass();
152 const base::ListValue
* ids
= NULL
;
153 if (!value
.GetList(kIdsKey
, &ids
)) {
155 return result
.Pass();
158 for (base::ListValue::const_iterator i
= ids
->begin(); i
!= ids
->end(); ++i
) {
160 if (!(*i
)->GetAsString(&id
)) {
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() {
180 bool InstallSigner::VerifySignature(const InstallSignature
& signature
) {
181 if (signature
.ids
.empty())
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
))
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
))
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()),
210 verifier
.VerifyUpdate(reinterpret_cast<const uint8
*>(signed_data
.data()),
212 return verifier
.VerifyFinal();
216 class InstallSigner::FetcherDelegate
: public net::URLFetcherDelegate
{
218 explicit FetcherDelegate(const base::Closure
& callback
)
219 : callback_(callback
) {
222 virtual ~FetcherDelegate() {
225 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) OVERRIDE
{
230 base::Closure callback_
;
231 DISALLOW_COPY_AND_ASSIGN(FetcherDelegate
);
235 ExtensionIdSet
InstallSigner::GetForcedNotFromWebstore() {
236 std::string value
= CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
237 switches::kExtensionsNotWebstore
);
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.
255 if (!callback_
.is_null())
256 callback_
.Run(scoped_ptr
<InstallSignature
>(new InstallSignature()));
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();
270 if (!context_getter_
) {
271 ReportErrorViaCallback();
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:
285 // "protocol_version": "1",
286 // "hash": "<base64-encoded hash value here>",
287 // "ids": [ "<id1>", "id2" ]
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());
298 base::JSONWriter::Write(&dictionary
, &json
);
300 ReportErrorViaCallback();
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
) ||
318 ReportErrorViaCallback();
322 // The response is JSON of the form:
324 // "protocol_version": "1",
325 // "signature": "<base64-encoded signature>",
326 // "expiry": "<date in YYYY-MM-DD form>",
327 // "invalid_ids": [ "<id3>", "<id4>" ]
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();
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();
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
++) {
360 if (!invalid_ids_list
->GetString(i
, &id
)) {
361 ReportErrorViaCallback();
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",
392 if (!callback_
.is_null())
393 callback_
.Run(result
.Pass());
397 } // namespace extensions