/** * Zero-Knowledge Cryptography Utility * Uses the Web Crypto API for hardware-accelerated AES-GCM. */ export async function importKey(keyData: Uint8Array): Promise { return await self.crypto.subtle.importKey( "raw", keyData.buffer as ArrayBuffer, { name: "AES-GCM" }, false, ["decrypt", "encrypt"] ); } export async function decryptLog( encryptedData: Uint8Array, key: CryptoKey ): Promise { // Our Go implementation sends [Nonce (12 bytes)][Ciphertext] const nonce = encryptedData.slice(0, 12); const ciphertext = encryptedData.slice(12); const decrypted = await self.crypto.subtle.decrypt( { name: "AES-GCM", iv: nonce, }, key, ciphertext ); return new TextDecoder().decode(decrypted); } export async function encryptData( plaintext: string, key: CryptoKey ): Promise { const enc = new TextEncoder(); const data = enc.encode(plaintext); const nonce = self.crypto.getRandomValues(new Uint8Array(12)); const encrypted = await self.crypto.subtle.encrypt( { name: "AES-GCM", iv: nonce, }, key, data ); const result = new Uint8Array(nonce.length + encrypted.byteLength); result.set(nonce); result.set(new Uint8Array(encrypted), nonce.length); return result; } export async function generateBlindIndex( value: string, key: Uint8Array ): Promise { const enc = new TextEncoder(); const data = enc.encode(value); const hmacKey = await self.crypto.subtle.importKey( "raw", key, { name: "HMAC", hash: "SHA-256" }, false, ["sign"] ); const signature = await self.crypto.subtle.sign( "HMAC", hmacKey, data ); return Array.from(new Uint8Array(signature)) .map(b => b.toString(16).padStart(2, '0')) .join(''); } /** * Derives a 256-bit key from a password and salt using PBKDF2. * (Used if WebAuthn is not providing a raw key directly) */ export async function deriveKey(password: string, salt: string): Promise { const enc = new TextEncoder(); const keyMaterial = await self.crypto.subtle.importKey( "raw", enc.encode(password), "PBKDF2", false, ["deriveBits", "deriveKey"] ); const keyBits = await self.crypto.subtle.deriveBits( { name: "PBKDF2", salt: enc.encode(salt), iterations: 100000, hash: "SHA-256", }, keyMaterial, 256 ); return new Uint8Array(keyBits); }