crypto.test.ts

1.7 KB · raw

1import { describe, it, expect } from "vitest";2import { deriveKey, generateSalt } from "./argon2";3import { encrypt, decrypt } from "../background/aes";45describe("vault crypto", () => {6  it("round-trips plaintext through encrypt/decrypt", async () => {7    const salt = generateSalt();8    const key = await deriveKey("correct-horse", salt);9    const { nonce, ciphertext, tag } = await encrypt(key, "hello peach");10    const plain = await decrypt(key, nonce, ciphertext, tag);11    expect(plain).toBe("hello peach");12  });1314  it("rejects tampered ciphertext", async () => {15    const salt = generateSalt();16    const key = await deriveKey("correct-horse", salt);17    const { nonce, ciphertext, tag } = await encrypt(key, "secret data");18    // Flip the first nibble to a guaranteed-different value. Appending a19    // fixed "ff" was a ~1/256 flake: when the random ciphertext already20    // ended in ff the "tamper" was a no-op and decrypt rightly succeeded.21    const tampered = (ciphertext[0] === "f" ? "0" : "f") + ciphertext.slice(1);22    await expect(decrypt(key, nonce, tampered, tag)).rejects.toThrow();23  });2425  it("rejects wrong password", async () => {26    const salt = generateSalt();27    const key1 = await deriveKey("right-password", salt);28    const { nonce, ciphertext, tag } = await encrypt(key1, "secret");29    const key2 = await deriveKey("wrong-password", salt);30    await expect(decrypt(key2, nonce, ciphertext, tag)).rejects.toThrow();31  });3233  it("generates fresh nonce on each encrypt", async () => {34    const salt = generateSalt();35    const key = await deriveKey("test", salt);36    const e1 = await encrypt(key, "data");37    const e2 = await encrypt(key, "data");38    expect(e1.nonce).not.toBe(e2.nonce);39  });40});