aes.test.ts

2.8 KB · raw

1import { describe, expect, it } from "vitest";23import { decrypt, encrypt } from "./aes";4import { deriveKey, generateSalt } from "../crypto/argon2";56describe("background aes", () => {7  it("encrypt then decrypt round-trips the plaintext", async () => {8    const key = await deriveKey("correct-pass-12345", generateSalt());910    const encrypted = await encrypt(key, "hello peach");11    const plaintext = await decrypt(12      key,13      encrypted.nonce,14      encrypted.ciphertext,15      encrypted.tag16    );1718    expect(plaintext).toBe("hello peach");19  });2021  it("decrypt with the wrong key throws", async () => {22    const salt = generateSalt();23    const rightKey = await deriveKey("correct-pass-12345", salt);24    const wrongKey = await deriveKey("wrong-pass-67890", salt);25    const encrypted = await encrypt(rightKey, "secret");2627    await expect(28      decrypt(wrongKey, encrypted.nonce, encrypted.ciphertext, encrypted.tag)29    ).rejects.toThrow();30  });3132  it("decrypt with tampered ciphertext throws", async () => {33    const key = await deriveKey("correct-pass-12345", generateSalt());34    const encrypted = await encrypt(key, "secret");35    const lastByte = Number.parseInt(encrypted.ciphertext.slice(-2), 16);36    const tamperedByte = (lastByte ^ 0xff).toString(16).padStart(2, "0");37    const tamperedCiphertext = `${encrypted.ciphertext.slice(0, -2)}${tamperedByte}`;3839    await expect(40      decrypt(key, encrypted.nonce, tamperedCiphertext, encrypted.tag)41    ).rejects.toThrow();42  });4344  it("encrypt uses a fresh nonce each time", async () => {45    const key = await deriveKey("correct-pass-12345", generateSalt());4647    const first = await encrypt(key, "same plaintext");48    const second = await encrypt(key, "same plaintext");4950    expect(first.nonce).not.toBe(second.nonce);51    expect(first.ciphertext).not.toBe(second.ciphertext);52  });5354  it("encrypt output includes the 16-byte auth tag overhead", async () => {55    const key = await deriveKey("correct-pass-12345", generateSalt());56    const plaintext = "hello";5758    const encrypted = await encrypt(key, plaintext);5960    expect(encrypted.tag).toHaveLength(32);61    expect(encrypted.ciphertext.length / 2).toBe(new TextEncoder().encode(plaintext).length);62  });63  it("rejects malformed hex before decrypting", async () => {64    const key = new Uint8Array(32);65    await expect(decrypt(key, "abc", "00", "00".repeat(16))).rejects.toThrow(/nonce/i);66    await expect(decrypt(key, "00".repeat(12), "0", "00".repeat(16))).rejects.toThrow(/ciphertext/i);67    await expect(decrypt(key, "00".repeat(12), "gg", "00".repeat(16))).rejects.toThrow(/ciphertext/i);68    await expect(decrypt(key, "00".repeat(12), "", "00".repeat(16))).rejects.toThrow(/ciphertext/i);69    await expect(decrypt(key, "00".repeat(12), "00", "00".repeat(15))).rejects.toThrow(/tag/i);70  });71});