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