import { describe, expect, it } from "vitest"; import { decrypt, encrypt } from "./aes"; import { deriveKey, generateSalt } from "../crypto/argon2"; describe("background aes", () => { it("encrypt then decrypt round-trips the plaintext", async () => { const key = await deriveKey("correct-pass-12345", generateSalt()); const encrypted = await encrypt(key, "hello peach"); const plaintext = await decrypt( key, encrypted.nonce, encrypted.ciphertext, encrypted.tag ); expect(plaintext).toBe("hello peach"); }); it("decrypt with the wrong key throws", async () => { const salt = generateSalt(); const rightKey = await deriveKey("correct-pass-12345", salt); const wrongKey = await deriveKey("wrong-pass-67890", salt); const encrypted = await encrypt(rightKey, "secret"); await expect( decrypt(wrongKey, encrypted.nonce, encrypted.ciphertext, encrypted.tag) ).rejects.toThrow(); }); it("decrypt with tampered ciphertext throws", async () => { const key = await deriveKey("correct-pass-12345", generateSalt()); const encrypted = await encrypt(key, "secret"); const lastByte = Number.parseInt(encrypted.ciphertext.slice(-2), 16); const tamperedByte = (lastByte ^ 0xff).toString(16).padStart(2, "0"); const tamperedCiphertext = `${encrypted.ciphertext.slice(0, -2)}${tamperedByte}`; await expect( decrypt(key, encrypted.nonce, tamperedCiphertext, encrypted.tag) ).rejects.toThrow(); }); it("encrypt uses a fresh nonce each time", async () => { const key = await deriveKey("correct-pass-12345", generateSalt()); const first = await encrypt(key, "same plaintext"); const second = await encrypt(key, "same plaintext"); expect(first.nonce).not.toBe(second.nonce); expect(first.ciphertext).not.toBe(second.ciphertext); }); it("encrypt output includes the 16-byte auth tag overhead", async () => { const key = await deriveKey("correct-pass-12345", generateSalt()); const plaintext = "hello"; const encrypted = await encrypt(key, plaintext); expect(encrypted.tag).toHaveLength(32); expect(encrypted.ciphertext.length / 2).toBe(new TextEncoder().encode(plaintext).length); }); it("rejects malformed hex before decrypting", async () => { const key = new Uint8Array(32); await expect(decrypt(key, "abc", "00", "00".repeat(16))).rejects.toThrow(/nonce/i); await expect(decrypt(key, "00".repeat(12), "0", "00".repeat(16))).rejects.toThrow(/ciphertext/i); await expect(decrypt(key, "00".repeat(12), "gg", "00".repeat(16))).rejects.toThrow(/ciphertext/i); await expect(decrypt(key, "00".repeat(12), "", "00".repeat(16))).rejects.toThrow(/ciphertext/i); await expect(decrypt(key, "00".repeat(12), "00", "00".repeat(15))).rejects.toThrow(/tag/i); }); });