blob: c22c88817fea9f08134f503475e4b27454547b90 [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.PrimitiveSetTest');
goog.setTestOnly('tink.PrimitiveSetTest');
const Aead = goog.require('tink.Aead');
const CryptoFormat = goog.require('tink.CryptoFormat');
const PbKeyStatusType = goog.require('proto.google.crypto.tink.KeyStatusType');
const PbKeysetKey = goog.require('proto.google.crypto.tink.Keyset.Key');
const PbOutputPrefixType = goog.require('proto.google.crypto.tink.OutputPrefixType');
const PrimitiveSet = goog.require('tink.PrimitiveSet');
const SecurityException = goog.require('tink.exception.SecurityException');
const testSuite = goog.require('goog.testing.testSuite');
testSuite({
/////////////////////////////////////////////////////////////////////////////
// tests for addPrimitive method
testAddPrimitiveUnknownCryptoFormat() {
const primitive = new DummyAead1();
const key =
createKey().setOutputPrefixType(PbOutputPrefixType.UNKNOWN_PREFIX);
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
try {
primitiveSet.addPrimitive(primitive, key);
} catch (e) {
assertEquals(ExceptionText.unknownPrefixType(), e.toString());
return;
}
fail('An exception should be thrown.');
},
testAddPrimitiveNullPrimitive() {
const primitive = null;
const key = createKey();
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
try {
primitiveSet.addPrimitive(primitive, key);
} catch (e) {
assertEquals(ExceptionText.addingNullPrimitive(), e.toString());
return;
}
fail('An exception should be thrown.');
},
testAddPrimitiveNullKey() {
const primitive = new DummyAead1();
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
try {
primitiveSet.addPrimitive(primitive, null);
} catch (e) {
assertEquals(ExceptionText.addingNullKey(), e.toString());
return;
}
fail('An exception should be thrown.');
},
testAddPrimitiveMultipleTimesShouldWork() {
const key = createKey();
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
for (let i = 0; i < 4; i++) {
let primitive;
if (i % 2 === 0) {
primitive = new DummyAead1();
} else {
primitive = new DummyAead2();
}
const result = primitiveSet.addPrimitive(primitive, key);
assertObjectEquals(primitive, result.getPrimitive());
assertEquals(key.getStatus(), result.getKeyStatus());
assertEquals(key.getOutputPrefixType(), result.getOutputPrefixType());
assertObjectEquals(
CryptoFormat.getOutputPrefix(key), result.getIdentifier());
}
},
/////////////////////////////////////////////////////////////////////////////
// tests for getPrimitives method
testGetPrimitivesWhichWereNotAdded() {
// Fill in the structure with some primitives.
const numberOfAddedPrimitives = 12;
const primitiveSet = initPrimitiveSet(numberOfAddedPrimitives);
const key = createKey(/* opt_keyId = */ numberOfAddedPrimitives + 1);
const identifier = CryptoFormat.getOutputPrefix(key);
const result = primitiveSet.getPrimitives(identifier);
assertObjectEquals([], result);
},
testGetPrimitivesDifferentIdentifiers() {
// Fill in the structure with some primitives.
const n = 100;
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
let added = [];
for (let id = 0; id < n; id++) {
const legacyKeyType = ((id % 2) < 1);
const enabledKey = ((id % 4) < 2);
const key = createKey(id, legacyKeyType, enabledKey);
let primitive;
if ((id % 8) < 4) {
primitive = new DummyAead1();
} else {
primitive = new DummyAead2();
}
const res = primitiveSet.addPrimitive(primitive, key);
added.push({key: key, entry: res});
}
// Test that getPrimitives return correct value for them.
const addedLength = added.length;
for (let i = 0; i < addedLength; i++) {
const identifier = CryptoFormat.getOutputPrefix(added[i].key);
// Should return a set containing only one primitive as each added
// primitive has different identifier.
const expectedResult = [added[i].entry];
const result = primitiveSet.getPrimitives(identifier);
assertObjectEquals(expectedResult, result);
}
},
testGetPrimitivesSameIdentifiers() {
// Fill in the structure with some primitives.
const numberOfAddedPrimitives = 50;
const primitiveSet = initPrimitiveSet(numberOfAddedPrimitives);
// Add a group of primitives with same identifier.
const n = 12;
const keyId = 0xABCDEF98;
const legacyKeyType = false;
let expectedResult = [];
for (let i = 0; i < n; i++) {
const enabledKey = ((i % 2) < 1);
const key = createKey(keyId, legacyKeyType, enabledKey);
let primitive;
if ((i % 4) < 2) {
primitive = new DummyAead1();
} else {
primitive = new DummyAead2();
}
const res = primitiveSet.addPrimitive(primitive, key);
expectedResult.push(res);
}
const identifier =
CryptoFormat.getOutputPrefix(createKey(keyId, legacyKeyType));
const result = primitiveSet.getPrimitives(identifier);
assertObjectEquals(expectedResult, result);
},
/////////////////////////////////////////////////////////////////////////////
// tests for getRawPrimitives method
testGetRawPrimitives() {
const numberOfAddedPrimitives = 20;
const primitiveSet = initPrimitiveSet(numberOfAddedPrimitives);
// No RAW primitives were added.
let expectedResult = [];
let result = primitiveSet.getRawPrimitives();
assertObjectEquals(expectedResult, result);
// Add RAW primitives and check the result again after each adding.
let key = createKey().setOutputPrefixType(PbOutputPrefixType.RAW);
let addResult = primitiveSet.addPrimitive(new DummyAead1(), key);
expectedResult.push(addResult);
result = primitiveSet.getRawPrimitives();
assertObjectEquals(expectedResult, result);
key.setStatus(PbKeyStatusType.DISABLED);
addResult = primitiveSet.addPrimitive(new DummyAead2(), key);
expectedResult.push(addResult);
result = primitiveSet.getRawPrimitives();
assertObjectEquals(expectedResult, result);
},
/////////////////////////////////////////////////////////////////////////////
// tests for setPrimary and getPrimary methods
testSetPrimaryToNull() {
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
try {
primitiveSet.setPrimary(null);
} catch (e) {
assertEquals(ExceptionText.setPrimaryToNull(), e.toString());
return;
}
fail('An exception should be thrown.');
},
testSetPrimaryToNonholdedEntry() {
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
const entry = new PrimitiveSet.Entry(
new DummyAead1(), new Uint8Array(10), PbKeyStatusType.ENABLED,
PbOutputPrefixType.TINK);
try {
primitiveSet.setPrimary(entry);
} catch (e) {
assertEquals(ExceptionText.setPrimaryToMissingEntry(), e.toString());
return;
}
fail('An exception should be thrown.');
},
testSetPrimaryToEntryWithDisabledKeyStatus() {
const key = createKey(/* opt_keyId = */ 0x12345678,
/* opt_legacy = */ false, /* opt_enabled = */ false);
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
const primary = primitiveSet.addPrimitive(new DummyAead1(), key);
try {
primitiveSet.setPrimary(primary);
} catch (e) {
assertEquals(ExceptionText.setPrimaryToDisabled(), e.toString());
return;
}
fail('An exception should be thrown.');
},
testSetAndGetPrimary() {
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
assertEquals(null, primitiveSet.getPrimary());
const key1 = createKey(/* opt_keyId = */ 0xBBBCCC);
const result = primitiveSet.addPrimitive(new DummyAead1(), key1);
// Check that primary remains unset, set it to newly added and verify that
// it was set.
assertEquals(null, primitiveSet.getPrimary());
primitiveSet.setPrimary(result);
assertObjectEquals(result, primitiveSet.getPrimary());
const key2 = createKey(/* opt_keyId = */ 0xAAABBB);
// Add new primitive, check that it does not change primary.
const result2 = primitiveSet.addPrimitive(new DummyAead2(), key2);
assertObjectEquals(result, primitiveSet.getPrimary());
// Change the primary and verify the change.
primitiveSet.setPrimary(result2);
assertObjectEquals(result2, primitiveSet.getPrimary());
},
testSetPrimary_rawPrimitives() {
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
for (let i = 0; i < 3; i++) {
const key = createKey(i).setOutputPrefixType(PbOutputPrefixType.RAW);
primitiveSet.addPrimitive(new DummyAead1(), key);
}
const primaryKey =
createKey(255).setOutputPrefixType(PbOutputPrefixType.RAW);
const primaryEntry =
primitiveSet.addPrimitive(new DummyAead1(), primaryKey);
primitiveSet.setPrimary(primaryEntry);
},
testGetPrimaryType() {
const primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
assertObjectEquals(Aead, primitiveSet.getPrimitiveType());
},
});
// Helper classes and functions used for testing purposes.
class ExceptionText {
/** @return {string} */
static unknownPrefixType() {
return 'CustomError: Unsupported key prefix type.';
}
/** @return {string} */
static addingNullPrimitive() {
return 'CustomError: Primitive has to be non null.';
}
/** @return {string} */
static addingNullKey() {
return 'CustomError: Key has to be non null.';
}
/** @return {string} */
static setPrimaryToNull() {
return 'CustomError: Primary cannot be set to null.';
}
/** @return {string} */
static setPrimaryToMissingEntry() {
return 'CustomError: Primary cannot be set to an entry which is ' +
'not held by this primitive set.';
}
/** @return {string} */
static setPrimaryToDisabled() {
return 'CustomError: Primary has to be enabled.';
}
}
/**
* @implements {Aead}
* @final
*/
class DummyAead1 {
constructor() {}
/** @override */
encrypt(plaintext, aad) {
throw new SecurityException(
'Not implemented, intentended just for testing.');
}
/** @override */
decrypt(ciphertext, aad) {
throw new SecurityException(
'Not implemented, intentended just for testing.');
}
}
/**
* @implements {Aead}
* @final
*/
class DummyAead2 {
constructor() {}
/** @override */
encrypt(plaintext, aad) {
throw new SecurityException(
'Not implemented, intentended just for testing.');
}
/** @override */
decrypt(ciphertext, aad) {
throw new SecurityException(
'Not implemented, intentended just for testing.');
}
}
/**
* Function for creating primitive sets for testing purposes.
* Returns a primitive set containing n values with keyIds from {0, .. , n-1}.
* There are different combinations of
* primitive types ({DummyAead1, DummyAead2}),
* key status (enabled, disabled),
* and key types (legacy, tink).
*
* @param {number} n
* @return {!PrimitiveSet.PrimitiveSet}
*/
const initPrimitiveSet = function(n) {
let primitiveSet = new PrimitiveSet.PrimitiveSet(Aead);
// Set primary.
const primaryKey = createKey(/* opt_id = */ 0, /* opt_legacy = */ false,
/* opt_enabled = */ true);
const primary = primitiveSet.addPrimitive(new DummyAead1(), primaryKey);
primitiveSet.setPrimary(primary);
// Add n-1 other keys to primitive set.
for (let id = 1; id < n; id++) {
const legacyKeyType = ((id % 2) < 1);
const enabledKey = ((id % 4) < 2);
const key = createKey(id, legacyKeyType, enabledKey);
let primitive;
if ((id % 8) < 4) {
primitive = new DummyAead1();
} else {
primitive = new DummyAead2();
}
primitiveSet.addPrimitive(primitive, key);
}
return primitiveSet;
};
/**
* Function for creating keys for testing purposes.
* If doesn't set otherwise it generates a key with id 0x12345678, which is
* ENABLED and with prefix type TINK.
*
* @param {number=} opt_keyId
* @param {boolean=} opt_legacy
* @param {boolean=} opt_enabled
*
* @return{!PbKeysetKey}
*/
const createKey = function(opt_keyId = 0x12345678, opt_legacy = false,
opt_enabled = true) {
let key = new PbKeysetKey();
if (opt_enabled) {
key.setStatus(PbKeyStatusType.ENABLED);
} else {
key.setStatus(PbKeyStatusType.DISABLED);
}
if (opt_legacy) {
key.setOutputPrefixType(PbOutputPrefixType.LEGACY);
} else {
key.setOutputPrefixType(PbOutputPrefixType.TINK);
}
key.setKeyId(opt_keyId);
return key;
};