import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto'; import { config } from '../config.js'; const ALGORITHM = 'aes-256-gcm'; const IV_LEN = 12; const TAG_LEN = 16; let keyCache = null; /** * Ключ из ENCRYPTION_KEY. Принимаем как 64-символьный hex (готовые 32 байта), * так и произвольную парольную фразу — её растягиваем scrypt до 32 байт. */ function getKey() { if (keyCache) return keyCache; const raw = config.encryptionKey(); if (/^[0-9a-f]{64}$/i.test(raw)) { keyCache = Buffer.from(raw, 'hex'); } else { keyCache = scryptSync(raw, 'telegrambusiness/v1', 32); } return keyCache; } /** * Шифрует буфер. Формат на выходе: [iv(12)][tag(16)][ciphertext]. * Так весь артефакт самодостаточен — расшифровка не требует внешних метаданных. */ export function encrypt(plaintext) { const iv = randomBytes(IV_LEN); const cipher = createCipheriv(ALGORITHM, getKey(), iv); const data = Buffer.concat([cipher.update(plaintext), cipher.final()]); return Buffer.concat([iv, cipher.getAuthTag(), data]); } export function decrypt(payload) { const iv = payload.subarray(0, IV_LEN); const tag = payload.subarray(IV_LEN, IV_LEN + TAG_LEN); const data = payload.subarray(IV_LEN + TAG_LEN); const decipher = createDecipheriv(ALGORITHM, getKey(), iv); decipher.setAuthTag(tag); return Buffer.concat([decipher.update(data), decipher.final()]); } export const encryptString = (text) => encrypt(Buffer.from(text, 'utf8')).toString('base64'); export const decryptString = (b64) => decrypt(Buffer.from(b64, 'base64')).toString('utf8');