diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 5bafbab..d1d8ba1 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -6,26 +6,44 @@ package protocol import ( + "github.com/coniks-sys/coniks-go/crypto" "github.com/coniks-sys/coniks-go/crypto/sign" ) type directoryHistory struct { + dirName string signKey sign.PublicKey snapshots map[uint64]*DirSTR latestSTR *DirSTR } // A ConiksAuditLog maintains the histories -// of all CONIKS directories known to a CONIKS auditor. -// Each history includes the directory's public signing key -// enabling the auditor to verify the corresponding signed -// tree roots. +// of all CONIKS directories known to a CONIKS auditor, +// indexing the histories by the hash of a directory's initial +// STR. +// Each history includes the directory's domain name as a string, its +// public signing key enabling the auditor to verify the corresponding +// signed tree roots, and a list with all observed snapshots in +// chronological order. type ConiksAuditLog struct { - histories map[string]*directoryHistory + histories map[[crypto.HashSizeByte]byte]*directoryHistory } -func newDirectoryHistory(signKey sign.PublicKey, str *DirSTR) *directoryHistory { +// computeInitSTRHash is a wrapper for the digest function; +// returns nil if the STR isn't an initial STR (i.e. str.Epoch != 0) +func computeInitSTRHash(initSTR *DirSTR) [crypto.HashSizeByte]byte { + var initSTRHash [crypto.HashSizeByte]byte + + if initSTR.Epoch == 0 { + // ugh HACK: need to call copy to convert slice into the array + copy(initSTRHash[:], crypto.Digest(initSTR.Serialize())) + } + return initSTRHash +} + +func newDirectoryHistory(dirName string, signKey sign.PublicKey, str *DirSTR) *directoryHistory { h := new(directoryHistory) + h.dirName = dirName h.signKey = signKey h.snapshots = make(map[uint64]*DirSTR) h.latestSTR = str @@ -37,16 +55,17 @@ func newDirectoryHistory(signKey sign.PublicKey, str *DirSTR) *directoryHistory // the first time it observes an STR for that directory. func NewAuditLog() *ConiksAuditLog { l := new(ConiksAuditLog) - l.histories = make(map[string]*directoryHistory) + l.histories = make(map[[crypto.HashSizeByte]byte]*directoryHistory) return l } // IsKnownDirectory checks to see if an entry for the directory -// address addr exists in the audit log l. IsKnownDirectory() does not +// (indexed by the hash of its initial STR dirInitHash) exists +// in the audit log l. IsKnownDirectory() does not // validate the entries themselves. It returns true if an entry exists, // and false otherwise. -func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { - h := l.histories[addr] +func (l *ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) bool { + h := l.histories[dirInitHash] if h != nil { return true } @@ -70,19 +89,26 @@ func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, oldSTRs map[uint64]*DirSTR, latestSTR *DirSTR) error { - // error if we want to create a new entry for an addr we already know - if l.IsKnownDirectory(addr) { - return ErrAuditLog - } - // make sure we have oldEpochs unless we're inserting right // at the start of a directory's history if oldSTRs == nil && latestSTR.Epoch > 0 { return ErrMalformedDirectoryMessage } + // compute the hash of the initial STR + dirInitHash := computeInitSTRHash(latestSTR) + if oldSTRs != nil { + dirInitHash = computeInitSTRHash(oldSTRs[0]) + } + + // error if we want to create a new entry for a directory + // we already know + if l.IsKnownDirectory(dirInitHash) { + return ErrAuditLog + } + // create the new directory history - h := newDirectoryHistory(signKey, latestSTR) + h := newDirectoryHistory(addr, signKey, latestSTR) startEp := uint64(0) endEp := latestSTR.Epoch @@ -101,8 +127,7 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // don't forget to add the latest STR h.snapshots[endEp] = latestSTR - - l.histories[addr] = h + l.histories[dirInitHash] = h // FIXME: verify the consistency of each new STR return nil @@ -116,14 +141,14 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // addr prior to its first call and thereby expects that an entry for addr // exists in the audit log l. // FIXME: pass Response message as param -func (l *ConiksAuditLog) Update(addr string, newSTR *DirSTR) error { +func (l *ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *DirSTR) error { // error if we want to update the entry for an addr we don't know - if !l.IsKnownDirectory(addr) { + if !l.IsKnownDirectory(dirInitHash) { return ErrAuditLog } - h := l.histories[addr] + h := l.histories[dirInitHash] if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { return err @@ -156,20 +181,15 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *DirSTR) error { func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, ErrorCode) { - // make sure the request is well-formed - if len(req.DirectoryAddr) <= 0 || req.StartEpoch > req.EndEpoch { - return NewErrorResponse(ErrMalformedClientMessage), - ErrMalformedClientMessage - } - - h := l.histories[req.DirectoryAddr] - - if h == nil { + // make sure we have a history for the requested directory in the log + if !l.IsKnownDirectory(req.DirInitSTRHash) { return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } - // also make sure the epoch range is well-formed - if req.StartEpoch > h.latestSTR.Epoch || req.EndEpoch > h.latestSTR.Epoch { + h := l.histories[req.DirInitSTRHash] + + // make sure the request is well-formed + if req.EndEpoch > h.latestSTR.Epoch || req.StartEpoch > req.EndEpoch { return NewErrorResponse(ErrMalformedClientMessage), ErrMalformedClientMessage } diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index f24f0c3..4b3eb13 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -1,6 +1,7 @@ package protocol import ( + "github.com/coniks-sys/coniks-go/crypto" "testing" ) @@ -26,8 +27,9 @@ func TestUpdateHistory(t *testing.T) { } // update the directory so we can update the audit log + dirInitHash := computeInitSTRHash(d.LatestSTR()) d.Update() - err = aud.Update("test-server", d.LatestSTR()) + err = aud.Update(dirInitHash, d.LatestSTR()) if err != nil { t.Fatal("Error updating the server history") @@ -81,7 +83,8 @@ func TestUpdateUnknownHistory(t *testing.T) { // let's make sure that we can't re-insert a new server // history into our log - err = aud.Update("unknown", d.LatestSTR()) + var unknown [crypto.HashSizeByte]byte + err = aud.Update(unknown, d.LatestSTR()) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when updating an unknown server history") } @@ -96,10 +99,13 @@ func TestGetLatestObservedSTR(t *testing.T) { t.Fatal("Error inserting new server history") } + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(d.LatestSTR()) + res, err := aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(d.LatestSTR().Epoch), - EndEpoch: uint64(d.LatestSTR().Epoch)}) + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(d.LatestSTR().Epoch), + EndEpoch: uint64(d.LatestSTR().Epoch)}) if err != ReqSuccess { t.Fatal("Unable to get latest observed STR") } @@ -125,6 +131,9 @@ func TestGetObservedSTRInEpoch(t *testing.T) { d.Update() } + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(priorSTRs[0]) + // now insert into the log err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) if err != nil { @@ -132,9 +141,9 @@ func TestGetObservedSTRInEpoch(t *testing.T) { } res, err := aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(6), - EndEpoch: uint64(8)}) + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(6), + EndEpoch: uint64(8)}) if err != ReqSuccess { t.Fatal("Unable to get latest range of STRs") @@ -170,18 +179,19 @@ func TestGetObservedSTRUnknown(t *testing.T) { t.Fatal("Error inserting new server history with prior STRs") } + var unknown [crypto.HashSizeByte]byte _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "unknown", - StartEpoch: uint64(0), - EndEpoch: uint64(d.LatestSTR().Epoch)}) + DirInitSTRHash: unknown, + StartEpoch: uint64(d.LatestSTR().Epoch), + EndEpoch: uint64(d.LatestSTR().Epoch)}) if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for latest STR") } _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "unknown", - StartEpoch: uint64(6), - EndEpoch: uint64(8)}) + DirInitSTRHash: unknown, + StartEpoch: uint64(6), + EndEpoch: uint64(8)}) if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for older STR") } @@ -200,47 +210,27 @@ func TestGetObservedSTRMalformed(t *testing.T) { d.Update() } + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(priorSTRs[0]) + // now insert into the log err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) if err != nil { t.Fatal("Error inserting new server history with prior STRs") } - _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "", - StartEpoch: uint64(0), - EndEpoch: uint64(d.LatestSTR().Epoch)}) - if err != ErrMalformedClientMessage { - t.Fatal("Expect ErrMalFormedClientMessage for latest STR") - } - - _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "", - StartEpoch: uint64(4), - EndEpoch: uint64(6)}) - if err != ErrMalformedClientMessage { - t.Fatal("Expect ErrMalformedClientMessage for older STR") - } - // also test the epoch range _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(6), - EndEpoch: uint64(4)}) + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(6), + EndEpoch: uint64(4)}) if err != ErrMalformedClientMessage { t.Fatal("Expect ErrMalformedClientMessage for bad end epoch") } _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(11), - EndEpoch: uint64(11)}) - if err != ErrMalformedClientMessage { - t.Fatal("Expect ErrMalformedClientMessage for bad start epoch") - } - _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(6), - EndEpoch: uint64(11)}) + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(6), + EndEpoch: uint64(11)}) if err != ErrMalformedClientMessage { t.Fatal("Expect ErrMalformedClientMessage for out-of-bounds epoch range") } diff --git a/protocol/message.go b/protocol/message.go index 1b5310f..bd3d58c 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -4,7 +4,10 @@ package protocol -import m "github.com/coniks-sys/coniks-go/merkletree" +import ( + "github.com/coniks-sys/coniks-go/crypto" + m "github.com/coniks-sys/coniks-go/merkletree" +) // The types of requests CONIKS clients send during the CONIKS protocols. const ( @@ -100,9 +103,9 @@ type MonitoringRequest struct { // The response to a successful request is an STRHistoryRange with // a list of STRs covering the epoch range [StartEpoch, EndEpoch]. type AuditingRequest struct { - DirectoryAddr string - StartEpoch uint64 - EndEpoch uint64 + DirInitSTRHash [crypto.HashSizeByte]byte + StartEpoch uint64 + EndEpoch uint64 } // A Response message indicates the result of a CONIKS client request