package main import ( "bytes" "crypto/openpgp" "crypto/openpgp/armor" "crypto/rand" "epoint/document" "epoint/key" "epoint/store" "fmt" "io" "log" "net/http" "net/url" "os" "strconv" ) // TODO: store documents, query document by id, easy submit var db *store.Conn const usage = `usage: ./epoint-client [k|d|s|v|c] [args..] < [seed|document] server is http://localhost:8080 by default i - make issuer key, use seed for generation, args: denomination h - make holder key, use seed for generation, args: issuer d - make draft, use seed for signing key, args: targetid value s - submit a (key|draft|cert) document, args: k|d|c [server] v - verify a document (prints body of the document if ok) c - connect to server and get server key, args: [server] ` func rnd(n int) (r []byte, err error) { r = make([]byte, n) _, err = io.ReadFull(rand.Reader, r) return } func k(r []byte, cmd, arg string) (err error) { var e *openpgp.Entity if cmd == "i" { e, err = key.Issuer(r, arg) } else { s, err1 := db.Get("key", arg) err = err1 if err != nil { return } ie, err1 := key.Parse(s) err = err1 if err != nil { return } isIssuer, _, denom, err1 := key.Check(ie) err = err1 if err != nil { return } if !isIssuer { err = fmt.Errorf("Not an issuer key: %s", arg) return } e, err = key.Holder(r, arg, denom) } if err != nil { return } log.Printf("generated key %s", key.Id(e)) d, err := db.Get("key", key.Id(e)) if err == nil { // TODO: issuer, denom check (remove keys from store if they are not sent to server?) log.Printf("key %s was found in store", key.Id(e)) } else { out := new(bytes.Buffer) w, err1 := armor.Encode(out, openpgp.PublicKeyType, nil) err = err1 if err != nil { return } // TODO: maybe Serialize should do this internally for _, ident := range e.Identities { err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey) if err != nil { return } } for _, subkey := range e.Subkeys { err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey) if err != nil { return } } err = e.Serialize(w) if err != nil { return } err = w.Close() if err != nil { return } _, err = out.Write([]byte{'\n'}) if err != nil { return } d = out.Bytes() err = db.Insert("key", key.Id(e), d) if err != nil { return } } _, err = os.Stdout.Write(d) return } func d(r []byte, target, value string) (err error) { v, err := strconv.Atoi64(value) if err != nil { return } e, err := key.Holder(r, "", "") if err != nil { return } log.Printf("drawer: %s", key.Id(e)) if key.Id(e) == target { err = fmt.Errorf("Drawer and beneficiary are the same") return } b, err := db.Get("key", key.Id(e)) if err != nil { return } e1, err := key.Parse(b) if err != nil { return } _, issuer, denom, err := key.Check(e1) if err != nil { return } // TODO: store server id as well? b, err = db.Get("", "serverkey") if err != nil { return } sk, err := key.Parse(b) if err != nil { return } // TODO: check beneficiary (check value?) draft := new(document.Draft) draft.Drawer = key.Id(e) draft.Beneficiary = target draft.Amount = v draft.Denomination = denom draft.Issuer = issuer draft.AuthorizedBy = key.Id(sk) nonce, err := rnd(10) if err != nil { return } draft.Nonce = fmt.Sprintf("%X", nonce) s, c, err := document.Format(draft, e) if err != nil { return } log.Printf("draft id: %s", document.Id(c)) _, err = os.Stdout.Write(s) return } func s(d []byte, cmd, server string) (err error) { m := map[string]string{ "k": "key", "d": "draft", "c": "debit", } k, ok := m[cmd] if !ok { err = fmt.Errorf("unknown submit command: %s", cmd) return } id := "" switch cmd { case "k": e, err1 := key.Parse(d) err = err1 if err != nil { return } id = key.Id(e) err = db.Set("key", id, d) case "d": _, s, err1 := document.Parse(d) err = err1 if err != nil { return } id = document.Id(s) err = db.Set("draft", id, d) case "c": _, s, err1 := document.Parse(d) err = err1 if err != nil { return } id = document.Id(s) err = db.Set("cert", id, d) } if err != nil { return } log.Printf("document id: %s, server: %s", id, server) resp, err := http.PostForm(server+"/submit", url.Values{k: {string(d)}}) if err != nil { return } if resp.StatusCode != 200 { log.Printf("request failed: %s", resp.Status) } // TODO: store result b, err := readall(resp.Body) defer resp.Body.Close() if err != nil { return } _, err = os.Stdout.Write(b) if err != nil { return } cert, s, err := document.Parse(b) if err != nil { return } d, err = db.Get("", "serverkey") if err != nil { return } e, err := key.Parse(d) if err != nil { return } err = document.Verify(s, openpgp.EntityList{e}) if err != nil { return } log.Printf("response type: %T, response id: %s", cert, document.Id(s)) err = db.Set("cert", document.Id(s), b) return } func v(d []byte) (err error) { // handle armored pubkey if bytes.Index(d, []byte(openpgp.PublicKeyType)) >= 0 { es, err1 := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d)) if err1 != nil { err = err1 return } for _, e := range es { isIssuer, issuer, denom, err1 := key.Check(e) if err1 != nil { if err != nil { log.Println(err) } err = err1 continue } if isIssuer { fmt.Println("Issuer key") } else { fmt.Println("Holder key") } fmt.Printf("Issuer: %s\nDenomination: %s\nId: %s\n", issuer, denom, key.Id(e)) } return } _, s, err := document.Parse(d) if err != nil { return } _, err = os.Stdout.Write(s.Body) return } func c(server string) (err error) { resp, err := http.Get(server + "/serverkey") if err != nil { return } if resp.StatusCode != 200 { log.Printf("request failed: %s\n", resp.Status) } b, err := readall(resp.Body) if err != nil { return } err = resp.Body.Close() if err != nil { return } e, err := key.Parse(b) if err != nil { return } log.Printf("got server key %s", key.Id(e)) err = db.Set("key", key.Id(e), b) if err != nil { return } err = db.Set("", "serverkey", b) return } // TODO: commmon code with server func initstore(dir string) (db *store.Conn, err error) { log.Printf("using root dir %s", dir) db, err = store.Open(dir) if err != nil { return } err = db.Ensure("key") if err != nil { return } err = db.Ensure("cert") if err != nil { return } err = db.Ensure("draft") if err != nil { return } err = db.Ensure("certby/draft") if err != nil { return } err = db.Ensure("certby/debit") if err != nil { return } err = db.Ensure("certby/key") if err != nil { return } err = db.Ensure("certby/key.serial") if err != nil { return } return } func storedir() string { dir := os.Getenv("HOME") if dir == "" { dir = "/var/cache/epoint" } else { dir += "/.epoint-client" } return dir } func readall(r io.Reader) ([]byte, error) { b := make([]byte, 10000) n, err := io.ReadFull(r, b) if err != io.ErrUnexpectedEOF { if err == nil { err = fmt.Errorf("too much input") } return nil, err } return b[:n], nil } func read() []byte { b, err := readall(os.Stdin) if err != nil { log.Fatal(err) } return b } func main() { if len(os.Args) < 2 { log.Fatal(usage) } var err error db, err = initstore(storedir()) server := "http://localhost:8080" switch os.Args[1] { case "h", "i": if len(os.Args) != 3 { log.Fatal(usage) } err = k(read(), os.Args[1], os.Args[2]) case "d": if len(os.Args) != 4 { log.Fatal(usage) } err = d(read(), os.Args[2], os.Args[3]) case "s": cmd := "" if len(os.Args) == 4 { cmd = os.Args[2] server = os.Args[3] } else if len(os.Args) == 3 { cmd = os.Args[2] } else { log.Fatal(usage) } err = s(read(), cmd, server) case "v": if len(os.Args) != 2 { log.Fatal(usage) } err = v(read()) case "c": if len(os.Args) == 3 { server = os.Args[2] } else if len(os.Args) != 2 { log.Fatal(usage) } err = c(server) default: log.Fatal(usage) } if err != nil { log.Fatal(err) } }