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