Added documenting comments
[oweals/gnunet.git] / src / util / gnunet-ecc.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012, 2013 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-ecc.c
23  * @brief tool to manipulate EDDSA key files
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_testing_lib.h"
29 #include <gcrypt.h>
30
31 #define KEY_STR_LEN sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)*8/5+1
32
33 /**
34  * Flag for listing public key.
35  */
36 static int list_keys;
37
38 /**
39  * Flag for listing public key.
40  */
41 static int list_keys_count;
42
43 /**
44  * Flag for printing public key.
45  */
46 static int print_public_key;
47
48 /**
49  * Flag for printing the output of random example operations.
50  */
51 static int print_examples_flag;
52
53 /**
54  * Option set to create a bunch of keys at once.
55  */
56 static unsigned int make_keys;
57
58
59 /**
60  * Create a flat file with a large number of key pairs for testing.
61  *
62  * @param fn File name to store the keys.
63  * @param prefix Desired prefix for the public keys, NULL if any key is OK.
64  */
65 static void
66 create_keys (const char *fn, const char *prefix)
67 {
68   FILE *f;
69   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
70   struct GNUNET_CRYPTO_EddsaPublicKey target_pub;
71   static char vanity[KEY_STR_LEN + 1];
72   int len;
73   int n;
74   int rest;
75   unsigned char mask;
76   unsigned target_byte;
77   char *s;
78
79   if (NULL == (f = fopen (fn, "w+")))
80   {
81     fprintf (stderr, _("Failed to open `%s': %s\n"), fn, STRERROR (errno));
82     return;
83   }
84   if (NULL != prefix)
85   {
86     strncpy (vanity, prefix, KEY_STR_LEN);
87     len = GNUNET_MIN (strlen (prefix), KEY_STR_LEN);
88     n = len * 5 / 8;
89     rest = len * 5 % 8;
90
91     memset (&vanity[len], '0', KEY_STR_LEN - len);
92     vanity[KEY_STR_LEN] = '\0';
93     GNUNET_assert (GNUNET_OK ==
94                    GNUNET_CRYPTO_eddsa_public_key_from_string (vanity,
95                                                                KEY_STR_LEN,
96                                                                &target_pub));
97     if (0 != rest)
98     {
99       /**
100        * Documentation by example:
101        * vanity = "A"
102        * len = 1
103        * n = 5/8 = 0 (bytes)
104        * rest = 5%8 = 5 (bits)
105        * mask = ~(2**(8 - 5) - 1) = ~(2**3 - 1) = ~(8 - 1) = ~b111 = b11111000
106        */
107       mask = ~ ((int)pow (2, 8 - rest) - 1);
108       target_byte = ((unsigned char *) &target_pub)[n] & mask;
109     }
110     else
111     {
112       /* Just so old (debian) versions of GCC calm down with the warnings. */
113       mask = target_byte = 0;
114     }
115     s = GNUNET_CRYPTO_eddsa_public_key_to_string (&target_pub);
116     fprintf (stderr,
117              _("Generating %u keys like %s, please wait"),
118              make_keys,
119              s);
120     GNUNET_free (s);
121     fprintf (stderr,
122              "\nattempt %s [%d, %X]\n",
123              vanity,
124              n,
125              mask);
126   }
127   else
128   {
129     fprintf (stderr,
130              _("Generating %u keys, please wait"),
131              make_keys);
132     /* Just so old (debian) versions of GCC calm down with the warnings. */
133     n = rest = target_byte = mask = 0;
134   }
135
136   while (0 < make_keys--)
137   {
138     fprintf (stderr, ".");
139     if (NULL == (pk = GNUNET_CRYPTO_eddsa_key_create ()))
140     {
141        GNUNET_break (0);
142        break;
143     }
144     if (NULL != prefix)
145     {
146       struct GNUNET_CRYPTO_EddsaPublicKey newkey;
147
148       GNUNET_CRYPTO_eddsa_key_get_public (pk, &newkey);
149       if (0 != memcmp (&target_pub, &newkey, n))
150       {
151         make_keys++;
152         continue;
153       }
154       if (0 != rest)
155       {
156         unsigned char new_byte;
157
158         new_byte = ((unsigned char *) &newkey)[n] & mask;
159         if (target_byte != new_byte)
160         {
161           make_keys++;
162           continue;
163         }
164       }
165     }
166     if (GNUNET_TESTING_HOSTKEYFILESIZE !=
167         fwrite (pk, 1,
168                 GNUNET_TESTING_HOSTKEYFILESIZE, f))
169     {
170       fprintf (stderr,
171                _("\nFailed to write to `%s': %s\n"),
172                fn,
173                STRERROR (errno));
174       GNUNET_free (pk);
175       break;
176     }
177     GNUNET_free (pk);
178   }
179   if (UINT_MAX == make_keys)
180     fprintf (stderr,
181              _("\nFinished!\n"));
182   else
183     fprintf (stderr,
184              _("\nError, %u keys not generated\n"),
185              make_keys);
186   fclose (f);
187 }
188
189
190 static void
191 print_hex (const char *msg,
192            const void *buf,
193            size_t size)
194 {
195   size_t i;
196
197   printf ("%s: ", msg);
198   for (i = 0; i < size; i++)
199   {
200     printf ("%02hhx", ((const char *)buf)[i]);
201   }
202   printf ("\n");
203 }
204
205
206 static void
207 print_examples_ecdh ()
208 {
209   struct GNUNET_CRYPTO_EcdhePrivateKey *dh_priv1;
210   struct GNUNET_CRYPTO_EcdhePublicKey *dh_pub1;
211   struct GNUNET_CRYPTO_EcdhePrivateKey *dh_priv2;
212   struct GNUNET_CRYPTO_EcdhePublicKey *dh_pub2;
213   struct GNUNET_HashCode hash;
214   char buf[128];
215
216   dh_pub1 = GNUNET_new (struct GNUNET_CRYPTO_EcdhePublicKey);
217   dh_priv1 = GNUNET_CRYPTO_ecdhe_key_create ();
218   dh_pub2 = GNUNET_new (struct GNUNET_CRYPTO_EcdhePublicKey);
219   dh_priv2 = GNUNET_CRYPTO_ecdhe_key_create ();
220   GNUNET_CRYPTO_ecdhe_key_get_public (dh_priv1, dh_pub1);
221   GNUNET_CRYPTO_ecdhe_key_get_public (dh_priv2, dh_pub2);
222
223   GNUNET_assert (NULL != GNUNET_STRINGS_data_to_string (dh_priv1, 32, buf, 128));
224   printf ("ECDHE key 1:\n");
225   printf ("private: %s\n", buf);
226   print_hex ("private(hex)", dh_priv1, sizeof *dh_priv1);
227   GNUNET_assert (NULL != GNUNET_STRINGS_data_to_string (dh_pub1, 32, buf, 128));
228   printf ("public: %s\n", buf);
229   print_hex ("public(hex)", dh_pub1, sizeof *dh_pub1);
230
231   GNUNET_assert (NULL != GNUNET_STRINGS_data_to_string (dh_priv2, 32, buf, 128));
232   printf ("ECDHE key 2:\n");
233   printf ("private: %s\n", buf);
234   print_hex ("private(hex)", dh_priv2, sizeof *dh_priv2);
235   GNUNET_assert (NULL != GNUNET_STRINGS_data_to_string (dh_pub2, 32, buf, 128));
236   printf ("public: %s\n", buf);
237   print_hex ("public(hex)", dh_pub2, sizeof *dh_pub2);
238
239   GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecc_ecdh (dh_priv1, dh_pub2, &hash));
240   GNUNET_assert (NULL != GNUNET_STRINGS_data_to_string (&hash, 64, buf, 128));
241   printf ("ECDH shared secret: %s\n", buf);
242
243   GNUNET_free (dh_priv1);
244   GNUNET_free (dh_priv2);
245   GNUNET_free (dh_pub1);
246   GNUNET_free (dh_pub2);
247 }
248
249
250 /**
251  * Print some random example operations to stdout.
252  */
253 static void
254 print_examples ()
255 {
256   print_examples_ecdh ();
257   // print_examples_ecdsa ();
258   // print_examples_eddsa ();
259 }
260
261
262 static void
263 print_key (const char *filename)
264 {
265   struct GNUNET_DISK_FileHandle *fd;
266   struct GNUNET_CRYPTO_EddsaPrivateKey private_key;
267   struct GNUNET_CRYPTO_EddsaPublicKey public_key;
268   char *hostkeys_data;
269   char *hostkey_str;
270   uint64_t fs;
271   unsigned int total_hostkeys;
272   unsigned int c;
273
274   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
275   {
276     fprintf (stderr,
277              _("Hostkeys file `%s' not found\n"),
278              filename);
279     return;
280   }
281
282   /* Check hostkey file size, read entire thing into memory */
283   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
284     fs = 0;
285   if (0 == fs)
286   {
287     fprintf (stderr,
288              _("Hostkeys file `%s' is empty\n"),
289              filename);
290     return;       /* File is empty */
291   }
292   if (0 != (fs % GNUNET_TESTING_HOSTKEYFILESIZE))
293   {
294     fprintf (stderr,
295              _("Incorrect hostkey file format: %s\n"),
296              filename);
297     return;
298   }
299   fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
300                                          GNUNET_DISK_PERM_NONE);
301   if (NULL == fd)
302   {
303     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
304     return;
305   }
306   hostkeys_data = GNUNET_malloc (fs);
307   if (fs != GNUNET_DISK_file_read (fd, hostkeys_data, fs))
308   {
309     fprintf (stderr,
310              _("Could not read hostkey file: %s\n"),
311              filename);
312     GNUNET_free (hostkeys_data);
313     GNUNET_DISK_file_close (fd);
314     return;
315   }
316   GNUNET_DISK_file_close (fd);
317
318   if (NULL == hostkeys_data)
319     return;
320   total_hostkeys = fs / GNUNET_TESTING_HOSTKEYFILESIZE;
321   for (c = 0; (c < total_hostkeys) && (c < list_keys_count); c++)
322   {
323     memcpy (&private_key,
324             hostkeys_data + (c * GNUNET_TESTING_HOSTKEYFILESIZE),
325             GNUNET_TESTING_HOSTKEYFILESIZE);
326     GNUNET_CRYPTO_eddsa_key_get_public (&private_key, &public_key);
327     hostkey_str = GNUNET_CRYPTO_eddsa_public_key_to_string (&public_key);
328     if (NULL != hostkey_str)
329     {
330       fprintf (stderr, "%4u: %s\n", c, hostkey_str);
331       GNUNET_free (hostkey_str);
332     }
333     else
334       fprintf (stderr, "%4u: %s\n", c, "invalid");
335   }
336   GNUNET_free (hostkeys_data);
337 }
338
339
340 /**
341  * Main function that will be run by the scheduler.
342  *
343  * @param cls closure
344  * @param args remaining command-line arguments
345  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
346  * @param cfg configuration
347  */
348 static void
349 run (void *cls, char *const *args, const char *cfgfile,
350      const struct GNUNET_CONFIGURATION_Handle *cfg)
351 {
352   if (print_examples_flag)
353   {
354     print_examples ();
355     return;
356   }
357   if (NULL == args[0])
358   {
359     FPRINTF (stderr,
360              "%s",
361              _("No hostkey file specified on command line\n"));
362     return;
363   }
364   if (list_keys)
365   {
366     print_key (args[0]);
367     return;
368   }
369   if (make_keys > 0)
370   {
371     create_keys (args[0], args[1]);
372     return;
373   }
374   if (print_public_key)
375   {
376     char *str;
377     struct GNUNET_DISK_FileHandle *keyfile;
378     struct GNUNET_CRYPTO_EddsaPrivateKey pk;
379     struct GNUNET_CRYPTO_EddsaPublicKey pub;
380
381     keyfile = GNUNET_DISK_file_open (args[0], GNUNET_DISK_OPEN_READ,
382                                      GNUNET_DISK_PERM_NONE);
383     if (NULL == keyfile)
384       return;
385     while (sizeof (pk) == GNUNET_DISK_file_read (keyfile, &pk, sizeof (pk)))
386     {
387       GNUNET_CRYPTO_eddsa_key_get_public (&pk, &pub);
388       str = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub);
389       FPRINTF (stdout, "%s\n", str);
390       GNUNET_free (str);
391     }
392     GNUNET_DISK_file_close (keyfile);
393   }
394
395 }
396
397
398 /**
399  * Program to manipulate ECC key files.
400  *
401  * @param argc number of arguments from the command line
402  * @param argv command line arguments
403  * @return 0 ok, 1 on error
404  */
405 int
406 main (int argc, char *const *argv)
407 {
408   list_keys_count = UINT32_MAX;
409   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
410     { 'i', "iterate", "FILE",
411       gettext_noop ("list keys included in a file (for testing)"),
412       0, &GNUNET_GETOPT_set_one, &list_keys },
413     { 'e', "end=", "COUNT",
414       gettext_noop ("number of keys to list included in a file (for testing)"),
415       1, &GNUNET_GETOPT_set_uint, &list_keys_count },
416     { 'g', "generate-keys", "COUNT",
417       gettext_noop ("create COUNT public-private key pairs (for testing)"),
418       1, &GNUNET_GETOPT_set_uint, &make_keys },
419     { 'p', "print-public-key", NULL,
420       gettext_noop ("print the public key in ASCII format"),
421       0, &GNUNET_GETOPT_set_one, &print_public_key },
422     { 'E', "examples", NULL,
423       gettext_noop ("print examples of ECC operations (used for compatibility testing)"),
424       0, &GNUNET_GETOPT_set_one, &print_examples_flag },
425     GNUNET_GETOPT_OPTION_END
426   };
427   int ret;
428
429   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
430     return 2;
431
432   ret = (GNUNET_OK ==
433          GNUNET_PROGRAM_run (argc, argv, "gnunet-ecc [OPTIONS] keyfile [VANITY_PREFIX]",
434                              gettext_noop ("Manipulate GNUnet private ECC key files"),
435                              options, &run, NULL)) ? 0 : 1;
436   GNUNET_free ((void*) argv);
437   return ret;
438 }
439
440 /* end of gnunet-ecc.c */