chiark / gitweb /
Patch up zero-knowledge property by passing an encrypted log with a
[tripe] / keyset.c
1 /* -*-c-*-
2  *
3  * $Id: keyset.c,v 1.5 2001/06/19 22:07:43 mdw Exp $
4  *
5  * Handling of symmetric keysets
6  *
7  * (c) 2001 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Trivial IP Encryption (TrIPE).
13  *
14  * TrIPE is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * TrIPE is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with TrIPE; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: keyset.c,v $
32  * Revision 1.5  2001/06/19 22:07:43  mdw
33  * Change the encrypted packet format to be non-malleable.
34  *
35  * Revision 1.4  2001/06/16 14:06:40  mdw
36  * Quantify collision probabilities for the stated data volume bounds.
37  *
38  * Revision 1.3  2001/02/16 21:39:55  mdw
39  * Major overhaul.  Separate functions for manipulating keysets from
40  * functions for manipulating keyset lists.  Introduce a concept of
41  * listening-only keys.
42  *
43  * Revision 1.2  2001/02/05 19:53:23  mdw
44  * Add sequence number protection.
45  *
46  * Revision 1.1  2001/02/03 20:26:37  mdw
47  * Initial checkin.
48  *
49  */
50
51 /*----- Header files ------------------------------------------------------*/
52
53 #include "tripe.h"
54
55 /*----- Tunable parameters ------------------------------------------------*/
56
57 /* --- Note on size limits --- *
58  *
59  * For a 64-bit block cipher (e.g., Blowfish), the probability of a collision
60  * occurring after 32 MB is less than %$2^{-21}$%, and the probability of a
61  * collision occurring after 64 MB is less than %$2^{-19}$%.
62  */
63
64 #define T_EXP MIN(60)                   /* Expiry time for a key */
65 #define T_REGEN MIN(45)                 /* Regeneration time for a key */
66 #define SZ_EXP MEG(64)                  /* Expiry data size for a key */
67 #define SZ_REGEN MEG(32)                /* Data size threshold for regen */
68
69 /*----- Handy macros ------------------------------------------------------*/
70
71 #define KEYOK(ks, now) ((ks)->sz_exp > 0 && (ks)->t_exp > now)
72
73 /*----- Low-level packet encryption and decryption ------------------------*/
74
75 /* --- Encrypted data format --- *
76  *
77  * Let %$p_i$% be the %$i$%-th plaintext message.  We first compute
78  *
79  *   %$c_i = \mathcal{E}\textrm{-CBC}_{K_{\text{E}}}(p_i)$%
80  *
81  * as the CBC-ciphertext of %$p_i$%, and then
82  *
83  *   %$\sigma_i = \mathcal{T}_{K_{\text{M}}}(i, c_i)$%
84  *
85  * as a MAC on the %%\emph{ciphertext}%%.  The message sent is then the pair
86  * %$(\sigma_i, c_i)$%.  This construction is provably secure in the NM-CCA
87  * sense (assuming that the cipher is IND-CPA, and the MAC is SUF-CMA)
88  * [Bellare and Namprempre].
89  *
90  * This also ensures that, assuming the key is good, we have a secure channel
91  * [Krawczyk].  Actually, [Krawczyk] shows that, if the cipher is either a
92  * simple stream cipher or a block cipher in CBC mode, we can use the MAC-
93  * then-encrypt scheme and still have a secure channel.  However, I like the
94  * NM-CCA guarantee from [Bellare and Namprempre].  I'm less worried about
95  * the Horton Principle [Wagner and Schneier].
96  */
97
98 /* --- @doencrypt@ --- *
99  *
100  * Arguments:   @keyset *ks@ = pointer to keyset to use
101  *              @buf *b@ = pointer to an input buffer
102  *              @buf *bb@ = pointer to an output buffer
103  *
104  * Returns:     Zero if OK, nonzero if a new key is required.
105  *
106  * Use:         Encrypts a message with the given key.  We assume that the
107  *              keyset is OK to use.
108  */
109
110 static int doencrypt(keyset *ks, buf *b, buf *bb)
111 {
112   ghash *h;
113   gcipher *c;
114   const octet *p = BCUR(b);
115   size_t sz = BLEFT(b);
116   octet *qmac, *qseq, *qiv, *qpk;
117   uint32 oseq;
118   size_t osz, nsz;
119   int rc = 0;
120
121   /* --- Allocate the required buffer space --- */
122
123   c = ks->cout;
124   if (buf_ensure(bb, MACSZ + SEQSZ + IVSZ + sz))
125     return (0); /* Caution! */
126   qmac = BCUR(bb); qseq = qmac + MACSZ; qiv = qseq + SEQSZ; qpk = qiv + IVSZ;
127   BSTEP(bb, MACSZ + SEQSZ + IVSZ + sz);
128
129   /* --- Encrypt the packet --- */
130
131   oseq = ks->oseq++; STORE32(qseq, oseq);
132   rand_get(RAND_GLOBAL, qiv, IVSZ);
133   c->ops->setiv(c, qiv);
134   c->ops->encrypt(c, p, qpk, sz);
135   IF_TRACING(T_KEYSET, {
136     trace(T_KEYSET, "keyset: encrypting packet %lu using keyset %u",
137           (unsigned long)oseq, ks->seq);
138     trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
139   })
140
141   /* --- Now compute the MAC --- */
142
143   h = ks->mout->ops->init(ks->mout);
144   h->ops->hash(h, qseq, SEQSZ + IVSZ + sz);
145   memcpy(qmac, h->ops->done(h, 0), MACSZ);
146   h->ops->destroy(h);
147   IF_TRACING(T_KEYSET, {
148     trace_block(T_CRYPTO, "crypto: computed MAC", qmac, MACSZ);
149   })
150
151   /* --- Deduct the packet size from the key's data life --- */
152
153   osz = ks->sz_exp;
154   if (osz > sz)
155     nsz = osz - sz;
156   else
157     nsz = 0;
158   if (osz >= SZ_REGEN && nsz < SZ_REGEN) {
159     T( trace(T_KEYSET, "keyset: keyset %u data regen limit exceeded -- "
160              "forcing exchange", ks->seq); )
161     rc = -1;
162   }
163   ks->sz_exp = nsz;
164   return (rc);  
165 }
166
167 /* --- @dodecrypt@ --- *
168  *
169  * Arguments:   @keyset *ks@ = pointer to keyset to use
170  *              @buf *b@ = pointer to an input buffer
171  *              @buf *bb@ = pointer to an output buffer
172  *              @uint32 *seq@ = where to store the sequence number
173  *
174  * Returns:     Zero if OK, nonzero if it failed.
175  *
176  * Use:         Attempts to decrypt a message with the given key.  No other
177  *              checking (e.g., sequence number checks) is performed.  We
178  *              assume that the keyset is OK to use, and that there is
179  *              sufficient output buffer space reserved.  If the decryption
180  *              is successful, the buffer pointer is moved past the decrypted
181  *              packet, and the packet's sequence number is stored in @*seq@.
182  */
183
184 static int dodecrypt(keyset *ks, buf *b, buf *bb, uint32 *seq)
185 {
186   const octet *pmac, *piv, *pseq, *ppk;
187   size_t psz = BLEFT(b);
188   size_t sz;
189   octet *q = BCUR(bb);
190   ghash *h;
191   gcipher *c = ks->cin;
192   size_t ivsz = c->ops->c->blksz;
193   octet *mac;
194   int eq;
195
196   /* --- Break up the packet into its components --- */
197
198   if (psz < ivsz + 4) {
199     T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
200     return (-1);
201   }
202   sz = psz - IVSZ - SEQSZ - MACSZ;
203   pmac = BCUR(b); pseq = pmac + MACSZ; piv = pseq + SEQSZ; ppk = piv + IVSZ;
204
205   /* --- Verify the MAC on the packet --- */
206
207   h = ks->min->ops->init(ks->min);
208   h->ops->hash(h, pseq, SEQSZ + IVSZ + sz);
209   mac = h->ops->done(h, 0);
210   eq = !memcmp(mac, pmac, MACSZ);
211   IF_TRACING(T_KEYSET, {
212     trace(T_KEYSET, "keyset: decrypting using keyset %u", ks->seq);
213     trace_block(T_CRYPTO, "crypto: computed MAC", mac, MACSZ);
214   })
215   h->ops->destroy(h);
216   if (!eq) {
217     IF_TRACING(T_KEYSET, {
218       trace(T_KEYSET, "keyset: decryption failed");
219       trace_block(T_CRYPTO, "crypto: expected MAC", pmac, MACSZ);
220     })
221     return (-1);
222   }
223
224   /* --- Decrypt the packet --- */
225
226   c->ops->setiv(c, piv);
227   c->ops->decrypt(c, ppk, q, sz);
228   if (seq)
229     *seq = LOAD32(pseq);
230   IF_TRACING(T_KEYSET, {
231     trace(T_KEYSET, "keyset: decrypted OK (sequence = %lu)",
232           (unsigned long)LOAD32(pseq));
233     trace_block(T_CRYPTO, "crypto: decrypted packet", q, sz);
234   })
235   BSTEP(bb, sz);
236   return (0);
237 }
238
239 /* --- @dosequence@ --- *
240  *
241  * Arguments:   @keyset *ks@ = pointer to a keyset
242  *              @uint32 seq@ = a sequence number from a packet
243  *
244  * Returns:     Zero if the sequence number is OK, nonzero if it's not.
245  *
246  * Use:         Checks a sequence number.  The data in the keyset which keeps
247  *              track of valid sequence numbers is updated if the sequence
248  *              number given is good.  It's assumed that the sequence number
249  *              has already been checked for authenticity.
250  */
251
252 static int dosequence(keyset *ks, uint32 seq)
253 {
254   uint32 seqbit;
255   uint32 n;
256
257   if (seq < ks->iseq) {
258     a_warn("received packet has old sequence number (possible replay)");
259     return (-1);
260   }
261   if (seq >= ks->iseq + KS_SEQWINSZ) {
262     n = seq - (ks->iseq + KS_SEQWINSZ - 1);
263     if (n < KS_SEQWINSZ)
264       ks->iwin >>= n;
265     else
266       ks->iwin = 0;
267     ks->iseq += n;
268   }
269   seqbit = 1 << (seq - ks->iseq);
270   if (ks->iwin & seqbit) {
271     a_warn("received packet repeats old sequence number");
272     return (-1);
273   }
274   ks->iwin |= seqbit;
275   return (0);
276 }
277
278 /*----- Operations on a single keyset -------------------------------------*/
279
280 /* --- @ks_drop@ --- *
281  *
282  * Arguments:   @keyset *ks@ = pointer to a keyset
283  *
284  * Returns:     ---
285  *
286  * Use:         Decrements a keyset's reference counter.  If the counter hits
287  *              zero, the keyset is freed.
288  */
289
290 void ks_drop(keyset *ks)
291 {
292   if (--ks->ref)
293     return;
294   ks->cin->ops->destroy(ks->cin);
295   ks->cout->ops->destroy(ks->cout);
296   ks->min->ops->destroy(ks->min);
297   ks->mout->ops->destroy(ks->mout);
298   DESTROY(ks);
299 }
300
301 /* --- @ks_gen@ --- *
302  *
303  * Arguments:   @const void *k@ = pointer to key material
304  *              @size_t x, y, z@ = offsets into key material (see below)
305  *
306  * Returns:     A pointer to the new keyset.
307  *
308  * Use:         Derives a new keyset from the given key material.  The
309  *              offsets @x@, @y@ and @z@ separate the key material into three
310  *              parts.  Between the @k@ and @k + x@ is `my' contribution to
311  *              the key material; between @k + x@ and @k + y@ is `your'
312  *              contribution; and between @k + y@ and @k + z@ is a shared
313  *              value we made together.  These are used to construct two
314  *              pairs of symmetric keys.  Each pair consists of an encryption
315  *              key and a message authentication key.  One pair is used for
316  *              outgoing messages, the other for incoming messages.
317  *
318  *              The new key is marked so that it won't be selected for output
319  *              by @ksl_encrypt@.  You can still encrypt data with it by
320  *              calling @ks_encrypt@ directly.
321  */
322
323 keyset *ks_gen(const void *k, size_t x, size_t y, size_t z)
324 {
325   HASH_CTX h;
326   octet buf[HASHSZ];
327   keyset *ks = CREATE(keyset);
328   time_t now = time(0);
329   const octet *p = k;
330   T( static unsigned seq = 0; )
331
332   T( trace(T_KEYSET, "keyset: adding new keyset %u", seq); )
333
334   /* --- Construct the various keys --- *
335    *
336    * This is done with macros, because it's quite tedious.
337    */
338
339 #define MINE HASH(&h, p, x)
340 #define YOURS HASH(&h, p + x, y - x)
341 #define OURS HASH(&h, p + y, z - y)
342
343 #define IN MINE; YOURS; OURS
344 #define OUT YOURS; MINE; OURS
345 #define STR_IN "incoming"
346 #define STR_OUT "outgoing"
347
348 #define GETHASH(str, dir) do {                                          \
349   HASH_INIT(&h);                                                        \
350   HASH_STRING(&h, "tripe-" str);                                        \
351   dir;                                                                  \
352   HASH_DONE(&h, buf);                                                   \
353   IF_TRACING(T_KEYSET, {                                                \
354     trace_block(T_CRYPTO, "crypto: " STR_##dir " key " str,             \
355                 buf, sizeof(buf));                                      \
356   })                                                                    \
357 } while (0)
358
359   GETHASH("encryption", IN); ks->cin = CIPHER->init(buf, sizeof(buf));
360   GETHASH("integrity", IN); ks->min = MAC->key(buf, sizeof(buf));
361   GETHASH("encryption", OUT); ks->cout = CIPHER->init(buf, sizeof(buf));
362   GETHASH("integrity", OUT); ks->mout = MAC->key(buf, sizeof(buf));
363
364 #undef MINE
365 #undef YOURS
366 #undef OURS
367 #undef IN
368 #undef OUT
369 #undef STR_IN
370 #undef STR_OUT
371 #undef GETHASH
372
373   T( ks->seq = seq++; )
374   ks->t_exp = now + T_EXP;
375   ks->sz_exp = SZ_EXP;
376   ks->oseq = ks->iseq = 0;
377   ks->iwin = 0;
378   ks->next = 0;
379   ks->f = KSF_LISTEN;
380   BURN(buf);
381   return (ks);
382 }
383
384 /* --- @ks_tregen@ --- *
385  *
386  * Arguments:   @keyset *ks@ = pointer to a keyset
387  *
388  * Returns:     The time at which moves ought to be made to replace this key.
389  */
390
391 time_t ks_tregen(keyset *ks) { return (ks->t_exp - T_EXP + T_REGEN); }
392
393 /* --- @ks_activate@ --- *
394  *
395  * Arguments:   @keyset *ks@ = pointer to a keyset
396  *
397  * Returns:     ---
398  *
399  * Use:         Activates a keyset, so that it can be used for encrypting
400  *              outgoing messages.
401  */
402
403 void ks_activate(keyset *ks)
404 {
405   if (ks->f & KSF_LISTEN) {
406     T( trace(T_KEYSET, "keyset: activating keyset %u", ks->seq); )
407     ks->f &= ~KSF_LISTEN;
408   }
409 }
410
411 /* --- @ks_encrypt@ --- *
412  *
413  * Arguments:   @keyset *ks@ = pointer to a keyset
414  *              @buf *b@ = pointer to input buffer
415  *              @buf *bb@ = pointer to output buffer
416  *
417  * Returns:     Zero if OK, nonzero if the key needs replacing.  If the
418  *              encryption failed, the output buffer is broken and zero is
419  *              returned.
420  *
421  * Use:         Encrypts a block of data using the key.  Note that the `key
422  *              ought to be replaced' notification is only ever given once
423  *              for each key.  Also note that this call forces a keyset to be
424  *              used even if it's marked as not for data output.
425  */
426
427 int ks_encrypt(keyset *ks, buf *b, buf *bb)
428 {
429   time_t now = time(0);
430
431   if (!KEYOK(ks, now)) {
432     buf_break(bb);
433     return (0);
434   }
435   return (doencrypt(ks, b, bb));
436 }
437
438 /* --- @ks_decrypt@ --- *
439  *
440  * Arguments:   @keyset *ks@ = pointer to a keyset
441  *              @buf *b@ = pointer to an input buffer
442  *              @buf *bb@ = pointer to an output buffer
443  *
444  * Returns:     Zero on success, or nonzero if there was some problem.
445  *
446  * Use:         Attempts to decrypt a message using a given key.  Note that
447  *              requesting decryption with a key directly won't clear a
448  *              marking that it's not for encryption.
449  */
450
451 int ks_decrypt(keyset *ks, buf *b, buf *bb)
452 {
453   time_t now = time(0);
454   uint32 seq;
455
456   if (!KEYOK(ks, now) ||
457       buf_ensure(bb, BLEN(b)) ||
458       dodecrypt(ks, b, bb, &seq) ||
459       dosequence(ks, seq))
460     return (-1);
461   return (0);
462 }
463
464 /*----- Keyset list handling ----------------------------------------------*/
465
466 /* --- @ksl_free@ --- *
467  *
468  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
469  *
470  * Returns:     ---
471  *
472  * Use:         Frees (releases references to) all of the keys in a keyset.
473  */
474
475 void ksl_free(keyset **ksroot)
476 {
477   keyset *ks, *ksn;
478   for (ks = *ksroot; ks; ks = ksn) {
479     ksn = ks->next;
480     ks->f &= ~KSF_LINK;
481     ks_drop(ks);
482   }
483 }
484
485 /* --- @ksl_link@ --- *
486  *
487  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
488  *              @keyset *ks@ = pointer to a keyset
489  *
490  * Returns:     ---
491  *
492  * Use:         Links a keyset into a list.  A keyset can only be on one list
493  *              at a time.  Bad things happen otherwise.
494  */
495
496 void ksl_link(keyset **ksroot, keyset *ks)
497 {
498   assert(!(ks->f & KSF_LINK));
499   ks->next = *ksroot;
500   *ksroot = ks;
501   ks->f |= KSF_LINK;
502   ks->ref++;
503 }
504
505 /* --- @ksl_prune@ --- *
506  *
507  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
508  *
509  * Returns:     ---
510  *
511  * Use:         Prunes the keyset list by removing keys which mustn't be used
512  *              any more.
513  */
514
515 void ksl_prune(keyset **ksroot)
516 {
517   time_t now = time(0);
518
519   while (*ksroot) {
520     keyset *ks = *ksroot;
521
522     if (ks->t_exp <= now) {
523       T( trace(T_KEYSET, "keyset: expiring keyset %u (time limit reached)",
524                ks->seq); )
525       goto kill;
526     } else if (ks->sz_exp == 0) {
527       T( trace(T_KEYSET, "keyset: expiring keyset %u (data limit reached)",
528                ks->seq); )
529       goto kill;
530     } else {
531       ksroot = &ks->next;
532       continue;
533     }
534
535   kill:
536     *ksroot = ks->next;
537     ks->f &= ~KSF_LINK;
538     ks_drop(ks);
539   }
540 }
541
542 /* --- @ksl_encrypt@ --- *
543  *
544  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
545  *              @buf *b@ = pointer to input buffer
546  *              @buf *bb@ = pointer to output buffer
547  *
548  * Returns:     Nonzero if a new key is needed.
549  *
550  * Use:         Encrypts a packet.
551  */
552
553 int ksl_encrypt(keyset **ksroot, buf *b, buf *bb)
554 {
555   time_t now = time(0);
556   keyset *ks = *ksroot;
557
558   for (;;) {
559     if (!ks) {
560       T( trace(T_KEYSET, "keyset: no suitable keysets found"); )
561       buf_break(bb);
562       return (-1);
563     }
564     if (KEYOK(ks, now) && !(ks->f & KSF_LISTEN))
565       break;
566     ks = ks->next;
567   }
568
569   return (doencrypt(ks, b, bb));
570 }
571
572 /* --- @ksl_decrypt@ --- *
573  *
574  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
575  *              @buf *b@ = pointer to input buffer
576  *              @buf *bb@ = pointer to output buffer
577  *
578  * Returns:     Nonzero if the packet couldn't be decrypted.
579  *
580  * Use:         Decrypts a packet.
581  */
582
583 int ksl_decrypt(keyset **ksroot, buf *b, buf *bb)
584 {
585   time_t now = time(0);
586   keyset *ks;
587   uint32 seq;
588
589   if (buf_ensure(bb, BLEN(b)))
590     return (-1);
591
592   for (ks = *ksroot; ks; ks = ks->next) {
593     if (!KEYOK(ks, now))
594       continue;
595     if (!dodecrypt(ks, b, bb, &seq)) {
596       if (ks->f & KSF_LISTEN) {
597         T( trace(T_KEYSET, "keyset: implicitly activating keyset %u",
598                  ks->seq); )
599         ks->f &= ~KSF_LISTEN;
600       }
601       return (dosequence(ks, seq));
602     }
603   }
604   T( trace(T_KEYSET, "keyset: no matching keys"); )
605   return (-1);
606 }
607
608 /*----- That's all, folks -------------------------------------------------*/