rendezvous.test.ts

3.1 KB · raw

1import { describe, expect, it, vi } from "vitest";2import { generateKeyPairFromSeed } from "@stablelib/x25519";34import {5  DEFAULT_RELAY_URL,6  buildRendezvousUrl,7  bytesToHex,8  deriveMeshRendezvousSecret,9  deriveRendezvousId,10  deriveRendezvousSecret,11  dialingRendezvousIds,12  generatePairingRendezvousToken,13  normalizeRelayUrl,14  rendezvousSecretFromHex,15  rendezvousWindowIndex,16  waitingRendezvousIds,17} from "./rendezvous";1819const HANDSHAKE_HASH = rendezvousSecretFromHex(20  "0fbab8cd1782bf4b17d69beb69f5b80b0bf24d93da4924959c76ee701c32e655",21);2223describe("relay rendezvous contract", () => {24  it("derives the symmetric cross-client mesh capability vector", () => {25    const alice = generateKeyPairFromSeed(new Uint8Array(32).fill(1));26    const bob = generateKeyPairFromSeed(new Uint8Array(32).fill(2));27    const forward = deriveMeshRendezvousSecret(alice.secretKey, alice.publicKey, bob.publicKey);28    const reverse = deriveMeshRendezvousSecret(bob.secretKey, bob.publicKey, alice.publicKey);29    expect(reverse).toEqual(forward);30  });3132  it("derives the cross-platform secret and hourly id KATs", () => {33    const secret = deriveRendezvousSecret(HANDSHAKE_HASH);34    expect(bytesToHex(secret)).toBe(35      "0803691c8ac7330883c0987e33cea4a171992366ce4b06b88691e84ca689773d",36    );37    expect(deriveRendezvousId(secret, 500_000)).toBe(38      "9UY3j5AHnwYSApvymtY965su9n60rQOv8QZ2PZzHuKI",39    );40  });4142  it("uses one-hour windows with mandatory skew tolerance order", () => {43    const secret = deriveRendezvousSecret(HANDSHAKE_HASH);44    const now = 500_000 * 3600 + 3599;45    expect(rendezvousWindowIndex(now)).toBe(500_000);46    expect(waitingRendezvousIds(secret, now)).toEqual([47      deriveRendezvousId(secret, 500_000),48      deriveRendezvousId(secret, 499_999),49    ]);50    expect(dialingRendezvousIds(secret, now)).toEqual([51      deriveRendezvousId(secret, 500_000),52      deriveRendezvousId(secret, 499_999),53      deriveRendezvousId(secret, 500_001),54    ]);55  });5657  it("generates a fresh 128-bit base64url first-pair token", () => {58    vi.spyOn(crypto, "getRandomValues").mockImplementation((array) => {59      (array as Uint8Array).set(Array.from({ length: 16 }, (_, i) => i));60      return array;61    });62    expect(generatePairingRendezvousToken()).toBe("AAECAwQFBgcICQoLDA0ODw");63  });6465  it("accepts only credential-free wss relay URLs", () => {66    expect(normalizeRelayUrl(`${DEFAULT_RELAY_URL}/`)).toBe(DEFAULT_RELAY_URL);67    expect(normalizeRelayUrl("wss://relay.example.test/base/")).toBe(68      "wss://relay.example.test/base",69    );70    expect(() => normalizeRelayUrl("ws://relay.example.test")).toThrow(/wss/);71    expect(() => normalizeRelayUrl("wss://user:[email protected]")).toThrow();72    expect(() => normalizeRelayUrl("wss://relay.example.test?token=x")).toThrow();73  });7475  it("builds the relay path without exposing identity", () => {76    const id = "AAECAwQFBgcICQoLDA0ODw";77    expect(buildRendezvousUrl(DEFAULT_RELAY_URL, id)).toBe(78      `${DEFAULT_RELAY_URL}/r/${id}`,79    );80    expect(() => buildRendezvousUrl(DEFAULT_RELAY_URL, "short")).toThrow();81  });82});