auth-app-api / src /utils /crypto.js
AndreaNotes1's picture
Upload 36 files
6e13570 verified
Raw History Blame Contribute Delete
2.24 kB
const crypto = require("crypto");
const { env } = require("../config/env");
const ALGORITHM = "aes-256-gcm";
const IV_LENGTH = 12; // 96-bit IV recommended for GCM
function getKey() {
const key = Buffer.from(env.fieldEncryptionKey || "", "hex");
if (key.length !== 32) {
throw new Error(
"FIELD_ENCRYPTION_KEY must be a 32-byte value encoded as 64 hex characters. " +
"Generate one with: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\""
);
}
return key;
}
/**
* Encrypts a plaintext string for storage at rest (e.g. bank wire
* reference numbers, Payoneer transaction IDs). Returns a single
* base64 payload containing iv + authTag + ciphertext so it can be
* stored as one string field in MongoDB.
*/
function encryptField(plaintext) {
if (plaintext === undefined || plaintext === null || plaintext === "") return null;
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, getKey(), iv);
const ciphertext = Buffer.concat([cipher.update(String(plaintext), "utf8"), cipher.final()]);
const authTag = cipher.getAuthTag();
return Buffer.concat([iv, authTag, ciphertext]).toString("base64");
}
/**
* Decrypts a payload produced by encryptField. Returns null if the
* input is empty rather than throwing, so callers can treat "no
* payment reference yet" gracefully.
*/
function decryptField(payload) {
if (!payload) return null;
const raw = Buffer.from(payload, "base64");
const iv = raw.subarray(0, IV_LENGTH);
const authTag = raw.subarray(IV_LENGTH, IV_LENGTH + 16);
const ciphertext = raw.subarray(IV_LENGTH + 16);
const decipher = crypto.createDecipheriv(ALGORITHM, getKey(), iv);
decipher.setAuthTag(authTag);
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
return plaintext.toString("utf8");
}
/** One-way hash used for equality lookups on encrypted fields (e.g. duplicate detection) without decrypting. */
function hmacIndex(value) {
if (!value) return null;
return crypto.createHmac("sha256", getKey()).update(String(value)).digest("hex");
}
module.exports = { encryptField, decryptField, hmacIndex };