blob: 1a9596bd0ff3b88dd940c0a299b47bd2d1c185c6 [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.KeysetHandle');
const Aead = goog.require('tink.Aead');
const InvalidArgumentsException = goog.require('tink.exception.InvalidArgumentsException');
const KeyManager = goog.require('tink.KeyManager');
const KeysetReader = goog.require('tink.KeysetReader');
const KeysetWriter = goog.require('tink.KeysetWriter');
const PbKeyMaterialType = goog.require('proto.google.crypto.tink.KeyData.KeyMaterialType');
const PbKeyStatusType = goog.require('proto.google.crypto.tink.KeyStatusType');
const PbKeyTemplate = goog.require('proto.google.crypto.tink.KeyTemplate');
const PbKeyset = goog.require('proto.google.crypto.tink.Keyset');
const PrimitiveSet = goog.require('tink.PrimitiveSet');
const Random = goog.require('tink.subtle.Random');
const Registry = goog.require('tink.Registry');
const SecurityException = goog.require('tink.exception.SecurityException');
const Util = goog.require('tink.Util');
/**
* Keyset handle provide abstracted access to Keysets, to limit the exposure of
* actual protocol buffers that hold sensitive key material.
*
* @final
*/
class KeysetHandle {
/**
* @param {!PbKeyset} keyset
*/
constructor(keyset) {
Util.validateKeyset(keyset);
/** @const @private {!PbKeyset} */
this.keyset_ = keyset;
}
/**
* Creates a KeysetHandle from an encrypted keyset obtained via reader, using
* masterKeyAead to decrypt the keyset.
*
* @param {!KeysetReader} reader
* @param {!Aead} masterKeyAead
*
* @return {!Promise<!KeysetHandle>}
*/
static async read(reader, masterKeyAead) {
// TODO implement
throw new SecurityException('KeysetHandle -- read: Not implemented yet.');
}
/**
* Creates a KeysetHandle from a keyset, obtained via reader, which
* must contain no secret key material.
*
* This can be used to load public keysets or envelope encryption keysets.
* Users that need to load cleartext keysets can use CleartextKeysetHandle.
*
* @param {!KeysetReader} reader
* @return {!KeysetHandle}
*/
static readNoSecret(reader) {
if (reader === null) {
throw new SecurityException('Reader has to be non-null.');
}
const keyset = reader.read();
const keyList = keyset.getKeyList();
for (let key of keyList) {
switch (key.getKeyData().getKeyMaterialType()) {
case PbKeyMaterialType.ASYMMETRIC_PUBLIC: // fall through
case PbKeyMaterialType.REMOTE:
continue;
}
throw new SecurityException('Keyset contains secret key material.');
}
return new KeysetHandle(keyset);
}
/**
* Returns a new KeysetHandle that contains a single new key generated
* according to keyTemplate.
*
* @param {!PbKeyTemplate} keyTemplate
*
* @return {!Promise<!KeysetHandle>}
*/
static async generateNew(keyTemplate) {
// TODO(thaidn): move this to a key manager.
const keyset = await KeysetHandle.generateNewKeyset_(keyTemplate);
return new KeysetHandle(keyset);
}
/**
* Generates a new Keyset that contains a single new key generated
* according to keyTemplate.
*
* @param {!PbKeyTemplate} keyTemplate
* @private
* @return {!Promise<!PbKeyset>}
*/
static async generateNewKeyset_(keyTemplate) {
const key = new PbKeyset.Key()
.setStatus(PbKeyStatusType.ENABLED)
.setOutputPrefixType(keyTemplate.getOutputPrefixType());
const keyId = KeysetHandle.generateNewKeyId_();
key.setKeyId(keyId);
const keyData = await Registry.newKeyData(keyTemplate);
key.setKeyData(keyData);
const keyset = new PbKeyset();
keyset.addKey(key);
keyset.setPrimaryKeyId(keyId);
return keyset;
}
/**
* Generates a new random key ID.
*
* @private
* @return {number} The key ID.
*/
static generateNewKeyId_() {
const bytes = Random.randBytes(4);
let value = 0;
for (let i = 0; i < bytes.length; i++) {
value += (bytes[i] & 0xFF) << (i * 8);
}
// Make sure the key ID is a positive integer smaller than 2^32.
return Math.abs(value) % 2 ** 32;
};
/**
* Returns a primitive that uses key material from this keyset handle. If
* opt_customKeyManager is defined then the provided key manager is used to
* instantiate primitives. Otherwise key manager from Registry is used.
*
* @template P
*
* @param {!Object} primitiveType
* @param {?KeyManager.KeyManager<P>=} opt_customKeyManager
*
* @return {!Promise<!P>}
*/
async getPrimitive(primitiveType, opt_customKeyManager) {
if (!primitiveType) {
throw new InvalidArgumentsException('primitive type must be non-null');
}
const primitiveSet =
await this.getPrimitiveSet_(primitiveType, opt_customKeyManager);
return Registry.wrap(primitiveSet);
}
/**
* Creates a set of primitives corresponding to the keys with status Enabled
* in the given keysetHandle, assuming all the correspoding key managers are
* present (keys with status different from Enabled are skipped). If provided
* uses customKeyManager instead of registered key managers for keys supported
* by the customKeyManager.
*
* @template P
* @private
*
* @param {!Object} primitiveType
* @param {?KeyManager.KeyManager<P>=} opt_customKeyManager
*
* @return {!Promise.<!PrimitiveSet.PrimitiveSet<P>>}
*/
async getPrimitiveSet_(primitiveType, opt_customKeyManager) {
const primitiveSet = new PrimitiveSet.PrimitiveSet(primitiveType);
const keys = this.keyset_.getKeyList();
const keysLength = keys.length;
for (let i = 0; i < keysLength; i++) {
const key = keys[i];
if (key.getStatus() === PbKeyStatusType.ENABLED) {
const keyData = key.getKeyData();
if (!keyData) {
throw new SecurityException('Key data has to be non null.');
}
let primitive;
if (opt_customKeyManager &&
opt_customKeyManager.getKeyType() === keyData.getTypeUrl()) {
primitive =
await opt_customKeyManager.getPrimitive(primitiveType, keyData);
} else {
primitive = await Registry.getPrimitive(primitiveType, keyData);
}
const entry = primitiveSet.addPrimitive(primitive, key);
if (key.getKeyId() === this.keyset_.getPrimaryKeyId()) {
primitiveSet.setPrimary(entry);
}
}
}
return primitiveSet;
}
/**
* Encrypts the underlying keyset with the provided masterKeyAead wnd writes
* the resulting encryptedKeyset to the given writer which must be non-null.
*
* @param {!KeysetWriter} writer
* @param {!Aead} masterKeyAead
*
*/
async write(writer, masterKeyAead) {
// TODO implement
throw new SecurityException('KeysetHandle -- write: Not implemented yet.');
}
/**
* Returns the keyset held by this KeysetHandle.
*
* @package
* @return {!PbKeyset}
*/
getKeyset() {
return this.keyset_;
}
}
goog.exportSymbol('tink.KeysetHandle', KeysetHandle);
exports = KeysetHandle;