export async function generateKeyPair() { return await crypto.subtle.generateKey( { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256", }, true, ["encrypt", "decrypt"] ); } export async function exportKey(key: CryptoKey) { const exported = await crypto.subtle.exportKey("jwk", key); return exported; } export async function importKey(jwk: JsonWebKey, usage: KeyUsage[]) { return await crypto.subtle.importKey( "jwk", jwk, { name: "RSA-OAEP", hash: "SHA-256", }, true, usage ); } export async function encryptMessage(publicKey: CryptoKey, message: string) { const encoder = new TextEncoder(); const data = encoder.encode(message); const encrypted = await crypto.subtle.encrypt( { name: "RSA-OAEP", }, publicKey, data ); return arrayBufferToBase64(encrypted); } export async function decryptMessage(privateKey: CryptoKey, encryptedMessage: string) { const data = base64ToArrayBuffer(encryptedMessage); const decrypted = await crypto.subtle.decrypt( { name: "RSA-OAEP", }, privateKey, data ); const decoder = new TextDecoder(); return decoder.decode(decrypted); } function arrayBufferToBase64(buffer: ArrayBuffer) { let binary = ""; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } function base64ToArrayBuffer(base64: string) { const binaryString = atob(base64); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; } // ========== AES-GCM Functions for Hybrid Encryption ========== export async function generateSymKey() { return await crypto.subtle.generateKey( { name: "AES-GCM", length: 256, }, true, ["encrypt", "decrypt"] ); } export async function encryptSymMessage(key: CryptoKey, message: string) { const encoder = new TextEncoder(); const data = encoder.encode(message); const iv = crypto.getRandomValues(new Uint8Array(12)); const encrypted = await crypto.subtle.encrypt( { name: "AES-GCM", iv: iv, }, key, data ); // Combine IV and ciphertext const combined = new Uint8Array(iv.length + encrypted.byteLength); combined.set(iv); combined.set(new Uint8Array(encrypted), iv.length); return arrayBufferToBase64(combined.buffer); } export async function decryptSymMessage(key: CryptoKey, encryptedMessage: string) { const combined = base64ToArrayBuffer(encryptedMessage); const combinedArray = new Uint8Array(combined); const iv = combinedArray.slice(0, 12); const data = combinedArray.slice(12); const decrypted = await crypto.subtle.decrypt( { name: "AES-GCM", iv: iv, }, key, data ); const decoder = new TextDecoder(); return decoder.decode(decrypted); } export async function exportSymKey(key: CryptoKey) { const exported = await crypto.subtle.exportKey("raw", key); return arrayBufferToBase64(exported); } export async function importSymKey(rawKey: string) { const buffer = base64ToArrayBuffer(rawKey); return await crypto.subtle.importKey( "raw", buffer, { name: "AES-GCM", }, true, ["encrypt", "decrypt"] ); } // ========== FILE ENCRYPTION FUNCTIONS ========== /** * Encrypt a file (ArrayBuffer) with AES-GCM */ export async function encryptFile(aesKey: CryptoKey, fileData: ArrayBuffer): Promise { const iv = crypto.getRandomValues(new Uint8Array(12)); const encrypted = await crypto.subtle.encrypt( { name: "AES-GCM", iv, }, aesKey, fileData ); // Combine IV + encrypted data const combined = new Uint8Array(iv.length + encrypted.byteLength); combined.set(iv, 0); combined.set(new Uint8Array(encrypted), iv.length); return combined.buffer; } /** * Decrypt a file (ArrayBuffer) with AES-GCM */ export async function decryptFile(aesKey: CryptoKey, encryptedData: ArrayBuffer): Promise { const combined = new Uint8Array(encryptedData); const iv = combined.slice(0, 12); const data = combined.slice(12); const decrypted = await crypto.subtle.decrypt( { name: "AES-GCM", iv: iv, }, aesKey, data ); return decrypted; }