diff --git a/go.mod b/go.mod index 1dac8476e7..de85b1db8d 100644 --- a/go.mod +++ b/go.mod @@ -570,7 +570,7 @@ require ( github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sigstore/rekor v1.5.3 // indirect - github.com/sigstore/sigstore v1.10.8 // indirect + github.com/sigstore/sigstore v1.10.8 github.com/sirupsen/logrus v1.9.4 github.com/skeema/knownhosts v1.3.1 // indirect github.com/spdx/tools-golang v0.6.0-rc4 // indirect diff --git a/src/pkg/packager/layout/package.go b/src/pkg/packager/layout/package.go index 6bd5d823f0..9a2e50de2c 100644 --- a/src/pkg/packager/layout/package.go +++ b/src/pkg/packager/layout/package.go @@ -360,7 +360,7 @@ func (p *PackageLayout) VerifyPackageSignature(ctx context.Context, opts signing opts.Key = opts.KeyRef //nolint:staticcheck // intentional read of deprecated alias for migration sync } - hasKey := opts.Key != "" + hasKey := opts.Key != "" || opts.SecurityKey.Use hasKeylessIdentity := opts.CertVerify.CertIdentity != "" || opts.CertVerify.CertIdentityRegexp != "" hasCert := opts.CertVerify.Cert != "" hasVerificationMaterial := hasKey || hasKeylessIdentity || hasCert @@ -413,7 +413,8 @@ func (p *PackageLayout) VerifyPackageSignature(ctx context.Context, opts signing opts.CommonVerifyOptions.UseSignedTimestamps = true } ZarfYAMLPath := filepath.Join(p.dirPath, ZarfYAML) - return signing.CosignVerifyBlobWithOptions(ctx, ZarfYAMLPath, opts) + _, err := signing.SigstoreVerifyBundleWithOptions(ctx, ZarfYAMLPath, opts) + return err } if !errors.Is(bundleErr, os.ErrNotExist) { return fmt.Errorf("error checking bundle signature: %w", bundleErr) diff --git a/src/pkg/signing/cosign.go b/src/pkg/signing/cosign.go index 3ee8c0bf83..97d513f326 100644 --- a/src/pkg/signing/cosign.go +++ b/src/pkg/signing/cosign.go @@ -19,12 +19,6 @@ import ( // Register ambient OIDC credential providers (GitHub Actions, GCP, SPIFFE, etc.) _ "github.com/sigstore/cosign/v3/pkg/providers/all" - // Register the provider-specific plugins - _ "github.com/sigstore/sigstore/pkg/signature/kms/aws" - _ "github.com/sigstore/sigstore/pkg/signature/kms/azure" - _ "github.com/sigstore/sigstore/pkg/signature/kms/gcp" - _ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault" - "github.com/zarf-dev/zarf/src/pkg/logger" ) diff --git a/src/pkg/signing/providers.go b/src/pkg/signing/providers.go new file mode 100644 index 0000000000..a8d0f40350 --- /dev/null +++ b/src/pkg/signing/providers.go @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +package signing + +import ( + // Register KMS schemes used by both legacy Cosign and direct Sigstore verification. + _ "github.com/sigstore/sigstore/pkg/signature/kms/aws" + _ "github.com/sigstore/sigstore/pkg/signature/kms/azure" + _ "github.com/sigstore/sigstore/pkg/signature/kms/gcp" + _ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault" +) diff --git a/src/pkg/signing/providers_test.go b/src/pkg/signing/providers_test.go new file mode 100644 index 0000000000..970ded1052 --- /dev/null +++ b/src/pkg/signing/providers_test.go @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +package signing + +import ( + "testing" + + "github.com/sigstore/sigstore/pkg/signature/kms" + "github.com/stretchr/testify/require" +) + +func TestKMSProvidersRegistered(t *testing.T) { + providers := kms.SupportedProviders() + + for _, scheme := range []string{ + "awskms://", + "azurekms://", + "gcpkms://", + "hashivault://", + "openbao://", + } { + require.Contains(t, providers, scheme) + } +} diff --git a/src/pkg/signing/sigstore.go b/src/pkg/signing/sigstore.go new file mode 100644 index 0000000000..53dbe3cd12 --- /dev/null +++ b/src/pkg/signing/sigstore.go @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +package signing + +import ( + "bytes" + "context" + "crypto" + "encoding/hex" + "errors" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" + "time" + + cosigngit "github.com/sigstore/cosign/v3/pkg/cosign/git" + "github.com/sigstore/cosign/v3/pkg/cosign/kubernetes" + "github.com/sigstore/cosign/v3/pkg/cosign/pivkey" + "github.com/sigstore/cosign/v3/pkg/cosign/pkcs11key" + "github.com/sigstore/sigstore-go/pkg/bundle" + "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" + "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore-go/pkg/verify" + "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature" + "github.com/sigstore/sigstore/pkg/signature/kms" + + "github.com/zarf-dev/zarf/src/pkg/logger" +) + +// SigstoreVerifyBundleWithOptions verifies a Sigstore bundle directly with +// sigstore-go and returns the verified bundle contents. +func SigstoreVerifyBundleWithOptions(ctx context.Context, blobPath string, opts VerifyBlobOptions) (*verify.VerificationResult, error) { + l := logger.From(ctx) + + if opts.KeyRef != "" { + l.Warn("VerifyBlobOptions.KeyRef is deprecated, use Key (removed in v1.0)") + if opts.Key == "" { + opts.Key = opts.KeyRef + } + } + if opts.SigRef != "" { + l.Warn("VerifyBlobOptions.SigRef is deprecated, use Signature (removed in v1.0)") + if opts.Signature == "" { + opts.Signature = opts.SigRef + } + } + if err := validateSigstoreBundleOptions(opts); err != nil { + return nil, err + } + if opts.BundlePath == "" { + return nil, errors.New("bundle path is required") + } + if opts.Timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, opts.Timeout) + defer cancel() + } + + b, err := bundle.LoadJSONFromPath(opts.BundlePath) + if err != nil { + return nil, fmt.Errorf("loading Sigstore bundle: %w", err) + } + + hashAlgorithm, err := opts.SignatureDigest.HashAlgorithm() + if err != nil { + return nil, err + } + keyVerifier, closeKey, err := resolveBundleVerifier(ctx, opts, hashAlgorithm) + if err != nil { + return nil, fmt.Errorf("loading verifier from key options: %w", err) + } + defer closeKey() + + useSignedTimestamps := opts.CommonVerifyOptions.UseSignedTimestamps + if !opts.CommonVerifyOptions.IgnoreTlog && keyVerifier == nil { + v1, v2, err := rekorBundleVersions(b) + if err != nil { + return nil, err + } + // Rekor v2 does not provide an integrated timestamp. This mirrors + // cosign's new-bundle verifier, which enables TSA validation for a + // v2-only bundle automatically. + if v2 && !v1 { + useSignedTimestamps = true + } + } + + trustedMaterial, err := trustedMaterialForBundle(opts, keyVerifier, useSignedTimestamps) + if err != nil { + return nil, err + } + verifierOptions, policyOptions, err := sigstoreVerificationOptions(opts, keyVerifier != nil, useSignedTimestamps) + if err != nil { + return nil, err + } + sev, err := verify.NewVerifier(trustedMaterial, verifierOptions...) + if err != nil { + return nil, fmt.Errorf("creating Sigstore verifier: %w", err) + } + + artifactPolicy, err := bundleArtifactPolicy(ctx, blobPath) + if err != nil { + return nil, err + } + result, err := sev.Verify(b, verify.NewPolicy(artifactPolicy, policyOptions...)) + if err != nil { + return nil, err + } + l.Debug("blob signature verified successfully") + return result, nil +} + +func validateSigstoreBundleOptions(opts VerifyBlobOptions) error { + if opts.Key != "" && (opts.CertVerify.CertIdentity != "" || opts.CertVerify.CertIdentityRegexp != "") { + return errors.New("key cannot be combined with certificate identity verification") + } + if opts.Key != "" && opts.SecurityKey.Use { + return errors.New("key cannot be combined with security-key verification") + } + // These options are rejected by cosign for protobuf bundles. Keeping that + // contract prevents detached material from silently weakening verification. + unsupported := []struct { + value string + name string + }{ + {opts.Signature, "detached signature"}, + {opts.CertVerify.Cert, "certificate"}, + {opts.CertVerify.CertChain, "certificate chain"}, + {opts.CertVerify.CARoots, "certificate authority roots"}, + {opts.CertVerify.CAIntermediates, "certificate authority intermediates"}, + {opts.CommonVerifyOptions.TSACertChainPath, "timestamp certificate chain"}, + {opts.CertVerify.SCT, "signed certificate timestamp"}, + } + for _, option := range unsupported { + if option.value != "" { + return fmt.Errorf("unsupported verification material for Sigstore bundles: %s", option.name) + } + } + return nil +} + +func sigstoreVerificationOptions(opts VerifyBlobOptions, hasKey bool, useSignedTimestamps bool) ([]verify.VerifierOption, []verify.PolicyOption, error) { + verifierOptions := []verify.VerifierOption{} + policyOptions := []verify.PolicyOption{} + if hasKey { + policyOptions = append(policyOptions, verify.WithKey()) + } else { + san, err := verify.NewSANMatcher(opts.CertVerify.CertIdentity, opts.CertVerify.CertIdentityRegexp) + if err != nil { + return nil, nil, err + } + issuer, err := verify.NewIssuerMatcher(opts.CertVerify.CertOidcIssuer, opts.CertVerify.CertOidcIssuerRegexp) + if err != nil { + return nil, nil, err + } + identity, err := verify.NewCertificateIdentity(san, issuer, certificate.Extensions{ + GithubWorkflowTrigger: opts.CertVerify.CertGithubWorkflowTrigger, + GithubWorkflowSHA: opts.CertVerify.CertGithubWorkflowSha, + GithubWorkflowName: opts.CertVerify.CertGithubWorkflowName, + GithubWorkflowRepository: opts.CertVerify.CertGithubWorkflowRepository, + GithubWorkflowRef: opts.CertVerify.CertGithubWorkflowRef, + }) + if err != nil { + return nil, nil, err + } + policyOptions = append(policyOptions, verify.WithCertificateIdentity(identity)) + if !opts.CertVerify.IgnoreSCT { + verifierOptions = append(verifierOptions, verify.WithSignedCertificateTimestamps(1)) + } + } + + if !opts.CommonVerifyOptions.IgnoreTlog { + verifierOptions = append(verifierOptions, verify.WithTransparencyLog(1)) + if !useSignedTimestamps { + if hasKey { + verifierOptions = append(verifierOptions, verify.WithNoObserverTimestamps()) + } else { + verifierOptions = append(verifierOptions, verify.WithIntegratedTimestamps(1)) + } + } + } + if useSignedTimestamps { + verifierOptions = append(verifierOptions, verify.WithSignedTimestamps(1)) + } + if opts.CommonVerifyOptions.IgnoreTlog && !useSignedTimestamps { + if hasKey { + verifierOptions = append(verifierOptions, verify.WithNoObserverTimestamps()) + } else { + verifierOptions = append(verifierOptions, verify.WithCurrentTime()) + } + } + return verifierOptions, policyOptions, nil +} + +func trustedMaterialForBundle(opts VerifyBlobOptions, keyVerifier signature.Verifier, useSignedTimestamps bool) (root.TrustedMaterial, error) { + var material root.TrustedMaterial = &root.BaseTrustedMaterial{} + needRoot := opts.CommonVerifyOptions.TrustedRootPath != "" || keyVerifier == nil || !opts.CommonVerifyOptions.IgnoreTlog || useSignedTimestamps + if needRoot { + var err error + if path := opts.CommonVerifyOptions.TrustedRootPath; path != "" { + material, err = root.NewTrustedRootFromPath(path) + } else { + material, err = root.NewTrustedRootFromJSON(embeddedTrustedRoot) + } + if err != nil { + return nil, fmt.Errorf("loading trusted root: %w", err) + } + } + if keyVerifier == nil { + return material, nil + } + expiringKey := root.NewExpiringKey(keyVerifier, time.Time{}, time.Time{}) + keyMaterial := root.NewTrustedPublicKeyMaterial(func(_ string) (root.TimeConstrainedVerifier, error) { + return expiringKey, nil + }) + return root.TrustedMaterialCollection{material, keyMaterial}, nil +} + +func rekorBundleVersions(b *bundle.Bundle) (hasV1, hasV2 bool, err error) { + entries, err := b.TlogEntries() + if err != nil { + return false, false, err + } + for _, entry := range entries { + if entry.IntegratedTime().IsZero() { + hasV2 = true + } else { + hasV1 = true + } + } + return hasV1, hasV2, nil +} + +func resolveBundleVerifier(ctx context.Context, opts VerifyBlobOptions, hashAlgorithm crypto.Hash) (signature.Verifier, func(), error) { + if opts.SecurityKey.Use { + key, err := pivkey.GetKeyWithSlot(opts.SecurityKey.Slot) + if err != nil { + return nil, func() {}, err + } + verifier, err := key.Verifier() + if err != nil { + key.Close() + return nil, func() {}, err + } + return verifier, key.Close, nil + } + if opts.Key == "" { + return nil, func() {}, nil + } + if strings.HasPrefix(opts.Key, "k8s://") { + secret, err := kubernetes.GetKeyPairSecret(ctx, opts.Key) + if err != nil { + return nil, func() {}, err + } + return verifierFromPEM(secret.Data["cosign.pub"], hashAlgorithm) + } + if strings.HasPrefix(opts.Key, "gitlab://") { + provider, reference, ok := strings.Cut(opts.Key, "://") + if !ok || reference == "" { + return nil, func() {}, errors.New("could not parse key reference, use gitlab://") + } + gitProvider := cosigngit.GetProvider(provider) + if gitProvider == nil { + return nil, func() {}, fmt.Errorf("no git provider found for %q", provider) + } + publicKey, err := gitProvider.GetSecret(ctx, reference, "COSIGN_PUBLIC_KEY") + if err != nil { + return nil, func() {}, err + } + return verifierFromPEM([]byte(publicKey), hashAlgorithm) + } + if strings.HasPrefix(opts.Key, "pkcs11:") { + config := pkcs11key.NewPkcs11UriConfig() + if err := config.Parse(opts.Key); err != nil { + return nil, func() {}, fmt.Errorf("parsing pkcs11 uri: %w", err) + } + key, err := pkcs11key.GetKeyWithURIConfig(config, false) + if err != nil { + return nil, func() {}, fmt.Errorf("opening pkcs11 token key: %w", err) + } + verifier, err := key.Verifier() + if err != nil { + key.Close() + return nil, func() {}, fmt.Errorf("initializing pkcs11 token verifier: %w", err) + } + return verifier, key.Close, nil + } + + verifier, err := kms.Get(ctx, opts.Key, hashAlgorithm) + if err == nil { + return verifier, func() {}, nil + } + var providerNotFound *kms.ProviderNotFoundError + if !errors.As(err, &providerNotFound) { + return nil, func() {}, fmt.Errorf("kms get: %w", err) + } + raw, err := loadPublicKeyReference(ctx, opts.Key) + if err != nil { + return nil, func() {}, err + } + return verifierFromPEM(raw, hashAlgorithm) +} + +func verifierFromPEM(raw []byte, hashAlgorithm crypto.Hash) (signature.Verifier, func(), error) { + publicKey, err := cryptoutils.UnmarshalPEMToPublicKey(raw) + if err != nil { + return nil, func() {}, fmt.Errorf("pem to public key: %w", err) + } + verifier, err := signature.LoadVerifier(publicKey, hashAlgorithm) + return verifier, func() {}, err +} + +func loadPublicKeyReference(ctx context.Context, reference string) ([]byte, error) { + switch { + case strings.HasPrefix(reference, "env://"): + value, ok := os.LookupEnv(strings.TrimPrefix(reference, "env://")) + if !ok { + return nil, fmt.Errorf("loading URL: env var $%s not found", strings.TrimPrefix(reference, "env://")) + } + return []byte(value), nil + case strings.HasPrefix(reference, "http://") || strings.HasPrefix(reference, "https://"): + // #nosec G107 -- the public key location is an explicit user input. + request, err := http.NewRequestWithContext(ctx, http.MethodGet, reference, nil) + if err != nil { + return nil, err + } + response, err := http.DefaultClient.Do(request) + if err != nil { + return nil, err + } + if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { + if closeErr := response.Body.Close(); closeErr != nil { + return nil, closeErr + } + return nil, fmt.Errorf("loading URL %s: server returned HTTP %d", reference, response.StatusCode) + } + raw, readErr := io.ReadAll(response.Body) + if closeErr := response.Body.Close(); closeErr != nil && readErr == nil { + return nil, closeErr + } + return raw, readErr + case strings.Contains(reference, "://"): + return nil, fmt.Errorf("loading URL: unrecognized scheme: %s", strings.SplitN(reference, "://", 2)[0]+"://") + default: + return os.ReadFile(filepath.Clean(reference)) + } +} + +func readBundleArtifact(ctx context.Context, reference string) ([]byte, error) { + if reference == "-" { + return io.ReadAll(os.Stdin) + } + return loadPublicKeyReference(ctx, reference) +} + +// bundleArtifactPolicy mirrors cosign's blob verifier: an unreadable artifact +// may instead be an explicitly supplied algorithm:hex-digest reference. +func bundleArtifactPolicy(ctx context.Context, reference string) (verify.ArtifactPolicyOption, error) { + artifact, readErr := readBundleArtifact(ctx, reference) + if readErr == nil { + return verify.WithArtifact(bytes.NewReader(artifact)), nil + } + + algorithm, encodedDigest, found := strings.Cut(reference, ":") + if !found { + return nil, readErr + } + digest, err := hex.DecodeString(encodedDigest) + if err != nil { + return nil, err + } + return verify.WithArtifactDigest(algorithm, digest), nil +} diff --git a/src/pkg/signing/sigstore_test.go b/src/pkg/signing/sigstore_test.go new file mode 100644 index 0000000000..eb1c516fe4 --- /dev/null +++ b/src/pkg/signing/sigstore_test.go @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +package signing + +import ( + "context" + "crypto" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "net" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/zarf-dev/zarf/src/test/testutil" + corev1 "k8s.io/api/core/v1" +) + +func TestSigstoreVerifyBundleWithOptions(t *testing.T) { + ctx := testutil.TestContext(t) + + const keyPath = "./testdata/cosign.key" + const pubPath = "./testdata/cosign.pub" + const password = "test" + + newBundle := func(t *testing.T) (string, string) { + t.Helper() + blobPath := filepath.Join(t.TempDir(), "payload.txt") + bundlePath := filepath.Join(t.TempDir(), "sig.bundle") + require.NoError(t, os.WriteFile(blobPath, []byte("direct verifier payload"), 0o644)) + + signOpts := DefaultSignBlobOptions() + signOpts.Key = keyPath + signOpts.Password = password + signOpts.BundlePath = bundlePath + _, err := CosignSignBlobWithOptions(ctx, blobPath, signOpts) + require.NoError(t, err) + return blobPath, bundlePath + } + + verify := func(t *testing.T, blobPath, bundlePath, key string) error { + t.Helper() + opts := DefaultVerifyBlobOptions() + opts.Key = key + opts.BundlePath = bundlePath + _, err := SigstoreVerifyBundleWithOptions(ctx, blobPath, opts) + return err + } + + t.Run("matches cosign for valid and tampered local-key bundles", func(t *testing.T) { + blobPath, bundlePath := newBundle(t) + require.NoError(t, verify(t, blobPath, bundlePath, pubPath)) + + cosignOpts := DefaultVerifyBlobOptions() + cosignOpts.Key = pubPath + cosignOpts.BundlePath = bundlePath + require.NoError(t, CosignVerifyBlobWithOptions(ctx, blobPath, cosignOpts)) + + require.NoError(t, os.WriteFile(blobPath, []byte("tampered"), 0o644)) + require.Error(t, verify(t, blobPath, bundlePath, pubPath)) + require.Error(t, CosignVerifyBlobWithOptions(ctx, blobPath, cosignOpts)) + }) + + t.Run("matches cosign for digest artifact references", func(t *testing.T) { + blobPath, bundlePath := newBundle(t) + payload, err := os.ReadFile(blobPath) + require.NoError(t, err) + digest := sha256.Sum256(payload) + artifactRef := "sha256:" + hex.EncodeToString(digest[:]) + + directOpts := DefaultVerifyBlobOptions() + directOpts.Key = pubPath + directOpts.BundlePath = bundlePath + _, err = SigstoreVerifyBundleWithOptions(ctx, artifactRef, directOpts) + require.NoError(t, err) + + cosignOpts := directOpts + require.NoError(t, CosignVerifyBlobWithOptions(ctx, artifactRef, cosignOpts)) + }) + + t.Run("accepts environment public-key references", func(t *testing.T) { + blobPath, bundlePath := newBundle(t) + publicKey, err := os.ReadFile(pubPath) + require.NoError(t, err) + t.Setenv("ZARF_TEST_COSIGN_PUBLIC_KEY", string(publicKey)) + require.NoError(t, verify(t, blobPath, bundlePath, "env://ZARF_TEST_COSIGN_PUBLIC_KEY")) + }) + + t.Run("accepts Kubernetes public-key references", func(t *testing.T) { + blobPath, bundlePath := newBundle(t) + publicKey, err := os.ReadFile(pubPath) + require.NoError(t, err) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) { + w.Header().Set("Content-Type", "application/json") + if request.URL.Path != "/api/v1/namespaces/signature-test/secrets/signing-public-key" { + http.NotFound(w, request) + return + } + if err := json.NewEncoder(w).Encode(corev1.Secret{ + Data: map[string][]byte{"cosign.pub": publicKey}, + }); err != nil { + t.Errorf("writing Kubernetes Secret response: %v", err) + } + })) + t.Cleanup(server.Close) + + kubeconfigPath := filepath.Join(t.TempDir(), "kubeconfig") + kubeconfig := fmt.Sprintf(`apiVersion: v1 +clusters: +- cluster: + server: %s + name: signing-test +contexts: +- context: + cluster: signing-test + namespace: signature-test + name: signing-test +current-context: signing-test +`, server.URL) + require.NoError(t, os.WriteFile(kubeconfigPath, []byte(kubeconfig), 0o600)) + t.Setenv("KUBECONFIG", kubeconfigPath) + + require.NoError(t, verify(t, blobPath, bundlePath, "k8s://signature-test/signing-public-key")) + }) + + t.Run("accepts URL public-key references", func(t *testing.T) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Skipf("loopback listener unavailable: %v", err) + } + blobPath, bundlePath := newBundle(t) + publicKey, err := os.ReadFile(pubPath) + require.NoError(t, err) + server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + if _, err := w.Write(publicKey); err != nil { + t.Errorf("writing public-key response: %v", err) + } + })) + server.Listener = listener + server.Start() + defer server.Close() + require.NoError(t, verify(t, blobPath, bundlePath, server.URL)) + }) + + t.Run("URL public-key retrieval honors verification timeout", func(t *testing.T) { + blobPath, bundlePath := newBundle(t) + requestStarted := make(chan struct{}) + server := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, request *http.Request) { + close(requestStarted) + <-request.Context().Done() + })) + t.Cleanup(server.Close) + + opts := DefaultVerifyBlobOptions() + opts.Key = server.URL + opts.BundlePath = bundlePath + opts.Timeout = 100 * time.Millisecond + _, err := SigstoreVerifyBundleWithOptions(ctx, blobPath, opts) + + require.ErrorIs(t, err, context.DeadlineExceeded) + select { + case <-requestStarted: + default: + t.Fatal("expected public-key request") + } + }) + + t.Run("rejects wrong keys and corrupt bundles without cosign fallback", func(t *testing.T) { + blobPath, bundlePath := newBundle(t) + require.Error(t, verify(t, blobPath, bundlePath, "./testdata/nonexistent.pub")) + require.NoError(t, os.WriteFile(bundlePath, []byte("not a bundle"), 0o644)) + require.Error(t, verify(t, blobPath, bundlePath, pubPath)) + }) + + t.Run("rejects detached verification material for bundles", func(t *testing.T) { + blobPath, bundlePath := newBundle(t) + opts := DefaultVerifyBlobOptions() + opts.Key = pubPath + opts.BundlePath = bundlePath + opts.Signature = "detached.sig" + _, err := SigstoreVerifyBundleWithOptions(ctx, blobPath, opts) + require.ErrorContains(t, err, "detached signature") + require.NotContains(t, err.Error(), "--") + }) + + t.Run("supports deprecated key alias", func(t *testing.T) { + blobPath, bundlePath := newBundle(t) + opts := DefaultVerifyBlobOptions() + opts.KeyRef = pubPath + opts.BundlePath = bundlePath + _, err := SigstoreVerifyBundleWithOptions(ctx, blobPath, opts) + require.NoError(t, err) + }) + + t.Run("uses embedded trusted root for keyless verification", func(t *testing.T) { + opts := DefaultVerifyBlobOptions() + material, err := trustedMaterialForBundle(opts, nil, false) + require.NoError(t, err) + require.NotEmpty(t, material.FulcioCertificateAuthorities()) + }) + + t.Run("uses embedded trusted root for keyed tlog verification", func(t *testing.T) { + publicKey, err := os.ReadFile(pubPath) + require.NoError(t, err) + keyVerifier, closeVerifier, err := verifierFromPEM(publicKey, crypto.SHA256) + require.NoError(t, err) + defer closeVerifier() + + opts := DefaultVerifyBlobOptions() + opts.CommonVerifyOptions.IgnoreTlog = false + material, err := trustedMaterialForBundle(opts, keyVerifier, false) + require.NoError(t, err) + require.NotEmpty(t, material.RekorLogs()) + require.NotEmpty(t, material.TimestampingAuthorities()) + }) + + t.Run("verifies keyless public-good bundle", func(t *testing.T) { + opts := DefaultVerifyBlobOptions() + opts.BundlePath = "./testdata/sigstore-js-2.0.0-provenance.sigstore.json" + opts.CertVerify.CertIdentityRegexp = "^https://github.com/sigstore/sigstore-js/" + opts.CertVerify.CertOidcIssuer = "https://token.actions.githubusercontent.com" + opts.CommonVerifyOptions.IgnoreTlog = false + + const digestReference = "sha512:46d4e2f74c4877316640000a6fdf8a8b59f1e0847667973e9859f774dd31b8f1e0937813b777fb66a2ac67d50540fe34640966eee9fc2ccca387082b4c85cd3c" + result, err := SigstoreVerifyBundleWithOptions(ctx, digestReference, opts) + require.NoError(t, err) + require.NotNil(t, result) + require.NotNil(t, result.VerifiedIdentity) + + invalidOpts := opts + invalidOpts.CertVerify.CertIdentityRegexp = "^https://github.com/sigstore/other-project/" + _, err = SigstoreVerifyBundleWithOptions(ctx, digestReference, invalidOpts) + require.Error(t, err) + }) +} + +func TestSigstoreBundleValidationErrorsUseLibraryTerms(t *testing.T) { + tests := []struct { + name string + configure func(*VerifyBlobOptions) + want string + }{ + { + name: "requires bundle path", + want: "bundle path is required", + }, + { + name: "rejects key with certificate identity", + configure: func(opts *VerifyBlobOptions) { + opts.BundlePath = "bundle.json" + opts.Key = "key.pem" + opts.CertVerify.CertIdentity = "https://example.test" + }, + want: "key cannot be combined with certificate identity verification", + }, + { + name: "rejects key with security key", + configure: func(opts *VerifyBlobOptions) { + opts.BundlePath = "bundle.json" + opts.Key = "key.pem" + opts.SecurityKey.Use = true + }, + want: "key cannot be combined with security-key verification", + }, + { + name: "rejects detached signature", + configure: func(opts *VerifyBlobOptions) { + opts.Signature = "signature" + }, + want: "unsupported verification material for Sigstore bundles: detached signature", + }, + { + name: "rejects certificate", + configure: func(opts *VerifyBlobOptions) { + opts.CertVerify.Cert = "certificate" + }, + want: "unsupported verification material for Sigstore bundles: certificate", + }, + { + name: "rejects certificate chain", + configure: func(opts *VerifyBlobOptions) { + opts.CertVerify.CertChain = "chain" + }, + want: "unsupported verification material for Sigstore bundles: certificate chain", + }, + { + name: "rejects certificate authority roots", + configure: func(opts *VerifyBlobOptions) { + opts.CertVerify.CARoots = "roots" + }, + want: "unsupported verification material for Sigstore bundles: certificate authority roots", + }, + { + name: "rejects certificate authority intermediates", + configure: func(opts *VerifyBlobOptions) { + opts.CertVerify.CAIntermediates = "intermediates" + }, + want: "unsupported verification material for Sigstore bundles: certificate authority intermediates", + }, + { + name: "rejects timestamp certificate chain", + configure: func(opts *VerifyBlobOptions) { + opts.CommonVerifyOptions.TSACertChainPath = "timestamp-chain" + }, + want: "unsupported verification material for Sigstore bundles: timestamp certificate chain", + }, + { + name: "rejects signed certificate timestamp", + configure: func(opts *VerifyBlobOptions) { + opts.CertVerify.SCT = "sct" + }, + want: "unsupported verification material for Sigstore bundles: signed certificate timestamp", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + opts := DefaultVerifyBlobOptions() + if tc.configure != nil { + tc.configure(&opts) + } + + var err error + if opts.BundlePath == "" { + _, err = SigstoreVerifyBundleWithOptions(testutil.TestContext(t), "", opts) + } else { + err = validateSigstoreBundleOptions(opts) + } + require.ErrorContains(t, err, tc.want) + require.NotContains(t, err.Error(), "--") + }) + } +} diff --git a/src/pkg/signing/testdata/sigstore-js-2.0.0-provenance.sigstore.json b/src/pkg/signing/testdata/sigstore-js-2.0.0-provenance.sigstore.json new file mode 100644 index 0000000000..628496f378 --- /dev/null +++ b/src/pkg/signing/testdata/sigstore-js-2.0.0-provenance.sigstore.json @@ -0,0 +1,60 @@ +{ + "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1", + "verificationMaterial": { + "x509CertificateChain": { + "certificates": [ + { + "rawBytes": "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" + } + ] + }, + "tlogEntries": [ + { + "logIndex": "31821305", + "logId": { + "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" + }, + "kindVersion": { + "kind": "intoto", + "version": "0.0.2" + }, + "integratedTime": "1692374735", + "inclusionPromise": { + "signedEntryTimestamp": "MEQCIBIG9TnhANgIZKrx20e1YQ0V7rnVs4/cKTf9tn3Y+NVIAiB8A0UwYu+Mc+E9pcP9ju7QOQYvLk8NajSeLp6sPLB1aA==" + }, + "inclusionProof": { + "logIndex": "27657874", + "rootHash": "v+7gOn1wovHHKBEVizJ5FFgTKUBCN9UxLo5KQ1Jz8cw=", + "treeSize": "27657875", + "hashes": [ + "/pZbqoFwAGIZaonQ2KdQj3HSGP7/4yfdZBUxKadw9Z8=", + "xZNrgfzUc8Ys5AKdeIpQ91hqM3mgCVdekTXsrM3GeBk=", + "0vtqRSUOxFOmLkErow/DJ4p9SYw2PsjCgIRfKa7/twg=", + "KXsEVwvzXH3v7vszv53J+jiAoKq1S9NCESUsKPStlUE=", + "NTFwGNVKjiF6zpAaoug3Zdn4bcdMPFje53W1Nq5UgEI=", + "aOgwCE1YnPdqr2RqEQElhpXvw1/6v+l9KuwI8pDg/j8=", + "ZW26eQRJVw4L+5bsecao28mT5P+mmfOQkz1yVnnLHOY=", + "uLuBRins5nkqq2rqd17R27pQTUF+xetttC6MsmlUzd0=", + "jRUq4D8O+FI47Wbw96s7yHCu4qzWUxpIVfxQEeprDmc=", + "rXEsmEJN4PEoTU8US4qVtdIsGB1MCiRlGOepoiC99kM=" + ], + "checkpoint": { + "envelope": "rekor.sigstore.dev - 2605736670972794746\n27657875\nv+7gOn1wovHHKBEVizJ5FFgTKUBCN9UxLo5KQ1Jz8cw=\nTimestamp: 1692374735595899989\n\n— rekor.sigstore.dev wNI9ajBEAiAzHmfHSCMNTSzP9h0Pzzdg95z3uaFP2n1992qoazwr5AIgPdgJIrzOe2CRYLLZTjMWFe9pBIg0r2hAevmsWrnXSyk=\n" + } + }, + "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVWQwZWtORFFtcDVaMEYzU1VKQlowbFZabVF2TlVaT09EaEZXRFJpZDNBM1l6ZFJOVnB5VDFoblVuYzBkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BOZDA5RVJUUk5WRmwzVGxSTk1WZG9ZMDVOYWsxM1QwUkZORTFVV1hoT1ZFMHhWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVVeVExcGFOR2RVV0VGeE5HazFiVmxGYkRNMlltUjNLMUpWVmtFeFNXRkROWFYzTmtrS2MwSjNhWGxtUlM5RVRITk5ibUpRY0dJdk1IWjNXRVZvTUdReFJrUlhaV1ZzTlZKYVpERTVkMVFyU1RCbFJEaHpURXRQUTBKV2MzZG5aMVpZVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWSlNFRmxDbEZpVVZwNk9YWkNkVU55SzB4cllYSmFWRzR6T0VOcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQxbDNXVVJXVWpCU1FWRklMMEpHYTNkV05GcFdZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRETk9jRm96VGpCaU0wcHNURE5PY0FwYU0wNHdZak5LYkV4WGNIcE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU0wcHNZa2RXYUdNeVZYVmxWekZ6VVVoS2JGcHVUWFpoUjFab0NscElUWFppVjBad1ltcEJOVUpuYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQUtZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVSkpSME5wYzBkQlVWRkNaemM0ZDBGUlNVVkNTRUl4WXpKbmQwNW5XVXRMZDFsQ1FrRkhSQXAyZWtGQ1FYZFJiMXBxUW1sT1JHeG9UVVJTYkU1WFJUSk5ha2t4VFVkVmQxcHFXWGRhYlVsNFRXcG5kMDFFVW1oT2VrMTRUVlJDYlZwVVRYaE5WRUZXQ2tKbmIzSkNaMFZGUVZsUEwwMUJSVVZDUVdSVFdsZDRiRmxZVG14TlEwbEhRMmx6UjBGUlVVSm5OemgzUVZGVlJVWklUbkJhTTA0d1lqTktiRXd6VG5BS1dqTk9NR0l6U214TVYzQjZUVUl3UjBOcGMwZEJVVkZDWnpjNGQwRlJXVVZFTTBwc1dtNU5kbUZIVm1oYVNFMTJZbGRHY0dKcVFUZENaMjl5UW1kRlJRcEJXVTh2VFVGRlNVSkRNRTFMTW1nd1pFaENlazlwT0haa1J6bHlXbGMwZFZsWFRqQmhWemwxWTNrMWJtRllVbTlrVjBveFl6SldlVmt5T1hWa1IxWjFDbVJETldwaU1qQjNXbEZaUzB0M1dVSkNRVWRFZG5wQlFrTlJVbGhFUmxadlpFaFNkMk42YjNaTU1tUndaRWRvTVZscE5XcGlNakIyWXpKc2JtTXpVbllLWTIxVmRtTXliRzVqTTFKMlkyMVZkR0Z1VFhaTWJXUndaRWRvTVZscE9UTmlNMHB5V20xNGRtUXpUWFpqYlZaeldsZEdlbHBUTlRWaVYzaEJZMjFXYlFwamVUbHZXbGRHYTJONU9YUlpWMngxVFVSblIwTnBjMGRCVVZGQ1p6YzRkMEZSYjBWTFozZHZXbXBDYVU1RWJHaE5SRkpzVGxkRk1rMXFTVEZOUjFWM0NscHFXWGRhYlVsNFRXcG5kMDFFVW1oT2VrMTRUVlJDYlZwVVRYaE5WRUZrUW1kdmNrSm5SVVZCV1U4dlRVRkZURUpCT0UxRVYyUndaRWRvTVZscE1XOEtZak5PTUZwWFVYZE9kMWxMUzNkWlFrSkJSMFIyZWtGQ1JFRlJjRVJEWkc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFpqTW14dVl6TlNkZ3BqYlZWMll6SnNibU16VW5aamJWVjBZVzVOZDA5QldVdExkMWxDUWtGSFJIWjZRVUpFVVZGeFJFTm9iVTFIU1RCUFYwVjNUa2RWTVZsVVdYbE5hbFYzQ2xwVVFtMU9ha0p0V1dwRmVVOUVRWGRPUjBVelRYcEZlRTFIV214TmVrVjRUVUk0UjBOcGMwZEJVVkZDWnpjNGQwRlJORVZGVVhkUVkyMVdiV041T1c4S1dsZEdhMk41T1hSWlYyeDFUVUpyUjBOcGMwZEJVVkZDWnpjNGQwRlJPRVZEZDNkS1RrUnJNVTVVWXpCT1ZGVXhUVU56UjBOcGMwZEJVVkZDWnpjNGR3cEJVa0ZGU0ZGM1ltRklVakJqU0UwMlRIazVibUZZVW05a1YwbDFXVEk1ZEV3elRuQmFNMDR3WWpOS2JFMUNaMGREYVhOSFFWRlJRbWMzT0hkQlVrVkZDa05uZDBsT2VrVjNUMVJaZWs1VVRYZGFVVmxMUzNkWlFrSkJSMFIyZWtGQ1JXZFNXRVJHVm05a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFlLWXpKc2JtTXpVblpqYlZWMll6SnNibU16VW5aamJWVjBZVzVOZGt4dFpIQmtSMmd4V1drNU0ySXpTbkphYlhoMlpETk5kbU50Vm5OYVYwWjZXbE0xTlFwaVYzaEJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOUkdkSFEybHpSMEZSVVVKbk56aDNRVkpOUlV0bmQyOWFha0pwVGtSc2FFMUVVbXhPVjBVeUNrMXFTVEZOUjFWM1dtcFpkMXB0U1hoTmFtZDNUVVJTYUU1NlRYaE5WRUp0V2xSTmVFMVVRVlZDWjI5eVFtZEZSVUZaVHk5TlFVVlZRa0ZaVFVKSVFqRUtZekpuZDFkbldVdExkMWxDUWtGSFJIWjZRVUpHVVZKTlJFVndiMlJJVW5kamVtOTJUREprY0dSSGFERlphVFZxWWpJd2RtTXliRzVqTTFKMlkyMVZkZ3BqTW14dVl6TlNkbU50VlhSaGJrMTJXVmRPTUdGWE9YVmplVGw1WkZjMWVreDZWVFZOUkZFeVQxUlpNMDVxVVhaWldGSXdXbGN4ZDJSSVRYWk5WRUZYQ2tKbmIzSkNaMFZGUVZsUEwwMUJSVmRDUVdkTlFtNUNNVmx0ZUhCWmVrTkNhWGRaUzB0M1dVSkNRVWhYWlZGSlJVRm5VamxDU0hOQlpWRkNNMEZPTURrS1RVZHlSM2g0UlhsWmVHdGxTRXBzYms1M1MybFRiRFkwTTJwNWRDODBaVXRqYjBGMlMyVTJUMEZCUVVKcFoyeHNSMUpCUVVGQlVVUkJSV2QzVW1kSmFBcEJTU3M0TTBKS1pEbGpPR2hOVlROdlRqTXpRbE5IYjNjM1ZVMDBZbk01YWtKSGFtOVFXa3QxTVZOS1UwRnBSVUZ2WTBacFRqWkRVVVk0ZEd3cldYTXhDa0V6T1dOMFJrWjRUMFp1TWtOeU5VNWhUemc1VVhwaVIxWk9WWGREWjFsSlMyOWFTWHBxTUVWQmQwMUVZVkZCZDFwblNYaEJUVU5wZEhwTlJ6aFFWbGdLUTJsaWEzRkJXVWhQUldOcGNteFRkVTVrY1V4UFIxTjRhblpSZGxweEsyNHZURkZFUVZoUVIyOTJlaTh2ZGxWSU0waFZXa3hCU1hoQlNqaFFjRnBYY0FwRlUyaDBLM2RETDI0eEt6SlVSVWRDUWpkaFJVbEJTbUpqUmxsS01rRnhSbEZKU1dwcWMxUmpRa3h0VGtwVU0wVkVRV2QwU2tOSVJraEJQVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUT09Iiwic2lnIjoiVFVWUlEwbEdWM0pRY0ROcE5UaHpibFZKYXpsSU5UbG9lbmxZU0hwUVJuTXpLMGRhUkhBclEzcGtUa3RZWTBKRlFXbENVVkZxZGxWaFZFZDRTMmxQUjJ4SE1VZFJlRXRzT1RGWldrVTRhMFZZTW5kaFVYQnpNRTVPVTFORlp6MDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZTBjZjg1NDI4MzQ0ZDRmZjE3N2E4ZWRjNDMxZTNmOTJiNDQ4Nzc1YTJiMDBiN2ZjZDdhN2FiM2QyZjk4ZWNhYyJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjA3NDJhNmZlMmE5MWViN2UyYzI3NDE0NGY2MTIzZjU5YTc5OTczMmM5ZDliZmQzYjdmZWFjNDg3ZjcyZWI0NGMifX19fQ==" + } + ], + "timestampVerificationData": null + }, + "dsseEnvelope": { + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicGtnOm5wbS9zaWdzdG9yZUAyLjAuMCIsImRpZ2VzdCI6eyJzaGE1MTIiOiI0NmQ0ZTJmNzRjNDg3NzMxNjY0MDAwMGE2ZmRmOGE4YjU5ZjFlMDg0NzY2Nzk3M2U5ODU5Zjc3NGRkMzFiOGYxZTA5Mzc4MTNiNzc3ZmI2NmEyYWM2N2Q1MDU0MGZlMzQ2NDA5NjZlZWU5ZmMyY2NjYTM4NzA4MmI0Yzg1Y2QzYyJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjEiLCJwcmVkaWNhdGUiOnsiYnVpbGREZWZpbml0aW9uIjp7ImJ1aWxkVHlwZSI6Imh0dHBzOi8vc2xzYS1mcmFtZXdvcmsuZ2l0aHViLmlvL2dpdGh1Yi1hY3Rpb25zLWJ1aWxkdHlwZXMvd29ya2Zsb3cvdjEiLCJleHRlcm5hbFBhcmFtZXRlcnMiOnsid29ya2Zsb3ciOnsicmVmIjoicmVmcy9oZWFkcy9tYWluIiwicmVwb3NpdG9yeSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qcyIsInBhdGgiOiIuZ2l0aHViL3dvcmtmbG93cy9yZWxlYXNlLnltbCJ9fSwiaW50ZXJuYWxQYXJhbWV0ZXJzIjp7ImdpdGh1YiI6eyJldmVudF9uYW1lIjoicHVzaCIsInJlcG9zaXRvcnlfaWQiOiI0OTU1NzQ1NTUiLCJyZXBvc2l0b3J5X293bmVyX2lkIjoiNzEwOTYzNTMifX0sInJlc29sdmVkRGVwZW5kZW5jaWVzIjpbeyJ1cmkiOiJnaXQraHR0cHM6Ly9naXRodWIuY29tL3NpZ3N0b3JlL3NpZ3N0b3JlLWpzQHJlZnMvaGVhZHMvbWFpbiIsImRpZ2VzdCI6eyJnaXRDb21taXQiOiJmMGI0OWEwNGU1YTYyMjUwZTBmNjBmYjEyODAwNGE3MzExMGZlMzExIn19XX0sInJ1bkRldGFpbHMiOnsiYnVpbGRlciI6eyJpZCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9hY3Rpb25zL3J1bm5lci9naXRodWItaG9zdGVkIn0sIm1ldGFkYXRhIjp7Imludm9jYXRpb25JZCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qcy9hY3Rpb25zL3J1bnMvNTkwNDY5Njc2NC9hdHRlbXB0cy8xIn19fX0=", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "sig": "MEQCIFWrPp3i58snUIk9H59hzyXHzPFs3+GZDp+CzdNKXcBEAiBQQjvUaTGxKiOGlG1GQxKl91YZE8kEX2waQps0NNSSEg==", + "keyid": "" + } + ] + } +} diff --git a/src/test/e2e/34_custom_init_package_test.go b/src/test/e2e/34_custom_init_package_test.go index c6ecbe56d5..df3b64e8c0 100644 --- a/src/test/e2e/34_custom_init_package_test.go +++ b/src/test/e2e/34_custom_init_package_test.go @@ -32,7 +32,6 @@ func TestCustomInit(t *testing.T) { // Test that we don't get an error when we remember to provide the public key stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", "definition", pkgName, publicKeyFlag) require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "Verified OK") /* Test operations during package deploy */ // Test that we get an error when trying to deploy a package without providing the public key