chiark / gitweb /
server/chal.c: Capture `master->algs.bulk' in a variable.
[tripe] / server / bulkcrypto.c
1 /* -*-c-*-
2  *
3  * Bulk crypto transformations
4  *
5  * (c) 2014 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Trivial IP Encryption (TrIPE).
11  *
12  * TrIPE is free software: you can redistribute it and/or modify it under
13  * the terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your
15  * option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE.  If not, see <https://www.gnu.org/licenses/>.
24  */
25
26 /*----- Header files ------------------------------------------------------*/
27
28 #include "tripe.h"
29
30 /*----- Utilities ---------------------------------------------------------*/
31
32 #define SEQSZ 4                         /* Size of sequence number packet */
33
34 #define TRACE_IV(qiv, ivsz) do { IF_TRACING(T_KEYSET, {                 \
35   trace_block(T_CRYPTO, "crypto: initialization vector",                \
36               (qiv), (ivsz));                                           \
37 }) } while (0)
38
39 #define TRACE_CT(qpk, sz) do { IF_TRACING(T_KEYSET, {                   \
40   trace_block(T_CRYPTO, "crypto: encrypted packet", (qpk), (sz));       \
41 }) } while (0)
42
43 #define TRACE_MAC(qmac, tagsz) do { IF_TRACING(T_KEYSET, {              \
44   trace_block(T_CRYPTO, "crypto: computed MAC", (qmac), (tagsz));       \
45 }) } while (0)
46
47 #define TRACE_MACERR(pmac, tagsz) do { IF_TRACING(T_KEYSET, {           \
48   trace(T_KEYSET, "keyset: incorrect MAC: decryption failed");          \
49   trace_block(T_CRYPTO, "crypto: expected MAC", (pmac), (tagsz));       \
50 }) } while (0)
51
52 /* --- @derivekey@ --- *
53  *
54  * Arguments:   @octet *k@ = pointer to an output buffer of at least
55  *                      @MAXHASHSZ@ bytes
56  *              @size_t ksz@ = actual size wanted (for tracing)
57  *              @const deriveargs@ = derivation parameters, as passed into
58  *                      @genkeys@
59  *              @int dir@ = direction for the key (@DIR_IN@ or @DIR_OUT@)
60  *              @const char *what@ = label for the key (input to derivation)
61  *
62  * Returns:     ---
63  *
64  * Use:         Derives a session key, for use on incoming or outgoing data.
65  */
66
67 static void derivekey(octet *k, size_t ksz, const deriveargs *a,
68                       int dir, const char *what)
69 {
70   const gchash *hc = a->hc;
71   ghash *h;
72
73   assert(ksz <= hc->hashsz);
74   assert(hc->hashsz <= MAXHASHSZ);
75   h = GH_INIT(hc);
76   GH_HASH(h, a->what, strlen(a->what)); GH_HASH(h, what, strlen(what) + 1);
77   switch (dir) {
78     case DIR_IN:
79       if (a->x) GH_HASH(h, a->k, a->x);
80       if (a->y != a->x) GH_HASH(h, a->k + a->x, a->y - a->x);
81       break;
82     case DIR_OUT:
83       if (a->y != a->x) GH_HASH(h, a->k + a->x, a->y - a->x);
84       if (a->x) GH_HASH(h, a->k, a->x);
85       break;
86     default:
87       abort();
88   }
89   GH_HASH(h, a->k + a->y, a->z - a->y);
90   GH_DONE(h, k);
91   GH_DESTROY(h);
92   IF_TRACING(T_KEYSET, { IF_TRACING(T_CRYPTO, {
93     char _buf[32];
94     sprintf(_buf, "crypto: %s key %s", dir ? "outgoing" : "incoming", what);
95     trace_block(T_CRYPTO, _buf, k, ksz);
96   }) })
97 }
98
99 /*----- Common functionality for generic-composition transforms -----------*/
100
101 #define CHECK_MAC(h, pmac, tagsz) do {                                  \
102   ghash *_h = (h);                                                      \
103   const octet *_pmac = (pmac);                                          \
104   size_t _tagsz = (tagsz);                                              \
105   octet *_mac = GH_DONE(_h, 0);                                         \
106   int _eq = ct_memeq(_mac, _pmac, _tagsz);                              \
107   TRACE_MAC(_mac, _tagsz);                                              \
108   GH_DESTROY(_h);                                                       \
109   if (!_eq) {                                                           \
110     TRACE_MACERR(_pmac, _tagsz);                                        \
111     return (KSERR_DECRYPT);                                             \
112   }                                                                     \
113 } while (0)
114
115 typedef struct gencomp_algs {
116   const gccipher *c; size_t cksz;
117   const gcmac *m; size_t mksz; size_t tagsz;
118 } gencomp_algs;
119
120 typedef struct gencomp_chal {
121   bulkchal _b;
122   gmac *m;
123 } gencomp_chal;
124
125 static int gencomp_getalgs(gencomp_algs *a, const algswitch *asw,
126                            dstr *e, key_file *kf, key *k)
127 {
128   const char *p;
129   char *q, *qq;
130   unsigned long n;
131   dstr d = DSTR_INIT;
132   int rc = -1;
133
134   /* --- Symmetric encryption --- */
135
136   if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc";
137   if ((a->c = gcipher_byname(p)) == 0) {
138     a_format(e, "unknown-cipher", "%s", p, A_END);
139     goto done;
140   }
141
142   /* --- Message authentication --- */
143
144   if ((p = key_getattr(kf, k, "mac")) != 0) {
145     dstr_reset(&d);
146     dstr_puts(&d, p);
147     if ((q = strrchr(d.buf, '/')) != 0)
148       *q++ = 0;
149     if ((a->m = gmac_byname(d.buf)) == 0) {
150       a_format(e, "unknown-mac", "%s", d.buf, A_END);
151       goto done;
152     }
153     if (!q)
154       a->tagsz = a->m->hashsz;
155     else {
156       n = strtoul(q, &qq, 0);
157       if (*qq)  {
158         a_format(e, "bad-tag-length-string", "%s", q, A_END);
159         goto done;
160       }
161       if (n%8 || n/8 > a->m->hashsz) {
162         a_format(e, "bad-tag-length", "%lu", n, A_END);
163         goto done;
164       }
165       a->tagsz = n/8;
166     }
167   } else {
168     dstr_reset(&d);
169     dstr_putf(&d, "%s-hmac", asw->h->name);
170     if ((a->m = gmac_byname(d.buf)) == 0) {
171       a_format(e, "no-hmac-for-hash", "%s", asw->h->name, A_END);
172       goto done;
173     }
174     a->tagsz = asw->h->hashsz/2;
175   }
176
177   rc = 0;
178 done:
179   dstr_destroy(&d);
180   return (rc);
181 }
182
183 #ifndef NTRACE
184 static void gencomp_tracealgs(const gencomp_algs *a)
185 {
186   trace(T_CRYPTO, "crypto: cipher = %s", a->c->name);
187   trace(T_CRYPTO, "crypto: mac = %s/%lu",
188         a->m->name, (unsigned long)a->tagsz * 8);
189 }
190 #endif
191
192 static int gencomp_checkalgs(gencomp_algs *a, const algswitch *asw, dstr *e)
193 {
194   /* --- Derive the key sizes --- *
195    *
196    * Must ensure that we have non-empty keys.  This isn't ideal, but it
197    * provides a handy sanity check.  Also must be based on a 64- or 128-bit
198    * block cipher or we can't do the data expiry properly.
199    */
200
201   if ((a->cksz = keysz(asw->hashsz, a->c->keysz)) == 0) {
202     a_format(e, "cipher", "%s", a->c->name,
203              "no-key-size", "%lu", (unsigned long)asw->hashsz,
204              A_END);
205     return (-1);
206   }
207   if ((a->mksz = keysz(asw->hashsz, a->m->keysz)) == 0) {
208     a_format(e, "mac", "%s", a->m->name,
209              "no-key-size", "%lu", (unsigned long)asw->hashsz,
210              A_END);
211     return (-1);
212   }
213
214   return (0);
215 }
216
217 static void gencomp_alginfo(const gencomp_algs *a, admin *adm)
218 {
219   a_info(adm,
220          "cipher=%s", a->c->name,
221          "cipher-keysz=%lu", (unsigned long)a->cksz,
222          "cipher-blksz=%lu", (unsigned long)a->c->blksz,
223          A_END);
224   a_info(adm,
225          "mac=%s", a->m->name,
226          "mac-keysz=%lu", (unsigned long)a->mksz,
227          "mac-tagsz=%lu", (unsigned long)a->tagsz,
228          A_END);
229 }
230
231 static int gencomp_samealgsp(const gencomp_algs *a, const gencomp_algs *aa)
232 {
233   return (a->c == aa->c &&
234           a->m == aa->m && a->tagsz == aa->tagsz);
235 }
236
237 static size_t gencomp_expsz(const gencomp_algs *a)
238   { return (a->c->blksz < 16 ? MEG(64) : MEG(2048)); }
239
240 static bulkchal *gencomp_genchal(const gencomp_algs *a)
241 {
242   gencomp_chal *gc = CREATE(gencomp_chal);
243
244   rand_get(RAND_GLOBAL, buf_t, a->mksz);
245   gc->m = GM_KEY(a->m, buf_t, a->mksz);
246   gc->_b.tagsz = a->tagsz;
247   IF_TRACING(T_CHAL, {
248     trace(T_CHAL, "chal: generated new challenge key");
249     trace_block(T_CRYPTO, "chal: new key", buf_t, a->mksz);
250   })
251   return (&gc->_b);
252 }
253
254 static int gencomp_chaltag(bulkchal *bc, const void *m, size_t msz, void *t)
255 {
256   gencomp_chal *gc = (gencomp_chal *)bc;
257   ghash *h = GM_INIT(gc->m);
258
259   GH_HASH(h, m, msz);
260   memcpy(t, GH_DONE(h, 0), bc->tagsz);
261   GH_DESTROY(h);
262   return (0);
263 }
264
265 static int gencomp_chalvrf(bulkchal *bc, const void *m, size_t msz,
266                            const void *t)
267 {
268   gencomp_chal *gc = (gencomp_chal *)bc;
269   ghash *h = GM_INIT(gc->m);
270   int ok;
271
272   GH_HASH(h, m, msz);
273   ok = ct_memeq(GH_DONE(h, 0), t, gc->_b.tagsz);
274   GH_DESTROY(h);
275   return (ok ? 0 : -1);
276 }
277
278 static void gencomp_freechal(bulkchal *bc)
279   { gencomp_chal *gc = (gencomp_chal *)bc; GM_DESTROY(gc->m); DESTROY(gc); }
280
281 /*----- The original transform --------------------------------------------*
282  *
283  * We generate a random initialization vector (if the cipher needs one).  We
284  * encrypt the input message with the cipher, and format the type, sequence
285  * number, IV, and ciphertext as follows.
286  *
287  *              +------+ +------+---...---+------...------+
288  *              | type | | seq  |   iv    |   ciphertext  |
289  *              +------+ +------+---...---+------...------+
290  *                 32       32     blksz         sz
291  *
292  * All of this is fed into the MAC to compute a tag.  The type is not
293  * transmitted: the other end knows what type of message it expects, and the
294  * type is only here to prevent us from being confused because some other
295  * kind of ciphertext has been substituted.  The tag is prepended to the
296  * remainder, to yield the finished cryptogram, as follows.
297  *
298  *              +---...---+------+---...---+------...------+
299  *              |   tag   | seq  |   iv    |   ciphertext  |
300  *              +---...---+------+---...---+------...------+
301  *                 tagsz     32     blksz         sz
302  *
303  * Decryption: checks the overall size, verifies the tag, then decrypts the
304  * ciphertext and extracts the sequence number.
305  */
306
307 typedef struct v0_algs {
308   bulkalgs _b;
309   gencomp_algs ga;
310 } v0_algs;
311
312 typedef struct v0_ctx {
313   bulkctx _b;
314   size_t tagsz;
315   struct {
316     gcipher *c;
317     gmac *m;
318   } d[NDIR];
319 } v0_ctx;
320
321 static bulkalgs *v0_getalgs(const algswitch *asw, dstr *e,
322                             key_file *kf, key *k)
323 {
324   v0_algs *a = CREATE(v0_algs);
325   if (gencomp_getalgs(&a->ga, asw, e, kf, k)) { DESTROY(a); return (0); }
326   return (&a->_b);
327 }
328
329 #ifndef NTRACE
330 static void v0_tracealgs(const bulkalgs *aa)
331   { const v0_algs *a = (const v0_algs *)aa; gencomp_tracealgs(&a->ga); }
332 #endif
333
334 static int v0_checkalgs(bulkalgs *aa, const algswitch *asw, dstr *e)
335 {
336   v0_algs *a = (v0_algs *)aa;
337   if (gencomp_checkalgs(&a->ga, asw, e)) return (-1);
338   return (0);
339 }
340
341 static int v0_samealgsp(const bulkalgs *aa, const bulkalgs *bb)
342 {
343   const v0_algs *a = (const v0_algs *)aa, *b = (const v0_algs *)bb;
344   return (gencomp_samealgsp(&a->ga, &b->ga));
345 }
346
347 static void v0_alginfo(const bulkalgs *aa, admin *adm)
348   { const v0_algs *a = (const v0_algs *)aa; gencomp_alginfo(&a->ga, adm); }
349
350 static size_t v0_overhead(const bulkalgs *aa)
351 {
352   const v0_algs *a = (const v0_algs *)aa;
353   return (a->ga.tagsz + SEQSZ + a->ga.c->blksz);
354 }
355
356 static size_t v0_expsz(const bulkalgs *aa)
357   { const v0_algs *a = (const v0_algs *)aa; return (gencomp_expsz(&a->ga)); }
358
359 static bulkctx *v0_genkeys(const bulkalgs *aa, const deriveargs *da)
360 {
361   const v0_algs *a = (const v0_algs *)aa;
362   v0_ctx *bc = CREATE(v0_ctx);
363   octet k[MAXHASHSZ];
364   int i;
365
366   bc->tagsz = a->ga.tagsz;
367   for (i = 0; i < NDIR; i++) {
368     if (!(da->f&(1 << i))) { bc->d[i].c = 0; bc->d[i].m = 0; continue; }
369     derivekey(k, a->ga.cksz, da, i, "encryption");
370     bc->d[i].c = GC_INIT(a->ga.c, k, a->ga.cksz);
371     derivekey(k, a->ga.mksz, da, i, "integrity");
372     bc->d[i].m = GM_KEY(a->ga.m, k, a->ga.mksz);
373   }
374   return (&bc->_b);
375 }
376
377 static bulkchal *v0_genchal(const bulkalgs *aa)
378 {
379   const v0_algs *a = (const v0_algs *)aa;
380   return (gencomp_genchal(&a->ga));
381 }
382 #define v0_chaltag gencomp_chaltag
383 #define v0_chalvrf gencomp_chalvrf
384 #define v0_freechal gencomp_freechal
385
386 static void v0_freealgs(bulkalgs *aa)
387   { v0_algs *a = (v0_algs *)aa; DESTROY(a); }
388
389 static void v0_freectx(bulkctx *bbc)
390 {
391   v0_ctx *bc = (v0_ctx *)bbc;
392   int i;
393
394   for (i = 0; i < NDIR; i++) {
395     if (bc->d[i].c) GC_DESTROY(bc->d[i].c);
396     if (bc->d[i].m) GM_DESTROY(bc->d[i].m);
397   }
398   DESTROY(bc);
399 }
400
401 static int v0_encrypt(bulkctx *bbc, unsigned ty,
402                       buf *b, buf *bb, uint32 seq)
403 {
404   v0_ctx *bc = (v0_ctx *)bbc;
405   ghash *h;
406   gcipher *c = bc->d[DIR_OUT].c;
407   const octet *p = BCUR(b);
408   size_t sz = BLEFT(b);
409   octet *qmac, *qseq, *qiv, *qpk;
410   size_t ivsz;
411   size_t tagsz = bc->tagsz;
412   octet t[4];
413
414   assert(c);
415   ivsz = GC_CLASS(c)->blksz;
416
417   /* --- Determine the ciphertext layout --- */
418
419   if (buf_ensure(bb, tagsz + SEQSZ + ivsz + sz)) return (0);
420   qmac = BCUR(bb); qseq = qmac + tagsz; qiv = qseq + SEQSZ; qpk = qiv + ivsz;
421   BSTEP(bb, tagsz + SEQSZ + ivsz + sz);
422
423   /* --- Store the type --- *
424    *
425    * This isn't transmitted, but it's covered by the MAC.
426    */
427
428   STORE32(t, ty);
429
430   /* --- Store the sequence number --- */
431
432   STORE32(qseq, seq);
433
434   /* --- Establish an initialization vector if necessary --- */
435
436   if (ivsz) {
437     rand_get(RAND_GLOBAL, qiv, ivsz);
438     GC_SETIV(c, qiv);
439     TRACE_IV(qiv, ivsz);
440   }
441
442   /* --- Encrypt the packet --- */
443
444   GC_ENCRYPT(c, p, qpk, sz);
445   TRACE_CT(qpk, sz);
446
447   /* --- Compute a MAC over type, sequence number, IV, and ciphertext --- */
448
449   if (tagsz) {
450     h = GM_INIT(bc->d[DIR_OUT].m);
451     GH_HASH(h, t, sizeof(t));
452     GH_HASH(h, qseq, SEQSZ + ivsz + sz);
453     memcpy(qmac, GH_DONE(h, 0), tagsz);
454     GH_DESTROY(h);
455     TRACE_MAC(qmac, tagsz);
456   }
457
458   /* --- We're done --- */
459
460   return (0);
461 }
462
463 static int v0_decrypt(bulkctx *bbc, unsigned ty,
464                       buf *b, buf *bb, uint32 *seq)
465 {
466   v0_ctx *bc = (v0_ctx *)bbc;
467   const octet *pmac, *piv, *pseq, *ppk;
468   size_t psz = BLEFT(b);
469   size_t sz;
470   octet *q = BCUR(bb);
471   ghash *h;
472   gcipher *c = bc->d[DIR_IN].c;
473   size_t ivsz;
474   size_t tagsz = bc->tagsz;
475   octet t[4];
476
477   assert(c);
478   ivsz = GC_CLASS(c)->blksz;
479
480   /* --- Break up the packet into its components --- */
481
482   if (psz < ivsz + SEQSZ + tagsz) {
483     T( trace(T_KEYSET, "keyset: block too small for keyset"); )
484     return (KSERR_MALFORMED);
485   }
486   sz = psz - ivsz - SEQSZ - tagsz;
487   pmac = BCUR(b); pseq = pmac + tagsz; piv = pseq + SEQSZ; ppk = piv + ivsz;
488   STORE32(t, ty);
489
490   /* --- Verify the MAC on the packet --- */
491
492   if (tagsz) {
493     h = GM_INIT(bc->d[DIR_IN].m);
494     GH_HASH(h, t, sizeof(t));
495     GH_HASH(h, pseq, SEQSZ + ivsz + sz);
496     CHECK_MAC(h, pmac, tagsz);
497   }
498
499   /* --- Decrypt the packet --- */
500
501   if (ivsz) {
502     TRACE_IV(piv, ivsz);
503     GC_SETIV(c, piv);
504   }
505   GC_DECRYPT(c, ppk, q, sz);
506
507   /* --- Finished --- */
508
509   *seq = LOAD32(pseq);
510   BSTEP(bb, sz);
511   return (0);
512 }
513
514 /*----- The implicit-IV transform -----------------------------------------*
515  *
516  * The v0 transform makes everything explicit.  There's an IV because the
517  * cipher needs an IV; there's a sequence number because replay prevention
518  * needs a sequence number.
519  *
520  * This new transform works rather differently.  We make use of a block
521  * cipher to encrypt the sequence number, and use that as the IV.  We
522  * transmit the sequence number in the clear, as before.  This reduces
523  * overhead; and it's not a significant privacy leak because the adversary
524  * can see the order in which the messages are transmitted -- i.e., the
525  * sequence numbers are almost completely predictable anyway.
526  *
527  * So, a MAC is computed over
528  *
529  *              +------+ +------+------...------+
530  *              | type | | seq  |   ciphertext  |
531  *              +------+ +------+------...------+
532  *                 32       32         sz
533  *
534  * and we actually transmit the following as the cryptogram.
535  *
536  *              +---...---+------+------...------+
537  *              |   tag   | seq  |   ciphertext  |
538  *              +---...---+------+------...------+
539  *                 tagsz     32         sz
540  */
541
542 typedef struct iiv_algs {
543   bulkalgs _b;
544   gencomp_algs ga;
545   const gccipher *b; size_t bksz;
546 } iiv_algs;
547
548 typedef struct iiv_ctx {
549   bulkctx _b;
550   size_t tagsz;
551   struct {
552     gcipher *c, *b;
553     gmac *m;
554   } d[NDIR];
555 } iiv_ctx;
556
557
558 static bulkalgs *iiv_getalgs(const algswitch *asw, dstr *e,
559                             key_file *kf, key *k)
560 {
561   iiv_algs *a = CREATE(iiv_algs);
562   dstr d = DSTR_INIT, dd = DSTR_INIT;
563   const char *p;
564   char *q;
565
566   if (gencomp_getalgs(&a->ga, asw, e, kf, k)) goto fail;
567
568   if ((p = key_getattr(kf, k, "blkc")) == 0) {
569     dstr_puts(&dd, a->ga.c->name);
570     if ((q = strrchr(dd.buf, '-')) != 0) *q = 0;
571     p = dd.buf;
572   }
573   dstr_putf(&d, "%s-ecb", p);
574   if ((a->b = gcipher_byname(d.buf)) == 0) {
575     a_format(e, "unknown-blkc", "%s", p, A_END);
576     goto fail;
577   }
578
579   dstr_destroy(&d); dstr_destroy(&dd);
580   return (&a->_b);
581 fail:
582   dstr_destroy(&d); dstr_destroy(&dd);
583   DESTROY(a);
584   return (0);
585 }
586
587 #ifndef NTRACE
588 static void iiv_tracealgs(const bulkalgs *aa)
589 {
590   const iiv_algs *a = (const iiv_algs *)aa;
591
592   gencomp_tracealgs(&a->ga);
593   trace(T_CRYPTO,
594         "crypto: blkc = %.*s", (int)strlen(a->b->name) - 4, a->b->name);
595 }
596 #endif
597
598 static int iiv_checkalgs(bulkalgs *aa, const algswitch *asw, dstr *e)
599 {
600   iiv_algs *a = (iiv_algs *)aa;
601
602   if (gencomp_checkalgs(&a->ga, asw, e)) return (-1);
603
604   if ((a->bksz = keysz(asw->hashsz, a->b->keysz)) == 0) {
605     a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name,
606              "no-key-size", "%lu", (unsigned long)asw->hashsz,
607              A_END);
608     return (-1);
609   }
610   if (a->b->blksz < a->ga.c->blksz) {
611     a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name,
612              "blksz-insufficient", A_END);
613     return (-1);
614   }
615   return (0);
616 }
617
618 static int iiv_samealgsp(const bulkalgs *aa, const bulkalgs *bb)
619 {
620   const iiv_algs *a = (const iiv_algs *)aa, *b = (const iiv_algs *)bb;
621   return (gencomp_samealgsp(&a->ga, &b->ga) && a->b == b->b);
622 }
623
624 static void iiv_alginfo(const bulkalgs *aa, admin *adm)
625 {
626   const iiv_algs *a = (const iiv_algs *)aa;
627   gencomp_alginfo(&a->ga, adm);
628   a_info(adm,
629          "blkc=%.*s", strlen(a->b->name) - 4, a->b->name,
630          "blkc-keysz=%lu", (unsigned long)a->bksz,
631          "blkc-blksz=%lu", (unsigned long)a->b->blksz,
632          A_END);
633 }
634
635 static size_t iiv_overhead(const bulkalgs *aa)
636   { const iiv_algs *a = (const iiv_algs *)aa; return (a->ga.tagsz + SEQSZ); }
637
638 static size_t iiv_expsz(const bulkalgs *aa)
639 {
640   const iiv_algs *a = (const iiv_algs *)aa;
641   return (gencomp_expsz(&a->ga));
642 }
643
644 static bulkctx *iiv_genkeys(const bulkalgs *aa, const deriveargs *da)
645 {
646   const iiv_algs *a = (const iiv_algs *)aa;
647   iiv_ctx *bc = CREATE(iiv_ctx);
648   octet k[MAXHASHSZ];
649   int i;
650
651   bc->tagsz = a->ga.tagsz;
652   for (i = 0; i < NDIR; i++) {
653     if (!(da->f&(1 << i)))
654       { bc->d[i].c = 0; bc->d[i].b = 0; bc->d[i].m = 0; continue; }
655     derivekey(k, a->ga.cksz, da, i, "encryption");
656     bc->d[i].c = GC_INIT(a->ga.c, k, a->ga.cksz);
657     derivekey(k, a->bksz, da, i, "blkc");
658     bc->d[i].b = GC_INIT(a->b, k, a->bksz);
659     derivekey(k, a->ga.mksz, da, i, "integrity");
660     bc->d[i].m = GM_KEY(a->ga.m, k, a->ga.mksz);
661   }
662   return (&bc->_b);
663 }
664
665 static bulkchal *iiv_genchal(const bulkalgs *aa)
666 {
667   const iiv_algs *a = (const iiv_algs *)aa;
668   return (gencomp_genchal(&a->ga));
669 }
670 #define iiv_chaltag gencomp_chaltag
671 #define iiv_chalvrf gencomp_chalvrf
672 #define iiv_freechal gencomp_freechal
673
674 static void iiv_freealgs(bulkalgs *aa)
675   { iiv_algs *a = (iiv_algs *)aa; DESTROY(a); }
676
677 static void iiv_freectx(bulkctx *bbc)
678 {
679   iiv_ctx *bc = (iiv_ctx *)bbc;
680   int i;
681
682   for (i = 0; i < NDIR; i++) {
683     if (bc->d[i].c) GC_DESTROY(bc->d[i].c);
684     if (bc->d[i].b) GC_DESTROY(bc->d[i].b);
685     if (bc->d[i].m) GM_DESTROY(bc->d[i].m);
686   }
687   DESTROY(bc);
688 }
689
690 #define TRACE_PRESEQ(qseq, ivsz) do { IF_TRACING(T_KEYSET, {            \
691   trace_block(T_CRYPTO, "crypto: IV derivation input", (qseq), (ivsz)); \
692 }) } while (0)
693
694 static int iiv_encrypt(bulkctx *bbc, unsigned ty,
695                        buf *b, buf *bb, uint32 seq)
696 {
697   iiv_ctx *bc = (iiv_ctx *)bbc;
698   ghash *h;
699   gcipher *c = bc->d[DIR_OUT].c, *blkc = bc->d[DIR_OUT].b;
700   const octet *p = BCUR(b);
701   size_t sz = BLEFT(b);
702   octet *qmac, *qseq, *qpk;
703   size_t ivsz, blkcsz;
704   size_t tagsz = bc->tagsz;
705   octet t[4];
706
707   assert(c); assert(blkc);
708   ivsz = GC_CLASS(c)->blksz;
709   blkcsz = GC_CLASS(blkc)->blksz;
710
711   /* --- Determine the ciphertext layout --- */
712
713   if (buf_ensure(bb, tagsz + SEQSZ + sz)) return (0);
714   qmac = BCUR(bb); qseq = qmac + tagsz; qpk = qseq + SEQSZ;
715   BSTEP(bb, tagsz + SEQSZ + sz);
716
717   /* --- Store the type --- *
718    *
719    * This isn't transmitted, but it's covered by the MAC.
720    */
721
722   STORE32(t, ty);
723
724   /* --- Store the sequence number --- */
725
726   STORE32(qseq, seq);
727
728   /* --- Establish an initialization vector if necessary --- */
729
730   if (ivsz) {
731     memset(buf_u, 0, blkcsz - SEQSZ);
732     memcpy(buf_u + blkcsz - SEQSZ, qseq, SEQSZ);
733     TRACE_PRESEQ(buf_u, ivsz);
734     GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz);
735     GC_SETIV(c, buf_u);
736     TRACE_IV(buf_u, ivsz);
737   }
738
739   /* --- Encrypt the packet --- */
740
741   GC_ENCRYPT(c, p, qpk, sz);
742   TRACE_CT(qpk, sz);
743
744   /* --- Compute a MAC over type, sequence number, and ciphertext --- */
745
746   if (tagsz) {
747     h = GM_INIT(bc->d[DIR_OUT].m);
748     GH_HASH(h, t, sizeof(t));
749     GH_HASH(h, qseq, SEQSZ + sz);
750     memcpy(qmac, GH_DONE(h, 0), tagsz);
751     GH_DESTROY(h);
752     TRACE_MAC(qmac, tagsz);
753   }
754
755   /* --- We're done --- */
756
757   return (0);
758 }
759
760 static int iiv_decrypt(bulkctx *bbc, unsigned ty,
761                        buf *b, buf *bb, uint32 *seq)
762 {
763   iiv_ctx *bc = (iiv_ctx *)bbc;
764   const octet *pmac, *pseq, *ppk;
765   size_t psz = BLEFT(b);
766   size_t sz;
767   octet *q = BCUR(bb);
768   ghash *h;
769   gcipher *c = bc->d[DIR_IN].c, *blkc = bc->d[DIR_IN].b;
770   size_t ivsz, blkcsz;
771   size_t tagsz = bc->tagsz;
772   octet t[4];
773
774   assert(c); assert(blkc);
775   ivsz = GC_CLASS(c)->blksz;
776   blkcsz = GC_CLASS(blkc)->blksz;
777
778   /* --- Break up the packet into its components --- */
779
780   if (psz < SEQSZ + tagsz) {
781     T( trace(T_KEYSET, "keyset: block too small for keyset"); )
782     return (KSERR_MALFORMED);
783   }
784   sz = psz - SEQSZ - tagsz;
785   pmac = BCUR(b); pseq = pmac + tagsz; ppk = pseq + SEQSZ;
786   STORE32(t, ty);
787
788   /* --- Verify the MAC on the packet --- */
789
790   if (tagsz) {
791     h = GM_INIT(bc->d[DIR_IN].m);
792     GH_HASH(h, t, sizeof(t));
793     GH_HASH(h, pseq, SEQSZ + sz);
794     CHECK_MAC(h, pmac, tagsz);
795   }
796
797   /* --- Decrypt the packet --- */
798
799   if (ivsz) {
800     memset(buf_u, 0, blkcsz - SEQSZ);
801     memcpy(buf_u + blkcsz - SEQSZ, pseq, SEQSZ);
802     TRACE_PRESEQ(buf_u, ivsz);
803     GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz);
804     GC_SETIV(c, buf_u);
805     TRACE_IV(buf_u, ivsz);
806   }
807   GC_DECRYPT(c, ppk, q, sz);
808
809   /* --- Finished --- */
810
811   *seq = LOAD32(pseq);
812   BSTEP(bb, sz);
813   return (0);
814 }
815
816 /*----- The NaCl box transform --------------------------------------------*
817  *
818  * This transform is very similar to the NaCl `crypto_secretbox' transform
819  * described in Bernstein, `Cryptography in NaCl', with the difference that,
820  * rather than using XSalsa20, we use either Salsa20/r or ChaChar, because we
821  * have no need of XSalsa20's extended nonce.  The default cipher is Salsa20.
822  *
823  * Salsa20 and ChaCha accept a 64-bit nonce.  The low 32 bits are the
824  * sequence number, and the high 32 bits are the type, both big-endian.
825  *
826  *              +------+------+
827  *              | seq  | type |
828  *              +------+------+
829  *                 32     32
830  *
831  * A stream is generated by concatenating the raw output blocks generated
832  * with this nonce and successive counter values starting from zero.  The
833  * first 32 bytes of the stream are used as a key for Poly1305: the first 16
834  * bytes are the universal hash key r, and the second 16 bytes are the mask
835  * value s.
836  *
837  *              +------+------+ +------...------+
838  *              |  r   |  s   | |   keystream   |
839  *              +------+------+ +------...------+
840  *                128    128           sz
841  *
842  * The remainder of the stream is XORed with the incoming plaintext to form a
843  * ciphertext with the same length.  The ciphertext (only) is then tagged
844  * using Poly1305.  The tag, sequence number, and ciphertext are concatenated
845  * in this order, and transmitted.
846  *
847  *
848  *              +---...---+------+------...------+
849  *              |   tag   | seq  |   ciphertext  |
850  *              +---...---+------+------...------+
851  *                  128     32          sz
852  *
853  * Note that there is no need to authenticate the type separately, since it
854  * was used to select the cipher nonce, and hence the Poly1305 key.  The
855  * Poly1305 tag length is fixed.
856  */
857
858 typedef struct naclbox_algs {
859   bulkalgs _b;
860   const gccipher *c; size_t cksz;
861 } naclbox_algs;
862
863 typedef struct naclbox_ctx {
864   bulkctx _b;
865   struct { gcipher *c; } d[NDIR];
866 } naclbox_ctx;
867
868
869 static bulkalgs *naclbox_getalgs(const algswitch *asw, dstr *e,
870                                  key_file *kf, key *k)
871 {
872   naclbox_algs *a = CREATE(naclbox_algs);
873   const char *p;
874   char *qq;
875   unsigned long n;
876
877   /* --- Collect the selected cipher and check that it's supported --- */
878
879   p = key_getattr(kf, k, "cipher");
880   if (!p || strcmp(p, "salsa20") == 0) a->c = &salsa20;
881   else if (strcmp(p, "salsa20/12") == 0) a->c = &salsa2012;
882   else if (strcmp(p, "salsa20/8") == 0) a->c = &salsa208;
883   else if (strcmp(p, "chacha20") == 0) a->c = &chacha20;
884   else if (strcmp(p, "chacha12") == 0) a->c = &chacha12;
885   else if (strcmp(p, "chacha8") == 0) a->c = &chacha8;
886   else {
887     a_format(e, "unknown-cipher", "%s", p, A_END);
888     goto fail;
889   }
890
891   /* --- Collect the selected MAC, and check the tag length --- */
892
893   p = key_getattr(kf, k, "mac");
894   if (!p)
895     ;
896   else if (strncmp(p, "poly1305", 8) != 0 || (p[8] && p[8] != '/')) {
897     a_format(e, "unknown-mac", "%s", p, A_END);
898     goto fail;
899   } else if (p[8] == '/') {
900     n = strtoul(p + 9, &qq, 0);
901     if (*qq) {
902       a_format(e, "bad-tag-length-string", "%s", p + 9, A_END);
903       goto fail;
904     }
905     if (n != 128) {
906       a_format(e, "bad-tag-length", "%lu", n, A_END);
907       goto fail;
908     }
909   }
910
911   return (&a->_b);
912 fail:
913   DESTROY(a);
914   return (0);
915 }
916
917 #ifndef NTRACE
918 static void naclbox_tracealgs(const bulkalgs *aa)
919 {
920   const naclbox_algs *a = (const naclbox_algs *)aa;
921
922   trace(T_CRYPTO, "crypto: cipher = %s", a->c->name);
923   trace(T_CRYPTO, "crypto: mac = poly1305/128");
924 }
925 #endif
926
927 static int naclbox_checkalgs(bulkalgs *aa, const algswitch *asw, dstr *e)
928 {
929   naclbox_algs *a = (naclbox_algs *)aa;
930
931   if ((a->cksz = keysz(asw->hashsz, a->c->keysz)) == 0) {
932     a_format(e, "cipher", "%s", a->c->name,
933              "no-key-size", "%lu", (unsigned long)asw->hashsz,
934              A_END);
935     return (-1);
936   }
937   return (0);
938 }
939
940 static int naclbox_samealgsp(const bulkalgs *aa, const bulkalgs *bb)
941 {
942   const naclbox_algs *a = (const naclbox_algs *)aa,
943     *b = (const naclbox_algs *)bb;
944   return (a->c == b->c);
945 }
946
947 static void naclbox_alginfo(const bulkalgs *aa, admin *adm)
948 {
949   const naclbox_algs *a = (const naclbox_algs *)aa;
950   a_info(adm, "cipher=%s", a->c->name, "cipher-keysz=32", A_END);
951   a_info(adm, "mac=poly1305", "mac-tagsz=16", A_END);
952 }
953
954 static size_t naclbox_overhead(const bulkalgs *aa)
955   { return (POLY1305_TAGSZ + SEQSZ); }
956
957 static size_t naclbox_expsz(const bulkalgs *aa)
958   { return (MEG(2048)); }
959
960 static bulkctx *naclbox_genkeys(const bulkalgs *aa, const deriveargs *da)
961 {
962   const naclbox_algs *a = (const naclbox_algs *)aa;
963   naclbox_ctx *bc = CREATE(naclbox_ctx);
964   octet k[MAXHASHSZ];
965   int i;
966
967   for (i = 0; i < NDIR; i++) {
968     if (!(da->f&(1 << i))) { bc->d[i].c = 0; continue; }
969     derivekey(k, a->cksz, da, i, "encryption");
970     bc->d[i].c = GC_INIT(a->c, k, a->cksz);
971   }
972   return (&bc->_b);
973 }
974
975 typedef struct naclbox_chal {
976   bulkchal _b;
977   gcipher *c;
978 } naclbox_chal;
979
980 static bulkchal *naclbox_genchal(const bulkalgs *aa)
981 {
982   const naclbox_algs *a = (const naclbox_algs *)aa;
983   naclbox_chal *c = CREATE(naclbox_chal);
984   rand_get(RAND_GLOBAL, buf_t, a->cksz);
985   c->c = GC_INIT(a->c, buf_t, a->cksz);
986   IF_TRACING(T_CHAL, {
987     trace(T_CHAL, "chal: generated new challenge key");
988     trace_block(T_CRYPTO, "chal: new key", buf_t, a->cksz);
989   })
990   c->_b.tagsz = 16;
991   return (&c->_b);
992 }
993
994 static int naclbox_chaltag(bulkchal *bc, const void *m, size_t msz, void *t)
995 {
996   naclbox_chal *c = (naclbox_chal *)bc;
997   octet b0[SALSA20_NONCESZ];
998   assert(msz <= sizeof(b0));
999   memcpy(b0, m, msz); memset(b0 + msz, 0, sizeof(b0) - msz);
1000   GC_SETIV(c->c, b0);
1001   GC_ENCRYPT(c->c, 0, t, c->_b.tagsz);
1002   return (0);
1003 }
1004
1005 static int naclbox_chalvrf(bulkchal *bc, const void *m, size_t msz,
1006                            const void *t)
1007 {
1008   naclbox_chal *c = (naclbox_chal *)bc;
1009   octet b0[SALSA20_NONCESZ], b1[16];
1010   assert(msz <= sizeof(b0)); assert(c->_b.tagsz <= sizeof(b1));
1011   memcpy(b0, m, msz); memset(b0 + msz, 0, sizeof(b0) - msz);
1012   GC_SETIV(c->c, b0);
1013   GC_ENCRYPT(c->c, 0, b1, c->_b.tagsz);
1014   return (ct_memeq(t, b1, c->_b.tagsz) ? 0 : -1);
1015 }
1016
1017 static void naclbox_freechal(bulkchal *bc)
1018   { naclbox_chal *c = (naclbox_chal *)bc; GC_DESTROY(c->c); DESTROY(c); }
1019
1020 static void naclbox_freealgs(bulkalgs *aa)
1021   { naclbox_algs *a = (naclbox_algs *)aa; DESTROY(a); }
1022
1023 static void naclbox_freectx(bulkctx *bbc)
1024 {
1025   naclbox_ctx *bc = (naclbox_ctx *)bbc;
1026   int i;
1027
1028   for (i = 0; i < NDIR; i++) { if (bc->d[i].c) GC_DESTROY(bc->d[i].c); }
1029   DESTROY(bc);
1030 }
1031
1032 static int naclbox_encrypt(bulkctx *bbc, unsigned ty,
1033                            buf *b, buf *bb, uint32 seq)
1034 {
1035   naclbox_ctx *bc = (naclbox_ctx *)bbc;
1036   gcipher *c = bc->d[DIR_OUT].c;
1037   poly1305_key polyk;
1038   poly1305_ctx poly;
1039   const octet *p = BCUR(b);
1040   size_t sz = BLEFT(b);
1041   octet *qmac, *qseq, *qpk;
1042
1043   assert(c);
1044
1045   /* --- Determine the ciphertext layout --- */
1046
1047   if (buf_ensure(bb, POLY1305_TAGSZ + SEQSZ + sz)) return (0);
1048   qmac = BCUR(bb); qseq = qmac + POLY1305_TAGSZ; qpk = qseq + SEQSZ;
1049   BSTEP(bb, POLY1305_TAGSZ + SEQSZ + sz);
1050
1051   /* --- Construct and set the nonce --- */
1052
1053   STORE32(qseq, seq);
1054   memcpy(buf_u, qseq, SEQSZ); STORE32(buf_u + SEQSZ, ty);
1055   GC_SETIV(c, buf_u);
1056   TRACE_IV(buf_u, SALSA20_NONCESZ);
1057
1058   /* --- Determine the MAC key --- */
1059
1060   GC_ENCRYPT(c, 0, buf_u, POLY1305_KEYSZ + POLY1305_MASKSZ);
1061   poly1305_keyinit(&polyk, buf_u, POLY1305_KEYSZ);
1062   poly1305_macinit(&poly, &polyk, buf_u + POLY1305_KEYSZ);
1063
1064   /* --- Encrypt the message --- */
1065
1066   GC_ENCRYPT(c, p, qpk, sz);
1067   TRACE_CT(qpk, sz);
1068
1069   /* --- Compute the MAC --- */
1070
1071   poly1305_hash(&poly, qpk, sz);
1072   poly1305_done(&poly, qmac);
1073   TRACE_MAC(qmac, POLY1305_TAGSZ);
1074
1075   /* --- We're done --- */
1076
1077   return (0);
1078 }
1079
1080 static int naclbox_decrypt(bulkctx *bbc, unsigned ty,
1081                        buf *b, buf *bb, uint32 *seq)
1082 {
1083   naclbox_ctx *bc = (naclbox_ctx *)bbc;
1084   gcipher *c = bc->d[DIR_IN].c;
1085   poly1305_key polyk;
1086   poly1305_ctx poly;
1087   const octet *pmac, *pseq, *ppk;
1088   size_t psz = BLEFT(b);
1089   size_t sz;
1090   octet *q = BCUR(bb);
1091
1092   assert(c);
1093
1094   /* --- Break up the packet into its components --- */
1095
1096   if (psz < SEQSZ + POLY1305_TAGSZ) {
1097     T( trace(T_KEYSET, "keyset: block too small for keyset"); )
1098     return (KSERR_MALFORMED);
1099   }
1100   sz = psz - SEQSZ - POLY1305_TAGSZ;
1101   pmac = BCUR(b); pseq = pmac + POLY1305_TAGSZ; ppk = pseq + SEQSZ;
1102
1103   /* --- Construct and set the nonce --- */
1104
1105   memcpy(buf_u, pseq, SEQSZ); STORE32(buf_u + SEQSZ, ty);
1106   GC_SETIV(c, buf_u);
1107   TRACE_IV(buf_u, SALSA20_NONCESZ);
1108
1109   /* --- Determine the MAC key --- */
1110
1111   GC_ENCRYPT(c, 0, buf_u, POLY1305_KEYSZ + POLY1305_MASKSZ);
1112   poly1305_keyinit(&polyk, buf_u, POLY1305_KEYSZ);
1113   poly1305_macinit(&poly, &polyk, buf_u + POLY1305_KEYSZ);
1114
1115   /* --- Verify the MAC on the packet --- */
1116
1117   poly1305_hash(&poly, ppk, sz);
1118   poly1305_done(&poly, buf_u);
1119   TRACE_MAC(buf_u, POLY1305_TAGSZ);
1120   if (!ct_memeq(buf_u, pmac, POLY1305_TAGSZ)) {
1121     TRACE_MACERR(pmac, POLY1305_TAGSZ);
1122     return (KSERR_DECRYPT);
1123   }
1124
1125   /* --- Decrypt the packet --- */
1126
1127   GC_DECRYPT(c, ppk, q, sz);
1128
1129   /* --- Finished --- */
1130
1131   *seq = LOAD32(pseq);
1132   BSTEP(bb, sz);
1133   return (0);
1134 }
1135
1136 /*----- Bulk crypto transform table ---------------------------------------*/
1137
1138 const bulkops bulktab[] = {
1139
1140 #define COMMA ,
1141
1142 #define BULK(name, pre)                                                 \
1143   { name, pre##_getalgs, T( pre##_tracealgs COMMA )                     \
1144     pre##_checkalgs, pre##_samealgsp,                                   \
1145     pre##_alginfo, pre##_overhead, pre##_expsz,                         \
1146     pre##_genkeys, pre##_genchal, pre##_freealgs,                       \
1147     pre##_encrypt, pre##_decrypt, pre##_freectx,                        \
1148     pre##_chaltag, pre##_chalvrf, pre##_freechal }
1149
1150   BULK("v0", v0),
1151   BULK("iiv", iiv),
1152   BULK("naclbox", naclbox),
1153
1154 #undef BULK
1155   { 0 }
1156 };
1157
1158 /*----- That's all, folks -------------------------------------------------*/