blob: e93ba0790d0cc3cb62c5aa1ec4bf62ef17e6214f [file] [log] [blame]
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {AeadKeyTemplates} from '../aead/aead_key_templates';
import {PbEciesAeadDemParams, PbEciesAeadHkdfKeyFormat, PbEciesAeadHkdfParams, PbEciesAeadHkdfPrivateKey, PbEciesAeadHkdfPublicKey, PbEciesHkdfKemParams, PbEllipticCurveType, PbHashType, PbKeyTemplate, PbPointFormat} from '../internal/proto';
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 * as EciesAeadHkdfValidators from './ecies_aead_hkdf_validators';
describe('ecies aead hkdf validators 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('validate params, missing kem params', function() {
const invalidParams = createParams().setKemParams(null);
try {
EciesAeadHkdfValidators.validateParams(invalidParams);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingKemParams());
}
});
it('validate params, invalid kem params, unknown hash type', function() {
const invalidParams = createParams();
invalidParams.getKemParams()?.setHkdfHashType(PbHashType.UNKNOWN_HASH);
try {
EciesAeadHkdfValidators.validateParams(invalidParams);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unknownHashType());
}
});
it('validate params, invalid kem params, unknown curve type', function() {
const invalidParams = createParams();
invalidParams.getKemParams()?.setCurveType(
PbEllipticCurveType.UNKNOWN_CURVE);
try {
EciesAeadHkdfValidators.validateParams(invalidParams);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unknownCurveType());
}
});
it('validate params, missing dem params', function() {
const invalidParams = createParams().setDemParams(null);
try {
EciesAeadHkdfValidators.validateParams(invalidParams);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingDemParams());
}
});
it('validate params, invalid dem params, missing aead template', function() {
const invalidParams = createParams();
invalidParams.getDemParams()?.setAeadDem(null);
try {
EciesAeadHkdfValidators.validateParams(invalidParams);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingAeadTemplate());
}
});
it('validate params, invalid dem params, unsupported aead template',
function() {
const unsupportedTypeUrl = 'UNSUPPORTED_KEY_TYPE_URL';
const invalidParams = createParams();
invalidParams.getDemParams()?.getAeadDem()?.setTypeUrl(
unsupportedTypeUrl);
try {
EciesAeadHkdfValidators.validateParams(invalidParams);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString())
.toBe(ExceptionText.unsupportedKeyTemplate(unsupportedTypeUrl));
}
});
it('validate params, unknown point format', function() {
const invalidParams =
createParams().setEcPointFormat(PbPointFormat.UNKNOWN_FORMAT);
try {
EciesAeadHkdfValidators.validateParams(invalidParams);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unknownPointFormat());
}
});
it('validate params, different valid values', function() {
for (const curve
of [PbEllipticCurveType.NIST_P256,
PbEllipticCurveType.NIST_P384,
PbEllipticCurveType.NIST_P521,
]) {
for (const hashType
of [PbHashType.SHA1,
PbHashType.SHA384,
PbHashType.SHA256,
PbHashType.SHA512,
]) {
for (const keyTemplate
of [AeadKeyTemplates.aes128CtrHmacSha256(),
AeadKeyTemplates.aes128Gcm(),
]) {
for (const pointFormat
of [PbPointFormat.UNCOMPRESSED,
PbPointFormat.COMPRESSED,
PbPointFormat.DO_NOT_USE_CRUNCHY_UNCOMPRESSED,
]) {
const params =
createParams(curve, hashType, keyTemplate, pointFormat);
EciesAeadHkdfValidators.validateParams(params);
}
}
}
}
});
it('validate key format, missing params', function() {
const invalidKeyFormat = new PbEciesAeadHkdfKeyFormat();
try {
EciesAeadHkdfValidators.validateKeyFormat(invalidKeyFormat);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingFormatParams());
}
});
it('validate key format, invalid params', function() {
const invalidKeyFormat =
new PbEciesAeadHkdfKeyFormat().setParams(createParams());
// Check that also params were checked.
// Test missing DEM params.
invalidKeyFormat.getParams()?.setDemParams(null);
try {
EciesAeadHkdfValidators.validateKeyFormat(invalidKeyFormat);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingDemParams());
}
invalidKeyFormat.getParams()?.setDemParams(createDemParams());
// Test UNKNOWN_HASH in KEM params.
invalidKeyFormat.getParams()?.getKemParams()?.setHkdfHashType(
PbHashType.UNKNOWN_HASH);
try {
EciesAeadHkdfValidators.validateKeyFormat(invalidKeyFormat);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unknownHashType());
}
});
it('validate public key, missing params', function() {
const invalidPublicKey = new PbEciesAeadHkdfPublicKey();
try {
EciesAeadHkdfValidators.validatePublicKey(invalidPublicKey, 0);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingKeyParams());
}
});
it('validate public key, missing values x y', function() {
const invalidPublicKey =
new PbEciesAeadHkdfPublicKey().setParams(createParams());
// Both X and Y are set to empty.
try {
EciesAeadHkdfValidators.validatePublicKey(invalidPublicKey, 0);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingXY());
}
// The key with only Y set to empty is also invalid.
invalidPublicKey.setX(new Uint8Array(10));
try {
EciesAeadHkdfValidators.validatePublicKey(invalidPublicKey, 0);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingXY());
}
// The key with only X set to empty is also invalid.
invalidPublicKey.setY(new Uint8Array(10));
invalidPublicKey.setX(new Uint8Array(0));
try {
EciesAeadHkdfValidators.validatePublicKey(invalidPublicKey, 0);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingXY());
}
});
it('validate public key, invalid params', async function() {
const invalidPublicKey = await createPublicKey();
// Check that also params were checked.
// Test missing DEM params.
invalidPublicKey.getParams()?.setDemParams(null);
try {
EciesAeadHkdfValidators.validatePublicKey(invalidPublicKey, 0);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingDemParams());
}
invalidPublicKey.getParams()?.setDemParams(createDemParams());
// Test UNKNOWN_HASH in KEM params.
invalidPublicKey.getParams()?.getKemParams()?.setHkdfHashType(
PbHashType.UNKNOWN_HASH);
try {
EciesAeadHkdfValidators.validatePublicKey(invalidPublicKey, 0);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unknownHashType());
}
});
it('validate public key, version out of bounds', async function() {
const managerVersion = 0;
const invalidPublicKey = (await createPublicKey()).setVersion(1);
try {
EciesAeadHkdfValidators.validatePublicKey(
invalidPublicKey, managerVersion);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString())
.toBe(ExceptionText.versionOutOfBounds(managerVersion));
}
});
it('validate private key, missing public key', async function() {
const invalidPrivateKey = (await createPrivateKey()).setPublicKey(null);
try {
EciesAeadHkdfValidators.validatePrivateKey(invalidPrivateKey, 0, 0);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingPublicKey());
}
});
it('validate private key, invalid public key', async function() {
const invalidPrivateKey = await createPrivateKey();
invalidPrivateKey.getPublicKey()?.setParams(null);
try {
EciesAeadHkdfValidators.validatePrivateKey(invalidPrivateKey, 0, 0);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.missingKeyParams());
}
});
it('validate private key, should work', async function() {
const privateKey = await createPrivateKey();
EciesAeadHkdfValidators.validatePrivateKey(privateKey, 0, 0);
});
it('validate private key, version out of bounds', async function() {
const managerVersion = 0;
const invalidPrivateKey = (await createPrivateKey()).setVersion(1);
try {
EciesAeadHkdfValidators.validatePrivateKey(
invalidPrivateKey, managerVersion, managerVersion);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString())
.toBe(ExceptionText.versionOutOfBounds(managerVersion));
}
});
});
// Helper classes and functions
class ExceptionText {
static missingFormatParams(): string {
return 'SecurityException: Invalid key format - missing key params.';
}
static missingKeyParams(): string {
return 'SecurityException: Invalid public key - missing key params.';
}
static unknownPointFormat(): string {
return 'SecurityException: Invalid key params - unknown EC point format.';
}
static missingKemParams(): string {
return 'SecurityException: Invalid params - missing KEM params.';
}
static unknownHashType(): string {
return 'SecurityException: Invalid KEM params - unknown hash type.';
}
static unknownCurveType(): string {
return 'SecurityException: Invalid KEM params - unknown curve type.';
}
static missingDemParams(): string {
return 'SecurityException: Invalid params - missing DEM params.';
}
static missingAeadTemplate(): string {
return 'SecurityException: Invalid DEM params - missing AEAD key template.';
}
static unsupportedKeyTemplate(templateTypeUrl: string): string {
return 'SecurityException: Invalid DEM params - ' + templateTypeUrl +
' template is not supported by ECIES AEAD HKDF.';
}
static missingXY(): string {
return 'SecurityException: Invalid public key - missing value of X or Y.';
}
static missingPublicKey(): string {
return 'SecurityException: Invalid private key - missing public key information.';
}
static missingPrivateKeyValue(): string {
return 'SecurityException: Invalid private key - missing private key value.';
}
static versionOutOfBounds(version: number): string {
return 'SecurityException: Version is out of bound, must be between 0 and ' +
version + '.';
}
}
function createKemParams(
opt_curveType: PbEllipticCurveType = PbEllipticCurveType.NIST_P256,
opt_hashType: PbHashType = PbHashType.SHA256): PbEciesHkdfKemParams {
const kemParams = new PbEciesHkdfKemParams()
.setCurveType(opt_curveType)
.setHkdfHashType(opt_hashType);
return kemParams;
}
function createDemParams(opt_keyTemplate?: PbKeyTemplate):
PbEciesAeadDemParams {
if (!opt_keyTemplate) {
opt_keyTemplate = AeadKeyTemplates.aes128CtrHmacSha256();
}
const demParams = new PbEciesAeadDemParams().setAeadDem(opt_keyTemplate);
return demParams;
}
function createParams(
opt_curveType?: PbEllipticCurveType, opt_hashType?: PbHashType,
opt_keyTemplate?: PbKeyTemplate,
opt_pointFormat: PbPointFormat =
PbPointFormat.UNCOMPRESSED): PbEciesAeadHkdfParams {
const params = new PbEciesAeadHkdfParams()
.setKemParams(createKemParams(opt_curveType, opt_hashType))
.setDemParams(createDemParams(opt_keyTemplate))
.setEcPointFormat(opt_pointFormat);
return params;
}
async function createPrivateKey(
opt_curveType: PbEllipticCurveType = PbEllipticCurveType.NIST_P256,
opt_hashType?: PbHashType, opt_keyTemplate?: PbKeyTemplate,
opt_pointFormat?: PbPointFormat): Promise<PbEciesAeadHkdfPrivateKey> {
const curveSubtleType = Util.curveTypeProtoToSubtle(opt_curveType);
const curveName = EllipticCurves.curveToString(curveSubtleType);
const publicKeyProto =
new PbEciesAeadHkdfPublicKey().setVersion(0).setParams(createParams(
opt_curveType, opt_hashType, opt_keyTemplate, opt_pointFormat));
const keyPair = await EllipticCurves.generateKeyPair('ECDH', curveName);
const jsonPublicKey =
await EllipticCurves.exportCryptoKey(keyPair.publicKey!);
publicKeyProto.setX(Bytes.fromBase64(
assertExists(jsonPublicKey['x']), /* opt_webSafe = */ true));
publicKeyProto.setY(Bytes.fromBase64(
assertExists(jsonPublicKey['y']), /* opt_webSafe = */ true));
const privateKeyProto =
new PbEciesAeadHkdfPrivateKey().setVersion(0).setPublicKey(
publicKeyProto);
const jsonPrivateKey =
await EllipticCurves.exportCryptoKey(keyPair.privateKey!);
privateKeyProto.setKeyValue(Bytes.fromBase64(
assertExists(jsonPrivateKey['d']), /* opt_webSafe = */ true));
return privateKeyProto;
}
async function createPublicKey(
opt_curveType?: PbEllipticCurveType, opt_hashType?: PbHashType,
opt_keyTemplate?: PbKeyTemplate,
opt_pointFormat?: PbPointFormat): Promise<PbEciesAeadHkdfPublicKey> {
const key = await createPrivateKey(
opt_curveType, opt_hashType, opt_keyTemplate, opt_pointFormat);
return assertExists(key.getPublicKey());
}