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