blob: f2b739727655d128c833985437d66cd0feefd16d [file] [log] [blame]
package main
import (
"bytes"
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"
"syscall"
"time"
docopt "github.com/flynn/go-docopt"
tuf "github.com/theupdateframework/go-tuf"
"github.com/theupdateframework/go-tuf/util"
"golang.org/x/term"
)
func main() {
log.SetFlags(0)
usage := `usage: tuf [-h|--help] [-d|--dir=<dir>] [--insecure-plaintext] <command> [<args>...]
Options:
-h, --help
-d <dir> The path to the repository (defaults to the current working directory)
--insecure-plaintext Don't encrypt signing keys
Commands:
help Show usage for a specific command
init Initialize a new repository
gen-key Generate a new signing key for a specific metadata file
revoke-key Revoke a signing key
add Add target file(s)
remove Remove a target file
snapshot Update the snapshot metadata file
timestamp Update the timestamp metadata file
payload Output a role's metadata file for signing
add-signatures Adds signatures generated offline
sign Sign a role's metadata file
sign-payload Sign a file from the "payload" command.
status Check if a role's metadata has expired
commit Commit staged files to the repository
regenerate Recreate the targets metadata file [Not supported yet]
set-threshold Sets the threshold for a role
get-threshold Outputs the threshold for a role
change-passphrase Changes the passphrase for given role keys file
root-keys Output a JSON serialized array of root keys to STDOUT
clean Remove all staged metadata files
See "tuf help <command>" for more information on a specific command
`
args, _ := docopt.Parse(usage, nil, true, "", true)
cmd := args.String["<command>"]
cmdArgs := args.All["<args>"].([]string)
if cmd == "help" {
if len(cmdArgs) == 0 { // `tuf help`
fmt.Println(usage)
return
} else { // `tuf help <command>`
cmd = cmdArgs[0]
cmdArgs = []string{"--help"}
}
}
dir, ok := args.String["-d"]
if !ok {
dir = args.String["--dir"]
}
if dir == "" {
var err error
dir, err = os.Getwd()
if err != nil {
log.Fatal(err)
}
}
if err := runCommand(cmd, cmdArgs, dir, args.Bool["--insecure-plaintext"]); err != nil {
log.Fatalln("ERROR:", err)
}
}
type cmdFunc func(*docopt.Args, *tuf.Repo) error
type command struct {
usage string
f cmdFunc
}
var commands = make(map[string]*command)
func register(name string, f cmdFunc, usage string) {
commands[name] = &command{usage: usage, f: f}
}
func runCommand(name string, args []string, dir string, insecure bool) error {
argv := make([]string, 1, 1+len(args))
argv[0] = name
argv = append(argv, args...)
cmd, ok := commands[name]
if !ok {
return fmt.Errorf("%s is not a tuf command. See 'tuf help'", name)
}
parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", true)
if err != nil {
return err
}
var p util.PassphraseFunc
if !insecure {
p = getPassphrase
}
repo, err := tuf.NewRepo(tuf.FileSystemStore(dir, p))
if err != nil {
return err
}
return cmd.f(parsedArgs, repo)
}
func parseExpires(arg string) (time.Time, error) {
days, err := strconv.Atoi(arg)
if err != nil {
return time.Time{}, fmt.Errorf("failed to parse --expires arg: %s", err)
}
return time.Now().AddDate(0, 0, days).UTC(), nil
}
func getPassphrase(role string, confirm bool, change bool) ([]byte, error) {
// In case of change we need to prompt explicitly for a new passphrase
// and not read it from the environment variable, if present
if pass := os.Getenv(fmt.Sprintf("TUF_%s_PASSPHRASE", strings.ToUpper(role))); pass != "" && !change {
return []byte(pass), nil
}
// Alter role string if we are prompting for a passphrase change
if change {
// Check if environment variable for new passphrase exist
if new_pass := os.Getenv(fmt.Sprintf("TUF_NEW_%s_PASSPHRASE", strings.ToUpper(role))); new_pass != "" {
// If so, just read the new passphrase from it and return
return []byte(new_pass), nil
}
// No environment variable set, so proceed prompting for new passphrase
role = fmt.Sprintf("new %s", role)
}
fmt.Fprintf(os.Stderr, "Enter %s keys passphrase: ", role)
passphrase, err := term.ReadPassword(int(syscall.Stdin))
fmt.Fprintln(os.Stderr)
if err != nil {
return nil, err
}
if !confirm {
return passphrase, nil
}
fmt.Fprintf(os.Stderr, "Repeat %s keys passphrase: ", role)
confirmation, err := term.ReadPassword(int(syscall.Stdin))
fmt.Fprintln(os.Stderr)
if err != nil {
return nil, err
}
if !bytes.Equal(passphrase, confirmation) {
return nil, errors.New("the entered passphrases do not match")
}
return passphrase, nil
}