1import { beforeEach, describe, expect, it, vi } from "vitest";2import { generateKeyPairFromSeed } from "@stablelib/x25519";3import { initHash as nkInitHash, hkdf2 as nkHkdf2 } from "./noise-js";456789101112131415vi.mock("@stablelib/x25519", async (importOriginal) => {16 const actual = await importOriginal<typeof import("@stablelib/x25519")>();17 return {18 ...actual,19 generateKeyPair: () => {20 const g = globalThis as { __kpCounter?: number };21 g.__kpCounter = (g.__kpCounter ?? 0) + 1;22 return actual.generateKeyPairFromSeed(new Uint8Array(32).fill(g.__kpCounter));23 },24 };25});2627function memoryPair() {28 const a: Uint8Array[] = [];29 const b: Uint8Array[] = [];30 const aR: Array<(v: Uint8Array) => void> = [];31 const bR: Array<(v: Uint8Array) => void> = [];32 const tA = {33 async send(m: Uint8Array) { const r = bR.shift(); if (r) r(m); else b.push(m); },34 receive(): Promise<Uint8Array> {35 const v = a.shift();36 if (v) return Promise.resolve(v);37 return new Promise<Uint8Array>((resolve) => aR.push(resolve));38 },39 };40 const tB = {41 async send(m: Uint8Array) { const r = aR.shift(); if (r) r(m); else a.push(m); },42 receive(): Promise<Uint8Array> {43 const v = b.shift();44 if (v) return Promise.resolve(v);45 return new Promise<Uint8Array>((resolve) => bR.push(resolve));46 },47 };48 return [tA, tB] as const;49}5051function hex(b: Uint8Array): string {52 return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");53}5455565758describe("PCH-01-002 — BLAKE2s primitive vectors (shared with peach-android)", () => {59 const CK = new Uint8Array(32).map((_, i) => i); 60 const IKM = new Uint8Array(32).map((_, i) => i + 32); 6162 it("initHash is BLAKE2s-256 of the NK protocol name", () => {63 expect(hex(nkInitHash())).toBe(64 "e01688d376ef280a27a8af5c559024a02017585b3763763e4f3c7118a979b09e",65 );66 });6768 it("hkdf2 (HKDF-BLAKE2s) matches the frozen vector", () => {69 const [k1, k2] = nkHkdf2(CK, IKM);70 expect(hex(k1)).toBe("6a96444e20e8d4c1cee974416acae1c10b3c92886010e54ed94dafb2c3b80ea0");71 expect(hex(k2)).toBe("57af120b0de7acbe7907ec149c5ae870a2dbb74232b65777ba4123f1f7f888f5");72 });73});7475describe("PCH-01-002 — Noise handshakes derive over BLAKE2s", () => {76 beforeEach(() => {77 (globalThis as { __kpCounter?: number }).__kpCounter = 0;78 });7980 it("noise_nk_handshake_hash_is_blake2s_derived", async () => {81 const { noiseNKInitiator, noiseNKResponder } = await import("./noise-js");82 const responder = generateKeyPairFromSeed(new Uint8Array(32).fill(0xc3));83 const [tA, tB] = memoryPair();8485 const [a, b] = await Promise.all([86 noiseNKInitiator(responder.publicKey, tA.send, tA.receive),87 noiseNKResponder(responder.secretKey, responder.publicKey, tB.send, tB.receive),88 ]);8990 expect(hex(a.handshakeHash)).toBe(hex(b.handshakeHash));91 expect(hex(a.handshakeHash)).toBe(92 "0fbab8cd1782bf4b17d69beb69f5b80b0bf24d93da4924959c76ee701c32e655",93 );9495 const ct = a.encrypt(new TextEncoder().encode("ping"));96 expect(new TextDecoder().decode(b.decrypt(ct))).toBe("ping");97 });98});