From e93c13f675acadf768f5d1427dc9d948c97679a3 Mon Sep 17 00:00:00 2001 From: tfn18001 Date: Tue, 19 Apr 2022 01:38:34 -0400 Subject: [PATCH] Reworked folder structure: PoM is now in Gossip package. --- CTng/HTTP/types.go | 170 ---------------- CTng/ctng.go | 16 +- CTng/gossip/PoM_test.go | 46 +++++ .../accusation_validation.go} | 2 +- .../accusations.go} | 22 +-- CTng/gossip/gossip_object.go | 177 +++++++++++++++++ CTng/gossip/gossiper.go | 187 ++++++------------ .../process_object.go} | 35 ++-- CTng/gossip/types.go | 92 +++++++-- CTng/gossiper_data.json | 1 - CTng/monitor/monitor.go | 72 +++++++ CTng/monitor/types.go | 95 +++++++++ .../Gossiper.go => server/Gossiper_server.go} | 124 ++---------- CTng/{HTTP => server}/Logger.go | 4 + CTng/{HTTP => server}/Monitor_ng.go | 9 +- .../Monitor.go => server/Monitor_server.go} | 90 ++------- CTng/{HTTP => server}/Readme.md | 0 CTng/{HTTP => server}/helpers.go | 0 CTng/util/types.go | 12 ++ 19 files changed, 611 insertions(+), 543 deletions(-) delete mode 100644 CTng/HTTP/types.go create mode 100644 CTng/gossip/PoM_test.go rename CTng/{PoM/PoM_validation.go => gossip/accusation_validation.go} (98%) rename CTng/{PoM/Gossiper_Process_Accusation.go => gossip/accusations.go} (80%) create mode 100644 CTng/gossip/gossip_object.go rename CTng/{HTTP/Gossiper_process.go => gossip/process_object.go} (62%) delete mode 100644 CTng/gossiper_data.json create mode 100644 CTng/monitor/monitor.go create mode 100644 CTng/monitor/types.go rename CTng/{HTTP/Gossiper.go => server/Gossiper_server.go} (55%) rename CTng/{HTTP => server}/Logger.go (79%) rename CTng/{HTTP => server}/Monitor_ng.go (94%) rename CTng/{HTTP/Monitor.go => server/Monitor_server.go} (62%) rename CTng/{HTTP => server}/Readme.md (100%) rename CTng/{HTTP => server}/helpers.go (100%) create mode 100644 CTng/util/types.go diff --git a/CTng/HTTP/types.go b/CTng/HTTP/types.go deleted file mode 100644 index 078771c..0000000 --- a/CTng/HTTP/types.go +++ /dev/null @@ -1,170 +0,0 @@ -package server - -import ( - "CTng/POM" - "CTng/config" - "CTng/gossip" - "CTng/util" - "encoding/json" - "net/http" - "sync" - "time" -) - -// This is the state of a gossiper server. It contains: -// The gossiper Configuration, -// Storage utilized by the gossiper, -// Any objects needed throughout the gossiper's lifetime (such as the http client). -type GossiperContext struct { - Config *config.Gossiper_config - Storage *gossip.Gossip_Storage - Accusations *POM.AccusationDB - StorageFile string // Where storage can be stored. - // Client: used for HTTP connections, allows for timeouts - // and more control over the connections we make. - Client *http.Client - HasPom map[string]bool -} - -func (c *GossiperContext) SaveStorage() error { - storageList := []gossip.Gossip_object{} - for _, gossipObject := range *c.Storage { - storageList = append(storageList, gossipObject) - } - err := util.WriteData(c.StorageFile, storageList) - return err -} - -// Read every gossip object from c.StorageFile(). -// Store all files in c.Storage by their ID. -func (c *GossiperContext) LoadStorage() error { - storageList := []gossip.Gossip_object{} - bytes, err := util.ReadByte(c.StorageFile) - period := c.Config.Public.Period_interval - if err != nil { - return err - } - err = json.Unmarshal(bytes, &storageList) - if err != nil { - return err - } - for _, gossipObject := range storageList { - (*c.Storage)[gossipObject.GetID(period)] = gossipObject - } - return nil -} - -// Stores an object in storage by its ID. Note that the ID utilizes Config.Public.Periodf_interval. -func (c *GossiperContext) StoreObject(o gossip.Gossip_object) { - (*c.Storage)[o.GetID(c.Config.Public.Period_interval)] = o -} - -// Returns the object, and whether or not the object was successfully found. -func (c *GossiperContext) GetObject(id gossip.Gossip_object_ID) (gossip.Gossip_object, bool) { - obj := (*c.Storage)[id] - if obj == (gossip.Gossip_object{}) { - return obj, false - } - return obj, true -} - -// Given a gossip object, check if the an object with the same ID exists. -func (c *GossiperContext) IsDuplicate(g gossip.Gossip_object) bool { - id := g.GetID(c.Config.Public.Period_interval) - _, exists := c.GetObject(id) - return exists -} - -type MonitorContext struct { - Config *config.Monitor_config - Storage *gossip.Gossip_Storage - StorageFile string - Timer_CAs TimedMap - Timer_Loggers TimedMap - Timer_Loggers_LastCheckIn int64 - Timer_CAs_LastCheckIn int64 - // TODO: Utilize Storage directory: A folder for the files of each MMD. - // Folder should be set to the current MMD "Period" String upon initialization. - StorageDirectory string - Client *http.Client -} - -type TimedMap struct { - m map[string]int64 - mu *sync.Mutex -} - -func (c *MonitorContext) InitializeTimedMap() { - var tm1 TimedMap - var tm2 TimedMap - var m1 map[string]int64 - var m2 map[string]int64 - tm1.m = m1 - tm2.m = m2 - for _, v := range c.Config.Logger_URLs { - tm1.m[v] = time.Now().Unix() - } - for _, v := range c.Config.CA_URLs { - tm1.m[v] = time.Now().Unix() - } - c.Timer_Loggers = tm1 - c.Timer_CAs = tm2 - c.Timer_Loggers_LastCheckIn = time.Now().Unix() - c.Timer_CAs_LastCheckIn = time.Now().Unix() -} -func (c *MonitorContext) SaveStorage() error { - storageList := []gossip.Gossip_object{} - for _, gossipObject := range *c.Storage { - storageList = append(storageList, gossipObject) - } - err := util.WriteData(c.StorageFile, storageList) - return err -} - -func (c *MonitorContext) LoadStorage() error { - storageList := []gossip.Gossip_object{} - bytes, err := util.ReadByte(c.StorageFile) - if err != nil { - return err - } - err = json.Unmarshal(bytes, &storageList) - if err != nil { - return err - } - for _, gossipObject := range storageList { - (*c.Storage)[gossipObject.GetID(int64(c.Config.Public.Gossip_wait_time))] = gossipObject - } - return nil -} - -func (c *MonitorContext) GetObject(id gossip.Gossip_object_ID) (gossip.Gossip_object, bool) { - obj := (*c.Storage)[id] - if obj == (gossip.Gossip_object{}) { - return obj, false - } - return obj, true -} -func (c *MonitorContext) IsDuplicate(g gossip.Gossip_object) bool { - //no public period time for monitor :/ - id := g.GetID(int64(c.Config.Public.Gossip_wait_time)) - obj := (*c.Storage)[id] - if obj == (gossip.Gossip_object{}) { - return false - } - return true -} - -func (c *MonitorContext) StoreObject(o gossip.Gossip_object) { - (*c.Storage)[o.GetID(int64(c.Config.Public.Gossip_wait_time))] = o -} - -// Terminal colors. Use by adding the color to the beginning of a string. -// add RESET after the string to reset the color: otherwise all text will remain this color -// in future prints. -const ( - RED = "\x1b[31m" - GREEN = "\x1b[32m" - YELLOW = "\x1b[33m" - BLUE = "\x1b[34m" - RESET = "\x1b[0m" -) diff --git a/CTng/ctng.go b/CTng/ctng.go index fe1ae18..a694269 100644 --- a/CTng/ctng.go +++ b/CTng/ctng.go @@ -1,10 +1,10 @@ package main import ( - server "CTng/HTTP" - "CTng/POM" "CTng/config" "CTng/gossip" + "CTng/monitor" + "CTng/server" fakeLogger "CTng/testData/fakeLogger" "fmt" "os" @@ -26,10 +26,10 @@ func main() { } storage := new(gossip.Gossip_Storage) *storage = make(gossip.Gossip_Storage) - accusationdb := new(POM.AccusationDB) - *accusationdb = make(POM.AccusationDB) + accusationdb := new(gossip.AccusationDB) + *accusationdb = make(gossip.AccusationDB) - ctx := server.GossiperContext{ + ctx := gossip.GossiperContext{ Config: &conf, Storage: storage, Accusations: accusationdb, @@ -37,7 +37,7 @@ func main() { HasPom: make(map[string]bool), } ctx.Config = &conf - server.StartGossiper(&ctx) + server.StartGossiperServer(&ctx) // break // break unneeded in go. case "monitor": conf, err := config.LoadMonitorConfig(os.Args[2], os.Args[3], os.Args[4]) @@ -45,13 +45,13 @@ func main() { fmt.Println(helpText) panic(err) } - ctx := server.MonitorContext{ + ctx := monitor.MonitorContext{ Config: &conf, Storage: new(gossip.Gossip_Storage), StorageFile: "monitor_data.json", } ctx.Config = &conf - server.StartMonitor(&ctx) + server.StartMonitorServer(&ctx) case "logger": fakeLogger.RunLogger(os.Args[2]) default: diff --git a/CTng/gossip/PoM_test.go b/CTng/gossip/PoM_test.go new file mode 100644 index 0000000..268b051 --- /dev/null +++ b/CTng/gossip/PoM_test.go @@ -0,0 +1,46 @@ +package gossip + +// import ( +// "CTng/crypto" +// "testing" +// ) + +// func TestAccusationUpdates(t *testing.T) { +// println("test start\n") +// var accs AccusationDB +// var cp1 Accusation +// var cp2 Accusation +// var cp3 Accusation +// var cp4 Accusation +// cp1.Accuser = "monitor_01_DNS_place_holder" +// cp1.Entity_URL = "logger_01_DNS_place_holder" +// cp1.Partial_sig = crypto.SigFragment{} +// cp2.Accuser = "monitor_02_DNS_place_holder" +// cp2.Entity_URL = "logger_02_DNS_place_holder" +// cp2.Partial_sig = crypto.SigFragment{} +// cp3.Accuser = "monitor_01_DNS_place_holder" +// cp3.Entity_URL = "logger_02_DNS_place_holder" +// cp3.Partial_sig = crypto.SigFragment{} +// cp4.Accuser = "monitor_02_DNS_place_holder" +// cp4.Entity_URL = "logger_01_DNS_place_holder" +// cp4.Partial_sig = crypto.SigFragment{} +// println("data structures initialized") +// accs = Process_Accusation(cp1, accs) +// accs = Process_Accusation(cp1, accs) +// accs = Process_Accusation(cp2, accs) +// accs = Process_Accusation(cp3, accs) +// println("Accsuation processing went through") +// println("_______________________________________") +// println("start listing accused entities and the number of accusations") +// for i := 0; i < len(accs); i++ { +// println("\nThe accused entity DNS is: ", accs[i].Entity_URL) +// println("The total number of accusations is", accs[i].Num_acc) +// for j := 0; j < len(accs[i].Accusers); j++ { +// print("Accusers No.", j+1, "is ") +// println(accs[i].Accusers[j]) +// // print("Partial sigature No.", j+1, "is ") +// // println(accs[i].Partial_sigs[j]) +// } +// } + +// } diff --git a/CTng/PoM/PoM_validation.go b/CTng/gossip/accusation_validation.go similarity index 98% rename from CTng/PoM/PoM_validation.go rename to CTng/gossip/accusation_validation.go index dfa1a5c..107113a 100644 --- a/CTng/PoM/PoM_validation.go +++ b/CTng/gossip/accusation_validation.go @@ -1,4 +1,4 @@ -package POM +package gossip import ( "CTng/crypto" diff --git a/CTng/PoM/Gossiper_Process_Accusation.go b/CTng/gossip/accusations.go similarity index 80% rename from CTng/PoM/Gossiper_Process_Accusation.go rename to CTng/gossip/accusations.go index 2f74e2e..3b58008 100644 --- a/CTng/PoM/Gossiper_Process_Accusation.go +++ b/CTng/gossip/accusations.go @@ -1,22 +1,12 @@ -package POM +package gossip import ( "CTng/crypto" - "CTng/gossip" "errors" "fmt" ) -type EntityAccusations struct { - Accusers []string - Entity_URL string - Num_acc int - Partial_sigs []crypto.SigFragment - PoM_status bool -} -type AccusationDB map[string]*EntityAccusations - -func Process_Accusation(new_acc gossip.Gossip_object, accs *AccusationDB, c *crypto.CryptoConfig) (*gossip.Gossip_object, bool, error) { +func Process_Accusation(new_acc Gossip_object, accs *AccusationDB, c *crypto.CryptoConfig) (*Gossip_object, bool, error) { // Convert signature string p_sig, err := crypto.SigFragmentFromString(new_acc.Signature[0]) if err != nil { @@ -57,13 +47,13 @@ func Process_Accusation(new_acc gossip.Gossip_object, accs *AccusationDB, c *cry fmt.Println(converr) } val.PoM_status = true - gossip_obj := new(gossip.Gossip_object) - *gossip_obj = gossip.Gossip_object{} + gossip_obj := new(Gossip_object) + *gossip_obj = Gossip_object{} gossip_obj.Application = "Ctng" - gossip_obj.Type = gossip.GOSSIP_POM + gossip_obj.Type = GOSSIP_POM // gossip_obj.Signer Signers are contained within the signature. gossip_obj.Signature[0] = sig_string - gossip_obj.Timestamp = gossip.GetCurrentTimestamp() + gossip_obj.Timestamp = GetCurrentTimestamp() gossip_obj.Payload[0] = acc_pom.Entity_URL return gossip_obj, true, nil } diff --git a/CTng/gossip/gossip_object.go b/CTng/gossip/gossip_object.go new file mode 100644 index 0000000..68e562f --- /dev/null +++ b/CTng/gossip/gossip_object.go @@ -0,0 +1,177 @@ +package gossip + +import ( + "CTng/config" + "CTng/crypto" + "encoding/json" + "errors" + "fmt" + "log" + "reflect" + "time" +) + +// Given a period interval, create a gossip object ID from the given period. +func (g Gossip_object) GetID(period_interval int64) Gossip_object_ID { + // Convert g.Timestamp to a time.Time + t, err := time.Parse(time.RFC3339, g.Timestamp) + if err != nil { + fmt.Println(err) + } + // Floor the time to the nearest period interval + rounded := t.Truncate(time.Duration(period_interval) * time.Second) + // Construct the ID + return Gossip_object_ID{ + Application: g.Application, + Type: g.Type, + Signer: g.Signer, + Period: rounded.String(), + } +} + +// This function is highly associated with this type, so I think it belongs here. +func GetCurrentTimestamp() string { + return time.Now().UTC().Format(time.RFC3339) +} + +//takes struct and marshalls to bytes for sending, this seems to already be built in our gossipdata func, can be removed +func package_object(obj Gossip_object) (packaged []byte) { + pack, err := json.Marshal(obj) + if err != nil { + log.Fatal("Error during Marshal(): ", err) + } + return pack +} + +//receives bytes and unpacks to gossip object, right now it returns a new object but we could modify it to take in an object too +func unpack_object(obj []byte) (depacked Gossip_object) { + var depack Gossip_object + + err := json.Unmarshal(obj, &depack) + if err != nil { + log.Fatal("Error during Unmarshal(): ", err) + } + return depack +} + +//checks if Gossip_object_ID already exists in map returns bool rn but I want to change it to just add the object to storage +func check_object_data(conf *config.Gossiper_config, obj Gossip_object, stor Gossip_Storage) { + obj_ID := obj.GetID(conf.Public.Period_interval) + // if object exists, make sure values match + if stored_object, exists := stor[obj_ID]; exists { + + check_stored := reflect.ValueOf(&stored_object).Elem() + given_object := reflect.ValueOf(&obj).Elem() + + for i := 0; i < check_stored.NumField(); i++ { + temp_stored := check_stored.Field(i).Interface() + temp_given := given_object.Field(i).Interface() + if temp_given == temp_stored { + continue + } else { + log.Fatal("Error given object does not match stored object") + } + } + + } else { + stor[obj_ID] = obj + } + +} + +//verify gossip pom takes a gossip object as input +func Verify_gossip_pom(g Gossip_object, c *crypto.CryptoConfig) error { + if g.Type == "http://ctng.uconn.edu/001" { + //gossip pom refers to Pom generated due to conflicting information + //From Finn's gossiper design, gossip poms are defaulted to have 2 non empty fields for signature and paypload + if g.Signature[1] != "" && g.Payload[1] != "" { + if g.Signature[0] != g.Signature[1] { + //that means there are conflicting information + //the PoM is valid and the verification went through + sig1, sigerr1 := crypto.ThresholdSigFromString(g.Signature[0]) + sig2, sigerr2 := crypto.ThresholdSigFromString(g.Signature[1]) + err1 := c.ThresholdVerify(g.Payload[0], sig1) + err2 := c.ThresholdVerify(g.Payload[1], sig2) + if err1 == nil && err2 == nil { + return nil + } else { + return errors.New("Message Signature Mismatch" + fmt.Sprint(sigerr1) + fmt.Sprint(sigerr2)) + } + } else { + //if signatures are the same, there are no conflicting information + return errors.New("This is not a valid gossip pom") + } + } else { + //type mislabeled + return errors.New("Gossip object label Mismatch") + } + + } + return errors.New("the input is not an gossip pom") +} + +//verifies signature fragments match with payload +func Verify_PayloadFrag(g Gossip_object, c *crypto.CryptoConfig) error { + if g.Signature[0] != "" && g.Payload[0] != "" { + sig, _ := crypto.SigFragmentFromString(g.Signature[0]) + err := c.FragmentVerify(g.Payload[0], sig) + if err != nil { + return errors.New(No_Sig_Match) + } + return nil + } else { + return errors.New(Mislabel) + } +} + +//verifies threshold signatures match payload +func Verfiy_PayloadThreshold(g Gossip_object, c *crypto.CryptoConfig) error { + if g.Signature[0] != "" && g.Payload[0] != "" { + sig, _ := crypto.ThresholdSigFromString(g.Signature[0]) + err := c.ThresholdVerify(g.Payload[0], sig) + if err != nil { + return errors.New(No_Sig_Match) + } + return nil + } else { + return errors.New(Mislabel) + } +} + +// Verifies RSAsig matches payload, wait.... i think this just works out of the box with what we have +func Verify_RSAPayload(g Gossip_object, c *crypto.CryptoConfig) error { + if g.Signature[0] != "" && g.Payload[0] != "" { + // TODO: convert RSASig from and to a string. + sig, err := crypto.RSASigFromString(g.Signature[0]) + if err != nil { + return errors.New(No_Sig_Match) + } + return c.Verify([]byte(g.Payload[0]), sig) + + } else { + return errors.New(Mislabel) + } +} + +//Verifies Gossip object based on Type +func (g Gossip_object) Verify(c *crypto.CryptoConfig) error { + // If everything Verified correctly, we return nil + switch g.Type { + case GOSSIP_POM: + return Verify_gossip_pom(g, c) + case STH: + return Verify_RSAPayload(g, c) + case REVOCATION: + return Verify_RSAPayload(g, c) + case STH_FRAG: + return Verify_PayloadFrag(g, c) + case REVOCATION_FRAG: + return Verify_PayloadFrag(g, c) + case ACCUSATION_FRAG: + return Verify_PayloadFrag(g, c) + case APPLICATION_POM: + return Verfiy_PayloadThreshold(g, c) + default: + return errors.New(Invalid_Type) + } +} diff --git a/CTng/gossip/gossiper.go b/CTng/gossip/gossiper.go index 413891d..48698c3 100644 --- a/CTng/gossip/gossiper.go +++ b/CTng/gossip/gossiper.go @@ -1,153 +1,86 @@ package gossip import ( - "CTng/config" - "CTng/crypto" + "CTng/util" + "bytes" "encoding/json" - "errors" "fmt" - "log" - "reflect" + "strings" ) -//takes struct and marshalls to bytes for sending, this seems to already be built in our gossipdata func, can be removed -func package_object(obj Gossip_object) (packaged []byte) { - pack, err := json.Marshal(obj) - if err != nil { - log.Fatal("Error during Marshal(): ", err) +// This isn't called and should likely move to the monitor. +func Accuse(c *GossiperContext, url string) { + // Create a new Gossip_object for the accusation + sig, _ := c.Config.Crypto.ThresholdSign(url) + obj := Gossip_object{ + Application: "CTng", + Type: ACCUSATION_FRAG, + Signer: c.Config.Crypto.SelfID.String(), + Signature: [2]string{sig.String(), ""}, + Timestamp: GetCurrentTimestamp(), + Payload: [2]string{url, ""}, + } + // If it's not a duplicate, process it. + // These kind of checks are very important, otherwise infinite loops can occur. + if !c.IsDuplicate(obj) { + ProcessValidObject(c, obj) } - return pack } -//receives bytes and unpacks to gossip object, right now it returns a new object but we could modify it to take in an object too -func unpack_object(obj []byte) (depacked Gossip_object) { - var depack Gossip_object - - err := json.Unmarshal(obj, &depack) +// Sends a gossip object to all connected gossipers. +// This function assumes you are passing valid data. ALWAYS CHECK BEFORE CALLING THIS FUNCTION. +func gossipData(c *GossiperContext, gossip_obj Gossip_object) error { + // Convert gossip object to JSON + msg, err := json.Marshal(gossip_obj) if err != nil { - log.Fatal("Error during Unmarshal(): ", err) + return err } - return depack -} -//checks if Gossip_object_ID already exists in map returns bool rn but I want to change it to just add the object to storage -func check_object_data(conf *config.Gossiper_config, obj Gossip_object, stor Gossip_Storage) { - obj_ID := obj.GetID(conf.Public.Period_interval) - // if object exists, make sure values match - if stored_object, exists := stor[obj_ID]; exists { - - check_stored := reflect.ValueOf(&stored_object).Elem() - given_object := reflect.ValueOf(&obj).Elem() - - for i := 0; i < check_stored.NumField(); i++ { - temp_stored := check_stored.Field(i).Interface() - temp_given := given_object.Field(i).Interface() - if temp_given == temp_stored { - continue - } else { - log.Fatal("Error given object does not match stored object") - } + // Send the gossip object to all connected gossipers. + for _, url := range c.Config.Connected_Gossipers { + if c.HasPom[url] { + continue } - - } else { - stor[obj_ID] = obj - } - -} - -//verify gossip pom takes a gossip object as input -func Verify_gossip_pom(g Gossip_object, c *crypto.CryptoConfig) error { - if g.Type == "http://ctng.uconn.edu/001" { - //gossip pom refers to Pom generated due to conflicting information - //From Finn's gossiper design, gossip poms are defaulted to have 2 non empty fields for signature and paypload - if g.Signature[1] != "" && g.Payload[1] != "" { - if g.Signature[0] != g.Signature[1] { - //that means there are conflicting information - //the PoM is valid and the verification went through - sig1, sigerr1 := crypto.ThresholdSigFromString(g.Signature[0]) - sig2, sigerr2 := crypto.ThresholdSigFromString(g.Signature[1]) - err1 := c.ThresholdVerify(g.Payload[0], sig1) - err2 := c.ThresholdVerify(g.Payload[1], sig2) - if err1 == nil && err2 == nil { - return nil - } else { - return errors.New("Message Signature Mismatch" + fmt.Sprint(sigerr1) + fmt.Sprint(sigerr2)) - } + // HTTP POST the data to the url or IP address. + resp, err := c.Client.Post("http://"+url+"/gossip/push-data", "application/json", bytes.NewBuffer(msg)) + if err != nil { + if strings.Contains(err.Error(), "Client.Timeout") || + strings.Contains(err.Error(), "connection refused") { + fmt.Println(util.RED+"Connection failed to "+url+".", util.RESET) + // Don't accuse gossipers for inactivity. + // defer Accuse(c, url) } else { - //if signatures are the same, there are no conflicting information - return errors.New("This is not a valid gossip pom") + fmt.Println(util.RED+err.Error(), "sending to "+url+".", util.RESET) } - } else { - //type mislabeled - return errors.New("Gossip object label Mismatch") + continue } - + // Close the response, mentioned by http.Post + // Alernatively, we could return the response from this function. + defer resp.Body.Close() + // do something with the response? + fmt.Println("Gossiped to " + url + " and recieved " + resp.Status) } - return errors.New("the input is not an gossip pom") + return nil } -//verifies signature fragments match with payload -func Verify_PayloadFrag(g Gossip_object, c *crypto.CryptoConfig) error { - if g.Signature[0] != "" && g.Payload[0] != "" { - sig, _ := crypto.SigFragmentFromString(g.Signature[0]) - err := c.FragmentVerify(g.Payload[0], sig) - if err != nil { - return errors.New(No_Sig_Match) - } - return nil - } else { - return errors.New(Mislabel) +// Sends a gossip object to the owner of the object. +func sendToOwner(c *GossiperContext, obj Gossip_object) { + // Convert gossip object to JSON + msg, err := json.Marshal(obj) + if err != nil { + fmt.Println(err) } -} - -//verifies threshold signatures match payload -func Verfiy_PayloadThreshold(g Gossip_object, c *crypto.CryptoConfig) error { - if g.Signature[0] != "" && g.Payload[0] != "" { - sig, _ := crypto.ThresholdSigFromString(g.Signature[0]) - err := c.ThresholdVerify(g.Payload[0], sig) - if err != nil { - return errors.New(No_Sig_Match) - } - return nil + // Send the gossip object to the owner. + resp, postErr := c.Client.Post("http://"+c.Config.Owner_URL+"/monitor/recieve-gossip", "application/json", bytes.NewBuffer(msg)) + if postErr != nil { + fmt.Errorf("Error sending object to owner: " + postErr.Error()) } else { - return errors.New(Mislabel) + // Close the response, mentioned by http.Post + // Alernatively, we could return the response from this function. + defer resp.Body.Close() + fmt.Println("Owner responded with " + resp.Status) } -} - -// Verifies RSAsig matches payload, wait.... i think this just works out of the box with what we have -func Verify_RSAPayload(g Gossip_object, c *crypto.CryptoConfig) error { - if g.Signature[0] != "" && g.Payload[0] != "" { - // TODO: convert RSASig from and to a string. - sig, err := crypto.RSASigFromString(g.Signature[0]) - if err != nil { - return errors.New(No_Sig_Match) - } - return c.Verify([]byte(g.Payload[0]), sig) - } else { - return errors.New(Mislabel) - } -} + // Handling errors from owner could go here. -//Verifies Gossip object based on Type -func (g Gossip_object) Verify(c *crypto.CryptoConfig) error { - // If everything Verified correctly, we return nil - switch g.Type { - case GOSSIP_POM: - return Verify_gossip_pom(g, c) - case STH: - return Verify_RSAPayload(g, c) - case REVOCATION: - return Verify_RSAPayload(g, c) - case STH_FRAG: - return Verify_PayloadFrag(g, c) - case REVOCATION_FRAG: - return Verify_PayloadFrag(g, c) - case ACCUSATION_FRAG: - return Verify_PayloadFrag(g, c) - case APPLICATION_POM: - return Verfiy_PayloadThreshold(g, c) - default: - return errors.New(Invalid_Type) - } } diff --git a/CTng/HTTP/Gossiper_process.go b/CTng/gossip/process_object.go similarity index 62% rename from CTng/HTTP/Gossiper_process.go rename to CTng/gossip/process_object.go index 274d517..ba8483c 100644 --- a/CTng/HTTP/Gossiper_process.go +++ b/CTng/gossip/process_object.go @@ -1,38 +1,37 @@ -package server +package gossip import ( - "CTng/POM" - "CTng/gossip" + "CTng/util" "fmt" ) // Once an object is verified, it is stored and given its neccessary data path. // At this point, the object has not yet been stored in the database. // What we know is that the signature is valid for the provided data. -func processValidObject(c *GossiperContext, obj gossip.Gossip_object) { +func ProcessValidObject(c *GossiperContext, obj Gossip_object) { // This function is incomplete -- requires more individual object direction // Note: Object needs to be stored before Gossiping so it is recognized as a duplicate. c.StoreObject(obj) var err error = nil switch obj.Type { - case gossip.STH: + case STH: sendToOwner(c, obj) err = gossipData(c, obj) - case gossip.REVOCATION: + case REVOCATION: sendToOwner(c, obj) err = gossipData(c, obj) - case gossip.STH_FRAG: + case STH_FRAG: sendToOwner(c, obj) err = gossipData(c, obj) - case gossip.GOSSIP_POM: + case GOSSIP_POM: sendToOwner(c, obj) err = gossipData(c, obj) - case gossip.REVOCATION_FRAG: + case REVOCATION_FRAG: err = gossipData(c, obj) - case gossip.ACCUSATION_FRAG: - processAccusation(c, obj) + case ACCUSATION_FRAG: + ProcessAccusation(c, obj) err = gossipData(c, obj) - case gossip.APPLICATION_POM: + case APPLICATION_POM: sendToOwner(c, obj) err = gossipData(c, obj) default: @@ -43,25 +42,25 @@ func processValidObject(c *GossiperContext, obj gossip.Gossip_object) { } } -func processInvalidObject(obj gossip.Gossip_object, e error) { +func ProcessInvalidObject(obj Gossip_object, e error) { // TODO: // Determine Conflict/misbehavior // Log error // Send neccessary accusations } -func processAccusation(c *GossiperContext, acc gossip.Gossip_object) { - pom, shouldGossip, err := POM.Process_Accusation(acc, c.Accusations, c.Config.Crypto) +func ProcessAccusation(c *GossiperContext, acc Gossip_object) { + pom, shouldGossip, err := Process_Accusation(acc, c.Accusations, c.Config.Crypto) if err != nil { - fmt.Println(RED, err, RESET) + fmt.Println(util.RED, err, util.RESET) } else { - fmt.Println(YELLOW+"Processed accusation against", acc.Payload[0], RESET) + fmt.Println(util.YELLOW+"Processed accusation against", acc.Payload[0], util.RESET) } if shouldGossip { gossipData(c, acc) } if pom != nil { - fmt.Println(RED+"Generated POM for", acc.Payload[0], RESET) + fmt.Println(util.RED+"Generated POM for", acc.Payload[0], util.RESET) c.StoreObject(*pom) c.HasPom[acc.Payload[0]] = true // We do not currently gossip PoMs. diff --git a/CTng/gossip/types.go b/CTng/gossip/types.go index 9be6f7e..9757de4 100644 --- a/CTng/gossip/types.go +++ b/CTng/gossip/types.go @@ -1,8 +1,11 @@ package gossip import ( - "fmt" - "time" + "CTng/config" + "CTng/crypto" + "CTng/util" + "encoding/json" + "net/http" ) // This type could be a string if we convert the payload object to a json string first @@ -64,25 +67,78 @@ type Gossip_object_ID struct { type Gossip_Storage map[Gossip_object_ID]Gossip_object -// Given a period interval, create a gossip object ID from the given period. -func (g Gossip_object) GetID(period_interval int64) Gossip_object_ID { - // Convert g.Timestamp to a time.Time - t, err := time.Parse(time.RFC3339, g.Timestamp) +// Gossiper Context +// Ths type represents the current state of a gossiper HTTP server. +// This is the state of a gossiper server. It contains: +// The gossiper Configuration, +// Storage utilized by the gossiper, +// Any objects needed throughout the gossiper's lifetime (such as the http client). +type GossiperContext struct { + Config *config.Gossiper_config + Storage *Gossip_Storage + Accusations *AccusationDB + StorageFile string // Where storage can be stored. + // Client: used for HTTP connections, allows for timeouts + // and more control over the connections we make. + Client *http.Client + HasPom map[string]bool +} + +func (c *GossiperContext) SaveStorage() error { + storageList := []Gossip_object{} + for _, gossipObject := range *c.Storage { + storageList = append(storageList, gossipObject) + } + err := util.WriteData(c.StorageFile, storageList) + return err +} + +// Read every gossip object from c.StorageFile(). +// Store all files in c.Storage by their ID. +func (c *GossiperContext) LoadStorage() error { + storageList := []Gossip_object{} + bytes, err := util.ReadByte(c.StorageFile) + period := c.Config.Public.Period_interval + if err != nil { + return err + } + err = json.Unmarshal(bytes, &storageList) if err != nil { - fmt.Println(err) + return err } - // Floor the time to the nearest period interval - rounded := t.Truncate(time.Duration(period_interval) * time.Second) - // Construct the ID - return Gossip_object_ID{ - Application: g.Application, - Type: g.Type, - Signer: g.Signer, - Period: rounded.String(), + for _, gossipObject := range storageList { + (*c.Storage)[gossipObject.GetID(period)] = gossipObject } + return nil +} + +// Stores an object in storage by its ID. Note that the ID utilizes Config.Public.Periodf_interval. +func (c *GossiperContext) StoreObject(o Gossip_object) { + (*c.Storage)[o.GetID(c.Config.Public.Period_interval)] = o +} + +// Returns the object, and whether or not the object was successfully found. +func (c *GossiperContext) GetObject(id Gossip_object_ID) (Gossip_object, bool) { + obj := (*c.Storage)[id] + if obj == (Gossip_object{}) { + return obj, false + } + return obj, true +} + +// Given a gossip object, check if the an object with the same ID exists. +func (c *GossiperContext) IsDuplicate(g Gossip_object) bool { + id := g.GetID(c.Config.Public.Period_interval) + _, exists := c.GetObject(id) + return exists } -// This function is highly associated with this type, so I think it belongs here. -func GetCurrentTimestamp() string { - return time.Now().UTC().Format(time.RFC3339) +// Accusation DBs for Jie's Accusation functions +type EntityAccusations struct { + Accusers []string + Entity_URL string + Num_acc int + Partial_sigs []crypto.SigFragment + PoM_status bool } +type AccusationDB map[string]*EntityAccusations diff --git a/CTng/gossiper_data.json b/CTng/gossiper_data.json deleted file mode 100644 index 0637a08..0000000 --- a/CTng/gossiper_data.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/CTng/monitor/monitor.go b/CTng/monitor/monitor.go new file mode 100644 index 0000000..ea0d5d6 --- /dev/null +++ b/CTng/monitor/monitor.go @@ -0,0 +1,72 @@ +package monitor + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "time" +) + +func QueryLoggers(loggerURLs []string) { + for _, logger := range loggerURLs { + + // Get today's STH from logger. + + // Get today's date in format YYYY-MM-DD + var today = time.Now().UTC().Format(time.RFC3339)[0:10] + + sthResp, err := http.Get(logger + "ctng/v1/get-sth/" + today) + if err != nil { + log.Fatalln(err) + } + + sthBody, err := ioutil.ReadAll(sthResp.Body) + if err != nil { + log.Fatalln(err) + } + STH := string(sthBody) + fmt.Printf("STH from logger " + logger + ": " + STH + "\n") //temp + + // Get today's entries from logger + entriesResp, err := http.Get(logger + "ctng/v1/get-entries/" + today) + if err != nil { + log.Fatalln(err) + } + + entiresBody, err := ioutil.ReadAll(entriesResp.Body) + if err != nil { + log.Fatalln(err) + } + entries := string(entiresBody) + fmt.Printf("Entries from logger " + logger + ": " + entries + "\n") //temp + + // TODO - process STH and entries + } + +} + +func QueryAuthorities(authURLs []string) { + for _, CA := range authURLs { + + // Get today's revocation information from CA. + + // Get today's date in format YYYY-MM-DD + var today = time.Now().UTC().Format(time.RFC3339)[0:10] + + revResp, err := http.Get(CA + "/ctng/v1/get-revocations/" + today) + if err != nil { + log.Fatalln(err) + } + + revBody, err := ioutil.ReadAll(revResp.Body) + if err != nil { + log.Fatalln(err) + } + rev := string(revBody) + fmt.Printf("Revocation information from CA " + CA + ": " + rev + "\n") + + // TODO - process revocation data + } + +} diff --git a/CTng/monitor/types.go b/CTng/monitor/types.go new file mode 100644 index 0000000..3bb9b27 --- /dev/null +++ b/CTng/monitor/types.go @@ -0,0 +1,95 @@ +package monitor + +import ( + "CTng/config" + "CTng/gossip" + "CTng/util" + "encoding/json" + "net/http" + "sync" + "time" +) + +type MonitorContext struct { + Config *config.Monitor_config + Storage *gossip.Gossip_Storage + StorageFile string + Timer_CAs TimedMap + Timer_Loggers TimedMap + Timer_Loggers_LastCheckIn int64 + Timer_CAs_LastCheckIn int64 + // TODO: Utilize Storage directory: A folder for the files of each MMD. + // Folder should be set to the current MMD "Period" String upon initialization. + StorageDirectory string + Client *http.Client +} + +type TimedMap struct { + m map[string]int64 + mu *sync.Mutex +} + +func (c *MonitorContext) InitializeTimedMap() { + var tm1 TimedMap + var tm2 TimedMap + var m1 map[string]int64 + var m2 map[string]int64 + tm1.m = m1 + tm2.m = m2 + for _, v := range c.Config.Logger_URLs { + tm1.m[v] = time.Now().Unix() + } + for _, v := range c.Config.CA_URLs { + tm1.m[v] = time.Now().Unix() + } + c.Timer_Loggers = tm1 + c.Timer_CAs = tm2 + c.Timer_Loggers_LastCheckIn = time.Now().Unix() + c.Timer_CAs_LastCheckIn = time.Now().Unix() +} + +func (c *MonitorContext) SaveStorage() error { + storageList := []gossip.Gossip_object{} + for _, gossipObject := range *c.Storage { + storageList = append(storageList, gossipObject) + } + err := util.WriteData(c.StorageFile, storageList) + return err +} + +func (c *MonitorContext) LoadStorage() error { + storageList := []gossip.Gossip_object{} + bytes, err := util.ReadByte(c.StorageFile) + if err != nil { + return err + } + err = json.Unmarshal(bytes, &storageList) + if err != nil { + return err + } + for _, gossipObject := range storageList { + (*c.Storage)[gossipObject.GetID(int64(c.Config.Public.Gossip_wait_time))] = gossipObject + } + return nil +} + +func (c *MonitorContext) GetObject(id gossip.Gossip_object_ID) (gossip.Gossip_object, bool) { + obj := (*c.Storage)[id] + if obj == (gossip.Gossip_object{}) { + return obj, false + } + return obj, true +} +func (c *MonitorContext) IsDuplicate(g gossip.Gossip_object) bool { + //no public period time for monitor :/ + id := g.GetID(int64(c.Config.Public.Gossip_wait_time)) + obj := (*c.Storage)[id] + if obj == (gossip.Gossip_object{}) { + return false + } + return true +} + +func (c *MonitorContext) StoreObject(o gossip.Gossip_object) { + (*c.Storage)[o.GetID(int64(c.Config.Public.Gossip_wait_time))] = o +} diff --git a/CTng/HTTP/Gossiper.go b/CTng/server/Gossiper_server.go similarity index 55% rename from CTng/HTTP/Gossiper.go rename to CTng/server/Gossiper_server.go index fec36db..9880b03 100644 --- a/CTng/HTTP/Gossiper.go +++ b/CTng/server/Gossiper_server.go @@ -2,7 +2,7 @@ package server import ( "CTng/gossip" - "bytes" + "CTng/util" "encoding/json" "fmt" "log" @@ -13,12 +13,7 @@ import ( "github.com/gorilla/mux" ) -// The list containing all IP addresses and domain names of connected gossipers to push data to. -// TODO: Populate with domain names from config file. - -const PROTOCOL = "http://" - -type gossiper interface { +type Gossiper interface { // Response to entering the 'base page' of a gossiper. // TODO: Create informational landing page @@ -54,19 +49,19 @@ type gossiper interface { } // Binds the context to the functions we pass to the router. -func bindContext(context *GossiperContext, fn func(context *GossiperContext, w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { +func bindContext(context *gossip.GossiperContext, fn func(context *gossip.GossiperContext, w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { fn(context, w, r) } } -func handleRequests(c *GossiperContext) { +func handleRequests(c *gossip.GossiperContext) { // MUX which routes HTTP directories to functions. gorillaRouter := mux.NewRouter().StrictSlash(true) // homePage() is ran when base directory is accessed. - gorillaRouter.HandleFunc("/gossip/", bindContext(c, homePage)) + gorillaRouter.HandleFunc("/gossip/", homePage) // Inter-gossiper endpoints gorillaRouter.HandleFunc("/gossip/push-data", bindContext(c, handleGossip)).Methods("POST") @@ -78,20 +73,20 @@ func handleRequests(c *GossiperContext) { // Start the HTTP server. http.Handle("/", gorillaRouter) - fmt.Println(BLUE+"Listening on port:", c.Config.Port, RESET) + fmt.Println(util.BLUE+"Listening on port:", c.Config.Port, util.RESET) err := http.ListenAndServe(":"+c.Config.Port, nil) // We wont get here unless there's an error. log.Fatal("ListenAndServe: ", err) os.Exit(1) } -func homePage(c *GossiperContext, w http.ResponseWriter, r *http.Request) { +func homePage(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Welcome to the base page for the CTng gossiper.") } // handleGossipObjectRequest() is run when /gossip/get-data is accessed or sent a GET request. // Expects a gossip_object identifier, and returns the gossip object if it has it, 400 otherwise. -func handleGossipObjectRequest(c *GossiperContext, w http.ResponseWriter, r *http.Request) { +func handleGossipObjectRequest(c *gossip.GossiperContext, w http.ResponseWriter, r *http.Request) { // Verify the user sent a valid gossip_object identifier. gossipID, err := gossipIDFromParams(r) if err != nil { @@ -110,14 +105,12 @@ func handleGossipObjectRequest(c *GossiperContext, w http.ResponseWriter, r *htt if err != nil { // Internal Server error http.Error(w, "Internal Server Error", 500) - return } - return } // handleGossip() is ran when POST is recieved at /gossip/push-data. // It should verify the Gossip object and then send it to the network. -func handleGossip(c *GossiperContext, w http.ResponseWriter, r *http.Request) { +func handleGossip(c *gossip.GossiperContext, w http.ResponseWriter, r *http.Request) { // Parse sent object. // Converts JSON passed in the body of a POST to a Gossip_object. var gossip_obj gossip.Gossip_object @@ -129,7 +122,7 @@ func handleGossip(c *GossiperContext, w http.ResponseWriter, r *http.Request) { err = gossip_obj.Verify(c.Config.Crypto) if err != nil { fmt.Println("Recieved invalid object from " + getSenderURL(r) + ".") - processInvalidObject(gossip_obj, err) + gossip.ProcessInvalidObject(gossip_obj, err) http.Error(w, err.Error(), http.StatusOK) return } @@ -139,11 +132,11 @@ func handleGossip(c *GossiperContext, w http.ResponseWriter, r *http.Request) { // If the object is already stored, still return OK.{ fmt.Println("Duplicate:", gossip_obj.Type, getSenderURL(r)+".") http.Error(w, "Gossip object already stored.", http.StatusOK) - // processDuplicateObject(c, gossip_obj, stored_obj) + // gossip.ProcessDuplicateObject(c, gossip_obj, stored_obj) return } else { - fmt.Println(GREEN+"Recieved new, valid", gossip_obj.Type, "from "+getSenderURL(r)+".", RESET) - processValidObject(c, gossip_obj) + fmt.Println(util.GREEN+"Recieved new, valid", gossip_obj.Type, "from "+getSenderURL(r)+".", util.RESET) + gossip.ProcessValidObject(c, gossip_obj) c.SaveStorage() } http.Error(w, "Gossip object Processed.", http.StatusOK) @@ -152,7 +145,7 @@ func handleGossip(c *GossiperContext, w http.ResponseWriter, r *http.Request) { // Runs when /gossip/gossip-data is sent a POST request. // Should verify gossip object and then send it to the network // With the exception of not handling invalidObjects, this feels identical to gossipObject.. -func handleOwnerGossip(c *GossiperContext, w http.ResponseWriter, r *http.Request) { +func handleOwnerGossip(c *gossip.GossiperContext, w http.ResponseWriter, r *http.Request) { var gossip_obj gossip.Gossip_object // Verify sender is an owner. if !isOwner(c.Config.Owner_URL, getSenderURL(r)) { @@ -168,8 +161,8 @@ func handleOwnerGossip(c *GossiperContext, w http.ResponseWriter, r *http.Reques err = gossip_obj.Verify(c.Config.Crypto) if err != nil { // Might not want to handle invalid object for our owner: Just warn them. - // processInvalidObject(gossip_obj, err) - fmt.Println(RED+"Owner sent invalid object.", RESET) + // gossip.ProcessInvalidObject(gossip_obj, err) + fmt.Println(util.RED+"Owner sent invalid object.", util.RESET) http.Error(w, err.Error(), http.StatusBadRequest) } _, found := c.GetObject(gossip_obj.GetID(c.Config.Public.Period_interval)) @@ -177,94 +170,17 @@ func handleOwnerGossip(c *GossiperContext, w http.ResponseWriter, r *http.Reques // If the object is already stored, still return OK.{ fmt.Println("Recieved duplicate object from Owner.") http.Error(w, "Gossip object already stored.", http.StatusOK) - // processDuplicateObject(c, gossip_obj, stored_obj) + // gossip.ProcessDuplicateObject(c, gossip_obj, stored_obj) return } else { // Prints the body of the post request to the server console - fmt.Println(GREEN+"Recieved new, valid", gossip_obj.Type, "from owner.", RESET) - processValidObject(c, gossip_obj) + fmt.Println(util.GREEN+"Recieved new, valid", gossip_obj.Type, "from owner.", util.RESET) + gossip.ProcessValidObject(c, gossip_obj) c.SaveStorage() } } -// Sends a gossip object to all connected gossipers. -// This function assumes you are passing valid data. ALWAYS CHECK BEFORE CALLING THIS FUNCTION. -func gossipData(c *GossiperContext, gossip_obj gossip.Gossip_object) error { - // Convert gossip object to JSON - msg, err := json.Marshal(gossip_obj) - if err != nil { - return err - } - - // Send the gossip object to all connected gossipers. - for _, url := range c.Config.Connected_Gossipers { - if c.HasPom[url] { - continue - } - // HTTP POST the data to the url or IP address. - resp, err := c.Client.Post(PROTOCOL+url+"/gossip/push-data", "application/json", bytes.NewBuffer(msg)) - if err != nil { - if strings.Contains(err.Error(), "Client.Timeout") || - strings.Contains(err.Error(), "connection refused") { - fmt.Println(RED+"Connection failed to "+url+".", RESET) - // Don't accuse gossipers for inactivity. - // defer Accuse(c, url) - } else { - fmt.Println(RED+err.Error(), "sending to "+url+".", RESET) - } - continue - } - // Close the response, mentioned by http.Post - // Alernatively, we could return the response from this function. - defer resp.Body.Close() - // do something with the response? - fmt.Println("Gossiped to " + url + " and recieved " + resp.Status) - } - return nil -} - -// This isn't called and should likely move to the monitor. -func Accuse(c *GossiperContext, url string) { - // Create a new Gossip_object for the accusation - sig, _ := c.Config.Crypto.ThresholdSign(url) - obj := gossip.Gossip_object{ - Application: "CTng", - Type: gossip.ACCUSATION_FRAG, - Signer: c.Config.Crypto.SelfID.String(), - Signature: [2]string{sig.String(), ""}, - Timestamp: gossip.GetCurrentTimestamp(), - Payload: [2]string{url, ""}, - } - // If it's not a duplicate, process it. - // These kind of checks are very important, otherwise infinite loops can occur. - if !c.IsDuplicate(obj) { - processValidObject(c, obj) - } -} - -// Sends a gossip object to the owner of the object. -func sendToOwner(c *GossiperContext, obj gossip.Gossip_object) { - // Convert gossip object to JSON - msg, err := json.Marshal(obj) - if err != nil { - fmt.Println(err) - } - // Send the gossip object to the owner. - resp, postErr := c.Client.Post(PROTOCOL+c.Config.Owner_URL+"/monitor/recieve-gossip", "application/json", bytes.NewBuffer(msg)) - if postErr != nil { - fmt.Errorf("Error sending object to owner: " + postErr.Error()) - } else { - // Close the response, mentioned by http.Post - // Alernatively, we could return the response from this function. - defer resp.Body.Close() - fmt.Println("Owner responded with " + resp.Status) - } - - // Handling errors from owner could go here. - -} - -func StartGossiper(c *GossiperContext) { +func StartGossiperServer(c *gossip.GossiperContext) { // Check if the storage file exists in this directory err := c.LoadStorage() if err != nil { diff --git a/CTng/HTTP/Logger.go b/CTng/server/Logger.go similarity index 79% rename from CTng/HTTP/Logger.go rename to CTng/server/Logger.go index 2513ba7..bed9519 100644 --- a/CTng/HTTP/Logger.go +++ b/CTng/server/Logger.go @@ -4,6 +4,10 @@ import ( "net/http" ) +/* For now, this file represents the functionality required of a ctng logger. +It should be implemented in the future. For now, we utilize fakelogger (in the testData folder) for testing. +*/ + // Struct for JSON STH object type STH struct { date string `json:"date"` diff --git a/CTng/HTTP/Monitor_ng.go b/CTng/server/Monitor_ng.go similarity index 94% rename from CTng/HTTP/Monitor_ng.go rename to CTng/server/Monitor_ng.go index 818c398..836fc3c 100644 --- a/CTng/HTTP/Monitor_ng.go +++ b/CTng/server/Monitor_ng.go @@ -3,6 +3,7 @@ package server import ( "CTng/crypto" "CTng/gossip" + "CTng/monitor" "bytes" "encoding/json" "errors" @@ -39,7 +40,7 @@ func Monitor_accuseEntity(c *crypto.CryptoConfig, Accused string) (gossip.Gossip } //just reused Finn's code for sending gossip object, changed owner URL to Gossiper URL -func Monitor_send_to_gossiper(c *MonitorContext, g gossip.Gossip_object) { +func Monitor_send_to_gossiper(c *monitor.MonitorContext, g gossip.Gossip_object) { // Convert gossip object to JSON msg, err := json.Marshal(g) if err != nil { @@ -59,7 +60,7 @@ func Monitor_send_to_gossiper(c *MonitorContext, g gossip.Gossip_object) { } //Need to discuss about this ****************************************************** -func Monitor_process_valid_object(c *MonitorContext, g gossip.Gossip_object) { +func Monitor_process_valid_object(c *monitor.MonitorContext, g gossip.Gossip_object) { for _, v := range c.Config.Logger_URLs { //if the valid object is from the logger in the monitor config logger URL list //This handles the STHS @@ -113,7 +114,7 @@ func Monitor_process_valid_object(c *MonitorContext, g gossip.Gossip_object) { //this function takes the name of the entity as input and check if there is a POM against it //this should be invoked after the monitor receives the information from its loggers and CAs prior to threshold signning it -func Monitor_check_entity_pom(c *MonitorContext, name string) error { +func Monitor_check_entity_pom(c *monitor.MonitorContext, name string) error { for _, v := range *c.Storage { if v.Payload[0] == name && (v.Type == gossip.ACCUSATION_POM || v.Type == gossip.APPLICATION_POM || v.Type == gossip.GOSSIP_POM) { return errors.New("There exists a proof of misbehavior against this entity") @@ -122,7 +123,7 @@ func Monitor_check_entity_pom(c *MonitorContext, name string) error { return nil } -func Monitor_handle_gossip(c *MonitorContext, w http.ResponseWriter, r *http.Request) { +func Monitor_handle_gossip(c *monitor.MonitorContext, w http.ResponseWriter, r *http.Request) { // Parse sent object. // Converts JSON passed in the body of a POST to a Gossip_object. var gossip_obj gossip.Gossip_object diff --git a/CTng/HTTP/Monitor.go b/CTng/server/Monitor_server.go similarity index 62% rename from CTng/HTTP/Monitor.go rename to CTng/server/Monitor_server.go index 21f4b72..02ef6e9 100644 --- a/CTng/HTTP/Monitor.go +++ b/CTng/server/Monitor_server.go @@ -2,6 +2,7 @@ package server import ( "CTng/gossip" + "CTng/monitor" "encoding/json" "fmt" "io/ioutil" @@ -15,13 +16,13 @@ import ( ) // Binds the context to the functions we pass to the router. -func bindMonitorContext(context *MonitorContext, fn func(context *MonitorContext, w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { +func bindMonitorContext(context *monitor.MonitorContext, fn func(context *monitor.MonitorContext, w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { fn(context, w, r) } } -func handleMonitorRequests(c *MonitorContext) { +func handleMonitorRequests(c *monitor.MonitorContext) { // MUX which routes HTTP directories to functions. gorillaRouter := mux.NewRouter().StrictSlash(true) @@ -42,7 +43,7 @@ func handleMonitorRequests(c *MonitorContext) { log.Fatal(http.ListenAndServe(":"+c.Config.Port, nil)) } -func receiveGossip(c *MonitorContext, w http.ResponseWriter, r *http.Request) { +func receiveGossip(c *monitor.MonitorContext, w http.ResponseWriter, r *http.Request) { // Post request, parse sent object. body, err := ioutil.ReadAll(r.Body) @@ -71,7 +72,7 @@ func receiveGossip(c *MonitorContext, w http.ResponseWriter, r *http.Request) { // TODO - Validate, parse, and store postData } -func receivePOM(c *MonitorContext, w http.ResponseWriter, r *http.Request) { +func receivePOM(c *monitor.MonitorContext, w http.ResponseWriter, r *http.Request) { // Post request, parse sent object. body, err := ioutil.ReadAll(r.Body) @@ -86,7 +87,7 @@ func receivePOM(c *MonitorContext, w http.ResponseWriter, r *http.Request) { // TODO - Validate, process and save PoM } -func getRevocations(c *MonitorContext, w http.ResponseWriter, r *http.Request) { +func getRevocations(c *monitor.MonitorContext, w http.ResponseWriter, r *http.Request) { // if no revocation data found, return a 404 http.Error(w, "Revocation information not found.", 404) @@ -94,7 +95,7 @@ func getRevocations(c *MonitorContext, w http.ResponseWriter, r *http.Request) { // if revocations found, send to requester } -func getRevocation(c *MonitorContext, w http.ResponseWriter, r *http.Request) { +func getRevocation(c *monitor.MonitorContext, w http.ResponseWriter, r *http.Request) { // Get {date} from the end of the URL date := path.Base(r.URL.Path) @@ -106,7 +107,7 @@ func getRevocation(c *MonitorContext, w http.ResponseWriter, r *http.Request) { // if revocations found, send to requester } -func getSTH(c *MonitorContext, w http.ResponseWriter, r *http.Request) { +func getSTH(c *monitor.MonitorContext, w http.ResponseWriter, r *http.Request) { // Get {date} from the end of the URL date := path.Base(r.URL.Path) @@ -118,7 +119,7 @@ func getSTH(c *MonitorContext, w http.ResponseWriter, r *http.Request) { // if STH found, send to requester } -func getPOM(c *MonitorContext, w http.ResponseWriter, r *http.Request) { +func getPOM(c *monitor.MonitorContext, w http.ResponseWriter, r *http.Request) { // Get {date} from the end of the URL date := path.Base(r.URL.Path) @@ -130,70 +131,7 @@ func getPOM(c *MonitorContext, w http.ResponseWriter, r *http.Request) { // if POM found, send to requester } -func QueryLoggers(loggerURLs []string) { - for _, logger := range loggerURLs { - - // Get today's STH from logger. - - // Get today's date in format YYYY-MM-DD - var today = time.Now().UTC().Format(time.RFC3339)[0:10] - - sthResp, err := http.Get(logger + "ctng/v1/get-sth/" + today) - if err != nil { - log.Fatalln(err) - } - - sthBody, err := ioutil.ReadAll(sthResp.Body) - if err != nil { - log.Fatalln(err) - } - STH := string(sthBody) - fmt.Printf("STH from logger " + logger + ": " + STH + "\n") //temp - - // Get today's entries from logger - entriesResp, err := http.Get(logger + "ctng/v1/get-entries/" + today) - if err != nil { - log.Fatalln(err) - } - - entiresBody, err := ioutil.ReadAll(entriesResp.Body) - if err != nil { - log.Fatalln(err) - } - entries := string(entiresBody) - fmt.Printf("Entries from logger " + logger + ": " + entries + "\n") //temp - - // TODO - process STH and entries - } - -} - -func QueryAuthorities(authURLs []string) { - for _, CA := range authURLs { - - // Get today's revocation information from CA. - - // Get today's date in format YYYY-MM-DD - var today = time.Now().UTC().Format(time.RFC3339)[0:10] - - revResp, err := http.Get(CA + "/ctng/v1/get-revocations/" + today) - if err != nil { - log.Fatalln(err) - } - - revBody, err := ioutil.ReadAll(revResp.Body) - if err != nil { - log.Fatalln(err) - } - rev := string(revBody) - fmt.Printf("Revocation information from CA " + CA + ": " + rev + "\n") - - // TODO - process revocation data - } - -} - -func StartMonitor(c *MonitorContext) { +func StartMonitorServer(c *monitor.MonitorContext) { // Check if the storage file exists in this directory err := c.LoadStorage() @@ -218,16 +156,16 @@ func StartMonitor(c *MonitorContext) { queryInterval := time.Hour * 24 // Perform startup queries - QueryLoggers(c.Config.Logger_URLs) - QueryAuthorities(c.Config.CA_URLs) + monitor.QueryLoggers(c.Config.Logger_URLs) + monitor.QueryAuthorities(c.Config.CA_URLs) queryTime := time.Now() // Monitor client loop for { // Check if enough time has passed to query loggers and CAs again if time.Since(queryTime) >= queryInterval { - QueryLoggers(c.Config.Logger_URLs) - QueryAuthorities(c.Config.CA_URLs) + monitor.QueryLoggers(c.Config.Logger_URLs) + monitor.QueryAuthorities(c.Config.CA_URLs) queryTime = time.Now() } time.Sleep(time.Second) diff --git a/CTng/HTTP/Readme.md b/CTng/server/Readme.md similarity index 100% rename from CTng/HTTP/Readme.md rename to CTng/server/Readme.md diff --git a/CTng/HTTP/helpers.go b/CTng/server/helpers.go similarity index 100% rename from CTng/HTTP/helpers.go rename to CTng/server/helpers.go diff --git a/CTng/util/types.go b/CTng/util/types.go new file mode 100644 index 0000000..813cf39 --- /dev/null +++ b/CTng/util/types.go @@ -0,0 +1,12 @@ +package util + +// Terminal colors. Use by adding the color to the beginning of a string. +// add RESET after the string to reset the color: otherwise all text will remain this color +// in future prints. +const ( + RED = "\x1b[31m" + GREEN = "\x1b[32m" + YELLOW = "\x1b[33m" + BLUE = "\x1b[34m" + RESET = "\x1b[0m" +)