diff --git a/internal/app/broker.go b/internal/app/broker.go index 42875f3f..214bccae 100644 --- a/internal/app/broker.go +++ b/internal/app/broker.go @@ -26,9 +26,9 @@ func Broker() { ethRPC := ethereum.New() pos.New(ethRPC) - //frostService := frost.New() + // frostService := frost.New() - //scheduler := scheduler.New( + // scheduler := scheduler.New( // scheduler.WithFrostEvents(frostService), //) @@ -36,5 +36,5 @@ func Broker() { websocket.WithWebsocket(), ) - //scheduler.Start() + // scheduler.Start() } diff --git a/internal/consts/errors.go b/internal/consts/errors.go index 4f7775d6..50c9e8c4 100644 --- a/internal/consts/errors.go +++ b/internal/consts/errors.go @@ -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") ) diff --git a/internal/crypto/frost/convert.go b/internal/crypto/frost/convert.go new file mode 100644 index 00000000..6b6aee8c --- /dev/null +++ b/internal/crypto/frost/convert.go @@ -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 +} diff --git a/internal/crypto/frost/identity.go b/internal/crypto/frost/identity.go index 63ef8394..831a92a8 100644 --- a/internal/crypto/frost/identity.go +++ b/internal/crypto/frost/identity.go @@ -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 @@ -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 } @@ -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(), @@ -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 } diff --git a/internal/crypto/frost/identity_test.go b/internal/crypto/frost/identity_test.go index 7e1bc9fe..515a1629 100644 --- a/internal/crypto/frost/identity_test.go +++ b/internal/crypto/frost/identity_test.go @@ -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 ( @@ -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]) @@ -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) @@ -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) } diff --git a/internal/crypto/frost/signer.go b/internal/crypto/frost/signer.go index b6d8a1a7..7548b3f5 100644 --- a/internal/crypto/frost/signer.go +++ b/internal/crypto/frost/signer.go @@ -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 { @@ -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 } @@ -69,5 +103,5 @@ func (s *DistributedSigner) NewSigner(data []byte) (*MessageSigner, *frost.Commi commitment: commitment, } - return signer, commitment, nil + return signer, commitment.Encode(), nil } diff --git a/internal/model/bls.go b/internal/model/bls.go index d0ce1671..54882dd6 100644 --- a/internal/model/bls.go +++ b/internal/model/bls.go @@ -43,7 +43,7 @@ func (s Signers) Sia() sia.Sia { }) } -//func (s Signers) FromBytes(payload []byte) Signers { +// func (s Signers) FromBytes(payload []byte) Signers { // signers := Signers{} // // siaArray := sia.ArraySia[Signer]{ diff --git a/internal/service/frost/sync.go b/internal/service/frost/sync.go index ded26950..6248b88b 100644 --- a/internal/service/frost/sync.go +++ b/internal/service/frost/sync.go @@ -2,6 +2,7 @@ package frost import ( "encoding/json" + "github.com/TimeleapLabs/unchained/internal/transport/server/pubsub" "github.com/TimeleapLabs/unchained/internal/consts" diff --git a/internal/transport/client/handler/frost.go b/internal/transport/client/handler/frost.go index 6729c763..36bf09cc 100644 --- a/internal/transport/client/handler/frost.go +++ b/internal/transport/client/handler/frost.go @@ -10,7 +10,7 @@ func (h *consumer) InitFrostSigner(ctx context.Context, message []byte) { } func (w worker) InitFrostSigner(ctx context.Context, message []byte) { - //packet := new(model.Signers).FromBytes(message) - //TODO implement me + // packet := new(model.Signers).FromBytes(message) + // TODO implement me panic("implement me") }