| 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..5493e181674be925363dc23b78c39d098cc63d9a
|
| --- /dev/null
|
| +++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
|
| @@ -0,0 +1,563 @@
|
| +// 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/location.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 "chrome/browser/profiles/profile.h"
|
| +#include "content/public/browser/browser_thread.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"
|
| +
|
| +using content::BrowserThread;
|
| +
|
| +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:
|
| + NSSOperationState();
|
| + virtual ~NSSOperationState() {}
|
| +
|
| + // Called if an error occurred during the execution of the NSS operation
|
| + // described by this object.
|
| + virtual void OnError(const tracked_objects::Location& from,
|
| + const std::string& error_message) = 0;
|
| +
|
| + crypto::ScopedPK11Slot slot_;
|
| +
|
| + // 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::Callback<void(net::NSSCertDatabase* cert_db)> GetCertDBCallback;
|
| +
|
| +// Called back with the NSSCertDatabase associated to the given |token_id|.
|
| +// Calls |callback| if the database was successfully retrieved. Used by
|
| +// GetCertDatabaseOnIOThread.
|
| +void DidGetCertDBOnIOThread(const GetCertDBCallback& callback,
|
| + NSSOperationState* state,
|
| + net::NSSCertDatabase* cert_db) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + if (!cert_db) {
|
| + LOG(ERROR) << "Couldn't get NSSCertDatabase.";
|
| + state->OnError(FROM_HERE, kErrorInternal);
|
| + return;
|
| + }
|
| +
|
| + state->slot_ = cert_db->GetPrivateSlot();
|
| + if (!state->slot_) {
|
| + LOG(ERROR) << "No private slot";
|
| + state->OnError(FROM_HERE, kErrorInternal);
|
| + return;
|
| + }
|
| +
|
| + callback.Run(cert_db);
|
| +}
|
| +
|
| +// Retrieves the NSSCertDatabase from |context|. Must be called on the IO
|
| +// thread.
|
| +void GetCertDatabaseOnIOThread(content::ResourceContext* context,
|
| + const GetCertDBCallback& callback,
|
| + NSSOperationState* state) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext(
|
| + context, base::Bind(&DidGetCertDBOnIOThread, callback, state));
|
| +
|
| + if (cert_db)
|
| + DidGetCertDBOnIOThread(callback, state, cert_db);
|
| +}
|
| +
|
| +// Asynchronously fetches the NSSCertDatabase and PK11Slot for |token_id|.
|
| +// Stores the slot in |state| and passes the database to |callback|. Will run
|
| +// |callback| on the IO thread.
|
| +void GetCertDatabase(const std::string& token_id,
|
| + const GetCertDBCallback& callback,
|
| + Profile* profile,
|
| + NSSOperationState* state) {
|
| + // TODO(pneubeck): Decide which DB to retrieve depending on |token_id|.
|
| + BrowserThread::PostTask(BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(&GetCertDatabaseOnIOThread,
|
| + profile->GetResourceContext(),
|
| + callback,
|
| + state));
|
| +}
|
| +
|
| +class GenerateRSAKeyState : public NSSOperationState {
|
| + public:
|
| + GenerateRSAKeyState(unsigned int modulus_length,
|
| + const GenerateKeyCallback& callback);
|
| + virtual ~GenerateRSAKeyState() {}
|
| +
|
| + virtual void OnError(const tracked_objects::Location& from,
|
| + const std::string& error_message) OVERRIDE {
|
| + CallBack(from, std::string() /* no public key */, error_message);
|
| + }
|
| +
|
| + void CallBack(const tracked_objects::Location& from,
|
| + const std::string& public_key_spki_der,
|
| + const std::string& error_message) {
|
| + origin_task_runner_->PostTask(
|
| + from, 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);
|
| + virtual ~SignState() {}
|
| +
|
| + virtual void OnError(const tracked_objects::Location& from,
|
| + const std::string& error_message) OVERRIDE {
|
| + CallBack(from, std::string() /* no signature */, error_message);
|
| + }
|
| +
|
| + void CallBack(const tracked_objects::Location& from,
|
| + const std::string& signature,
|
| + const std::string& error_message) {
|
| + origin_task_runner_->PostTask(
|
| + from, 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:
|
| + explicit GetCertificatesState(const GetCertificatesCallback& callback);
|
| + virtual ~GetCertificatesState() {}
|
| +
|
| + virtual void OnError(const tracked_objects::Location& from,
|
| + const std::string& error_message) OVERRIDE {
|
| + CallBack(from,
|
| + scoped_ptr<net::CertificateList>() /* no certificates */,
|
| + error_message);
|
| + }
|
| +
|
| + void CallBack(const tracked_objects::Location& from,
|
| + scoped_ptr<net::CertificateList> certs,
|
| + const std::string& error_message) {
|
| + origin_task_runner_->PostTask(
|
| + from, base::Bind(callback_, base::Passed(&certs), error_message));
|
| + }
|
| +
|
| + scoped_ptr<net::CertificateList> certs_;
|
| +
|
| + 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);
|
| + virtual ~ImportCertificateState() {}
|
| +
|
| + virtual void OnError(const tracked_objects::Location& from,
|
| + const std::string& error_message) OVERRIDE {
|
| + CallBack(from, error_message);
|
| + }
|
| +
|
| + void CallBack(const tracked_objects::Location& from,
|
| + const std::string& error_message) {
|
| + origin_task_runner_->PostTask(from, 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);
|
| + virtual ~RemoveCertificateState() {}
|
| +
|
| + virtual void OnError(const tracked_objects::Location& from,
|
| + const std::string& error_message) OVERRIDE {
|
| + CallBack(from, error_message);
|
| + }
|
| +
|
| + void CallBack(const tracked_objects::Location& from,
|
| + const std::string& error_message) {
|
| + origin_task_runner_->PostTask(from, base::Bind(callback_, error_message));
|
| + }
|
| +
|
| + scoped_refptr<net::X509Certificate> certificate_;
|
| +
|
| + private:
|
| + // Must be called on origin thread, use CallBack() therefore.
|
| + RemoveCertificateCallback callback_;
|
| +};
|
| +
|
| +NSSOperationState::NSSOperationState()
|
| + : origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
|
| +}
|
| +
|
| +GenerateRSAKeyState::GenerateRSAKeyState(unsigned int modulus_length,
|
| + const GenerateKeyCallback& callback)
|
| + : modulus_length_(modulus_length), callback_(callback) {
|
| +}
|
| +
|
| +SignState::SignState(const std::string& public_key,
|
| + const std::string& data,
|
| + const SignCallback& callback)
|
| + : public_key_(public_key), data_(data), callback_(callback) {
|
| +}
|
| +
|
| +GetCertificatesState::GetCertificatesState(
|
| + const GetCertificatesCallback& callback)
|
| + : callback_(callback) {
|
| +}
|
| +
|
| +ImportCertificateState::ImportCertificateState(
|
| + scoped_refptr<net::X509Certificate> certificate,
|
| + const ImportCertificateCallback& callback)
|
| + : certificate_(certificate), callback_(callback) {
|
| +}
|
| +
|
| +RemoveCertificateState::RemoveCertificateState(
|
| + scoped_refptr<net::X509Certificate> certificate,
|
| + const RemoveCertificateCallback& callback)
|
| + : certificate_(certificate), callback_(callback) {
|
| +}
|
| +
|
| +// Does the actual key generation on a worker thread. Used by
|
| +// GenerateRSAKeyWithDB().
|
| +void GenerateRSAKeyOnWorkerThread(scoped_ptr<GenerateRSAKeyState> state) {
|
| + 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(FROM_HERE, 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(FROM_HERE, kErrorInternal);
|
| + return;
|
| + }
|
| + state->CallBack(
|
| + FROM_HERE,
|
| + std::string(public_key_spki_der.begin(), public_key_spki_der.end()),
|
| + std::string() /* no error */);
|
| +}
|
| +
|
| +// Continues generating a RSA key with the obtained NSSCertDatabase. Used by
|
| +// GenerateRSAKey().
|
| +void GenerateRSAKeyWithDB(scoped_ptr<GenerateRSAKeyState> state,
|
| + net::NSSCertDatabase* cert_db) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + // Only the slot and not the NSSCertDatabase is required. Ignore |cert_db|.
|
| + base::WorkerPool::PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&GenerateRSAKeyOnWorkerThread, base::Passed(&state)),
|
| + true /*task is slow*/);
|
| +}
|
| +
|
| +// Does the actual signing on a worker thread. Used by RSASignWithDB().
|
| +void RSASignOnWorkerThread(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 || rsa_key->key()->pkcs11Slot != state->slot_) {
|
| + state->OnError(FROM_HERE, 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(FROM_HERE, kErrorInternal);
|
| + return;
|
| + }
|
| +
|
| + std::string signature(reinterpret_cast<const char*>(sign_result.data),
|
| + sign_result.len);
|
| + state->CallBack(FROM_HERE, signature, std::string() /* no error */);
|
| +}
|
| +
|
| +// Continues signing with the obtained NSSCertDatabase. Used by Sign().
|
| +void RSASignWithDB(scoped_ptr<SignState> state, net::NSSCertDatabase* cert_db) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + // Only the slot and not the NSSCertDatabase is required. Ignore |cert_db|.
|
| + base::WorkerPool::PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&RSASignOnWorkerThread, base::Passed(&state)),
|
| + true /*task is slow*/);
|
| +}
|
| +
|
| +// Filters the obtained certificates on a worker thread. Used by
|
| +// DidGetCertificates().
|
| +void FilterCertificatesOnWorkerThread(scoped_ptr<GetCertificatesState> state) {
|
| + scoped_ptr<net::CertificateList> client_certs(new net::CertificateList);
|
| + for (net::CertificateList::const_iterator it = state->certs_->begin();
|
| + it != state->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(FROM_HERE, client_certs.Pass(), std::string() /* no error */);
|
| +}
|
| +
|
| +// Passes the obtained certificates to the worker thread for filtering. Used by
|
| +// GetCertificatesWithDB().
|
| +void DidGetCertificates(scoped_ptr<GetCertificatesState> state,
|
| + scoped_ptr<net::CertificateList> all_certs) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + state->certs_ = all_certs.Pass();
|
| + base::WorkerPool::PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&FilterCertificatesOnWorkerThread, base::Passed(&state)),
|
| + true /*task is slow*/);
|
| +}
|
| +
|
| +// Continues getting certificates with the obtained NSSCertDatabase. Used by
|
| +// GetCertificates().
|
| +void GetCertificatesWithDB(scoped_ptr<GetCertificatesState> state,
|
| + net::NSSCertDatabase* cert_db) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + // Get the pointer to slot before base::Passed releases |state|.
|
| + PK11SlotInfo* slot = state->slot_.get();
|
| + cert_db->ListCertsInSlot(
|
| + base::Bind(&DidGetCertificates, base::Passed(&state)), slot);
|
| +}
|
| +
|
| +// Does the actual certificate importing on the IO thread. Used by
|
| +// ImportCertificate().
|
| +void ImportCertificateWithDB(scoped_ptr<ImportCertificateState> state,
|
| + net::NSSCertDatabase* cert_db) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + // TODO(pneubeck): Use |state->slot_| to verify that we're really importing to
|
| + // the correct token.
|
| + // |cert_db| is not required, ignore it.
|
| + net::CertDatabase* db = net::CertDatabase::GetInstance();
|
| +
|
| + const net::Error cert_status =
|
| + static_cast<net::Error>(db->CheckUserCert(state->certificate_));
|
| + if (cert_status == net::ERR_NO_PRIVATE_KEY_FOR_CERT) {
|
| + state->OnError(FROM_HERE, kErrorKeyNotFound);
|
| + return;
|
| + } else if (cert_status != net::OK) {
|
| + state->OnError(FROM_HERE, net::ErrorToString(cert_status));
|
| + return;
|
| + }
|
| +
|
| + const net::Error import_status =
|
| + static_cast<net::Error>(db->AddUserCert(state->certificate_.get()));
|
| + if (import_status != net::OK) {
|
| + LOG(ERROR) << "Could not import certificate.";
|
| + state->OnError(FROM_HERE, net::ErrorToString(import_status));
|
| + return;
|
| + }
|
| +
|
| + state->CallBack(FROM_HERE, std::string() /* no error */);
|
| +}
|
| +
|
| +// Called on IO thread after the certificate removal is finished.
|
| +void DidRemoveCertificate(scoped_ptr<RemoveCertificateState> state,
|
| + bool certificate_found,
|
| + bool success) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + // CertificateNotFound error has precedence over an internal error.
|
| + if (!certificate_found) {
|
| + state->OnError(FROM_HERE, kErrorCertificateNotFound);
|
| + return;
|
| + }
|
| + if (!success) {
|
| + state->OnError(FROM_HERE, kErrorInternal);
|
| + return;
|
| + }
|
| +
|
| + state->CallBack(FROM_HERE, std::string() /* no error */);
|
| +}
|
| +
|
| +// Does the actual certificate removal on the IO thread. Used by
|
| +// RemoveCertificate().
|
| +void RemoveCertificateWithDB(scoped_ptr<RemoveCertificateState> state,
|
| + net::NSSCertDatabase* cert_db) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + // Get the pointer before base::Passed clears |state|.
|
| + scoped_refptr<net::X509Certificate> certificate = state->certificate_;
|
| + bool certificate_found = certificate->os_cert_handle()->isperm;
|
| + cert_db->DeleteCertAndKeyAsync(
|
| + certificate,
|
| + base::Bind(
|
| + &DidRemoveCertificate, base::Passed(&state), certificate_found));
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +void GenerateRSAKey(const std::string& token_id,
|
| + unsigned int modulus_length,
|
| + const GenerateKeyCallback& callback,
|
| + Profile* profile) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + scoped_ptr<GenerateRSAKeyState> state(
|
| + new GenerateRSAKeyState(modulus_length, callback));
|
| +
|
| + if (modulus_length > kMaxRSAModulusLength) {
|
| + state->OnError(FROM_HERE, kErrorAlgorithmNotSupported);
|
| + return;
|
| + }
|
| +
|
| + // Get the pointer to |state| before base::Passed releases |state|.
|
| + NSSOperationState* state_ptr = state.get();
|
| + GetCertDatabase(token_id,
|
| + base::Bind(&GenerateRSAKeyWithDB, base::Passed(&state)),
|
| + profile,
|
| + state_ptr);
|
| +}
|
| +
|
| +void Sign(const std::string& token_id,
|
| + const std::string& public_key,
|
| + const std::string& data,
|
| + const SignCallback& callback,
|
| + Profile* profile) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + scoped_ptr<SignState> state(new SignState(public_key, data, callback));
|
| + // Get the pointer to |state| before base::Passed releases |state|.
|
| + NSSOperationState* state_ptr = state.get();
|
| +
|
| + // The NSSCertDatabase object is not required. But in case it's not available
|
| + // we would get more informative error messages and we can double check that
|
| + // we use a key of the correct token.
|
| + GetCertDatabase(token_id,
|
| + base::Bind(&RSASignWithDB, base::Passed(&state)),
|
| + profile,
|
| + state_ptr);
|
| +}
|
| +
|
| +void GetCertificates(const std::string& token_id,
|
| + const GetCertificatesCallback& callback,
|
| + Profile* profile) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + scoped_ptr<GetCertificatesState> state(new GetCertificatesState(callback));
|
| + // Get the pointer to |state| before base::Passed releases |state|.
|
| + NSSOperationState* state_ptr = state.get();
|
| + GetCertDatabase(token_id,
|
| + base::Bind(&GetCertificatesWithDB, base::Passed(&state)),
|
| + profile,
|
| + state_ptr);
|
| +}
|
| +
|
| +void ImportCertificate(const std::string& token_id,
|
| + scoped_refptr<net::X509Certificate> certificate,
|
| + const ImportCertificateCallback& callback,
|
| + Profile* profile) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + scoped_ptr<ImportCertificateState> state(
|
| + new ImportCertificateState(certificate, callback));
|
| + // Get the pointer to |state| before base::Passed releases |state|.
|
| + NSSOperationState* state_ptr = state.get();
|
| +
|
| + // The NSSCertDatabase object is not required. But in case it's not available
|
| + // we would get more informative error messages and we can double check that
|
| + // we use a key of the correct token.
|
| + GetCertDatabase(token_id,
|
| + base::Bind(&ImportCertificateWithDB, base::Passed(&state)),
|
| + profile,
|
| + state_ptr);
|
| +}
|
| +
|
| +void RemoveCertificate(const std::string& token_id,
|
| + scoped_refptr<net::X509Certificate> certificate,
|
| + const RemoveCertificateCallback& callback,
|
| + Profile* profile) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + scoped_ptr<RemoveCertificateState> state(
|
| + new RemoveCertificateState(certificate, callback));
|
| + // Get the pointer to |state| before base::Passed releases |state|.
|
| + NSSOperationState* state_ptr = state.get();
|
| +
|
| + // The NSSCertDatabase object is not required. But in case it's not available
|
| + // we would get more informative error messages.
|
| + GetCertDatabase(token_id,
|
| + base::Bind(&RemoveCertificateWithDB, base::Passed(&state)),
|
| + profile,
|
| + state_ptr);
|
| +}
|
| +
|
| +} // namespace platform_keys
|
| +
|
| +} // namespace chromeos
|
|
|