| import crypto from 'crypto'; |
| import Database from 'better-sqlite3'; |
|
|
| const ALGORITHM = 'aes-256-gcm'; |
|
|
| let cachedKey: Buffer | null = null; |
|
|
| |
| |
| |
| |
| export function initEncryptionKey(db: Database.Database): void { |
| |
| const envKey = process.env.ENCRYPTION_KEY; |
| if (envKey && envKey !== 'your-64-char-hex-key-here') { |
| cachedKey = Buffer.from(envKey, 'hex'); |
| return; |
| } |
|
|
| |
| const row = db.prepare("SELECT value FROM settings WHERE key = 'encryption_key'").get() as { value: string } | undefined; |
| if (row) { |
| cachedKey = Buffer.from(row.value, 'hex'); |
| return; |
| } |
|
|
| |
| cachedKey = crypto.randomBytes(32); |
| db.prepare("INSERT INTO settings (key, value) VALUES ('encryption_key', ?)").run(cachedKey.toString('hex')); |
| } |
|
|
| function getEncryptionKey(): Buffer { |
| if (!cachedKey) { |
| throw new Error('Encryption key not initialized. Call initEncryptionKey() first.'); |
| } |
| return cachedKey; |
| } |
|
|
| export function encrypt(text: string): { encrypted: string; iv: string; authTag: string } { |
| const key = getEncryptionKey(); |
| const iv = crypto.randomBytes(16); |
| const cipher = crypto.createCipheriv(ALGORITHM, key, iv); |
|
|
| let encrypted = cipher.update(text, 'utf8', 'hex'); |
| encrypted += cipher.final('hex'); |
| const authTag = cipher.getAuthTag().toString('hex'); |
|
|
| return { |
| encrypted, |
| iv: iv.toString('hex'), |
| authTag, |
| }; |
| } |
|
|
| export function decrypt(encrypted: string, iv: string, authTag: string): string { |
| const key = getEncryptionKey(); |
| const decipher = crypto.createDecipheriv(ALGORITHM, key, Buffer.from(iv, 'hex')); |
| decipher.setAuthTag(Buffer.from(authTag, 'hex')); |
|
|
| let decrypted = decipher.update(encrypted, 'hex', 'utf8'); |
| decrypted += decipher.final('utf8'); |
| return decrypted; |
| } |
|
|
| export function maskKey(key: string): string { |
| if (key.length <= 8) return '****' + key.slice(-4); |
| return key.slice(0, 4) + '...' + key.slice(-4); |
| } |
|
|