X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=cmd%2Fepoint-client%2Fepoint-client.go;h=019a4d3ccc6ed9266b93ef6f63a9769a244e6757;hb=e98810ec012932029c799f53d200b9c3115ad77d;hp=da29a2cadf1e00b14b1a4bd0a52780888bf3e532;hpb=60f039199482cbeffc865a259f31809aa17d9698;p=epoint diff --git a/cmd/epoint-client/epoint-client.go b/cmd/epoint-client/epoint-client.go index da29a2c..019a4d3 100644 --- a/cmd/epoint-client/epoint-client.go +++ b/cmd/epoint-client/epoint-client.go @@ -1,4 +1,434 @@ 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) + } }