X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=cmd%2Fepoint-client%2Fepoint-client.go;h=71bbe048abe425d6ef6a8e2c18a6ebc5aed69687;hb=b7259bc293641fbe83e44d0f886cea52b8816769;hp=518346c66e535168fc897e2c51e593f96a393e0f;hpb=c7ee01bf43364405cb85b4dde3757f0a8c38aa9b;p=epoint diff --git a/cmd/epoint-client/epoint-client.go b/cmd/epoint-client/epoint-client.go index 518346c..71bbe04 100644 --- a/cmd/epoint-client/epoint-client.go +++ b/cmd/epoint-client/epoint-client.go @@ -1,11 +1,13 @@ package main import ( + "bytes" "crypto/openpgp" "crypto/openpgp/armor" "crypto/rand" "epoint/document" "epoint/key" + "epoint/store" "fmt" "io" "log" @@ -15,13 +17,20 @@ import ( "strconv" ) -const usage = `usage: ./epoint-client [k|d|s|v] [args..] < [seed|document] +// TODO: store documents, query document by id, easy submit + +var db *store.Conn + +const usage = `usage: ./epoint-client [i|h|d|q|s|v|c] [args..] < [seed|document] server is http://localhost:8080 by default -k - make key, use seed for generation, args: [issuer] denomination -d - make draft, use seed as signing key, args: targetid value -s - submit a document, args: k[ey]|d[raft]|c[ert] [server] +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 +q - query document, args: k|d|c id [server] +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) { @@ -30,76 +39,256 @@ func rnd(n int) (r []byte, err error) { return } -func k(r []byte, issuer, denom string) (err error) { +func k(r []byte, cmd, arg string) (err error) { var e *openpgp.Entity - if issuer == "" { - e, err = key.Issuer(r, denom) + if cmd == "i" { + e, err = key.Issuer(r, arg) } else { - e, err = key.Holder(r, issuer, denom) - } - if err != nil { - return + 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) } - w, err := armor.Encode(os.Stdout, openpgp.PublicKeyType, nil) 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) + 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 } - } - for _, subkey := range e.Subkeys { - err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey) + // 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 +} - err = e.Serialize(w) +func d(r []byte, target, value string) (err error) { + v, err := strconv.Atoi64(value) if err != nil { return } - _, err = w.Write([]byte{'\n'}) + e, err := key.Holder(r, "", "") if err != nil { return } - err = w.Close() - return -} - -func d(r []byte, target, value string) (err error) { - v, err := strconv.Atoi64(value) + 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 } - e, err := key.Holder(r, "", "") + 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 = "" // TODO - draft.Issuer = "" // TODO - draft.AuthorizedBy = "" // TODO + 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, _, err := document.Format(draft, e) + 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 q(cmd, id, server string) (err error) { + log.Printf("document id: %s, server: %s", id, server) + m := map[string]string{ + "k": "key", + "d": "draft", + "c": "cert", + } + k, ok := m[cmd] + if !ok { + err = fmt.Errorf("unknown query command: %s", cmd) + return + } + d, err := db.Get(k, id) + if err != nil { + if _, ok := err.(store.NotFoundError); !ok { + return + } + } else { + _, err = os.Stdout.Write(d) + log.Printf("found %s in local store", id) + return + } + resp, err := http.Get(server+"/"+k+"/"+id) + if err != nil { + return + } + d, err = readall(resp.Body) + if err != nil { + return + } + err = resp.Body.Close() + if err != nil { + return + } + _, err = os.Stdout.Write(d) + if resp.StatusCode != 200 { + err = fmt.Errorf("request failed: %s", resp.Status) + return + } + if err != nil { + return + } + log.Printf("got %s from the server", id) + switch cmd { + case "k": + e, err1 := key.Parse(d) + err = err1 + if err != nil { + return + } + if id != key.Id(e) { + err = fmt.Errorf("id mismatch, expected %s, got %s", id, key.Id(e)) + return + } + err = db.Set("key", id, d) + case "d": + i, s, err1 := document.Parse(d) + err = err1 + if err != nil { + return + } + if id != document.Id(s) { + err = fmt.Errorf("id mismatch, expected %s, got %s", id, document.Id(s)) + return + } + draft := i.(*document.Draft) + b, err1 := db.Get("key", draft.Drawer) + err = err1 + if err != nil { + return + } + e, err1 := key.Parse(b) + err = err1 + if err != nil { + return + } + err = document.Verify(s, openpgp.EntityList{e}) + if err != nil { + return + } + err = db.Set("draft", id, d) + case "c": + i, s, err1 := document.Parse(d) + err = err1 + if err != nil { + return + } + if id != document.Id(s) { + err = fmt.Errorf("id mismatch, expected %s, got %s", id, document.Id(s)) + return + } + cert, err1 := document.ToCert(i) + err = err1 + if err != nil { + return + } + // TODO: check serverkey + b, err1 := db.Get("key", cert.AuthorizedBy) + err = err1 + if err != nil { + return + } + e, err1 := key.Parse(b) + err = err1 + if err != nil { + return + } + err = document.Verify(s, openpgp.EntityList{e}) + if err != nil { + return + } + err = db.Set("cert", id, d) + } + return +} + func s(d []byte, cmd, server string) (err error) { m := map[string]string{ "k": "key", @@ -111,15 +300,101 @@ func s(d []byte, cmd, server string) (err error) { err = fmt.Errorf("unknown submit command: %s", cmd) return } - resp, err := http.PostForm(server, url.Values{k: {string(d)}}) + 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 } - fmt.Printf("%v", resp) + _, 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 @@ -128,16 +403,101 @@ func v(d []byte) (err error) { return } -func read() []byte { +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(os.Stdin, b) + n, err := io.ReadFull(r, b) if err != io.ErrUnexpectedEOF { if err == nil { - log.Fatal("too much input") + 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[:n] + return b } func main() { @@ -146,26 +506,34 @@ func main() { } var err error + db, err = initstore(storedir()) + server := "http://localhost:8080" switch os.Args[1] { - case "k": - issuer := "" - denom := "" - if len(os.Args) == 4 { - issuer = os.Args[2] - denom = os.Args[3] - } else if len(os.Args) == 3 { - denom = os.Args[2] - } else { + case "h", "i": + if len(os.Args) != 3 { log.Fatal(usage) } - err = k(read(), issuer, denom) + 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 "q": + cmd := "" + id := "" + if len(os.Args) == 5 { + cmd = os.Args[2] + id = os.Args[3] + server = os.Args[4] + } else if len(os.Args) == 4 { + cmd = os.Args[2] + id = os.Args[3] + } else { + log.Fatal(usage) + } + err = q(cmd, id, server) case "s": - server := "http://localhost:8080" cmd := "" if len(os.Args) == 4 { cmd = os.Args[2] @@ -175,12 +543,21 @@ func main() { } else { log.Fatal(usage) } - err = s(read(), cmd, server+"/submit") + 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)