chiark / gitweb /
9b804ea67ad8734d4c3347f52e0abe77e1cafee1
[tripe] / keyset.c
1 /* -*-c-*-
2  *
3  * $Id$
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 /*----- Header files ------------------------------------------------------*/
30
31 #include "tripe.h"
32
33 /*----- Tunable parameters ------------------------------------------------*/
34
35 /* --- Note on size limits --- *
36  *
37  * For a 64-bit block cipher (e.g., Blowfish), the probability of a collision
38  * occurring after 32 MB is less than %$2^{-21}$%, and the probability of a
39  * collision occurring after 64 MB is less than %$2^{-19}$%.  These could be
40  * adjusted dependent on the encryption scheme, but it's too much pain.
41  */
42
43 #define T_EXP MIN(60)                   /* Expiry time for a key */
44 #define T_REGEN MIN(45)                 /* Regeneration time for a key */
45 #define SZ_EXP MEG(64)                  /* Expiry data size for a key */
46 #define SZ_REGEN MEG(32)                /* 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 #define SEQSZ 4                         /* Size of sequence number packet */
53
54 /*----- Low-level packet encryption and decryption ------------------------*/
55
56 /* --- Encrypted data format --- *
57  *
58  * Let %$p_i$% be the %$i$%-th plaintext message, with type %$t$%.  We first
59  * compute 
60  *
61  *   %$c_i = \mathcal{E}\textrm{-CBC}_{K_{\text{E}}}(p_i)$%
62  *
63  * as the CBC-ciphertext of %$p_i$%, and then
64  *
65  *   %$\sigma_i = \mathcal{T}_{K_{\text{M}}}(t, i, c_i)$%
66  *
67  * as a MAC on the %%\emph{ciphertext}%%.  The message sent is then the pair
68  * %$(\sigma_i, c_i)$%.  This construction is provably secure in the NM-CCA
69  * sense (assuming that the cipher is IND-CPA, and the MAC is SUF-CMA)
70  * [Bellare and Namprempre].
71  *
72  * This also ensures that, assuming the key is good, we have a secure channel
73  * [Krawczyk].  Actually, [Krawczyk] shows that, if the cipher is either a
74  * simple stream cipher or a block cipher in CBC mode, we can use the MAC-
75  * then-encrypt scheme and still have a secure channel.  However, I like the
76  * NM-CCA guarantee from [Bellare and Namprempre].  I'm less worried about
77  * the Horton Principle [Wagner and Schneier].
78  */
79
80 /* --- @doencrypt@ --- *
81  *
82  * Arguments:   @keyset *ks@ = pointer to keyset to use
83  *              @unsigned ty@ = type of message this is
84  *              @buf *b@ = pointer to an input buffer
85  *              @buf *bb@ = pointer to an output buffer
86  *
87  * Returns:     Zero if OK, nonzero if a new key is required.
88  *
89  * Use:         Encrypts a message with the given key.  We assume that the
90  *              keyset is OK to use.
91  */
92
93 static int doencrypt(keyset *ks, unsigned ty, buf *b, buf *bb)
94 {
95   ghash *h;
96   gcipher *c = ks->cout;
97   const octet *p = BCUR(b);
98   size_t sz = BLEFT(b);
99   octet *qmac, *qseq, *qiv, *qpk;
100   uint32 oseq;
101   size_t ivsz = GC_CLASS(c)->blksz;
102   size_t tagsz = ks->tagsz;
103   size_t osz, nsz;
104   octet t[4];
105   int rc = 0;
106
107   /* --- Allocate the required buffer space --- */
108
109   if (buf_ensure(bb, tagsz + SEQSZ + ivsz + sz))
110     return (0); /* Caution! */
111   qmac = BCUR(bb); qseq = qmac + tagsz; qiv = qseq + SEQSZ; qpk = qiv + ivsz;
112   BSTEP(bb, tagsz + SEQSZ + ivsz + sz);
113   STORE32(t, ty);
114
115   oseq = ks->oseq++; STORE32(qseq, oseq);
116   IF_TRACING(T_KEYSET, {
117     trace(T_KEYSET, "keyset: encrypting packet %lu using keyset %u",
118           (unsigned long)oseq, ks->seq);
119     trace_block(T_CRYPTO, "crypto: plaintext packet", p, sz);
120   })
121
122   /* --- Encrypt the packet --- */
123
124   if (ivsz) {
125     rand_get(RAND_GLOBAL, qiv, ivsz);
126     GC_SETIV(c, qiv);
127     IF_TRACING(T_KEYSET, {
128       trace_block(T_CRYPTO, "crypto: initialization vector", qiv, ivsz);
129     })
130   }
131   GC_ENCRYPT(c, p, qpk, sz);
132   IF_TRACING(T_KEYSET, {
133     trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
134   })
135
136   /* --- Now compute the MAC --- */
137
138   if (tagsz) {
139     h = GM_INIT(ks->mout);
140     GH_HASH(h, t, sizeof(t));
141     GH_HASH(h, qseq, SEQSZ + ivsz + sz);
142     memcpy(qmac, GH_DONE(h, 0), tagsz);
143     GH_DESTROY(h);
144     IF_TRACING(T_KEYSET, {
145       trace_block(T_CRYPTO, "crypto: computed MAC", qmac, tagsz);
146     })
147   }
148
149   /* --- Deduct the packet size from the key's data life --- */
150
151   osz = ks->sz_exp;
152   if (osz > sz)
153     nsz = osz - sz;
154   else
155     nsz = 0;
156   if (osz >= SZ_REGEN && nsz < SZ_REGEN) {
157     T( trace(T_KEYSET, "keyset: keyset %u data regen limit exceeded -- "
158              "forcing exchange", ks->seq); )
159     rc = -1;
160   }
161   ks->sz_exp = nsz;
162   return (rc);  
163 }
164
165 /* --- @dodecrypt@ --- *
166  *
167  * Arguments:   @keyset *ks@ = pointer to keyset to use
168  *              @unsigned ty@ = expected type code
169  *              @buf *b@ = pointer to an input buffer
170  *              @buf *bb@ = pointer to an output buffer
171  *              @uint32 *seq@ = where to store the sequence number
172  *
173  * Returns:     Zero if OK, nonzero if it failed.
174  *
175  * Use:         Attempts to decrypt a message with the given key.  No other
176  *              checking (e.g., sequence number checks) is performed.  We
177  *              assume that the keyset is OK to use, and that there is
178  *              sufficient output buffer space reserved.  If the decryption
179  *              is successful, the buffer pointer is moved past the decrypted
180  *              packet, and the packet's sequence number is stored in @*seq@.
181  */
182
183 static int dodecrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq)
184 {
185   const octet *pmac, *piv, *pseq, *ppk;
186   size_t psz = BLEFT(b);
187   size_t sz;
188   octet *q = BCUR(bb);
189   ghash *h;
190   gcipher *c = ks->cin;
191   size_t ivsz = GC_CLASS(c)->blksz;
192   size_t tagsz = ks->tagsz;
193   octet *mac;
194   int eq;
195   octet t[4];
196
197   /* --- Break up the packet into its components --- */
198
199   if (psz < ivsz + SEQSZ + tagsz) {
200     T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
201     return (-1);
202   }
203   sz = psz - ivsz - SEQSZ - tagsz;
204   pmac = BCUR(b); pseq = pmac + tagsz; piv = pseq + SEQSZ; ppk = piv + ivsz;
205   STORE32(t, ty);
206
207   IF_TRACING(T_KEYSET, {
208     trace(T_KEYSET, "keyset: decrypting using keyset %u", ks->seq);
209     trace_block(T_CRYPTO, "crypto: ciphertext packet", ppk, sz);
210   })
211
212   /* --- Verify the MAC on the packet --- */
213
214   if (tagsz) {
215     h = GM_INIT(ks->min);
216     GH_HASH(h, t, sizeof(t));
217     GH_HASH(h, pseq, SEQSZ + ivsz + sz);
218     mac = GH_DONE(h, 0);
219     eq = !memcmp(mac, pmac, tagsz);
220     IF_TRACING(T_KEYSET, {
221       trace_block(T_CRYPTO, "crypto: computed MAC", mac, tagsz);
222     })
223     GH_DESTROY(h);
224     if (!eq) {
225       IF_TRACING(T_KEYSET, {
226         trace(T_KEYSET, "keyset: incorrect MAC: decryption failed");
227         trace_block(T_CRYPTO, "crypto: expected MAC", pmac, tagsz);
228       })
229       return (-1);
230     }
231   }
232
233   /* --- Decrypt the packet --- */
234
235   if (ivsz) {
236     GC_SETIV(c, piv);
237     IF_TRACING(T_KEYSET, {
238       trace_block(T_CRYPTO, "crypto: initialization vector", piv, ivsz);
239     })
240   }
241   GC_DECRYPT(c, ppk, q, sz);
242   if (seq)
243     *seq = LOAD32(pseq);
244   IF_TRACING(T_KEYSET, {
245     trace(T_KEYSET, "keyset: decrypted OK (sequence = %lu)",
246           (unsigned long)LOAD32(pseq));
247     trace_block(T_CRYPTO, "crypto: decrypted packet", q, sz);
248   })
249   BSTEP(bb, sz);
250   return (0);
251 }
252
253 /* --- @dosequence@ --- *
254  *
255  * Arguments:   @keyset *ks@ = pointer to a keyset
256  *              @uint32 seq@ = a sequence number from a packet
257  *
258  * Returns:     Zero if the sequence number is OK, nonzero if it's not.
259  *
260  * Use:         Checks a sequence number.  The data in the keyset which keeps
261  *              track of valid sequence numbers is updated if the sequence
262  *              number given is good.  It's assumed that the sequence number
263  *              has already been checked for authenticity.
264  */
265
266 static int dosequence(keyset *ks, uint32 seq)
267 {
268   switch (seq_check(&ks->iseq, seq)) {
269     case SEQ_OK: break;
270     case SEQ_OLD: a_warn("SYMM replay old-sequence"); return (-1);
271     case SEQ_REPLAY: a_warn("SYMM replay duplicated-sequence"); return (-1);
272     default: abort();
273   }
274   return (0);
275 }
276
277 /*----- Operations on a single keyset -------------------------------------*/
278
279 /* --- @ks_drop@ --- *
280  *
281  * Arguments:   @keyset *ks@ = pointer to a keyset
282  *
283  * Returns:     ---
284  *
285  * Use:         Decrements a keyset's reference counter.  If the counter hits
286  *              zero, the keyset is freed.
287  */
288
289 void ks_drop(keyset *ks)
290 {
291   if (--ks->ref)
292     return;
293   GC_DESTROY(ks->cin);
294   GC_DESTROY(ks->cout);
295   GM_DESTROY(ks->min);
296   GM_DESTROY(ks->mout);
297   DESTROY(ks);
298 }
299
300 /* --- @ks_gen@ --- *
301  *
302  * Arguments:   @const void *k@ = pointer to key material
303  *              @size_t x, y, z@ = offsets into key material (see below)
304  *              @peer *p@ = pointer to peer information 
305  *
306  * Returns:     A pointer to the new keyset.
307  *
308  * Use:         Derives a new keyset from the given key material.  The
309  *              offsets @x@, @y@ and @z@ separate the key material into three
310  *              parts.  Between the @k@ and @k + x@ is `my' contribution to
311  *              the key material; between @k + x@ and @k + y@ is `your'
312  *              contribution; and between @k + y@ and @k + z@ is a shared
313  *              value we made together.  These are used to construct two
314  *              pairs of symmetric keys.  Each pair consists of an encryption
315  *              key and a message authentication key.  One pair is used for
316  *              outgoing messages, the other for incoming messages.
317  *
318  *              The new key is marked so that it won't be selected for output
319  *              by @ksl_encrypt@.  You can still encrypt data with it by
320  *              calling @ks_encrypt@ directly.
321  */
322
323 keyset *ks_gen(const void *k, size_t x, size_t y, size_t z, peer *p)
324 {
325   ghash *h;
326   const octet *hh;
327   keyset *ks = CREATE(keyset);
328   time_t now = time(0);
329   const octet *pp = k;
330   T( static unsigned seq = 0; )
331
332   T( trace(T_KEYSET, "keyset: adding new keyset %u", seq); )
333
334   /* --- Construct the various keys --- *
335    *
336    * This is done with macros, because it's quite tedious.
337    */
338
339 #define MINE GH_HASH(h, pp, x)
340 #define YOURS GH_HASH(h, pp + x, y - x)
341 #define OURS GH_HASH(h, pp + y, z - y)
342
343 #define HASH_in MINE; YOURS; OURS
344 #define HASH_out YOURS; MINE; OURS
345 #define INIT_c(k) GC_INIT(algs.c, (k), algs.cksz)
346 #define INIT_m(k) GM_KEY(algs.m, (k), algs.mksz)
347 #define STR_c "encryption"
348 #define STR_m "integrity"
349 #define STR_in "incoming"
350 #define STR_out "outgoing"
351
352 #define SETKEY(a, dir) do {                                             \
353   h = GH_INIT(algs.h);                                                  \
354   HASH_STRING(h, "tripe-" STR_##a);                                     \
355   HASH_##dir;                                                           \
356   hh = GH_DONE(h, 0);                                                   \
357   IF_TRACING(T_KEYSET, {                                                \
358     trace_block(T_CRYPTO, "crypto: " STR_##dir " key " STR_##a,         \
359                 hh, algs.a##ksz);                                       \
360   })                                                                    \
361   ks->a##dir = INIT_##a(hh);                                            \
362   GH_DESTROY(h);                                                        \
363 } while (0)
364
365   SETKEY(c, in); SETKEY(c, out);
366   SETKEY(m, in); SETKEY(m, out);
367
368 #undef MINE
369 #undef YOURS
370 #undef OURS
371 #undef STR_c
372 #undef STR_m
373 #undef STR_in
374 #undef STR_out
375 #undef INIT_c
376 #undef INIT_m
377 #undef HASH_in
378 #undef HASH_out
379 #undef SETKEY
380
381   T( ks->seq = seq++; )
382   ks->ref = 1;
383   ks->t_exp = now + T_EXP;
384   ks->sz_exp = SZ_EXP;
385   ks->oseq = 0;
386   seq_reset(&ks->iseq);
387   ks->next = 0;
388   ks->p = p;
389   ks->f = KSF_LISTEN;
390   ks->tagsz = algs.tagsz;
391   return (ks);
392 }
393
394 /* --- @ks_tregen@ --- *
395  *
396  * Arguments:   @keyset *ks@ = pointer to a keyset
397  *
398  * Returns:     The time at which moves ought to be made to replace this key.
399  */
400
401 time_t ks_tregen(keyset *ks) { return (ks->t_exp - T_EXP + T_REGEN); }
402
403 /* --- @ks_activate@ --- *
404  *
405  * Arguments:   @keyset *ks@ = pointer to a keyset
406  *
407  * Returns:     ---
408  *
409  * Use:         Activates a keyset, so that it can be used for encrypting
410  *              outgoing messages.
411  */
412
413 void ks_activate(keyset *ks)
414 {
415   if (ks->f & KSF_LISTEN) {
416     T( trace(T_KEYSET, "keyset: activating keyset %u", ks->seq); )
417     ks->f &= ~KSF_LISTEN;
418   }
419 }
420
421 /* --- @ks_encrypt@ --- *
422  *
423  * Arguments:   @keyset *ks@ = pointer to a keyset
424  *              @unsigned ty@ = message type
425  *              @buf *b@ = pointer to input buffer
426  *              @buf *bb@ = pointer to output buffer
427  *
428  * Returns:     Zero if OK, nonzero if the key needs replacing.  If the
429  *              encryption failed, the output buffer is broken and zero is
430  *              returned.
431  *
432  * Use:         Encrypts a block of data using the key.  Note that the `key
433  *              ought to be replaced' notification is only ever given once
434  *              for each key.  Also note that this call forces a keyset to be
435  *              used even if it's marked as not for data output.
436  */
437
438 int ks_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb)
439 {
440   time_t now = time(0);
441
442   if (!KEYOK(ks, now)) {
443     buf_break(bb);
444     return (0);
445   }
446   return (doencrypt(ks, ty, b, bb));
447 }
448
449 /* --- @ks_decrypt@ --- *
450  *
451  * Arguments:   @keyset *ks@ = pointer to a keyset
452  *              @unsigned ty@ = expected type code
453  *              @buf *b@ = pointer to an input buffer
454  *              @buf *bb@ = pointer to an output buffer
455  *
456  * Returns:     Zero on success, or nonzero if there was some problem.
457  *
458  * Use:         Attempts to decrypt a message using a given key.  Note that
459  *              requesting decryption with a key directly won't clear a
460  *              marking that it's not for encryption.
461  */
462
463 int ks_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb)
464 {
465   time_t now = time(0);
466   uint32 seq;
467
468   if (!KEYOK(ks, now) ||
469       buf_ensure(bb, BLEN(b)) ||
470       dodecrypt(ks, ty, b, bb, &seq) ||
471       dosequence(ks, seq))
472     return (-1);
473   return (0);
474 }
475
476 /*----- Keyset list handling ----------------------------------------------*/
477
478 /* --- @ksl_free@ --- *
479  *
480  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
481  *
482  * Returns:     ---
483  *
484  * Use:         Frees (releases references to) all of the keys in a keyset.
485  */
486
487 void ksl_free(keyset **ksroot)
488 {
489   keyset *ks, *ksn;
490   for (ks = *ksroot; ks; ks = ksn) {
491     ksn = ks->next;
492     ks->f &= ~KSF_LINK;
493     ks_drop(ks);
494   }
495 }
496
497 /* --- @ksl_link@ --- *
498  *
499  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
500  *              @keyset *ks@ = pointer to a keyset
501  *
502  * Returns:     ---
503  *
504  * Use:         Links a keyset into a list.  A keyset can only be on one list
505  *              at a time.  Bad things happen otherwise.
506  */
507
508 void ksl_link(keyset **ksroot, keyset *ks)
509 {
510   assert(!(ks->f & KSF_LINK));
511   ks->next = *ksroot;
512   *ksroot = ks;
513   ks->f |= KSF_LINK;
514   ks->ref++;
515 }
516
517 /* --- @ksl_prune@ --- *
518  *
519  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
520  *
521  * Returns:     ---
522  *
523  * Use:         Prunes the keyset list by removing keys which mustn't be used
524  *              any more.
525  */
526
527 void ksl_prune(keyset **ksroot)
528 {
529   time_t now = time(0);
530
531   while (*ksroot) {
532     keyset *ks = *ksroot;
533
534     if (ks->t_exp <= now) {
535       T( trace(T_KEYSET, "keyset: expiring keyset %u (time limit reached)",
536                ks->seq); )
537       goto kill;
538     } else if (ks->sz_exp == 0) {
539       T( trace(T_KEYSET, "keyset: expiring keyset %u (data limit reached)",
540                ks->seq); )
541       goto kill;
542     } else {
543       ksroot = &ks->next;
544       continue;
545     }
546
547   kill:
548     *ksroot = ks->next;
549     ks->f &= ~KSF_LINK;
550     ks_drop(ks);
551   }
552 }
553
554 /* --- @ksl_encrypt@ --- *
555  *
556  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
557  *              @unsigned ty@ = message type
558  *              @buf *b@ = pointer to input buffer
559  *              @buf *bb@ = pointer to output buffer
560  *
561  * Returns:     Nonzero if a new key is needed.
562  *
563  * Use:         Encrypts a packet.
564  */
565
566 int ksl_encrypt(keyset **ksroot, unsigned ty, buf *b, buf *bb)
567 {
568   time_t now = time(0);
569   keyset *ks = *ksroot;
570
571   for (;;) {
572     if (!ks) {
573       T( trace(T_KEYSET, "keyset: no suitable keysets found"); )
574       buf_break(bb);
575       return (-1);
576     }
577     if (KEYOK(ks, now) && !(ks->f & KSF_LISTEN))
578       break;
579     ks = ks->next;
580   }
581
582   return (doencrypt(ks, ty, b, bb));
583 }
584
585 /* --- @ksl_decrypt@ --- *
586  *
587  * Arguments:   @keyset **ksroot@ = pointer to keyset list head
588  *              @unsigned ty@ = expected type code
589  *              @buf *b@ = pointer to input buffer
590  *              @buf *bb@ = pointer to output buffer
591  *
592  * Returns:     Nonzero if the packet couldn't be decrypted.
593  *
594  * Use:         Decrypts a packet.
595  */
596
597 int ksl_decrypt(keyset **ksroot, unsigned ty, buf *b, buf *bb)
598 {
599   time_t now = time(0);
600   keyset *ks;
601   uint32 seq;
602
603   if (buf_ensure(bb, BLEN(b)))
604     return (-1);
605
606   for (ks = *ksroot; ks; ks = ks->next) {
607     if (!KEYOK(ks, now))
608       continue;
609     if (!dodecrypt(ks, ty, b, bb, &seq)) {
610       if (ks->f & KSF_LISTEN) {
611         T( trace(T_KEYSET, "keyset: implicitly activating keyset %u",
612                  ks->seq); )
613         ks->f &= ~KSF_LISTEN;
614       }
615       return (dosequence(ks, seq));
616     }
617   }
618   T( trace(T_KEYSET, "keyset: no matching keys, or incorrect MAC"); )
619   return (-1);
620 }
621
622 /*----- That's all, folks -------------------------------------------------*/