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.h"
6
7#include "base/bind.h"
8#include "base/values.h"
9#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
10#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
11#include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
12#include "chrome/common/extensions/api/enterprise_platform_keys.h"
13#include "chrome/common/extensions/api/enterprise_platform_keys_internal.h"
14#include "content/public/browser/browser_thread.h"
15#include "net/cert/x509_certificate.h"
16
17namespace extensions {
18
19namespace {
20
21namespace api_epk = api::enterprise_platform_keys;
22namespace api_epki = api::enterprise_platform_keys_internal;
23
24// This error will occur if a token is removed and will be exposed to the
25// extension. Keep this in sync with the custom binding in Javascript.
26const char kErrorInvalidToken[] = "The token is not valid.";
27
28const char kErrorAlgorithmNotSupported[] = "Algorithm not supported.";
29const char kErrorInvalidX509Cert[] =
30    "Certificate is not a valid X.509 certificate.";
31const char kTokenIdUser[] = "user";
32
33// Returns whether |token_id| references a known Token.
34bool ValidateToken(const std::string& token_id) {
35  // For now, the user token is the only valid one.
36  return token_id == kTokenIdUser;
37}
38
39}  // namespace
40
41EnterprisePlatformKeysInternalGenerateKeyFunction::
42    ~EnterprisePlatformKeysInternalGenerateKeyFunction() {
43}
44
45ExtensionFunction::ResponseAction
46EnterprisePlatformKeysInternalGenerateKeyFunction::Run() {
47  scoped_ptr<api_epki::GenerateKey::Params> params(
48      api_epki::GenerateKey::Params::Create(*args_));
49  // TODO(pneubeck): Add support for unsigned integers to IDL.
50  EXTENSION_FUNCTION_VALIDATE(params && params->modulus_length >= 0);
51  if (!ValidateToken(params->token_id))
52    return RespondNow(Error(kErrorInvalidToken));
53
54  chromeos::PlatformKeysService* service =
55      chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
56          browser_context());
57  DCHECK(service);
58
59  service->GenerateRSAKey(
60      params->token_id,
61      params->modulus_length,
62      extension_id(),
63      base::Bind(
64          &EnterprisePlatformKeysInternalGenerateKeyFunction::OnGeneratedKey,
65          this));
66  return RespondLater();
67}
68
69void EnterprisePlatformKeysInternalGenerateKeyFunction::OnGeneratedKey(
70    const std::string& public_key_der,
71    const std::string& error_message) {
72  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
73  if (error_message.empty()) {
74    Respond(
75        ArgumentList(api_epki::GenerateKey::Results::Create(public_key_der)));
76  } else {
77    Respond(Error(error_message));
78  }
79}
80
81EnterprisePlatformKeysInternalSignFunction::
82    ~EnterprisePlatformKeysInternalSignFunction() {
83}
84
85ExtensionFunction::ResponseAction
86EnterprisePlatformKeysInternalSignFunction::Run() {
87  scoped_ptr<api_epki::Sign::Params> params(
88      api_epki::Sign::Params::Create(*args_));
89  EXTENSION_FUNCTION_VALIDATE(params);
90  if (!ValidateToken(params->token_id))
91    return RespondNow(Error(kErrorInvalidToken));
92
93  chromeos::platform_keys::HashAlgorithm hash_algorithm;
94  if (params->hash_algorithm_name == "SHA-1")
95    hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA1;
96  else if (params->hash_algorithm_name == "SHA-256")
97    hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA256;
98  else if (params->hash_algorithm_name == "SHA-384")
99    hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA384;
100  else if (params->hash_algorithm_name == "SHA-512")
101    hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA512;
102  else
103    return RespondNow(Error(kErrorAlgorithmNotSupported));
104
105  chromeos::PlatformKeysService* service =
106      chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
107          browser_context());
108  DCHECK(service);
109
110  service->Sign(
111      params->token_id,
112      params->public_key,
113      hash_algorithm,
114      params->data,
115      extension_id(),
116      base::Bind(&EnterprisePlatformKeysInternalSignFunction::OnSigned, this));
117  return RespondLater();
118}
119
120void EnterprisePlatformKeysInternalSignFunction::OnSigned(
121    const std::string& signature,
122    const std::string& error_message) {
123  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
124  if (error_message.empty())
125    Respond(ArgumentList(api_epki::Sign::Results::Create(signature)));
126  else
127    Respond(Error(error_message));
128}
129
130EnterprisePlatformKeysGetCertificatesFunction::
131    ~EnterprisePlatformKeysGetCertificatesFunction() {
132}
133
134ExtensionFunction::ResponseAction
135EnterprisePlatformKeysGetCertificatesFunction::Run() {
136  scoped_ptr<api_epk::GetCertificates::Params> params(
137      api_epk::GetCertificates::Params::Create(*args_));
138  EXTENSION_FUNCTION_VALIDATE(params);
139  if (!ValidateToken(params->token_id))
140    return RespondNow(Error(kErrorInvalidToken));
141
142  chromeos::platform_keys::GetCertificates(
143      params->token_id,
144      base::Bind(
145          &EnterprisePlatformKeysGetCertificatesFunction::OnGotCertificates,
146          this),
147      browser_context());
148  return RespondLater();
149}
150
151void EnterprisePlatformKeysGetCertificatesFunction::OnGotCertificates(
152    scoped_ptr<net::CertificateList> certs,
153    const std::string& error_message) {
154  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
155  if (!error_message.empty()) {
156    Respond(Error(error_message));
157    return;
158  }
159
160  scoped_ptr<base::ListValue> client_certs(new base::ListValue());
161  for (net::CertificateList::const_iterator it = certs->begin();
162       it != certs->end();
163       ++it) {
164    std::string der_encoding;
165    net::X509Certificate::GetDEREncoded((*it)->os_cert_handle(), &der_encoding);
166    client_certs->Append(base::BinaryValue::CreateWithCopiedBuffer(
167        der_encoding.data(), der_encoding.size()));
168  }
169
170  scoped_ptr<base::ListValue> results(new base::ListValue());
171  results->Append(client_certs.release());
172  Respond(ArgumentList(results.Pass()));
173}
174
175EnterprisePlatformKeysImportCertificateFunction::
176    ~EnterprisePlatformKeysImportCertificateFunction() {
177}
178
179ExtensionFunction::ResponseAction
180EnterprisePlatformKeysImportCertificateFunction::Run() {
181  scoped_ptr<api_epk::ImportCertificate::Params> params(
182      api_epk::ImportCertificate::Params::Create(*args_));
183  EXTENSION_FUNCTION_VALIDATE(params);
184  if (!ValidateToken(params->token_id))
185    return RespondNow(Error(kErrorInvalidToken));
186
187  const std::string& cert_der = params->certificate;
188  scoped_refptr<net::X509Certificate> cert_x509 =
189      net::X509Certificate::CreateFromBytes(cert_der.data(), cert_der.size());
190  if (!cert_x509)
191    return RespondNow(Error(kErrorInvalidX509Cert));
192
193  chromeos::platform_keys::ImportCertificate(
194      params->token_id,
195      cert_x509,
196      base::Bind(&EnterprisePlatformKeysImportCertificateFunction::
197                     OnImportedCertificate,
198                 this),
199      browser_context());
200  return RespondLater();
201}
202
203void EnterprisePlatformKeysImportCertificateFunction::OnImportedCertificate(
204    const std::string& error_message) {
205  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
206  if (error_message.empty())
207    Respond(NoArguments());
208  else
209    Respond(Error(error_message));
210}
211
212EnterprisePlatformKeysRemoveCertificateFunction::
213    ~EnterprisePlatformKeysRemoveCertificateFunction() {
214}
215
216ExtensionFunction::ResponseAction
217EnterprisePlatformKeysRemoveCertificateFunction::Run() {
218  scoped_ptr<api_epk::RemoveCertificate::Params> params(
219      api_epk::RemoveCertificate::Params::Create(*args_));
220  EXTENSION_FUNCTION_VALIDATE(params);
221  if (!ValidateToken(params->token_id))
222    return RespondNow(Error(kErrorInvalidToken));
223
224  const std::string& cert_der = params->certificate;
225  scoped_refptr<net::X509Certificate> cert_x509 =
226      net::X509Certificate::CreateFromBytes(cert_der.data(), cert_der.size());
227  if (!cert_x509)
228    return RespondNow(Error(kErrorInvalidX509Cert));
229
230  chromeos::platform_keys::RemoveCertificate(
231      params->token_id,
232      cert_x509,
233      base::Bind(&EnterprisePlatformKeysRemoveCertificateFunction::
234                     OnRemovedCertificate,
235                 this),
236      browser_context());
237  return RespondLater();
238}
239
240void EnterprisePlatformKeysRemoveCertificateFunction::OnRemovedCertificate(
241    const std::string& error_message) {
242  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
243  if (error_message.empty())
244    Respond(NoArguments());
245  else
246    Respond(Error(error_message));
247}
248
249EnterprisePlatformKeysInternalGetTokensFunction::
250    ~EnterprisePlatformKeysInternalGetTokensFunction() {
251}
252
253ExtensionFunction::ResponseAction
254EnterprisePlatformKeysInternalGetTokensFunction::Run() {
255  EXTENSION_FUNCTION_VALIDATE(args_->empty());
256
257  std::vector<std::string> token_ids;
258  token_ids.push_back(kTokenIdUser);
259  return RespondNow(
260      ArgumentList(api_epki::GetTokens::Results::Create(token_ids)));
261}
262
263}  // namespace extensions
264