mirror of
https://github.com/luxfi/threshold.git
synced 2026-10-30 15:15:53 +00:00
VerifyMessage/StoreMessage/MessageContent/Number/BroadcastContent on round structs were value receivers. Go FAQ: "if any method on T takes *T, all methods should take *T." Finalize already takes *T (it mutates round state) so the others should too. Aligns the method set, removes silent struct-copies on every dispatch, and prevents future drift where a value-receiver method accidentally observes pre-Finalize state. No behavior change — all current methods are no-ops or return constants. Touched: round.Abort, round.Output, all CMP keygen/presign/sign rounds, all Doerner keygen/sign rounds, all FROST keygen/sign rounds, xor example. Build + vet clean; CMP/FROST/Doerner -race tests PASS.
158 lines
4.7 KiB
Go
158 lines
4.7 KiB
Go
package presign
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/cronokirby/saferith"
|
|
"github.com/luxfi/threshold/internal/round"
|
|
"github.com/luxfi/threshold/pkg/hash"
|
|
"github.com/luxfi/threshold/pkg/math/arith"
|
|
"github.com/luxfi/threshold/pkg/paillier"
|
|
"github.com/luxfi/threshold/pkg/party"
|
|
zknth "github.com/luxfi/threshold/pkg/zk/nth"
|
|
)
|
|
|
|
var _ round.Round = (*abort1)(nil)
|
|
|
|
type abort1 struct {
|
|
*presign6
|
|
GammaShares map[party.ID]*saferith.Int
|
|
KShares map[party.ID]*saferith.Int
|
|
// DeltaAlphas[j][k] = αⱼₖ
|
|
DeltaAlphas map[party.ID]map[party.ID]*saferith.Int
|
|
}
|
|
|
|
type broadcastAbort1 struct {
|
|
round.NormalBroadcastContent
|
|
// GammaShare = γᵢ
|
|
GammaShare *saferith.Int
|
|
KProof *abortNth
|
|
DeltaProofs map[party.ID]*abortNth
|
|
}
|
|
|
|
// StoreBroadcastMessage implements round.BroadcastRound.
|
|
func (r *abort1) StoreBroadcastMessage(msg round.Message) error {
|
|
from := msg.From
|
|
body, ok := msg.Content.(*broadcastAbort1)
|
|
if !ok || body == nil {
|
|
return round.ErrInvalidContent
|
|
}
|
|
|
|
alphas := make(map[party.ID]*saferith.Int, len(body.DeltaProofs))
|
|
for id, deltaProof := range body.DeltaProofs {
|
|
alphas[id] = deltaProof.Plaintext
|
|
}
|
|
r.DeltaAlphas[from] = alphas
|
|
r.GammaShares[from] = body.GammaShare
|
|
r.KShares[from] = body.KProof.Plaintext
|
|
|
|
public := r.Paillier[from]
|
|
if !body.KProof.Verify(r.HashForID(from), public, r.K[from]) {
|
|
return errors.New("failed to verify validity of k")
|
|
}
|
|
|
|
BigGammaShareActual := r.Group().NewScalar().SetNat(body.GammaShare.Mod(r.Group().Order())).ActOnBase()
|
|
if !r.BigGammaShare[from].Equal(BigGammaShareActual) {
|
|
return errors.New("different BigGammaShare")
|
|
}
|
|
|
|
for id, deltaProof := range body.DeltaProofs {
|
|
if !deltaProof.Verify(r.HashForID(from), public, r.DeltaCiphertext[from][id]) {
|
|
return errors.New("failed to validate Delta MtA Nth proof")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VerifyMessage implements round.Round.
|
|
func (*abort1) VerifyMessage(round.Message) error { return nil }
|
|
|
|
// StoreMessage implements round.Round.
|
|
func (*abort1) StoreMessage(round.Message) error { return nil }
|
|
|
|
// Finalize implements round.Round.
|
|
func (r *abort1) Finalize(chan<- *round.Message) (round.Session, error) {
|
|
var (
|
|
culprits []party.ID
|
|
delta, tmp saferith.Int
|
|
)
|
|
for _, j := range r.OtherPartyIDs() {
|
|
delta.Mul(r.KShares[j], r.GammaShares[j], -1)
|
|
for _, l := range r.PartyIDs() {
|
|
if l == j {
|
|
continue
|
|
}
|
|
delta.Add(&delta, r.DeltaAlphas[j][l], -1)
|
|
tmp.Mul(r.KShares[l], r.GammaShares[j], -1)
|
|
delta.Add(&delta, &tmp, -1)
|
|
tmp.SetInt(r.DeltaAlphas[l][j]).Neg(1)
|
|
delta.Add(&delta, &tmp, -1)
|
|
}
|
|
deltaScalar := r.Group().NewScalar().SetNat(delta.Mod(r.Group().Order()))
|
|
if !deltaScalar.Equal(r.DeltaShares[j]) {
|
|
culprits = append(culprits, j)
|
|
}
|
|
}
|
|
return r.AbortRound(errors.New("abort1: detected culprit"), culprits...), nil
|
|
}
|
|
|
|
// MessageContent implements round.Round.
|
|
func (*abort1) MessageContent() round.Content { return nil }
|
|
|
|
// RoundNumber implements round.Content.
|
|
func (broadcastAbort1) RoundNumber() round.Number { return 7 }
|
|
|
|
// BroadcastContent implements round.BroadcastRound.
|
|
func (r *abort1) BroadcastContent() round.BroadcastContent { return &broadcastAbort1{} }
|
|
|
|
// Number implements round.Round.
|
|
func (*abort1) Number() round.Number { return 7 }
|
|
|
|
// abortNth for a given ciphertext c = end(m,r) contains:
|
|
// - the message m,
|
|
// - the "hidden" nonce r^N % N^2, equal to enc(0,r)
|
|
// - a proof of knowledge of r
|
|
type abortNth struct {
|
|
Plaintext *saferith.Int
|
|
Nonce *saferith.Nat
|
|
Proof *zknth.Proof
|
|
}
|
|
|
|
// proveNth decrypts the message and the nonce contained in the ciphertext c, using the private key.
|
|
// Returns an abortNth proving knowledge of the nonce
|
|
func proveNth(hash *hash.Hash, paillierSecret *paillier.SecretKey, c *paillier.Ciphertext) *abortNth {
|
|
NSquared := paillierSecret.ModulusSquared()
|
|
N := paillierSecret.Modulus()
|
|
deltaShareAlpha, deltaNonce, _ := paillierSecret.DecWithRandomness(c)
|
|
deltaNonceHidden := NSquared.Exp(deltaNonce, N.Nat())
|
|
proof := zknth.NewProof(hash, zknth.Public{
|
|
N: paillierSecret.PublicKey,
|
|
R: deltaNonceHidden,
|
|
}, zknth.Private{Rho: deltaNonce})
|
|
return &abortNth{
|
|
Plaintext: deltaShareAlpha,
|
|
Nonce: deltaNonceHidden,
|
|
Proof: proof,
|
|
}
|
|
}
|
|
|
|
func (msg *abortNth) Verify(hash *hash.Hash, paillierPublic *paillier.PublicKey, c *paillier.Ciphertext) bool {
|
|
if msg == nil || !arith.IsValidNatModN(paillierPublic.ModulusSquared().Modulus, msg.Nonce) || msg.Plaintext == nil {
|
|
return false
|
|
}
|
|
one := new(saferith.Nat).SetUint64(1)
|
|
cExpected := c.Nat()
|
|
cActual := paillierPublic.EncWithNonce(msg.Plaintext, one).Nat()
|
|
cActual.ModMul(cActual, msg.Nonce, paillierPublic.ModulusSquared().Modulus)
|
|
if cExpected.Eq(cActual) != 1 {
|
|
return false
|
|
}
|
|
if !msg.Proof.Verify(hash, zknth.Public{
|
|
N: paillierPublic,
|
|
R: msg.Nonce,
|
|
}) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|