diff --git a/cmd/cosign/cli/commands.go b/cmd/cosign/cli/commands.go index b41ee0e91a7..3e333367889 100644 --- a/cmd/cosign/cli/commands.go +++ b/cmd/cosign/cli/commands.go @@ -45,6 +45,8 @@ func normalizeCertificateFlags(_ *pflag.FlagSet, name string) pflag.NormalizedNa name = "certificate-oidc-issuer" case "output-cert": name = "output-certificate" + case "cert-identity": + name = "certificate-identity" } return pflag.NormalizedName(name) } diff --git a/cmd/cosign/cli/options/certificate.go b/cmd/cosign/cli/options/certificate.go index f4effe5e1eb..9495c908b63 100644 --- a/cmd/cosign/cli/options/certificate.go +++ b/cmd/cosign/cli/options/certificate.go @@ -22,6 +22,7 @@ import ( type CertVerifyOptions struct { Cert string CertEmail string + CertIdentity string CertOidcIssuer string CertGithubWorkflowTrigger string CertGithubWorkflowSha string @@ -43,6 +44,9 @@ func (o *CertVerifyOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.CertEmail, "certificate-email", "", "the email expected in a valid Fulcio certificate") + cmd.Flags().StringVar(&o.CertIdentity, "certificate-identity", "", + "the identity expected in a valid Fulcio certificate. Valid values include email address, DNS names, IP addresses, and URIs.") + cmd.Flags().StringVar(&o.CertOidcIssuer, "certificate-oidc-issuer", "", "the OIDC issuer expected in a valid Fulcio certificate, e.g. https://token.actions.githubusercontent.com or https://oauth2.sigstore.dev/auth") diff --git a/cmd/cosign/cli/verify.go b/cmd/cosign/cli/verify.go index 93fa25e3bdb..d93f346bd62 100644 --- a/cmd/cosign/cli/verify.go +++ b/cmd/cosign/cli/verify.go @@ -98,6 +98,7 @@ against the transparency log.`, KeyRef: o.Key, CertRef: o.CertVerify.Cert, CertEmail: o.CertVerify.CertEmail, + CertIdentity: o.CertVerify.CertIdentity, CertOidcIssuer: o.CertVerify.CertOidcIssuer, CertGithubWorkflowTrigger: o.CertVerify.CertGithubWorkflowTrigger, CertGithubWorkflowSha: o.CertVerify.CertGithubWorkflowSha, @@ -181,6 +182,7 @@ against the transparency log.`, CheckClaims: o.CheckClaims, CertRef: o.CertVerify.Cert, CertEmail: o.CertVerify.CertEmail, + CertIdentity: o.CertVerify.CertIdentity, CertOidcIssuer: o.CertVerify.CertOidcIssuer, CertChain: o.CertVerify.CertChain, CertGithubWorkflowTrigger: o.CertVerify.CertGithubWorkflowTrigger, @@ -264,7 +266,7 @@ The blob may be specified as a path to a file or - for stdin.`, BundlePath: o.BundlePath, } if err := verify.VerifyBlobCmd(cmd.Context(), ko, o.CertVerify.Cert, - o.CertVerify.CertEmail, o.CertVerify.CertOidcIssuer, o.CertVerify.CertChain, + o.CertVerify.CertEmail, o.CertVerify.CertIdentity, o.CertVerify.CertOidcIssuer, o.CertVerify.CertChain, o.Signature, args[0], o.CertVerify.CertGithubWorkflowTrigger, o.CertVerify.CertGithubWorkflowSha, o.CertVerify.CertGithubWorkflowName, o.CertVerify.CertGithubWorkflowRepository, o.CertVerify.CertGithubWorkflowRef, o.CertVerify.EnforceSCT); err != nil { diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index 85cd5f35f32..ae843103d02 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -52,6 +52,7 @@ type VerifyCommand struct { KeyRef string CertRef string CertEmail string + CertIdentity string CertOidcIssuer string CertGithubWorkflowTrigger string CertGithubWorkflowSha string @@ -59,6 +60,7 @@ type VerifyCommand struct { CertGithubWorkflowRepository string CertGithubWorkflowRef string CertChain string + CertOidcProvider string EnforceSCT bool Sk bool Slot string @@ -100,6 +102,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { Annotations: c.Annotations.Annotations, RegistryClientOpts: ociremoteOpts, CertEmail: c.CertEmail, + CertIdentity: c.CertIdentity, CertOidcIssuer: c.CertOidcIssuer, CertGithubWorkflowTrigger: c.CertGithubWorkflowTrigger, CertGithubWorkflowSha: c.CertGithubWorkflowSha, diff --git a/cmd/cosign/cli/verify/verify_attestation.go b/cmd/cosign/cli/verify/verify_attestation.go index 9a9f82b58a9..65d16782f48 100644 --- a/cmd/cosign/cli/verify/verify_attestation.go +++ b/cmd/cosign/cli/verify/verify_attestation.go @@ -46,6 +46,7 @@ type VerifyAttestationCommand struct { KeyRef string CertRef string CertEmail string + CertIdentity string CertOidcIssuer string CertGithubWorkflowTrigger string CertGithubWorkflowSha string @@ -80,6 +81,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e co := &cosign.CheckOpts{ RegistryClientOpts: ociremoteOpts, CertEmail: c.CertEmail, + CertIdentity: c.CertIdentity, CertOidcIssuer: c.CertOidcIssuer, CertGithubWorkflowTrigger: c.CertGithubWorkflowTrigger, CertGithubWorkflowSha: c.CertGithubWorkflowSha, diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index b27296ae3fe..3d4163eb27f 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -66,7 +66,7 @@ func isb64(data []byte) bool { } // nolint -func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail, +func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail, certIdentity, certOidcIssuer, certChain, sigRef, blobRef, certGithubWorkflowTrigger, certGithubWorkflowSha, certGithubWorkflowName, certGithubWorkflowRepository, @@ -90,6 +90,7 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail, co := &cosign.CheckOpts{ CertEmail: certEmail, + CertIdentity: certIdentity, CertOidcIssuer: certOidcIssuer, CertGithubWorkflowTrigger: certGithubWorkflowTrigger, CertGithubWorkflowSha: certGithubWorkflowSha, diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go index 20dfe816fb1..d0c408111ad 100644 --- a/cmd/cosign/cli/verify/verify_blob_test.go +++ b/cmd/cosign/cli/verify/verify_blob_test.go @@ -721,6 +721,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ // Cert is fetched from bundle identity, /*certEmail*/ + "", /*certIdentity*/ issuer, /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -765,6 +766,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ // Cert is fetched from bundle "", /*certEmail*/ + "", /*certIdentity*/ "", /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -803,6 +805,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ // Cert is fetched from bundle "", /*certEmail*/ + "", /*certIdentity*/ "", /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -841,6 +844,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ // Cert is fetched from bundle "", /*certEmail*/ + "", /*certIdentity*/ "", /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -879,6 +883,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ // Cert is fetched from bundle "", /*certEmail*/ + "", /*certIdentity*/ "", /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -917,6 +922,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ // Cert is fetched from bundle "invalid@example.com", /*certEmail*/ + "", /*certIdentity*/ issuer, /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -925,7 +931,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { "", "", "", "", "", // GitHub identity flags end false /*enforceSCT*/) - if err == nil || !strings.Contains(err.Error(), "expected email not found in certificate") { + if err == nil || !strings.Contains(err.Error(), "expected identity not found in certificate") { t.Fatalf("expected error with mismatched identity, got %v", err) } }) @@ -955,6 +961,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ // Cert is fetched from bundle identity, /*certEmail*/ + "", /*certIdentity*/ "invalid", /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -994,6 +1001,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, certPath, /*certRef*/ identity, /*certEmail*/ + "", /*certIdentity*/ issuer, /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -1032,6 +1040,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ identity, /*certEmail*/ + "", /*certIdentity*/ issuer, /*certOidcIssuer*/ os.Getenv("SIGSTORE_ROOT_FILE"), /*certChain*/ "", /*sigRef*/ // Sig is fetched from bundle @@ -1081,6 +1090,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ identity, /*certEmail*/ + "", /*certIdentity*/ issuer, /*certOidcIssuer*/ tmpChainFile.Name(), /*certChain*/ "", /*sigRef*/ // Sig is fetched from bundle @@ -1126,6 +1136,7 @@ func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, certPath, /*certRef*/ identity, /*certEmail*/ + "", /*certIdentity*/ issuer, /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle @@ -1164,6 +1175,7 @@ func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { options.KeyOpts{BundlePath: bundlePath}, "", /*certRef*/ // Fetched from bundle identity, /*certEmail*/ + "", /*certIdentity*/ issuer, /*certOidcIssuer*/ "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE "", /*sigRef*/ // Sig is fetched from bundle diff --git a/doc/cosign_dockerfile_verify.md b/doc/cosign_dockerfile_verify.md index 5bfabce5e48..b198b979a28 100644 --- a/doc/cosign_dockerfile_verify.md +++ b/doc/cosign_dockerfile_verify.md @@ -65,6 +65,7 @@ cosign dockerfile verify [flags] --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon --certificate-github-workflow-sha string contains the sha claim from the GitHub OIDC Identity token that contains the commit SHA that the workflow run was based upon. --certificate-github-workflow-trigger string contains the event_name claim from the GitHub OIDC Identity token that contains the name of the event that triggered the workflow run + --certificate-identity string the identity expected in a valid Fulcio certificate. Valid values include email address, DNS names, IP addresses, and URIs. --certificate-oidc-issuer string the OIDC issuer expected in a valid Fulcio certificate, e.g. https://token.actions.githubusercontent.com or https://oauth2.sigstore.dev/auth --check-claims whether to check the claims found (default true) --enforce-sct whether to enforce that a certificate contain an embedded SCT, a proof of inclusion in a certificate transparency log diff --git a/doc/cosign_manifest_verify.md b/doc/cosign_manifest_verify.md index aa98b7e0ce7..bb79de314d4 100644 --- a/doc/cosign_manifest_verify.md +++ b/doc/cosign_manifest_verify.md @@ -59,6 +59,7 @@ cosign manifest verify [flags] --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon --certificate-github-workflow-sha string contains the sha claim from the GitHub OIDC Identity token that contains the commit SHA that the workflow run was based upon. --certificate-github-workflow-trigger string contains the event_name claim from the GitHub OIDC Identity token that contains the name of the event that triggered the workflow run + --certificate-identity string the identity expected in a valid Fulcio certificate. Valid values include email address, DNS names, IP addresses, and URIs. --certificate-oidc-issuer string the OIDC issuer expected in a valid Fulcio certificate, e.g. https://token.actions.githubusercontent.com or https://oauth2.sigstore.dev/auth --check-claims whether to check the claims found (default true) --enforce-sct whether to enforce that a certificate contain an embedded SCT, a proof of inclusion in a certificate transparency log diff --git a/doc/cosign_verify-attestation.md b/doc/cosign_verify-attestation.md index 655cc1edbd8..ca7fbdcbe35 100644 --- a/doc/cosign_verify-attestation.md +++ b/doc/cosign_verify-attestation.md @@ -69,6 +69,7 @@ cosign verify-attestation [flags] --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon --certificate-github-workflow-sha string contains the sha claim from the GitHub OIDC Identity token that contains the commit SHA that the workflow run was based upon. --certificate-github-workflow-trigger string contains the event_name claim from the GitHub OIDC Identity token that contains the name of the event that triggered the workflow run + --certificate-identity string the identity expected in a valid Fulcio certificate. Valid values include email address, DNS names, IP addresses, and URIs. --certificate-oidc-issuer string the OIDC issuer expected in a valid Fulcio certificate, e.g. https://token.actions.githubusercontent.com or https://oauth2.sigstore.dev/auth --check-claims whether to check the claims found (default true) --enforce-sct whether to enforce that a certificate contain an embedded SCT, a proof of inclusion in a certificate transparency log diff --git a/doc/cosign_verify-blob.md b/doc/cosign_verify-blob.md index 2bcedf555f6..ca1535b8ee8 100644 --- a/doc/cosign_verify-blob.md +++ b/doc/cosign_verify-blob.md @@ -69,6 +69,7 @@ cosign verify-blob [flags] --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon --certificate-github-workflow-sha string contains the sha claim from the GitHub OIDC Identity token that contains the commit SHA that the workflow run was based upon. --certificate-github-workflow-trigger string contains the event_name claim from the GitHub OIDC Identity token that contains the name of the event that triggered the workflow run + --certificate-identity string the identity expected in a valid Fulcio certificate. Valid values include email address, DNS names, IP addresses, and URIs. --certificate-oidc-issuer string the OIDC issuer expected in a valid Fulcio certificate, e.g. https://token.actions.githubusercontent.com or https://oauth2.sigstore.dev/auth --enforce-sct whether to enforce that a certificate contain an embedded SCT, a proof of inclusion in a certificate transparency log -h, --help help for verify-blob diff --git a/doc/cosign_verify.md b/doc/cosign_verify.md index ca75762512f..17ecd2f1949 100644 --- a/doc/cosign_verify.md +++ b/doc/cosign_verify.md @@ -78,6 +78,7 @@ cosign verify [flags] --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon --certificate-github-workflow-sha string contains the sha claim from the GitHub OIDC Identity token that contains the commit SHA that the workflow run was based upon. --certificate-github-workflow-trigger string contains the event_name claim from the GitHub OIDC Identity token that contains the name of the event that triggered the workflow run + --certificate-identity string the identity expected in a valid Fulcio certificate. Valid values include email address, DNS names, IP addresses, and URIs. --certificate-oidc-issuer string the OIDC issuer expected in a valid Fulcio certificate, e.g. https://token.actions.githubusercontent.com or https://oauth2.sigstore.dev/auth --check-claims whether to check the claims found (default true) --enforce-sct whether to enforce that a certificate contain an embedded SCT, a proof of inclusion in a certificate transparency log diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index c79d722624e..2f3a1ea77c3 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -92,6 +92,8 @@ type CheckOpts struct { 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 + // CertIdentity is the identity expected for a certificate to be valid. + CertIdentity string // CertOidcIssuer is the OIDC issuer expected for a certificate to be valid. The empty string means any certificate can be valid. CertOidcIssuer string @@ -219,17 +221,9 @@ func ValidateAndUnpackCert(cert *x509.Certificate, co *CheckOpts) (signature.Ver // the expected values. func CheckCertificatePolicy(cert *x509.Certificate, co *CheckOpts) error { ce := CertExtensions{Cert: cert} - if co.CertEmail != "" { - emailVerified := false - for _, em := range cert.EmailAddresses { - if co.CertEmail == em { - emailVerified = true - break - } - } - if !emailVerified { - return &VerificationError{"expected email not found in certificate"} - } + + if err := validateCertIdentity(cert, co); err != nil { + return err } if err := validateCertExtensions(ce, co); err != nil { @@ -332,6 +326,35 @@ func validateCertExtensions(ce CertExtensions, co *CheckOpts) error { return nil } +func validateCertIdentity(cert *x509.Certificate, co *CheckOpts) error { + // TODO: Make it mandatory to include one of these options. + if co.CertEmail == "" && co.CertIdentity == "" { + return nil + } + + for _, dns := range cert.DNSNames { + if co.CertIdentity == dns { + return nil + } + } + for _, em := range cert.EmailAddresses { + if co.CertIdentity == em || co.CertEmail == em { + return nil + } + } + for _, ip := range cert.IPAddresses { + if co.CertIdentity == ip.String() { + return nil + } + } + for _, uri := range cert.URIs { + if co.CertIdentity == uri.String() { + return nil + } + } + return &VerificationError{"expected identity not found in certificate"} +} + // getSubjectAlternateNames returns all of the following for a Certificate. // DNSNames // EmailAddresses diff --git a/pkg/cosign/verify_test.go b/pkg/cosign/verify_test.go index 62fa9044116..4876f7d74a0 100644 --- a/pkg/cosign/verify_test.go +++ b/pkg/cosign/verify_test.go @@ -526,6 +526,138 @@ func TestValidateAndUnpackCertWithoutRequiredSCT(t *testing.T) { require.Contains(t, err.Error(), "certificate does not include required embedded SCT") } +func TestValidateAndUnpackCertSuccessWithDnsSan(t *testing.T) { + subject := "example.com" + oidcIssuer := "https://accounts.google.com" + + rootCert, rootKey, _ := test.GenerateRootCa() + leafCert, _, _ := test.GenerateLeafCertWithSubjectAlternateNames( + []string{subject}, /* dnsNames */ + nil, /* emailAddresses */ + nil, /* ipAddresses */ + nil, /* uris */ + oidcIssuer, /* oidcIssuer */ + rootCert, + rootKey) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + + co := &CheckOpts{ + RootCerts: rootPool, + CertIdentity: subject, + CertOidcIssuer: oidcIssuer, + } + + _, err := ValidateAndUnpackCert(leafCert, co) + if err != nil { + t.Errorf("ValidateAndUnpackCert expected no error, got err = %v", err) + } + err = CheckCertificatePolicy(leafCert, co) + if err != nil { + t.Errorf("CheckCertificatePolicy expected no error, got err = %v", err) + } +} + +func TestValidateAndUnpackCertSuccessWithEmailSan(t *testing.T) { + subject := "email@email" + oidcIssuer := "https://accounts.google.com" + + rootCert, rootKey, _ := test.GenerateRootCa() + leafCert, _, _ := test.GenerateLeafCertWithSubjectAlternateNames( + nil, /* dnsNames */ + []string{subject}, /* emailAddresses */ + nil, /* ipAddresses */ + nil, /* uris */ + oidcIssuer, /* oidcIssuer */ + rootCert, + rootKey) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + + co := &CheckOpts{ + RootCerts: rootPool, + CertIdentity: subject, + CertOidcIssuer: oidcIssuer, + } + + _, err := ValidateAndUnpackCert(leafCert, co) + if err != nil { + t.Errorf("ValidateAndUnpackCert expected no error, got err = %v", err) + } + err = CheckCertificatePolicy(leafCert, co) + if err != nil { + t.Errorf("CheckCertificatePolicy expected no error, got err = %v", err) + } +} + +func TestValidateAndUnpackCertSuccessWithIpAddressSan(t *testing.T) { + subject := "127.0.0.1" + oidcIssuer := "https://accounts.google.com" + + rootCert, rootKey, _ := test.GenerateRootCa() + leafCert, _, _ := test.GenerateLeafCertWithSubjectAlternateNames( + nil, /* dnsNames */ + nil, /* emailAddresses */ + []net.IP{net.ParseIP(subject)}, /* ipAddresses */ + nil, /* uris */ + oidcIssuer, /* oidcIssuer */ + rootCert, + rootKey) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + + co := &CheckOpts{ + RootCerts: rootPool, + CertIdentity: subject, + CertOidcIssuer: oidcIssuer, + } + + _, err := ValidateAndUnpackCert(leafCert, co) + if err != nil { + t.Errorf("ValidateAndUnpackCert expected no error, got err = %v", err) + } + err = CheckCertificatePolicy(leafCert, co) + if err != nil { + t.Errorf("CheckCertificatePolicy expected no error, got err = %v", err) + } +} + +func TestValidateAndUnpackCertSuccessWithUriSan(t *testing.T) { + subject, _ := url.Parse("scheme://userinfo@host") + oidcIssuer := "https://accounts.google.com" + + rootCert, rootKey, _ := test.GenerateRootCa() + leafCert, _, _ := test.GenerateLeafCertWithSubjectAlternateNames( + nil, /* dnsNames */ + nil, /* emailAddresses */ + nil, /* ipAddresses */ + []*url.URL{subject}, /* uris */ + oidcIssuer, /* oidcIssuer */ + rootCert, + rootKey) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + + co := &CheckOpts{ + RootCerts: rootPool, + CertIdentity: "scheme://userinfo@host", + CertOidcIssuer: oidcIssuer, + } + + _, err := ValidateAndUnpackCert(leafCert, co) + if err != nil { + t.Errorf("ValidateAndUnpackCert expected no error, got err = %v", err) + } + err = CheckCertificatePolicy(leafCert, co) + if err != nil { + t.Errorf("CheckCertificatePolicy expected no error, got err = %v", err) + } +} + func TestValidateAndUnpackCertInvalidRoot(t *testing.T) { subject := "email@email" oidcIssuer := "https://accounts.google.com" @@ -587,9 +719,9 @@ func TestValidateAndUnpackCertInvalidEmail(t *testing.T) { } _, err := ValidateAndUnpackCert(leafCert, co) - require.Contains(t, err.Error(), "expected email not found in certificate") + require.Contains(t, err.Error(), "expected identity not found in certificate") err = CheckCertificatePolicy(leafCert, co) - require.Contains(t, err.Error(), "expected email not found in certificate") + require.Contains(t, err.Error(), "expected identity not found in certificate") } func TestValidateAndUnpackCertInvalidGithubWorkflowTrigger(t *testing.T) { diff --git a/test/e2e_test.go b/test/e2e_test.go index 769379b3353..563ab2dfbb3 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -627,8 +627,8 @@ func TestSignBlob(t *testing.T) { KeyRef: pubKeyPath2, } // Verify should fail on a bad input - mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, "badsig", blob, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowName*/, "", "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) - mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, "badsig", blob, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowName*/, "", "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) + mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "badsig", blob, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) + mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "badsig", blob, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) // Now sign the blob with one key ko := options.KeyOpts{ @@ -640,8 +640,8 @@ func TestSignBlob(t *testing.T) { t.Fatal(err) } // Now verify should work with that one, but not the other - must(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, "", "", "", "", "", false), t) - mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, "", "", "", "", "", false), t) + must(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) + mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) } func TestSignBlobBundle(t *testing.T) { @@ -666,7 +666,7 @@ func TestSignBlobBundle(t *testing.T) { BundlePath: bundlePath, } // Verify should fail on a bad input - mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", blob, "", "", "", "", "", false), t) + mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "" /*sigRef*/, blob, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) // Now sign the blob with one key ko := options.KeyOpts{ @@ -679,7 +679,7 @@ func TestSignBlobBundle(t *testing.T) { t.Fatal(err) } // Now verify should work - must(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", bp, "", "", "", "", "", false), t) + must(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "" /*sigRef*/, bp, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) // Now we turn on the tlog and sign again defer setenv(t, options.ExperimentalEnv, "1")() @@ -689,7 +689,7 @@ func TestSignBlobBundle(t *testing.T) { // Point to a fake rekor server to make sure offline verification of the tlog entry works os.Setenv(serverEnv, "notreal") - must(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", bp, "", "", "", "", "", false), t) + must(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "" /*sigRef*/, bp, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t) } func TestGenerate(t *testing.T) {