12345import { describe, expect, it } from "vitest";6import { generateKeyPair } from "@stablelib/x25519";7import { noiseNKInitiator, noiseNKResponder } from "./noise-js";89const ZERO_POINT = new Uint8Array(32);1011function queuePair() {12 const makeQueue = () => {13 const items: Uint8Array[] = [];14 const waiters: Array<(m: Uint8Array) => void> = [];15 return {16 push(m: Uint8Array) {17 const w = waiters.shift();18 if (w) w(m);19 else items.push(m);20 },21 pull(): Promise<Uint8Array> {22 const m = items.shift();23 return m ? Promise.resolve(m) : new Promise((r) => waiters.push(r));24 },25 };26 };27 const aToB = makeQueue();28 const bToA = makeQueue();29 return {30 a: { send: async (m: Uint8Array) => aToB.push(m), receive: () => bToA.pull() },31 b: { send: async (m: Uint8Array) => bToA.push(m), receive: () => aToB.pull() },32 };33}3435const deadTransport = {36 send: async () => undefined,37 receive: () => new Promise<Uint8Array>(() => undefined),38};3940describe("PCH-02-009 — reject zero X25519 DH outputs", () => {41 it("NK initiator aborts against an all-zero responder static key", async () => {42 await expect(43 noiseNKInitiator(ZERO_POINT, deadTransport.send, deadTransport.receive),44 ).rejects.toThrow(/diffie|dh/i);45 });4647 it("NK responder aborts on an all-zero msg-1 ephemeral", async () => {48 const ourStatic = generateKeyPair();49 const msg1 = new Uint8Array(48);50 await expect(51 noiseNKResponder(52 ourStatic.secretKey,53 ourStatic.publicKey,54 deadTransport.send,55 async () => msg1,56 ),57 ).rejects.toThrow(/diffie|dh/i);58 });5960 it("honest NK handshake still completes and transports a frame (regression)", async () => {61 const responderStatic = generateKeyPair();62 const { a, b } = queuePair();6364 const [initiator, responder] = await Promise.all([65 noiseNKInitiator(responderStatic.publicKey, a.send, a.receive),66 noiseNKResponder(responderStatic.secretKey, responderStatic.publicKey, b.send, b.receive),67 ]);6869 const frame = new TextEncoder().encode("peach-nk-frame");70 expect(responder.decrypt(initiator.encrypt(frame))).toEqual(frame);71 expect(initiator.handshakeHash).toEqual(responder.handshakeHash);72 });7374});