Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / extensions / api / platform_keys / platform_keys_api.cc
blob1281220f9a2def7fda1a352906b2ddfe8c9066e8
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/web_contents_modal_dialog_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();
201 scoped_ptr<net::CertificateList> client_certs;
202 if (params->details.client_certs) {
203 client_certs.reset(new net::CertificateList);
204 for (const std::vector<char>& client_cert_der :
205 *params->details.client_certs) {
206 if (client_cert_der.empty())
207 return RespondNow(Error(platform_keys::kErrorInvalidX509Cert));
208 scoped_refptr<net::X509Certificate> client_cert_x509 =
209 net::X509Certificate::CreateFromBytes(
210 vector_as_array(&client_cert_der), client_cert_der.size());
211 if (!client_cert_x509)
212 return RespondNow(Error(platform_keys::kErrorInvalidX509Cert));
213 client_certs->push_back(client_cert_x509);
217 content::WebContents* web_contents = nullptr;
218 if (params->details.interactive) {
219 web_contents = GetSenderWebContents();
221 // Ensure that this function is called in a context that allows opening
222 // dialogs.
223 if (!web_contents ||
224 !web_modal::WebContentsModalDialogManager::FromWebContents(
225 web_contents)) {
226 return RespondNow(Error(kErrorInteractiveCallFromBackground));
230 service->SelectClientCertificates(
231 request, client_certs.Pass(), params->details.interactive, extension_id(),
232 base::Bind(&PlatformKeysInternalSelectClientCertificatesFunction::
233 OnSelectedCertificates,
234 this),
235 web_contents);
236 return RespondLater();
239 void PlatformKeysInternalSelectClientCertificatesFunction::
240 OnSelectedCertificates(scoped_ptr<net::CertificateList> matches,
241 const std::string& error_message) {
242 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
244 if (!error_message.empty()) {
245 Respond(Error(error_message));
246 return;
248 DCHECK(matches);
249 std::vector<linked_ptr<api_pk::Match>> result_matches;
250 for (const scoped_refptr<net::X509Certificate>& match : *matches) {
251 PublicKeyInfo key_info;
252 key_info.public_key_spki_der =
253 chromeos::platform_keys::GetSubjectPublicKeyInfo(match);
254 if (!chromeos::platform_keys::GetPublicKey(match, &key_info.key_type,
255 &key_info.key_size_bits)) {
256 LOG(ERROR) << "Could not retrieve public key info.";
257 continue;
259 if (key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) {
260 LOG(ERROR) << "Skipping unsupported certificate with non-RSA key.";
261 continue;
264 linked_ptr<api_pk::Match> result_match(new api_pk::Match);
265 std::string der_encoded_cert;
266 net::X509Certificate::GetDEREncoded(match->os_cert_handle(),
267 &der_encoded_cert);
268 result_match->certificate.assign(der_encoded_cert.begin(),
269 der_encoded_cert.end());
271 BuildWebCryptoRSAAlgorithmDictionary(
272 key_info, &result_match->key_algorithm.additional_properties);
273 result_matches.push_back(result_match);
275 Respond(ArgumentList(
276 api_pki::SelectClientCertificates::Results::Create(result_matches)));
279 PlatformKeysInternalSignFunction::~PlatformKeysInternalSignFunction() {
282 ExtensionFunction::ResponseAction PlatformKeysInternalSignFunction::Run() {
283 scoped_ptr<api_pki::Sign::Params> params(
284 api_pki::Sign::Params::Create(*args_));
285 EXTENSION_FUNCTION_VALIDATE(params);
286 std::string platform_keys_token_id;
287 if (!params->token_id.empty() &&
288 !platform_keys::ValidateToken(params->token_id,
289 &platform_keys_token_id)) {
290 return RespondNow(Error(platform_keys::kErrorInvalidToken));
293 chromeos::PlatformKeysService* service =
294 chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
295 browser_context());
296 DCHECK(service);
298 if (params->hash_algorithm_name == "none") {
299 service->SignRSAPKCS1Raw(
300 platform_keys_token_id,
301 std::string(params->data.begin(), params->data.end()),
302 std::string(params->public_key.begin(), params->public_key.end()),
303 extension_id(),
304 base::Bind(&PlatformKeysInternalSignFunction::OnSigned, this));
305 } else {
306 chromeos::platform_keys::HashAlgorithm hash_algorithm;
307 if (params->hash_algorithm_name == "SHA-1") {
308 hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA1;
309 } else if (params->hash_algorithm_name == "SHA-256") {
310 hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA256;
311 } else if (params->hash_algorithm_name == "SHA-384") {
312 hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA384;
313 } else if (params->hash_algorithm_name == "SHA-512") {
314 hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA512;
315 } else {
316 return RespondNow(Error(kErrorAlgorithmNotSupported));
318 service->SignRSAPKCS1Digest(
319 platform_keys_token_id,
320 std::string(params->data.begin(), params->data.end()),
321 std::string(params->public_key.begin(), params->public_key.end()),
322 hash_algorithm, extension_id(),
323 base::Bind(&PlatformKeysInternalSignFunction::OnSigned, this));
326 return RespondLater();
329 void PlatformKeysInternalSignFunction::OnSigned(
330 const std::string& signature,
331 const std::string& error_message) {
332 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
334 if (error_message.empty())
335 Respond(ArgumentList(api_pki::Sign::Results::Create(
336 std::vector<char>(signature.begin(), signature.end()))));
337 else
338 Respond(Error(error_message));
341 PlatformKeysVerifyTLSServerCertificateFunction::
342 ~PlatformKeysVerifyTLSServerCertificateFunction() {
345 ExtensionFunction::ResponseAction
346 PlatformKeysVerifyTLSServerCertificateFunction::Run() {
347 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
349 scoped_ptr<api_pk::VerifyTLSServerCertificate::Params> params(
350 api_pk::VerifyTLSServerCertificate::Params::Create(*args_));
351 EXTENSION_FUNCTION_VALIDATE(params.get());
353 VerifyTrustAPI::GetFactoryInstance()
354 ->Get(browser_context())
355 ->Verify(params.Pass(), extension_id(),
356 base::Bind(&PlatformKeysVerifyTLSServerCertificateFunction::
357 FinishedVerification,
358 this));
360 return RespondLater();
363 void PlatformKeysVerifyTLSServerCertificateFunction::FinishedVerification(
364 const std::string& error,
365 int verify_result,
366 int cert_status) {
367 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
369 if (!error.empty()) {
370 Respond(Error(error));
371 return;
374 api_pk::VerificationResult result;
375 result.trusted = verify_result == net::OK;
376 if (net::IsCertificateError(verify_result)) {
377 // Only report errors, not internal informational statuses.
378 const int masked_cert_status = cert_status & net::CERT_STATUS_ALL_ERRORS;
379 for (size_t i = 0; i < arraysize(kCertStatusErrors); ++i) {
380 if ((masked_cert_status & kCertStatusErrors[i].value) ==
381 kCertStatusErrors[i].value) {
382 result.debug_errors.push_back(kCertStatusErrors[i].name);
387 Respond(ArgumentList(
388 api_pk::VerifyTLSServerCertificate::Results::Create(result)));
391 } // namespace extensions