diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index 70846fd3af6d..e27522552852 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -75,6 +75,8 @@ type CheckOpts struct { // RootCerts are the root CA certs used to verify a signature's chained certificate. RootCerts *x509.CertPool + // IntermediateCerts are the optional intermediate CA certs used to verify a certificate chain. + IntermediateCerts *x509.CertPool // CertEmail is the email expected for a certificate to be valid. The empty string means any certificate can be valid. CertEmail string // CertOidcIssuer is the OIDC issuer expected for a certificate to be valid. The empty string means any certificate can be valid. @@ -149,7 +151,7 @@ func ValidateAndUnpackCert(cert *x509.Certificate, co *CheckOpts) (signature.Ver } // Now verify the cert, then the signature. - if err := TrustedCert(cert, co.RootCerts); err != nil { + if err := TrustedCert(cert, co.RootCerts, co.IntermediateCerts); err != nil { return nil, err } if co.CertEmail != "" { @@ -783,13 +785,14 @@ func VerifySET(bundlePayload cbundle.RekorPayload, signature []byte, pub *ecdsa. return nil } -func TrustedCert(cert *x509.Certificate, roots *x509.CertPool) error { +func TrustedCert(cert *x509.Certificate, roots *x509.CertPool, intermediates *x509.CertPool) error { if _, err := cert.Verify(x509.VerifyOptions{ // THIS IS IMPORTANT: WE DO NOT CHECK TIMES HERE // THE CERTIFICATE IS TREATED AS TRUSTED FOREVER // WE CHECK THAT THE SIGNATURES WERE CREATED DURING THIS WINDOW - CurrentTime: cert.NotBefore, - Roots: roots, + CurrentTime: cert.NotBefore, + Roots: roots, + Intermediates: intermediates, KeyUsages: []x509.ExtKeyUsage{ x509.ExtKeyUsageCodeSigning, }, diff --git a/pkg/cosign/verify_test.go b/pkg/cosign/verify_test.go index a9e9b52264bc..a1bdb356c1a8 100644 --- a/pkg/cosign/verify_test.go +++ b/pkg/cosign/verify_test.go @@ -234,3 +234,50 @@ func TestCompareSigs(t *testing.T) { }) } } + +func TestTrustedCertSuccess(t *testing.T) { + rootCert, rootKey, _ := test.GenerateRootCa() + subCert, subKey, _ := test.GenerateSubordinateCa(rootCert, rootKey) + leafCert, _, _ := test.GenerateLeafCert("subject", "oidc-issuer", subCert, subKey) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + subPool := x509.NewCertPool() + subPool.AddCert(subCert) + + err := TrustedCert(leafCert, rootPool, subPool) + if err != nil { + t.Fatalf("expected no error verifying certificate, got %v", err) + } +} + +func TestTrustedCertSuccessNoIntermediates(t *testing.T) { + rootCert, rootKey, _ := test.GenerateRootCa() + leafCert, _, _ := test.GenerateLeafCert("subject", "oidc-issuer", rootCert, rootKey) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + + err := TrustedCert(leafCert, rootPool, nil) + if err != nil { + t.Fatalf("expected no error verifying certificate, got %v", err) + } +} + +// Tests that verification succeeds if both a root and subordinate pool are +// present, but a chain is built with only the leaf and root certificates. +func TestTrustedCertSuccessChainFromRoot(t *testing.T) { + rootCert, rootKey, _ := test.GenerateRootCa() + leafCert, _, _ := test.GenerateLeafCert("subject", "oidc-issuer", rootCert, rootKey) + subCert, _, _ := test.GenerateSubordinateCa(rootCert, rootKey) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + subPool := x509.NewCertPool() + subPool.AddCert(subCert) + + err := TrustedCert(leafCert, rootPool, subPool) + if err != nil { + t.Fatalf("expected no error verifying certificate, got %v", err) + } +}