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