-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add ecdsatss, currently refactoring ecdsa/keygen for simpler system
- Loading branch information
1 parent
409388d
commit 5e39ded
Showing
4 changed files
with
393 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package ecdsatss | ||
|
||
import ( | ||
"math/big" | ||
|
||
"github.com/ModChain/tss-lib/v2/crypto" | ||
"github.com/ModChain/tss-lib/v2/crypto/paillier" | ||
) | ||
|
||
// Key represents the data for a local share of key | ||
type Key struct { | ||
LocalPreParams | ||
LocalSecrets | ||
|
||
Ks []*big.Int // original indexes (ki in signing preparation phase) | ||
NTildej, H1j, H2j []*big.Int // // n-tilde, h1, h2 for range proofs | ||
// public keys (Xj = uj*G for each Pj) | ||
BigXj []*crypto.ECPoint // Xj | ||
PaillierPKs []*paillier.PublicKey // pkj | ||
// used for test assertions (may be discarded) | ||
ECDSAPub *crypto.ECPoint // y | ||
} | ||
|
||
type LocalPreParams struct { | ||
PaillierSK *paillier.PrivateKey // ski | ||
NTildei, H1i, H2i, Alpha, Beta, P, Q *big.Int | ||
} | ||
|
||
type LocalSecrets struct { | ||
// secret fields (not shared, but stored locally) | ||
Xi, ShareID *big.Int // xi, kj | ||
} | ||
|
||
func (preParams LocalPreParams) Validate() bool { | ||
return preParams.PaillierSK != nil && | ||
preParams.NTildei != nil && | ||
preParams.H1i != nil && | ||
preParams.H2i != nil | ||
} | ||
|
||
func (preParams LocalPreParams) ValidateWithProof() bool { | ||
return preParams.Validate() && | ||
preParams.PaillierSK.P != nil && | ||
preParams.PaillierSK.Q != nil && | ||
preParams.Alpha != nil && | ||
preParams.Beta != nil && | ||
preParams.P != nil && | ||
preParams.Q != nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package ecdsatss | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"math/big" | ||
|
||
"github.com/ModChain/tss-lib/v2/common" | ||
"github.com/ModChain/tss-lib/v2/crypto" | ||
cmts "github.com/ModChain/tss-lib/v2/crypto/commitments" | ||
"github.com/ModChain/tss-lib/v2/crypto/dlnproof" | ||
"github.com/ModChain/tss-lib/v2/crypto/vss" | ||
"github.com/ModChain/tss-lib/v2/tss" | ||
) | ||
|
||
var zero = big.NewInt(0) | ||
|
||
// Keygen is an object used to track a key currently being generated | ||
type Keygen struct { | ||
params *tss.Parameters // contains curve, parties, etc | ||
KGCs []cmts.HashCommitment | ||
vs vss.Vs | ||
ssid []byte // ssid for current round/values | ||
ssidNonce *big.Int | ||
shares vss.Shares | ||
deCommitPolyG cmts.HashDeCommitment | ||
data *Key // key data currently being generated | ||
round int // current round | ||
|
||
Receiver tss.JsonExpect | ||
} | ||
|
||
func NewKeygen(params *tss.Parameters) (*Keygen, error) { | ||
partyCount := params.PartyCount() | ||
res := &Keygen{ | ||
params: params, | ||
KGCs: make([]cmts.HashCommitment, partyCount), | ||
data: &Key{}, | ||
round: 1, | ||
} | ||
err := res.round1() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return res, nil | ||
} | ||
|
||
// getSSID returns ssid from local params | ||
func (kg *Keygen) getSSID(roundNum int) ([]byte, error) { | ||
ssidList := []*big.Int{kg.params.EC().Params().P, kg.params.EC().Params().N, kg.params.EC().Params().Gx, kg.params.EC().Params().Gy} // ec curve | ||
ssidList = append(ssidList, kg.params.Parties().IDs().Keys()...) | ||
ssidList = append(ssidList, big.NewInt(int64(roundNum))) // round number | ||
ssidList = append(ssidList, kg.ssidNonce) | ||
ssid := common.SHA512_256i(ssidList...).Bytes() | ||
|
||
return ssid, nil | ||
} | ||
|
||
func (kg *Keygen) round1() error { | ||
Pi := kg.params.PartyID() | ||
i := Pi.Index | ||
// 1. calculate "partial" key share ui | ||
ui := common.GetRandomPositiveInt(kg.params.PartialKeyRand(), kg.params.EC().Params().N) | ||
|
||
// 2. compute the vss shares | ||
ids := kg.params.Parties().IDs().Keys() | ||
vs, shares, err := vss.Create(kg.params.EC(), kg.params.Threshold(), ui, ids, kg.params.Rand()) | ||
if err != nil { | ||
return err | ||
} | ||
kg.data.Ks = ids | ||
|
||
// security: the original u_i may be discarded | ||
ui = zero // clears the secret data from memory | ||
_ = ui // silences a linter warning | ||
|
||
// make commitment -> (C, D) | ||
pGFlat, err := crypto.FlattenECPoints(vs) | ||
if err != nil { | ||
return err | ||
} | ||
cmt := cmts.NewHashCommitment(kg.params.Rand(), pGFlat...) | ||
|
||
// 4. generate Paillier public key E_i, private key and proof | ||
// 5-7. generate safe primes for ZKPs used later on | ||
// 9-11. compute ntilde, h1, h2 (uses safe primes) | ||
var preParams *LocalPreParams | ||
if kg.data.LocalPreParams.Validate() && !kg.data.LocalPreParams.ValidateWithProof() { | ||
return errors.New("`optionalPreParams` failed to validate; it might have been generated with an older version of tss-lib") | ||
} else if kg.data.LocalPreParams.ValidateWithProof() { | ||
preParams = &kg.data.LocalPreParams | ||
} else { | ||
ctx, cancel := context.WithTimeout(context.Background(), kg.params.SafePrimeGenTimeout()) | ||
defer cancel() | ||
preParams, err = (&LocalPreGenerator{Context: ctx, Rand: kg.params.Rand(), Concurrency: kg.params.Concurrency()}).Generate() | ||
if err != nil { | ||
return errors.New("pre-params generation failed") | ||
} | ||
} | ||
kg.data.LocalPreParams = *preParams | ||
kg.data.NTildej[i] = preParams.NTildei | ||
kg.data.H1j[i], kg.data.H2j[i] = preParams.H1i, preParams.H2i | ||
|
||
// generate the dlnproofs for keygen | ||
h1i, h2i, alpha, beta, p, q, NTildei := preParams.H1i, preParams.H2i, preParams.Alpha, preParams.Beta, preParams.P, preParams.Q, preParams.NTildei | ||
dlnProof1 := dlnproof.NewDLNProof(h1i, h2i, alpha, p, q, NTildei, kg.params.Rand()) | ||
dlnProof2 := dlnproof.NewDLNProof(h2i, h1i, beta, p, q, NTildei, kg.params.Rand()) | ||
|
||
// for this P: SAVE | ||
// - shareID | ||
// and keep in temporary storage: | ||
// - VSS Vs | ||
// - our set of Shamir shares | ||
kg.ssidNonce = new(big.Int).SetUint64(0) | ||
kg.data.ShareID = ids[i] | ||
kg.vs = vs | ||
ssid, err := kg.getSSID(kg.round) // for round 1 | ||
if err != nil { | ||
return errors.New("failed to generate ssid") | ||
} | ||
kg.ssid = ssid | ||
kg.shares = shares | ||
|
||
// for this P: SAVE de-commitments, paillier keys for round 2 | ||
kg.data.PaillierSK = preParams.PaillierSK | ||
kg.data.PaillierPKs[i] = &preParams.PaillierSK.PublicKey | ||
kg.deCommitPolyG = cmt.D | ||
|
||
// send commitments, paillier pk + proof; round 1 message | ||
// round.PartyID(), cmt.C, &preParams.PaillierSK.PublicKey, preParams.NTildei, preParams.H1i, preParams.H2i, dlnProof1, dlnProof2 | ||
dlnProof1Bz, err := dlnProof1.Serialize() | ||
if err != nil { | ||
return err | ||
} | ||
dlnProof2Bz, err := dlnProof2.Serialize() | ||
if err != nil { | ||
return err | ||
} | ||
msg := &keygenRound1msg{ | ||
Commitment: cmt.C.Bytes(), | ||
PaillierN: preParams.PaillierSK.PublicKey.N.Bytes(), | ||
NTilde: preParams.NTildei.Bytes(), | ||
H1: preParams.H1i.Bytes(), | ||
H2: preParams.H2i.Bytes(), | ||
Dlnproof_1: dlnProof1Bz, | ||
Dlnproof_2: dlnProof2Bz, | ||
} | ||
|
||
var otherIds []*tss.PartyID | ||
for n, p := range kg.params.Parties().IDs() { | ||
if n == i { | ||
// do not send to self | ||
continue | ||
} | ||
otherIds = append(otherIds, p) | ||
m := tss.JsonWrap("ecdsa:keygen:round1", msg, Pi, p) | ||
_ = m | ||
} | ||
|
||
kg.Receiver = tss.NewJsonExpect[keygenRound2msg1]("ecdsa:keygen:round2-1", otherIds, kg.round2) | ||
|
||
return nil | ||
} | ||
|
||
func (kg *Keygen) round2(otherIds []*tss.PartyID, r2msg1 []*keygenRound2msg1) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package ecdsatss | ||
|
||
// messages for keygen | ||
|
||
type keygenRound1msg struct { | ||
Commitment []byte | ||
PaillierN []byte | ||
NTilde []byte | ||
H1 []byte | ||
H2 []byte | ||
Dlnproof_1 [][]byte | ||
Dlnproof_2 [][]byte | ||
} | ||
|
||
type keygenRound2msg1 struct { | ||
Share []byte | ||
FacProof [][]byte | ||
} | ||
|
||
type keygenRound2msg2 struct { | ||
DeCommitment [][]byte | ||
ModProof [][]byte | ||
} | ||
|
||
type keygenRound3msg struct { | ||
PaillierProof [][]byte | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package ecdsatss | ||
|
||
import ( | ||
"context" | ||
"crypto/rand" | ||
"errors" | ||
"io" | ||
"math/big" | ||
"runtime" | ||
"time" | ||
|
||
"github.com/ModChain/tss-lib/v2/common" | ||
"github.com/ModChain/tss-lib/v2/crypto/paillier" | ||
) | ||
|
||
const ( | ||
// Using a modulus length of 2048 is recommended in the GG18 spec | ||
paillierModulusLen = 2048 | ||
// Two 1024-bit safe primes to produce NTilde | ||
safePrimeBitLen = 1024 | ||
// Ticker for printing log statements while generating primes/modulus | ||
logProgressTickInterval = 8 * time.Second | ||
// Safe big len using random for ssid | ||
SafeBitLen = 1024 | ||
) | ||
|
||
type LocalPreGenerator struct { | ||
context.Context // Context used to stop generation if needed | ||
Rand io.Reader // reader used for random, defaults to rand.Reader if nil | ||
Concurrency int // concurrency, defaults to runtime.NumCPU() if nil | ||
} | ||
|
||
// GeneratePreParams finds two safe primes and computes the Paillier secret required for the protocol. | ||
// This can be a time consuming process so it is recommended to do it out-of-band. | ||
// If not specified, a concurrency value equal to the number of available CPU cores will be used. | ||
// If pre-parameters could not be generated before the timeout, an error is returned. | ||
func GeneratePreParams(timeout time.Duration, optionalConcurrency ...int) (*LocalPreParams, error) { | ||
ctx, cancel := context.WithTimeout(context.Background(), timeout) | ||
defer cancel() | ||
opts := &LocalPreGenerator{Context: ctx} | ||
if len(optionalConcurrency) > 0 { | ||
opts.Concurrency = optionalConcurrency[0] | ||
} | ||
return opts.Generate() | ||
} | ||
|
||
func (g *LocalPreGenerator) getConcurrency() int { | ||
if g == nil || g.Concurrency <= 0 { | ||
return runtime.NumCPU() | ||
} | ||
return g.Concurrency | ||
} | ||
|
||
func (g *LocalPreGenerator) getRand() io.Reader { | ||
if g == nil || g.Rand == nil { | ||
return rand.Reader | ||
} | ||
return g.Rand | ||
} | ||
|
||
func (g *LocalPreGenerator) getContext() context.Context { | ||
if g == nil || g.Context == nil { | ||
return context.Background() | ||
} | ||
return g | ||
} | ||
|
||
func (g *LocalPreGenerator) Generate() (*LocalPreParams, error) { | ||
concurrency := g.getConcurrency() | ||
|
||
if concurrency /= 3; concurrency < 1 { | ||
concurrency = 1 | ||
} | ||
|
||
// prepare for concurrent Paillier and safe prime generation | ||
paiCh := make(chan *paillier.PrivateKey, 1) | ||
sgpCh := make(chan []*common.GermainSafePrime, 1) | ||
|
||
// 4. generate Paillier public key E_i, private key and proof | ||
go func(ch chan<- *paillier.PrivateKey) { | ||
// more concurrency weight is assigned here because the paillier primes have a requirement of having "large" P-Q | ||
PiPaillierSk, _, err := paillier.GenerateKeyPair(g.getContext(), g.getRand(), paillierModulusLen, concurrency*2) | ||
if err != nil { | ||
ch <- nil | ||
return | ||
} | ||
ch <- PiPaillierSk | ||
}(paiCh) | ||
|
||
// 5-7. generate safe primes for ZKPs used later on | ||
go func(ch chan<- []*common.GermainSafePrime) { | ||
var err error | ||
sgps, err := common.GetRandomSafePrimesConcurrent(g.getContext(), safePrimeBitLen, 2, concurrency, g.getRand()) | ||
if err != nil { | ||
ch <- nil | ||
return | ||
} | ||
ch <- sgps | ||
}(sgpCh) | ||
|
||
var sgps []*common.GermainSafePrime | ||
var paiSK *paillier.PrivateKey | ||
|
||
consumer: | ||
for { | ||
select { | ||
case sgps = <-sgpCh: | ||
if sgps == nil || | ||
sgps[0] == nil || sgps[1] == nil || | ||
!sgps[0].Prime().ProbablyPrime(30) || !sgps[1].Prime().ProbablyPrime(30) || | ||
!sgps[0].SafePrime().ProbablyPrime(30) || !sgps[1].SafePrime().ProbablyPrime(30) { | ||
return nil, errors.New("timeout or error while generating the safe primes") | ||
} | ||
if paiSK != nil { | ||
break consumer | ||
} | ||
case paiSK = <-paiCh: | ||
if paiSK == nil { | ||
return nil, errors.New("timeout or error while generating the Paillier secret key") | ||
} | ||
if sgps != nil { | ||
break consumer | ||
} | ||
} | ||
} | ||
|
||
P, Q := sgps[0].SafePrime(), sgps[1].SafePrime() | ||
NTildei := new(big.Int).Mul(P, Q) | ||
modNTildeI := common.ModInt(NTildei) | ||
|
||
p, q := sgps[0].Prime(), sgps[1].Prime() | ||
modPQ := common.ModInt(new(big.Int).Mul(p, q)) | ||
f1 := common.GetRandomPositiveRelativelyPrimeInt(g.getRand(), NTildei) | ||
alpha := common.GetRandomPositiveRelativelyPrimeInt(g.getRand(), NTildei) | ||
beta := modPQ.ModInverse(alpha) | ||
h1i := modNTildeI.Mul(f1, f1) | ||
h2i := modNTildeI.Exp(h1i, alpha) | ||
|
||
preParams := &LocalPreParams{ | ||
PaillierSK: paiSK, | ||
NTildei: NTildei, | ||
H1i: h1i, | ||
H2i: h2i, | ||
Alpha: alpha, | ||
Beta: beta, | ||
P: p, | ||
Q: q, | ||
} | ||
return preParams, nil | ||
} |