blob: 08e3f3c1989c1f088e0b26dd525df40f01df9a53 [file] [log] [blame]
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {Aead} from '../aead';
import {AeadKeyTemplates} from '../aead/aead_key_templates';
import {SecurityException} from '../exception/security_exception';
import {HybridDecrypt, HybridEncrypt} from '../hybrid';
import * as HybridConfig from '../hybrid/hybrid_config';
import {HybridKeyTemplates} from '../hybrid/hybrid_key_templates';
import {Mac} from '../mac';
import * as Bytes from '../subtle/bytes';
import * as Random from '../subtle/random';
import {assertExists, assertMessageEquals, createKeyset} from '../testing/internal/test_utils';
import {BinaryKeysetReader} from './binary_keyset_reader';
import {BinaryKeysetWriter} from './binary_keyset_writer';
import {CleartextKeysetHandle} from './cleartext_keyset_handle';
import {KeyFactory, KeyManager} from './key_manager';
import {generateNew, KeysetHandle, read, readNoSecret} from './keyset_handle';
import {PbKeyData, PbKeyMaterialType, PbKeyset, PbKeysetKey, PbKeyStatusType, PbMessage, PbOutputPrefixType} from './proto';
import * as Registry from './registry';
import {Constructor} from './util';
describe('KeysetHandle', () => {
beforeEach(() => {
// Use a generous promise timeout for running continuously.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 1000; // 1000s
HybridConfig.register();
});
afterEach(() => {
Registry.reset();
// Reset the promise timeout to default value.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; // 1s
});
describe('constructor', () => {
it('throws with empty list of keys', async () => {
const keyset = new PbKeyset().setKeyList([]);
expect(() => new KeysetHandle(keyset))
.toThrowError(
SecurityException,
'Keyset should be non null and must contain at least one key.');
});
it('does not throw for valid keyset protos', async () => {
const keyset = createKeyset();
expect(() => new KeysetHandle(keyset)).not.toThrow();
});
});
describe('getKeyset', () => {
it('returns the underlying keyset proto', async () => {
const keyset = createKeyset();
const keysetHandle = new KeysetHandle(keyset);
const result = keysetHandle.getKeyset();
expect(result).toEqual(keyset);
});
});
describe('read', () => {
it('is not yet implemented', async () => {
const keyTemplate = AeadKeyTemplates.aes128CtrHmacSha256();
const keysetHandle = await generateNew(keyTemplate);
const serializedKeyset =
CleartextKeysetHandle.serializeToBinary(keysetHandle);
const keysetReader = new BinaryKeysetReader(serializedKeyset);
const aead = await keysetHandle.getPrimitive<Aead>(Aead);
await expectAsync(read(keysetReader, aead))
.toBeRejectedWithError(
SecurityException, 'KeysetHandle -- read: Not implemented yet.');
});
});
describe('generateNew', () => {
it('generates new keyset handles given a key template', async () => {
const keyTemplate = AeadKeyTemplates.aes128CtrHmacSha256();
const keysetHandle = await generateNew(keyTemplate);
const keyset = keysetHandle.getKeyset();
expect(1).toBe(keyset.getKeyList().length);
const key = keyset.getKeyList()[0];
expect(keyset.getPrimaryKeyId()).toBe(key.getKeyId());
expect(keyTemplate.getOutputPrefixType()).toBe(key.getOutputPrefixType());
expect(PbKeyStatusType.ENABLED).toBe(key.getStatus());
const keyData = assertExists(key.getKeyData());
expect(keyTemplate.getTypeUrl()).toBe(keyData.getTypeUrl());
const aead = await keysetHandle.getPrimitive(Aead);
const plaintext = Random.randBytes(20);
const ciphertext = await aead.encrypt(plaintext);
expect(await aead.decrypt(ciphertext)).toEqual(plaintext);
});
});
describe('write', () => {
it('is not yet implemented', async () => {
const keyset = createKeysetAndInitializeRegistry(Aead);
const keysetHandle = new KeysetHandle(keyset);
const keysetWriter = new BinaryKeysetWriter();
const aead = await keysetHandle.getPrimitive<Aead>(Aead);
await expectAsync(keysetHandle.write(keysetWriter, aead))
.toBeRejectedWithError(
SecurityException, 'KeysetHandle -- write: Not implemented yet.');
});
});
describe('getPrimitive', () => {
it('aead', async () => {
const keyset = createKeysetAndInitializeRegistry(Aead);
const keysetHandle = new KeysetHandle(keyset);
const aead = await keysetHandle.getPrimitive<Aead>(Aead);
// Test the aead primitive returned by getPrimitive method.
const plaintext = new Uint8Array([1, 2, 3, 4, 5, 6]);
const ciphertext = await aead.encrypt(plaintext);
const decryptedText = await aead.decrypt(ciphertext);
expect(decryptedText).toEqual(plaintext);
});
it('hybrid encrypt', async () => {
const keyset = createKeysetAndInitializeRegistry(HybridEncrypt);
const keysetHandle = new KeysetHandle(keyset);
// Test the HybridEncrypt primitive returned by getPrimitive method.
const hybridEncrypt =
await keysetHandle.getPrimitive<HybridEncrypt>(HybridEncrypt);
const plaintext = Random.randBytes(10);
const ciphertext = await hybridEncrypt.encrypt(plaintext);
// DummyHybridEncrypt just appends a ciphertext suffix to the plaintext.
// Since the primary key id is 1, the ciphertext prefix should also be 1.
expect(ciphertext)
.toEqual(Bytes.concat(
new Uint8Array([
0, 0, 0, 0, 1
]) /* prefix which is 1-byte version + 4-byte primary key id*/,
plaintext,
new Uint8Array([1]) /* suffix which is 1-byte primary key id */));
});
it('hybrid decrypt', async () => {
const decryptKeysetHandle =
new KeysetHandle(createKeysetAndInitializeRegistry(HybridDecrypt));
const hybridDecrypt =
await decryptKeysetHandle.getPrimitive<HybridDecrypt>(HybridDecrypt);
const encryptKeysetHandle =
new KeysetHandle(createKeysetAndInitializeRegistry(HybridEncrypt));
const hybridEncrypt =
await encryptKeysetHandle.getPrimitive<HybridEncrypt>(HybridEncrypt);
const plaintext = Random.randBytes(10);
const ciphertext = await hybridEncrypt.encrypt(plaintext);
const decrypted = await hybridDecrypt.decrypt(ciphertext);
expect(decrypted).toEqual(plaintext);
});
it('aead, custom key manager', async () => {
const keyset = new PbKeyset();
// Add a new key with a new key type associated to custom key manager
// to the keyset.
const keyTypeUrl = 'new_custom_aead_key_type';
const keyId = 0xFFFFFFFF;
const key =
createKey({keyId, outputPrefix: PbOutputPrefixType.TINK, keyTypeUrl});
keyset.addKey(key);
keyset.setPrimaryKeyId(keyId);
const keysetHandle = new KeysetHandle(keyset);
// Create a custom key manager.
const customKeyManager = new DummyKeyManager(
keyTypeUrl, new DummyAead(Random.randBytes(10)), Aead);
// Encrypt with the primitive returned by customKeyManager.
const aead =
await keysetHandle.getPrimitive<Aead>(Aead, customKeyManager);
const plaintext = Random.randBytes(20);
const ciphertext = await aead.encrypt(plaintext);
// Register another key manager with the custom key type.
const managerInRegistry = new DummyKeyManager(
keyTypeUrl, new DummyAead(Random.randBytes(10)), Aead);
Registry.registerKeyManager(managerInRegistry);
// Check that the primitive returned by getPrimitive cannot decrypt the
// ciphertext. This is because managerInRegistry is different from
// customKeyManager.
const aeadFromRegistry = await keysetHandle.getPrimitive<Aead>(Aead);
await expectAsync(aeadFromRegistry.decrypt(ciphertext))
.toBeRejectedWithError(
SecurityException, 'Decryption failed for the given ciphertext.');
// Check that the primitive returned by getPrimitive with customKeyManager
// decrypts correctly.
const aeadFromCustomKeyManager =
await keysetHandle.getPrimitive<Aead>(Aead, customKeyManager);
const decryptedText = await aeadFromCustomKeyManager.decrypt(ciphertext);
expect(decryptedText).toEqual(plaintext);
});
it('hybrid encrypt, custom key manager', async () => {
const keyset = new PbKeyset();
// Add a new key with a new key type associated to custom key manager
// to the keyset.
const keyTypeUrl = 'new_custom_hybrid_encrypt_key_type';
const keyId = 0xFFFFFFFF;
const key =
createKey({keyId, outputPrefix: PbOutputPrefixType.TINK, keyTypeUrl});
keyset.addKey(key);
keyset.setPrimaryKeyId(keyId);
const keysetHandle = new KeysetHandle(keyset);
// Create a custom key manager.
const customKeyManager = new DummyKeyManager(
keyTypeUrl, new DummyHybridEncrypt(Random.randBytes(10)),
HybridEncrypt);
// Encrypt with the primitive returned by customKeyManager.
const customHybridEncrypt =
await keysetHandle.getPrimitive<HybridEncrypt>(
HybridEncrypt, customKeyManager);
const plaintext = Random.randBytes(20);
const ciphertext = await customHybridEncrypt.encrypt(plaintext);
// Register another key manager with the custom key type.
const managerInRegistry = new DummyKeyManager(
keyTypeUrl, new DummyHybridEncrypt(Random.randBytes(10)),
HybridEncrypt);
Registry.registerKeyManager(managerInRegistry);
// Check that the primitive returned by getPrimitive is not the same as
// customHybridEncrypt. This is because managerInRegistry is different
// from customKeyManager.
const hybridFromRegistry =
await keysetHandle.getPrimitive<HybridEncrypt>(HybridEncrypt);
const ciphertext2 = await hybridFromRegistry.encrypt(plaintext);
expect(ciphertext2).not.toEqual(ciphertext);
// Check that the primitive returned by getPrimitive with customKeyManager
// is the same as customHybridEncrypt.
const hybridEncryptFromCustomKeyManager =
await keysetHandle.getPrimitive<HybridEncrypt>(
HybridEncrypt, customKeyManager);
const ciphertext3 =
await hybridEncryptFromCustomKeyManager.encrypt(plaintext);
expect(ciphertext3).toEqual(ciphertext);
});
it('hybrid decrypt, custom key manager', async () => {
// Both private and public keys have the same key id.
const keyId = 0xFFFFFFFF;
// Create a public keyset.
const publicKeyset = new PbKeyset();
// Add a new key with a new key type associated to custom key manager
// to the keyset.
const publicKeyTypeUrl = 'new_custom_hybrid_encrypt_key_type';
const publicKey = createKey({
keyId,
outputPrefix: PbOutputPrefixType.TINK,
keyTypeUrl: publicKeyTypeUrl
});
publicKeyset.addKey(publicKey);
publicKeyset.setPrimaryKeyId(keyId);
const publicKeysetHandle = new KeysetHandle(publicKeyset);
// Create a corresponding private keyset.
const privateKeyset = new PbKeyset();
// Add a new key with a new key type associated to custom key manager
// to the keyset.
const privateKeyTypeUrl = 'new_custom_hybrid_decrypt_key_type';
const privateKey = createKey({
keyId,
outputPrefix: PbOutputPrefixType.TINK,
keyTypeUrl: privateKeyTypeUrl
});
privateKeyset.addKey(privateKey);
privateKeyset.setPrimaryKeyId(keyId);
const privateKeysetHandle = new KeysetHandle(privateKeyset);
// DummyHybridEncrypt (and DummyHybridDecrypt) just appends (and removes)
// a suffix to the plaintext. Create a random suffix that allows to
// determine which HybridDecrypt object is valid.
const ciphertextSuffix = Random.randBytes(10);
// Register a public key manager that uses the legit ciphertext suffix.
const publicKeyManagerInRegistry = new DummyKeyManager(
publicKeyTypeUrl, new DummyHybridEncrypt(ciphertextSuffix),
HybridEncrypt);
Registry.registerKeyManager(publicKeyManagerInRegistry);
// Encrypt with the primitive returned by getPrimitive.
const hybridEncrypt =
await publicKeysetHandle.getPrimitive<HybridEncrypt>(HybridEncrypt);
const plaintext = Random.randBytes(20);
const ciphertext = await hybridEncrypt.encrypt(plaintext);
// Register a private key manager that uses a random ciphertext suffix.
const keyManagerWithRandomSuffix = new DummyKeyManager(
privateKeyTypeUrl, new DummyHybridDecrypt(Random.randBytes(10)),
HybridDecrypt);
Registry.registerKeyManager(keyManagerWithRandomSuffix);
// Check that the primitive returned by getPrimitive cannot decrypt. This
// is because the ciphertext suffix is different.
const hybridDecryptFromRegistry =
await privateKeysetHandle.getPrimitive<HybridDecrypt>(HybridDecrypt);
await expectAsync(hybridDecryptFromRegistry.decrypt(ciphertext))
.toBeRejectedWithError(
SecurityException, 'Decryption failed for the given ciphertext.');
// Create a custom private key manager with the correct ciphertext suffix.
const customHybridDecryptKeyManager = new DummyKeyManager(
privateKeyTypeUrl, new DummyHybridDecrypt(ciphertextSuffix),
HybridDecrypt);
// Check that the primitive returned by getPrimitive with
// customHybridDecryptKeyManager can decrypt.
const customHybridDecrypt =
await privateKeysetHandle.getPrimitive<HybridDecrypt>(
HybridDecrypt, customHybridDecryptKeyManager);
const decrypted = await customHybridDecrypt.decrypt(ciphertext);
expect(decrypted).toEqual(plaintext);
});
it('keyset contains key corresponding to different primitive', async () => {
const keyset = createKeysetAndInitializeRegistry(Aead);
// Add new key with new key type url to the keyset and register a key
// manager providing Mac primitives with this key.
const macKeyTypeUrl = 'mac_key_type_1';
const macKeyId = 0xFFFFFFFF;
const macKey = createKey({
keyId: macKeyId,
outputPrefix: PbOutputPrefixType.TINK,
keyTypeUrl: macKeyTypeUrl
});
keyset.addKey(macKey);
const primitive = new DummyMac(new Uint8Array([0xFF]));
Registry.registerKeyManager(
new DummyKeyManager(macKeyTypeUrl, primitive, Mac));
const keysetHandle = new KeysetHandle(keyset);
await expectAsync(keysetHandle.getPrimitive<Aead>(Aead))
.toBeRejectedWithError(
SecurityException,
'Requested primitive type which is not supported by ' +
'this key manager.');
});
});
describe('getPrimitiveSet', () => {
it('primary key is the enabled key with given id', async () => {
const keyId = 1;
const primaryUrl = 'key_type_url_for_primary_key';
const disabledUrl = 'key_type_url_for_disabled_key';
const keyset = new PbKeyset();
keyset.addKey(createKey({
keyId,
outputPrefix: PbOutputPrefixType.TINK,
keyTypeUrl: disabledUrl,
enabled: false
}));
keyset.addKey(createKey({
keyId,
outputPrefix: PbOutputPrefixType.LEGACY,
keyTypeUrl: disabledUrl,
enabled: false
}));
keyset.addKey(createKey({
keyId,
outputPrefix: PbOutputPrefixType.RAW,
keyTypeUrl: disabledUrl,
enabled: false
}));
keyset.addKey(createKey({
keyId,
outputPrefix: PbOutputPrefixType.TINK,
keyTypeUrl: primaryUrl,
enabled: true
}));
keyset.setPrimaryKeyId(keyId);
const keysetHandle = new KeysetHandle(keyset);
const primitive = new DummyAead(new Uint8Array(Random.randBytes(10)));
Registry.registerKeyManager(
new DummyKeyManager(primaryUrl, primitive, Aead));
Registry.registerKeyManager(new DummyKeyManager(
disabledUrl, new DummyAead(new Uint8Array(Random.randBytes(10))),
Aead));
const primitiveSet = await keysetHandle.getPrimitiveSet(Aead);
const primary = assertExists(primitiveSet.getPrimary());
expect(primary.getPrimitive()).toBe(primitive);
});
it('disabled keys should be ignored', async () => {
const enabledRawKeysCount = 10;
const enabledUrl = 'enabled_key_type_url';
const disabledUrl = 'disabled_key_type_url';
// Create keyset with both enabled and disabled RAW keys.
const keyset = new PbKeyset();
// Add RAW keys with different ids from [1, ENABLED_RAW_KEYS_COUNT].
for (let i = 0; i < enabledRawKeysCount; i++) {
keyset.addKey(createKey({
keyId: 1 + i,
outputPrefix: PbOutputPrefixType.RAW,
keyTypeUrl: enabledUrl,
enabled: true
}));
keyset.addKey(createKey({
keyId: 1 + i,
outputPrefix: PbOutputPrefixType.RAW,
keyTypeUrl: disabledUrl,
enabled: false
}));
}
keyset.setPrimaryKeyId(1);
const keysetHandle = new KeysetHandle(keyset);
// Register KeyManager (the key manager for enabled keys should be
// enough).
const primitive = new DummyAead(new Uint8Array(Random.randBytes(10)));
Registry.registerKeyManager(
new DummyKeyManager(enabledUrl, primitive, Aead));
// Get primitives and get all raw primitives.
const primitiveSet = await keysetHandle.getPrimitiveSet(Aead);
const rawPrimitives = primitiveSet.getRawPrimitives();
// Should return all enabled RAW primitives and nothing else (disabled
// primitives should not be added into primitive set).
expect(rawPrimitives.length).toBe(enabledRawKeysCount);
// Test that it returns the correct RAW primitives by using getPrimitive.
for (let i = 0; i < enabledRawKeysCount; ++i) {
expect(rawPrimitives[i].getPrimitive()).toBe(primitive);
}
});
it('with custom key manager', async () => {
// Create keyset handle.
const keyTypeUrl = 'some_key_type_url';
const keyId = 1;
const key =
createKey({keyId, outputPrefix: PbOutputPrefixType.TINK, keyTypeUrl});
const keyset = new PbKeyset();
keyset.addKey(key);
keyset.setPrimaryKeyId(keyId);
const keysetHandle = new KeysetHandle(keyset);
// Register key manager for the given keyType.
const primitive = new DummyAead(new Uint8Array(Random.randBytes(10)));
Registry.registerKeyManager(
new DummyKeyManager(keyTypeUrl, primitive, Aead));
// Use getPrimitives with custom key manager for the keyType.
const customPrimitive =
new DummyAead(new Uint8Array(Random.randBytes(10)));
const customKeyManager =
new DummyKeyManager(keyTypeUrl, customPrimitive, Aead);
const primitiveSet =
await keysetHandle.getPrimitiveSet(Aead, customKeyManager);
// Primary should be the entry corresponding to the keyTypeUrl and thus
// getPrimitive should return customPrimitive.
const primary = assertExists(primitiveSet.getPrimary());
expect(primary.getPrimitive()).toBe(customPrimitive);
});
});
describe('readNoSecret', () => {
it('throws for keysets containing secret key material', () => {
const secretKeyMaterialTypes = [
PbKeyMaterialType.SYMMETRIC, PbKeyMaterialType.ASYMMETRIC_PRIVATE,
PbKeyMaterialType.UNKNOWN_KEYMATERIAL
];
for (const secretKeyMaterialType of secretKeyMaterialTypes) {
// Create a public keyset.
const keyset = new PbKeyset();
for (let i = 0; i < 3; i++) {
const key = createKey({
keyId: i + 1,
outputPrefix: PbOutputPrefixType.TINK,
keyTypeUrl: 'someType',
enabled: (i % 4) < 2,
keyMaterialType: PbKeyMaterialType.ASYMMETRIC_PUBLIC,
});
keyset.addKey(key);
}
keyset.setPrimaryKeyId(1);
const key = createKey({
keyId: 0xFFFFFFFF,
outputPrefix: PbOutputPrefixType.RAW,
keyTypeUrl: 'someType',
enabled: true,
keyMaterialType: secretKeyMaterialType,
});
keyset.addKey(key);
const reader =
BinaryKeysetReader.withUint8Array(keyset.serializeBinary());
expect(() => readNoSecret(reader))
.toThrowError(
SecurityException, 'Keyset contains secret key material.');
}
});
it('returns non-secret keysets', () => {
// Create a public keyset.
const keyset = new PbKeyset();
for (let i = 0; i < 3; i++) {
const key = createKey({
keyId: i + 1,
outputPrefix: PbOutputPrefixType.TINK,
keyTypeUrl: 'someType',
enabled: (i % 4) < 2,
keyMaterialType: PbKeyMaterialType.ASYMMETRIC_PUBLIC,
});
keyset.addKey(key);
}
keyset.setPrimaryKeyId(1);
const reader =
BinaryKeysetReader.withUint8Array(keyset.serializeBinary());
const keysetHandle = readNoSecret(reader);
assertMessageEquals(keysetHandle.getKeyset(), keyset);
});
});
describe('getPublicKeysetHandle', () => {
it('can get a public keyset from a private keyset', async () => {
const privateHandle = await generateNew(
HybridKeyTemplates.eciesP256HkdfHmacSha256Aes128Gcm());
expect(() => privateHandle.getPublicKeysetHandle())
.not.toThrowError(
SecurityException, 'The keyset contains a non-private key');
});
it('can not get a public keyset from another public keyset', async () => {
const privateHandle = await generateNew(
HybridKeyTemplates.eciesP256HkdfHmacSha256Aes128Gcm());
const publicHandle = privateHandle.getPublicKeysetHandle();
expect(() => publicHandle.getPublicKeysetHandle())
.toThrowError(
SecurityException, 'The keyset contains a non-private key');
});
});
describe('writeNoSecret', () => {
it('throws if the keyset contains secret keys', async () => {
const privateHandle = await generateNew(
HybridKeyTemplates.eciesP256HkdfHmacSha256Aes128Gcm());
expect(() => privateHandle.writeNoSecret(new BinaryKeysetWriter()))
.toThrowError(SecurityException);
});
it('writes bytes if the keyset contains no secret keys', async () => {
const privateHandle = await generateNew(
HybridKeyTemplates.eciesP256HkdfHmacSha256Aes128Gcm());
const publicHandle = privateHandle.getPublicKeysetHandle();
expect(() => publicHandle.writeNoSecret(new BinaryKeysetWriter()))
.not.toThrow();
});
it('can import the keyset using readNoSecret', async () => {
const privateHandle = await generateNew(
HybridKeyTemplates.eciesP256HkdfHmacSha256Aes128Gcm());
const publicHandle = privateHandle.getPublicKeysetHandle();
const keysetBytes = publicHandle.writeNoSecret(new BinaryKeysetWriter());
const importedHandle = readNoSecret(new BinaryKeysetReader(keysetBytes));
assertMessageEquals(publicHandle.getKeyset(), importedHandle.getKeyset());
});
});
});
/** Function for creating keys for testing purposes. */
function createKey({
keyId,
outputPrefix,
keyTypeUrl,
enabled = true,
keyMaterialType = PbKeyMaterialType.SYMMETRIC
}: {
keyId: number,
outputPrefix: PbOutputPrefixType,
keyTypeUrl: string,
enabled?: boolean,
keyMaterialType?: PbKeyMaterialType,
}): PbKeysetKey {
return new PbKeysetKey()
.setStatus(enabled ? PbKeyStatusType.ENABLED : PbKeyStatusType.DISABLED)
.setOutputPrefixType(outputPrefix)
.setKeyId(keyId)
.setKeyData(new PbKeyData()
.setTypeUrl(keyTypeUrl)
.setValue(new Uint8Array([1]))
.setKeyMaterialType(keyMaterialType));
}
/**
* Function for creating keysets for testing purposes.
* Primary has id 1.
*
* The function also register DummyKeyManager providing primitives for each
* keyType added to the Keyset.
*/
function createKeysetAndInitializeRegistry(
primitiveType: Constructor<unknown>, numberOfKeys = 15): PbKeyset {
const numberOfKeyTypes = 5;
const keyTypePrefix = 'key_type_';
for (let i = 0; i < numberOfKeyTypes; i++) {
const typeUrl = keyTypePrefix + i.toString();
let primitive;
switch (primitiveType) {
case HybridDecrypt:
primitive = new DummyHybridDecrypt(new Uint8Array([i]));
break;
case HybridEncrypt:
primitive = new DummyHybridEncrypt(new Uint8Array([i]));
break;
default:
primitive = new DummyAead(new Uint8Array([i]));
break;
}
Registry.registerKeyManager(
new DummyKeyManager(typeUrl, primitive, primitiveType));
}
const keyset = new PbKeyset();
for (let i = 1; i < numberOfKeys; i++) {
const keyTypeUrl = keyTypePrefix + (i % numberOfKeyTypes).toString();
let outputPrefix: PbOutputPrefixType;
switch (i % 3) {
case 0:
outputPrefix = PbOutputPrefixType.TINK;
break;
case 1:
outputPrefix = PbOutputPrefixType.LEGACY;
break;
default:
outputPrefix = PbOutputPrefixType.RAW;
}
// There are no primitives added to PrimitiveSet for disabled keys, thus
// they are quite rarely added into the Keyset.
keyset.addKey(
createKey({keyId: i, outputPrefix, keyTypeUrl, enabled: i % 7 < 6}));
}
keyset.setPrimaryKeyId(1);
return keyset;
}
class DummyAead extends Aead {
constructor(private readonly ciphertextSuffix: Uint8Array) {
super();
}
// Encrypt method just append the primitive identifier to plaintext.
async encrypt(plaintext: Uint8Array, associatedData?: Uint8Array) {
const result =
new Uint8Array(plaintext.length + this.ciphertextSuffix.length);
result.set(plaintext, 0);
result.set(this.ciphertextSuffix, plaintext.length);
return result;
}
// Decrypt method throws an exception whenever ciphertext does not end with
// ciphertext suffix, otherwise it returns the first part (without
// ciphertext suffix).
async decrypt(ciphertext: Uint8Array, associatedData?: Uint8Array) {
const plaintext = ciphertext.subarray(
0, ciphertext.length - this.ciphertextSuffix.length);
const ciphertextSuffix = ciphertext.subarray(
ciphertext.length - this.ciphertextSuffix.length, ciphertext.length);
if ([...ciphertextSuffix].toString() !==
[...this.ciphertextSuffix].toString()) {
throw new SecurityException('Ciphertext decryption failed.');
}
return plaintext;
}
}
class DummyMac extends Mac {
constructor(private readonly tag: Uint8Array) {
super();
}
/**
* Just appends the tag to the data.
*/
async computeMac(data: Uint8Array) {
return this.tag;
}
/**
* Returns whether data ends with tag.
*/
async verifyMac(tag: Uint8Array, data: Uint8Array) {
return [...tag].toString() === [...this.tag].toString();
}
}
class DummyHybridEncrypt extends HybridEncrypt {
constructor(private readonly ciphertextSuffix: Uint8Array) {
super();
}
// Async is used here just because real primitives returns Promise.
async encrypt(plaintext: Uint8Array, associatedData?: Uint8Array) {
return Bytes.concat(plaintext, this.ciphertextSuffix);
}
}
class DummyHybridDecrypt extends HybridDecrypt {
constructor(private readonly ciphertextSuffix: Uint8Array) {
super();
}
async decrypt(ciphertext: Uint8Array, associatedData?: Uint8Array) {
const cipherLen = ciphertext.length;
const suffixLen = this.ciphertextSuffix.length;
const plaintext = ciphertext.subarray(0, cipherLen - suffixLen);
const suffix = ciphertext.subarray(cipherLen - suffixLen, cipherLen);
if (!Bytes.isEqual(this.ciphertextSuffix, suffix)) {
throw new SecurityException('Ciphertext decryption failed.');
}
return plaintext;
}
}
class DummyKeyManager<T> implements KeyManager<T> {
constructor(
private readonly keyType: string, private readonly primitive: T,
private readonly primitiveType: Constructor<T>) {}
async getPrimitive(primitiveType: Constructor<T>, key: PbKeyData|PbMessage) {
if (primitiveType !== this.getPrimitiveType()) {
throw new SecurityException(
'Requested primitive type which is not ' +
'supported by this key manager.');
}
return this.primitive;
}
doesSupport(keyType: string) {
return keyType === this.getKeyType();
}
getKeyType() {
return this.keyType;
}
getPrimitiveType() {
return this.primitiveType;
}
getVersion(): number {
throw new SecurityException('Not implemented, function is not needed.');
}
getKeyFactory(): KeyFactory {
throw new SecurityException('Not implemented, function is not needed.');
}
}