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