sonr/crypto/ted25519/frost/rounds_test.go

405 lines
12 KiB
Go
Raw Permalink Normal View History

feature/1114 implement account interface (#1167) - **refactor: move session-related code to middleware package** - **refactor: update PKL build process and adjust related configurations** - **feat: integrate base.cosmos.v1 Genesis module** - **refactor: pass session context to modal rendering functions** - **refactor: move nebula package to app directory and update templ version** - **refactor: Move home section video view to dedicated directory** - **refactor: remove unused views file** - **refactor: move styles and UI components to global scope** - **refactor: Rename images.go to cdn.go** - **feat: Add Empty State Illustrations** - **refactor: Consolidate Vault Index Logic** - **fix: References to App.wasm and remove Vault Directory embedded CDN files** - **refactor: Move CDN types to Models** - **fix: Correct line numbers in templ error messages for arch_templ.go** - **refactor: use common types for peer roles** - **refactor: move common types and ORM to a shared package** - **fix: Config import dwn** - **refactor: move nebula directory to app** - **feat: Rebuild nebula** - **fix: correct file paths in panels templates** - **feat: Remove duplicate types** - **refactor: Move dwn to pkg/core** - **refactor: Binary Structure** - **feat: Introduce Crypto Pkg** - **fix: Broken Process Start** - **feat: Update pkg/* structure** - **feat: Refactor PKL Structure** - **build: update pkl build process** - **chore: Remove Empty Files** - **refactor: remove unused macaroon package** - **feat: Add WebAwesome Components** - **refactor: consolidate build and generation tasks into a single taskfile, remove redundant makefile targets** - **refactor: refactor server and move components to pkg/core/dwn** - **build: update go modules** - **refactor: move gateway logic into dedicated hway command** - **feat: Add KSS (Krawczyk-Song-Song) MPC cryptography module** - **feat: Implement MPC-based JWT signing and UCAN token generation** - **feat: add support for MPC-based JWT signing** - **feat: Implement MPC-based UCAN capabilities for smart accounts** - **feat: add address field to keyshareSource** - **feat: Add comprehensive MPC test suite for keyshares, UCAN tokens, and token attenuations** - **refactor: improve MPC keyshare management and signing process** - **feat: enhance MPC capability hierarchy documentation** - **refactor: rename GenerateKeyshares function to NewKeyshareSource for clarity** - **refactor: remove unused Ethereum address computation** - **feat: Add HasHandle and IsAuthenticated methods to HTTPContext** - **refactor: Add context.Context support to session HTTPContext** - **refactor: Resolve context interface conflicts in HTTPContext** - **feat: Add session ID context key and helper functions** - **feat: Update WebApp Page Rendering** - **refactor: Simplify context management by using single HTTPContext key** - **refactor: Simplify HTTPContext creation and context management in session middleware** - **refactor: refactor session middleware to use a single data structure** - **refactor: Simplify HTTPContext implementation and session data handling** - **refactor: Improve session context handling and prevent nil pointer errors** - **refactor: Improve session context handling with nil safety and type support** - **refactor: improve session data injection** - **feat: add full-screen modal component and update registration flow** - **chore: add .air.toml to .gitignore** - **feat: add Air to devbox and update dependencies**
2024-11-23 01:28:58 -05:00
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package frost
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves"
dkg "github.com/onsonr/sonr/crypto/dkg/frost"
"github.com/onsonr/sonr/crypto/sharing"
feature/1114 implement account interface (#1167) - **refactor: move session-related code to middleware package** - **refactor: update PKL build process and adjust related configurations** - **feat: integrate base.cosmos.v1 Genesis module** - **refactor: pass session context to modal rendering functions** - **refactor: move nebula package to app directory and update templ version** - **refactor: Move home section video view to dedicated directory** - **refactor: remove unused views file** - **refactor: move styles and UI components to global scope** - **refactor: Rename images.go to cdn.go** - **feat: Add Empty State Illustrations** - **refactor: Consolidate Vault Index Logic** - **fix: References to App.wasm and remove Vault Directory embedded CDN files** - **refactor: Move CDN types to Models** - **fix: Correct line numbers in templ error messages for arch_templ.go** - **refactor: use common types for peer roles** - **refactor: move common types and ORM to a shared package** - **fix: Config import dwn** - **refactor: move nebula directory to app** - **feat: Rebuild nebula** - **fix: correct file paths in panels templates** - **feat: Remove duplicate types** - **refactor: Move dwn to pkg/core** - **refactor: Binary Structure** - **feat: Introduce Crypto Pkg** - **fix: Broken Process Start** - **feat: Update pkg/* structure** - **feat: Refactor PKL Structure** - **build: update pkl build process** - **chore: Remove Empty Files** - **refactor: remove unused macaroon package** - **feat: Add WebAwesome Components** - **refactor: consolidate build and generation tasks into a single taskfile, remove redundant makefile targets** - **refactor: refactor server and move components to pkg/core/dwn** - **build: update go modules** - **refactor: move gateway logic into dedicated hway command** - **feat: Add KSS (Krawczyk-Song-Song) MPC cryptography module** - **feat: Implement MPC-based JWT signing and UCAN token generation** - **feat: add support for MPC-based JWT signing** - **feat: Implement MPC-based UCAN capabilities for smart accounts** - **feat: add address field to keyshareSource** - **feat: Add comprehensive MPC test suite for keyshares, UCAN tokens, and token attenuations** - **refactor: improve MPC keyshare management and signing process** - **feat: enhance MPC capability hierarchy documentation** - **refactor: rename GenerateKeyshares function to NewKeyshareSource for clarity** - **refactor: remove unused Ethereum address computation** - **feat: Add HasHandle and IsAuthenticated methods to HTTPContext** - **refactor: Add context.Context support to session HTTPContext** - **refactor: Resolve context interface conflicts in HTTPContext** - **feat: Add session ID context key and helper functions** - **feat: Update WebApp Page Rendering** - **refactor: Simplify context management by using single HTTPContext key** - **refactor: Simplify HTTPContext creation and context management in session middleware** - **refactor: refactor session middleware to use a single data structure** - **refactor: Simplify HTTPContext implementation and session data handling** - **refactor: Improve session context handling and prevent nil pointer errors** - **refactor: Improve session context handling with nil safety and type support** - **refactor: improve session data injection** - **feat: add full-screen modal component and update registration flow** - **chore: add .air.toml to .gitignore** - **feat: add Air to devbox and update dependencies**
2024-11-23 01:28:58 -05:00
)
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)
}