Merge #57: fix Ecdsa.generateKey in WebCrypto tests.
While I'm here, fix a bunch of Lint errors. NOKEYCHECK=True PiperOrigin-RevId: 286042650 GitOrigin-RevId: d644e50fa0b8246d679f1e5b6daa32a76a130ebf
This commit is contained in:
@@ -41,7 +41,7 @@ var Ecdsa = function() {};
|
|||||||
/**
|
/**
|
||||||
* Verifies the given signature using the given ECDSA public key.
|
* Verifies the given signature using the given ECDSA public key.
|
||||||
* @param {!CryptoKey} pk The ECDSA public key
|
* @param {!CryptoKey} pk The ECDSA public key
|
||||||
* @param {!string} hashAlg The hash algorithm
|
* @param {string} hashAlg The hash algorithm
|
||||||
* @param {!ArrayBuffer} msg The message to be verified
|
* @param {!ArrayBuffer} msg The message to be verified
|
||||||
* @param {!ArrayBuffer} sig The signature to be verified
|
* @param {!ArrayBuffer} sig The signature to be verified
|
||||||
*
|
*
|
||||||
@@ -49,56 +49,40 @@ var Ecdsa = function() {};
|
|||||||
*/
|
*/
|
||||||
Ecdsa.verify = function(pk, hashAlg, msg, sig) {
|
Ecdsa.verify = function(pk, hashAlg, msg, sig) {
|
||||||
return crypto.subtle.verify(
|
return crypto.subtle.verify(
|
||||||
{
|
{name: 'ECDSA', hash: {name: hashAlg}}, pk, sig, msg);
|
||||||
name: 'ECDSA',
|
|
||||||
hash: {name: hashAlg}
|
|
||||||
},
|
|
||||||
pk,
|
|
||||||
sig,
|
|
||||||
msg
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs a message using the given ECDSA private key and hash algorithm.
|
* Signs a message using the given ECDSA private key and hash algorithm.
|
||||||
* @param {!CryptoKey} sk The ECDSA private key
|
* @param {!CryptoKey} sk The ECDSA private key
|
||||||
* @param {!ArrayBuffer} msg The message to be signed
|
* @param {!ArrayBuffer} msg The message to be signed
|
||||||
* @param {!string} hashAlg The hash algorithm
|
* @param {string} hashAlg The hash algorithm
|
||||||
*
|
*
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
Ecdsa.sign = function(sk, msg, hashAlg) {
|
Ecdsa.sign = function(sk, msg, hashAlg) {
|
||||||
return crypto.subtle.sign(
|
return crypto.subtle.sign(
|
||||||
{
|
{
|
||||||
name: 'ECDSA',
|
name: 'ECDSA',
|
||||||
hash: {name: hashAlg},
|
hash: {name: hashAlg},
|
||||||
},
|
},
|
||||||
sk,
|
sk, msg);
|
||||||
msg
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a ECDSA public key.
|
* Imports a ECDSA public key.
|
||||||
* @param {!JSONObject} keyData The key data in JWK format
|
* @param {!JSONObject} keyData The key data in JWK format
|
||||||
* @param {!string} hashAlg The hash algorithm
|
* @param {string} hashAlg The hash algorithm
|
||||||
* @param {!Array<string>} usages The usages of the key
|
* @param {!Array<string>} usages The usages of the key
|
||||||
*
|
*
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
Ecdsa.importPublicKey = function(keyData, hashAlg, usages) {
|
Ecdsa.importPublicKey = function(keyData, hashAlg, usages) {
|
||||||
return crypto.subtle.importKey(
|
return crypto.subtle.importKey(
|
||||||
'jwk',
|
'jwk', keyData,
|
||||||
keyData,
|
{name: 'ECDSA', namedCurve: keyData['crv'], hash: {name: hashAlg}}, true,
|
||||||
{
|
usages);
|
||||||
name: 'ECDSA',
|
|
||||||
namedCurve: keyData['crv'],
|
|
||||||
hash: {name: hashAlg}
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
usages
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,20 +97,18 @@ Ecdsa.exportKey = function(key) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an ECDSA key pair using the given hash algorithm and curve name.
|
* Generates an ECDSA key pair using the given hash algorithm and curve name.
|
||||||
* @param {!string} hashAlg The hash algorithm
|
* @param {string} hashAlg The hash algorithm
|
||||||
* @param {!string} curveName The curve name
|
* @param {string} curveName The curve name
|
||||||
*
|
*
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
Ecdsa.generateKey = function(hashAlg, curveName) {
|
Ecdsa.generateKey = function(hashAlg, curveName) {
|
||||||
return crypto.subtle.generateKey(
|
return crypto.subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: 'ECDSA',
|
name: 'ECDSA',
|
||||||
namedCurve: curveName,
|
namedCurve: curveName,
|
||||||
},
|
},
|
||||||
true,
|
true, ['sign', 'verify']);
|
||||||
['sign', 'verify']
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -139,20 +121,25 @@ Ecdsa.generateKey = function(hashAlg, curveName) {
|
|||||||
Ecdsa.testVerify = function() {
|
Ecdsa.testVerify = function() {
|
||||||
tc = this;
|
tc = this;
|
||||||
var promise = new Promise((resolve, reject) => {
|
var promise = new Promise((resolve, reject) => {
|
||||||
Ecdsa.importPublicKey(tc.keyData, tc.hashAlg, ['verify']).then(function(pk){
|
Ecdsa.importPublicKey(tc.keyData, tc.hashAlg, ['verify'])
|
||||||
Ecdsa.verify(pk, tc.hashAlg, tc.msg, tc.sig).then(function(isValid){
|
.then(function(pk) {
|
||||||
if ((tc.result == 'valid' && !isValid) ||
|
Ecdsa.verify(pk, tc.hashAlg, tc.msg, tc.sig)
|
||||||
(tc.result == 'invalid' && isValid)) {
|
.then(function(isValid) {
|
||||||
reject('Failed in test case ' + tc.id);
|
if ((tc.result == 'valid' && !isValid) ||
|
||||||
}
|
(tc.result == 'invalid' && isValid)) {
|
||||||
resolve();
|
reject('Failed in test case ' + tc.id);
|
||||||
}).catch(function(err){
|
}
|
||||||
// don't expect any exception in signature verification
|
resolve();
|
||||||
reject('Unexpected exception on test case ' + tc.id + ": " + err);
|
})
|
||||||
});
|
.catch(function(err) {
|
||||||
}).catch(function(err){
|
// don't expect any exception in signature verification
|
||||||
reject('Failed to import key in test case ' + tc.id + ': ' + err);
|
reject(
|
||||||
});
|
'Unexpected exception on test case ' + tc.id + ': ' + err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
reject('Failed to import key in test case ' + tc.id + ': ' + err);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
@@ -162,10 +149,10 @@ Ecdsa.testVerify = function() {
|
|||||||
* Parameters of a ECDSA signature verification test.
|
* Parameters of a ECDSA signature verification test.
|
||||||
* @param {!number} id Test case's id
|
* @param {!number} id Test case's id
|
||||||
* @param {!JSONObject} keyData The key data in JWK format
|
* @param {!JSONObject} keyData The key data in JWK format
|
||||||
* @param {!string} hashAlg The hash algorithm
|
* @param {string} hashAlg The hash algorithm
|
||||||
* @param {!ArrayBuffer} msg The message that was signed
|
* @param {!ArrayBuffer} msg The message that was signed
|
||||||
* @param {!ArrayBuffer} sig The signature to be verified
|
* @param {!ArrayBuffer} sig The signature to be verified
|
||||||
* @param {!string} result The expected result of the test case
|
* @param {string} result The expected result of the test case
|
||||||
*/
|
*/
|
||||||
var EcdsaVerifyTestCase = function(id, keyData, hashAlg, msg, sig, result) {
|
var EcdsaVerifyTestCase = function(id, keyData, hashAlg, msg, sig, result) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -191,8 +178,8 @@ function testEcdsaVectors() {
|
|||||||
var keyData = tg['jwk'];
|
var keyData = tg['jwk'];
|
||||||
var curveName = keyData['crv'];
|
var curveName = keyData['crv'];
|
||||||
var hashAlg = tg['sha'];
|
var hashAlg = tg['sha'];
|
||||||
if (SUPPORTED['ecdsa-curve'].indexOf(curveName) == -1
|
if (SUPPORTED['ecdsa-curve'].indexOf(curveName) == -1 ||
|
||||||
|| SUPPORTED['hash'].indexOf(hashAlg) == -1) {
|
SUPPORTED['hash'].indexOf(hashAlg) == -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (var j = 0; j < tg['tests'].length; j++) {
|
for (var j = 0; j < tg['tests'].length; j++) {
|
||||||
@@ -201,8 +188,8 @@ function testEcdsaVectors() {
|
|||||||
var result = tc['result'];
|
var result = tc['result'];
|
||||||
var msg = TestUtil.hexToArrayBuffer(tc['msg']);
|
var msg = TestUtil.hexToArrayBuffer(tc['msg']);
|
||||||
var sig = TestUtil.hexToArrayBuffer(tc['sig']);
|
var sig = TestUtil.hexToArrayBuffer(tc['sig']);
|
||||||
var test = new EcdsaVerifyTestCase(
|
var test =
|
||||||
tcId, keyData, hashAlg, msg, sig, result);
|
new EcdsaVerifyTestCase(tcId, keyData, hashAlg, msg, sig, result);
|
||||||
testCase.addNewTest('Test ' + tcId, Ecdsa.testVerify, test);
|
testCase.addNewTest('Test ' + tcId, Ecdsa.testVerify, test);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,9 +206,9 @@ function testEcdsaVectors() {
|
|||||||
Ecdsa.extractSig = function(sig) {
|
Ecdsa.extractSig = function(sig) {
|
||||||
var bytes = new Uint8Array(sig);
|
var bytes = new Uint8Array(sig);
|
||||||
var byteLen = bytes.length;
|
var byteLen = bytes.length;
|
||||||
var rBytes = bytes.subarray(0, byteLen/2);
|
var rBytes = bytes.subarray(0, byteLen / 2);
|
||||||
var r = new BigInteger(rBytes);
|
var r = new BigInteger(rBytes);
|
||||||
var sBytes = bytes.subarray(byteLen/2, byteLen);
|
var sBytes = bytes.subarray(byteLen / 2, byteLen);
|
||||||
var s = new BigInteger(sBytes);
|
var s = new BigInteger(sBytes);
|
||||||
return [r, s];
|
return [r, s];
|
||||||
};
|
};
|
||||||
@@ -250,7 +237,7 @@ Ecdsa.extractNonce = function(h, r, s, d, curveSpec) {
|
|||||||
* @param {!wycheproof.BigInteger} s The 's' value of the signature
|
* @param {!wycheproof.BigInteger} s The 's' value of the signature
|
||||||
* @param {!wycheproof.BigInteger} d The private component of the ECDSA key
|
* @param {!wycheproof.BigInteger} d The private component of the ECDSA key
|
||||||
* @param {!wycheproof.BigInteger} k The nonce that needs to be checked
|
* @param {!wycheproof.BigInteger} k The nonce that needs to be checked
|
||||||
* @param {!string} curveName The curve name
|
* @param {string} curveName The curve name
|
||||||
*/
|
*/
|
||||||
Ecdsa.checkNonceCorrectness = function(msg, r, s, d, k, curveName) {
|
Ecdsa.checkNonceCorrectness = function(msg, r, s, d, k, curveName) {
|
||||||
var e2eCurveMap = {
|
var e2eCurveMap = {
|
||||||
@@ -259,14 +246,13 @@ Ecdsa.checkNonceCorrectness = function(msg, r, s, d, k, curveName) {
|
|||||||
'P-521': 'P_521',
|
'P-521': 'P_521',
|
||||||
};
|
};
|
||||||
var e2eCurveName = e2eCurveMap[curveName];
|
var e2eCurveName = e2eCurveMap[curveName];
|
||||||
var key = new e2e.ecc.Ecdsa(e2eCurveName,
|
var key = new e2e.ecc.Ecdsa(e2eCurveName, {privKey: d.toByteArray()});
|
||||||
{privKey: d.toByteArray()});
|
|
||||||
var msgBytes = new Uint8Array(msg);
|
var msgBytes = new Uint8Array(msg);
|
||||||
var calSig = key.signForTestingOnly(msgBytes, k);
|
var calSig = key.signForTestingOnly(msgBytes, k);
|
||||||
var calR = new BigInteger(calSig['r']);
|
var calR = new BigInteger(calSig['r']);
|
||||||
var calS = new BigInteger(calSig['s']);
|
var calS = new BigInteger(calSig['s']);
|
||||||
assertTrue('Nonce calculation was incorrect',
|
assertTrue(
|
||||||
r.isEqual(calR) && s.isEqual(calS));
|
'Nonce calculation was incorrect', r.isEqual(calR) && s.isEqual(calS));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -285,61 +271,72 @@ Ecdsa.checkNonceCorrectness = function(msg, r, s, d, k, curveName) {
|
|||||||
*/
|
*/
|
||||||
Ecdsa.testBias = function() {
|
Ecdsa.testBias = function() {
|
||||||
var tc = this;
|
var tc = this;
|
||||||
var nDone = 0;
|
|
||||||
var countLsb = 0;
|
var countLsb = 0;
|
||||||
var countMsb = 0;
|
var countMsb = 0;
|
||||||
var promise = new Promise(function(resolve, reject){
|
|
||||||
for (var i = 0; i < tc.nTests; i++) {
|
|
||||||
Ecdsa.generateKey(tc.hashAlg, tc.curveName).then(function(key){
|
|
||||||
Ecdsa.exportKey(key.privateKey).then(function(keyData){
|
|
||||||
Ecdsa.sign(key.privateKey, tc.msg, tc.hashAlg).then(function(sig){
|
|
||||||
HashUtil.digest(tc.hashAlg, tc.msg).then(function(digest){
|
|
||||||
var curveSpec = EcUtil.getCurveSpec(tc.curveName);
|
|
||||||
var h = new BigInteger(new Uint8Array(digest));
|
|
||||||
// private key value
|
|
||||||
var d = BigInteger.fromHex(TestUtil.base64UrlToHex(keyData['d']));
|
|
||||||
var r, s;
|
|
||||||
[r, s] = Ecdsa.extractSig(sig);
|
|
||||||
var k = Ecdsa.extractNonce(h, r, s, d, curveSpec);
|
|
||||||
// Uncomment this line to check correctness of nonce calculation
|
|
||||||
// Ecdsa.checkNonceCorrectness(tc.msg, r, s, d, k, tc.curveName);
|
|
||||||
var halfN = curveSpec.n.shiftRight(1);
|
|
||||||
if (k.isBitSet(0)) countLsb += 1;
|
|
||||||
if (k.compare(halfN) == 1) countMsb += 1;
|
|
||||||
|
|
||||||
nDone += 1;
|
return Ecdsa.generateKey(tc.hashAlg, tc.curveName)
|
||||||
if (nDone == tc.nTests) {
|
.then(function(key) {
|
||||||
if (countLsb < tc.minCount || countLsb > tc.nTests-tc.minCount) {
|
return Ecdsa.exportKey(key.privateKey)
|
||||||
reject("Bias detected in the LSB of k" +
|
.then(function(keyData) {
|
||||||
", hash: " + tc.hashAlg + ", curve: " + tc.curveName +
|
var promises = [];
|
||||||
", countLSB: " + countLsb + ", countMSB: " + countMsb);
|
|
||||||
}
|
for (var i = 0; i < tc.nTests; i++) {
|
||||||
if (countMsb < tc.minCount || countMsb > tc.nTests-tc.minCount) {
|
promises.push(
|
||||||
reject("Bias detected in the MSB of k" +
|
Ecdsa.sign(key.privateKey, tc.msg, tc.hashAlg)
|
||||||
", hash: " + tc.hashAlg + ", curve: " + tc.curveName +
|
.then(function(sig) {
|
||||||
", countLSB: " + countLsb + ", countMSB: " + countMsb);
|
return HashUtil.digest(tc.hashAlg, tc.msg)
|
||||||
}
|
.then(function(digest) {
|
||||||
resolve();
|
var curveSpec =
|
||||||
|
EcUtil.getCurveSpec(tc.curveName);
|
||||||
|
var h = new BigInteger(new Uint8Array(digest));
|
||||||
|
// private key value
|
||||||
|
var d = BigInteger.fromHex(
|
||||||
|
TestUtil.base64UrlToHex(keyData['d']));
|
||||||
|
var r, s;
|
||||||
|
[r, s] = Ecdsa.extractSig(sig);
|
||||||
|
var k =
|
||||||
|
Ecdsa.extractNonce(h, r, s, d, curveSpec);
|
||||||
|
// Uncomment this line to check correctness of
|
||||||
|
// nonce calculation
|
||||||
|
// Ecdsa.checkNonceCorrectness(tc.msg, r, s, d,
|
||||||
|
// k, tc.curveName);
|
||||||
|
var halfN = curveSpec.n.shiftRight(1);
|
||||||
|
if (k.isBitSet(0)) countLsb += 1;
|
||||||
|
if (k.compare(halfN) == 1) countMsb += 1;
|
||||||
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises).then(function() {
|
||||||
|
if (countLsb < tc.minCount ||
|
||||||
|
countLsb > tc.nTests - tc.minCount) {
|
||||||
|
reject(
|
||||||
|
'Bias detected in the LSB of k' +
|
||||||
|
', hash: ' + tc.hashAlg + ', curve: ' + tc.curveName +
|
||||||
|
', countLSB: ' + countLsb + ', countMSB: ' + countMsb);
|
||||||
|
}
|
||||||
|
if (countMsb < tc.minCount ||
|
||||||
|
countMsb > tc.nTests - tc.minCount) {
|
||||||
|
reject(
|
||||||
|
'Bias detected in the MSB of k' +
|
||||||
|
', hash: ' + tc.hashAlg + ', curve: ' + tc.curveName +
|
||||||
|
', countLSB: ' + countLsb + ', countMSB: ' + countMsb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
throw new Error('Failed to export private key: ' + err);
|
||||||
});
|
});
|
||||||
}).catch(function(err){
|
})
|
||||||
reject('Failed to sign: ' + err);
|
.catch(function(err) {
|
||||||
});
|
throw new Error('Failed to generate key: ' + err);
|
||||||
}).catch(function(err){
|
|
||||||
reject('Failed to export private key: ' + err);
|
|
||||||
});
|
|
||||||
}).catch(function(err){
|
|
||||||
reject('Failed to generate key: ' + err);
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
return promise;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameters of a ECDSA bias test.
|
* Parameters of a ECDSA bias test.
|
||||||
* @param {!string} hashAlg The hash algorithm
|
* @param {string} hashAlg The hash algorithm
|
||||||
* @param {!string} curveName The curve name
|
* @param {string} curveName The curve name
|
||||||
* @param {!ArrayBuffer} msg The message that was signed
|
* @param {!ArrayBuffer} msg The message that was signed
|
||||||
* @param {!number} nTests The number of key to be generated
|
* @param {!number} nTests The number of key to be generated
|
||||||
* @param {!number} minCount
|
* @param {!number} minCount
|
||||||
@@ -362,17 +359,19 @@ var EcdsaBiasTestCase = function(hashAlg, curveName, msg, nTests, minCount) {
|
|||||||
*/
|
*/
|
||||||
function testEcdsaBiasAll() {
|
function testEcdsaBiasAll() {
|
||||||
var testCase = new goog.testing.TestCase();
|
var testCase = new goog.testing.TestCase();
|
||||||
testCase.promiseTimeout = 120*1000;
|
testCase.promiseTimeout = 120 * 1000;
|
||||||
var msg = TestUtil.hexToArrayBuffer('48656c6c6f'); // msg = 'Hello'
|
var msg = TestUtil.hexToArrayBuffer('48656c6c6f'); // msg = 'Hello'
|
||||||
var nTests = 1024;
|
var nTests = 1024;
|
||||||
var minCount = 410;
|
var minCount = 410;
|
||||||
var biasTest256 = new EcdsaBiasTestCase('SHA-256', 'P-256', msg, nTests, minCount);
|
var biasTest256 =
|
||||||
|
new EcdsaBiasTestCase('SHA-256', 'P-256', msg, nTests, minCount);
|
||||||
testCase.addNewTest('bias256', Ecdsa.testBias, biasTest256);
|
testCase.addNewTest('bias256', Ecdsa.testBias, biasTest256);
|
||||||
var biasTest384 = new EcdsaBiasTestCase('SHA-384', 'P-384', msg, nTests, minCount);
|
var biasTest384 =
|
||||||
|
new EcdsaBiasTestCase('SHA-384', 'P-384', msg, nTests, minCount);
|
||||||
testCase.addNewTest('bias384', Ecdsa.testBias, biasTest384);
|
testCase.addNewTest('bias384', Ecdsa.testBias, biasTest384);
|
||||||
var biasTest521 = new EcdsaBiasTestCase('SHA-512', 'P-521', msg, nTests, minCount);
|
var biasTest521 =
|
||||||
|
new EcdsaBiasTestCase('SHA-512', 'P-521', msg, nTests, minCount);
|
||||||
testCase.addNewTest('bias521', Ecdsa.testBias, biasTest521);
|
testCase.addNewTest('bias521', Ecdsa.testBias, biasTest521);
|
||||||
return testCase.runTestsReturningPromise()
|
return testCase.runTestsReturningPromise().then(
|
||||||
.then(wycheproof.TestUtil.checkTestCaseResult);
|
wycheproof.TestUtil.checkTestCaseResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user