diff --git a/cmd/timestamp-cli/app/verify.go b/cmd/timestamp-cli/app/verify.go index 38c50795f..11e5c610c 100644 --- a/cmd/timestamp-cli/app/verify.go +++ b/cmd/timestamp-cli/app/verify.go @@ -16,19 +16,14 @@ package app import ( - "bytes" "crypto/x509" - "errors" "fmt" - "hash" - "io" "os" "path/filepath" - "github.com/digitorus/pkcs7" - "github.com/digitorus/timestamp" "github.com/sigstore/timestamp-authority/cmd/timestamp-cli/app/format" "github.com/sigstore/timestamp-authority/pkg/log" + "github.com/sigstore/timestamp-authority/pkg/verify" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -72,80 +67,27 @@ func runVerify() (interface{}, error) { return nil, fmt.Errorf("Error reading request from file: %w", err) } - ts, err := timestamp.ParseResponse(tsrBytes) - if err != nil { - pe := timestamp.ParseError("") - if errors.As(err, &pe) { - return nil, fmt.Errorf("Given timestamp response is not valid: %w", err) - } - return nil, fmt.Errorf("Error parsing response into Timestamp: %w", err) - } - - // verify the timestamp response against the certificate chain PEM file - err = verifyTSRWithPEM(ts) - if err != nil { - return nil, err - } - - // verify the timestamp response signature against the local arficat hash - err = verifyArtifactWithTSR(ts) - if err != nil { - return nil, err - } - - return &verifyCmdOutput{TimestampPath: tsrPath}, nil -} - -func verifyTSRWithPEM(ts *timestamp.Timestamp) error { - p7Message, err := pkcs7.Parse(ts.RawToken) - if err != nil { - return fmt.Errorf("Error parsing hashed message: %w", err) - } - certChainPEM := viper.GetString("cert-chain") pemBytes, err := os.ReadFile(filepath.Clean(certChainPEM)) if err != nil { - return fmt.Errorf("Error reading request from file: %w", err) + return nil, fmt.Errorf("Error reading request from file: %w", err) } certPool := x509.NewCertPool() ok := certPool.AppendCertsFromPEM(pemBytes) if !ok { - return fmt.Errorf("Error while appending certs from PEM") - } - - err = p7Message.VerifyWithChain(certPool) - if err != nil { - return fmt.Errorf("Error while verifying with chain: %w", err) + return nil, fmt.Errorf("Error parsing response into Timestamp while appending certs from PEM") } - log.CliLogger.Info("Verified with chain") - - return nil -} - -func verifyArtifactWithTSR(ts *timestamp.Timestamp) error { artifactPath := viper.GetString("artifact") artifact, err := os.Open(filepath.Clean(artifactPath)) if err != nil { - return err - } - - return verifyHashedMessages(ts.HashAlgorithm.New(), ts.HashedMessage, artifact) -} - -func verifyHashedMessages(hashAlg hash.Hash, hashedMessage []byte, artifactReader io.Reader) error { - h := hashAlg - if _, err := io.Copy(h, artifactReader); err != nil { - return fmt.Errorf("failed to create hash %w", err) + return nil, err } - localHashedMsg := h.Sum(nil) - if !bytes.Equal(localHashedMsg, hashedMessage) { - return fmt.Errorf("Hashed messages don't match") - } + err = verify.TimestampResponse(tsrBytes, artifact, certPool) - return nil + return &verifyCmdOutput{TimestampPath: tsrPath}, err } func init() { diff --git a/pkg/tests/cli_test.go b/pkg/tests/cli_test.go index d302be324..b03274949 100644 --- a/pkg/tests/cli_test.go +++ b/pkg/tests/cli_test.go @@ -108,7 +108,7 @@ func TestVerify_InvalidTSR(t *testing.T) { // It should return a message that the PEM is not valid out := runCliErr(t, "--timestamp_server", restapiURL, "verify", "--timestamp", invalidTSR, "--artifact", artifactPath, "--cert-chain", pemPath) - outputContains(t, out, "Error parsing response into Timestamp") + outputContains(t, out, "Error parsing response into Timestamp while appending certs from PEM") } func TestVerify_InvalidPEM(t *testing.T) { diff --git a/pkg/verify/verify.go b/pkg/verify/verify.go new file mode 100644 index 000000000..00113f243 --- /dev/null +++ b/pkg/verify/verify.go @@ -0,0 +1,77 @@ +// +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package verify + +import ( + "bytes" + "crypto/x509" + "errors" + "fmt" + "hash" + "io" + + "github.com/digitorus/pkcs7" + "github.com/digitorus/timestamp" +) + +// VerifyTimestampResponse the timestamp response using a timestamp certificate chain. +func TimestampResponse(tsrBytes []byte, artifact io.Reader, certPool *x509.CertPool) error { + ts, err := timestamp.ParseResponse(tsrBytes) + if err != nil { + pe := timestamp.ParseError("") + if errors.As(err, &pe) { + return fmt.Errorf("Given timestamp response is not valid: %w", err) + } + return fmt.Errorf("Error parsing response into Timestamp: %w", err) + } + + // verify the timestamp response against the certificate chain PEM file + err = verifyTSRWithPEM(ts, certPool) + if err != nil { + return err + } + + // verify the timestamp response signature against the local arficat hash + return verifyHashedMessages(ts.HashAlgorithm.New(), ts.HashedMessage, artifact) +} + +func verifyTSRWithPEM(ts *timestamp.Timestamp, certPool *x509.CertPool) error { + p7Message, err := pkcs7.Parse(ts.RawToken) + if err != nil { + return fmt.Errorf("Error parsing hashed message: %w", err) + } + + err = p7Message.VerifyWithChain(certPool) + if err != nil { + return fmt.Errorf("Error while verifying with chain: %w", err) + } + + return nil +} + +func verifyHashedMessages(hashAlg hash.Hash, hashedMessage []byte, artifactReader io.Reader) error { + h := hashAlg + if _, err := io.Copy(h, artifactReader); err != nil { + return fmt.Errorf("failed to create hash %w", err) + } + localHashedMsg := h.Sum(nil) + + if !bytes.Equal(localHashedMsg, hashedMessage) { + return fmt.Errorf("Hashed messages don't match") + } + + return nil +} diff --git a/cmd/timestamp-cli/app/verify_test.go b/pkg/verify/verify_test.go similarity index 99% rename from cmd/timestamp-cli/app/verify_test.go rename to pkg/verify/verify_test.go index 34bed0eae..aafd78080 100644 --- a/cmd/timestamp-cli/app/verify_test.go +++ b/pkg/verify/verify_test.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package app +package verify import ( "bytes"