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