Files
bruno/packages/bruno-electron/src/utils/encryption.js
lohit 1cb0d4e191 chore: node version bump -- v22.11.0 (#3508)
node version bump with updates to cipher logic
2024-11-20 17:09:02 +05:30

143 lines
4.1 KiB
JavaScript

const crypto = require('crypto');
const { machineIdSync } = require('@usebruno/node-machine-id');
const { safeStorage } = require('electron');
// Constants for algorithm identification
const ELECTRONSAFESTORAGE_ALGO = '00';
const AES256_ALGO = '01';
function deriveKeyAndIv(password, keyLength, ivLength) {
const key = Buffer.alloc(keyLength);
const iv = Buffer.alloc(ivLength);
const derivedBytes = [];
let lastHash = null;
while (Buffer.concat(derivedBytes).length < keyLength + ivLength) {
const hash = crypto.createHash('md5');
if (lastHash) {
hash.update(lastHash);
}
hash.update(Buffer.from(password, 'utf8'));
lastHash = hash.digest();
derivedBytes.push(lastHash);
}
const concatenatedBytes = Buffer.concat(derivedBytes);
concatenatedBytes.copy(key, 0, 0, keyLength);
concatenatedBytes.copy(iv, 0, keyLength, keyLength + ivLength);
return { key, iv };
}
function aes256Encrypt(data) {
const rawKey = machineIdSync();
const iv = Buffer.alloc(16, 0); // Default IV for new encryption
const key = crypto.createHash('sha256').update(rawKey).digest(); // Derive a 32-byte key
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
function aes256Decrypt(data) {
const rawKey = machineIdSync();
// Attempt to decrypt using new method first
const iv = Buffer.alloc(16, 0); // Default IV for new encryption
const key = crypto.createHash('sha256').update(rawKey).digest(); // Derive a 32-byte key
try {
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
} catch (err) {
// If decryption fails, fall back to old key derivation
const { key: oldKey, iv: oldIv } = deriveKeyAndIv(rawKey, 32, 16);
const decipher = crypto.createDecipheriv('aes-256-cbc', oldKey, oldIv);
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// electron safe storage encryption and decryption functions
function safeStorageEncrypt(str) {
let encryptedStringBuffer = safeStorage.encryptString(str);
// Convert the encrypted buffer to a hexadecimal string
const encryptedString = encryptedStringBuffer.toString('hex');
return encryptedString;
}
function safeStorageDecrypt(str) {
// Convert the hexadecimal string to a buffer
const encryptedStringBuffer = Buffer.from(str, 'hex');
// Decrypt the buffer
const decryptedStringBuffer = safeStorage.decryptString(encryptedStringBuffer);
// Convert the decrypted buffer to a string
const decryptedString = decryptedStringBuffer.toString();
return decryptedString;
}
function encryptString(str) {
if (typeof str !== 'string') {
throw new Error('Encrypt failed: invalid string');
}
let encryptedString = '';
if (safeStorage && safeStorage.isEncryptionAvailable()) {
encryptedString = safeStorageEncrypt(str);
return `$${ELECTRONSAFESTORAGE_ALGO}:${encryptedString}`;
}
// fallback to aes256
encryptedString = aes256Encrypt(str);
return `$${AES256_ALGO}:${encryptedString}`;
}
function decryptString(str) {
if (!str) {
throw new Error('Decrypt failed: unrecognized string format');
}
// Find the index of the first colon
const colonIndex = str.indexOf(':');
if (colonIndex === -1) {
throw new Error('Decrypt failed: unrecognized string format');
}
// Extract algo and encryptedString based on the colon index
const algo = str.substring(1, colonIndex);
const encryptedString = str.substring(colonIndex + 1);
if ([ELECTRONSAFESTORAGE_ALGO, AES256_ALGO].indexOf(algo) === -1) {
throw new Error('Decrypt failed: Invalid algo');
}
if (algo === ELECTRONSAFESTORAGE_ALGO) {
if (safeStorage && safeStorage.isEncryptionAvailable()) {
return safeStorageDecrypt(encryptedString);
} else {
return '';
}
}
if (algo === AES256_ALGO) {
return aes256Decrypt(encryptedString);
}
}
module.exports = {
encryptString,
decryptString
};