sync-identity.test.ts

3.1 KB · raw

1import { describe, expect, it } from "vitest";2import { sharedKey } from "@stablelib/x25519";3import { computeVaultId, hexToPubkey, pubkeyToHex } from "./sync-identity";4import { deriveSyncIdentityKeypair } from "./legacy-identity-derivation.test-support";56const MASTER = Uint8Array.from(Array.from({ length: 32 }, (_, i) => (i + 1) & 0xff));7const OTHER_MASTER = Uint8Array.from(Array.from({ length: 32 }, (_, i) => (i * 3 + 5) & 0xff));89describe("deriveSyncIdentityKeypair", () => {10  it("is deterministic across calls", () => {11    const a = deriveSyncIdentityKeypair(MASTER, "abc-123");12    const b = deriveSyncIdentityKeypair(MASTER, "abc-123");13    expect(a.publicKey).toEqual(b.publicKey);14    expect(a.secretKey).toEqual(b.secretKey);15  });1617  it("differs across device_id (same master_key)", () => {18    const a = deriveSyncIdentityKeypair(MASTER, "device-A");19    const b = deriveSyncIdentityKeypair(MASTER, "device-B");20    expect(a.publicKey).not.toEqual(b.publicKey);21  });2223  it("differs across master_key (same device_id)", () => {24    const a = deriveSyncIdentityKeypair(MASTER, "device-A");25    const b = deriveSyncIdentityKeypair(OTHER_MASTER, "device-A");26    expect(a.publicKey).not.toEqual(b.publicKey);27  });2829  it("produces 32-byte keys", () => {30    const kp = deriveSyncIdentityKeypair(MASTER, "x");31    expect(kp.publicKey.length).toBe(32);32    expect(kp.secretKey.length).toBe(32);33  });3435  it("yields a working X25519 keypair (DH commutes)", () => {36    const alice = deriveSyncIdentityKeypair(MASTER, "alice");37    const bob = deriveSyncIdentityKeypair(MASTER, "bob");38    const ab = sharedKey(alice.secretKey, bob.publicKey);39    const ba = sharedKey(bob.secretKey, alice.publicKey);40    expect(ab).toEqual(ba);41  });4243  it("rejects empty master key", () => {44    expect(() => deriveSyncIdentityKeypair(new Uint8Array(), "x")).toThrow();45  });4647  it("rejects empty device id", () => {48    expect(() => deriveSyncIdentityKeypair(MASTER, "")).toThrow();49  });50});5152describe("computeVaultId", () => {53  it("is deterministic for the same master key", async () => {54    const a = await computeVaultId(MASTER);55    const b = await computeVaultId(MASTER);56    expect(a).toBe(b);57  });5859  it("differs across master keys", async () => {60    const a = await computeVaultId(MASTER);61    const b = await computeVaultId(OTHER_MASTER);62    expect(a).not.toBe(b);63  });6465  it("returns 64 hex chars (full 32-byte BLAKE3 digest)", async () => {66    const id = await computeVaultId(MASTER);67    expect(id).toMatch(/^[0-9a-f]{64}$/);68  });69});7071describe("pubkeyToHex / hexToPubkey", () => {72  it("round-trips", () => {73    const pk = Uint8Array.from(Array.from({ length: 32 }, (_, i) => i));74    const hex = pubkeyToHex(pk);75    expect(hexToPubkey(hex)).toEqual(pk);76  });7778  it("rejects keys that are not exactly 32 bytes", () => {79    expect(() => hexToPubkey("abc")).toThrow();80    expect(() => hexToPubkey("00".repeat(31))).toThrow();81    expect(() => hexToPubkey("00".repeat(33))).toThrow();82  });8384  it("rejects invalid hex chars", () => {85    expect(() => hexToPubkey("zz".repeat(32))).toThrow();86  });87});