pooja-docucomb-tink-crypto
Version:
A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.
137 lines (120 loc) • 3.88 kB
text/typescript
/**
* @license
* Copyright 2022 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {InvalidArgumentsException} from '../../exception/invalid_arguments_exception';
import {SecurityException} from '../../exception/security_exception';
import * as bytes from '../../subtle/bytes';
/**
* The only supported IV size.
*
*/
export const IV_SIZE_IN_BYTES: number = 12;
/**
* The only supported tag size.
*
*/
const TAG_SIZE_IN_BYTES: number = 16;
/**
* Insecure version of `subtle/aes_gcm.ts` that allows the caller to set
* the IV.
*
* @final
*/
export class InsecureIvAesGcm {
private readonly key: CryptoKey;
private readonly prependIv: boolean;
constructor({key, prependIv}:
{readonly key: CryptoKey, readonly prependIv: boolean}) {
this.key = key;
this.prependIv = prependIv;
}
async encrypt(
iv: Uint8Array, plaintext: Uint8Array,
associatedData?: Uint8Array): Promise<Uint8Array> {
if (iv.length !== IV_SIZE_IN_BYTES) {
throw new SecurityException(`IV must be ${IV_SIZE_IN_BYTES} bytes`);
}
const alg: AesGcmParams = {
'name': 'AES-GCM',
'iv': iv,
'tagLength': TAG_SIZE_IN_BYTES * 8
};
if (associatedData) {
alg.additionalData = associatedData;
}
const ciphertext =
await self.crypto.subtle.encrypt(alg, this.key, plaintext);
return (
this.prependIv ? bytes.concat(iv, new Uint8Array(ciphertext)) :
new Uint8Array(ciphertext));
}
async decrypt(
iv: Uint8Array, ciphertext: Uint8Array,
associatedData?: Uint8Array): Promise<Uint8Array> {
const expectedLength = this.prependIv ?
IV_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES :
TAG_SIZE_IN_BYTES;
if (ciphertext.length < expectedLength) {
throw new SecurityException('ciphertext too short');
}
if (iv.length !== IV_SIZE_IN_BYTES) {
throw new SecurityException(`IV must be ${IV_SIZE_IN_BYTES} bytes`);
}
const alg: AesGcmParams = {
'name': 'AES-GCM',
'iv': iv,
'tagLength': TAG_SIZE_IN_BYTES * 8
};
if (associatedData) {
alg.additionalData = associatedData;
}
const ciphertextWithoutIv = this.prependIv ?
new Uint8Array(ciphertext.subarray(IV_SIZE_IN_BYTES)) :
ciphertext;
try {
return new Uint8Array(
await self.crypto.subtle.decrypt(alg, this.key, ciphertextWithoutIv));
// Preserving old behavior when moving to
// https://www.typescriptlang.org/tsconfig#useUnknownInCatchVariables
// tslint:disable-next-line:no-any
} catch (e: any) {
throw new SecurityException(e.toString());
}
}
}
/**
* Returns an instantiated `InsecureIvAesGcm` given a raw byte array `key`
* and `prependIv` flag.
*/
export async function insecureIvAesGcmFromRawKey(
{key, prependIv}: {key: Uint8Array, prependIv: boolean}):
Promise<InsecureIvAesGcm> {
validateAesKeySize(key.length);
const webCryptoKey = await self.crypto.subtle.importKey(
/* format */
'raw', key,
/* keyData */
{'name': 'AES-GCM', 'length': key.length},
/* algo */
false,
/* extractable*/
['encrypt', 'decrypt']);
/* usage */
return new InsecureIvAesGcm({key: webCryptoKey, prependIv});
}
/**
* Validates AES key sizes (only 128-bit and 256-bit keys are supported).
*
* @param n the key size in bytes
* @throws {!InvalidArgumentsException}
*
* Copied from `javascript/subtle/validators`
* TODO(b/201071402#comment8): Delete once circular dependency is resolved
*/
function validateAesKeySize(n: number) {
if (![16, 32].includes(n)) {
throw new InvalidArgumentsException('unsupported AES key size: ${n}');
}
}