Encrypted-Chat-Client/components/crypto.jsx
2024-12-15 06:40:35 -08:00

128 lines
4.5 KiB
JavaScript

import { Buffer } from 'buffer';
import { sha256 } from "js-sha256";
import JSChaCha20 from 'js-chacha20';
function bigIntToUint8Array(bigInt, byteLength) {
const hex = bigInt.toString(16).padStart(byteLength * 2, '0');
const byteArray = new Uint8Array(byteLength);
for (let i = 0; i < byteLength; i++) {
byteArray[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return byteArray;
}
export function bigIntToBase64(bigInt) {
const hexString = bigInt.toString(16).padStart(bigInt.toString(16).length + (bigInt.toString(16).length % 2), '0');
return Buffer.from(hexString, 'hex').toString('base64');
}
export function base64ToBigInt(base64) {
return BigInt('0x' + Buffer.from(base64, 'base64').toString('hex'));
}
const pBase64 = "2IGKThw2+H+X0Mc5ZxIfOGX1b3lLc/mDB2Ne73FdoEiJRRfWaETlLUg8dAFUWn4Jxg/QSbP7+f/Q0dZbPCShAXrqWsqxScNk+XvFRTUAhqq1h82Bh4Puok2P1ke6sPR/k+LCt9uHMlt/X0TodDonFIMr87KjB9bQ+zysfPXU2G9chMjqPpY2AkInPHfaMtKtIfBslXKbhGwtaK6t0h0GGJ7W3GU8e1dzo/JgwVDnqoTryAKdoFmrpjg41naQOC5Hl59i+Ik5yEL+NCvSis7IYo55sM6cbu8B4N4wNwDKkefgElADvhSKQJirbmSpPXs5Lr7GXgRBR6t/AGrYTgahPw==";
const gBase64 = "5";
const p = base64ToBigInt(pBase64);
const g = BigInt(gBase64);
export function generatePrivateKey() {
return generateRandomBigInt(p - 1n);
}
function generateRandomBigInt(max) {
let randomHex = '';
while (randomHex.length < max.toString(16).length) {
randomHex += Math.floor(Math.random() * 16).toString(16);
}
return BigInt('0x' + randomHex.slice(0, max.toString(16).length));
}
export function calculatePublicKey(privateKey) {
return modExponentiation(g, privateKey, p);
}
function modExponentiation(base, exponent, modulus) {
let result = BigInt(1);
base = base % modulus;
while (exponent > 0n) {
if (exponent % 2n === 1n) result = (result * base) % modulus;
exponent /= 2n;
base = (base * base) % modulus;
}
return result;
}
export function computeSharedSecret(publicKey, privateKey) {
return modExponentiation(publicKey, privateKey, p);
}
export function chacha20Encrypt(jsonobj) {
try {
let { message, receiver, pubkey } = JSON.parse(jsonobj);
message = new TextEncoder().encode(message);
let storedUsers = JSON.parse(localStorage.getItem("privateKeys")) || [];
const user = storedUsers.find(user => user.username === receiver);
if (!user) return console.error("User not found in localStorage");
const clientPrivKey = base64ToBigInt(user.private64);
const secret = computeSharedSecret(base64ToBigInt(pubkey), clientPrivKey);
const secretArray = bigIntToUint8Array(secret);
const chachaKeyUint8 = new Uint8Array(sha256.create().update(secretArray).digest());
const nonce = generateNonce();
let nonceB64 = Buffer.from(nonce).toString("base64");
const chacha = new JSChaCha20(chachaKeyUint8, nonce);
const encrypted = chacha.encrypt(message);
const encryptedArray = new Uint8Array(encrypted);
const encryptedB64 = Buffer.from(encryptedArray).toString("base64");
return JSON.stringify({
encrypted: encryptedB64,
nonce: nonceB64,
pubKey: pubkey,
receiverUsername: receiver
});
} catch (error) {
console.error("Error during encryption:", error);
return null;
}
}
export function chacha20Decrypt(data) {
try {
const { content, nonce, pubKey, receiverUsername } = data;
let nonceArray = new Uint8Array(Buffer.from(nonce, 'base64'));
if (nonceArray.length !== 12) throw new Error("Nonce should be a 12 byte array!");
let storedUsers = JSON.parse(localStorage.getItem("privateKeys")) || [];
const user = storedUsers.find(user => user.username === receiverUsername);
if (!user) throw new Error("User not found in localStorage for decryption");
const clientPrivKey = base64ToBigInt(user.private64);
const secret = computeSharedSecret(base64ToBigInt(pubKey), clientPrivKey);
const secretArray = bigIntToUint8Array(secret);
const chachaKeyUint8 = new Uint8Array(sha256.create().update(secretArray).digest());
let encryptedArray = new Uint8Array(Buffer.from(content, 'base64'));
const chacha = new JSChaCha20(chachaKeyUint8, nonceArray);
const decrypted = chacha.decrypt(encryptedArray);
return new TextDecoder().decode(decrypted);
} catch (error) {
console.error("Decryption Error:", error);
return "Error decrypting message: " + error.message;
}
}
export function generateNonce(length = 12) {
const nonce = new Uint8Array(length);
for (let i = 0; i < length; i++) nonce[i] = Math.floor(Math.random() * 256);
return nonce;
}