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

bundle support for sign-blob #3138

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions cmd/cosign/cli/options/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type KeyOpts struct {
OIDCDisableProviders bool // Disable OIDC credential providers in keyless signer
OIDCProvider string // Specify which OIDC credential provider to use for keyless signer
BundlePath string
UsePBBundleFormat bool
SkipConfirmation bool
TSAClientCACert string
TSAClientCert string
Expand Down
4 changes: 4 additions & 0 deletions cmd/cosign/cli/options/signblob.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type SignBlobOptions struct {
OIDC OIDCOptions
Registry RegistryOptions
BundlePath string
UsePBBundleFormat bool
SkipConfirmation bool
TlogUpload bool
TSAServerURL string
Expand Down Expand Up @@ -71,6 +72,9 @@ func (o *SignBlobOptions) AddFlags(cmd *cobra.Command) {
"write everything required to verify the blob to a FILE")
_ = cmd.Flags().SetAnnotation("bundle", cobra.BashCompFilenameExt, []string{})

cmd.Flags().BoolVar(&o.UsePBBundleFormat, "use-new-bundle-format", false,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a TODO to eventually make this flag a no-op and add a deprecated-format flag?

"use the new bundle format defined in sigstore/protobuf-specs")

cmd.Flags().BoolVarP(&o.SkipConfirmation, "yes", "y", false,
"skip confirmation prompts for non-destructive operations")

Expand Down
90 changes: 77 additions & 13 deletions cmd/cosign/cli/sign/sign_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@ import (
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa/client"
"github.com/sigstore/cosign/v2/internal/ui"
cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle"
pbbundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1"
pbcommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
pbrekor "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1"
"github.com/sigstore/rekor/pkg/generated/models"

"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
internal "github.com/sigstore/cosign/v2/internal/pkg/cosign"
"github.com/sigstore/cosign/v2/pkg/cosign"
"github.com/sigstore/cosign/v2/pkg/protobundle"
"github.com/sigstore/sigstore/pkg/cryptoutils"
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options"
"google.golang.org/protobuf/encoding/protojson"
)

// nolint
Expand Down Expand Up @@ -71,6 +77,7 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string
}

signedPayload := cosign.LocalSignedPayload{}
var rekorEntry *models.LogEntryAnon

var rfc3161Timestamp *cbundle.RFC3161Timestamp
if ko.TSAServerURL != "" {
Expand Down Expand Up @@ -117,26 +124,83 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string
}
ui.Infof(ctx, "tlog entry created with index: %d", *entry.LogIndex)
signedPayload.Bundle = cbundle.EntryToBundle(entry)
rekorEntry = entry
}

// if bundle is specified, just do that and ignore the rest
if ko.BundlePath != "" {
signedPayload.Base64Signature = base64.StdEncoding.EncodeToString(sig)
if ko.UsePBBundleFormat {
vm := pbbundle.VerificationMaterial{}

certBytes, err := extractCertificate(ctx, sv)
if err != nil {
return nil, err
}
signedPayload.Cert = base64.StdEncoding.EncodeToString(certBytes)
certBytes, err := sv.Bytes(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll also need to handle where sv.Bytes() returns a key, since that goes into a different message. We could change sv.Bytes() to return whether it's a key or cert, or try to parse the return value to determine if it's a key or cert.

if err != nil {
return nil, fmt.Errorf("error getting signer: %w", err)
}
certChain, err := protobundle.GenerateX509CertificateChain(certBytes)
Copy link
Contributor

Choose a reason for hiding this comment

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

A recent decision was to have this only be the non-trusted certificates, and any CA certs that appear in the root of trust should not be included - https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_common.proto#L171-L188

We can either:

if err != nil {
return nil, err
}
vm.Content = &pbbundle.VerificationMaterial_X509CertificateChain{
X509CertificateChain: certChain,
}

contents, err := json.Marshal(signedPayload)
if err != nil {
return nil, err
}
if err := os.WriteFile(ko.BundlePath, contents, 0600); err != nil {
return nil, fmt.Errorf("create bundle file: %w", err)
// getting inclusion proof if available
if rekorEntry != nil {
tlEntry, err := protobundle.GenerateTransparencyLogEntry(*rekorEntry)
if err != nil {
return nil, fmt.Errorf("error generating tle for bundle: %w", err)
}
vm.TlogEntries = []*pbrekor.TransparencyLogEntry{tlEntry}
}

if rfc3161Timestamp != nil {
vm.TimestampVerificationData = &pbbundle.TimestampVerificationData{
Rfc3161Timestamps: []*pbcommon.RFC3161SignedTimestamp{
{SignedTimestamp: rfc3161Timestamp.SignedRFC3161Timestamp},
},
}
}

bundle := pbbundle.Bundle{
MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1",
VerificationMaterial: &vm,
Content: &pbbundle.Bundle_MessageSignature{
MessageSignature: &pbcommon.MessageSignature{
MessageDigest: &pbcommon.HashOutput{
Algorithm: pbcommon.HashAlgorithm_SHA2_256,
Digest: payload.Sum(nil),
},
Signature: sig,
},
},
}

contents, err := protojson.Marshal(&bundle)
if err != nil {
return nil, err
}
if err := os.WriteFile(ko.BundlePath, contents, 0600); err != nil {
return nil, fmt.Errorf("create bundle file: %w", err)
}
ui.Infof(ctx, "Wrote bundle to file %s", ko.BundlePath)
} else {
signedPayload.Base64Signature = base64.StdEncoding.EncodeToString(sig)

certBytes, err := extractCertificate(ctx, sv)
if err != nil {
return nil, err
}
signedPayload.Cert = base64.StdEncoding.EncodeToString(certBytes)

contents, err := json.Marshal(signedPayload)
if err != nil {
return nil, err
}
if err := os.WriteFile(ko.BundlePath, contents, 0600); err != nil {
return nil, fmt.Errorf("create bundle file: %w", err)
}
ui.Infof(ctx, "Wrote bundle to file %s", ko.BundlePath)
}
ui.Infof(ctx, "Wrote bundle to file %s", ko.BundlePath)
}

if outputSignature != "" {
Expand Down
1 change: 1 addition & 0 deletions cmd/cosign/cli/signblob.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func SignBlob() *cobra.Command {
OIDCRedirectURL: o.OIDC.RedirectURL,
OIDCDisableProviders: o.OIDC.DisableAmbientProviders,
BundlePath: o.BundlePath,
UsePBBundleFormat: o.UsePBBundleFormat,
SkipConfirmation: o.SkipConfirmation,
TSAServerURL: o.TSAServerURL,
RFC3161TimestampPath: o.RFC3161TimestampPath,
Expand Down
1 change: 1 addition & 0 deletions doc/cosign_sign-blob.md

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

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/secure-systems-lab/go-securesystemslib v0.7.0
github.com/sigstore/fulcio v1.4.0
github.com/sigstore/protobuf-specs v0.1.0
github.com/sigstore/rekor v1.2.2
github.com/sigstore/sigstore v1.7.1
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.7.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,8 @@ github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sigstore/fulcio v1.4.0 h1:05+k8BFvwTQzfCkVxESWzCN4b70KIRliGYz0Upmdrs8=
github.com/sigstore/fulcio v1.4.0/go.mod h1:wcjlktbhoy6+ZTxO3yXpvqUxsLV+JEH4FF3a5Jz4VPI=
github.com/sigstore/protobuf-specs v0.1.0 h1:X0l/E2C2c79t/rI/lmSu8WAoKWsQtMqDzAMiDdEMGr8=
github.com/sigstore/protobuf-specs v0.1.0/go.mod h1:5shUCxf82hGnjUEFVWiktcxwzdtn6EfeeJssxZ5Q5HE=
github.com/sigstore/rekor v1.2.2 h1:5JK/zKZvcQpL/jBmHvmFj3YbpDMBQnJQ6ygp8xdF3bY=
github.com/sigstore/rekor v1.2.2/go.mod h1:FGnWBGWzeNceJnp0x9eDFd41mI8aQqCjj+Zp0IEs0Qg=
github.com/sigstore/sigstore v1.7.1 h1:fCATemikcBK0cG4+NcM940MfoIgmioY1vC6E66hXxks=
Expand Down
46 changes: 46 additions & 0 deletions pkg/protobundle/bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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 protobundle

import (
"fmt"

pbcommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
pbrekor "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1"
"github.com/sigstore/rekor/pkg/generated/models"

"github.com/sigstore/rekor/pkg/tle"
"github.com/sigstore/sigstore/pkg/cryptoutils"
)

// GenerateTransparencyLogEntry returns a sigstore/protobuf-specs compliant message containing a
// TransparencyLogEntry as defined at https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_rekor.proto
func GenerateTransparencyLogEntry(anon models.LogEntryAnon) (*pbrekor.TransparencyLogEntry, error) {
return tle.GenerateTransparencyLogEntry(anon)
cdris marked this conversation as resolved.
Show resolved Hide resolved
}

// GenerateX509CertificateChain returns a sigstore/protobuf-specs compliant message containing a
// X509CertificateChain as defined at https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_common.proto
func GenerateX509CertificateChain(pemBytes []byte) (*pbcommon.X509CertificateChain, error) {
certs, err := cryptoutils.UnmarshalCertificatesFromPEM(pemBytes)
if err != nil {
return nil, fmt.Errorf("error getting cert: %w", err)
}
chain := []*pbcommon.X509Certificate{}
for _, cert := range certs {
chain = append(chain, &pbcommon.X509Certificate{RawBytes: cert.Raw})
}
return &pbcommon.X509CertificateChain{Certificates: chain}, nil
}
Loading