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 43 44 await expect(45 noiseNKInitiator(ZERO_POINT, deadTransport.send, deadTransport.receive),46 ).rejects.toThrow(/diffie|dh|small-order/i);47 });4849 it("NK responder aborts on an all-zero msg-1 ephemeral", async () => {50 const ourStatic = generateKeyPair();51 const msg1 = new Uint8Array(48);52 await expect(53 noiseNKResponder(54 ourStatic.secretKey,55 ourStatic.publicKey,56 deadTransport.send,57 async () => msg1,58 ),59 ).rejects.toThrow(/diffie|dh/i);60 });6162 it("honest NK handshake still completes and transports a frame (regression)", async () => {63 const responderStatic = generateKeyPair();64 const { a, b } = queuePair();6566 const [initiator, responder] = await Promise.all([67 noiseNKInitiator(responderStatic.publicKey, a.send, a.receive),68 noiseNKResponder(responderStatic.secretKey, responderStatic.publicKey, b.send, b.receive),69 ]);7071 const frame = new TextEncoder().encode("peach-nk-frame");72 expect(responder.decrypt(initiator.encrypt(frame))).toEqual(frame);73 expect(initiator.handshakeHash).toEqual(responder.handshakeHash);74 });7576});