Merge branch 'master' of ssh://gnunet.org/gnunet
[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 /**
45  * Wait for a short time (we're trying to lock a file or want
46  * to give another process a shot at finishing a disk write, etc.).
47  * Sleeps for 100ms (as that should be long enough for virtually all
48  * modern systems to context switch and allow another process to do
49  * some 'real' work).
50  */
51 static void
52 short_wait ()
53 {
54   struct GNUNET_TIME_Relative timeout;
55
56   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100);
57   (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
58 }
59
60
61 /**
62  * Create a new private key by reading it from a file.  If the
63  * files does not exist, create a new key and write it to the
64  * file.  Caller must free return value.  Note that this function
65  * can not guarantee that another process might not be trying
66  * the same operation on the same file at the same time.
67  * If the contents of the file
68  * are invalid the old file is deleted and a fresh key is
69  * created.
70  *
71  * @param filename name of file to use to store the key
72  * @return new private key, NULL on error (for example,
73  *   permission denied)
74  */
75 struct GNUNET_CRYPTO_EddsaPrivateKey *
76 GNUNET_CRYPTO_eddsa_key_create_from_file (const char *filename)
77 {
78   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
79   struct GNUNET_DISK_FileHandle *fd;
80   unsigned int cnt;
81   int ec;
82   uint64_t fs;
83
84   if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
85     return NULL;
86   while (GNUNET_YES != GNUNET_DISK_file_test (filename))
87   {
88     fd = GNUNET_DISK_file_open (filename,
89                                 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
90                                 | GNUNET_DISK_OPEN_FAILIFEXISTS,
91                                 GNUNET_DISK_PERM_USER_READ |
92                                 GNUNET_DISK_PERM_USER_WRITE);
93     if (NULL == fd)
94     {
95       if (EEXIST == errno)
96       {
97         if (GNUNET_YES != GNUNET_DISK_file_test (filename))
98         {
99           /* must exist but not be accessible, fail for good! */
100           if (0 != ACCESS (filename, R_OK))
101             LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", filename);
102           else
103             GNUNET_break (0);   /* what is going on!? */
104           return NULL;
105         }
106         continue;
107       }
108       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
109       return NULL;
110     }
111     cnt = 0;
112     while (GNUNET_YES !=
113            GNUNET_DISK_file_lock (fd, 0,
114                                   sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey),
115                                   GNUNET_YES))
116     {
117       short_wait ();
118       if (0 == ++cnt % 10)
119       {
120         ec = errno;
121         LOG (GNUNET_ERROR_TYPE_ERROR,
122              _("Could not acquire lock on file `%s': %s...\n"), 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, (unsigned int) fs,
196              (unsigned int) sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey));
197         LOG (GNUNET_ERROR_TYPE_ERROR,
198              _("This may be ok if someone is currently generating a key.\n"));
199       }
200       short_wait ();                /* wait a bit longer! */
201       continue;
202     }
203     break;
204   }
205   fs = sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey);
206   priv = GNUNET_malloc (fs);
207   GNUNET_assert (fs == GNUNET_DISK_file_read (fd, priv, fs));
208   if (GNUNET_YES !=
209       GNUNET_DISK_file_unlock (fd, 0,
210                                sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
211     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
212   GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
213   return priv;
214 }
215
216
217 /**
218  * Create a new private key by reading it from a file.  If the
219  * files does not exist, create a new key and write it to the
220  * file.  Caller must free return value.  Note that this function
221  * can not guarantee that another process might not be trying
222  * the same operation on the same file at the same time.
223  * If the contents of the file
224  * are invalid the old file is deleted and a fresh key is
225  * created.
226  *
227  * @param filename name of file to use to store the key
228  * @return new private key, NULL on error (for example,
229  *   permission denied)
230  */
231 struct GNUNET_CRYPTO_EcdsaPrivateKey *
232 GNUNET_CRYPTO_ecdsa_key_create_from_file (const char *filename)
233 {
234   struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
235   struct GNUNET_DISK_FileHandle *fd;
236   unsigned int cnt;
237   int ec;
238   uint64_t fs;
239
240   if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
241     return NULL;
242   while (GNUNET_YES != GNUNET_DISK_file_test (filename))
243   {
244     fd = GNUNET_DISK_file_open (filename,
245                                 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
246                                 | GNUNET_DISK_OPEN_FAILIFEXISTS,
247                                 GNUNET_DISK_PERM_USER_READ |
248                                 GNUNET_DISK_PERM_USER_WRITE);
249     if (NULL == fd)
250     {
251       if (EEXIST == errno)
252       {
253         if (GNUNET_YES != GNUNET_DISK_file_test (filename))
254         {
255           /* must exist but not be accessible, fail for good! */
256           if (0 != ACCESS (filename, R_OK))
257             LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", filename);
258           else
259             GNUNET_break (0);   /* what is going on!? */
260           return NULL;
261         }
262         continue;
263       }
264       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
265       return NULL;
266     }
267     cnt = 0;
268     while (GNUNET_YES !=
269            GNUNET_DISK_file_lock (fd, 0,
270                                   sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
271                                   GNUNET_YES))
272     {
273       short_wait ();
274       if (0 == ++cnt % 10)
275       {
276         ec = errno;
277         LOG (GNUNET_ERROR_TYPE_ERROR,
278              _("Could not acquire lock on file `%s': %s...\n"), filename,
279              STRERROR (ec));
280       }
281     }
282     LOG (GNUNET_ERROR_TYPE_INFO,
283          _("Creating a new private key.  This may take a while.\n"));
284     priv = GNUNET_CRYPTO_ecdsa_key_create ();
285     GNUNET_assert (NULL != priv);
286     GNUNET_assert (sizeof (*priv) ==
287                    GNUNET_DISK_file_write (fd, priv, sizeof (*priv)));
288     GNUNET_DISK_file_sync (fd);
289     if (GNUNET_YES !=
290         GNUNET_DISK_file_unlock (fd, 0,
291                                  sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
292       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
293     GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
294     return priv;
295   }
296   /* key file exists already, read it! */
297   fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
298                               GNUNET_DISK_PERM_NONE);
299   if (NULL == fd)
300   {
301     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
302     return NULL;
303   }
304   cnt = 0;
305   while (1)
306   {
307     if (GNUNET_YES !=
308         GNUNET_DISK_file_lock (fd, 0,
309                                sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
310                                GNUNET_NO))
311     {
312       if (0 == ++cnt % 60)
313       {
314         ec = errno;
315         LOG (GNUNET_ERROR_TYPE_ERROR,
316              _("Could not acquire lock on file `%s': %s...\n"), filename,
317              STRERROR (ec));
318         LOG (GNUNET_ERROR_TYPE_ERROR,
319              _
320              ("This may be ok if someone is currently generating a private key.\n"));
321       }
322       short_wait ();
323       continue;
324     }
325     if (GNUNET_YES != GNUNET_DISK_file_test (filename))
326     {
327       /* eh, what!? File we opened is now gone!? */
328       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
329       if (GNUNET_YES !=
330           GNUNET_DISK_file_unlock (fd, 0,
331                                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
332         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
333       GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd));
334
335       return NULL;
336     }
337     if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
338       fs = 0;
339     if (fs < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))
340     {
341       /* maybe we got the read lock before the key generating
342        * process had a chance to get the write lock; give it up! */
343       if (GNUNET_YES !=
344           GNUNET_DISK_file_unlock (fd, 0,
345                                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
346         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
347       if (0 == ++cnt % 10)
348       {
349         LOG (GNUNET_ERROR_TYPE_ERROR,
350              _("When trying to read key file `%s' I found %u bytes but I need at least %u.\n"),
351              filename, (unsigned int) fs,
352              (unsigned int) sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
353         LOG (GNUNET_ERROR_TYPE_ERROR,
354              _("This may be ok if someone is currently generating a key.\n"));
355       }
356       short_wait ();                /* wait a bit longer! */
357       continue;
358     }
359     break;
360   }
361   fs = sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
362   priv = GNUNET_malloc (fs);
363   GNUNET_assert (fs == GNUNET_DISK_file_read (fd, priv, fs));
364   if (GNUNET_YES !=
365       GNUNET_DISK_file_unlock (fd, 0,
366                                sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
367     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
368   GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
369   return priv;
370 }
371
372
373 /**
374  * Create a new private key by reading our peer's key from
375  * the file specified in the configuration.
376  *
377  * @param cfg the configuration to use
378  * @return new private key, NULL on error (for example,
379  *   permission denied)
380  */
381 struct GNUNET_CRYPTO_EddsaPrivateKey *
382 GNUNET_CRYPTO_eddsa_key_create_from_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
383 {
384   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
385   char *fn;
386
387   if (GNUNET_OK !=
388       GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY", &fn))
389     return NULL;
390   priv = GNUNET_CRYPTO_eddsa_key_create_from_file (fn);
391   GNUNET_free (fn);
392   return priv;
393 }
394
395
396 /**
397  * Retrieve the identity of the host's peer.
398  *
399  * @param cfg configuration to use
400  * @param dst pointer to where to write the peer identity
401  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the identity
402  *         could not be retrieved
403  */
404 int
405 GNUNET_CRYPTO_get_peer_identity (const struct GNUNET_CONFIGURATION_Handle *cfg,
406                                  struct GNUNET_PeerIdentity *dst)
407 {
408   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
409
410   if (NULL == (priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg)))
411   {
412     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
413                 _("Could not load peer's private key\n"));
414     return GNUNET_SYSERR;
415   }
416   GNUNET_CRYPTO_eddsa_key_get_public (priv, &dst->public_key);
417   GNUNET_free (priv);
418   return GNUNET_OK;
419 }
420
421
422 /**
423  * Setup a key file for a peer given the name of the
424  * configuration file (!).  This function is used so that
425  * at a later point code can be certain that reading a
426  * key is fast (for example in time-dependent testcases).
427  *
428  * @param cfg_name name of the configuration file to use
429  */
430 void
431 GNUNET_CRYPTO_eddsa_setup_key (const char *cfg_name)
432 {
433   struct GNUNET_CONFIGURATION_Handle *cfg;
434   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
435
436   cfg = GNUNET_CONFIGURATION_create ();
437   (void) GNUNET_CONFIGURATION_load (cfg, cfg_name);
438   priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
439   if (NULL != priv)
440     GNUNET_free (priv);
441   GNUNET_CONFIGURATION_destroy (cfg);
442 }
443
444 /* end of crypto_ecc_setup.c */