first batch of license fixes (boring)
[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 it
6      under the terms of the GNU General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15
16 /**
17  * @file revocation/gnunet-revocation.c
18  * @brief tool for revoking public keys
19  * @author Christian Grothoff
20  */
21 #include "platform.h"
22 #include "gnunet_util_lib.h"
23 #include "gnunet_revocation_service.h"
24 #include "gnunet_identity_service.h"
25
26
27 /**
28  * Final status code.
29  */
30 static int ret;
31
32 /**
33  * Was "-p" specified?
34  */
35 static int perform;
36
37 /**
38  * -f option.
39  */
40 static char *filename;
41
42 /**
43  * -R option
44  */
45 static char *revoke_ego;
46
47 /**
48  * -t option.
49  */
50 static char *test_ego;
51
52 /**
53  * Handle for revocation query.
54  */
55 static struct GNUNET_REVOCATION_Query *q;
56
57 /**
58  * Handle for revocation.
59  */
60 static struct GNUNET_REVOCATION_Handle *h;
61
62 /**
63  * Handle for our ego lookup.
64  */
65 static struct GNUNET_IDENTITY_EgoLookup *el;
66
67 /**
68  * Our configuration.
69  */
70 static const struct GNUNET_CONFIGURATION_Handle *cfg;
71
72 /**
73  * Number of matching bits required for revocation.
74  */
75 static unsigned long long matching_bits;
76
77 /**
78  * Task used for proof-of-work calculation.
79  */
80 static struct GNUNET_SCHEDULER_Task *pow_task;
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  * Write the current state of the revocation data
230  * to disk.
231  *
232  * @param rd data to sync
233  */
234 static void
235 sync_rd (const struct RevocationData *rd)
236 {
237   if ( (NULL != filename) &&
238        (sizeof (struct RevocationData) ==
239         GNUNET_DISK_fn_write (filename,
240                               &rd,
241                               sizeof (rd),
242                               GNUNET_DISK_PERM_USER_READ |
243                               GNUNET_DISK_PERM_USER_WRITE)) )
244     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
245                               "write",
246                               filename);
247 }
248
249
250 /**
251  * Perform the proof-of-work calculation.
252  *
253  * @param cls the `struct RevocationData`
254  */
255 static void
256 calculate_pow_shutdown (void *cls)
257 {
258   struct RevocationData *rd = cls;
259
260   if (NULL != pow_task)
261   {
262     GNUNET_SCHEDULER_cancel (pow_task);
263     pow_task = NULL;
264   }
265   sync_rd (rd);
266   GNUNET_free (rd);
267 }
268
269
270 /**
271  * Perform the proof-of-work calculation.
272  *
273  * @param cls the `struct RevocationData`
274  */
275 static void
276 calculate_pow (void *cls)
277 {
278   struct RevocationData *rd = cls;
279
280   /* store temporary results */
281   pow_task = NULL;
282   if (0 == (rd->pow % 128))
283     sync_rd (rd);
284   /* display progress estimate */
285   if ( (0 == ((1 << matching_bits) / 100 / 50)) ||
286        (0 == (rd->pow % ((1 << matching_bits) / 100 / 50))) )
287     FPRINTF (stderr, "%s", ".");
288   if ( (0 != rd->pow) &&
289        ( (0 == ((1 << matching_bits) / 100)) ||
290          (0 == (rd->pow % ((1 << matching_bits) / 100))) ) )
291     FPRINTF (stderr, " - @ %3u%% (estimate)\n",
292              (unsigned int) (rd->pow * 100) / (1 << matching_bits));
293   /* actually do POW calculation */
294   rd->pow++;
295   if (GNUNET_OK ==
296       GNUNET_REVOCATION_check_pow (&rd->key,
297                                    rd->pow,
298                                    (unsigned int) matching_bits))
299   {
300     if ( (NULL != filename) &&
301          (sizeof (struct RevocationData) !=
302           GNUNET_DISK_fn_write (filename,
303                                 rd,
304                                 sizeof (struct RevocationData),
305                                 GNUNET_DISK_PERM_USER_READ |
306                                 GNUNET_DISK_PERM_USER_WRITE)) )
307       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
308                                 "write",
309                                 filename);
310     if (perform)
311     {
312       perform_revocation (rd);
313     }
314     else
315     {
316       FPRINTF (stderr, "%s", "\n");
317       FPRINTF (stderr,
318                _("Revocation certificate for `%s' stored in `%s'\n"),
319                revoke_ego,
320                filename);
321       GNUNET_SCHEDULER_shutdown ();
322     }
323     return;
324   }
325   pow_task = GNUNET_SCHEDULER_add_now (&calculate_pow,
326                                        rd);
327 }
328
329
330 /**
331  * Function called with the result from the ego lookup.
332  *
333  * @param cls closure
334  * @param ego the ego, NULL if not found
335  */
336 static void
337 ego_callback (void *cls,
338               const struct GNUNET_IDENTITY_Ego *ego)
339 {
340   struct RevocationData *rd;
341   struct GNUNET_CRYPTO_EcdsaPublicKey key;
342
343   el = NULL;
344   if (NULL == ego)
345   {
346     FPRINTF (stdout,
347              _("Ego `%s' not found.\n"),
348              revoke_ego);
349     GNUNET_SCHEDULER_shutdown ();
350     return;
351   }
352   GNUNET_IDENTITY_ego_get_public_key (ego,
353                                       &key);
354   rd = GNUNET_new (struct RevocationData);
355   if ( (NULL != filename) &&
356        (GNUNET_YES ==
357         GNUNET_DISK_file_test (filename)) &&
358        (sizeof (struct RevocationData) ==
359         GNUNET_DISK_fn_read (filename,
360                              rd,
361                              sizeof (struct RevocationData))) )
362   {
363     if (0 != memcmp (&rd->key,
364                      &key,
365                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
366     {
367       fprintf (stderr,
368                _("Error: revocation certificate in `%s' is not for `%s'\n"),
369                filename,
370                revoke_ego);
371       GNUNET_free (rd);
372       return;
373     }
374   }
375   else
376   {
377     GNUNET_REVOCATION_sign_revocation (GNUNET_IDENTITY_ego_get_private_key (ego),
378                                        &rd->sig);
379     rd->key = key;
380   }
381   if (GNUNET_YES ==
382       GNUNET_REVOCATION_check_pow (&key,
383                                    rd->pow,
384                                    (unsigned int) matching_bits))
385   {
386     FPRINTF (stderr,
387              "%s",
388              _("Revocation certificate ready\n"));
389     if (perform)
390       perform_revocation (rd);
391     else
392       GNUNET_SCHEDULER_shutdown ();
393     GNUNET_free (rd);
394     return;
395   }
396   FPRINTF (stderr,
397            "%s",
398            _("Revocation certificate not ready, calculating proof of work\n"));
399   pow_task = GNUNET_SCHEDULER_add_now (&calculate_pow,
400                                        rd);
401   GNUNET_SCHEDULER_add_shutdown (&calculate_pow_shutdown,
402                                  rd);
403 }
404
405
406 /**
407  * Main function that will be run by the scheduler.
408  *
409  * @param cls closure
410  * @param args remaining command-line arguments
411  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
412  * @param c configuration
413  */
414 static void
415 run (void *cls,
416      char *const *args,
417      const char *cfgfile,
418      const struct GNUNET_CONFIGURATION_Handle *c)
419 {
420   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
421   struct RevocationData rd;
422
423   cfg = c;
424   if (NULL != test_ego)
425   {
426     if (GNUNET_OK !=
427         GNUNET_CRYPTO_ecdsa_public_key_from_string (test_ego,
428                                                        strlen (test_ego),
429                                                        &pk))
430     {
431       FPRINTF (stderr,
432                _("Public key `%s' malformed\n"),
433                test_ego);
434       return;
435     }
436     GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
437                                    NULL);
438     q = GNUNET_REVOCATION_query (cfg,
439                                  &pk,
440                                  &print_query_result,
441                                  NULL);
442     if (NULL != revoke_ego)
443       FPRINTF (stderr,
444                "%s",
445                _("Testing and revoking at the same time is not allowed, only executing test.\n"));
446     return;
447   }
448   if (GNUNET_OK !=
449       GNUNET_CONFIGURATION_get_value_number (cfg,
450                                              "REVOCATION",
451                                              "WORKBITS",
452                                              &matching_bits))
453   {
454     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
455                                "REVOCATION",
456                                "WORKBITS");
457     return;
458   }
459   if (NULL != revoke_ego)
460   {
461     if ( !perform && (NULL == filename) )
462     {
463         FPRINTF (stderr,
464                  "%s",
465                  _("No filename to store revocation certificate given.\n"));
466         return;
467     }
468     /* main code here */
469     el = GNUNET_IDENTITY_ego_lookup (cfg,
470                                      revoke_ego,
471                                      &ego_callback,
472                                      NULL);
473     GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
474                                    NULL);
475     return;
476   }
477   if ( (NULL != filename) &&
478        (perform) )
479   {
480     if (sizeof (rd) !=
481         GNUNET_DISK_fn_read (filename,
482                              &rd,
483                              sizeof (rd)))
484     {
485       fprintf (stderr,
486                _("Failed to read revocation certificate from `%s'\n"),
487                filename);
488       return;
489     }
490     GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
491                                    NULL);
492     if (GNUNET_YES !=
493         GNUNET_REVOCATION_check_pow (&rd.key,
494                                      rd.pow,
495                                      (unsigned int) matching_bits))
496     {
497       struct RevocationData *cp = GNUNET_new (struct RevocationData);
498
499       *cp = rd;
500       pow_task = GNUNET_SCHEDULER_add_now (&calculate_pow,
501                                            cp);
502       GNUNET_SCHEDULER_add_shutdown (&calculate_pow_shutdown,
503                                      cp);
504       return;
505     }
506     perform_revocation (&rd);
507     return;
508   }
509   FPRINTF (stderr,
510            "%s",
511            _("No action specified. Nothing to do.\n"));
512 }
513
514
515 /**
516  * The main function of gnunet-revocation.
517  *
518  * @param argc number of arguments from the command line
519  * @param argv command line arguments
520  * @return 0 ok, 1 on error
521  */
522 int
523 main (int argc, char *const *argv)
524 {
525   struct GNUNET_GETOPT_CommandLineOption options[] = {
526
527     GNUNET_GETOPT_option_string ('f',
528                                  "filename",
529                                  "NAME",
530                                  gettext_noop ("use NAME for the name of the revocation file"),
531                                  &filename),
532
533     GNUNET_GETOPT_option_string ('R',
534                                  "revoke",
535                                  "NAME",
536                                  gettext_noop ("revoke the private key associated for the the private key associated with the ego NAME "),
537                                  &revoke_ego), 
538
539     GNUNET_GETOPT_option_flag ('p',
540                                   "perform",
541                                   gettext_noop ("actually perform revocation, otherwise we just do the precomputation"),
542                                   &perform),
543
544     GNUNET_GETOPT_option_string ('t',
545                                  "test",
546                                  "KEY",
547                                  gettext_noop ("test if the public key KEY has been revoked"),
548                                  &test_ego), 
549
550     GNUNET_GETOPT_OPTION_END
551   };
552   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
553     return 2;
554
555   ret = (GNUNET_OK ==
556          GNUNET_PROGRAM_run (argc, argv, "gnunet-revocation",
557                              gettext_noop ("help text"), options, &run,
558                              NULL)) ? ret : 1;
559   GNUNET_free ((void*) argv);
560   return ret;
561 }
562
563 /* end of gnunet-revocation.c */