Skip to content
Open
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
2 changes: 1 addition & 1 deletion cmd/cosign/cli/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func SignCmd(ro *options.RootOptions, ko options.KeyOpts, signOpts options.SignO

if digest, ok := ref.(name.Digest); ok && !signOpts.Recursive {
se, err := ociremote.SignedEntity(ref, opts...)
if _, isEntityNotFoundErr := err.(*ociremote.EntityNotFoundError); isEntityNotFoundErr {
if _, isEntityNotFoundErr := err.(*ociremote.EntityNotFoundError); isEntityNotFoundErr || !signOpts.Upload {
se = ociremote.SignedUnknown(digest)
} else if err != nil {
return fmt.Errorf("accessing image: %w", err)
Expand Down
1 change: 1 addition & 0 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
default:
return errors.New("no certificate chain provided to verify certificate")
}
co.CodeSigningCert = cert

if c.SCTRef != "" {
sct, err := os.ReadFile(filepath.Clean(c.SCTRef))
Expand Down
11 changes: 10 additions & 1 deletion cmd/cosign/cli/verify/verify_blob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,19 @@ func TestVerifyBlob(t *testing.T) {
shouldErr: true,
},
{
name: "valid signature with public key - experimental rekor entry success",
name: "valid signature with public key - experimental rekor entry fail no certificate",
blob: blobBytes,
signature: blobSignature,
key: pubKeyBytes,
rekorEntry: []*models.LogEntry{makeRekorEntry(t, *rekorSigner, blobBytes, []byte(blobSignature),
pubKeyBytes, true)},
shouldErr: true,
},
{
name: "valid signature with public key - experimental rekor entry success",
blob: blobBytes,
signature: blobSignature,
cert: unexpiredLeafCert,
rekorEntry: []*models.LogEntry{makeRekorEntry(t, *rekorSigner, blobBytes, []byte(blobSignature),
pubKeyBytes, true)},
shouldErr: false,
Expand Down
35 changes: 32 additions & 3 deletions pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ type CheckOpts struct {
RootCerts *x509.CertPool
// IntermediateCerts are the optional intermediate CA certs used to verify a certificate chain.
IntermediateCerts *x509.CertPool
// CodeSigningCert is the code signing certificate, if provided
CodeSigningCert *x509.Certificate

// CertGithubWorkflowTrigger is the GitHub Workflow Trigger name expected for a certificate to be valid. The empty string means any certificate can be valid.
CertGithubWorkflowTrigger string
Expand Down Expand Up @@ -704,7 +706,13 @@ func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash,
return false, fmt.Errorf("rekor client not provided for online verification")
}

pemBytes, err := keyBytes(sig, co)
certBytes, err := sig.Cert()
if err != nil {
return false, err
} else if certBytes == nil || len(certBytes.Raw) == 0 {
return false, fmt.Errorf("code signing certificate not provided for rekor lookup")
}
pemBytes, err := cryptoutils.MarshalCertificateToPEM(certBytes)
if err != nil {
return false, err
}
Expand All @@ -715,7 +723,7 @@ func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash,
}
t := time.Unix(*e.IntegratedTime, 0)
acceptableRekorBundleTime = &t
bundleVerified = true
bundleVerified = false
}
}

Expand Down Expand Up @@ -872,7 +880,28 @@ func loadSignatureFromFile(ctx context.Context, sigRef string, signedImgRef name
}
}

sig, err := static.NewSignature(payload, b64sig)
// Gather the cert for the signature and add the cert along with the
// cert chain into the signature object.
opts := []static.Option{}
var certPEM []byte
if co.CodeSigningCert != nil {
certPEM, err = cryptoutils.MarshalCertificateToPEM(co.CodeSigningCert)
if err != nil {
return nil, err
}
// TODO: what if there are no chains?
chains, err := TrustedCert(co.CodeSigningCert, co.RootCerts, co.IntermediateCerts)
if err != nil {
return nil, err
}
chainPEM, err := cryptoutils.MarshalCertificatesToPEM(chains[0])
if err != nil {
return nil, err
}
opts = append(opts, static.WithCertChain(certPEM, chainPEM))
}

sig, err := static.NewSignature(payload, b64sig, opts...)
if err != nil {
return nil, err
}
Expand Down
50 changes: 42 additions & 8 deletions pkg/cosign/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,8 +537,14 @@ func TestImageSignatureVerificationWithRekor(t *testing.T) {
signer, publicKey := generateSigner(t)
blob, blobSignature, blobSignatureBase64 := generateBlobSignature(t, signer)

// Create an OCI signature which will be verified.
ociSignature, err := static.NewSignature(blob, blobSignatureBase64)
// Create an OCI signature with a mock certificate which will be verified.
opts := []static.Option{}
opts = append(opts, static.WithCertChain([]byte(testLeafCert), []byte{}))
ociSignature, err := static.NewSignature(blob, blobSignatureBase64, opts...)
require.NoError(t, err, "error creating OCI signature with certificate")

// Create an OCI signature without certificate to test error
ociSignatureNoCert, err := static.NewSignature(blob, blobSignatureBase64)
require.NoError(t, err, "error creating OCI signature")

// Set up mock Rekor signer and log ID.
Expand Down Expand Up @@ -582,13 +588,27 @@ func TestImageSignatureVerificationWithRekor(t *testing.T) {

tests := []struct {
name string
signature oci.Signature
checkOpts CheckOpts
rekorClient *client.Rekor
expectError bool
errorMsg string
}{
{
name: "Verification succeeds with valid Rekor public keys",
name: "Verification fails without certificate",
signature: ociSignatureNoCert,
checkOpts: CheckOpts{
SigVerifier: signer,
RekorClient: mockClient,
RekorPubKeys: trustedRekorPubKeys,
Identities: []Identity{{Subject: "[email protected]", Issuer: "oidc-issuer"}},
},
rekorClient: mockClient,
expectError: true,
},
{
name: "Verification succeeds with valid Rekor public keys",
signature: ociSignature,
checkOpts: CheckOpts{
SigVerifier: signer,
RekorClient: mockClient,
Expand All @@ -599,7 +619,8 @@ func TestImageSignatureVerificationWithRekor(t *testing.T) {
expectError: false,
},
{
name: "Verification fails with no Rekor public keys",
name: "Verification fails with no Rekor public keys",
signature: ociSignature,
checkOpts: CheckOpts{
SigVerifier: signer,
RekorClient: mockClient,
Expand All @@ -610,7 +631,8 @@ func TestImageSignatureVerificationWithRekor(t *testing.T) {
errorMsg: "no valid tlog entries found no trusted rekor public keys provided",
},
{
name: "Verification fails with non-matching Rekor public keys",
name: "Verification fails with non-matching Rekor public keys",
signature: ociSignature,
checkOpts: CheckOpts{
SigVerifier: signer,
RekorClient: mockClient,
Expand All @@ -625,13 +647,14 @@ func TestImageSignatureVerificationWithRekor(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bundleVerified, err := VerifyImageSignature(ctx, ociSignature, v1.Hash{}, &tt.checkOpts)
bundleVerified, err := VerifyImageSignature(ctx, tt.signature, v1.Hash{}, &tt.checkOpts)
if tt.expectError {
assert.Error(t, err)
assert.Contains(t, err.Error(), tt.errorMsg)
} else {
assert.NoError(t, err)
assert.True(t, bundleVerified, "bundle verification failed")
// when verifying against Rekor, we expect bundleVerified to be false
assert.False(t, bundleVerified, "bundle verification failed")
}
})
}
Expand Down Expand Up @@ -711,7 +734,18 @@ func TestVerifyImageSignatureWithSigVerifierAndRekorTSA(t *testing.T) {
if err != nil {
t.Fatalf("error signing the payload with the rekor and tsa clients: %v", err)
}
if _, err := VerifyImageSignature(context.TODO(), sig, v1.Hash{}, &CheckOpts{
rawSig, err := sig.Signature()
if err != nil {
t.Fatalf("error getting raw signature: %v", err)
}

// Create an OCI signature with a mock certificate which will be verified.
opts := []static.Option{}
opts = append(opts, static.WithCertChain([]byte(testLeafCert), []byte{}))
ociSig, err := static.NewSignature(payload, base64.StdEncoding.EncodeToString(rawSig), opts...)
require.NoError(t, err, "error creating OCI signature with certificate")

if _, err := VerifyImageSignature(context.TODO(), ociSig, v1.Hash{}, &CheckOpts{
SigVerifier: sv,
TSACertificate: leaves[0],
TSAIntermediateCertificates: intermediates,
Expand Down
Loading