blob: 5ffae6668ca3c664ee25acbddcac7ff758894778 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
goog.module('tink.subtle.EcdsaVerifyTest');
goog.setTestOnly('tink.subtle.EcdsaVerifyTest');
const Bytes = goog.require('tink.subtle.Bytes');
const EcdsaSign = goog.require('tink.subtle.EcdsaSign');
const EcdsaVerify = goog.require('tink.subtle.EcdsaVerify');
const EllipticCurves = goog.require('tink.subtle.EllipticCurves');
const Environment = goog.require('tink.subtle.Environment');
const Random = goog.require('tink.subtle.Random');
const TestCase = goog.require('goog.testing.TestCase');
const Validators = goog.require('tink.subtle.Validators');
const testSuite = goog.require('goog.testing.testSuite');
const userAgent = goog.require('goog.userAgent');
const wycheproofEcdsaTestVectors = goog.require('tink.subtle.wycheproofEcdsaTestVectors');
testSuite({
shouldRunTests() {
return Environment.IS_WEBCRYPTO_AVAILABLE &&
!userAgent.EDGE; // b/120286783
},
setUp() {
// Use a generous promise timeout for running continuously.
TestCase.getActiveTestCase().promiseTimeout = 1000 * 1000; // 1000s
},
tearDown() {
// Reset the promise timeout to default value.
TestCase.getActiveTestCase().promiseTimeout = 1000; // 1s
},
async testVerify() {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const signer = await EcdsaSign.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.privateKey), 'SHA-256');
const verifier = await EcdsaVerify.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
for (let i = 0; i < 100; i++) {
const data = Random.randBytes(i);
const signature = await signer.sign(data);
assertTrue(await verifier.verify(signature, data));
}
},
async testVerifyWithDerEncoding() {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const signer = await EcdsaSign.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.privateKey), 'SHA-256',
EllipticCurves.EcdsaSignatureEncodingType.DER);
const verifier = await EcdsaVerify.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
const verifierDer = await EcdsaVerify.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256',
EllipticCurves.EcdsaSignatureEncodingType.DER);
for (let i = 0; i < 100; i++) {
const data = Random.randBytes(i);
const signature = await signer.sign(data);
assertFalse(await verifier.verify(signature, data));
assertTrue(await verifierDer.verify(signature, data));
}
},
async testConstructorWithNullPublicKey() {
try {
await EcdsaVerify.newInstance(null, 'SHA-256');
fail('Should throw an exception.');
} catch (e) {
assertEquals('CustomError: public key has to be non-null', e.toString());
}
},
async testConstructorWithInvalidHash() {
try {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
await EcdsaVerify.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-1');
fail('Should throw an exception.');
} catch (e) {
assertEquals(
'CustomError: expected SHA-256 (because curve is P-256) but got SHA-1',
e.toString());
}
try {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-384');
await EcdsaVerify.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
fail('Should throw an exception.');
} catch (e) {
assertEquals(
'CustomError: expected SHA-384 or SHA-512 (because curve is P-384) but got SHA-256',
e.toString());
}
try {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-521');
await EcdsaVerify.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
fail('Should throw an exception.');
} catch (e) {
assertEquals(
'CustomError: expected SHA-512 (because curve is P-521) but got SHA-256',
e.toString());
}
},
async testConstructorWithInvalidCurve() {
try {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const jwk = await EllipticCurves.exportCryptoKey(keyPair.publicKey);
jwk.crv = 'blah';
await EcdsaVerify.newInstance(jwk, 'SHA-256');
fail('Should throw an exception.');
} catch (e) {
assertEquals('CustomError: unsupported curve: blah', e.toString());
}
},
async testVerifyModifiedSignature() {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const signer = await EcdsaSign.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.privateKey), 'SHA-256');
const verifier = await EcdsaVerify.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
const data = Random.randBytes(20);
const signature = await signer.sign(data);
for (let i = 0; i < signature.length; i++) {
for (let j = 0; j < 8; j++) {
const s1 = new Uint8Array(signature);
s1[i] = (s1[i] ^ (1 << j));
assertFalse(await verifier.verify(s1, data));
}
}
},
async testVerifyModifiedData() {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const signer = await EcdsaSign.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.privateKey), 'SHA-256');
const verifier = await EcdsaVerify.newInstance(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
const data = Random.randBytes(20);
const signature = await signer.sign(data);
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < 8; j++) {
const data1 = new Uint8Array(data);
data1[i] = (data1[i] ^ (1 << j));
assertFalse(await verifier.verify(signature, data1));
}
}
},
async testWycheproof() {
for (let testGroup of wycheproofEcdsaTestVectors['testGroups']) {
try {
Validators.validateEcdsaParams(
testGroup['jwk']['crv'], testGroup['sha']);
} catch (e) {
// Tink does not support this config.
continue;
}
const verifier =
await EcdsaVerify.newInstance(testGroup['jwk'], testGroup['sha']);
let errors = '';
for (let test of testGroup['tests']) {
errors += await runWycheproofTest(verifier, test);
}
if (errors !== '') {
fail(errors);
}
}
},
});
/**
* Runs the test with test vector given as an input and returns either empty
* string or a text describing the failure.
*
* @param {!EcdsaVerify} verifier
* @param {!Object} test - JSON object with test data
* @return {!Promise<string>}
*/
const runWycheproofTest = async function(verifier, test) {
try {
const sig = Bytes.fromHex(test['sig']);
const msg = Bytes.fromHex(test['msg']);
const isValid = await verifier.verify(sig, msg);
if (isValid) {
if (test['result'] === 'invalid') {
return 'invalid signature accepted on test ' + test['tcId'] + '\n';
}
} else {
if (test['result'] === 'valid') {
return 'valid signature rejected on test ' + test['tcId'] + '\n';
}
}
} catch (e) {
if (test['result'] === 'valid') {
return 'valid signature rejected on test ' + test['tcId'] +
': unexpected exception \"' + e.toString() + '\".\n';
}
}
// If the test passes return an empty string.
return '';
};