import { Injectable } from '@nestjs/common'; import * as crypto from 'crypto'; import { ConfigurationService } from 'src/modules/config/config.service'; import { v4 } from 'uuid'; import * as CSRF from 'csrf'; import { Request } from 'express'; const IV_LENGTH = 16; const ALGORITHM = 'aes-256-cbc'; @Injectable() export class TokenService { public csrf = new CSRF({ saltLength: 16, secretLength: 32, }); constructor(private config: ConfigurationService) {} public verifyCSRF(req: Request, token?: string): boolean { return this.csrf.verify(req.cookies.XSRF, token || req.body._csrf); } public generateString(length: number): string { return crypto.randomBytes(length).toString('hex').slice(0, length); } public generateSecret(): string { return crypto.randomBytes(256 / 8).toString('hex'); } public insecureHash(input: string): string { return crypto.createHash('md5').update(input).digest('hex'); } public createUUID(): string { return v4(); } // https://stackoverflow.com/q/52212430 /** * Symmetric encryption function * @param text String to encrypt * @param key Encryption key * @returns Encrypted text */ public encrypt(text: string, key: string): string { const iv = crypto.randomBytes(IV_LENGTH); const cipher = crypto.createCipheriv( ALGORITHM, Buffer.from(key, 'hex'), iv, ); let encrypted = cipher.update(text); encrypted = Buffer.concat([encrypted, cipher.final()]); return `${iv.toString('hex')}:${encrypted.toString('hex')}`; } /** * Symmetric decryption function * @param text Encrypted string * @param key Decryption key * @returns Decrypted text */ public decrypt(text: string, key: string): string { const [iv, encryptedText] = text .split(':') .map((part) => Buffer.from(part, 'hex')); const decipher = crypto.createDecipheriv( ALGORITHM, Buffer.from(key, 'hex'), iv, ); let decrypted = decipher.update(encryptedText); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted.toString(); } public async encryptChallenge( challenge: Record, ): Promise { return this.encrypt( JSON.stringify(challenge), this.config.get('app.challenge_secret'), ); } public async decryptChallenge( challenge: string, ): Promise> { return JSON.parse( this.decrypt(challenge, this.config.get('app.challenge_secret')), ); } }