Skip to content

Commit

Permalink
feat: implement cosign download attestation (#1216)
Browse files Browse the repository at this point in the history
* signing attestation should private key

Signed-off-by: Ivan Wallis <[email protected]>

* initial commit

Signed-off-by: Ivan Wallis <[email protected]>

* error handling

Signed-off-by: Ivan Wallis <[email protected]>

* doc gen CI check fix

Signed-off-by: Ivan Wallis <[email protected]>
  • Loading branch information
venafi-iw authored Dec 17, 2021
1 parent d318979 commit ac8a7e9
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 0 deletions.
19 changes: 19 additions & 0 deletions cmd/cosign/cli/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func Download() *cobra.Command {
cmd.AddCommand(
downloadSignature(),
downloadSBOM(),
downloadAttestation(),
)

return cmd
Expand Down Expand Up @@ -72,3 +73,21 @@ func downloadSBOM() *cobra.Command {

return cmd
}

func downloadAttestation() *cobra.Command {
o := &options.RegistryOptions{}

cmd := &cobra.Command{
Use: "attestation",
Short: "Download in-toto attestations from the supplied container image",
Example: " cosign download attesation <image uri>",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return download.AttestationCmd(cmd.Context(), *o, args[0])
},
}

o.AddFlags(cmd)

return cmd
}
49 changes: 49 additions & 0 deletions cmd/cosign/cli/download/attestation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// Copyright 2021 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 download

import (
"context"
"encoding/json"
"fmt"

"github.com/google/go-containerregistry/pkg/name"
"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/pkg/cosign"
)

func AttestationCmd(ctx context.Context, regOpts options.RegistryOptions, imageRef string) error {
ref, err := name.ParseReference(imageRef)
if err != nil {
return err
}
ociremoteOpts, err := regOpts.ClientOpts(ctx)
if err != nil {
return err
}
attestations, err := cosign.FetchAttestationsForReference(ctx, ref, ociremoteOpts...)
if err != nil {
return err
}
for _, att := range attestations {
b, err := json.Marshal(att)
if err != nil {
return err
}
fmt.Println(string(b))
}
return nil
}
1 change: 1 addition & 0 deletions doc/cosign_download.md

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

35 changes: 35 additions & 0 deletions doc/cosign_download_attestation.md

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

50 changes: 50 additions & 0 deletions pkg/cosign/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cosign
import (
"context"
"crypto/x509"
"encoding/json"
"fmt"
"runtime"

Expand All @@ -37,6 +38,17 @@ type SignedPayload struct {
Bundle *oci.Bundle
}

type Signatures struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
}

type AttestationPayload struct {
PayloadType string `json:"payloadType"`
PayLoad string `json:"payload"`
Signatures []Signatures `json:"signatures"`
}

const (
SignatureTagSuffix = ".sig"
SBOMTagSuffix = ".sbom"
Expand Down Expand Up @@ -98,3 +110,41 @@ func FetchSignaturesForReference(ctx context.Context, ref name.Reference, opts .

return signatures, nil
}

func FetchAttestationsForReference(ctx context.Context, ref name.Reference, opts ...ociremote.Option) ([]AttestationPayload, error) {
simg, err := ociremote.SignedEntity(ref, opts...)
if err != nil {
return nil, err
}

atts, err := simg.Attestations()
if err != nil {
return nil, errors.Wrap(err, "remote image")
}
l, err := atts.Get()
if err != nil {
return nil, errors.Wrap(err, "fetching attestations")
}
if len(l) == 0 {
return nil, fmt.Errorf("no attestations associated with %v", ref)
}

g := pool.New(runtime.NumCPU())
attestations := make([]AttestationPayload, len(l))
for i, att := range l {
i, att := i, att
g.Go(func() (err error) {
attestPayload, _ := att.Payload()
err = json.Unmarshal(attestPayload, &attestations[i])
if err != nil {
return err
}
return err
})
}
if err := g.Wait(); err != nil {
return nil, err
}

return attestations, nil
}

0 comments on commit ac8a7e9

Please sign in to comment.