"epoint/document"
"epoint/key"
"epoint/store"
+ "flag"
"fmt"
"io"
"log"
"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)
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
}
if err != nil {
return
}
+ if cmd == "k" {
+ // TODO: signed reply?
+ return
+ }
cert, s, err := document.Parse(b)
if err != nil {
return
// 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
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
}
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)
}
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
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))
}
}
+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)
}
_ = 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))
}
}
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) {
"crypto"
"crypto/dsa"
"crypto/openpgp"
+ "crypto/openpgp/armor"
"crypto/openpgp/packet"
"crypto/rand"
"crypto/sha1"
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", "", "")
} 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) {
import (
"bytes"
"crypto/openpgp"
- "crypto/rand"
"epoint/document"
"epoint/key"
"epoint/store"
"fmt"
+ "log"
"time"
)
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
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
}
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
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
} 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 {
} 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
}
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
// internal error
return
}
+ w.out = c
certid := document.Id(signed)
err = db.Set("cert", certid, c)
if err != nil {
if err != nil {
return
}
+ newchan = make(chan *work)
+ delchan = make(chan *work)
go dispatch()
return
}