| // 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. |
| //////////////////////////////////////////////////////////////////////////////// |
| import {Aead} from '../aead/internal/aead'; |
| import {SecurityException} from '../exception/security_exception'; |
| import {HybridDecrypt} from '../hybrid/internal/hybrid_decrypt'; |
| |
| import {EciesAeadHkdfDemHelper} from './ecies_aead_hkdf_dem_helper'; |
| import {EciesHkdfKemRecipient, fromJsonWebKey as kemRecipientFromJsonWebKey} from './ecies_hkdf_kem_recipient'; |
| import * as EllipticCurves from './elliptic_curves'; |
| |
| /** |
| * Implementation of ECIES AEAD HKDF hybrid decryption. |
| * |
| * @final |
| */ |
| export class EciesAeadHkdfHybridDecrypt implements HybridDecrypt { |
| private readonly kemRecipient_: EciesHkdfKemRecipient; |
| private readonly hkdfHash_: string; |
| private readonly pointFormat_: EllipticCurves.PointFormatType; |
| private readonly demHelper_: EciesAeadHkdfDemHelper; |
| private readonly headerSize_: number; |
| private readonly hkdfSalt_: Uint8Array|undefined; |
| |
| /** |
| * @param hkdfHash the name of the HMAC algorithm, accepted names |
| * are: SHA-1, SHA-256 and SHA-512. |
| */ |
| constructor( |
| recipientPrivateKey: JsonWebKey, kemRecipient: EciesHkdfKemRecipient, |
| hkdfHash: string, pointFormat: EllipticCurves.PointFormatType, |
| demHelper: EciesAeadHkdfDemHelper, opt_hkdfSalt?: Uint8Array) { |
| if (!recipientPrivateKey) { |
| throw new SecurityException('Recipient private key has to be non-null.'); |
| } |
| if (!kemRecipient) { |
| throw new SecurityException('KEM recipient has to be non-null.'); |
| } |
| if (!hkdfHash) { |
| throw new SecurityException('HKDF hash algorithm has to be non-null.'); |
| } |
| if (!pointFormat) { |
| throw new SecurityException('Point format has to be non-null.'); |
| } |
| if (!demHelper) { |
| throw new SecurityException('DEM helper has to be non-null.'); |
| } |
| const {crv} = recipientPrivateKey; |
| if (!crv) { |
| throw new SecurityException('Curve has to be defined.'); |
| } |
| const curveType = EllipticCurves.curveFromString(crv); |
| const headerSize = |
| EllipticCurves.encodingSizeInBytes(curveType, pointFormat); |
| this.kemRecipient_ = kemRecipient; |
| this.hkdfHash_ = hkdfHash; |
| this.pointFormat_ = pointFormat; |
| this.demHelper_ = demHelper; |
| this.headerSize_ = headerSize; |
| this.hkdfSalt_ = opt_hkdfSalt; |
| } |
| |
| /** |
| * Decrypts ciphertext using opt_contextInfo as info parameter of the |
| * underlying HKDF. |
| * |
| * @override |
| */ |
| async decrypt(ciphertext: Uint8Array, associatedData?: Uint8Array) { |
| if (ciphertext.length < this.headerSize_) { |
| throw new SecurityException('Ciphertext is too short.'); |
| } |
| |
| // Split the ciphertext to KEM token and AEAD ciphertext. |
| const kemToken = ciphertext.slice(0, this.headerSize_); |
| const ciphertextBody = |
| ciphertext.slice(this.headerSize_, ciphertext.length); |
| const aead = await this.getAead_(kemToken, associatedData); |
| return aead.decrypt(ciphertextBody); |
| } |
| |
| private async getAead_( |
| kemToken: Uint8Array, opt_contextInfo?: Uint8Array|null): Promise<Aead> { |
| // Variable hkdfInfo is not optional for decapsulate method. Thus it should |
| // be an empty array in case that it is not defined by the caller of decrypt |
| // method. |
| if (!opt_contextInfo) { |
| opt_contextInfo = new Uint8Array(0); |
| } |
| const symmetricKey = await this.kemRecipient_.decapsulate( |
| kemToken, this.demHelper_.getDemKeySizeInBytes(), this.pointFormat_, |
| this.hkdfHash_, opt_contextInfo, this.hkdfSalt_); |
| return this.demHelper_.getAead(symmetricKey); |
| } |
| } |
| |
| /** |
| * @param hkdfHash the name of the HMAC algorithm, accepted names |
| * are: SHA-1, SHA-256 and SHA-512. |
| */ |
| export async function fromJsonWebKey( |
| recipientPrivateKey: JsonWebKey, hkdfHash: string, |
| pointFormat: EllipticCurves.PointFormatType, |
| demHelper: EciesAeadHkdfDemHelper, |
| opt_hkdfSalt?: Uint8Array): Promise<HybridDecrypt> { |
| if (!recipientPrivateKey) { |
| throw new SecurityException('Recipient private key has to be non-null.'); |
| } |
| if (!hkdfHash) { |
| throw new SecurityException('HKDF hash algorithm has to be non-null.'); |
| } |
| if (!pointFormat) { |
| throw new SecurityException('Point format has to be non-null.'); |
| } |
| if (!demHelper) { |
| throw new SecurityException('DEM helper has to be non-null.'); |
| } |
| if (!recipientPrivateKey) { |
| throw new SecurityException('Recipient private key has to be non-null.'); |
| } |
| const kemRecipient = await kemRecipientFromJsonWebKey(recipientPrivateKey); |
| return new EciesAeadHkdfHybridDecrypt( |
| recipientPrivateKey, kemRecipient, hkdfHash, pointFormat, demHelper, |
| opt_hkdfSalt); |
| } |