8f113328d144dec7d575d4bafa86be62fd126e3c
[epoint] / pkg / server / server.go
1 package server
2
3 // main transfer logic
4
5 import (
6         "bytes"
7         "crypto/openpgp"
8         "epoint/document"
9         "epoint/key"
10         "epoint/store"
11         "fmt"
12         "log"
13         "time"
14 )
15
16 // TODO: do in docs?
17 const IntLimit = 1e15
18
19 // transaction: either draft or debit cert
20 type work struct {
21         next    *work
22         docid   string
23         account string
24         in      []byte
25         out     []byte
26         err     error
27         signed  *document.Signed
28         draft   *document.Draft
29         debit   *document.DebitCert
30         sync    chan int
31 }
32
33 var db *store.Conn
34 var serverkey *openpgp.Entity
35 var newchan chan *work
36 var delchan chan *work
37
38 type worklist struct {
39         head *work
40         tail *work
41 }
42
43 func pushwork(ws *worklist, w *work) {
44         if ws.tail == nil {
45                 ws.head = w
46                 ws.tail = w
47         } else {
48                 ws.tail.next = w
49                 ws.tail = w
50         }
51 }
52
53 func popwork(ws *worklist) *work {
54         w := ws.head
55         if w == nil {
56                 return nil
57         }
58         ws.head = w.next
59         if ws.head == nil {
60                 ws.tail = nil
61         }
62         return w
63 }
64
65 func setserverkey(e *openpgp.Entity) (err error) {
66         serverkey = e
67         err = key.SelfSign(e)
68         if err != nil {
69                 return
70         }
71         d, err := key.Format(e)
72         if err != nil {
73                 return
74         }
75         err = db.Set("key", key.Id(e), d)
76         if err != nil {
77                 return
78         }
79         err = db.Set("", "serverkey", d)
80         return
81 }
82
83 func GetKeys(fpr string) (es openpgp.EntityList, err error) {
84         b, err := db.Get("key", fpr)
85         if err != nil {
86                 return
87         }
88         es, err = openpgp.ReadKeyRing(bytes.NewBuffer(b))
89         if err != nil {
90                 // internal error: pubkey cannot be parsed
91                 return
92         }
93         return
94 }
95
96 func AddKeys(d []byte) (err error) {
97         entities, err := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
98         if err != nil {
99                 return
100         }
101         // TODO: allow multiple key uploads at once?
102         if len(entities) > 100 {
103                 err = fmt.Errorf("expected at most 100 keys; got %d", len(entities))
104                 return
105         }
106         for _, e := range entities {
107                 // TODO: various checks..
108                 // TODO: collect errors instead of aborting addkeys
109                 isIssuer, issuer, denom, err1 := key.Check(e)
110                 err = err1
111                 if err != nil {
112                         return
113                 }
114                 if !isIssuer {
115                         es, err1 := GetKeys(issuer)
116                         err = err1
117                         if err != nil {
118                                 return
119                         }
120                         ok, _, den, err1 := key.Check(es[0])
121                         err = err1
122                         if err != nil {
123                                 // internal error
124                                 return
125                         }
126                         if !ok || den != denom {
127                                 err = fmt.Errorf("Issuer key check failed")
128                                 return
129                         }
130                 }
131                 b := new(bytes.Buffer)
132                 err = e.Serialize(b)
133                 if err != nil {
134                         return
135                 }
136                 fpr := key.Id(e)
137                 err = db.Insert("key", fpr, b.Bytes())
138                 if err != nil {
139                         return
140                 }
141                 err = db.Append("keysby/64", fpr[len(fpr)-16:], []byte(fpr))
142                 if err != nil {
143                         return
144                 }
145                 err = db.Append("keysby/32", fpr[len(fpr)-8:], []byte(fpr))
146                 if err != nil {
147                         return
148                 }
149         }
150         return
151 }
152
153 // Get cert through the named store
154 func GetCert(name, id string) (d []byte, err error) {
155         certid, err := db.Get(name, id)
156         if err != nil {
157                 // ok if notfound
158                 return
159         }
160         d, err = db.Get("cert", string(certid))
161         if err != nil {
162                 // internal error: cert is not available
163                 return
164         }
165         return
166 }
167
168 func EvalDraft(d []byte) (c []byte, err error) {
169         iv, signed, err := document.Parse(d)
170         if err != nil {
171                 return
172         }
173         draft, ok := iv.(*document.Draft)
174         if !ok {
175                 err = fmt.Errorf("EvalDraft: expected a draft document")
176                 return
177         }
178         k, err := db.Get("key", draft.Drawer)
179         if err != nil {
180                 return
181         }
182         // TODO: key.Parse
183         kr, err := openpgp.ReadKeyRing(bytes.NewBuffer(k))
184         if err != nil {
185                 // internal error: pubkey cannot be parsed
186                 return
187         }
188         err = document.Verify(signed, kr)
189         if err != nil {
190                 return
191         }
192         //      _, issuer, denom, err := key.Check(kr[0])
193         //      if err != nil {
194         //              return
195         //      }
196         // TODO: do various format checks (AuthorizedBy check etc)
197         if draft.Amount <= 0 || draft.Amount >= IntLimit {
198                 err = fmt.Errorf("draft amount is invalid: %d", draft.Amount)
199                 return
200         }
201         k, err = db.Get("key", draft.Beneficiary)
202         if err != nil {
203                 return
204         }
205         kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
206         if err != nil {
207                 // internal error: pubkey cannot be parsed
208                 return
209         }
210         return addDraft(d, signed, draft)
211 }
212
213 func EvalDebitCert(d []byte) (c []byte, err error) {
214         iv, signed, err := document.Parse(d)
215         if err != nil {
216                 return
217         }
218         cert, ok := iv.(*document.DebitCert)
219         if !ok {
220                 err = fmt.Errorf("ParseDebitCert: expected a debit docuent")
221                 return
222         }
223
224         kr := openpgp.EntityList{serverkey}
225         if key.Id(serverkey) != cert.AuthorizedBy {
226                 // TODO: ...
227                 k := []byte(nil)
228                 k, err = db.Get("key", cert.AuthorizedBy)
229                 if err != nil {
230                         return
231                 }
232                 kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
233                 if err != nil {
234                         // internal error: pubkey cannot be parsed
235                         return
236                 }
237         }
238         // must clean up to make sure the hash is ok
239         err = document.Verify(signed, kr)
240         if err != nil {
241                 return
242         }
243         return addDebit(d, signed, cert)
244 }
245
246 func addDraft(d []byte, signed *document.Signed, draft *document.Draft) (c []byte, err error) {
247         w := new(work)
248         w.docid = document.Id(signed)
249         w.account = fmt.Sprintf("%s.%s", draft.Drawer, draft.Issuer)
250         w.in = d
251         w.signed = signed
252         w.draft = draft
253         w.sync = make(chan int)
254         log.Printf("add draft work: %s", w.account)
255         newchan <- w
256         <-w.sync
257         return w.out, w.err
258 }
259
260 func addDebit(d []byte, signed *document.Signed, cert *document.DebitCert) (c []byte, err error) {
261         w := new(work)
262         w.docid = document.Id(signed)
263         w.account = fmt.Sprintf("%s.%s", cert.Holder, cert.Issuer)
264         w.in = d
265         w.signed = signed
266         w.debit = cert
267         w.sync = make(chan int)
268         log.Printf("add debit work: %s", w.account)
269         newchan <- w
270         <-w.sync
271         return w.out, w.err
272 }
273
274 func dispatch() {
275         log.Printf("start dispatch")
276         works := make(map[string]*worklist)
277         for {
278                 select {
279                 case w := <-newchan:
280                         log.Printf("queue work: %s", w.account)
281                         ws := works[w.account]
282                         if ws == nil {
283                                 // TODO: unnecessary alloc
284                                 works[w.account] = new(worklist)
285                                 go handle(w)
286                         } else {
287                                 pushwork(ws, w)
288                         }
289                 case w := <-delchan:
290                         log.Printf("unqueue work: %s", w.account)
291                         ws := works[w.account]
292                         wnext := popwork(ws)
293                         if wnext == nil {
294                                 delete(works, w.account)
295                         } else {
296                                 go handle(wnext)
297                         }
298                 }
299         }
300 }
301
302 func handle(w *work) {
303         log.Printf("start work: %s", w.account)
304         if w.debit != nil {
305                 handleDebit(w)
306         } else if w.draft != nil {
307                 handleDraft(w)
308         } else {
309                 panic("unreachable")
310         }
311         log.Printf("finish work: %s outlen: %d, errtype: %T", w.account, len(w.out), w.err)
312         delchan <- w
313         w.sync <- 0
314 }
315
316 func handleDraft(w *work) {
317         nonce := fmt.Sprintf("%s.%s", w.account, w.draft.Nonce)
318         oldid, err := db.Get("draftby/key.issuer.nonce", nonce)
319         if err == nil {
320                 if string(oldid) != w.docid {
321                         w.err = fmt.Errorf("draft nonce is not unique (see draft %s)", oldid)
322                 } else {
323                         w.out, w.err = GetCert("certby/draft", w.docid)
324                 }
325                 return
326         } else if _, ok := err.(store.NotFoundError); !ok {
327                 w.err = err
328                 return
329         }
330
331         err = db.Begin(w.docid)
332         if err != nil {
333                 w.err = err
334                 return
335         }
336         err = db.Set("draft", w.docid, w.in)
337         if err != nil {
338                 w.err = err
339                 return
340         }
341         err = db.Set("draftby/key.issuer.nonce", nonce, []byte(w.docid))
342         if err != nil {
343                 w.err = err
344                 return
345         }
346         cert, err := newDebitCert(w)
347         if err != nil {
348                 // probably client error
349                 db.End(w.docid)
350                 return
351         }
352         c, signed, err := document.Format(cert, serverkey)
353         if err != nil {
354                 return
355         }
356         w.out = c
357         certid := document.Id(signed)
358         err = db.Set("cert", certid, c)
359         if err != nil {
360                 // internal error
361                 return
362         }
363         err = db.Set("certby/draft", w.docid, []byte(certid))
364         if err != nil {
365                 // internal error
366                 return
367         }
368         // TODO: append?
369         err = db.Set("certby/key.issuer.serial", fmt.Sprintf("%s.%09d", w.account, cert.Serial), []byte(certid))
370         if err != nil {
371                 // internal error
372                 return
373         }
374         err = db.Set("certby/key.issuer", w.account, []byte(certid))
375         if err != nil {
376                 // internal error
377                 return
378         }
379         // TODO: run journal cleanup in case of client errors
380         err = db.End(w.docid)
381         return
382 }
383
384 func handleDebit(w *work) {
385         c, err := GetCert("certby/debit", w.docid)
386         if err == nil {
387                 w.out = c
388                 return
389         } else if _, ok := err.(store.NotFoundError); !ok {
390                 // internal error
391                 w.err = err
392                 return
393         }
394         err = db.Begin(w.docid)
395         if err != nil {
396                 w.err = err
397                 return
398         }
399         err = db.Set("cert", w.docid, w.in)
400         if err != nil {
401                 w.err = err
402                 return
403         }
404         // TODO: check pubkey etc
405         cert, err := newCreditCert(w)
406         if err != nil {
407                 // internal error
408                 return
409         }
410         c, signed, err := document.Format(cert, serverkey)
411         if err != nil {
412                 // internal error
413                 return
414         }
415         w.out = c
416         certid := document.Id(signed)
417         err = db.Set("cert", certid, c)
418         if err != nil {
419                 // internal error
420                 return
421         }
422         err = db.Set("certby/debit", w.docid, []byte(certid))
423         if err != nil {
424                 // internal error
425                 return
426         }
427         // TODO: append?
428         err = db.Set("certby/key.issuer.serial", fmt.Sprintf("%s.%09d", w.account, cert.Serial), []byte(certid))
429         if err != nil {
430                 // internal error
431                 return
432         }
433         err = db.Set("certby/key.issuer", w.account, []byte(certid))
434         if err != nil {
435                 // internal error
436                 return
437         }
438         db.End(w.docid)
439         return
440 }
441
442 func newDebitCert(w *work) (*document.DebitCert, error) {
443         cert := new(document.DebitCert)
444         cert.Holder = w.draft.Drawer
445         cert.Date = time.Now().Unix()
446         cert.Denomination = "epoint"
447         cert.Issuer = w.draft.Issuer
448         cert.AuthorizedBy = w.draft.AuthorizedBy
449         cert.Difference = -w.draft.Amount
450         cert.Draft = w.docid
451         cert.Beneficiary = w.draft.Beneficiary
452
453         oid, err := db.Get("certby/key.issuer", w.account)
454         oldcertid := string(oid)
455         if err != nil {
456                 // first cert: drawer is issuer
457                 if w.draft.Drawer != w.draft.Issuer {
458                         return nil, fmt.Errorf("drawer must be the issuer when drawing an empty account (%s != %s)", w.draft.Drawer, w.draft.Issuer)
459                 }
460                 cert.Serial = 1
461                 cert.Balance = cert.Difference
462                 cert.LastDebitSerial = 0
463                 cert.LastCreditSerial = 0
464         } else {
465                 d, err := db.Get("cert", oldcertid)
466                 if err != nil {
467                         return nil, err
468                 }
469                 iv, _, err := document.Parse(d)
470                 if err != nil {
471                         // internal error
472                         return nil, err
473                 }
474                 // TODO: this is a hack
475                 oldcert, err := document.ToCert(iv)
476                 if err != nil {
477                         // internal error
478                         return nil, err
479                 }
480                 // TODO: sanity checks? oldcert.Holder == draft.Drawer
481                 cert.Serial = oldcert.Serial + 1
482                 cert.Balance = oldcert.Balance + cert.Difference
483                 if cert.Balance <= -IntLimit {
484                         return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
485                 }
486                 if oldcert.Balance > 0 && cert.Balance < 0 {
487                         return nil, fmt.Errorf("insufficient funds: %d", oldcert.Balance)
488                 }
489                 cert.LastDebitSerial = oldcert.LastDebitSerial
490                 cert.LastCreditSerial = oldcert.LastCreditSerial
491                 if _, ok := iv.(*document.DebitCert); ok {
492                         cert.LastDebitSerial = oldcert.Serial
493                 } else {
494                         cert.LastCreditSerial = oldcert.Serial
495                 }
496                 cert.LastCert = &oldcertid
497         }
498         return cert, nil
499 }
500
501 func newCreditCert(w *work) (*document.CreditCert, error) {
502         cert := new(document.CreditCert)
503         // TODO: get from old cert instead?
504         cert.Holder = w.debit.Beneficiary
505         cert.Date = time.Now().Unix()
506         // TODO: get these from the cert holder pubkey
507         cert.Denomination = "epoint"
508         cert.Issuer = w.debit.Issuer
509         cert.AuthorizedBy = w.debit.AuthorizedBy // TODO: draft vs dcert vs serverside decision
510         cert.Difference = -w.debit.Difference
511         cert.Draft = w.debit.Draft
512         cert.Drawer = w.debit.Holder
513         cert.DebitCert = w.docid
514
515         oid, err := db.Get("certby/key", w.debit.Beneficiary)
516         oldcertid := string(oid)
517         if err != nil {
518                 // this is the first cert
519                 cert.Serial = 1
520                 cert.Balance = cert.Difference
521                 cert.LastDebitSerial = 0
522                 cert.LastCreditSerial = 0
523         } else {
524                 d, err := db.Get("cert", oldcertid)
525                 if err != nil {
526                         // internal error
527                         return nil, err
528                 }
529                 iv, _, err := document.Parse(d)
530                 if err != nil {
531                         // internal error
532                         return nil, err
533                 }
534                 // TODO: this is a hack
535                 oldcert, err := document.ToCert(iv)
536                 if err != nil {
537                         // internal error
538                         return nil, err
539                 }
540                 cert.Serial = oldcert.Serial + 1
541                 cert.Balance = oldcert.Balance + cert.Difference
542                 if cert.Balance >= IntLimit {
543                         return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
544                 }
545                 cert.LastDebitSerial = oldcert.LastDebitSerial
546                 cert.LastCreditSerial = oldcert.LastCreditSerial
547                 if _, ok := iv.(*document.DebitCert); ok {
548                         cert.LastDebitSerial = oldcert.Serial
549                 } else {
550                         cert.LastCreditSerial = oldcert.Serial
551                 }
552                 cert.LastCert = &oldcertid
553         }
554         return cert, nil
555 }
556
557 func Init(rootdir string, sk *openpgp.Entity) (err error) {
558         db, err = store.Open(rootdir)
559         if err != nil {
560                 return
561         }
562         err = db.Ensure("key")
563         if err != nil {
564                 return
565         }
566         err = db.Ensure("cert")
567         if err != nil {
568                 return
569         }
570         err = db.Ensure("draft")
571         if err != nil {
572                 return
573         }
574         err = db.Ensure("certby/draft")
575         if err != nil {
576                 return
577         }
578         err = db.Ensure("certby/debit")
579         if err != nil {
580                 return
581         }
582         err = db.Ensure("certby/key.issuer")
583         if err != nil {
584                 return
585         }
586         err = db.Ensure("certby/key.issuer.serial")
587         if err != nil {
588                 return
589         }
590         err = db.Ensure("draftby/key.issuer.nonce")
591         if err != nil {
592                 return
593         }
594         err = db.Ensure("keysby/64")
595         if err != nil {
596                 return
597         }
598         err = db.Ensure("keysby/32")
599         if err != nil {
600                 return
601         }
602         err = setserverkey(sk)
603         if err != nil {
604                 return
605         }
606         newchan = make(chan *work)
607         delchan = make(chan *work)
608         go dispatch()
609         return
610 }