From 4d101a980d20a432256380260481f16121813467 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Wed, 16 Jun 2021 11:55:28 -0400 Subject: [PATCH] add set signature and verification on get Signed-off-by: Asra Ali --- cmd/rekor-cli/app/get.go | 10 +++++ cmd/rekor-cli/app/upload.go | 4 ++ pkg/api/entries.go | 75 +++++++++++++++++++++++-------------- 3 files changed, 61 insertions(+), 28 deletions(-) diff --git a/cmd/rekor-cli/app/get.go b/cmd/rekor-cli/app/get.go index d40801c37..6a20d4cbd 100644 --- a/cmd/rekor-cli/app/get.go +++ b/cmd/rekor-cli/app/get.go @@ -17,6 +17,7 @@ package app import ( "bytes" + "context" "encoding/base64" "encoding/json" "errors" @@ -88,6 +89,10 @@ var getCmd = &cobra.Command{ return nil, err } for ix, entry := range resp.Payload { + if verified, err := verifyLogEntry(context.Background(), rekorClient, entry); err != nil || !verified { + return nil, fmt.Errorf("unable to verify entry was added to log %w", err) + } + return parseEntry(ix, entry) } } @@ -106,6 +111,11 @@ var getCmd = &cobra.Command{ if k != uuid { continue } + + if verified, err := verifyLogEntry(context.Background(), rekorClient, entry); err != nil || !verified { + return nil, fmt.Errorf("unable to verify entry was added to log %w", err) + } + return parseEntry(k, entry) } } diff --git a/cmd/rekor-cli/app/upload.go b/cmd/rekor-cli/app/upload.go index ad49db223..f7e84159d 100644 --- a/cmd/rekor-cli/app/upload.go +++ b/cmd/rekor-cli/app/upload.go @@ -143,6 +143,10 @@ func verifyLogEntry(ctx context.Context, rekorClient *client.Rekor, logEntry mod return false, nil } // verify the entry + if logEntry.Verification.SignedEntryTimestamp == nil { + return false, fmt.Errorf("signature missing") + } + le := &models.LogEntryAnon{ IntegratedTime: logEntry.IntegratedTime, LogIndex: logEntry.LogIndex, diff --git a/pkg/api/entries.go b/pkg/api/entries.go index cc8ef34b4..861591450 100644 --- a/pkg/api/entries.go +++ b/pkg/api/entries.go @@ -39,10 +39,28 @@ import ( "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/types" + "github.com/sigstore/sigstore/pkg/signature" ) -// logEntryFromLeaf creates LogEntry struct from trillian structs -func logEntryFromLeaf(tc TrillianClient, leaf *trillian.LogLeaf, signedLogRoot *trillian.SignedLogRoot, proof *trillian.Proof) (models.LogEntry, error) { +func signEntry(ctx context.Context, signer signature.Signer, entry models.LogEntryAnon) ([]byte, error) { + payload, err := entry.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("marshalling error: %v", err) + } + canonicalized, err := jsoncanonicalizer.Transform(payload) + if err != nil { + return nil, fmt.Errorf("canonicalizing error: %v", err) + } + signature, _, err := signer.Sign(ctx, canonicalized) + if err != nil { + return nil, fmt.Errorf("signing error: %v", err) + } + return signature, nil +} + +// logEntryFromLeaf creates a signed LogEntry struct from trillian structs +func logEntryFromLeaf(ctx context.Context, signer signature.Signer, tc TrillianClient, leaf *trillian.LogLeaf, + signedLogRoot *trillian.SignedLogRoot, proof *trillian.Proof) (models.LogEntry, error) { root := &ttypes.LogRootV1{} if err := root.UnmarshalBinary(signedLogRoot.LogRoot); err != nil { @@ -53,6 +71,18 @@ func logEntryFromLeaf(tc TrillianClient, leaf *trillian.LogLeaf, signedLogRoot * hashes = append(hashes, hex.EncodeToString(hash)) } + logEntryAnon := models.LogEntryAnon{ + LogID: swag.String(api.pubkeyHash), + LogIndex: &leaf.LeafIndex, + Body: leaf.LeafValue, + IntegratedTime: swag.Int64(leaf.IntegrateTimestamp.AsTime().Unix()), + } + + signature, err := signEntry(ctx, signer, logEntryAnon) + if err != nil { + return nil, fmt.Errorf("signing entry error: %w", err) + } + inclusionProof := models.InclusionProof{ TreeSize: swag.Int64(int64(root.TreeSize)), RootHash: swag.String(hex.EncodeToString(root.RootHash)), @@ -60,24 +90,19 @@ func logEntryFromLeaf(tc TrillianClient, leaf *trillian.LogLeaf, signedLogRoot * Hashes: hashes, } - logEntry := models.LogEntry{ - hex.EncodeToString(leaf.MerkleLeafHash): models.LogEntryAnon{ - LogID: swag.String(api.pubkeyHash), - LogIndex: &leaf.LeafIndex, - Body: leaf.LeafValue, - IntegratedTime: swag.Int64(leaf.IntegrateTimestamp.AsTime().Unix()), - Verification: &models.LogEntryAnonVerification{ - InclusionProof: &inclusionProof, - }, - }, + logEntryAnon.Verification = &models.LogEntryAnonVerification{ + InclusionProof: &inclusionProof, + SignedEntryTimestamp: strfmt.Base64(signature), } - return logEntry, nil + return models.LogEntry{ + hex.EncodeToString(leaf.MerkleLeafHash): logEntryAnon}, nil } // GetLogEntryAndProofByIndexHandler returns the entry and inclusion proof for a specified log index func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middleware.Responder { - tc := NewTrillianClient(params.HTTPRequest.Context()) + ctx := params.HTTPRequest.Context() + tc := NewTrillianClient(ctx) resp := tc.getLeafAndProofByIndex(params.LogIndex) switch resp.status { @@ -94,7 +119,7 @@ func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middlewa return handleRekorAPIError(params, http.StatusNotFound, errors.New("grpc returned 0 leaves with success code"), "") } - logEntry, err := logEntryFromLeaf(tc, leaf, result.SignedLogRoot, result.Proof) + logEntry, err := logEntryFromLeaf(ctx, api.signer, tc, leaf, result.SignedLogRoot, result.Proof) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, err, err.Error()) } @@ -174,18 +199,11 @@ func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Respo }() } - payload, err := logEntryAnon.MarshalBinary() + signature, err := signEntry(ctx, api.signer, logEntryAnon) if err != nil { - return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("signing error: %v", err), signingError) - } - canonicalized, err := jsoncanonicalizer.Transform(payload) - if err != nil { - return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("canonicalizing error: %v", err), signingError) - } - signature, _, err := api.signer.Sign(ctx, canonicalized) - if err != nil { - return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("signing error: %v", err), signingError) + return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("signing entry error: %v", err), signingError) } + logEntryAnon.Verification = &models.LogEntryAnonVerification{ SignedEntryTimestamp: strfmt.Base64(signature), } @@ -210,6 +228,7 @@ func getEntryURL(locationURL url.URL, uuid string) strfmt.URI { // GetLogEntryByUUIDHandler gets log entry and inclusion proof for specified UUID aka merkle leaf hash func GetLogEntryByUUIDHandler(params entries.GetLogEntryByUUIDParams) middleware.Responder { + ctx := params.HTTPRequest.Context() hashValue, _ := hex.DecodeString(params.EntryUUID) tc := NewTrillianClient(params.HTTPRequest.Context()) @@ -228,7 +247,7 @@ func GetLogEntryByUUIDHandler(params entries.GetLogEntryByUUIDParams) middleware return handleRekorAPIError(params, http.StatusNotFound, errors.New("grpc returned 0 leaves with success code"), "") } - logEntry, err := logEntryFromLeaf(tc, leaf, result.SignedLogRoot, result.Proof) + logEntry, err := logEntryFromLeaf(ctx, api.signer, tc, leaf, result.SignedLogRoot, result.Proof) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, err, "") } @@ -313,7 +332,7 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo for _, leafResp := range searchByHashResults { if leafResp != nil { - logEntry, err := logEntryFromLeaf(tc, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof) + logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tc, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof) if err != nil { return handleRekorAPIError(params, code, err, err.Error()) } @@ -350,7 +369,7 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo for _, result := range leafResults { if result != nil { - logEntry, err := logEntryFromLeaf(tc, result.Leaf, result.SignedLogRoot, result.Proof) + logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tc, result.Leaf, result.SignedLogRoot, result.Proof) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, err, trillianUnexpectedResult) }