diff --git a/cmd/cosign/cli/attest/attest_blob.go b/cmd/cosign/cli/attest/attest_blob.go index 0ccae643da1..ec52a3d2319 100644 --- a/cmd/cosign/cli/attest/attest_blob.go +++ b/cmd/cosign/cli/attest/attest_blob.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "crypto" + "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -26,39 +27,60 @@ import ( "path" "path/filepath" "strings" + "time" "github.com/pkg/errors" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" + "github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa" "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/sigstore/cosign/v2/pkg/cosign/attestation" + cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle" "github.com/sigstore/cosign/v2/pkg/types" + "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/dsse" signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" + tsaclient "github.com/sigstore/timestamp-authority/pkg/client" ) // nolint type AttestBlobCommand struct { - KeyRef string + options.KeyOpts + CertPath string + CertChainPath string + ArtifactHash string PredicatePath string PredicateType string + TlogUpload bool + Timeout time.Duration + OutputSignature string OutputAttestation string - - PassFunc cosign.PassFunc + OutputCertificate string } // nolint func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error { - // TODO: Add in experimental keyless mode - if !options.OneOf(c.KeyRef) { + // We can't have both a key and a security key + if options.NOf(c.KeyRef, c.Sk) > 1 { return &options.KeyParseError{} } + if c.Timeout != 0 { + var cancelFn context.CancelFunc + ctx, cancelFn = context.WithTimeout(ctx, c.Timeout) + defer cancelFn() + } + + if c.TSAServerURL != "" && c.RFC3161TimestampPath == "" { + return errors.New("expected an rfc3161-timestamp path when using a TSA server") + } + var artifact []byte var hexDigest string var err error @@ -75,17 +97,6 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error } } - ko := options.KeyOpts{ - KeyRef: c.KeyRef, - PassFunc: c.PassFunc, - } - - sv, err := sign.SignerFromKeyOpts(ctx, "", "", ko) - if err != nil { - return errors.Wrap(err, "getting signer") - } - defer sv.Close() - if c.ArtifactHash == "" { digest, _, err := signature.ComputeDigestForSigning(bytes.NewReader(artifact), crypto.SHA256, []crypto.Hash{crypto.SHA256, crypto.SHA384}) if err != nil { @@ -95,7 +106,6 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error } else { hexDigest = c.ArtifactHash } - wrapped := dsse.WrapSigner(sv, types.IntotoPayloadType) fmt.Fprintln(os.Stderr, "Using predicate from:", c.PredicatePath) predicate, err := os.Open(c.PredicatePath) @@ -104,6 +114,13 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error } defer predicate.Close() + sv, err := sign.SignerFromKeyOpts(ctx, c.CertPath, c.CertChainPath, c.KeyOpts) + if err != nil { + return fmt.Errorf("getting signer: %w", err) + } + defer sv.Close() + wrapped := dsse.WrapSigner(sv, types.IntotoPayloadType) + base := path.Base(artifactPath) sh, err := attestation.GenerateStatement(attestation.GenerateOpts{ @@ -126,6 +143,68 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error return errors.Wrap(err, "signing") } + var rfc3161Timestamp *cbundle.RFC3161Timestamp + if c.TSAServerURL != "" { + clientTSA, err := tsaclient.GetTimestampClient(c.TSAServerURL) + if err != nil { + return fmt.Errorf("failed to create TSA client: %w", err) + } + respBytes, err := tsa.GetTimestampedSignature(sig, clientTSA) + if err != nil { + return err + } + rfc3161Timestamp = cbundle.TimestampToRFC3161Timestamp(respBytes) + // TODO: Consider uploading RFC3161 TS to Rekor + + if rfc3161Timestamp == nil { + return fmt.Errorf("rfc3161 timestamp is nil") + } + ts, err := json.Marshal(rfc3161Timestamp) + if err != nil { + return err + } + if err := os.WriteFile(c.RFC3161TimestampPath, ts, 0600); err != nil { + return fmt.Errorf("create RFC3161 timestamp file: %w", err) + } + fmt.Fprintln(os.Stderr, "RFC3161 timestamp bundle written to file ", c.RFC3161TimestampPath) + } + + rekorBytes, err := sv.Bytes(ctx) + if err != nil { + return err + } + shouldUpload, err := sign.ShouldUploadToTlog(ctx, c.KeyOpts, nil, c.TlogUpload) + if err != nil { + return fmt.Errorf("upload to tlog: %w", err) + } + signedPayload := cosign.LocalSignedPayload{} + if shouldUpload { + rekorClient, err := rekor.NewClient(c.RekorURL) + if err != nil { + return err + } + entry, err := cosign.TLogUploadInTotoAttestation(ctx, rekorClient, sig, rekorBytes) + if err != nil { + return err + } + fmt.Fprintln(os.Stderr, "tlog entry created with index:", *entry.LogIndex) + signedPayload.Bundle = cbundle.EntryToBundle(entry) + } + + if c.BundlePath != "" { + signedPayload.Base64Signature = base64.StdEncoding.EncodeToString(sig) + signedPayload.Cert = base64.StdEncoding.EncodeToString(rekorBytes) + + contents, err := json.Marshal(signedPayload) + if err != nil { + return err + } + if err := os.WriteFile(c.BundlePath, contents, 0600); err != nil { + return fmt.Errorf("create bundle file: %w", err) + } + fmt.Fprintln(os.Stderr, "Bundle wrote in the file ", c.BundlePath) + } + if c.OutputSignature != "" { if err := os.WriteFile(c.OutputSignature, sig, 0600); err != nil { return fmt.Errorf("create signature file: %w", err) @@ -142,5 +221,28 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error fmt.Fprintf(os.Stderr, "Attestation written in %s\n", c.OutputAttestation) } + if c.OutputCertificate != "" { + signer, err := sv.Bytes(ctx) + if err != nil { + return fmt.Errorf("error getting signer: %w", err) + } + cert, err := cryptoutils.UnmarshalCertificatesFromPEM(signer) + // signer is a certificate + if err != nil { + fmt.Fprintln(os.Stderr, "Could not output signer certificate. Was a certificate used? ", err) + return nil + + } + if len(cert) != 1 { + fmt.Fprintln(os.Stderr, "Could not output signer certificate. Expected a single certificate") + return nil + } + bts := signer + if err := os.WriteFile(c.OutputCertificate, bts, 0600); err != nil { + return fmt.Errorf("create certificate file: %w", err) + } + fmt.Fprintln(os.Stderr, "Certificate written to file ", c.OutputCertificate) + } + return nil } diff --git a/cmd/cosign/cli/attest/attest_blob_test.go b/cmd/cosign/cli/attest/attest_blob_test.go new file mode 100644 index 00000000000..6a21fb778cd --- /dev/null +++ b/cmd/cosign/cli/attest/attest_blob_test.go @@ -0,0 +1,243 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attest + +import ( + "bytes" + "context" + "crypto" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/in-toto/in-toto-golang/in_toto" + "github.com/pkg/errors" + ssldsse "github.com/secure-systems-lab/go-securesystemslib/dsse" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/generate" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/cosign/v2/test" + "github.com/sigstore/sigstore/pkg/signature" + "github.com/sigstore/sigstore/pkg/signature/dsse" + "github.com/theupdateframework/go-tuf/encrypted" +) + +// TestAttestBlobCmdLocalKeyAndSk verifies the AttestBlobCmd returns an error +// if both a local key path and a sk are specified +func TestAttestBlobCmdLocalKeyAndSk(t *testing.T) { + ctx := context.Background() + for _, ko := range []options.KeyOpts{ + // local and sk keys + { + KeyRef: "testLocalPath", + PassFunc: generate.GetPass, + Sk: true, + }, + } { + at := AttestBlobCommand{ + KeyOpts: ko, + } + err := at.Exec(ctx, "some/path") + if (errors.Is(err, &options.KeyParseError{}) == false) { + t.Fatal("expected KeyParseError") + } + } +} + +func writeFile(t *testing.T, td string, blob string, name string) string { + // Write blob to disk + blobPath := filepath.Join(td, name) + if err := os.WriteFile(blobPath, []byte(blob), 0644); err != nil { + t.Fatal(err) + } + return blobPath +} + +// TestAttestBlobCmdWithCert verifies the AttestBlobCmd checks +// that the cmd correctly matches the signing key with the cert +// provided. +func TestAttestBlobCmdLocalKeyAndCert(t *testing.T) { + td := t.TempDir() + rootCert, rootKey, _ := test.GenerateRootCa() + subCert, subKey, _ := test.GenerateSubordinateCa(rootCert, rootKey) + leafCert, privKey, _ := test.GenerateLeafCert("subject", "oidc-issuer", subCert, subKey) + pemChain := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw}) + subCertPem := writeFile(t, td, string(pemChain), "other_cert.pem") + pemChain = append(pemChain, + pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw})...) + pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw}) + + otherRootCert, _, _ := test.GenerateRootCa() + pemOtherRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: otherRootCert.Raw}) + otherRootPem := writeFile(t, td, string(pemOtherRoot), "other_root_cert.pem") + + x509Encoded, _ := x509.MarshalPKCS8PrivateKey(privKey) + encBytes, _ := encrypted.Encrypt(x509Encoded, nil) + keyPem := pem.EncodeToMemory(&pem.Block{ + Type: cosign.CosignPrivateKeyPemType, + Bytes: encBytes}) + keyRef := writeFile(t, td, string(keyPem), "key.pem") + certRef := writeFile(t, td, string(pemLeaf), "cert.pem") + chainRef := writeFile(t, td, string(pemChain), "chain.pem") + + blob := writeFile(t, td, "foo", "foo.txt") + predicate := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }` + predicateType := "slsaprovenance" + predicatePath := writeFile(t, td, predicate, "predicate.json") + + ctx := context.Background() + for _, tc := range []struct { + name string + keyref string + certref string + certchainref string + errString string + }{ + { + name: "no cert", + keyref: keyRef, + }, + { + name: "cert matches key", + keyref: keyRef, + certref: certRef, + }, + { + name: "fail: cert no match key", + keyref: keyRef, + certref: subCertPem, + errString: "public key in certificate does not match the provided public key", + }, + { + name: "cert chain matches key", + keyref: keyRef, + certref: certRef, + certchainref: chainRef, + }, + { + name: "cert chain partial", + keyref: keyRef, + certref: certRef, + certchainref: subCertPem, + }, + { + name: "fail: cert chain bad", + keyref: keyRef, + certref: certRef, + certchainref: otherRootPem, + errString: "unable to validate certificate chain", + }, + } { + t.Run(tc.name, func(t *testing.T) { + at := AttestBlobCommand{ + KeyOpts: options.KeyOpts{KeyRef: tc.keyref}, + CertPath: tc.certref, + CertChainPath: tc.certchainref, + PredicatePath: predicatePath, + PredicateType: predicateType, + } + err := at.Exec(ctx, blob) + if err != nil { + if tc.errString == "" { + t.Fatalf("unexpected error %v", err) + } + if !strings.Contains(err.Error(), tc.errString) { + t.Fatalf("expected error %v got %v", tc.errString, err) + } + return + } + if tc.errString != "" { + t.Fatalf("expected error %v", tc.errString) + } + }) + } +} + +// TestAttestBlob tests the main functionality -- does the command produce +// a validly signed DSSE envelope? (Using an on disk key) +func TestAttestBlob(t *testing.T) { + ctx := context.Background() + td := t.TempDir() + + keys, _ := cosign.GenerateKeyPair(nil) + keyRef := writeFile(t, td, string(keys.PrivateBytes), "key.pem") + pubKeyRef := writeFile(t, td, string(keys.PublicBytes), "key.pub") + + blob := []byte("foo") + blobPath := writeFile(t, td, string(blob), "foo.txt") + digest, _, _ := signature.ComputeDigestForSigning(bytes.NewReader(blob), crypto.SHA256, []crypto.Hash{crypto.SHA256, crypto.SHA384}) + blobDigest := strings.ToLower(hex.EncodeToString(digest)) + predicate := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }` + predicateType := "slsaprovenance" + predicatePath := writeFile(t, td, predicate, "predicate.json") + + dssePath := filepath.Join(td, "dsse.intoto.jsonl") + at := AttestBlobCommand{ + KeyOpts: options.KeyOpts{KeyRef: keyRef}, + PredicatePath: predicatePath, + PredicateType: predicateType, + OutputSignature: dssePath, + } + err := at.Exec(ctx, blobPath) + if err != nil { + t.Fatal(err) + } + + // Load the attestation. + dsseBytes, _ := os.ReadFile(dssePath) + env := &ssldsse.Envelope{} + if err := json.Unmarshal(dsseBytes, env); err != nil { + t.Fatal(err) + } + + if len(env.Signatures) != 1 { + t.Fatalf("expected 1 signature, got %d", len(env.Signatures)) + } + + // Verify the subject digest + decodedPredicate, err := base64.StdEncoding.DecodeString(env.Payload) + if err != nil { + t.Fatalf("decoding dsse payload: %v", err) + } + var statement in_toto.Statement + if err := json.Unmarshal(decodedPredicate, &statement); err != nil { + t.Fatalf("decoding predicate: %v", err) + } + if statement.Subject == nil || len(statement.Subject) != 1 { + t.Fatalf("expected one subject in intoto statement") + } + if statement.Subject[0].Digest["sha256"] != blobDigest { + t.Fatalf("expected matching digest") + } + if statement.PredicateType != options.PredicateTypeMap[predicateType] { + t.Fatalf("expected matching predicate type") + } + + // Load a verifier and DSSE verify + verifier, _ := signature.LoadVerifierFromPEMFile(pubKeyRef, crypto.SHA256) + dssev, err := ssldsse.NewEnvelopeVerifier(&dsse.VerifierAdapter{SignatureVerifier: verifier}) + if err != nil { + t.Fatalf("new envelope verifier: %v", err) + } + if _, err := dssev.Verify(env); err != nil { + t.Fatalf("dsse verify: %v", err) + } +} diff --git a/cmd/cosign/cli/attest_blob.go b/cmd/cosign/cli/attest_blob.go index 2a0398b341b..81a85b2f682 100644 --- a/cmd/cosign/cli/attest_blob.go +++ b/cmd/cosign/cli/attest_blob.go @@ -47,14 +47,41 @@ func AttestBlob() *cobra.Command { Args: cobra.ExactArgs(1), PersistentPreRun: options.BindViper, RunE: func(cmd *cobra.Command, args []string) error { + oidcClientSecret, err := o.OIDC.ClientSecret() + if err != nil { + return err + } + ko := options.KeyOpts{ + KeyRef: o.Key, + PassFunc: generate.GetPass, + Sk: o.SecurityKey.Use, + Slot: o.SecurityKey.Slot, + FulcioURL: o.Fulcio.URL, + IDToken: o.Fulcio.IdentityToken, + InsecureSkipFulcioVerify: o.Fulcio.InsecureSkipFulcioVerify, + RekorURL: o.Rekor.URL, + OIDCIssuer: o.OIDC.Issuer, + OIDCClientID: o.OIDC.ClientID, + OIDCClientSecret: oidcClientSecret, + OIDCRedirectURL: o.OIDC.RedirectURL, + OIDCProvider: o.OIDC.Provider, + SkipConfirmation: o.SkipConfirmation, + TSAServerURL: o.TSAServerURL, + RFC3161TimestampPath: o.RFC3161TimestampPath, + BundlePath: o.BundlePath, + } v := attest.AttestBlobCommand{ - KeyRef: o.Key, + KeyOpts: ko, + CertPath: o.Cert, + CertChainPath: o.CertChain, ArtifactHash: o.Hash, + TlogUpload: o.TlogUpload, PredicateType: o.Predicate.Type, PredicatePath: o.Predicate.Path, OutputSignature: o.OutputSignature, OutputAttestation: o.OutputAttestation, - PassFunc: generate.GetPass, + OutputCertificate: o.OutputCertificate, + Timeout: ro.Timeout, } return v.Exec(cmd.Context(), args[0]) }, diff --git a/cmd/cosign/cli/options/attest_blob.go b/cmd/cosign/cli/options/attest_blob.go index d530d122dff..b724d3cf898 100644 --- a/cmd/cosign/cli/options/attest_blob.go +++ b/cmd/cosign/cli/options/attest_blob.go @@ -20,12 +20,27 @@ import ( // AttestOptions is the top level wrapper for the attest command. type AttestBlobOptions struct { - Key string - Hash string + Key string + Cert string + CertChain string + + SkipConfirmation bool + TlogUpload bool + TSAServerURL string + RFC3161TimestampPath string + + Hash string + Predicate PredicateLocalOptions + OutputSignature string OutputAttestation string + OutputCertificate string + BundlePath string - Predicate PredicateLocalOptions + Rekor RekorOptions + Fulcio FulcioOptions + OIDC OIDCOptions + SecurityKey SecurityKeyOptions } var _ Interface = (*AttestOptions)(nil) @@ -33,9 +48,25 @@ var _ Interface = (*AttestOptions)(nil) // AddFlags implements Interface func (o *AttestBlobOptions) AddFlags(cmd *cobra.Command) { o.Predicate.AddFlags(cmd) + o.Rekor.AddFlags(cmd) + o.Fulcio.AddFlags(cmd) + o.OIDC.AddFlags(cmd) + o.SecurityKey.AddFlags(cmd) cmd.Flags().StringVar(&o.Key, "key", "", "path to the private key file, KMS URI or Kubernetes Secret") + _ = cmd.Flags().SetAnnotation("key", cobra.BashCompFilenameExt, []string{"key"}) + + cmd.Flags().StringVar(&o.Cert, "certificate", "", + "path to the X.509 certificate in PEM format to include in the OCI Signature") + _ = cmd.Flags().SetAnnotation("certificate", cobra.BashCompFilenameExt, []string{"cert"}) + + cmd.Flags().StringVar(&o.CertChain, "certificate-chain", "", + "path to a list of CA X.509 certificates in PEM format which will be needed "+ + "when building the certificate chain for the signing certificate. "+ + "Must start with the parent intermediate CA certificate of the "+ + "signing certificate and end with the root certificate. Included in the OCI Signature") + _ = cmd.Flags().SetAnnotation("certificate-chain", cobra.BashCompFilenameExt, []string{"cert"}) cmd.Flags().StringVar(&o.OutputSignature, "output-signature", "", "write the signature to FILE") @@ -44,6 +75,27 @@ func (o *AttestBlobOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.OutputAttestation, "output-attestation", "", "write the attestation to FILE") + cmd.Flags().StringVar(&o.OutputCertificate, "output-certificate", "", + "write the certificate to FILE") + _ = cmd.Flags().SetAnnotation("key", cobra.BashCompFilenameExt, []string{}) + + cmd.Flags().StringVar(&o.BundlePath, "bundle", "", + "write everything required to verify the blob to a FILE") + _ = cmd.Flags().SetAnnotation("bundle", cobra.BashCompFilenameExt, []string{}) + cmd.Flags().StringVar(&o.Hash, "hash", "", "hash of blob in hexadecimal (base16). Used if you want to sign an artifact stored elsewhere and have the hash") + + cmd.Flags().BoolVarP(&o.SkipConfirmation, "yes", "y", false, + "skip confirmation prompts for non-destructive operations") + + cmd.Flags().BoolVar(&o.TlogUpload, "tlog-upload", true, + "whether or not to upload to the tlog") + + cmd.Flags().StringVar(&o.TSAServerURL, "timestamp-server-url", "", + "url to the Timestamp RFC3161 server, default none") + + cmd.Flags().StringVar(&o.RFC3161TimestampPath, "rfc3161-timestamp-bundle", "", + "path to an RFC 3161 timestamp bundle FILE") + _ = cmd.Flags().SetAnnotation("rfc3161-timestamp-bundle", cobra.BashCompFilenameExt, []string{}) } diff --git a/doc/cosign_attest-blob.md b/doc/cosign_attest-blob.md index 78b1aee319d..599b3277e6e 100644 --- a/doc/cosign_attest-blob.md +++ b/doc/cosign_attest-blob.md @@ -30,13 +30,33 @@ cosign attest-blob [flags] ### Options ``` - --hash string hash of blob in hexadecimal (base16). Used if you want to sign an artifact stored elsewhere and have the hash - -h, --help help for attest-blob - --key string path to the private key file, KMS URI or Kubernetes Secret - --output-attestation string write the attestation to FILE - --output-signature string write the signature to FILE - --predicate string path to the predicate file. - --type string specify a predicate type (slsaprovenance|link|spdx|spdxjson|cyclonedx|vuln|custom) or an URI (default "custom") + --bundle string write everything required to verify the blob to a FILE + --certificate string path to the X.509 certificate in PEM format to include in the OCI Signature + --certificate-chain string path to a list of CA X.509 certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Included in the OCI Signature + --fulcio-url string [EXPERIMENTAL] address of sigstore PKI server (default "https://fulcio.sigstore.dev") + --hash string hash of blob in hexadecimal (base16). Used if you want to sign an artifact stored elsewhere and have the hash + -h, --help help for attest-blob + --identity-token string [EXPERIMENTAL] identity token to use for certificate from fulcio. the token or a path to a file containing the token is accepted. + --insecure-skip-verify [EXPERIMENTAL] skip verifying fulcio published to the SCT (this should only be used for testing). + --key string path to the private key file, KMS URI or Kubernetes Secret + --oidc-client-id string [EXPERIMENTAL] OIDC client ID for application (default "sigstore") + --oidc-client-secret-file string [EXPERIMENTAL] Path to file containing OIDC client secret for application + --oidc-disable-ambient-providers [EXPERIMENTAL] Disable ambient OIDC providers. When true, ambient credentials will not be read + --oidc-issuer string [EXPERIMENTAL] OIDC provider to be used to issue ID token (default "https://oauth2.sigstore.dev/auth") + --oidc-provider string [EXPERIMENTAL] Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github, filesystem] + --oidc-redirect-url string [EXPERIMENTAL] OIDC redirect URL (Optional). The default oidc-redirect-url is 'http://localhost:0/auth/callback'. + --output-attestation string write the attestation to FILE + --output-certificate string write the certificate to FILE + --output-signature string write the signature to FILE + --predicate string path to the predicate file. + --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") + --rfc3161-timestamp-bundle string path to an RFC 3161 timestamp bundle FILE + --sk whether to use a hardware security key + --slot string security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management) + --timestamp-server-url string url to the Timestamp RFC3161 server, default none + --tlog-upload whether or not to upload to the tlog (default true) + --type string specify a predicate type (slsaprovenance|link|spdx|spdxjson|cyclonedx|vuln|custom) or an URI (default "custom") + -y, --yes skip confirmation prompts for non-destructive operations ``` ### Options inherited from parent commands diff --git a/test/e2e_test.go b/test/e2e_test.go index c31ba6fcc7c..f130144c7ad 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -1858,12 +1858,15 @@ func TestAttestBlobSignVerify(t *testing.T) { mustErr(blobVerifyAttestationCmd.Exec(ctx, bp), t) // Now attest the blob with the private key + ko := options.KeyOpts{ + KeyRef: privKeyPath1, + PassFunc: passFunc, + } attestBlobCmd := attest.AttestBlobCommand{ - KeyRef: privKeyPath1, + KeyOpts: ko, PredicatePath: predicatePath, PredicateType: predicateType, OutputSignature: outputSignature, - PassFunc: passFunc, } must(attestBlobCmd.Exec(ctx, bp), t)