From 1db660bb33662e469631fb497dae005a7f4441a6 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Mon, 3 Jul 2017 19:07:39 -0400 Subject: [PATCH] Insert latest STR into snapshot map right away --- protocol/auditlog.go | 86 +++++++++--------- protocol/auditlog_test.go | 185 +++++++++++++++----------------------- protocol/testutil.go | 26 ++++++ 3 files changed, 142 insertions(+), 155 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index d1d8ba1..8c8b3c7 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -25,9 +25,7 @@ type directoryHistory struct { // 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[[crypto.HashSizeByte]byte]*directoryHistory -} +type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory // computeInitSTRHash is a wrapper for the digest function; // returns nil if the STR isn't an initial STR (i.e. str.Epoch != 0) @@ -35,27 +33,27 @@ 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 + // 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 { +func newDirectoryHistory(dirName string, signKey sign.PublicKey) *directoryHistory { h := new(directoryHistory) h.dirName = dirName h.signKey = signKey h.snapshots = make(map[uint64]*DirSTR) - h.latestSTR = str + h.latestSTR = nil return h } // NewAuditLog constructs a new ConiksAuditLog. It creates an empty // log; the auditor will add an entry for each CONIKS directory // the first time it observes an STR for that directory. -func NewAuditLog() *ConiksAuditLog { - l := new(ConiksAuditLog) - l.histories = make(map[[crypto.HashSizeByte]byte]*directoryHistory) +func NewAuditLog() ConiksAuditLog { + l := make(map[[crypto.HashSizeByte]byte]*directoryHistory) return l } @@ -64,20 +62,26 @@ func NewAuditLog() *ConiksAuditLog { // 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(dirInitHash [crypto.HashSizeByte]byte) bool { - h := l.histories[dirInitHash] +func (l ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) bool { + h := l[dirInitHash] if h != nil { return true } return false } +// updateLatestSTR inserts a new STR into a directory history; +// assumes the STR has been validated by the caller +func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { + h.snapshots[newLatest.Epoch] = newLatest + h.latestSTR = newLatest +} + // Insert creates a new directory history for the key directory addr // and inserts it into the audit log l. // The directory history is initialized with the key directory's -// signing key signKey, a list of STRs representing the -// directory's prior history oldSTRs, and the directory's latest STR -// latestSTR. +// signing key signKey, and a list of STRs representing the +// directory's STR history so far (as a map of epochs to STRs). // Insert() returns an ErrAuditLog if the auditor attempts to create // a new history for a known directory, an ErrMalformedDirectoryMessage // if oldSTRs is malformed, and nil otherwise. @@ -86,20 +90,16 @@ func (l *ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) // FIXME: pass Response message as param // masomel: will probably want to write a more generic function // for "catching up" on a history in case an auditor misses epochs -func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, - oldSTRs map[uint64]*DirSTR, latestSTR *DirSTR) error { +func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, + hist map[uint64]*DirSTR) error { - // make sure we have oldEpochs unless we're inserting right - // at the start of a directory's history - if oldSTRs == nil && latestSTR.Epoch > 0 { + // make sure we're getting an initial STR at the very least + if len(hist) < 1 && hist[0].Epoch != 0 { return ErrMalformedDirectoryMessage } // compute the hash of the initial STR - dirInitHash := computeInitSTRHash(latestSTR) - if oldSTRs != nil { - dirInitHash = computeInitSTRHash(oldSTRs[0]) - } + dirInitHash := computeInitSTRHash(hist[0]) // error if we want to create a new entry for a directory // we already know @@ -108,28 +108,30 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, } // create the new directory history - h := newDirectoryHistory(addr, signKey, latestSTR) + h := newDirectoryHistory(addr, signKey) startEp := uint64(0) - endEp := latestSTR.Epoch + endEp := uint64(len(hist)) - // add each old STR into the history - // This loop automatically catches if oldSTRs is malformed - // (i.e. oldSTRs starts at epoch 0 and - // len(oldSTRs) != latestSTR.Epoch-1) + // add each STR into the history + // This loop automatically catches if hist is malformed + // (i.e. hist is missing and epoch) + // FIXME: verify the consistency of each new STR before inserting + // into the audit log for ep := startEp; ep < endEp; ep++ { - str := oldSTRs[ep] + str := hist[ep] if str == nil { return ErrMalformedDirectoryMessage } h.snapshots[ep] = str } - // don't forget to add the latest STR - h.snapshots[endEp] = latestSTR - l.histories[dirInitHash] = h + // Make sure to update the latestSTR + // in this particular call, the latestSTR has already been + // inserted into the snapshots map in the loop above + h.updateLatestSTR(hist[endEp-1]) + l[dirInitHash] = h - // FIXME: verify the consistency of each new STR return nil } @@ -141,22 +143,21 @@ 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(dirInitHash [crypto.HashSizeByte]byte, 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(dirInitHash) { return ErrAuditLog } - h := l.histories[dirInitHash] + h := l[dirInitHash] if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { return err } // update the latest STR - h.snapshots[h.latestSTR.Epoch] = h.latestSTR - h.latestSTR = newSTR + h.updateLatestSTR(newSTR) return nil } @@ -178,7 +179,7 @@ func (l *ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *D // If the auditor doesn't have any history entries for the requested CONIKS // directory, GetObservedSTRs() returns a // message.NewErrorResponse(ReqUnknownDirectory) tuple. -func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, +func (l ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, ErrorCode) { // make sure we have a history for the requested directory in the log @@ -186,7 +187,7 @@ func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } - h := l.histories[req.DirInitSTRHash] + h := l[req.DirInitSTRHash] // make sure the request is well-formed if req.EndEpoch > h.latestSTR.Epoch || req.StartEpoch > req.EndEpoch { @@ -195,13 +196,10 @@ func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, } var strs []*DirSTR - for ep := req.StartEpoch; ep < req.EndEpoch; ep++ { + for ep := req.StartEpoch; ep <= req.EndEpoch; ep++ { str := h.snapshots[ep] strs = append(strs, str) } - // don't forget to append the STR for EndEpoch - strs = append(strs, h.snapshots[req.EndEpoch]) - return NewSTRHistoryRange(strs) } diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index 4b3eb13..a59774b 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -6,30 +6,18 @@ import ( ) func TestInsertEmptyHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") - } + // create basic test directory and audit log with 1 STR + _, _, _ = NewTestAuditLog(t, 0) } func TestUpdateHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") - } + // create basic test directory and audit log with 1 STR + d, aud, hist := NewTestAuditLog(t, 0) // update the directory so we can update the audit log - dirInitHash := computeInitSTRHash(d.LatestSTR()) + dirInitHash := computeInitSTRHash(hist[0]) d.Update() - err = aud.Update(dirInitHash, d.LatestSTR()) + err := aud.Update(dirInitHash, d.LatestSTR()) if err != nil { t.Fatal("Error updating the server history") @@ -37,70 +25,59 @@ func TestUpdateHistory(t *testing.T) { } func TestInsertPriorHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - // create 10 epochs - priorSTRs := make(map[uint64]*DirSTR) - for i := 0; i < 10; i++ { - priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() - d.Update() - } - - // now insert - err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history with prior STRs") - } + // create basic test directory and audit log with 11 STRs + _, _, _ = NewTestAuditLog(t, 10) } func TestInsertExistingHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") - } + // create basic test directory and audit log with 1 STR + _, aud, hist := NewTestAuditLog(t, 0) // let's make sure that we can't re-insert a new server // history into our log - err = aud.Insert("test-server", pk, nil, d.LatestSTR()) + err := aud.Insert("test-server", nil, hist) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when inserting an existing server history") } } func TestUpdateUnknownHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") - } + // create basic test directory and audit log with 1 STR + d, aud, _ := NewTestAuditLog(t, 0) - // let's make sure that we can't re-insert a new server - // history into our log + // let's make sure that we can't update a history for an unknown + // directory in our log var unknown [crypto.HashSizeByte]byte - err = aud.Update(unknown, d.LatestSTR()) + err := aud.Update(unknown, d.LatestSTR()) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when updating an unknown server history") } } -func TestGetLatestObservedSTR(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") +func TestUpdateBadNewSTR(t *testing.T) { + // create basic test directory and audit log with 11 STRs + d, aud, hist := NewTestAuditLog(t, 10) + + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(hist[0]) + + // update the directory a few more times and then try + // to update + d.Update() + d.Update() + + err := aud.Update(dirInitHash, d.LatestSTR()) + if err != CheckBadSTR { + t.Fatal("Expected a CheckBadSTR when attempting update a server history with a bad STR") } +} + +func TestGetLatestObservedSTR(t *testing.T) { + // create basic test directory and audit log with 1 STR + d, aud, hist := NewTestAuditLog(t, 0) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(d.LatestSTR()) + dirInitHash := computeInitSTRHash(hist[0]) res, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, @@ -111,34 +88,20 @@ func TestGetLatestObservedSTR(t *testing.T) { } obs := res.DirectoryResponse.(*STRHistoryRange) - if len(obs.STR) != 1 { + if len(obs.STR) == 0 { t.Fatal("Expect returned STR to be not nil") } if obs.STR[0].Epoch != d.LatestSTR().Epoch { - t.Fatal("Unexpected epoch for returned STR") + t.Fatal("Unexpected epoch for returned latest STR") } } func TestGetObservedSTRInEpoch(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - // create 10 epochs - priorSTRs := make(map[uint64]*DirSTR) - for i := 0; i < 10; i++ { - priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() - d.Update() - } + // create basic test directory and audit log with 11 STRs + _, aud, hist := NewTestAuditLog(t, 10) // 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") - } + dirInitHash := computeInitSTRHash(hist[0]) res, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, @@ -161,26 +124,40 @@ func TestGetObservedSTRInEpoch(t *testing.T) { } } -func TestGetObservedSTRUnknown(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() +func TestGetEntireObservedSTRHist(t *testing.T) { + // create basic test directory and audit log with 2 STRs + d, aud, hist := NewTestAuditLog(t, 1) - // create 10 epochs - priorSTRs := make(map[uint64]*DirSTR) - for i := 0; i < 10; i++ { - priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() - d.Update() + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(hist[0]) + + res, err := aud.GetObservedSTRs(&AuditingRequest{ + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(0), + EndEpoch: d.LatestSTR().Epoch}) + + if err != ReqSuccess { + t.Fatal("Unable to get latest range of STRs") } - // 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") + obs := res.DirectoryResponse.(*STRHistoryRange) + if len(obs.STR) != 2 { + t.Fatal("Unexpected number of returned STRs") + } + if obs.STR[0].Epoch != 0 { + t.Fatal("Unexpected initial epoch for returned STR range") + } + if obs.STR[1].Epoch != d.LatestSTR().Epoch { + t.Fatal("Unexpected latest STR epoch for returned STR") } +} + +func TestGetObservedSTRUnknown(t *testing.T) { + // create basic test directory and audit log with 11 STRs + d, aud, _ := NewTestAuditLog(t, 10) var unknown [crypto.HashSizeByte]byte - _, err = aud.GetObservedSTRs(&AuditingRequest{ + _, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: unknown, StartEpoch: uint64(d.LatestSTR().Epoch), EndEpoch: uint64(d.LatestSTR().Epoch)}) @@ -199,28 +176,14 @@ func TestGetObservedSTRUnknown(t *testing.T) { } func TestGetObservedSTRMalformed(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - // create 10 epochs - priorSTRs := make(map[uint64]*DirSTR) - for i := 0; i < 10; i++ { - priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() - d.Update() - } + // create basic test directory and audit log with 11 STRs + _, aud, hist := NewTestAuditLog(t, 10) // 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") - } + dirInitHash := computeInitSTRHash(hist[0]) // also test the epoch range - _, err = aud.GetObservedSTRs(&AuditingRequest{ + _, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, StartEpoch: uint64(6), EndEpoch: uint64(4)}) diff --git a/protocol/testutil.go b/protocol/testutil.go index 4e464ba..ee2bbb4 100644 --- a/protocol/testutil.go +++ b/protocol/testutil.go @@ -28,3 +28,29 @@ func NewTestDirectory(t *testing.T, useTBs bool) ( d := NewDirectory(1, vrfKey, signKey, 10, useTBs) return d, pk } + +// NewTestAuditLog creates a ConiksAuditLog and corresponding +// ConiksDirectory used for testing auditor-side CONIKS operations. +// The new audit log can be initialized with the number of epochs +// indicating the length of the directory history with which to +// initialize the log; if numEpochs > 0, the history contains numEpochs+1 +// STRs as it always includes the STR after the last directory update +func NewTestAuditLog(t *testing.T, numEpochs int) (*ConiksDirectory, ConiksAuditLog, map[uint64]*DirSTR) { + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + hist := make(map[uint64]*DirSTR) + for ep := 0; ep < numEpochs; ep++ { + hist[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + // always include the actual latest STR + hist[d.LatestSTR().Epoch] = d.LatestSTR() + + err := aud.Insert("test-server", pk, hist) + if err != nil { + t.Fatal("Error inserting a new history with %d epochs", numEpochs) + } + + return d, aud, hist +}