import { BinaryToTextEncoding , createCipheriv , createDecipheriv , createHash , Encoding , randomBytes , } from 'crypto' ; import { Injectable } from '@nestjs/common' ; import { LowEntropyArgumentException } from './exceptions' ; const NONCE_LENGTH = 12 ; const AUTHTAG_LENGTH = 16 ; const CIPHER_HEAD_LENGTH = NONCE_LENGTH + AUTHTAG_LENGTH ; export const RANDOM_MIN_ENTROPY = 32 ; @ Injectable ( ) export class CryptographyService { see https param key param data returns encryptSymetric ( key , data ) { const buffer = this . encrypt ( key , data ) ; return buffer . toString ( 'base64' ) ; } see https param key param cipher returns decryptSymetric ( key , data ) { const cipher = Buffer . from ( data , 'base64' ) ; return this . decrypt ( key , cipher ) ; } hash ( data , inputEncoding = 'utf8' , alg = 'sha256' , outputDigest = 'hex' ) { const hash = createHash ( alg ) ; hash . update ( data , inputEncoding ) ; return hash . digest ( outputDigest ) ; } param byteLength genRandomString ( byteLength ) { if ( byteLength < RANDOM_MIN_ENTROPY ) { throw new LowEntropyArgumentException ( byteLength ) ; } return randomBytes ( byteLength ) . toString ( 'hex' ) ; } see https param key param data returns private encrypt ( key , data ) { const nonce = randomBytes ( NONCE_LENGTH ) ; const cipher = createCipheriv ( 'aes-256-gcm' , key , nonce , { authTagLength : AUTHTAG_LENGTH , } ) ; const ciphertext = cipher . update ( data , 'utf8' ) ; cipher . final ( ) ; const tag = cipher . getAuthTag ( ) ; return Buffer . concat ( [ nonce , tag , ciphertext ] ) ; } see https param key param cipher returns decrypt ( key , cipher ) { if ( Buffer . byteLength ( cipher ) <= CIPHER_HEAD_LENGTH ) { TODO see https throw new Error ( 'Authentication failed !' ) ; } const nonce = cipher . slice ( 0 , 12 ) ; const tag = cipher . slice ( 12 , 28 ) ; const ciphertext = cipher . slice ( 28 ) ; const decipher = createDecipheriv ( 'aes-256-gcm' , key , nonce , { authTagLength : AUTHTAG_LENGTH , } ) ; decipher . setAuthTag ( tag ) ; const receivedPlaintext = decipher . update ( ciphertext , null , 'utf8' ) ; try { decipher . final ( ) ; } catch ( err ) { TODO see https throw new Error ( 'Authentication failed !' ) ; } return receivedPlaintext ; } }