global reindent, now with uncrustify hook enabled
[oweals/gnunet.git] / src / util / gnunet-scrypt.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2014 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  * @file util/gnunet-scrypt.c
22  * @brief tool to manipulate SCRYPT proofs of work.
23  * @author Bart Polot
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include <gcrypt.h>
28
29 /**
30  * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
31  */
32 static unsigned long long nse_work_required;
33
34 /**
35  * Interval between proof find runs.
36  */
37 static struct GNUNET_TIME_Relative proof_find_delay;
38
39 static struct GNUNET_CRYPTO_EddsaPublicKey pub;
40
41 static uint64_t proof;
42
43 static struct GNUNET_SCHEDULER_Task *proof_task;
44
45 static const struct GNUNET_CONFIGURATION_Handle *cfg;
46
47 static char *pkfn;
48
49 static char *pwfn;
50
51
52 /**
53  * Write our current proof to disk.
54  *
55  * @param cls closure
56  */
57 static void
58 shutdown_task (void *cls)
59 {
60   (void) cls;
61   if (sizeof(proof) != GNUNET_DISK_fn_write (pwfn,
62                                              &proof,
63                                              sizeof(proof),
64                                              GNUNET_DISK_PERM_USER_READ
65                                              | GNUNET_DISK_PERM_USER_WRITE))
66     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", pwfn);
67 }
68
69
70 /**
71  * Calculate the 'proof-of-work' hash (an expensive hash).
72  *
73  * @param buf data to hash
74  * @param buf_len number of bytes in @a buf
75  * @param result where to write the resulting hash
76  */
77 static void
78 pow_hash (const void *buf, size_t buf_len, struct GNUNET_HashCode *result)
79 {
80   GNUNET_break (
81     0 == gcry_kdf_derive (buf,
82                           buf_len,
83                           GCRY_KDF_SCRYPT,
84                           1 /* subalgo */,
85                           "gnunet-proof-of-work",
86                           strlen ("gnunet-proof-of-work"),
87                           2 /* iterations; keep cost of individual op small */,
88                           sizeof(struct GNUNET_HashCode),
89                           result));
90 }
91
92
93 /**
94  * Count the leading zeroes in hash.
95  *
96  * @param hash to count leading zeros in
97  * @return the number of leading zero bits.
98  */
99 static unsigned int
100 count_leading_zeroes (const struct GNUNET_HashCode *hash)
101 {
102   unsigned int hash_count;
103
104   hash_count = 0;
105   while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
106     hash_count++;
107   return hash_count;
108 }
109
110
111 /**
112  * Find our proof of work.
113  *
114  * @param cls closure (unused)
115  * @param tc task context
116  */
117 static void
118 find_proof (void *cls)
119 {
120 #define ROUND_SIZE 10
121   uint64_t counter;
122   char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
123            + sizeof(uint64_t)] GNUNET_ALIGN;
124   struct GNUNET_HashCode result;
125   unsigned int i;
126   struct GNUNET_TIME_Absolute timestamp;
127   struct GNUNET_TIME_Relative elapsed;
128
129   (void) cls;
130   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131               "Got Proof of Work %llu\n",
132               (unsigned long long) proof);
133   proof_task = NULL;
134   GNUNET_memcpy (&buf[sizeof(uint64_t)],
135                  &pub,
136                  sizeof(struct GNUNET_CRYPTO_EddsaPublicKey));
137   i = 0;
138   counter = proof;
139   timestamp = GNUNET_TIME_absolute_get ();
140   while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
141   {
142     GNUNET_memcpy (buf, &counter, sizeof(uint64_t));
143     pow_hash (buf, sizeof(buf), &result);
144     if (nse_work_required <= count_leading_zeroes (&result))
145     {
146       proof = counter;
147       fprintf (stdout,
148                "Proof of work found: %llu!\n",
149                (unsigned long long) proof);
150       GNUNET_SCHEDULER_shutdown ();
151       return;
152     }
153     counter++;
154     i++;
155   }
156   elapsed = GNUNET_TIME_absolute_get_duration (timestamp);
157   elapsed = GNUNET_TIME_relative_divide (elapsed, ROUND_SIZE);
158   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
159               "Current: %llu [%s/proof]\n",
160               (unsigned long long) counter,
161               GNUNET_STRINGS_relative_time_to_string (elapsed, 0));
162   if (proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
163   {
164     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
165                 "Testing proofs currently at %llu\n",
166                 (unsigned long long) counter);
167     /* remember progress every 100 rounds */
168     proof = counter;
169     shutdown_task (NULL);
170   }
171   else
172   {
173     proof = counter;
174   }
175   proof_task =
176     GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
177                                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
178                                                 &find_proof,
179                                                 NULL);
180 }
181
182
183 /**
184  * Main function that will be run by the scheduler.
185  *
186  * @param cls closure
187  * @param args remaining command-line arguments
188  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
189  * @param cfg configuration
190  */
191 static void
192 run (void *cls,
193      char *const *args,
194      const char *cfgfile,
195      const struct GNUNET_CONFIGURATION_Handle *config)
196 {
197   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
198   char *pids;
199
200   (void) cls;
201   (void) args;
202   (void) cfgfile;
203   cfg = config;
204   /* load proof of work */
205   if (NULL == pwfn)
206   {
207     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
208                                                               "NSE",
209                                                               "PROOFFILE",
210                                                               &pwfn))
211     {
212       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
213       GNUNET_SCHEDULER_shutdown ();
214       return;
215     }
216   }
217   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Proof of Work file: %s\n", pwfn);
218   if ((GNUNET_YES != GNUNET_DISK_file_test (pwfn)) ||
219       (sizeof(proof) != GNUNET_DISK_fn_read (pwfn, &proof, sizeof(proof))))
220     proof = 0;
221
222   /* load private key */
223   if (NULL == pkfn)
224   {
225     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
226                                                               "PEER",
227                                                               "PRIVATE_KEY",
228                                                               &pkfn))
229     {
230       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
231                                  "PEER",
232                                  "PRIVATE_KEY");
233       return;
234     }
235   }
236   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Private Key file: %s\n", pkfn);
237   if (NULL == (pk = GNUNET_CRYPTO_eddsa_key_create_from_file (pkfn)))
238   {
239     fprintf (stderr, _ ("Loading hostkey from `%s' failed.\n"), pkfn);
240     GNUNET_free (pkfn);
241     return;
242   }
243   GNUNET_free (pkfn);
244   GNUNET_CRYPTO_eddsa_key_get_public (pk, &pub);
245   GNUNET_free (pk);
246   pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub);
247   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer ID: %s\n", pids);
248   GNUNET_free (pids);
249
250   /* get target bit amount */
251   if (0 == nse_work_required)
252   {
253     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
254                                                             "NSE",
255                                                             "WORKBITS",
256                                                             &nse_work_required))
257     {
258       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
259       GNUNET_SCHEDULER_shutdown ();
260       return;
261     }
262     if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8)
263     {
264       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
265                                  "NSE",
266                                  "WORKBITS",
267                                  _ ("Value is too large.\n"));
268       GNUNET_SCHEDULER_shutdown ();
269       return;
270     }
271     else if (0 == nse_work_required)
272     {
273       GNUNET_SCHEDULER_shutdown ();
274       return;
275     }
276   }
277   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Bits: %llu\n", nse_work_required);
278
279   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
280               "Delay between tries: %s\n",
281               GNUNET_STRINGS_relative_time_to_string (proof_find_delay, 1));
282   proof_task =
283     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
284                                         &find_proof,
285                                         NULL);
286   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
287 }
288
289
290 /**
291  * Program to manipulate ECC key files.
292  *
293  * @param argc number of arguments from the command line
294  * @param argv command line arguments
295  * @return 0 ok, 1 on error
296  */
297 int
298 main (int argc, char *const *argv)
299 {
300   struct GNUNET_GETOPT_CommandLineOption options[] =
301   { GNUNET_GETOPT_option_ulong (
302       'b',
303       "bits",
304       "BITS",
305       gettext_noop ("number of bits to require for the proof of work"),
306       &nse_work_required),
307     GNUNET_GETOPT_option_filename (
308       'k',
309       "keyfile",
310       "FILE",
311       gettext_noop ("file with private key, otherwise default is used"),
312       &pkfn),
313     GNUNET_GETOPT_option_filename (
314       'o',
315       "outfile",
316       "FILE",
317       gettext_noop ("file with proof of work, otherwise default is used"),
318       &pwfn),
319     GNUNET_GETOPT_option_relative_time ('t',
320                                         "timeout",
321                                         "TIME",
322                                         gettext_noop (
323                                           "time to wait between calculations"),
324                                         &proof_find_delay),
325     GNUNET_GETOPT_OPTION_END };
326   int ret;
327
328   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
329     return 2;
330
331   ret =
332     (GNUNET_OK ==
333      GNUNET_PROGRAM_run (argc,
334                          argv,
335                          "gnunet-scrypt [OPTIONS] prooffile",
336                          gettext_noop ("Manipulate GNUnet proof of work files"),
337                          options,
338                          &run,
339                          NULL))
340     ? 0
341     : 1;
342   GNUNET_free ((void *) argv);
343   GNUNET_free_non_null (pwfn);
344   return ret;
345 }
346
347 /* end of gnunet-scrypt.c */