epoint-client += query documents by id
[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 [i|h|d|q|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 q - query document, args: k|d|c id [server]
31 s - submit a (key|draft|cert) document, args: k|d|c [server]
32 v - verify a document (prints body of the document if ok)
33 c - connect to server and get server key, args: [server]
34 `
35
36 func rnd(n int) (r []byte, err error) {
37         r = make([]byte, n)
38         _, err = io.ReadFull(rand.Reader, r)
39         return
40 }
41
42 func k(r []byte, cmd, arg string) (err error) {
43         var e *openpgp.Entity
44
45         if cmd == "i" {
46                 e, err = key.Issuer(r, arg)
47         } else {
48                 s, err1 := db.Get("key", arg)
49                 err = err1
50                 if err != nil {
51                         return
52                 }
53                 ie, err1 := key.Parse(s)
54                 err = err1
55                 if err != nil {
56                         return
57                 }
58                 isIssuer, _, denom, err1 := key.Check(ie)
59                 err = err1
60                 if err != nil {
61                         return
62                 }
63                 if !isIssuer {
64                         err = fmt.Errorf("Not an issuer key: %s", arg)
65                         return
66                 }
67                 e, err = key.Holder(r, arg, denom)
68         }
69         if err != nil {
70                 return
71         }
72         log.Printf("generated key %s", key.Id(e))
73         d, err := db.Get("key", key.Id(e))
74         if err == nil {
75                 // TODO: issuer, denom check (remove keys from store if they are not sent to server?)
76                 log.Printf("key %s was found in store", key.Id(e))
77         } else {
78                 out := new(bytes.Buffer)
79                 w, err1 := armor.Encode(out, openpgp.PublicKeyType, nil)
80                 err = err1
81                 if err != nil {
82                         return
83                 }
84                 // TODO: maybe Serialize should do this internally
85                 for _, ident := range e.Identities {
86                         err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey)
87                         if err != nil {
88                                 return
89                         }
90                 }
91                 for _, subkey := range e.Subkeys {
92                         err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey)
93                         if err != nil {
94                                 return
95                         }
96                 }
97                 err = e.Serialize(w)
98                 if err != nil {
99                         return
100                 }
101                 err = w.Close()
102                 if err != nil {
103                         return
104                 }
105                 _, err = out.Write([]byte{'\n'})
106                 if err != nil {
107                         return
108                 }
109                 d = out.Bytes()
110                 err = db.Insert("key", key.Id(e), d)
111                 if err != nil {
112                         return
113                 }
114         }
115         _, err = os.Stdout.Write(d)
116         return
117 }
118
119 func d(r []byte, target, value string) (err error) {
120         v, err := strconv.Atoi64(value)
121         if err != nil {
122                 return
123         }
124         e, err := key.Holder(r, "", "")
125         if err != nil {
126                 return
127         }
128         log.Printf("drawer: %s", key.Id(e))
129         if key.Id(e) == target {
130                 err = fmt.Errorf("Drawer and beneficiary are the same")
131                 return
132         }
133         b, err := db.Get("key", key.Id(e))
134         if err != nil {
135                 return
136         }
137         e1, err := key.Parse(b)
138         if err != nil {
139                 return
140         }
141         _, issuer, denom, err := key.Check(e1)
142         if err != nil {
143                 return
144         }
145         // TODO: store server id as well?
146         b, err = db.Get("", "serverkey")
147         if err != nil {
148                 return
149         }
150         sk, err := key.Parse(b)
151         if err != nil {
152                 return
153         }
154         // TODO: check beneficiary (check value?)
155         draft := new(document.Draft)
156         draft.Drawer = key.Id(e)
157         draft.Beneficiary = target
158         draft.Amount = v
159         draft.Denomination = denom
160         draft.Issuer = issuer
161         draft.AuthorizedBy = key.Id(sk)
162         nonce, err := rnd(10)
163         if err != nil {
164                 return
165         }
166         draft.Nonce = fmt.Sprintf("%X", nonce)
167         s, c, err := document.Format(draft, e)
168         if err != nil {
169                 return
170         }
171         log.Printf("draft id: %s", document.Id(c))
172         _, err = os.Stdout.Write(s)
173         return
174 }
175
176 func q(cmd, id, server string) (err error) {
177         log.Printf("document id: %s, server: %s", id, server)
178         m := map[string]string{
179                 "k": "key",
180                 "d": "draft",
181                 "c": "cert",
182         }
183         k, ok := m[cmd]
184         if !ok {
185                 err = fmt.Errorf("unknown query command: %s", cmd)
186                 return
187         }
188         d, err := db.Get(k, id)
189         if err != nil {
190                 if _, ok := err.(store.NotFoundError); !ok {
191                         return
192                 }
193         } else {
194                 _, err = os.Stdout.Write(d)
195                 log.Printf("found %s in local store", id)
196                 return
197         }
198         resp, err := http.Get(server+"/"+k+"/"+id)
199         if err != nil {
200                 return
201         }
202         d, err = readall(resp.Body)
203         if err != nil {
204                 return
205         }
206         err = resp.Body.Close()
207         if err != nil {
208                 return
209         }
210         _, err = os.Stdout.Write(d)
211         if resp.StatusCode != 200 {
212                 err = fmt.Errorf("request failed: %s", resp.Status)
213                 return
214         }
215         if err != nil {
216                 return
217         }
218         log.Printf("got %s from the server", id)
219         switch cmd {
220         case "k":
221                 e, err1 := key.Parse(d)
222                 err = err1
223                 if err != nil {
224                         return
225                 }
226                 if id != key.Id(e) {
227                         err = fmt.Errorf("id mismatch, expected %s, got %s", id, key.Id(e))
228                         return
229                 }
230                 err = db.Set("key", id, d)
231         case "d":
232                 i, s, err1 := document.Parse(d)
233                 err = err1
234                 if err != nil {
235                         return
236                 }
237                 if id != document.Id(s) {
238                         err = fmt.Errorf("id mismatch, expected %s, got %s", id, document.Id(s))
239                         return
240                 }
241                 draft := i.(*document.Draft)
242                 b, err1 := db.Get("key", draft.Drawer)
243                 err = err1
244                 if err != nil {
245                         return
246                 }
247                 e, err1 := key.Parse(b)
248                 err = err1
249                 if err != nil {
250                         return
251                 }
252                 err = document.Verify(s, openpgp.EntityList{e})
253                 if err != nil {
254                         return
255                 }
256                 err = db.Set("draft", id, d)
257         case "c":
258                 i, s, err1 := document.Parse(d)
259                 err = err1
260                 if err != nil {
261                         return
262                 }
263                 if id != document.Id(s) {
264                         err = fmt.Errorf("id mismatch, expected %s, got %s", id, document.Id(s))
265                         return
266                 }
267                 cert, err1 := document.ToCert(i)
268                 err = err1
269                 if err != nil {
270                         return
271                 }
272                 // TODO: check serverkey
273                 b, err1 := db.Get("key", cert.AuthorizedBy)
274                 err = err1
275                 if err != nil {
276                         return
277                 }
278                 e, err1 := key.Parse(b)
279                 err = err1
280                 if err != nil {
281                         return
282                 }
283                 err = document.Verify(s, openpgp.EntityList{e})
284                 if err != nil {
285                         return
286                 }
287                 err = db.Set("cert", id, d)
288         }
289         return
290 }
291
292 func s(d []byte, cmd, server string) (err error) {
293         m := map[string]string{
294                 "k": "key",
295                 "d": "draft",
296                 "c": "debit",
297         }
298         k, ok := m[cmd]
299         if !ok {
300                 err = fmt.Errorf("unknown submit command: %s", cmd)
301                 return
302         }
303         id := ""
304         switch cmd {
305         case "k":
306                 e, err1 := key.Parse(d)
307                 err = err1
308                 if err != nil {
309                         return
310                 }
311                 id = key.Id(e)
312                 err = db.Set("key", id, d)
313         case "d":
314                 _, s, err1 := document.Parse(d)
315                 err = err1
316                 if err != nil {
317                         return
318                 }
319                 id = document.Id(s)
320                 err = db.Set("draft", id, d)
321         case "c":
322                 _, s, err1 := document.Parse(d)
323                 err = err1
324                 if err != nil {
325                         return
326                 }
327                 id = document.Id(s)
328                 err = db.Set("cert", id, d)
329         }
330         if err != nil {
331                 return
332         }
333         log.Printf("document id: %s, server: %s", id, server)
334         resp, err := http.PostForm(server+"/submit", url.Values{k: {string(d)}})
335         if err != nil {
336                 return
337         }
338         if resp.StatusCode != 200 {
339                 log.Printf("request failed: %s", resp.Status)
340         }
341         // TODO: store result
342         b, err := readall(resp.Body)
343         defer resp.Body.Close()
344         if err != nil {
345                 return
346         }
347         _, err = os.Stdout.Write(b)
348         if err != nil {
349                 return
350         }
351         cert, s, err := document.Parse(b)
352         if err != nil {
353                 return
354         }
355         d, err = db.Get("", "serverkey")
356         if err != nil {
357                 return
358         }
359         e, err := key.Parse(d)
360         if err != nil {
361                 return
362         }
363         err = document.Verify(s, openpgp.EntityList{e})
364         if err != nil {
365                 return
366         }
367         log.Printf("response type: %T, response id: %s", cert, document.Id(s))
368         err = db.Set("cert", document.Id(s), b)
369         return
370 }
371
372 func v(d []byte) (err error) {
373         // handle armored pubkey
374         if bytes.Index(d, []byte(openpgp.PublicKeyType)) >= 0 {
375                 es, err1 := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
376                 if err1 != nil {
377                         err = err1
378                         return
379                 }
380                 for _, e := range es {
381                         isIssuer, issuer, denom, err1 := key.Check(e)
382                         if err1 != nil {
383                                 if err != nil {
384                                         log.Println(err)
385                                 }
386                                 err = err1
387                                 continue
388                         }
389                         if isIssuer {
390                                 fmt.Println("Issuer key")
391                         } else {
392                                 fmt.Println("Holder key")
393                         }
394                         fmt.Printf("Issuer: %s\nDenomination: %s\nId: %s\n", issuer, denom, key.Id(e))
395                 }
396                 return
397         }
398         _, s, err := document.Parse(d)
399         if err != nil {
400                 return
401         }
402         _, err = os.Stdout.Write(s.Body)
403         return
404 }
405
406 func c(server string) (err error) {
407         resp, err := http.Get(server + "/serverkey")
408         if err != nil {
409                 return
410         }
411         if resp.StatusCode != 200 {
412                 log.Printf("request failed: %s\n", resp.Status)
413         }
414         b, err := readall(resp.Body)
415         if err != nil {
416                 return
417         }
418         err = resp.Body.Close()
419         if err != nil {
420                 return
421         }
422         e, err := key.Parse(b)
423         if err != nil {
424                 return
425         }
426         log.Printf("got server key %s", key.Id(e))
427         err = db.Set("key", key.Id(e), b)
428         if err != nil {
429                 return
430         }
431         err = db.Set("", "serverkey", b)
432         return
433 }
434
435 // TODO: commmon code with server
436 func initstore(dir string) (db *store.Conn, err error) {
437         log.Printf("using root dir %s", dir)
438         db, err = store.Open(dir)
439         if err != nil {
440                 return
441         }
442         err = db.Ensure("key")
443         if err != nil {
444                 return
445         }
446         err = db.Ensure("cert")
447         if err != nil {
448                 return
449         }
450         err = db.Ensure("draft")
451         if err != nil {
452                 return
453         }
454         err = db.Ensure("certby/draft")
455         if err != nil {
456                 return
457         }
458         err = db.Ensure("certby/debit")
459         if err != nil {
460                 return
461         }
462         err = db.Ensure("certby/key")
463         if err != nil {
464                 return
465         }
466         err = db.Ensure("certby/key.serial")
467         if err != nil {
468                 return
469         }
470         return
471 }
472
473 func storedir() string {
474         dir := os.Getenv("HOME")
475         if dir == "" {
476                 dir = "/var/cache/epoint"
477         } else {
478                 dir += "/.epoint-client"
479         }
480         return dir
481 }
482
483 func readall(r io.Reader) ([]byte, error) {
484         b := make([]byte, 10000)
485         n, err := io.ReadFull(r, b)
486         if err != io.ErrUnexpectedEOF {
487                 if err == nil {
488                         err = fmt.Errorf("too much input")
489                 }
490                 return nil, err
491         }
492         return b[:n], nil
493 }
494
495 func read() []byte {
496         b, err := readall(os.Stdin)
497         if err != nil {
498                 log.Fatal(err)
499         }
500         return b
501 }
502
503 func main() {
504         if len(os.Args) < 2 {
505                 log.Fatal(usage)
506         }
507
508         var err error
509         db, err = initstore(storedir())
510         server := "http://localhost:8080"
511         switch os.Args[1] {
512         case "h", "i":
513                 if len(os.Args) != 3 {
514                         log.Fatal(usage)
515                 }
516                 err = k(read(), os.Args[1], os.Args[2])
517         case "d":
518                 if len(os.Args) != 4 {
519                         log.Fatal(usage)
520                 }
521                 err = d(read(), os.Args[2], os.Args[3])
522         case "q":
523                 cmd := ""
524                 id := ""
525                 if len(os.Args) == 5 {
526                         cmd = os.Args[2]
527                         id = os.Args[3]
528                         server = os.Args[4]
529                 } else if len(os.Args) == 4 {
530                         cmd = os.Args[2]
531                         id = os.Args[3]
532                 } else {
533                         log.Fatal(usage)
534                 }
535                 err = q(cmd, id, server)
536         case "s":
537                 cmd := ""
538                 if len(os.Args) == 4 {
539                         cmd = os.Args[2]
540                         server = os.Args[3]
541                 } else if len(os.Args) == 3 {
542                         cmd = os.Args[2]
543                 } else {
544                         log.Fatal(usage)
545                 }
546                 err = s(read(), cmd, server)
547         case "v":
548                 if len(os.Args) != 2 {
549                         log.Fatal(usage)
550                 }
551                 err = v(read())
552         case "c":
553                 if len(os.Args) == 3 {
554                         server = os.Args[2]
555                 } else if len(os.Args) != 2 {
556                         log.Fatal(usage)
557                 }
558                 err = c(server)
559         default:
560                 log.Fatal(usage)
561         }
562         if err != nil {
563                 log.Fatal(err)
564         }
565 }