mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 01:41:44 +00:00
(no commit message provided)
This commit is contained in:
committed by
Prad Nukala (aider)
parent
5fd43dfd6b
commit
2f976209db
@@ -1,13 +0,0 @@
|
||||
---
|
||||
aliases: [README]
|
||||
tags: []
|
||||
title: README
|
||||
linter-yaml-title-alias: README
|
||||
date created: Wednesday, April 17th 2024, 4:11:40 pm
|
||||
date modified: Thursday, April 18th 2024, 8:19:25 am
|
||||
---
|
||||
|
||||
## FROST: Flexible Round-Optimized Schnorr Threshold Signatures
|
||||
|
||||
This package is an implementation of t-of-n threshold signature of
|
||||
[FROST: Flexible Round-Optimized Schnorr Threshold Signatures](https://eprint.iacr.org/2020/852.pdf)
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package frost
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
|
||||
"github.com/onsonr/hway/crypto/core/curves"
|
||||
)
|
||||
|
||||
type ChallengeDerive interface {
|
||||
DeriveChallenge(msg []byte, pubKey curves.Point, r curves.Point) (curves.Scalar, error)
|
||||
}
|
||||
|
||||
type Ed25519ChallengeDeriver struct{}
|
||||
|
||||
func (ed Ed25519ChallengeDeriver) DeriveChallenge(msg []byte, pubKey curves.Point, r curves.Point) (curves.Scalar, error) {
|
||||
h := sha512.New()
|
||||
_, _ = h.Write(r.ToAffineCompressed())
|
||||
_, _ = h.Write(pubKey.ToAffineCompressed())
|
||||
_, _ = h.Write(msg)
|
||||
return new(curves.ScalarEd25519).SetBytesWide(h.Sum(nil))
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Package frost is an implementation of t-of-n threshold signature of https://eprint.iacr.org/2020/852.pdf
|
||||
package frost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/hway/crypto/core/curves"
|
||||
"github.com/onsonr/hway/crypto/dkg/frost"
|
||||
"github.com/onsonr/hway/crypto/internal"
|
||||
)
|
||||
|
||||
// Signer is a tSchnorr player performing the signing operation.
|
||||
type Signer struct {
|
||||
skShare curves.Scalar // secret signing share for this signer
|
||||
vkShare curves.Point // store verification key share
|
||||
verificationKey curves.Point // verification key
|
||||
id uint32 // The ID assigned to this signer's shamir share
|
||||
threshold uint32
|
||||
curve *curves.Curve
|
||||
round uint
|
||||
lCoeffs map[uint32]curves.Scalar // lCoeffs are Lagrange coefficients of each cosigner.
|
||||
cosigners []uint32
|
||||
state *state // Accumulated intermediate values associated with signing
|
||||
challengeDeriver ChallengeDerive
|
||||
}
|
||||
|
||||
type state struct {
|
||||
// Round 1
|
||||
capD, capE curves.Point // capD, capE are commitments this signer generates in signing round 1
|
||||
smallD, smallE curves.Scalar // smallD, smallE are scalars this signer generates in signing round 1
|
||||
|
||||
// Round 2
|
||||
commitments map[uint32]*Round1Bcast // Store commitments broadcast after signing round 1
|
||||
msg []byte
|
||||
c curves.Scalar
|
||||
capRs map[uint32]curves.Point
|
||||
sumR curves.Point
|
||||
}
|
||||
|
||||
// NewSigner create a signer from a dkg participant
|
||||
// Note that we can pre-assign Lagrange coefficients lcoeffs of each cosigner. This optimizes performance.
|
||||
// See paragraph 3 of section 3 in the draft - https://tools.ietf.org/pdf/draft-komlo-frost-00.pdf
|
||||
func NewSigner(info *frost.DkgParticipant, id, thresh uint32, lcoeffs map[uint32]curves.Scalar, cosigners []uint32, challengeDeriver ChallengeDerive) (*Signer, error) {
|
||||
if info == nil || len(cosigners) == 0 || len(lcoeffs) == 0 {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
|
||||
if thresh > uint32(len(cosigners)) {
|
||||
return nil, fmt.Errorf("threshold is higher than number of signers")
|
||||
}
|
||||
|
||||
if len(lcoeffs) != len(cosigners) {
|
||||
return nil, fmt.Errorf("expected coefficients to be equal to number of cosigners")
|
||||
}
|
||||
|
||||
// Check if cosigners and lcoeffs contain the same IDs
|
||||
for i := 0; i < len(cosigners); i++ {
|
||||
id := cosigners[i]
|
||||
if _, ok := lcoeffs[id]; !ok {
|
||||
return nil, fmt.Errorf("lcoeffs and cosigners have inconsistent ID")
|
||||
}
|
||||
}
|
||||
|
||||
return &Signer{
|
||||
skShare: info.SkShare,
|
||||
vkShare: info.VkShare,
|
||||
verificationKey: info.VerificationKey,
|
||||
id: id,
|
||||
threshold: thresh,
|
||||
curve: info.Curve,
|
||||
round: 1,
|
||||
lCoeffs: lcoeffs,
|
||||
cosigners: cosigners,
|
||||
state: &state{},
|
||||
challengeDeriver: challengeDeriver,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package frost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
crand "crypto/rand"
|
||||
"encoding/gob"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/onsonr/hway/crypto/core/curves"
|
||||
"github.com/onsonr/hway/crypto/internal"
|
||||
)
|
||||
|
||||
// Round1Bcast contains values to be broadcast to all players after the completion of signing round 1.
|
||||
type Round1Bcast struct {
|
||||
Di, Ei curves.Point
|
||||
}
|
||||
|
||||
func (result *Round1Bcast) Encode() ([]byte, error) {
|
||||
gob.Register(result.Di) // just the point for now
|
||||
gob.Register(result.Ei)
|
||||
buf := &bytes.Buffer{}
|
||||
enc := gob.NewEncoder(buf)
|
||||
if err := enc.Encode(result); err != nil {
|
||||
return nil, errors.Wrap(err, "couldn't encode round 1 broadcast")
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (result *Round1Bcast) Decode(input []byte) error {
|
||||
buf := bytes.NewBuffer(input)
|
||||
dec := gob.NewDecoder(buf)
|
||||
if err := dec.Decode(result); err != nil {
|
||||
return errors.Wrap(err, "couldn't encode round 1 broadcast")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (signer *Signer) SignRound1() (*Round1Bcast, error) {
|
||||
// Make sure signer is not empty
|
||||
if signer == nil || signer.curve == nil {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
|
||||
// Make sure round number is correct
|
||||
if signer.round != 1 {
|
||||
return nil, internal.ErrInvalidRound
|
||||
}
|
||||
|
||||
// Step 1 - Sample di, ei
|
||||
di := signer.curve.Scalar.Random(crand.Reader)
|
||||
|
||||
ei := signer.curve.Scalar.Random(crand.Reader)
|
||||
|
||||
// Step 2 - Compute Di, Ei
|
||||
Di := signer.curve.ScalarBaseMult(di)
|
||||
|
||||
Ei := signer.curve.ScalarBaseMult(ei)
|
||||
|
||||
// Update round number
|
||||
signer.round = 2
|
||||
|
||||
// Store di, ei, Di, Ei locally and broadcast Di, Ei
|
||||
signer.state.capD = Di
|
||||
signer.state.capE = Ei
|
||||
signer.state.smallD = di
|
||||
signer.state.smallE = ei
|
||||
return &Round1Bcast{
|
||||
Di,
|
||||
Ei,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package frost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/onsonr/hway/crypto/core/curves"
|
||||
"github.com/onsonr/hway/crypto/internal"
|
||||
)
|
||||
|
||||
// Round2Bcast contains values that will be broadcast to other signers after completion of round 2.
|
||||
type Round2Bcast struct {
|
||||
Zi curves.Scalar
|
||||
Vki curves.Point
|
||||
}
|
||||
|
||||
func (result *Round2Bcast) Encode() ([]byte, error) {
|
||||
gob.Register(result.Zi)
|
||||
gob.Register(result.Vki) // just the point for now
|
||||
buf := &bytes.Buffer{}
|
||||
enc := gob.NewEncoder(buf)
|
||||
if err := enc.Encode(result); err != nil {
|
||||
return nil, errors.Wrap(err, "couldn't encode round 1 broadcast")
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (result *Round2Bcast) Decode(input []byte) error {
|
||||
buf := bytes.NewBuffer(input)
|
||||
dec := gob.NewDecoder(buf)
|
||||
if err := dec.Decode(result); err != nil {
|
||||
return errors.Wrap(err, "couldn't encode round 1 broadcast")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignRound2 implements FROST signing round 2.
|
||||
func (signer *Signer) SignRound2(msg []byte, round2Input map[uint32]*Round1Bcast) (*Round2Bcast, error) {
|
||||
// Make sure necessary items of signer are not empty
|
||||
if signer == nil || signer.curve == nil || signer.state == nil {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
|
||||
// Make sure those private d is not empty and not zero
|
||||
if signer.state.smallD == nil || signer.state.smallD.IsZero() {
|
||||
return nil, fmt.Errorf("empty d or d is zero")
|
||||
}
|
||||
|
||||
// Make sure those private e is not empty and not zero
|
||||
if signer.state.smallE == nil || signer.state.smallE.IsZero() {
|
||||
return nil, fmt.Errorf("empty e or e is zero")
|
||||
}
|
||||
|
||||
// Make sure msg is not empty
|
||||
if len(msg) == 0 {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
|
||||
// Make sure the round number is correct
|
||||
if signer.round != 2 {
|
||||
return nil, internal.ErrInvalidRound
|
||||
}
|
||||
|
||||
// Check length of round2Input
|
||||
if uint32(len(round2Input)) != signer.threshold {
|
||||
return nil, fmt.Errorf("Invalid length of round2Input")
|
||||
}
|
||||
|
||||
// Step 2 - Check Dj, Ej on the curve and Store round2Input
|
||||
for id, input := range round2Input {
|
||||
if input == nil || input.Di == nil || input.Ei == nil {
|
||||
return nil, fmt.Errorf("round2Input is nil from participant with id %d\n", id)
|
||||
}
|
||||
if !input.Di.IsOnCurve() || input.Di.IsIdentity() {
|
||||
return nil, fmt.Errorf("commitment Di is not on the curve with id %d\n", id)
|
||||
}
|
||||
if !input.Ei.IsOnCurve() || input.Ei.IsIdentity() {
|
||||
return nil, fmt.Errorf("commitment Ei is not on the curve with id %d\n", id)
|
||||
}
|
||||
}
|
||||
// Store Dj, Ej for further usage.
|
||||
signer.state.commitments = round2Input
|
||||
|
||||
// Step 3-6
|
||||
R := signer.curve.NewIdentityPoint()
|
||||
var ri curves.Scalar
|
||||
Rs := make(map[uint32]curves.Point, signer.threshold)
|
||||
for id, data := range round2Input {
|
||||
// Construct the blob (j, m, {Dj, Ej})
|
||||
blob := concatHashArray(id, msg, round2Input, signer.cosigners)
|
||||
|
||||
// Step 4 - rj = H(j,m,{Dj,Ej}_{j in [1...t]})
|
||||
rj := signer.curve.Scalar.Hash(blob)
|
||||
if signer.id == id {
|
||||
ri = rj
|
||||
}
|
||||
|
||||
// Step 5 - R_j = D_j + r_j*E_j
|
||||
rjEj := data.Ei.Mul(rj)
|
||||
Rj := rjEj.Add(data.Di)
|
||||
// assign Rj
|
||||
Rs[id] = Rj
|
||||
|
||||
// Step 6 - R = R+Rj
|
||||
R = R.Add(Rj)
|
||||
}
|
||||
|
||||
// Step 7 - c = H(m, R)
|
||||
c, err := signer.challengeDeriver.DeriveChallenge(msg, signer.verificationKey, R)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Step 8 - Record c, R, Rjs
|
||||
signer.state.c = c
|
||||
signer.state.capRs = Rs
|
||||
signer.state.sumR = R
|
||||
|
||||
// Step 9 - zi = di + ei*ri + Li*ski*c
|
||||
Li := signer.lCoeffs[signer.id]
|
||||
Liski := Li.Mul(signer.skShare)
|
||||
|
||||
Liskic := Liski.Mul(c)
|
||||
|
||||
if R.IsNegative() {
|
||||
signer.state.smallE = signer.state.smallE.Neg()
|
||||
signer.state.smallD = signer.state.smallD.Neg()
|
||||
}
|
||||
|
||||
eiri := signer.state.smallE.Mul(ri)
|
||||
|
||||
// Compute zi = di+ei*ri+Li*ski*c
|
||||
zi := Liskic.Add(eiri)
|
||||
zi = zi.Add(signer.state.smallD)
|
||||
|
||||
// Update round number and store message
|
||||
signer.round = 3
|
||||
signer.state.msg = msg
|
||||
|
||||
// set smallD and smallE to zero since they are one-time use
|
||||
signer.state.smallD = signer.curve.NewScalar()
|
||||
signer.state.smallE = signer.curve.NewScalar()
|
||||
|
||||
// Step 10 - Broadcast zi, vki to other participants
|
||||
return &Round2Bcast{
|
||||
zi,
|
||||
signer.vkShare,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// concatHashArray puts id, msg and (Dj,Ej), j=1...t into a byte array
|
||||
func concatHashArray(id uint32, msg []byte, round2Input map[uint32]*Round1Bcast, cosigners []uint32) []byte {
|
||||
var blob []byte
|
||||
// Append identity id
|
||||
blob = append(blob, byte(id))
|
||||
|
||||
// Append message msg
|
||||
blob = append(blob, msg...)
|
||||
|
||||
// Append (Dj, Ej) for all j in [1...t]
|
||||
for i := 0; i < len(cosigners); i++ {
|
||||
id := cosigners[i]
|
||||
bytesDi := round2Input[id].Di.ToAffineCompressed()
|
||||
bytesEi := round2Input[id].Ei.ToAffineCompressed()
|
||||
|
||||
// Following the spec, we should add each party's identity.
|
||||
blob = append(blob, byte(id))
|
||||
blob = append(blob, bytesDi...)
|
||||
blob = append(blob, bytesEi...)
|
||||
}
|
||||
return blob
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package frost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/hway/crypto/core/curves"
|
||||
"github.com/onsonr/hway/crypto/internal"
|
||||
)
|
||||
|
||||
// Round3Bcast contains the output of FROST signature, i.e., it contains FROST signature (z,c) and the
|
||||
// corresponding message msg.
|
||||
type Round3Bcast struct {
|
||||
R curves.Point
|
||||
Z, C curves.Scalar
|
||||
msg []byte
|
||||
}
|
||||
|
||||
// Define frost signature type
|
||||
type Signature struct {
|
||||
Z curves.Scalar
|
||||
C curves.Scalar
|
||||
}
|
||||
|
||||
func (signer *Signer) SignRound3(round3Input map[uint32]*Round2Bcast) (*Round3Bcast, error) {
|
||||
// Make sure signer is not empty
|
||||
if signer == nil || signer.curve == nil {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
|
||||
// Make sure signer's smallD and smallE are zero
|
||||
if !signer.state.smallD.IsZero() || !signer.state.smallE.IsZero() {
|
||||
return nil, fmt.Errorf("signer's private smallD and smallE should be zero since one-time use")
|
||||
}
|
||||
|
||||
// Make sure the signer has had the msg
|
||||
if len(signer.state.msg) == 0 {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
|
||||
// Validate Round3Input
|
||||
if round3Input == nil {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
for _, data := range round3Input {
|
||||
if data == nil {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the signer has commitments stored at the end of round 1.
|
||||
if signer.state.commitments == nil || len(signer.state.commitments) != len(round3Input) {
|
||||
return nil, internal.ErrNilArguments
|
||||
}
|
||||
|
||||
// Make sure the round number is correct
|
||||
if signer.round != 3 {
|
||||
return nil, internal.ErrInvalidRound
|
||||
}
|
||||
|
||||
// Round2 Input has different length of threshold
|
||||
if uint32(len(round3Input)) != signer.threshold {
|
||||
return nil, fmt.Errorf("invalid length of round3Input")
|
||||
}
|
||||
|
||||
// Step 1-3
|
||||
// Step 1: For j in [1...t]
|
||||
z := signer.curve.NewScalar()
|
||||
negate := signer.state.sumR.IsNegative()
|
||||
for id, data := range round3Input {
|
||||
zj := data.Zi
|
||||
vkj := data.Vki
|
||||
|
||||
// Step 2: Verify zj*G = Rj + c*Lj*vkj
|
||||
// zj*G
|
||||
zjG := signer.curve.ScalarBaseMult(zj)
|
||||
|
||||
// c*Lj
|
||||
cLj := signer.state.c.Mul(signer.lCoeffs[id])
|
||||
|
||||
// cLjvkj
|
||||
cLjvkj := vkj.Mul(cLj)
|
||||
|
||||
// Rj + c*Lj*vkj
|
||||
Rj := signer.state.capRs[id]
|
||||
if negate {
|
||||
Rj = Rj.Neg()
|
||||
}
|
||||
right := cLjvkj.Add(Rj)
|
||||
|
||||
// Check equation
|
||||
if !zjG.Equal(right) {
|
||||
return nil, fmt.Errorf("zjG != right with participant id %d\n", id)
|
||||
}
|
||||
|
||||
// Step 3 - z = z+zj
|
||||
z = z.Add(zj)
|
||||
}
|
||||
|
||||
// Step 4 - 7: Self verify the signature (z, c)
|
||||
// Step 5 - R' = z*G + (-c)*vk
|
||||
zG := signer.curve.ScalarBaseMult(z)
|
||||
cvk := signer.verificationKey.Mul(signer.state.c.Neg())
|
||||
tempR := zG.Add(cvk)
|
||||
// Step 6 - c' = H(m, R')
|
||||
tempC, err := signer.challengeDeriver.DeriveChallenge(signer.state.msg, signer.verificationKey, tempR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Step 7 - Check c = c'
|
||||
if tempC.Cmp(signer.state.c) != 0 {
|
||||
return nil, fmt.Errorf("invalid signature: c != c'")
|
||||
}
|
||||
|
||||
// Updating round number
|
||||
signer.round = 4
|
||||
|
||||
// Step 8 - Broadcast signature and message
|
||||
return &Round3Bcast{
|
||||
signer.state.sumR,
|
||||
z,
|
||||
signer.state.c,
|
||||
signer.state.msg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Method to verify a frost signature.
|
||||
func Verify(curve *curves.Curve, challengeDeriver ChallengeDerive, vk curves.Point, msg []byte, signature *Signature) (bool, error) {
|
||||
if vk == nil || msg == nil || len(msg) == 0 || signature.C == nil || signature.Z == nil {
|
||||
return false, fmt.Errorf("invalid input")
|
||||
}
|
||||
z := signature.Z
|
||||
c := signature.C
|
||||
|
||||
// R' = z*G - c*vk
|
||||
zG := curve.ScalarBaseMult(z)
|
||||
cvk := vk.Mul(c.Neg())
|
||||
tempR := zG.Add(cvk)
|
||||
|
||||
// c' = H(m, R')
|
||||
tempC, err := challengeDeriver.DeriveChallenge(msg, vk, tempR)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check c == c'
|
||||
if tempC.Cmp(c) != 0 {
|
||||
return false, fmt.Errorf("invalid signature: c != c'")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package frost
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/onsonr/hway/crypto/core/curves"
|
||||
dkg "github.com/onsonr/hway/crypto/dkg/frost"
|
||||
"github.com/onsonr/hway/crypto/sharing"
|
||||
)
|
||||
|
||||
var (
|
||||
testCurve = curves.ED25519()
|
||||
ctx = "string to prevent replay attack"
|
||||
)
|
||||
|
||||
// Create two DKG participants.
|
||||
func PrepareDkgOutput(t *testing.T) (*dkg.DkgParticipant, *dkg.DkgParticipant) {
|
||||
// Initiate two participants and running DKG round 1
|
||||
p1, err := dkg.NewDkgParticipant(1, 2, ctx, testCurve, 2)
|
||||
require.NoError(t, err)
|
||||
p2, err := dkg.NewDkgParticipant(2, 2, ctx, testCurve, 1)
|
||||
require.NoError(t, err)
|
||||
bcast1, p2psend1, _ := p1.Round1(nil)
|
||||
bcast2, p2psend2, _ := p2.Round1(nil)
|
||||
bcast := make(map[uint32]*dkg.Round1Bcast)
|
||||
p2p1 := make(map[uint32]*sharing.ShamirShare)
|
||||
p2p2 := make(map[uint32]*sharing.ShamirShare)
|
||||
bcast[1] = bcast1
|
||||
bcast[2] = bcast2
|
||||
p2p1[2] = p2psend2[1]
|
||||
p2p2[1] = p2psend1[2]
|
||||
|
||||
// Running DKG round 2
|
||||
_, _ = p1.Round2(bcast, p2p1)
|
||||
_, _ = p2.Round2(bcast, p2p2)
|
||||
return p1, p2
|
||||
}
|
||||
|
||||
// Test FROST signing round 1
|
||||
func TestSignRound1Works(t *testing.T) {
|
||||
p1, p2 := PrepareDkgOutput(t)
|
||||
require.NotNil(t, p1)
|
||||
require.NotNil(t, p2)
|
||||
|
||||
scheme, _ := sharing.NewShamir(2, 2, testCurve)
|
||||
lCoeffs, err := scheme.LagrangeCoeffs([]uint32{p1.Id, p2.Id})
|
||||
require.NoError(t, err)
|
||||
signer1, err := NewSigner(p1, 1, 2, lCoeffs, []uint32{1, 2}, &Ed25519ChallengeDeriver{})
|
||||
require.NoError(t, err)
|
||||
round1Out, _ := signer1.SignRound1()
|
||||
require.NotNil(t, round1Out.Ei)
|
||||
require.NotNil(t, round1Out.Di)
|
||||
require.NotNil(t, signer1.state.smallE)
|
||||
require.NotNil(t, signer1.state.smallD)
|
||||
require.NotNil(t, signer1.state.capD)
|
||||
require.NotNil(t, signer1.state.capE)
|
||||
require.Equal(t, signer1.round, uint(2))
|
||||
require.Equal(t, signer1.cosigners, []uint32{1, 2})
|
||||
}
|
||||
|
||||
func TestSignRound1RepeatCall(t *testing.T) {
|
||||
p1, p2 := PrepareDkgOutput(t)
|
||||
scheme, _ := sharing.NewShamir(2, 2, testCurve)
|
||||
lCoeffs, err := scheme.LagrangeCoeffs([]uint32{p1.Id, p2.Id})
|
||||
require.NoError(t, err)
|
||||
signer1, _ := NewSigner(p1, 1, 2, lCoeffs, []uint32{1, 2}, &Ed25519ChallengeDeriver{})
|
||||
_, err = signer1.SignRound1()
|
||||
require.NoError(t, err)
|
||||
_, err = signer1.SignRound1()
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func PrepareNewSigners(t *testing.T) (*Signer, *Signer) {
|
||||
threshold := uint32(2)
|
||||
limit := uint32(2)
|
||||
p1, p2 := PrepareDkgOutput(t)
|
||||
require.Equal(t, p1.VerificationKey, p2.VerificationKey)
|
||||
scheme, err := sharing.NewShamir(threshold, limit, testCurve)
|
||||
// field = sharing.NewField(p1.curve.Params().N)
|
||||
require.NotNil(t, scheme)
|
||||
require.NoError(t, err)
|
||||
lCoeffs, err := scheme.LagrangeCoeffs([]uint32{p1.Id, p2.Id})
|
||||
require.NotNil(t, lCoeffs[1])
|
||||
require.NotNil(t, lCoeffs[2])
|
||||
require.NoError(t, err)
|
||||
signer1, err := NewSigner(p1, p1.Id, threshold, lCoeffs, []uint32{p1.Id, p2.Id}, &Ed25519ChallengeDeriver{})
|
||||
require.NotNil(t, signer1)
|
||||
require.NoError(t, err)
|
||||
signer2, err := NewSigner(p2, p2.Id, threshold, lCoeffs, []uint32{p1.Id, p2.Id}, &Ed25519ChallengeDeriver{})
|
||||
require.NotNil(t, signer2)
|
||||
require.NoError(t, err)
|
||||
return signer1, signer2
|
||||
}
|
||||
|
||||
func TestSignRound2Works(t *testing.T) {
|
||||
// Preparing round 2 inputs
|
||||
signer1, signer2 := PrepareNewSigners(t)
|
||||
require.Equal(t, signer1.verificationKey, signer2.verificationKey)
|
||||
require.NotNil(t, signer1)
|
||||
require.NotNil(t, signer2)
|
||||
round1Out1, _ := signer1.SignRound1()
|
||||
round1Out2, _ := signer2.SignRound1()
|
||||
round2Input := make(map[uint32]*Round1Bcast)
|
||||
round2Input[signer1.id] = round1Out1
|
||||
round2Input[signer2.id] = round1Out2
|
||||
|
||||
// Actual Test
|
||||
msg := []byte("message")
|
||||
round2Out, err := signer1.SignRound2(msg, round2Input)
|
||||
require.NotNil(t, round2Out)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, signer1.round, uint(3))
|
||||
require.NotNil(t, signer1.state.commitments)
|
||||
require.Equal(t, signer1.state.msg, msg)
|
||||
require.True(t, signer1.state.smallD.IsZero())
|
||||
require.True(t, signer1.state.smallE.IsZero())
|
||||
}
|
||||
|
||||
func TestSignRound2RepeatCall(t *testing.T) {
|
||||
// Preparing round 2 inputs
|
||||
signer1, signer2 := PrepareNewSigners(t)
|
||||
require.NotNil(t, signer1)
|
||||
require.NotNil(t, signer2)
|
||||
round1Out1, _ := signer1.SignRound1()
|
||||
round1Out2, _ := signer2.SignRound1()
|
||||
round2Input := make(map[uint32]*Round1Bcast)
|
||||
round2Input[signer1.id] = round1Out1
|
||||
round2Input[signer2.id] = round1Out2
|
||||
|
||||
// Actual Test
|
||||
msg := []byte("message")
|
||||
_, err := signer1.SignRound2(msg, round2Input)
|
||||
require.NoError(t, err)
|
||||
_, err = signer1.SignRound2(msg, round2Input)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestSignRound2BadInput(t *testing.T) {
|
||||
// Preparing round 2 inputs
|
||||
signer1, signer2 := PrepareNewSigners(t)
|
||||
_, _ = signer1.SignRound1()
|
||||
round1Out2, _ := signer2.SignRound1()
|
||||
round2Input := make(map[uint32]*Round1Bcast)
|
||||
|
||||
// Actual Test: Set an input to nil
|
||||
round2Input[signer1.id] = nil
|
||||
round2Input[signer2.id] = round1Out2
|
||||
msg := []byte("message")
|
||||
_, err := signer1.SignRound2(msg, round2Input)
|
||||
require.Error(t, err)
|
||||
|
||||
// Preparing round 2 inputs
|
||||
signer1, signer2 = PrepareNewSigners(t)
|
||||
round1Out1, _ := signer1.SignRound1()
|
||||
round1Out2, _ = signer2.SignRound1()
|
||||
round2Input = make(map[uint32]*Round1Bcast)
|
||||
round2Input[signer1.id] = round1Out1
|
||||
round2Input[signer2.id] = round1Out2
|
||||
// Actual Test: Nil message
|
||||
_, err = signer1.SignRound2(nil, round2Input)
|
||||
require.Error(t, err)
|
||||
|
||||
// Preparing round 2 inputs
|
||||
signer1, signer2 = PrepareNewSigners(t)
|
||||
round1Out1, _ = signer1.SignRound1()
|
||||
round1Out2, _ = signer2.SignRound1()
|
||||
round2Input = make(map[uint32]*Round1Bcast)
|
||||
|
||||
// Actual Test: Set invalid round2Input length
|
||||
round2Input[signer1.id] = round1Out1
|
||||
round2Input[signer2.id] = round1Out2
|
||||
round2Input[3] = round1Out2
|
||||
_, err = signer1.SignRound2(msg, round2Input)
|
||||
require.Error(t, err)
|
||||
|
||||
// Preparing round 2 inputs
|
||||
signer1, signer2 = PrepareNewSigners(t)
|
||||
round1Out1, _ = signer1.SignRound1()
|
||||
round1Out2, _ = signer2.SignRound1()
|
||||
round2Input = make(map[uint32]*Round1Bcast)
|
||||
|
||||
// Actual Test: Set invalid round2Input length
|
||||
round1Out2.Ei = nil
|
||||
round2Input[signer1.id] = round1Out1
|
||||
round2Input[signer2.id] = round1Out2
|
||||
_, err = signer1.SignRound2(msg, round2Input)
|
||||
require.Error(t, err)
|
||||
|
||||
// Preparing round 2 inputs
|
||||
signer1, signer2 = PrepareNewSigners(t)
|
||||
round1Out1, _ = signer1.SignRound1()
|
||||
round1Out2, _ = signer2.SignRound1()
|
||||
round2Input = make(map[uint32]*Round1Bcast)
|
||||
round2Input[signer1.id] = round1Out1
|
||||
round2Input[signer2.id] = round1Out2
|
||||
|
||||
// Actual Test: Set nil smallD and smallE
|
||||
signer1.state.smallD = testCurve.NewScalar()
|
||||
signer1.state.smallE = nil
|
||||
_, err = signer1.SignRound2(msg, round2Input)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func PrepareRound3Input(t *testing.T) (*Signer, *Signer, map[uint32]*Round2Bcast) {
|
||||
// Running sign round 1
|
||||
threshold := uint32(2)
|
||||
limit := uint32(2)
|
||||
p1, p2 := PrepareDkgOutput(t)
|
||||
require.Equal(t, p1.VerificationKey, p2.VerificationKey)
|
||||
scheme, err := sharing.NewShamir(threshold, limit, testCurve)
|
||||
require.NotNil(t, scheme)
|
||||
require.NoError(t, err)
|
||||
lCoeffs, err := scheme.LagrangeCoeffs([]uint32{p1.Id, p2.Id})
|
||||
require.NotNil(t, lCoeffs[1])
|
||||
require.NotNil(t, lCoeffs[2])
|
||||
require.NoError(t, err)
|
||||
|
||||
signer1, err := NewSigner(p1, p1.Id, threshold, lCoeffs, []uint32{p1.Id, p2.Id}, &Ed25519ChallengeDeriver{})
|
||||
require.NotNil(t, signer1)
|
||||
require.NoError(t, err)
|
||||
signer2, err := NewSigner(p2, p2.Id, threshold, lCoeffs, []uint32{p1.Id, p2.Id}, &Ed25519ChallengeDeriver{})
|
||||
require.NotNil(t, signer2)
|
||||
require.NoError(t, err)
|
||||
|
||||
round1Out1, _ := signer1.SignRound1()
|
||||
round1Out2, _ := signer2.SignRound1()
|
||||
round2Input := make(map[uint32]*Round1Bcast, threshold)
|
||||
round2Input[signer1.id] = round1Out1
|
||||
round2Input[signer2.id] = round1Out2
|
||||
|
||||
// Running sign round 2
|
||||
msg := []byte("message")
|
||||
round2Out1, _ := signer1.SignRound2(msg, round2Input)
|
||||
round2Out2, _ := signer2.SignRound2(msg, round2Input)
|
||||
round3Input := make(map[uint32]*Round2Bcast, threshold)
|
||||
round3Input[signer1.id] = round2Out1
|
||||
round3Input[signer2.id] = round2Out2
|
||||
return signer1, signer2, round3Input
|
||||
}
|
||||
|
||||
func TestSignRound3Works(t *testing.T) {
|
||||
signer1, signer2, round3Input := PrepareRound3Input(t)
|
||||
round3Out1, err := signer1.SignRound3(round3Input)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, round3Out1)
|
||||
round3Out2, err := signer2.SignRound3(round3Input)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, round3Out2)
|
||||
// signer1 and signer2 outputs the same signature
|
||||
require.Equal(t, round3Out1.Z, round3Out2.Z)
|
||||
require.Equal(t, round3Out1.C, round3Out2.C)
|
||||
|
||||
// test verify method
|
||||
msg := []byte("message")
|
||||
signature := &Signature{
|
||||
round3Out1.Z,
|
||||
round3Out1.C,
|
||||
}
|
||||
vk := signer1.verificationKey
|
||||
ok, err := Verify(signer1.curve, signer1.challengeDeriver, vk, msg, signature)
|
||||
require.True(t, ok)
|
||||
require.NoError(t, err)
|
||||
|
||||
ok, err = Verify(signer2.curve, signer2.challengeDeriver, vk, msg, signature)
|
||||
require.True(t, ok)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestSignRound3RepeatCall(t *testing.T) {
|
||||
signer1, _, round3Input := PrepareRound3Input(t)
|
||||
_, err := signer1.SignRound3(round3Input)
|
||||
require.NoError(t, err)
|
||||
_, err = signer1.SignRound3(round3Input)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestSignRound3BadInput(t *testing.T) {
|
||||
signer1, _, round3Input := PrepareRound3Input(t)
|
||||
|
||||
// Actual test: nil input
|
||||
round3Input[signer1.id] = nil
|
||||
_, err := signer1.SignRound3(round3Input)
|
||||
require.Error(t, err)
|
||||
round3Input = nil
|
||||
_, err = signer1.SignRound3(round3Input)
|
||||
require.Error(t, err)
|
||||
|
||||
// Actual test: set invalid length of round3Input
|
||||
signer1, _, round3Input = PrepareRound3Input(t)
|
||||
round3Input[100] = round3Input[signer1.id]
|
||||
_, err = signer1.SignRound3(round3Input)
|
||||
require.Error(t, err)
|
||||
|
||||
// Actual test: maul the round3Input
|
||||
signer1, _, round3Input = PrepareRound3Input(t)
|
||||
round3Input[signer1.id].Zi = round3Input[signer1.id].Zi.Add(testCurve.Scalar.New(2))
|
||||
_, err = signer1.SignRound3(round3Input)
|
||||
require.Error(t, err)
|
||||
|
||||
// Actual test: set non-zero smallD and smallE
|
||||
signer1, _, round3Input = PrepareRound3Input(t)
|
||||
signer1.state.smallD = testCurve.Scalar.New(1)
|
||||
_, err = signer1.SignRound3(round3Input)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestFullRoundsWorks(t *testing.T) {
|
||||
// Give a full-round test (FROST DKG + FROST Signing) with threshold = 2 and limit = 3, same as the test of tECDSA
|
||||
threshold := 2
|
||||
limit := 3
|
||||
|
||||
// Prepare DKG participants
|
||||
participants := make(map[uint32]*dkg.DkgParticipant, limit)
|
||||
for i := 1; i <= limit; i++ {
|
||||
otherIds := make([]uint32, limit-1)
|
||||
idx := 0
|
||||
for j := 1; j <= limit; j++ {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
otherIds[idx] = uint32(j)
|
||||
idx++
|
||||
}
|
||||
p, err := dkg.NewDkgParticipant(uint32(i), uint32(threshold), ctx, testCurve, otherIds...)
|
||||
require.NoError(t, err)
|
||||
participants[uint32(i)] = p
|
||||
}
|
||||
|
||||
// FROST DKG round 1
|
||||
rnd1Bcast := make(map[uint32]*dkg.Round1Bcast, len(participants))
|
||||
rnd1P2p := make(map[uint32]dkg.Round1P2PSend, len(participants))
|
||||
for id, p := range participants {
|
||||
bcast, p2psend, err := p.Round1(nil)
|
||||
require.NoError(t, err)
|
||||
rnd1Bcast[id] = bcast
|
||||
rnd1P2p[id] = p2psend
|
||||
}
|
||||
|
||||
// FROST DKG round 2
|
||||
for id := range rnd1Bcast {
|
||||
rnd1P2pForP := make(map[uint32]*sharing.ShamirShare)
|
||||
for jid := range rnd1P2p {
|
||||
if jid == id {
|
||||
continue
|
||||
}
|
||||
rnd1P2pForP[jid] = rnd1P2p[jid][id]
|
||||
}
|
||||
_, err := participants[id].Round2(rnd1Bcast, rnd1P2pForP)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Prepare Lagrange coefficients
|
||||
scheme, _ := sharing.NewShamir(uint32(threshold), uint32(limit), testCurve)
|
||||
|
||||
// Here we use {1, 3} as 2 of 3 cosigners, we can also set cosigners as {1, 2}, {2, 3}
|
||||
signerIds := []uint32{1, 3}
|
||||
lCoeffs, err := scheme.LagrangeCoeffs(signerIds)
|
||||
require.NoError(t, err)
|
||||
signers := make(map[uint32]*Signer, threshold)
|
||||
for _, id := range signerIds {
|
||||
signers[id], err = NewSigner(participants[id], id, uint32(threshold), lCoeffs, signerIds, &Ed25519ChallengeDeriver{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, signers[id].skShare)
|
||||
}
|
||||
|
||||
// Running sign round 1
|
||||
round2Input := make(map[uint32]*Round1Bcast, threshold)
|
||||
for id := range signers {
|
||||
round1Out, err := signers[id].SignRound1()
|
||||
require.NoError(t, err)
|
||||
round2Input[signers[id].id] = round1Out
|
||||
}
|
||||
|
||||
// Running sign round 2
|
||||
msg := []byte("message")
|
||||
round3Input := make(map[uint32]*Round2Bcast, threshold)
|
||||
for id := range signers {
|
||||
round2Out, err := signers[id].SignRound2(msg, round2Input)
|
||||
require.NoError(t, err)
|
||||
round3Input[signers[id].id] = round2Out
|
||||
}
|
||||
|
||||
// Running sign round 3
|
||||
result := make(map[uint32]*Round3Bcast, threshold)
|
||||
for id := range signers {
|
||||
round3Out, err := signers[id].SignRound3(round3Input)
|
||||
require.NoError(t, err)
|
||||
result[signers[id].id] = round3Out
|
||||
}
|
||||
|
||||
// Every signer has the same output Schnorr signature
|
||||
require.Equal(t, result[1].Z, result[3].Z)
|
||||
// require.Equal(t, z, result[3].Z)
|
||||
require.Equal(t, result[1].C, result[3].C)
|
||||
// require.Equal(t, c, result[3].C)
|
||||
}
|
||||
Reference in New Issue
Block a user