export async function deriveKey(password: string, userId: string) { const iterations = 1_000_000; const saltBuffer = new TextEncoder().encode(userId); const passwordBuffer = new TextEncoder().encode(password); const baseKey = await window.crypto.subtle.importKey( "raw", passwordBuffer, { name: "PBKDF2" }, false, ["deriveKey"] ); const derivedKey = await window.crypto.subtle.deriveKey( { name: "PBKDF2", salt: saltBuffer, iterations: iterations, hash: "SHA-256", }, baseKey, { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"] ); const encryptionKey = await crypto.subtle.exportKey( 'jwk', derivedKey ); return encryptionKey; } function generateIv() { return window.crypto.getRandomValues(new Uint8Array(12)); } export function uint8ArrayToBase64(bytes: Uint8Array) { let binaryString = ''; for (let i = 0; i < bytes.byteLength; i++) { binaryString += String.fromCharCode(bytes[i]!); } return window.btoa(binaryString); } export function base64ToUint8Array(base64: string) { const binaryString = window.atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes; } export async function encryptData(key: CryptoKey, plaintext: string) { const iv = generateIv(); const data = new TextEncoder().encode(plaintext); const encryptedContent = await window.crypto.subtle.encrypt( { name: "AES-GCM", iv: iv }, key, data ); // Combine IV and Ciphertext into one Uint8Array for easy storage const combined = new Uint8Array(iv.length + encryptedContent.byteLength); combined.set(iv); combined.set(new Uint8Array(encryptedContent), iv.length); return combined; } export async function decrypt(key: CryptoKey, combinedData: Uint8Array) { try { const iv = combinedData.slice(0, 12); const ciphertext = combinedData.slice(12); const decryptedBuffer = await window.crypto.subtle.decrypt( { name: "AES-GCM", iv: iv }, key, ciphertext ); return new TextDecoder().decode(decryptedBuffer); } catch (error) { throw new Error("Decryption failed. Incorrect password or corrupted data."); } }