BillTracker/services/encryptionService.js

44 lines
1.5 KiB
JavaScript
Raw Normal View History

'use strict';
const crypto = require('crypto');
const ALGORITHM = 'aes-256-gcm';
const IV_BYTES = 12;
const TAG_BYTES = 16;
function getKey() {
const raw = process.env.TOKEN_ENCRYPTION_KEY || '';
if (!raw) throw new Error('TOKEN_ENCRYPTION_KEY is not set');
const buf = Buffer.from(raw, 'utf8');
if (buf.length < 32) throw new Error('TOKEN_ENCRYPTION_KEY must be at least 32 bytes');
return crypto.createHash('sha256').update(buf).digest();
}
function assertEncryptionReady() {
getKey();
}
function encryptSecret(plaintext) {
const key = getKey();
const iv = crypto.randomBytes(IV_BYTES);
const cipher = crypto.createCipheriv(ALGORITHM, key, iv, { authTagLength: TAG_BYTES });
const ct = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
const tag = cipher.getAuthTag();
return `${iv.toString('hex')}:${tag.toString('hex')}:${ct.toString('hex')}`;
}
function decryptSecret(stored) {
const parts = stored.split(':');
if (parts.length !== 3) throw new Error('Invalid encrypted secret format');
const [ivHex, tagHex, ctHex] = parts;
const key = getKey();
const iv = Buffer.from(ivHex, 'hex');
const tag = Buffer.from(tagHex, 'hex');
const ct = Buffer.from(ctHex, 'hex');
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv, { authTagLength: TAG_BYTES });
decipher.setAuthTag(tag);
return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');
}
module.exports = { assertEncryptionReady, encryptSecret, decryptSecret };