Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(877)

Unified Diff: chrome/browser/chromeos/platform_keys/platform_keys_nss.cc

Issue 214863002: Extension API enterprise.platformKeys. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: NSS operations now asynchronous. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1abb2c988d84857c4fa9c998fd47b5b3c09b1a21
--- /dev/null
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
@@ -0,0 +1,470 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
+
+#include <cryptohi.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/worker_pool.h"
+#include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h"
+#include "chrome/browser/net/nss_context.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/crypto_module.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_database.h"
+#include "net/cert/nss_cert_database.h"
+#include "net/cert/x509_certificate.h"
+
+namespace {
+const char kErrorInternal[] = "Internal Error.";
+const char kErrorKeyNotFound[] = "Key not found.";
+const char kErrorCertificateNotFound[] = "Certificate could not be found.";
+const char kErrorAlgorithmNotSupported[] = "Algorithm not supported.";
+
+// The current maximal RSA modulus length that ChromeOS's TPM supports for key
+// generation.
+const unsigned int kMaxRSAModulusLength = 2048;
+}
+
+namespace chromeos {
+
+namespace platform_keys {
+
+namespace {
+
+// Base class to store state that is common to all NSS database operations and
+// to provide convenience methods to call back.
+// Keeps track of the originating task runner.
+class NSSOperationState {
+ public:
+ explicit NSSOperationState(Profile* profile);
+ virtual ~NSSOperationState() {}
+
+ // Called if an error occurred during the execution of the NSS operation
+ // described by this object.
+ virtual void OnError(const std::string& error_message) = 0;
+
+ Profile* profile_;
+ crypto::ScopedPK11Slot slot_;
+ net::NSSCertDatabase* cert_db_;
+
+ // The task runner on which the NSS operation was called. Any reply must be
+ // posted to this runner.
+ scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NSSOperationState);
+};
+
+typedef base::Closure GetCertDBCallback;
+
+// Callback of GetCertDatabase. Called back with the NSSCertDatabase associated
+// to the given |token_id|.
+void DidGetCertDB(const GetCertDBCallback& callback,
+ NSSOperationState* state,
+ net::NSSCertDatabase* cert_db) {
+ if (!cert_db) {
+ LOG(ERROR) << "Couldn't get NSSCertDatabase.";
+ state->OnError(kErrorInternal);
+ return;
+ }
+
+ state->cert_db_ = cert_db;
+ state->slot_ = cert_db->GetPrivateSlot();
+ if (!state->slot_) {
+ LOG(ERROR) << "No private slot";
+ state->OnError(kErrorInternal);
+ return;
+ }
+
+ base::WorkerPool::PostTask(FROM_HERE, callback, true /*task is slow*/);
mattm 2014/05/16 20:27:12 This is problematic, since the cert_db_ could go a
pneubeck (no reviews) 2014/05/19 19:17:19 Thanks for pointing this out, I though of this too
+}
+
+// Asynchronously fetches the NSSCertDatabase for |token_id| and passes it to
+// |callback|. Will run |callback| on a worker thread.
+void GetCertDatabase(const std::string& token_id,
+ const GetCertDBCallback& callback,
+ NSSOperationState* state) {
+ GetNSSCertDatabaseForProfile(state->profile_,
+ base::Bind(&DidGetCertDB, callback, state));
+}
+
+class GenerateRSAKeyState : public NSSOperationState {
+ public:
+ GenerateRSAKeyState(unsigned int modulus_length,
+ const GenerateKeyCallback& callback,
+ Profile* profile);
+ virtual ~GenerateRSAKeyState() {}
+
+ virtual void OnError(const std::string& error_message) OVERRIDE {
+ CallBack(std::string() /* no public key */, error_message);
+ }
+
+ void CallBack(const std::string& public_key_spki_der,
mattm 2014/05/16 20:27:12 Pass the FROM_HERE through the CallBack methods ar
pneubeck (no reviews) 2014/05/19 19:17:19 Done.
+ const std::string& error_message) {
+ origin_task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback_, public_key_spki_der, error_message));
+ }
+
+ unsigned int modulus_length_;
+
+ private:
+ // Must be called on origin thread, use CallBack() therefore.
+ GenerateKeyCallback callback_;
+};
+
+class SignState : public NSSOperationState {
+ public:
+ SignState(const std::string& public_key,
+ const std::string& data,
+ const SignCallback& callback,
+ Profile* profile);
+ virtual ~SignState() {}
+
+ virtual void OnError(const std::string& error_message) OVERRIDE {
+ CallBack(std::string() /* no signature */, error_message);
+ }
+
+ void CallBack(const std::string& signature,
+ const std::string& error_message) {
+ origin_task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback_, signature, error_message));
+ }
+
+ std::string public_key_;
+ std::string data_;
+
+ private:
+ // Must be called on origin thread, use CallBack() therefore.
+ SignCallback callback_;
+};
+
+class GetCertificatesState : public NSSOperationState {
+ public:
+ GetCertificatesState(const GetCertificatesCallback& callback,
+ Profile* profile);
+ virtual ~GetCertificatesState() {}
+
+ virtual void OnError(const std::string& error_message) OVERRIDE {
+ CallBack(scoped_ptr<net::CertificateList>() /* no certificates */,
+ error_message);
+ }
+
+ void CallBack(scoped_ptr<net::CertificateList> certs,
+ const std::string& error_message) {
+ origin_task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback_, base::Passed(&certs), error_message));
+ }
+
+ private:
+ // Must be called on origin thread, use CallBack() therefore.
+ GetCertificatesCallback callback_;
+};
+
+class ImportCertificateState : public NSSOperationState {
+ public:
+ ImportCertificateState(scoped_refptr<net::X509Certificate> certificate,
+ const ImportCertificateCallback& callback,
+ Profile* profile);
+ virtual ~ImportCertificateState() {}
+
+ virtual void OnError(const std::string& error_message) OVERRIDE {
+ CallBack(error_message);
+ }
+
+ void CallBack(const std::string& error_message) {
+ origin_task_runner_->PostTask(FROM_HERE,
+ base::Bind(callback_, error_message));
+ }
+
+ scoped_refptr<net::X509Certificate> certificate_;
+
+ private:
+ // Must be called on origin thread, use CallBack() therefore.
+ ImportCertificateCallback callback_;
+};
+
+class RemoveCertificateState : public NSSOperationState {
+ public:
+ RemoveCertificateState(scoped_refptr<net::X509Certificate> certificate,
+ const RemoveCertificateCallback& callback,
+ Profile* profile);
+ virtual ~RemoveCertificateState() {}
+
+ virtual void OnError(const std::string& error_message) OVERRIDE {
+ CallBack(error_message);
+ }
+
+ void CallBack(const std::string& error_message) {
+ origin_task_runner_->PostTask(FROM_HERE,
+ base::Bind(callback_, error_message));
+ }
+
+ scoped_refptr<net::X509Certificate> certificate_;
+
+ private:
+ // Must be called on origin thread, use CallBack() therefore.
+ RemoveCertificateCallback callback_;
+};
+
+NSSOperationState::NSSOperationState(Profile* profile)
+ : profile_(profile),
+ cert_db_(NULL),
+ origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+}
+
+GenerateRSAKeyState::GenerateRSAKeyState(unsigned int modulus_length,
+ const GenerateKeyCallback& callback,
+ Profile* profile)
+ : NSSOperationState(profile),
+ modulus_length_(modulus_length),
+ callback_(callback) {
+}
+
+SignState::SignState(const std::string& public_key,
+ const std::string& data,
+ const SignCallback& callback,
+ Profile* profile)
+ : NSSOperationState(profile),
+ public_key_(public_key),
+ data_(data),
+ callback_(callback) {
+}
+
+GetCertificatesState::GetCertificatesState(
+ const GetCertificatesCallback& callback,
+ Profile* profile)
+ : NSSOperationState(profile), callback_(callback) {
+}
+
+ImportCertificateState::ImportCertificateState(
+ scoped_refptr<net::X509Certificate> certificate,
+ const ImportCertificateCallback& callback,
+ Profile* profile)
+ : NSSOperationState(profile),
+ certificate_(certificate),
+ callback_(callback) {
+}
+
+RemoveCertificateState::RemoveCertificateState(
+ scoped_refptr<net::X509Certificate> certificate,
+ const RemoveCertificateCallback& callback,
+ Profile* profile)
+ : NSSOperationState(profile),
+ certificate_(certificate),
+ callback_(callback) {
+}
+
+// Continues generating a RSA key with the obtained NSSCertDatabase. Used by
+// GenerateRSAKey().
+void GenerateRSAKeyWithDB(scoped_ptr<GenerateRSAKeyState> state) {
+ if (state->modulus_length_ > kMaxRSAModulusLength) {
+ state->OnError(kErrorAlgorithmNotSupported);
+ return;
+ }
+ scoped_ptr<crypto::RSAPrivateKey> rsa_key(
+ crypto::RSAPrivateKey::CreateSensitive(state->slot_.get(),
+ state->modulus_length_));
+ if (!rsa_key) {
+ LOG(ERROR) << "Couldn't create key.";
+ state->OnError(kErrorInternal);
+ return;
+ }
+
+ std::vector<uint8> public_key_spki_der;
+ if (!rsa_key->ExportPublicKey(&public_key_spki_der)) {
+ // TODO(pneubeck): Remove rsa_key from storage.
+ LOG(ERROR) << "Couldn't export public key.";
+ state->OnError(kErrorInternal);
+ return;
+ }
+ state->CallBack(
+ std::string(public_key_spki_der.begin(), public_key_spki_der.end()),
+ std::string() /* no error */);
+}
+
+// Continues signing with the obtained NSSCertDatabase. Used by Sign().
+void RSASignWithDB(scoped_ptr<SignState> state) {
+ const uint8* public_key_uint8 =
+ reinterpret_cast<const uint8*>(state->public_key_.data());
+ std::vector<uint8> public_key_vector(
+ public_key_uint8, public_key_uint8 + state->public_key_.size());
+
+ // TODO(pneubeck): This searches all slots. Change to look only at |slot_|.
+ scoped_ptr<crypto::RSAPrivateKey> rsa_key(
+ crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key_vector));
+ if (!rsa_key) {
+ state->OnError(kErrorKeyNotFound);
+ return;
+ }
+
+ SECItem sign_result = {siBuffer, NULL, 0};
+ if (SEC_SignData(&sign_result,
+ reinterpret_cast<const unsigned char*>(state->data_.data()),
+ state->data_.size(),
+ rsa_key->key(),
+ SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION) != SECSuccess) {
+ LOG(ERROR) << "Couldn't sign.";
+ state->OnError(kErrorInternal);
+ return;
+ }
+
+ std::string signature(reinterpret_cast<const char*>(sign_result.data),
+ sign_result.len);
+ state->CallBack(signature, std::string() /* no error */);
+}
+
+// Continues getting certificates with the certificates returned by
+// NSSCertDatabase::ListCertsInSlot. Used by GetCertificatesWithDB().
+void DidGetCertificates(scoped_ptr<GetCertificatesState> state,
+ scoped_ptr<net::CertificateList> all_certs) {
+ scoped_ptr<net::CertificateList> client_certs(new net::CertificateList);
+ for (net::CertificateList::const_iterator it = all_certs->begin();
+ it != all_certs->end();
+ ++it) {
+ net::X509Certificate::OSCertHandle cert_handle = (*it)->os_cert_handle();
+ crypto::ScopedPK11Slot cert_slot(PK11_KeyForCertExists(cert_handle,
+ NULL, // keyPtr
+ NULL)); // wincx
+
+ // Keep only user certificates, i.e. certs for which the private key is
+ // present and stored in the queried slot.
+ if (cert_slot != state->slot_)
+ continue;
+
+ client_certs->push_back(*it);
+ }
+
+ state->CallBack(client_certs.Pass(), std::string() /* no error */);
+}
+
+// Continues getting certificates with the obtained NSSCertDatabase. Used by
+// GetCertificates().
+void GetCertificatesWithDB(scoped_ptr<GetCertificatesState> state) {
+ // Get the pointer to slot before base::Passed releases |state|.
+ PK11SlotInfo* slot = state->slot_.get();
+ state->cert_db_->ListCertsInSlot(
+ base::Bind(&DidGetCertificates, base::Passed(&state)), slot);
+}
+
+// Continues certificate importing with the obtained NSSCertDatabase. Used by
+// ImportCertificate().
+void ImportCertificateWithDB(scoped_ptr<ImportCertificateState> state) {
+ net::CertDatabase* db = net::CertDatabase::GetInstance();
+
+ const net::Error cert_status = db->CheckUserCert(state->certificate_);
+ if (cert_status == net::ERR_NO_PRIVATE_KEY_FOR_CERT) {
+ state->OnError(kErrorKeyNotFound);
+ return;
+ } else if (cert_status != net::OK) {
+ state->OnError(net::ErrorToString(cert_status));
+ return;
+ }
+
+ const net::Error import_status = db->AddUserCert(state->certificate_.get());
+ if (import_status != net::OK) {
+ LOG(ERROR) << "Could not import certificate.";
+ state->OnError(net::ErrorToString(import_status));
+ return;
+ }
+
+ state->CallBack(std::string() /* no error */);
+}
+
+// Continues certificate removal with the obtained NSSCertDatabase. Used by
+// RemoveCertificate().
+void RemoveCertificateWithDB(scoped_ptr<RemoveCertificateState> state) {
+ bool certificate_found = state->certificate_->os_cert_handle()->isperm;
+ bool success = state->cert_db_->DeleteCertAndKey(state->certificate_);
+
+ // CertificateNotFound error has precedence over an internal error.
+ if (!certificate_found) {
+ state->OnError(kErrorCertificateNotFound);
+ return;
+ }
+ if (!success) {
+ state->OnError(kErrorInternal);
+ return;
+ }
+
+ state->CallBack(std::string() /* no error */);
+}
+
+} // namespace
+
+void GenerateRSAKey(const std::string& token_id,
+ unsigned int modulus_length,
+ const GenerateKeyCallback& callback,
+ Profile* profile) {
+ scoped_ptr<GenerateRSAKeyState> state(
+ new GenerateRSAKeyState(modulus_length, callback, profile));
+ // Get the pointer to |state| before base::Passed releases |state|.
+ NSSOperationState* state_ptr = state.get();
+ GetCertDatabase(token_id,
+ base::Bind(&GenerateRSAKeyWithDB, base::Passed(&state)),
+ state_ptr);
+}
+
+void Sign(const std::string& token_id,
+ const std::string& public_key,
+ const std::string& data,
+ const SignCallback& callback,
+ Profile* profile) {
+ scoped_ptr<SignState> state(
+ new SignState(public_key, data, callback, profile));
+ // Get the pointer to |state| before base::Passed releases |state|.
+ NSSOperationState* state_ptr = state.get();
+ GetCertDatabase(
+ token_id, base::Bind(&RSASignWithDB, base::Passed(&state)), state_ptr);
+}
+
+void GetCertificates(const std::string& token_id,
+ const GetCertificatesCallback& callback,
+ Profile* profile) {
+ scoped_ptr<GetCertificatesState> state(
+ new GetCertificatesState(callback, profile));
+ // Get the pointer to |state| before base::Passed releases |state|.
+ NSSOperationState* state_ptr = state.get();
+ GetCertDatabase(token_id,
+ base::Bind(&GetCertificatesWithDB, base::Passed(&state)),
+ state_ptr);
+}
+
+void ImportCertificate(const std::string& token_id,
+ scoped_refptr<net::X509Certificate> certificate,
+ const ImportCertificateCallback& callback,
+ Profile* profile) {
+ scoped_ptr<ImportCertificateState> state(
+ new ImportCertificateState(certificate, callback, profile));
+ // Get the pointer to |state| before base::Passed releases |state|.
+ NSSOperationState* state_ptr = state.get();
+ GetCertDatabase(token_id,
+ base::Bind(&ImportCertificateWithDB, base::Passed(&state)),
+ state_ptr);
+}
+
+void RemoveCertificate(const std::string& token_id,
+ scoped_refptr<net::X509Certificate> certificate,
+ const RemoveCertificateCallback& callback,
+ Profile* profile) {
+ scoped_ptr<RemoveCertificateState> state(
+ new RemoveCertificateState(certificate, callback, profile));
+ // Get the pointer to |state| before base::Passed releases |state|.
+ NSSOperationState* state_ptr = state.get();
+ GetCertDatabase(token_id,
+ base::Bind(&RemoveCertificateWithDB, base::Passed(&state)),
+ state_ptr);
+}
+
+} // namespace platform_keys
+
+} // namespace chromeos
« no previous file with comments | « chrome/browser/chromeos/platform_keys/platform_keys.h ('k') | chrome/browser/extensions/api/enterprise_platform_keys/OWNERS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698