OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/install_signer.h" | 5 #include "chrome/browser/extensions/install_signer.h" |
6 | 6 |
7 #include "base/base64.h" | 7 #include "base/base64.h" |
8 #include "base/bind.h" | 8 #include "base/bind.h" |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/json/json_reader.h" | 10 #include "base/json/json_reader.h" |
(...skipping 30 matching lines...) Expand all Loading... |
41 using extensions::ExtensionIdSet; | 41 using extensions::ExtensionIdSet; |
42 | 42 |
43 const char kExpireDateKey[] = "expire_date"; | 43 const char kExpireDateKey[] = "expire_date"; |
44 const char kExpiryKey[] = "expiry"; | 44 const char kExpiryKey[] = "expiry"; |
45 const char kHashKey[] = "hash"; | 45 const char kHashKey[] = "hash"; |
46 const char kIdsKey[] = "ids"; | 46 const char kIdsKey[] = "ids"; |
47 const char kInvalidIdsKey[] = "invalid_ids"; | 47 const char kInvalidIdsKey[] = "invalid_ids"; |
48 const char kProtocolVersionKey[] = "protocol_version"; | 48 const char kProtocolVersionKey[] = "protocol_version"; |
49 const char kSaltKey[] = "salt"; | 49 const char kSaltKey[] = "salt"; |
50 const char kSignatureKey[] = "signature"; | 50 const char kSignatureKey[] = "signature"; |
| 51 const char kSignatureFormatVersionKey[] = "signature_format_version"; |
51 const char kTimestampKey[] = "timestamp"; | 52 const char kTimestampKey[] = "timestamp"; |
52 | 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 |
53 const size_t kSaltBytes = 32; | 59 const size_t kSaltBytes = 32; |
54 | 60 |
55 const char kBackendUrl[] = | 61 const char kBackendUrl[] = |
56 "https://www.googleapis.com/chromewebstore/v1.1/items/verify"; | 62 "https://www.googleapis.com/chromewebstore/v1.1/items/verify"; |
57 | 63 |
58 const char kPublicKeyPEM[] = \ | 64 const char kPublicKeyPEM[] = \ |
59 "-----BEGIN PUBLIC KEY-----" \ | 65 "-----BEGIN PUBLIC KEY-----" \ |
60 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj/u/XDdjlDyw7gHEtaaa" \ | 66 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj/u/XDdjlDyw7gHEtaaa" \ |
61 "sZ9GdG8WOKAyJzXd8HFrDtz2Jcuy7er7MtWvHgNDA0bwpznbI5YdZeV4UfCEsA4S" \ | 67 "sZ9GdG8WOKAyJzXd8HFrDtz2Jcuy7er7MtWvHgNDA0bwpznbI5YdZeV4UfCEsA4S" \ |
62 "rA5b3MnWTHwA1bgbiDM+L9rrqvcadcKuOlTeN48Q0ijmhHlNFbTzvT9W0zw/GKv8" \ | 68 "rA5b3MnWTHwA1bgbiDM+L9rrqvcadcKuOlTeN48Q0ijmhHlNFbTzvT9W0zw/GKv8" \ |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
102 if (i == 4 || i == 7) { | 108 if (i == 4 || i == 7) { |
103 if (input[i] != '-') | 109 if (input[i] != '-') |
104 return false; | 110 return false; |
105 } else if (!IsAsciiDigit(input[i])) { | 111 } else if (!IsAsciiDigit(input[i])) { |
106 return false; | 112 return false; |
107 } | 113 } |
108 } | 114 } |
109 return true; | 115 return true; |
110 } | 116 } |
111 | 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 |
112 } // namespace | 151 } // namespace |
113 | 152 |
114 namespace extensions { | 153 namespace extensions { |
115 | 154 |
116 InstallSignature::InstallSignature() { | 155 InstallSignature::InstallSignature() { |
117 } | 156 } |
118 InstallSignature::~InstallSignature() { | 157 InstallSignature::~InstallSignature() { |
119 } | 158 } |
120 | 159 |
121 void InstallSignature::ToValue(base::DictionaryValue* value) const { | 160 void InstallSignature::ToValue(base::DictionaryValue* value) const { |
122 CHECK(value); | 161 CHECK(value); |
123 | 162 |
124 base::ListValue* id_list = new base::ListValue(); | 163 value->SetInteger(kSignatureFormatVersionKey, kSignatureFormatVersion); |
125 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); | 164 SetExtensionIdSet(value, kIdsKey, ids); |
126 ++i) | 165 SetExtensionIdSet(value, kInvalidIdsKey, invalid_ids); |
127 id_list->AppendString(*i); | |
128 | |
129 value->Set(kIdsKey, id_list); | |
130 value->SetString(kExpireDateKey, expire_date); | 166 value->SetString(kExpireDateKey, expire_date); |
131 std::string salt_base64; | 167 std::string salt_base64; |
132 std::string signature_base64; | 168 std::string signature_base64; |
133 base::Base64Encode(salt, &salt_base64); | 169 base::Base64Encode(salt, &salt_base64); |
134 base::Base64Encode(signature, &signature_base64); | 170 base::Base64Encode(signature, &signature_base64); |
135 value->SetString(kSaltKey, salt_base64); | 171 value->SetString(kSaltKey, salt_base64); |
136 value->SetString(kSignatureKey, signature_base64); | 172 value->SetString(kSignatureKey, signature_base64); |
137 value->SetString(kTimestampKey, | 173 value->SetString(kTimestampKey, |
138 base::Int64ToString(timestamp.ToInternalValue())); | 174 base::Int64ToString(timestamp.ToInternalValue())); |
139 } | 175 } |
140 | 176 |
141 // static | 177 // static |
142 scoped_ptr<InstallSignature> InstallSignature::FromValue( | 178 scoped_ptr<InstallSignature> InstallSignature::FromValue( |
143 const base::DictionaryValue& value) { | 179 const base::DictionaryValue& value) { |
144 | 180 |
145 scoped_ptr<InstallSignature> result(new InstallSignature); | 181 scoped_ptr<InstallSignature> result(new InstallSignature); |
146 | 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 |
147 std::string salt_base64; | 192 std::string salt_base64; |
148 std::string signature_base64; | 193 std::string signature_base64; |
149 if (!value.GetString(kExpireDateKey, &result->expire_date) || | 194 if (!value.GetString(kExpireDateKey, &result->expire_date) || |
150 !value.GetString(kSaltKey, &salt_base64) || | 195 !value.GetString(kSaltKey, &salt_base64) || |
151 !value.GetString(kSignatureKey, &signature_base64) || | 196 !value.GetString(kSignatureKey, &signature_base64) || |
152 !base::Base64Decode(salt_base64, &result->salt) || | 197 !base::Base64Decode(salt_base64, &result->salt) || |
153 !base::Base64Decode(signature_base64, &result->signature)) { | 198 !base::Base64Decode(signature_base64, &result->signature)) { |
154 result.reset(); | 199 result.reset(); |
155 return result.Pass(); | 200 return result.Pass(); |
156 } | 201 } |
157 | 202 |
158 // Note: earlier versions of the code did not write out a timestamp value | 203 // Note: earlier versions of the code did not write out a timestamp value |
159 // so older entries will not necessarily have this. | 204 // so older entries will not necessarily have this. |
160 if (value.HasKey(kTimestampKey)) { | 205 if (value.HasKey(kTimestampKey)) { |
161 std::string timestamp; | 206 std::string timestamp; |
162 int64 timestamp_value = 0; | 207 int64 timestamp_value = 0; |
163 if (!value.GetString(kTimestampKey, ×tamp) || | 208 if (!value.GetString(kTimestampKey, ×tamp) || |
164 !base::StringToInt64(timestamp, ×tamp_value)) { | 209 !base::StringToInt64(timestamp, ×tamp_value)) { |
165 result.reset(); | 210 result.reset(); |
166 return result.Pass(); | 211 return result.Pass(); |
167 } | 212 } |
168 result->timestamp = base::Time::FromInternalValue(timestamp_value); | 213 result->timestamp = base::Time::FromInternalValue(timestamp_value); |
169 } | 214 } |
170 | 215 |
171 const base::ListValue* ids = NULL; | 216 if (!GetExtensionIdSet(value, kIdsKey, &result->ids) || |
172 if (!value.GetList(kIdsKey, &ids)) { | 217 !GetExtensionIdSet(value, kInvalidIdsKey, &result->invalid_ids)) { |
173 result.reset(); | 218 result.reset(); |
174 return result.Pass(); | 219 return result.Pass(); |
175 } | 220 } |
176 | 221 |
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(); | 222 return result.Pass(); |
187 } | 223 } |
188 | 224 |
189 | 225 |
190 InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter, | 226 InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter, |
191 const ExtensionIdSet& ids) | 227 const ExtensionIdSet& ids) |
192 : ids_(ids), context_getter_(context_getter) { | 228 : ids_(ids), context_getter_(context_getter) { |
193 } | 229 } |
194 | 230 |
195 InstallSigner::~InstallSigner() { | 231 InstallSigner::~InstallSigner() { |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
353 dictionary.Set(kIdsKey, id_list.release()); | 389 dictionary.Set(kIdsKey, id_list.release()); |
354 std::string json; | 390 std::string json; |
355 base::JSONWriter::Write(&dictionary, &json); | 391 base::JSONWriter::Write(&dictionary, &json); |
356 if (json.empty()) { | 392 if (json.empty()) { |
357 ReportErrorViaCallback(); | 393 ReportErrorViaCallback(); |
358 return; | 394 return; |
359 } | 395 } |
360 url_fetcher_->SetUploadData("application/json", json); | 396 url_fetcher_->SetUploadData("application/json", json); |
361 LogRequestStartHistograms(); | 397 LogRequestStartHistograms(); |
362 request_start_time_ = base::Time::Now(); | 398 request_start_time_ = base::Time::Now(); |
| 399 VLOG(1) << "Sending request: " << json; |
363 url_fetcher_->Start(); | 400 url_fetcher_->Start(); |
364 } | 401 } |
365 | 402 |
366 void InstallSigner::ReportErrorViaCallback() { | 403 void InstallSigner::ReportErrorViaCallback() { |
367 InstallSignature* null_signature = NULL; | 404 InstallSignature* null_signature = NULL; |
368 if (!callback_.is_null()) | 405 if (!callback_.is_null()) |
369 callback_.Run(scoped_ptr<InstallSignature>(null_signature)); | 406 callback_.Run(scoped_ptr<InstallSignature>(null_signature)); |
370 } | 407 } |
371 | 408 |
372 void InstallSigner::ParseFetchResponse() { | 409 void InstallSigner::ParseFetchResponse() { |
373 bool fetch_success = url_fetcher_->GetStatus().is_success(); | 410 bool fetch_success = url_fetcher_->GetStatus().is_success(); |
374 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.FetchSuccess", fetch_success); | 411 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.FetchSuccess", fetch_success); |
375 | 412 |
376 std::string response; | 413 std::string response; |
377 if (fetch_success) { | 414 if (fetch_success) { |
378 if (!url_fetcher_->GetResponseAsString(&response)) | 415 if (!url_fetcher_->GetResponseAsString(&response)) |
379 response.clear(); | 416 response.clear(); |
380 } | 417 } |
381 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.GetResponseSuccess", | 418 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.GetResponseSuccess", |
382 !response.empty()); | 419 !response.empty()); |
383 if (!fetch_success || response.empty()) { | 420 if (!fetch_success || response.empty()) { |
384 ReportErrorViaCallback(); | 421 ReportErrorViaCallback(); |
385 return; | 422 return; |
386 } | 423 } |
| 424 VLOG(1) << "Got response: " << response; |
387 | 425 |
388 // The response is JSON of the form: | 426 // The response is JSON of the form: |
389 // { | 427 // { |
390 // "protocol_version": "1", | 428 // "protocol_version": "1", |
391 // "signature": "<base64-encoded signature>", | 429 // "signature": "<base64-encoded signature>", |
392 // "expiry": "<date in YYYY-MM-DD form>", | 430 // "expiry": "<date in YYYY-MM-DD form>", |
393 // "invalid_ids": [ "<id3>", "<id4>" ] | 431 // "invalid_ids": [ "<id3>", "<id4>" ] |
394 // } | 432 // } |
395 // where |invalid_ids| is a list of ids from the original request that | 433 // where |invalid_ids| is a list of ids from the original request that |
396 // could not be verified to be in the webstore. | 434 // could not be verified to be in the webstore. |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
444 void InstallSigner::HandleSignatureResult(const std::string& signature, | 482 void InstallSigner::HandleSignatureResult(const std::string& signature, |
445 const std::string& expire_date, | 483 const std::string& expire_date, |
446 const ExtensionIdSet& invalid_ids) { | 484 const ExtensionIdSet& invalid_ids) { |
447 ExtensionIdSet valid_ids = | 485 ExtensionIdSet valid_ids = |
448 base::STLSetDifference<ExtensionIdSet>(ids_, invalid_ids); | 486 base::STLSetDifference<ExtensionIdSet>(ids_, invalid_ids); |
449 | 487 |
450 scoped_ptr<InstallSignature> result; | 488 scoped_ptr<InstallSignature> result; |
451 if (!signature.empty()) { | 489 if (!signature.empty()) { |
452 result.reset(new InstallSignature); | 490 result.reset(new InstallSignature); |
453 result->ids = valid_ids; | 491 result->ids = valid_ids; |
| 492 result->invalid_ids = invalid_ids; |
454 result->salt = salt_; | 493 result->salt = salt_; |
455 result->signature = signature; | 494 result->signature = signature; |
456 result->expire_date = expire_date; | 495 result->expire_date = expire_date; |
457 result->timestamp = request_start_time_; | 496 result->timestamp = request_start_time_; |
458 bool verified = VerifySignature(*result); | 497 bool verified = VerifySignature(*result); |
459 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ResultWasValid", verified); | 498 UMA_HISTOGRAM_BOOLEAN("ExtensionInstallSigner.ResultWasValid", verified); |
460 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.InvalidCount", | 499 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallSigner.InvalidCount", |
461 invalid_ids.size()); | 500 invalid_ids.size()); |
462 if (!verified) | 501 if (!verified) |
463 result.reset(); | 502 result.reset(); |
464 } | 503 } |
465 | 504 |
466 if (!callback_.is_null()) | 505 if (!callback_.is_null()) |
467 callback_.Run(result.Pass()); | 506 callback_.Run(result.Pass()); |
468 } | 507 } |
469 | 508 |
470 | 509 |
471 } // namespace extensions | 510 } // namespace extensions |
OLD | NEW |