check signatures, addkeys, only drawer.nonce must be uniq
[epoint] / logic / logic.go
1 package logic
2
3 // main transfer logic
4
5 import (
6         "bytes"
7         "crypto/openpgp"
8         "epoint/document"
9         "epoint/dsakey"
10         "epoint/store"
11         "fmt"
12         "time"
13 )
14
15 // TODO: do in docs?
16 const IntLimit = 1e15
17
18 var db *store.Conn
19
20 func StoreSk(sk *openpgp.Entity) (err error) {
21         // TODO: initkey should save serverkey in db
22         b := new(bytes.Buffer)
23         err = sk.Serialize(b)
24         if err != nil {
25                 return
26         }
27         return db.Set("key", fmt.Sprintf("%X", sk.PrimaryKey.Fingerprint), b.Bytes())
28 }
29
30 func GetKeys(fpr string) (es openpgp.EntityList, err error) {
31         b, err := db.Get("key", fpr)
32         if err != nil {
33                 return
34         }
35         es, err = openpgp.ReadKeyRing(bytes.NewBuffer(b))
36         if err != nil {
37                 // internal error: pubkey cannot be parsed
38                 return
39         }
40         return
41 }
42
43 func AddKeys(d []byte) (err error) {
44         entities, err := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
45         if err != nil {
46                 return
47         }
48         // TODO: allow multiple key uploads at once?
49         if len(entities) > 100 {
50                 err = fmt.Errorf("expected at most 100 keys; got %d", len(entities))
51                 return
52         }
53         for _, e := range entities {
54                 // TODO: various checks..
55                 isIssuer, issuer, denom, err1 := dsakey.CheckEntity(e)
56                 err = err1
57                 if err != nil {
58                         // TODO..
59                         continue
60                 }
61                 if !isIssuer {
62                         es, err := GetKeys(issuer)
63                         if err != nil {
64                                 // TODO..
65                                 continue
66                         }
67                         ok, _, den, err := dsakey.CheckEntity(es[0])
68                         if !ok || err != nil || den != denom {
69                                 // TODO..
70                                 continue
71                         }
72                 }
73                 b := new(bytes.Buffer)
74                 err = e.Serialize(b)
75                 if err != nil {
76                         return
77                 }
78                 fpr := fmt.Sprintf("%X", e.PrimaryKey.Fingerprint)
79                 err = db.Set("key", fpr, b.Bytes())
80                 if err != nil {
81                         return
82                 }
83                 err = db.Append("keysby/64", fpr[len(fpr)-16:], []byte(fpr))
84                 if err != nil {
85                         return
86                 }
87                 err = db.Append("keysby/32", fpr[len(fpr)-8:], []byte(fpr))
88                 if err != nil {
89                         return
90                 }
91         }
92         return
93 }
94
95 func CertByDraft(draftid string) (d []byte, err error) {
96         certid, err := db.Get("certby/draft", draftid)
97         if err != nil {
98                 // TODO: we have the draft but the cert is not ready
99                 return
100         }
101         d, err = db.Get("cert", string(certid))
102         if err != nil {
103                 // shouldn't happen, cert is not available
104                 return
105         }
106         return
107 }
108
109 func CertByDebitCert(debitid string) (d []byte, err error) {
110         creditid, err := db.Get("certby/debit", debitid)
111         if err != nil {
112                 // TODO: we have the debit cert but the credit cert is not ready
113                 return
114         }
115         d, err = db.Get("cert", string(creditid))
116         if err != nil {
117                 // shouldn't happen, cert is not available
118                 return
119         }
120         return
121 }
122
123 // parse clear signed draft and verify it
124 func ParseDraft(d []byte) (draft *document.Draft, draftid string, err error) {
125         iv, signed, err := document.Parse(d)
126         if err != nil {
127                 return
128         }
129         draft, ok := iv.(*document.Draft)
130         if !ok {
131                 err = fmt.Errorf("ParseDraft: expected a draft docuent")
132                 return
133         }
134         draftid = document.Id(signed)
135
136         k, err := db.Get("key", draft.Drawer)
137         if err != nil {
138                 return
139         }
140         kr, err := openpgp.ReadKeyRing(bytes.NewBuffer(k))
141         if err != nil {
142                 // internal error: pubkey cannot be parsed
143                 return
144         }
145         err = document.Verify(signed, kr)
146         if err != nil {
147                 return
148         }
149         _, issuer, denom, err := dsakey.CheckEntity(kr[0])
150         if err != nil {
151                 return
152         }
153         k, err = db.Get("key", draft.Beneficiary)
154         if err != nil {
155                 return
156         }
157         kr, err = openpgp.ReadKeyRing(bytes.NewBuffer(k))
158         if err != nil {
159                 // internal error: pubkey cannot be parsed
160                 return
161         }
162         _, issuer2, denom2, err := dsakey.CheckEntity(kr[0])
163         if err != nil {
164                 return
165         }
166         if draft.Issuer != issuer ||
167                 draft.Issuer != issuer2 ||
168                 draft.Denomination != denom ||
169                 draft.Denomination != denom2 {
170                 err = fmt.Errorf("Issuer or denomination mismatch")
171                 return
172         }
173
174         // TODO: do various format checks (AuthorizedBy check etc)
175         if draft.Amount <= 0 || draft.Amount >= IntLimit {
176                 err = fmt.Errorf("draft amount is invalid: %d", draft.Amount)
177                 return
178         }
179         return
180 }
181
182 func ParseDebitCert(d []byte) (cert *document.DebitCert, certid string, err error) {
183         iv, signed, err := document.Parse(d)
184         if err != nil {
185                 return
186         }
187         cert, ok := iv.(*document.DebitCert)
188         if !ok {
189                 err = fmt.Errorf("ParseDebitCert: expected a debit docuent")
190                 return
191         }
192
193         k, err := db.Get("key", cert.AuthorizedBy)
194         if err != nil {
195                 return
196         }
197         // TODO: keep our key at hand
198         kr, err := openpgp.ReadKeyRing(bytes.NewBuffer(k))
199         if err != nil {
200                 // internal error: pubkey cannot be parsed
201                 return
202         }
203         // must clean up to make sure the hash is ok
204         err = document.Verify(signed, kr)
205         if err != nil {
206                 return
207         }
208
209         certid = document.Id(signed)
210         return
211 }
212
213 func NewDebitCert(draftid string, draft *document.Draft) (*document.DebitCert, error) {
214         cert := new(document.DebitCert)
215         cert.Holder = draft.Drawer
216         cert.Date = time.Seconds()
217         cert.Denomination = "epoint"
218         cert.Issuer = draft.Issuer
219         cert.AuthorizedBy = draft.AuthorizedBy
220         cert.Difference = -draft.Amount
221         cert.Draft = draftid
222         cert.Beneficiary = draft.Beneficiary
223
224         oid, err := db.Get("certby/key", draft.Drawer)
225         oldcertid := string(oid)
226         if err != nil {
227                 // first cert: drawer is issuer
228                 if draft.Drawer != draft.Issuer {
229                         return nil, fmt.Errorf("drawer must be the issuer when drawing an empty account")
230                 }
231                 cert.Serial = 1
232                 cert.Balance = cert.Difference
233                 cert.LastDebitSerial = 0
234                 cert.LastCreditSerial = 0
235         } else {
236                 d, err := db.Get("cert", oldcertid)
237                 if err != nil {
238                         return nil, err
239                 }
240                 iv, _, err := document.Parse(d)
241                 if err != nil {
242                         // internal error
243                         return nil, err
244                 }
245                 // TODO: this is a hack
246                 oldcert, err := document.ToCert(iv)
247                 // TODO: sanity checks? oldcert.Holder == draft.Drawer
248                 cert.Serial = oldcert.Serial + 1
249                 cert.Balance = oldcert.Balance + cert.Difference
250                 if cert.Balance <= -IntLimit {
251                         return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
252                 }
253                 if oldcert.Balance > 0 && cert.Balance < 0 {
254                         return nil, fmt.Errorf("insufficient funds: %d", oldcert.Balance)
255                 }
256                 cert.LastDebitSerial = oldcert.LastDebitSerial
257                 cert.LastCreditSerial = oldcert.LastCreditSerial
258                 if oldcert.IsDebit {
259                         cert.LastDebitSerial = oldcert.Serial
260                 } else {
261                         cert.LastCreditSerial = oldcert.Serial
262                 }
263                 cert.LastCert = &oldcertid
264         }
265         return cert, nil
266 }
267
268 func NewCreditCert(draftid string, draft *document.Draft, dcertid string, dcert *document.DebitCert) (*document.CreditCert, error) {
269         cert := new(document.CreditCert)
270         // TODO: get from old cert instead?
271         cert.Holder = dcert.Beneficiary
272         cert.Date = time.Seconds()
273         // TODO: get these from the cert holder pubkey
274         cert.Denomination = "epoint"
275         cert.Issuer = draft.Issuer
276         cert.AuthorizedBy = dcert.AuthorizedBy // TODO: draft vs dcert vs serverside decision
277         cert.Difference = -dcert.Difference
278         cert.Draft = draftid
279         cert.Drawer = dcert.Holder
280         cert.DebitCert = dcertid
281
282         oid, err := db.Get("certby/key", dcert.Beneficiary)
283         oldcertid := string(oid)
284         if err != nil {
285                 // this is the first cert
286                 cert.Serial = 1
287                 cert.Balance = cert.Difference
288                 cert.LastDebitSerial = 0
289                 cert.LastCreditSerial = 0
290         } else {
291                 d, err := db.Get("cert", oldcertid)
292                 if err != nil {
293                         // internal error
294                         return nil, err
295                 }
296                 iv, _, err := document.Parse(d)
297                 if err != nil {
298                         // internal error
299                         return nil, err
300                 }
301                 // TODO: this is a hack
302                 oldcert, err := document.ToCert(iv)
303                 if err != nil {
304                         // internal error
305                         return nil, err
306                 }
307                 cert.Serial = oldcert.Serial + 1
308                 cert.Balance = oldcert.Balance + cert.Difference
309                 if cert.Balance >= IntLimit {
310                         return nil, fmt.Errorf("balance limit exceeded: %d", cert.Balance)
311                 }
312                 cert.LastDebitSerial = oldcert.LastDebitSerial
313                 cert.LastCreditSerial = oldcert.LastCreditSerial
314                 if oldcert.IsDebit {
315                         cert.LastDebitSerial = oldcert.Serial
316                 } else {
317                         cert.LastCreditSerial = oldcert.Serial
318                 }
319                 cert.LastCert = &oldcertid
320         }
321         return cert, nil
322 }
323
324 func EvalDraft(d []byte, sk *openpgp.Entity) (r []byte, err error) {
325         draft, draftid, err := ParseDraft(d)
326         if err != nil {
327                 return
328         }
329         _, err = db.Get("draft", draftid)
330         if err == nil {
331                 // found
332                 // TODO: certby/draft might not be ready even if draft is there
333                 return CertByDraft(draftid)
334         }
335         // if draft is ok we save it
336         err = db.Set("draft", draftid, d)
337         if err != nil {
338                 // internal error
339                 return
340         }
341         // TODO: db.Insert: fails if key exists
342         s := fmt.Sprintf("%s.%s", draft.Drawer, draft.Nonce)
343         _, err = db.Get("draftby/key.nonce", s)
344         if err == nil {
345                 err = fmt.Errorf("draft nonce is not unique")
346                 return
347         }
348         err = db.Set("draftby/key.nonce", s, d)
349         if err != nil {
350                 // internal error
351                 return
352         }
353
354         // debit cert
355         cert, err := NewDebitCert(draftid, draft)
356         if err != nil {
357                 return
358         }
359         r, signed, err := document.Format(cert, sk)
360         certid := document.Id(signed)
361         err = db.Set("cert", certid, r)
362         if err != nil {
363                 // internal error
364                 return
365         }
366         err = db.Set("certby/draft", draftid, []byte(certid))
367         if err != nil {
368                 // internal error
369                 return
370         }
371         err = db.Set("certby/key", cert.Holder, []byte(certid))
372         if err != nil {
373                 // internal error
374                 return
375         }
376         // TODO: append?
377         err = db.Set("certby/key.serial", fmt.Sprintf("%s.%09d", cert.Holder, cert.Serial), []byte(certid))
378         if err != nil {
379                 // internal error
380                 return
381         }
382         return
383 }
384
385 func EvalDebitCert(d []byte, sk *openpgp.Entity) (r []byte, err error) {
386         dcert, dcertid, err := ParseDebitCert(d)
387         if err != nil {
388                 return
389         }
390         r, err = CertByDebitCert(dcertid)
391         if err == nil {
392                 // found
393                 return
394         }
395         // TODO: we only need the draft to know the issuer (+beneficiary)
396         // it should be in the pubkey
397         d, err = db.Get("draft", dcert.Draft)
398         if err != nil {
399                 // internal error
400                 return
401         }
402         iv, _, err := document.Parse(d)
403         if err != nil {
404                 // internal error
405                 return
406         }
407         draft, ok := iv.(*document.Draft)
408         if !ok {
409                 // internal error
410                 err = fmt.Errorf("EvalDebitCert: expected draft from internal db")
411                 return
412         }
413
414         // credit side
415         // TODO: check pubkey etc
416         cert, err := NewCreditCert(dcert.Draft, draft, dcertid, dcert)
417         if err != nil {
418                 // internal error
419                 return
420         }
421         r, signed, err := document.Format(cert, sk)
422         if err != nil {
423                 // internal error
424                 return
425         }
426         certid := document.Id(signed)
427         err = db.Set("cert", certid, r)
428         if err != nil {
429                 // internal error
430                 return
431         }
432         err = db.Set("certby/debit", dcertid, []byte(certid))
433         if err != nil {
434                 // internal error
435                 return
436         }
437         err = db.Set("certby/key", cert.Holder, []byte(certid))
438         if err != nil {
439                 // internal error
440                 return
441         }
442         // TODO: append?
443         err = db.Set("certby/key.serial", fmt.Sprintf("%s.%09d", cert.Holder, cert.Serial), []byte(certid))
444         if err != nil {
445                 // internal error
446                 return
447         }
448         return
449 }
450
451 func Init(rootdir string) (err error) {
452         db, err = store.Open(rootdir)
453         if err != nil {
454                 return
455         }
456         err = db.Ensure("key")
457         if err != nil {
458                 return
459         }
460         err = db.Ensure("cert")
461         if err != nil {
462                 return
463         }
464         err = db.Ensure("draft")
465         if err != nil {
466                 return
467         }
468         err = db.Ensure("certby/draft")
469         if err != nil {
470                 return
471         }
472         err = db.Ensure("certby/debit")
473         if err != nil {
474                 return
475         }
476         err = db.Ensure("certby/key")
477         if err != nil {
478                 return
479         }
480         err = db.Ensure("certby/key.serial")
481         if err != nil {
482                 return
483         }
484         err = db.Ensure("draftby/key.nonce")
485         if err != nil {
486                 return
487         }
488         err = db.Ensure("keysby/64")
489         if err != nil {
490                 return
491         }
492         err = db.Ensure("keysby/32")
493         if err != nil {
494                 return
495         }
496         return
497 }