Chromium Code Reviews| 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..272ceef4ae5a423123e39d8794981c2a6e6f7f8c |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc |
| @@ -0,0 +1,412 @@ |
| +// 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 "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."; |
| +} |
| + |
| +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. |
| +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_; |
| + |
| + 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; |
| + } |
| + callback.Run(); |
| +} |
| + |
| +// Asynchronously fetches the NSSCertDatabase for |token_id| and passes it to |
| +// |callback|. |
| +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(int modulus_length, |
| + const GenerateKeyCallback& callback, |
| + Profile* profile); |
| + virtual ~GenerateRSAKeyState() {} |
| + |
| + virtual void OnError(const std::string& error_message) OVERRIDE { |
| + callback_.Run(std::string() /* no public key */, error_message); |
| + } |
| + |
| + int modulus_length_; |
| + 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_.Run(std::string() /* no signature */, error_message); |
| + } |
| + |
| + std::string public_key_; |
| + std::string data_; |
| + 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_.Run(scoped_ptr<net::CertificateList>() /* no certificates */, |
| + error_message); |
| + } |
| + |
| + 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_.Run(error_message); |
| + } |
| + |
| + scoped_refptr<net::X509Certificate> certificate_; |
| + 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_.Run(error_message); |
| + } |
| + |
| + scoped_refptr<net::X509Certificate> certificate_; |
| + RemoveCertificateCallback callback_; |
| +}; |
| + |
| +NSSOperationState::NSSOperationState(Profile* profile) |
| + : profile_(profile), cert_db_(NULL) { |
| +} |
| + |
| +GenerateRSAKeyState::GenerateRSAKeyState(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_ > 2048) { |
|
Ryan Sleevi
2014/05/15 19:57:13
and negative moduli?
should 2048 be a const? kMaxR
pneubeck (no reviews)
2014/05/16 12:30:16
Done.
|
| + 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_der; |
| + if (!rsa_key->ExportPublicKey(&public_key_der)) { |
| + // TODO(pneubeck): Remove rsa_key from storage. |
| + LOG(ERROR) << "Couldn't export public key."; |
| + state->OnError(kErrorInternal); |
| + return; |
| + } |
| + std::string public_key_der_str(public_key_der.begin(), public_key_der.end()); |
| + state->callback_.Run(public_key_der_str, 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_.Run(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_.Run(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_.Run(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_.Run(std::string() /* no error */); |
| +} |
| + |
| +} // namespace |
| + |
| +void GenerateRSAKey(const std::string& token_id, |
| + 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 |