blob: 7f7877fd36e8e8c2d733e7ddd39e433a1d6103b7 [file] [log] [blame]
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {PbEcdsaParams, PbEcdsaPublicKey, PbEcdsaSignatureEncoding, PbEllipticCurveType, PbHashType, PbKeyData} from '../internal/proto';
import * as Registry from '../internal/registry';
import * as Util from '../internal/util';
import * as Bytes from '../subtle/bytes';
import * as EllipticCurves from '../subtle/elliptic_curves';
import {assertExists} from '../testing/internal/test_utils';
import {EcdsaPublicKeyManager} from './ecdsa_public_key_manager';
import {PublicKeyVerify} from './internal/public_key_verify';
const KEY_TYPE = 'type.googleapis.com/google.crypto.tink.EcdsaPublicKey';
const VERSION = 0;
const PRIMITIVE = PublicKeyVerify;
describe('ecdsa public key manager test', function() {
beforeEach(function() {
// Use a generous promise timeout for running continuously.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 1000; // 1000s
});
afterEach(function() {
Registry.reset();
// Reset the promise timeout to default value.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; // 1s
});
it('new key', function() {
const manager = new EcdsaPublicKeyManager();
try {
manager.getKeyFactory().newKey(new Uint8Array(0));
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.notSupported());
}
});
it('new key data', function() {
const manager = new EcdsaPublicKeyManager();
try {
manager.getKeyFactory().newKeyData(new Uint8Array(0));
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.notSupported());
}
});
it('get primitive, unsupported key data type', async function() {
const manager = new EcdsaPublicKeyManager();
const keyData =
(await createKeyData()).setTypeUrl('unsupported_key_type_url');
try {
await manager.getPrimitive(PRIMITIVE, keyData);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString())
.toBe(ExceptionText.unsupportedKeyType(keyData.getTypeUrl()));
}
});
it('get primitive, unsupported key type', async function() {
const manager = new EcdsaPublicKeyManager();
const key = new PbEcdsaParams();
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeyType());
}
});
it('get primitive, high version', async function() {
const version = 1;
const manager = new EcdsaPublicKeyManager();
const key = (await createKey()).setVersion(version);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.versionOutOfBounds());
}
});
it('get primitive, missing params', async function() {
const manager = new EcdsaPublicKeyManager();
const key = (await createKey()).setParams(null);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingParams());
}
});
it('get primitive, invalid params', async function() {
const manager = new EcdsaPublicKeyManager();
const key = await createKey();
// Unknown encoding.
key.getParams()?.setEncoding(PbEcdsaSignatureEncoding.UNKNOWN_ENCODING);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unknownEncoding());
}
key.getParams()?.setEncoding(PbEcdsaSignatureEncoding.DER);
// Unknown hash.
key.getParams()?.setHashType(PbHashType.UNKNOWN_HASH);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unknownHash());
}
key.getParams()?.setHashType(PbHashType.SHA256);
// Unknown curve.
key.getParams()?.setCurve(PbEllipticCurveType.UNKNOWN_CURVE);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unknownCurve());
}
// Bad hash + curve combinations.
key.getParams()?.setCurve(PbEllipticCurveType.NIST_P384);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString())
.toBe(
'SecurityException: expected SHA-384 or SHA-512 (because curve is P-384) but got SHA-256');
}
key.getParams()?.setCurve(PbEllipticCurveType.NIST_P521);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString())
.toBe(
'SecurityException: expected SHA-512 (because curve is P-521) but got SHA-256');
}
});
it('get primitive, invalid key', async function() {
const manager = new EcdsaPublicKeyManager();
const key = await createKey();
const x = key.getX_asU8();
key.setX(new Uint8Array([0]));
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(ExceptionText.webCryptoErrors()).toContain(e.toString());
}
key.setX(x);
key.setY(new Uint8Array([0]));
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e: any) {
expect(ExceptionText.webCryptoErrors()).toContain(e.toString());
}
});
it('get primitive, invalid serialized key', async function() {
const manager = new EcdsaPublicKeyManager();
const keyData = await createKeyData();
for (let i = 0; i < 2; ++i) {
// Set the value of keyData to something which is not a serialization of a
// proper key.
keyData.setValue(new Uint8Array(i));
try {
await manager.getPrimitive(PRIMITIVE, keyData);
fail('An exception should be thrown ' + i.toString());
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.invalidSerializedKey());
}
}
});
// tests for getting primitive from valid key/keyData
it('get primitive, from key', async function() {
const manager = new EcdsaPublicKeyManager();
const keys = await createTestSetOfKeys();
for (const key of keys) {
await manager.getPrimitive(PRIMITIVE, key);
}
});
it('get primitive, from key data', async function() {
const manager = new EcdsaPublicKeyManager();
const keyDatas = await createTestSetOfKeyDatas();
for (const key of keyDatas) {
await manager.getPrimitive(PRIMITIVE, key);
}
});
it('does support', function() {
const manager = new EcdsaPublicKeyManager();
expect(manager.doesSupport(KEY_TYPE)).toBe(true);
});
it('get key type', function() {
const manager = new EcdsaPublicKeyManager();
expect(manager.getKeyType()).toBe(KEY_TYPE);
});
it('get primitive type', function() {
const manager = new EcdsaPublicKeyManager();
expect(manager.getPrimitiveType()).toBe(PRIMITIVE);
});
it('get version', function() {
const manager = new EcdsaPublicKeyManager();
expect(manager.getVersion()).toBe(VERSION);
});
});
// Helper classes and functions
class ExceptionText {
static notSupported(): string {
return 'SecurityException: This operation is not supported for public keys. ' +
'Use EcdsaPrivateKeyManager to generate new keys.';
}
static unsupportedPrimitive(): string {
return 'SecurityException: Requested primitive type which is not supported by ' +
'this key manager.';
}
static unsupportedKeyType(opt_requestedKeyType?: string): string {
const prefix = 'SecurityException: Key type';
const suffix =
'is not supported. This key manager supports ' + KEY_TYPE + '.';
if (opt_requestedKeyType) {
return prefix + ' ' + opt_requestedKeyType + ' ' + suffix;
} else {
return prefix + ' ' + suffix;
}
}
static versionOutOfBounds(): string {
return 'SecurityException: Version is out of bound, must be between 0 and ' +
VERSION + '.';
}
static unknownEncoding(): string {
return 'SecurityException: Invalid public key - missing signature encoding.';
}
static unknownHash(): string {
return 'SecurityException: Unknown hash type.';
}
static unknownCurve(): string {
return 'SecurityException: Unknown curve type.';
}
static missingParams(): string {
return 'SecurityException: Invalid public key - missing params.';
}
static missingXY(): string {
return 'SecurityException: Invalid public key - missing value of X or Y.';
}
static invalidSerializedKey(): string {
return 'SecurityException: Input cannot be parsed as ' + KEY_TYPE +
' key-proto.';
}
static webCryptoErrors(): string[] {
return [
'DataError',
// Firefox
'DataError: Data provided to an operation does not meet requirements',
];
}
}
function createParams(
curveType: PbEllipticCurveType, hashType: PbHashType,
encoding: PbEcdsaSignatureEncoding): PbEcdsaParams {
const params = (new PbEcdsaParams())
.setCurve(curveType)
.setHashType(hashType)
.setEncoding(encoding);
return params;
}
async function createKey(
opt_curveType: PbEllipticCurveType = PbEllipticCurveType.NIST_P256,
opt_hashType: PbHashType = PbHashType.SHA256,
opt_encoding: PbEcdsaSignatureEncoding =
PbEcdsaSignatureEncoding.DER): Promise<PbEcdsaPublicKey> {
const curveSubtleType = Util.curveTypeProtoToSubtle(opt_curveType);
const curveName = EllipticCurves.curveToString(curveSubtleType);
const key =
(new PbEcdsaPublicKey())
.setVersion(0)
.setParams(createParams(opt_curveType, opt_hashType, opt_encoding));
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', curveName);
const publicKey = await EllipticCurves.exportCryptoKey(keyPair.publicKey!);
key.setX(
Bytes.fromBase64(assertExists(publicKey['x']), /* opt_webSafe = */ true));
key.setY(
Bytes.fromBase64(assertExists(publicKey['y']), /* opt_webSafe = */ true));
return key;
}
function createKeyDataFromKey(key: PbEcdsaPublicKey): PbKeyData {
const keyData =
new PbKeyData()
.setTypeUrl(KEY_TYPE)
.setValue(key.serializeBinary())
.setKeyMaterialType(PbKeyData.KeyMaterialType.ASYMMETRIC_PUBLIC);
return keyData;
}
async function createKeyData(
opt_curveType?: PbEllipticCurveType, opt_hashType?: PbHashType,
opt_encoding?: PbEcdsaSignatureEncoding): Promise<PbKeyData> {
const key = await createKey(opt_curveType, opt_hashType, opt_encoding);
return createKeyDataFromKey(key);
}
// Create set of keys with all possible predefined/supported parameters.
async function createTestSetOfKeys(): Promise<PbEcdsaPublicKey[]> {
const keys: PbEcdsaPublicKey[] = [];
keys.push(await createKey(
PbEllipticCurveType.NIST_P256, PbHashType.SHA256,
PbEcdsaSignatureEncoding.DER));
keys.push(await createKey(
PbEllipticCurveType.NIST_P256, PbHashType.SHA256,
PbEcdsaSignatureEncoding.IEEE_P1363));
keys.push(await createKey(
PbEllipticCurveType.NIST_P384, PbHashType.SHA512,
PbEcdsaSignatureEncoding.DER));
keys.push(await createKey(
PbEllipticCurveType.NIST_P384, PbHashType.SHA512,
PbEcdsaSignatureEncoding.IEEE_P1363));
keys.push(await createKey(
PbEllipticCurveType.NIST_P521, PbHashType.SHA512,
PbEcdsaSignatureEncoding.DER));
keys.push(await createKey(
PbEllipticCurveType.NIST_P521, PbHashType.SHA512,
PbEcdsaSignatureEncoding.IEEE_P1363));
return keys;
}
// Create set of keyData protos with keys of all possible predefined/supported
// parameters.
async function createTestSetOfKeyDatas(): Promise<PbKeyData[]> {
const keys = await createTestSetOfKeys();
const keyDatas: PbKeyData[] = [];
for (const key of keys) {
const keyData = await createKeyDataFromKey(key);
keyDatas.push(keyData);
}
return keyDatas;
}