chiark / gitweb /
a10971344fdf6356404d08c1ee975dcb34c4dc1d
[tripe] / keymgmt.c
1 /* -*-c-*-
2  *
3  * $Id: keymgmt.c,v 1.4 2004/04/03 12:35:13 mdw Exp $
4  *
5  * Key loading and storing
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: keymgmt.c,v $
32  * Revision 1.4  2004/04/03 12:35:13  mdw
33  * Support elliptic curve key exchange.
34  *
35  * Revision 1.3  2001/06/22 19:40:36  mdw
36  * Support expiry of other peers' public keys.
37  *
38  * Revision 1.2  2001/06/19 22:07:09  mdw
39  * Cosmetic fixes.
40  *
41  * Revision 1.1  2001/02/03 20:26:37  mdw
42  * Initial checkin.
43  *
44  */
45
46 /*----- Header files ------------------------------------------------------*/
47
48 #include "tripe.h"
49
50 /*----- Global variables --------------------------------------------------*/
51
52 group *gg;
53 mp *kpriv;
54
55 /*----- Static variables --------------------------------------------------*/
56
57 static key_file *kf_pub;
58 static const char *kr_priv, *kr_pub, *tag_priv;
59 static fwatch w_priv, w_pub;
60
61 /*----- Key groups --------------------------------------------------------*/
62
63 typedef struct kgops {
64   const char *ty;
65   const char *(*loadpriv)(key_data *, group **, mp **, dstr *);
66   const char *(*loadpub)(key_data *, group **, ge **, dstr *);
67 } kgops;
68
69 /* --- Diffie-Hellman --- */
70
71 static const char *kgdh_priv(key_data *kd, group **g, mp **x, dstr *t)
72 {
73   key_packstruct kps[DH_PRIVFETCHSZ];
74   key_packdef *kp;
75   dh_priv dp;
76   const char *e;
77   int rc;
78
79   kp = key_fetchinit(dh_privfetch, kps, &dp);
80   if ((rc = key_unpack(kp, kd, t)) != 0) {
81     e = key_strerror(rc);
82     goto done;
83   }
84   *g = group_prime(&dp.dp);
85   *x = MP_COPY(dp.x);
86   e = 0;
87 done:
88   key_fetchdone(kp);
89   return (e);
90 }
91
92 static const char *kgdh_pub(key_data *kd, group **g, ge **p, dstr *t)
93 {
94   key_packstruct kps[DH_PUBFETCHSZ];
95   key_packdef *kp;
96   dh_pub dp;
97   const char *e;
98   int rc;
99
100   kp = key_fetchinit(dh_pubfetch, kps, &dp);
101   if ((rc = key_unpack(kp, kd, t)) != 0) {
102     e = key_strerror(rc);
103     goto done;
104   }
105   *g = group_prime(&dp.dp);
106   *p = G_CREATE(*g);
107   if (G_FROMINT(*g, *p, dp.y)) {
108     e = "bad public value";
109     goto done;
110   }
111   e = 0;
112 done:
113   key_fetchdone(kp);
114   return (e);
115 }
116
117 static const kgops kgdh_ops = { "tripe-dh", kgdh_priv, kgdh_pub };
118
119 /* --- Elliptic curve --- */
120
121 static const char *kgec_priv(key_data *kd, group **g, mp **x, dstr *t)
122 {
123   key_packstruct kps[EC_PRIVFETCHSZ];
124   key_packdef *kp;
125   ec_priv ep;
126   ec_info ei;
127   const char *e;
128   int rc;
129
130   kp = key_fetchinit(ec_privfetch, kps, &ep);
131   if ((rc = key_unpack(kp, kd, t)) != 0) {
132     e = key_strerror(rc);
133     goto done;
134   }
135   if ((e = ec_getinfo(&ei, ep.cstr)) != 0)
136     goto done;
137   *g = group_ec(&ei);
138   *x = MP_COPY(ep.x);
139   e = 0;
140 done:
141   key_fetchdone(kp);
142   return (e);
143 }
144
145 static const char *kgec_pub(key_data *kd, group **g, ge **p, dstr *t)
146 {
147   key_packstruct kps[EC_PUBFETCHSZ];
148   key_packdef *kp;
149   ec_pub ep;
150   ec_info ei;
151   const char *e;
152   int rc;
153
154   kp = key_fetchinit(ec_pubfetch, kps, &ep);
155   if ((rc = key_unpack(kp, kd, t)) != 0) {
156     e = key_strerror(rc);
157     goto done;
158   }
159   if ((e = ec_getinfo(&ei, ep.cstr)) != 0)
160     goto done;
161   *g = group_ec(&ei);
162   *p = G_CREATE(*g);
163   if (G_FROMEC(*g, *p, &ep.p)) {
164     e = "bad public point";
165     goto done;
166   }
167   e = 0;
168 done:
169   key_fetchdone(kp);
170   return (e);
171 }
172
173 static const kgops kgec_ops = { "tripe-ec", kgec_priv, kgec_pub };
174
175 /* --- Table of supported key types --- */
176
177 static const kgops *kgtab[] = { &kgdh_ops, &kgec_ops, 0 };
178
179 /*----- Main code ---------------------------------------------------------*/
180
181 /* --- @keymoan@ --- *
182  *
183  * Arguments:   @const char *file@ = name of the file
184  *              @int line@ = line number in file
185  *              @const char *msg@ = error message
186  *              @void *p@ = argument pointer
187  *
188  * Returns:     ---
189  *
190  * Use:         Reports an error message about loading a key file.
191  */
192
193 static void keymoan(const char *file, int line, const char *msg, void *p)
194   { a_warn("%s:%i: error: %s", file, line, msg); }
195
196 /* --- @loadpriv@ --- *
197  *
198  * Arguments:   @dstr *d@ = string to write errors in
199  *
200  * Returns:     Zero if OK, nonzero on error.
201  *
202  * Use:         Loads the private key from its keyfile.
203  */
204
205 static int loadpriv(dstr *d)
206 {
207   key_file kf;
208   key *k;
209   key_data *kd;
210   dstr t = DSTR_INIT;
211   group *g = 0;
212   mp *x = 0;
213   int rc = -1;
214   const kgops **ko;
215   const char *e;
216
217   /* --- Open the private key file --- */
218
219   if (key_open(&kf, kr_priv, KOPEN_READ, keymoan, 0)) {
220     dstr_putf(d, "error reading private keyring `%s': %s",
221               kr_priv, strerror(errno));
222     goto done_0;
223   }
224
225   /* --- Find the private key --- */
226
227   if (key_qtag(&kf, tag_priv, &t, &k, &kd)) {
228     dstr_putf(d, "private key `%s' not found in keyring `%s'",
229               tag_priv, kr_priv);
230     goto done_1;
231   }
232
233   /* --- Look up the key type in the table --- */
234
235   for (ko = kgtab; *ko; ko++) {
236     if (strcmp((*ko)->ty, k->type) == 0)
237       goto tymatch;
238   }
239   dstr_putf(d, "private key `%s' has unknown type `%s'", t.buf, k->type);
240   goto done_1;
241 tymatch:;
242
243   /* --- Load the key --- */
244
245   if ((e = (*ko)->loadpriv(kd, &g, &x, &t)) != 0) {
246     dstr_putf(d, "error reading private key `%s': %s", t.buf, e);
247     goto done_1;
248   }
249
250   /* --- Check that the key is sensible --- */
251
252   if ((e = G_CHECK(g, &rand_global)) != 0) {
253     dstr_putf(d, "bad group in private key `%s': %s", t.buf, e);
254     goto done_1;
255   }
256
257   /* --- Good, we're happy --- *
258    *
259    * Dodginess!  We change the group over here, but don't free any old group
260    * elements.  This assumes that the new group is basically the same as the
261    * old one, and will happily adopt the existing elements.  If it isn't,
262    * then we lose badly.  Check this, then.
263    */
264
265   if (gg) {
266     if (!group_samep(g, gg)) {
267       dstr_putf(d, "private key `%s' has different group", t.buf);
268       goto done_1;
269     }
270     G_DESTROYGROUP(gg);
271   }
272   if (kpriv)
273     mp_drop(kpriv);
274
275   /* --- Dump out the group --- */
276
277   IF_TRACING(T_KEYMGMT, {
278     trace(T_KEYMGMT, "keymgmt: extracted private key `%s'", t.buf);
279     IF_TRACING(T_CRYPTO, {
280       trace(T_CRYPTO, "crypto: r = %s", mpstr(g->r));
281       trace(T_CRYPTO, "crypto: h = %s", mpstr(g->h));
282       trace(T_CRYPTO, "crypto: x = %s", mpstr(x));
283     })
284   })
285
286   /* --- Success! --- */
287
288   gg = g; g = 0;
289   kpriv = x; x = 0;
290   rc = 0;
291
292   /* --- Tidy up --- */
293
294 done_1:
295   key_close(&kf);
296 done_0:
297   dstr_destroy(&t);
298   if (x) mp_drop(x);
299   if (g) G_DESTROYGROUP(g);
300   return (rc);
301 }
302
303 /* --- @loadpub@ --- *
304  *
305  * Arguments:   @dstr *d@ = string to write errors to
306  *
307  * Returns:     Zero if OK, nonzero on error.
308  *
309  * Use:         Reloads the public keyring.
310  */
311
312 static int loadpub(dstr *d)
313 {
314   key_file *kf = CREATE(key_file);
315
316   if (key_open(kf, kr_pub, KOPEN_READ, keymoan, 0)) {
317     dstr_putf(d, "error reading public keyring `%s': %s",
318               kr_pub, strerror(errno));
319     DESTROY(kf);
320     return (-1);
321   }
322   kf_pub = kf;
323   T( trace(T_KEYMGMT, "keymgmt: loaded public keyring `%s'", kr_pub); )
324   return (0);
325 }
326
327 /* --- @km_interval@ --- *
328  *
329  * Arguments:   ---
330  *
331  * Returns:     Zero if OK, nonzero to force reloading of keys.
332  *
333  * Use:         Called on the interval timer to perform various useful jobs.
334  */
335
336 int km_interval(void)
337 {
338   dstr d = DSTR_INIT;
339   key_file *kf;
340   int reload = 0;
341
342   /* --- Check the private key first --- */
343
344   if (fwatch_update(&w_priv, kr_priv)) {
345     T( trace(T_KEYMGMT, "keymgmt: private keyring updated: reloading..."); )
346     DRESET(&d);
347     if (loadpriv(&d))
348       a_warn("%s -- ignoring changes", d.buf);
349     else
350       reload = 1;
351   }
352
353   /* --- Now check the public keys --- */
354
355   if (fwatch_update(&w_pub, kr_pub)) {
356     T( trace(T_KEYMGMT, "keymgmt: public keyring updated: reloading..."); )
357     kf = kf_pub;
358     DRESET(&d);
359     if (loadpub(&d))
360       a_warn("%s -- ignoring changes", d.buf);
361     else {
362       reload = 1;
363       key_close(kf);
364       DESTROY(kf);
365     }
366   }
367
368   /* --- Done --- */
369
370   return (reload);
371 }
372
373 /* --- @km_init@ --- *
374  *
375  * Arguments:   @const char *priv@ = private keyring file
376  *              @const char *pub@ = public keyring file
377  *              @const char *tag@ = tag to load
378  *
379  * Returns:     ---
380  *
381  * Use:         Initializes, and loads the private key.
382  */
383
384 void km_init(const char *priv, const char *pub, const char *tag)
385 {
386   dstr d = DSTR_INIT;
387
388   kr_priv = priv;
389   kr_pub = pub;
390   tag_priv = tag;
391   fwatch_init(&w_priv, kr_priv);
392   fwatch_init(&w_pub, kr_pub);
393
394   DRESET(&d);
395   if (loadpriv(&d))
396     die(EXIT_FAILURE, "%s", d.buf);
397   if (loadpub(&d))
398     die(EXIT_FAILURE, "%s", d.buf);
399 }
400
401 /* --- @km_getpubkey@ --- *
402  *
403  * Arguments:   @const char *tag@ = public key tag to load
404  *              @ge *kpub@ = where to put the public key
405  *              @time_t *t_exp@ = where to put the expiry time
406  *
407  * Returns:     Zero if OK, nonzero if it failed.
408  *
409  * Use:         Fetches a public key from the keyring.
410  */
411
412 int km_getpubkey(const char *tag, ge *kpub, time_t *t_exp)
413 {
414   key *k;
415   key_data *kd;
416   dstr t = DSTR_INIT;
417   const kgops **ko;
418   const char *e;
419   group *g = 0;
420   ge *p = 0;
421   int rc = -1;
422
423   /* --- Find the key --- */
424
425   if (key_qtag(kf_pub, tag, &t, &k, &kd)) {
426     a_warn("private key `%s' not found in keyring `%s'", tag_priv, kr_priv);
427     goto done;
428   }
429
430   /* --- Look up the key type in the table --- */
431
432   for (ko = kgtab; *ko; ko++) {
433     if (strcmp((*ko)->ty, k->type) == 0)
434       goto tymatch;
435   }
436   a_warn("public key `%s' has unknown type `%s'", t.buf, k->type);
437   goto done;
438 tymatch:;
439
440   /* --- Load the key --- */
441
442   if ((e = (*ko)->loadpub(kd, &g, &p, &t)) != 0) {
443     a_warn("error reading public key `%s': %s", t.buf, e);
444     goto done;
445   }
446
447   /* --- Ensure that the group is correct --- *
448    *
449    * Dodginess!  We assume that if this works, our global group is willing to
450    * adopt this public element.  Probably reasonable.
451    */
452
453   if (!group_samep(gg, g)) {
454     a_warn("public key `%s' has incorrect group", t.buf);
455     goto done;
456   }
457
458   /* --- Check the public group element --- */
459
460   if (group_check(gg, p)) {
461     a_warn("public key `%s' has bad public group element", t.buf);
462     goto done;
463   }
464
465   /* --- Dump the public key --- */
466
467   IF_TRACING(T_KEYMGMT, {
468     trace(T_KEYMGMT, "keymgmt: extracted public key `%s'", t.buf);
469     trace(T_CRYPTO, "crypto: p = %s", gestr(gg, p));
470   })
471
472   /* --- OK, accept the public key --- */
473
474   *t_exp = k->exp;
475   G_COPY(gg, kpub, p);
476   rc = 0;
477
478   /* --- Tidy up --- */
479
480 done:
481   if (p) G_DESTROY(g, p);
482   if (g) G_DESTROYGROUP(g);
483   dstr_destroy(&t);
484   return (rc);
485 }
486
487 /*----- That's all, folks -------------------------------------------------*/