blob: d0697661a659566c91f910291b1ddffaa1a35be9 [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.AeadWrapper');
const {Aead} = goog.require('google3.third_party.tink.javascript.aead.internal.aead');
const {CryptoFormat} = goog.require('google3.third_party.tink.javascript.internal.crypto_format');
const PrimitiveSet = goog.require('google3.third_party.tink.javascript.internal.primitive_set');
const {PrimitiveWrapper} = goog.require('google3.third_party.tink.javascript.internal.primitive_wrapper');
const Registry = goog.require('google3.third_party.tink.javascript.internal.registry');
const {SecurityException} = goog.require('google3.third_party.tink.javascript.exception.security_exception');
const {PbKeyStatusType} = goog.require('google3.third_party.tink.javascript.internal.proto');
/**
* @final
*/
class WrappedAead extends Aead {
/**
* @param {!PrimitiveSet.PrimitiveSet} aeadSet
*/
// The constructor should be @private, but it is not supported by Closure
// (see https://github.com/google/closure-compiler/issues/2761).
constructor(aeadSet) {
super();
/** @private @const {!PrimitiveSet.PrimitiveSet} */
this.aeadSet_ = aeadSet;
}
/**
* @param {!PrimitiveSet.PrimitiveSet} aeadSet
*
* @return {!Aead}
*/
static newAead(aeadSet) {
if (!aeadSet) {
throw new SecurityException('Primitive set has to be non-null.');
}
if (!aeadSet.getPrimary()) {
throw new SecurityException('Primary has to be non-null.');
}
return new WrappedAead(aeadSet);
}
/**
* @override
*/
async encrypt(plaintext, opt_associatedData) {
if (!plaintext) {
throw new SecurityException('Plaintext has to be non-null.');
}
const primitive = this.aeadSet_.getPrimary().getPrimitive();
const encryptedText =
await primitive.encrypt(plaintext, opt_associatedData);
const keyId = this.aeadSet_.getPrimary().getIdentifier();
const ciphertext = new Uint8Array(keyId.length + encryptedText.length);
ciphertext.set(keyId, 0);
ciphertext.set(encryptedText, keyId.length);
return ciphertext;
}
/**
* @override
*/
async decrypt(ciphertext, opt_associatedData) {
if (!ciphertext) {
throw new SecurityException('Ciphertext has to be non-null.');
}
if (ciphertext.length > CryptoFormat.NON_RAW_PREFIX_SIZE) {
const keyId = ciphertext.subarray(0, CryptoFormat.NON_RAW_PREFIX_SIZE);
const entries = await this.aeadSet_.getPrimitives(keyId);
const rawCiphertext = ciphertext.subarray(
CryptoFormat.NON_RAW_PREFIX_SIZE, ciphertext.length);
let /** @type {!Uint8Array} */ decryptedText;
try {
decryptedText = await this.tryDecryption_(
entries, rawCiphertext, opt_associatedData);
} catch (e) {
}
if (decryptedText) {
return decryptedText;
}
}
const entries = await this.aeadSet_.getRawPrimitives();
const decryptedText =
await this.tryDecryption_(entries, ciphertext, opt_associatedData);
return decryptedText;
}
/**
* Tries to decrypt the ciphertext using each entry in entriesArray and
* returns the ciphertext decrypted by first primitive which succeed. It
* throws an exception if no entry succeeds.
*
* @private
* @param {!Array<!PrimitiveSet.Entry>} entriesArray
* @param {!Uint8Array} ciphertext
* @param {?Uint8Array=} opt_associatedData
*
* @return {!Promise<!Uint8Array>}
*/
async tryDecryption_(entriesArray, ciphertext, opt_associatedData) {
const entriesArrayLength = entriesArray.length;
for (let i = 0; i < entriesArrayLength; i++) {
if (entriesArray[i].getKeyStatus() != PbKeyStatusType.ENABLED) {
continue;
}
const primitive = entriesArray[i].getPrimitive();
let decryptionResult;
try {
decryptionResult =
await primitive.decrypt(ciphertext, opt_associatedData);
} catch (e) {
continue;
}
return decryptionResult;
}
throw new SecurityException('Decryption failed for the given ciphertext.');
}
}
/**
* @implements {PrimitiveWrapper<Aead>}
*/
class AeadWrapper {
constructor() {}
/**
* @override
*/
wrap(primitiveSet) {
return WrappedAead.newAead(primitiveSet);
}
/**
* @override
*/
getPrimitiveType() {
return Aead;
}
static register() {
Registry.registerPrimitiveWrapper(new AeadWrapper());
}
}
exports = AeadWrapper;