epoint-client: separate issuer and holder key gen
[epoint] / cmd / epoint-client / epoint-client.go
1 package main
2
3 import (
4         "bytes"
5         "crypto/openpgp"
6         "crypto/openpgp/armor"
7         "crypto/rand"
8         "epoint/document"
9         "epoint/key"
10         "epoint/store"
11         "fmt"
12         "io"
13         "log"
14         "net/http"
15         "net/url"
16         "os"
17         "strconv"
18 )
19
20 // TODO: store documents, query document by id, easy submit
21
22 var db *store.Conn
23
24 const usage = `usage: ./epoint-client [k|d|s|v|c] [args..] < [seed|document]
25 server is http://localhost:8080 by default
26
27 i - make issuer key, use seed for generation, args: denomination
28 h - make holder key, use seed for generation, args: issuer
29 d - make draft, use seed for signing key, args: targetid value
30 s - submit a (key|draft|cert) document, args: k|d|c [server]
31 v - verify a document (prints body of the document if ok)
32 c - connect to server and get server key, args: [server]
33 `
34
35 func rnd(n int) (r []byte, err error) {
36         r = make([]byte, n)
37         _, err = io.ReadFull(rand.Reader, r)
38         return
39 }
40
41 func k(r []byte, cmd, arg string) (err error) {
42         var e *openpgp.Entity
43
44         if cmd == "i" {
45                 e, err = key.Issuer(r, arg)
46         } else {
47                 s, err1 := db.Get("key", arg)
48                 err = err1
49                 if err != nil {
50                         return
51                 }
52                 ie, err1 := key.Parse(s)
53                 err = err1
54                 if err != nil {
55                         return
56                 }
57                 isIssuer, _, denom, err1 := key.Check(ie)
58                 err = err1
59                 if err != nil {
60                         return
61                 }
62                 if !isIssuer {
63                         err = fmt.Errorf("Not an issuer key: %s", arg)
64                         return
65                 }
66                 e, err = key.Holder(r, arg, denom)
67         }
68         if err != nil {
69                 return
70         }
71         log.Printf("generated key %s", key.Id(e))
72         d, err := db.Get("key", key.Id(e))
73         if err == nil {
74                 // TODO: issuer, denom check (remove keys from store if they are not sent to server?)
75                 log.Printf("key %s was found in store", key.Id(e))
76         } else {
77                 out := new(bytes.Buffer)
78                 w, err1 := armor.Encode(out, openpgp.PublicKeyType, nil)
79                 err = err1
80                 if err != nil {
81                         return
82                 }
83                 // TODO: maybe Serialize should do this internally
84                 for _, ident := range e.Identities {
85                         err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey)
86                         if err != nil {
87                                 return
88                         }
89                 }
90                 for _, subkey := range e.Subkeys {
91                         err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey)
92                         if err != nil {
93                                 return
94                         }
95                 }
96                 err = e.Serialize(w)
97                 if err != nil {
98                         return
99                 }
100                 err = w.Close()
101                 if err != nil {
102                         return
103                 }
104                 _, err = out.Write([]byte{'\n'})
105                 if err != nil {
106                         return
107                 }
108                 d = out.Bytes()
109                 err = db.Insert("key", key.Id(e), d)
110                 if err != nil {
111                         return
112                 }
113         }
114         _, err = os.Stdout.Write(d)
115         return
116 }
117
118 func d(r []byte, target, value string) (err error) {
119         v, err := strconv.Atoi64(value)
120         if err != nil {
121                 return
122         }
123         e, err := key.Holder(r, "", "")
124         if err != nil {
125                 return
126         }
127         log.Printf("drawer: %s", key.Id(e))
128         if key.Id(e) == target {
129                 err = fmt.Errorf("Drawer and beneficiary are the same")
130                 return
131         }
132         b, err := db.Get("key", key.Id(e))
133         if err != nil {
134                 return
135         }
136         e1, err := key.Parse(b)
137         if err != nil {
138                 return
139         }
140         _, issuer, denom, err := key.Check(e1)
141         if err != nil {
142                 return
143         }
144         // TODO: store server id as well?
145         b, err = db.Get("", "serverkey")
146         if err != nil {
147                 return
148         }
149         sk, err := key.Parse(b)
150         if err != nil {
151                 return
152         }
153         // TODO: check beneficiary (check value?)
154         draft := new(document.Draft)
155         draft.Drawer = key.Id(e)
156         draft.Beneficiary = target
157         draft.Amount = v
158         draft.Denomination = denom
159         draft.Issuer = issuer
160         draft.AuthorizedBy = key.Id(sk)
161         nonce, err := rnd(10)
162         if err != nil {
163                 return
164         }
165         draft.Nonce = fmt.Sprintf("%X", nonce)
166         s, c, err := document.Format(draft, e)
167         if err != nil {
168                 return
169         }
170         log.Printf("draft id: %s", document.Id(c))
171         _, err = os.Stdout.Write(s)
172         return
173 }
174
175 func s(d []byte, cmd, server string) (err error) {
176         m := map[string]string{
177                 "k": "key",
178                 "d": "draft",
179                 "c": "debit",
180         }
181         k, ok := m[cmd]
182         if !ok {
183                 err = fmt.Errorf("unknown submit command: %s", cmd)
184                 return
185         }
186         id := ""
187         switch cmd {
188         case "k":
189                 e, err1 := key.Parse(d)
190                 err = err1
191                 if err != nil {
192                         return
193                 }
194                 id = key.Id(e)
195                 err = db.Set("key", id, d)
196         case "d":
197                 _, s, err1 := document.Parse(d)
198                 err = err1
199                 if err != nil {
200                         return
201                 }
202                 id = document.Id(s)
203                 err = db.Set("draft", id, d)
204         case "c":
205                 _, s, err1 := document.Parse(d)
206                 err = err1
207                 if err != nil {
208                         return
209                 }
210                 id = document.Id(s)
211                 err = db.Set("cert", id, d)
212         }
213         if err != nil {
214                 return
215         }
216         log.Printf("document id: %s, server: %s", id, server)
217         resp, err := http.PostForm(server+"/submit", url.Values{k: {string(d)}})
218         if err != nil {
219                 return
220         }
221         if resp.StatusCode != 200 {
222                 log.Printf("request failed: %s", resp.Status)
223         }
224         // TODO: store result
225         b, err := readall(resp.Body)
226         defer resp.Body.Close()
227         if err != nil {
228                 return
229         }
230         _, err = os.Stdout.Write(b)
231         if err != nil {
232                 return
233         }
234         cert, s, err := document.Parse(b)
235         if err != nil {
236                 return
237         }
238         d, err = db.Get("", "serverkey")
239         if err != nil {
240                 return
241         }
242         e, err := key.Parse(d)
243         if err != nil {
244                 return
245         }
246         err = document.Verify(s, openpgp.EntityList{e})
247         if err != nil {
248                 return
249         }
250         log.Printf("response type: %T, response id: %s", cert, document.Id(s))
251         err = db.Set("cert", document.Id(s), b)
252         return
253 }
254
255 func v(d []byte) (err error) {
256         // handle armored pubkey
257         if bytes.Index(d, []byte(openpgp.PublicKeyType)) >= 0 {
258                 es, err1 := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
259                 if err1 != nil {
260                         err = err1
261                         return
262                 }
263                 for _, e := range es {
264                         isIssuer, issuer, denom, err1 := key.Check(e)
265                         if err1 != nil {
266                                 if err != nil {
267                                         log.Println(err)
268                                 }
269                                 err = err1
270                                 continue
271                         }
272                         if isIssuer {
273                                 fmt.Println("Issuer key")
274                         } else {
275                                 fmt.Println("Holder key")
276                         }
277                         fmt.Printf("Issuer: %s\nDenomination: %s\nId: %s\n", issuer, denom, key.Id(e))
278                 }
279                 return
280         }
281         _, s, err := document.Parse(d)
282         if err != nil {
283                 return
284         }
285         _, err = os.Stdout.Write(s.Body)
286         return
287 }
288
289 func c(server string) (err error) {
290         resp, err := http.Get(server + "/serverkey")
291         if err != nil {
292                 return
293         }
294         if resp.StatusCode != 200 {
295                 log.Printf("request failed: %s\n", resp.Status)
296         }
297         b, err := readall(resp.Body)
298         if err != nil {
299                 return
300         }
301         err = resp.Body.Close()
302         if err != nil {
303                 return
304         }
305         e, err := key.Parse(b)
306         if err != nil {
307                 return
308         }
309         log.Printf("got server key %s", key.Id(e))
310         err = db.Set("key", key.Id(e), b)
311         if err != nil {
312                 return
313         }
314         err = db.Set("", "serverkey", b)
315         return
316 }
317
318 // TODO: commmon code with server
319 func initstore(dir string) (db *store.Conn, err error) {
320         log.Printf("using root dir %s", dir)
321         db, err = store.Open(dir)
322         if err != nil {
323                 return
324         }
325         err = db.Ensure("key")
326         if err != nil {
327                 return
328         }
329         err = db.Ensure("cert")
330         if err != nil {
331                 return
332         }
333         err = db.Ensure("draft")
334         if err != nil {
335                 return
336         }
337         err = db.Ensure("certby/draft")
338         if err != nil {
339                 return
340         }
341         err = db.Ensure("certby/debit")
342         if err != nil {
343                 return
344         }
345         err = db.Ensure("certby/key")
346         if err != nil {
347                 return
348         }
349         err = db.Ensure("certby/key.serial")
350         if err != nil {
351                 return
352         }
353         return
354 }
355
356 func storedir() string {
357         dir := os.Getenv("HOME")
358         if dir == "" {
359                 dir = "/var/cache/epoint"
360         } else {
361                 dir += "/.epoint-client"
362         }
363         return dir
364 }
365
366 func readall(r io.Reader) ([]byte, error) {
367         b := make([]byte, 10000)
368         n, err := io.ReadFull(r, b)
369         if err != io.ErrUnexpectedEOF {
370                 if err == nil {
371                         err = fmt.Errorf("too much input")
372                 }
373                 return nil, err
374         }
375         return b[:n], nil
376 }
377
378 func read() []byte {
379         b, err := readall(os.Stdin)
380         if err != nil {
381                 log.Fatal(err)
382         }
383         return b
384 }
385
386 func main() {
387         if len(os.Args) < 2 {
388                 log.Fatal(usage)
389         }
390
391         var err error
392         db, err = initstore(storedir())
393         server := "http://localhost:8080"
394         switch os.Args[1] {
395         case "h", "i":
396                 if len(os.Args) != 3 {
397                         log.Fatal(usage)
398                 }
399                 err = k(read(), os.Args[1], os.Args[2])
400         case "d":
401                 if len(os.Args) != 4 {
402                         log.Fatal(usage)
403                 }
404                 err = d(read(), os.Args[2], os.Args[3])
405         case "s":
406                 cmd := ""
407                 if len(os.Args) == 4 {
408                         cmd = os.Args[2]
409                         server = os.Args[3]
410                 } else if len(os.Args) == 3 {
411                         cmd = os.Args[2]
412                 } else {
413                         log.Fatal(usage)
414                 }
415                 err = s(read(), cmd, server)
416         case "v":
417                 if len(os.Args) != 2 {
418                         log.Fatal(usage)
419                 }
420                 err = v(read())
421         case "c":
422                 if len(os.Args) == 3 {
423                         server = os.Args[2]
424                 } else if len(os.Args) != 2 {
425                         log.Fatal(usage)
426                 }
427                 err = c(server)
428         default:
429                 log.Fatal(usage)
430         }
431         if err != nil {
432                 log.Fatal(err)
433         }
434 }