diff options
9 files changed, 106 insertions, 24 deletions
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys.h b/chrome/browser/chromeos/platform_keys/platform_keys.h index a1a4827..2a61275 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys.h +++ b/chrome/browser/chromeos/platform_keys/platform_keys.h @@ -26,6 +26,14 @@ namespace chromeos { namespace platform_keys { +// Supported hash algorithms. +enum HashAlgorithm { + HASH_ALGORITHM_SHA1, + HASH_ALGORITHM_SHA256, + HASH_ALGORITHM_SHA384, + HASH_ALGORITHM_SHA512 +}; + namespace subtle { // Functions of this namespace shouldn't be called directly from the context of // an extension. Instead use PlatformKeysService which enforces restrictions @@ -46,14 +54,16 @@ void GenerateRSAKey(const std::string& token_id, typedef base::Callback<void(const std::string& signature, const std::string& error_message)> SignCallback; -// Signs |data| with the private key matching |public_key|, if that key is -// stored in the given token. |token_id| is currently ignored, instead the user -// token associated with |browser_context| is always used. |public_key| must be -// the DER encoding of a SubjectPublicKeyInfo. |callback| will be invoked with -// the signature or an error message. +// Digests |data| with |hash_algorithm| and afterwards signs the digest with the +// private key matching |public_key|, if that key is stored in the given token. +// |token_id| is currently ignored, instead the user token associated with +// |browser_context| is always used. |public_key| must be the DER encoding of a +// SubjectPublicKeyInfo. |callback| will be invoked with the signature or an +// error message. // Currently supports RSA keys only. void Sign(const std::string& token_id, const std::string& public_key, + HashAlgorithm hash_algorithm, const std::string& data, const SignCallback& callback, content::BrowserContext* browser_context); diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc index 87dfde6..f3a0208 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc +++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc @@ -152,6 +152,7 @@ class GenerateRSAKeyState : public NSSOperationState { class SignState : public NSSOperationState { public: SignState(const std::string& public_key, + HashAlgorithm hash_algorithm, const std::string& data, const subtle::SignCallback& callback); virtual ~SignState() {} @@ -169,6 +170,7 @@ class SignState : public NSSOperationState { } const std::string public_key_; + HashAlgorithm hash_algorithm_; const std::string data_; private: @@ -259,9 +261,13 @@ GenerateRSAKeyState::GenerateRSAKeyState( } SignState::SignState(const std::string& public_key, + HashAlgorithm hash_algorithm, const std::string& data, const subtle::SignCallback& callback) - : public_key_(public_key), data_(data), callback_(callback) { + : public_key_(public_key), + hash_algorithm_(hash_algorithm), + data_(data), + callback_(callback) { } GetCertificatesState::GetCertificatesState( @@ -333,12 +339,28 @@ void RSASignOnWorkerThread(scoped_ptr<SignState> state) { 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; + } + 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) { + sign_alg_tag) != SECSuccess) { LOG(ERROR) << "Couldn't sign."; state->OnError(FROM_HERE, kErrorInternal); return; @@ -495,11 +517,13 @@ void GenerateRSAKey(const std::string& token_id, 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) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - scoped_ptr<SignState> state(new SignState(public_key, data, callback)); + scoped_ptr<SignState> state( + new SignState(public_key, hash_algorithm, data, callback)); // Get the pointer to |state| before base::Passed releases |state|. NSSOperationState* state_ptr = state.get(); diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc index 124cb7a..9b1d0b5 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc +++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc @@ -49,6 +49,7 @@ void WrapGenerateKeyCallback( // |callback| with an error. void CheckValidityAndSign(const std::string& token_id, const std::string& public_key_spki_der, + platform_keys::HashAlgorithm hash_algorithm, const std::string& data, const PlatformKeysService::SignCallback& callback, content::BrowserContext* browser_context, @@ -58,8 +59,12 @@ void CheckValidityAndSign(const std::string& token_id, kErrorKeyNotAllowedForSigning); return; } - platform_keys::subtle::Sign( - token_id, public_key_spki_der, data, callback, browser_context); + platform_keys::subtle::Sign(token_id, + public_key_spki_der, + hash_algorithm, + data, + callback, + browser_context); } } // namespace @@ -94,6 +99,7 @@ void PlatformKeysService::GenerateRSAKey(const std::string& token_id, void PlatformKeysService::Sign(const std::string& token_id, const std::string& public_key_spki_der, + platform_keys::HashAlgorithm hash_algorithm, const std::string& data, const std::string& extension_id, const SignCallback& callback) { @@ -103,6 +109,7 @@ void PlatformKeysService::Sign(const std::string& token_id, base::Bind(&CheckValidityAndSign, token_id, public_key_spki_der, + hash_algorithm, data, callback, browser_context_)); diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.h b/chrome/browser/chromeos/platform_keys/platform_keys_service.h index e5b1259..d6401a4 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys_service.h +++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "chrome/browser/chromeos/platform_keys/platform_keys.h" #include "components/keyed_service/core/keyed_service.h" namespace content { @@ -66,8 +67,9 @@ class PlatformKeysService : public KeyedService { typedef base::Callback<void(const std::string& signature, const std::string& error_message)> SignCallback; - // Signs |data| with the private key matching |public_key_spki_der|, if that - // key is stored in the given token and wasn't used for signing before. + // Digests |data| with |hash_algorithm| and afterwards signs the digest with + // the private key matching |public_key_spki_der|, if that key is stored in + // the given token and wasn't used for signing before. // Unregisters the key so that every future attempt to sign data with this key // is rejected. |token_id| is currently ignored, instead the user token // associated with |browser_context| is always used. |public_key_spki_der| @@ -77,6 +79,7 @@ class PlatformKeysService : public KeyedService { // Will only call back during the lifetime of this object. void Sign(const std::string& token_id, const std::string& public_key_spki_der, + platform_keys::HashAlgorithm hash_algorithm, const std::string& data, const std::string& extension_id, const SignCallback& callback); diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc index 7ee7fb0..16a907f 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc @@ -25,6 +25,7 @@ namespace api_epki = api::enterprise_platform_keys_internal; // extension. Keep this in sync with the custom binding in Javascript. const char kErrorInvalidToken[] = "The token is not valid."; +const char kErrorAlgorithmNotSupported[] = "Algorithm not supported."; const char kErrorInvalidX509Cert[] = "Certificate is not a valid X.509 certificate."; const char kTokenIdUser[] = "user"; @@ -89,6 +90,18 @@ EnterprisePlatformKeysInternalSignFunction::Run() { if (!ValidateToken(params->token_id)) return RespondNow(Error(kErrorInvalidToken)); + chromeos::platform_keys::HashAlgorithm hash_algorithm; + if (params->hash_algorithm_name == "SHA-1") + hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA1; + else if (params->hash_algorithm_name == "SHA-256") + hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA256; + else if (params->hash_algorithm_name == "SHA-384") + hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA384; + else if (params->hash_algorithm_name == "SHA-512") + hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA512; + else + return RespondNow(Error(kErrorAlgorithmNotSupported)); + chromeos::PlatformKeysService* service = chromeos::PlatformKeysServiceFactory::GetForBrowserContext( browser_context()); @@ -97,6 +110,7 @@ EnterprisePlatformKeysInternalSignFunction::Run() { service->Sign( params->token_id, params->public_key, + hash_algorithm, params->data, extension_id(), base::Bind(&EnterprisePlatformKeysInternalSignFunction::OnSigned, this)); diff --git a/chrome/common/extensions/api/enterprise_platform_keys_internal.idl b/chrome/common/extensions/api/enterprise_platform_keys_internal.idl index b6d8f06..347f1a9 100644 --- a/chrome/common/extensions/api/enterprise_platform_keys_internal.idl +++ b/chrome/common/extensions/api/enterprise_platform_keys_internal.idl @@ -38,12 +38,18 @@ namespace enterprise.platformKeysInternal { // |tokenId| The id of a Token returned by |getTokens|. // |publicKey| The Subject Public Key Info of a key previously generated by // |generateKey| in DER encoding. + // |hashAlgorithmName| The recognized algorithm name as specified by + // WebCrypto of the hash algorithm that will be used to digest |data| + // before signing. Currently supported are: SHA-{1,256,384,512}. + // TODO(pneubeck): use an enum once supported: + // http://www.crbug.com/385539 . // |data| The data to sign. // |callback| Called back with the signature of |data|. // TODO: Instead of ArrayBuffer should be (ArrayBuffer or ArrayBufferView), // or at least (ArrayBuffer or Uint8Array). static void sign(DOMString tokenId, ArrayBuffer publicKey, + DOMString hashAlgorithmName, ArrayBuffer data, SignCallback callback); }; diff --git a/chrome/renderer/extensions/enterprise_platform_keys_natives.cc b/chrome/renderer/extensions/enterprise_platform_keys_natives.cc index 2e11b25..9bccc81 100644 --- a/chrome/renderer/extensions/enterprise_platform_keys_natives.cc +++ b/chrome/renderer/extensions/enterprise_platform_keys_natives.cc @@ -56,6 +56,15 @@ scoped_ptr<base::DictionaryValue> WebCryptoAlgorithmToBaseValue( base::BinaryValue::CreateWithCopiedBuffer( reinterpret_cast<const char*>(public_exponent.data()), public_exponent.size())); + + const blink::WebCryptoAlgorithm& hash = rsaHashedKeyGen->hash(); + DCHECK(!hash.isNull()); + const blink::WebCryptoAlgorithmInfo* hash_info = + blink::WebCryptoAlgorithm::lookupAlgorithmInfo(hash.id()); + + scoped_ptr<base::DictionaryValue> hash_dict(new base::DictionaryValue); + hash_dict->SetStringWithoutPathExpansion("name", hash_info->name); + dict->SetWithoutPathExpansion("hash", hash_dict.release()); } // Otherwise, |algorithm| is missing support here or no parameters were // required. diff --git a/chrome/renderer/extensions/enterprise_platform_keys_natives.h b/chrome/renderer/extensions/enterprise_platform_keys_natives.h index 6afd99fc..9649aee 100644 --- a/chrome/renderer/extensions/enterprise_platform_keys_natives.h +++ b/chrome/renderer/extensions/enterprise_platform_keys_natives.h @@ -23,7 +23,8 @@ class EnterprisePlatformKeysNatives : public ObjectBackedNativeHandler { // |operation|: A string describing the operation. Supported operations are // "GenerateKey", "Sign" and "Verify". // Returns the normalized dictionary on success, or null if some required - // parameters are missing or not supported. + // parameters are missing or not supported. Note that it returns untyped + // arrays instead of typed arrays (e.g. for RSA publicExponent). void NormalizeAlgorithm(const v8::FunctionCallbackInfo<v8::Value>& call_info); DISALLOW_COPY_AND_ASSIGN(EnterprisePlatformKeysNatives); diff --git a/chrome/renderer/resources/extensions/enterprise_platform_keys/subtle_crypto.js b/chrome/renderer/resources/extensions/enterprise_platform_keys/subtle_crypto.js index b3aece1..017a3e2 100644 --- a/chrome/renderer/resources/extensions/enterprise_platform_keys/subtle_crypto.js +++ b/chrome/renderer/resources/extensions/enterprise_platform_keys/subtle_crypto.js @@ -102,6 +102,11 @@ SubtleCryptoImpl.prototype.generateKey = throw CreateSyntaxError(); } + // normalizeAlgorithm returns an array, but publicExponent should be a + // Uint8Array. + normalizedAlgorithmParameters.publicExponent = + new Uint8Array(normalizedAlgorithmParameters.publicExponent); + if (normalizedAlgorithmParameters.name !== 'RSASSA-PKCS1-v1_5' || !equalsStandardPublicExponent( normalizedAlgorithmParameters.publicExponent)) { @@ -118,7 +123,7 @@ SubtleCryptoImpl.prototype.generateKey = reject(CreateOperationError()); return; } - resolve(new KeyPair(spki, algorithm, keyUsages)); + resolve(new KeyPair(spki, normalizedAlgorithmParameters, keyUsages)); }); }); }; @@ -141,16 +146,19 @@ SubtleCryptoImpl.prototype.sign = function(algorithm, key, dataView) { // might contain more data than dataView. var data = dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength); - internalAPI.sign( - subtleCrypto.tokenId, getSpki(key), data, function(signature) { - if (catchInvalidTokenError(reject)) - return; - if (chrome.runtime.lastError) { - reject(CreateOperationError()); - return; - } - resolve(signature); - }); + internalAPI.sign(subtleCrypto.tokenId, + getSpki(key), + key.algorithm.hash.name, + data, + function(signature) { + if (catchInvalidTokenError(reject)) + return; + if (chrome.runtime.lastError) { + reject(CreateOperationError()); + return; + } + resolve(signature); + }); }); }; |