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