Files
Compass/backend/shared/src/encryption.ts
Martin Braquet ba9b3cfb06 Add pretty formatting (#29)
* Test

* Add pretty formatting

* Fix Tests

* Fix Tests

* Fix Tests

* Fix

* Add pretty formatting fix

* Fix

* Test

* Fix tests

* Clean typeckech

* Add prettier check

* Fix api tsconfig

* Fix api tsconfig

* Fix tsconfig

* Fix

* Fix

* Prettier
2026-02-20 17:32:27 +01:00

67 lines
2.1 KiB
TypeScript

import {ENV_CONFIG} from 'common/envs/constants'
import crypto from 'crypto'
/**
* MASTER_KEY must be a 32-byte Buffer (AES-256).
* Load it from a secrets manager at runtime; do NOT hardcode.
*/
let _MASTER_KEY: Buffer | null = null
const getMasterKey = () => {
if (_MASTER_KEY) return _MASTER_KEY
if (ENV_CONFIG.dbEncryptionKey) {
const MASTER_KEY_BASE64 = ENV_CONFIG.dbEncryptionKey
_MASTER_KEY = Buffer.from(MASTER_KEY_BASE64, 'base64')
if (_MASTER_KEY.length !== 32) throw new Error('MASTER_KEY must be 32 bytes')
}
if (!_MASTER_KEY) throw new Error('MASTER_KEY not set')
return _MASTER_KEY
}
/**
* Encrypt a UTF-8 message string into base64 ciphertext + iv + tag.
* The IV makes the encryption probabilistic to ensure uniqueness in ciphertexts even when encrypting the same plaintext
* multiple times and has therefore no intent of being secret. The authentication tag works similar to a MAC.
* It's used to prove the authenticity and integrity of a message
*/
export function encryptMessage(plaintext: string) {
const iv = crypto.randomBytes(12) // 96-bit IV, recommended for AES-GCM
const cipher = crypto.createCipheriv('aes-256-gcm', getMasterKey(), iv)
const ciphertext = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()])
const tag = cipher.getAuthTag()
// console.debug(plaintext, iv, ciphertext, tag)
return {
ciphertext: ciphertext.toString('base64'),
iv: iv.toString('base64'),
tag: tag.toString('base64'),
}
}
/**
* Decrypt base64 ciphertext + iv + tag -> plaintext string.
* Throws on auth failure.
*/
export function decryptMessage({
ciphertext,
iv,
tag,
}: {
ciphertext: string
iv: string
tag: string
}) {
const ivBuf = Buffer.from(iv, 'base64')
const ctBuf = Buffer.from(ciphertext, 'base64')
const tagBuf = Buffer.from(tag, 'base64')
const decipher = crypto.createDecipheriv('aes-256-gcm', getMasterKey(), ivBuf)
decipher.setAuthTag(tagBuf)
const plaintext = Buffer.concat([decipher.update(ctBuf), decipher.final()]).toString('utf8')
// console.debug("Decrypted message:", plaintext);
return plaintext
}