various fixes to make testing possible
authornsz <nsz@port70.net>
Thu, 19 Jan 2012 14:22:10 +0000 (15:22 +0100)
committernsz <nsz@port70.net>
Thu, 19 Jan 2012 14:22:10 +0000 (15:22 +0100)
cmd/epoint-client/epoint-client.go
cmd/epoint-server/epoint-server.go
pkg/document/document.go
pkg/key/key.go
pkg/key/key_test.go
pkg/server/server.go

index 7ddd923..21541c3 100644 (file)
@@ -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)
        }
index 6e3bccd..292d8bd 100644 (file)
@@ -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))
 }
index b82b167..c7803ca 100644 (file)
@@ -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) {
index 19537d6..a32c998 100644 (file)
@@ -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", "", "")
index 65651a8..a2c1426 100644 (file)
@@ -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) {
index ab41250..8f11332 100644 (file)
@@ -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
 }