99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import crypto from 'crypto';
|
|
|
|
/**
|
|
* Encryption utilities using AES-256-CBC for local database encryption
|
|
* EXACTEMENT comme dans Fastify System.ts
|
|
*/
|
|
|
|
const ALGORITHM = 'aes-256-cbc';
|
|
const KEY_LENGTH = 32; // 256 bits
|
|
const IV_LENGTH = 16; // 128 bits
|
|
const SALT_LENGTH = 64;
|
|
|
|
/**
|
|
* Generate a unique encryption key for a user
|
|
* This key is generated once at first login and stored securely in electron-store
|
|
* @param userId - The user's unique identifier
|
|
* @returns Base64 encoded encryption key
|
|
*/
|
|
export function generateUserEncryptionKey(userId: string): string {
|
|
// Generate a random salt for this user
|
|
const salt = crypto.randomBytes(SALT_LENGTH);
|
|
|
|
// Create a deterministic key based on userId and random salt
|
|
// This ensures each user has a unique, strong key
|
|
const key = crypto.pbkdf2Sync(
|
|
userId,
|
|
salt,
|
|
100000, // iterations
|
|
KEY_LENGTH,
|
|
'sha512'
|
|
);
|
|
|
|
// Combine salt and key for storage
|
|
const combined = Buffer.concat([salt, key]);
|
|
return combined.toString('base64');
|
|
}
|
|
|
|
/**
|
|
* Extract the actual encryption key from the stored combined salt+key
|
|
* @param storedKey - Base64 encoded salt+key combination
|
|
* @returns Encryption key buffer
|
|
*/
|
|
function extractKeyFromStored(storedKey: string): Buffer {
|
|
const combined = Buffer.from(storedKey, 'base64');
|
|
// Extract key (last KEY_LENGTH bytes)
|
|
return combined.subarray(SALT_LENGTH, SALT_LENGTH + KEY_LENGTH);
|
|
}
|
|
|
|
/**
|
|
* Encrypt data with user key - EXACTEMENT comme Fastify
|
|
* @param data - Data to encrypt
|
|
* @param userKey - User's encryption key
|
|
* @returns Encrypted string with format "iv:encryptedData"
|
|
*/
|
|
export function encryptDataWithUserKey(data: string, userKey: string): string {
|
|
const key = extractKeyFromStored(userKey);
|
|
const iv = crypto.randomBytes(IV_LENGTH);
|
|
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
|
let encryptedData = cipher.update(data, 'utf8', 'hex');
|
|
encryptedData += cipher.final('hex');
|
|
return iv.toString('hex') + ':' + encryptedData;
|
|
}
|
|
|
|
/**
|
|
* Decrypt data with user key - EXACTEMENT comme Fastify
|
|
* @param encryptedData - Encrypted string with format "iv:encryptedData"
|
|
* @param userKey - User's encryption key
|
|
* @returns Decrypted data
|
|
*/
|
|
export function decryptDataWithUserKey(encryptedData: string, userKey: string): string {
|
|
const [ivHex, encryptedHex] = encryptedData.split(':');
|
|
const iv = Buffer.from(ivHex, 'hex');
|
|
const key = extractKeyFromStored(userKey);
|
|
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
|
let decryptedData = decipher.update(encryptedHex, 'hex', 'utf8');
|
|
decryptedData += decipher.final('utf8');
|
|
return decryptedData || '';
|
|
}
|
|
|
|
/**
|
|
* Hash data using SHA-256 (for non-reversible hashing like titles)
|
|
* @param data - Data to hash
|
|
* @returns Hex encoded hash
|
|
*/
|
|
export function hashElement(data: string): string {
|
|
return crypto.createHash('sha256').update(data.toLowerCase().trim()).digest('hex');
|
|
}
|
|
|
|
// Pour compatibilité avec l'ancien code
|
|
export const encrypt = encryptDataWithUserKey;
|
|
export const decrypt = decryptDataWithUserKey;
|
|
export const hash = hashElement;
|
|
|
|
// Interface pour compatibilité (pas utilisée avec AES-CBC)
|
|
export interface EncryptedData {
|
|
encryptedData: string;
|
|
iv: string;
|
|
authTag: string;
|
|
} |