blob: 51d6fd95b677cab55afc88b07c13b845906b7c6b [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.PrimitiveSet');
const CryptoFormat = goog.require('tink.CryptoFormat');
const PbKeyStatusType = goog.require('proto.google.crypto.tink.KeyStatusType');
const PbKeyset = goog.require('proto.google.crypto.tink.Keyset');
const PbOutputPrefixType = goog.require('proto.google.crypto.tink.OutputPrefixType');
const SecurityException = goog.require('tink.exception.SecurityException');
/**
* Auxiliary class for PrimitiveSet
* Entry-objects hold individual instances of primitives in the set.
*
* @template P
* @final
*/
class Entry {
/**
* @param {!P} primitive
* @param {!Uint8Array} identifier
* @param {!PbKeyStatusType} keyStatus
* @param {!PbOutputPrefixType} outputPrefixType
*/
constructor(primitive, identifier, keyStatus, outputPrefixType) {
/** @const @private {!P} */
this.primitive_ = primitive;
/** @const @private {!Uint8Array} */
this.identifier_ = identifier;
/** @const @private {!PbKeyStatusType} */
this.status_ = keyStatus;
/** @const @private {!PbOutputPrefixType} */
this.outputPrefixType_ = outputPrefixType;
}
/**
* @return {!P}
*/
getPrimitive() {
return this.primitive_;
}
/**
* @return {!Uint8Array}
*/
getIdentifier() {
return this.identifier_;
}
/**
* @return {!PbKeyStatusType}
*/
getKeyStatus() {
return this.status_;
}
/**
* @return {!PbOutputPrefixType}
*/
getOutputPrefixType() {
return this.outputPrefixType_;
}
}
/**
* A container class for a set of primitives (i.e. implementations of
* cryptographic primitives offered by Tink). It provides also additional
* properties for the primitives it holds. In particular, one of the primitives
* in the set can be distinguished as "the primary" one.
*
* PrimitiveSet is an auxiliary class used for supporting key rotation:
* primitives in a set correspond to keys in a keyset. Users will usually work
* with primitive instances which essentially wrap primitive sets. For example
* an instance of an Aead-primitive for a given keyset holds a set of
* Aead-primitives corresponding to the keys in the keyset, and uses the set
* members to do the actual crypto operations: to encrypt data the primary
* Aead-primitive from the set is used, and upon decryption the ciphertext's
* prefix determines the identifier of the primitive from the set.
*
* PrimitiveSet is a public class to allow its use in implementations of custom
* primitives.
*
* @template P
* @final
*/
class PrimitiveSet {
/**
* @param {!Object} primitiveType
*/
constructor(primitiveType) {
/**
* @private {!Object}
*/
this.primitiveType_ = primitiveType;
/**
* @private {?Entry<P>}
*/
this.primary_ = null;
// Keys have to be stored as strings as two Uint8Arrays holding the same
// digits are still different objects.
/**
* @private {!Map<string, !Array<!Entry<P>>>}
*/
this.identifierToPrimitivesMap_ = new Map();
}
/**
* Returns the type of primitives contained in this set.
*
* @return {!Object}
*/
getPrimitiveType() {
return this.primitiveType_;
}
/**
* Creates an entry in the primitive table and returns it.
*
* @param {!P} primitive
* @param {!PbKeyset.Key} key
*
* @return {!Entry<P>}
*/
addPrimitive(primitive, key) {
if (!primitive) {
throw new SecurityException('Primitive has to be non null.');
}
if (!key) {
throw new SecurityException('Key has to be non null.');
}
const identifier = CryptoFormat.getOutputPrefix(key);
const entry = new Entry(primitive, identifier, key.getStatus(),
key.getOutputPrefixType());
this.addPrimitiveToMap_(entry);
return entry;
}
/**
* Returns the entry with the primary primitive.
*
* @return {?Entry<P>}
*/
getPrimary() {
return this.primary_;
}
/**
* Sets given Entry as the primary one.
*
* @param {!Entry<P>} primitive
*/
setPrimary(primitive) {
if (!primitive) {
throw new SecurityException('Primary cannot be set to null.');
}
if (primitive.getKeyStatus() != PbKeyStatusType.ENABLED) {
throw new SecurityException('Primary has to be enabled.');
}
// There has to be exactly one key enabled with this identifier.
const entries = this.getPrimitives(primitive.getIdentifier());
let entryFound = false;
const entriesLength = entries.length;
for (let i = 0; i < entriesLength; i++) {
if (entries[i].getKeyStatus() === PbKeyStatusType.ENABLED) {
entryFound = true;
break;
}
}
if (!entryFound) {
throw new SecurityException(
'Primary cannot be set to an entry which is ' +
'not held by this primitive set.');
}
this.primary_ = primitive;
}
/**
* Returns all primitives using RAW prefix.
*
* @return {!Array<!Entry<P>>}
*/
getRawPrimitives() {
return this.getPrimitives(CryptoFormat.RAW_PREFIX);
}
/**
* Returns the entries with primitive identified with identifier.
*
* @param {!Uint8Array} identifier
*
* @return {!Array<!Entry<P>>}
*/
getPrimitives(identifier) {
const result = this.getPrimitivesFromMap_(identifier);
if (!result) {
return [];
} else {
return result;
}
}
/**
* Returns a set of primitives which corresponds to the given identifier.
*
* @private
* @param {!Uint8Array|string} identifier
*
* @return {!Array<!Entry<P>>|undefined}
*/
getPrimitivesFromMap_(identifier) {
if (identifier instanceof Uint8Array) {
identifier = [...identifier].toString();
}
return this.identifierToPrimitivesMap_.get(identifier);
}
/**
* Add primitive to map.
*
* @private
* @param {!Entry<P>} entry
*/
addPrimitiveToMap_(entry) {
const identifier = entry.getIdentifier();
const id = [...identifier].toString();
let existing = this.getPrimitivesFromMap_(id);
if (!existing) {
this.identifierToPrimitivesMap_.set(id, [entry]);
} else {
existing.push(entry);
this.identifierToPrimitivesMap_.set(id, existing);
}
}
}
goog.exportSymbol('tink.Entry', Entry);
goog.exportSymbol('tink.PrimitiveSet', PrimitiveSet);
exports = {
Entry,
PrimitiveSet,
};