123456import { describe, expect, it } from "vitest";7import { generateKeyPair } from "@stablelib/x25519";8import {9 computeAuthProof,10 hexToBytes,11 isSmallOrderPubkey,12 noiseKKInitiator,13 noiseKKResponder,14 performResponderAuth,15} from "./noise-js";16import type { NoiseNKSession } from "./noise-js";1718const SMALL_ORDER_PUBKEY_HEX = [19 "00".repeat(32),20 "01" + "00".repeat(31),21 "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880",22 "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7",23 "ec" + "ff".repeat(30) + "7f",24 "ed" + "ff".repeat(30) + "7f",25 "ee" + "ff".repeat(30) + "7f",26 "cdeb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880",27 "4c9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7",28];2930const deadTransport = {31 send: async () => undefined,32 receive: () => new Promise<Uint8Array>(() => undefined),33};3435describe("PB-26-014 follow-up — JS-side small-order X25519 rejection", () => {36 it("flags all nine small-order u-coordinates; a real generated key passes", () => {37 for (const hex of SMALL_ORDER_PUBKEY_HEX) {38 expect(isSmallOrderPubkey(hexToBytes(hex))).toBe(true);39 }40 expect(isSmallOrderPubkey(generateKeyPair().publicKey)).toBe(false);41 });4243 it("KK initiator aborts against a small-order responder static", async () => {44 const initiator = generateKeyPair();45 for (const hex of SMALL_ORDER_PUBKEY_HEX) {46 await expect(47 noiseKKInitiator(48 initiator.secretKey,49 initiator.publicKey,50 hexToBytes(hex),51 deadTransport.send,52 deadTransport.receive,53 ),54 ).rejects.toThrow(/small-order/i);55 }56 });5758 it("PB-26-601: KK responder aborts against a small-order initiator static", async () => {59 const responder = generateKeyPair();60 for (const hex of SMALL_ORDER_PUBKEY_HEX) {61 62 63 const touched = { send: 0, receive: 0 };64 await expect(65 noiseKKResponder(66 responder.secretKey,67 responder.publicKey,68 hexToBytes(hex),69 async () => {70 touched.send += 1;71 },72 () => {73 touched.receive += 1;74 return Promise.reject(new Error("reached the WASM core"));75 },76 ),77 ).rejects.toThrow(/small-order/i);78 expect(touched).toEqual({ send: 0, receive: 0 });79 }80 });8182 it("computeAuthProof refuses a small-order responder key", () => {83 const initiator = generateKeyPair();84 const nonce = new Uint8Array(32);85 const handshakeHash = new Uint8Array(32);86 expect(() =>87 computeAuthProof(88 initiator.secretKey,89 initiator.publicKey,90 hexToBytes(SMALL_ORDER_PUBKEY_HEX[3]!),91 nonce,92 handshakeHash,93 ),94 ).toThrow(/small-order/i);95 });9697 it("performResponderAuth refuses a PROOF frame with a small-order initiator key", async () => {98 const responder = generateKeyPair();99 const session = { handshakeHash: new Uint8Array(32) } as NoiseNKSession;100 const proofFrame = new TextEncoder().encode(101 JSON.stringify({102 type: "PROOF",103 initiator_public_key_hex: SMALL_ORDER_PUBKEY_HEX[0],104 proof_hex: "00".repeat(32),105 }),106 );107 await expect(108 performResponderAuth(109 session,110 responder.secretKey,111 responder.publicKey,112 async () => undefined,113 async () => proofFrame,114 ),115 ).rejects.toThrow(/small-order/i);116 });117});