const crypto = require('crypto'); const os = require('os'); const fs = require('fs'); const path = require('path'); class CryptoUtils { static getMachineKey(cacheDir) { const cachePath = cacheDir ? path.join(cacheDir, '.machine-key-cache') : null; if (cachePath && fs.existsSync(cachePath)) { try { const cached = fs.readFileSync(cachePath, 'utf8').trim(); if (cached && cached.length === 64) return cached; } catch (e) { } } let key; try { const isMac = os.platform() === 'darwin'; const machineId = [ isMac ? '' : os.hostname(), os.platform(), os.arch(), os.cpus()[0] ? os.cpus()[0].model : '', process.env.COMPUTERNAME || '', process.env.USERNAME || (isMac ? os.userInfo().username : '') ].join('|'); const hash = crypto.createHash('sha256'); hash.update(machineId); key = hash.digest('hex'); } catch (error) { const fallback = crypto.createHash('sha256'); fallback.update(os.hostname() + Date.now()); key = fallback.digest('hex'); } if (cachePath) { try { fs.writeFileSync(cachePath, key, 'utf8'); } catch (e) { } } return key; } static deriveKey(machineKey, salt = 'vyla-home-salt') { const hash = crypto.createHash('sha256'); hash.update(machineKey + salt); return hash.digest(); } static encrypt(plaintext, key) { try { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); let encrypted = cipher.update(plaintext, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`; } catch (error) { throw new Error(`Encryption failed: ${error.message}`); } } static decrypt(ciphertext, key) { try { const parts = ciphertext.split(':'); if (parts.length !== 3) { throw new Error('Invalid ciphertext format'); } const iv = Buffer.from(parts[0], 'hex'); const authTag = Buffer.from(parts[1], 'hex'); const encrypted = parts[2]; const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(authTag); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } catch (error) { throw new Error(`Decryption failed: ${error.message}`); } } static encryptObject(obj, key) { const jsonString = JSON.stringify(obj); return this.encrypt(jsonString, key); } static decryptObject(ciphertext, key) { const jsonString = this.decrypt(ciphertext, key); return JSON.parse(jsonString); } } module.exports = CryptoUtils;