Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
Signed-off-by: Jake Sanders <jsand@google.com>
  • Loading branch information
Jake Sanders committed Dec 2, 2021
1 parent 6458dd7 commit b441219
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 288 deletions.
255 changes: 74 additions & 181 deletions cmd/cosign/cli/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,77 @@ package attest
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/rsa"
_ "crypto/sha256" // for `crypto.SHA256`
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"net/url"
"os"
"time"

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

"github.com/sigstore/cosign/cmd/cosign/cli/fulcio"
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio/fulcioverifier"
"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
"github.com/sigstore/cosign/internal/pkg/cosign/payload"
irekor "github.com/sigstore/cosign/internal/pkg/cosign/rekor"
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/attestation"
"github.com/sigstore/cosign/pkg/cosign/pivkey"
"github.com/sigstore/cosign/pkg/cosign/pkcs11key"
cremote "github.com/sigstore/cosign/pkg/cosign/remote"
"github.com/sigstore/cosign/pkg/oci"
"github.com/sigstore/cosign/pkg/oci/mutate"
ociremote "github.com/sigstore/cosign/pkg/oci/remote"
"github.com/sigstore/cosign/pkg/providers"
rekPkgClient "github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/signature"

icos "github.com/sigstore/cosign/internal/pkg/cosign"
ifulcio "github.com/sigstore/cosign/internal/pkg/cosign/fulcio"
"github.com/sigstore/cosign/pkg/oci/static"
sigs "github.com/sigstore/cosign/pkg/signature"
fulcPkgClient "github.com/sigstore/fulcio/pkg/client"
"github.com/sigstore/cosign/pkg/types"
rekPkgClient "github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/sigstore/pkg/signature/dsse"
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options"
)

// TODO(dekkagaijin): remove this in favor of a function in pkg which handles both signatures and attestations
func bundle(entry *models.LogEntryAnon) *oci.Bundle {
if entry.Verification == nil {
return nil
}
return &oci.Bundle{
SignedEntryTimestamp: entry.Verification.SignedEntryTimestamp,
Payload: oci.BundlePayload{
Body: entry.Body,
IntegratedTime: *entry.IntegratedTime,
LogIndex: *entry.LogIndex,
LogID: *entry.LogID,
},
}
}

type tlogUploadFn func(*client.Rekor, []byte) (*models.LogEntryAnon, error)

func uploadToTlog(ctx context.Context, sv *sign.SignerVerifier, rekorURL string, upload tlogUploadFn) (*oci.Bundle, error) {
var rekorBytes []byte
// Upload the cert or the public key, depending on what we have
if sv.Cert != nil {
rekorBytes = sv.Cert
} else {
pemBytes, err := sigs.PublicKeyPem(sv, signatureoptions.WithContext(ctx))
if err != nil {
return nil, err
}
rekorBytes = pemBytes
}

rekorClient, err := rekPkgClient.GetRekorClient(rekorURL)
if err != nil {
return nil, err
}
entry, err := upload(rekorClient, rekorBytes)
if err != nil {
return nil, err
}
fmt.Fprintln(os.Stderr, "tlog entry created with index:", *entry.LogIndex)
return bundle(entry), nil
}

//nolint
func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOptions, imageRef string, certPath string,
noUpload bool, predicatePath string, force bool, predicateType string, replace bool, timeout time.Duration) error {
Expand Down Expand Up @@ -101,13 +133,12 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
// each access.
ref = digest // nolint

attestor, sv, closeFn, err := AttestorFromKeyOpts(ctx, certPath, predicateURI, ko)
sv, err := sign.SignerFromKeyOpts(ctx, certPath, ko)
if err != nil {
return errors.Wrap(err, "getting signer")
}
if closeFn != nil {
defer closeFn()
}
defer sv.Close()
wrapped := dsse.WrapSigner(sv, predicateURI)
dd := cremote.NewDupeDetector(sv)

fmt.Fprintln(os.Stderr, "Using payload from:", predicatePath)
Expand All @@ -131,30 +162,37 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
if err != nil {
return err
}
signedPayload, err := wrapped.SignMessage(bytes.NewReader(payload), signatureoptions.WithContext(ctx))
if err != nil {
return errors.Wrap(err, "signing")
}

if noUpload {
fmt.Println(string(signedPayload))
return nil
}

opts := []static.Option{static.WithLayerMediaType(types.DssePayloadType)}
if sv.Cert != nil {
opts = append(opts, static.WithCertChain(sv.Cert, sv.Chain))
}

// Check whether we should be uploading to the transparency log
if sign.ShouldUploadToTlog(ctx, digest, force, ko.RekorURL) {
rClient, err := rekPkgClient.GetRekorClient(ko.RekorURL)
bundle, err := uploadToTlog(ctx, sv, ko.RekorURL, func(r *client.Rekor, b []byte) (*models.LogEntryAnon, error) {
return cosign.TLogUploadInTotoAttestation(ctx, r, signedPayload, b)
})
if err != nil {
return err
}
attestor = irekor.WrapDSSEAttestor(attestor, rClient)
opts = append(opts, static.WithBundle(bundle))
}

ociAtt, _, err := attestor.Attest(ctx, bytes.NewReader(payload))
sig, err := static.NewAttestation(signedPayload, opts...)
if err != nil {
return err
}

if noUpload {
signedPayload, err := ociAtt.Payload()
if err != nil {
return err
}
fmt.Println(string(signedPayload))
return nil
}

se, err := ociremote.SignedEntity(digest, ociremoteOpts...)
if err != nil {
return err
Expand All @@ -170,156 +208,11 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
}

// Attach the attestation to the entity.
newSE, err := mutate.AttachAttestationToEntity(se, ociAtt, signOpts...)
newSE, err := mutate.AttachAttestationToEntity(se, sig, signOpts...)
if err != nil {
return err
}

// Publish the attestations associated with this entity
return ociremote.WriteAttestations(digest.Repository, newSE, ociremoteOpts...)
}

func attestorFromSecurityKey(keySlot, predicateURI string) (attestor icos.Attestor, sv signature.SignerVerifier, closeFn func(), err error) {
sk, err := pivkey.GetKeyWithSlot(keySlot)
if err != nil {
return nil, nil, nil, err
}
sv, err = sk.SignerVerifier()
if err != nil {
sk.Close()
return nil, nil, nil, err
}

// Handle the -cert flag.
// With PIV, we assume the certificate is in the same slot on the PIV
// token as the private key. If it's not there, show a warning to the
// user.
certFromPIV, err := sk.Certificate()
var certPem []byte
if err != nil {
fmt.Fprintln(os.Stderr, "warning: no x509 certificate retrieved from the PIV token")
} else {
certPem, err = cryptoutils.MarshalCertificateToPEM(certFromPIV)
if err != nil {
sk.Close()
return nil, nil, nil, err
}
}

return payload.NewDSSEAttestor(sv, nil, nil, certPem, nil, predicateURI), sv, sk.Close, nil
}

func attestorFromKeyRef(ctx context.Context, certPath, keyRef string, passFunc cosign.PassFunc, predicateURI string) (attestor icos.Attestor, sv signature.SignerVerifier, closeFn func(), err error) {
k, err := sigs.SignerVerifierFromKeyRef(ctx, keyRef, passFunc)
if err != nil {
return nil, nil, nil, errors.Wrap(err, "reading key")
}

var certBytes []byte

// Handle the -cert flag
// With PKCS11, we assume the certificate is in the same slot on the PKCS11
// token as the private key. If it's not there, show a warning to the
// user.
if pkcs11Key, ok := k.(*pkcs11key.Key); ok {
certFromPKCS11, _ := pkcs11Key.Certificate()
if certFromPKCS11 == nil {
fmt.Fprintln(os.Stderr, "warning: no x509 certificate retrieved from the PKCS11 token")
} else {
certBytes, err = cryptoutils.MarshalCertificateToPEM(certFromPKCS11)
if err != nil {
pkcs11Key.Close()
return nil, nil, nil, err
}
}

return payload.NewDSSEAttestor(k, nil, nil, certBytes, nil, predicateURI), k, pkcs11Key.Close, nil
}

if certPath == "" {
return payload.NewDSSEAttestor(k, nil, nil, nil, nil, predicateURI), k, nil, nil
}

certBytes, err = os.ReadFile(certPath)
if err != nil {
return nil, nil, nil, errors.Wrap(err, "read certificate")
}
// Handle PEM.
if bytes.HasPrefix(certBytes, []byte("-----")) {
decoded, _ := pem.Decode(certBytes)
if decoded.Type != "CERTIFICATE" {
return nil, nil, nil, fmt.Errorf("supplied PEM file is not a certificate: %s", certPath)
}
certBytes = decoded.Bytes
}
parsedCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, nil, nil, errors.Wrap(err, "parse x509 certificate")
}
pk, err := k.PublicKey()
if err != nil {
return nil, nil, nil, errors.Wrap(err, "get public key")
}
switch kt := parsedCert.PublicKey.(type) {
case *ecdsa.PublicKey:
if !kt.Equal(pk) {
return nil, nil, nil, errors.New("public key in certificate does not match that in the signing key")
}
case *rsa.PublicKey:
if !kt.Equal(pk) {
return nil, nil, nil, errors.New("public key in certificate does not match that in the signing key")
}
default:
return nil, nil, nil, fmt.Errorf("unsupported key type: %T", parsedCert.PublicKey)
}
pemBytes, err := cryptoutils.MarshalCertificateToPEM(parsedCert)
if err != nil {
return nil, nil, nil, errors.Wrap(err, "marshaling certificate to PEM")
}

return payload.NewDSSEAttestor(k, nil, nil, pemBytes, nil, predicateURI), k, nil, nil
}

func keylessAttestor(ctx context.Context, predicateURI string, ko sign.KeyOpts) (attestor icos.Attestor, sv signature.SignerVerifier, err error) {
fulcioServer, err := url.Parse(ko.FulcioURL)
if err != nil {
return nil, nil, errors.Wrap(err, "parsing Fulcio URL")
}
fClient := fulcPkgClient.New(fulcioServer)
tok := ko.IDToken
if providers.Enabled(ctx) {
tok, err = providers.Provide(ctx, "sigstore")
if err != nil {
return nil, nil, errors.Wrap(err, "fetching ambient OIDC credentials")
}
}

var k *fulcio.Signer

if ko.InsecureSkipFulcioVerify {
if k, err = fulcio.NewSigner(ctx, tok, ko.OIDCIssuer, ko.OIDCClientID, fClient); err != nil {
return nil, nil, errors.Wrap(err, "getting key from Fulcio")
}
} else {
if k, err = fulcioverifier.NewSigner(ctx, tok, ko.OIDCIssuer, ko.OIDCClientID, fClient); err != nil {
return nil, nil, errors.Wrap(err, "getting key from Fulcio")
}
}

return ifulcio.WrapAttestor(payload.NewDSSEAttestor(k, nil, nil, nil, nil, predicateURI), k.Cert, k.Chain), k, nil
}

func AttestorFromKeyOpts(ctx context.Context, certPath, predicateURI string, ko sign.KeyOpts) (attestor icos.Attestor, sv signature.SignerVerifier, closeFn func(), err error) {
if ko.Sk {
return attestorFromSecurityKey(ko.Slot, predicateURI)
}

if ko.KeyRef != "" {
return attestorFromKeyRef(ctx, certPath, ko.KeyRef, ko.PassFunc, predicateURI)
}

// Default Keyless!
fmt.Fprintln(os.Stderr, "Generating ephemeral keys...")
attestor, sv, err = keylessAttestor(ctx, predicateURI, ko)
return attestor, sv, nil, err
}
4 changes: 1 addition & 3 deletions cmd/cosign/cli/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,9 @@ func signDigest(ctx context.Context, digest name.Digest, payload []byte, ko KeyO
}
}

// TODO(dekkagaijin): hoist the creation of these into SignerFromKeyOpts
var s icos.Signer
s = ipayload.NewSigner(sv, nil, nil, nil, nil)
s = ipayload.NewSigner(sv, nil, nil)
s = ifulcio.NewSigner(s, sv.Cert, sv.Chain)

if ShouldUploadToTlog(ctx, digest, force, ko.RekorURL) {
rClient, err := rekorClient.GetRekorClient(ko.RekorURL)
if err != nil {
Expand Down
Loading

0 comments on commit b441219

Please sign in to comment.