1 package main
2
3 import (
4 "bytes"
5 "errors"
6 "fmt"
7 "log"
8 "os"
9 "strconv"
10 "strings"
11 "syscall"
12 "time"
13
14 docopt "github.com/flynn/go-docopt"
15 tuf "github.com/theupdateframework/go-tuf"
16 "github.com/theupdateframework/go-tuf/util"
17 "golang.org/x/term"
18 )
19
20 func main() {
21 log.SetFlags(0)
22
23 usage := `usage: tuf [-h|--help] [-d|--dir=<dir>] [--insecure-plaintext] <command> [<args>...]
24
25 Options:
26 -h, --help
27 -d <dir> The path to the repository (defaults to the current working directory)
28 --insecure-plaintext Don't encrypt signing keys
29
30 Commands:
31 help Show usage for a specific command
32 init Initialize a new repository
33 add-key Adds a new signing key for a specific role
34 gen-key Generate a new signing key for a specific metadata file
35 revoke-key Revoke a signing key
36 add Add target file(s)
37 remove Remove a target file
38 snapshot Update the snapshot metadata file
39 timestamp Update the timestamp metadata file
40 payload Output a role's metadata file for signing
41 add-signatures Adds signatures generated offline
42 sign Sign a role's metadata file
43 sign-payload Sign a file from the "payload" command.
44 status Check if a role's metadata has expired
45 commit Commit staged files to the repository
46 regenerate Recreate the targets metadata file [Not supported yet]
47 set-threshold Sets the threshold for a role
48 get-threshold Outputs the threshold for a role
49 change-passphrase Changes the passphrase for given role keys file
50 root-keys Output a JSON serialized array of root keys to STDOUT
51 clean Remove all staged metadata files
52
53 See "tuf help <command>" for more information on a specific command
54 `
55
56 args, _ := docopt.Parse(usage, nil, true, "", true)
57 cmd := args.String["<command>"]
58 cmdArgs := args.All["<args>"].([]string)
59
60 if cmd == "help" {
61 if len(cmdArgs) == 0 {
62 fmt.Fprint(os.Stdout, usage)
63 return
64 } else {
65 cmd = cmdArgs[0]
66 cmdArgs = []string{"--help"}
67 }
68 }
69
70 dir, ok := args.String["-d"]
71 if !ok {
72 dir = args.String["--dir"]
73 }
74 if dir == "" {
75 var err error
76 dir, err = os.Getwd()
77 if err != nil {
78 log.Fatal(err)
79 }
80 }
81
82 if err := runCommand(cmd, cmdArgs, dir, args.Bool["--insecure-plaintext"]); err != nil {
83 log.Fatalln("ERROR:", err)
84 }
85 }
86
87 type cmdFunc func(*docopt.Args, *tuf.Repo) error
88
89 type command struct {
90 usage string
91 f cmdFunc
92 }
93
94 var commands = make(map[string]*command)
95
96 func register(name string, f cmdFunc, usage string) {
97 commands[name] = &command{usage: usage, f: f}
98 }
99
100 func runCommand(name string, args []string, dir string, insecure bool) error {
101 argv := make([]string, 1, 1+len(args))
102 argv[0] = name
103 argv = append(argv, args...)
104
105 cmd, ok := commands[name]
106 if !ok {
107 return fmt.Errorf("%s is not a tuf command. See 'tuf help'", name)
108 }
109
110 parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", true)
111 if err != nil {
112 return err
113 }
114
115 var p util.PassphraseFunc
116 if !insecure {
117 p = getPassphrase
118 }
119 logger := log.New(os.Stdout, "", 0)
120 storeOpts := tuf.StoreOpts{Logger: logger, PassFunc: p}
121
122 repo, err := tuf.NewRepoWithOpts(tuf.FileSystemStoreWithOpts(dir, storeOpts),
123 tuf.WithLogger(logger))
124 if err != nil {
125 return err
126 }
127 return cmd.f(parsedArgs, repo)
128 }
129
130 func parseExpires(arg string) (time.Time, error) {
131 days, err := strconv.Atoi(arg)
132 if err != nil {
133 return time.Time{}, fmt.Errorf("failed to parse --expires arg: %s", err)
134 }
135 return time.Now().AddDate(0, 0, days).UTC(), nil
136 }
137
138 func getPassphrase(role string, confirm bool, change bool) ([]byte, error) {
139
140
141 if pass := os.Getenv(fmt.Sprintf("TUF_%s_PASSPHRASE", strings.ToUpper(role))); pass != "" && !change {
142 return []byte(pass), nil
143 }
144
145 if change {
146
147 if new_pass := os.Getenv(fmt.Sprintf("TUF_NEW_%s_PASSPHRASE", strings.ToUpper(role))); new_pass != "" {
148
149 return []byte(new_pass), nil
150 }
151
152 role = fmt.Sprintf("new %s", role)
153 }
154 fmt.Fprintf(os.Stderr, "Enter %s keys passphrase: ", role)
155 passphrase, err := term.ReadPassword(int(syscall.Stdin))
156 fmt.Fprintln(os.Stderr)
157 if err != nil {
158 return nil, err
159 }
160
161 if !confirm {
162 return passphrase, nil
163 }
164
165 fmt.Fprintf(os.Stderr, "Repeat %s keys passphrase: ", role)
166 confirmation, err := term.ReadPassword(int(syscall.Stdin))
167 fmt.Fprintln(os.Stderr)
168 if err != nil {
169 return nil, err
170 }
171
172 if !bytes.Equal(passphrase, confirmation) {
173 return nil, errors.New("the entered passphrases do not match")
174 }
175 return passphrase, nil
176 }
177
View as plain text