Skip to content

Commit

Permalink
Updating frost be compliant with draft v15.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Feb 20, 2024
1 parent cd6a307 commit 72293a5
Show file tree
Hide file tree
Showing 9 changed files with 439 additions and 391 deletions.
40 changes: 22 additions & 18 deletions tss/frost/combiner.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
package frost

import (
"errors"
"fmt"
)
import "fmt"

type Combiner struct {
type Coordinator struct {
Suite
threshold uint
maxSigners uint
}

func NewCombiner(s Suite, threshold, maxSigners uint) (*Combiner, error) {
func NewCoordinator(s Suite, threshold, maxSigners uint) (*Coordinator, error) {
if threshold > maxSigners {
return nil, errors.New("frost: invalid parameters")
return nil, fmt.Errorf("frost: invalid parameters")
}

return &Combiner{Suite: s, threshold: threshold, maxSigners: maxSigners}, nil
return &Coordinator{Suite: s, threshold: threshold, maxSigners: maxSigners}, nil
}

func (c Combiner) CheckSignShares(
signShares []*SignShare,
pubKeySigners []*PublicKey,
coms []*Commitment,
pubKeyGroup *PublicKey,
func (c Coordinator) CheckSignShares(
msg []byte,
groupPublicKey PublicKey,
signShares []SignShare,
coms []Commitment,
pubKeySigners []PublicKey,
) bool {
if l := len(signShares); !(int(c.threshold) < l && l <= int(c.maxSigners)) {
return false
Expand All @@ -37,25 +34,32 @@ func (c Combiner) CheckSignShares(
}

for i := range signShares {
if !signShares[i].Verify(c.Suite, pubKeySigners[i], coms[i], coms, pubKeyGroup, msg) {
if !signShares[i].Verify(msg, groupPublicKey, pubKeySigners[i], coms[i], coms) {
return false
}
}

return true
}

func (c Combiner) Sign(msg []byte, coms []*Commitment, signShares []*SignShare) ([]byte, error) {
func (c Coordinator) Aggregate(
msg []byte,
groupPublicKey PublicKey,
signShares []SignShare,
coms []Commitment,
) ([]byte, error) {
if l := len(coms); l <= int(c.threshold) {
return nil, fmt.Errorf("frost: only %v shares of %v required", l, c.threshold)
}

bindingFactors, err := c.Suite.getBindingFactors(coms, msg)
p := c.Suite.getParams()
bindingFactors, err := getBindingFactors(p, msg, groupPublicKey, coms)
if err != nil {
return nil, err
}

groupCom, err := c.Suite.getGroupCommitment(coms, bindingFactors)
g := p.group()
groupCom, err := getGroupCommitment(g, coms, bindingFactors)
if err != nil {
return nil, err
}
Expand All @@ -65,7 +69,7 @@ func (c Combiner) Sign(msg []byte, coms []*Commitment, signShares []*SignShare)
return nil, err
}

z := c.Suite.g.NewScalar()
z := g.NewScalar()
for i := range signShares {
z.Add(z, signShares[i].s.Value)
}
Expand Down
68 changes: 31 additions & 37 deletions tss/frost/commit.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,30 @@
package frost

import (
"errors"
"fmt"
"io"
"sort"

"github.com/cloudflare/circl/group"
)

type Nonce struct {
ID group.Scalar
hiding, binding group.Scalar
id group.Scalar
hiding group.Scalar
binding group.Scalar
}

func (s Suite) nonceGenerate(rnd io.Reader, secret group.Scalar) (group.Scalar, error) {
randomBytes := make([]byte, 32)
_, err := io.ReadFull(rnd, randomBytes)
if err != nil {
return nil, err
}
secretEnc, err := secret.MarshalBinary()
if err != nil {
return nil, err
}

return s.hasher.h3(append(randomBytes, secretEnc...)), nil
func nonceGenerate(p params, randomBytes, secretEnc []byte) group.Scalar {
return p.h3(append(append([]byte{}, randomBytes...), secretEnc...))
}

type Commitment struct {
ID group.Scalar
hiding, binding group.Element
id group.Scalar
hiding group.Element
binding group.Element
}

func (c Commitment) MarshalBinary() ([]byte, error) {
id, err := c.ID.MarshalBinary()
id, err := c.id.MarshalBinary()
if err != nil {
return nil, err
}
Expand All @@ -50,12 +40,11 @@ func (c Commitment) MarshalBinary() ([]byte, error) {
return append(append(id, h...), b...), nil
}

func encodeCommitments(coms []*Commitment) ([]byte, error) {
func encodeCommitments(coms []Commitment) (out []byte, err error) {
sort.SliceStable(coms, func(i, j int) bool {
return coms[i].ID.(fmt.Stringer).String() < coms[j].ID.(fmt.Stringer).String()
return coms[i].id.(fmt.Stringer).String() < coms[j].id.(fmt.Stringer).String()
})

var out []byte
for i := range coms {
cEnc, err := coms[i].MarshalBinary()
if err != nil {
Expand All @@ -71,43 +60,48 @@ type bindingFactor struct {
factor group.Scalar
}

func (s Suite) getBindingFactorFromID(bindingFactors []bindingFactor, id group.Scalar) (group.Scalar, error) {
func getBindingFactorFromID(bindingFactors []bindingFactor, id group.Scalar) (group.Scalar, error) {
for i := range bindingFactors {
if bindingFactors[i].ID.IsEqual(id) {
return bindingFactors[i].factor, nil
}
}
return nil, errors.New("frost: id not found")
return nil, fmt.Errorf("frost: id not found")
}

func (s Suite) getBindingFactors(coms []*Commitment, msg []byte) ([]bindingFactor, error) {
msgHash := s.hasher.h4(msg)
func getBindingFactors(p params, msg []byte, groupPublicKey PublicKey, coms []Commitment) ([]bindingFactor, error) {
groupPublicKeyEnc, err := groupPublicKey.key.MarshalBinaryCompress()
if err != nil {
return nil, err
}

msgHash := p.h4(msg)
encodeComs, err := encodeCommitments(coms)
if err != nil {
return nil, err
}
encodeComsHash := s.hasher.h5(encodeComs)
rhoInputPrefix := append(msgHash, encodeComsHash...)
encodeComsHash := p.h5(encodeComs)
rhoInputPrefix := append(append(groupPublicKeyEnc, msgHash...), encodeComsHash...)

bindingFactors := make([]bindingFactor, len(coms))
for i := range coms {
id, err := coms[i].ID.MarshalBinary()
id, err := coms[i].id.MarshalBinary()
if err != nil {
return nil, err
}
rhoInput := append(append([]byte{}, rhoInputPrefix...), id...)
bf := s.hasher.h1(rhoInput)
bindingFactors[i] = bindingFactor{ID: coms[i].ID, factor: bf}
bf := p.h1(rhoInput)
bindingFactors[i] = bindingFactor{ID: coms[i].id, factor: bf}
}

return bindingFactors, nil
}

func (s Suite) getGroupCommitment(coms []*Commitment, bindingFactors []bindingFactor) (group.Element, error) {
gc := s.g.NewElement()
tmp := s.g.NewElement()
func getGroupCommitment(g group.Group, coms []Commitment, bindingFactors []bindingFactor) (group.Element, error) {
gc := g.NewElement()
tmp := g.NewElement()
for i := range coms {
bf, err := s.getBindingFactorFromID(bindingFactors, coms[i].ID)
bf, err := getBindingFactorFromID(bindingFactors, coms[i].id)
if err != nil {
return nil, err
}
Expand All @@ -119,7 +113,7 @@ func (s Suite) getGroupCommitment(coms []*Commitment, bindingFactors []bindingFa
return gc, nil
}

func (s Suite) getChallenge(groupCom group.Element, pubKey *PublicKey, msg []byte) (group.Scalar, error) {
func getChallenge(p params, groupCom group.Element, msg []byte, pubKey PublicKey) (group.Scalar, error) {
gcEnc, err := groupCom.MarshalBinaryCompress()
if err != nil {
return nil, err
Expand All @@ -130,5 +124,5 @@ func (s Suite) getChallenge(groupCom group.Element, pubKey *PublicKey, msg []byt
}
chInput := append(append(append([]byte{}, gcEnc...), pkEnc...), msg...)

return s.hasher.h2(chInput), nil
return p.h2(chInput), nil
}
63 changes: 35 additions & 28 deletions tss/frost/frost.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// Package frost provides the FROST threshold signature scheme for Schnorr signatures.
//
// References
// FROST paper: https://eprint.iacr.org/2020/852
//
// FROST paper: https://eprint.iacr.org/2020/852
// draft-irtf-cfrg-frost: https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost
// draft-irtf-cfrg-frost: https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost
//
// Version supported: v11
// Version supported: v15
package frost

import (
Expand All @@ -17,62 +16,70 @@ import (

type PrivateKey struct {
Suite
key group.Scalar
pubKey *PublicKey
key group.Scalar
publicKey *PublicKey
}

type PublicKey struct {
Suite
key group.Element
}

func GenerateKey(s Suite, rnd io.Reader) *PrivateKey {
return &PrivateKey{s, s.g.RandomNonZeroScalar(rnd), nil}
func GenerateKey(s Suite, rnd io.Reader) PrivateKey {
g := s.getParams().group()
return PrivateKey{s, g.RandomNonZeroScalar(rnd), nil}
}

func (k *PrivateKey) Public() *PublicKey {
return &PublicKey{k.Suite, k.Suite.g.NewElement().MulGen(k.key)}
func (k *PrivateKey) PublicKey() PublicKey {
if k.publicKey == nil {
g := k.Suite.getParams().group()
k.publicKey = &PublicKey{k.Suite, g.NewElement().MulGen(k.key)}
}

return *k.publicKey
}

func (k *PrivateKey) Split(rnd io.Reader, threshold, maxSigners uint) (
[]PeerSigner, secretsharing.SecretCommitment, error,
peers []PeerSigner, groupPublicKey PublicKey, comm secretsharing.SecretCommitment,
) {
ss := secretsharing.New(rnd, threshold, k.key)
shares := ss.Share(maxSigners)
comm = ss.CommitSecret()
groupPublicKey = PublicKey{k.Suite, comm[0]}

peers := make([]PeerSigner, len(shares))
peers = make([]PeerSigner, len(shares))
for i := range shares {
peers[i] = PeerSigner{
Suite: k.Suite,
threshold: uint16(threshold),
maxSigners: uint16(maxSigners),
keyShare: secretsharing.Share{
ID: shares[i].ID,
Value: shares[i].Value,
},
myPubKey: nil,
Suite: k.Suite,
threshold: uint16(threshold),
maxSigners: uint16(maxSigners),
keyShare: shares[i],
groupPublicKey: groupPublicKey,
myPublicKey: nil,
}
}

return peers, ss.CommitSecret(), nil
return peers, groupPublicKey, comm
}

func Verify(s Suite, pubKey *PublicKey, msg, signature []byte) bool {
params := s.g.Params()
func Verify(msg []byte, pubKey PublicKey, signature []byte) bool {
p := pubKey.Suite.getParams()
g := p.group()
params := g.Params()
Ne, Ns := params.CompressedElementLength, params.ScalarLength
if len(signature) < int(Ne+Ns) {
return false
}

REnc := signature[:Ne]
R := s.g.NewElement()
R := g.NewElement()
err := R.UnmarshalBinary(REnc)
if err != nil {
return false
}

zEnc := signature[Ne : Ne+Ns]
z := s.g.NewScalar()
z := g.NewScalar()
err = z.UnmarshalBinary(zEnc)
if err != nil {
return false
Expand All @@ -84,10 +91,10 @@ func Verify(s Suite, pubKey *PublicKey, msg, signature []byte) bool {
}

chInput := append(append(append([]byte{}, REnc...), pubKeyEnc...), msg...)
c := s.hasher.h2(chInput)
c := p.h2(chInput)

l := s.g.NewElement().MulGen(z)
r := s.g.NewElement().Mul(pubKey.key, c)
l := g.NewElement().MulGen(z)
r := g.NewElement().Mul(pubKey.key, c)
r.Add(r, R)

return l.IsEqual(r)
Expand Down
Loading

0 comments on commit 72293a5

Please sign in to comment.