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