428151937c44847b4b23efebbca3ee9c6147b480
[epoint] / cmd / epoint-client / epoint-client.go
1 package main
2
3 import (
4         "bytes"
5         "crypto/openpgp"
6         "crypto/openpgp/armor"
7         "crypto/rand"
8         "epoint/document"
9         "epoint/key"
10         "epoint/store"
11         "fmt"
12         "io"
13         "log"
14         "net/http"
15         "net/url"
16         "os"
17         "strconv"
18 )
19
20 // TODO: store documents, query store (keys), easy submit
21
22 var db *store.Conn
23
24 const usage = `usage: ./epoint-client [k|d|s|v|c] [args..] < [seed|document]
25 server is http://localhost:8080 by default
26
27 k - make key, use seed for generation, args: [issuer] denomination
28 d - make draft, use seed for signing key, args: targetid value
29 s - submit a (key|draft|cert) document, args: k|d|c [server]
30 v - verify a document (prints body of the document if ok)
31 c - connect to server and get server key, args: [server]
32 `
33
34 func rnd(n int) (r []byte, err error) {
35         r = make([]byte, n)
36         _, err = io.ReadFull(rand.Reader, r)
37         return
38 }
39
40 func k(r []byte, issuer, denom string) (err error) {
41         var e *openpgp.Entity
42
43         if issuer == "" {
44                 e, err = key.Issuer(r, denom)
45         } else {
46                 e, err = key.Holder(r, issuer, denom)
47         }
48         if err != nil {
49                 return
50         }
51         log.Printf("generated key %s", key.Id(e))
52         d, err := db.Get("key", key.Id(e))
53         if err == nil {
54                 // TODO: issuer, denom check (remove keys from store if they are not sent to server?)
55                 log.Printf("key %s was found in store", key.Id(e))
56         } else {
57                 out := new(bytes.Buffer)
58                 w, err1 := armor.Encode(out, openpgp.PublicKeyType, nil)
59                 err = err1
60                 if err != nil {
61                         return
62                 }
63                 // TODO: maybe Serialize should do this internally
64                 for _, ident := range e.Identities {
65                         err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey)
66                         if err != nil {
67                                 return
68                         }
69                 }
70                 for _, subkey := range e.Subkeys {
71                         err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey)
72                         if err != nil {
73                                 return
74                         }
75                 }
76                 err = e.Serialize(w)
77                 if err != nil {
78                         return
79                 }
80                 err = w.Close()
81                 if err != nil {
82                         return
83                 }
84                 _, err = out.Write([]byte{'\n'})
85                 if err != nil {
86                         return
87                 }
88                 d = out.Bytes()
89                 err = db.Insert("key", key.Id(e), d)
90                 if err != nil {
91                         return
92                 }
93         }
94         _, err = os.Stdout.Write(d)
95         return
96 }
97
98 func d(r []byte, target, value string) (err error) {
99         v, err := strconv.Atoi64(value)
100         if err != nil {
101                 return
102         }
103         e, err := key.Holder(r, "", "")
104         if err != nil {
105                 return
106         }
107         log.Printf("drawer: %s", key.Id(e))
108         if key.Id(e) == target {
109                 err = fmt.Errorf("Drawer and beneficiary are the same")
110                 return
111         }
112         b, err := db.Get("key", key.Id(e))
113         if err != nil {
114                 return
115         }
116         e1, err := key.Parse(b)
117         if err != nil {
118                 return
119         }
120         _, issuer, denom, err := key.Check(e1)
121         if err != nil {
122                 return
123         }
124         // TODO: store server id as well?
125         b, err = db.Get("", "serverkey")
126         if err != nil {
127                 return
128         }
129         sk, err := key.Parse(b)
130         if err != nil {
131                 return
132         }
133         // TODO: check beneficiary (check value?)
134         draft := new(document.Draft)
135         draft.Drawer = key.Id(e)
136         draft.Beneficiary = target
137         draft.Amount = v
138         draft.Denomination = denom
139         draft.Issuer = issuer
140         draft.AuthorizedBy = key.Id(sk)
141         nonce, err := rnd(10)
142         if err != nil {
143                 return
144         }
145         draft.Nonce = fmt.Sprintf("%X", nonce)
146         s, _, err := document.Format(draft, e)
147         if err != nil {
148                 return
149         }
150         _, err = os.Stdout.Write(s)
151         return
152 }
153
154 func s(d []byte, cmd, server string) (err error) {
155         m := map[string]string{
156                 "k": "key",
157                 "d": "draft",
158                 "c": "debit",
159         }
160         k, ok := m[cmd]
161         if !ok {
162                 err = fmt.Errorf("unknown submit command: %s", cmd)
163                 return
164         }
165         resp, err := http.PostForm(server+"/submit", url.Values{k: {string(d)}})
166         if err != nil {
167                 return
168         }
169         if resp.StatusCode != 200 {
170                 log.Printf("request failed: %s\n", resp.Status)
171         }
172         // TODO: store result
173         _, err = io.Copy(os.Stdout, resp.Body)
174         if err != nil {
175                 return
176         }
177         err = resp.Body.Close()
178         return
179 }
180
181 func v(d []byte) (err error) {
182         // handle armored pubkey
183         if bytes.Index(d, []byte(openpgp.PublicKeyType)) >= 0 {
184                 es, err1 := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
185                 if err1 != nil {
186                         err = err1
187                         return
188                 }
189                 for _, e := range es {
190                         isIssuer, issuer, denom, err1 := key.Check(e)
191                         if err1 != nil {
192                                 if err != nil {
193                                         log.Println(err)
194                                 }
195                                 err = err1
196                                 continue
197                         }
198                         if isIssuer {
199                                 fmt.Println("Issuer key")
200                         } else {
201                                 fmt.Println("Holder key")
202                         }
203                         fmt.Printf("Issuer: %s\nDenomination: %s\nId: %s\n", issuer, denom, key.Id(e))
204                 }
205                 return
206         }
207         _, s, err := document.Parse(d)
208         if err != nil {
209                 return
210         }
211         _, err = os.Stdout.Write(s.Body)
212         return
213 }
214
215 func c(server string) (err error) {
216         resp, err := http.Get(server + "/serverkey")
217         if err != nil {
218                 return
219         }
220         if resp.StatusCode != 200 {
221                 log.Printf("request failed: %s\n", resp.Status)
222         }
223         b, err := readall(resp.Body)
224         if err != nil {
225                 return
226         }
227         err = resp.Body.Close()
228         if err != nil {
229                 return
230         }
231         e, err := key.Parse(b)
232         if err != nil {
233                 return
234         }
235         log.Printf("got server key %s", key.Id(e))
236         err = db.Set("", "serverkey", b)
237         return
238 }
239
240 // TODO: commmon code with server
241 func initstore(dir string) (db *store.Conn, err error) {
242         log.Printf("using root dir %s", dir)
243         db, err = store.Open(dir)
244         if err != nil {
245                 return
246         }
247         err = db.Ensure("key")
248         if err != nil {
249                 return
250         }
251         err = db.Ensure("cert")
252         if err != nil {
253                 return
254         }
255         err = db.Ensure("draft")
256         if err != nil {
257                 return
258         }
259         err = db.Ensure("certby/draft")
260         if err != nil {
261                 return
262         }
263         err = db.Ensure("certby/debit")
264         if err != nil {
265                 return
266         }
267         err = db.Ensure("certby/key")
268         if err != nil {
269                 return
270         }
271         err = db.Ensure("certby/key.serial")
272         if err != nil {
273                 return
274         }
275         return
276 }
277
278 func storedir() string {
279         dir := os.Getenv("HOME")
280         if dir == "" {
281                 dir = "/var/cache/epoint"
282         } else {
283                 dir += "/.epoint-client"
284         }
285         return dir
286 }
287
288 func readall(r io.Reader) ([]byte, error) {
289         b := make([]byte, 10000)
290         n, err := io.ReadFull(r, b)
291         if err != io.ErrUnexpectedEOF {
292                 if err == nil {
293                         err = fmt.Errorf("too much input")
294                 }
295                 return nil, err
296         }
297         return b[:n], nil
298 }
299
300 func read() []byte {
301         b, err := readall(os.Stdin)
302         if err != nil {
303                 log.Fatal(err)
304         }
305         return b
306 }
307
308 func main() {
309         if len(os.Args) < 2 {
310                 log.Fatal(usage)
311         }
312
313         var err error
314         db, err = initstore(storedir())
315         server := "http://localhost:8080"
316         switch os.Args[1] {
317         case "k":
318                 issuer := ""
319                 denom := ""
320                 if len(os.Args) == 4 {
321                         issuer = os.Args[2]
322                         denom = os.Args[3]
323                 } else if len(os.Args) == 3 {
324                         denom = os.Args[2]
325                 } else {
326                         log.Fatal(usage)
327                 }
328                 err = k(read(), issuer, denom)
329         case "d":
330                 if len(os.Args) != 4 {
331                         log.Fatal(usage)
332                 }
333                 err = d(read(), os.Args[2], os.Args[3])
334         case "s":
335                 cmd := ""
336                 if len(os.Args) == 4 {
337                         cmd = os.Args[2]
338                         server = os.Args[3]
339                 } else if len(os.Args) == 3 {
340                         cmd = os.Args[2]
341                 } else {
342                         log.Fatal(usage)
343                 }
344                 err = s(read(), cmd, server)
345         case "v":
346                 if len(os.Args) != 2 {
347                         log.Fatal(usage)
348                 }
349                 err = v(read())
350         case "c":
351                 if len(os.Args) == 3 {
352                         server = os.Args[2]
353                 } else if len(os.Args) != 2 {
354                         log.Fatal(usage)
355                 }
356                 err = c(server)
357         default:
358                 log.Fatal(usage)
359         }
360         if err != nil {
361                 log.Fatal(err)
362         }
363 }