-first draft for revocation command-line tool
[oweals/gnunet.git] / src / revocation / gnunet-revocation.c
1 /*
2      This file is part of GNUnet.
3      (C) 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file revocation/gnunet-revocation.c
23  * @brief tool for revoking public keys
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_revocation_service.h"
29 #include "gnunet_identity_service.h"
30
31
32 /**
33  * Final status code.
34  */
35 static int ret;
36
37 /**
38  * Was "-p" specified?
39  */
40 static int perform;
41
42 /**
43  * -f option.
44  */
45 static char *filename;
46
47 /**
48  * -R option
49  */
50 static char *revoke_ego;
51
52 /**
53  * -t option.
54  */
55 static char *test_ego;
56
57 /**
58  * Handle for revocation query.
59  */
60 static struct GNUNET_REVOCATION_Query *q;
61
62 /**
63  * Handle for revocation.
64  */ 
65 static struct GNUNET_REVOCATION_Handle *h;
66
67 /**
68  * Handle for our ego lookup.
69  */
70 static struct GNUNET_IDENTITY_EgoLookup *el;
71
72 /**
73  * Our configuration.
74  */ 
75 static const struct GNUNET_CONFIGURATION_Handle *cfg;
76
77 /**
78  * Number of matching bits required for revocation.
79  */ 
80 static unsigned long long matching_bits;
81
82
83 /**
84  * Function run if the user aborts with CTRL-C.
85  *
86  * @param cls closure
87  * @param tc scheduler context
88  */
89 static void
90 do_shutdown (void *cls,
91              const struct GNUNET_SCHEDULER_TaskContext *tc)
92 {
93   if (NULL != q)
94   {
95     GNUNET_REVOCATION_query_cancel (q);
96     q = NULL;
97   }
98   if (NULL != h)
99   {
100     GNUNET_REVOCATION_revoke_cancel (h);
101     h = NULL;
102   }
103 }
104
105
106 /**
107  * Print the result from a revocation query.
108  *
109  * @param cls NULL
110  * @param is_valid #GNUNET_YES if the key is still valid, #GNUNET_NO if not, #GNUNET_SYSERR on error
111  */
112 static void
113 print_query_result (void *cls,
114                     int is_valid)
115 {
116   q = NULL;
117   switch (is_valid)
118   {
119   case GNUNET_YES:
120     FPRINTF (stdout,
121              _("Key `%s' is valid\n"),
122              test_ego);
123     break;
124   case GNUNET_NO:
125     FPRINTF (stdout,
126              _("Key `%s' has been revoked\n"),
127              test_ego);
128     break;
129   case GNUNET_SYSERR:
130     FPRINTF (stdout,
131              "%s",
132              _("Internal error\n"));
133     break;
134   default:
135     GNUNET_break (0);
136     break;
137   }
138   GNUNET_SCHEDULER_shutdown ();
139 }
140
141
142 /**
143  * Print the result from a revocation request.
144  *
145  * @param cls NULL
146  * @param is_valid #GNUNET_YES if the key is still valid, #GNUNET_NO if not, #GNUNET_SYSERR on error
147  */
148 static void
149 print_revocation_result (void *cls,
150                          int is_valid)
151 {
152   h = NULL;
153   switch (is_valid)
154   {
155   case GNUNET_YES:
156     FPRINTF (stdout,
157              _("Key for ego `%s' is still valid, revocation failed (!)\n"),
158              test_ego);
159     break;
160   case GNUNET_NO:
161     FPRINTF (stdout,
162              _("Key for ego `%s' has been successfully revoked\n"),
163              test_ego);
164     break;
165   case GNUNET_SYSERR:
166     FPRINTF (stdout,
167              "%s",
168              _("Internal error, key revocation might have failed\n"));
169     break;
170   default:
171     GNUNET_break (0);
172     break;
173   }
174   GNUNET_SCHEDULER_shutdown ();
175 }
176
177
178 /**
179  * Data needed to perform a revocation.
180  */
181 struct RevocationData 
182 {
183   /**
184    * Public key.
185    */
186   struct GNUNET_CRYPTO_EccPublicSignKey key;
187
188   /**
189    * Revocation signature data.
190    */
191   struct GNUNET_CRYPTO_EccSignature sig;
192
193   /**
194    * Proof of work (in NBO).
195    */
196   uint64_t pow GNUNET_PACKED;
197 };
198
199
200 /**
201  * Perform the revocation.
202  */
203 static void
204 perform_revocation (const struct RevocationData *rd)
205 {
206   h = GNUNET_REVOCATION_revoke (cfg,
207                                 &rd->key,
208                                 &rd->sig,
209                                 rd->pow,
210                                 &print_revocation_result,
211                                 NULL);
212 }
213
214
215 /**
216  * Perform the proof-of-work calculation.
217  *
218  * @param cls the `struct RevocationData`
219  * @param tc scheduler context
220  */
221 static void
222 calculate_pow (void *cls,
223                const struct GNUNET_SCHEDULER_TaskContext *tc)
224 {
225   struct RevocationData *rd = cls;
226
227   if ( (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) ||
228        (0 == (rd->pow % 128) ) )
229   {    
230     if (0 == (rd->pow % 128 * 1024))
231     {
232       if (0 == (rd->pow % (1024 * 128 * 80)))
233         fprintf (stderr, "\n");
234       fprintf (stderr, ".");
235     }
236     if ( (NULL != filename) &&
237          (sizeof (struct RevocationData) ==
238           GNUNET_DISK_fn_write (filename,
239                                 &rd,
240                                 sizeof (rd),
241                                 GNUNET_DISK_PERM_USER_READ |
242                                 GNUNET_DISK_PERM_USER_WRITE)) )
243       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
244                                 "write",
245                                 filename);
246   }
247   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
248   {
249     GNUNET_free (rd);
250     return;
251   }
252   rd->pow++;
253   if (GNUNET_OK ==
254       GNUNET_REVOCATION_check_pow (&rd->key,
255                                    rd->pow,
256                                    (unsigned int) matching_bits))
257   {
258     if ( (NULL != filename) &&
259          (sizeof (struct RevocationData) ==
260           GNUNET_DISK_fn_write (filename,
261                                 &rd,
262                                 sizeof (rd),
263                                 GNUNET_DISK_PERM_USER_READ |
264                                 GNUNET_DISK_PERM_USER_WRITE)) )
265       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
266                                 "write",
267                                 filename);
268     if (perform)
269       perform_revocation (rd);
270     else
271     {
272       FPRINTF (stderr,
273                _("Revocation certificate for `%s' stored in `%s'\n"),
274                revoke_ego,
275                filename);
276       GNUNET_SCHEDULER_shutdown ();
277     }
278     GNUNET_free (rd);
279   }
280   GNUNET_SCHEDULER_add_now (&calculate_pow,
281                             rd);
282 }
283
284
285 /**
286  * Function called with the result from the ego lookup.
287  *
288  * @param cls closure
289  * @param ego the ego, NULL if not found
290  */
291 static void
292 ego_callback (void *cls,
293               const struct GNUNET_IDENTITY_Ego *ego)
294 {
295   struct RevocationData *rd;
296   struct GNUNET_CRYPTO_EccPublicSignKey key;
297
298   el = NULL;
299   if (NULL == ego)
300   {
301     FPRINTF (stdout,
302              _("Ego `%s' not found.\n"),
303              test_ego);
304     GNUNET_SCHEDULER_shutdown ();
305     return;
306   }
307   GNUNET_IDENTITY_ego_get_public_key (ego,
308                                       &key);
309   rd = GNUNET_new (struct RevocationData);
310   if ( (NULL != filename) &&
311        (GNUNET_YES == 
312         GNUNET_DISK_file_test (filename)) &&
313        (sizeof (struct RevocationData) ==
314         GNUNET_DISK_fn_read (filename,
315                              &rd,
316                              sizeof (rd))) )
317   {
318     if (0 != memcmp (&rd->key,
319                      &key,
320                      sizeof (struct GNUNET_CRYPTO_EccPublicSignKey)))
321     {
322       fprintf (stderr,
323                _("Error: revocation certificate in `%s' is not for `%s'\n"),
324                filename,
325                revoke_ego);
326       GNUNET_free (rd);
327       return;
328     }    
329   }
330   else
331   {    
332     GNUNET_REVOCATION_sign_revocation (GNUNET_IDENTITY_ego_get_private_key (ego),
333                                        &rd->sig);
334     rd->key = key;
335   }
336   if (GNUNET_YES ==
337       GNUNET_REVOCATION_check_pow (&key,
338                                    rd->pow,
339                                    (unsigned int) matching_bits))
340   {
341     FPRINTF (stderr,
342              "%s",
343              _("Revocation certificate ready, initiating revocation\n"));
344     perform_revocation (rd);
345     GNUNET_free (rd);
346     return;
347   }
348   FPRINTF (stderr,
349            "%s",
350            _("Revocation certificate not ready, calculating proof of work\n"));
351   GNUNET_SCHEDULER_add_now (&calculate_pow,
352                             rd);
353 }
354
355
356 /**
357  * Main function that will be run by the scheduler.
358  *
359  * @param cls closure
360  * @param args remaining command-line arguments
361  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
362  * @param c configuration
363  */
364 static void
365 run (void *cls, 
366      char *const *args, 
367      const char *cfgfile,
368      const struct GNUNET_CONFIGURATION_Handle *c)
369 {
370   struct GNUNET_CRYPTO_EccPublicSignKey pk;
371   struct RevocationData rd;
372
373   cfg = c;
374   if (NULL != test_ego)
375   {
376     if (GNUNET_OK !=
377         GNUNET_CRYPTO_ecc_public_sign_key_from_string (test_ego,
378                                                        strlen (test_ego),
379                                                        &pk))
380     {
381       FPRINTF (stderr,
382                _("Public key `%s' malformed\n"),
383                test_ego);
384       return;
385     }
386     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
387                                   &do_shutdown,
388                                   NULL);
389     q = GNUNET_REVOCATION_query (cfg,
390                                  &pk,
391                                  &print_query_result,
392                                  NULL);
393     if (NULL != revoke_ego)
394       FPRINTF (stderr,
395                "%s",
396                _("Testing and revoking at the same time is not allowed, only executing test.\n"));
397     return;
398   }
399   if (GNUNET_OK !=
400       GNUNET_CONFIGURATION_get_value_number (cfg,
401                                              "REVOCATION",
402                                              "WORKBITS",
403                                              &matching_bits))
404   {
405     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
406                                "REVOCATION",
407                                "WORKBITS");
408     return;
409   }
410   if (NULL != revoke_ego)
411   {
412     /* main code here */
413     el = GNUNET_IDENTITY_ego_lookup (cfg,
414                                      revoke_ego,
415                                      &ego_callback,
416                                      NULL);
417     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
418                                   &do_shutdown,
419                                   NULL);
420     return;
421   }
422   if ( (NULL != filename) &&
423        (perform) )
424   {
425     if (sizeof (rd) !=
426         GNUNET_DISK_fn_read (filename,
427                              &rd,
428                              sizeof (rd)))
429     {
430       fprintf (stderr,
431                _("Failed to read revocation certificate from `%s'\n"),
432                filename);
433       return;
434     }
435     perform_revocation (&rd);
436     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
437                                   &do_shutdown,
438                                   NULL);
439     return;
440   }
441   FPRINTF (stderr,
442            "%s",
443            _("No action specified. Nothing to do.\n"));
444 }
445
446
447 /**
448  * The main function of gnunet-revocation.
449  *
450  * @param argc number of arguments from the command line
451  * @param argv command line arguments
452  * @return 0 ok, 1 on error
453  */
454 int
455 main (int argc, char *const *argv)
456 {
457   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
458     {'f', "filename", "NAME",
459      gettext_noop ("use NAME for the name of the revocation file"),
460      1, &GNUNET_GETOPT_set_string, &filename},
461     {'R', "revoke", "NAME",
462      gettext_noop ("revoke the private key associated with the ego NAME "),
463      1, &GNUNET_GETOPT_set_string, &revoke_ego},
464     {'p', "perform", NULL,
465      gettext_noop ("actually perform the revocation revocation file, otherwise we just do the precomputation"),
466      0, &GNUNET_GETOPT_set_one, &perform},
467     {'t', "test", "KEY",
468      gettext_noop ("test if the public key KEY has been revoked"),
469      1, &GNUNET_GETOPT_set_string, &test_ego},
470     GNUNET_GETOPT_OPTION_END
471   };
472   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
473     return 2;
474
475   ret = (GNUNET_OK ==
476          GNUNET_PROGRAM_run (argc, argv, "gnunet-revocation",
477                              gettext_noop ("help text"), options, &run,
478                              NULL)) ? ret : 1;
479   GNUNET_free ((void*) argv);
480   return ret;
481 }
482
483 /* end of gnunet-revocation.c */