blob: b61c055b5203d0a6492ab87b4880bd5e50beb5be [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.EciesAeadHkdfPrivateKeyManager');
const Bytes = goog.require('tink.subtle.Bytes');
const EciesAeadHkdfHybridDecrypt = goog.require('tink.subtle.EciesAeadHkdfHybridDecrypt');
const EciesAeadHkdfPublicKeyManager = goog.require('tink.hybrid.EciesAeadHkdfPublicKeyManager');
const EciesAeadHkdfUtil = goog.require('tink.hybrid.EciesAeadHkdfUtil');
const EciesAeadHkdfValidators = goog.require('tink.hybrid.EciesAeadHkdfValidators');
const EllipticCurves = goog.require('tink.subtle.EllipticCurves');
const HybridDecrypt = goog.require('tink.HybridDecrypt');
const KeyManager = goog.require('tink.KeyManager');
const PbEciesAeadHkdfKeyFormat = goog.require('proto.google.crypto.tink.EciesAeadHkdfKeyFormat');
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 PbKeyData = goog.require('proto.google.crypto.tink.KeyData');
const PbKeyTemplate = goog.require('proto.google.crypto.tink.KeyTemplate');
const PbMessage = goog.require('jspb.Message');
const RegistryEciesAeadHkdfDemHelper = goog.require('tink.hybrid.RegistryEciesAeadHkdfDemHelper');
const SecurityException = goog.require('tink.exception.SecurityException');
const Util = goog.require('tink.Util');
/**
* @implements {KeyManager.PrivateKeyFactory}
* @final
*/
class EciesAeadHkdfPrivateKeyFactory {
/**
* @override
* @return {!Promise<!PbEciesAeadHkdfPrivateKey>}
*/
async newKey(keyFormat) {
if (!keyFormat) {
throw new SecurityException('Key format has to be non-null.');
}
const keyFormatProto =
EciesAeadHkdfPrivateKeyFactory.getKeyFormatProto_(keyFormat);
EciesAeadHkdfValidators.validateKeyFormat(keyFormatProto);
return await EciesAeadHkdfPrivateKeyFactory.newKeyImpl_(keyFormatProto);
}
/**
* @override
* @return {!Promise<!PbKeyData>}
*/
async newKeyData(serializedKeyFormat) {
const key = await this.newKey(serializedKeyFormat);
const keyData =
new PbKeyData()
.setTypeUrl(EciesAeadHkdfPrivateKeyManager.KEY_TYPE)
.setValue(key.serializeBinary())
.setKeyMaterialType(PbKeyData.KeyMaterialType.ASYMMETRIC_PRIVATE);
return keyData;
}
/** @override */
getPublicKeyData(serializedPrivateKey) {
const privateKey = EciesAeadHkdfPrivateKeyManager.deserializePrivateKey_(
serializedPrivateKey);
const publicKeyData =
new PbKeyData()
.setValue(privateKey.getPublicKey().serializeBinary())
.setTypeUrl(EciesAeadHkdfPublicKeyManager.KEY_TYPE)
.setKeyMaterialType(PbKeyData.KeyMaterialType.ASYMMETRIC_PUBLIC);
return publicKeyData;
}
/**
* Generates key corresponding to the given key format.
* WARNING: This function assume that the keyFormat has been validated.
*
* @private
* @param {!PbEciesAeadHkdfKeyFormat} keyFormat
* @return {!Promise<!PbEciesAeadHkdfPrivateKey>}
*/
static async newKeyImpl_(keyFormat) {
const params =
/** @type {!PbEciesAeadHkdfParams} */ (keyFormat.getParams());
const curveTypeProto = params.getKemParams().getCurveType();
const curveTypeSubtle = Util.curveTypeProtoToSubtle(curveTypeProto);
const curveName = EllipticCurves.curveToString(curveTypeSubtle);
const keyPair = await EllipticCurves.generateKeyPair('ECDH', curveName);
const jsonPublicKey =
await EllipticCurves.exportCryptoKey(/** @type {?} */ (keyPair).publicKey);
const jsonPrivateKey =
await EllipticCurves.exportCryptoKey(/** @type {?} */ (keyPair).privateKey);
return EciesAeadHkdfPrivateKeyFactory.jsonToProtoKey_(
jsonPrivateKey, jsonPublicKey, params);
}
/**
* Creates a private key proto corresponding to given JSON key pair and with
* the given params.
*
* @private
* @param {!webCrypto.JsonWebKey} jsonPrivateKey
* @param {!webCrypto.JsonWebKey} jsonPublicKey
* @param {!PbEciesAeadHkdfParams} params
* @return {!PbEciesAeadHkdfPrivateKey}
*/
static jsonToProtoKey_(jsonPrivateKey, jsonPublicKey, params) {
const publicKeyProto =
new PbEciesAeadHkdfPublicKey()
.setVersion(EciesAeadHkdfPublicKeyManager.VERSION)
.setParams(params)
.setX(Bytes.fromBase64(jsonPublicKey['x'], true))
.setY(Bytes.fromBase64(jsonPublicKey['y'], true));
const privateKeyProto =
new PbEciesAeadHkdfPrivateKey()
.setVersion(EciesAeadHkdfPrivateKeyManager.VERSION_)
.setPublicKey(publicKeyProto)
.setKeyValue(Bytes.fromBase64(jsonPrivateKey['d'], true));
return privateKeyProto;
}
/**
* The input keyFormat is either deserialized (in case that the input is
* Uint8Array) or checked to be an EciesAeadHkdfKeyFormat-proto (otherwise).
*
* @private
* @param {!PbMessage|!Uint8Array} keyFormat
* @return {!PbEciesAeadHkdfKeyFormat}
*/
static getKeyFormatProto_(keyFormat) {
if (keyFormat instanceof Uint8Array) {
return EciesAeadHkdfPrivateKeyFactory.deserializeKeyFormat_(keyFormat);
} else {
if (keyFormat instanceof PbEciesAeadHkdfKeyFormat) {
return keyFormat;
} else {
throw new SecurityException(
'Expected ' + EciesAeadHkdfPrivateKeyManager.KEY_TYPE +
' key format proto.');
}
}
}
/**
* @private
* @param {!Uint8Array} keyFormat
* @return {!PbEciesAeadHkdfKeyFormat}
*/
static deserializeKeyFormat_(keyFormat) {
let /** !PbEciesAeadHkdfKeyFormat */ keyFormatProto;
try {
keyFormatProto = PbEciesAeadHkdfKeyFormat.deserializeBinary(keyFormat);
} catch (e) {
throw new SecurityException(
'Input cannot be parsed as ' +
EciesAeadHkdfPrivateKeyManager.KEY_TYPE + ' key format proto.');
}
if (!keyFormatProto.getParams()) {
throw new SecurityException(
'Input cannot be parsed as ' +
EciesAeadHkdfPrivateKeyManager.KEY_TYPE + ' key format proto.');
}
return keyFormatProto;
}
}
/**
* @implements {KeyManager.KeyManager<HybridDecrypt>}
* @final
*/
class EciesAeadHkdfPrivateKeyManager {
constructor() {
this.keyFactory = new EciesAeadHkdfPrivateKeyFactory();
}
/** @override */
async getPrimitive(primitiveType, key) {
if (primitiveType !== this.getPrimitiveType()) {
throw new SecurityException(
'Requested primitive type which is not ' +
'supported by this key manager.');
}
const keyProto = EciesAeadHkdfPrivateKeyManager.getKeyProto_(key);
EciesAeadHkdfValidators.validatePrivateKey(
keyProto, EciesAeadHkdfPrivateKeyManager.VERSION_,
EciesAeadHkdfPublicKeyManager.VERSION);
const recepientPrivateKey = EciesAeadHkdfUtil.getJsonWebKeyFromProto(keyProto);
const params = /** @type {!PbEciesAeadHkdfParams} */ (
keyProto.getPublicKey().getParams());
const keyTemplate =
/** @type {!PbKeyTemplate} */ (params.getDemParams().getAeadDem());
const demHelper = new RegistryEciesAeadHkdfDemHelper(keyTemplate);
const pointFormat =
Util.pointFormatProtoToSubtle(params.getEcPointFormat());
const hkdfHash =
Util.hashTypeProtoToString(params.getKemParams().getHkdfHashType());
const hkdfSalt = params.getKemParams().getHkdfSalt_asU8();
return await EciesAeadHkdfHybridDecrypt.newInstance(
recepientPrivateKey, hkdfHash, pointFormat, demHelper, hkdfSalt);
}
/** @override */
doesSupport(keyType) {
return keyType === this.getKeyType();
}
/** @override */
getKeyType() {
return EciesAeadHkdfPrivateKeyManager.KEY_TYPE;
}
/** @override */
getPrimitiveType() {
return EciesAeadHkdfPrivateKeyManager.SUPPORTED_PRIMITIVE_;
}
/** @override */
getVersion() {
return EciesAeadHkdfPrivateKeyManager.VERSION_;
}
/** @override */
getKeyFactory() {
return this.keyFactory;
}
/**
* @private
* @param {!PbKeyData|!PbMessage} keyMaterial
* @return {!PbEciesAeadHkdfPrivateKey}
*/
static getKeyProto_(keyMaterial) {
if (keyMaterial instanceof PbKeyData) {
return EciesAeadHkdfPrivateKeyManager.getKeyProtoFromKeyData_(
keyMaterial);
}
if (keyMaterial instanceof PbEciesAeadHkdfPrivateKey) {
return keyMaterial;
}
throw new SecurityException(
'Key type is not supported. This key ' +
'manager supports ' + EciesAeadHkdfPrivateKeyManager.KEY_TYPE + '.');
}
/**
* @private
* @param {!PbKeyData} keyData
* @return {!PbEciesAeadHkdfPrivateKey}
*/
static getKeyProtoFromKeyData_(keyData) {
if (keyData.getTypeUrl() !== EciesAeadHkdfPrivateKeyManager.KEY_TYPE) {
throw new SecurityException(
'Key type ' + keyData.getTypeUrl() +
' is not supported. This key manager supports ' +
EciesAeadHkdfPrivateKeyManager.KEY_TYPE + '.');
}
return EciesAeadHkdfPrivateKeyManager.deserializePrivateKey_(
keyData.getValue_asU8());
}
/**
* @private
* @param {!Uint8Array} serializedPrivateKey
* @return {!PbEciesAeadHkdfPrivateKey}
*/
static deserializePrivateKey_(serializedPrivateKey) {
let /** PbEciesAeadHkdfPrivateKey */ key;
try {
key = PbEciesAeadHkdfPrivateKey.deserializeBinary(serializedPrivateKey);
} catch (e) {
throw new SecurityException(
'Input cannot be parsed as ' +
EciesAeadHkdfPrivateKeyManager.KEY_TYPE + ' key-proto.');
}
if (!key.getPublicKey() || !key.getKeyValue()) {
throw new SecurityException(
'Input cannot be parsed as ' +
EciesAeadHkdfPrivateKeyManager.KEY_TYPE + ' key-proto.');
}
return key;
}
}
/** @const @private {!Object} */
EciesAeadHkdfPrivateKeyManager.SUPPORTED_PRIMITIVE_ = HybridDecrypt;
/** @const @public {string} */
EciesAeadHkdfPrivateKeyManager.KEY_TYPE =
'type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey';
/** @const @private {number} */
EciesAeadHkdfPrivateKeyManager.VERSION_ = 0;
exports = EciesAeadHkdfPrivateKeyManager;