Index: chrome/browser/extensions/api/enterprise_platform_keys/token_method_nss.cc |
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/token_method_nss.cc b/chrome/browser/extensions/api/enterprise_platform_keys/token_method_nss.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d19a3efc865cdc2c2a2975bd442ca9fa3d0f0a85 |
--- /dev/null |
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/token_method_nss.cc |
@@ -0,0 +1,396 @@ |
+// 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/extensions/api/enterprise_platform_keys/token_method.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/memory/weak_ptr.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 kErrorInvalidX509Cert[] = |
+ "Certificate is not a valid X.509 certificate."; |
+const char kErrorCertificateNotFound[] = "Certificate could not be found."; |
+const char kErrorAlgorithmNotSupported[] = "Algorithm not supported."; |
+} |
+ |
+namespace extensions { |
+ |
+namespace enterprise_platform_keys_details { |
+ |
+namespace { |
+ |
+namespace api_epk = api::enterprise_platform_keys; |
+namespace api_epki = api::enterprise_platform_keys_internal; |
+ |
+typedef base::Callback<void(crypto::ScopedPK11Slot slot, |
+ net::NSSCertDatabase* cert_db)> GetCertDBCallback; |
+ |
+void DidGetCertDB(const GetCertDBCallback& callback, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func, |
+ net::NSSCertDatabase* cert_db) { |
+ if (!cert_db) { |
+ LOG(ERROR) << "Couldn't get NSSCertDatabase."; |
+ func->RespondWithError(kErrorInternal); |
+ return; |
+ } |
+ |
+ crypto::ScopedPK11Slot slot = cert_db->GetPrivateSlot(); |
+ if (!slot) { |
+ LOG(ERROR) << "No private slot"; |
+ func->RespondWithError(kErrorInternal); |
+ return; |
+ } |
+ callback.Run(slot.Pass(), cert_db); |
+} |
+ |
+void GetCertDatabase(const std::string& token_id, |
+ const GetCertDBCallback& callback, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) { |
+ GetNSSCertDatabaseForProfile( |
+ func->GetProfile(), |
+ base::Bind(&DidGetCertDB, callback, base::Unretained(func))); |
+} |
+ |
+class GenerateKey : public TokenMethod { |
+ public: |
+ GenerateKey(scoped_ptr<api_epki::GenerateKey::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func); |
+ virtual ~GenerateKey() {} |
+ virtual void Run() OVERRIDE; |
+ |
+ void DidGetDB(crypto::ScopedPK11Slot slot, net::NSSCertDatabase* cert_db); |
+ |
+ private: |
+ scoped_ptr<api_epki::GenerateKey::Params> params_; |
+ base::WeakPtrFactory<GenerateKey> weak_factory_; |
+}; |
+ |
+class Sign : public TokenMethod { |
+ public: |
+ Sign(scoped_ptr<api_epki::Sign::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func); |
+ virtual ~Sign() {} |
+ virtual void Run() OVERRIDE; |
+ |
+ void DidGetDB(crypto::ScopedPK11Slot slot, net::NSSCertDatabase* cert_db); |
+ |
+ private: |
+ scoped_ptr<api_epki::Sign::Params> params_; |
+ base::WeakPtrFactory<Sign> weak_factory_; |
+}; |
+ |
+class GetCertificates : public TokenMethod { |
+ public: |
+ GetCertificates(scoped_ptr<api_epk::GetCertificates::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func); |
+ virtual ~GetCertificates() {} |
+ virtual void Run() OVERRIDE; |
+ |
+ void DidGetDB(crypto::ScopedPK11Slot slot, net::NSSCertDatabase* cert_db); |
+ void DidGetCertificates(scoped_ptr<net::CertificateList> certs); |
+ |
+ private: |
+ scoped_ptr<api_epk::GetCertificates::Params> params_; |
+ crypto::ScopedPK11Slot slot_; |
+ base::WeakPtrFactory<GetCertificates> weak_factory_; |
+}; |
+ |
+class ImportCertificate : public TokenMethod { |
+ public: |
+ ImportCertificate(scoped_ptr<api_epk::ImportCertificate::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func); |
+ virtual ~ImportCertificate() {} |
+ virtual void Run() OVERRIDE; |
+ |
+ void DidGetDB(crypto::ScopedPK11Slot slot, net::NSSCertDatabase* cert_db); |
+ |
+ private: |
+ scoped_ptr<api_epk::ImportCertificate::Params> params_; |
+ base::WeakPtrFactory<ImportCertificate> weak_factory_; |
+}; |
+ |
+class RemoveCertificate : public TokenMethod { |
+ public: |
+ RemoveCertificate(scoped_ptr<api_epk::RemoveCertificate::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func); |
+ virtual ~RemoveCertificate() {} |
+ virtual void Run() OVERRIDE; |
+ |
+ void DidGetDB(crypto::ScopedPK11Slot slot, net::NSSCertDatabase* cert_db); |
+ |
+ private: |
+ scoped_ptr<api_epk::RemoveCertificate::Params> params_; |
+ base::WeakPtrFactory<RemoveCertificate> weak_factory_; |
+}; |
+ |
+GenerateKey::GenerateKey( |
+ scoped_ptr<api_epki::GenerateKey::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) |
+ : TokenMethod(func), params_(params.Pass()), weak_factory_(this) { |
+} |
+ |
+void GenerateKey::Run() { |
+ GetCertDatabase( |
+ params_->token_id, |
+ base::Bind(&GenerateKey::DidGetDB, weak_factory_.GetWeakPtr()), |
+ func_); |
+} |
+ |
+void GenerateKey::DidGetDB(crypto::ScopedPK11Slot slot, |
+ net::NSSCertDatabase* cert_db) { |
+ const int modulus_length = params_->modulus_length; |
+ if (modulus_length > 2048) { |
+ func_->RespondWithError(kErrorAlgorithmNotSupported); |
+ return; |
+ } |
+ scoped_ptr<crypto::RSAPrivateKey> rsa_key( |
+ crypto::RSAPrivateKey::CreateSensitive(slot.get(), modulus_length)); |
+ if (!rsa_key) { |
+ LOG(ERROR) << "Couldn't create key."; |
+ func_->RespondWithError(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."; |
+ func_->RespondWithError(kErrorInternal); |
+ return; |
+ } |
+ std::string public_key_der_str(public_key_der.begin(), public_key_der.end()); |
+ |
+ func_->RespondWithResults( |
+ api_epki::GenerateKey::Results::Create(public_key_der_str)); |
+} |
+ |
+Sign::Sign(scoped_ptr<api_epki::Sign::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) |
+ : TokenMethod(func), params_(params.Pass()), weak_factory_(this) { |
+} |
+ |
+void Sign::Run() { |
+ GetCertDatabase(params_->token_id, |
+ base::Bind(&Sign::DidGetDB, weak_factory_.GetWeakPtr()), |
+ func_); |
+} |
+ |
+void Sign::DidGetDB(crypto::ScopedPK11Slot slot, |
+ net::NSSCertDatabase* cert_db) { |
+ const std::string& public_key = params_->public_key; |
+ const uint8* public_key_uint8 = |
+ reinterpret_cast<const uint8*>(public_key.data()); |
+ std::vector<uint8> public_key_vector(public_key_uint8, |
+ public_key_uint8 + 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) { |
+ func_->RespondWithError(kErrorKeyNotFound); |
+ return; |
+ } |
+ |
+ const std::string& data = params_->data; |
+ SECItem sign_result = {siBuffer, NULL, 0}; |
+ if (SEC_SignData(&sign_result, |
+ reinterpret_cast<const unsigned char*>(data.data()), |
+ data.size(), |
+ rsa_key->key(), |
+ SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION) != SECSuccess) { |
+ LOG(ERROR) << "Couldn't sign."; |
+ func_->RespondWithError(kErrorInternal); |
+ return; |
+ } |
+ |
+ std::string signature(reinterpret_cast<const char*>(sign_result.data), |
+ sign_result.len); |
+ func_->RespondWithResults(api_epki::Sign::Results::Create(signature)); |
+} |
+ |
+GetCertificates::GetCertificates( |
+ scoped_ptr<api_epk::GetCertificates::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) |
+ : TokenMethod(func), params_(params.Pass()), weak_factory_(this) { |
+} |
+ |
+void GetCertificates::Run() { |
+ GetCertDatabase( |
+ params_->token_id, |
+ base::Bind(&GetCertificates::DidGetDB, weak_factory_.GetWeakPtr()), |
+ func_); |
+} |
+ |
+void GetCertificates::DidGetDB(crypto::ScopedPK11Slot slot, |
+ net::NSSCertDatabase* cert_db) { |
+ slot_ = slot.Pass(); |
+ cert_db->ListCertsInSlot(base::Bind(&GetCertificates::DidGetCertificates, |
+ weak_factory_.GetWeakPtr()), |
+ slot_.get()); |
+} |
+ |
+void GetCertificates::DidGetCertificates( |
+ scoped_ptr<net::CertificateList> certs) { |
+ // std::vector<std::string> client_certs; |
+ scoped_ptr<base::ListValue> client_certs(new base::ListValue()); |
+ for (net::CertificateList::const_iterator it = certs->begin(); |
+ it != 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 certificate, i.e. certs for which the private key is |
+ // present, and certs where the private key is stored in the queried slot. |
+ if (cert_slot != slot_) |
+ continue; |
+ |
+ std::string der_encoding; |
+ net::X509Certificate::GetDEREncoded(cert_handle, &der_encoding); |
+ // client_certs.push_back(der_encoding); |
+ client_certs->Append(base::BinaryValue::CreateWithCopiedBuffer( |
+ der_encoding.data(), der_encoding.size())); |
+ } |
+ scoped_ptr<base::ListValue> results(new base::ListValue()); |
+ results->Append(client_certs.release()); |
+ func_->RespondWithResults(results.Pass()); |
+ // results_ = api_eci::GetClientCertificates::Results::Create(client_certs); |
+} |
+ |
+ImportCertificate::ImportCertificate( |
+ scoped_ptr<api_epk::ImportCertificate::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) |
+ : TokenMethod(func), params_(params.Pass()), weak_factory_(this) { |
+} |
+ |
+void ImportCertificate::Run() { |
+ GetCertDatabase( |
+ params_->token_id, |
+ base::Bind(&ImportCertificate::DidGetDB, weak_factory_.GetWeakPtr()), |
+ func_); |
+} |
+ |
+void ImportCertificate::DidGetDB(crypto::ScopedPK11Slot slot, |
+ net::NSSCertDatabase* cert_db) { |
+ const std::string& certificate = params_->certificate; |
+ scoped_refptr<net::X509Certificate> cert_x509 = |
+ net::X509Certificate::CreateFromBytes(certificate.data(), |
+ certificate.size()); |
+ if (!cert_x509) { |
+ func_->RespondWithError(kErrorInvalidX509Cert); |
+ return; |
+ } |
+ |
+ net::CertDatabase* db = net::CertDatabase::GetInstance(); |
+ |
+ const net::Error cert_status = db->CheckUserCert(cert_x509); |
+ if (cert_status == net::ERR_NO_PRIVATE_KEY_FOR_CERT) { |
+ func_->RespondWithError(kErrorKeyNotFound); |
+ return; |
+ } else if (cert_status != net::OK) { |
+ func_->RespondWithError(net::ErrorToString(cert_status)); |
+ return; |
+ } |
+ |
+ const net::Error import_status = db->AddUserCert(cert_x509.get()); |
+ if (import_status != net::OK) { |
+ LOG(ERROR) << "Could not import certificate."; |
+ func_->RespondWithError(net::ErrorToString(import_status)); |
+ return; |
+ } |
+ |
+ func_->RespondWithoutResult(); |
+} |
+ |
+RemoveCertificate::RemoveCertificate( |
+ scoped_ptr<api_epk::RemoveCertificate::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) |
+ : TokenMethod(func), params_(params.Pass()), weak_factory_(this) { |
+} |
+ |
+void RemoveCertificate::Run() { |
+ GetCertDatabase( |
+ params_->token_id, |
+ base::Bind(&RemoveCertificate::DidGetDB, weak_factory_.GetWeakPtr()), |
+ func_); |
+} |
+ |
+void RemoveCertificate::DidGetDB(crypto::ScopedPK11Slot slot, |
+ net::NSSCertDatabase* cert_db) { |
+ const std::string& certificate = params_->certificate; |
+ scoped_refptr<net::X509Certificate> cert_x509 = |
+ net::X509Certificate::CreateFromBytes(certificate.data(), |
+ certificate.size()); |
+ if (!cert_x509) { |
+ func_->RespondWithError(kErrorInvalidX509Cert); |
+ return; |
+ } |
+ |
+ bool certificate_found = cert_x509->os_cert_handle()->isperm; |
+ bool success = cert_db->DeleteCertAndKey(cert_x509); |
+ |
+ // CertificateNotFound error has precedence over an internal error. |
+ if (!certificate_found) { |
+ func_->RespondWithError(kErrorCertificateNotFound); |
+ return; |
+ } |
+ if (!success) { |
+ func_->RespondWithError(kErrorInternal); |
+ return; |
+ } |
+ |
+ func_->RespondWithoutResult(); |
+} |
+ |
+} // namespace |
+ |
+scoped_ptr<TokenMethod> CreateGenerateKey( |
+ scoped_ptr<api_epki::GenerateKey::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) { |
+ return scoped_ptr<TokenMethod>(new GenerateKey(params.Pass(), func)); |
+} |
+ |
+scoped_ptr<TokenMethod> CreateSign( |
+ scoped_ptr<api_epki::Sign::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) { |
+ return scoped_ptr<TokenMethod>(new Sign(params.Pass(), func)); |
+} |
+ |
+scoped_ptr<TokenMethod> CreateGetCertificates( |
+ scoped_ptr<api_epk::GetCertificates::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) { |
+ return scoped_ptr<TokenMethod>(new GetCertificates(params.Pass(), func)); |
+} |
+ |
+scoped_ptr<TokenMethod> CreateImportCertificate( |
+ scoped_ptr<api_epk::ImportCertificate::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) { |
+ return scoped_ptr<TokenMethod>(new ImportCertificate(params.Pass(), func)); |
+} |
+ |
+scoped_ptr<TokenMethod> CreateRemoveCertificate( |
+ scoped_ptr<api_epk::RemoveCertificate::Params> params, |
+ EnterprisePlatformKeysTokenMethodExtensionFunction* func) { |
+ return scoped_ptr<TokenMethod>(new RemoveCertificate(params.Pass(), func)); |
+} |
+ |
+} // namespace enterprise_platform_keys_details |
+ |
+} // namespace extensions |