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/values.h"
12 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
13 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
14 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
15 #include "chrome/common/extensions/api/platform_keys_internal.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "net/cert/x509_certificate.h"
19 namespace extensions
{
21 namespace api_pk
= api::platform_keys
;
22 namespace api_pki
= api::platform_keys_internal
;
26 const char kErrorAlgorithmNotSupported
[] = "Algorithm not supported.";
27 const char kErrorAlgorithmNotPermittedByCertificate
[] =
28 "The requested Algorithm is not permitted by the certificate.";
29 const char kErrorInvalidX509Cert
[] =
30 "Certificate is not a valid X.509 certificate.";
32 const char kWebCryptoRSASSA_PKCS1_v1_5
[] = "RSASSA-PKCS1-v1_5";
34 struct PublicKeyInfo
{
35 // The X.509 Subject Public Key Info of the key in DER encoding.
36 std::string public_key_spki_der
;
38 // The type of the key.
39 net::X509Certificate::PublicKeyType key_type
=
40 net::X509Certificate::kPublicKeyTypeUnknown
;
42 // The size of the key in bits.
43 size_t key_size_bits
= 0;
46 // Builds a partial WebCrypto Algorithm object from the parameters available in
47 // |key_info|, which must the info of an RSA key. This doesn't include sign/hash
48 // parameters and thus isn't complete.
49 // platform_keys::GetPublicKey() enforced the public exponent 65537.
50 void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo
& key_info
,
51 base::DictionaryValue
* algorithm
) {
52 CHECK_EQ(net::X509Certificate::kPublicKeyTypeRSA
, key_info
.key_type
);
53 algorithm
->SetStringWithoutPathExpansion("name", kWebCryptoRSASSA_PKCS1_v1_5
);
54 algorithm
->SetIntegerWithoutPathExpansion("modulusLength",
55 key_info
.key_size_bits
);
58 const unsigned char defaultPublicExponent
[] = {0x01, 0x00, 0x01};
59 algorithm
->SetWithoutPathExpansion(
61 base::BinaryValue::CreateWithCopiedBuffer(
62 reinterpret_cast<const char*>(defaultPublicExponent
),
63 arraysize(defaultPublicExponent
)));
68 namespace platform_keys
{
70 const char kErrorInvalidToken
[] = "The token is not valid.";
71 const char kTokenIdUser
[] = "user";
72 const char kTokenIdSystem
[] = "system";
74 // Returns whether |token_id| references a known Token.
75 bool ValidateToken(const std::string
& token_id
,
76 std::string
* platform_keys_token_id
) {
77 platform_keys_token_id
->clear();
78 if (token_id
== kTokenIdUser
) {
79 *platform_keys_token_id
= chromeos::platform_keys::kTokenIdUser
;
82 if (token_id
== kTokenIdSystem
) {
83 *platform_keys_token_id
= chromeos::platform_keys::kTokenIdSystem
;
89 std::string
PlatformKeysTokenIdToApiId(
90 const std::string
& platform_keys_token_id
) {
91 if (platform_keys_token_id
== chromeos::platform_keys::kTokenIdUser
)
93 if (platform_keys_token_id
== chromeos::platform_keys::kTokenIdSystem
)
94 return kTokenIdSystem
;
99 } // namespace platform_keys
101 PlatformKeysInternalGetPublicKeyFunction::
102 ~PlatformKeysInternalGetPublicKeyFunction() {
105 ExtensionFunction::ResponseAction
106 PlatformKeysInternalGetPublicKeyFunction::Run() {
107 scoped_ptr
<api_pki::GetPublicKey::Params
> params(
108 api_pki::GetPublicKey::Params::Create(*args_
));
109 EXTENSION_FUNCTION_VALIDATE(params
);
111 const std::vector
<char>& cert_der
= params
->certificate
;
112 if (cert_der
.empty())
113 return RespondNow(Error(kErrorInvalidX509Cert
));
114 scoped_refptr
<net::X509Certificate
> cert_x509
=
115 net::X509Certificate::CreateFromBytes(vector_as_array(&cert_der
),
118 return RespondNow(Error(kErrorInvalidX509Cert
));
120 PublicKeyInfo key_info
;
121 key_info
.public_key_spki_der
=
122 chromeos::platform_keys::GetSubjectPublicKeyInfo(cert_x509
);
123 if (!chromeos::platform_keys::GetPublicKey(cert_x509
, &key_info
.key_type
,
124 &key_info
.key_size_bits
) ||
125 key_info
.key_type
!= net::X509Certificate::kPublicKeyTypeRSA
) {
126 return RespondNow(Error(kErrorAlgorithmNotSupported
));
129 // Currently, the only supported combination is:
130 // A certificate declaring rsaEncryption in the SubjectPublicKeyInfo used
131 // with the RSASSA-PKCS1-v1.5 algorithm.
132 if (params
->algorithm_name
!= kWebCryptoRSASSA_PKCS1_v1_5
) {
133 return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate
));
136 api_pki::GetPublicKey::Results::Algorithm algorithm
;
137 BuildWebCryptoRSAAlgorithmDictionary(key_info
,
138 &algorithm
.additional_properties
);
140 return RespondNow(ArgumentList(api_pki::GetPublicKey::Results::Create(
141 std::vector
<char>(key_info
.public_key_spki_der
.begin(),
142 key_info
.public_key_spki_der
.end()),
146 PlatformKeysInternalSelectClientCertificatesFunction::
147 ~PlatformKeysInternalSelectClientCertificatesFunction() {
150 ExtensionFunction::ResponseAction
151 PlatformKeysInternalSelectClientCertificatesFunction::Run() {
152 scoped_ptr
<api_pki::SelectClientCertificates::Params
> params(
153 api_pki::SelectClientCertificates::Params::Create(*args_
));
154 EXTENSION_FUNCTION_VALIDATE(params
);
156 chromeos::PlatformKeysService
* service
=
157 chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
161 chromeos::platform_keys::ClientCertificateRequest request
;
162 for (const std::vector
<char>& cert_authority
:
163 params
->details
.request
.certificate_authorities
) {
164 request
.certificate_authorities
.push_back(
165 std::string(cert_authority
.begin(), cert_authority
.end()));
168 service
->SelectClientCertificates(
169 request
, params
->details
.interactive
, extension_id(),
170 base::Bind(&PlatformKeysInternalSelectClientCertificatesFunction::
171 OnSelectedCertificates
,
173 GetAssociatedWebContents());
174 return RespondLater();
177 void PlatformKeysInternalSelectClientCertificatesFunction::
178 OnSelectedCertificates(scoped_ptr
<net::CertificateList
> matches
,
179 const std::string
& error_message
) {
180 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
181 if (!error_message
.empty()) {
182 Respond(Error(error_message
));
186 std::vector
<linked_ptr
<api_pk::Match
>> result_matches
;
187 for (const scoped_refptr
<net::X509Certificate
>& match
: *matches
) {
188 PublicKeyInfo key_info
;
189 key_info
.public_key_spki_der
=
190 chromeos::platform_keys::GetSubjectPublicKeyInfo(match
);
191 if (!chromeos::platform_keys::GetPublicKey(match
, &key_info
.key_type
,
192 &key_info
.key_size_bits
)) {
193 LOG(ERROR
) << "Could not retrieve public key info.";
196 if (key_info
.key_type
!= net::X509Certificate::kPublicKeyTypeRSA
) {
197 LOG(ERROR
) << "Skipping unsupported certificate with non-RSA key.";
201 linked_ptr
<api_pk::Match
> result_match(new api_pk::Match
);
202 std::string der_encoded_cert
;
203 net::X509Certificate::GetDEREncoded(match
->os_cert_handle(),
205 result_match
->certificate
.assign(der_encoded_cert
.begin(),
206 der_encoded_cert
.end());
208 BuildWebCryptoRSAAlgorithmDictionary(
209 key_info
, &result_match
->key_algorithm
.additional_properties
);
210 result_matches
.push_back(result_match
);
212 Respond(ArgumentList(
213 api_pki::SelectClientCertificates::Results::Create(result_matches
)));
216 PlatformKeysInternalSignFunction::~PlatformKeysInternalSignFunction() {
219 ExtensionFunction::ResponseAction
PlatformKeysInternalSignFunction::Run() {
220 scoped_ptr
<api_pki::Sign::Params
> params(
221 api_pki::Sign::Params::Create(*args_
));
222 EXTENSION_FUNCTION_VALIDATE(params
);
223 std::string platform_keys_token_id
;
224 if (!params
->token_id
.empty() &&
225 !platform_keys::ValidateToken(params
->token_id
,
226 &platform_keys_token_id
)) {
227 return RespondNow(Error(platform_keys::kErrorInvalidToken
));
230 chromeos::PlatformKeysService
* service
=
231 chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
235 if (params
->hash_algorithm_name
== "none") {
236 service
->SignRSAPKCS1Raw(
237 platform_keys_token_id
,
238 std::string(params
->data
.begin(), params
->data
.end()),
239 std::string(params
->public_key
.begin(), params
->public_key
.end()),
241 base::Bind(&PlatformKeysInternalSignFunction::OnSigned
, this));
243 chromeos::platform_keys::HashAlgorithm hash_algorithm
;
244 if (params
->hash_algorithm_name
== "SHA-1") {
245 hash_algorithm
= chromeos::platform_keys::HASH_ALGORITHM_SHA1
;
246 } else if (params
->hash_algorithm_name
== "SHA-256") {
247 hash_algorithm
= chromeos::platform_keys::HASH_ALGORITHM_SHA256
;
248 } else if (params
->hash_algorithm_name
== "SHA-384") {
249 hash_algorithm
= chromeos::platform_keys::HASH_ALGORITHM_SHA384
;
250 } else if (params
->hash_algorithm_name
== "SHA-512") {
251 hash_algorithm
= chromeos::platform_keys::HASH_ALGORITHM_SHA512
;
253 return RespondNow(Error(kErrorAlgorithmNotSupported
));
255 service
->SignRSAPKCS1Digest(
256 platform_keys_token_id
,
257 std::string(params
->data
.begin(), params
->data
.end()),
258 std::string(params
->public_key
.begin(), params
->public_key
.end()),
259 hash_algorithm
, extension_id(),
260 base::Bind(&PlatformKeysInternalSignFunction::OnSigned
, this));
263 return RespondLater();
266 void PlatformKeysInternalSignFunction::OnSigned(
267 const std::string
& signature
,
268 const std::string
& error_message
) {
269 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
270 if (error_message
.empty())
271 Respond(ArgumentList(api_pki::Sign::Results::Create(
272 std::vector
<char>(signature
.begin(), signature
.end()))));
274 Respond(Error(error_message
));
277 } // namespace extensions