Files
Home/crypto-utils.js
2026-10-29 22:19:03 -07:00

104 lines
2.7 KiB
JavaScript

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 machineId = [
os.hostname(),
os.platform(),
os.arch(),
os.cpus()[0] ? os.cpus()[0].model : '',
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 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;