- ttl is deprecated, don't warn
[oweals/gnunet.git] / src / util / gnunet-scrypt.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2014 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
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 static void
56 write_proof ()
57 {
58   if (sizeof (proof) !=
59       GNUNET_DISK_fn_write (pwfn, &proof, sizeof (proof),
60                             GNUNET_DISK_PERM_USER_READ |
61                             GNUNET_DISK_PERM_USER_WRITE))
62     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
63 }
64
65
66 /**
67  * Calculate the 'proof-of-work' hash (an expensive hash).
68  *
69  * @param buf data to hash
70  * @param buf_len number of bytes in @a buf
71  * @param result where to write the resulting hash
72  */
73 static void
74 pow_hash (const void *buf,
75           size_t buf_len,
76           struct GNUNET_HashCode *result)
77 {
78   GNUNET_break (0 ==
79   gcry_kdf_derive (buf, buf_len,
80                    GCRY_KDF_SCRYPT,
81                    1 /* subalgo */,
82                    "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
83                    2 /* iterations; keep cost of individual op small */,
84                    sizeof (struct GNUNET_HashCode), result));
85 }
86
87
88 /**
89  * Count the leading zeroes in hash.
90  *
91  * @param hash to count leading zeros in
92  * @return the number of leading zero bits.
93  */
94 static unsigned int
95 count_leading_zeroes (const struct GNUNET_HashCode *hash)
96 {
97   unsigned int hash_count;
98
99   hash_count = 0;
100   while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
101     hash_count++;
102   return hash_count;
103 }
104
105
106 /**
107  * Find our proof of work.
108  *
109  * @param cls closure (unused)
110  * @param tc task context
111  */
112 static void
113 find_proof (void *cls,
114             const struct GNUNET_SCHEDULER_TaskContext *tc)
115 {
116   #define ROUND_SIZE 10
117   uint64_t counter;
118   char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
119            sizeof (uint64_t)] GNUNET_ALIGN;
120   struct GNUNET_HashCode result;
121   unsigned int i;
122   struct GNUNET_TIME_Absolute timestamp;
123   struct GNUNET_TIME_Relative elapsed;
124
125   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
126   {
127     write_proof ();
128     return;
129   }
130   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got Proof of Work %llu\n", proof);
131   proof_task = NULL;
132   memcpy (&buf[sizeof (uint64_t)], &pub,
133           sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
134   i = 0;
135   counter = proof;
136   timestamp = GNUNET_TIME_absolute_get ();
137   while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
138   {
139     memcpy (buf, &counter, sizeof (uint64_t));
140     pow_hash (buf, sizeof (buf), &result);
141     if (nse_work_required <= count_leading_zeroes (&result))
142     {
143       proof = counter;
144       FPRINTF (stdout, "Proof of work found: %llu!\n",
145                (unsigned long long) proof);
146       write_proof ();
147       return;
148     }
149     counter++;
150     i++;
151   }
152   elapsed = GNUNET_TIME_absolute_get_duration (timestamp);
153   elapsed = GNUNET_TIME_relative_divide (elapsed, ROUND_SIZE);
154   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
155               "Current: %llu [%s/proof]\n",
156               (unsigned long long) counter,
157               GNUNET_STRINGS_relative_time_to_string (elapsed, 0));
158   if (proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
159   {
160     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
161                 (unsigned long long) counter);
162     /* remember progress every 100 rounds */
163     proof = counter;
164     write_proof ();
165   }
166   else
167   {
168     proof = counter;
169   }
170   proof_task =
171   GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
172                                               GNUNET_SCHEDULER_PRIORITY_IDLE,
173                                               &find_proof, NULL);
174 }
175
176
177 /**
178  * Main function that will be run by the scheduler.
179  *
180  * @param cls closure
181  * @param args remaining command-line arguments
182  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
183  * @param cfg configuration
184  */
185 static void
186 run (void *cls,
187      char *const *args,
188      const char *cfgfile,
189      const struct GNUNET_CONFIGURATION_Handle *config)
190 {
191   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
192   char *pids;
193
194   cfg = config;
195
196   /* load proof of work */
197   if (NULL == pwfn)
198   {
199     if (GNUNET_OK !=
200         GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE",
201                                                  "PROOFFILE",
202                                                  &pwfn))
203     {
204       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
205                                  "NSE", "PROOFFILE");
206       GNUNET_SCHEDULER_shutdown ();
207       return;
208     }
209   }
210   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
211               "Proof of Work file: %s\n",
212               pwfn);
213   if ( (GNUNET_YES != GNUNET_DISK_file_test (pwfn)) ||
214        (sizeof (proof) !=
215         GNUNET_DISK_fn_read (pwfn, &proof, sizeof (proof))))
216     proof = 0;
217
218   /* load private key */
219   if (NULL == pkfn)
220   {
221     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER",
222                                                               "PRIVATE_KEY",
223                                                               &pkfn))
224     {
225       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
226                                  "PEER", "PRIVATE_KEY");
227       return;
228     }
229   }
230   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Private Key file: %s\n", pkfn);
231   if (NULL == (pk = GNUNET_CRYPTO_eddsa_key_create_from_file (pkfn)))
232   {
233     FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), pkfn);
234     GNUNET_free (pkfn);
235     return;
236   }
237   GNUNET_free (pkfn);
238   GNUNET_CRYPTO_eddsa_key_get_public (pk, &pub);
239   GNUNET_free (pk);
240   pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub);
241   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
242               "Peer ID: %s\n",
243               pids);
244   GNUNET_free (pids);
245
246   /* get target bit amount */
247   if (0 == nse_work_required)
248   {
249     if (GNUNET_OK !=
250         GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
251                                                &nse_work_required))
252     {
253       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
254       GNUNET_SCHEDULER_shutdown ();
255       return;
256     }
257     if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
258     {
259       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS",
260                                 _("Value is too large.\n"));
261       GNUNET_SCHEDULER_shutdown ();
262       return;
263     } else if (0 == nse_work_required)
264     {
265       write_proof ();
266       GNUNET_SCHEDULER_shutdown ();
267       return;
268     }
269   }
270   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
271               "Bits: %llu\n",
272               nse_work_required);
273
274   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
275               "Delay between tries: %s\n",
276               GNUNET_STRINGS_relative_time_to_string (proof_find_delay, 1));
277   GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
278                                       &find_proof, NULL);
279 }
280
281
282 /**
283  * Program to manipulate ECC key files.
284  *
285  * @param argc number of arguments from the command line
286  * @param argv command line arguments
287  * @return 0 ok, 1 on error
288  */
289 int
290 main (int argc, char *const *argv)
291 {
292   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
293     { 'b', "bits", "BITS",
294       gettext_noop ("number of bits to require for the proof of work"),
295       1, &GNUNET_GETOPT_set_ulong, &nse_work_required },
296     { 'k', "keyfile", "FILE",
297       gettext_noop ("file with private key, otherwise default is used"),
298       1, &GNUNET_GETOPT_set_filename, &pkfn },
299     { 'o', "outfile", "FILE",
300       gettext_noop ("file with proof of work, otherwise default is used"),
301       1, &GNUNET_GETOPT_set_filename, &pwfn },
302     { 't', "timeout", "TIME",
303       gettext_noop ("time to wait between calculations"),
304       1, &GNUNET_GETOPT_set_relative_time, &proof_find_delay },
305     GNUNET_GETOPT_OPTION_END
306   };
307   int ret;
308
309   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
310     return 2;
311
312   ret = (GNUNET_OK ==
313          GNUNET_PROGRAM_run (argc, argv,
314                              "gnunet-scrypt [OPTIONS] prooffile",
315                              gettext_noop ("Manipulate GNUnet proof of work files"),
316                              options, &run, NULL)) ? 0 : 1;
317   GNUNET_free ((void*) argv);
318   GNUNET_free_non_null (pwfn);
319   return ret;
320 }
321
322 /* end of gnunet-scrypt.c */