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"
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
;
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 "
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
);
63 const unsigned char defaultPublicExponent
[] = {0x01, 0x00, 0x01};
64 algorithm
->SetWithoutPathExpansion(
66 base::BinaryValue::CreateWithCopiedBuffer(
67 reinterpret_cast<const char*>(defaultPublicExponent
),
68 arraysize(defaultPublicExponent
)));
71 const struct NameValuePair
{
72 const char* const name
;
74 } kCertStatusErrors
[] = {
75 #define CERT_STATUS_FLAG(name, value) \
78 #include "net/cert/cert_status_flags_list.h"
79 #undef CERT_STATUS_FLAG
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
;
100 if (token_id
== kTokenIdSystem
) {
101 *platform_keys_token_id
= chromeos::platform_keys::kTokenIdSystem
;
107 std::string
PlatformKeysTokenIdToApiId(
108 const std::string
& platform_keys_token_id
) {
109 if (platform_keys_token_id
== chromeos::platform_keys::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
),
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()),
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(
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
) {
188 case api_pk::CLIENT_CERTIFICATE_TYPE_ECDSASIGN
:
189 request
.certificate_key_types
.push_back(
190 net::X509Certificate::kPublicKeyTypeECDSA
);
192 case api_pk::CLIENT_CERTIFICATE_TYPE_RSASIGN
:
193 request
.certificate_key_types
.push_back(
194 net::X509Certificate::kPublicKeyTypeRSA
);
196 case api_pk::CLIENT_CERTIFICATE_TYPE_NONE
:
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
224 !web_modal::WebContentsModalDialogManager::FromWebContents(
226 return RespondNow(Error(kErrorInteractiveCallFromBackground
));
230 service
->SelectClientCertificates(
231 request
, client_certs
.Pass(), params
->details
.interactive
, extension_id(),
232 base::Bind(&PlatformKeysInternalSelectClientCertificatesFunction::
233 OnSelectedCertificates
,
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
));
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.";
259 if (key_info
.key_type
!= net::X509Certificate::kPublicKeyTypeRSA
) {
260 LOG(ERROR
) << "Skipping unsupported certificate with non-RSA key.";
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(),
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(
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()),
304 base::Bind(&PlatformKeysInternalSignFunction::OnSigned
, this));
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
;
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()))));
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
,
360 return RespondLater();
363 void PlatformKeysVerifyTLSServerCertificateFunction::FinishedVerification(
364 const std::string
& error
,
367 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
369 if (!error
.empty()) {
370 Respond(Error(error
));
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