diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index cd97b73..f24570b 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -25,8 +25,6 @@ import ( // subsequent responses from the ConiksDirectory to any // client request. type ConsistencyChecks struct { - // PinnedSTR stores the pinned signed tree root at epoch 0. This is hardcoded into the client. - PinnedSTR *m.SignedTreeRoot // SavedSTR stores the latest verified signed tree root. SavedSTR *m.SignedTreeRoot // Bindings stores all the verified name-to-key bindings. @@ -47,22 +45,17 @@ type ConsistencyChecks struct { // TODO: maybe remove savedSTR from the parameters // and have something like RestoreState(savedSTR, bindings, regepoch) instead. // Possibly it somehow relate to #137 (client storage). -func NewCC(pinnedSTR, savedSTR *m.SignedTreeRoot, useTBs bool, signKey sign.PublicKey) *ConsistencyChecks { +func NewCC(savedSTR *m.SignedTreeRoot, useTBs bool, signKey sign.PublicKey) *ConsistencyChecks { // TODO: see #110 if !useTBs { panic("[coniks] Currently the server is forced to use TBs") } - // TODO: this will break our test client - if pinnedSTR == nil { - panic("[coniks] ConsistencyChecks requires a pinned STR at epoch 0") - } cc := &ConsistencyChecks{ - PinnedSTR: pinnedSTR, - SavedSTR: savedSTR, - Bindings: make(map[string][]byte), - RegEpoch: make(map[string]uint64), - useTBs: useTBs, - signKey: signKey, + SavedSTR: savedSTR, + Bindings: make(map[string][]byte), + RegEpoch: make(map[string]uint64), + useTBs: useTBs, + signKey: signKey, } if useTBs { cc.TBs = make(map[string]*TemporaryBinding) @@ -141,14 +134,15 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { } case MonitoringType: - // the requested epoch can be equal to saved epoch or the next expected epoch - // or epoch 0. + // the requested epoch can be equal to/or less than + // the saved epoch or the next expected epoch strs := msg.DirectoryResponse.(*DirectoryProofs).STR switch { - case strs[0].Epoch == 0: /* prior history verification */ - if err := verifySTR(cc.PinnedSTR, strs[0]); err != nil { - return err - } + case strs[0].Epoch < cc.SavedSTR.Epoch: /* prior history verification */ + // FIXME: TOFU until we introduce auditor modules? + // if err := verifySTR(???, strs[0]); err != nil { + // return err + // } case strs[0].Epoch == cc.SavedSTR.Epoch: if err := verifySTR(cc.SavedSTR, strs[0]); err != nil { return err @@ -158,7 +152,7 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { return err } default: - panic("[coniks] The next expected monitoring epochs should be read from SavedSTR.Epoch.") + panic("[coniks] The monitoring epochs cannot be greater than current the saved epoch") } for i := 1; i < len(strs); i++ { @@ -166,7 +160,10 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { return err } } - str = strs[len(strs)-1] + str = cc.SavedSTR + if strs[len(strs)-1].Epoch > str.Epoch { + str = strs[len(strs)-1] + } default: panic("[coniks] Unknown request type") @@ -273,9 +270,11 @@ func (cc *ConsistencyChecks) verifyMonitoring(msg *Response, ap0 := dfs.AP[0] regEp, ok := cc.RegEpoch[uname] + // TODO: is this actually faster? wasUnameAbsent := ap0.ProofType() == m.ProofOfAbsence switch { - case str0.Epoch == 0 && wasUnameAbsent: /* prior history verification */ + case !ok && str0.Epoch < cc.SavedSTR.Epoch && wasUnameAbsent: /* prior history verification */ + case ok && str0.Epoch < regEp && wasUnameAbsent: /* prior history verification */ case ok && str0.Epoch == regEp && wasUnameAbsent: /* registration epoch */ case ok && str0.Epoch >= regEp+1 && !wasUnameAbsent: /* after registration */ default: @@ -289,8 +288,9 @@ func (cc *ConsistencyChecks) verifyMonitoring(msg *Response, str := dfs.STR[i] ap := dfs.AP[i] switch { - case str0.Epoch == 0 && ap.ProofType() == m.ProofOfAbsence: /* prior history verification */ - case str0.Epoch > 0 && ap.ProofType() == m.ProofOfInclusion: + case !ok && ap.ProofType() == m.ProofOfAbsence: + case str0.Epoch <= cc.SavedSTR.Epoch && ap.ProofType() == m.ProofOfAbsence: /* prior history verification */ + case str0.Epoch > cc.SavedSTR.Epoch && ap.ProofType() == m.ProofOfInclusion: default: return CheckBadAuthPath } diff --git a/protocol/consistencychecks_test.go b/protocol/consistencychecks_test.go index 570eafe..21fa548 100644 --- a/protocol/consistencychecks_test.go +++ b/protocol/consistencychecks_test.go @@ -51,7 +51,7 @@ func TestVerifyWithError(t *testing.T) { str.Signature = append([]byte{}, str.Signature...) str.Signature[0]++ - cc := NewCC(d.LatestSTR(), &str, true, pk) + cc := NewCC(&str, true, pk) if e1, e2 := registerAndVerify(d, cc, alice, key); e1 != ReqSuccess || e2 != CheckBadSTR { t.Error("Expect", ReqSuccess, "got", e1) @@ -61,7 +61,7 @@ func TestVerifyWithError(t *testing.T) { func TestMalformedClientMessage(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) request := &RegistrationRequest{ Username: "", // invalid username @@ -75,7 +75,7 @@ func TestMalformedClientMessage(t *testing.T) { func TestMalformedDirectoryMessage(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) request := &RegistrationRequest{ Username: "alice", @@ -92,7 +92,7 @@ func TestMalformedDirectoryMessage(t *testing.T) { func TestVerifyRegistrationResponseWithTB(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) if e1, e2 := registerAndVerify(d, cc, alice, key); e1 != ReqSuccess || e2 != CheckPassed { t.Error(e1) @@ -136,7 +136,7 @@ func TestVerifyRegistrationResponseWithTB(t *testing.T) { func TestVerifyFullfilledPromise(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) if e1, e2 := registerAndVerify(d, cc, alice, key); e1 != ReqSuccess || e2 != CheckPassed { t.Error(e1) @@ -177,7 +177,7 @@ func TestVerifyFullfilledPromise(t *testing.T) { func TestVerifyKeyLookupResponseWithTB(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) // do lookup first if e1, e2 := lookupAndVerify(d, cc, alice, key); e1 != ReqNameNotFound || e2 != CheckPassed { @@ -236,7 +236,7 @@ func TestVerifyKeyLookupResponseWithTB(t *testing.T) { func TestVerifyTimeSkew(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) N := 5 @@ -267,7 +267,7 @@ func TestVerifyTimeSkew(t *testing.T) { func TestVerifyMonitoring(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) N := 5 @@ -308,29 +308,49 @@ func TestVerifyMonitoring(t *testing.T) { if err := monitorAndVerify(d, cc, alice, key, cc.SavedSTR.Epoch, d.LatestSTR().Epoch); err != CheckPassed { t.Error(err) } + // TODO: more test (prior history but after registration) } -// Expect the ConsistencyChecks to panic: -// - If: StartEpoch < SavedEpoch + 1 -func TestVerifyMonitoringBadEpoch0(t *testing.T) { +func TestVeriryPriorHistory(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) N := 5 + // verify prior history for i := 0; i < N; i++ { d.Update() } if err := monitorAndVerify(d, cc, alice, nil, 0, d.LatestSTR().Epoch); err != CheckPassed { - t.Error("Unexpected verification result") + t.Error(err) + } + if err := monitorAndVerify(d, cc, alice, nil, 0, d.LatestSTR().Epoch-2); err != CheckPassed || + cc.SavedSTR.Epoch != d.LatestSTR().Epoch { + t.Error("Expect saved epoch is", d.LatestSTR().Epoch, "got", cc.SavedSTR.Epoch) + t.Error(err) } - defer func() { - if recover() == nil { - t.Fatal("Expect HandleResponse panic") - } - }() - if err := monitorAndVerify(d, cc, alice, nil, cc.SavedSTR.Epoch-1, d.LatestSTR().Epoch); err != CheckPassed { + // register + if _, e2 := registerAndVerify(d, cc, alice, key); e2 != CheckPassed { + t.Error("Cannot register new binding") + } + // or we can verify prior history after registering + if err := monitorAndVerify(d, cc, alice, nil, 0, d.LatestSTR().Epoch); err != CheckPassed { + t.Error(err) + } + if err := monitorAndVerify(d, cc, alice, nil, 3, d.LatestSTR().Epoch); err != CheckPassed { + t.Error(err) + } + if err := monitorAndVerify(d, cc, alice, nil, d.LatestSTR().Epoch-1, d.LatestSTR().Epoch); err != CheckPassed { + t.Error(err) + } + if err := monitorAndVerify(d, cc, alice, nil, 0, d.LatestSTR().Epoch-5); err != CheckPassed { + t.Error(err) + } + + // monitor binding was inserted + d.Update() + if err := monitorAndVerify(d, cc, alice, key, cc.SavedSTR.Epoch, d.LatestSTR().Epoch); err != CheckPassed { t.Error(err) } } @@ -339,7 +359,7 @@ func TestVerifyMonitoringBadEpoch0(t *testing.T) { // - If: StartEpoch > SavedEpoch + 1 func TestVerifyMonitoringBadEpoch1(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) N := 5 @@ -366,7 +386,7 @@ func TestVerifyMonitoringBadEpoch1(t *testing.T) { func TestMalformedMonitoringResponse(t *testing.T) { d, pk := NewTestDirectory(t, true) - cc := NewCC(d.LatestSTR(), d.LatestSTR(), true, pk) + cc := NewCC(d.LatestSTR(), true, pk) // len(AP) == 0 malformedResponse := &Response{