small API change: do no longer pass rarely needed GNUNET_SCHEDULER_TaskContext to...
[oweals/gnunet.git] / src / revocation / gnunet-revocation.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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  */
88 static void
89 do_shutdown (void *cls)
90 {
91   if (NULL != el)
92   {
93     GNUNET_IDENTITY_ego_lookup_cancel (el);
94     el = NULL;
95   }
96   if (NULL != q)
97   {
98     GNUNET_REVOCATION_query_cancel (q);
99     q = NULL;
100   }
101   if (NULL != h)
102   {
103     GNUNET_REVOCATION_revoke_cancel (h);
104     h = NULL;
105   }
106 }
107
108
109 /**
110  * Print the result from a revocation query.
111  *
112  * @param cls NULL
113  * @param is_valid #GNUNET_YES if the key is still valid, #GNUNET_NO if not, #GNUNET_SYSERR on error
114  */
115 static void
116 print_query_result (void *cls,
117                     int is_valid)
118 {
119   q = NULL;
120   switch (is_valid)
121   {
122   case GNUNET_YES:
123     FPRINTF (stdout,
124              _("Key `%s' is valid\n"),
125              test_ego);
126     break;
127   case GNUNET_NO:
128     FPRINTF (stdout,
129              _("Key `%s' has been revoked\n"),
130              test_ego);
131     break;
132   case GNUNET_SYSERR:
133     FPRINTF (stdout,
134              "%s",
135              _("Internal error\n"));
136     break;
137   default:
138     GNUNET_break (0);
139     break;
140   }
141   GNUNET_SCHEDULER_shutdown ();
142 }
143
144
145 /**
146  * Print the result from a revocation request.
147  *
148  * @param cls NULL
149  * @param is_valid #GNUNET_YES if the key is still valid, #GNUNET_NO if not, #GNUNET_SYSERR on error
150  */
151 static void
152 print_revocation_result (void *cls,
153                          int is_valid)
154 {
155   h = NULL;
156   switch (is_valid)
157   {
158   case GNUNET_YES:
159     if (NULL != revoke_ego)
160       FPRINTF (stdout,
161                _("Key for ego `%s' is still valid, revocation failed (!)\n"),
162                revoke_ego);
163     else
164       FPRINTF (stdout,
165                "%s",
166                _("Revocation failed (!)\n"));
167     break;
168   case GNUNET_NO:
169     if (NULL != revoke_ego)
170       FPRINTF (stdout,
171                _("Key for ego `%s' has been successfully revoked\n"),
172                revoke_ego);
173     else
174       FPRINTF (stdout,
175                "%s",
176                _("Revocation successful.\n"));
177     break;
178   case GNUNET_SYSERR:
179     FPRINTF (stdout,
180              "%s",
181              _("Internal error, key revocation might have failed\n"));
182     break;
183   default:
184     GNUNET_break (0);
185     break;
186   }
187   GNUNET_SCHEDULER_shutdown ();
188 }
189
190
191 /**
192  * Data needed to perform a revocation.
193  */
194 struct RevocationData
195 {
196   /**
197    * Public key.
198    */
199   struct GNUNET_CRYPTO_EcdsaPublicKey key;
200
201   /**
202    * Revocation signature data.
203    */
204   struct GNUNET_CRYPTO_EcdsaSignature sig;
205
206   /**
207    * Proof of work (in NBO).
208    */
209   uint64_t pow GNUNET_PACKED;
210 };
211
212
213 /**
214  * Perform the revocation.
215  */
216 static void
217 perform_revocation (const struct RevocationData *rd)
218 {
219   h = GNUNET_REVOCATION_revoke (cfg,
220                                 &rd->key,
221                                 &rd->sig,
222                                 rd->pow,
223                                 &print_revocation_result,
224                                 NULL);
225 }
226
227
228 /**
229  * Perform the proof-of-work calculation.
230  *
231  * @param cls the `struct RevocationData`
232  */
233 static void
234 calculate_pow (void *cls)
235 {
236   struct RevocationData *rd = cls;
237   const struct GNUNET_SCHEDULER_TaskContext *tc;
238
239   /* store temporary results */
240   tc = GNUNET_SCHEDULER_get_task_context ();
241   if ( (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) ||
242        (0 == (rd->pow % 128) ) )
243   {
244     if ( (NULL != filename) &&
245          (sizeof (struct RevocationData) ==
246           GNUNET_DISK_fn_write (filename,
247                                 &rd,
248                                 sizeof (rd),
249                                 GNUNET_DISK_PERM_USER_READ |
250                                 GNUNET_DISK_PERM_USER_WRITE)) )
251       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
252                                 "write",
253                                 filename);
254   }
255   /* display progress estimate */
256   if ( (0 == ((1 << matching_bits) / 100 / 50)) ||
257        (0 == (rd->pow % ((1 << matching_bits) / 100 / 50))) )
258     FPRINTF (stderr, "%s", ".");
259   if ( (0 != rd->pow) &&
260        ( (0 == ((1 << matching_bits) / 100)) ||
261          (0 == (rd->pow % ((1 << matching_bits) / 100))) ) )
262     FPRINTF (stderr, " - @ %3u%% (estimate)\n",
263              (unsigned int) (rd->pow * 100) / (1 << matching_bits));
264   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
265   {
266     GNUNET_free (rd);
267     return;
268   }
269   /* actually do POW calculation */
270   rd->pow++;
271   if (GNUNET_OK ==
272       GNUNET_REVOCATION_check_pow (&rd->key,
273                                    rd->pow,
274                                    (unsigned int) matching_bits))
275   {
276     if ( (NULL != filename) &&
277          (sizeof (struct RevocationData) !=
278           GNUNET_DISK_fn_write (filename,
279                                 rd,
280                                 sizeof (struct RevocationData),
281                                 GNUNET_DISK_PERM_USER_READ |
282                                 GNUNET_DISK_PERM_USER_WRITE)) )
283       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
284                                 "write",
285                                 filename);
286     if (perform)
287       perform_revocation (rd);
288     else
289     {
290       FPRINTF (stderr, "%s", "\n");
291       FPRINTF (stderr,
292                _("Revocation certificate for `%s' stored in `%s'\n"),
293                revoke_ego,
294                filename);
295       GNUNET_SCHEDULER_shutdown ();
296     }
297     GNUNET_free (rd);
298     return;
299   }
300   GNUNET_SCHEDULER_add_now (&calculate_pow,
301                             rd);
302 }
303
304
305 /**
306  * Function called with the result from the ego lookup.
307  *
308  * @param cls closure
309  * @param ego the ego, NULL if not found
310  */
311 static void
312 ego_callback (void *cls,
313               const struct GNUNET_IDENTITY_Ego *ego)
314 {
315   struct RevocationData *rd;
316   struct GNUNET_CRYPTO_EcdsaPublicKey key;
317
318   el = NULL;
319   if (NULL == ego)
320   {
321     FPRINTF (stdout,
322              _("Ego `%s' not found.\n"),
323              revoke_ego);
324     GNUNET_SCHEDULER_shutdown ();
325     return;
326   }
327   GNUNET_IDENTITY_ego_get_public_key (ego,
328                                       &key);
329   rd = GNUNET_new (struct RevocationData);
330   if ( (NULL != filename) &&
331        (GNUNET_YES ==
332         GNUNET_DISK_file_test (filename)) &&
333        (sizeof (struct RevocationData) ==
334         GNUNET_DISK_fn_read (filename,
335                              rd,
336                              sizeof (struct RevocationData))) )
337   {
338     if (0 != memcmp (&rd->key,
339                      &key,
340                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
341     {
342       fprintf (stderr,
343                _("Error: revocation certificate in `%s' is not for `%s'\n"),
344                filename,
345                revoke_ego);
346       GNUNET_free (rd);
347       return;
348     }
349   }
350   else
351   {
352     GNUNET_REVOCATION_sign_revocation (GNUNET_IDENTITY_ego_get_private_key (ego),
353                                        &rd->sig);
354     rd->key = key;
355   }
356   if (GNUNET_YES ==
357       GNUNET_REVOCATION_check_pow (&key,
358                                    rd->pow,
359                                    (unsigned int) matching_bits))
360   {
361     FPRINTF (stderr,
362              "%s",
363              _("Revocation certificate ready\n"));
364     if (perform)
365       perform_revocation (rd);
366     else
367       GNUNET_SCHEDULER_shutdown ();
368     GNUNET_free (rd);
369     return;
370   }
371   FPRINTF (stderr,
372            "%s",
373            _("Revocation certificate not ready, calculating proof of work\n"));
374   GNUNET_SCHEDULER_add_now (&calculate_pow,
375                             rd);
376 }
377
378
379 /**
380  * Main function that will be run by the scheduler.
381  *
382  * @param cls closure
383  * @param args remaining command-line arguments
384  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
385  * @param c configuration
386  */
387 static void
388 run (void *cls,
389      char *const *args,
390      const char *cfgfile,
391      const struct GNUNET_CONFIGURATION_Handle *c)
392 {
393   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
394   struct RevocationData rd;
395
396   cfg = c;
397   if (NULL != test_ego)
398   {
399     if (GNUNET_OK !=
400         GNUNET_CRYPTO_ecdsa_public_key_from_string (test_ego,
401                                                        strlen (test_ego),
402                                                        &pk))
403     {
404       FPRINTF (stderr,
405                _("Public key `%s' malformed\n"),
406                test_ego);
407       return;
408     }
409     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
410                                   &do_shutdown,
411                                   NULL);
412     q = GNUNET_REVOCATION_query (cfg,
413                                  &pk,
414                                  &print_query_result,
415                                  NULL);
416     if (NULL != revoke_ego)
417       FPRINTF (stderr,
418                "%s",
419                _("Testing and revoking at the same time is not allowed, only executing test.\n"));
420     return;
421   }
422   if (GNUNET_OK !=
423       GNUNET_CONFIGURATION_get_value_number (cfg,
424                                              "REVOCATION",
425                                              "WORKBITS",
426                                              &matching_bits))
427   {
428     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
429                                "REVOCATION",
430                                "WORKBITS");
431     return;
432   }
433   if (NULL != revoke_ego)
434   {
435     if ( !perform && (NULL == filename) )
436     {
437         FPRINTF (stderr,
438                  "%s",
439                  _("No filename to store revocation certificate given.\n"));
440         return;
441     }
442     /* main code here */
443     el = GNUNET_IDENTITY_ego_lookup (cfg,
444                                      revoke_ego,
445                                      &ego_callback,
446                                      NULL);
447     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
448                                   &do_shutdown,
449                                   NULL);
450     return;
451   }
452   if ( (NULL != filename) &&
453        (perform) )
454   {
455     if (sizeof (rd) !=
456         GNUNET_DISK_fn_read (filename,
457                              &rd,
458                              sizeof (rd)))
459     {
460       fprintf (stderr,
461                _("Failed to read revocation certificate from `%s'\n"),
462                filename);
463       return;
464     }
465     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
466                                   &do_shutdown,
467                                   NULL);
468     if (GNUNET_YES !=
469         GNUNET_REVOCATION_check_pow (&rd.key,
470                                      rd.pow,
471                                      (unsigned int) matching_bits))
472     {
473       struct RevocationData *cp = GNUNET_new (struct RevocationData);
474
475       *cp = rd;
476       GNUNET_SCHEDULER_add_now (&calculate_pow,
477                                 cp);
478       return;
479
480     }
481     perform_revocation (&rd);
482     return;
483   }
484   FPRINTF (stderr,
485            "%s",
486            _("No action specified. Nothing to do.\n"));
487 }
488
489
490 /**
491  * The main function of gnunet-revocation.
492  *
493  * @param argc number of arguments from the command line
494  * @param argv command line arguments
495  * @return 0 ok, 1 on error
496  */
497 int
498 main (int argc, char *const *argv)
499 {
500   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
501     {'f', "filename", "NAME",
502      gettext_noop ("use NAME for the name of the revocation file"),
503      1, &GNUNET_GETOPT_set_string, &filename},
504     {'R', "revoke", "NAME",
505      gettext_noop ("revoke the private key associated for the the private key associated with the ego NAME "),
506      1, &GNUNET_GETOPT_set_string, &revoke_ego},
507     {'p', "perform", NULL,
508      gettext_noop ("actually perform revocation, otherwise we just do the precomputation"),
509      0, &GNUNET_GETOPT_set_one, &perform},
510     {'t', "test", "KEY",
511      gettext_noop ("test if the public key KEY has been revoked"),
512      1, &GNUNET_GETOPT_set_string, &test_ego},
513     GNUNET_GETOPT_OPTION_END
514   };
515   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
516     return 2;
517
518   ret = (GNUNET_OK ==
519          GNUNET_PROGRAM_run (argc, argv, "gnunet-revocation",
520                              gettext_noop ("help text"), options, &run,
521                              NULL)) ? ret : 1;
522   GNUNET_free ((void*) argv);
523   return ret;
524 }
525
526 /* end of gnunet-revocation.c */