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