| // 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; |