From bc814eceb704b8483dff3ac584e17ed6b37a11b8 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 28 Sep 2022 11:55:24 -0700 Subject: [PATCH 1/5] Search through all shards when searching by hash Signed-off-by: Priya Wadhwa --- pkg/api/entries.go | 11 ++++++----- pkg/sharding/ranges.go | 9 +++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/api/entries.go b/pkg/api/entries.go index 64251a16a..303912247 100644 --- a/pkg/api/entries.go +++ b/pkg/api/entries.go @@ -441,13 +441,14 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo } for _, leafResp := range searchByHashResults { - if leafResp != nil { - logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tc, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof, api.logRanges.ActiveTreeID(), api.logRanges) + if leafResp == nil { + continue + } + for _, shard := range api.logRanges.AllShards() { + logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tc, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof, shard, api.logRanges) if err != nil { - code = http.StatusInternalServerError - return handleRekorAPIError(params, code, err, err.Error()) + continue } - resultPayload = append(resultPayload, logEntry) } } diff --git a/pkg/sharding/ranges.go b/pkg/sharding/ranges.go index 859f7e6e3..c5f62dc64 100644 --- a/pkg/sharding/ranges.go +++ b/pkg/sharding/ranges.go @@ -138,6 +138,15 @@ func (l *LogRanges) NoInactive() bool { return l.inactive == nil } +// AllShards returns all shards, starting with the active shard and then the inactive shards +func (l *LogRanges) AllShards() []int64 { + shards := []int64{l.ActiveTreeID()} + for _, in := range l.GetInactive() { + shards = append(shards, in.TreeID) + } + return shards +} + // TotalInactiveLength returns the total length across all inactive shards; // we don't know the length of the active shard. func (l *LogRanges) TotalInactiveLength() int64 { From b82ef62ffe0976ab30048c6f99c27c771d00fa5b Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 28 Sep 2022 12:00:55 -0700 Subject: [PATCH 2/5] Add cosign sign-blob and verify-blob to sharding e2e test Signed-off-by: Priya Wadhwa --- tests/sharding-e2e-test.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/sharding-e2e-test.sh b/tests/sharding-e2e-test.sh index b72b79f10..ac4f631be 100755 --- a/tests/sharding-e2e-test.sh +++ b/tests/sharding-e2e-test.sh @@ -24,6 +24,8 @@ set -ex echo "Installing createtree..." go install github.com/google/trillian/cmd/createtree@latest +echo "Installing cosign..." +go install github.com/sigstore/cosign/cmd/cosign@latest echo "starting services" docker-compose up -d @@ -99,10 +101,9 @@ trap "collectLogsOnFailure $?" EXIT echo "Waiting for rekor server to come up..." waitForRekorServer -# Add some things to the tlog :) -pushd tests -$REKOR_CLI upload --artifact test_file.txt --signature test_file.sig --public-key test_public_key.key --rekor_server http://localhost:3000 -popd +# Sign a blob and push to the log with cosign +COSIGN_PASSWORD=pw cosign generate-key-pair +COSIGN_PASSWORD=pw COSIGN_EXPERIMENTAL=1 cosign sign-blob README.md --key cosign.key --rekor-url http://localhost:3000 --output-signature ./signature # Make sure we can prove consistency $REKOR_CLI loginfo --rekor_server http://localhost:3000 @@ -273,4 +274,7 @@ echo echo "Testing rekor-cli verification via Entry ID..." DEBUG=1 $REKOR_CLI verify --uuid $ENTRY_ID_1 --rekor_server http://localhost:3000 +# Make sure we can verify the blob we entered into the now-inactive shard +COSIGN_PASSWORD=pw COSIGN_EXPERIMENTAL=1 cosign verify-blob README.md --key cosign.pub --rekor-url http://localhost:3000 --signature ./signature + echo "Test passed successfully :)" From ff26fde547e8808bfdcee476a429e47be6e91866 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 28 Sep 2022 12:37:21 -0700 Subject: [PATCH 3/5] Search hashes through all shards Signed-off-by: Priya Wadhwa --- .gitignore | 2 ++ pkg/api/entries.go | 33 +++++++++++++++++++++------------ tests/sharding-e2e-test.sh | 8 +++++--- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index f5ff5d8b8..5f5eb843e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ rekorServerImagerefs rekorCliImagerefs trillianServerImagerefs trillianSignerImagerefs +cosign.* +signature diff --git a/pkg/api/entries.go b/pkg/api/entries.go index 303912247..520328f10 100644 --- a/pkg/api/entries.go +++ b/pkg/api/entries.go @@ -415,23 +415,27 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo searchHashes = append(searchHashes, hash) } - searchByHashResults := make([]*trillian.GetEntryAndProofResponse, len(searchHashes)) + searchByHashResults := make([][]*trillian.GetEntryAndProofResponse, len(searchHashes)) g, _ = errgroup.WithContext(httpReqCtx) for i, hash := range searchHashes { i, hash := i, hash // https://golang.org/doc/faq#closures_and_goroutines g.Go(func() error { - resp := tc.getLeafAndProofByHash(hash) - switch resp.status { - case codes.OK: - case codes.NotFound: - code = http.StatusNotFound - return resp.err - default: + var results []*trillian.GetEntryAndProofResponse + for _, shard := range api.logRanges.AllShards() { + tcs := NewTrillianClientFromTreeID(httpReqCtx, shard) + resp := tcs.getLeafAndProofByHash(hash) + if resp.status != codes.OK { + continue + } + leafResult := resp.getLeafAndProofResult + if leafResult != nil && leafResult.Leaf != nil { + results = append(results, resp.getLeafAndProofResult) + } } - leafResult := resp.getLeafAndProofResult - if leafResult != nil && leafResult.Leaf != nil { - searchByHashResults[i] = leafResult + if results == nil { + return fmt.Errorf("no responses found") } + searchByHashResults[i] = results return nil }) } @@ -440,7 +444,12 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo return handleRekorAPIError(params, code, err, err.Error()) } - for _, leafResp := range searchByHashResults { + var flattenedHashResults []*trillian.GetEntryAndProofResponse + for _, s := range searchByHashResults { + flattenedHashResults = append(flattenedHashResults, s...) + } + + for _, leafResp := range flattenedHashResults { if leafResp == nil { continue } diff --git a/tests/sharding-e2e-test.sh b/tests/sharding-e2e-test.sh index ac4f631be..257610ff3 100755 --- a/tests/sharding-e2e-test.sh +++ b/tests/sharding-e2e-test.sh @@ -36,6 +36,7 @@ go build -o rekor-cli ./cmd/rekor-cli REKOR_CLI=$(pwd)/rekor-cli go build -o rekor-server ./cmd/rekor-server + function check_log_index () { logIndex=$1 # make sure we can get this log index from rekor @@ -102,6 +103,7 @@ echo "Waiting for rekor server to come up..." waitForRekorServer # Sign a blob and push to the log with cosign +rm cosign.* COSIGN_PASSWORD=pw cosign generate-key-pair COSIGN_PASSWORD=pw COSIGN_EXPERIMENTAL=1 cosign sign-blob README.md --key cosign.key --rekor-url http://localhost:3000 --output-signature ./signature @@ -252,6 +254,9 @@ ENTRY_ID_2=$($REKOR_CLI get --log-index 3 --rekor_server http://localhost:3000 - NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\"]}" | jq '. | length') stringsMatch $NUM_ELEMENTS "1" +# Make sure we can verify the blob we entered into the now-inactive shard +COSIGN_EXPERIMENTAL=1 cosign verify-blob README.md --key cosign.pub --rekor-url http://localhost:3000 --signature ./signature + # -f makes sure we exit on failure NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\", \"$ENTRY_ID_2\"]}" | jq '. | length') stringsMatch $NUM_ELEMENTS "2" @@ -274,7 +279,4 @@ echo echo "Testing rekor-cli verification via Entry ID..." DEBUG=1 $REKOR_CLI verify --uuid $ENTRY_ID_1 --rekor_server http://localhost:3000 -# Make sure we can verify the blob we entered into the now-inactive shard -COSIGN_PASSWORD=pw COSIGN_EXPERIMENTAL=1 cosign verify-blob README.md --key cosign.pub --rekor-url http://localhost:3000 --signature ./signature - echo "Test passed successfully :)" From 1512822440c42170c1b66705e92432587286974c Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 28 Sep 2022 13:12:03 -0700 Subject: [PATCH 4/5] Fix searching by hash Signed-off-by: Priya Wadhwa --- .gitignore | 1 + pkg/api/entries.go | 35 +++++++++++++++++++---------------- tests/sharding-e2e-test.sh | 3 ++- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 5f5eb843e..55fce30e7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ trillianServerImagerefs trillianSignerImagerefs cosign.* signature +rekor.pub diff --git a/pkg/api/entries.go b/pkg/api/entries.go index 520328f10..378ca8f53 100644 --- a/pkg/api/entries.go +++ b/pkg/api/entries.go @@ -350,7 +350,6 @@ func GetLogEntryByUUIDHandler(params entries.GetLogEntryByUUIDParams) middleware func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Responder { httpReqCtx := params.HTTPRequest.Context() resultPayload := []models.LogEntry{} - tc := NewTrillianClient(httpReqCtx) totalQueries := len(params.Entry.EntryUUIDs) + len(params.Entry.Entries()) + len(params.Entry.LogIndexes) if totalQueries > maxSearchQueries { @@ -415,24 +414,31 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo searchHashes = append(searchHashes, hash) } - searchByHashResults := make([][]*trillian.GetEntryAndProofResponse, len(searchHashes)) + searchByHashResults := make([]map[int64]*trillian.GetEntryAndProofResponse, len(searchHashes)) g, _ = errgroup.WithContext(httpReqCtx) for i, hash := range searchHashes { i, hash := i, hash // https://golang.org/doc/faq#closures_and_goroutines g.Go(func() error { - var results []*trillian.GetEntryAndProofResponse + var results map[int64]*trillian.GetEntryAndProofResponse for _, shard := range api.logRanges.AllShards() { tcs := NewTrillianClientFromTreeID(httpReqCtx, shard) resp := tcs.getLeafAndProofByHash(hash) if resp.status != codes.OK { continue } + if resp.err != nil { + continue + } leafResult := resp.getLeafAndProofResult if leafResult != nil && leafResult.Leaf != nil { - results = append(results, resp.getLeafAndProofResult) + if results == nil { + results = map[int64]*trillian.GetEntryAndProofResponse{} + } + results[shard] = resp.getLeafAndProofResult } } if results == nil { + code = http.StatusNotFound return fmt.Errorf("no responses found") } searchByHashResults[i] = results @@ -444,20 +450,17 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo return handleRekorAPIError(params, code, err, err.Error()) } - var flattenedHashResults []*trillian.GetEntryAndProofResponse - for _, s := range searchByHashResults { - flattenedHashResults = append(flattenedHashResults, s...) - } - - for _, leafResp := range flattenedHashResults { - if leafResp == nil { - continue - } - for _, shard := range api.logRanges.AllShards() { - logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tc, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof, shard, api.logRanges) - if err != nil { + for _, hashMap := range searchByHashResults { + for shard, leafResp := range hashMap { + if leafResp == nil { continue } + tcs := NewTrillianClientFromTreeID(httpReqCtx, shard) + logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tcs, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof, shard, api.logRanges) + if err != nil { + code = http.StatusInternalServerError + return handleRekorAPIError(params, code, err, err.Error()) + } resultPayload = append(resultPayload, logEntry) } } diff --git a/tests/sharding-e2e-test.sh b/tests/sharding-e2e-test.sh index 257610ff3..6c6dcba4f 100755 --- a/tests/sharding-e2e-test.sh +++ b/tests/sharding-e2e-test.sh @@ -255,7 +255,8 @@ NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Con stringsMatch $NUM_ELEMENTS "1" # Make sure we can verify the blob we entered into the now-inactive shard -COSIGN_EXPERIMENTAL=1 cosign verify-blob README.md --key cosign.pub --rekor-url http://localhost:3000 --signature ./signature +echo $NEW_PUB_KEY > rekor.pub +COSIGN_EXPERIMENTAL=1 SIGSTORE_REKOR_PUBLIC_KEY=./rekor.pub cosign verify-blob README.md --key cosign.pub --rekor-url http://localhost:3000 --signature ./signature # -f makes sure we exit on failure NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\", \"$ENTRY_ID_2\"]}" | jq '. | length') From 0541dd00e5cbd76e6647a66edf20d1344bd72988 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 28 Sep 2022 14:52:48 -0700 Subject: [PATCH 5/5] Use rekor-cli instead of cosign for testing upload and verify Signed-off-by: Priya Wadhwa --- tests/sharding-e2e-test.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/sharding-e2e-test.sh b/tests/sharding-e2e-test.sh index 6c6dcba4f..700f4ad2a 100755 --- a/tests/sharding-e2e-test.sh +++ b/tests/sharding-e2e-test.sh @@ -24,9 +24,6 @@ set -ex echo "Installing createtree..." go install github.com/google/trillian/cmd/createtree@latest -echo "Installing cosign..." -go install github.com/sigstore/cosign/cmd/cosign@latest - echo "starting services" docker-compose up -d rm ~/.rekor/state.json || true @@ -102,10 +99,10 @@ trap "collectLogsOnFailure $?" EXIT echo "Waiting for rekor server to come up..." waitForRekorServer -# Sign a blob and push to the log with cosign -rm cosign.* -COSIGN_PASSWORD=pw cosign generate-key-pair -COSIGN_PASSWORD=pw COSIGN_EXPERIMENTAL=1 cosign sign-blob README.md --key cosign.key --rekor-url http://localhost:3000 --output-signature ./signature +# Add some things to the tlog :) +pushd tests +$REKOR_CLI upload --artifact test_file.txt --signature test_file.sig --public-key test_public_key.key --rekor_server http://localhost:3000 +popd # Make sure we can prove consistency $REKOR_CLI loginfo --rekor_server http://localhost:3000 @@ -254,9 +251,10 @@ ENTRY_ID_2=$($REKOR_CLI get --log-index 3 --rekor_server http://localhost:3000 - NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\"]}" | jq '. | length') stringsMatch $NUM_ELEMENTS "1" -# Make sure we can verify the blob we entered into the now-inactive shard -echo $NEW_PUB_KEY > rekor.pub -COSIGN_EXPERIMENTAL=1 SIGSTORE_REKOR_PUBLIC_KEY=./rekor.pub cosign verify-blob README.md --key cosign.pub --rekor-url http://localhost:3000 --signature ./signature +# Make sure we can verify the entry we entered into the now-inactive shard +pushd tests +$REKOR_CLI verify --artifact test_file.txt --signature test_file.sig --public-key test_public_key.key --rekor_server http://localhost:3000 +popd # -f makes sure we exit on failure NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\", \"$ENTRY_ID_2\"]}" | jq '. | length')