chiark / gitweb /
Change ERR response to FAIL for consistency with other programs.
[tripe] / keyset.c
1 /* -*-c-*-
2  *
3  * $Id: keyset.c,v 1.2 2001/02/05 19:53:23 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.2  2001/02/05 19:53:23  mdw
33  * Add sequence number protection.
34  *
35  * Revision 1.1  2001/02/03 20:26:37  mdw
36  * Initial checkin.
37  *
38  */
39
40 /*----- Header files ------------------------------------------------------*/
41
42 #include "tripe.h"
43
44 /*----- Tunable parameters ------------------------------------------------*/
45
46 #define KEY_EXPTIME MIN(60)             /* Expiry time for a key */
47 #define KEY_REGENTIME MIN(45)           /* Regeneration time for a key */
48 #define KEY_EXPSZ MEG(512)              /* Expiry data size for a key */
49 #define KEY_REGENSZ MEG(256)            /* Data size threshold for regen */
50
51 /*----- Handy macros ------------------------------------------------------*/
52
53 #define KEYOK(ks, now) ((ks)->sz_exp > 0 && (ks)->t_exp > now)
54
55 /*----- Main code ---------------------------------------------------------*/
56
57 /* --- @freeks@ --- *
58  *
59  * Arguments:   @keyset *ks@ = pointer to a keyset
60  *
61  * Returns:     ---
62  *
63  * Use:         Frees a keyset.
64  */
65
66 static void freeks(keyset *ks)
67 {
68   ks->c->ops->destroy(ks->c);
69   ks->m->ops->destroy(ks->m);
70   DESTROY(ks);
71 }
72
73 /* --- @ks_free@ --- *
74  *
75  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
76  *
77  * Returns:     ---
78  *
79  * Use:         Frees all of the keys in a keyset.
80  */
81
82 void ks_free(keyset **ksroot)
83 {
84   keyset *ks, *ksn;
85   for (ks = *ksroot; ks; ks = ksn) {
86     ksn = ks->next;
87     freeks(ks);
88   }
89 }
90
91 /* --- @ks_prune@ --- *
92  *
93  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
94  *
95  * Returns:     ---
96  *
97  * Use:         Prunes the keyset list by removing keys which mustn't be used
98  *              any more.
99  */
100
101 void ks_prune(keyset **ksroot)
102 {
103   time_t now = time(0);
104
105   while (*ksroot) {
106     keyset *ks = *ksroot;
107     if (ks->t_exp <= now) {
108       T( trace(T_KEYSET, "keyset: expiring keyset %u (time limit reached)",
109                ks->seq); )
110       *ksroot = ks->next;
111       freeks(ks);
112     } else if (ks->sz_exp == 0) {
113       T( trace(T_KEYSET, "keyset: expiring keyset %u (data limit reached)",
114                ks->seq); )
115       *ksroot = ks->next;
116       freeks(ks);
117     } else
118       ksroot = &ks->next;
119   }
120 }
121
122 /* --- @ks_gen@ --- *
123  *
124  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
125  *              @const void *k@ = pointer to key material
126  *              @size_t sz@ = size of the key material
127  *
128  * Returns:     The regeneration time for the new key.
129  *
130  * Use:         Derives a keyset from the given key material and adds it to
131  *              the list.
132  */
133
134 time_t ks_gen(keyset **ksroot, const void *k, size_t sz)
135 {
136   rmd160_ctx r;
137   octet buf[RMD160_HASHSZ];
138   keyset *ks = CREATE(keyset);
139   time_t now = time(0);
140   T( static unsigned seq = 0; )
141
142   T( trace(T_KEYSET, "keyset: adding new keyset %u", seq); )
143
144 #define GETHASH(str) do {                                               \
145   rmd160_init(&r);                                                      \
146   rmd160_hash(&r, str, sizeof(str) - 1);                                \
147   rmd160_hash(&r, k, sz);                                               \
148   rmd160_done(&r, buf);                                                 \
149   IF_TRACING(T_KEYSET, {                                                \
150     trace_block(T_CRYPTO, "crypto: key " str, buf, sizeof(buf));        \
151   })                                                                    \
152 } while (0)
153
154   GETHASH("tripe-encryption "); ks->c = blowfish_cbc.init(buf, sizeof(buf));
155   GETHASH("tripe-integrity "); ks->m = rmd160_hmac.key(buf, sizeof(buf));
156
157 #undef GETHASH
158
159   T( ks->seq = seq++; )
160   ks->t_exp = now + KEY_EXPTIME;
161   ks->sz_exp = KEY_EXPSZ;
162   ks->oseq = ks->iseq = 0;
163   ks->iwin = 0;
164   ks->next = *ksroot;
165   *ksroot = ks;
166   BURN(buf);
167   return (now + KEY_REGENTIME);
168 }
169
170 /* --- @ks_encrypt@ --- *
171  *
172  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
173  *              @buf *b@ = pointer to input buffer
174  *              @buf *bb@ = pointer to output buffer
175  *
176  * Returns:     Nonzero if a new key is needed.
177  *
178  * Use:         Encrypts a packet.
179  */
180
181 int ks_encrypt(keyset **ksroot, buf *b, buf *bb)
182 {
183   time_t now = time(0);
184   keyset *ks;
185   ghash *h;
186   gcipher *c;
187   size_t ivsz;
188   const octet *p = BCUR(b);
189   size_t sz = BLEFT(b);
190   octet *qiv, *qseq, *qpk;
191   uint32 oseq;
192   size_t osz, nsz;
193   int rc = 0;
194
195   /* --- Get the latest valid key --- */
196
197   ks = *ksroot;
198   for (;;) {
199     if (!ks) {
200       T( trace(T_KEYSET, "keyset: no active keys -- forcing exchange"); )
201       buf_break(bb);
202       return (-1);
203     }
204     if (KEYOK(ks, now))
205       break;
206     ks = ks->next;
207   }
208
209   /* --- Allocate the required buffer space --- */
210
211   c = ks->c;
212   ivsz = c->ops->c->blksz;
213   if (buf_ensure(bb, ivsz + 4 + sz)) return (0);
214   qiv = BCUR(bb); qseq = qiv + ivsz; qpk = qseq + 4;
215   BSTEP(bb, ivsz + 4 + sz);
216
217   /* --- MAC and encrypt the packet --- */
218
219   oseq = ks->oseq++; STORE32(qseq, oseq);
220   h = ks->m->ops->init(ks->m);
221   h->ops->hash(h, qseq, 4);
222   h->ops->hash(h, p, sz);
223   memcpy(qiv, h->ops->done(h, 0), ivsz);
224   h->ops->destroy(h);
225   IF_TRACING(T_KEYSET, {
226     trace(T_KEYSET, "keyset: encrypting packet %lu using keyset %u",
227           (unsigned long)oseq, ks->seq);
228     trace_block(T_CRYPTO, "crypto: computed MAC", qiv, ivsz);
229   })
230   c->ops->setiv(c, qiv);
231   c->ops->encrypt(c, p, qpk, sz);
232   IF_TRACING(T_KEYSET, {
233     trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
234   })
235
236   /* --- Deduct the packet size from the key's data life --- */
237
238   osz = ks->sz_exp;
239   if (osz > sz)
240     nsz = osz - sz;
241   else
242     nsz = 0;
243   if (osz >= KEY_REGENSZ && nsz < KEY_REGENSZ) {
244     T( trace(T_KEYSET, "keyset: keyset %u data regen limit exceeded -- "
245              "forcing exchange", ks->seq); )
246     rc = 1;
247   }
248   ks->sz_exp = nsz;
249   return (rc);
250 }
251
252 /* --- @ks_decrypt@ --- *
253  *
254  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
255  *              @buf *b@ = pointer to input buffer
256  *              @buf *bb@ = pointer to output buffer
257  *
258  * Returns:     Nonzero if the packet couldn't be decrypted.
259  *
260  * Use:         Decrypts a packet.
261  */
262
263 int ks_decrypt(keyset **ksroot, buf *b, buf *bb)
264 {
265   time_t now = time(0);
266   const octet *piv, *pseq, *ppk;
267   size_t psz = BLEFT(b);
268   size_t sz;
269   octet *q = BCUR(bb);
270   uint32 iseq;
271   unsigned seqbit;
272   keyset *ks;
273
274   /* --- Allocate space in the output buffer --- */
275
276   T( trace(T_KEYSET, "keyset: attempting to decrypt packet"); )
277   if (buf_ensure(bb, psz))
278     return (-1);
279
280   /* --- Try all of the valid keys --- */
281
282   for (ks = *ksroot; ks; ks = ks->next) {
283     ghash *h;
284     gcipher *c = ks->c;
285     size_t ivsz = c->ops->c->blksz;
286     octet *mac;
287     int eq;
288
289     /* --- Break up the packet into its components --- */
290
291     if (!KEYOK(ks, now))
292       continue;
293     if (psz < ivsz + 4) {
294       T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
295       continue;
296     }
297     sz = psz - ivsz - 4;
298     piv = BCUR(b); pseq = piv + ivsz; ppk = pseq + 4;
299
300     /* --- Attempt to decrypt the packet --- */
301
302     c->ops->setiv(c, piv);
303     c->ops->decrypt(c, ppk, q, sz);
304     h = ks->m->ops->init(ks->m);
305     h->ops->hash(h, pseq, 4);
306     h->ops->hash(h, q, sz);
307     mac = h->ops->done(h, 0);
308     eq = !memcmp(mac, piv, ivsz);
309     IF_TRACING(T_KEYSET, {
310       trace(T_KEYSET, "keyset: decrypting using keyset %u", ks->seq);
311       trace_block(T_CRYPTO, "crypto: computed MAC", mac, ivsz);
312     })
313     h->ops->destroy(h);
314     if (eq) {
315       BSTEP(bb, sz);
316       goto match;
317     }
318     IF_TRACING(T_KEYSET, {
319       trace(T_KEYSET, "keyset: decryption failed");
320       trace_block(T_CRYPTO, "crypto: expected MAC", piv, ivsz);
321       trace_block(T_CRYPTO, "crypto: invalid packet", q, sz);
322     })
323   }
324   T( trace(T_KEYSET, "keyset: no matching keys"); )
325   return (-1);
326
327   /* --- We've found a match, so check the sequence number --- */
328
329 match:
330   iseq = LOAD32(pseq);
331   IF_TRACING(T_KEYSET, {
332     trace(T_KEYSET, "keyset: decrypted OK (sequence = %lu)",
333           (unsigned long)iseq);
334     trace_block(T_CRYPTO, "crypto: decrypted packet", q, sz);
335   })
336   if (iseq < ks->iseq) {
337     a_warn("received packet has old sequence number (possible replay)");
338     return (-1);
339   }
340   if (iseq >= ks->iseq + KS_SEQWINSZ) {
341     uint32 n = iseq - (ks->iseq + KS_SEQWINSZ - 1);
342     if (n < KS_SEQWINSZ)
343       ks->iwin >>= n;
344     else
345       ks->iwin = 0;
346     ks->iseq += n;
347   }
348   seqbit = 1 << (iseq - ks->iseq);
349   if (ks->iwin & seqbit) {
350     a_warn("received packet repeats old sequence number");
351     return (-1);
352   }
353   ks->iwin |= seqbit;
354   return (0);
355 }
356
357 /*----- That's all, folks -------------------------------------------------*/