Skip to content

Validate signing round 9 decommitments as curve points#7

Open
mswilkison wants to merge 1 commit into
codex/review-residual-cleanupfrom
codex/round9-decommit-validation
Open

Validate signing round 9 decommitments as curve points#7
mswilkison wants to merge 1 commit into
codex/review-residual-cleanupfrom
codex/round9-decommit-validation

Conversation

@mswilkison

@mswilkison mswilkison commented Jun 10, 2026

Copy link
Copy Markdown

Stacked on #6.

Summary

Round 9 of ECDSA signing was the last place in the protocol where adversarial wire data reached raw elliptic.Curve.Add without on-curve validation. The decommitted U_j/T_j coordinates from SignRound7Message/SignRound8Message went straight into group addition, while the analogous decommitments in rounds 5 and 7 are validated via crypto.NewECPoint.

  • Malformed decommitments could crash or misdirect honest signers. Go's stdlib curves (registrable via tss.RegisterCurve) panic on off-curve inputs to Add since Go 1.19, so a malicious decommitment was a remote crash vector. btcec returns undefined coordinates instead, turning the malformed input into a U != T abort that blamed the honest reporting party itself (round.WrapError(..., round.PartyID())) — actively wrong fault attribution for orchestration layers that act on culprits.
  • Fix: validate decommitted coordinates as canonical curve points before any group operation and attribute the failure to the sending party; accumulate with ECPoint.Add so an identity-summing decommitment (e.g. U_j = -U_i) is also rejected with sender attribution. The final U != T mismatch — which proves phase-5 misbehaviour but cannot identify the misbehaving party — is now reported without a culprit.
  • Round 1 message validity: also reject nil and negative m up front. Nil previously panicked on Cmp inside the protocol goroutine (bypassing tss.Error reporting), and a negative message only surfaced as an unattributed signature-verification failure in finalize.

Upstream BNB master has the same round-9 gap (plus the !ok && len(values) != 4 decommit condition this fork already fixed), so there is no upstream commit to port; this is a residual finding from re-reviewing the hardened surface.

Tests

  • TestRound9_RejectsMalformedDecommitments — off-curve U_j, off-curve T_j, and identity-summing U_j each abort with the sender attributed (previously: panic on stdlib curves / self-blame on btcec).
  • TestRound9_UTMismatchHasNoCulprit — on-curve but inconsistent decommitments abort with no culprit.
  • TestRound9_ConsistentDecommitmentsSucceed — accept path still completes.
  • TestSigning_Start_RejectsInvalidMessage — nil and negative messages fail Start cleanly.
  • go test ./..., go vet ./..., gofmt -l all clean.

Round 9 was the last place where adversarial wire data reached raw
elliptic.Curve.Add without on-curve validation: a malformed U_j/T_j
decommitment panics Go stdlib curves and yields undefined coordinates on
btcec, and the resulting U != T abort blamed the honest reporting party
itself. Validate the decommitted coordinates via crypto.NewECPoint,
attribute failures to the sender, and report the unattributable U != T
mismatch without a culprit.

Also reject nil and negative messages in signing round 1: nil previously
panicked on Cmp inside the protocol goroutine, and negative values only
surfaced as an unattributed verification failure in finalize.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant