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