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