noise-small-order-dh.test.ts

3.0 KB · raw

1// PB-26-014 follow-up — the JS-side DH boundaries in noise-js.ts mirror the2// Rust core's small-order X25519 blocklist (libhydrogen's contributory-behaviour3// list). The WASM core rejects these before every handshake DH; the TypeScript4// sharedKey calls must too, before doing the DH.56import { describe, expect, it } from "vitest";7import { generateKeyPair } from "@stablelib/x25519";8import {9  computeAuthProof,10  hexToBytes,11  isSmallOrderPubkey,12  noiseKKInitiator,13  performResponderAuth,14} from "./noise-js";15import type { NoiseNKSession } from "./noise-js";1617const SMALL_ORDER_PUBKEY_HEX = [18  "00".repeat(32),19  "01" + "00".repeat(31),20  "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880",21  "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7",22  "ec" + "ff".repeat(30) + "7f",23  "ed" + "ff".repeat(30) + "7f",24  "ee" + "ff".repeat(30) + "7f",25  "cdeb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880",26  "4c9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7",27];2829const deadTransport = {30  send: async () => undefined,31  receive: () => new Promise<Uint8Array>(() => undefined),32};3334describe("PB-26-014 follow-up — JS-side small-order X25519 rejection", () => {35  it("flags all nine small-order u-coordinates; a real generated key passes", () => {36    for (const hex of SMALL_ORDER_PUBKEY_HEX) {37      expect(isSmallOrderPubkey(hexToBytes(hex))).toBe(true);38    }39    expect(isSmallOrderPubkey(generateKeyPair().publicKey)).toBe(false);40  });4142  it("KK initiator aborts against a small-order responder static", async () => {43    const initiator = generateKeyPair();44    for (const hex of SMALL_ORDER_PUBKEY_HEX) {45      await expect(46        noiseKKInitiator(47          initiator.secretKey,48          initiator.publicKey,49          hexToBytes(hex),50          deadTransport.send,51          deadTransport.receive,52        ),53      ).rejects.toThrow(/small-order/i);54    }55  });5657  it("computeAuthProof refuses a small-order responder key", () => {58    const initiator = generateKeyPair();59    const nonce = new Uint8Array(32);60    const handshakeHash = new Uint8Array(32);61    expect(() =>62      computeAuthProof(63        initiator.secretKey,64        initiator.publicKey,65        hexToBytes(SMALL_ORDER_PUBKEY_HEX[3]!),66        nonce,67        handshakeHash,68      ),69    ).toThrow(/small-order/i);70  });7172  it("performResponderAuth refuses a PROOF frame with a small-order initiator key", async () => {73    const responder = generateKeyPair();74    const session = { handshakeHash: new Uint8Array(32) } as NoiseNKSession;75    const proofFrame = new TextEncoder().encode(76      JSON.stringify({77        type: "PROOF",78        initiator_public_key_hex: SMALL_ORDER_PUBKEY_HEX[0],79        proof_hex: "00".repeat(32),80      }),81    );82    await expect(83      performResponderAuth(84        session,85        responder.secretKey,86        responder.publicKey,87        async () => undefined,88        async () => proofFrame,89      ),90    ).rejects.toThrow(/small-order/i);91  });92});