Skip to content

Commit

Permalink
Add testing in verify, update e2e test
Browse files Browse the repository at this point in the history
Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
  • Loading branch information
haydentherapper committed Nov 30, 2022
1 parent 81f9dc1 commit f06d610
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 19 deletions.
4 changes: 4 additions & 0 deletions cmd/cosign/cli/verify/verify_blob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,10 @@ func TestVerifyBlob(t *testing.T) {
expiredLeafPem, true)},
shouldErr: true,
},
// TODO: Add tests for TSA:
// * With or without bundle
// * Mismatched signature
// * Unexpired and expired certificate
}
for _, tt := range tts {
t.Run(tt.name, func(t *testing.T) {
Expand Down
32 changes: 16 additions & 16 deletions pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash,
}
}
if co.TSACerts != nil {
bundleVerified, err = VerifyRFC3161Timestamp(ctx, sig, co.TSACerts)
err = VerifyRFC3161Timestamp(sig, co.TSACerts)
if err != nil {
return false, fmt.Errorf("unable to verify RFC3161 timestamp bundle: %w", err)
}
Expand Down Expand Up @@ -986,59 +986,59 @@ func VerifyBundle(ctx context.Context, sig oci.Signature, co *CheckOpts, rekorCl
return true, nil
}

func VerifyRFC3161Timestamp(ctx context.Context, sig oci.Signature, tsaCerts *x509.CertPool) (bool, error) {
bundle, err := sig.RFC3161Timestamp()
func VerifyRFC3161Timestamp(sig oci.Signature, tsaCerts *x509.CertPool) error {
ts, err := sig.RFC3161Timestamp()
if err != nil {
return false, err
} else if bundle == nil {
return false, nil
return err
} else if ts == nil {
return nil
}

b64Sig, err := sig.Base64Signature()
if err != nil {
return false, fmt.Errorf("reading base64signature: %w", err)
return fmt.Errorf("reading base64signature: %w", err)
}

cert, err := sig.Cert()
if err != nil {
return false, err
return err
}

var tsBytes []byte
if len(b64Sig) == 0 {
// For attestations, the Base64Signature is not set, therefore we rely on the signed payload
signedPayload, err := sig.Payload()
if err != nil {
return false, fmt.Errorf("reading the payload: %w", err)
return fmt.Errorf("reading the payload: %w", err)
}
tsBytes = signedPayload
} else {
// create timestamp over raw bytes of signature
rawSig, err := base64.StdEncoding.DecodeString(b64Sig)
if err != nil {
return false, err
return err
}
tsBytes = rawSig
}

err = tsaverification.VerifyTimestampResponse(bundle.SignedRFC3161Timestamp, bytes.NewReader(tsBytes), tsaCerts)
err = tsaverification.VerifyTimestampResponse(ts.SignedRFC3161Timestamp, bytes.NewReader(tsBytes), tsaCerts)
if err != nil {
return false, fmt.Errorf("unable to verify TimestampResponse: %w", err)
return fmt.Errorf("unable to verify TimestampResponse: %w", err)
}

if cert != nil {
ts, err := timestamp.ParseResponse(bundle.SignedRFC3161Timestamp)
ts, err := timestamp.ParseResponse(ts.SignedRFC3161Timestamp)
if err != nil {
return false, fmt.Errorf("error parsing response into timestamp: %w", err)
return fmt.Errorf("error parsing response into timestamp: %w", err)
}
// Verify the cert against the integrated time.
// Note that if the caller requires the certificate to be present, it has to ensure that itself.
if err := CheckExpiry(cert, ts.Time); err != nil {
return false, fmt.Errorf("checking expiry on cert: %w", err)
return fmt.Errorf("checking expiry on certificate: %w", err)
}
}

return true, nil
return nil
}

// compare bundle signature to the signature we are verifying
Expand Down
111 changes: 111 additions & 0 deletions pkg/cosign/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
Expand Down Expand Up @@ -1286,3 +1287,113 @@ func Test_getSubjectAltnernativeNames(t *testing.T) {
t.Fatalf("unexpected URL SAN value")
}
}

func TestVerifyRFC3161Timestamp(t *testing.T) {
// generate signed artifact
rootCert, rootKey, _ := test.GenerateRootCa()
leafCert, privKey, _ := test.GenerateLeafCert("subject", "oidc-issuer", rootCert, rootKey)
pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw})
pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw})
payload := []byte{1, 2, 3, 4}
h := sha256.Sum256(payload)
signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256)

// TODO: Replace with a TSA mock client, blocked by https://github.com/sigstore/timestamp-authority/issues/146
viper.Set("timestamp-signer", "memory")
apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, 10*time.Second, 10*time.Second)
server := httptest.NewServer(apiServer.GetHandler())
t.Cleanup(server.Close)
client, err := tsaclient.GetTimestampClient(server.URL)
if err != nil {
t.Fatal(err)
}

tsBytes, err := tsa.GetTimestampedSignature(signature, client)
if err != nil {
t.Fatalf("unexpected error creating timestamp: %v", err)
}
rfc3161TS := bundle.RFC3161Timestamp{SignedRFC3161Timestamp: tsBytes}
chain, err := client.Timestamp.GetTimestampCertChain(nil)
if err != nil {
t.Fatalf("unexpected error getting timestamp chain: %v", err)
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM([]byte(chain.Payload)) {
t.Fatalf("error creating trust root pool")
}

ociSig, _ := static.NewSignature(payload,
base64.StdEncoding.EncodeToString(signature),
static.WithCertChain(pemLeaf, appendSlices([][]byte{pemRoot})),
static.WithRFC3161Timestamp(&rfc3161TS))

// success, signing over signature
err = VerifyRFC3161Timestamp(ociSig, pool)
if err != nil {
t.Fatalf("unexpected error verifying timestamp with signature: %v", err)
}

// success, signing over payload
tsBytes, err = tsa.GetTimestampedSignature(payload, client)
if err != nil {
t.Fatalf("unexpected error creating timestamp: %v", err)
}
rfc3161TS = bundle.RFC3161Timestamp{SignedRFC3161Timestamp: tsBytes}
ociSig, _ = static.NewSignature(payload,
"", /*signature*/
static.WithCertChain(pemLeaf, appendSlices([][]byte{pemRoot})),
static.WithRFC3161Timestamp(&rfc3161TS))
err = VerifyRFC3161Timestamp(ociSig, pool)
if err != nil {
t.Fatalf("unexpected error verifying timestamp with payload: %v", err)
}

// failure with non-base64 encoded signature
ociSig, _ = static.NewSignature(payload,
string(signature),
static.WithCertChain(pemLeaf, appendSlices([][]byte{pemRoot})),
static.WithRFC3161Timestamp(&rfc3161TS))
err = VerifyRFC3161Timestamp(ociSig, pool)
if err == nil || !strings.Contains(err.Error(), "base64 data") {
t.Fatalf("expected error verifying timestamp with raw signature, got: %v", err)
}

// failure with mismatched signature
tsBytes, err = tsa.GetTimestampedSignature(signature, client)
if err != nil {
t.Fatalf("unexpected error creating timestamp: %v", err)
}
rfc3161TS = bundle.RFC3161Timestamp{SignedRFC3161Timestamp: tsBytes}
// regenerate signature
signature, _ = privKey.Sign(rand.Reader, h[:], crypto.SHA256)
ociSig, _ = static.NewSignature(payload,
base64.StdEncoding.EncodeToString(signature),
static.WithCertChain(pemLeaf, appendSlices([][]byte{pemRoot})),
static.WithRFC3161Timestamp(&rfc3161TS))
err = VerifyRFC3161Timestamp(ociSig, pool)
if err == nil || !strings.Contains(err.Error(), "hashed messages don't match") {
t.Fatalf("expected error verifying mismatched signatures, got: %v", err)
}

// failure with old certificate
leafPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
// generate old certificate
leafCert, _ = test.GenerateLeafCertWithExpiration("subject", "oidc-issuer", time.Now().AddDate(-1, 0, 0), leafPriv, rootCert, rootKey)
pemLeaf = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw})
tsBytes, err = tsa.GetTimestampedSignature(signature, client)
if err != nil {
t.Fatalf("unexpected error creating timestamp: %v", err)
}
rfc3161TS = bundle.RFC3161Timestamp{SignedRFC3161Timestamp: tsBytes}
ociSig, _ = static.NewSignature(payload,
base64.StdEncoding.EncodeToString(signature),
static.WithCertChain(pemLeaf, appendSlices([][]byte{pemRoot})),
static.WithRFC3161Timestamp(&rfc3161TS))
err = VerifyRFC3161Timestamp(ociSig, pool)
if err == nil || !strings.Contains(err.Error(), "checking expiry on certificate") {
t.Fatalf("expected error verifying old certificate, got: %v", err)
}
}
9 changes: 6 additions & 3 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,8 @@ func TestSignBlobRFC3161TimestampBundle(t *testing.T) {
os.RemoveAll(td1)
})
bp := filepath.Join(td1, blob)
bundlePath := filepath.Join(td1, "rfc3161TimestampBundle.sig")
bundlePath := filepath.Join(td1, "bundle.sig")
tsPath := filepath.Join(td1, "rfc3161Timestamp.json")

if err := os.WriteFile(bp, []byte(blob), 0644); err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -1023,7 +1024,8 @@ func TestSignBlobRFC3161TimestampBundle(t *testing.T) {

ko1 := options.KeyOpts{
KeyRef: pubKeyPath1,
RFC3161TimestampPath: bundlePath,
BundlePath: bundlePath,
RFC3161TimestampPath: tsPath,
TSACertChainPath: file.Name(),
}
// Verify should fail on a bad input
Expand All @@ -1036,7 +1038,8 @@ func TestSignBlobRFC3161TimestampBundle(t *testing.T) {
ko := options.KeyOpts{
KeyRef: privKeyPath1,
PassFunc: passFunc,
RFC3161TimestampPath: bundlePath,
BundlePath: bundlePath,
RFC3161TimestampPath: tsPath,
TSAServerURL: server.URL,
}
if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", "", false); err != nil {
Expand Down

0 comments on commit f06d610

Please sign in to comment.