| 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
|
| index e9e32558b2818ee555c57f4988ac945b1ae05177..978f1159a37f207e3f423299d7f192ba427e5813 100644
|
| --- a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
|
| +++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
|
| @@ -4,7 +4,10 @@
|
|
|
| #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
|
|
|
| +#include <cert.h>
|
| #include <cryptohi.h>
|
| +#include <keyhi.h>
|
| +#include <secder.h>
|
|
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| @@ -14,6 +17,7 @@
|
| #include "base/logging.h"
|
| #include "base/macros.h"
|
| #include "base/single_thread_task_runner.h"
|
| +#include "base/stl_util.h"
|
| #include "base/thread_task_runner_handle.h"
|
| #include "base/threading/worker_pool.h"
|
| #include "chrome/browser/browser_process.h"
|
| @@ -33,6 +37,7 @@
|
| #include "net/cert/cert_database.h"
|
| #include "net/cert/nss_cert_database.h"
|
| #include "net/cert/x509_certificate.h"
|
| +#include "net/cert/x509_util_nss.h"
|
| #include "net/ssl/client_cert_store_chromeos.h"
|
| #include "net/ssl/ssl_cert_request_info.h"
|
|
|
| @@ -168,13 +173,14 @@ class GenerateRSAKeyState : public NSSOperationState {
|
| subtle::GenerateKeyCallback callback_;
|
| };
|
|
|
| -class SignState : public NSSOperationState {
|
| +class SignRSAState : public NSSOperationState {
|
| public:
|
| - SignState(const std::string& public_key,
|
| - HashAlgorithm hash_algorithm,
|
| - const std::string& data,
|
| - const subtle::SignCallback& callback);
|
| - ~SignState() override {}
|
| + SignRSAState(const std::string& data,
|
| + const std::string& public_key,
|
| + bool sign_direct_pkcs_padded,
|
| + HashAlgorithm hash_algorithm,
|
| + const subtle::SignCallback& callback);
|
| + ~SignRSAState() override {}
|
|
|
| void OnError(const tracked_objects::Location& from,
|
| const std::string& error_message) override {
|
| @@ -188,10 +194,21 @@ class SignState : public NSSOperationState {
|
| from, base::Bind(callback_, signature, error_message));
|
| }
|
|
|
| - const std::string public_key_;
|
| - HashAlgorithm hash_algorithm_;
|
| + // The data that will be signed.
|
| const std::string data_;
|
|
|
| + // Must be the DER encoding of a SubjectPublicKeyInfo.
|
| + const std::string public_key_;
|
| +
|
| + // If true, |data_| will not be hashed before signing. Only PKCS#1 v1.5
|
| + // padding will be applied before signing.
|
| + // If false, |hash_algorithm_| must be set to a value != NONE.
|
| + const bool sign_direct_pkcs_padded_;
|
| +
|
| + // Determines the hash algorithm that is used to digest |data| before signing.
|
| + // Ignored if |sign_direct_pkcs_padded_| is true.
|
| + const HashAlgorithm hash_algorithm_;
|
| +
|
| private:
|
| // Must be called on origin thread, therefore use CallBack().
|
| subtle::SignCallback callback_;
|
| @@ -202,7 +219,7 @@ class SelectCertificatesState : public NSSOperationState {
|
| explicit SelectCertificatesState(
|
| const std::string& username_hash,
|
| const bool use_system_key_slot,
|
| - scoped_refptr<net::SSLCertRequestInfo> request,
|
| + const scoped_refptr<net::SSLCertRequestInfo>& request,
|
| const subtle::SelectCertificatesCallback& callback);
|
| ~SelectCertificatesState() override {}
|
|
|
| @@ -258,7 +275,7 @@ class GetCertificatesState : public NSSOperationState {
|
|
|
| class ImportCertificateState : public NSSOperationState {
|
| public:
|
| - ImportCertificateState(scoped_refptr<net::X509Certificate> certificate,
|
| + ImportCertificateState(const scoped_refptr<net::X509Certificate>& certificate,
|
| const ImportCertificateCallback& callback);
|
| ~ImportCertificateState() override {}
|
|
|
| @@ -281,7 +298,7 @@ class ImportCertificateState : public NSSOperationState {
|
|
|
| class RemoveCertificateState : public NSSOperationState {
|
| public:
|
| - RemoveCertificateState(scoped_refptr<net::X509Certificate> certificate,
|
| + RemoveCertificateState(const scoped_refptr<net::X509Certificate>& certificate,
|
| const RemoveCertificateCallback& callback);
|
| ~RemoveCertificateState() override {}
|
|
|
| @@ -336,20 +353,22 @@ GenerateRSAKeyState::GenerateRSAKeyState(
|
| : modulus_length_bits_(modulus_length_bits), callback_(callback) {
|
| }
|
|
|
| -SignState::SignState(const std::string& public_key,
|
| - HashAlgorithm hash_algorithm,
|
| - const std::string& data,
|
| - const subtle::SignCallback& callback)
|
| - : public_key_(public_key),
|
| +SignRSAState::SignRSAState(const std::string& data,
|
| + const std::string& public_key,
|
| + bool sign_direct_pkcs_padded,
|
| + HashAlgorithm hash_algorithm,
|
| + const subtle::SignCallback& callback)
|
| + : data_(data),
|
| + public_key_(public_key),
|
| + sign_direct_pkcs_padded_(sign_direct_pkcs_padded),
|
| hash_algorithm_(hash_algorithm),
|
| - data_(data),
|
| callback_(callback) {
|
| }
|
|
|
| SelectCertificatesState::SelectCertificatesState(
|
| const std::string& username_hash,
|
| const bool use_system_key_slot,
|
| - scoped_refptr<net::SSLCertRequestInfo> cert_request_info,
|
| + const scoped_refptr<net::SSLCertRequestInfo>& cert_request_info,
|
| const subtle::SelectCertificatesCallback& callback)
|
| : username_hash_(username_hash),
|
| use_system_key_slot_(use_system_key_slot),
|
| @@ -363,13 +382,13 @@ GetCertificatesState::GetCertificatesState(
|
| }
|
|
|
| ImportCertificateState::ImportCertificateState(
|
| - scoped_refptr<net::X509Certificate> certificate,
|
| + const scoped_refptr<net::X509Certificate>& certificate,
|
| const ImportCertificateCallback& callback)
|
| : certificate_(certificate), callback_(callback) {
|
| }
|
|
|
| RemoveCertificateState::RemoveCertificateState(
|
| - scoped_refptr<net::X509Certificate> certificate,
|
| + const scoped_refptr<net::X509Certificate>& certificate,
|
| const RemoveCertificateCallback& callback)
|
| : certificate_(certificate), callback_(callback) {
|
| }
|
| @@ -415,8 +434,8 @@ void GenerateRSAKeyWithDB(scoped_ptr<GenerateRSAKeyState> state,
|
| true /*task is slow*/);
|
| }
|
|
|
| -// Does the actual signing on a worker thread. Used by RSASignWithDB().
|
| -void RSASignOnWorkerThread(scoped_ptr<SignState> state) {
|
| +// Does the actual signing on a worker thread. Used by SignRSAWithDB().
|
| +void SignRSAOnWorkerThread(scoped_ptr<SignRSAState> state) {
|
| const uint8* public_key_uint8 =
|
| reinterpret_cast<const uint8*>(state->public_key_.data());
|
| std::vector<uint8> public_key_vector(
|
| @@ -425,50 +444,83 @@ void RSASignOnWorkerThread(scoped_ptr<SignState> state) {
|
| // 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_) {
|
| +
|
| + // Fail if the key was not found. If a specific slot was requested, also fail
|
| + // if the key was found in the wrong slot.
|
| + if (!rsa_key ||
|
| + (state->slot_ && rsa_key->key()->pkcs11Slot != state->slot_)) {
|
| state->OnError(FROM_HERE, kErrorKeyNotFound);
|
| return;
|
| }
|
|
|
| - SECOidTag sign_alg_tag = SEC_OID_UNKNOWN;
|
| - switch (state->hash_algorithm_) {
|
| - case HASH_ALGORITHM_SHA1:
|
| - sign_alg_tag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
|
| - break;
|
| - case HASH_ALGORITHM_SHA256:
|
| - sign_alg_tag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
|
| - break;
|
| - case HASH_ALGORITHM_SHA384:
|
| - sign_alg_tag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
|
| - break;
|
| - case HASH_ALGORITHM_SHA512:
|
| - sign_alg_tag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
|
| - break;
|
| - }
|
| -
|
| - crypto::ScopedSECItem sign_result(SECITEM_AllocItem(NULL, NULL, 0));
|
| - if (SEC_SignData(sign_result.get(),
|
| - reinterpret_cast<const unsigned char*>(state->data_.data()),
|
| - state->data_.size(),
|
| - rsa_key->key(),
|
| - sign_alg_tag) != SECSuccess) {
|
| + std::string signature_str;
|
| + if (state->sign_direct_pkcs_padded_) {
|
| + static_assert(
|
| + sizeof(*state->data_.data()) == sizeof(char),
|
| + "Can't reinterpret data if it's characters are not 8 bit large.");
|
| + SECItem input = {siBuffer,
|
| + reinterpret_cast<unsigned char*>(
|
| + const_cast<char*>(state->data_.data())),
|
| + state->data_.size()};
|
| +
|
| + // Compute signature of hash.
|
| + int signature_len = PK11_SignatureLen(rsa_key->key());
|
| + if (signature_len <= 0) {
|
| + state->OnError(FROM_HERE, kErrorInternal);
|
| + return;
|
| + }
|
| +
|
| + std::vector<unsigned char> signature(signature_len);
|
| + SECItem signature_output = {
|
| + siBuffer, vector_as_array(&signature), signature.size()};
|
| + if (PK11_Sign(rsa_key->key(), &signature_output, &input) == SECSuccess)
|
| + signature_str.assign(signature.begin(), signature.end());
|
| + } else {
|
| + SECOidTag sign_alg_tag = SEC_OID_UNKNOWN;
|
| + switch (state->hash_algorithm_) {
|
| + case HASH_ALGORITHM_SHA1:
|
| + sign_alg_tag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
|
| + break;
|
| + case HASH_ALGORITHM_SHA256:
|
| + sign_alg_tag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
|
| + break;
|
| + case HASH_ALGORITHM_SHA384:
|
| + sign_alg_tag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
|
| + break;
|
| + case HASH_ALGORITHM_SHA512:
|
| + sign_alg_tag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
|
| + break;
|
| + case HASH_ALGORITHM_NONE:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| +
|
| + SECItem sign_result = {siBuffer, nullptr, 0};
|
| + if (SEC_SignData(
|
| + &sign_result,
|
| + reinterpret_cast<const unsigned char*>(state->data_.data()),
|
| + state->data_.size(), rsa_key->key(), sign_alg_tag) == SECSuccess) {
|
| + signature_str.assign(sign_result.data,
|
| + sign_result.data + sign_result.len);
|
| + }
|
| + }
|
| +
|
| + if (signature_str.empty()) {
|
| 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 */);
|
| + state->CallBack(FROM_HERE, signature_str, std::string() /* no error */);
|
| }
|
|
|
| // Continues signing with the obtained NSSCertDatabase. Used by Sign().
|
| -void RSASignWithDB(scoped_ptr<SignState> state, net::NSSCertDatabase* cert_db) {
|
| +void SignRSAWithDB(scoped_ptr<SignRSAState> 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)),
|
| + FROM_HERE, base::Bind(&SignRSAOnWorkerThread, base::Passed(&state)),
|
| true /*task is slow*/);
|
| }
|
|
|
| @@ -655,25 +707,43 @@ void GenerateRSAKey(const std::string& token_id,
|
| state_ptr);
|
| }
|
|
|
| -void Sign(const std::string& token_id,
|
| - const std::string& public_key,
|
| - HashAlgorithm hash_algorithm,
|
| - const std::string& data,
|
| - const SignCallback& callback,
|
| - BrowserContext* browser_context) {
|
| +void SignRSAPKCS1Digest(const std::string& token_id,
|
| + const std::string& data,
|
| + const std::string& public_key,
|
| + HashAlgorithm hash_algorithm,
|
| + const SignCallback& callback,
|
| + content::BrowserContext* browser_context) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - scoped_ptr<SignState> state(
|
| - new SignState(public_key, hash_algorithm, data, callback));
|
| + scoped_ptr<SignRSAState> state(
|
| + new SignRSAState(data, public_key, false /* digest before signing */,
|
| + hash_algorithm, 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)),
|
| - browser_context,
|
| - state_ptr);
|
| + GetCertDatabase(token_id, base::Bind(&SignRSAWithDB, base::Passed(&state)),
|
| + browser_context, state_ptr);
|
| +}
|
| +
|
| +void SignRSAPKCS1Raw(const std::string& token_id,
|
| + const std::string& data,
|
| + const std::string& public_key,
|
| + const SignCallback& callback,
|
| + content::BrowserContext* browser_context) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + scoped_ptr<SignRSAState> state(new SignRSAState(
|
| + data, public_key, true /* sign directly without hashing */,
|
| + HASH_ALGORITHM_NONE, 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(&SignRSAWithDB, base::Passed(&state)),
|
| + browser_context, state_ptr);
|
| }
|
|
|
| void SelectClientCertificates(const ClientCertificateRequest& request,
|
| @@ -706,6 +776,46 @@ void SelectClientCertificates(const ClientCertificateRequest& request,
|
|
|
| } // namespace subtle
|
|
|
| +bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
|
| + std::string* public_key_spki_der,
|
| + net::X509Certificate::PublicKeyType* key_type,
|
| + size_t* key_size_bits) {
|
| + const SECItem& spki_der = certificate->os_cert_handle()->derPublicKey;
|
| +
|
| + net::X509Certificate::PublicKeyType key_type_tmp =
|
| + net::X509Certificate::kPublicKeyTypeUnknown;
|
| + size_t key_size_bits_tmp = 0;
|
| + net::X509Certificate::GetPublicKeyInfo(certificate->os_cert_handle(),
|
| + &key_size_bits_tmp, &key_type_tmp);
|
| +
|
| + if (key_type_tmp == net::X509Certificate::kPublicKeyTypeUnknown) {
|
| + LOG(WARNING) << "Could not extract public key of certificate.";
|
| + return false;
|
| + }
|
| + if (key_type_tmp != net::X509Certificate::kPublicKeyTypeRSA) {
|
| + LOG(WARNING) << "Keys of other type than RSA are not supported.";
|
| + return false;
|
| + }
|
| +
|
| + crypto::ScopedSECKEYPublicKey public_key(
|
| + CERT_ExtractPublicKey(certificate->os_cert_handle()));
|
| + if (!public_key) {
|
| + LOG(WARNING) << "Could not extract public key of certificate.";
|
| + return false;
|
| + }
|
| + long public_exponent = DER_GetInteger(&public_key->u.rsa.publicExponent);
|
| + if (public_exponent != 65537L) {
|
| + LOG(ERROR) << "Rejecting RSA public exponent that is unequal 65537.";
|
| + return false;
|
| + }
|
| +
|
| + public_key_spki_der->assign(spki_der.data, spki_der.data + spki_der.len);
|
| + *key_type = key_type_tmp;
|
| + *key_size_bits = key_size_bits_tmp;
|
| +
|
| + return true;
|
| +}
|
| +
|
| void GetCertificates(const std::string& token_id,
|
| const GetCertificatesCallback& callback,
|
| BrowserContext* browser_context) {
|
| @@ -720,7 +830,7 @@ void GetCertificates(const std::string& token_id,
|
| }
|
|
|
| void ImportCertificate(const std::string& token_id,
|
| - scoped_refptr<net::X509Certificate> certificate,
|
| + const scoped_refptr<net::X509Certificate>& certificate,
|
| const ImportCertificateCallback& callback,
|
| BrowserContext* browser_context) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| @@ -739,7 +849,7 @@ void ImportCertificate(const std::string& token_id,
|
| }
|
|
|
| void RemoveCertificate(const std::string& token_id,
|
| - scoped_refptr<net::X509Certificate> certificate,
|
| + const scoped_refptr<net::X509Certificate>& certificate,
|
| const RemoveCertificateCallback& callback,
|
| BrowserContext* browser_context) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|