feat(chat): Upgrade to hybrid encryption with AES-256-GCM and RSA-OAEP
- Replace pure RSA encryption with hybrid encryption scheme for better performance - Implement AES-256-GCM for fast message encryption without size limitations - Use RSA-OAEP for secure AES key exchange between chat participants - Add EncryptedPayload interface to structure encrypted data (key, IV, ciphertext, auth tag) - Update encryptMessage() to generate random AES key and IV, encrypt with AES-GCM, then wrap AES key with RSA - Update decryptMessage() to unwrap AES key with RSA, then decrypt message with AES-GCM - Include GCM authentication tag for message integrity verification - Encode all binary components as base64 strings in JSON payload for transport - Improves encryption performance while maintaining security for end-to-end swarm messaging
This commit is contained in:
@@ -1,27 +1,58 @@
|
|||||||
/**
|
/**
|
||||||
* Swarm Chat Cryptography
|
* Swarm Chat Cryptography
|
||||||
*
|
*
|
||||||
* End-to-end encryption for chat messages using public key cryptography.
|
* End-to-end encryption for chat messages using hybrid encryption:
|
||||||
|
* - AES-256-GCM for message encryption (fast, no size limit)
|
||||||
|
* - RSA-OAEP for encrypting the AES key (secure key exchange)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
interface EncryptedPayload {
|
||||||
|
encryptedKey: string; // RSA-encrypted AES key (base64)
|
||||||
|
iv: string; // AES initialization vector (base64)
|
||||||
|
ciphertext: string; // AES-encrypted message (base64)
|
||||||
|
authTag: string; // GCM authentication tag (base64)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt a message using the recipient's public key
|
* Encrypt a message using hybrid encryption (AES + RSA)
|
||||||
*/
|
*/
|
||||||
export function encryptMessage(message: string, recipientPublicKey: string): string {
|
export function encryptMessage(message: string, recipientPublicKey: string): string {
|
||||||
try {
|
try {
|
||||||
// Use RSA-OAEP for encryption
|
// Generate a random AES-256 key
|
||||||
const encrypted = crypto.publicEncrypt(
|
const aesKey = crypto.randomBytes(32);
|
||||||
|
|
||||||
|
// Generate a random IV for AES-GCM
|
||||||
|
const iv = crypto.randomBytes(12);
|
||||||
|
|
||||||
|
// Encrypt the message with AES-256-GCM
|
||||||
|
const cipher = crypto.createCipheriv('aes-256-gcm', aesKey, iv);
|
||||||
|
const encrypted = Buffer.concat([
|
||||||
|
cipher.update(message, 'utf8'),
|
||||||
|
cipher.final()
|
||||||
|
]);
|
||||||
|
const authTag = cipher.getAuthTag();
|
||||||
|
|
||||||
|
// Encrypt the AES key with RSA-OAEP
|
||||||
|
const encryptedKey = crypto.publicEncrypt(
|
||||||
{
|
{
|
||||||
key: recipientPublicKey,
|
key: recipientPublicKey,
|
||||||
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
||||||
oaepHash: 'sha256',
|
oaepHash: 'sha256',
|
||||||
},
|
},
|
||||||
Buffer.from(message, 'utf8')
|
aesKey
|
||||||
);
|
);
|
||||||
|
|
||||||
return encrypted.toString('base64');
|
// Package everything together
|
||||||
|
const payload: EncryptedPayload = {
|
||||||
|
encryptedKey: encryptedKey.toString('base64'),
|
||||||
|
iv: iv.toString('base64'),
|
||||||
|
ciphertext: encrypted.toString('base64'),
|
||||||
|
authTag: authTag.toString('base64'),
|
||||||
|
};
|
||||||
|
|
||||||
|
return JSON.stringify(payload);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to encrypt message:', error);
|
console.error('Failed to encrypt message:', error);
|
||||||
throw new Error('Encryption failed');
|
throw new Error('Encryption failed');
|
||||||
@@ -29,19 +60,36 @@ export function encryptMessage(message: string, recipientPublicKey: string): str
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt a message using the recipient's private key
|
* Decrypt a message using hybrid encryption (AES + RSA)
|
||||||
*/
|
*/
|
||||||
export function decryptMessage(encryptedMessage: string, privateKey: string): string {
|
export function decryptMessage(encryptedMessage: string, privateKey: string): string {
|
||||||
try {
|
try {
|
||||||
const decrypted = crypto.privateDecrypt(
|
// Parse the encrypted payload
|
||||||
|
const payload: EncryptedPayload = JSON.parse(encryptedMessage);
|
||||||
|
|
||||||
|
// Decrypt the AES key with RSA
|
||||||
|
const aesKey = crypto.privateDecrypt(
|
||||||
{
|
{
|
||||||
key: privateKey,
|
key: privateKey,
|
||||||
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
||||||
oaepHash: 'sha256',
|
oaepHash: 'sha256',
|
||||||
},
|
},
|
||||||
Buffer.from(encryptedMessage, 'base64')
|
Buffer.from(payload.encryptedKey, 'base64')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Decrypt the message with AES-256-GCM
|
||||||
|
const decipher = crypto.createDecipheriv(
|
||||||
|
'aes-256-gcm',
|
||||||
|
aesKey,
|
||||||
|
Buffer.from(payload.iv, 'base64')
|
||||||
|
);
|
||||||
|
decipher.setAuthTag(Buffer.from(payload.authTag, 'base64'));
|
||||||
|
|
||||||
|
const decrypted = Buffer.concat([
|
||||||
|
decipher.update(Buffer.from(payload.ciphertext, 'base64')),
|
||||||
|
decipher.final()
|
||||||
|
]);
|
||||||
|
|
||||||
return decrypted.toString('utf8');
|
return decrypted.toString('utf8');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to decrypt message:', error);
|
console.error('Failed to decrypt message:', error);
|
||||||
|
|||||||
Reference in New Issue
Block a user