From ebc1d7ae9a87a88e7b823f0f6921e74899cda914 Mon Sep 17 00:00:00 2001 From: priyawadhwa <priya@chainguard.dev> Date: Wed, 16 Mar 2022 21:40:30 +0000 Subject: [PATCH] Refactor rekor-cli loginfo (#734) This refactors the loginfo file in preparation for also getting info for inactive shards and verifying them as well. Signed-off-by: Priya Wadhwa <priya@chainguard.dev> --- cmd/rekor-cli/app/log_info.go | 158 ++++++++++++++++++++-------------- 1 file changed, 91 insertions(+), 67 deletions(-) diff --git a/cmd/rekor-cli/app/log_info.go b/cmd/rekor-cli/app/log_info.go index 3c8307e..304a291 100644 --- a/cmd/rekor-cli/app/log_info.go +++ b/cmd/rekor-cli/app/log_info.go @@ -28,6 +28,7 @@ import ( "github.com/go-openapi/swag" "github.com/google/trillian/merkle/logverifier" "github.com/google/trillian/merkle/rfc6962" + rclient "github.com/sigstore/rekor/pkg/generated/client" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -81,89 +82,112 @@ var logInfoCmd = &cobra.Command{ logInfo := result.GetPayload() sth := util.SignedCheckpoint{} - if err := sth.UnmarshalText([]byte(*logInfo.SignedTreeHead)); err != nil { + signedTreeHead := swag.StringValue(logInfo.SignedTreeHead) + if err := sth.UnmarshalText([]byte(signedTreeHead)); err != nil { return nil, err } - publicKey := viper.GetString("rekor_server_public_key") - if publicKey == "" { - // fetch key from server - keyResp, err := rekorClient.Pubkey.GetPublicKey(nil) - if err != nil { - return nil, err - } - publicKey = keyResp.Payload - } - - block, _ := pem.Decode([]byte(publicKey)) - if block == nil { - return nil, errors.New("failed to decode public key of server") - } - - pub, err := x509.ParsePKIXPublicKey(block.Bytes) - if err != nil { + if err := verifyTree(rekorClient, signedTreeHead, serverURL); err != nil { return nil, err } - verifier, err := signature.LoadVerifier(pub, crypto.SHA256) - if err != nil { - return nil, err - } - - if !sth.Verify(verifier) { - return nil, errors.New("signature on tree head did not verify") - } - cmdOutput := &logInfoCmdOutput{ TreeSize: swag.Int64Value(logInfo.TreeSize), RootHash: swag.StringValue(logInfo.RootHash), TimestampNanos: sth.GetTimestamp(), TreeID: swag.StringValue(logInfo.TreeID), } + return cmdOutput, nil + }), +} - oldState := state.Load(serverURL) - if oldState != nil { - persistedSize := oldState.Size - if persistedSize < sth.Size { - log.CliLogger.Infof("Found previous log state, proving consistency between %d and %d", oldState.Size, sth.Size) - params := tlog.NewGetLogProofParams() - firstSize := int64(persistedSize) - params.FirstSize = &firstSize - params.LastSize = int64(sth.Size) - proof, err := rekorClient.Tlog.GetLogProof(params) - if err != nil { - return nil, err - } - hashes := [][]byte{} - for _, h := range proof.Payload.Hashes { - b, _ := hex.DecodeString(h) - hashes = append(hashes, b) - } - v := logverifier.New(rfc6962.DefaultHasher) - if err := v.VerifyConsistencyProof(firstSize, int64(sth.Size), oldState.Hash, - sth.Hash, hashes); err != nil { - return nil, err - } - log.CliLogger.Infof("Consistency proof valid!") - } else if persistedSize == sth.Size { - if !bytes.Equal(oldState.Hash, sth.Hash) { - return nil, errors.New("root hash returned from server does not match previously persisted state") - } - log.CliLogger.Infof("Persisted log state matches the current state of the log") - } else if persistedSize > sth.Size { - return nil, fmt.Errorf("current size of tree reported from server %d is less than previously persisted state %d", sth.Size, persistedSize) - } - } else { - log.CliLogger.Infof("No previous log state stored, unable to prove consistency") +func verifyTree(rekorClient *rclient.Rekor, signedTreeHead, serverURL string) error { + oldState := state.Load(serverURL) + sth := util.SignedCheckpoint{} + if err := sth.UnmarshalText([]byte(signedTreeHead)); err != nil { + return err + } + verifier, err := loadVerifier(rekorClient) + if err != nil { + return err + } + if !sth.Verify(verifier) { + return errors.New("signature on tree head did not verify") + } + + if err := proveConsistency(rekorClient, oldState, sth); err != nil { + return err + } + + if viper.GetBool("store_tree_state") { + if err := state.Dump(serverURL, &sth); err != nil { + log.CliLogger.Infof("Unable to store previous state: %v", err) } + } + return nil +} - if viper.GetBool("store_tree_state") { - if err := state.Dump(serverURL, &sth); err != nil { - log.CliLogger.Infof("Unable to store previous state: %v", err) - } +func proveConsistency(rekorClient *rclient.Rekor, oldState *util.SignedCheckpoint, sth util.SignedCheckpoint) error { + if oldState == nil { + log.CliLogger.Infof("No previous log state stored, unable to prove consistency") + return nil + } + persistedSize := oldState.Size + switch { + case persistedSize < sth.Size: + log.CliLogger.Infof("Found previous log state, proving consistency between %d and %d", oldState.Size, sth.Size) + params := tlog.NewGetLogProofParams() + firstSize := int64(persistedSize) + params.FirstSize = &firstSize + params.LastSize = int64(sth.Size) + proof, err := rekorClient.Tlog.GetLogProof(params) + if err != nil { + return err } - return cmdOutput, nil - }), + hashes := [][]byte{} + for _, h := range proof.Payload.Hashes { + b, _ := hex.DecodeString(h) + hashes = append(hashes, b) + } + v := logverifier.New(rfc6962.DefaultHasher) + if err := v.VerifyConsistencyProof(firstSize, int64(sth.Size), oldState.Hash, + sth.Hash, hashes); err != nil { + return err + } + log.CliLogger.Infof("Consistency proof valid!") + case persistedSize == sth.Size: + if !bytes.Equal(oldState.Hash, sth.Hash) { + return errors.New("root hash returned from server does not match previously persisted state") + } + log.CliLogger.Infof("Persisted log state matches the current state of the log") + default: + return fmt.Errorf("current size of tree reported from server %d is less than previously persisted state %d", sth.Size, persistedSize) + } + return nil +} + +func loadVerifier(rekorClient *rclient.Rekor) (signature.Verifier, error) { + publicKey := viper.GetString("rekor_server_public_key") + if publicKey == "" { + // fetch key from server + keyResp, err := rekorClient.Pubkey.GetPublicKey(nil) + if err != nil { + return nil, err + } + publicKey = keyResp.Payload + } + + block, _ := pem.Decode([]byte(publicKey)) + if block == nil { + return nil, errors.New("failed to decode public key of server") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, err + } + + return signature.LoadVerifier(pub, crypto.SHA256) } func init() { -- GitLab