Skip to content

Commit

Permalink
Added support for attaching Time stamp authority Response in attach c…
Browse files Browse the repository at this point in the history
…ommand (#3001)

* Added support for attaching Time stamp authority Response in attach command

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>

* Fixed white space issue

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>

* Fixed Lint issue gofmt -s error

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>

* updated timestampResponse argument to --tsr

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>

* updated timestampResponse argument to --tsr

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>

* added e2e test case for attach with rfc3161 time stamp response

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>

* reverted the changes for using freetsa in attach test

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>

* removed unused cert and implemented review comments

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>

---------

Signed-off-by: Mukuls77 <mukul.sharma@nokia.com>
Co-authored-by: Mukuls77 <mukul.sharma@nokia.com>
  • Loading branch information
Mukuls77 and MukulSharma77 authored Jun 6, 2023
1 parent 255ac31 commit 1894263
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 12 deletions.
2 changes: 1 addition & 1 deletion cmd/cosign/cli/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,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, args[0])
return attach.SignatureCmd(cmd.Context(), o.Registry, o.Signature, o.Payload, o.Cert, o.CertChain, o.TimeStampedSig, args[0])
},
}

Expand Down
15 changes: 12 additions & 3 deletions cmd/cosign/cli/attach/sig.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ import (
"path/filepath"

"github.com/google/go-containerregistry/pkg/name"

"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/pkg/cosign"
"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
"github.com/sigstore/cosign/v2/pkg/oci/mutate"
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
"github.com/sigstore/cosign/v2/pkg/oci/static"
)

func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef, payloadRef, certRef, certChainRef, imageRef string) error {
func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef, payloadRef, certRef, certChainRef, timeStampedSigRef, imageRef string) error {
b64SigBytes, err := signatureBytes(sigRef)
if err != nil {
return err
Expand Down Expand Up @@ -73,6 +73,7 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef,

var cert []byte
var certChain []byte
var timeStampedSig []byte

if certRef != "" {
cert, err = os.ReadFile(filepath.Clean(certRef))
Expand All @@ -88,7 +89,15 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef,
}
}

newSig, err := mutate.Signature(sig, mutate.WithCertChain(cert, certChain))
if timeStampedSigRef != "" {
timeStampedSig, err = os.ReadFile(filepath.Clean(timeStampedSigRef))
if err != nil {
return err
}
}
bundle := bundle.TimestampToRFC3161Timestamp(timeStampedSig)

newSig, err := mutate.Signature(sig, mutate.WithCertChain(cert, certChain), mutate.WithRFC3161Timestamp(bundle))
if err != nil {
return err
}
Expand Down
13 changes: 8 additions & 5 deletions cmd/cosign/cli/options/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ import (

// AttachSignatureOptions is the top level wrapper for the attach signature command.
type AttachSignatureOptions struct {
Signature string
Payload string
Cert string
CertChain string
Registry RegistryOptions
Signature string
Payload string
Cert string
CertChain string
TimeStampedSig string
Registry RegistryOptions
}

var _ Interface = (*AttachSignatureOptions)(nil)
Expand All @@ -54,6 +55,8 @@ func (o *AttachSignatureOptions) AddFlags(cmd *cobra.Command) {
"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().StringVar(&o.TimeStampedSig, "tsr", "",
"path to the Time Stamped Signature Response from RFC3161 compliant TSA")
}

// AttachSBOMOptions is the top level wrapper for the attach sbom command.
Expand Down
1 change: 1 addition & 0 deletions doc/cosign_attach_signature.md

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

104 changes: 102 additions & 2 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ import (
"bytes"
"context"
"crypto"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"net/http/httptest"
"net/url"
Expand Down Expand Up @@ -55,6 +59,8 @@ import (
"github.com/sigstore/cosign/v2/cmd/cosign/cli/publickey"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
cliverify "github.com/sigstore/cosign/v2/cmd/cosign/cli/verify"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa/client"
"github.com/sigstore/cosign/v2/pkg/cosign"
"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
"github.com/sigstore/cosign/v2/pkg/cosign/env"
Expand Down Expand Up @@ -112,6 +118,23 @@ var verifyTSA = func(keyRef, imageRef string, checkClaims bool, annotations map[
return cmd.Exec(context.Background(), args)
}

var verifyKeylessTSA = func(imageRef string, tsaCertChain string, skipSCT bool, skipTlogVerify bool) error {
cmd := cliverify.VerifyCommand{
CertVerifyOptions: options.CertVerifyOptions{
CertOidcIssuerRegexp: ".*",
CertIdentityRegexp: ".*",
},
HashAlgorithm: crypto.SHA256,
TSACertChainPath: tsaCertChain,
IgnoreSCT: skipSCT,
IgnoreTlog: skipTlogVerify,
}

args := []string{imageRef}

return cmd.Exec(context.Background(), args)
}

// Used to verify local images stored on disk
var verifyLocal = func(keyRef, path string, checkClaims bool, annotations map[string]interface{}, attachment string) error {
cmd := cliverify.VerifyCommand{
Expand Down Expand Up @@ -809,6 +832,84 @@ func TestAttestationRFC3161Timestamp(t *testing.T) {
must(verifyAttestation.Exec(ctx, []string{imgName}), t)
}

func TestAttachWithRFC3161Timestamp(t *testing.T) {
ctx := context.Background()
// TSA server needed to create timestamp
viper.Set("timestamp-signer", "memory")
apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, 10*time.Second, 10*time.Second)
server := httptest.NewServer(apiServer.GetHandler())
t.Cleanup(server.Close)

repo, stop := reg(t)
defer stop()
td := t.TempDir()

imgName := path.Join(repo, "cosign-attach-timestamp-e2e")

_, _, cleanup := mkimage(t, imgName)
defer cleanup()

b := bytes.Buffer{}
must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, nil, &b), t)

rootCert, rootKey, _ := GenerateRootCa()
subCert, subKey, _ := GenerateSubordinateCa(rootCert, rootKey)
leafCert, privKey, _ := GenerateLeafCert("subject@mail.com", "oidc-issuer", subCert, subKey)
pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw})
pemSub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw})
pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw})

rootPool := x509.NewCertPool()
rootPool.AddCert(rootCert)

payloadref := mkfile(b.String(), td, t)

h := sha256.Sum256(b.Bytes())
signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256)
b64signature := base64.StdEncoding.EncodeToString([]byte(signature))
sigRef := mkfile(b64signature, td, t)
pemleafRef := mkfile(string(pemLeaf), td, t)
pemrootRef := mkfile(string(pemRoot), td, t)

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

t.Setenv("SIGSTORE_ROOT_FILE", pemrootRef)

tsclient, err := tsaclient.GetTimestampClient(server.URL)
if err != nil {
t.Error(err)
}

chain, err := tsclient.Timestamp.GetTimestampCertChain(nil)
if err != nil {
t.Fatalf("unexpected error getting timestamp chain: %v", err)
}

file, err := os.CreateTemp(os.TempDir(), "tempfile")
if err != nil {
t.Fatalf("error creating temp file: %v", err)
}
defer os.Remove(file.Name())
_, err = file.WriteString(chain.Payload)
if err != nil {
t.Fatalf("error writing chain payload to temp file: %v", err)
}

tsBytes, err := tsa.GetTimestampedSignature(signature, client.NewTSAClient(server.URL+"/api/v1/timestamp"))
if err != nil {
t.Fatalf("unexpected error creating timestamp: %v", err)
}
rfc3161TSRef := mkfile(string(tsBytes), td, t)

// Upload it!
err = attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadref, pemleafRef, certchainRef, rfc3161TSRef, imgName)
if err != nil {
t.Fatal(err)
}

must(verifyKeylessTSA(imgName, file.Name(), true, true), t)
}

func TestRekorBundle(t *testing.T) {
// turn on the tlog
defer setenv(t, env.VariableExperimental.String(), "1")()
Expand Down Expand Up @@ -1490,9 +1591,8 @@ func TestUploadDownload(t *testing.T) {
} else {
sigRef = signature
}

// Upload it!
err := attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadPath, "", "", imgName)
err := attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadPath, "", "", "", imgName)
if testCase.expectedErr {
mustErr(err, t)
} else {
Expand Down
2 changes: 1 addition & 1 deletion test/e2e_test_attach.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ crane manifest $(./cosign triangulate $IMAGE_URI_DIGEST) | grep -q "dev.sigstore
## Verify Signature, payload, cert and cert-chain using SIGSTORE_ROOT_FILE

export SIGSTORE_ROOT_FILE=./rootcert.pem
./cosign verify $IMAGE_URI_DIGEST --insecure-ignore-sct --insecure-skip-tlog-verify --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*'
./cosign verify $IMAGE_URI_DIGEST --insecure-ignore-sct --insecure-ignore-tlog --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*'


# clean up a bit
Expand Down

0 comments on commit 1894263

Please sign in to comment.