chiark / gitweb /
Quantify collision probabilities for the stated data volume bounds.
[tripe] / keyset.c
... / ...
CommitLineData
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
84static 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
157static 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
224static 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
262void 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
295keyset *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
363time_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
375void 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
399int 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
423int 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
447void 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
468void 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
487void 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
525int 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
555int 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 -------------------------------------------------*/