blob: 3703c029143b5b1605280d9d58c7f2de14e7a6bb [file] [log] [blame]
package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"
"time"
docopt "github.com/flynn/go-docopt"
tuf "github.com/flynn/go-tuf"
"github.com/flynn/go-tuf/util"
)
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
gen-key Generate a new signing key for a specific manifest
revoke-key Revoke a signing key
add Add target file(s)
remove Remove a target file
snapshot Update the snapshot manifest
timestamp Update the timestamp manifest
sign Sign a manifest
commit Commit staged files to the repository
regenerate Recreate the targets manifest
clean Remove all staged manifests
root-keys Output a JSON serialized array of root keys to STDOUT
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) ([]byte, error) {
if pass := os.Getenv(fmt.Sprintf("TUF_%s_PASSPHRASE", strings.ToUpper(role))); pass != "" {
return []byte(pass), nil
}
stdin := bufio.NewReader(os.Stdin)
fmt.Printf("Enter %s keys passphrase: ", role)
passphrase, err := stdin.ReadBytes('\n')
fmt.Println()
if err != nil {
return nil, err
}
passphrase = passphrase[0 : len(passphrase)-1]
if !confirm {
return passphrase, nil
}
fmt.Printf("Repeat %s keys passphrase: ", role)
confirmation, err := stdin.ReadBytes('\n')
fmt.Println()
if err != nil {
return nil, err
}
confirmation = confirmation[0 : len(confirmation)-1]
if !bytes.Equal(passphrase, confirmation) {
return nil, errors.New("The entered passphrases do not match")
}
return passphrase, nil
}