Skip to content

Commit 5159d01

Browse files
authored
feat: add verification functions (sigstore#986)
* feat: add verification functions Signed-off-by: Asra Ali <asraa@google.com>
1 parent 7492834 commit 5159d01

File tree

6 files changed

+481
-120
lines changed

6 files changed

+481
-120
lines changed

cmd/rekor-cli/app/get.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/sigstore/rekor/pkg/log"
3737
"github.com/sigstore/rekor/pkg/sharding"
3838
"github.com/sigstore/rekor/pkg/types"
39+
"github.com/sigstore/rekor/pkg/verify"
3940
)
4041

4142
type getCmdOutput struct {
@@ -79,6 +80,7 @@ var getCmd = &cobra.Command{
7980
}
8081
},
8182
Run: format.WrapCmd(func(args []string) (interface{}, error) {
83+
ctx := context.Background()
8284
rekorClient, err := client.GetRekorClient(viper.GetString("rekor_server"), client.WithUserAgent(UserAgent()))
8385
if err != nil {
8486
return nil, err
@@ -89,6 +91,11 @@ var getCmd = &cobra.Command{
8991
if logIndex == "" && uuid == "" {
9092
return nil, errors.New("either --uuid or --log-index must be specified")
9193
}
94+
// retrieve rekor pubkey for verification
95+
verifier, err := loadVerifier(rekorClient)
96+
if err != nil {
97+
return nil, fmt.Errorf("retrieving rekor public key")
98+
}
9299

93100
if logIndex != "" {
94101
params := entries.NewGetLogEntryByIndexParams()
@@ -103,9 +110,12 @@ var getCmd = &cobra.Command{
103110
if err != nil {
104111
return nil, err
105112
}
113+
var e models.LogEntryAnon
106114
for ix, entry := range resp.Payload {
107-
if verified, err := verifyLogEntry(context.Background(), rekorClient, entry); err != nil || !verified {
108-
return nil, fmt.Errorf("unable to verify entry was added to log %w", err)
115+
// verify log entry
116+
e = entry
117+
if err := verify.VerifyLogEntry(ctx, &e, verifier); err != nil {
118+
return nil, fmt.Errorf("unable to verify entry was added to log: %w", err)
109119
}
110120

111121
return parseEntry(ix, entry)
@@ -132,13 +142,16 @@ var getCmd = &cobra.Command{
132142
return nil, err
133143
}
134144

145+
var e models.LogEntryAnon
135146
for k, entry := range resp.Payload {
136147
if k != u {
137148
continue
138149
}
139150

140-
if verified, err := verifyLogEntry(context.Background(), rekorClient, entry); err != nil || !verified {
141-
return nil, fmt.Errorf("unable to verify entry was added to log %w", err)
151+
// verify log entry
152+
e = entry
153+
if err := verify.VerifyLogEntry(ctx, &e, verifier); err != nil {
154+
return nil, fmt.Errorf("unable to verify entry was added to log: %w", err)
142155
}
143156

144157
return parseEntry(k, entry)

cmd/rekor-cli/app/log_info.go

Lines changed: 15 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616
package app
1717

1818
import (
19-
"bytes"
19+
"context"
2020
"crypto"
2121
"crypto/x509"
22-
"encoding/hex"
2322
"encoding/pem"
2423
"errors"
2524
"fmt"
@@ -28,10 +27,10 @@ import (
2827
"github.com/go-openapi/swag"
2928
rclient "github.com/sigstore/rekor/pkg/generated/client"
3029
"github.com/sigstore/rekor/pkg/generated/models"
30+
31+
"github.com/sigstore/rekor/pkg/verify"
3132
"github.com/spf13/cobra"
3233
"github.com/spf13/viper"
33-
"github.com/transparency-dev/merkle/proof"
34-
"github.com/transparency-dev/merkle/rfc6962"
3534

3635
"github.com/sigstore/rekor/cmd/rekor-cli/app/format"
3736
"github.com/sigstore/rekor/cmd/rekor-cli/app/state"
@@ -70,6 +69,7 @@ var logInfoCmd = &cobra.Command{
7069
Long: `Prints info about the transparency log`,
7170
Run: format.WrapCmd(func(args []string) (interface{}, error) {
7271
serverURL := viper.GetString("rekor_server")
72+
ctx := context.Background()
7373
rekorClient, err := client.GetRekorClient(serverURL, client.WithUserAgent(UserAgent()))
7474
if err != nil {
7575
return nil, err
@@ -85,7 +85,7 @@ var logInfoCmd = &cobra.Command{
8585
logInfo := result.GetPayload()
8686

8787
// Verify inactive shards
88-
if err := verifyInactiveTrees(rekorClient, serverURL, logInfo.InactiveShards); err != nil {
88+
if err := verifyInactiveTrees(ctx, rekorClient, serverURL, logInfo.InactiveShards); err != nil {
8989
return nil, err
9090
}
9191

@@ -97,7 +97,7 @@ var logInfoCmd = &cobra.Command{
9797
}
9898
treeID := swag.StringValue(logInfo.TreeID)
9999

100-
if err := verifyTree(rekorClient, signedTreeHead, serverURL, treeID); err != nil {
100+
if err := verifyTree(ctx, rekorClient, signedTreeHead, serverURL, treeID); err != nil {
101101
return nil, err
102102
}
103103

@@ -112,23 +112,23 @@ var logInfoCmd = &cobra.Command{
112112
}),
113113
}
114114

115-
func verifyInactiveTrees(rekorClient *rclient.Rekor, serverURL string, inactiveShards []*models.InactiveShardLogInfo) error {
115+
func verifyInactiveTrees(ctx context.Context, rekorClient *rclient.Rekor, serverURL string, inactiveShards []*models.InactiveShardLogInfo) error {
116116
if inactiveShards == nil {
117117
return nil
118118
}
119119
log.CliLogger.Infof("Validating inactive shards...")
120120
for _, shard := range inactiveShards {
121121
signedTreeHead := swag.StringValue(shard.SignedTreeHead)
122122
treeID := swag.StringValue(shard.TreeID)
123-
if err := verifyTree(rekorClient, signedTreeHead, serverURL, treeID); err != nil {
123+
if err := verifyTree(ctx, rekorClient, signedTreeHead, serverURL, treeID); err != nil {
124124
return fmt.Errorf("verifying inactive shard with ID %s: %w", treeID, err)
125125
}
126126
}
127127
log.CliLogger.Infof("Successfully validated inactive shards")
128128
return nil
129129
}
130130

131-
func verifyTree(rekorClient *rclient.Rekor, signedTreeHead, serverURL, treeID string) error {
131+
func verifyTree(ctx context.Context, rekorClient *rclient.Rekor, signedTreeHead, serverURL, treeID string) error {
132132
oldState := state.Load(serverURL)
133133
if treeID != "" {
134134
oldState = state.Load(treeID)
@@ -145,8 +145,12 @@ func verifyTree(rekorClient *rclient.Rekor, signedTreeHead, serverURL, treeID st
145145
return errors.New("signature on tree head did not verify")
146146
}
147147

148-
if err := proveConsistency(rekorClient, oldState, sth, treeID); err != nil {
149-
return err
148+
if oldState != nil {
149+
if err := verify.ProveConsistency(ctx, rekorClient, oldState, &sth, treeID); err != nil {
150+
return err
151+
}
152+
} else {
153+
log.CliLogger.Infof("No previous log state stored, unable to prove consistency")
150154
}
151155

152156
if viper.GetBool("store_tree_state") {
@@ -162,45 +166,6 @@ func verifyTree(rekorClient *rclient.Rekor, signedTreeHead, serverURL, treeID st
162166
return nil
163167
}
164168

165-
func proveConsistency(rekorClient *rclient.Rekor, oldState *util.SignedCheckpoint, sth util.SignedCheckpoint, treeID string) error {
166-
if oldState == nil {
167-
log.CliLogger.Infof("No previous log state stored, unable to prove consistency")
168-
return nil
169-
}
170-
persistedSize := oldState.Size
171-
switch {
172-
case persistedSize < sth.Size:
173-
log.CliLogger.Infof("Found previous log state, proving consistency between %d and %d", oldState.Size, sth.Size)
174-
params := tlog.NewGetLogProofParams()
175-
firstSize := int64(persistedSize)
176-
params.FirstSize = &firstSize
177-
params.LastSize = int64(sth.Size)
178-
params.TreeID = &treeID
179-
logProof, err := rekorClient.Tlog.GetLogProof(params)
180-
if err != nil {
181-
return err
182-
}
183-
hashes := [][]byte{}
184-
for _, h := range logProof.Payload.Hashes {
185-
b, _ := hex.DecodeString(h)
186-
hashes = append(hashes, b)
187-
}
188-
if err := proof.VerifyConsistency(rfc6962.DefaultHasher, persistedSize, sth.Size, hashes, oldState.Hash,
189-
sth.Hash); err != nil {
190-
return err
191-
}
192-
log.CliLogger.Infof("Consistency proof valid!")
193-
case persistedSize == sth.Size:
194-
if !bytes.Equal(oldState.Hash, sth.Hash) {
195-
return errors.New("root hash returned from server does not match previously persisted state")
196-
}
197-
log.CliLogger.Infof("Persisted log state matches the current state of the log")
198-
default:
199-
return fmt.Errorf("current size of tree reported from server %d is less than previously persisted state %d", sth.Size, persistedSize)
200-
}
201-
return nil
202-
}
203-
204169
func loadVerifier(rekorClient *rclient.Rekor) (signature.Verifier, error) {
205170
publicKey := viper.GetString("rekor_server_public_key")
206171
if publicKey == "" {

cmd/rekor-cli/app/upload.go

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,25 @@ package app
1717

1818
import (
1919
"context"
20-
"crypto/ecdsa"
21-
"crypto/sha256"
2220
"fmt"
2321
"io"
2422
"net/http"
2523
"net/url"
2624
"os"
2725
"path/filepath"
2826

29-
"github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
3027
"github.com/go-openapi/runtime"
3128
"github.com/go-openapi/swag"
3229
"github.com/spf13/cobra"
3330
"github.com/spf13/viper"
3431

3532
"github.com/sigstore/rekor/cmd/rekor-cli/app/format"
3633
"github.com/sigstore/rekor/pkg/client"
37-
genclient "github.com/sigstore/rekor/pkg/generated/client"
3834
"github.com/sigstore/rekor/pkg/generated/client/entries"
3935
"github.com/sigstore/rekor/pkg/generated/models"
4036
"github.com/sigstore/rekor/pkg/log"
4137
"github.com/sigstore/rekor/pkg/types"
42-
"github.com/sigstore/rekor/pkg/util"
38+
"github.com/sigstore/rekor/pkg/verify"
4339
)
4440

4541
type uploadCmdOutput struct {
@@ -138,7 +134,11 @@ var uploadCmd = &cobra.Command{
138134
}
139135

140136
// verify log entry
141-
if verified, err := verifyLogEntry(ctx, rekorClient, logEntry); err != nil || !verified {
137+
verifier, err := loadVerifier(rekorClient)
138+
if err != nil {
139+
return nil, fmt.Errorf("retrieving rekor public key")
140+
}
141+
if err := verify.VerifySignedEntryTimestamp(ctx, &logEntry, verifier); err != nil {
142142
return nil, fmt.Errorf("unable to verify entry was added to log: %w", err)
143143
}
144144

@@ -149,45 +149,6 @@ var uploadCmd = &cobra.Command{
149149
}),
150150
}
151151

152-
func verifyLogEntry(ctx context.Context, rekorClient *genclient.Rekor, logEntry models.LogEntryAnon) (bool, error) {
153-
if logEntry.Verification == nil {
154-
return false, nil
155-
}
156-
// verify the entry
157-
if logEntry.Verification.SignedEntryTimestamp == nil {
158-
return false, fmt.Errorf("signature missing")
159-
}
160-
161-
le := &models.LogEntryAnon{
162-
IntegratedTime: logEntry.IntegratedTime,
163-
LogIndex: logEntry.LogIndex,
164-
Body: logEntry.Body,
165-
LogID: logEntry.LogID,
166-
}
167-
168-
payload, err := le.MarshalBinary()
169-
if err != nil {
170-
return false, err
171-
}
172-
canonicalized, err := jsoncanonicalizer.Transform(payload)
173-
if err != nil {
174-
return false, err
175-
}
176-
177-
// get rekor's public key
178-
rekorPubKey, err := util.PublicKey(ctx, rekorClient)
179-
if err != nil {
180-
return false, err
181-
}
182-
183-
// verify the SET against the public key
184-
hash := sha256.Sum256(canonicalized)
185-
if !ecdsa.VerifyASN1(rekorPubKey, hash[:], []byte(logEntry.Verification.SignedEntryTimestamp)) {
186-
return false, fmt.Errorf("unable to verify")
187-
}
188-
return true, nil
189-
}
190-
191152
func init() {
192153
initializePFlagMap()
193154
if err := addArtifactPFlags(uploadCmd); err != nil {

cmd/rekor-cli/app/verify.go

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,14 @@
1616
package app
1717

1818
import (
19-
"bytes"
2019
"context"
21-
"encoding/base64"
2220
"encoding/hex"
2321
"fmt"
2422
"math/bits"
2523
"strconv"
2624

2725
"github.com/spf13/cobra"
2826
"github.com/spf13/viper"
29-
"github.com/transparency-dev/merkle/proof"
3027
"github.com/transparency-dev/merkle/rfc6962"
3128

3229
"github.com/sigstore/rekor/cmd/rekor-cli/app/format"
@@ -36,6 +33,7 @@ import (
3633
"github.com/sigstore/rekor/pkg/log"
3734
"github.com/sigstore/rekor/pkg/sharding"
3835
"github.com/sigstore/rekor/pkg/types"
36+
"github.com/sigstore/rekor/pkg/verify"
3937
)
4038

4139
type verifyCmdOutput struct {
@@ -88,6 +86,7 @@ var verifyCmd = &cobra.Command{
8886
return nil
8987
},
9088
Run: format.WrapCmd(func(args []string) (interface{}, error) {
89+
ctx := context.Background()
9190
rekorClient, err := client.GetRekorClient(viper.GetString("rekor_server"), client.WithUserAgent(UserAgent()))
9291
if err != nil {
9392
return nil, err
@@ -139,7 +138,7 @@ var verifyCmd = &cobra.Command{
139138
logEntry := resp.Payload[0]
140139

141140
var o *verifyCmdOutput
142-
var entryBytes []byte
141+
var entry models.LogEntryAnon
143142
for k, v := range logEntry {
144143
o = &verifyCmdOutput{
145144
RootHash: *v.Verification.InclusionProof.RootHash,
@@ -148,10 +147,7 @@ var verifyCmd = &cobra.Command{
148147
Size: *v.Verification.InclusionProof.TreeSize,
149148
Hashes: v.Verification.InclusionProof.Hashes,
150149
}
151-
entryBytes, err = base64.StdEncoding.DecodeString(v.Body.(string))
152-
if err != nil {
153-
return nil, err
154-
}
150+
entry = v
155151
}
156152

157153
if viper.IsSet("uuid") {
@@ -164,23 +160,17 @@ var verifyCmd = &cobra.Command{
164160
}
165161
}
166162

167-
// Note: the returned entry UUID is the UUID (not include the Tree ID)
168-
leafHash, _ := hex.DecodeString(o.EntryUUID)
169-
if !bytes.Equal(rfc6962.DefaultHasher.HashLeaf(entryBytes), leafHash) {
170-
return nil, fmt.Errorf("computed leaf hash did not match entry UUID")
163+
// Get Rekor Pub
164+
// TODO(asraa): Replace with sigstore's GetRekorPubs to use TUF.
165+
verifier, err := loadVerifier(rekorClient)
166+
if err != nil {
167+
return nil, err
171168
}
172169

173-
hashes := [][]byte{}
174-
for _, h := range o.Hashes {
175-
hb, _ := hex.DecodeString(h)
176-
hashes = append(hashes, hb)
170+
if err := verify.VerifyLogEntry(ctx, &entry, verifier); err != nil {
171+
return nil, fmt.Errorf("validating entry: %w", err)
177172
}
178173

179-
rootHash, _ := hex.DecodeString(o.RootHash)
180-
181-
if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(o.Index), uint64(o.Size), leafHash, hashes, rootHash); err != nil {
182-
return nil, err
183-
}
184174
return o, err
185175
}),
186176
}

0 commit comments

Comments
 (0)