UNPKG

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.

102 lines (95 loc) 3.72 kB
/** * @license * Copyright 2020 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import {Aead} from '../aead/internal/aead'; import {SecurityException} from '../exception/security_exception'; import {Mac} from '../mac/internal/mac'; import * as aesCtr from './aes_ctr'; import * as Bytes from './bytes'; import * as hmac from './hmac'; import {IndCpaCipher} from './ind_cpa_cipher'; import * as Validators from './validators'; /** * This primitive performs an encrypt-then-Mac operation on plaintext and * additional authenticated data (aad). * * The Mac is computed over `aad || ciphertext || size of aad`, thus it * doesn't violate https://en.wikipedia.org/wiki/Horton_Principle. * * This implementation is based on * http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05. * * @final */ export class EncryptThenAuthenticate extends Aead { /** * @param ivSize the IV size in bytes * @param tagSize the MAC tag size in bytes * @throws {InvalidArgumentsException} */ constructor( private readonly cipher: IndCpaCipher, private readonly ivSize: number, private readonly mac: Mac, private readonly tagSize: number) { super(); } /** * The plaintext is encrypted with an {@link IndCpaCipher}, then MAC * is computed over `aad || ciphertext || t` where t is aad's length in bits * represented as 64-bit bigendian unsigned integer. The final ciphertext * format is `ind-cpa ciphertext || mac`. * */ async encrypt(plaintext: Uint8Array, associatedData = new Uint8Array(0)): Promise<Uint8Array> { Validators.requireUint8Array(plaintext); const payload = await this.cipher.encrypt(plaintext); Validators.requireUint8Array(associatedData); const aadLength = Bytes.fromNumber(associatedData.length * 8); const mac = await this.mac.computeMac( Bytes.concat(associatedData, payload, aadLength)); if (this.tagSize != mac.length) { throw new SecurityException( 'invalid tag size, expected ' + this.tagSize + ' but got ' + mac.length); } return Bytes.concat(payload, mac); } /** */ async decrypt(ciphertext: Uint8Array, associatedData = new Uint8Array(0)): Promise<Uint8Array> { Validators.requireUint8Array(ciphertext); if (ciphertext.length < this.ivSize + this.tagSize) { throw new SecurityException('ciphertext too short'); } const payload = new Uint8Array( ciphertext.subarray(0, ciphertext.length - this.tagSize)); Validators.requireUint8Array(associatedData); const aadLength = Bytes.fromNumber(associatedData.length * 8); const input = Bytes.concat(associatedData, payload, aadLength); const tag = new Uint8Array(ciphertext.subarray(payload.length)); const isValidMac = await this.mac.verifyMac(tag, input); if (!isValidMac) { throw new SecurityException('invalid MAC'); } return this.cipher.decrypt(payload); } } /** * @param ivSize the size of the IV * @param hmacHashAlgo accepted names are SHA-1, SHA-256 and SHA-512 * @param tagSize the size of the tag * @throws {InvalidArgumentsException} * @static */ export async function aesCtrHmacFromRawKeys( aesKey: Uint8Array, ivSize: number, hmacHashAlgo: string, hmacKey: Uint8Array, tagSize: number): Promise<EncryptThenAuthenticate> { Validators.requireUint8Array(aesKey); Validators.requireUint8Array(hmacKey); const cipher = await aesCtr.fromRawKey(aesKey, ivSize); const mac = await hmac.fromRawKey(hmacHashAlgo, hmacKey, tagSize); return new EncryptThenAuthenticate(cipher, ivSize, mac, tagSize); }