export class WebCryptoHelper { static async generateKeyPair() { const keyPair = await window.crypto.subtle.generateKey( { name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey', 'deriveBits'] ); const exportedPublic = await window.crypto.subtle.exportKey('raw', keyPair.publicKey); const publicKeyBase64 = btoa(String.fromCharCode(...new Uint8Array(exportedPublic))); return { privateKey: keyPair.privateKey, publicKeyBase64 }; } static async deriveSharedKey(clientPrivateKey: any, serverPublicKeyBase64: any) { const binarySign = atob(serverPublicKeyBase64); const bytes = new Uint8Array(binarySign.length); for (let i = 0; i < binarySign.length; i++) { bytes[i] = binarySign.charCodeAt(i); } const importedServerPublic = await window.crypto.subtle.importKey( 'raw', bytes, { name: 'ECDH', namedCurve: 'P-256' }, true, [] ); return await window.crypto.subtle.deriveKey( { name: 'ECDH', public: importedServerPublic }, clientPrivateKey, { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'] ); } static async decryptMessage(aesKey: any, ciphertextBase64: any, ivBase64: any) { const rawCipher = Uint8Array.from(atob(ciphertextBase64), (c) => c.charCodeAt(0)); const rawIv = Uint8Array.from(atob(ivBase64), (c) => c.charCodeAt(0)); const decryptedBuffer = await window.crypto.subtle.decrypt( { name: 'AES-GCM', iv: rawIv }, aesKey, rawCipher ); return new TextDecoder().decode(decryptedBuffer); } // Encrypt outgoing messages before sending them to your Axum backend static async encryptMessage(aesKey: any, plainText: any) { const iv = window.crypto.getRandomValues(new Uint8Array(12)); // 12-byte nonce const encodedText = new TextEncoder().encode(plainText); const ciphertextBuffer = await window.crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, aesKey, encodedText ); const ciphertextBase64 = btoa(String.fromCharCode(...new Uint8Array(ciphertextBuffer))); const ivBase64 = btoa(String.fromCharCode(...iv)); return { ciphertext: ciphertextBase64, iv: ivBase64 }; } }