1 // Copyright 2014 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.
4
5 #include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/optional.h"
11 #include "base/values.h"
12 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h"
13 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h"
14 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
15 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
16 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
17 #include "chrome/browser/extensions/api/platform_keys/platform_keys_api.h"
18 #include "chrome/common/extensions/api/enterprise_platform_keys.h"
19 #include "chrome/common/extensions/api/enterprise_platform_keys_internal.h"
20 #include "content/public/browser/browser_task_traits.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "net/cert/x509_certificate.h"
23 #include "net/cert/x509_util.h"
24
25 namespace extensions {
26
27 namespace {
28
29 namespace api_epk = api::enterprise_platform_keys;
30 namespace api_epki = api::enterprise_platform_keys_internal;
31
32 // This error will occur if a token is removed and will be exposed to the
33 // extension. Keep this in sync with the custom binding in Javascript.
34 const char kEnterprisePlatformErrorInternal[] = "Internal Error.";
35
36 const char kEnterprisePlatformErrorInvalidX509Cert[] =
37 "Certificate is not a valid X.509 certificate.";
38
VectorFromString(const std::string & s)39 std::vector<uint8_t> VectorFromString(const std::string& s) {
40 return std::vector<uint8_t>(s.begin(), s.end());
41 }
42
StringFromVector(const std::vector<uint8_t> & v)43 std::string StringFromVector(const std::vector<uint8_t>& v) {
44 return std::string(v.begin(), v.end());
45 }
46
47 } // namespace
48
49 EnterprisePlatformKeysInternalGenerateKeyFunction::
50 ~EnterprisePlatformKeysInternalGenerateKeyFunction() = default;
51
52 ExtensionFunction::ResponseAction
Run()53 EnterprisePlatformKeysInternalGenerateKeyFunction::Run() {
54 std::unique_ptr<api_epki::GenerateKey::Params> params(
55 api_epki::GenerateKey::Params::Create(*args_));
56
57 EXTENSION_FUNCTION_VALIDATE(params);
58 base::Optional<chromeos::platform_keys::TokenId> platform_keys_token_id =
59 platform_keys::ApiIdToPlatformKeysTokenId(params->token_id);
60 if (!platform_keys_token_id)
61 return RespondNow(Error(platform_keys::kErrorInvalidToken));
62
63 chromeos::ExtensionPlatformKeysService* service =
64 chromeos::ExtensionPlatformKeysServiceFactory::GetForBrowserContext(
65 browser_context());
66 DCHECK(service);
67
68 if (params->algorithm.name == "RSASSA-PKCS1-v1_5") {
69 // TODO(pneubeck): Add support for unsigned integers to IDL.
70 EXTENSION_FUNCTION_VALIDATE(params->algorithm.modulus_length &&
71 *(params->algorithm.modulus_length) >= 0);
72 service->GenerateRSAKey(
73 platform_keys_token_id.value(), *(params->algorithm.modulus_length),
74 extension_id(),
75 base::Bind(
76 &EnterprisePlatformKeysInternalGenerateKeyFunction::OnGeneratedKey,
77 this));
78 } else if (params->algorithm.name == "ECDSA") {
79 EXTENSION_FUNCTION_VALIDATE(params->algorithm.named_curve);
80 service->GenerateECKey(
81 platform_keys_token_id.value(), *(params->algorithm.named_curve),
82 extension_id(),
83 base::Bind(
84 &EnterprisePlatformKeysInternalGenerateKeyFunction::OnGeneratedKey,
85 this));
86 } else {
87 NOTREACHED();
88 EXTENSION_FUNCTION_VALIDATE(false);
89 }
90 return RespondLater();
91 }
92
OnGeneratedKey(const std::string & public_key_der,chromeos::platform_keys::Status status)93 void EnterprisePlatformKeysInternalGenerateKeyFunction::OnGeneratedKey(
94 const std::string& public_key_der,
95 chromeos::platform_keys::Status status) {
96 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
97 if (status == chromeos::platform_keys::Status::kSuccess) {
98 Respond(ArgumentList(api_epki::GenerateKey::Results::Create(
99 std::vector<uint8_t>(public_key_der.begin(), public_key_der.end()))));
100 } else {
101 Respond(Error(chromeos::platform_keys::StatusToString(status)));
102 }
103 }
104
105 EnterprisePlatformKeysGetCertificatesFunction::
~EnterprisePlatformKeysGetCertificatesFunction()106 ~EnterprisePlatformKeysGetCertificatesFunction() {}
107
108 ExtensionFunction::ResponseAction
Run()109 EnterprisePlatformKeysGetCertificatesFunction::Run() {
110 std::unique_ptr<api_epk::GetCertificates::Params> params(
111 api_epk::GetCertificates::Params::Create(*args_));
112 EXTENSION_FUNCTION_VALIDATE(params);
113 base::Optional<chromeos::platform_keys::TokenId> platform_keys_token_id =
114 platform_keys::ApiIdToPlatformKeysTokenId(params->token_id);
115 if (!platform_keys_token_id)
116 return RespondNow(Error(platform_keys::kErrorInvalidToken));
117
118 chromeos::platform_keys::PlatformKeysService* platform_keys_service =
119 chromeos::platform_keys::PlatformKeysServiceFactory::GetForBrowserContext(
120 browser_context());
121 platform_keys_service->GetCertificates(
122 platform_keys_token_id.value(),
123 base::BindOnce(
124 &EnterprisePlatformKeysGetCertificatesFunction::OnGotCertificates,
125 this));
126 return RespondLater();
127 }
128
OnGotCertificates(std::unique_ptr<net::CertificateList> certs,chromeos::platform_keys::Status status)129 void EnterprisePlatformKeysGetCertificatesFunction::OnGotCertificates(
130 std::unique_ptr<net::CertificateList> certs,
131 chromeos::platform_keys::Status status) {
132 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
133 if (status != chromeos::platform_keys::Status::kSuccess) {
134 Respond(Error(chromeos::platform_keys::StatusToString(status)));
135 return;
136 }
137
138 std::unique_ptr<base::ListValue> client_certs(new base::ListValue());
139 for (net::CertificateList::const_iterator it = certs->begin();
140 it != certs->end(); ++it) {
141 base::StringPiece cert_der =
142 net::x509_util::CryptoBufferAsStringPiece((*it)->cert_buffer());
143 client_certs->Append(std::make_unique<base::Value>(
144 base::Value::BlobStorage(cert_der.begin(), cert_der.end())));
145 }
146
147 std::unique_ptr<base::ListValue> results(new base::ListValue());
148 results->Append(std::move(client_certs));
149 Respond(ArgumentList(std::move(results)));
150 }
151
152 EnterprisePlatformKeysImportCertificateFunction::
~EnterprisePlatformKeysImportCertificateFunction()153 ~EnterprisePlatformKeysImportCertificateFunction() {}
154
155 ExtensionFunction::ResponseAction
Run()156 EnterprisePlatformKeysImportCertificateFunction::Run() {
157 std::unique_ptr<api_epk::ImportCertificate::Params> params(
158 api_epk::ImportCertificate::Params::Create(*args_));
159 EXTENSION_FUNCTION_VALIDATE(params);
160 base::Optional<chromeos::platform_keys::TokenId> platform_keys_token_id =
161 platform_keys::ApiIdToPlatformKeysTokenId(params->token_id);
162 if (!platform_keys_token_id)
163 return RespondNow(Error(platform_keys::kErrorInvalidToken));
164
165 const std::vector<uint8_t>& cert_der = params->certificate;
166 // Allow UTF-8 inside PrintableStrings in client certificates. See
167 // crbug.com/770323 and crbug.com/788655.
168 net::X509Certificate::UnsafeCreateOptions options;
169 options.printable_string_is_utf8 = true;
170 scoped_refptr<net::X509Certificate> cert_x509 =
171 net::X509Certificate::CreateFromBytesUnsafeOptions(
172 reinterpret_cast<const char*>(cert_der.data()), cert_der.size(),
173 options);
174 if (!cert_x509.get())
175 return RespondNow(Error(kEnterprisePlatformErrorInvalidX509Cert));
176
177 chromeos::platform_keys::PlatformKeysService* platform_keys_service =
178 chromeos::platform_keys::PlatformKeysServiceFactory::GetForBrowserContext(
179 browser_context());
180 CHECK(platform_keys_service);
181
182 platform_keys_service->ImportCertificate(
183 platform_keys_token_id.value(), cert_x509,
184 base::BindOnce(&EnterprisePlatformKeysImportCertificateFunction::
185 OnImportedCertificate,
186 this));
187 return RespondLater();
188 }
189
OnImportedCertificate(chromeos::platform_keys::Status status)190 void EnterprisePlatformKeysImportCertificateFunction::OnImportedCertificate(
191 chromeos::platform_keys::Status status) {
192 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
193 if (status == chromeos::platform_keys::Status::kSuccess)
194 Respond(NoArguments());
195 else
196 Respond(Error(chromeos::platform_keys::StatusToString(status)));
197 }
198
199 EnterprisePlatformKeysRemoveCertificateFunction::
~EnterprisePlatformKeysRemoveCertificateFunction()200 ~EnterprisePlatformKeysRemoveCertificateFunction() {}
201
202 ExtensionFunction::ResponseAction
Run()203 EnterprisePlatformKeysRemoveCertificateFunction::Run() {
204 std::unique_ptr<api_epk::RemoveCertificate::Params> params(
205 api_epk::RemoveCertificate::Params::Create(*args_));
206 EXTENSION_FUNCTION_VALIDATE(params);
207 base::Optional<chromeos::platform_keys::TokenId> platform_keys_token_id =
208 platform_keys::ApiIdToPlatformKeysTokenId(params->token_id);
209 if (!platform_keys_token_id)
210 return RespondNow(Error(platform_keys::kErrorInvalidToken));
211
212 const std::vector<uint8_t>& cert_der = params->certificate;
213 // Allow UTF-8 inside PrintableStrings in client certificates. See
214 // crbug.com/770323 and crbug.com/788655.
215 net::X509Certificate::UnsafeCreateOptions options;
216 options.printable_string_is_utf8 = true;
217 scoped_refptr<net::X509Certificate> cert_x509 =
218 net::X509Certificate::CreateFromBytesUnsafeOptions(
219 reinterpret_cast<const char*>(cert_der.data()), cert_der.size(),
220 options);
221 if (!cert_x509.get())
222 return RespondNow(Error(kEnterprisePlatformErrorInvalidX509Cert));
223
224 chromeos::platform_keys::PlatformKeysService* platform_keys_service =
225 chromeos::platform_keys::PlatformKeysServiceFactory::GetForBrowserContext(
226 browser_context());
227 CHECK(platform_keys_service);
228
229 platform_keys_service->RemoveCertificate(
230 platform_keys_token_id.value(), cert_x509,
231 base::BindOnce(&EnterprisePlatformKeysRemoveCertificateFunction::
232 OnRemovedCertificate,
233 this));
234 return RespondLater();
235 }
236
OnRemovedCertificate(chromeos::platform_keys::Status status)237 void EnterprisePlatformKeysRemoveCertificateFunction::OnRemovedCertificate(
238 chromeos::platform_keys::Status status) {
239 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
240 if (status == chromeos::platform_keys::Status::kSuccess)
241 Respond(NoArguments());
242 else
243 Respond(Error(chromeos::platform_keys::StatusToString(status)));
244 }
245
246 EnterprisePlatformKeysInternalGetTokensFunction::
~EnterprisePlatformKeysInternalGetTokensFunction()247 ~EnterprisePlatformKeysInternalGetTokensFunction() {}
248
249 ExtensionFunction::ResponseAction
Run()250 EnterprisePlatformKeysInternalGetTokensFunction::Run() {
251 EXTENSION_FUNCTION_VALIDATE(args_->empty());
252
253 chromeos::platform_keys::PlatformKeysService* platform_keys_service =
254 chromeos::platform_keys::PlatformKeysServiceFactory::GetForBrowserContext(
255 browser_context());
256 CHECK(platform_keys_service);
257
258 platform_keys_service->GetTokens(base::BindOnce(
259 &EnterprisePlatformKeysInternalGetTokensFunction::OnGotTokens, this));
260 return RespondLater();
261 }
262
OnGotTokens(std::unique_ptr<std::vector<chromeos::platform_keys::TokenId>> platform_keys_token_ids,chromeos::platform_keys::Status status)263 void EnterprisePlatformKeysInternalGetTokensFunction::OnGotTokens(
264 std::unique_ptr<std::vector<chromeos::platform_keys::TokenId>>
265 platform_keys_token_ids,
266 chromeos::platform_keys::Status status) {
267 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
268 if (status != chromeos::platform_keys::Status::kSuccess) {
269 Respond(Error(chromeos::platform_keys::StatusToString(status)));
270 return;
271 }
272
273 std::vector<std::string> token_ids;
274 for (auto token_id : *platform_keys_token_ids) {
275 std::string api_token_id =
276 platform_keys::PlatformKeysTokenIdToApiId(token_id);
277 if (api_token_id.empty()) {
278 Respond(Error(kEnterprisePlatformErrorInternal));
279 return;
280 }
281 token_ids.push_back(api_token_id);
282 }
283
284 Respond(ArgumentList(api_epki::GetTokens::Results::Create(token_ids)));
285 }
286
287 EnterprisePlatformKeysChallengeMachineKeyFunction::
288 EnterprisePlatformKeysChallengeMachineKeyFunction() = default;
289
290 EnterprisePlatformKeysChallengeMachineKeyFunction::
291 ~EnterprisePlatformKeysChallengeMachineKeyFunction() = default;
292
293 ExtensionFunction::ResponseAction
Run()294 EnterprisePlatformKeysChallengeMachineKeyFunction::Run() {
295 std::unique_ptr<api_epk::ChallengeMachineKey::Params> params(
296 api_epk::ChallengeMachineKey::Params::Create(*args_));
297 EXTENSION_FUNCTION_VALIDATE(params);
298 chromeos::attestation::TpmChallengeKeyCallback callback = base::BindOnce(
299 &EnterprisePlatformKeysChallengeMachineKeyFunction::OnChallengedKey,
300 this);
301 // base::Unretained is safe on impl_ since its life-cycle matches |this| and
302 // |callback| holds a reference to |this|.
303 base::OnceClosure task = base::BindOnce(
304 &EPKPChallengeKey::Run, base::Unretained(&impl_),
305 chromeos::attestation::KEY_DEVICE, scoped_refptr<ExtensionFunction>(this),
306 std::move(callback), StringFromVector(params->challenge),
307 params->register_key ? *params->register_key : false);
308 content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task));
309 return RespondLater();
310 }
311
OnChallengedKey(const chromeos::attestation::TpmChallengeKeyResult & result)312 void EnterprisePlatformKeysChallengeMachineKeyFunction::OnChallengedKey(
313 const chromeos::attestation::TpmChallengeKeyResult& result) {
314 if (result.IsSuccess()) {
315 Respond(ArgumentList(api_epk::ChallengeMachineKey::Results::Create(
316 VectorFromString(result.challenge_response))));
317 } else {
318 Respond(Error(result.GetErrorMessage()));
319 }
320 }
321
322 EnterprisePlatformKeysChallengeUserKeyFunction::
323 EnterprisePlatformKeysChallengeUserKeyFunction() = default;
324
325 EnterprisePlatformKeysChallengeUserKeyFunction::
326 ~EnterprisePlatformKeysChallengeUserKeyFunction() = default;
327
328 ExtensionFunction::ResponseAction
Run()329 EnterprisePlatformKeysChallengeUserKeyFunction::Run() {
330 std::unique_ptr<api_epk::ChallengeUserKey::Params> params(
331 api_epk::ChallengeUserKey::Params::Create(*args_));
332 EXTENSION_FUNCTION_VALIDATE(params);
333 chromeos::attestation::TpmChallengeKeyCallback callback = base::BindOnce(
334 &EnterprisePlatformKeysChallengeUserKeyFunction::OnChallengedKey, this);
335 // base::Unretained is safe on impl_ since its life-cycle matches |this| and
336 // |callback| holds a reference to |this|.
337 base::OnceClosure task = base::BindOnce(
338 &EPKPChallengeKey::Run, base::Unretained(&impl_),
339 chromeos::attestation::KEY_USER, scoped_refptr<ExtensionFunction>(this),
340 std::move(callback), StringFromVector(params->challenge),
341 params->register_key);
342 content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task));
343 return RespondLater();
344 }
345
OnChallengedKey(const chromeos::attestation::TpmChallengeKeyResult & result)346 void EnterprisePlatformKeysChallengeUserKeyFunction::OnChallengedKey(
347 const chromeos::attestation::TpmChallengeKeyResult& result) {
348 if (result.IsSuccess()) {
349 Respond(ArgumentList(api_epk::ChallengeUserKey::Results::Create(
350 VectorFromString(result.challenge_response))));
351 } else {
352 Respond(Error(result.GetErrorMessage()));
353 }
354 }
355
356 } // namespace extensions
357