import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'crypto'; import * as bcrypt from 'bcrypt'; import * as crypto from 'crypto'; export async function hash(data: string): Promise { return bcrypt.hash(data, 10); } export async function generateApiKey(): Promise<{ apiKey: string; apiSecret: string; apiKeyHash: string; apiSecretHash: string; }> { const apiKey = `goa_${crypto.randomBytes(16).toString('hex')}`; const apiSecret = crypto.randomBytes(32).toString('hex'); const [apiKeyHash, apiSecretHash] = await Promise.all([hash(apiKey), hash(apiSecret)]); return { apiKey, apiSecret, apiKeyHash, apiSecretHash, }; } export class CryptoUtil { private static readonly ALGORITHM = 'aes-256-gcm'; private static readonly SALT_LENGTH = 16; private static readonly TAG_LENGTH = 16; private static readonly IV_LENGTH = 16; static encrypt(data: string, password: string): string { const salt = randomBytes(CryptoUtil.SALT_LENGTH); const key = scryptSync(password, salt, 32); const iv = randomBytes(CryptoUtil.IV_LENGTH); const cipher = createCipheriv(CryptoUtil.ALGORITHM, key, iv); const encrypted = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]); const authTag = cipher.getAuthTag(); return Buffer.concat([salt, iv, authTag, encrypted]).toString('hex'); } static decrypt(encryptedData: string, password: string): string { const buffer = Buffer.from(encryptedData, 'hex'); const salt = buffer.subarray(0, CryptoUtil.SALT_LENGTH); const iv = buffer.subarray( CryptoUtil.SALT_LENGTH, CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH, ); const authTag = buffer.subarray( CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH, CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH + CryptoUtil.TAG_LENGTH, ); const encrypted = buffer.subarray( CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH + CryptoUtil.TAG_LENGTH, ); const key = scryptSync(password, salt, 32); const decipher = createDecipheriv(CryptoUtil.ALGORITHM, key, iv); decipher.setAuthTag(authTag); return decipher.update(encrypted) + decipher.final('utf8'); } static generateKey(length: number = 32): string { return randomBytes(length).toString('hex'); } static generateIV(length: number = 16): string { return randomBytes(length).toString('hex'); } }