chiark / gitweb /
Don't link the client against Catacomb.
[tripe] / keyset.c
CommitLineData
410c8acf 1/* -*-c-*-
2 *
09585a65 3 * $Id: keyset.c,v 1.2 2001/02/05 19:53:23 mdw Exp $
410c8acf 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 $
09585a65 32 * Revision 1.2 2001/02/05 19:53:23 mdw
33 * Add sequence number protection.
34 *
410c8acf 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
66static 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
82void 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
101void 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
134time_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;
09585a65 162 ks->oseq = ks->iseq = 0;
163 ks->iwin = 0;
410c8acf 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
181int 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);
410c8acf 189 size_t sz = BLEFT(b);
09585a65 190 octet *qiv, *qseq, *qpk;
191 uint32 oseq;
410c8acf 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
09585a65 209 /* --- Allocate the required buffer space --- */
410c8acf 210
211 c = ks->c;
212 ivsz = c->ops->c->blksz;
09585a65 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);
410c8acf 220 h = ks->m->ops->init(ks->m);
09585a65 221 h->ops->hash(h, qseq, 4);
410c8acf 222 h->ops->hash(h, p, sz);
09585a65 223 memcpy(qiv, h->ops->done(h, 0), ivsz);
224 h->ops->destroy(h);
410c8acf 225 IF_TRACING(T_KEYSET, {
09585a65 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);
410c8acf 229 })
09585a65 230 c->ops->setiv(c, qiv);
231 c->ops->encrypt(c, p, qpk, sz);
410c8acf 232 IF_TRACING(T_KEYSET, {
09585a65 233 trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
410c8acf 234 })
410c8acf 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); )
09585a65 246 rc = 1;
410c8acf 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
263int ks_decrypt(keyset **ksroot, buf *b, buf *bb)
264{
265 time_t now = time(0);
09585a65 266 const octet *piv, *pseq, *ppk;
267 size_t psz = BLEFT(b);
268 size_t sz;
410c8acf 269 octet *q = BCUR(bb);
09585a65 270 uint32 iseq;
271 unsigned seqbit;
410c8acf 272 keyset *ks;
273
09585a65 274 /* --- Allocate space in the output buffer --- */
275
410c8acf 276 T( trace(T_KEYSET, "keyset: attempting to decrypt packet"); )
09585a65 277 if (buf_ensure(bb, psz))
410c8acf 278 return (-1);
09585a65 279
280 /* --- Try all of the valid keys --- */
281
410c8acf 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
09585a65 289 /* --- Break up the packet into its components --- */
290
410c8acf 291 if (!KEYOK(ks, now))
292 continue;
09585a65 293 if (psz < ivsz + 4) {
410c8acf 294 T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
295 continue;
296 }
09585a65 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);
410c8acf 304 h = ks->m->ops->init(ks->m);
09585a65 305 h->ops->hash(h, pseq, 4);
306 h->ops->hash(h, q, sz);
410c8acf 307 mac = h->ops->done(h, 0);
09585a65 308 eq = !memcmp(mac, piv, ivsz);
410c8acf 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) {
09585a65 315 BSTEP(bb, sz);
316 goto match;
410c8acf 317 }
318 IF_TRACING(T_KEYSET, {
319 trace(T_KEYSET, "keyset: decryption failed");
09585a65 320 trace_block(T_CRYPTO, "crypto: expected MAC", piv, ivsz);
321 trace_block(T_CRYPTO, "crypto: invalid packet", q, sz);
410c8acf 322 })
323 }
324 T( trace(T_KEYSET, "keyset: no matching keys"); )
325 return (-1);
09585a65 326
327 /* --- We've found a match, so check the sequence number --- */
328
329match:
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);
410c8acf 355}
356
357/*----- That's all, folks -------------------------------------------------*/