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