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/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
;
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
:
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
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
,
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
));
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.";
241 if (key_info
.key_type
!= net::X509Certificate::kPublicKeyTypeRSA
) {
242 LOG(ERROR
) << "Skipping unsupported certificate with non-RSA key.";
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(),
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(
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()),
286 base::Bind(&PlatformKeysInternalSignFunction::OnSigned
, this));
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
;
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()))));
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
,
342 return RespondLater();
345 void PlatformKeysVerifyTLSServerCertificateFunction::FinishedVerification(
346 const std::string
& error
,
349 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
351 if (!error
.empty()) {
352 Respond(Error(error
));
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