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