20 // TODO: store documents, query document by id, easy submit
24 const usage = `usage: ./epoint-client [i|h|d|r|q|s|v|c] [args..] < [seed|document]
25 server is http://localhost:8080 by default
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 r - sign raw document, use seed for signing key, args: document
31 q - query document, args: k|d|c id [server]
32 s - submit a (key|draft|cert) document, args: k|d|c [server]
33 v - verify a document (prints body of the document if ok)
34 c - connect to server and get server key, args: [server]
37 func rnd(n int) (r []byte, err error) {
39 _, err = io.ReadFull(rand.Reader, r)
43 func k(r []byte, cmd, arg string) (err error) {
47 e, err = key.Issuer(r, arg)
49 s, err1 := db.Get("key", arg)
54 ie, err1 := key.Parse(s)
59 isIssuer, _, denom, err1 := key.Check(ie)
65 err = fmt.Errorf("Not an issuer key: %s", arg)
68 e, err = key.Holder(r, arg, denom)
73 log.Printf("generated key %s", key.Id(e))
74 d, err := db.Get("key", key.Id(e))
76 // TODO: issuer, denom check (remove keys from store if they are not sent to server?)
77 log.Printf("key %s was found in store", key.Id(e))
79 out := new(bytes.Buffer)
80 w, err1 := armor.Encode(out, openpgp.PublicKeyType, nil)
85 // TODO: maybe Serialize should do this internally
86 for _, ident := range e.Identities {
87 err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey)
92 for _, subkey := range e.Subkeys {
93 err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey)
106 _, err = out.Write([]byte{'\n'})
111 err = db.Insert("key", key.Id(e), d)
116 _, err = os.Stdout.Write(d)
120 func d(r []byte, target, value string) (err error) {
121 v, err := strconv.ParseInt(value, 10, 64)
125 e, err := key.Holder(r, "", "")
129 log.Printf("drawer: %s", key.Id(e))
130 if key.Id(e) == target {
131 err = fmt.Errorf("Drawer and beneficiary are the same")
134 b, err := db.Get("key", key.Id(e))
138 e1, err := key.Parse(b)
142 _, issuer, denom, err := key.Check(e1)
146 // TODO: store server id as well?
147 b, err = db.Get("", "serverkey")
151 sk, err := key.Parse(b)
155 // TODO: check beneficiary (check value?)
156 draft := new(document.Draft)
157 draft.Drawer = key.Id(e)
158 draft.Beneficiary = target
160 draft.Denomination = denom
161 draft.Issuer = issuer
162 draft.AuthorizedBy = key.Id(sk)
163 nonce, err := rnd(10)
167 draft.Nonce = fmt.Sprintf("%X", nonce)
168 s, c, err := document.Format(draft, e)
172 log.Printf("draft id: %s", document.Id(c))
173 _, err = os.Stdout.Write(s)
177 // TODO: document.Sign does not handle dash escape
178 func raw(r []byte, name string) (err error) {
179 e, err := key.Holder(r, "", "")
183 log.Printf("signer: %s", key.Id(e))
184 f, err := os.Open(name)
188 doc, err := readall(f)
192 d, err := document.Sign(doc, e)
196 s, err := document.FormatSigned(d)
200 _, err = os.Stdout.Write(s)
204 func q(cmd, id, server string) (err error) {
205 log.Printf("document id: %s, server: %s", id, server)
206 m := map[string]string{
213 err = fmt.Errorf("unknown query command: %s", cmd)
216 d, err := db.Get(k, id)
218 if _, ok := err.(store.NotFoundError); !ok {
222 _, err = os.Stdout.Write(d)
223 log.Printf("found %s in local store", id)
226 resp, err := http.Get(server+"/"+k+"/"+id)
230 d, err = readall(resp.Body)
234 err = resp.Body.Close()
238 _, err = os.Stdout.Write(d)
239 if resp.StatusCode != 200 {
240 err = fmt.Errorf("request failed: %s", resp.Status)
246 log.Printf("got %s from the server", id)
249 e, err1 := key.Parse(d)
255 err = fmt.Errorf("id mismatch, expected %s, got %s", id, key.Id(e))
258 err = db.Set("key", id, d)
260 i, s, err1 := document.Parse(d)
265 if id != document.Id(s) {
266 err = fmt.Errorf("id mismatch, expected %s, got %s", id, document.Id(s))
269 draft := i.(*document.Draft)
270 b, err1 := db.Get("key", draft.Drawer)
275 e, err1 := key.Parse(b)
280 err = document.Verify(s, openpgp.EntityList{e})
284 err = db.Set("draft", id, d)
286 i, s, err1 := document.Parse(d)
291 if id != document.Id(s) {
292 err = fmt.Errorf("id mismatch, expected %s, got %s", id, document.Id(s))
295 cert, err1 := document.ToCert(i)
300 // TODO: check serverkey
301 b, err1 := db.Get("key", cert.AuthorizedBy)
306 e, err1 := key.Parse(b)
311 err = document.Verify(s, openpgp.EntityList{e})
315 err = db.Set("cert", id, d)
320 func s(d []byte, cmd, server string) (err error) {
321 m := map[string]string{
328 err = fmt.Errorf("unknown submit command: %s", cmd)
334 e, err1 := key.Parse(d)
340 err = db.Set("key", id, d)
342 _, s, err1 := document.Parse(d)
348 err = db.Set("draft", id, d)
350 _, s, err1 := document.Parse(d)
356 err = db.Set("cert", id, d)
361 log.Printf("document id: %s, server: %s", id, server)
362 resp, err := http.PostForm(server+"/submit", url.Values{k: {string(d)}})
366 if resp.StatusCode != 200 {
367 log.Printf("request failed: %s", resp.Status)
369 // TODO: store result
370 b, err := readall(resp.Body)
371 defer resp.Body.Close()
375 _, err = os.Stdout.Write(b)
379 cert, s, err := document.Parse(b)
383 d, err = db.Get("", "serverkey")
387 e, err := key.Parse(d)
391 err = document.Verify(s, openpgp.EntityList{e})
395 log.Printf("response type: %T, response id: %s", cert, document.Id(s))
396 err = db.Set("cert", document.Id(s), b)
400 func v(d []byte) (err error) {
401 // handle armored pubkey
402 if bytes.Index(d, []byte(openpgp.PublicKeyType)) >= 0 {
403 es, err1 := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
408 for _, e := range es {
409 isIssuer, issuer, denom, err1 := key.Check(e)
418 fmt.Println("Issuer key")
420 fmt.Println("Holder key")
422 fmt.Printf("Issuer: %s\nDenomination: %s\nId: %s\n", issuer, denom, key.Id(e))
426 _, s, err := document.Parse(d)
430 _, err = os.Stdout.Write(s.Body)
434 func c(server string) (err error) {
435 resp, err := http.Get(server + "/serverkey")
439 if resp.StatusCode != 200 {
440 log.Printf("request failed: %s\n", resp.Status)
442 b, err := readall(resp.Body)
446 err = resp.Body.Close()
450 e, err := key.Parse(b)
454 log.Printf("got server key %s", key.Id(e))
455 err = db.Set("key", key.Id(e), b)
459 err = db.Set("", "serverkey", b)
463 // TODO: commmon code with server
464 func initstore(dir string) (db *store.Conn, err error) {
465 log.Printf("using root dir %s", dir)
466 db, err = store.Open(dir)
470 err = db.Ensure("key")
474 err = db.Ensure("cert")
478 err = db.Ensure("draft")
482 err = db.Ensure("certby/draft")
486 err = db.Ensure("certby/debit")
490 err = db.Ensure("certby/key")
494 err = db.Ensure("certby/key.serial")
501 func storedir() string {
502 dir := os.Getenv("HOME")
504 dir = "/var/cache/epoint"
506 dir += "/.epoint-client"
511 // read all but at most 1M from r
512 func readall(r io.Reader) ([]byte, error) {
513 b := make([]byte, 1<<20)
514 n, err := io.ReadFull(r, b)
515 if err != io.ErrUnexpectedEOF {
517 err = fmt.Errorf("too much input")
525 b, err := readall(os.Stdin)
533 if len(os.Args) < 2 {
538 db, err = initstore(storedir())
539 server := "http://localhost:8080"
542 if len(os.Args) != 3 {
545 err = k(read(), os.Args[1], os.Args[2])
547 if len(os.Args) != 4 {
550 err = d(read(), os.Args[2], os.Args[3])
552 if len(os.Args) != 3 {
555 err = raw(read(), os.Args[2])
559 if len(os.Args) == 5 {
563 } else if len(os.Args) == 4 {
569 err = q(cmd, id, server)
572 if len(os.Args) == 4 {
575 } else if len(os.Args) == 3 {
580 err = s(read(), cmd, server)
582 if len(os.Args) != 2 {
587 if len(os.Args) == 3 {
589 } else if len(os.Args) != 2 {