blob: e3220cc26fb26b67ffd2f8124c5f62e7d7befb9c [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('google3.third_party.tink.javascript.subtle.bytes');
const EllipticCurves = goog.require('google3.third_party.tink.javascript.subtle.elliptic_curves');
const Random = goog.require('google3.third_party.tink.javascript.subtle.random');
const Validators = goog.require('google3.third_party.tink.javascript.subtle.validators');
const wycheproofEcdsaTestVectors = goog.require('tink.subtle.wycheproofEcdsaTestVectors');
const ecdsaSign = goog.require('google3.third_party.tink.javascript.subtle.ecdsa_sign');
const ecdsaVerify = goog.require('google3.third_party.tink.javascript.subtle.ecdsa_verify');
const {PublicKeyVerify} = goog.require('google3.third_party.tink.javascript.signature.internal.public_key_verify');
describe('ecdsa verify test', function() {
beforeEach(function() {
// Use a generous promise timeout for running continuously.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 1000; // 1000s
});
afterEach(function() {
// Reset the promise timeout to default value.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; // 1s
});
it('verify', async function() {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const signer = await ecdsaSign.fromJsonWebKey(
await EllipticCurves.exportCryptoKey(keyPair.privateKey), 'SHA-256');
const verifier = await ecdsaVerify.fromJsonWebKey(
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);
expect(await verifier.verify(signature, data)).toBe(true);
}
});
it('verify with der encoding', async function() {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const signer = await ecdsaSign.fromJsonWebKey(
await EllipticCurves.exportCryptoKey(keyPair.privateKey), 'SHA-256',
EllipticCurves.EcdsaSignatureEncodingType.DER);
const verifier = await ecdsaVerify.fromJsonWebKey(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
const verifierDer = await ecdsaVerify.fromJsonWebKey(
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);
expect(await verifier.verify(signature, data)).toBe(false);
expect(await verifierDer.verify(signature, data)).toBe(true);
}
});
it('constructor with invalid hash', async function() {
try {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
await ecdsaVerify.fromJsonWebKey(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-1');
fail('Should throw an exception.');
} catch (e) {
expect(e.toString())
.toBe(
'SecurityException: expected SHA-256 (because curve is P-256) but got SHA-1');
}
try {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-384');
await ecdsaVerify.fromJsonWebKey(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
fail('Should throw an exception.');
} catch (e) {
expect(e.toString())
.toBe(
'SecurityException: expected SHA-384 or SHA-512 (because curve is P-384) but got SHA-256');
}
try {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-521');
await ecdsaVerify.fromJsonWebKey(
await EllipticCurves.exportCryptoKey(keyPair.publicKey), 'SHA-256');
fail('Should throw an exception.');
} catch (e) {
expect(e.toString())
.toBe(
'SecurityException: expected SHA-512 (because curve is P-521) but got SHA-256');
}
});
it('constructor with invalid curve', async function() {
try {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const jwk = await EllipticCurves.exportCryptoKey(keyPair.publicKey);
jwk.crv = 'blah';
await ecdsaVerify.fromJsonWebKey(jwk, 'SHA-256');
fail('Should throw an exception.');
} catch (e) {
expect(e.toString()).toBe('SecurityException: unsupported curve: blah');
}
});
it('verify modified signature', async function() {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const signer = await ecdsaSign.fromJsonWebKey(
await EllipticCurves.exportCryptoKey(keyPair.privateKey), 'SHA-256');
const verifier = await ecdsaVerify.fromJsonWebKey(
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));
expect(await verifier.verify(s1, data)).toBe(false);
}
}
});
it('verify modified data', async function() {
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', 'P-256');
const signer = await ecdsaSign.fromJsonWebKey(
await EllipticCurves.exportCryptoKey(keyPair.privateKey), 'SHA-256');
const verifier = await ecdsaVerify.fromJsonWebKey(
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));
expect(await verifier.verify(signature, data1)).toBe(false);
}
}
});
it('wycheproof', async function() {
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.fromJsonWebKey(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 {!PublicKeyVerify} 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 '';
};