Commit 53f36907 authored by pneubeck@chromium.org's avatar pneubeck@chromium.org

enterprise.platformKeys: Respect the 'hash' argument of generateKey.

Before, the hash argument was simply ignored and defaulted to SHA-1. Instead it accepts now all values specified by WebCrypto, i.e. SHA-{1,256,384,512}.

BUG=385085

Review URL: https://codereview.chromium.org/331173002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278293 0039d316-1c4b-4281-b951-d872f2087c98
parent b35490fa
...@@ -26,6 +26,14 @@ namespace chromeos { ...@@ -26,6 +26,14 @@ namespace chromeos {
namespace platform_keys { namespace platform_keys {
// Supported hash algorithms.
enum HashAlgorithm {
HASH_ALGORITHM_SHA1,
HASH_ALGORITHM_SHA256,
HASH_ALGORITHM_SHA384,
HASH_ALGORITHM_SHA512
};
namespace subtle { namespace subtle {
// Functions of this namespace shouldn't be called directly from the context of // Functions of this namespace shouldn't be called directly from the context of
// an extension. Instead use PlatformKeysService which enforces restrictions // an extension. Instead use PlatformKeysService which enforces restrictions
...@@ -46,14 +54,16 @@ void GenerateRSAKey(const std::string& token_id, ...@@ -46,14 +54,16 @@ void GenerateRSAKey(const std::string& token_id,
typedef base::Callback<void(const std::string& signature, typedef base::Callback<void(const std::string& signature,
const std::string& error_message)> SignCallback; const std::string& error_message)> SignCallback;
// Signs |data| with the private key matching |public_key|, if that key is // Digests |data| with |hash_algorithm| and afterwards signs the digest with the
// stored in the given token. |token_id| is currently ignored, instead the user // private key matching |public_key|, if that key is stored in the given token.
// token associated with |browser_context| is always used. |public_key| must be // |token_id| is currently ignored, instead the user token associated with
// the DER encoding of a SubjectPublicKeyInfo. |callback| will be invoked with // |browser_context| is always used. |public_key| must be the DER encoding of a
// the signature or an error message. // SubjectPublicKeyInfo. |callback| will be invoked with the signature or an
// error message.
// Currently supports RSA keys only. // Currently supports RSA keys only.
void Sign(const std::string& token_id, void Sign(const std::string& token_id,
const std::string& public_key, const std::string& public_key,
HashAlgorithm hash_algorithm,
const std::string& data, const std::string& data,
const SignCallback& callback, const SignCallback& callback,
content::BrowserContext* browser_context); content::BrowserContext* browser_context);
......
...@@ -152,6 +152,7 @@ class GenerateRSAKeyState : public NSSOperationState { ...@@ -152,6 +152,7 @@ class GenerateRSAKeyState : public NSSOperationState {
class SignState : public NSSOperationState { class SignState : public NSSOperationState {
public: public:
SignState(const std::string& public_key, SignState(const std::string& public_key,
HashAlgorithm hash_algorithm,
const std::string& data, const std::string& data,
const subtle::SignCallback& callback); const subtle::SignCallback& callback);
virtual ~SignState() {} virtual ~SignState() {}
...@@ -169,6 +170,7 @@ class SignState : public NSSOperationState { ...@@ -169,6 +170,7 @@ class SignState : public NSSOperationState {
} }
const std::string public_key_; const std::string public_key_;
HashAlgorithm hash_algorithm_;
const std::string data_; const std::string data_;
private: private:
...@@ -259,9 +261,13 @@ GenerateRSAKeyState::GenerateRSAKeyState( ...@@ -259,9 +261,13 @@ GenerateRSAKeyState::GenerateRSAKeyState(
} }
SignState::SignState(const std::string& public_key, SignState::SignState(const std::string& public_key,
HashAlgorithm hash_algorithm,
const std::string& data, const std::string& data,
const subtle::SignCallback& callback) 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( GetCertificatesState::GetCertificatesState(
...@@ -333,12 +339,28 @@ void RSASignOnWorkerThread(scoped_ptr<SignState> state) { ...@@ -333,12 +339,28 @@ void RSASignOnWorkerThread(scoped_ptr<SignState> state) {
return; 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}; SECItem sign_result = {siBuffer, NULL, 0};
if (SEC_SignData(&sign_result, if (SEC_SignData(&sign_result,
reinterpret_cast<const unsigned char*>(state->data_.data()), reinterpret_cast<const unsigned char*>(state->data_.data()),
state->data_.size(), state->data_.size(),
rsa_key->key(), rsa_key->key(),
SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION) != SECSuccess) { sign_alg_tag) != SECSuccess) {
LOG(ERROR) << "Couldn't sign."; LOG(ERROR) << "Couldn't sign.";
state->OnError(FROM_HERE, kErrorInternal); state->OnError(FROM_HERE, kErrorInternal);
return; return;
...@@ -495,11 +517,13 @@ void GenerateRSAKey(const std::string& token_id, ...@@ -495,11 +517,13 @@ void GenerateRSAKey(const std::string& token_id,
void Sign(const std::string& token_id, void Sign(const std::string& token_id,
const std::string& public_key, const std::string& public_key,
HashAlgorithm hash_algorithm,
const std::string& data, const std::string& data,
const SignCallback& callback, const SignCallback& callback,
BrowserContext* browser_context) { BrowserContext* browser_context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 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|. // Get the pointer to |state| before base::Passed releases |state|.
NSSOperationState* state_ptr = state.get(); NSSOperationState* state_ptr = state.get();
......
...@@ -49,6 +49,7 @@ void WrapGenerateKeyCallback( ...@@ -49,6 +49,7 @@ void WrapGenerateKeyCallback(
// |callback| with an error. // |callback| with an error.
void CheckValidityAndSign(const std::string& token_id, void CheckValidityAndSign(const std::string& token_id,
const std::string& public_key_spki_der, const std::string& public_key_spki_der,
platform_keys::HashAlgorithm hash_algorithm,
const std::string& data, const std::string& data,
const PlatformKeysService::SignCallback& callback, const PlatformKeysService::SignCallback& callback,
content::BrowserContext* browser_context, content::BrowserContext* browser_context,
...@@ -58,8 +59,12 @@ void CheckValidityAndSign(const std::string& token_id, ...@@ -58,8 +59,12 @@ void CheckValidityAndSign(const std::string& token_id,
kErrorKeyNotAllowedForSigning); kErrorKeyNotAllowedForSigning);
return; return;
} }
platform_keys::subtle::Sign( platform_keys::subtle::Sign(token_id,
token_id, public_key_spki_der, data, callback, browser_context); public_key_spki_der,
hash_algorithm,
data,
callback,
browser_context);
} }
} // namespace } // namespace
...@@ -94,6 +99,7 @@ void PlatformKeysService::GenerateRSAKey(const std::string& token_id, ...@@ -94,6 +99,7 @@ void PlatformKeysService::GenerateRSAKey(const std::string& token_id,
void PlatformKeysService::Sign(const std::string& token_id, void PlatformKeysService::Sign(const std::string& token_id,
const std::string& public_key_spki_der, const std::string& public_key_spki_der,
platform_keys::HashAlgorithm hash_algorithm,
const std::string& data, const std::string& data,
const std::string& extension_id, const std::string& extension_id,
const SignCallback& callback) { const SignCallback& callback) {
...@@ -103,6 +109,7 @@ void PlatformKeysService::Sign(const std::string& token_id, ...@@ -103,6 +109,7 @@ void PlatformKeysService::Sign(const std::string& token_id,
base::Bind(&CheckValidityAndSign, base::Bind(&CheckValidityAndSign,
token_id, token_id,
public_key_spki_der, public_key_spki_der,
hash_algorithm,
data, data,
callback, callback,
browser_context_)); browser_context_));
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_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" #include "components/keyed_service/core/keyed_service.h"
namespace content { namespace content {
...@@ -66,8 +67,9 @@ class PlatformKeysService : public KeyedService { ...@@ -66,8 +67,9 @@ class PlatformKeysService : public KeyedService {
typedef base::Callback<void(const std::string& signature, typedef base::Callback<void(const std::string& signature,
const std::string& error_message)> SignCallback; const std::string& error_message)> SignCallback;
// Signs |data| with the private key matching |public_key_spki_der|, if that // Digests |data| with |hash_algorithm| and afterwards signs the digest with
// key is stored in the given token and wasn't used for signing before. // 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 // 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 // is rejected. |token_id| is currently ignored, instead the user token
// associated with |browser_context| is always used. |public_key_spki_der| // associated with |browser_context| is always used. |public_key_spki_der|
...@@ -77,6 +79,7 @@ class PlatformKeysService : public KeyedService { ...@@ -77,6 +79,7 @@ class PlatformKeysService : public KeyedService {
// Will only call back during the lifetime of this object. // Will only call back during the lifetime of this object.
void Sign(const std::string& token_id, void Sign(const std::string& token_id,
const std::string& public_key_spki_der, const std::string& public_key_spki_der,
platform_keys::HashAlgorithm hash_algorithm,
const std::string& data, const std::string& data,
const std::string& extension_id, const std::string& extension_id,
const SignCallback& callback); const SignCallback& callback);
......
...@@ -25,6 +25,7 @@ namespace api_epki = api::enterprise_platform_keys_internal; ...@@ -25,6 +25,7 @@ namespace api_epki = api::enterprise_platform_keys_internal;
// extension. Keep this in sync with the custom binding in Javascript. // extension. Keep this in sync with the custom binding in Javascript.
const char kErrorInvalidToken[] = "The token is not valid."; const char kErrorInvalidToken[] = "The token is not valid.";
const char kErrorAlgorithmNotSupported[] = "Algorithm not supported.";
const char kErrorInvalidX509Cert[] = const char kErrorInvalidX509Cert[] =
"Certificate is not a valid X.509 certificate."; "Certificate is not a valid X.509 certificate.";
const char kTokenIdUser[] = "user"; const char kTokenIdUser[] = "user";
...@@ -89,6 +90,18 @@ EnterprisePlatformKeysInternalSignFunction::Run() { ...@@ -89,6 +90,18 @@ EnterprisePlatformKeysInternalSignFunction::Run() {
if (!ValidateToken(params->token_id)) if (!ValidateToken(params->token_id))
return RespondNow(Error(kErrorInvalidToken)); 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::PlatformKeysService* service =
chromeos::PlatformKeysServiceFactory::GetForBrowserContext( chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
browser_context()); browser_context());
...@@ -97,6 +110,7 @@ EnterprisePlatformKeysInternalSignFunction::Run() { ...@@ -97,6 +110,7 @@ EnterprisePlatformKeysInternalSignFunction::Run() {
service->Sign( service->Sign(
params->token_id, params->token_id,
params->public_key, params->public_key,
hash_algorithm,
params->data, params->data,
extension_id(), extension_id(),
base::Bind(&EnterprisePlatformKeysInternalSignFunction::OnSigned, this)); base::Bind(&EnterprisePlatformKeysInternalSignFunction::OnSigned, this));
......
...@@ -38,12 +38,18 @@ namespace enterprise.platformKeysInternal { ...@@ -38,12 +38,18 @@ namespace enterprise.platformKeysInternal {
// |tokenId| The id of a Token returned by |getTokens|. // |tokenId| The id of a Token returned by |getTokens|.
// |publicKey| The Subject Public Key Info of a key previously generated by // |publicKey| The Subject Public Key Info of a key previously generated by
// |generateKey| in DER encoding. // |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. // |data| The data to sign.
// |callback| Called back with the signature of |data|. // |callback| Called back with the signature of |data|.
// TODO: Instead of ArrayBuffer should be (ArrayBuffer or ArrayBufferView), // TODO: Instead of ArrayBuffer should be (ArrayBuffer or ArrayBufferView),
// or at least (ArrayBuffer or Uint8Array). // or at least (ArrayBuffer or Uint8Array).
static void sign(DOMString tokenId, static void sign(DOMString tokenId,
ArrayBuffer publicKey, ArrayBuffer publicKey,
DOMString hashAlgorithmName,
ArrayBuffer data, ArrayBuffer data,
SignCallback callback); SignCallback callback);
}; };
......
...@@ -56,6 +56,15 @@ scoped_ptr<base::DictionaryValue> WebCryptoAlgorithmToBaseValue( ...@@ -56,6 +56,15 @@ scoped_ptr<base::DictionaryValue> WebCryptoAlgorithmToBaseValue(
base::BinaryValue::CreateWithCopiedBuffer( base::BinaryValue::CreateWithCopiedBuffer(
reinterpret_cast<const char*>(public_exponent.data()), reinterpret_cast<const char*>(public_exponent.data()),
public_exponent.size())); 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 // Otherwise, |algorithm| is missing support here or no parameters were
// required. // required.
......
...@@ -23,7 +23,8 @@ class EnterprisePlatformKeysNatives : public ObjectBackedNativeHandler { ...@@ -23,7 +23,8 @@ class EnterprisePlatformKeysNatives : public ObjectBackedNativeHandler {
// |operation|: A string describing the operation. Supported operations are // |operation|: A string describing the operation. Supported operations are
// "GenerateKey", "Sign" and "Verify". // "GenerateKey", "Sign" and "Verify".
// Returns the normalized dictionary on success, or null if some required // 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); void NormalizeAlgorithm(const v8::FunctionCallbackInfo<v8::Value>& call_info);
DISALLOW_COPY_AND_ASSIGN(EnterprisePlatformKeysNatives); DISALLOW_COPY_AND_ASSIGN(EnterprisePlatformKeysNatives);
......
...@@ -102,6 +102,11 @@ SubtleCryptoImpl.prototype.generateKey = ...@@ -102,6 +102,11 @@ SubtleCryptoImpl.prototype.generateKey =
throw CreateSyntaxError(); 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' || if (normalizedAlgorithmParameters.name !== 'RSASSA-PKCS1-v1_5' ||
!equalsStandardPublicExponent( !equalsStandardPublicExponent(
normalizedAlgorithmParameters.publicExponent)) { normalizedAlgorithmParameters.publicExponent)) {
...@@ -118,7 +123,7 @@ SubtleCryptoImpl.prototype.generateKey = ...@@ -118,7 +123,7 @@ SubtleCryptoImpl.prototype.generateKey =
reject(CreateOperationError()); reject(CreateOperationError());
return; return;
} }
resolve(new KeyPair(spki, algorithm, keyUsages)); resolve(new KeyPair(spki, normalizedAlgorithmParameters, keyUsages));
}); });
}); });
}; };
...@@ -141,8 +146,11 @@ SubtleCryptoImpl.prototype.sign = function(algorithm, key, dataView) { ...@@ -141,8 +146,11 @@ SubtleCryptoImpl.prototype.sign = function(algorithm, key, dataView) {
// might contain more data than dataView. // might contain more data than dataView.
var data = dataView.buffer.slice(dataView.byteOffset, var data = dataView.buffer.slice(dataView.byteOffset,
dataView.byteOffset + dataView.byteLength); dataView.byteOffset + dataView.byteLength);
internalAPI.sign( internalAPI.sign(subtleCrypto.tokenId,
subtleCrypto.tokenId, getSpki(key), data, function(signature) { getSpki(key),
key.algorithm.hash.name,
data,
function(signature) {
if (catchInvalidTokenError(reject)) if (catchInvalidTokenError(reject))
return; return;
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment