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