UNPKG

pooja-docucomb-tink-crypto

Version:

A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.

252 lines (231 loc) 8.37 kB
/** * @license * Copyright 2020 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import {Aead} from '../aead/internal/aead'; import {InvalidArgumentsException} from '../exception/invalid_arguments_exception'; import {SecurityException} from '../exception/security_exception'; import * as Random from '../subtle/random'; import * as KeyManager from './key_manager'; import {KeysetReader} from './keyset_reader'; import {KeysetWriter} from './keyset_writer'; import * as PrimitiveSet from './primitive_set'; import {PbKeyData, PbKeyMaterialType, PbKeyset, PbKeysetKey, PbKeyStatusType, PbKeyTemplate} from './proto'; import {bytesAsU8} from './proto_shims'; import * as Registry from './registry'; import * as Util from './util'; /** * Keyset handle provide abstracted access to Keysets, to limit the exposure of * actual protocol buffers that hold sensitive key material. * * @final */ export class KeysetHandle { private readonly keyset_: PbKeyset; constructor(keyset: PbKeyset) { Util.validateKeyset(keyset); this.keyset_ = keyset; } /** * 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. */ async getPrimitive<P>( primitiveType: Util.Constructor<P>, opt_customKeyManager?: KeyManager.KeyManager<P>|null): Promise<P> { 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. * * Visible for testing. */ async getPrimitiveSet<P>( primitiveType: Util.Constructor<P>, opt_customKeyManager?: KeyManager.KeyManager<P>| null): Promise<PrimitiveSet.PrimitiveSet<P>> { const primitiveSet = new PrimitiveSet.PrimitiveSet<P>(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<P>(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. * * */ async write(writer: KeysetWriter, masterKeyAead: Aead) { // TODO implement throw new SecurityException('KeysetHandle -- write: Not implemented yet.'); } /** * Writes this keyset using `writer` if and only if the keyset doesn't contain * any secret key material. * * This can be used to persist public keysets or envelope encryption keysets. * Use `CleartextKeysetHandle` to persist keysets containing secret key * material. */ writeNoSecret(writer: KeysetWriter): Uint8Array { assertNoSecretKeyMaterial(this.keyset_); return writer.encodeBinary(this.keyset_); } /** * Returns the keyset held by this KeysetHandle. * */ getKeyset(): PbKeyset { return this.keyset_; } /** * If the managed keyset contains private keys, returns a `KeysetHandle` of * the public keys. */ getPublicKeysetHandle(): KeysetHandle { const publicKeyset = new PbKeyset(); for (const key of this.keyset_.getKeyList()) { publicKeyset.addKey(key.clone().setKeyData(createPublicKeyData( nonNull('Key data', key.getKeyData())))); } publicKeyset.setPrimaryKeyId(this.keyset_.getPrimaryKeyId()); return new KeysetHandle(publicKeyset); } } function nonNull<T>(desc: string, value: T|null|undefined): T { if (value == null) { throw new SecurityException(`${desc} has to be non null.`); } return value; } function createPublicKeyData(privateKeyData: PbKeyData): PbKeyData { if (privateKeyData.getKeyMaterialType() !== PbKeyData.KeyMaterialType.ASYMMETRIC_PRIVATE) { throw new SecurityException('The keyset contains a non-private key'); } return Registry.getPublicKeyData( privateKeyData.getTypeUrl(), bytesAsU8(privateKeyData.getValue())); } /** * Validates that `keyset` doesn't contain any secret key material. * * @throws SecurityException if `keyset` contains secret key material. */ function assertNoSecretKeyMaterial(keyset: PbKeyset) { for (const key of keyset.getKeyList()) { const keyData = nonNull('Key data', key.getKeyData()); if (isSecretKeyMaterialType(keyData.getKeyMaterialType())) { throw new SecurityException('Keyset contains secret key material.'); } } } /** Returns true if the key material type is secret. */ function isSecretKeyMaterialType(type: PbKeyMaterialType) { return type === PbKeyMaterialType.UNKNOWN_KEYMATERIAL || type === PbKeyMaterialType.SYMMETRIC || type === PbKeyMaterialType.ASYMMETRIC_PRIVATE; } /** * Creates a KeysetHandle from an encrypted keyset obtained via reader, using * masterKeyAead to decrypt the keyset. * * */ export async function read( reader: KeysetReader, masterKeyAead: Aead): Promise<KeysetHandle> { // TODO implement throw new SecurityException('KeysetHandle -- read: Not implemented yet.'); } /** * Returns a new KeysetHandle that contains a single new key generated * according to keyTemplate. * * */ export async function generateNew(keyTemplate: PbKeyTemplate): Promise<KeysetHandle> { // TODO(thaidn): move this to a key manager. const keyset = await generateNewKeyset_(keyTemplate); return new KeysetHandle(keyset); } /** * Generates a new Keyset that contains a single new key generated * according to keyTemplate. * */ async function generateNewKeyset_(keyTemplate: PbKeyTemplate): Promise<PbKeyset> { const key = (new PbKeysetKey()) .setStatus(PbKeyStatusType.ENABLED) .setOutputPrefixType(keyTemplate.getOutputPrefixType()); const keyId = 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. * * @return The key ID. */ function generateNewKeyId_(): number { const bytes = Random.randBytes(4); let value = 0; for (let i = 0; i < bytes.length; i++) { value += (bytes[i] & 255) << i * 8; } // Make sure the key ID is a positive integer smaller than 2^32. return Math.abs(value) % 2 ** 32; } /** * 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. * */ export function readNoSecret(reader: KeysetReader): KeysetHandle { if (reader === null) { throw new SecurityException('Reader has to be non-null.'); } const keyset = reader.read(); assertNoSecretKeyMaterial(keyset); return new KeysetHandle(keyset); }