Files
sapien/server/encryption.js
2026-05-13 22:19:37 -05:00

56 lines
1.9 KiB
JavaScript

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 || ''),
})