--- /dev/null
+package main
+
+import (
+ "bufio"
+ "crypto/openpgp"
+ "crypto/openpgp/armor"
+ "flag"
+ "io"
+ "log"
+ "os"
+)
+
+var dearmor = flag.Bool("d", false, "dearmor binary key")
+var publiconly = flag.Bool("p", false, "ouput public part only, even if input is a private key")
+
+func serialize(w io.Writer, e *openpgp.Entity, publiconly bool) (err error) {
+ if publiconly {
+ err = e.Serialize(w)
+ } else {
+ err = e.SerializePrivate(w)
+ }
+ return
+}
+
+func code(e *openpgp.Entity, arm bool) (err error) {
+ pub := e.PrivateKey == nil || *publiconly
+ header := openpgp.PublicKeyType
+ if !pub {
+ header = openpgp.PrivateKeyType
+ }
+ out := bufio.NewWriter(os.Stdout)
+ if arm {
+ w, err1 := armor.Encode(out, header, nil)
+ err = err1
+ if err != nil {
+ return
+ }
+ err = serialize(w, e, pub)
+ if err != nil {
+ return
+ }
+ err = w.Close()
+ if err != nil {
+ return
+ }
+ _, err = out.Write([]byte{'\n'})
+ if err != nil {
+ return
+ }
+ } else {
+ err = serialize(out, e, pub)
+ if err != nil {
+ return
+ }
+ }
+ err = out.Flush()
+ return
+}
+
+func main() {
+ flag.Parse()
+ var elist openpgp.EntityList
+ var err error
+ in := bufio.NewReader(os.Stdin)
+ if *dearmor {
+ elist, err = openpgp.ReadArmoredKeyRing(in)
+ } else {
+ elist, err = openpgp.ReadKeyRing(in)
+ }
+ if err != nil {
+ log.Fatal(err)
+ }
+ if len(elist) != 1 {
+ log.Fatal("expected a single key")
+ }
+ err = code(elist[0], !*dearmor)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return
+}
"crypto/rand"
"epoint/document"
"epoint/key"
+ "epoint/store"
"fmt"
"io"
"log"
"strconv"
)
-const usage = `usage: ./epoint-client [k|d|s|v] [args..] < [seed|document]
+// TODO: store documents, query store (keys), 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
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]
+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) {
if err != nil {
return
}
- 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.Close()
+ e, err := key.Holder(r, "", "")
if err != nil {
return
}
- _, err = os.Stdout.Write([]byte{'\n'})
- 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
err = fmt.Errorf("unknown submit command: %s", cmd)
return
}
- resp, err := http.PostForm(server, url.Values{k: {string(d)}})
+ resp, err := http.PostForm(server+"/submit", url.Values{k: {string(d)}})
if err != nil {
return
}
if resp.StatusCode != 200 {
log.Printf("request failed: %s\n", resp.Status)
}
+ // TODO: store result
_, err = io.Copy(os.Stdout, resp.Body)
if err != nil {
return
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("", "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() {
}
var err error
+ db, err = initstore(storedir())
+ server := "http://localhost:8080"
switch os.Args[1] {
case "k":
issuer := ""
}
err = d(read(), os.Args[2], os.Args[3])
case "s":
- server := "http://localhost:8080"
cmd := ""
if len(os.Args) == 4 {
cmd = os.Args[2]
} 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)