Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse the rekor bundle correctly #3461

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
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/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func attachSignature() *cobra.Command {
PersistentPreRun: options.BindViper,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return attach.SignatureCmd(cmd.Context(), o.Registry, o.Signature, o.Payload, o.Cert, o.CertChain, o.TimeStampedSig, o.RekorBundle, args[0])
return attach.SignatureCmd(cmd.Context(), o.Registry, o.Signature, o.Payload, o.Cert, o.CertChain, o.TimeStampedSig, o.RekorResponse, args[0])
},
}

Expand Down
29 changes: 19 additions & 10 deletions cmd/cosign/cli/attach/sig.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
Expand All @@ -32,7 +33,7 @@ import (
"github.com/sigstore/cosign/v2/pkg/oci/static"
)

func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef, payloadRef, certRef, certChainRef, timeStampedSigRef, rekorBundleRef, imageRef string) error {
func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef, payloadRef, certRef, certChainRef, timeStampedSigRef, rekorResponseRef, imageRef string) error {
b64SigBytes, err := signatureBytes(sigRef)
if err != nil {
return err
Expand Down Expand Up @@ -75,8 +76,7 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef,
var cert []byte
var certChain []byte
var timeStampedSig []byte
var rekorBundle *bundle.RekorBundle

rekorBundle := &bundle.RekorBundle{}
if certRef != "" {
cert, err = os.ReadFile(filepath.Clean(certRef))
if err != nil {
Expand All @@ -98,20 +98,29 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef,
}
}
tsBundle := bundle.TimestampToRFC3161Timestamp(timeStampedSig)

if rekorBundleRef != "" {
rekorBundleByte, err := os.ReadFile(filepath.Clean(rekorBundleRef))
if rekorResponseRef != "" {
rekorResponseByte, err := os.ReadFile(filepath.Clean(rekorResponseRef))
if err != nil {
return err
}

var localCosignPayload cosign.LocalSignedPayload
err = json.Unmarshal(rekorBundleByte, &localCosignPayload)
var rekorResponse cosign.RekorResponse
err = json.Unmarshal(rekorResponseByte, &rekorResponse)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wouldn't want to return on the first error, since the input may be one of two structures. We'll want something like:

var localCosignPayload cosign.LocalSignedPayload
if err := json.Unmarshal(rekorResponseByte, &localCosignPayload); err == nil {
  rekorBundle = localCosignPayload.Bundle
} else {
  err := json.Unmarshal(rekorResponseByte, rekorBundle)
  if err != nil {
    return fmt.Errorf("unable to parse Rekor response to attach to image")
  }
}

This also removes the need for the extra duplicateLocalCosignPayload variable since you can use rekorBundle directly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment still relevant. Line 110 would return immediately if there was an error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@viveksahu26 Just bumping

return err
return fmt.Errorf("unmarshal rekorResponse error: %w", err)
}

rekorBundle = localCosignPayload.Bundle
if rekorResponse == nil {
return fmt.Errorf("unable to parse rekor-response to attach to image")
}

for _, v := range rekorResponse {
rekorBundle.SignedEntryTimestamp = v.Verification.SignedEntryTimestamp
rekorBundle.Payload.Body = v.Body
rekorBundle.Payload.IntegratedTime = *v.IntegratedTime
rekorBundle.Payload.LogIndex = *v.LogIndex
rekorBundle.Payload.LogID = *v.LogID
}
}

newSig, err := mutate.Signature(sig, mutate.WithCertChain(cert, certChain), mutate.WithRFC3161Timestamp(tsBundle), mutate.WithBundle(rekorBundle))
Expand Down
8 changes: 5 additions & 3 deletions cmd/cosign/cli/options/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type AttachSignatureOptions struct {
Cert string
CertChain string
TimeStampedSig string
RekorBundle string
RekorResponse string
Registry RegistryOptions
}

Expand All @@ -58,8 +58,10 @@ func (o *AttachSignatureOptions) AddFlags(cmd *cobra.Command) {
"signing certificate and end with the root certificate. Included in the OCI Signature")
cmd.Flags().StringVar(&o.TimeStampedSig, "tsr", "",
"path to the Time Stamped Signature Response from RFC3161 compliant TSA")
cmd.Flags().StringVar(&o.RekorBundle, "rekor-response", "",
"path to the rekor bundle")
cmd.Flags().StringVar(&o.RekorResponse, "rekor-response", "",
"NOTE: the path can be either bundle, i.e. `bundle.json` which can be retrieve as o/p of command "+
"`cosign sign --bundle < bundle.json >` or "+
"rekor bundle formatted from rekor-response.")
}

// AttachSBOMOptions is the top level wrapper for the attach sbom command.
Expand Down
2 changes: 1 addition & 1 deletion doc/cosign_attach_signature.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/cosign/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
"github.com/sigstore/cosign/v2/pkg/oci"
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
"github.com/sigstore/rekor/pkg/generated/models"
"golang.org/x/sync/errgroup"
)

Expand All @@ -51,6 +52,8 @@ type LocalSignedPayload struct {
Bundle *bundle.RekorBundle `json:"rekorBundle,omitempty"`
}

type RekorResponse map[string]models.LogEntryAnon

type Signatures struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
Expand Down
48 changes: 30 additions & 18 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"github.com/google/go-containerregistry/pkg/registry"
"github.com/google/go-containerregistry/pkg/v1/random"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/sigstore/rekor/pkg/generated/models"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8s "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -236,7 +237,6 @@ func TestSignVerifyClean(t *testing.T) {
}

func TestImportSignVerifyClean(t *testing.T) {

repo, stop := reg(t)
defer stop()
td := t.TempDir()
Expand Down Expand Up @@ -930,7 +930,7 @@ func TestAttachWithRFC3161Timestamp(t *testing.T) {
must(verifyKeylessTSA(imgName, file.Name(), true, true), t)
}

func TestAttachWithRekorBundle(t *testing.T) {
func TestAttachWithRekorResponse(t *testing.T) {
ctx := context.Background()

repo, stop := reg(t)
Expand Down Expand Up @@ -968,31 +968,45 @@ func TestAttachWithRekorBundle(t *testing.T) {

certchainRef := mkfile(string(append(pemSub[:], pemRoot[:]...)), td, t)

localPayload := cosign.LocalSignedPayload{
Base64Signature: b64signature,
Cert: string(pemLeaf),
Bundle: &bundle.RekorBundle{
SignedEntryTimestamp: strfmt.Base64("MEUCIEDcarEwRYkrxE9ne+kzEVvUhnWaauYzxhUyXOLy1hwAAiEA4VdVCvNRs+D/5o33C2KBy+q2YX3lP4Y7nqRFU+K3hi0="),
Payload: bundle.RekorPayload{
Body: "REMOVED",
IntegratedTime: 1631646761,
LogIndex: 693591,
LogID: "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
rekorResponse := make(cosign.RekorResponse)
integratedTime := int64(1706680021)
logID := "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"
logIndex := int64(67934953)
checkPoint := "rekor.sigstore.dev - 2605736670972794746\n64319120\nW7/06fgDSjOxApliccDgG4fKqDxExG6JO0e4FGf9gIw=\nTimestamp: 1706845443714164712\n\n— rekor.sigstore.dev wNI9ajBFAiEAnduIhP1Jjz8E0ZAP8e1x0aKqzJCtmWZyV1mRJB/PlOoCIBoeHdjeONYmxlD2Za7sU0NeK/60skNnwoelsa3m2M8z\n"
logIndexValue := int64(63771522)
rootHash := "5bbff4e9f8034a33b102996271c0e01b87caa83c44c46e893b47b81467fd808c"
treeSize := int64(64319120)

logEntry := models.LogEntryAnon{
Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI3YTIxODlmNzNlYTVkYmVlMmQ1NjgxMGU4NjBjZDgxNjdlYTFiMGYzMTdkZGRjMmU0YzE2NmU3ZDY4NzUzOGYwIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJQlZtMVc1YlNSOXBTYVFyRWRVL2dOeEZuaVQzMCs4RGp5SG9naERwWHM3b0FpQXMyby82c3l2bzRaTDd3YVUrMXBNeVIvR1dNWlZTaUdzRm5hSm04ZDZZZ0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCUVZVSk1TVU1nUzBWWkxTMHRMUzBLVFVacmQwVjNXVWhMYjFwSmVtb3dRMEZSV1VsTGIxcEplbW93UkVGUlkwUlJaMEZGUldGSWNHUmhZVE13ZEVOaVMxbG5lalpyUlhsQmNqTXJORmh5TWdwb1pWUjNaM2hQVVVrM2QwcDRiVWhIWW5OU1dYcEJWbFJyUVN0RGIzVjZUblZrTUc5eGRuUTRXVXB0Tm5CQlFUZG5SbUZFUkZGU05EUlJQVDBLTFMwdExTMUZUa1FnVUZWQ1RFbERJRXRGV1MwdExTMHRDZz09In19fX0=",
IntegratedTime: &integratedTime,
LogID: &logID,
LogIndex: &logIndex,
Verification: &models.LogEntryAnonVerification{
InclusionProof: &models.InclusionProof{
Checkpoint: &checkPoint,
LogIndex: &logIndexValue,
RootHash: &rootHash,
TreeSize: &treeSize,
},
SignedEntryTimestamp: strfmt.Base64("MEUCIQCLiNiSLxFk8vgkCopYcuFQXGEcvr6YM0TXgFUe5HcAHAIgSJuWj3uH8QrQeaEfc5ddMpIwU4JdXmQD3bgfkntEVTk="),
},
}

jsonBundle, err := json.Marshal(localPayload)
rekorResponse["24296fb24b8ad77a8ada322cbba201d23b88acd2f68b29e358668e3aef36306ddb2c253ca0dc9ede"] = logEntry

jsonRekorResponsePath, err := json.Marshal(rekorResponse)
if err != nil {
t.Fatal(err)
}
bundlePath := filepath.Join(td, "bundle.json")
if err := os.WriteFile(bundlePath, jsonBundle, 0644); err != nil {

rekorResponsePath := filepath.Join(td, "rekor-response.json")
if err := os.WriteFile(rekorResponsePath, jsonRekorResponsePath, 0644); err != nil {
t.Fatal(err)
}

// Upload it!
err = attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadref, pemleafRef, certchainRef, "", bundlePath, imgName)
err = attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadref, pemleafRef, certchainRef, "", rekorResponsePath, imgName)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -1615,7 +1629,6 @@ func keypair(t *testing.T, td string) (*cosign.KeysBytes, string, string) {
}

func importKeyPair(t *testing.T, td string) (*cosign.KeysBytes, string, string) {

const validrsa1 = `-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAx5piWVlE62NnZ0UzJ8Z6oKiKOC4dbOZ1HsNhIRtqkM+Oq4G+
25yq6P+0JU/Qvr9veOGEb3R/J9u8JBo+hv2i5X8OtgvP2V2pi6f1s6vK7L0+6uRb
Expand Down Expand Up @@ -1675,7 +1688,6 @@ qGzRVIDGbNkrVHM0IsAtHRpC0rYrtZY+9OwiraGcsqUMLwwQdCA=
t.Fatal(err)
}
return keys, privKeyPath, pubKeyPath

}

func TestUploadDownload(t *testing.T) {
Expand Down
Loading