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; }