21 // TODO: create certby/ draftby/
22 // TODO: query document by id, easy submit
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
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
40 rootdir = flag.String("dir", "", "root dir for storage, empty sets a sane default")
41 server = flag.String("server", "http://localhost:8080", "server address")
45 func rnd(n int) (r []byte, err error) {
47 _, err = io.ReadFull(rand.Reader, r)
51 func k(r []byte, cmd, arg string) (err error) {
55 e, err = key.Issuer(r, arg)
57 s, err1 := db.Get("key", arg)
62 ie, err1 := key.Parse(s)
67 isIssuer, _, denom, err1 := key.Check(ie)
73 err = fmt.Errorf("Not an issuer key: %s", arg)
76 e, err = key.Holder(r, arg, denom)
81 log.Printf("generated key %s", key.Id(e))
82 d, err := db.Get("key", key.Id(e))
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))
87 out := new(bytes.Buffer)
88 w, err1 := armor.Encode(out, openpgp.PublicKeyType, nil)
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)
100 for _, subkey := range e.Subkeys {
101 err = subkey.Sig.SignKey(rand.Reader, subkey.PublicKey, e.PrivateKey)
114 _, err = out.Write([]byte{'\n'})
119 err = db.Insert("key", key.Id(e), d)
124 _, err = os.Stdout.Write(d)
128 func d(r []byte, target, value string) (err error) {
129 v, err := strconv.ParseInt(value, 10, 64)
133 e, err := key.Holder(r, "", "")
137 log.Printf("drawer: %s", key.Id(e))
138 if key.Id(e) == target {
139 err = fmt.Errorf("Drawer and beneficiary are the same")
142 b, err := db.Get("key", key.Id(e))
146 e1, err := key.Parse(b)
150 _, issuer, denom, err := key.Check(e1)
154 // TODO: store server id as well?
155 b, err = db.Get("", "serverkey")
159 sk, err := key.Parse(b)
163 // TODO: check beneficiary (check value?)
164 draft := new(document.Draft)
165 draft.Drawer = key.Id(e)
166 draft.Beneficiary = target
168 draft.Denomination = denom
169 draft.Issuer = issuer
170 draft.AuthorizedBy = key.Id(sk)
171 nonce, err := rnd(10)
175 draft.Nonce = fmt.Sprintf("%X", nonce)
176 s, c, err := document.Format(draft, e)
180 log.Printf("draft id: %s", document.Id(c))
181 _, err = os.Stdout.Write(s)
185 // TODO: document.Sign does not handle dash escape
186 func raw(r []byte, name string) (err error) {
187 e, err := key.Holder(r, "", "")
191 log.Printf("signer: %s", key.Id(e))
192 f, err := os.Open(name)
196 doc, err := readall(f)
200 d, err := document.Sign(doc, e)
204 s, err := document.FormatSigned(d)
208 _, err = os.Stdout.Write(s)
212 func q(cmd, id, server string) (err error) {
213 log.Printf("document id: %s, server: %s", id, server)
214 m := map[string]string{
221 err = fmt.Errorf("unknown query command: %s", cmd)
224 d, err := db.Get(k, id)
226 if _, ok := err.(store.NotFoundError); !ok {
230 _, err = os.Stdout.Write(d)
231 log.Printf("found %s in local store", id)
234 resp, err := http.Get(server + "/" + k + "/" + id)
238 d, err = readall(resp.Body)
242 err = resp.Body.Close()
246 _, err = os.Stdout.Write(d)
247 if resp.StatusCode != 200 {
248 err = fmt.Errorf("request failed: %s", resp.Status)
254 log.Printf("got %s from the server", id)
257 e, err1 := key.Parse(d)
263 err = fmt.Errorf("id mismatch, expected %s, got %s", id, key.Id(e))
266 err = db.Set("key", id, d)
268 i, s, err1 := document.Parse(d)
273 if id != document.Id(s) {
274 err = fmt.Errorf("id mismatch, expected %s, got %s", id, document.Id(s))
277 draft := i.(*document.Draft)
278 b, err1 := db.Get("key", draft.Drawer)
283 e, err1 := key.Parse(b)
288 err = document.Verify(s, openpgp.EntityList{e})
292 err = db.Set("draft", id, d)
294 i, s, err1 := document.Parse(d)
299 if id != document.Id(s) {
300 err = fmt.Errorf("id mismatch, expected %s, got %s", id, document.Id(s))
303 cert, err1 := document.ToCert(i)
308 // TODO: check serverkey
309 b, err1 := db.Get("key", cert.AuthorizedBy)
314 e, err1 := key.Parse(b)
319 err = document.Verify(s, openpgp.EntityList{e})
323 err = db.Set("cert", id, d)
328 func s(d []byte, cmd, server string) (err error) {
329 m := map[string]string{
336 err = fmt.Errorf("unknown submit command: %s", cmd)
342 e, err1 := key.Parse(d)
348 err = db.Set("key", id, d)
350 _, s, err1 := document.Parse(d)
356 err = db.Set("draft", id, d)
358 _, s, err1 := document.Parse(d)
364 err = db.Set("cert", id, d)
369 log.Printf("document id: %s, server: %s", id, server)
370 resp, err := http.PostForm(server+"/submit", url.Values{k: {string(d)}})
374 if resp.StatusCode != 200 {
375 log.Printf("request failed: %s", resp.Status)
377 // TODO: store result
378 b, err := readall(resp.Body)
379 defer resp.Body.Close()
383 _, err = os.Stdout.Write(b)
388 // TODO: signed reply?
391 cert, s, err := document.Parse(b)
395 d, err = db.Get("", "serverkey")
399 e, err := key.Parse(d)
403 err = document.Verify(s, openpgp.EntityList{e})
407 log.Printf("response type: %T, response id: %s", cert, document.Id(s))
408 err = db.Set("cert", document.Id(s), b)
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))
420 for _, e := range es {
421 isIssuer, issuer, denom, err1 := key.Check(e)
430 fmt.Println("Issuer key")
432 fmt.Println("Holder key")
434 fmt.Printf("Issuer: %s\nDenomination: %s\nId: %s\n", issuer, denom, key.Id(e))
438 _, s, err := document.Parse(d)
442 _, err = os.Stdout.Write(s.Body)
446 func c(server string) (err error) {
447 resp, err := http.Get(server + "/serverkey")
451 if resp.StatusCode != 200 {
452 log.Printf("request failed: %s\n", resp.Status)
454 b, err := readall(resp.Body)
458 err = resp.Body.Close()
462 e, err := key.Parse(b)
466 log.Printf("got server key %s", key.Id(e))
467 err = db.Set("key", key.Id(e), b)
471 err = db.Set("", "serverkey", b)
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)
482 err = db.Ensure("key")
486 err = db.Ensure("cert")
490 err = db.Ensure("draft")
494 err = db.Ensure("certby/draft")
498 err = db.Ensure("certby/debit")
502 err = db.Ensure("certby/key.issuer")
506 err = db.Ensure("certby/key.issuer.serial")
513 func storedir() string {
514 dir := os.Getenv("HOME")
516 dir = "/var/cache/epoint"
518 dir += "/.epoint-client"
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 {
529 err = fmt.Errorf("too much input")
537 b, err := readall(os.Stdin)
544 func cmdargs() (cmd string, args []string) {
556 cmd, args := cmdargs()
563 db, err = initstore(dir)
569 err = k(read(), cmd, args[0])
574 err = d(read(), args[0], args[1])
576 if len(os.Args) != 1 {
579 err = raw(read(), args[0])
584 err = q(args[0], args[1], *server)
589 err = s(read(), args[0], *server)