export async function deriveKeyFromPassword(password: string): Promise { const encoder = new TextEncoder(); const keyMaterial = await crypto.subtle.importKey( "raw", encoder.encode(password), "PBKDF2", false, ["deriveBits", "deriveKey"] ); const key = await crypto.subtle.deriveKey( { name: "PBKDF2", salt: encoder.encode("dead-man-switch-salt"), iterations: 100000, hash: "SHA-256", }, keyMaterial, { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"] ); const exported = await crypto.subtle.exportKey("raw", key); return btoa(String.fromCharCode(...new Uint8Array(exported))); } export async function generateKey(): Promise { const key = await crypto.subtle.generateKey( { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"] ); const exported = await crypto.subtle.exportKey("raw", key); return btoa(String.fromCharCode(...new Uint8Array(exported))); } export async function importKey(keyString: string): Promise { const keyData = Uint8Array.from(atob(keyString), (c) => c.charCodeAt(0)); return crypto.subtle.importKey( "raw", keyData, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"] ); } export async function encrypt(data: string, keyString: string): Promise { const key = await importKey(keyString); const iv = crypto.getRandomValues(new Uint8Array(12)); const encoded = new TextEncoder().encode(data); const encrypted = await crypto.subtle.encrypt( { name: "AES-GCM", iv }, key, encoded ); const combined = new Uint8Array(iv.length + encrypted.byteLength); combined.set(iv); combined.set(new Uint8Array(encrypted), iv.length); return btoa(String.fromCharCode(...combined)); } export async function decrypt(encryptedData: string, keyString: string): Promise { const key = await importKey(keyString); const combined = Uint8Array.from(atob(encryptedData), (c) => c.charCodeAt(0)); const iv = combined.slice(0, 12); const data = combined.slice(12); const decrypted = await crypto.subtle.decrypt( { name: "AES-GCM", iv }, key, data ); return new TextDecoder().decode(decrypted); } export async function encryptFile(file: File, keyString: string): Promise { const key = await importKey(keyString); const iv = crypto.getRandomValues(new Uint8Array(12)); const arrayBuffer = await file.arrayBuffer(); const encrypted = await crypto.subtle.encrypt( { name: "AES-GCM", iv }, key, arrayBuffer ); const combined = new Uint8Array(iv.length + encrypted.byteLength); combined.set(iv); combined.set(new Uint8Array(encrypted), iv.length); return new Blob([combined], { type: "application/octet-stream" }); } export async function decryptFile(encryptedBlob: Blob, keyString: string): Promise { const key = await importKey(keyString); const combined = new Uint8Array(await encryptedBlob.arrayBuffer()); const iv = combined.slice(0, 12); const data = combined.slice(12); return crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, data); }