From: nsz Date: Thu, 19 Jan 2012 14:22:10 +0000 (+0100) Subject: various fixes to make testing possible X-Git-Url: http://nsz.repo.hu/git/?p=epoint;a=commitdiff_plain;h=d24526e0d767f7b45956d362180d2684e1e17294;hp=b9595460ff43dfd8cf07fe56f9908583456dc9c6 various fixes to make testing possible --- diff --git a/cmd/epoint-client/epoint-client.go b/cmd/epoint-client/epoint-client.go index 7ddd923..21541c3 100644 --- a/cmd/epoint-client/epoint-client.go +++ b/cmd/epoint-client/epoint-client.go @@ -8,6 +8,7 @@ import ( "epoint/document" "epoint/key" "epoint/store" + "flag" "fmt" "io" "log" @@ -17,23 +18,30 @@ import ( "strconv" ) -// TODO: store documents, query document by id, easy submit +// TODO: create certby/ draftby/ +// TODO: query document by id, easy submit var db *store.Conn -const usage = `usage: ./epoint-client [i|h|d|r|q|s|v|c] [args..] < [seed|document] -server is http://localhost:8080 by default +const usage = `usage: ./epoint-client [flags] [i|h|d|r|q|s|v|c] [args..] < [seed|document] +flags - one of -help, -dir rootdir, -server serveraddr 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 r - sign raw document, use seed for signing key, args: document -q - query document, args: k|d|c id [server] -s - submit a (key|draft|cert) document, args: k|d|c [server] +q - query document, args: k|d|c id +s - submit a (key|draft|cert) document, args: k|d|c v - verify a document (prints body of the document if ok) -c - connect to server and get server key, args: [server] +c - connect to server and get server key ` +var ( + rootdir = flag.String("dir", "", "root dir for storage, empty sets a sane default") + server = flag.String("server", "http://localhost:8080", "server address") + // TODO: verbose +) + func rnd(n int) (r []byte, err error) { r = make([]byte, n) _, err = io.ReadFull(rand.Reader, r) @@ -223,7 +231,7 @@ func q(cmd, id, server string) (err error) { log.Printf("found %s in local store", id) return } - resp, err := http.Get(server+"/"+k+"/"+id) + resp, err := http.Get(server + "/" + k + "/" + id) if err != nil { return } @@ -376,6 +384,10 @@ func s(d []byte, cmd, server string) (err error) { if err != nil { return } + if cmd == "k" { + // TODO: signed reply? + return + } cert, s, err := document.Parse(b) if err != nil { return @@ -462,7 +474,7 @@ func c(server string) (err error) { // TODO: commmon code with server func initstore(dir string) (db *store.Conn, err error) { - log.Printf("using root dir %s", dir) + // log.Printf("using root dir %s", dir) db, err = store.Open(dir) if err != nil { return @@ -487,11 +499,11 @@ func initstore(dir string) (db *store.Conn, err error) { if err != nil { return } - err = db.Ensure("certby/key") + err = db.Ensure("certby/key.issuer") if err != nil { return } - err = db.Ensure("certby/key.serial") + err = db.Ensure("certby/key.issuer.serial") if err != nil { return } @@ -529,67 +541,62 @@ func read() []byte { return b } +func cmdargs() (cmd string, args []string) { + a := flag.Args() + cmd = a[0] + args = a[1:] + return +} + func main() { - if len(os.Args) < 2 { + flag.Parse() + if flag.NArg() < 1 { log.Fatal(usage) } + cmd, args := cmdargs() var err error - db, err = initstore(storedir()) - server := "http://localhost:8080" - switch os.Args[1] { + dir := *rootdir + if dir == "" { + dir = storedir() + } + db, err = initstore(dir) + switch cmd { case "h", "i": - if len(os.Args) != 3 { + if len(args) != 1 { log.Fatal(usage) } - err = k(read(), os.Args[1], os.Args[2]) + err = k(read(), cmd, args[0]) case "d": - if len(os.Args) != 4 { + if len(args) != 2 { log.Fatal(usage) } - err = d(read(), os.Args[2], os.Args[3]) + err = d(read(), args[0], args[1]) case "r": - if len(os.Args) != 3 { + if len(os.Args) != 1 { log.Fatal(usage) } - err = raw(read(), os.Args[2]) + err = raw(read(), args[0]) 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 { + if len(args) != 2 { log.Fatal(usage) } - err = q(cmd, id, server) + err = q(args[0], args[1], *server) 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 { + if len(args) != 1 { log.Fatal(usage) } - err = s(read(), cmd, server) + err = s(read(), args[0], *server) case "v": - if len(os.Args) != 2 { + if len(args) != 0 { log.Fatal(usage) } err = v(read()) case "c": - if len(os.Args) == 3 { - server = os.Args[2] - } else if len(os.Args) != 2 { + if len(args) != 0 { log.Fatal(usage) } - err = c(server) + err = c(*server) default: log.Fatal(usage) } diff --git a/cmd/epoint-server/epoint-server.go b/cmd/epoint-server/epoint-server.go index 6e3bccd..292d8bd 100644 --- a/cmd/epoint-server/epoint-server.go +++ b/cmd/epoint-server/epoint-server.go @@ -1,18 +1,21 @@ package main import ( + "crypto/openpgp" "epoint/key" "epoint/server" + "flag" "fmt" + "io/ioutil" "log" "net/http" "os" ) -const ( - addr = ":8080" - rootdir = "docroot" - seckey = "./key.sec" +var ( + rootdir = flag.String("dir", "docroot", "root dir for storage") + seckey = flag.String("key", "", "secret key, empty means generated") + addr = flag.String("addr", ":8080", "address to listen on") ) // todo: http header limit: 64K, body limit: 64K @@ -70,7 +73,7 @@ func submitHandler(w http.ResponseWriter, r *http.Request) { msg := fmt.Sprintf("add keys failed: %s", err) httpError(w, 404, msg) } else { - w.Write([]byte("ok\nTODO: create cert 1 here?")) + w.Write([]byte("ok\n")) } default: msg := fmt.Sprintf("expected key, draft or debit param, got: %s", httpReq(r)) @@ -78,19 +81,31 @@ func submitHandler(w http.ResponseWriter, r *http.Request) { } } +func initkey(path string) (sk *openpgp.Entity, err error) { + if path == "" { + // TODO: serious keygen, server key depends on time + return key.Server([]byte("secret seed")) + } + d, err := ioutil.ReadFile(path) + if err != nil { + return + } + return key.Parse(d) +} + func main() { - // TODO: serious keygen - serverkey, err := key.Server([]byte("secret seed")) + flag.Parse() + serverkey, err := initkey(*seckey) if err != nil { log.Fatal(err) } - err = server.Init(rootdir, serverkey) + err = server.Init(*rootdir, serverkey) if err != nil { log.Fatal(err) } // TODO: url from key - f, err := os.Create(rootdir + "/form.html") + f, err := os.Create(*rootdir + "/form.html") if err != nil { log.Fatal(err) } @@ -114,12 +129,12 @@ where 'name' is 'key', 'draft' or 'debit'. _ = f.Close() // queries - http.Handle("/", http.FileServer(http.Dir(rootdir))) + http.Handle("/", http.FileServer(http.Dir(*rootdir))) // actions // withdraw, draw, deposit, process, clear http.HandleFunc("/submit", submitHandler) - log.Printf("start service on %s, server key id: %s\n", addr, key.Id(serverkey)) - log.Fatal(http.ListenAndServe(addr, nil)) + log.Printf("start service on %s, server key id: %s\n", *addr, key.Id(serverkey)) + log.Fatal(http.ListenAndServe(*addr, nil)) } diff --git a/pkg/document/document.go b/pkg/document/document.go index b82b167..c7803ca 100644 --- a/pkg/document/document.go +++ b/pkg/document/document.go @@ -679,7 +679,7 @@ func parseDate(s string) (int64, error) { } func formatDate(i int64) string { - return time.Unix(i,0).UTC().Format(time.RFC3339) + return time.Unix(i, 0).UTC().Format(time.RFC3339) } func getLine(data []byte) (line, rest []byte) { diff --git a/pkg/key/key.go b/pkg/key/key.go index 19537d6..a32c998 100644 --- a/pkg/key/key.go +++ b/pkg/key/key.go @@ -20,6 +20,7 @@ import ( "crypto" "crypto/dsa" "crypto/openpgp" + "crypto/openpgp/armor" "crypto/openpgp/packet" "crypto/rand" "crypto/sha1" @@ -120,14 +121,61 @@ func Parse(d []byte) (e *openpgp.Entity, err error) { return } +// Prepare self signatures of private key +func SelfSign(e *openpgp.Entity) (err error) { + // TODO: maybe e.Serialize should do this internally + if e.PrivateKey == nil { + err = fmt.Errorf("SelfSign: not a private key") + return + } + for _, ident := range e.Identities { + err = ident.SelfSignature.SignUserId(rand.Reader, ident.UserId.Id, e.PrimaryKey, e.PrivateKey) + if err != nil { + return + } + } + for _, subkey := range e.Subkeys { + err = subkey.Sig.SignKey(rand.Reader, subkey.PublicKey, e.PrivateKey) + if err != nil { + return + } + } + return +} + +// Format into an armored public key +func Format(e *openpgp.Entity) (d []byte, err error) { + b := new(bytes.Buffer) + w, err := armor.Encode(b, openpgp.PublicKeyType, nil) + if err != nil { + return + } + err = e.Serialize(w) + if err != nil { + return + } + err = w.Close() + if err != nil { + return + } + _, err = b.Write([]byte{'\n'}) + if err != nil { + return + } + d = b.Bytes() + return +} + // Issuer generates a key for obligation issuer clients from random seed r func Issuer(r []byte, denomination string) (e *openpgp.Entity, err error) { - return New(DsaKey(r), time.Unix(0,0), "Issuer", denomination, "") + return New(DsaKey(r), time.Unix(0, 0), "Issuer", denomination, "") } + // Holder generates a key for obligation holder clients from random seed r func Holder(r []byte, issuer, denomination string) (e *openpgp.Entity, err error) { - return New(DsaKey(r), time.Unix(0,0), "Holder of "+issuer, denomination, "") + return New(DsaKey(r), time.Unix(0, 0), "Holder of "+issuer, denomination, "") } + // Server generates a key for the server from random seed r func Server(r []byte) (e *openpgp.Entity, err error) { return New(DsaKey(r), time.Now(), "Server", "", "") diff --git a/pkg/key/key_test.go b/pkg/key/key_test.go index 65651a8..a2c1426 100644 --- a/pkg/key/key_test.go +++ b/pkg/key/key_test.go @@ -35,6 +35,20 @@ func TestKey(t *testing.T) { } else { testSignAndVerify(t, priv) } + + // serialize + err = SelfSign(priv) + if err != nil { + t.Errorf("SelfSign failed: %s", err) + } + d, err := Format(priv) + if err != nil { + t.Errorf("Format failed: %s", err) + } + _, err = Parse(d) + if err != nil { + t.Errorf("Parse failed: %s", err) + } } func TestId(t *testing.T) { diff --git a/pkg/server/server.go b/pkg/server/server.go index ab41250..8f11332 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -5,11 +5,11 @@ package server import ( "bytes" "crypto/openpgp" - "crypto/rand" "epoint/document" "epoint/key" "epoint/store" "fmt" + "log" "time" ) @@ -33,7 +33,7 @@ type work struct { var db *store.Conn var serverkey *openpgp.Entity var newchan chan *work -var delchan chan string +var delchan chan *work type worklist struct { head *work @@ -64,31 +64,19 @@ func popwork(ws *worklist) *work { func setserverkey(e *openpgp.Entity) (err error) { serverkey = e - - // TODO: maybe Serialize should do this internally - for _, ident := range e.Identities { - err = ident.SelfSignature.SignUserId(rand.Reader, ident.UserId.Id, e.PrimaryKey, e.PrivateKey) - if err != nil { - return - } - } - for _, subkey := range e.Subkeys { - err = subkey.Sig.SignKey(rand.Reader, subkey.PublicKey, e.PrivateKey) - if err != nil { - return - } + err = key.SelfSign(e) + if err != nil { + return } - - b := new(bytes.Buffer) - err = e.Serialize(b) + d, err := key.Format(e) if err != nil { return } - err = db.Set("key", key.Id(e), b.Bytes()) + err = db.Set("key", key.Id(e), d) if err != nil { return } - err = db.Set("", "serverkey", b.Bytes()) + err = db.Set("", "serverkey", d) return } @@ -263,6 +251,7 @@ func addDraft(d []byte, signed *document.Signed, draft *document.Draft) (c []byt w.signed = signed w.draft = draft w.sync = make(chan int) + log.Printf("add draft work: %s", w.account) newchan <- w <-w.sync return w.out, w.err @@ -276,16 +265,19 @@ func addDebit(d []byte, signed *document.Signed, cert *document.DebitCert) (c [] w.signed = signed w.debit = cert w.sync = make(chan int) + log.Printf("add debit work: %s", w.account) newchan <- w <-w.sync return w.out, w.err } func dispatch() { + log.Printf("start dispatch") works := make(map[string]*worklist) for { select { case w := <-newchan: + log.Printf("queue work: %s", w.account) ws := works[w.account] if ws == nil { // TODO: unnecessary alloc @@ -294,19 +286,21 @@ func dispatch() { } else { pushwork(ws, w) } - case account := <-delchan: - ws := works[account] - w := popwork(ws) - if w == nil { - delete(works, account) + case w := <-delchan: + log.Printf("unqueue work: %s", w.account) + ws := works[w.account] + wnext := popwork(ws) + if wnext == nil { + delete(works, w.account) } else { - go handle(w) + go handle(wnext) } } } } func handle(w *work) { + log.Printf("start work: %s", w.account) if w.debit != nil { handleDebit(w) } else if w.draft != nil { @@ -314,7 +308,8 @@ func handle(w *work) { } else { panic("unreachable") } - delchan <- w.account + log.Printf("finish work: %s outlen: %d, errtype: %T", w.account, len(w.out), w.err) + delchan <- w w.sync <- 0 } @@ -358,8 +353,8 @@ func handleDraft(w *work) { if err != nil { return } - certid := document.Id(signed) w.out = c + certid := document.Id(signed) err = db.Set("cert", certid, c) if err != nil { // internal error @@ -417,6 +412,7 @@ func handleDebit(w *work) { // internal error return } + w.out = c certid := document.Id(signed) err = db.Set("cert", certid, c) if err != nil { @@ -607,6 +603,8 @@ func Init(rootdir string, sk *openpgp.Entity) (err error) { if err != nil { return } + newchan = make(chan *work) + delchan = make(chan *work) go dispatch() return }