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