| /** |
| * @license |
| * Copyright 2020 Google LLC |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| import {SecurityException} from '../exception/security_exception'; |
| import {CryptoFormat} from '../internal/crypto_format'; |
| import * as PrimitiveSet from '../internal/primitive_set'; |
| import {PrimitiveWrapper} from '../internal/primitive_wrapper'; |
| import {PbKeyStatusType} from '../internal/proto'; |
| |
| import {HybridDecrypt} from './internal/hybrid_decrypt'; |
| |
| /** |
| * @final |
| */ |
| class WrappedHybridDecrypt extends HybridDecrypt { |
| // The constructor should be @private, but it is not supported by Closure |
| // (see https://github.com/google/closure-compiler/issues/2761). |
| constructor(private readonly hybridDecryptPrimitiveSet: |
| PrimitiveSet.PrimitiveSet<HybridDecrypt>) { |
| super(); |
| } |
| |
| static newHybridDecrypt(hybridDecryptPrimitiveSet: |
| PrimitiveSet.PrimitiveSet<HybridDecrypt>): |
| HybridDecrypt { |
| if (!hybridDecryptPrimitiveSet) { |
| throw new SecurityException('Primitive set has to be non-null.'); |
| } |
| return new WrappedHybridDecrypt(hybridDecryptPrimitiveSet); |
| } |
| |
| async decrypt(ciphertext: Uint8Array, opt_contextInfo?: Uint8Array) { |
| 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 primitives = |
| await this.hybridDecryptPrimitiveSet.getPrimitives(keyId); |
| const rawCiphertext = ciphertext.subarray( |
| CryptoFormat.NON_RAW_PREFIX_SIZE, ciphertext.length); |
| let decryptedText: Uint8Array|undefined; |
| try { |
| decryptedText = await this.tryDecryption( |
| primitives, rawCiphertext, opt_contextInfo); |
| } catch (e) { |
| } |
| if (decryptedText) { |
| return decryptedText; |
| } |
| } |
| const primitives = await this.hybridDecryptPrimitiveSet.getRawPrimitives(); |
| return this.tryDecryption(primitives, ciphertext, opt_contextInfo); |
| } |
| |
| /** |
| * Tries to decrypt the ciphertext using each entry in primitives and |
| * returns the ciphertext decrypted by first primitive which succeed. It |
| * throws an exception if no entry succeeds. |
| * |
| * |
| */ |
| private async tryDecryption( |
| primitives: Array<PrimitiveSet.Entry<HybridDecrypt>>, |
| ciphertext: Uint8Array, |
| opt_contextInfo?: Uint8Array|null): Promise<Uint8Array> { |
| const primitivesLength = primitives.length; |
| for (let i = 0; i < primitivesLength; i++) { |
| if (primitives[i].getKeyStatus() != PbKeyStatusType.ENABLED) { |
| continue; |
| } |
| const primitive = primitives[i].getPrimitive(); |
| let decryptionResult; |
| try { |
| decryptionResult = await primitive.decrypt(ciphertext, opt_contextInfo); |
| } catch (e) { |
| continue; |
| } |
| return decryptionResult; |
| } |
| throw new SecurityException('Decryption failed for the given ciphertext.'); |
| } |
| } |
| |
| export class HybridDecryptWrapper implements PrimitiveWrapper<HybridDecrypt> { |
| /** |
| */ |
| wrap(primitiveSet: PrimitiveSet.PrimitiveSet<HybridDecrypt>) { |
| return WrappedHybridDecrypt.newHybridDecrypt(primitiveSet); |
| } |
| |
| /** |
| */ |
| getPrimitiveType() { |
| return HybridDecrypt; |
| } |
| } |