| // 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.Registry'); |
| |
| const KeyManager = goog.require('tink.KeyManager'); |
| 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 PrimitiveSet = goog.require('tink.PrimitiveSet'); |
| const PrimitiveWrapper = goog.require('tink.PrimitiveWrapper'); |
| const SecurityException = goog.require('tink.exception.SecurityException'); |
| |
| /** |
| * Registry for KeyManagers. |
| * |
| * Registry maps supported key types to corresponding KeyManager objects (i.e. |
| * the KeyManagers which may instantiate the primitive corresponding to the |
| * given key or generate new key of the given type). Keeping KeyManagers for all |
| * primitives in a single Registry (rather than having a separate keyManager per |
| * primitive) enables modular construction of compound primitives from "simple" |
| * ones (e.g. AES-CTR-HMAC AEAD encryption from IND-CPA encryption and MAC). |
| * |
| * Regular users will not usually work with Registry directly, but via primitive |
| * factories, which query Registry for the specific KeyManagers in the |
| * background. |
| * |
| * @final |
| */ |
| class Registry { |
| /** |
| * Register the given manager for the given key type. Manager must be |
| * non-nullptr. New keys are allowed if not specified. |
| * |
| * @template P |
| * @static |
| * |
| * @param {!KeyManager.KeyManager<P>} manager |
| * @param {boolean=} opt_newKeyAllowed |
| */ |
| static registerKeyManager(manager, opt_newKeyAllowed) { |
| if (opt_newKeyAllowed === undefined) { |
| opt_newKeyAllowed = true; |
| } |
| if (!manager) { |
| throw new SecurityException('Key manager cannot be null.'); |
| } |
| const typeUrl = manager.getKeyType(); |
| |
| if (Registry.typeToManagerMap_.has(typeUrl)) { |
| // Cannot overwrite the existing key manager by a new one. |
| if (!(Registry.typeToManagerMap_.get(typeUrl) instanceof |
| manager.constructor)) { |
| throw new SecurityException( |
| 'Key manager for key type ' + typeUrl + |
| ' has already been registered and cannot be overwritten.'); |
| } |
| |
| // It is forbidden to change new_key_allowed from false to true. |
| if (!(Registry.typeToNewKeyAllowedMap_.get(typeUrl)) && |
| opt_newKeyAllowed) { |
| throw new SecurityException( |
| 'Key manager for key type ' + typeUrl + |
| ' has already been registered with forbidden new key operation.'); |
| } |
| Registry.typeToNewKeyAllowedMap_.set(typeUrl, opt_newKeyAllowed); |
| } |
| |
| Registry.typeToManagerMap_.set(typeUrl, manager); |
| Registry.typeToNewKeyAllowedMap_.set(typeUrl, opt_newKeyAllowed); |
| } |
| |
| /** |
| * Returns a key manager for the given key type or throws an exception if no |
| * such manager found. |
| * |
| * @template P |
| * @static |
| * |
| * @param {string} typeUrl -- key type |
| * |
| * @return {!KeyManager.KeyManager<P>} |
| */ |
| static getKeyManager(typeUrl) { |
| const res = Registry.typeToManagerMap_.get(typeUrl); |
| if (!res) { |
| throw new SecurityException( |
| 'Key manager for key type ' + typeUrl + ' has not been registered.'); |
| } |
| return res; |
| } |
| |
| /** |
| * It finds KeyManager according to key type (which is either given by |
| * PbKeyData or given by opt_typeUrl), than calls the corresponding |
| * manager's getPrimitive method. |
| * |
| * Either key is of type PbKeyData or opt_typeUrl must be provided. |
| * |
| * @template P |
| * @static |
| * |
| * @param {!Object} primitiveType |
| * @param {!PbKeyData|!PbMessage} key -- key is either a proto of some key |
| * or key data. |
| * @param {?string=} opt_typeUrl -- key type |
| * |
| * @return {!Promise.<!P>} |
| */ |
| static async getPrimitive(primitiveType, key, opt_typeUrl) { |
| if (key instanceof PbKeyData) { |
| if (opt_typeUrl && key.getTypeUrl() != opt_typeUrl) { |
| throw new SecurityException( |
| 'Key type is ' + opt_typeUrl + ', but it is expected to be ' + |
| key.getTypeUrl() + ' or undefined.'); |
| } |
| opt_typeUrl = key.getTypeUrl(); |
| } |
| |
| if (!opt_typeUrl) { |
| throw new SecurityException('Key type has to be specified.'); |
| } |
| |
| const manager = Registry.getKeyManager(opt_typeUrl); |
| return await manager.getPrimitive(primitiveType, key); |
| } |
| |
| /** |
| * Generates a new PbKeyData for the specified keyTemplate. It finds a |
| * KeyManager given by keyTemplate.typeUrl and calls the newKeyData method of |
| * that manager. |
| * |
| * @static |
| * |
| * @param {!PbKeyTemplate} keyTemplate |
| * |
| * @return {!Promise<!PbKeyData>} |
| */ |
| static async newKeyData(keyTemplate) { |
| const manager = Registry.getKeyManagerWithNewKeyAllowedCheck_(keyTemplate); |
| return await manager.getKeyFactory().newKeyData( |
| keyTemplate.getValue_asU8()); |
| } |
| |
| /** |
| * Generates a new key for the specified keyTemplate using the |
| * KeyManager determined by typeUrl field of the keyTemplate. |
| * |
| * @static |
| * |
| * @param {!PbKeyTemplate} keyTemplate |
| * |
| * @return {!Promise<!PbMessage>} returns a key proto |
| */ |
| static async newKey(keyTemplate) { |
| const manager = Registry.getKeyManagerWithNewKeyAllowedCheck_(keyTemplate); |
| return await manager.getKeyFactory().newKey(keyTemplate.getValue_asU8()); |
| } |
| |
| /** |
| * Convenience method for extracting the public key data from the private key |
| * given by serializedPrivateKey. |
| * It looks up a KeyManager identified by typeUrl, which must hold |
| * PrivateKeyFactory, and calls getPublicKeyData method of that factory. |
| * |
| * @param {string} typeUrl |
| * @param {!Uint8Array} serializedPrivateKey |
| * @return {!PbKeyData} |
| */ |
| static getPublicKeyData(typeUrl, serializedPrivateKey) { |
| const manager = Registry.getKeyManager(typeUrl); |
| // This solution might cause some problems in the future due to Closure |
| // compiler optimizations, which may map factory.getPublicKeyData to |
| // concrete function. |
| const factory = /** @type{?} */ (manager.getKeyFactory()); |
| if (!factory.getPublicKeyData) { |
| throw new SecurityException( |
| 'Key manager for key type ' + typeUrl + |
| ' does not have a private key factory.'); |
| } |
| return factory.getPublicKeyData(serializedPrivateKey); |
| } |
| |
| /** |
| * Resets the registry. |
| * After reset the registry is empty, i.e. it contains no key managers. |
| * |
| * This method is only for testing. |
| * |
| * @static |
| */ |
| static reset() { |
| Registry.typeToManagerMap_.clear(); |
| Registry.typeToNewKeyAllowedMap_.clear(); |
| } |
| |
| /** |
| * It finds a KeyManager given by keyTemplate.typeUrl and returns it if it |
| * allows creating new keys. |
| * |
| * @private |
| * @param {!PbKeyTemplate} keyTemplate |
| * |
| * @return {!KeyManager.KeyManager} |
| */ |
| static getKeyManagerWithNewKeyAllowedCheck_(keyTemplate) { |
| const keyType = keyTemplate.getTypeUrl(); |
| const manager = Registry.getKeyManager(keyType); |
| if (!Registry.typeToNewKeyAllowedMap_.get(keyType)) { |
| throw new SecurityException( |
| 'New key operation is forbidden for ' + |
| 'key type: ' + keyType + '.'); |
| } |
| |
| return manager; |
| } |
| |
| /** |
| * Tries to register a primitive wrapper. |
| * |
| * @template P |
| * @static |
| * |
| * @param {!PrimitiveWrapper<P>} wrapper |
| */ |
| static registerPrimitiveWrapper(wrapper) { |
| if (!wrapper) { |
| throw new SecurityException('primitive wrapper cannot be null'); |
| } |
| const primitiveType = wrapper.getPrimitiveType(); |
| if (!primitiveType) { |
| throw new SecurityException('primitive wrapper cannot be undefined'); |
| } |
| |
| if (Registry.primitiveTypeToWrapper_.has(primitiveType)) { |
| // Cannot overwrite the existing key manager by a new one. |
| if (!(Registry.primitiveTypeToWrapper_.get(primitiveType) instanceof |
| wrapper.constructor)) { |
| throw new SecurityException( |
| 'primitive wrapper for type ' + primitiveType + |
| ' has already been registered and cannot be overwritten'); |
| } |
| } |
| |
| Registry.primitiveTypeToWrapper_.set(primitiveType, wrapper); |
| } |
| |
| /** |
| * Wraps a PrimitiveSet and returns a single instance. |
| * |
| * @template P |
| * @static |
| * |
| * @param {!PrimitiveSet.PrimitiveSet<P>} primitiveSet |
| * @return {!P} |
| */ |
| static wrap(primitiveSet) { |
| if (!primitiveSet) { |
| throw new SecurityException('primitive set cannot be null.'); |
| } |
| const primitiveType = primitiveSet.getPrimitiveType(); |
| const wrapper = Registry.primitiveTypeToWrapper_.get(primitiveType); |
| if (!wrapper) { |
| throw new SecurityException( |
| 'no primitive wrapper found for type ' + primitiveType); |
| } |
| return wrapper.wrap(primitiveSet); |
| } |
| } |
| // key managers maps |
| /** |
| * @static @private {!Map<string,!KeyManager.KeyManager>} |
| * |
| */ |
| Registry.typeToManagerMap_ = new Map(); |
| /** |
| * @static @private {!Map<string,boolean>} |
| */ |
| Registry.typeToNewKeyAllowedMap_ = new Map(); |
| |
| // primitive wrappers map |
| /** |
| * @static @private {!Map<!Object,!PrimitiveWrapper>} |
| */ |
| Registry.primitiveTypeToWrapper_ = new Map(); |
| |
| exports = Registry; |