blob: 36366d83abc402c6b4f2413161343dc67a52a894 [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.hybrid.EciesAeadHkdfPublicKeyManagerTest');
goog.setTestOnly('tink.hybrid.EciesAeadHkdfPublicKeyManagerTest');
const AeadConfig = goog.require('tink.aead.AeadConfig');
const AeadKeyTemplates = goog.require('tink.aead.AeadKeyTemplates');
const Bytes = goog.require('google3.third_party.tink.javascript.subtle.bytes');
const EciesAeadHkdfPublicKeyManager = goog.require('tink.hybrid.EciesAeadHkdfPublicKeyManager');
const EllipticCurves = goog.require('google3.third_party.tink.javascript.subtle.elliptic_curves');
const {HybridEncrypt} = goog.require('google3.third_party.tink.javascript.hybrid.internal.hybrid_encrypt');
const {Mac} = goog.require('google3.third_party.tink.javascript.mac.internal.mac');
const Random = goog.require('google3.third_party.tink.javascript.subtle.random');
const Registry = goog.require('google3.third_party.tink.javascript.internal.registry');
const Util = goog.require('google3.third_party.tink.javascript.internal.util');
const {PbAesCtrKey, PbEciesAeadDemParams, PbEciesAeadHkdfParams, PbEciesAeadHkdfPublicKey, PbEciesHkdfKemParams, PbEllipticCurveType, PbHashType, PbKeyData, PbKeyTemplate, PbPointFormat} = goog.require('google3.third_party.tink.javascript.internal.proto');
const {assertExists} = goog.require('google3.third_party.tink.javascript.testing.internal.test_utils');
const KEY_TYPE =
'type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey';
const VERSION = 0;
const PRIMITIVE = HybridEncrypt;
describe('ecies aead hkdf public key manager test', function() {
beforeEach(function() {
AeadConfig.register();
// 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 EciesAeadHkdfPublicKeyManager();
try {
manager.getKeyFactory().newKey(new Uint8Array(0));
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.notSupported());
}
});
it('new key data', function() {
const manager = new EciesAeadHkdfPublicKeyManager();
try {
manager.getKeyFactory().newKeyData(new Uint8Array(0));
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.notSupported());
}
});
it('get primitive, unsupported primitive type', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
const key = await createKey();
try {
await manager.getPrimitive(Mac, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.unsupportedPrimitive());
}
});
it('get primitive, unsupported key data type', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
const keyData =
(await createKeyData()).setTypeUrl('unsupported_key_type_url');
try {
await manager.getPrimitive(PRIMITIVE, keyData);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString())
.toBe(ExceptionText.unsupportedKeyType(keyData.getTypeUrl()));
}
});
it('get primitive, unsupported key type', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
let key = new PbAesCtrKey();
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeyType());
}
});
it('get primitive, high version', async function() {
const version = 1;
const manager = new EciesAeadHkdfPublicKeyManager();
const key = (await createKey()).setVersion(version);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.versionOutOfBounds());
}
});
it('get primitive, missing params', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
const key = (await createKey()).setParams(null);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.missingParams());
}
});
it('get primitive, invalid params', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
const key = await createKey();
// unknown point format
key.getParams().setEcPointFormat(PbPointFormat.UNKNOWN_FORMAT);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.unknownPointFormat());
}
key.getParams().setEcPointFormat(PbPointFormat.UNCOMPRESSED);
// missing KEM params
key.getParams().setKemParams(null);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.missingKemParams());
}
key.getParams().setKemParams(createKemParams());
// unsupported AEAD key template
const typeUrl = 'UNSUPPORTED_KEY_TYPE_URL';
key.getParams().getDemParams().getAeadDem().setTypeUrl(typeUrl);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeyTemplate(typeUrl));
}
});
it('get primitive, invalid key', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
const key = (await createKey()).setX('');
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.missingXY());
}
key.getParams().setEcPointFormat(PbPointFormat.UNCOMPRESSED);
// missing KEM params
key.getParams().setKemParams(null);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.missingKemParams());
}
key.getParams().setKemParams(createKemParams());
// unsupported AEAD key template
const typeUrl = 'UNSUPPORTED_KEY_TYPE_URL';
key.getParams().getDemParams().getAeadDem().setTypeUrl(typeUrl);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeyTemplate(typeUrl));
}
});
it('get primitive, invalid serialized key', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
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) {
expect(e.toString()).toBe(ExceptionText.invalidSerializedKey());
}
}
});
// tests for getting primitive from valid key/keyData
it('get primitive, from key', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
const keys = await createTestSetOfKeys();
for (let key of keys) {
const /** !HybridEncrypt */ primitive =
assertExists(await manager.getPrimitive(PRIMITIVE, key));
const plaintext = Random.randBytes(10);
const ciphertext = await primitive.encrypt(plaintext);
expect(ciphertext).not.toEqual(plaintext);
}
});
it('get primitive, from key data', async function() {
const manager = new EciesAeadHkdfPublicKeyManager();
const keyDatas = await createTestSetOfKeyDatas();
for (let key of keyDatas) {
const /** !HybridEncrypt */ primitive =
assertExists(await manager.getPrimitive(PRIMITIVE, key));
const plaintext = Random.randBytes(10);
const ciphertext = await primitive.encrypt(plaintext);
expect(ciphertext).not.toEqual(plaintext);
}
});
it('does support', function() {
const manager = new EciesAeadHkdfPublicKeyManager();
expect(manager.doesSupport(KEY_TYPE)).toBe(true);
});
it('get key type', function() {
const manager = new EciesAeadHkdfPublicKeyManager();
expect(manager.getKeyType()).toBe(KEY_TYPE);
});
it('get primitive type', function() {
const manager = new EciesAeadHkdfPublicKeyManager();
expect(manager.getPrimitiveType()).toBe(PRIMITIVE);
});
it('get version', function() {
const manager = new EciesAeadHkdfPublicKeyManager();
expect(manager.getVersion()).toBe(VERSION);
});
});
// Helper classes and functions
class ExceptionText {
/** @return {string} */
static notSupported() {
return 'SecurityException: This operation is not supported for public keys. ' +
'Use EciesAeadHkdfPrivateKeyManager to generate new keys.';
}
/** @return {string} */
static unsupportedPrimitive() {
return 'SecurityException: Requested primitive type which is not supported by ' +
'this key manager.';
}
/**
* @param {string=} opt_requestedKeyType
* @return {string}
*/
static unsupportedKeyType(opt_requestedKeyType) {
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;
}
}
/** @return {string} */
static versionOutOfBounds() {
return 'SecurityException: Version is out of bound, must be between 0 and ' +
VERSION + '.';
}
/** @return {string} */
static missingParams() {
return 'SecurityException: Invalid public key - missing key params.';
}
/** @return {string} */
static unknownPointFormat() {
return 'SecurityException: Invalid key params - unknown EC point format.';
}
/** @return {string} */
static missingKemParams() {
return 'SecurityException: Invalid params - missing KEM params.';
}
/**
* @param {string} templateTypeUrl
* @return {string}
*/
static unsupportedKeyTemplate(templateTypeUrl) {
return 'SecurityException: Invalid DEM params - ' + templateTypeUrl +
' template is not supported by ECIES AEAD HKDF.';
}
/** @return {string} */
static missingXY() {
return 'SecurityException: Invalid public key - missing value of X or Y.';
}
/** @return {string} */
static invalidSerializedKey() {
return 'SecurityException: Input cannot be parsed as ' + KEY_TYPE +
' key-proto.';
}
}
/**
* @param {!PbEllipticCurveType=} opt_curveType (default: NIST_P256)
* @param {!PbHashType=} opt_hashType (default: SHA256)
*
* @return {!PbEciesHkdfKemParams}
*/
const createKemParams = function(
opt_curveType = PbEllipticCurveType.NIST_P256,
opt_hashType = PbHashType.SHA256) {
const kemParams = new PbEciesHkdfKemParams()
.setCurveType(opt_curveType)
.setHkdfHashType(opt_hashType);
return kemParams;
};
/**
* @param {!PbKeyTemplate=} opt_keyTemplate (default: aes128CtrHmac256)
*
* @return {!PbEciesAeadDemParams}
*/
const createDemParams = function(opt_keyTemplate) {
if (!opt_keyTemplate) {
opt_keyTemplate = AeadKeyTemplates.aes128CtrHmacSha256();
}
const demParams = new PbEciesAeadDemParams().setAeadDem(opt_keyTemplate);
return demParams;
};
/**
* @param {!PbEllipticCurveType=} opt_curveType (default: NIST_P256)
* @param {!PbHashType=} opt_hashType (default: SHA256)
* @param {!PbKeyTemplate=} opt_keyTemplate (default: aes128CtrHmac256)
* @param {!PbPointFormat=} opt_pointFormat (default: UNCOMPRESSED)
*
* @return {!PbEciesAeadHkdfParams}
*/
const createKeyParams = function(
opt_curveType, opt_hashType, opt_keyTemplate,
opt_pointFormat = PbPointFormat.UNCOMPRESSED) {
const params = new PbEciesAeadHkdfParams()
.setKemParams(createKemParams(opt_curveType, opt_hashType))
.setDemParams(createDemParams(opt_keyTemplate))
.setEcPointFormat(opt_pointFormat);
return params;
};
/**
* @param {!PbEllipticCurveType=} opt_curveType (default: NIST_P256)
* @param {!PbHashType=} opt_hashType (default: SHA256)
* @param {!PbKeyTemplate=} opt_keyTemplate (default: aes128CtrHmac256)
* @param {!PbPointFormat=} opt_pointFormat (default: UNCOMPRESSED)
*
* @return {!Promise<!PbEciesAeadHkdfPublicKey>}
*/
const createKey = async function(
opt_curveType = PbEllipticCurveType.NIST_P256, opt_hashType,
opt_keyTemplate, opt_pointFormat) {
const curveSubtleType = Util.curveTypeProtoToSubtle(opt_curveType);
const curveName = EllipticCurves.curveToString(curveSubtleType);
const key =
new PbEciesAeadHkdfPublicKey().setVersion(0).setParams(createKeyParams(
opt_curveType, opt_hashType, opt_keyTemplate, opt_pointFormat));
const keyPair = await EllipticCurves.generateKeyPair('ECDH', curveName);
const publicKey = await EllipticCurves.exportCryptoKey(keyPair.publicKey);
key.setX(Bytes.fromBase64(publicKey['x'], /* opt_webSafe = */ true));
key.setY(Bytes.fromBase64(publicKey['y'], /* opt_webSafe = */ true));
return key;
};
/**
* @param {!PbEciesAeadHkdfPublicKey} key
*
* @return {!PbKeyData}
*/
const createKeyDataFromKey = function(key) {
const keyData =
new PbKeyData()
.setTypeUrl(KEY_TYPE)
.setValue(key.serializeBinary())
.setKeyMaterialType(PbKeyData.KeyMaterialType.ASYMMETRIC_PUBLIC);
return keyData;
};
/**
* @param {!PbEllipticCurveType=} opt_curveType (default: NIST_P256)
* @param {!PbHashType=} opt_hashType (default: SHA256)
* @param {!PbKeyTemplate=} opt_keyTemplate (default: aes128CtrHmac256)
* @param {!PbPointFormat=} opt_pointFormat (default: UNCOMPRESSED)
*
* @return {!Promise<!PbKeyData>}
*/
const createKeyData = async function(
opt_curveType, opt_hashType, opt_keyTemplate, opt_pointFormat) {
const key = await createKey(
opt_curveType, opt_hashType, opt_keyTemplate, opt_pointFormat);
return createKeyDataFromKey(key);
};
// Create set of keys with all possible predefined/supported parameters.
/** @return {!Promise<!Array<!PbEciesAeadHkdfPublicKey>>} */
const createTestSetOfKeys = async function() {
const curveTypes = [
PbEllipticCurveType.NIST_P256, PbEllipticCurveType.NIST_P384,
PbEllipticCurveType.NIST_P521
];
const hashTypes = [PbHashType.SHA1, PbHashType.SHA256, PbHashType.SHA512];
const keyTemplates =
[AeadKeyTemplates.aes128CtrHmacSha256(), AeadKeyTemplates.aes256Gcm()];
const pointFormats = [PbPointFormat.UNCOMPRESSED];
const /** !Array<!PbEciesAeadHkdfPublicKey> */ keys = [];
for (let curve of curveTypes) {
for (let hkdfHash of hashTypes) {
for (let keyTemplate of keyTemplates) {
for (let pointFormat of pointFormats) {
const key =
await createKey(curve, hkdfHash, keyTemplate, pointFormat);
keys.push(key);
}
}
}
}
return keys;
};
// Create set of keyData protos with keys of all possible predefined/supported
// parameters.
/** @return {!Promise<!Array<!PbKeyData>>} */
const createTestSetOfKeyDatas = async function() {
const keys = await createTestSetOfKeys();
const /** !Array<!PbKeyData> */ keyDatas = [];
for (let key of keys) {
const keyData = await createKeyDataFromKey(key);
keyDatas.push(keyData);
}
return keyDatas;
};