blob: fb2b9f179acee7f36c8c0881b308d0081090adad [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.EciesAeadHkdfUtilTest');
goog.setTestOnly('tink.hybrid.EciesAeadHkdfUtilTest');
const AeadKeyTemplates = goog.require('tink.aead.AeadKeyTemplates');
const Bytes = goog.require('tink.subtle.Bytes');
const EciesAeadHkdfUtil = goog.require('tink.hybrid.EciesAeadHkdfUtil');
const EllipticCurves = goog.require('tink.subtle.EllipticCurves');
const PbEciesAeadDemParams = goog.require('proto.google.crypto.tink.EciesAeadDemParams');
const PbEciesAeadHkdfParams = goog.require('proto.google.crypto.tink.EciesAeadHkdfParams');
const PbEciesAeadHkdfPrivateKey = goog.require('proto.google.crypto.tink.EciesAeadHkdfPrivateKey');
const PbEciesAeadHkdfPublicKey = goog.require('proto.google.crypto.tink.EciesAeadHkdfPublicKey');
const PbEciesHkdfKemParams = goog.require('proto.google.crypto.tink.EciesHkdfKemParams');
const PbEllipticCurveType = goog.require('proto.google.crypto.tink.EllipticCurveType');
const PbHashType = goog.require('proto.google.crypto.tink.HashType');
const PbKeyTemplate = goog.require('proto.google.crypto.tink.KeyTemplate');
const PbPointFormat = goog.require('proto.google.crypto.tink.EcPointFormat');
const TestCase = goog.require('goog.testing.TestCase');
const Util = goog.require('tink.Util');
const testSuite = goog.require('goog.testing.testSuite');
const userAgent = goog.require('goog.userAgent');
testSuite({
shouldRunTests() {
return !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 testGetJsonWebKeyFromProto_publicKey() {
const curves = Object.keys(PbEllipticCurveType);
for (let curveId of curves) {
const curve = PbEllipticCurveType[curveId];
if (curve === PbEllipticCurveType.UNKNOWN_CURVE ||
curve === PbEllipticCurveType.CURVE25519) {
continue;
}
const key = await createKey(curve);
const jwk = EciesAeadHkdfUtil.getJsonWebKeyFromProto(key.getPublicKey());
// Test the returned jwk.
const curveTypeSubtle = Util.curveTypeProtoToSubtle(curve);
const curveTypeString = EllipticCurves.curveToString(curveTypeSubtle);
assertEquals('EC', jwk['kty']);
assertEquals(curveTypeString, jwk['crv']);
assertObjectEquals(
key.getPublicKey().getX_asU8(),
Bytes.fromBase64(jwk['x'], /* opt_webSafe = */ true));
assertObjectEquals(
key.getPublicKey().getY_asU8(),
Bytes.fromBase64(jwk['y'], /* opt_webSafe = */ true));
assertObjectEquals(undefined, jwk['d']);
assertTrue(jwk['ext']);
}
},
async testGetJsonWebKeyFromProto_publicKey_withLeadingZeros() {
const curves = Object.keys(PbEllipticCurveType);
for (let curveId of curves) {
const curve = PbEllipticCurveType[curveId];
if (curve === PbEllipticCurveType.UNKNOWN_CURVE ||
curve === PbEllipticCurveType.CURVE25519) {
continue;
}
const key = await createKey(curve);
// Add leading zeros to x and y value of key.
const x = key.getPublicKey().getX();
const y = key.getPublicKey().getY();
key.getPublicKey().setX(Bytes.concat(new Uint8Array([0, 0, 0, 0, 0]), x));
key.getPublicKey().setY(Bytes.concat(new Uint8Array([0, 0, 0]), y));
const jwk = EciesAeadHkdfUtil.getJsonWebKeyFromProto(key.getPublicKey());
// Test the returned jwk.
const curveTypeSubtle = Util.curveTypeProtoToSubtle(curve);
const curveTypeString = EllipticCurves.curveToString(curveTypeSubtle);
assertEquals('EC', jwk['kty']);
assertEquals(curveTypeString, jwk['crv']);
assertObjectEquals(
x, Bytes.fromBase64(jwk['x'], /* opt_webSafe = */ true));
assertObjectEquals(
y, Bytes.fromBase64(jwk['y'], /* opt_webSafe = */ true));
assertObjectEquals(undefined, jwk['d']);
assertTrue(jwk['ext']);
}
},
async testGetJsonWebKeyFromProto_publicKey_leadingNonzero() {
const curve = PbEllipticCurveType.NIST_P256;
const key = await createKey(curve);
const x = key.getPublicKey().getX();
key.getPublicKey().setX(Bytes.concat(new Uint8Array([1, 0]), x));
try {
EciesAeadHkdfUtil.getJsonWebKeyFromProto(key.getPublicKey());
fail('An exception should be thrown.');
} catch (e) {
assertEquals(
'CustomError: Number needs more bytes to be represented.',
e.toString());
}
},
async testGetJsonWebKeyFromProto_privateKey() {
const curves = Object.keys(PbEllipticCurveType);
for (let curveId of curves) {
const curve = PbEllipticCurveType[curveId];
if (curve === PbEllipticCurveType.UNKNOWN_CURVE ||
curve === PbEllipticCurveType.CURVE25519) {
continue;
}
const key = await createKey(curve);
const jwk = EciesAeadHkdfUtil.getJsonWebKeyFromProto(key);
// Test the returned jwk.
const curveTypeSubtle = Util.curveTypeProtoToSubtle(curve);
const curveTypeString = EllipticCurves.curveToString(curveTypeSubtle);
assertEquals('EC', jwk['kty']);
assertEquals(curveTypeString, jwk['crv']);
assertObjectEquals(
key.getPublicKey().getX_asU8(),
Bytes.fromBase64(jwk['x'], /* opt_webSafe = */ true));
assertObjectEquals(
key.getPublicKey().getY_asU8(),
Bytes.fromBase64(jwk['y'], /* opt_webSafe = */ true));
assertObjectEquals(
key.getKeyValue_asU8(),
Bytes.fromBase64(jwk['d'], /* opt_webSafe = */ true));
assertTrue(jwk['ext']);
}
},
async testGetJsonWebKeyFromProto_privateKey_leadingZeros() {
const curves = Object.keys(PbEllipticCurveType);
for (let curveId of curves) {
const curve = PbEllipticCurveType[curveId];
if (curve === PbEllipticCurveType.UNKNOWN_CURVE ||
curve === PbEllipticCurveType.CURVE25519) {
continue;
}
const key = await createKey(curve);
const d = key.getKeyValue_asU8();
key.setKeyValue(Bytes.concat(new Uint8Array([0, 0, 0]), d));
const jwk = EciesAeadHkdfUtil.getJsonWebKeyFromProto(key);
// Test the returned jwk.
const curveTypeSubtle = Util.curveTypeProtoToSubtle(curve);
const curveTypeString = EllipticCurves.curveToString(curveTypeSubtle);
assertEquals('EC', jwk['kty']);
assertEquals(curveTypeString, jwk['crv']);
assertObjectEquals(
key.getPublicKey().getX_asU8(),
Bytes.fromBase64(jwk['x'], /* opt_webSafe = */ true));
assertObjectEquals(
key.getPublicKey().getY_asU8(),
Bytes.fromBase64(jwk['y'], /* opt_webSafe = */ true));
assertObjectEquals(
d, Bytes.fromBase64(jwk['d'], /* opt_webSafe = */ true));
assertTrue(jwk['ext']);
}
},
});
/**
* @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<!PbEciesAeadHkdfPrivateKey>}
*/
const createKey = async function(
opt_curveType = PbEllipticCurveType.NIST_P256, opt_hashType,
opt_keyTemplate, opt_pointFormat) {
const curveTypeSubtle = Util.curveTypeProtoToSubtle(
/** @type {!PbEllipticCurveType} */ (opt_curveType));
const curveName = EllipticCurves.curveToString(curveTypeSubtle);
const publicKeyProto =
new PbEciesAeadHkdfPublicKey().setVersion(0).setParams(createKeyParams(
opt_curveType, opt_hashType, opt_keyTemplate, opt_pointFormat));
const keyPair = await EllipticCurves.generateKeyPair('ECDH', curveName);
const publicKeyJson = await EllipticCurves.exportCryptoKey(keyPair.publicKey);
publicKeyProto.setX(
Bytes.fromBase64(publicKeyJson['x'], /* opt_webSafe = */ true));
publicKeyProto.setY(
Bytes.fromBase64(publicKeyJson['y'], /* opt_webSafe = */ true));
const privateKeyProto = new PbEciesAeadHkdfPrivateKey();
const privateKeyJson =
await EllipticCurves.exportCryptoKey(keyPair.privateKey);
privateKeyProto.setKeyValue(
Bytes.fromBase64(privateKeyJson['d'], /* opt_webSafe = */ true));
privateKeyProto.setVersion(0);
privateKeyProto.setPublicKey(publicKeyProto);
return privateKeyProto;
};