const crypto = require('crypto'); const os = require('os'); const { execSync } = require('child_process'); class CryptoUtils { static getMachineKey(cacheDir) { const path = require('path'); const fs = require('fs'); 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) return cached; } catch (e) { } } let key; try { const machineId = [ os.hostname(), os.platform(), os.arch(), os.cpus()[0].model, this.getMachineId(), process.env.COMPUTERNAME || '', process.env.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 getMachineId() { const fs = require('fs'); if (os.platform() === 'win32') { try { const { spawnSync } = require('child_process'); const res = spawnSync('reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\Cryptography', '/v', 'MachineGuid'], { encoding: 'utf8' }); if (res.stdout) { const match = res.stdout.match(/MachineGuid\s+REG_SZ\s+([^\r\n]+)/); if (match) return match[1].trim(); } } catch (e) { } return 'windows-node-' + os.hostname(); } else if (os.platform() === 'darwin') { try { const { spawnSync } = require('child_process'); const res = spawnSync('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice'], { encoding: 'utf8' }); const match = res.stdout.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/); if (match) return match[1].trim(); } catch (e) { } return 'darwin-node-' + os.hostname(); } else { for (const idPath of ['/etc/machine-id', '/var/lib/dbus/machine-id']) { try { if (fs.existsSync(idPath)) return fs.readFileSync(idPath, 'utf8').trim(); } catch (e) { } } return 'linux-node-' + os.hostname(); } } 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(encryptedData, key) { try { const parts = encryptedData.split(':'); if (parts.length !== 3) { throw new Error('Invalid encrypted data 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 json = JSON.stringify(obj); return this.encrypt(json, key); } static decryptObject(encryptedData, key) { const json = this.decrypt(encryptedData, key); return JSON.parse(json); } static hashData(data) { const hash = crypto.createHash('sha256'); hash.update(data); return hash.digest('hex'); } static verifyHash(data, expectedHash) { const actualHash = this.hashData(data); return actualHash === expectedHash; } } module.exports = CryptoUtils;