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 |