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:
thaidn
2019-12-17 13:10:29 -08:00
committed by Tink Team
parent e6d7461b3c
commit d8ed1ba95a
+112 -113
View File
@@ -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);
} }