1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
package sigstore
import (
"errors"
"fmt"
"io"
"net/url"
"github.com/containers/image/v5/pkg/cli"
"github.com/containers/image/v5/pkg/cli/sigstore/params"
"github.com/containers/image/v5/signature/signer"
"github.com/containers/image/v5/signature/sigstore"
"github.com/containers/image/v5/signature/sigstore/fulcio"
"github.com/containers/image/v5/signature/sigstore/rekor"
)
// Options collects data that the caller should provide to NewSignerFromParameterFile.
// The caller should set all fields unless documented otherwise.
type Options struct {
PrivateKeyPassphrasePrompt func(keyFile string) (string, error) // A function to call to interactively prompt for a passphrase
Stdin io.Reader
Stdout io.Writer
}
// NewSignerFromParameterFile returns a signature.Signer which creates sigstore signatures based a parameter file at the specified path.
//
// The caller must call Close() on the returned Signer.
func NewSignerFromParameterFile(path string, options *Options) (*signer.Signer, error) {
params, err := params.ParseFile(path)
if err != nil {
return nil, fmt.Errorf("setting up signing using parameter file %q: %w", path, err)
}
return newSignerFromParameterData(params, options)
}
// newSignerFromParameterData returns a signature.Signer which creates sigstore signatures based on parameter file contents.
//
// The caller must call Close() on the returned Signer.
func newSignerFromParameterData(params *params.SigningParameterFile, options *Options) (*signer.Signer, error) {
opts := []sigstore.Option{}
if params.PrivateKeyFile != "" {
var getPassphrase func(keyFile string) (string, error)
switch {
case params.PrivateKeyPassphraseFile != "":
getPassphrase = func(_ string) (string, error) {
return cli.ReadPassphraseFile(params.PrivateKeyPassphraseFile)
}
case options.PrivateKeyPassphrasePrompt != nil:
getPassphrase = options.PrivateKeyPassphrasePrompt
default: // This shouldn’t happen, the caller is expected to set options.PrivateKeyPassphrasePrompt
return nil, fmt.Errorf("private key %s specified, but no way to get a passphrase", params.PrivateKeyFile)
}
passphrase, err := getPassphrase(params.PrivateKeyFile)
if err != nil {
return nil, err
}
opts = append(opts, sigstore.WithPrivateKeyFile(params.PrivateKeyFile, []byte(passphrase)))
}
if params.Fulcio != nil {
fulcioOpt, err := fulcioOption(params.Fulcio, options)
if err != nil {
return nil, err
}
opts = append(opts, fulcioOpt)
}
if params.RekorURL != "" {
rekorURL, err := url.Parse(params.RekorURL)
if err != nil {
return nil, fmt.Errorf("parsing rekorURL %q: %w", params.RekorURL, err)
}
opts = append(opts, rekor.WithRekor(rekorURL))
}
return sigstore.NewSigner(opts...)
}
// fulcioOption returns a sigstore.Option for Fulcio use based on f.
func fulcioOption(f *params.SigningParameterFileFulcio, options *Options) (sigstore.Option, error) {
if f.FulcioURL == "" {
return nil, errors.New("missing fulcioURL")
}
fulcioURL, err := url.Parse(f.FulcioURL)
if err != nil {
return nil, fmt.Errorf("parsing fulcioURL %q: %w", f.FulcioURL, err)
}
if f.OIDCMode == params.OIDCModeStaticToken {
if f.OIDCIDToken == "" {
return nil, errors.New("missing oidcToken")
}
return fulcio.WithFulcioAndPreexistingOIDCIDToken(fulcioURL, f.OIDCIDToken), nil
}
if f.OIDCIssuerURL == "" {
return nil, errors.New("missing oidcIssuerURL")
}
oidcIssuerURL, err := url.Parse(f.OIDCIssuerURL)
if err != nil {
return nil, fmt.Errorf("parsing oidcIssuerURL %q: %w", f.OIDCIssuerURL, err)
}
switch f.OIDCMode {
case params.OIDCModeDeviceGrant:
return fulcio.WithFulcioAndDeviceAuthorizationGrantOIDC(fulcioURL, oidcIssuerURL, f.OIDCClientID, f.OIDCClientSecret,
options.Stdout), nil
case params.OIDCModeInteractive:
return fulcio.WithFulcioAndInteractiveOIDC(fulcioURL, oidcIssuerURL, f.OIDCClientID, f.OIDCClientSecret,
options.Stdin, options.Stdout), nil
case "":
return nil, errors.New("missing oidcMode")
case params.OIDCModeStaticToken:
return nil, errors.New("internal inconsistency: SigningParameterFileOIDCModeStaticToken was supposed to already be handled")
default:
return nil, fmt.Errorf("unknown oidcMode value %q", f.OIDCMode)
}
}
|