Follow-up to #417's round 2 (PR landing now). Mirrors the shape of #541 for the signing-module work.
What round 2 shipped
Unit tests on `keep-frost-net/src/ecdh.rs` killed 47 of 53 mutations that survived the baseline:
| Function |
Before |
After |
Pure helpers (derive_ecdh_session_id, compute_partial_ecdh, lagrange_coefficient, aggregate_ecdh_shares) |
9 surviving |
1 surviving |
EcdhSession state machine + accessors |
29 surviving |
2 surviving |
EcdhSessionManager lifecycle |
15 surviving |
0 surviving |
Final: 63/110 caught (57%), up from 16/110 (15%).
What's still surviving in keep-frost-net/src/node/ecdh.rs (20 mutations)
All on the async coordination handlers, same shape as #541's signing-module survivors. They need multi-peer integration test infrastructure to kill.
| Function |
Surviving mutations |
What that means |
handle_ecdh_request |
5 |
Responder gates: replay window check, peer-policy, participant-membership; multiple delete ! and ==/!= mutations on early-return predicates. |
request_ecdh |
10 |
Requester orchestration: match arms on EcdhComplete / EcdhFailed / Err, terminator predicates, the has_all_shares match guard. |
handle_ecdh_complete |
3 |
Final-secret delivery: subscriber notification + session-id equality check. |
handle_ecdh_share |
2 |
Share ingestion: full function "replace with Ok(())" and the share-index match. |
Remaining 6 in keep-frost-net/src/ecdh.rs
Triaged but deliberately not chased:
aggregate_ecdh_shares line 96 (+= ↔ -=): equivalent on the single-party case because IDENTITY + p = IDENTITY - p produces points with the same x-only output. Distinguishing requires multi-party, same scope as the node/ecdh.rs survivors.
EcdhSession::state line 169 (> ↔ >=): off-by-one at the exact timeout boundary. Hitting it deterministically requires sub-nanosecond timing control.
EcdhSession::try_complete line 222 (- ↔ /): byte-index arithmetic on the Identifier serialization buffer. Both len-2=30 and len/2=16 index into a 32-byte buffer; for small share indices the high bytes are 0 in both positions.
shared_secret() accessor: 3 mutations on the return shape. The new `ecdh_session_shared_secret_returns_stored_value_only_after_completion` test kills the None and constant-leak mutations on the post-completion path; the pre-completion-None path is exercised by the same test.
Proposed approach
Same as #541 — an in-process multi-peer simulator that drives ECDH sessions without a real relay, covering:
- 2-of-3 happy path (kills the "replace Result<()> with Ok(())" mutations because the absence of a real send on any handler breaks the round).
- Replay window edge: a request just outside the window must be refused (kills the
==/!= mutations in handle_ecdh_request).
- Failed completion → subscriber notification (kills the
handle_ecdh_complete mutations).
- Re-run `cargo mutants -p keep-frost-net --file keep-frost-net/src/node/ecdh.rs` and confirm survival rate drops materially.
Related
Follow-up to #417's round 2 (PR landing now). Mirrors the shape of #541 for the signing-module work.
What round 2 shipped
Unit tests on `keep-frost-net/src/ecdh.rs` killed 47 of 53 mutations that survived the baseline:
derive_ecdh_session_id,compute_partial_ecdh,lagrange_coefficient,aggregate_ecdh_shares)EcdhSessionstate machine + accessorsEcdhSessionManagerlifecycleFinal: 63/110 caught (57%), up from 16/110 (15%).
What's still surviving in
keep-frost-net/src/node/ecdh.rs(20 mutations)All on the async coordination handlers, same shape as #541's signing-module survivors. They need multi-peer integration test infrastructure to kill.
handle_ecdh_requestdelete !and==/!=mutations on early-return predicates.request_ecdhEcdhComplete/EcdhFailed/Err, terminator predicates, thehas_all_sharesmatch guard.handle_ecdh_completehandle_ecdh_shareRemaining 6 in
keep-frost-net/src/ecdh.rsTriaged but deliberately not chased:
aggregate_ecdh_sharesline 96 (+=↔-=): equivalent on the single-party case becauseIDENTITY + p = IDENTITY - pproduces points with the same x-only output. Distinguishing requires multi-party, same scope as the node/ecdh.rs survivors.EcdhSession::stateline 169 (>↔>=): off-by-one at the exact timeout boundary. Hitting it deterministically requires sub-nanosecond timing control.EcdhSession::try_completeline 222 (-↔/): byte-index arithmetic on the Identifier serialization buffer. Bothlen-2=30andlen/2=16index into a 32-byte buffer; for small share indices the high bytes are 0 in both positions.shared_secret()accessor: 3 mutations on the return shape. The new `ecdh_session_shared_secret_returns_stored_value_only_after_completion` test kills theNoneand constant-leak mutations on the post-completion path; the pre-completion-None path is exercised by the same test.Proposed approach
Same as #541 — an in-process multi-peer simulator that drives ECDH sessions without a real relay, covering:
==/!=mutations inhandle_ecdh_request).handle_ecdh_completemutations).Related
rotate-password,rotate-data-key,frost refreshsecurity-critical mutations #438 broader security testing.