2343fcaa7564
fix: noiseKKResponder rejects small-order X25519 initiator keys before the WASM core (audit PB-26-601, v5.1.10)
- time
- 2026-09-06 14:44 UTC · 1 month ago
- machine
- koh2
- agent
- openrouter/tencent/hy4-preview
- session
- PB-26-601..611 audit closeout
- parent
- 21d00cee79fd
↓ peach-browser-2343fcaa.tar.gzatlas at this save
files changed5 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/shared/noise-js.ts+3 −0src/shared/noise-small-order-dh.test.ts+25 −0manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "5.1.9",4+ "version": "5.1.10",55 "description": "Zero-knowledge password manager",66 "browser_specific_settings": {77 "gecko": {manifest.json+1 −1
@@ -2,7 +2,7 @@22 "manifest_version": 3,33 "minimum_chrome_version": "116",44 "name": "Peach Password Manager",5- "version": "5.1.9",5+ "version": "5.1.10",66 "description": "Zero-knowledge password manager",77 "action": {88 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "5.1.9",3+ "version": "5.1.10",44 "description": "Zero-knowledge password manager",55 "license": "GPL-3.0-only",66 "type": "module",src/shared/noise-js.ts+3 −0
@@ -118,6 +118,9 @@118118 send: (msg: Uint8Array) => Promise<void>,119119 receive: () => Promise<Uint8Array>,120120 ): Promise<NoiseNKSession> {121+ if (isSmallOrderPubkey(expectedInitiatorStaticPublicKey)) {122+ throw new Error("noise: small-order X25519 initiator static key");123+ }121124 return rustNoiseKkResponder(122125 ourStaticPrivateKey,123126 ourStaticPublicKey,src/shared/noise-small-order-dh.test.ts+25 −0
@@ -10,6 +10,7 @@1010 hexToBytes,1111 isSmallOrderPubkey,1212 noiseKKInitiator,13+ noiseKKResponder,1314 performResponderAuth,1415 } from "./noise-js";1516 import type { NoiseNKSession } from "./noise-js";@@ -51,6 +52,30 @@5152 deadTransport.receive,5253 ),5354 ).rejects.toThrow(/small-order/i);55+ }56+ });57+58+ 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+ // A transport that reports if the WASM core is reached: the guard must62+ // throw first, so neither callback may ever run.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 });5479 }5580 });5681