mirror of
https://github.com/luxfi/mpc.git
synced 2026-09-24 00:26:24 +00:00
fix(keygen): emit full compressed ECDSA pubkey (33B), not bare X — fixes ~50% wrong addresses (#19)
The CGGMP21 keygen session serialized the group public key as the bare 32-byte X-coordinate (PublicPoint().XScalar()), dropping the Y parity. pubKeyToEVMAddress and pubKeyToBtcAddress then GUESS even-Y (0x02 prefix) when handed a 32-byte key, so for every ODD-Y key (~50%) they derive the WRONG address — the sibling curve point — which does NOT match the key the ring actually signs with. A threshold signature over a digest then recovers to a different address than keygen reported, making the wallet unusable (funds sent to the reported address are unspendable). Fix: serialize PublicPoint().MarshalBinary() (33-byte compressed, 0x02/0x03 || X) — exactly what signing_session_lss.go already uses for recovery — so keygen's address == the signing key's address. pubKeyToEVMAddress (case 33), pubKeyToBtcAddress (case 33), and CalculateRecoveryByte all already accept the 33-byte compressed form; the 32-byte path stays only as a legacy fallback. Root-caused live: direct ring keygen returned evm_address 0x84e0…f894 for ecdsa_pub_key X=5b0e9ad0…; a direct threshold sign recovered pubkey 03·5b0e9ad0… (odd Y) → real address 0x10B8…15A9. The +1 parity bit is the whole bug. Co-authored-by: hanzo-dev <[email protected]>
This commit is contained in:
committed by
GitHub
co-authored by
hanzo-dev
parent
acdd6b6dfd
commit
d625ef533a
@@ -395,13 +395,20 @@ func (s *cggmp21KeygenSession) publishResult() {
|
||||
return
|
||||
}
|
||||
|
||||
// Convert public key to hex
|
||||
// Use the X coordinate as a simple representation
|
||||
// Serialize the FULL public-key POINT (33-byte compressed: 0x02/0x03 || X),
|
||||
// NOT the bare 32-byte X-scalar. MarshalBinary preserves the Y parity, which
|
||||
// the address/recovery paths need. Emitting only X loses parity, and
|
||||
// pubKeyToEVMAddress/pubKeyToBtcAddress then GUESS even-Y (0x02) — deriving the
|
||||
// WRONG address (the sibling curve point) for ~50% of keys (every odd-Y key),
|
||||
// so the reported address does not match the key the ring actually signs with.
|
||||
// The signing session already uses PublicPoint().MarshalBinary() (see
|
||||
// signing_session_lss.go); keygen MUST serialize identically so keygen address
|
||||
// == signing key. pubKeyToEVMAddress/pubKeyToBtcAddress + CalculateRecoveryByte
|
||||
// all accept the 33-byte compressed form.
|
||||
var pubKeyHex string
|
||||
if s.config != nil && s.config.PublicPoint() != nil {
|
||||
if xScalar := s.config.PublicPoint().XScalar(); xScalar != nil {
|
||||
xBytes, _ := xScalar.MarshalBinary()
|
||||
pubKeyHex = fmt.Sprintf("%x", xBytes)
|
||||
if pubBytes, err := s.config.PublicPoint().MarshalBinary(); err == nil && len(pubBytes) > 0 {
|
||||
pubKeyHex = fmt.Sprintf("%x", pubBytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user