Skip to content

Commit

Permalink
✨ feat(frost): add encode/decode functions and fix some issues >>> ⏰ 4h
Browse files Browse the repository at this point in the history
  • Loading branch information
logicalangel committed May 23, 2024
1 parent e3811ca commit 7d877e1
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 40 deletions.
6 changes: 3 additions & 3 deletions internal/app/broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ func Broker() {
ethRPC := ethereum.New()
pos.New(ethRPC)

//frostService := frost.New()
// frostService := frost.New()

//scheduler := scheduler.New(
// scheduler := scheduler.New(
// scheduler.WithFrostEvents(frostService),
//)

server.New(
websocket.WithWebsocket(),
)

//scheduler.Start()
// scheduler.Start()
}
1 change: 1 addition & 0 deletions internal/consts/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ var (
ErrAlreadySynced = errors.New("already synced")
ErrSignerIsNotReady = errors.New("signer is not ready yet")
ErrCantSign = errors.New("cant sign message")
ErrCantDecode = errors.New("cant decode message")
ErrCantVerify = errors.New("cant verify message")
)
158 changes: 158 additions & 0 deletions internal/crypto/frost/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package frost

import (
"encoding/hex"

"github.com/TimeleapLabs/unchained/internal/consts"
"github.com/TimeleapLabs/unchained/internal/utils"
"github.com/bytemare/crypto"
"github.com/bytemare/frost"
"github.com/bytemare/frost/dkg"
)

// DecodeCommitment will decode commitment from bytes.
func (s *MessageSigner) DecodeCommitment(data []byte) (*frost.Commitment, error) {
scalarLen := s.participant.Ciphersuite.Group.ScalarLength()
commitment := &frost.Commitment{
Identifier: s.participant.Ciphersuite.Group.NewScalar(),
HidingNonce: s.participant.Ciphersuite.Group.NewElement(),
BindingNonce: s.participant.Ciphersuite.Group.NewElement(),
}

err := commitment.Identifier.Decode(data[:scalarLen])
if err != nil {
utils.Logger.With("err", err, "value", hex.EncodeToString(data[:scalarLen])).Error("cant decode commitment identifier")
return nil, err
}

err = commitment.HidingNonce.Decode(data[scalarLen : scalarLen*2])
if err != nil {
utils.Logger.With("err", err, "value", hex.EncodeToString(data[:scalarLen])).Error("cant decode commitment hiding_nonce")
// return nil, err
}

err = commitment.BindingNonce.Decode(data[scalarLen*2 : scalarLen*3])
if err != nil {
utils.Logger.With("err", err, "value", hex.EncodeToString(data[:scalarLen])).Error("cant decode commitment binding_nonce")
return nil, err
}

return commitment, nil
}

// DecodeSignature will decode signature from bytes.
func (s *MessageSigner) DecodeSignature(data []byte) (*frost.Signature, error) {
signature := frost.Signature{
R: s.participant.Ciphersuite.Group.NewElement(),
Z: s.participant.Ciphersuite.Group.NewScalar(),
}
err := signature.Decode(s.participant.Ciphersuite.Group, data)
if err != nil {
utils.Logger.With("err", err).Error("cant decode signature")
return &signature, consts.ErrCantDecode
}

return &signature, nil
}

func EncodeRoundOneMessage(message *dkg.Round1Data) []byte {
commitment := []byte{}
for _, commit := range message.Commitment {
commitment = append(commitment, commit.Encode()...)
}

out := []byte{}
out = append(out, message.SenderIdentifier.Encode()...)
out = append(out, message.ProofOfKnowledge.Encode()...)
out = append(out, commitment...)

return out
}

func EncodeRoundTwoMessages(message []*dkg.Round2Data) [][]byte {
out := [][]byte{}

for _, msg := range message {
out = append(out, EncodeRoundTwoMessage(msg))
}

return out
}

func EncodeRoundTwoMessage(message *dkg.Round2Data) []byte {
out := []byte{}

out = append(out, message.SenderIdentifier.Encode()...)
out = append(out, message.ReceiverIdentifier.Encode()...)
out = append(out, message.SecretShare.Encode()...)

return out
}

func (s *DistributedSigner) DecodeRoundTwoMessage(message []byte) (*dkg.Round2Data, error) {
r2Data := &dkg.Round2Data{
SenderIdentifier: s.config.Ciphersuite.Group.NewScalar(),
ReceiverIdentifier: s.config.Ciphersuite.Group.NewScalar(),
SecretShare: s.config.Ciphersuite.Group.NewScalar(),
}

err := r2Data.SenderIdentifier.Decode(message[:s.config.Ciphersuite.Group.ScalarLength()])
if err != nil {
utils.Logger.With("err", err).Error("cant decode sender_identifier")
return nil, err
}

err = r2Data.ReceiverIdentifier.Decode(message[s.config.Ciphersuite.Group.ScalarLength() : s.config.Ciphersuite.Group.ScalarLength()*2])
if err != nil {
utils.Logger.With("err", err).Error("cant decode receiver_identifier")
return nil, err
}

err = r2Data.SecretShare.Decode(message[s.config.Ciphersuite.Group.ScalarLength()*2:])
if err != nil {
utils.Logger.With("err", err).Error("cant decode secret_share")
return nil, err
}

return r2Data, nil
}

func (s *DistributedSigner) DecodeRoundOneMessage(message []byte) (*dkg.Round1Data, error) {
senderBytes := message[:s.config.Ciphersuite.Group.ScalarLength()]
proofBytes := message[s.config.Ciphersuite.Group.ScalarLength() : s.config.Ciphersuite.Group.ScalarLength()+64]
commitmentsBytes := message[s.config.Ciphersuite.Group.ScalarLength()+64:]

r1Data := &dkg.Round1Data{
ProofOfKnowledge: frost.Signature{
R: s.config.Ciphersuite.Group.NewElement(),
Z: s.config.Ciphersuite.Group.NewScalar(),
},
SenderIdentifier: s.config.Ciphersuite.Group.NewScalar(),
Commitment: []*crypto.Element{},
}

for i := 0; i < len(commitmentsBytes); i += s.config.Ciphersuite.Group.ElementLength() {
commitment := s.config.Ciphersuite.Group.NewElement()
err := commitment.Decode(commitmentsBytes[i : i+s.config.Ciphersuite.Group.ElementLength()])
if err != nil {
utils.Logger.With("err", err).Error("cant decode commitment")
return nil, err
}

r1Data.Commitment = append(r1Data.Commitment, commitment)
}

err := r1Data.ProofOfKnowledge.Decode(s.config.Ciphersuite.Group, proofBytes)
if err != nil {
utils.Logger.With("err", err).Error("cant decode proof_of_knowledge")
return nil, err
}

err = r1Data.SenderIdentifier.Decode(senderBytes)
if err != nil {
utils.Logger.With("err", err).Error("cant decode identifier")
return nil, err
}

return r1Data, nil
}
22 changes: 16 additions & 6 deletions internal/crypto/frost/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ type DistributedSigner struct {
}

// Update function will update the identity key about other parties.
func (s *DistributedSigner) Update(msg *dkg.Round1Data) ([]*dkg.Round2Data, error) {
s.accumulatedMessages = append(s.accumulatedMessages, msg)
func (s *DistributedSigner) Update(msg []byte) ([][]byte, error) {
round1Data, err := s.DecodeRoundOneMessage(msg)
if err != nil {
return nil, err
}

s.accumulatedMessages = append(s.accumulatedMessages, round1Data)

if len(s.accumulatedMessages) != s.signerCount {
return nil, nil
Expand All @@ -37,11 +42,16 @@ func (s *DistributedSigner) Update(msg *dkg.Round1Data) ([]*dkg.Round2Data, erro
return nil, errors.New("number of accept messages is not correct")
}

return round2Data, nil
return EncodeRoundTwoMessages(round2Data), nil
}

// Finalize function will confirm the identity key about other parties updates.
func (s *DistributedSigner) Finalize(msg *dkg.Round2Data) error {
func (s *DistributedSigner) Finalize(msgByte []byte) error {
msg, err := s.DecodeRoundTwoMessage(msgByte)
if err != nil {
return err
}

if msg.ReceiverIdentifier.Equal(s.currentParticipant.Identifier) == 0 {
return nil
}
Expand All @@ -68,7 +78,7 @@ func (s *DistributedSigner) Finalize(msg *dkg.Round2Data) error {
}

// NewIdentity creates a new Frost identity.
func NewIdentity(id int, signerCount int, minSigningCount int) (*dkg.Round1Data, *DistributedSigner) {
func NewIdentity(id int, signerCount int, minSigningCount int) ([]byte, *DistributedSigner) {
signer := DistributedSigner{
accumulatedMessages: make([]*dkg.Round1Data, 0, signerCount),
config: frost.Ristretto255.Configuration(),
Expand All @@ -89,5 +99,5 @@ func NewIdentity(id int, signerCount int, minSigningCount int) (*dkg.Round1Data,
panic("this is just a test, and it failed")
}

return round1Data, &signer
return EncodeRoundOneMessage(round1Data), &signer
}
37 changes: 14 additions & 23 deletions internal/crypto/frost/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import (
"testing"

"github.com/TimeleapLabs/unchained/internal/utils"
"github.com/bytemare/frost"
"github.com/bytemare/frost/dkg"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

const (
numOfSigners = 100
minNumOfSigners = 51
numOfSigners = 10
minNumOfSigners = 6
)

var (
Expand All @@ -33,14 +31,14 @@ func (s *FrostIdentityTestSuite) SetupTest() {
signers = append(signers, i+1)
}

r1Messages := []*dkg.Round1Data{}
r1Messages := [][]byte{}
for i := 0; i < numOfSigners; i++ {
r1Msg, party := NewIdentity(signers[i], numOfSigners, minNumOfSigners)
s.parties = append(s.parties, party)
r1Messages = append(r1Messages, r1Msg)
}

r2Messages := []*dkg.Round2Data{}
r2Messages := [][]byte{}
for i := 0; i < numOfSigners; i++ {
for j := 0; j < numOfSigners; j++ {
r2Msgs, err := s.parties[i].Update(r1Messages[j])
Expand All @@ -60,7 +58,7 @@ func (s *FrostIdentityTestSuite) SetupTest() {

func (s *FrostIdentityTestSuite) TestSign() {
signers := make([]*MessageSigner, 0, minNumOfSigners)
commits := make([]*frost.Commitment, 0, minNumOfSigners)
commits := [][]byte{}

for i := 0; i < minNumOfSigners; i++ {
signer, msg, err := s.parties[i].NewSigner(testData)
Expand All @@ -70,31 +68,24 @@ func (s *FrostIdentityTestSuite) TestSign() {
signers = append(signers, signer)
}

signatureShares := make([]*frost.SignatureShare, 0, minNumOfSigners)
signatureShares := make([][]byte, 0, minNumOfSigners)

for i := 0; i < minNumOfSigners; i++ {
for j := 0; j < minNumOfSigners; j++ {
signature, err := signers[i].Confirm(commits[j])
for _, signer := range signers {
for _, commit := range commits {
signature, err := signer.Confirm(commit)
assert.NoError(s.T(), err)

if signature != nil {
signatureShares = append(signatureShares, signature)
}
}
}

signature := signers[0].participant.Aggregate(
signers[0].commitments,
testData,
signatureShares,
)

ok := frost.Verify(
frost.Ristretto255.Configuration().Ciphersuite,
testData,
signature,
signers[0].participant.GroupPublicKey,
)
signature, err := signers[0].Aggregate(signatureShares)
assert.NoError(s.T(), err)

ok, err := signers[0].Verify(signature)
assert.NoError(s.T(), err)
assert.True(s.T(), ok)
}

Expand Down
44 changes: 39 additions & 5 deletions internal/crypto/frost/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ type MessageSigner struct {
}

// Confirm function will set other parties confirms.
func (s *MessageSigner) Confirm(commitment *frost.Commitment) (*frost.SignatureShare, error) {
func (s *MessageSigner) Confirm(commitmentBytes []byte) ([]byte, error) {
commitment, err := s.DecodeCommitment(commitmentBytes)
if err != nil {
utils.Logger.With("err", err).Error("cant decode commitment")
return nil, consts.ErrCantDecode
}

s.commitments = append(s.commitments, commitment)

if len(s.commitments.Participants()) < s.partySize {
Expand All @@ -41,17 +47,45 @@ func (s *MessageSigner) Confirm(commitment *frost.Commitment) (*frost.SignatureS
return nil, consts.ErrCantVerify
}

return signatureShare, nil
return signatureShare.Encode(), nil
}

func (s *MessageSigner) AggregateSignatures(signatureShares []*frost.SignatureShare) ([]byte, error) {
// Aggregate function will generate final signature by combining parties signature shares.
func (s *MessageSigner) Aggregate(signatureSharesBytes [][]byte) ([]byte, error) {
signatureShares := []*frost.SignatureShare{}

for _, signatureShareBytes := range signatureSharesBytes {
signatureShare, err := frost.Ristretto255.Configuration().DecodeSignatureShare(signatureShareBytes)
if err != nil {
utils.Logger.With("err", err).Error("cant decode signature share")
return nil, consts.ErrCantDecode
}

signatureShares = append(signatureShares, signatureShare)
}

signature := s.participant.Aggregate(s.commitments, s.data, signatureShares)

return signature.Encode(), nil
}

// Verify function will verify the signature.
func (s *MessageSigner) Verify(signatureBytes []byte) (bool, error) {
signature, err := s.DecodeSignature(signatureBytes)
if err != nil {
return false, err
}

return frost.Verify(
frost.Ristretto255.Configuration().Ciphersuite,
s.data,
signature,
s.participant.GroupPublicKey,
), nil
}

// NewSigner create a new signing state for a message.
func (s *DistributedSigner) NewSigner(data []byte) (*MessageSigner, *frost.Commitment, error) {
func (s *DistributedSigner) NewSigner(data []byte) (*MessageSigner, []byte, error) {
if s.finalParticipant == nil {
return nil, nil, consts.ErrSignerIsNotReady
}
Expand All @@ -69,5 +103,5 @@ func (s *DistributedSigner) NewSigner(data []byte) (*MessageSigner, *frost.Commi
commitment: commitment,
}

return signer, commitment, nil
return signer, commitment.Encode(), nil
}
Loading

0 comments on commit 7d877e1

Please sign in to comment.