diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go index 9221bda27f5..7c3c16174fb 100644 --- a/cmd/cosign/cli/sign.go +++ b/cmd/cosign/cli/sign.go @@ -88,7 +88,7 @@ func Sign() *ffcli.Command { payloadPath = flagset.String("payload", "", "path to a payload file to use rather than generating one.") force = flagset.Bool("f", false, "skip warnings and confirmations") recursive = flagset.Bool("r", false, "if a multi-arch image is specified, additionally sign each discrete image") - fulcioURL = flagset.String("fulcio-server", "https://fulcio.sigstore.dev", "[EXPERIMENTAL] address of sigstore PKI server") + fulcioURL = flagset.String("fulcio-url", "https://fulcio.sigstore.dev", "[EXPERIMENTAL] address of sigstore PKI server") rekorURL = flagset.String("rekor-url", "https://rekor.sigstore.dev", "[EXPERIMENTAL] address of rekor STL server") idToken = flagset.String("identity-token", "", "[EXPERIMENTAL] identity token to use for certificate from fulcio") oidcIssuer = flagset.String("oidc-issuer", "https://oauth2.sigstore.dev/auth", "[EXPERIMENTAL] OIDC provider to be used to issue ID token") @@ -258,9 +258,8 @@ func SignCmd(ctx context.Context, ko KeyOpts, annotations map[string]interface{} var rekorBytes []byte if uploadTLog { // Upload the cert or the public key, depending on what we have - if sv.Cert != "" { - rekorBytes = []byte(sv.Cert) - } else { + rekorBytes = sv.Cert + if rekorBytes == nil { pemBytes, err := cosign.PublicKeyPem(sv, options.WithContext(ctx)) if err != nil { return err @@ -398,9 +397,8 @@ func signerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*certS if err != nil { return nil, err } - cert := string(pemBytes) return &certSignVerifier{ - Cert: cert, + Cert: pemBytes, SignerVerifier: sv, }, nil @@ -454,12 +452,16 @@ func signerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*certS if err != nil { return nil, errors.Wrap(err, "marshaling certificate to PEM") } - certSigner.Cert = string(pemBytes) + certSigner.Cert = pemBytes return certSigner, nil } // Default Keyless! fmt.Fprintln(os.Stderr, "Generating ephemeral keys...") - k, err := fulcio.NewSigner(ctx, ko.IDToken, ko.OIDCIssuer, ko.OIDCClientID, ko.FulcioURL) + fClient, err := fulcio.NewClient(ko.FulcioURL) + if err != nil { + return nil, errors.Wrap(err, "creating Fulcio client") + } + k, err := fulcio.NewSigner(ctx, ko.IDToken, ko.OIDCIssuer, ko.OIDCClientID, fClient) if err != nil { return nil, errors.Wrap(err, "getting key from Fulcio") } @@ -471,7 +473,7 @@ func signerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*certS } type certSignVerifier struct { - Cert string - Chain string + Cert []byte + Chain []byte signature.SignerVerifier } diff --git a/cmd/cosign/cli/sign_blob.go b/cmd/cosign/cli/sign_blob.go index 0cde0240e65..65b6d7817c2 100644 --- a/cmd/cosign/cli/sign_blob.go +++ b/cmd/cosign/cli/sign_blob.go @@ -41,7 +41,7 @@ func SignBlob() *ffcli.Command { b64 = flagset.Bool("b64", true, "whether to base64 encode the output") sk = flagset.Bool("sk", false, "whether to use a hardware security key") slot = flagset.String("slot", "", "security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)") - fulcioURL = flagset.String("fulcio-server", "https://fulcio.sigstore.dev", "[EXPERIMENTAL] address of sigstore PKI server") + fulcioURL = flagset.String("fulcio-url", "https://fulcio.sigstore.dev", "[EXPERIMENTAL] address of sigstore PKI server") rekorURL = flagset.String("rekor-url", "https://rekor.sigstore.dev", "[EXPERIMENTAL] address of rekor STL server") idToken = flagset.String("identity-token", "", "[EXPERIMENTAL] identity token to use for certificate from fulcio") oidcIssuer = flagset.String("oidc-issuer", "https://oauth2.sigstore.dev/auth", "[EXPERIMENTAL] OIDC provider to be used to issue ID token") @@ -146,10 +146,8 @@ func SignBlobCmd(ctx context.Context, ko KeyOpts, payloadPath string, b64 bool, if EnableExperimental() { // TODO: Refactor with sign.go - var rekorBytes []byte - if sv.Cert != "" { - rekorBytes = []byte(sv.Cert) - } else { + rekorBytes := sv.Cert + if rekorBytes == nil { pemBytes, err := cosign.PublicKeyPem(sv, options.WithContext(ctx)) if err != nil { return nil, err diff --git a/pkg/cosign/fulcio/fulcio.go b/pkg/cosign/fulcio/fulcio.go index 25af9572d7e..7fbfbad23a4 100644 --- a/pkg/cosign/fulcio/fulcio.go +++ b/pkg/cosign/fulcio/fulcio.go @@ -68,22 +68,22 @@ type signingCertProvider interface { SigningCert(params *operations.SigningCertParams, authInfo runtime.ClientAuthInfoWriter, opts ...operations.ClientOption) (*operations.SigningCertCreated, error) } -func getCertForOauthID(priv *ecdsa.PrivateKey, scp signingCertProvider, connector oidcConnector, oidcIssuer string, oidcClientID string) (string, string, error) { +func getCertForOauthID(priv *ecdsa.PrivateKey, scp signingCertProvider, connector oidcConnector, oidcIssuer string, oidcClientID string) (certPem, chainPem []byte, err error) { pubBytes, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) if err != nil { - return "", "", err + return nil, nil, err } tok, err := connector.OIDConnect(oidcIssuer, oidcClientID, "") if err != nil { - return "", "", err + return nil, nil, err } // Sign the email address as part of the request h := sha256.Sum256([]byte(tok.Subject)) proof, err := ecdsa.SignASN1(rand.Reader, priv, h[:]) if err != nil { - return "", "", err + return nil, nil, err } bearerAuth := httptransport.BearerToken(tok.RawString) @@ -103,16 +103,16 @@ func getCertForOauthID(priv *ecdsa.PrivateKey, scp signingCertProvider, connecto resp, err := scp.SigningCert(params, bearerAuth) if err != nil { - return "", "", err + return nil, nil, err } // split the cert and the chain certBlock, chainPem := pem.Decode([]byte(resp.Payload)) - certPem := pem.EncodeToMemory(certBlock) - return string(certPem), string(chainPem), nil + certPem = pem.EncodeToMemory(certBlock) + return certPem, chainPem, nil } -func getFulcioClient(addr string) (*fulcioClient.Fulcio, error) { +func NewClient(addr string) (*fulcioClient.Fulcio, error) { url, err := url.Parse(addr) if err != nil { return nil, err @@ -124,12 +124,7 @@ func getFulcioClient(addr string) (*fulcioClient.Fulcio, error) { } // 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 string, oidcIssuer string, oidcClientID string, fulcioClient string) (string, string, error) { - fcli, err := getFulcioClient(fulcioClient) - if err != nil { - return "", "", err - } - +func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow, oidcIssuer, oidcClientID string, fClient *fulcioClient.Fulcio) (certPemBytes, chainPemBytes []byte, err error) { c := &realConnector{} switch flow { case FlowDevice: @@ -140,20 +135,20 @@ func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow string, case FlowToken: c.flow = &oauthflow.StaticTokenGetter{RawToken: idToken} default: - return "", "", fmt.Errorf("unsupported oauth flow: %s", flow) + return nil, nil, fmt.Errorf("unsupported oauth flow: %s", flow) } - return getCertForOauthID(priv, fcli.Operations, c, oidcIssuer, oidcClientID) + return getCertForOauthID(priv, fClient.Operations, c, oidcIssuer, oidcClientID) } type Signer struct { - Cert string - Chain string + Cert []byte + Chain []byte pub *ecdsa.PublicKey *signature.ECDSASignerVerifier } -func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID, fulcioClient string) (*Signer, error) { +func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID string, fClient *fulcioClient.Fulcio) (*Signer, error) { priv, err := cosign.GeneratePrivateKey() if err != nil { return nil, errors.Wrap(err, "generating cert") @@ -174,7 +169,7 @@ func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID, fulcioCli default: flow = FlowNormal } - cert, chain, err := GetCert(ctx, priv, idToken, flow, oidcIssuer, oidcClientID, fulcioClient) // TODO, use the chain. + cert, chain, err := GetCert(ctx, priv, idToken, flow, oidcIssuer, oidcClientID, fClient) // TODO, use the chain. if err != nil { return nil, errors.Wrap(err, "retrieving cert") } diff --git a/pkg/cosign/fulcio/fulcio_test.go b/pkg/cosign/fulcio/fulcio_test.go index 30794297688..f820747ee41 100644 --- a/pkg/cosign/fulcio/fulcio_test.go +++ b/pkg/cosign/fulcio/fulcio_test.go @@ -126,12 +126,14 @@ func TestGetCertForOauthID(t *testing.T) { } expectedCert := string(expectedCertBytes) - if cert != expectedCert { - t.Errorf("getCertForOauthID returned cert %q, wanted %q", cert, expectedCert) + actualCert := string(cert) + if actualCert != expectedCert { + t.Errorf("getCertForOauthID returned cert %q, wanted %q", actualCert, expectedCert) } expectedChain := string(expectedExtraBytes) - if chain != expectedChain { - t.Errorf("getCertForOauthID returned chain %q, wanted %q", chain, expectedChain) + actualChain := string(chain) + if actualChain != expectedChain { + t.Errorf("getCertForOauthID returned chain %q, wanted %q", actualChain, expectedChain) } }) } diff --git a/pkg/cosign/remote/remote.go b/pkg/cosign/remote/remote.go index 6d8854f88d6..177f2abb076 100644 --- a/pkg/cosign/remote/remote.go +++ b/pkg/cosign/remote/remote.go @@ -141,8 +141,8 @@ type Bundle struct { } type UploadOpts struct { - Cert string - Chain string + Cert []byte + Chain []byte DupeDetector signature.Verifier Bundle *Bundle AdditionalAnnotations map[string]string @@ -169,9 +169,9 @@ func UploadSignature(signature, payload []byte, dst name.Reference, opts UploadO annotations := map[string]string{ sigkey: base64.StdEncoding.EncodeToString(signature), } - if opts.Cert != "" { - annotations[certkey] = opts.Cert - annotations[chainkey] = opts.Chain + if opts.Cert != nil { + annotations[certkey] = string(opts.Cert) + annotations[chainkey] = string(opts.Chain) } if opts.Bundle != nil { b, err := swag.WriteJSON(opts.Bundle)