import BaseService from "@Services/BaseService"; import { Service } from "typedi"; import { BackendVariables } from "@Common/config/variables/Variables"; import crypto from "crypto"; @Service() export default class CryptoService extends BaseService { private key: CryptoKey; private jwkKey: any; constructor(protected variables: BackendVariables) { super(); this.key = new CryptoKey(); this.jwkKey = { kty: "oct", k: variables.KEY_DATA, alg: "A256GCM", ext: true, }; } private async getKey() { if (!this.key) this.key = await crypto.subtle.importKey("jwk", this.jwkKey, {name: "AES-GCM"}, false, ["encrypt", "decrypt"]); return this.key; } public getTextEncoderDecoder() { return { encoder: new TextEncoder(), decoder: new TextDecoder("utf-8") } } /** * @description : encrypt data * @throws {Error} If data cannot be encrypted */ public async encrypt(data: any) { const { encoder, decoder } = this.getTextEncoderDecoder(); const encodedData = encoder.encode(data); const iv = crypto.getRandomValues(new Uint8Array(16)); const key = await this.getKey(); const cipherData = await crypto.subtle.encrypt( { name: "AES-GCM", iv, }, key, encodedData, ); const cipherText = decoder.decode(cipherData); const ivStringified = decoder.decode(iv); return { cipherText, ivStringified }; } /** * @description : decrypt data with an initialization vector * @throws {Error} If data cannot be decrypted */ public async decrypt(cipherText: string, ivStringified: string): Promise { const { encoder, decoder } = this.getTextEncoderDecoder(); const cipherData = encoder.encode(cipherText); const iv = encoder.encode(ivStringified); const key = await this.getKey(); const decryptedData = await crypto.subtle.decrypt( { name: "AES-GCM", iv, }, key, cipherData, ); return decoder.decode(decryptedData); } }