export interface AesGcmCiphertext { iv: Uint8Array; ciphertext: Uint8Array; } export async function aesGcmEncrypt(key: CryptoKey, plaintext: Uint8Array | ArrayBuffer): Promise { const iv = crypto.getRandomValues(new Uint8Array(12)); const plaintextBuffer = plaintext instanceof Uint8Array ? plaintext.buffer as ArrayBuffer : plaintext; const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintextBuffer); return { iv, ciphertext: new Uint8Array(ct) }; } export async function aesGcmDecrypt(key: CryptoKey, iv: Uint8Array | ArrayBuffer, ciphertext: Uint8Array | ArrayBuffer): Promise { // Normalize IV to ArrayBuffer (12 bytes for AES-GCM) const ivBuf: ArrayBuffer = iv instanceof Uint8Array ? (iv.buffer as ArrayBuffer).slice(iv.byteOffset, iv.byteOffset + iv.byteLength) : (iv as ArrayBuffer); // Normalize ciphertext to a contiguous ArrayBuffer slice const ctBuf: ArrayBuffer = ciphertext instanceof Uint8Array ? (ciphertext.buffer as ArrayBuffer).slice(ciphertext.byteOffset, ciphertext.byteOffset + ciphertext.byteLength) : (ciphertext as ArrayBuffer); const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: ivBuf }, key, ctBuf); return new Uint8Array(pt as ArrayBuffer); } export async function importAesGcmKey(rawKey: Uint8Array | ArrayBuffer): Promise { const keyBuffer = rawKey instanceof Uint8Array ? rawKey.buffer as ArrayBuffer : rawKey; return crypto.subtle.importKey("raw", keyBuffer, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]); }