mirror of
https://github.com/luxfi/threshold.git
synced 2026-10-30 15:15:53 +00:00
167 lines
4.2 KiB
Go
167 lines
4.2 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/cronokirby/saferith"
|
|
"github.com/fxamacker/cbor/v2"
|
|
"github.com/luxfi/threshold/internal/types"
|
|
"github.com/luxfi/threshold/pkg/math/curve"
|
|
"github.com/luxfi/threshold/pkg/paillier"
|
|
"github.com/luxfi/threshold/pkg/party"
|
|
"github.com/luxfi/threshold/pkg/pedersen"
|
|
)
|
|
|
|
// EmptyConfig creates an empty Config with a fixed group, ready for unmarshalling.
|
|
//
|
|
// This needs to be used for unmarshalling, otherwise the points on the curve can't
|
|
// be decoded.
|
|
func EmptyConfig(group curve.Curve) *Config {
|
|
return &Config{
|
|
Group: group,
|
|
}
|
|
}
|
|
|
|
type configMarshal struct {
|
|
ID party.ID
|
|
Threshold int
|
|
ECDSA, ElGamal curve.Scalar
|
|
P, Q *saferith.Nat
|
|
RID, ChainKey types.RID
|
|
Public []cbor.RawMessage
|
|
}
|
|
|
|
type publicMarshal struct {
|
|
ID party.ID
|
|
ECDSA, ElGamal curve.Point
|
|
N *saferith.Modulus
|
|
S, T *saferith.Nat
|
|
}
|
|
|
|
func (c *Config) MarshalBinary() ([]byte, error) {
|
|
ps := make([]cbor.RawMessage, 0, len(c.Public))
|
|
for _, id := range c.PartyIDs() {
|
|
p := c.Public[id]
|
|
pm := &publicMarshal{
|
|
ID: id,
|
|
ECDSA: p.ECDSA,
|
|
ElGamal: p.ElGamal,
|
|
N: p.Pedersen.N(),
|
|
S: p.Pedersen.S(),
|
|
T: p.Pedersen.T(),
|
|
}
|
|
data, err := cbor.Marshal(pm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ps = append(ps, data)
|
|
}
|
|
return cbor.Marshal(&configMarshal{
|
|
ID: c.ID,
|
|
Threshold: c.Threshold,
|
|
ECDSA: c.ECDSA,
|
|
ElGamal: c.ElGamal,
|
|
P: c.Paillier.P(),
|
|
Q: c.Paillier.Q(),
|
|
RID: c.RID,
|
|
ChainKey: c.ChainKey,
|
|
Public: ps,
|
|
})
|
|
}
|
|
|
|
func (c *Config) UnmarshalBinary(data []byte) error {
|
|
if c.Group == nil {
|
|
return errors.New("config must be initialized using EmptyConfig")
|
|
}
|
|
cm := &configMarshal{
|
|
ECDSA: c.Group.NewScalar(),
|
|
ElGamal: c.Group.NewScalar(),
|
|
}
|
|
if err := cbor.Unmarshal(data, &cm); err != nil {
|
|
return fmt.Errorf("config: %w", err)
|
|
}
|
|
|
|
// check ECDSA, ElGamal
|
|
if cm.ECDSA.IsZero() || cm.ElGamal.IsZero() {
|
|
return errors.New("config: ECDSA or ElGamal secret key is zero")
|
|
}
|
|
|
|
// get Paillier secret key
|
|
if err := paillier.ValidatePrime(cm.P); err != nil {
|
|
return fmt.Errorf("config: prime P: %w", err)
|
|
}
|
|
if err := paillier.ValidatePrime(cm.Q); err != nil {
|
|
return fmt.Errorf("config: prime Q: %w", err)
|
|
}
|
|
paillierSecret := paillier.NewSecretKeyFromPrimes(cm.P, cm.Q)
|
|
|
|
// handle public parameters
|
|
ps := make(map[party.ID]*Public, len(cm.Public))
|
|
for _, pm := range cm.Public {
|
|
p := &publicMarshal{
|
|
ECDSA: c.Group.NewPoint(),
|
|
ElGamal: c.Group.NewPoint(),
|
|
}
|
|
if err := cbor.Unmarshal(pm, p); err != nil {
|
|
return fmt.Errorf("config: party %s: %w", p.ID, err)
|
|
}
|
|
if _, ok := ps[p.ID]; ok {
|
|
return fmt.Errorf("config: party %s: duplicate entry", p.ID)
|
|
}
|
|
|
|
// handle our own key separately
|
|
if p.ID == cm.ID {
|
|
ps[p.ID] = &Public{
|
|
ECDSA: cm.ECDSA.ActOnBase(),
|
|
ElGamal: cm.ElGamal.ActOnBase(),
|
|
Paillier: paillierSecret.PublicKey,
|
|
Pedersen: pedersen.New(paillierSecret.Modulus(), p.S, p.T),
|
|
}
|
|
continue
|
|
}
|
|
|
|
if err := paillier.ValidateN(p.N); err != nil {
|
|
return fmt.Errorf("config: party %s: %w", p.ID, err)
|
|
}
|
|
if err := pedersen.ValidateParameters(p.N, p.S, p.T); err != nil {
|
|
return fmt.Errorf("config: party %s: %w", p.ID, err)
|
|
}
|
|
if p.ECDSA.IsIdentity() || p.ElGamal.IsIdentity() {
|
|
return fmt.Errorf("config: party %s: ECDSA or ElGamal public key is identity", p.ID)
|
|
}
|
|
|
|
paillierPublic := paillier.NewPublicKey(p.N)
|
|
ps[p.ID] = &Public{
|
|
ECDSA: p.ECDSA,
|
|
ElGamal: p.ElGamal,
|
|
Paillier: paillierPublic,
|
|
Pedersen: pedersen.New(paillierPublic.Modulus(), p.S, p.T),
|
|
}
|
|
}
|
|
|
|
// verify number of parties w.r.t. threshold
|
|
// want 0 ⩽ threshold ⩽ n-1
|
|
if !ValidThreshold(cm.Threshold, len(ps)) {
|
|
return fmt.Errorf("config: threshold %d is invalid", cm.Threshold)
|
|
}
|
|
|
|
// check that we are included
|
|
if _, ok := ps[cm.ID]; !ok {
|
|
return errors.New("config: no public data for this party")
|
|
}
|
|
|
|
*c = Config{
|
|
Group: c.Group,
|
|
ID: cm.ID,
|
|
Threshold: cm.Threshold,
|
|
ECDSA: cm.ECDSA,
|
|
ElGamal: cm.ElGamal,
|
|
Paillier: paillierSecret,
|
|
RID: cm.RID,
|
|
ChainKey: cm.ChainKey,
|
|
Public: ps,
|
|
}
|
|
return nil
|
|
}
|