blob: 6e8f157047349f9c3b616860af3f1d31c75c025a [file] [log] [blame]
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {PbAesCtrKey, PbAesCtrKeyFormat, PbAesGcmKey, PbAesGcmKeyFormat, PbKeyData} from '../internal/proto';
import * as Random from '../subtle/random';
import {AesGcmKeyManager} from './aes_gcm_key_manager';
import {Aead} from './internal/aead';
const KEY_TYPE = 'type.googleapis.com/google.crypto.tink.AesGcmKey';
const VERSION = 0;
const PRIMITIVE = Aead;
describe('aes gcm key manager test', function() {
/////////////////////////////////////////////////////////////////////////////
// tests for newKey method
// newKey method -- key formats
it('new key, invalid key format', function() {
const keyFormat = new PbAesCtrKeyFormat();
const manager = new AesGcmKeyManager();
try {
manager.getKeyFactory().newKey(keyFormat);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.invalidKeyFormat());
}
});
it('new key, invalid serialized key format', function() {
const keyFormat = new Uint8Array(0);
const manager = new AesGcmKeyManager();
try {
manager.getKeyFactory().newKey(keyFormat);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.invalidSerializedKeyFormat());
}
});
it('new key, unsupported key sizes', function() {
const manager = new AesGcmKeyManager();
for (let keySize = 0; keySize < 40; keySize++) {
if (keySize === 16 || keySize === 32) {
// Keys of size 16 and 32 bytes are supported.
continue;
}
const keyFormat = createTestKeyFormat(keySize);
try {
manager.getKeyFactory().newKey(keyFormat);
fail('An exception should be thrown.');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeySize(keySize));
}
}
});
it('new key, via format proto', function() {
const manager = new AesGcmKeyManager();
const keyFormat = createTestKeyFormat();
const key = manager.getKeyFactory().newKey(keyFormat);
expect(key.getKeyValue_asU8().length).toBe(keyFormat.getKeySize());
});
it('new key, via serialized format proto', function() {
const manager = new AesGcmKeyManager();
const keyFormat = createTestKeyFormat();
const serializedKeyFormat = keyFormat.serializeBinary();
const key = manager.getKeyFactory().newKey(serializedKeyFormat);
expect(key.getKeyValue_asU8().length).toBe(keyFormat.getKeySize());
});
/////////////////////////////////////////////////////////////////////////////
// tests for NewKeyData method
it('new key data, should work', function() {
const keyFormat = createTestKeyFormat();
const serializedKeyFormat = keyFormat.serializeBinary();
const manager = new AesGcmKeyManager();
const keyData = manager.getKeyFactory().newKeyData(serializedKeyFormat);
expect(keyData.getTypeUrl()).toBe(KEY_TYPE);
expect(keyData.getKeyMaterialType())
.toBe(PbKeyData.KeyMaterialType.SYMMETRIC);
const key = PbAesGcmKey.deserializeBinary(keyData.getValue_asU8());
expect(key.getKeyValue_asU8().length).toBe(keyFormat.getKeySize());
});
/////////////////////////////////////////////////////////////////////////////
// tests for getPrimitive method
it('get primitive, unsupported key data type', async function() {
const manager = new AesGcmKeyManager();
const keyData = createTestKeyData().setTypeUrl('bad_type_url');
try {
await manager.getPrimitive(PRIMITIVE, keyData);
fail('An exception should be thrown');
} catch (e: any) {
expect(e.toString())
.toBe(ExceptionText.unsupportedKeyType(keyData.getTypeUrl()));
}
});
it('get primitive, unsupported key type', async function() {
const manager = new AesGcmKeyManager();
const key = new PbAesCtrKey();
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeyType());
}
});
it('get primitive, bad version', async function() {
const version = 1;
const manager = new AesGcmKeyManager();
const key = createTestKey().setVersion(version);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.versionOutOfBounds());
}
});
it('get primitive, unsupported key sizes', async function() {
const manager = new AesGcmKeyManager();
for (let keySize = 0; keySize < 40; keySize++) {
if (keySize === 16 || keySize === 32) {
// Keys of sizes 16 and 32 bytes are supported.
continue;
}
const key: PbAesGcmKey = createTestKey(keySize);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown');
} catch (e: any) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeySize(keySize));
}
}
});
it('get primitive, bad serialization', async function() {
const manager = new AesGcmKeyManager();
const keyData = createTestKeyData().setValue(new Uint8Array([0]));
try {
await manager.getPrimitive(PRIMITIVE, keyData);
fail('An exception should be thrown');
} catch (e: any) {
let message = e.toString();
if (message === ExceptionText.unsupportedKeySize(0)) {
message = ExceptionText.invalidSerializedKey();
}
expect(message).toBe(ExceptionText.invalidSerializedKey());
}
});
// Tests for getting primitive from valid key/keyData.
it('get primitive, from key', async function() {
const manager = new AesGcmKeyManager();
const key = createTestKey();
// Get the primitive from key manager.
const primitive: Aead = await manager.getPrimitive(PRIMITIVE, key);
// Test the returned primitive.
const plaintext = Random.randBytes(8);
const aad = Random.randBytes(8);
const ciphertext = await primitive.encrypt(plaintext, aad);
const decryptedCiphertext = await primitive.decrypt(ciphertext, aad);
expect(decryptedCiphertext).toEqual(plaintext);
});
it('get primitive, from key data', async function() {
const manager = new AesGcmKeyManager();
const keyData = createTestKeyData();
// Get primitive.
const primitive: Aead = await manager.getPrimitive(PRIMITIVE, keyData);
// Test the returned primitive.
const plaintext = Random.randBytes(8);
const aad = Random.randBytes(8);
const ciphertext = await primitive.encrypt(plaintext, aad);
const decryptedCiphertext = await primitive.decrypt(ciphertext, aad);
expect(decryptedCiphertext).toEqual(plaintext);
});
/////////////////////////////////////////////////////////////////////////////
// tests for getVersion, getKeyType and doesSupport methods
it('get version, should be zero', function() {
const manager = new AesGcmKeyManager();
expect(manager.getVersion()).toBe(0);
});
it('get key type, should be aes gcm key type', function() {
const manager = new AesGcmKeyManager();
expect(manager.getKeyType()).toBe(KEY_TYPE);
});
it('does support, should support aes gcm key type', function() {
const manager = new AesGcmKeyManager();
expect(manager.doesSupport(KEY_TYPE)).toBe(true);
});
it('get primitive type, should be aead', function() {
const manager = new AesGcmKeyManager();
expect(manager.getPrimitiveType()).toBe(PRIMITIVE);
});
});
/////////////////////////////////////////////////////////////////////////////
// Helper functions for tests
class ExceptionText {
static unsupportedPrimitive(): string {
return 'SecurityException: Requested primitive type which is not supported ' +
'by this key manager.';
}
static unsupportedKeySize(keySize: number): string {
return 'InvalidArgumentsException: unsupported AES key size: ' + keySize;
}
static versionOutOfBounds(): string {
return 'SecurityException: Version is out of bound, must be between 0 and ' +
VERSION + '.';
}
static unsupportedKeyType(opt_unsupportedKeyType?: string): string {
const prefix = 'SecurityException: Key type';
const suffix =
'is not supported. This key manager supports ' + KEY_TYPE + '.';
if (opt_unsupportedKeyType) {
return prefix + ' ' + opt_unsupportedKeyType + ' ' + suffix;
} else {
return prefix + ' ' + suffix;
}
}
static invalidSerializedKey(): string {
return 'SecurityException: Could not parse the input as a serialized proto of ' +
KEY_TYPE + ' key.';
}
static invalidSerializedKeyFormat() {
return 'SecurityException: Could not parse the input as a serialized proto of ' +
KEY_TYPE + ' key format.';
}
static invalidKeyFormat(): string {
return 'SecurityException: Expected AesGcmKeyFormat-proto';
}
}
function createTestKeyFormat(opt_keySize: number = 16): PbAesGcmKeyFormat {
const keyFormat = new PbAesGcmKeyFormat().setKeySize(opt_keySize);
return keyFormat;
}
function createTestKey(opt_keySize: number = 16): PbAesGcmKey {
const key = new PbAesGcmKey().setVersion(0).setKeyValue(
Random.randBytes(opt_keySize));
return key;
}
function createTestKeyData(opt_keySize?: number): PbKeyData {
const keyData = new PbKeyData()
.setTypeUrl(KEY_TYPE)
.setValue(createTestKey(opt_keySize).serializeBinary())
.setKeyMaterialType(PbKeyData.KeyMaterialType.SYMMETRIC);
return keyData;
}