Files
nodecrypt/worker/utils.js
T
2025-05-27 18:31:29 +08:00

160 lines
3.8 KiB
JavaScript

import crypto from 'node:crypto';
// Generate a new RSA key pair using Node.js crypto API
export const generateRSAKeyPair = async () => {
try {
console.log('Generating new RSA keypair...');
return new Promise((resolve, reject) => {
crypto.generateKeyPair('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
}, (err, publicKey, privateKey) => {
if (err) {
reject(err);
} else {
// Convert PEM to base64 (remove headers and newlines)
const rsaPublic = publicKey
.replace(/-----BEGIN PUBLIC KEY-----/, '')
.replace(/-----END PUBLIC KEY-----/, '')
.replace(/\n/g, '');
resolve({
rsaPublic,
rsaPrivate: privateKey
});
}
});
});
} catch (error) {
console.error('Error generating RSA key pair:', error);
throw error;
}
};
export const generateClientId = () => {
try {
return (crypto.randomBytes(8).toString('hex'));
} catch (error) {
logEvent('generateClientId', error, 'error');
return (null);
}
};
export const encryptMessage = (message, key) => {
let encrypted = '';
try {
const messageBuffer = Buffer.from(JSON.stringify(message), 'utf8');
// Match server.js padding logic exactly
const paddedBuffer = (messageBuffer.length % 16) !== 0 ?
Buffer.concat([messageBuffer, Buffer.alloc(16 - (messageBuffer.length % 16))]) :
messageBuffer;
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
cipher.setAutoPadding(false);
encrypted = iv.toString('base64') + '|' + cipher.update(paddedBuffer, '', 'base64') + cipher.final('base64');
} catch (error) {
logEvent('encryptMessage', error, 'error');
}
return (encrypted);
};
export const decryptMessage = (message, key) => {
let decrypted = {};
try {
const parts = message.split('|');
const decipher = crypto.createDecipheriv(
'aes-256-cbc',
key,
Buffer.from(parts[0], 'base64')
);
decipher.setAutoPadding(false);
const decryptedText = decipher.update(parts[1], 'base64', 'utf8') + decipher.final('utf8');
decrypted = JSON.parse(decryptedText.replace(/\0+$/, ''));
} catch (error) {
logEvent('decryptMessage', error, 'error');
}
return (decrypted);
};
export const logEvent = (source, message, level) => {
if (
level !== 'debug' ||
true // config.debug would go here
) {
const date = new Date(),
dateString = date.getFullYear() + '-' +
('0' + (date.getMonth() + 1)).slice(-2) + '-' +
('0' + date.getDate()).slice(-2) + ' ' +
('0' + date.getHours()).slice(-2) + ':' +
('0' + date.getMinutes()).slice(-2) + ':' +
('0' + date.getSeconds()).slice(-2);
console.log('[' + dateString + ']', (level ? level.toUpperCase() : 'INFO'), source + (message ? ':' : ''), (message ? message : ''));
}
};
export const getTime = () => {
return (new Date().getTime());
};
export const isString = (value) => {
return (
value &&
Object.prototype.toString.call(value) === '[object String]' ?
true :
false
);
};
export const isArray = (value) => {
return (
value &&
Object.prototype.toString.call(value) === '[object Array]' ?
true :
false
);
};
export const isObject = (value) => {
return (
value &&
Object.prototype.toString.call(value) === '[object Object]' ?
true :
false
);
};
// Note: Since Cloudflare Workers don't have access to global.gc,
// we're not including the garbage collection interval that's in server.js
// setInterval(() => {
// if (global.gc) {
// global.gc();
// }
// }, 30000);