import crypto from 'node:crypto' const configuredKey = process.env.MEMBERSHIP_ENCRYPTION_KEY const fallbackKey = '0000000000000000000000000000000000000000000000000000000000000000' if (!configuredKey) { console.warn('MEMBERSHIP_ENCRYPTION_KEY is not set. Using a development fallback key.') } const getKey = () => { const key = Buffer.from(configuredKey || fallbackKey, 'hex') if (key.length !== 32) { throw new Error('MEMBERSHIP_ENCRYPTION_KEY must be 64 hex characters.') } return key } export const encryptField = (value = '') => { if (!value) return '' const iv = crypto.randomBytes(16) const cipher = crypto.createCipheriv('aes-256-gcm', getKey(), iv) const encrypted = Buffer.concat([cipher.update(String(value), 'utf8'), cipher.final()]) const tag = cipher.getAuthTag() return `${iv.toString('hex')}:${tag.toString('hex')}:${encrypted.toString('hex')}` } export const decryptField = (value = '') => { if (!value || !String(value).includes(':')) return value || '' const parts = String(value).split(':') if (parts.length !== 3 || parts[0].length !== 32 || parts[1].length !== 32) return value const [ivHex, tagHex, encryptedHex] = parts const decipher = crypto.createDecipheriv('aes-256-gcm', getKey(), Buffer.from(ivHex, 'hex')) decipher.setAuthTag(Buffer.from(tagHex, 'hex')) return Buffer.concat([ decipher.update(Buffer.from(encryptedHex, 'hex')), decipher.final(), ]).toString('utf8') } export const encryptMembership = (member) => ({ ...member, fullName: encryptField(member.fullName), email: encryptField(member.email || ''), phone: encryptField(member.phone || ''), signature: encryptField(member.signature || ''), }) export const decryptMembership = (member) => ({ ...member, fullName: decryptField(member.fullName), email: decryptField(member.email || ''), phone: decryptField(member.phone || ''), signature: decryptField(member.signature || ''), })