From b2e10535ef094e8bc9995769c2a22a1ee29cbe57 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Mon, 23 Nov 2020 21:36:00 +0100 Subject: added start on isolated trillian checks --- trillian_test.go | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 206 insertions(+), 8 deletions(-) (limited to 'trillian_test.go') diff --git a/trillian_test.go b/trillian_test.go index 174fa13..6c9b7af 100644 --- a/trillian_test.go +++ b/trillian_test.go @@ -6,18 +6,139 @@ import ( "github.com/google/trillian" "github.com/google/trillian/types" - "github.com/system-transparency/stfe/server/testdata" + "github.com/system-transparency/stfe/testdata" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -// TODO: TestCheckQueueLeaf func TestCheckQueueLeaf(t *testing.T) { + for _, table := range []struct { + description string + rsp *trillian.QueueLeafResponse + err error + wantErr bool + }{ + { + description: "trillian error", + err: fmt.Errorf("backend error"), + wantErr: true, + }, + { + description: "empty trillian response", + wantErr: true, + }, + { + description: "partial trillian response: empty QueuedLeaf field", + rsp: &trillian.QueueLeafResponse{}, + wantErr: true, + }, + { + description: "ok: duplicate leaf", + rsp: makeTrillianQueueLeafResponse(t, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true), + }, + { + description: "ok: new leaf", + rsp: makeTrillianQueueLeafResponse(t, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, false), + }, + } { + if err := checkQueueLeaf(table.rsp, table.err); (err != nil) != table.wantErr { + t.Errorf("got error %v, but wanted error %v in test %q", err, table.wantErr, table.description) + } + } } -// TODO: TestCheckGetLeavesByRange func TestCheckGetLeavesByRange(t *testing.T) { + // rsp without leaves + noLeaves := makeTrillianGetLeavesByRangeResponse(t, 0, 1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true) + noLeaves.Leaves = nil + + // rsp without signed log root + noSlr := makeTrillianGetLeavesByRangeResponse(t, 0, 1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true) + noSlr.SignedLogRoot = nil + + // rsp without log root + noLr := makeTrillianGetLeavesByRangeResponse(t, 0, 1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true) + noLr.SignedLogRoot.LogRoot = nil + + // rsp with root that cannot be unmarshalled + tr := makeTrillianGetLeavesByRangeResponse(t, 0, 1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true) + tr.SignedLogRoot.LogRoot = tr.SignedLogRoot.LogRoot[1:] + + // rsp with fixed tree size + fixedSize := makeTrillianGetLeavesByRangeResponse(t, int64(testTreeSize)-1, int64(testTreeSize)-1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true) + fixedSize.SignedLogRoot = makeLatestSignedLogRootResponse(t, 0, testTreeSize, testNodeHash).SignedLogRoot + + for _, table := range []struct { + description string + req *GetEntriesRequest + rsp *trillian.GetLeavesByRangeResponse + err error + wantErr bool + }{ + { + description: "trillian error", + err: fmt.Errorf("backend error"), + wantErr: true, + }, + { + description: "empty trillian response", + wantErr: true, + }, + { + description: "partial trillian response: no leaves", + rsp: noLeaves, + wantErr: true, + }, + { + description: "partial trillian response: no signed log root", + rsp: noSlr, + wantErr: true, + }, + { + description: "partial trillian response: no log root", + rsp: noLr, + wantErr: true, + }, + { + description: "bad response: too many leaves", + req: &GetEntriesRequest{Start: 0, End: 1}, + rsp: makeTrillianGetLeavesByRangeResponse(t, 0, 2, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true), + wantErr: true, + }, + { + description: "bad response: too many leaves", + req: &GetEntriesRequest{Start: 0, End: 1}, + rsp: tr, + wantErr: true, + }, + { + description: "bad response: start is not a valid index", + req: &GetEntriesRequest{Start: int64(testTreeSize), End: int64(testTreeSize)}, + rsp: fixedSize, + wantErr: true, + }, + { + description: "ok response: interval refers to the latest leaf", + req: &GetEntriesRequest{Start: int64(testTreeSize) - 1, End: int64(testTreeSize) - 1}, + rsp: fixedSize, + }, + { + description: "bad response: invalid leaf indices", + req: &GetEntriesRequest{Start: 10, End: 11}, + rsp: makeTrillianGetLeavesByRangeResponse(t, 11, 12, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true), + wantErr: true, + }, + { + description: "ok response: a bunch of leaves", + req: &GetEntriesRequest{Start: 10, End: 20}, + rsp: makeTrillianGetLeavesByRangeResponse(t, 10, 20, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true), + }, + } { + if _, err := checkGetLeavesByRange(table.req, table.rsp, table.err); (err != nil) != table.wantErr { + t.Errorf("got error %v, but wanted error %v in test %q", err, table.wantErr, table.description) + } + } } // TODO: TestCheckGetInclusionProofByHash @@ -28,8 +149,61 @@ func TestCheckGetInclusionProofByHash(t *testing.T) { func TestCheckGetConsistencyProof(t *testing.T) { } -// TODO: TestCheckGetLatestSignedLogRoot func TestCheckGetLatestSignedLogRoot(t *testing.T) { + // response with no log root + noLr := makeLatestSignedLogRootResponse(t, 0, 0, testNodeHash) + noLr.SignedLogRoot.LogRoot = nil + + // response with truncated log root + tr := makeLatestSignedLogRootResponse(t, 0, 0, testNodeHash) + tr.SignedLogRoot.LogRoot = tr.SignedLogRoot.LogRoot[1:] + + lp := makeTestLogParameters(t, nil) + for _, table := range []struct { + description string + rsp *trillian.GetLatestSignedLogRootResponse + err error + wantErr bool + }{ + { + description: "bad trillian response: error", + err: fmt.Errorf("backend failure"), + wantErr: true, + }, + { + description: "bad trillian response: empty", + wantErr: true, + }, + { + description: "bad trillian response: no signed log root", + rsp: &trillian.GetLatestSignedLogRootResponse{SignedLogRoot: nil}, + wantErr: true, + }, + { + description: "bad trillian response: no log root", + rsp: noLr, + wantErr: true, + }, + { + description: "bad trillian response: truncated log root", + rsp: tr, + wantErr: true, + }, + { + description: "bad trillian response: invalid root hash size", + rsp: makeLatestSignedLogRootResponse(t, 0, 0, make([]byte, 31)), + wantErr: true, + }, + { + description: "ok response", + rsp: makeLatestSignedLogRootResponse(t, 0, 0, testNodeHash), + }, + } { + var lr types.LogRootV1 + if err := checkGetLatestSignedLogRoot(lp, table.rsp, table.err, &lr); (err != nil) != table.wantErr { + t.Errorf("got error %v, but wanted error %v in test %q", err, table.wantErr, table.description) + } + } } // makeTrillianQueueLeafResponse creates a valid trillian QueueLeafResponse @@ -37,9 +211,13 @@ func TestCheckGetLatestSignedLogRoot(t *testing.T) { // is a PEM-encoded ed25519 signing key, and pemChain its certificate chain. // // Note: MerkleLeafHash and LeafIdentityHash are unset (not used by stfe). -func makeTrillianQueueLeafResponse(t *testing.T, name, pemChain, pemKey []byte) *trillian.QueueLeafResponse { +func makeTrillianQueueLeafResponse(t *testing.T, name, pemChain, pemKey []byte, dupCode bool) *trillian.QueueLeafResponse { t.Helper() leaf, appendix := makeTestLeaf(t, name, pemChain, pemKey) + s := status.New(codes.OK, "ok").Proto() + if dupCode { + s = status.New(codes.AlreadyExists, "duplicate").Proto() + } return &trillian.QueueLeafResponse{ QueuedLeaf: &trillian.QueuedLogLeaf{ Leaf: &trillian.LogLeaf{ @@ -49,7 +227,7 @@ func makeTrillianQueueLeafResponse(t *testing.T, name, pemChain, pemKey []byte) LeafIndex: 0, // not applicable (log is not pre-ordered) LeafIdentityHash: nil, // not used by stfe }, - Status: status.New(codes.OK, "ok").Proto(), + Status: s, }, } } @@ -94,7 +272,7 @@ func makeTrillianGetConsistencyProofResponse(t *testing.T, path [][]byte) *trill // Note: MerkleLeafHash and LeafIdentityHash are unset (not used by stfe). func makeTrillianGetLeavesByRangeResponse(t *testing.T, start, end int64, name, pemChain, pemKey []byte, valid bool) *trillian.GetLeavesByRangeResponse { t.Helper() - leaves := make([]*trillian.LogLeaf, 0, start-end+1) + leaves := make([]*trillian.LogLeaf, 0, end-start+1) for i, n := start, end+1; i < n; i++ { leaf, appendix := makeTestLeaf(t, append(name, []byte(fmt.Sprintf("_%d", i))...), pemChain, pemKey) if !valid { @@ -110,11 +288,13 @@ func makeTrillianGetLeavesByRangeResponse(t *testing.T, start, end int64, name, } return &trillian.GetLeavesByRangeResponse{ Leaves: leaves, - SignedLogRoot: testdata.NewGetLatestSignedLogRootResponse(t, 0, uint64(end)+1, make([]byte, 32)).SignedLogRoot, + SignedLogRoot: makeLatestSignedLogRootResponse(t, 0, uint64(end)+1, make([]byte, 32)).SignedLogRoot, } } +// makeTrillianLogRoot: docdoc func makeTrillianLogRoot(t *testing.T, timestamp, size uint64, hash []byte) *types.LogRootV1 { + t.Helper() return &types.LogRootV1{ TreeSize: size, RootHash: hash, @@ -123,3 +303,21 @@ func makeTrillianLogRoot(t *testing.T, timestamp, size uint64, hash []byte) *typ Metadata: nil, // not used by stfe } } + +// makeLatestSignedLogRootResponse creates a new trillian STH. Revision, +// Metadata, Proof, KeyHint, and LogRootSignature are unsset. +func makeLatestSignedLogRootResponse(t *testing.T, timestamp, size uint64, hash []byte) *trillian.GetLatestSignedLogRootResponse { + t.Helper() + rootBytes, err := makeTrillianLogRoot(t, timestamp, size, hash).MarshalBinary() + if err != nil { + t.Fatalf("failed to marshal root in test: %v", err) + } + return &trillian.GetLatestSignedLogRootResponse{ + SignedLogRoot: &trillian.SignedLogRoot{ + KeyHint: nil, // not used by stfe + LogRoot: rootBytes, + LogRootSignature: nil, // not used by stfe + }, + Proof: nil, // not used by stfe + } +} -- cgit v1.2.3