Commit 50041efb authored by Maria Petrisor's avatar Maria Petrisor Committed by Commit Bot

Add getKeyPairBySpki to platformKeys

Add the new method chrome.platformKeys.getKeyPairBySpki() that,
similarly to chrome.platformKeys.getKeyPair() returns subtleCrypto
objects for a private and a public key.
The key pair already exists on the device, and getKeyPairBySpki()
receives a public_key_spki_der as input to identify which key pair
to return.
The already existing API function getKeyPair() receives a certificate,
extracting the public_key_spki_der from it and returning the key
pair based on that spki.
In our use case (the Imprivata extension) we don't have access to a
certificate in order for us to use getKeyPair(). A key pair is needed
in Imprivata for sending authorization requests (signing data in the
request with the private key) to the Imprivata server, which verifies
our authorization request using the public key.

Bug: 1073466
Change-Id: I44ef0335ede498b0e5f4878554050999dbbeda85
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2190675
Commit-Queue: Maria Petrisor <mpetrisor@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarRyan Sleevi <rsleevi@chromium.org>
Reviewed-by: default avatarOmar Morsi <omorsi@google.com>
Reviewed-by: default avatarAlexander Hendrich <hendrich@chromium.org>
Reviewed-by: default avatarPavol Marko <pmarko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#781300}
parent 7a4c0bbb
...@@ -72,6 +72,16 @@ bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate, ...@@ -72,6 +72,16 @@ bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
net::X509Certificate::PublicKeyType* key_type, net::X509Certificate::PublicKeyType* key_type,
size_t* key_size_bits); size_t* key_size_bits);
// Obtains information about the public key in |spki|.
// If |spki| is an RSA key, sets |key_size_bits| to the modulus
// length, and |key_type| to type RSA and returns true.
// If |spki| is any other key type, returns false and does not update any
// of the output parameters.
// All pointer arguments must not be null.
bool GetPublicKeyBySpki(const std::string& spki,
net::X509Certificate::PublicKeyType* key_type,
size_t* key_size_bits);
struct ClientCertificateRequest { struct ClientCertificateRequest {
ClientCertificateRequest(); ClientCertificateRequest();
ClientCertificateRequest(const ClientCertificateRequest& other); ClientCertificateRequest(const ClientCertificateRequest& other);
......
...@@ -1543,6 +1543,28 @@ std::string GetSubjectPublicKeyInfo( ...@@ -1543,6 +1543,28 @@ std::string GetSubjectPublicKeyInfo(
return spki_bytes.as_string(); return spki_bytes.as_string();
} }
// Extracts the public exponent out of an EVP_PKEY and verifies if it is equal
// to 65537 (Fermat number with n=4). This values is enforced by
// platform_keys::GetPublicKey() and platform_keys::GetPublicKeyBySpki().
// The caller of this function needs to have an OpenSSLErrStackTracer or
// otherwise clean up the error stack on failure.
bool VerifyRSAPublicExponent(EVP_PKEY* pkey) {
RSA* rsa = EVP_PKEY_get0_RSA(pkey);
if (!rsa) {
LOG(WARNING) << "Could not get RSA from PKEY.";
return false;
}
const BIGNUM* public_exponent = nullptr;
RSA_get0_key(rsa, nullptr /* out_n */, &public_exponent, nullptr /* out_d */);
if (BN_get_word(public_exponent) != 65537L) {
LOG(ERROR) << "Rejecting RSA public exponent that is unequal 65537.";
return false;
}
return true;
}
bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate, bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
net::X509Certificate::PublicKeyType* key_type, net::X509Certificate::PublicKeyType* key_type,
size_t* key_size_bits) { size_t* key_size_bits) {
...@@ -1574,16 +1596,7 @@ bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate, ...@@ -1574,16 +1596,7 @@ bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
switch (EVP_PKEY_type(pkey->type)) { switch (EVP_PKEY_type(pkey->type)) {
case EVP_PKEY_RSA: { case EVP_PKEY_RSA: {
RSA* rsa = EVP_PKEY_get0_RSA(pkey.get()); if (!VerifyRSAPublicExponent(pkey.get())) {
if (!rsa) {
LOG(WARNING) << "Could not get RSA from PKEY.";
return false;
}
const BIGNUM* public_exponent;
RSA_get0_key(rsa, nullptr /* out_n */, &public_exponent,
nullptr /* out_d */);
if (BN_get_word(public_exponent) != 65537L) {
LOG(ERROR) << "Rejecting RSA public exponent that is unequal 65537.";
return false; return false;
} }
break; break;
...@@ -1613,6 +1626,39 @@ bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate, ...@@ -1613,6 +1626,39 @@ bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
return true; return true;
} }
bool GetPublicKeyBySpki(const std::string& spki,
net::X509Certificate::PublicKeyType* key_type,
size_t* key_size_bits) {
net::X509Certificate::PublicKeyType key_type_tmp =
net::X509Certificate::kPublicKeyTypeUnknown;
size_t key_size_bits_tmp = 0;
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
CBS cbs;
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(spki.data()), spki.size());
bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_public_key(&cbs));
if (!pkey) {
LOG(WARNING) << "Could not extract public key from SPKI.";
return false;
}
int type = EVP_PKEY_type(pkey->type);
if (type == EVP_PKEY_RSA) {
key_type_tmp = net::X509Certificate::kPublicKeyTypeRSA;
} else {
LOG(WARNING) << "Keys of other type than RSA are not supported.";
return false;
}
key_size_bits_tmp = base::saturated_cast<size_t>(EVP_PKEY_bits(pkey.get()));
if (!VerifyRSAPublicExponent(pkey.get())) {
return false;
}
*key_type = key_type_tmp;
*key_size_bits = key_size_bits_tmp;
return true;
}
void PlatformKeysServiceImpl::GetCertificates( void PlatformKeysServiceImpl::GetCertificates(
const std::string& token_id, const std::string& token_id,
const GetCertificatesCallback& callback) { const GetCertificatesCallback& callback) {
......
...@@ -96,6 +96,7 @@ const struct NameValuePair { ...@@ -96,6 +96,7 @@ const struct NameValuePair {
namespace platform_keys { namespace platform_keys {
const char kErrorInvalidSpki[] = "The SubjectPublicKeyInfo is not valid.";
const char kErrorInvalidToken[] = "The token is not valid."; const char kErrorInvalidToken[] = "The token is not valid.";
const char kErrorInvalidX509Cert[] = const char kErrorInvalidX509Cert[] =
"Certificate is not a valid X.509 certificate."; "Certificate is not a valid X.509 certificate.";
...@@ -200,6 +201,44 @@ PlatformKeysInternalGetPublicKeyFunction::Run() { ...@@ -200,6 +201,44 @@ PlatformKeysInternalGetPublicKeyFunction::Run() {
return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate)); return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate));
} }
PlatformKeysInternalGetPublicKeyBySpkiFunction::
~PlatformKeysInternalGetPublicKeyBySpkiFunction() = default;
ExtensionFunction::ResponseAction
PlatformKeysInternalGetPublicKeyBySpkiFunction::Run() {
std::unique_ptr<api_pki::GetPublicKeyBySpki::Params> params(
api_pki::GetPublicKeyBySpki::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
const auto& public_key_spki_der = params->public_key_spki_der;
if (public_key_spki_der.empty())
return RespondNow(Error(platform_keys::kErrorInvalidSpki));
PublicKeyInfo key_info;
key_info.public_key_spki_der.assign(std::begin(public_key_spki_der),
std::end(public_key_spki_der));
if (!chromeos::platform_keys::GetPublicKeyBySpki(key_info.public_key_spki_der,
&key_info.key_type,
&key_info.key_size_bits) ||
key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) {
return RespondNow(Error(kErrorAlgorithmNotSupported));
}
// Currently, the only supported combination is:
// A SPKI declaring rsaEncryption used with the RSASSA-PKCS1-v1.5 algorithm.
if (params->algorithm_name != kWebCryptoRSASSA_PKCS1_v1_5) {
return RespondNow(Error(kErrorAlgorithmNotSupported));
}
api_pki::GetPublicKeyBySpki::Results::Algorithm algorithm;
BuildWebCryptoRSAAlgorithmDictionary(key_info,
&algorithm.additional_properties);
return RespondNow(ArgumentList(api_pki::GetPublicKeyBySpki::Results::Create(
public_key_spki_der, algorithm)));
}
PlatformKeysInternalSelectClientCertificatesFunction:: PlatformKeysInternalSelectClientCertificatesFunction::
~PlatformKeysInternalSelectClientCertificatesFunction() {} ~PlatformKeysInternalSelectClientCertificatesFunction() {}
......
...@@ -56,6 +56,16 @@ class PlatformKeysInternalGetPublicKeyFunction : public ExtensionFunction { ...@@ -56,6 +56,16 @@ class PlatformKeysInternalGetPublicKeyFunction : public ExtensionFunction {
PLATFORMKEYSINTERNAL_GETPUBLICKEY) PLATFORMKEYSINTERNAL_GETPUBLICKEY)
}; };
class PlatformKeysInternalGetPublicKeyBySpkiFunction
: public ExtensionFunction {
private:
~PlatformKeysInternalGetPublicKeyBySpkiFunction() override;
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("platformKeysInternal.getPublicKeyBySpki",
PLATFORMKEYSINTERNAL_GETPUBLICKEYBYSPKI)
};
class PlatformKeysInternalSignFunction : public ExtensionFunction { class PlatformKeysInternalSignFunction : public ExtensionFunction {
private: private:
~PlatformKeysInternalSignFunction() override; ~PlatformKeysInternalSignFunction() override;
......
...@@ -97,7 +97,7 @@ namespace platformKeys { ...@@ -97,7 +97,7 @@ namespace platformKeys {
// $(ref:platformKeys.subtleCrypto). // $(ref:platformKeys.subtleCrypto).
// |privateKey|: Might be <code>null</code> if this extension does not have // |privateKey|: Might be <code>null</code> if this extension does not have
// access to it. // access to it.
callback GetKeyPairCallback = void (object publicKey, callback GetKeyPairCallback = void (object publicKey,
optional object privateKey); optional object privateKey);
callback VerificationCallback = void (VerificationResult result); callback VerificationCallback = void (VerificationResult result);
...@@ -136,6 +136,28 @@ namespace platformKeys { ...@@ -136,6 +136,28 @@ namespace platformKeys {
object parameters, object parameters,
GetKeyPairCallback callback); GetKeyPairCallback callback);
// Passes the key pair identified by <code>publicKeySpkiDer</code> for
// usage with $(ref:platformKeys.subtleCrypto) to <code>callback</code>.
// |publicKeySpkiDer|: A DER-encoded X.509 SubjectPublicKeyInfo, obtained
// e.g. by calling WebCrypto's exportKey function with format="spki".
// |parameters|: Provides signature and hash algorithm parameters, in
// addition to those fixed by the key itself. The same parameters are
// accepted as by WebCrypto's <a
// href="http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey">importKey</a>
// function, e.g. <code>RsaHashedImportParams</code> for a RSASSA-PKCS1-v1_5
// key. For RSASSA-PKCS1-v1_5 keys, we need to also pass a "hash" parameter
// <code>{ "hash": { "name": string } }</code>. The "hash" parameter
// represents the name of the hashing algorithm to be used in the digest
// operation before a sign. It is possible to pass "none" as the hash name,
// in which case the sign function will apply PKCS#1 v1.5 padding and but
// not hash the given data.
// <p>Currently, this function only supports the "RSASSA-PKCS1-v1_5"
// algorithm with one of the hashing algorithms "none", "SHA-1", "SHA-256",
// "SHA-384", and "SHA-512".</p>
[nocompile] static void getKeyPairBySpki(ArrayBuffer publicKeySpkiDer,
object parameters,
GetKeyPairCallback callback);
// An implementation of WebCrypto's // An implementation of WebCrypto's
// <a href="http://www.w3.org/TR/WebCryptoAPI/#subtlecrypto-interface"> // <a href="http://www.w3.org/TR/WebCryptoAPI/#subtlecrypto-interface">
// SubtleCrypto</a> // SubtleCrypto</a>
......
...@@ -59,5 +59,16 @@ namespace platformKeysInternal { ...@@ -59,5 +59,16 @@ namespace platformKeysInternal {
static void getPublicKey(ArrayBuffer certificate, static void getPublicKey(ArrayBuffer certificate,
DOMString algorithmName, DOMString algorithmName,
GetPublicKeyCallback callback); GetPublicKeyCallback callback);
// Takes as arguments a <code>publicKeySpkiDer</code> and
// <code>algorithmName</code>. Checks if <code>publicKeySpkiDer</code> is
// not empty and if the <code>algorithmName</code> specified is supported.
// If so, calls back <code>callback</code> with the key info and a WebCrypto
// <code>KeyAlgorithm</code> dictionary describing the key's algorithm. The
// <code>name</code> property will equal <code>algorithmName</code>.
// Otherwise, calls back with an error.
static void getPublicKeyBySpki(ArrayBuffer publicKeySpkiDer,
DOMString algorithmName,
GetPublicKeyCallback callback);
}; };
}; };
...@@ -158,7 +158,7 @@ void ChromeExtensionsDispatcherDelegate::PopulateSourceMap( ...@@ -158,7 +158,7 @@ void ChromeExtensionsDispatcherDelegate::PopulateSourceMap(
IDR_FILE_SYSTEM_PROVIDER_CUSTOM_BINDINGS_JS); IDR_FILE_SYSTEM_PROVIDER_CUSTOM_BINDINGS_JS);
source_map->RegisterSource("platformKeys", source_map->RegisterSource("platformKeys",
IDR_PLATFORM_KEYS_CUSTOM_BINDINGS_JS); IDR_PLATFORM_KEYS_CUSTOM_BINDINGS_JS);
source_map->RegisterSource("platformKeys.getPublicKey", source_map->RegisterSource("platformKeys.getPublicKeyUtil",
IDR_PLATFORM_KEYS_GET_PUBLIC_KEY_JS); IDR_PLATFORM_KEYS_GET_PUBLIC_KEY_JS);
source_map->RegisterSource("platformKeys.internalAPI", source_map->RegisterSource("platformKeys.internalAPI",
IDR_PLATFORM_KEYS_INTERNAL_API_JS); IDR_PLATFORM_KEYS_INTERNAL_API_JS);
......
...@@ -10,8 +10,7 @@ var normalizeAlgorithm = ...@@ -10,8 +10,7 @@ var normalizeAlgorithm =
// Returns the normalized parameters of |importParams|. // Returns the normalized parameters of |importParams|.
// Any unknown parameters will be ignored. // Any unknown parameters will be ignored.
function normalizeImportParams(importParams) { function normalizeImportParams(importParams) {
if (!importParams.name || if (!importParams.name || typeof importParams.name !== 'string') {
Object.prototype.toString.call(importParams.name) !== '[object String]') {
throw new Error('Algorithm: name: Missing or not a String'); throw new Error('Algorithm: name: Missing or not a String');
} }
...@@ -61,6 +60,7 @@ function combineAlgorithms(algorithm, importParams) { ...@@ -61,6 +60,7 @@ function combineAlgorithms(algorithm, importParams) {
} }
function getPublicKey(cert, importParams, callback) { function getPublicKey(cert, importParams, callback) {
// TODO(crbug.com/1096486): Check cert type is ArrayBuffer.
importParams = normalizeImportParams(importParams); importParams = normalizeImportParams(importParams);
internalAPI.getPublicKey( internalAPI.getPublicKey(
cert, importParams.name, function(publicKey, algorithm) { cert, importParams.name, function(publicKey, algorithm) {
...@@ -73,4 +73,21 @@ function getPublicKey(cert, importParams, callback) { ...@@ -73,4 +73,21 @@ function getPublicKey(cert, importParams, callback) {
}); });
} }
function getPublicKeyBySpki(publicKeySpkiDer, importParams, callback) {
if (!(publicKeySpkiDer instanceof ArrayBuffer)){
throw $Error.self('publicKeySpkiDer: Not an ArrayBuffer');
}
importParams = normalizeImportParams(importParams);
internalAPI.getPublicKeyBySpki(
publicKeySpkiDer, importParams.name, function(publicKey, algorithm) {
if (bindingUtil.hasLastError()) {
callback();
return;
}
var combinedAlgorithm = combineAlgorithms(algorithm, importParams);
callback(publicKey, combinedAlgorithm);
});
}
exports.$set('getPublicKey', getPublicKey); exports.$set('getPublicKey', getPublicKey);
exports.$set('getPublicKeyBySpki', getPublicKeyBySpki);
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// TODO(crbug.com/1096029): Make consumers use the API directly by calling
// getInternalApi themselves.
var binding = getInternalApi('platformKeysInternal'); var binding = getInternalApi('platformKeysInternal');
exports.$set('selectClientCertificates', binding.selectClientCertificates); exports.$set('selectClientCertificates', binding.selectClientCertificates);
exports.$set('sign', binding.sign); exports.$set('sign', binding.sign);
exports.$set('getPublicKey', binding.getPublicKey); exports.$set('getPublicKey', binding.getPublicKey);
exports.$set('getPublicKeyBySpki', binding.getPublicKeyBySpki);
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
// Custom binding for the platformKeys API. // Custom binding for the platformKeys API.
var SubtleCrypto = require('platformKeys.SubtleCrypto').SubtleCrypto; var SubtleCrypto = require('platformKeys.SubtleCrypto').SubtleCrypto;
var getPublicKey = require('platformKeys.getPublicKey').getPublicKey; var publicKeyUtil = require('platformKeys.getPublicKeyUtil');
var getPublicKey = publicKeyUtil.getPublicKey;
var getPublicKeyBySpki = publicKeyUtil.getPublicKeyBySpki;
var internalAPI = require('platformKeys.internalAPI'); var internalAPI = require('platformKeys.internalAPI');
var keyModule = require('platformKeys.Key'); var keyModule = require('platformKeys.Key');
...@@ -60,4 +62,18 @@ apiBridge.registerCustomHook(function(api) { ...@@ -60,4 +62,18 @@ apiBridge.registerCustomHook(function(api) {
createPrivateKey(publicKey, algorithm)); createPrivateKey(publicKey, algorithm));
}); });
}); });
apiFunctions.setHandleRequest(
'getKeyPairBySpki', function(publicKeySpkiDer, params, callback) {
getPublicKeyBySpki(
publicKeySpkiDer, params, function(publicKey, algorithm) {
if (bindingUtil.hasLastError()) {
callback();
return;
}
callback(
createPublicKey(publicKey, algorithm),
createPrivateKey(publicKey, algorithm));
});
});
}); });
...@@ -234,6 +234,8 @@ function testStaticMethods() { ...@@ -234,6 +234,8 @@ function testStaticMethods() {
assertTrue(!!chrome.platformKeys.selectClientCertificates, assertTrue(!!chrome.platformKeys.selectClientCertificates,
"No selectClientCertificates function."); "No selectClientCertificates function.");
assertTrue(!!chrome.platformKeys.getKeyPair, "No getKeyPair method."); assertTrue(!!chrome.platformKeys.getKeyPair, "No getKeyPair method.");
assertTrue(
!!chrome.platformKeys.getKeyPairBySpki, "No getKeyPairBySpki method.");
assertTrue(!!chrome.platformKeys.subtleCrypto, "No subtleCrypto getter."); assertTrue(!!chrome.platformKeys.subtleCrypto, "No subtleCrypto getter.");
assertTrue(!!chrome.platformKeys.subtleCrypto(), "No subtleCrypto object."); assertTrue(!!chrome.platformKeys.subtleCrypto(), "No subtleCrypto object.");
assertTrue(!!chrome.platformKeys.subtleCrypto().sign, "No sign method."); assertTrue(!!chrome.platformKeys.subtleCrypto().sign, "No sign method.");
...@@ -355,23 +357,33 @@ function testMatchResultRSA() { ...@@ -355,23 +357,33 @@ function testMatchResultRSA() {
})); }));
} }
function testGetKeyPairMissingAlgorithName() { function verifyMissingAlgorithmError(getKeyFunction, buffer, name) {
var keyParams = { var keyParams = {
// This is missing the algorithm name. // This is missing the algorithm name.
hash: {name: 'SHA-1'} hash: {name: 'SHA-1'}
}; };
try { try {
chrome.platformKeys.getKeyPair( getKeyFunction(buffer, keyParams, function(error) {
data.client_1.buffer, keyParams, function(error) { fail(`${name} call was expected to fail.`);
fail('getKeyPair call was expected to fail.'); });
}); fail(`${name} did not throw error`);
fail('getKeyPair did not throw error');
} catch (e) { } catch (e) {
assertEq('Algorithm: name: Missing or not a String', e.message); assertEq('Algorithm: name: Missing or not a String', e.message);
succeed(); succeed();
} }
} }
function testGetKeyPairMissingAlgorithmName() {
verifyMissingAlgorithmError(
chrome.platformKeys.getKeyPair, data.client_1.buffer, 'getKeyPair');
}
function testGetKeyPairBySpkiMissingAlgorithmName() {
verifyMissingAlgorithmError(
chrome.platformKeys.getKeyPairBySpki, data.client_1_spki.buffer,
'getKeyPairBySpki');
}
function testGetKeyPairRejectsRSAPSS() { function testGetKeyPairRejectsRSAPSS() {
var keyParams = { var keyParams = {
name: 'RSA-PSS', name: 'RSA-PSS',
...@@ -381,6 +393,37 @@ function testGetKeyPairRejectsRSAPSS() { ...@@ -381,6 +393,37 @@ function testGetKeyPairRejectsRSAPSS() {
data.client_1.buffer, keyParams, data.client_1.buffer, keyParams,
callbackFail( callbackFail(
'The requested Algorithm is not permitted by the certificate.')); 'The requested Algorithm is not permitted by the certificate.'));
chrome.platformKeys.getKeyPairBySpki(
data.client_1_spki.buffer, keyParams,
callbackFail('Algorithm not supported.'));
}
function verifyKeyPairValidity(publicKey, privateKey) {
var expectedAlgorithm = {
modulusLength: 2048,
name: 'RSASSA-PKCS1-v1_5',
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {name: 'SHA-1'}
};
assertEq(expectedAlgorithm, publicKey.algorithm);
assertEq(expectedAlgorithm, privateKey.algorithm);
checkPublicKeyFormat(publicKey);
checkPrivateKeyFormat(privateKey);
chrome.platformKeys.subtleCrypto()
.exportKey('spki', publicKey)
.then(
callbackPass(function(actualPublicKeySpki) {
assertTrue(
compareArrays(
data.client_1_spki, new Uint8Array(actualPublicKeySpki)) ==
0,
'Match did not contain correct public key');
}),
function(error) {
fail('Export failed: ' + error);
});
} }
function testGetRsaKeyPairRejectEcdsa() { function testGetRsaKeyPairRejectEcdsa() {
...@@ -410,26 +453,23 @@ function testGetRsaKeyPair() { ...@@ -410,26 +453,23 @@ function testGetRsaKeyPair() {
chrome.platformKeys.getKeyPair( chrome.platformKeys.getKeyPair(
data.client_1.buffer, keyParams, data.client_1.buffer, keyParams,
callbackPass(function(publicKey, privateKey) { callbackPass(function(publicKey, privateKey) {
var expectedAlgorithm = { verifyKeyPairValidity(publicKey, privateKey);
modulusLength: 2048, }));
name: "RSASSA-PKCS1-v1_5", chrome.platformKeys.getKeyPairBySpki(
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), data.client_1_spki.buffer, keyParams,
hash: {name: 'SHA-1'} callbackPass(function(publicKey, privateKey) {
}; verifyKeyPairValidity(publicKey, privateKey);
assertEq(expectedAlgorithm, publicKey.algorithm); }));
assertEq(expectedAlgorithm, privateKey.algorithm); }
checkPublicKeyFormat(publicKey);
checkPrivateKeyFormat(privateKey);
chrome.platformKeys.subtleCrypto() function verifySignWithNoHash(privateKey, signParams) {
.exportKey('spki', publicKey) chrome.platformKeys.subtleCrypto()
.then(callbackPass(function(actualPublicKeySpki) { .sign(signParams, privateKey, data.raw_data)
assertTrue( .then(callbackPass(function(signature) {
compareArrays(data.client_1_spki, actualPublicKeySpki) == 0, var actualSignature = new Uint8Array(signature);
'Match did not contain correct public key'); assertTrue(
}), compareArrays(data.signature_nohash_pkcs, actualSignature) == 0,
function(error) { fail("Export failed: " + error); }); 'Incorrect signature');
})); }));
} }
...@@ -473,14 +513,23 @@ function testSignNoHash() { ...@@ -473,14 +513,23 @@ function testSignNoHash() {
chrome.platformKeys.getKeyPair( chrome.platformKeys.getKeyPair(
data.client_1.buffer, keyParams, data.client_1.buffer, keyParams,
callbackPass(function(publicKey, privateKey) { callbackPass(function(publicKey, privateKey) {
chrome.platformKeys.subtleCrypto() verifySignWithNoHash(privateKey, signParams);
.sign(signParams, privateKey, data.raw_data) }));
.then(callbackPass(function(signature) { chrome.platformKeys.getKeyPairBySpki(
var actualSignature = new Uint8Array(signature); data.client_1_spki.buffer, keyParams,
assertTrue(compareArrays(data.signature_nohash_pkcs, callbackPass(function(publicKey, privateKey) {
actualSignature) == 0, verifySignWithNoHash(privateKey, signParams);
'Incorrect signature'); }));
})); }
function verifySignWithSha1(privateKey, signParams, client_signature) {
chrome.platformKeys.subtleCrypto()
.sign(signParams, privateKey, data.raw_data)
.then(callbackPass(function(signature) {
var actualSignature = new Uint8Array(signature);
assertTrue(
compareArrays(client_signature, actualSignature) == 0,
'Incorrect signature');
})); }));
} }
...@@ -497,15 +546,14 @@ function testSignSha1Client1() { ...@@ -497,15 +546,14 @@ function testSignSha1Client1() {
chrome.platformKeys.getKeyPair( chrome.platformKeys.getKeyPair(
data.client_1.buffer, keyParams, data.client_1.buffer, keyParams,
callbackPass(function(publicKey, privateKey) { callbackPass(function(publicKey, privateKey) {
chrome.platformKeys.subtleCrypto() verifySignWithSha1(
.sign(signParams, privateKey, data.raw_data) privateKey, signParams, data.signature_client1_sha1_pkcs);
.then(callbackPass(function(signature) { }));
var actualSignature = new Uint8Array(signature); chrome.platformKeys.getKeyPairBySpki(
assertTrue( data.client_1_spki.buffer, keyParams,
compareArrays( callbackPass(function(publicKey, privateKey) {
data.signature_client1_sha1_pkcs, actualSignature) == 0, verifySignWithSha1(
'Incorrect signature'); privateKey, signParams, data.signature_client1_sha1_pkcs);
}));
})); }));
} }
...@@ -522,15 +570,8 @@ function testSignSha1Client2() { ...@@ -522,15 +570,8 @@ function testSignSha1Client2() {
chrome.platformKeys.getKeyPair( chrome.platformKeys.getKeyPair(
data.client_2.buffer, keyParams, data.client_2.buffer, keyParams,
callbackPass(function(publicKey, privateKey) { callbackPass(function(publicKey, privateKey) {
chrome.platformKeys.subtleCrypto() verifySignWithSha1(
.sign(signParams, privateKey, data.raw_data) privateKey, signParams, data.signature_client2_sha1_pkcs);
.then(callbackPass(function(signature) {
var actualSignature = new Uint8Array(signature);
assertTrue(
compareArrays(
data.signature_client2_sha1_pkcs, actualSignature) == 0,
'Incorrect signature');
}));
})); }));
} }
...@@ -542,9 +583,22 @@ function testSignSha1Client2OnSystemTokenOnly() { ...@@ -542,9 +583,22 @@ function testSignSha1Client2OnSystemTokenOnly() {
} }
} }
function verifySignFail(privateKey, signParams) {
chrome.platformKeys.subtleCrypto()
.sign(signParams, privateKey, data.raw_data)
.then(function(signature) {
fail('sign was expected to fail.');
}, callbackPass(function(error) {
assertTrue(error instanceof Error);
assertEq(
'The operation failed for an operation-specific reason',
error.message);
}));
}
// TODO(pmarko,emaxx): Test this by verifying that no private key is returned, // TODO(pmarko,emaxx): Test this by verifying that no private key is returned,
// once that's implemented, see crbug.com/799410. // once that's implemented, see crbug.com/799410.
function testSignFails(cert) { function testSignFails(cert, spki) {
var keyParams = { var keyParams = {
name: 'RSASSA-PKCS1-v1_5', name: 'RSASSA-PKCS1-v1_5',
hash: {name: 'SHA-1'} hash: {name: 'SHA-1'}
...@@ -554,20 +608,19 @@ function testSignFails(cert) { ...@@ -554,20 +608,19 @@ function testSignFails(cert) {
}; };
chrome.platformKeys.getKeyPair( chrome.platformKeys.getKeyPair(
cert.buffer, keyParams, callbackPass(function(publicKey, privateKey) { cert.buffer, keyParams, callbackPass(function(publicKey, privateKey) {
chrome.platformKeys.subtleCrypto() verifySignFail(privateKey, signParams);
.sign(signParams, privateKey, data.raw_data)
.then(function(signature) { fail('sign was expected to fail.'); },
callbackPass(function(error) {
assertTrue(error instanceof Error);
assertEq(
'The operation failed for an operation-specific reason',
error.message);
}));
})); }));
if (spki) {
chrome.platformKeys.getKeyPairBySpki(
spki.buffer, keyParams, callbackPass(function(publicKey, privateKey) {
verifySignFail(privateKey, signParams);
}));
}
} }
function testSignClient1Fails() { function testSignClient1Fails() {
testSignFails(data.client_1); testSignFails(data.client_1, data.client_1_spki);
} }
function testSignClient2Fails() { function testSignClient2Fails() {
...@@ -679,7 +732,8 @@ var testSuites = { ...@@ -679,7 +732,8 @@ var testSuites = {
testMatchResultCA1, testMatchResultCA1,
testMatchResultECDSA, testMatchResultECDSA,
testMatchResultRSA, testMatchResultRSA,
testGetKeyPairMissingAlgorithName, testGetKeyPairMissingAlgorithmName,
testGetKeyPairBySpkiMissingAlgorithmName,
testGetKeyPairRejectsRSAPSS, testGetKeyPairRejectsRSAPSS,
testGetRsaKeyPairRejectEcdsa, testGetRsaKeyPairRejectEcdsa,
testGetEcKeyPairRejectRsa, testGetEcKeyPairRejectRsa,
......
...@@ -1544,6 +1544,7 @@ enum HistogramValue { ...@@ -1544,6 +1544,7 @@ enum HistogramValue {
AUTOTESTPRIVATE_DISABLESWITCHACCESSDIALOG = 1481, AUTOTESTPRIVATE_DISABLESWITCHACCESSDIALOG = 1481,
ENTERPRISE_NETWORKINGATTRIBUTES_GETNETWORKDETAILS = 1482, ENTERPRISE_NETWORKINGATTRIBUTES_GETNETWORKDETAILS = 1482,
INPUTMETHODPRIVATE_SETAUTOCORRECTRANGE = 1483, INPUTMETHODPRIVATE_SETAUTOCORRECTRANGE = 1483,
PLATFORMKEYSINTERNAL_GETPUBLICKEYBYSPKI = 1484,
// Last entry: Add new entries above, then run: // Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py // python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -23616,6 +23616,7 @@ Called by update_extension_histograms.py.--> ...@@ -23616,6 +23616,7 @@ Called by update_extension_histograms.py.-->
<int value="1481" label="AUTOTESTPRIVATE_DISABLESWITCHACCESSDIALOG"/> <int value="1481" label="AUTOTESTPRIVATE_DISABLESWITCHACCESSDIALOG"/>
<int value="1482" label="ENTERPRISE_NETWORKINGATTRIBUTES_GETNETWORKDETAILS"/> <int value="1482" label="ENTERPRISE_NETWORKINGATTRIBUTES_GETNETWORKDETAILS"/>
<int value="1483" label="INPUTMETHODPRIVATE_SETAUTOCORRECTRANGE"/> <int value="1483" label="INPUTMETHODPRIVATE_SETAUTOCORRECTRANGE"/>
<int value="1484" label="PLATFORMKEYSINTERNAL_GETPUBLICKEYBYSPKI"/>
</enum> </enum>
<enum name="ExtensionIconState"> <enum name="ExtensionIconState">
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