uncrustify as demanded.
[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 /* end of crypto_ecc_setup.c */