-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathshell.go
146 lines (123 loc) · 3.02 KB
/
shell.go
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"fmt"
"io"
"os"
"strconv"
"strings"
"github.com/google/shlex"
"gopkg.in/readline.v1"
"github.com/hashicorp/vault/shamir"
)
const (
shellHelp = `
This interactive shell will allow you to enter the key parts.
Commands:
add <part> - adds a key part
list - view the parts that have been entered
del <num> - deletes the part by #
done - indicate you have entered parts and are ready to decrypt
exit - exit immediately without decrypting
help - display this message
`
)
func obtainShamirKey() [56]byte {
parts := [][]byte{}
cli := setupReadline()
defer cli.Close()
showShellHelp()
for {
command, args, err := parseCommand(cli.Readline())
if err != nil {
if err != io.EOF && err.Error() != "Interrupt" {
fmt.Println("error: ", err)
}
continue
}
switch command {
case "add":
if len(args) == 0 {
fmt.Println("expected an encoded key")
continue
}
fmt.Println("add:", args[0])
part, err := decode(args[0], encoding)
if err != nil {
fmt.Printf("failed to decode key part using %s: %s\n", encoding, err)
continue
}
parts = append(parts, part)
case "del":
if len(args) == 0 {
fmt.Println("expected a key number")
continue
}
index, err := strconv.ParseInt(args[0], 10, 64)
if err != nil || index < 1 || int(index) > len(parts) {
fmt.Printf("key index is invalid")
continue
}
// Convert 0-based to 1-based
fmt.Printf("deleting key at index %d\n", index)
parts = append(parts[:index-1], parts[index:]...)
case "done":
// Check key for validity. If invalid, provide another chance to
// correct one or more key parts.
fmt.Printf("%d keys entered. validating.\n", len(parts))
key, err := shamir.Combine(parts)
if err != nil {
fmt.Println("error combining key parts:", err)
continue
}
if len(key) != 56 {
fmt.Printf("expected key of length 56. got length: %d\n", len(key))
continue
}
// Key looks good. Correct the type and return
var result [56]byte
copy(result[:], key)
return result
case "exit":
fmt.Println("quitting without decrypting")
os.Exit(0)
case "help":
showShellHelp()
case "list":
for i := range parts {
displayShamirKeyPart(i, parts[i], encoding)
}
}
}
}
func setupReadline() *readline.Instance {
completer := readline.NewPrefixCompleter(
readline.PcItem("add"),
readline.PcItem("del"),
readline.PcItem("done"),
readline.PcItem("exit"),
readline.PcItem("help"),
readline.PcItem("list"),
)
cli, err := readline.NewEx(&readline.Config{
Prompt: ">> ",
AutoComplete: completer,
})
exitOnError("failed to configure readline", err)
return cli
}
func parseCommand(line string, err error) (string, []string, error) {
if err != nil {
return "", nil, err
}
parts, err := shlex.Split(line)
if err != nil {
return "", nil, err
}
if len(parts) == 0 {
return "", []string{}, nil
}
return strings.ToLower(parts[0]), parts[1:], nil
}
func showShellHelp() {
fmt.Println(shellHelp)
}