const crypto = require('crypto'); const fs = require('fs'); const config = require('./config'); const ALGORITHM = 'aes-256-gcm'; const KEY_LENGTH = 32; const IV_LENGTH = 16; const SALT_LENGTH = 32; function deriveKey(passphrase, salt) { return crypto.pbkdf2Sync(passphrase, salt, 100000, KEY_LENGTH, 'sha512'); } function getMachineId() { const os = require('os'); const raw = `${os.hostname()}-${os.platform()}-${os.arch()}-${os.userInfo().username}`; return crypto.createHash('sha256').update(raw).digest('hex'); } function encrypt(plaintext) { const machineKey = getMachineId(); const salt = crypto.randomBytes(SALT_LENGTH); const key = deriveKey(machineKey, salt); const iv = crypto.randomBytes(IV_LENGTH); const cipher = crypto.createCipheriv(ALGORITHM, key, iv); let encrypted = cipher.update(plaintext, 'utf-8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); return { salt: salt.toString('hex'), iv: iv.toString('hex'), authTag: authTag.toString('hex'), data: encrypted, }; } function decrypt(encObj) { const machineKey = getMachineId(); const salt = Buffer.from(encObj.salt, 'hex'); const key = deriveKey(machineKey, salt); const iv = Buffer.from(encObj.iv, 'hex'); const authTag = Buffer.from(encObj.authTag, 'hex'); const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); decipher.setAuthTag(authTag); let decrypted = decipher.update(encObj.data, 'hex', 'utf-8'); decrypted += decipher.final('utf-8'); return decrypted; } function storePassword(password) { config.ensureConfigDir(); const encrypted = encrypt(password); const content = JSON.stringify(encrypted); fs.writeFileSync(config.PASSWORD_FILE, content, { encoding: 'utf-8', mode: 0o600 }); // On Windows, hide the file with attrib if (process.platform === 'win32') { try { const { execSync } = require('child_process'); execSync(`attrib +h "${config.PASSWORD_FILE}"`, { stdio: 'ignore' }); } catch (_) {} } return true; } function retrievePassword() { if (!fs.existsSync(config.PASSWORD_FILE)) return null; try { const content = fs.readFileSync(config.PASSWORD_FILE, 'utf-8'); const encObj = JSON.parse(content); return decrypt(encObj); } catch (err) { return null; } } function hasStoredPassword() { return fs.existsSync(config.PASSWORD_FILE); } function deletePassword() { if (fs.existsSync(config.PASSWORD_FILE)) { fs.unlinkSync(config.PASSWORD_FILE); return true; } return false; } module.exports = { storePassword, retrievePassword, hasStoredPassword, deletePassword, };