Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 1x 1x 1x 1x | /**
* Service de chiffrement AES-256-GCM.
*
* Utilisé pour chiffrer en base de données :
* - Les clés SSH privées
* - Les clés API (Anthropic, OpenAI, Mistral)
* - Les passphrases SSH
*
* Format stocké : "iv:authTag:ciphertext" (tout en hex, séparé par ':')
*
* Sécurité :
* - AES-256-GCM assure confidentialité + intégrité (authenticated encryption)
* - IV aléatoire de 96 bits (recommandation NIST pour GCM)
* - La clé de chiffrement vit dans les variables d'environnement, JAMAIS en base
* - La clé doit faire 64 caractères hex (= 32 bytes = 256 bits)
*
* @module services/crypto
*/
const crypto = require('node:crypto');
const ALGORITHM = 'aes-256-gcm';
const KEY_HEX_LEN = 64; // 32 bytes en hex = 256 bits
/**
* Récupère et valide la clé de chiffrement depuis l'environnement.
*
* @returns {Buffer} — La clé de 32 bytes
* @throws {Error} Si la clé est absente ou mal formatée
*/
function getKey() {
const hex = process.env.ENCRYPTION_KEY;
if (hex?.length !== KEY_HEX_LEN) {
throw new Error(
'ENCRYPTION_KEY invalide : doit être une chaîne hex de 64 caractères (32 bytes).',
);
}
return Buffer.from(hex, 'hex');
}
/**
* Chiffre une valeur texte avec AES-256-GCM.
*
* @param {string} plaintext — La valeur à chiffrer
* @returns {string} — "iv:authTag:ciphertext" en hex
*/
function encrypt(plaintext) {
const key = getKey();
const iv = crypto.randomBytes(12); // 96 bits recommandé pour GCM
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
const authTag = cipher.getAuthTag();
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted.toString('hex')}`;
}
/**
* Déchiffre une valeur produite par encrypt().
*
* @param {string} ciphertext — Format "iv:authTag:data" en hex
* @returns {string} — La valeur en clair
* @throws {Error} Si le format est invalide ou l'intégrité échoue
*/
function decrypt(ciphertext) {
const key = getKey();
const parts = ciphertext.split(':');
if (parts.length !== 3) {
throw new Error('Format de ciphertext invalide.');
}
const [ivHex, authTagHex, dataHex] = parts;
const iv = Buffer.from(ivHex, 'hex');
const authTag = Buffer.from(authTagHex, 'hex');
const data = Buffer.from(dataHex, 'hex');
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
decipher.setAuthTag(authTag);
return decipher.update(data) + decipher.final('utf8');
}
module.exports = { encrypt, decrypt };
|