noise.reject-zero-dh.test.ts

2.6 KB · raw

1// PCH-02-009 regression tests — every Noise NK DH must reject an all-zero2// shared secret (low-order / identity peer point) instead of feeding it into3// the key schedule.45import { 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    // The zero point is also small-order: the JS blocklist guard now rejects43    // it before the DH even runs, hence the small-order message.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});