/
main.go
203 lines (188 loc) · 5.38 KB
/
main.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// cypherlock implements a Cypherlock client.
package main
import (
"bytes"
"encoding/hex"
"flag"
"fmt"
"os"
"os/signal"
"time"
"unicode"
"github.com/JonathanLogan/cypherlock/clientinterface"
"github.com/JonathanLogan/cypherlock/msgcrypt"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh/terminal"
)
var (
flagSignatureKey string
flagServerURL string
flagPath string
flagValidFrom uint64
flagValidTo uint64
flagFD int
flagNL bool
flagFunctionExtend bool
flagFunctionCreate bool
flagFunctionUnlock bool
now uint64
)
func init() {
now = uint64(time.Now().Unix())
flag.BoolVar(&flagFunctionExtend, "extend", false, "extend existing Cypherlock")
flag.BoolVar(&flagFunctionCreate, "create", false, "create new Cypherlock")
flag.BoolVar(&flagFunctionUnlock, "unlock", false, "unlock Cypherlock")
flag.BoolVar(&flagNL, "nl", false, "add newline to secret when writing")
flag.StringVar(&flagPath, "path", "/tmp/cypherlock", "path to store lock")
flag.StringVar(&flagServerURL, "server", "127.0.0.1:11139", "Cypherlock server [IP:Port]")
flag.StringVar(&flagSignatureKey, "sigkey", "", "cypherlockd signature key. Required for -create and -extend")
flag.Uint64Var(&flagValidFrom, "from", now, "earliest unix timestamp at which the lock is valid")
flag.Uint64Var(&flagValidTo, "to", now+1800, "latest unix timestamp at which the lock is valid")
flag.IntVar(&flagFD, "fd", 3, "file descriptor to read/write secret from. Required for -create and -unlock")
flag.Parse()
}
func writeSecret(s []byte) {
q := s
if flagNL {
q = append(q, '\n')
}
file := os.NewFile(uintptr(flagFD), "pipe")
defer file.Close()
_, err := file.Write(q)
if err != nil {
fmt.Printf("ERR: %s\n", err)
os.Exit(1)
}
}
func readSecret() []byte {
d := make([]byte, 500)
file := os.NewFile(uintptr(flagFD), "pipe")
defer file.Close()
n, err := file.Read(d)
if n == 0 && err != nil {
fmt.Printf("ERR: %s\n", err)
os.Exit(1)
}
return bytes.TrimFunc(d[:n], unicode.IsSpace)
}
func getSigKey() *[ed25519.PublicKeySize]byte {
sigKey := new([ed25519.PublicKeySize]byte)
if len(flagSignatureKey) == 0 {
fmt.Println("Must give -sigkey.")
os.Exit(1)
}
sigKeyB, err := hex.DecodeString(flagSignatureKey)
if err != nil {
fmt.Printf("ERR: %s\n", err)
os.Exit(1)
}
copy(sigKey[:], sigKeyB)
return sigKey
}
func getPassphraseOnce(prompt string, fd int) []byte {
if !terminal.IsTerminal(fd) {
fmt.Println("ERR: Not a terminal.")
os.Exit(1)
}
fmt.Printf("%s: ", prompt)
state, err := terminal.GetState(fd)
if err != nil {
fmt.Printf("ERR: %s\n", err)
os.Exit(1)
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
terminal.Restore(fd, state)
fmt.Println("\ncancelled")
os.Exit(1)
}()
p, err := terminal.ReadPassword(fd)
if err != nil {
fmt.Printf("ERR: %s\n", err)
os.Exit(1)
}
signal.Stop(c)
fmt.Print("\n")
return bytes.TrimFunc(p, unicode.IsSpace)
}
func getPassphrase() []byte {
var p1 []byte
fd := 0
RequestLoop:
for {
p1 = getPassphraseOnce("Please enter passphrase (no echo)", fd)
if len(p1) == 0 {
fmt.Println("empty passphrase, please repeat.")
continue RequestLoop
}
p2 := getPassphraseOnce("Please repeat passphrase (no echo)", fd)
if bytes.Equal(p1, p2) {
break RequestLoop
}
fmt.Print("Passphrases dont match.\n")
}
fmt.Print("\n")
return p1
}
const timeFormat = "Mon Jan 2 15:04:05 -0700 MST 2006"
func main() {
if !(flagFunctionExtend || flagFunctionCreate || flagFunctionUnlock) {
fmt.Println("One of -extend , -create or -unlock required.")
os.Exit(1)
}
if (flagFunctionExtend && (flagFunctionCreate || flagFunctionUnlock)) ||
(flagFunctionCreate && (flagFunctionExtend || flagFunctionUnlock)) ||
(flagFunctionUnlock && (flagFunctionExtend || flagFunctionCreate)) {
fmt.Println("Only one of -extend , -create or -unlock allowed.")
os.Exit(1)
}
Config := &msgcrypt.Cypherlock{
ServerURL: flagServerURL,
Storage: &clientinterface.DefaultStorage{Path: flagPath},
ClientRPC: new(clientinterface.DefaultRPC),
}
if flagFunctionCreate || flagFunctionExtend {
Config.SignatureKey = getSigKey()
}
if flagFunctionCreate || flagFunctionUnlock {
if flagFD < 3 {
fmt.Println("ERR: fd must be 3 or higher.")
os.Exit(1)
}
}
if flagFunctionCreate {
passphrase := getPassphrase()
secret := readSecret()
validFrom, validTo, err := Config.CreateLock(passphrase, secret, flagValidFrom, flagValidTo)
if err != nil {
fmt.Printf("ERR: %s\n", err)
os.Exit(1)
}
validFromT, validToT := time.Unix(int64(validFrom), 0).Format(timeFormat), time.Unix(int64(validTo), 0).Format(timeFormat)
fmt.Printf("Lock created. From \"%s\" to \"%s\"\n", validFromT, validToT)
os.Exit(0)
}
passphrase := getPassphraseOnce("Please enter passphrase (no echo)", 0)
if flagFunctionExtend {
validFrom, validTo, err := Config.ExtendLock(passphrase, now, flagValidFrom, flagValidTo)
if err != nil {
fmt.Printf("ERR: %s\n", err)
os.Exit(1)
}
validFromT, validToT := time.Unix(int64(validFrom), 0).Format(timeFormat), time.Unix(int64(validTo), 0).Format(timeFormat)
fmt.Printf("Lock extended. From \"%s\" to \"%s\"\n", validFromT, validToT)
os.Exit(0)
}
if flagFunctionUnlock {
realSecret, err := Config.LoadLock(passphrase, now)
if err != nil {
fmt.Printf("ERR: %s\n", err)
os.Exit(1)
}
writeSecret(realSecret)
_ = realSecret
os.Exit(0)
}
}