Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add signedEntryTimestamp signature when getting entries and client verification #333

Merged
merged 1 commit into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions cmd/rekor-cli/app/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package app

import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"errors"
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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)
}
}
Expand Down
4 changes: 4 additions & 0 deletions cmd/rekor-cli/app/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
75 changes: 47 additions & 28 deletions pkg/api/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -53,31 +71,38 @@ 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)),
LogIndex: swag.Int64(proof.GetLeafIndex()),
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 {
Expand All @@ -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())
}
Expand Down Expand Up @@ -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),
}
Expand All @@ -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())

Expand All @@ -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, "")
}
Expand Down Expand Up @@ -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())
}
Expand Down Expand Up @@ -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)
}
Expand Down