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

Switch to calling gRPC-based fulcio v2 APIs #1762

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 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
6 changes: 3 additions & 3 deletions cmd/cosign/cli/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ type tlogUploadFn func(*client.Rekor, []byte) (*models.LogEntryAnon, error)
func uploadToTlog(ctx context.Context, sv *sign.SignerVerifier, rekorURL string, upload tlogUploadFn) (*cbundle.RekorBundle, error) {
var rekorBytes []byte
// Upload the cert or the public key, depending on what we have
if sv.Cert != nil {
rekorBytes = sv.Cert
if sv.Cert != "" {
rekorBytes = []byte(sv.Cert)
} else {
pemBytes, err := sigs.PublicKeyPem(sv, signatureoptions.WithContext(ctx))
if err != nil {
Expand Down Expand Up @@ -157,7 +157,7 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
}

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

Expand Down
66 changes: 44 additions & 22 deletions cmd/cosign/cli/fulcio/fulcio.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@ import (
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"fmt"
"net/url"
"os"

"github.com/pkg/errors"
"golang.org/x/term"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"

"github.com/sigstore/cosign/cmd/cosign/cli/fulcio/fulcioroots"
clioptions "github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/fulcio/pkg/api"
fulciopb "github.com/sigstore/fulcio/pkg/generated/protobuf"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/oauthflow"
"github.com/sigstore/sigstore/pkg/signature"
)
Expand All @@ -55,8 +59,8 @@ func (rf *realConnector) OIDConnect(url, clientID, secret, redirectURL string) (
return oauthflow.OIDConnect(url, clientID, secret, redirectURL, rf.flow)
}

func getCertForOauthID(priv *ecdsa.PrivateKey, fc api.Client, connector oidcConnector, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL string) (*api.CertificateResponse, error) {
pubBytes, err := x509.MarshalPKIXPublicKey(&priv.PublicKey)
func getCertForOauthID(ctx context.Context, priv *ecdsa.PrivateKey, fc fulciopb.CAClient, connector oidcConnector, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL string) (*fulciopb.SigningCertificate, error) {
pubPEM, err := cryptoutils.MarshalPublicKeyToPEM(&priv.PublicKey)
if err != nil {
return nil, err
}
Expand All @@ -73,19 +77,24 @@ func getCertForOauthID(priv *ecdsa.PrivateKey, fc api.Client, connector oidcConn
return nil, err
}

cr := api.CertificateRequest{
PublicKey: api.Key{
Algorithm: "ecdsa",
Content: pubBytes,
cscr := &fulciopb.CreateSigningCertificateRequest{
PublicKey: &fulciopb.PublicKey{
Algorithm: fulciopb.PublicKeyAlgorithm_ECDSA,
Content: string(pubPEM),
},
SignedEmailAddress: proof,
Credentials: &fulciopb.Credentials{
Credentials: &fulciopb.Credentials_OidcIdentityToken{
OidcIdentityToken: tok.RawString,
},
},
ProofOfPossession: proof,
}

return fc.SigningCert(cr, tok.RawString)
return fc.CreateSigningCertificate(ctx, cscr)
}

// GetCert returns the PEM-encoded signature of the OIDC identity returned as part of an interactive oauth2 flow plus the PEM-encoded cert chain.
func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL string, fClient api.Client) (*api.CertificateResponse, error) {
func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL string, fClient fulciopb.CAClient) (*fulciopb.SigningCertificate, error) {
c := &realConnector{}
switch flow {
case FlowDevice:
Expand All @@ -99,18 +108,18 @@ func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow, oidcIss
return nil, fmt.Errorf("unsupported oauth flow: %s", flow)
}

return getCertForOauthID(priv, fClient, c, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL)
return getCertForOauthID(ctx, priv, fClient, c, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL)
}

type Signer struct {
Cert []byte
Chain []byte
Cert string
Chain []string
SCT []byte
pub *ecdsa.PublicKey
*signature.ECDSASignerVerifier
}

func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL string, fClient api.Client) (*Signer, error) {
func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL string, fClient fulciopb.CAClient) (*Signer, error) {
priv, err := cosign.GeneratePrivateKey()
if err != nil {
return nil, errors.Wrap(err, "generating cert")
Expand All @@ -131,17 +140,23 @@ func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID, oidcClien
default:
flow = FlowNormal
}
Resp, err := GetCert(ctx, priv, idToken, flow, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL, fClient) // TODO, use the chain.
resp, err := GetCert(ctx, priv, idToken, flow, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL, fClient) // TODO, use the chain.
if err != nil {
return nil, errors.Wrap(err, "retrieving cert")
}

f := &Signer{
pub: &priv.PublicKey,
ECDSASignerVerifier: signer,
Cert: Resp.CertPEM,
Chain: Resp.ChainPEM,
SCT: Resp.SCT,
}
switch csc := resp.Certificate.(type) {
case *fulciopb.SigningCertificate_SignedCertificateDetachedSct:
f.SCT = csc.SignedCertificateDetachedSct.SignedCertificateTimestamp
f.Cert = csc.SignedCertificateDetachedSct.Chain.Certificates[0]
f.Chain = csc.SignedCertificateDetachedSct.Chain.Certificates[1:]
case *fulciopb.SigningCertificate_SignedCertificateEmbeddedSct:
f.Cert = csc.SignedCertificateEmbeddedSct.Chain.Certificates[0]
f.Chain = csc.SignedCertificateEmbeddedSct.Chain.Certificates[1:]
}

return f, nil
Expand All @@ -161,11 +176,18 @@ func GetIntermediates() *x509.CertPool {
return fulcioroots.GetIntermediates()
}

func NewClient(fulcioURL string) (api.Client, error) {
func NewClient(fulcioURL string) (fulciopb.CAClient, error) {
fulcioServer, err := url.Parse(fulcioURL)
if err != nil {
return nil, err
}
fClient := api.NewClient(fulcioServer, api.WithUserAgent(clioptions.UserAgent()))
return fClient, nil
dialOpt := grpc.WithTransportCredentials(insecure.NewCredentials())
if fulcioServer.Scheme == "https" {
dialOpt = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{MinVersion: tls.VersionTLS13}))
}
conn, err := grpc.Dial(fulcioServer.Host, dialOpt)
if err != nil {
return nil, err
}
return fulciopb.NewCAClient(conn), nil
}
45 changes: 24 additions & 21 deletions cmd/cosign/cli/fulcio/fulcio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package fulcio

import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
Expand All @@ -25,9 +26,9 @@ import (
"net/http/httptest"
"testing"

"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/fulcio/pkg/api"
fulciopb "github.com/sigstore/fulcio/pkg/generated/protobuf"
"github.com/sigstore/sigstore/pkg/oauthflow"
"google.golang.org/grpc"
)

type testFlow struct {
Expand All @@ -44,18 +45,18 @@ func (tf *testFlow) OIDConnect(url, clientID, secret, redirectURL string) (*oaut
}

type testClient struct {
payload api.CertificateResponse
rootResp api.RootResponse
payload fulciopb.SigningCertificate
rootResp fulciopb.TrustBundle
err error
}

var _ api.Client = (*testClient)(nil)
var _ fulciopb.CAClient = (*testClient)(nil)

func (p *testClient) SigningCert(cr api.CertificateRequest, token string) (*api.CertificateResponse, error) {
func (p *testClient) CreateSigningCertificate(_ context.Context, _ *fulciopb.CreateSigningCertificateRequest, _ ...grpc.CallOption) (*fulciopb.SigningCertificate, error) {
return &p.payload, p.err
}

func (p *testClient) RootCert() (*api.RootResponse, error) {
func (p *testClient) GetTrustBundle(_ context.Context, _ *fulciopb.GetTrustBundleRequest, _ ...grpc.CallOption) (*fulciopb.TrustBundle, error) {
return &p.rootResp, p.err
}

Expand Down Expand Up @@ -103,9 +104,14 @@ func TestGetCertForOauthID(t *testing.T) {
expectedCertBytes := pem.EncodeToMemory(expectedCertPem)
expectedExtraBytes := []byte("0123456789abcdef")
tscp := &testClient{
payload: api.CertificateResponse{
CertPEM: expectedCertBytes,
ChainPEM: expectedExtraBytes,
payload: fulciopb.SigningCertificate{
Certificate: &fulciopb.SigningCertificate_SignedCertificateDetachedSct{
SignedCertificateDetachedSct: &fulciopb.SigningCertificateDetachedSCT{
Chain: &fulciopb.CertificateChain{
Certificates: []string{string(expectedCertBytes), string(expectedExtraBytes)},
},
},
},
},
err: tc.signingCertErr,
}
Expand All @@ -118,25 +124,27 @@ func TestGetCertForOauthID(t *testing.T) {
err: tc.tokenGetterErr,
}

resp, err := getCertForOauthID(testKey, tscp, &tf, "", "", "", "")

resp, err := getCertForOauthID(context.Background(), testKey, tscp, &tf, "", "", "", "")
if err != nil {
if !tc.expectErr {
t.Fatalf("getCertForOauthID returned error: %v", err)
}
return
}

leaf := resp.GetSignedCertificateDetachedSct().Chain.Certificates[0]
extra := resp.GetSignedCertificateDetachedSct().Chain.Certificates[1]
if tc.expectErr {
t.Fatalf("getCertForOauthID got: %q, %q wanted error", resp.CertPEM, resp.ChainPEM)
t.Fatalf("getCertForOauthID got: %q, %q wanted error", leaf, extra)
}

expectedCert := string(expectedCertBytes)
actualCert := string(resp.CertPEM)
actualCert := leaf
if actualCert != expectedCert {
t.Errorf("getCertForOauthID returned cert %q, wanted %q", actualCert, expectedCert)
}
expectedChain := string(expectedExtraBytes)
actualChain := string(resp.ChainPEM)
actualChain := extra
if actualChain != expectedChain {
t.Errorf("getCertForOauthID returned chain %q, wanted %q", actualChain, expectedChain)
}
Expand All @@ -146,17 +154,12 @@ func TestGetCertForOauthID(t *testing.T) {

func TestNewClient(t *testing.T) {
t.Parallel()
expectedUserAgent := options.UserAgent()
requestReceived := false
testServer := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
requestReceived = true
file := []byte{}

got := r.UserAgent()
if got != expectedUserAgent {
t.Errorf("wanted User-Agent %q, got %q", expectedUserAgent, got)
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write(file)
}))
Expand All @@ -167,7 +170,7 @@ func TestNewClient(t *testing.T) {
t.Error(err)
}

_, _ = client.SigningCert(api.CertificateRequest{}, "")
_, _ = client.CreateSigningCertificate(context.Background(), &fulciopb.CreateSigningCertificateRequest{})

if !requestReceived {
t.Fatal("no requests were received")
Expand Down
8 changes: 4 additions & 4 deletions cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio"
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/tuf"
"github.com/sigstore/fulcio/pkg/api"
fulciopb "github.com/sigstore/fulcio/pkg/generated/protobuf"
)

// This is the CT log public key target name
Expand All @@ -51,7 +51,7 @@ const altCTLogPublicKeyLocation = "SIGSTORE_CT_LOG_PUBLIC_KEY_FILE"
// The SCT is a `Signed Certificate Timestamp`, which promises that
// the certificate issued by Fulcio was also added to the public CT log within
// some defined time period
func verifySCT(ctx context.Context, certPEM, rawSCT []byte) error {
func verifySCT(ctx context.Context, certPEM string, rawSCT []byte) error {
pubKeys := make(map[crypto.PublicKey]tuf.StatusKind)
rootEnv := os.Getenv(altCTLogPublicKeyLocation)
if rootEnv == "" {
Expand Down Expand Up @@ -87,7 +87,7 @@ func verifySCT(ctx context.Context, certPEM, rawSCT []byte) error {
if len(pubKeys) == 0 {
return errors.New("none of the CTFE keys have been found")
}
cert, err := x509util.CertificateFromPEM(certPEM)
cert, err := x509util.CertificateFromPEM([]byte(certPEM))
if err != nil {
return err
}
Expand All @@ -114,7 +114,7 @@ func verifySCT(ctx context.Context, certPEM, rawSCT []byte) error {
return verifySctErr
}

func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL string, fClient api.Client) (*fulcio.Signer, error) {
func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL string, fClient fulciopb.CAClient) (*fulcio.Signer, error) {
fs, err := fulcio.NewSigner(ctx, idToken, oidcIssuer, oidcClientID, oidcClientSecret, oidcRedirectURL, fClient)
if err != nil {
return nil, err
Expand Down
6 changes: 3 additions & 3 deletions cmd/cosign/cli/policy_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func signPolicy() *cobra.Command {
}
defer sv.Close()

certs, err := cryptoutils.LoadCertificatesFromPEM(bytes.NewReader(sv.Cert))
certs, err := cryptoutils.LoadCertificatesFromPEM(strings.NewReader(sv.Cert))
if err != nil {
return err
}
Expand Down Expand Up @@ -247,7 +247,7 @@ func signPolicy() *cobra.Command {
}
signature := tuf.Signature{
Signature: base64.StdEncoding.EncodeToString(sig),
Cert: base64.StdEncoding.EncodeToString(sv.Cert),
Cert: base64.StdEncoding.EncodeToString([]byte(sv.Cert)),
}
if err := signed.AddOrUpdateSignature(key, signature); err != nil {
return err
Expand All @@ -261,7 +261,7 @@ func signPolicy() *cobra.Command {
if err != nil {
return err
}
entry, err := cosign.TLogUpload(ctx, rekorClient, sig, signed.Signed, rekorBytes)
entry, err := cosign.TLogUpload(ctx, rekorClient, sig, signed.Signed, []byte(rekorBytes))
if err != nil {
return err
}
Expand Down
Loading