glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / util / crypto_ecc_setup.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2013, 2015 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
16 /**
17  * @file util/crypto_ecc_setup.c
18  * @brief helper function for easy EdDSA key setup
19  * @author Christian Grothoff
20  */
21 #include "platform.h"
22 #include <gcrypt.h>
23 #include "gnunet_util_lib.h"
24
25 #define LOG(kind,...) GNUNET_log_from (kind, "util-crypto-ecc", __VA_ARGS__)
26
27 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-crypto-ecc", syscall)
28
29 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-crypto-ecc", syscall, filename)
30
31 /**
32  * Log an error message at log-level 'level' that indicates
33  * a failure of the command 'cmd' with the message given
34  * by gcry_strerror(rc).
35  */
36 #define LOG_GCRY(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0)
37
38
39 #include "crypto_bug.c"
40
41
42 /**
43  * Wait for a short time (we're trying to lock a file or want
44  * to give another process a shot at finishing a disk write, etc.).
45  * Sleeps for 100ms (as that should be long enough for virtually all
46  * modern systems to context switch and allow another process to do
47  * some 'real' work).
48  */
49 static void
50 short_wait ()
51 {
52   struct GNUNET_TIME_Relative timeout;
53
54   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100);
55   (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
56 }
57
58
59 /**
60  * Create a new private key by reading it from a file.  If the
61  * files does not exist, create a new key and write it to the
62  * file.  Caller must free return value.  Note that this function
63  * can not guarantee that another process might not be trying
64  * the same operation on the same file at the same time.
65  * If the contents of the file
66  * are invalid the old file is deleted and a fresh key is
67  * created.
68  *
69  * @param filename name of file to use to store the key
70  * @return new private key, NULL on error (for example,
71  *   permission denied)
72  */
73 struct GNUNET_CRYPTO_EddsaPrivateKey *
74 GNUNET_CRYPTO_eddsa_key_create_from_file (const char *filename)
75 {
76   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
77   struct GNUNET_DISK_FileHandle *fd;
78   unsigned int cnt;
79   int ec;
80   uint64_t fs;
81   ssize_t sret;
82
83   if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
84     return NULL;
85   while (GNUNET_YES != GNUNET_DISK_file_test (filename))
86   {
87     fd = GNUNET_DISK_file_open (filename,
88                                 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
89                                 | GNUNET_DISK_OPEN_FAILIFEXISTS,
90                                 GNUNET_DISK_PERM_USER_READ |
91                                 GNUNET_DISK_PERM_USER_WRITE);
92     if (NULL == fd)
93     {
94       if (EEXIST == errno)
95       {
96         if (GNUNET_YES != GNUNET_DISK_file_test (filename))
97         {
98           /* must exist but not be accessible, fail for good! */
99           if (0 != ACCESS (filename, R_OK))
100             LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", filename);
101           else
102             GNUNET_break (0);   /* what is going on!? */
103           return NULL;
104         }
105         continue;
106       }
107       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
108       return NULL;
109     }
110     cnt = 0;
111     while (GNUNET_YES !=
112            GNUNET_DISK_file_lock (fd, 0,
113                                   sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey),
114                                   GNUNET_YES))
115     {
116       short_wait ();
117       if (0 == ++cnt % 10)
118       {
119         ec = errno;
120         LOG (GNUNET_ERROR_TYPE_ERROR,
121              _("Could not acquire lock on file `%s': %s...\n"),
122              filename,
123              STRERROR (ec));
124       }
125     }
126     LOG (GNUNET_ERROR_TYPE_INFO,
127          _("Creating a new private key.  This may take a while.\n"));
128     priv = GNUNET_CRYPTO_eddsa_key_create ();
129     GNUNET_assert (NULL != priv);
130     GNUNET_assert (sizeof (*priv) ==
131                    GNUNET_DISK_file_write (fd, priv, sizeof (*priv)));
132     GNUNET_DISK_file_sync (fd);
133     if (GNUNET_YES !=
134         GNUNET_DISK_file_unlock (fd, 0,
135                                  sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
136       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
137     GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
138     return priv;
139   }
140   /* key file exists already, read it! */
141   fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
142                               GNUNET_DISK_PERM_NONE);
143   if (NULL == fd)
144   {
145     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
146     return NULL;
147   }
148   cnt = 0;
149   while (1)
150   {
151     if (GNUNET_YES !=
152         GNUNET_DISK_file_lock (fd, 0,
153                                sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey),
154                                GNUNET_NO))
155     {
156       if (0 == ++cnt % 60)
157       {
158         ec = errno;
159         LOG (GNUNET_ERROR_TYPE_ERROR,
160              _("Could not acquire lock on file `%s': %s...\n"), filename,
161              STRERROR (ec));
162         LOG (GNUNET_ERROR_TYPE_ERROR,
163              _
164              ("This may be ok if someone is currently generating a private key.\n"));
165       }
166       short_wait ();
167       continue;
168     }
169     if (GNUNET_YES != GNUNET_DISK_file_test (filename))
170     {
171       /* eh, what!? File we opened is now gone!? */
172       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
173       if (GNUNET_YES !=
174           GNUNET_DISK_file_unlock (fd, 0,
175                                    sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
176         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
177       GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd));
178
179       return NULL;
180     }
181     if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
182       fs = 0;
183     if (fs < sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey))
184     {
185       /* maybe we got the read lock before the key generating
186        * process had a chance to get the write lock; give it up! */
187       if (GNUNET_YES !=
188           GNUNET_DISK_file_unlock (fd, 0,
189                                    sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
190         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
191       if (0 == ++cnt % 10)
192       {
193         LOG (GNUNET_ERROR_TYPE_ERROR,
194              _("When trying to read key file `%s' I found %u bytes but I need at least %u.\n"),
195              filename,
196              (unsigned int) fs,
197              (unsigned int) sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey));
198         LOG (GNUNET_ERROR_TYPE_ERROR,
199              _("This may be ok if someone is currently generating a key.\n"));
200       }
201       short_wait ();                /* wait a bit longer! */
202       continue;
203     }
204     break;
205   }
206   fs = sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey);
207   priv = GNUNET_malloc (fs);
208   sret = GNUNET_DISK_file_read (fd,
209                                 priv,
210                                 fs);
211   GNUNET_assert ( (sret >= 0) &&
212                   (fs == (size_t) sret) );
213   if (GNUNET_YES !=
214       GNUNET_DISK_file_unlock (fd,
215                                0,
216                                sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
217     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
218                        "fcntl",
219                        filename);
220   GNUNET_assert (GNUNET_YES ==
221                  GNUNET_DISK_file_close (fd));
222 #if CRYPTO_BUG
223   if (GNUNET_OK !=
224       check_eddsa_key (priv))
225   {
226     GNUNET_break (0);
227     GNUNET_free (priv);
228     return NULL;
229   }
230 #endif
231   return priv;
232 }
233
234
235 /**
236  * Create a new private key by reading it from a file.  If the
237  * files does not exist, create a new key and write it to the
238  * file.  Caller must free return value.  Note that this function
239  * can not guarantee that another process might not be trying
240  * the same operation on the same file at the same time.
241  * If the contents of the file
242  * are invalid the old file is deleted and a fresh key is
243  * created.
244  *
245  * @param filename name of file to use to store the key
246  * @return new private key, NULL on error (for example,
247  *   permission denied)
248  */
249 struct GNUNET_CRYPTO_EcdsaPrivateKey *
250 GNUNET_CRYPTO_ecdsa_key_create_from_file (const char *filename)
251 {
252   struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
253   struct GNUNET_DISK_FileHandle *fd;
254   unsigned int cnt;
255   int ec;
256   uint64_t fs;
257   ssize_t sret;
258
259   if (GNUNET_SYSERR ==
260       GNUNET_DISK_directory_create_for_file (filename))
261     return NULL;
262   while (GNUNET_YES != GNUNET_DISK_file_test (filename))
263   {
264     fd = GNUNET_DISK_file_open (filename,
265                                 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
266                                 | GNUNET_DISK_OPEN_FAILIFEXISTS,
267                                 GNUNET_DISK_PERM_USER_READ |
268                                 GNUNET_DISK_PERM_USER_WRITE);
269     if (NULL == fd)
270     {
271       if (EEXIST == errno)
272       {
273         if (GNUNET_YES != GNUNET_DISK_file_test (filename))
274         {
275           /* must exist but not be accessible, fail for good! */
276           if (0 != ACCESS (filename, R_OK))
277             LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR,
278                                "access",
279                                filename);
280           else
281             GNUNET_break (0);   /* what is going on!? */
282           return NULL;
283         }
284         continue;
285       }
286       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR,
287                          "open",
288                          filename);
289       return NULL;
290     }
291     cnt = 0;
292     while (GNUNET_YES !=
293            GNUNET_DISK_file_lock (fd,
294                                   0,
295                                   sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
296                                   GNUNET_YES))
297     {
298       short_wait ();
299       if (0 == ++cnt % 10)
300       {
301         ec = errno;
302         LOG (GNUNET_ERROR_TYPE_ERROR,
303              _("Could not acquire lock on file `%s': %s...\n"),
304              filename,
305              STRERROR (ec));
306       }
307     }
308     LOG (GNUNET_ERROR_TYPE_INFO,
309          _("Creating a new private key.  This may take a while.\n"));
310     priv = GNUNET_CRYPTO_ecdsa_key_create ();
311     GNUNET_assert (NULL != priv);
312     GNUNET_assert (sizeof (*priv) ==
313                    GNUNET_DISK_file_write (fd,
314                                            priv,
315                                            sizeof (*priv)));
316     GNUNET_DISK_file_sync (fd);
317     if (GNUNET_YES !=
318         GNUNET_DISK_file_unlock (fd, 0,
319                                  sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
320       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
321                          "fcntl",
322                          filename);
323     GNUNET_assert (GNUNET_YES ==
324                    GNUNET_DISK_file_close (fd));
325     return priv;
326   }
327   /* key file exists already, read it! */
328   fd = GNUNET_DISK_file_open (filename,
329                               GNUNET_DISK_OPEN_READ,
330                               GNUNET_DISK_PERM_NONE);
331   if (NULL == fd)
332   {
333     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR,
334                        "open",
335                        filename);
336     return NULL;
337   }
338   cnt = 0;
339   while (1)
340   {
341     if (GNUNET_YES !=
342         GNUNET_DISK_file_lock (fd, 0,
343                                sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
344                                GNUNET_NO))
345     {
346       if (0 == ++cnt % 60)
347       {
348         ec = errno;
349         LOG (GNUNET_ERROR_TYPE_ERROR,
350              _("Could not acquire lock on file `%s': %s...\n"),
351              filename,
352              STRERROR (ec));
353         LOG (GNUNET_ERROR_TYPE_ERROR,
354              _("This may be ok if someone is currently generating a private key.\n"));
355       }
356       short_wait ();
357       continue;
358     }
359     if (GNUNET_YES !=
360         GNUNET_DISK_file_test (filename))
361     {
362       /* eh, what!? File we opened is now gone!? */
363       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
364                          "stat",
365                          filename);
366       if (GNUNET_YES !=
367           GNUNET_DISK_file_unlock (fd, 0,
368                                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
369         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
370                            "fcntl",
371                            filename);
372       GNUNET_assert (GNUNET_OK ==
373                      GNUNET_DISK_file_close (fd));
374
375       return NULL;
376     }
377     if (GNUNET_OK !=
378         GNUNET_DISK_file_size (filename,
379                                &fs,
380                                GNUNET_YES,
381                                GNUNET_YES))
382       fs = 0;
383     if (fs < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))
384     {
385       /* maybe we got the read lock before the key generating
386        * process had a chance to get the write lock; give it up! */
387       if (GNUNET_YES !=
388           GNUNET_DISK_file_unlock (fd, 0,
389                                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
390         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
391                            "fcntl",
392                            filename);
393       if (0 == ++cnt % 10)
394       {
395         LOG (GNUNET_ERROR_TYPE_ERROR,
396              _("When trying to read key file `%s' I found %u bytes but I need at least %u.\n"),
397              filename, (unsigned int) fs,
398              (unsigned int) sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
399         LOG (GNUNET_ERROR_TYPE_ERROR,
400              _("This may be ok if someone is currently generating a key.\n"));
401       }
402       short_wait ();                /* wait a bit longer! */
403       continue;
404     }
405     break;
406   }
407   fs = sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
408   priv = GNUNET_malloc (fs);
409   sret = GNUNET_DISK_file_read (fd,
410                                 priv,
411                                 fs);
412   GNUNET_assert ( (sret >= 0) &&
413                   (fs == (size_t) sret) );
414   if (GNUNET_YES !=
415       GNUNET_DISK_file_unlock (fd, 0,
416                                sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
417     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
418                        "fcntl",
419                        filename);
420   GNUNET_assert (GNUNET_YES ==
421                  GNUNET_DISK_file_close (fd));
422   return priv;
423 }
424
425
426 /**
427  * Create a new private key by reading our peer's key from
428  * the file specified in the configuration.
429  *
430  * @param cfg the configuration to use
431  * @return new private key, NULL on error (for example,
432  *   permission denied)
433  */
434 struct GNUNET_CRYPTO_EddsaPrivateKey *
435 GNUNET_CRYPTO_eddsa_key_create_from_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
436 {
437   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
438   char *fn;
439
440   if (GNUNET_OK !=
441       GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY", &fn))
442     return NULL;
443   priv = GNUNET_CRYPTO_eddsa_key_create_from_file (fn);
444   GNUNET_free (fn);
445   return priv;
446 }
447
448
449 /**
450  * Retrieve the identity of the host's peer.
451  *
452  * @param cfg configuration to use
453  * @param dst pointer to where to write the peer identity
454  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the identity
455  *         could not be retrieved
456  */
457 int
458 GNUNET_CRYPTO_get_peer_identity (const struct GNUNET_CONFIGURATION_Handle *cfg,
459                                  struct GNUNET_PeerIdentity *dst)
460 {
461   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
462
463   if (NULL == (priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg)))
464   {
465     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
466                 _("Could not load peer's private key\n"));
467     return GNUNET_SYSERR;
468   }
469   GNUNET_CRYPTO_eddsa_key_get_public (priv, &dst->public_key);
470   GNUNET_free (priv);
471   return GNUNET_OK;
472 }
473
474
475 /**
476  * Setup a key file for a peer given the name of the
477  * configuration file (!).  This function is used so that
478  * at a later point code can be certain that reading a
479  * key is fast (for example in time-dependent testcases).
480  *
481  * @param cfg_name name of the configuration file to use
482  */
483 void
484 GNUNET_CRYPTO_eddsa_setup_key (const char *cfg_name)
485 {
486   struct GNUNET_CONFIGURATION_Handle *cfg;
487   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
488
489   cfg = GNUNET_CONFIGURATION_create ();
490   (void) GNUNET_CONFIGURATION_load (cfg, cfg_name);
491   priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
492   if (NULL != priv)
493     GNUNET_free (priv);
494   GNUNET_CONFIGURATION_destroy (cfg);
495 }
496
497 /* end of crypto_ecc_setup.c */