From 8ade9e29385cac06dde8b9ca12e00630bf347f21 Mon Sep 17 00:00:00 2001 From: Axel Date: Wed, 18 May 2016 17:50:31 +0200 Subject: [PATCH] [c][t] Add TTP calls from client --- dfssc/cmd/sign.go | 7 - dfssc/common/sequence_analysis.go | 45 +++- dfssc/common/sequence_analysis_test.go | 90 ++++++- dfssc/sign/promises.go | 13 +- dfssc/sign/protocol.go | 164 ++++++++++-- dfssc/sign/protocol_test.go | 141 +++++++++++ dfssc/sign/server.go | 7 + dfssc/sign/starter.go | 89 ++++--- dfsst/api/resolution.pb.go | 36 +-- dfsst/api/resolution.proto | 2 + dfsst/entities/archivesManager.go | 3 +- dfsst/entities/check_promise.go | 18 +- dfsst/entities/check_promise_test.go | 10 +- dfsst/entities/check_request.go | 11 +- dfsst/entities/signatureArchives.go | 6 +- dfsst/resolve/resolve.go | 142 ++++++++++- dfsst/resolve/resolve_test.go | 338 ++++++++++++++++++++++++- dfsst/server/server.go | 12 +- 18 files changed, 995 insertions(+), 139 deletions(-) create mode 100644 dfssc/sign/protocol_test.go diff --git a/dfssc/cmd/sign.go b/dfssc/cmd/sign.go index 7725a89..ca572b9 100644 --- a/dfssc/cmd/sign.go +++ b/dfssc/cmd/sign.go @@ -78,13 +78,6 @@ var signCmd = &cobra.Command{ os.Exit(1) } - // Persist evidencies, if any - err = manager.PersistSignaturesToFile() - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - fmt.Println("Signature complete! See .proof file for evidences.") }, } diff --git a/dfssc/common/sequence_analysis.go b/dfssc/common/sequence_analysis.go index a2d1df4..803fdca 100644 --- a/dfssc/common/sequence_analysis.go +++ b/dfssc/common/sequence_analysis.go @@ -4,6 +4,11 @@ import ( "errors" ) +// SequenceCoordinate : contains both the sequence id of a signer, and the index of the occurence in the sequence +type SequenceCoordinate struct { + Signer, Index uint32 +} + // FindNextIndex analyses the specified sequence and tries to find the next occurence of id after the specified index (excluded) // Therefore to find the first occurence of the id in the sequence, use -1 as the index // @@ -30,8 +35,8 @@ func FindNextIndex(s []uint32, id uint32, index int) (int, error) { // // If the index is not the one of the specified id in the sequence, the result still holds, but may be incomplete for your needs // If the id is not valid for the specified sequence, the result will be the set of ids of the sequence -func GetPendingSet(s []uint32, id uint32, index int) ([]uint32, error) { - res := []uint32{} +func GetPendingSet(s []uint32, id uint32, index int) ([]SequenceCoordinate, error) { + res := []SequenceCoordinate{} if index >= len(s) || index < 0 { return res, errors.New("Index out of range") @@ -44,8 +49,8 @@ func GetPendingSet(s []uint32, id uint32, index int) ([]uint32, error) { for curIndex > -1 && s[curIndex] != id { curID := s[curIndex] - if !contains(res, curID) { - res = append(res, curID) + if !containsCoordinate(res, curID) { + res = append(res, SequenceCoordinate{Signer: curID, Index: uint32(curIndex)}) } curIndex-- } @@ -59,8 +64,8 @@ func GetPendingSet(s []uint32, id uint32, index int) ([]uint32, error) { // // If the index is not the one of the specified id in the sequence, the result still holds, but may be incomplete for your needs // If the id is not valid for the specified sequence, the result will be the set of ids of the sequence -func GetSendSet(s []uint32, id uint32, index int) ([]uint32, error) { - res := []uint32{} +func GetSendSet(s []uint32, id uint32, index int) ([]SequenceCoordinate, error) { + res := []SequenceCoordinate{} if index >= len(s) || index < 0 { return res, errors.New("Index out of range") @@ -73,8 +78,8 @@ func GetSendSet(s []uint32, id uint32, index int) ([]uint32, error) { for curIndex < len(s) && s[curIndex] != id { curID := s[curIndex] - if !contains(res, curID) { - res = append(res, curID) + if !containsCoordinate(res, curID) { + res = append(res, SequenceCoordinate{Signer: curID, Index: uint32(curIndex)}) } curIndex++ } @@ -82,7 +87,17 @@ func GetSendSet(s []uint32, id uint32, index int) ([]uint32, error) { return res, nil } -// contains determines if s contains e +// containsCoordinate: determines if s contains the specified signer sequence id e +func containsCoordinate(s []SequenceCoordinate, e uint32) bool { + for _, a := range s { + if a.Signer == e { + return true + } + } + return false +} + +// contains : determines if s contains e func contains(s []uint32, e uint32) bool { for _, a := range s { if a == e { @@ -108,7 +123,17 @@ func GetAllButOne(s []uint32, e uint32) []uint32 { return res } -// Remove an ID from the sequence +// RemoveCoordinate : removes the first occurence of an ID from the coordinates array +func RemoveCoordinate(s []SequenceCoordinate, e uint32) ([]SequenceCoordinate, error) { + for i, a := range s { + if a.Signer == e { + return append(s[:i], s[i+1:]...), nil + } + } + return s, errors.New("ID not in sequence") +} + +// Remove the first occurence of an ID from the ids array func Remove(s []uint32, e uint32) ([]uint32, error) { for i, a := range s { if a == e { diff --git a/dfssc/common/sequence_analysis_test.go b/dfssc/common/sequence_analysis_test.go index b112440..a4354d8 100644 --- a/dfssc/common/sequence_analysis_test.go +++ b/dfssc/common/sequence_analysis_test.go @@ -50,19 +50,24 @@ func TestGetPendingSet(t *testing.T) { res, err = GetPendingSet(s, id, 1) assert.Equal(t, len(res), 1) - assert.Equal(t, res[0], uint32(0)) + assert.Equal(t, res[0].Signer, uint32(0)) + assert.Equal(t, res[0].Index, uint32(0)) assert.Equal(t, err, nil) res, err = GetPendingSet(s, id, 2) assert.Equal(t, len(res), 2) - assert.Equal(t, res[0], uint32(1)) - assert.Equal(t, res[1], uint32(0)) + assert.Equal(t, res[0].Signer, uint32(1)) + assert.Equal(t, res[0].Index, uint32(1)) + assert.Equal(t, res[1].Signer, uint32(0)) + assert.Equal(t, res[1].Index, uint32(0)) assert.Equal(t, err, nil) res, err = GetPendingSet(s, id, 5) assert.Equal(t, len(res), 2) - assert.Equal(t, res[0], uint32(1)) - assert.Equal(t, res[1], uint32(0)) + assert.Equal(t, res[0].Signer, uint32(1)) + assert.Equal(t, res[0].Index, uint32(4)) + assert.Equal(t, res[1].Signer, uint32(0)) + assert.Equal(t, res[1].Index, uint32(3)) assert.Equal(t, err, nil) res, err = GetPendingSet(s, id, -1) @@ -76,11 +81,23 @@ func TestGetPendingSet(t *testing.T) { s = []uint32{0, 1, 0, 2} res, err = GetPendingSet(s, id, 3) assert.Equal(t, len(res), 2) - assert.Equal(t, res[0], uint32(0)) - assert.Equal(t, res[1], uint32(1)) + assert.Equal(t, res[0].Signer, uint32(0)) + assert.Equal(t, res[0].Index, uint32(2)) + assert.Equal(t, res[1].Signer, uint32(1)) + assert.Equal(t, res[1].Index, uint32(1)) assert.Equal(t, err, nil) } +func TestContainsCoordinate(t *testing.T) { + var s []SequenceCoordinate + s = append(s, SequenceCoordinate{Signer: 0, Index: 0}) + s = append(s, SequenceCoordinate{Signer: 1, Index: 1}) + + assert.Equal(t, containsCoordinate(s, 0), true) + assert.Equal(t, containsCoordinate(s, 1), true) + assert.Equal(t, containsCoordinate(s, 2), false) +} + func TestContains(t *testing.T) { s := []uint32{0, 1} @@ -95,19 +112,24 @@ func TestGetSendSet(t *testing.T) { res, err := GetSendSet(s, id, 3) assert.Equal(t, len(res), 2) - assert.Equal(t, res[0], uint32(1)) - assert.Equal(t, res[1], uint32(2)) + assert.Equal(t, res[0].Signer, uint32(1)) + assert.Equal(t, res[0].Index, uint32(4)) + assert.Equal(t, res[1].Signer, uint32(2)) + assert.Equal(t, res[1].Index, uint32(5)) assert.Equal(t, err, nil) res, err = GetSendSet(s, id, 0) assert.Equal(t, len(res), 2) - assert.Equal(t, res[0], uint32(1)) - assert.Equal(t, res[1], uint32(2)) + assert.Equal(t, res[0].Signer, uint32(1)) + assert.Equal(t, res[0].Index, uint32(1)) + assert.Equal(t, res[1].Signer, uint32(2)) + assert.Equal(t, res[1].Index, uint32(2)) assert.Equal(t, err, nil) res, err = GetSendSet(s, 1, 4) assert.Equal(t, len(res), 1) - assert.Equal(t, res[0], uint32(2)) + assert.Equal(t, res[0].Signer, uint32(2)) + assert.Equal(t, res[0].Index, uint32(5)) assert.Equal(t, err, nil) res, err = GetSendSet(s, 2, 5) @@ -139,6 +161,46 @@ func TestGetAllButOne(t *testing.T) { assert.Equal(t, res[2], uint32(2)) } +func TestRemoveCoordinate(t *testing.T) { + var s []SequenceCoordinate + s = append(s, SequenceCoordinate{Signer: 0, Index: 0}) + s = append(s, SequenceCoordinate{Signer: 1, Index: 1}) + + assert.Equal(t, len(s), 2) + assert.Equal(t, containsCoordinate(s, 0), true) + assert.Equal(t, containsCoordinate(s, 1), true) + + s2, err := RemoveCoordinate(s, 3) + assert.Equal(t, err.Error(), "ID not in sequence") + assert.Equal(t, len(s2), 2) + assert.Equal(t, containsCoordinate(s2, 0), true) + assert.Equal(t, containsCoordinate(s2, 1), true) + + s3, err := RemoveCoordinate(s, 0) + assert.Equal(t, err, nil) + assert.Equal(t, len(s3), 1) + assert.Equal(t, containsCoordinate(s3, 0), false) + assert.Equal(t, containsCoordinate(s3, 1), true) +} + +func TestRemove(t *testing.T) { + s := []uint32{0, 1} + + assert.Equal(t, len(s), 2) + + s2, err := Remove(s, 3) + assert.Equal(t, err.Error(), "ID not in sequence") + assert.Equal(t, len(s2), 2) + assert.Equal(t, contains(s2, 0), true) + assert.Equal(t, contains(s2, 1), true) + + s3, err := Remove(s, 0) + assert.Equal(t, err, nil) + assert.Equal(t, len(s3), 1) + assert.Equal(t, contains(s3, 0), false) + assert.Equal(t, contains(s3, 1), true) +} + func ExampleFindNextIndex() { s := []uint32{0, 1, 2, 0, 1, 2} id := uint32(2) @@ -153,6 +215,6 @@ func ExampleFindNextIndex() { fmt.Println("Send Set:", sSet) // Output: // First index: 2 - // Pending Set: [1 0] - // Send Set: [0 1] + // Pending Set: [{1 1} {0 0}] + // Send Set: [{0 3} {1 4}] } diff --git a/dfssc/sign/promises.go b/dfssc/sign/promises.go index 0acd586..e5d3559 100644 --- a/dfssc/sign/promises.go +++ b/dfssc/sign/promises.go @@ -1,6 +1,7 @@ package sign import ( + "encoding/hex" "errors" "time" @@ -14,20 +15,23 @@ func (m *SignatureManager) createContext(from, to uint32) (*cAPI.Context, error) if int(from) >= len(m.keyHash) || int(to) >= len(m.keyHash) { return nil, errors.New("Invalid id for context creation") } + + h, _ := hex.DecodeString(m.contract.File.Hash) + return &cAPI.Context{ RecipientKeyHash: m.keyHash[to], SenderKeyHash: m.keyHash[from], Sequence: m.sequence, Signers: m.keyHash, - ContractDocumentHash: []byte(m.contract.File.Hash), + ContractDocumentHash: h, SignatureUUID: m.uuid, Seal: m.seal, }, nil } // CreatePromise creates a promise from 'from' to 'to', in the context of the SignatureManager -// provided the specified sequence indexes are valid -func (m *SignatureManager) CreatePromise(from, to uint32) (*cAPI.Promise, error) { +// provided that the specified sequence indexes are valid +func (m *SignatureManager) CreatePromise(from, to, at uint32) (*cAPI.Promise, error) { context, err := m.createContext(from, to) if err != nil { return nil, err @@ -38,7 +42,7 @@ func (m *SignatureManager) CreatePromise(from, to uint32) (*cAPI.Promise, error) } return &cAPI.Promise{ - Index: uint32(m.currentIndex), + Index: at, Context: context, Payload: []byte{0x41}, }, nil @@ -69,7 +73,6 @@ func (m *SignatureManager) SendEvidence(promise *cAPI.Promise, signature *cAPI.S m.archives.mutex.Lock() if promise != nil { dAPI.DLog("sent promise to " + mail) - m.archives.sentPromises = append(m.archives.sentPromises, promise) } else { dAPI.DLog("sent signature to " + mail) m.archives.sentSignatures = append(m.archives.sentSignatures, signature) diff --git a/dfssc/sign/protocol.go b/dfssc/sign/protocol.go index 21a870c..a97db80 100644 --- a/dfssc/sign/protocol.go +++ b/dfssc/sign/protocol.go @@ -2,12 +2,19 @@ package sign import ( + "bytes" + "errors" "fmt" + "io/ioutil" "time" cAPI "dfss/dfssc/api" "dfss/dfssc/common" dAPI "dfss/dfssd/api" + tAPI "dfss/dfsst/api" + "dfss/net" + "golang.org/x/net/context" + "google.golang.org/grpc" ) // Sign performs all the message exchanges for the contract to be signed @@ -24,7 +31,7 @@ func (m *SignatureManager) Sign() error { m.closeConnections() }() - myID, nextIndex, err := m.Initialize() + nextIndex, err := m.Initialize() if err != nil { return err } @@ -45,24 +52,27 @@ func (m *SignatureManager) Sign() error { dAPI.DLog("starting round at index [" + fmt.Sprintf("%d", m.currentIndex) + "] with nextIndex=" + fmt.Sprintf("%d", nextIndex)) // Set of promises we are waiting for - var pendingSet []uint32 - pendingSet, err = common.GetPendingSet(m.sequence, myID, m.currentIndex) + var pendingSet []common.SequenceCoordinate + pendingSet, err = common.GetPendingSet(m.sequence, m.myID, m.currentIndex) if err != nil { return err } // Set of the promises we must send - var sendSet []uint32 - sendSet, err = common.GetSendSet(m.sequence, myID, m.currentIndex) + var sendSet []common.SequenceCoordinate + sendSet, err = common.GetSendSet(m.sequence, m.myID, m.currentIndex) if err != nil { return err } // Exchange messages - m.promiseRound(pendingSet, sendSet, myID) + err = m.promiseRound(pendingSet, sendSet) + if err != nil { + return err + } m.currentIndex = nextIndex - nextIndex, err = common.FindNextIndex(m.sequence, myID, m.currentIndex) + nextIndex, err = common.FindNextIndex(m.sequence, m.myID, m.currentIndex) if err != nil { return err } @@ -78,7 +88,7 @@ func (m *SignatureManager) Sign() error { dAPI.DLog("exiting signature round") m.OnProgressUpdate(seqLen+1, seqLen+1) - return nil + return m.PersistSignaturesToFile() } // GetClient retrieves the Client to the specified sequence id provided it exists @@ -98,45 +108,52 @@ func (m *SignatureManager) makeSignersHashToIDMap() { } // promiseRound describes a promise round: reception and sending -func (m *SignatureManager) promiseRound(pendingSet, sendSet []uint32, myID uint32) { - +func (m *SignatureManager) promiseRound(pendingSet, sendSet []common.SequenceCoordinate) error { // Reception of the due promises + var promises []*cAPI.Promise for len(pendingSet) > 0 { select { case promiseIface := <-m.cServerIface.incomingPromises: promise := (promiseIface).(*cAPI.Promise) - senderID, exist := m.hashToID[fmt.Sprintf("%x", promise.Context.SenderKeyHash)] - if exist { + valid, senderID := m.checkPromise(pendingSet, promise) + if valid { var err error - pendingSet, err = common.Remove(pendingSet, senderID) + pendingSet, err = common.RemoveCoordinate(pendingSet, senderID) if err != nil { continue } - m.archives.receivedPromises = append(m.archives.receivedPromises, promise) + promises = append(promises, promise) + } else { + return m.resolve() } case <-time.After(time.Minute): - // TODO contact TTP - return + return m.resolve() } } + // Now that we received everything, we update the evidence we will give to the ttp + m.updateReceivedPromises(promises) + m.lastValidIndex = m.currentIndex + c := make(chan *cAPI.Promise, chanBufferSize) // Sending of due promises - for _, id := range sendSet { - go func(id uint32, m *SignatureManager) { - promise, err := m.CreatePromise(myID, id) + for _, coord := range sendSet { + go func(coord common.SequenceCoordinate, m *SignatureManager) { + promise, err := m.CreatePromise(m.myID, coord.Signer, uint32(m.currentIndex)) if err == nil { - _ = m.SendEvidence(promise, nil, id) + _ = m.SendEvidence(promise, nil, coord.Signer) } c <- promise - }(id, m) + }(coord, m) } // Verifying we sent all the due promises for range sendSet { <-c } + + return nil } // closeConnections tries to close all established connection with other peers and platform. @@ -149,3 +166,108 @@ func (m *SignatureManager) closeConnections() { } m.cServer.Stop() } + +// updateReceivedPromises : updates the RecievedPromises field of the SignatureManager with the provided promises: +// if we don't yet have a promise from this signer, we add it to the array. +// otherwise we replace the one we have by the provided promise. +func (m *SignatureManager) updateReceivedPromises(promises []*cAPI.Promise) { + for _, p := range promises { + present, index := m.containsPromiseFrom(p.Context.SenderKeyHash, p.Index) + + if present { + // it's present, so there is no index error + _ = m.removeReceivedPromise(index) + } + m.archives.receivedPromises = append(m.archives.receivedPromises, p) + } +} + +// containsPromiseFrom : determines if the SignatureManager has already archived a promise from the specified signer, previous to the specified index. +func (m *SignatureManager) containsPromiseFrom(signer []byte, index uint32) (bool, int) { + for i, p := range m.archives.receivedPromises { + if bytes.Equal(p.Context.SenderKeyHash, signer) { + return p.Index < index, i + } + } + return false, 0 +} + +// removeReceivedPromise : removes the promise at the specified index from the archived received promises. +// If the index is invalid, return an error. +// If the promise is not there, does nothing. +func (m *SignatureManager) removeReceivedPromise(index int) error { + promises := m.archives.receivedPromises + if index < 0 || index >= len(promises) { + return errors.New("Index out of range") + } + + m.archives.receivedPromises = append(promises[:index], promises[index+1:]...) + + return nil +} + +// callForResolve : calls the ttp for resolution. +func (m *SignatureManager) callForResolve() (*tAPI.TTPResponse, error) { + selfPromise, err := m.CreatePromise(m.myID, m.myID, uint32(m.lastValidIndex)) + if err != nil { + return nil, err + } + + toSend := append(m.archives.receivedPromises, selfPromise) + + request := &tAPI.AlertRequest{Promises: toSend} + + ctx, cancel := context.WithTimeout(context.Background(), net.DefaultTimeout) + defer cancel() + response, err := m.ttp.Alert(ctx, request) + if err != nil { + return nil, errors.New(grpc.ErrorDesc(err)) + } + + return response, nil +} + +// resolve : calls for the resolution, and persists the contract if obtained. +func (m *SignatureManager) resolve() error { + if m.ttp == nil { + return errors.New("No connection to TTP, aborting!") + } + + response, err := m.callForResolve() + if err != nil { + return err + } + if response.Abort { + return nil + } + return ioutil.WriteFile(m.mail+"-"+m.contract.UUID+".proof", response.Contract, 0600) +} + +// checkPromise : verifies that the promise is valid wrt the expected promises. +// We assume that the promise data is consistent wrt the platform seal. +func (m *SignatureManager) checkPromise(expected []common.SequenceCoordinate, promise *cAPI.Promise) (bool, uint32) { + // the promise is consistent, but not for the expected signature + // this should not happen + if promise.Context.SignatureUUID != m.uuid { + return false, 0 + } + + // the promise is not for us + recipientID, exist := m.hashToID[fmt.Sprintf("%x", promise.Context.RecipientKeyHash)] + if !exist || recipientID != m.myID { + return false, 0 + } + + // we didn't expect a promise from this client + senderID, exist := m.hashToID[fmt.Sprintf("%x", promise.Context.SenderKeyHash)] + if !exist { + return false, 0 + } + for _, c := range expected { + if c.Signer == senderID && c.Index == promise.Index { + return true, senderID + } + } + + return false, 0 +} diff --git a/dfssc/sign/protocol_test.go b/dfssc/sign/protocol_test.go new file mode 100644 index 0000000..84aa191 --- /dev/null +++ b/dfssc/sign/protocol_test.go @@ -0,0 +1,141 @@ +package sign + +import ( + "testing" + + cAPI "dfss/dfssc/api" + "github.com/stretchr/testify/assert" +) + +func TestContainsPromiseFrom(t *testing.T) { + sender := []byte{0, 0, 7} + + var promises []*cAPI.Promise + promises = append(promises, &cAPI.Promise{ + Context: &cAPI.Context{ + SenderKeyHash: []byte{0, 0, 8}, + }, + Index: uint32(1), + }) + promises = append(promises, &cAPI.Promise{ + Context: &cAPI.Context{ + SenderKeyHash: sender, + }, + Index: uint32(1), + }) + + m := SignatureManager{ + archives: &Archives{ + receivedPromises: promises, + }, + } + + present, index := m.containsPromiseFrom([]byte{0, 0, 6}, 0) + assert.Equal(t, present, false) + assert.Equal(t, index, 0) + + present, index = m.containsPromiseFrom([]byte{0, 0, 7}, 0) + assert.Equal(t, present, false) + assert.Equal(t, index, 1) + + present, index = m.containsPromiseFrom([]byte{0, 0, 7}, 2) + assert.Equal(t, present, true) + assert.Equal(t, index, 1) +} + +func TestRemoveReceivedPromise(t *testing.T) { + sender := []byte{0, 0, 7} + + var promises []*cAPI.Promise + promises = append(promises, &cAPI.Promise{ + Context: &cAPI.Context{ + SenderKeyHash: []byte{0, 0, 8}, + }, + Index: uint32(1), + }) + promises = append(promises, &cAPI.Promise{ + Context: &cAPI.Context{ + SenderKeyHash: sender, + }, + Index: uint32(1), + }) + + m := SignatureManager{ + archives: &Archives{ + receivedPromises: promises, + }, + } + + assert.Equal(t, len(m.archives.receivedPromises), 2) + + err := m.removeReceivedPromise(-1) + assert.Equal(t, err.Error(), "Index out of range") + + err = m.removeReceivedPromise(2) + assert.Equal(t, err.Error(), "Index out of range") + + err = m.removeReceivedPromise(1) + assert.Equal(t, err, nil) + assert.Equal(t, len(m.archives.receivedPromises), 1) + + present, index := m.containsPromiseFrom(sender, 2) + assert.Equal(t, present, false) + assert.Equal(t, index, 0) + present, index = m.containsPromiseFrom([]byte{0, 0, 8}, 2) + assert.Equal(t, present, true) + assert.Equal(t, index, 0) +} + +func TestUpdateRecievedPromises(t *testing.T) { + sender := []byte{0, 0, 7} + + var promises []*cAPI.Promise + promises = append(promises, &cAPI.Promise{ + Context: &cAPI.Context{ + SenderKeyHash: []byte{0, 0, 8}, + }, + Index: uint32(1), + }) + promises = append(promises, &cAPI.Promise{ + Context: &cAPI.Context{ + SenderKeyHash: sender, + }, + Index: uint32(1), + }) + + m := SignatureManager{ + archives: &Archives{ + receivedPromises: promises, + }, + } + + newPromise0 := &cAPI.Promise{ + Context: &cAPI.Context{ + SenderKeyHash: []byte{0, 0, 0}, + }, + Index: uint32(2), + } + + assert.Equal(t, len(m.archives.receivedPromises), 2) + + m.updateReceivedPromises([]*cAPI.Promise{newPromise0}) + assert.Equal(t, len(m.archives.receivedPromises), 3) + + present, index := m.containsPromiseFrom([]byte{0, 0, 0}, 3) + assert.Equal(t, present, true) + assert.Equal(t, index, 2) + + newPromise1 := &cAPI.Promise{ + Context: &cAPI.Context{ + SenderKeyHash: sender, + }, + Index: uint32(2), + } + + m.updateReceivedPromises([]*cAPI.Promise{newPromise1}) + assert.Equal(t, len(m.archives.receivedPromises), 3) + + present, index = m.containsPromiseFrom(sender, 3) + assert.Equal(t, present, true) + assert.Equal(t, index, 2) +} diff --git a/dfssc/sign/server.go b/dfssc/sign/server.go index 883fce2..a718dea 100644 --- a/dfssc/sign/server.go +++ b/dfssc/sign/server.go @@ -4,6 +4,7 @@ import ( "dfss" cAPI "dfss/dfssc/api" pAPI "dfss/dfssp/api" + "dfss/dfsst/entities" "dfss/net" "golang.org/x/net/context" "google.golang.org/grpc" @@ -26,6 +27,12 @@ func getServerErrorCode(c chan interface{}, in interface{}) *pAPI.ErrorCode { // // Handle incoming TreatPromise messages func (s *clientServer) TreatPromise(ctx context.Context, in *cAPI.Promise) (*pAPI.ErrorCode, error) { + // we check that the incoming promise is valid (ie. no data inconsistency) + // we do not check that we expected that promise + valid, _, _, _ := entities.IsRequestValid(ctx, []*cAPI.Promise{in}) + if !valid { + return &pAPI.ErrorCode{Code: pAPI.ErrorCode_SUCCESS}, nil + } return getServerErrorCode(s.incomingPromises, in), nil } diff --git a/dfssc/sign/starter.go b/dfssc/sign/starter.go index dded16c..49b2102 100644 --- a/dfssc/sign/starter.go +++ b/dfssc/sign/starter.go @@ -1,6 +1,8 @@ package sign import ( + "crypto/rsa" + "crypto/x509" "errors" "fmt" "strconv" @@ -14,6 +16,8 @@ import ( dAPI "dfss/dfssd/api" pAPI "dfss/dfssp/api" "dfss/dfssp/contract" + tAPI "dfss/dfsst/api" + "dfss/dfsst/entities" "dfss/net" "github.com/spf13/viper" "golang.org/x/net/context" @@ -25,25 +29,28 @@ const chanBufferSize = 100 // SignatureManager handles the signature of a contract. type SignatureManager struct { - auth *security.AuthContainer - contract *contract.JSON // contains the contractUUID, the list of the signers' hashes, the hash of the contract - platform pAPI.PlatformClient - platformConn *grpc.ClientConn - peersConn map[string]*grpc.ClientConn - peers map[string]*cAPI.ClientClient - hashToID map[string]uint32 - nbReady int - cServer *grpc.Server - cServerIface clientServer - sequence []uint32 - currentIndex int - uuid string - keyHash [][]byte - mail string - archives *Archives - seal []byte - cancelled bool - finished bool + auth *security.AuthContainer + contract *contract.JSON // contains the contractUUID, the list of the signers' hashes, the hash of the contract + platform pAPI.PlatformClient + platformConn *grpc.ClientConn + ttp tAPI.TTPClient + peersConn map[string]*grpc.ClientConn + peers map[string]*cAPI.ClientClient + hashToID map[string]uint32 + nbReady int + cServer *grpc.Server + cServerIface clientServer + sequence []uint32 + lastValidIndex int // the last index at which we sent a promise + currentIndex int + myID uint32 + uuid string + keyHash [][]byte + mail string + archives *Archives + seal []byte + cancelled bool + finished bool // Callbacks OnSignerStatusUpdate func(mail string, status SignerStatus, data string) @@ -53,8 +60,7 @@ type SignatureManager struct { // Archives stores the received and sent messages, as evidence if needed type Archives struct { - sentPromises []*cAPI.Promise - receivedPromises []*cAPI.Promise + receivedPromises []*cAPI.Promise // TODO: improve by using a map sentSignatures []*cAPI.Signature receivedSignatures []*cAPI.Signature mutex sync.Mutex @@ -66,7 +72,6 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager auth: security.NewAuthContainer(passphrase), contract: c, archives: &Archives{ - sentPromises: make([]*cAPI.Promise, 0), receivedPromises: make([]*cAPI.Promise, 0), sentSignatures: make([]*cAPI.Signature, 0), receivedSignatures: make([]*cAPI.Signature, 0), @@ -85,13 +90,19 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager m.cServer = m.GetServer() go func() { _ = net.Listen("0.0.0.0:"+strconv.Itoa(viper.GetInt("local_port")), m.cServer) }() - conn, err := net.Connect(viper.GetString("platform_addrport"), m.auth.Cert, m.auth.Key, m.auth.CA, nil) + connp, err := net.Connect(viper.GetString("platform_addrport"), m.auth.Cert, m.auth.Key, m.auth.CA, nil) if err != nil { return nil, err } - m.platform = pAPI.NewPlatformClient(conn) - m.platformConn = conn + m.platform = pAPI.NewPlatformClient(connp) + m.platformConn = connp + + // connect to the ttp + connt, err := m.connectToTTP(m.auth.Cert, m.auth.Key, m.auth.CA) + if err == nil { + m.ttp = tAPI.NewTTPClient(connt) + } m.peersConn = make(map[string]*grpc.ClientConn) m.peers = make(map[string]*cAPI.ClientClient) @@ -101,9 +112,24 @@ func NewSignatureManager(passphrase string, c *contract.JSON) (*SignatureManager } } + // Initialize TTP AuthContainer + // This is needed to use platform seal verification client-side + entities.AuthContainer = m.auth + return m, nil } +// connectToTTP : tries to open a connection with the ttp specified in the contract. +// If there was no specified ttp, returns an error. +// Otherwise, returns the connection, or an error if something else occured. +func (m *SignatureManager) connectToTTP(cert *x509.Certificate, key *rsa.PrivateKey, ca *x509.Certificate) (*grpc.ClientConn, error) { + if m.contract.TTP == nil { + return nil, errors.New("No specified ttp in contract") + } + addrPort := m.contract.TTP.IP + ":" + string(m.contract.TTP.Port) + return net.Connect(addrPort, cert, key, ca, nil) +} + // ConnectToPeers tries to fetch the list of users for this contract, and tries to establish a connection to each peer. func (m *SignatureManager) ConnectToPeers() error { localIps, err := net.ExternalInterfaceAddr() @@ -282,23 +308,26 @@ func (m *SignatureManager) SendReadySign() (signatureUUID string, err error) { } // Initialize computes the values needed for the start of the signing -func (m *SignatureManager) Initialize() (uint32, int, error) { +func (m *SignatureManager) Initialize() (int, error) { myID, err := m.FindID() if err != nil { - return 0, 0, err + return 0, err } + m.myID = myID m.currentIndex, err = common.FindNextIndex(m.sequence, myID, -1) if err != nil { - return 0, 0, err + return 0, err } nextIndex, err := common.FindNextIndex(m.sequence, myID, m.currentIndex) if err != nil { - return 0, 0, err + return 0, err } - return myID, nextIndex, nil + m.lastValidIndex = 0 + + return nextIndex, nil } // FindID finds the sequence id for the user's email and the contract to sign diff --git a/dfsst/api/resolution.pb.go b/dfsst/api/resolution.pb.go index f3b76e6..9bbafa9 100644 --- a/dfsst/api/resolution.pb.go +++ b/dfsst/api/resolution.pb.go @@ -37,6 +37,8 @@ const _ = proto.ProtoPackageIsVersion1 type AlertRequest struct { // / Promises obtained at this point of the main protocol Promises []*api2.Promise `protobuf:"bytes,1,rep,name=promises" json:"promises,omitempty"` + // / Index at which the alert token was sent + Index uint32 `protobuf:"varint,2,opt,name=index" json:"index,omitempty"` } func (m *AlertRequest) Reset() { *m = AlertRequest{} } @@ -190,20 +192,22 @@ var _TTP_serviceDesc = grpc.ServiceDesc{ } var fileDescriptor0 = []byte{ - // 231 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x8f, 0xcd, 0x4a, 0xc3, 0x40, - 0x10, 0xc7, 0xad, 0xa1, 0x1a, 0xa7, 0xa9, 0xd4, 0x11, 0xa1, 0x54, 0x50, 0xc9, 0xc5, 0x9e, 0x36, - 0x10, 0x9f, 0x40, 0xf0, 0xe2, 0xad, 0x84, 0xf6, 0x01, 0xd2, 0x75, 0x94, 0x85, 0xb8, 0x13, 0x77, - 0x26, 0x3e, 0xbf, 0xeb, 0x56, 0xa5, 0x42, 0x2f, 0x7b, 0xf8, 0x7f, 0xed, 0x6f, 0xe0, 0xf6, 0xe5, - 0x55, 0xa4, 0xfa, 0x7e, 0xb4, 0x6a, 0x7b, 0x57, 0x05, 0x12, 0xee, 0x06, 0x75, 0xec, 0x4d, 0x1f, - 0x58, 0x19, 0xb3, 0xa8, 0x2e, 0xae, 0xff, 0x52, 0x36, 0xa5, 0x6c, 0xe7, 0xc8, 0xeb, 0x2e, 0x51, - 0x1a, 0x28, 0x1e, 0x3b, 0x0a, 0xda, 0xd0, 0xc7, 0x40, 0xa2, 0x78, 0x03, 0x79, 0x34, 0xde, 0x9d, - 0x90, 0xcc, 0x47, 0x77, 0xd9, 0x72, 0x52, 0x17, 0x26, 0x96, 0xcc, 0x6a, 0x27, 0x96, 0xf7, 0x70, - 0xde, 0x90, 0xe5, 0x4f, 0x0a, 0xbf, 0x8d, 0x2b, 0x98, 0x8a, 0x7b, 0xf3, 0xad, 0x0e, 0x81, 0x36, - 0x9b, 0xe7, 0xa7, 0x58, 0x1b, 0x2d, 0xcf, 0xe2, 0xf0, 0x64, 0xbd, 0x5e, 0x35, 0x24, 0x3d, 0x7b, - 0x21, 0x9c, 0xc2, 0xb8, 0xdd, 0x72, 0xd0, 0xe4, 0xe6, 0x38, 0x83, 0xdc, 0xb2, 0xd7, 0xd0, 0x5a, - 0x9d, 0x1f, 0x47, 0xa5, 0xa8, 0x1d, 0x64, 0x31, 0x8f, 0x06, 0xc6, 0x89, 0x07, 0x2f, 0xd2, 0xb7, - 0xfb, 0x6c, 0x8b, 0x59, 0x92, 0xf6, 0x56, 0xcb, 0x23, 0xac, 0xe1, 0xf4, 0x87, 0x07, 0x2f, 0x93, - 0xfd, 0x9f, 0xee, 0x50, 0x67, 0x7b, 0x92, 0x4e, 0x7f, 0xf8, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x38, - 0x4a, 0x1c, 0x12, 0x3f, 0x01, 0x00, 0x00, + // 257 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x50, 0x4d, 0x4b, 0xc3, 0x40, + 0x10, 0x35, 0x86, 0x6a, 0x9c, 0xa6, 0xa2, 0xab, 0x87, 0x12, 0x0f, 0x96, 0xe0, 0x21, 0xa7, 0x04, + 0x22, 0x78, 0x15, 0xc1, 0x8b, 0x17, 0x29, 0x4b, 0xfb, 0x03, 0xd2, 0xed, 0x28, 0x0b, 0x71, 0x37, + 0xee, 0x4c, 0xc4, 0x9f, 0xef, 0x76, 0xa3, 0xa1, 0x05, 0x2f, 0x0b, 0xef, 0x63, 0xde, 0xbc, 0x59, + 0xb8, 0xdd, 0xbe, 0x11, 0x55, 0xbb, 0x87, 0xab, 0xa6, 0xd3, 0x95, 0x43, 0xb2, 0x6d, 0xcf, 0xda, + 0x9a, 0xb2, 0x73, 0x96, 0xad, 0x88, 0x3d, 0x9b, 0xdd, 0x8c, 0x2e, 0x15, 0x5c, 0xaa, 0xd5, 0x68, + 0x78, 0x70, 0xe4, 0xaf, 0x90, 0x3e, 0xb5, 0xe8, 0x58, 0xe2, 0x67, 0x8f, 0xc4, 0xa2, 0x80, 0xc4, + 0x0b, 0x1f, 0x9a, 0x90, 0xe6, 0xd1, 0x22, 0x2e, 0xa6, 0x75, 0x5a, 0xfa, 0xa1, 0x72, 0x39, 0x90, + 0x72, 0x54, 0xc5, 0x35, 0x4c, 0xb4, 0xd9, 0xe2, 0xf7, 0xfc, 0x78, 0x11, 0x15, 0x33, 0x39, 0x80, + 0xfc, 0x01, 0xce, 0x25, 0x2a, 0xfb, 0x85, 0xee, 0x2f, 0xf1, 0x0e, 0x66, 0xa4, 0xdf, 0x4d, 0xc3, + 0xbd, 0xc3, 0xf5, 0xfa, 0xe5, 0xd9, 0xc7, 0x46, 0xc5, 0x99, 0x3c, 0x24, 0xf3, 0x47, 0x98, 0xae, + 0x56, 0x4b, 0x89, 0xd4, 0x59, 0x43, 0xb8, 0x0b, 0x6f, 0x36, 0xd6, 0x71, 0x30, 0x27, 0x72, 0x00, + 0x22, 0x83, 0x44, 0x59, 0xc3, 0xae, 0x51, 0x1c, 0xb6, 0xa6, 0x72, 0xc4, 0xb5, 0x86, 0xd8, 0x07, + 0x88, 0x12, 0x26, 0xe1, 0x1e, 0x71, 0x19, 0x6a, 0xef, 0xdf, 0x96, 0x5d, 0x04, 0x6a, 0x6f, 0x4d, + 0x7e, 0x24, 0x6a, 0x38, 0xfd, 0xed, 0x2b, 0xae, 0x82, 0x7c, 0xd8, 0xfe, 0xbf, 0x99, 0xcd, 0x49, + 0xf8, 0xba, 0xfb, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x69, 0x9d, 0x41, 0xb0, 0x7f, 0x01, 0x00, + 0x00, } diff --git a/dfsst/api/resolution.proto b/dfsst/api/resolution.proto index ea7c63f..fd8ebf5 100644 --- a/dfsst/api/resolution.proto +++ b/dfsst/api/resolution.proto @@ -17,6 +17,8 @@ service TTP { message AlertRequest { /// Promises obtained at this point of the main protocol repeated Promise promises = 1; + /// Index at which the alert token was sent + uint32 index = 2; } message RecoverRequest { diff --git a/dfsst/entities/archivesManager.go b/dfsst/entities/archivesManager.go index a26b6a0..8ed6c61 100644 --- a/dfsst/entities/archivesManager.go +++ b/dfsst/entities/archivesManager.go @@ -112,9 +112,10 @@ func (manager *ArchivesManager) AddToDishonest(signerIndex uint32) { } // AddPromise : adds the specified promises to the list of received promises of the SignatureArchives. +// If we have already a promise from this client, does not add it. func (manager *ArchivesManager) AddPromise(promise *Promise) { for _, p := range manager.Archives.ReceivedPromises { - if (&p).Equal(promise) { + if p.SenderKeyIndex == promise.SenderKeyIndex { return } } diff --git a/dfsst/entities/check_promise.go b/dfsst/entities/check_promise.go index 0736fc4..33b5db3 100644 --- a/dfsst/entities/check_promise.go +++ b/dfsst/entities/check_promise.go @@ -1,6 +1,7 @@ package entities import ( + "bytes" "errors" cAPI "dfss/dfssc/api" @@ -26,11 +27,6 @@ func ArePromisesValid(promises []*cAPI.Promise) (bool, []*Promise) { // the index of the promise coresponds to an expected message from the sender in the signed sequence // If true, returns a new promise entity func IsPromiseValid(promise *cAPI.Promise) (bool, *Promise) { - valid := IsPromiseFromAtoB(promise) - if !valid { - return false, &Promise{} - } - // This checks if the index of the specified promise corresponds to an expected promise from the sender hash of the promise sender, recipient, index, err := GetPromiseProfile(promise) if err != nil { @@ -43,10 +39,14 @@ func IsPromiseValid(promise *cAPI.Promise) (bool, *Promise) { } // IsPromiseFromAtoB : determines if the specified promise, supposedly from 'A' to 'B' was indeed created by 'A' for 'B'. -func IsPromiseFromAtoB(promise *cAPI.Promise) bool { - // TODO - // This requires the implementation of promises - return true +func IsPromiseFromAtoB(promise *cAPI.Promise, from, to []byte, at uint32) bool { + if !(bytes.Equal(promise.Context.SenderKeyHash, from)) { + return false + } + if !(bytes.Equal(promise.Context.RecipientKeyHash, to)) { + return false + } + return promise.Index == at } // GetPromiseProfile : retrieves the indexes of the recipient and sender in the array of signers' hashes, and the index of the promise. diff --git a/dfsst/entities/check_promise_test.go b/dfsst/entities/check_promise_test.go index 31047ae..0712747 100644 --- a/dfsst/entities/check_promise_test.go +++ b/dfsst/entities/check_promise_test.go @@ -83,14 +83,18 @@ func TestIsPromiseValid(t *testing.T) { func TestIsPromiseFromAtoB(t *testing.T) { // TODO // This requires the implementation of promises + sender := []byte{1} + recipient := []byte{2} + index := uint32(5) promise := &cAPI.Promise{ Context: &cAPI.Context{ - RecipientKeyHash: []byte{}, - SenderKeyHash: []byte{}, + RecipientKeyHash: recipient, + SenderKeyHash: sender, Signers: signers, }, + Index: index, } - ok := IsPromiseFromAtoB(promise) + ok := IsPromiseFromAtoB(promise, sender, recipient, index) assert.Equal(t, ok, true) } diff --git a/dfsst/entities/check_request.go b/dfsst/entities/check_request.go index 8e43974..dc1564d 100644 --- a/dfsst/entities/check_request.go +++ b/dfsst/entities/check_request.go @@ -8,7 +8,6 @@ import ( "dfss/auth" cAPI "dfss/dfssc/api" pAPI "dfss/dfssp/api" - tAPI "dfss/dfsst/api" "dfss/net" "golang.org/x/net/context" "gopkg.in/mgo.v2/bson" @@ -17,14 +16,14 @@ import ( // IsRequestValid : determines if there are no errors in the received request. // ie: the information signed by the platform in the received promises is valid and consistent // the sender of the request is present amongst the signed signers of the promises -func IsRequestValid(ctx context.Context, request *tAPI.AlertRequest) (valid bool, signatureUUID bson.ObjectId, signers []Signer, senderIndex uint32) { +func IsRequestValid(ctx context.Context, promises []*cAPI.Promise) (valid bool, signatureUUID bson.ObjectId, signers []Signer, senderIndex uint32) { // Due to specifications, there should be at least one promise (from the sender to himself) - if len(request.Promises) == 0 { + if len(promises) == 0 { valid = false return } - ok, expectedUUID, signers := IsPromiseSignedByPlatform(request.Promises[0]) + ok, expectedUUID, signers := IsPromiseSignedByPlatform(promises[0]) if !ok { valid = false return @@ -36,7 +35,7 @@ func IsRequestValid(ctx context.Context, request *tAPI.AlertRequest) (valid bool return } - senderIndex, err = GetIndexOfSigner(request.Promises[0], sender) + senderIndex, err = GetIndexOfSigner(promises[0], sender) if err != nil { valid = false return @@ -45,7 +44,7 @@ func IsRequestValid(ctx context.Context, request *tAPI.AlertRequest) (valid bool // To check that all the promises contain the same signed information, we only need to check that: // - it is correctly signed // - promises are consistent wrt at least one signed field - for _, promise := range request.Promises { + for _, promise := range promises { ok, receivedUUID, _ := IsPromiseSignedByPlatform(promise) if !ok || (expectedUUID != receivedUUID) { valid = false diff --git a/dfsst/entities/signatureArchives.go b/dfsst/entities/signatureArchives.go index 80accc3..ce3f1af 100644 --- a/dfsst/entities/signatureArchives.go +++ b/dfsst/entities/signatureArchives.go @@ -29,7 +29,7 @@ type SignatureArchives struct { TextHash []byte `key:"textHash" bson:"textHash"` // Small hash of the contract Seal []byte `key:"seal" bson:"seal"` // Seal provided by the platform to authentify the context - ReceivedPromises []Promise `key:"receivedPromises" bson:"receivedPromises"` // All valid received promises + ReceivedPromises []Promise `key:"receivedPromises" bson:"receivedPromises"` // Set of valid received promises (1 by sender) AbortedSigners []AbortedSigner `key:"abortedSigners" bson:"abortedSigners"` // Signers that were sent an abort token DishonestSigners []uint32 `key:"dishonestSigners" bson:"dishonestSigners"` // Indexes of the signers that were evaluated as dishonest @@ -59,9 +59,9 @@ type Promise struct { ID bson.ObjectId `key:"_id" bson:"_id"` // Internal id of a Promise RecipientKeyIndex uint32 `key:"recipientKeyIndex" bson:"recipientKeyIndex"` // Index of the hash of the recipient's certificate in - // the `Signers` field of the enclosing SignatureArchives + // the `Signers` field of the enclosing SignatureArchives (identical to the one in the signers hashes array of the incoming promises) SenderKeyIndex uint32 `key:"senderKeyIndex" bson:"senderKeyIndex"` // Index of the hash of the sender's certificate in - // the `Signers` field of the enclosing SignatureArchives + // the `Signers` field of the enclosing SignatureArchives (identical to the one in the signers hashes array of the incoming promises) SequenceIndex uint32 `key:"sequenceIndex" bson:"sequenceIndex"` // Sequence index of the promise } diff --git a/dfsst/resolve/resolve.go b/dfsst/resolve/resolve.go index 842c41a..5362232 100644 --- a/dfsst/resolve/resolve.go +++ b/dfsst/resolve/resolve.go @@ -2,19 +2,143 @@ package resolve import ( + "errors" + cAPI "dfss/dfssc/api" + "dfss/dfssc/common" "dfss/dfsst/entities" ) // ArePromisesComplete : determines if the set of promises present in the AlertRequest is EQUAL (not just included) to the one expected from the TTP // for this signer, at this particular step of the signing sequence. -func ArePromisesComplete(promiseEntities []*entities.Promise, promise *cAPI.Promise) bool { - // TODO - // This requires to dig into the mathematical specifications, to determine the set of promises - // expected to be persent in the AlertRequest from this signer at this position +// The provided step is supposed valid (ie. in bound of the sequence) (see call to 'IsRequestValid' in 'Alert' route) +func ArePromisesComplete(promiseEntities []*entities.Promise, promise *cAPI.Promise, step uint32) bool { + expected, err := generateExpectedPromises(promise, step) + if err != nil { + return false + } + + if len(promiseEntities) != len(expected) { + return false + } + + for _, p := range expected { + if !containsPromise(promiseEntities, p) { + return false + } + } + return true } +// generateExpectedPromises : computes the list of expected promises for the recipient of the specified promise, at the specified sequence index +func generateExpectedPromises(promise *cAPI.Promise, step uint32) ([]*entities.Promise, error) { + var res []*entities.Promise + + seq := promise.Context.Sequence + recipientH := promise.Context.RecipientKeyHash + recipientID, err := entities.GetIndexOfSigner(promise, recipientH) + if err != nil { + return nil, err + } + + if seq[int(step)] != recipientID { + return nil, errors.New("Signer at step is not recipient") + } + + currentIndex, err := common.FindNextIndex(seq, recipientID, -1) + if err != nil { + return nil, err + } + + for currentIndex <= int(step) { + roundPromises, err := generationRound(seq, recipientID, currentIndex) + if err != nil { + return nil, err + } + + for _, p := range roundPromises { + res = addPromiseToExpected(res, p) + } + + currentIndex, _ = common.FindNextIndex(seq, recipientID, currentIndex) + + // if it was the last occurence, then we finish + if currentIndex == -1 { + break + } + } + + selfPromise := &entities.Promise{ + RecipientKeyIndex: recipientID, + SenderKeyIndex: recipientID, + SequenceIndex: step, + } + + return append(res, selfPromise), nil +} + +// generationRound : generates the list of promises expected bu seqID at index of seq +func generationRound(seq []uint32, seqID uint32, index int) ([]*entities.Promise, error) { + var res []*entities.Promise + + pendingSet, err := common.GetPendingSet(seq, seqID, index) + if err != nil { + return nil, err + } + + for _, c := range pendingSet { + p := &entities.Promise{ + RecipientKeyIndex: seqID, + SenderKeyIndex: c.Signer, + SequenceIndex: c.Index, + } + res = append(res, p) + } + + return res, nil +} + +// addPromiseToExpected : adds the specified promise to the list. +// If a previous promise from this sender to this recipient is in the list, replaces it with the new one. +// Otherwise, appends the promise to the list. +func addPromiseToExpected(expected []*entities.Promise, promise *entities.Promise) []*entities.Promise { + if containsPromise(expected, promise) { + return expected + } + + i := containsPreviousPromise(expected, promise) + if i != -1 { + expected[i] = promise + } else { + expected = append(expected, promise) + } + + return expected +} + +// containsPreviousPromise : determines if the specified promise has a previous occurence in the list (ie. same sender and recipient, but lower index). +// Returns the index at which the previous occurence is in the provided list. +// If there is no previous occurence, returns -1 +func containsPreviousPromise(expected []*entities.Promise, promise *entities.Promise) int { + for i, p := range expected { + if p.SenderKeyIndex == promise.SenderKeyIndex && p.RecipientKeyIndex == promise.RecipientKeyIndex && p.SequenceIndex < promise.SequenceIndex { + return i + } + } + return -1 +} + +// containsPromise : determines if the specified promise is present in the specified list. +func containsPromise(promises []*entities.Promise, promise *entities.Promise) bool { + for _, p := range promises { + if p.Equal(promise) { + return true + } + } + return false +} + // Solve : tries to generate the signed contract from present evidence. func Solve(manager *entities.ArchivesManager) (bool, []byte) { // Test if we can generate the contract @@ -33,8 +157,14 @@ func Solve(manager *entities.ArchivesManager) (bool, []byte) { // // XXX : Implementation needs cryptographic promises func GenerateSignedContract(archives *entities.SignatureArchives) []byte { - // TODO - return []byte{0, 0, 7} + var pseudoContract string + for _, p := range archives.ReceivedPromises { + signature := "SIGNATURE FROM SIGNER " + string(archives.Signers[p.SenderKeyIndex].Hash) + signature += " ON SIGNATURE n° " + string(archives.ID) + "\n" + pseudoContract += signature + } + + return []byte(pseudoContract) } // ComputeDishonestSigners : computes the dishonest signers from the provided evidence. diff --git a/dfsst/resolve/resolve_test.go b/dfsst/resolve/resolve_test.go index 4ad4d95..7ee01d6 100644 --- a/dfsst/resolve/resolve_test.go +++ b/dfsst/resolve/resolve_test.go @@ -6,6 +6,7 @@ import ( "testing" "crypto/sha512" + cAPI "dfss/dfssc/api" "dfss/dfsst/entities" "dfss/mgdb" "github.com/stretchr/testify/assert" @@ -73,8 +74,304 @@ func TestMain(m *testing.M) { } func TestArePromisesComplete(t *testing.T) { - // TODO - // This requires the implementation of the call to the ttp + var promises []*entities.Promise + promise := &cAPI.Promise{ + Context: &cAPI.Context{ + RecipientKeyHash: []byte{}, + Sequence: sequence, + Signers: signers, + }, + } + + complete := ArePromisesComplete(promises, promise, 2) + assert.False(t, complete) + + promise.Context.RecipientKeyHash = signers[2] + complete = ArePromisesComplete(promises, promise, 2) + assert.False(t, complete) + + for i := 1; i > -1; i-- { + p := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: sequence[i], + SequenceIndex: uint32(i), + } + promises = append(promises, p) + } + + complete = ArePromisesComplete(promises, promise, 2) + assert.False(t, complete) + + selfPromise := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 2, + SequenceIndex: 2, + } + promises = append(promises, selfPromise) + + complete = ArePromisesComplete(promises, promise, 2) + assert.True(t, complete) + + promises = []*entities.Promise{} + for i := 7; i > 5; i-- { + p := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: sequence[i], + SequenceIndex: uint32(i), + } + promises = append(promises, p) + } + + complete = ArePromisesComplete(promises, promise, 8) + assert.False(t, complete) + + selfPromise = &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 2, + SequenceIndex: 8, + } + promises = append(promises, selfPromise) + complete = ArePromisesComplete(promises, promise, 8) + assert.True(t, complete) +} + +func TestGenerateExpectedPromises(t *testing.T) { + var expected []*entities.Promise + promise := &cAPI.Promise{ + Context: &cAPI.Context{ + RecipientKeyHash: []byte{}, + Sequence: sequence, + Signers: signers, + }, + } + + promises, err := generateExpectedPromises(promise, 1) + assert.Equal(t, err.Error(), "Signer's hash couldn't be matched") + assert.Nil(t, promises) + + promise.Context.RecipientKeyHash = signers[1] + promises, err = generateExpectedPromises(promise, 1) + assert.Nil(t, err) + assert.Equal(t, len(promises), 2) + assert.True(t, promises[0].Equal(&entities.Promise{ + RecipientKeyIndex: 1, + SenderKeyIndex: 0, + SequenceIndex: 0, + })) + assert.True(t, promises[1].Equal(&entities.Promise{ + RecipientKeyIndex: 1, + SenderKeyIndex: 1, + SequenceIndex: 1, + })) + + promise.Context.RecipientKeyHash = signers[2] + promises, err = generateExpectedPromises(promise, 1) + assert.Equal(t, err.Error(), "Signer at step is not recipient") + assert.Nil(t, promises) + + promises, err = generateExpectedPromises(promise, 2) + assert.Nil(t, err) + + for i := 1; i > -1; i-- { + p := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: sequence[i], + SequenceIndex: uint32(i), + } + expected = append(expected, p) + } + selfPromise := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 2, + SequenceIndex: 2, + } + expected = append(expected, selfPromise) + assert.Equal(t, len(promises), 3) + assert.Equal(t, len(promises), len(expected)) + for i := 0; i < len(promises); i++ { + assert.True(t, expected[i].Equal(promises[i])) + } + + expected = []*entities.Promise{} + promises, err = generateExpectedPromises(promise, 8) + assert.Nil(t, err) + + for i := 7; i > 5; i-- { + p := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: sequence[i], + SequenceIndex: uint32(i), + } + expected = append(expected, p) + } + selfPromise = &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 2, + SequenceIndex: 8, + } + expected = append(expected, selfPromise) + assert.Equal(t, len(promises), 3) + assert.Equal(t, len(promises), len(expected)) + for i := 0; i < len(promises); i++ { + assert.True(t, expected[i].Equal(promises[i])) + } +} + +func TestGenerationRound(t *testing.T) { + var expected []*entities.Promise + + roundPromises, err := generationRound(sequence, 2, -1) + assert.Equal(t, err.Error(), "Index out of range") + assert.Equal(t, len(roundPromises), 0) + + roundPromises, err = generationRound(sequence, 2, 2) + for i := 1; i > -1; i-- { + p := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: sequence[i], + SequenceIndex: uint32(i), + } + expected = append(expected, p) + } + + assert.Equal(t, err, nil) + assert.Equal(t, len(roundPromises), 2) + for i := 0; i < 2; i++ { + assert.True(t, expected[i].Equal(roundPromises[i])) + } + + expected = []*entities.Promise{} + roundPromises, err = generationRound(sequence, 2, 5) + for i := 4; i > 2; i-- { + p := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: sequence[i], + SequenceIndex: uint32(i), + } + expected = append(expected, p) + } + + assert.Equal(t, err, nil) + assert.Equal(t, len(roundPromises), 2) + for i := 0; i < 2; i++ { + assert.True(t, expected[i].Equal(roundPromises[i])) + } + + expected = []*entities.Promise{} + roundPromises, err = generationRound(sequence, 2, 8) + for i := 7; i > 5; i-- { + p := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: sequence[i], + SequenceIndex: uint32(i), + } + expected = append(expected, p) + } + + assert.Equal(t, err, nil) + assert.Equal(t, len(roundPromises), 2) + for i := 0; i < 2; i++ { + assert.True(t, expected[i].Equal(roundPromises[i])) + } + + roundPromises, err = generationRound(sequence, 2, 9) + assert.Equal(t, err.Error(), "Index out of range") + assert.Equal(t, len(roundPromises), 0) +} + +func TestAddPromiseToExpected(t *testing.T) { + promise0 := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 0, + SequenceIndex: 0, + } + + promise1 := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 1, + SequenceIndex: 1, + } + + promise2 := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 1, + SequenceIndex: 2, + } + + expected := []*entities.Promise{} + + assert.Equal(t, len(expected), 0) + + expected = addPromiseToExpected(expected, promise0) + assert.Equal(t, len(expected), 1) + assert.True(t, promise0.Equal(expected[0])) + + expected = addPromiseToExpected(expected, promise0) + assert.Equal(t, len(expected), 1) + assert.True(t, promise0.Equal(expected[0])) + + expected = addPromiseToExpected(expected, promise1) + assert.Equal(t, len(expected), 2) + assert.True(t, promise0.Equal(expected[0])) + assert.True(t, promise1.Equal(expected[1])) + + expected = addPromiseToExpected(expected, promise2) + assert.Equal(t, len(expected), 2) + assert.True(t, promise0.Equal(expected[0])) + assert.True(t, promise2.Equal(expected[1])) +} + +func TestContainsPreviousPromise(t *testing.T) { + promise0 := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 0, + SequenceIndex: 0, + } + + promise1 := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 1, + SequenceIndex: 1, + } + + promise2 := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 1, + SequenceIndex: 2, + } + + promises := []*entities.Promise{} + + assert.Equal(t, containsPreviousPromise(promises, promise1), -1) + + promises = append(promises, promise0) + assert.Equal(t, containsPreviousPromise(promises, promise1), -1) + + promises = append(promises, promise1) + assert.Equal(t, containsPreviousPromise(promises, promise1), -1) + assert.Equal(t, containsPreviousPromise(promises, promise2), 1) + + promises = append(promises, promise2) + assert.Equal(t, containsPreviousPromise(promises, promise2), 1) +} + +func TestContainsPromise(t *testing.T) { + promise0 := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 0, + SequenceIndex: 0, + } + + promise1 := &entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 1, + SequenceIndex: 1, + } + + promises := []*entities.Promise{promise0} + + assert.False(t, containsPromise(promises, promise1)) + assert.True(t, containsPromise(promises, promise0)) } func TestSolve(t *testing.T) { @@ -115,14 +412,47 @@ func TestSolve(t *testing.T) { ok, contract = Solve(manager) assert.Equal(t, ok, true) if len(contract) == 0 { - t.Fatal("Contract should have need generated") + t.Fatal("Contract should have beed generated") } } // TO MODIFY WHEN SOURCE FUNCTION WILL BE UPDATED func TestGenerateSignedContract(t *testing.T) { // TODO - assert.Equal(t, true, true) + id := bson.NewObjectId() + var promises []entities.Promise + for i := 1; i > -1; i-- { + p := entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: sequence[i], + SequenceIndex: uint32(i), + } + promises = append(promises, p) + } + selfPromise := entities.Promise{ + RecipientKeyIndex: 2, + SenderKeyIndex: 2, + SequenceIndex: 2, + } + promises = append(promises, selfPromise) + + archives := &entities.SignatureArchives{ + ID: id, + Signers: signersEntities, + ReceivedPromises: promises, + } + + contract := GenerateSignedContract(archives) + + var pseudoContract string + for _, p := range promises { + signature := "SIGNATURE FROM SIGNER " + string(signersEntities[p.SenderKeyIndex].Hash) + signature += " ON SIGNATURE n° " + string(id) + "\n" + pseudoContract += signature + } + + expected := []byte(pseudoContract) + assert.Equal(t, contract, expected) } func TestComputeDishonestSigners(t *testing.T) { diff --git a/dfsst/server/server.go b/dfsst/server/server.go index 77baab3..7fee605 100644 --- a/dfsst/server/server.go +++ b/dfsst/server/server.go @@ -27,7 +27,11 @@ type ttpServer struct { // Alert route for the TTP. func (server *ttpServer) Alert(ctx context.Context, in *tAPI.AlertRequest) (*tAPI.TTPResponse, error) { - valid, signatureUUID, signers, senderIndex := entities.IsRequestValid(ctx, in) + valid, signatureUUID, signers, senderIndex := entities.IsRequestValid(ctx, in.Promises) + if !valid { + return nil, errors.New(InternalError) + } + valid = int(in.Index) >= len(in.Promises[0].Context.Sequence) if !valid { return nil, errors.New(InternalError) } @@ -46,7 +50,7 @@ func (server *ttpServer) Alert(ctx context.Context, in *tAPI.AlertRequest) (*tAP } // We check that the sender of the request sent valid and complete information - stop, message, tmpPromises, err := server.handleInvalidPromises(manager, in.Promises, senderIndex) + stop, message, tmpPromises, err := server.handleInvalidPromises(manager, in.Promises, senderIndex, in.Index) if stop { return message, err } @@ -109,9 +113,9 @@ func (server *ttpServer) handleAbortedSender(manager *entities.ArchivesManager, // // Updates the database with the new aborted signer. // If an error occurs during this process, it is returned. -func (server *ttpServer) handleInvalidPromises(manager *entities.ArchivesManager, promises []*cAPI.Promise, senderIndex uint32) (bool, *tAPI.TTPResponse, []*entities.Promise, error) { +func (server *ttpServer) handleInvalidPromises(manager *entities.ArchivesManager, promises []*cAPI.Promise, senderIndex, stepIndex uint32) (bool, *tAPI.TTPResponse, []*entities.Promise, error) { valid, tmpPromises := entities.ArePromisesValid(promises) - complete := resolve.ArePromisesComplete(tmpPromises, promises[0]) + complete := resolve.ArePromisesComplete(tmpPromises, promises[0], stepIndex) if !valid || !complete { manager.AddToAbort(senderIndex) manager.AddToDishonest(senderIndex) -- GitLab