TestEmbeddedDispatcher_BLSRoundTrip and _AuthGate drove "bls". thresholdd
stopped serving that scheme — trusted-dealer threshold-BLS custody was ripped
for security (H-2), and luxfi/threshold v1.12.5's dispatcher exposes exactly
cggmp21, frost, pulsar, corona, magnetar and doerner (pkg/thresholdd/zap_schema.go;
there is no bls.go). So both tests had been failing on every run:
bls.keygen: zapclient: unknown procedure "bls.keygen"
wrong-id: error zapclient: unknown procedure "bls.keygen" does not name unauthorized
The second line is the damaging one. _AuthGate asserts a wrong NodeID is refused
with "unauthorized"; it was instead getting "unknown procedure" back from a
scheme lookup that failed before auth was ever consulted. The auth gate has not
actually been exercised for as long as the scheme has been gone.
FROST is a live production scheme on the same ZAP wire and keygens as fast, so
the calls move to it and every assertion is kept verbatim — pubkey non-empty,
3 shares, non-empty signature, verify true, forgery rejected, wrong NodeID
refused with "unauthorized". Nothing is skipped, softened or dropped; the
round-trip now lands on a surface that exists. _UnknownScheme still drives
"bogus" and still proves the unknown-procedure contract.
Renamed to TestEmbeddedDispatcher_FROSTRoundTrip so the name states what it runs.
go test ./... is green across the module.
Co-authored-by: Hanzo Dev <[email protected]>
Red round 5 finding B1. The embedded dispatcher on --threshold-listen
was an anonymous local signing oracle:
1. Any process on the host could POST `{"method":"frost.sign",...}`
and obtain signatures with no auth gate (only the API on :9800
enforced MPC_INTERNAL_API_KEY).
2. An operator typo `MPC_THRESHOLD_LISTEN=0.0.0.0:7300` exposed it
cluster-wide.
3. Any SSRF inside mpcd pivoted into the dispatcher.
Fix mirrors the existing internal-API auth pattern (cmd/mpcd/main.go
line ~456):
- Auth token: read MPC_THRESHOLD_AUTH_TOKEN; if unset, derive a
deterministic per-cluster token from the node Ed25519 seed
(SHA-256(seed || "mpc-threshold-rpc") hex). All nodes share the
same key without extra config; log warns when derivation is used.
Server.SetAuthToken installs the gate.
- Loopback bind: refuse non-loopback addr unless
MPC_THRESHOLD_ALLOW_REMOTE=1 — operators must explicitly
acknowledge the exposure. Bare `:port` (= 0.0.0.0) is NOT loopback.
Also picks up luxfi/threshold v1.6.10 which removes the pulsar/corona
in-memory-token surface (Red HIGH B2) — pulsar.*/corona.* now return a
typed "not yet implemented" error.
Tests:
- TestIsThresholdLoopback locks the loopback predicate (8 cases:
127.0.0.1, [::1], localhost OK; 0.0.0.0, :port, RFC1918 refused).
- TestEmbeddedDispatcher_AuthGate proves 401 on missing/wrong token,
200 on valid token through the dispatcher mpcd installs.
4 tests pass in cmd/mpcd (was 2, +2).
mpcd is the production threshold daemon. With this commit it embeds
the JSON-RPC dispatcher from luxfi/threshold/pkg/thresholdd on a new
--threshold-listen flag (default 127.0.0.1:7300, MPC_THRESHOLD_LISTEN
env var). The dispatcher exposes every supported scheme:
cggmp21 (secp256k1 ECDSA, CGGMP21 fork)
frost (secp256k1 Schnorr, RFC 9591)
bls (BLS12-381 Shamir/Lagrange)
sr25519 (Ristretto255, via mpc node's own session — separate path)
pulsar (M-LWE / FIPS 204 ML-DSA-65 threshold, via luxfi/corona/threshold)
corona (R-LWE / Corona, via luxfi/corona/threshold)
doerner (reserved; upstream protocol non-functional, returns explicit error)
Wire format matches teleport/mpc/src/signers/rpc.ts byte-for-byte:
POST / {"jsonrpc":"2.0","id":N,"method":"<scheme>.<op>","params":{...}}
Methods per scheme: keygen / sign / verify. Public-key handles are
returned by keygen and bound on sign/verify.
Decomplecting note: the dispatcher carries zero policy. Profile
gating, auth, audit, and rate-limiting live on mpcd's existing API
surface (and on the cluster ingress in front of it). The threshold
listener is loopback by default; production exposure happens via the
KMS-facing ZAP server or the gateway.
There is no separate thresholdd daemon to deploy. The luxfi/threshold
repo's cmd/thresholdd remains as a developer / CI binary that imports
the same package — one wire, one implementation, two startup paths.
Tests: TestEmbeddedDispatcher_BLSRoundTrip (full keygen → sign →
verify → forgery rejection) and TestEmbeddedDispatcher_UnknownScheme
(-32601 method-not-found contract) both pass. README updated with the
new protocol table; full surface documented in
luxfi/threshold/LLM.md "thresholdd — unified JSON-RPC dispatcher".