blob: 75bd7d62ecccb973bf75014a6487c9126582eab4 [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.AesCtrHmacAeadKeyManagerTest');
goog.setTestOnly('tink.aead.AesCtrHmacAeadKeyManagerTest');
const Aead = goog.require('tink.Aead');
const AesCtrHmacAeadKeyManager = goog.require('tink.aead.AesCtrHmacAeadKeyManager');
const Mac = goog.require('tink.Mac');
const PbAesCtrHmacAeadKey = goog.require('proto.google.crypto.tink.AesCtrHmacAeadKey');
const PbAesCtrHmacAeadKeyFormat = goog.require('proto.google.crypto.tink.AesCtrHmacAeadKeyFormat');
const PbAesCtrKey = goog.require('proto.google.crypto.tink.AesCtrKey');
const PbAesCtrKeyFormat = goog.require('proto.google.crypto.tink.AesCtrKeyFormat');
const PbAesCtrParams = goog.require('proto.google.crypto.tink.AesCtrParams');
const PbHashType = goog.require('proto.google.crypto.tink.HashType');
const PbHmacKey = goog.require('proto.google.crypto.tink.HmacKey');
const PbHmacKeyFormat = goog.require('proto.google.crypto.tink.HmacKeyFormat');
const PbHmacParams = goog.require('proto.google.crypto.tink.HmacParams');
const PbKeyData = goog.require('proto.google.crypto.tink.KeyData');
const Random = goog.require('tink.subtle.Random');
const testSuite = goog.require('goog.testing.testSuite');
const KEY_TYPE = 'type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey';
const VERSION = 0;
/////////////////////////////////////////////////////////////////////////////
// Helper functions for tests
/**
* creates new AesCtrHmacAeadKeyFormat with allowed parameters
* @return {!PbAesCtrHmacAeadKeyFormat}
*/
const createTestKeyFormat = function() {
const KEY_SIZE = 16;
const IV_SIZE = 12;
const TAG_SIZE = 16;
let keyFormat = new PbAesCtrHmacAeadKeyFormat().setAesCtrKeyFormat(
new PbAesCtrKeyFormat());
keyFormat.getAesCtrKeyFormat().setKeySize(KEY_SIZE);
keyFormat.getAesCtrKeyFormat().setParams(new PbAesCtrParams());
keyFormat.getAesCtrKeyFormat().getParams().setIvSize(IV_SIZE);
// set HMAC key
keyFormat.setHmacKeyFormat(new PbHmacKeyFormat());
keyFormat.getHmacKeyFormat().setKeySize(KEY_SIZE);
keyFormat.getHmacKeyFormat().setParams(new PbHmacParams());
keyFormat.getHmacKeyFormat().getParams().setHash(PbHashType.SHA1);
keyFormat.getHmacKeyFormat().getParams().setTagSize(TAG_SIZE);
return keyFormat;
};
/**
* creates new AesCtrHmacAeadKey with allowed parameters
* @return {!PbAesCtrHmacAeadKey}
*/
const createTestKey = function() {
const KEY_SIZE = 16;
const IV_SIZE = 12;
const TAG_SIZE = 16;
let key =
new PbAesCtrHmacAeadKey().setVersion(0).setAesCtrKey(new PbAesCtrKey());
key.getAesCtrKey().setVersion(0);
key.getAesCtrKey().setParams(new PbAesCtrParams());
key.getAesCtrKey().getParams().setIvSize(IV_SIZE);
key.getAesCtrKey().setKeyValue(Random.randBytes(KEY_SIZE));
// set HMAC key
key.setHmacKey(new PbHmacKey());
key.getHmacKey().setVersion(0);
key.getHmacKey().setParams(new PbHmacParams());
key.getHmacKey().getParams().setHash(PbHashType.SHA1);
key.getHmacKey().getParams().setTagSize(TAG_SIZE);
key.getHmacKey().setKeyValue(Random.randBytes(KEY_SIZE));
return key;
};
/**
* creates new PbKeyData with allowed parameters
* @return {!PbKeyData}
*/
const createTestKeyData = function() {
let keyData = new PbKeyData()
.setTypeUrl(KEY_TYPE)
.setValue(createTestKey().serializeBinary())
.setKeyMaterialType(PbKeyData.KeyMaterialType.SYMMETRIC);
return keyData;
};
testSuite({
/////////////////////////////////////////////////////////////////////////////
// tests for newKey method
// newKey method -- key formats
async testNewKeyBadKeyFormat() {
const keyFormat = new PbAesCtrKeyFormat();
const manager = new AesCtrHmacAeadKeyManager();
try {
manager.getKeyFactory().newKey(keyFormat);
} catch (e) {
assertEquals(
'CustomError: Expected AesCtrHmacAeadKeyFormat-proto', e.toString());
return;
}
fail('An exception should be thrown.');
},
async testNewKeyBadSerializedKey() {
// this is not a serialized key format
const serializedKeyFormat = new Uint8Array(4);
const manager = new AesCtrHmacAeadKeyManager();
try {
manager.getKeyFactory().newKey(serializedKeyFormat);
} catch (e) {
assertEquals(
'CustomError: Could not parse the given Uint8Array as a serialized' +
' proto of ' + KEY_TYPE,
e.toString());
return;
}
fail('An exception should be thrown.');
},
// newKey method -- bad parametrs of AES CTR KEY format
async testNewKeyNotSupportedAesCtrKeySize() {
const /** number */ keySize = 11;
const manager = new AesCtrHmacAeadKeyManager();
let keyFormat = createTestKeyFormat();
keyFormat.getAesCtrKeyFormat().setKeySize(keySize);
try {
manager.getKeyFactory().newKey(keyFormat);
} catch (e) {
assertEquals(
'CustomError: unsupported AES key size: ' + keySize, e.toString());
return;
}
fail('An exception should be thrown.');
},
async testNewKeyIvSizeOutOfRange() {
const /** Array<number> */ ivSizeOutOfRange = [10, 18];
const manager = new AesCtrHmacAeadKeyManager();
let keyFormat = createTestKeyFormat();
const ivSizeOutOfRangeLength = ivSizeOutOfRange.length;
for (let i = 0; i < ivSizeOutOfRangeLength; i++) {
keyFormat.getAesCtrKeyFormat().getParams().setIvSize(ivSizeOutOfRange[i]);
try {
manager.getKeyFactory().newKey(keyFormat);
} catch (e) {
assertEquals(
'CustomError: Invalid AES CTR HMAC key format: IV size is ' +
'out of range: ' + ivSizeOutOfRange[i],
e.toString());
continue;
}
fail('An exception should be thrown.');
}
},
// newKey method -- bad parametrs of HMAC KEY format
async testNewKeySmallHmacKeySize() {
const /** number */ keySize = 11;
const manager = new AesCtrHmacAeadKeyManager();
let keyFormat = createTestKeyFormat();
keyFormat.getHmacKeyFormat().setKeySize(keySize);
try {
manager.getKeyFactory().newKey(keyFormat);
} catch (e) {
assertEquals(
'CustomError: Invalid AES CTR HMAC key format: HMAC key is' +
' too small: ' + keySize,
e.toString());
return;
}
fail('An exception should be thrown.');
},
async testNewKeyHashTypeUnsupported() {
const manager = new AesCtrHmacAeadKeyManager();
let keyFormat = createTestKeyFormat();
keyFormat.getHmacKeyFormat().getParams().setHash(PbHashType.UNKNOWN_HASH);
try {
manager.getKeyFactory().newKey(keyFormat);
} catch (e) {
assertEquals('CustomError: Unknown hash type.', e.toString());
return;
}
fail('An exception should be thrown.');
},
async testNewKeySmallTagSize() {
const SMALL_TAG_SIZE = 8;
const manager = new AesCtrHmacAeadKeyManager();
let keyFormat = createTestKeyFormat();
keyFormat.getHmacKeyFormat().getParams().setTagSize(SMALL_TAG_SIZE);
try {
manager.getKeyFactory().newKey(keyFormat);
} catch (e) {
assertEquals(
'CustomError: Invalid HMAC params: tag size ' + SMALL_TAG_SIZE +
' is too small.',
e.toString());
return;
}
fail('An exception should be thrown.');
},
async testNewKeyBigTagSizeForHashType() {
const tagSizes = [
{'hashType': PbHashType.SHA1, 'tagSize': 22},
{'hashType': PbHashType.SHA256, 'tagSize': 34},
{'hashType': PbHashType.SHA512, 'tagSize': 66},
];
const manager = new AesCtrHmacAeadKeyManager();
let keyFormat = createTestKeyFormat();
const tagSizesLength = tagSizes.length;
for (let i = 0; i < tagSizesLength; i++) {
keyFormat.getHmacKeyFormat().getParams().setHash(tagSizes[i]['hashType']);
keyFormat.getHmacKeyFormat().getParams().setTagSize(
tagSizes[i]['tagSize']);
try {
manager.getKeyFactory().newKey(keyFormat);
} catch (e) {
assertEquals(
'CustomError: Invalid HMAC params: tag size ' +
tagSizes[i]['tagSize'] + ' is out of range.',
e.toString());
continue;
}
fail('An exception should be thrown.');
}
},
async testNewKeyViaFormatProto() {
const manager = new AesCtrHmacAeadKeyManager();
const keyFormat = createTestKeyFormat();
const key = /** @type {!PbAesCtrHmacAeadKey}*/ (
manager.getKeyFactory().newKey(keyFormat));
// testing AES CTR key
assertEquals(
keyFormat.getAesCtrKeyFormat().getKeySize(),
key.getAesCtrKey().getKeyValue().length);
assertEquals(0, key.getAesCtrKey().getVersion());
assertEquals(
keyFormat.getAesCtrKeyFormat().getParams().getIvSize(),
key.getAesCtrKey().getParams().getIvSize());
// testing HMAC key
assertEquals(
keyFormat.getHmacKeyFormat().getKeySize(),
key.getHmacKey().getKeyValue().length);
assertEquals(0, key.getHmacKey().getVersion());
assertEquals(
keyFormat.getHmacKeyFormat().getParams().getHash(),
key.getHmacKey().getParams().getHash());
assertEquals(
keyFormat.getHmacKeyFormat().getParams().getTagSize(),
key.getHmacKey().getParams().getTagSize());
},
async testNewKeyViaSerializedFormatProto() {
const manager = new AesCtrHmacAeadKeyManager();
const keyFormat = createTestKeyFormat();
const serializedKeyFormat = keyFormat.serializeBinary();
const key = /** @type {!PbAesCtrHmacAeadKey} */ (
manager.getKeyFactory().newKey(serializedKeyFormat));
// testing AES CTR key
assertEquals(
keyFormat.getAesCtrKeyFormat().getKeySize(),
key.getAesCtrKey().getKeyValue().length);
assertEquals(0, key.getAesCtrKey().getVersion());
assertEquals(
keyFormat.getAesCtrKeyFormat().getParams().getIvSize(),
key.getAesCtrKey().getParams().getIvSize());
// testing HMAC key
assertEquals(
keyFormat.getHmacKeyFormat().getKeySize(),
key.getHmacKey().getKeyValue().length);
assertEquals(0, key.getHmacKey().getVersion());
assertEquals(
keyFormat.getHmacKeyFormat().getParams().getHash(),
key.getHmacKey().getParams().getHash());
assertEquals(
keyFormat.getHmacKeyFormat().getParams().getTagSize(),
key.getHmacKey().getParams().getTagSize());
},
/////////////////////////////////////////////////////////////////////////////
// tests for NewKeyData method
async testNewKeyDataBadSerializedKey() {
const serializedKeyFormats = [new Uint8Array(1), new Uint8Array(0)];
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
const serializedKeyFormatsLength = serializedKeyFormats.length;
for (let i = 0; i < serializedKeyFormatsLength; i++) {
try {
aeadKeyManager.getKeyFactory().newKeyData(serializedKeyFormats[i]);
} catch (e) {
assertEquals(
'CustomError: Could not parse the given Uint8Array as a ' +
'serialized proto of ' + KEY_TYPE,
e.toString());
continue;
}
fail(
'An exception should be thrown for the string: ' +
serializedKeyFormats[i]);
}
},
async testNewKeyDataFromValidKey() {
const keyFormat = createTestKeyFormat();
const serializedKeyFormat = keyFormat.serializeBinary();
const manager = new AesCtrHmacAeadKeyManager();
const keyData = manager.getKeyFactory().newKeyData(serializedKeyFormat);
assertEquals(KEY_TYPE, keyData.getTypeUrl());
assertEquals(
PbKeyData.KeyMaterialType.SYMMETRIC, keyData.getKeyMaterialType());
const key = PbAesCtrHmacAeadKey.deserializeBinary(keyData.getValue());
assertEquals(
keyFormat.getAesCtrKeyFormat().getKeySize(),
key.getAesCtrKey().getKeyValue().length);
assertEquals(
keyFormat.getHmacKeyFormat().getKeySize(),
key.getHmacKey().getKeyValue().length);
},
/////////////////////////////////////////////////////////////////////////////
// tests for getPrimitive method
async testGetPrimitiveUnsupportedKeyDataType() {
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
let keyData = createTestKeyData().setTypeUrl('bad type url');
try {
await aeadKeyManager.getPrimitive(Aead, keyData);
} catch (e) {
assertEquals(
'CustomError: Key type ' + keyData.getTypeUrl() +
' is not supported. This key manager supports ' +
KEY_TYPE + '.', e.toString());
return;
}
fail('An exception should be thrown');
},
async testGetPrimitiveUnsupportedKeyType() {
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
let key = new PbAesCtrKey();
try {
await aeadKeyManager.getPrimitive(Aead, key);
} catch (e) {
assertEquals(
'CustomError: Given key type is not supported. ' +
'This key manager supports ' + KEY_TYPE + '.', e.toString());
return;
}
fail('An exception should be thrown');
},
async testGetPrimitiveBadVersion() {
const version = 1;
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
let /** PbAesCtrHmacAeadKey */ key = createTestKey();
key.getAesCtrKey().setVersion(version);
try {
await aeadKeyManager.getPrimitive(Aead, key);
} catch (e) {
assertEquals(
'CustomError: Version is out of bound, must be between 0 ' +
'and ' + VERSION + '.',
e.toString());
return;
}
fail('An exception should be thrown');
},
async testGetPrimitiveShortAesCtrKey() {
const keySize = 5;
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
let /** PbAesCtrHmacAeadKey */ key = createTestKey();
key.getAesCtrKey().setKeyValue(new Uint8Array(keySize));
try {
await aeadKeyManager.getPrimitive(Aead, key);
} catch (e) {
assertEquals(
'CustomError: unsupported AES key size: ' + keySize, e.toString());
return;
}
fail('An exception should be thrown');
},
async testGetPrimitiveAesCtrKeySmallIvSize() {
const /** Array<number> */ ivSizeOutOfRange = [9, 19];
const manager = new AesCtrHmacAeadKeyManager();
let /** PbAesCtrHmacAeadKey */ key = createTestKey();
const ivSizeOutOfRangeLength = ivSizeOutOfRange.length;
for (let i = 0; i < ivSizeOutOfRangeLength; i++) {
key.getAesCtrKey().getParams().setIvSize(ivSizeOutOfRange[i]);
try {
await manager.getPrimitive(Aead, key);
} catch (e) {
assertEquals(
'CustomError: Invalid AES CTR HMAC key format: IV size is ' +
'out of range: ' + ivSizeOutOfRange[i],
e.toString());
continue;
}
fail('An exception should be thrown.');
}
},
async testGetPrimitiveShortHmacKey() {
const keySize = 5;
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
let /** PbAesCtrHmacAeadKey */ key = createTestKey();
key.getHmacKey().setKeyValue(new Uint8Array(keySize));
try {
await aeadKeyManager.getPrimitive(Aead, key);
} catch (e) {
assertEquals(
'CustomError: Invalid AES CTR HMAC key format: HMAC key is' +
' too small: ' + keySize,
e.toString());
return;
}
fail('An exception should be thrown');
},
async testGetPrimitiveHmacKeyUnsupportedHashType() {
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
let /** PbAesCtrHmacAeadKey */ key = createTestKey();
key.getHmacKey().getParams().setHash(PbHashType.UNKNOWN_HASH);
try {
await aeadKeyManager.getPrimitive(Aead, key);
} catch (e) {
assertEquals('CustomError: Unknown hash type.', e.toString());
return;
}
fail('An exception should be thrown');
},
async testGetPrimitiveHmacKeySmallTagSize() {
const SMALL_TAG_SIZE = 9;
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
let /** PbAesCtrHmacAeadKey */ key = createTestKey();
key.getHmacKey().getParams().setTagSize(SMALL_TAG_SIZE);
try {
await aeadKeyManager.getPrimitive(Aead, key);
} catch (e) {
assertEquals(
'CustomError: Invalid HMAC params: tag size ' + SMALL_TAG_SIZE +
' is too small.',
e.toString());
return;
}
fail('An exception should be thrown');
},
async testGetPrimitiveHmacBigTagSize() {
const tagSizes = [
{'hashType': PbHashType.SHA1, 'tagSize': 22},
{'hashType': PbHashType.SHA256, 'tagSize': 34},
{'hashType': PbHashType.SHA512, 'tagSize': 66},
];
const manager = new AesCtrHmacAeadKeyManager();
let /** PbAesCtrHmacAeadKey */ key = createTestKey();
const tagSizesLength = tagSizes.length;
for (let i = 0; i < tagSizesLength; i++) {
key.getHmacKey().getParams().setHash(tagSizes[i]['hashType']);
key.getHmacKey().getParams().setTagSize(tagSizes[i]['tagSize']);
try {
await manager.getPrimitive(Aead, key);
} catch (e) {
assertEquals(
'CustomError: Invalid HMAC params: tag size ' +
tagSizes[i]['tagSize'] + ' is out of range.',
e.toString());
continue;
}
fail('An exception should be thrown.');
}
},
// tests for getting primitive from valid key/keyData
async testGetPrimitiveFromKey() {
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
const key = createTestKey();
const plaintext = Random.randBytes(8);
const aad = Random.randBytes(8);
const /** Aead */ primitive = await aeadKeyManager.getPrimitive(Aead, key);
const ciphertext = await primitive.encrypt(plaintext, aad);
const decryptedCiphertext = await primitive.decrypt(ciphertext, aad);
assertObjectEquals(plaintext, decryptedCiphertext);
},
async testGetPrimitiveFromKeyData() {
const aeadKeyManager = new AesCtrHmacAeadKeyManager();
const keyData = createTestKeyData();
const plaintext = Random.randBytes(8);
const aad = Random.randBytes(8);
const /** Aead */ primitive =
await aeadKeyManager.getPrimitive(Aead, keyData);
const ciphertext = await primitive.encrypt(plaintext, aad);
const decryptedCiphertext = await primitive.decrypt(ciphertext, aad);
assertObjectEquals(plaintext, decryptedCiphertext);
},
async testGetPrimitiveUnsupportedPrimitive() {
const manager = new AesCtrHmacAeadKeyManager();
const keyData = createTestKeyData();
try {
await manager.getPrimitive(Mac, keyData);
} catch (e) {
assertEquals(
'CustomError: Requested primitive type which is not ' +
'supported by this key manager.',
e.toString());
return;
}
fail('An exception should be thrown.');
},
/////////////////////////////////////////////////////////////////////////////
// tests for getVersion, getKeyType and doesSupport methods
async testGetVersionShouldBeZero() {
const manager = new AesCtrHmacAeadKeyManager();
assertEquals(0, manager.getVersion());
},
async testGetKeyTypeShouldBeAesCtrHmacAeadKey() {
const manager = new AesCtrHmacAeadKeyManager();
assertEquals(KEY_TYPE, manager.getKeyType());
},
async testDoesSupportShouldSupportAesCtrHmacAeadKey() {
const manager = new AesCtrHmacAeadKeyManager();
assertTrue(manager.doesSupport(KEY_TYPE));
},
async testGetPrimitiveTypeShouldBeAead() {
const manager = new AesCtrHmacAeadKeyManager();
assertEquals(Aead, manager.getPrimitiveType());
},
});