blob: 8e332d2e47784c8c8bab1e3ca3584ab142ff6a66 [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.aead.AesGcmKeyManagerTest');
goog.setTestOnly('tink.aead.AesGcmKeyManagerTest');
const {Aead} = goog.require('google3.third_party.tink.javascript.aead.internal.aead');
const AesGcmKeyManager = goog.require('tink.aead.AesGcmKeyManager');
const {Mac} = goog.require('google3.third_party.tink.javascript.mac.internal.mac');
const Random = goog.require('google3.third_party.tink.javascript.subtle.random');
const {PbAesCtrKey, PbAesCtrKeyFormat, PbAesGcmKey, PbAesGcmKeyFormat, PbKeyData} = goog.require('google3.third_party.tink.javascript.internal.proto');
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) {
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) {
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) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeySize(keySize));
}
}
});
it('new key, via format proto', function() {
const manager = new AesGcmKeyManager();
const keyFormat = createTestKeyFormat();
const key =
/** @type {!PbAesGcmKey}*/ (manager.getKeyFactory().newKey(keyFormat));
expect(key.getKeyValue().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 = /** @type {!PbAesGcmKey} */ (
manager.getKeyFactory().newKey(serializedKeyFormat));
expect(key.getKeyValue().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());
expect(key.getKeyValue().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) {
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) {
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) {
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 /** !PbAesGcmKey */ key = createTestKey(keySize);
try {
await manager.getPrimitive(PRIMITIVE, key);
fail('An exception should be thrown');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.unsupportedKeySize(keySize));
}
}
});
it('get primitive, bad serialization', async function() {
const manager = new AesGcmKeyManager();
const keyData = createTestKeyData().setValue(new Uint8Array([]));
try {
await manager.getPrimitive(PRIMITIVE, keyData);
fail('An exception should be thrown');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.invalidSerializedKey());
}
});
it('get primitive, unsupported primitive', async function() {
const manager = new AesGcmKeyManager();
const keyData = createTestKeyData();
try {
await manager.getPrimitive(Mac, keyData);
fail('An exception should be thrown.');
} catch (e) {
expect(e.toString()).toBe(ExceptionText.unsupportedPrimitive());
}
});
// 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 /** Aead */ primitive = 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 /** Aead */ primitive =
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 {
/** @return {string} */
static unsupportedPrimitive() {
return 'SecurityException: Requested primitive type which is not supported ' +
'by this key manager.';
}
/**
* @param {number} keySize
* @return {string}
*/
static unsupportedKeySize(keySize) {
return 'InvalidArgumentsException: unsupported AES key size: ' + keySize;
}
/**
* @return {string}
*/
static versionOutOfBounds() {
return 'SecurityException: Version is out of bound, must be between 0 and ' +
VERSION + '.';
}
/**
* @param {string=} opt_unsupportedKeyType
* @return {string}
*/
static unsupportedKeyType(opt_unsupportedKeyType) {
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;
}
}
/**
* @return {string}
*/
static invalidSerializedKey() {
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.';
}
/**
* @return {string}
*/
static invalidKeyFormat() {
return 'SecurityException: Expected AesGcmKeyFormat-proto';
}
}
/**
* @param {number=} opt_keySize
*
* @return {!PbAesGcmKeyFormat}
*/
const createTestKeyFormat = function(opt_keySize = 16) {
const keyFormat = new PbAesGcmKeyFormat().setKeySize(opt_keySize);
return keyFormat;
};
/**
* @param {number=} opt_keySize
* @return {!PbAesGcmKey}
*/
const createTestKey = function(opt_keySize = 16) {
const key = new PbAesGcmKey().setVersion(0).setKeyValue(
Random.randBytes(opt_keySize));
return key;
};
/**
* @param {number=} opt_keySize
* @return {!PbKeyData}
*/
const createTestKeyData = function(opt_keySize) {
const keyData = new PbKeyData()
.setTypeUrl(KEY_TYPE)
.setValue(createTestKey(opt_keySize).serializeBinary())
.setKeyMaterialType(PbKeyData.KeyMaterialType.SYMMETRIC);
return keyData;
};