Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / platform_keys / platform_keys_api.cc
blob33dbcc6e31247b115f3424cb9653e2fb06a0f326
1 // Copyright 2015 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/api/platform_keys/platform_keys_api.h"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "base/values.h"
13 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
14 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
15 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
16 #include "chrome/browser/extensions/api/platform_keys/verify_trust_api.h"
17 #include "chrome/common/extensions/api/platform_keys_internal.h"
18 #include "components/web_modal/popup_manager.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "net/base/net_errors.h"
21 #include "net/cert/x509_certificate.h"
23 namespace extensions {
25 namespace api_pk = api::platform_keys;
26 namespace api_pki = api::platform_keys_internal;
28 namespace {
30 const char kErrorAlgorithmNotSupported[] = "Algorithm not supported.";
31 const char kErrorAlgorithmNotPermittedByCertificate[] =
32 "The requested Algorithm is not permitted by the certificate.";
33 const char kErrorInteractiveCallFromBackground[] =
34 "Interactive calls must happen in the context of a browser tab or a "
35 "window.";
37 const char kWebCryptoRSASSA_PKCS1_v1_5[] = "RSASSA-PKCS1-v1_5";
39 struct PublicKeyInfo {
40 // The X.509 Subject Public Key Info of the key in DER encoding.
41 std::string public_key_spki_der;
43 // The type of the key.
44 net::X509Certificate::PublicKeyType key_type =
45 net::X509Certificate::kPublicKeyTypeUnknown;
47 // The size of the key in bits.
48 size_t key_size_bits = 0;
51 // Builds a partial WebCrypto Algorithm object from the parameters available in
52 // |key_info|, which must the info of an RSA key. This doesn't include sign/hash
53 // parameters and thus isn't complete.
54 // platform_keys::GetPublicKey() enforced the public exponent 65537.
55 void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo& key_info,
56 base::DictionaryValue* algorithm) {
57 CHECK_EQ(net::X509Certificate::kPublicKeyTypeRSA, key_info.key_type);
58 algorithm->SetStringWithoutPathExpansion("name", kWebCryptoRSASSA_PKCS1_v1_5);
59 algorithm->SetIntegerWithoutPathExpansion("modulusLength",
60 key_info.key_size_bits);
62 // Equals 65537.
63 const unsigned char defaultPublicExponent[] = {0x01, 0x00, 0x01};
64 algorithm->SetWithoutPathExpansion(
65 "publicExponent",
66 base::BinaryValue::CreateWithCopiedBuffer(
67 reinterpret_cast<const char*>(defaultPublicExponent),
68 arraysize(defaultPublicExponent)));
71 const struct NameValuePair {
72 const char* const name;
73 const int value;
74 } kCertStatusErrors[] = {
75 #define CERT_STATUS_FLAG(name, value) \
76 { #name, value } \
78 #include "net/cert/cert_status_flags_list.h"
79 #undef CERT_STATUS_FLAG
82 } // namespace
84 namespace platform_keys {
86 const char kErrorInvalidToken[] = "The token is not valid.";
87 const char kErrorInvalidX509Cert[] =
88 "Certificate is not a valid X.509 certificate.";
89 const char kTokenIdUser[] = "user";
90 const char kTokenIdSystem[] = "system";
92 // Returns whether |token_id| references a known Token.
93 bool ValidateToken(const std::string& token_id,
94 std::string* platform_keys_token_id) {
95 platform_keys_token_id->clear();
96 if (token_id == kTokenIdUser) {
97 *platform_keys_token_id = chromeos::platform_keys::kTokenIdUser;
98 return true;
100 if (token_id == kTokenIdSystem) {
101 *platform_keys_token_id = chromeos::platform_keys::kTokenIdSystem;
102 return true;
104 return false;
107 std::string PlatformKeysTokenIdToApiId(
108 const std::string& platform_keys_token_id) {
109 if (platform_keys_token_id == chromeos::platform_keys::kTokenIdUser)
110 return kTokenIdUser;
111 if (platform_keys_token_id == chromeos::platform_keys::kTokenIdSystem)
112 return kTokenIdSystem;
114 return std::string();
117 } // namespace platform_keys
119 PlatformKeysInternalGetPublicKeyFunction::
120 ~PlatformKeysInternalGetPublicKeyFunction() {
123 ExtensionFunction::ResponseAction
124 PlatformKeysInternalGetPublicKeyFunction::Run() {
125 scoped_ptr<api_pki::GetPublicKey::Params> params(
126 api_pki::GetPublicKey::Params::Create(*args_));
127 EXTENSION_FUNCTION_VALIDATE(params);
129 const std::vector<char>& cert_der = params->certificate;
130 if (cert_der.empty())
131 return RespondNow(Error(platform_keys::kErrorInvalidX509Cert));
132 scoped_refptr<net::X509Certificate> cert_x509 =
133 net::X509Certificate::CreateFromBytes(vector_as_array(&cert_der),
134 cert_der.size());
135 if (!cert_x509)
136 return RespondNow(Error(platform_keys::kErrorInvalidX509Cert));
138 PublicKeyInfo key_info;
139 key_info.public_key_spki_der =
140 chromeos::platform_keys::GetSubjectPublicKeyInfo(cert_x509);
141 if (!chromeos::platform_keys::GetPublicKey(cert_x509, &key_info.key_type,
142 &key_info.key_size_bits) ||
143 key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) {
144 return RespondNow(Error(kErrorAlgorithmNotSupported));
147 // Currently, the only supported combination is:
148 // A certificate declaring rsaEncryption in the SubjectPublicKeyInfo used
149 // with the RSASSA-PKCS1-v1.5 algorithm.
150 if (params->algorithm_name != kWebCryptoRSASSA_PKCS1_v1_5) {
151 return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate));
154 api_pki::GetPublicKey::Results::Algorithm algorithm;
155 BuildWebCryptoRSAAlgorithmDictionary(key_info,
156 &algorithm.additional_properties);
158 return RespondNow(ArgumentList(api_pki::GetPublicKey::Results::Create(
159 std::vector<char>(key_info.public_key_spki_der.begin(),
160 key_info.public_key_spki_der.end()),
161 algorithm)));
164 PlatformKeysInternalSelectClientCertificatesFunction::
165 ~PlatformKeysInternalSelectClientCertificatesFunction() {
168 ExtensionFunction::ResponseAction
169 PlatformKeysInternalSelectClientCertificatesFunction::Run() {
170 scoped_ptr<api_pki::SelectClientCertificates::Params> params(
171 api_pki::SelectClientCertificates::Params::Create(*args_));
172 EXTENSION_FUNCTION_VALIDATE(params);
174 chromeos::PlatformKeysService* service =
175 chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
176 browser_context());
177 DCHECK(service);
179 chromeos::platform_keys::ClientCertificateRequest request;
180 for (const std::vector<char>& cert_authority :
181 params->details.request.certificate_authorities) {
182 request.certificate_authorities.push_back(
183 std::string(cert_authority.begin(), cert_authority.end()));
185 for (const api_pk::ClientCertificateType& cert_type :
186 params->details.request.certificate_types) {
187 switch (cert_type) {
188 case api_pk::CLIENT_CERTIFICATE_TYPE_ECDSASIGN:
189 request.certificate_key_types.push_back(
190 net::X509Certificate::kPublicKeyTypeECDSA);
191 break;
192 case api_pk::CLIENT_CERTIFICATE_TYPE_RSASIGN:
193 request.certificate_key_types.push_back(
194 net::X509Certificate::kPublicKeyTypeRSA);
195 break;
196 case api_pk::CLIENT_CERTIFICATE_TYPE_NONE:
197 NOTREACHED();
200 content::WebContents* web_contents = nullptr;
201 if (params->details.interactive) {
202 web_contents = GetSenderWebContents();
204 // Ensure that this function is called in a context that allows opening
205 // dialogs.
206 if (!web_contents ||
207 !web_modal::PopupManager::FromWebContents(web_contents)) {
208 return RespondNow(Error(kErrorInteractiveCallFromBackground));
212 service->SelectClientCertificates(
213 request, params->details.interactive, extension_id(),
214 base::Bind(&PlatformKeysInternalSelectClientCertificatesFunction::
215 OnSelectedCertificates,
216 this),
217 web_contents);
218 return RespondLater();
221 void PlatformKeysInternalSelectClientCertificatesFunction::
222 OnSelectedCertificates(scoped_ptr<net::CertificateList> matches,
223 const std::string& error_message) {
224 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
226 if (!error_message.empty()) {
227 Respond(Error(error_message));
228 return;
230 DCHECK(matches);
231 std::vector<linked_ptr<api_pk::Match>> result_matches;
232 for (const scoped_refptr<net::X509Certificate>& match : *matches) {
233 PublicKeyInfo key_info;
234 key_info.public_key_spki_der =
235 chromeos::platform_keys::GetSubjectPublicKeyInfo(match);
236 if (!chromeos::platform_keys::GetPublicKey(match, &key_info.key_type,
237 &key_info.key_size_bits)) {
238 LOG(ERROR) << "Could not retrieve public key info.";
239 continue;
241 if (key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) {
242 LOG(ERROR) << "Skipping unsupported certificate with non-RSA key.";
243 continue;
246 linked_ptr<api_pk::Match> result_match(new api_pk::Match);
247 std::string der_encoded_cert;
248 net::X509Certificate::GetDEREncoded(match->os_cert_handle(),
249 &der_encoded_cert);
250 result_match->certificate.assign(der_encoded_cert.begin(),
251 der_encoded_cert.end());
253 BuildWebCryptoRSAAlgorithmDictionary(
254 key_info, &result_match->key_algorithm.additional_properties);
255 result_matches.push_back(result_match);
257 Respond(ArgumentList(
258 api_pki::SelectClientCertificates::Results::Create(result_matches)));
261 PlatformKeysInternalSignFunction::~PlatformKeysInternalSignFunction() {
264 ExtensionFunction::ResponseAction PlatformKeysInternalSignFunction::Run() {
265 scoped_ptr<api_pki::Sign::Params> params(
266 api_pki::Sign::Params::Create(*args_));
267 EXTENSION_FUNCTION_VALIDATE(params);
268 std::string platform_keys_token_id;
269 if (!params->token_id.empty() &&
270 !platform_keys::ValidateToken(params->token_id,
271 &platform_keys_token_id)) {
272 return RespondNow(Error(platform_keys::kErrorInvalidToken));
275 chromeos::PlatformKeysService* service =
276 chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
277 browser_context());
278 DCHECK(service);
280 if (params->hash_algorithm_name == "none") {
281 service->SignRSAPKCS1Raw(
282 platform_keys_token_id,
283 std::string(params->data.begin(), params->data.end()),
284 std::string(params->public_key.begin(), params->public_key.end()),
285 extension_id(),
286 base::Bind(&PlatformKeysInternalSignFunction::OnSigned, this));
287 } else {
288 chromeos::platform_keys::HashAlgorithm hash_algorithm;
289 if (params->hash_algorithm_name == "SHA-1") {
290 hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA1;
291 } else if (params->hash_algorithm_name == "SHA-256") {
292 hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA256;
293 } else if (params->hash_algorithm_name == "SHA-384") {
294 hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA384;
295 } else if (params->hash_algorithm_name == "SHA-512") {
296 hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA512;
297 } else {
298 return RespondNow(Error(kErrorAlgorithmNotSupported));
300 service->SignRSAPKCS1Digest(
301 platform_keys_token_id,
302 std::string(params->data.begin(), params->data.end()),
303 std::string(params->public_key.begin(), params->public_key.end()),
304 hash_algorithm, extension_id(),
305 base::Bind(&PlatformKeysInternalSignFunction::OnSigned, this));
308 return RespondLater();
311 void PlatformKeysInternalSignFunction::OnSigned(
312 const std::string& signature,
313 const std::string& error_message) {
314 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
316 if (error_message.empty())
317 Respond(ArgumentList(api_pki::Sign::Results::Create(
318 std::vector<char>(signature.begin(), signature.end()))));
319 else
320 Respond(Error(error_message));
323 PlatformKeysVerifyTLSServerCertificateFunction::
324 ~PlatformKeysVerifyTLSServerCertificateFunction() {
327 ExtensionFunction::ResponseAction
328 PlatformKeysVerifyTLSServerCertificateFunction::Run() {
329 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
331 scoped_ptr<api_pk::VerifyTLSServerCertificate::Params> params(
332 api_pk::VerifyTLSServerCertificate::Params::Create(*args_));
333 EXTENSION_FUNCTION_VALIDATE(params.get());
335 VerifyTrustAPI::GetFactoryInstance()
336 ->Get(browser_context())
337 ->Verify(params.Pass(), extension_id(),
338 base::Bind(&PlatformKeysVerifyTLSServerCertificateFunction::
339 FinishedVerification,
340 this));
342 return RespondLater();
345 void PlatformKeysVerifyTLSServerCertificateFunction::FinishedVerification(
346 const std::string& error,
347 int verify_result,
348 int cert_status) {
349 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
351 if (!error.empty()) {
352 Respond(Error(error));
353 return;
356 api_pk::VerificationResult result;
357 result.trusted = verify_result == net::OK;
358 if (net::IsCertificateError(verify_result)) {
359 // Only report errors, not internal informational statuses.
360 const int masked_cert_status = cert_status & net::CERT_STATUS_ALL_ERRORS;
361 for (size_t i = 0; i < arraysize(kCertStatusErrors); ++i) {
362 if ((masked_cert_status & kCertStatusErrors[i].value) ==
363 kCertStatusErrors[i].value) {
364 result.debug_errors.push_back(kCertStatusErrors[i].name);
369 Respond(ArgumentList(
370 api_pk::VerifyTLSServerCertificate::Results::Create(result)));
373 } // namespace extensions