diff --git a/connect/host.go b/connect/host.go index b388b04a15dfe0add28bd93a0e81ab0e3b531833..22b02e8c97ebe3bb6f2b4c81fe42f80d547a5447 100644 --- a/connect/host.go +++ b/connect/host.go @@ -116,6 +116,11 @@ func (h *Host) IsDynamicHost() bool { return h.dynamicHost } +// Simple getter for the public key +func (h *Host) GetPubKey() *rsa.PublicKey { + return h.rsaPublicKey +} + // Connected checks if the given Host's connection is alive func (h *Host) Connected() bool { h.mux.RLock() diff --git a/network/dataStructures/ndf.go b/network/dataStructures/ndf.go index ea2c7c4a4cfccc8c18ac25e69927a3326fda746d..75f305d678df5b542cce13feb4a74abb294ac9aa 100644 --- a/network/dataStructures/ndf.go +++ b/network/dataStructures/ndf.go @@ -1,11 +1,97 @@ package dataStructures -import "gitlab.com/elixxir/primitives/ndf" +import ( + "bytes" + "crypto/sha256" + "github.com/pkg/errors" + pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/primitives/ndf" + "sync" +) type Ndf struct{ - f ndf.NetworkDefinition + f *ndf.NetworkDefinition + pb *pb.NDF hash []byte + lock sync.RWMutex } +//Updates to a new NDF if the passed NDF is valid +func(file *Ndf)Update(m *pb.NDF)error{ + + //build the ndf object + decoded, _, err := ndf.DecodeNDF(string(m.Ndf)) + + if err!=nil{ + return errors.WithMessage(err,"Could not decode the NDF") + } + + file.lock.Lock() + defer file.lock.Unlock() + + file.pb = m + file.f = decoded + + //set the ndf hash + marshaled, err := file.f.Marshal() + if err != nil { + return errors.WithMessage(err, + "Could not marshal NDF for hashing") + } + + // Serialize then hash the constructed ndf + hash := sha256.New() + hash.Write(marshaled) + file.hash = hash.Sum(nil) + + return nil +} + +//returns the ndf object +//fix-me: return a copy instead to ensure edits to not impact the +//original version +func(file *Ndf)Get()*ndf.NetworkDefinition{ + file.lock.RLock() + defer file.lock.RUnlock() + return file.f +} + +//returns the ndf hash +func(file *Ndf)GetHash()[]byte{ + file.lock.RLock() + defer file.lock.RUnlock() + rtn := make([]byte, len(file.hash)) + copy(rtn,file.hash) + return rtn +} + +//returns the ndf hash +//fix-me: return a copy instead to ensure edits to not impact the +//original version +func(file *Ndf)GetPb()*pb.NDF{ + file.lock.RLock() + defer file.lock.RUnlock() + return file.pb +} + +//evaluates if the passed ndf hash is the same as the stored one +//returns an error if no ndf is available, returns false if they are different +//and true if they are the same +func(file *Ndf)CompareHash(h []byte)(bool, error){ + file.lock.RLock() + defer file.lock.RUnlock() + //return the NO_NDF error if no NDF is available + if len(file.hash) == 0 { + errMsg := errors.Errorf(ndf.NO_NDF) + return false, errMsg + } + + //return true if the hashes are the same + if bytes.Compare(file.hash, h) == 0 { + return true, nil + } + + //return false if the hashes are different + return false, nil +} -func(ndf *Ndf)Update( diff --git a/network/dataStructures/roundData_test.go b/network/dataStructures/roundData_test.go index 6d2cb10ff31ff5bf5300f4903199a73763b6e78c..54e17276e965d26ed01c6ba939b275554ce99cd9 100644 --- a/network/dataStructures/roundData_test.go +++ b/network/dataStructures/roundData_test.go @@ -1 +1,29 @@ package dataStructures + +import ( + "gitlab.com/elixxir/comms/mixmessages" + "testing" +) + +func TestData_UpsertRound(t *testing.T) { + d := Data{} + err := d.UpsertRound(&mixmessages.RoundInfo{ + ID: 0, + UpdateID: 0, + }) + if err != nil { + t.Errorf("Failed to upsert round: %+v", err) + } +} + +func TestData_GetRound(t *testing.T) { + d := Data{} + _ = d.UpsertRound(&mixmessages.RoundInfo{ + ID: 0, + UpdateID: 3, + }) + _, err := d.GetRound(0) + if err != nil { + t.Errorf("Failed to get roundinfo with proper id") + } +} diff --git a/network/dataStructures/roundUpdates_test.go b/network/dataStructures/roundUpdates_test.go index 6ff5f52fb1978b0a97dacfdc2433a48f158569c8..a1b1dafdc0f33567bf7ed463d5329736316997ba 100644 --- a/network/dataStructures/roundUpdates_test.go +++ b/network/dataStructures/roundUpdates_test.go @@ -5,29 +5,6 @@ import ( "testing" ) -func TestData_UpsertRound(t *testing.T) { - d := Data{} - err := d.UpsertRound(&mixmessages.RoundInfo{ - ID: 0, - UpdateID: 0, - }) - if err != nil { - t.Errorf("Failed to upsert round: %+v", err) - } -} - -func TestData_GetRound(t *testing.T) { - d := Data{} - _ = d.UpsertRound(&mixmessages.RoundInfo{ - ID: 0, - UpdateID: 3, - }) - _, err := d.GetRound(0) - if err != nil { - t.Errorf("Failed to get roundinfo with proper id") - } -} - func TestUpdates_AddRound(t *testing.T) { u := Updates{} err := u.AddRound(&mixmessages.RoundInfo{ diff --git a/network/instance.go b/network/instance.go new file mode 100644 index 0000000000000000000000000000000000000000..55cbe90fc2dd8f3fe6a58e0f328e7d5d61ad5c25 --- /dev/null +++ b/network/instance.go @@ -0,0 +1,108 @@ +package network + +import( + "github.com/pkg/errors" + "gitlab.com/elixxir/comms/connect" + ds "gitlab.com/elixxir/comms/network/dataStructures" + pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/crypto/signature" + "gitlab.com/elixxir/primitives/id" + "sync" +) + +type Instance struct { + comm *connect.ProtoComms + + partial *SecuredNdf + full *SecuredNdf + roundUpdates *ds.Updates + roundData *ds.Data + + roundlock sync.RWMutex +} + + +func NewInstance(c *connect.ProtoComms)*Instance{ + return &Instance{ + c, + NewSecuredNdf(), + NewSecuredNdf(), + &ds.Updates{}, + &ds.Data{}, + sync.RWMutex{}, + } +} + +//update the partial ndf +func (i *Instance)UpdatePartialNdf(m *pb.NDF)error{ + perm, success := i.comm.GetHost(id.PERMISSIONING) + + if !success{ + return errors.New("Could not get permissioning Public Key" + + "for NDF partial verification") + } + + return i.partial.update(m,perm.GetPubKey()) +} + +//update the full ndf +func (i *Instance)UpdateFullNdf(m *pb.NDF)error{ + perm, success := i.comm.GetHost(id.PERMISSIONING) + + if !success{ + return errors.New("Could not get permissioning Public Key" + + "for full NDF verification") + } + + return i.full.update(m,perm.GetPubKey()) +} + +func (i *Instance)GetPartialNdf()*SecuredNdf{ + return i.partial +} + +func (i *Instance)GetFullNdf()*SecuredNdf{ + return i.full +} + +func (i *Instance)RoundUpdate(info *pb.RoundInfo)error{ + perm, success := i.comm.GetHost(id.PERMISSIONING) + + if !success{ + return errors.New("Could not get permissioning Public Key" + + "for round info verification") + } + + err := signature.Verify(info, perm.GetPubKey()) + if err!=nil{ + return errors.WithMessage(err, "Could not validate NDF") + } + + i.roundlock.Lock() + defer i.roundlock.Unlock() + + err = i.roundUpdates.AddRound(info) + if err!=nil{ + return err + } + + err = i.roundData.UpsertRound(info) + if err!=nil{ + return err + } + + return nil +} + +func (i *Instance)GetRound(id id.Round)(*pb.RoundInfo, error){ + return i.roundData.GetRound(int(id)) +} + +func (i *Instance)GetRoundUpdate(updateID int)(*pb.RoundInfo, error){ + return i.roundUpdates.GetUpdate(updateID) +} + +func (i *Instance)GetRoundUpdates(id id.Round)([]*pb.RoundInfo, error){ + return nil, nil +} + diff --git a/network/securedNdf.go b/network/securedNdf.go new file mode 100644 index 0000000000000000000000000000000000000000..bcc92a673736531f37c4c23ee60a6d3a87ff867f --- /dev/null +++ b/network/securedNdf.go @@ -0,0 +1,49 @@ +package network + +import( + "github.com/pkg/errors" + ds "gitlab.com/elixxir/comms/network/dataStructures" + "gitlab.com/elixxir/crypto/signature" + "gitlab.com/elixxir/crypto/signature/rsa" + pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/primitives/ndf" +) + +// wraps the ndf data structure, expoting all the functionality expect the +// ability to change the ndf +type SecuredNdf struct{ + f *ds.Ndf +} + +func NewSecuredNdf()*SecuredNdf{ + return &SecuredNdf{ + &ds.Ndf{}, + } +} + +// unexported NDF update code +func (sndf *SecuredNdf)update(m *pb.NDF, key *rsa.PublicKey)error{ + err := signature.Verify(m, key) + if err!=nil{ + return errors.WithMessage(err, "Could not validate NDF") + } + + return sndf.f.Update(m) +} + +func (sndf *SecuredNdf)Get()*ndf.NetworkDefinition{ + return sndf.f.Get() +} + +func (sndf *SecuredNdf)GetHash()[]byte{ + return sndf.f.GetHash() +} + +func (sndf *SecuredNdf)GetPb()*pb.NDF{ + return sndf.f.GetPb() +} + +func(sndf *SecuredNdf)CompareHash(h []byte)(bool, error) { + return sndf.f.CompareHash(h) +} + diff --git a/network/securedNdf_test.go b/network/securedNdf_test.go new file mode 100644 index 0000000000000000000000000000000000000000..20972f9acb1eb104802d1ca1b3695fa2caa7dba4 --- /dev/null +++ b/network/securedNdf_test.go @@ -0,0 +1,14 @@ +package network + +import "testing" + +func TestNewSecuredNdf(t *testing.T) { + sndf := NewSecuredNdf() + if sndf==nil{ + t.Errorf("Internal ndf object is nil") + } +} + +func TestSecuredNdf_CompareHash(t *testing.T) { + +} \ No newline at end of file