77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
import { test, expect } from "bun:test";
|
|
import {
|
|
generateKey,
|
|
encrypt,
|
|
decrypt,
|
|
deriveKey,
|
|
sha256,
|
|
hmacSign,
|
|
hmacVerify,
|
|
createKeyring,
|
|
} from "../src/index.ts";
|
|
|
|
test("sha256 is stable and hex-encoded", async () => {
|
|
const a = await sha256("hello");
|
|
expect(a).toBe("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824");
|
|
expect(await sha256("hello")).toBe(a);
|
|
expect(await sha256("world")).not.toBe(a);
|
|
});
|
|
|
|
test("hmacSign / hmacVerify (webhook signatures)", async () => {
|
|
const sig = await hmacSign("payload", "secret");
|
|
expect(await hmacVerify("payload", "secret", sig)).toBe(true);
|
|
expect(await hmacVerify("payload", "wrong", sig)).toBe(false);
|
|
expect(await hmacVerify("tampered", "secret", sig)).toBe(false);
|
|
});
|
|
|
|
test("encrypt/decrypt round-trip", async () => {
|
|
const key = await generateKey();
|
|
const box = await encrypt("card #1234 secret", key);
|
|
expect(box).not.toContain("card"); // opaque
|
|
expect(await decrypt(box, key)).toBe("card #1234 secret");
|
|
});
|
|
|
|
test("each encryption uses a fresh IV (different ciphertexts)", async () => {
|
|
const key = await generateKey();
|
|
const a = await encrypt("same", key);
|
|
const b = await encrypt("same", key);
|
|
expect(a).not.toBe(b);
|
|
expect(await decrypt(a, key)).toBe("same");
|
|
expect(await decrypt(b, key)).toBe("same");
|
|
});
|
|
|
|
test("wrong key or tampered data fails (authenticated)", async () => {
|
|
const key = await generateKey();
|
|
const other = await generateKey();
|
|
const box = await encrypt("secret", key);
|
|
await expect(decrypt(box, other)).rejects.toThrow();
|
|
await expect(decrypt(box.slice(0, -4) + "AAAA", key)).rejects.toThrow();
|
|
});
|
|
|
|
test("deriveKey is deterministic for the same password+salt", async () => {
|
|
const k1 = await deriveKey("hunter2", "user-salt");
|
|
const k2 = await deriveKey("hunter2", "user-salt");
|
|
const k3 = await deriveKey("hunter2", "other-salt");
|
|
expect(k1).toBe(k2);
|
|
expect(k1).not.toBe(k3);
|
|
// usable as an encryption key
|
|
expect(await decrypt(await encrypt("x", k1), k1)).toBe("x");
|
|
});
|
|
|
|
test("keyrings reject duplicate keys and return defensive copies", () => {
|
|
expect(() =>
|
|
createKeyring([
|
|
{ id: "one", secret: "secret-one", active: true },
|
|
{ id: "one", secret: "secret-two" },
|
|
]),
|
|
).toThrow("DUPLICATE");
|
|
|
|
const keyring = createKeyring([
|
|
{ id: "one", secret: "secret-one", active: true },
|
|
{ id: "two", secret: "secret-two" },
|
|
]);
|
|
const active = keyring.active();
|
|
active.secret = "changed";
|
|
expect(keyring.active().secret).toBe("secret-one");
|
|
});
|