-towards IdP2
[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"),
123              filename,
124              STRERROR (ec));
125       }
126     }
127     LOG (GNUNET_ERROR_TYPE_INFO,
128          _("Creating a new private key.  This may take a while.\n"));
129     priv = GNUNET_CRYPTO_eddsa_key_create ();
130     GNUNET_assert (NULL != priv);
131     GNUNET_assert (sizeof (*priv) ==
132                    GNUNET_DISK_file_write (fd, priv, sizeof (*priv)));
133     GNUNET_DISK_file_sync (fd);
134     if (GNUNET_YES !=
135         GNUNET_DISK_file_unlock (fd, 0,
136                                  sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
137       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
138     GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
139     return priv;
140   }
141   /* key file exists already, read it! */
142   fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
143                               GNUNET_DISK_PERM_NONE);
144   if (NULL == fd)
145   {
146     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
147     return NULL;
148   }
149   cnt = 0;
150   while (1)
151   {
152     if (GNUNET_YES !=
153         GNUNET_DISK_file_lock (fd, 0,
154                                sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey),
155                                GNUNET_NO))
156     {
157       if (0 == ++cnt % 60)
158       {
159         ec = errno;
160         LOG (GNUNET_ERROR_TYPE_ERROR,
161              _("Could not acquire lock on file `%s': %s...\n"), filename,
162              STRERROR (ec));
163         LOG (GNUNET_ERROR_TYPE_ERROR,
164              _
165              ("This may be ok if someone is currently generating a private key.\n"));
166       }
167       short_wait ();
168       continue;
169     }
170     if (GNUNET_YES != GNUNET_DISK_file_test (filename))
171     {
172       /* eh, what!? File we opened is now gone!? */
173       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
174       if (GNUNET_YES !=
175           GNUNET_DISK_file_unlock (fd, 0,
176                                    sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
177         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
178       GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd));
179
180       return NULL;
181     }
182     if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
183       fs = 0;
184     if (fs < sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey))
185     {
186       /* maybe we got the read lock before the key generating
187        * process had a chance to get the write lock; give it up! */
188       if (GNUNET_YES !=
189           GNUNET_DISK_file_unlock (fd, 0,
190                                    sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
191         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
192       if (0 == ++cnt % 10)
193       {
194         LOG (GNUNET_ERROR_TYPE_ERROR,
195              _("When trying to read key file `%s' I found %u bytes but I need at least %u.\n"),
196              filename, (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   GNUNET_assert (fs == GNUNET_DISK_file_read (fd, priv, fs));
209   if (GNUNET_YES !=
210       GNUNET_DISK_file_unlock (fd, 0,
211                                sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)))
212     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
213   GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
214   return priv;
215 }
216
217
218 /**
219  * Create a new private key by reading it from a file.  If the
220  * files does not exist, create a new key and write it to the
221  * file.  Caller must free return value.  Note that this function
222  * can not guarantee that another process might not be trying
223  * the same operation on the same file at the same time.
224  * If the contents of the file
225  * are invalid the old file is deleted and a fresh key is
226  * created.
227  *
228  * @param filename name of file to use to store the key
229  * @return new private key, NULL on error (for example,
230  *   permission denied)
231  */
232 struct GNUNET_CRYPTO_EcdsaPrivateKey *
233 GNUNET_CRYPTO_ecdsa_key_create_from_file (const char *filename)
234 {
235   struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
236   struct GNUNET_DISK_FileHandle *fd;
237   unsigned int cnt;
238   int ec;
239   uint64_t fs;
240
241   if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
242     return NULL;
243   while (GNUNET_YES != GNUNET_DISK_file_test (filename))
244   {
245     fd = GNUNET_DISK_file_open (filename,
246                                 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
247                                 | GNUNET_DISK_OPEN_FAILIFEXISTS,
248                                 GNUNET_DISK_PERM_USER_READ |
249                                 GNUNET_DISK_PERM_USER_WRITE);
250     if (NULL == fd)
251     {
252       if (EEXIST == errno)
253       {
254         if (GNUNET_YES != GNUNET_DISK_file_test (filename))
255         {
256           /* must exist but not be accessible, fail for good! */
257           if (0 != ACCESS (filename, R_OK))
258             LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", filename);
259           else
260             GNUNET_break (0);   /* what is going on!? */
261           return NULL;
262         }
263         continue;
264       }
265       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
266       return NULL;
267     }
268     cnt = 0;
269     while (GNUNET_YES !=
270            GNUNET_DISK_file_lock (fd, 0,
271                                   sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
272                                   GNUNET_YES))
273     {
274       short_wait ();
275       if (0 == ++cnt % 10)
276       {
277         ec = errno;
278         LOG (GNUNET_ERROR_TYPE_ERROR,
279              _("Could not acquire lock on file `%s': %s...\n"), filename,
280              STRERROR (ec));
281       }
282     }
283     LOG (GNUNET_ERROR_TYPE_INFO,
284          _("Creating a new private key.  This may take a while.\n"));
285     priv = GNUNET_CRYPTO_ecdsa_key_create ();
286     GNUNET_assert (NULL != priv);
287     GNUNET_assert (sizeof (*priv) ==
288                    GNUNET_DISK_file_write (fd, priv, sizeof (*priv)));
289     GNUNET_DISK_file_sync (fd);
290     if (GNUNET_YES !=
291         GNUNET_DISK_file_unlock (fd, 0,
292                                  sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
293       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
294     GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
295     return priv;
296   }
297   /* key file exists already, read it! */
298   fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
299                               GNUNET_DISK_PERM_NONE);
300   if (NULL == fd)
301   {
302     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
303     return NULL;
304   }
305   cnt = 0;
306   while (1)
307   {
308     if (GNUNET_YES !=
309         GNUNET_DISK_file_lock (fd, 0,
310                                sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
311                                GNUNET_NO))
312     {
313       if (0 == ++cnt % 60)
314       {
315         ec = errno;
316         LOG (GNUNET_ERROR_TYPE_ERROR,
317              _("Could not acquire lock on file `%s': %s...\n"),
318              filename,
319              STRERROR (ec));
320         LOG (GNUNET_ERROR_TYPE_ERROR,
321              _
322              ("This may be ok if someone is currently generating a private key.\n"));
323       }
324       short_wait ();
325       continue;
326     }
327     if (GNUNET_YES != GNUNET_DISK_file_test (filename))
328     {
329       /* eh, what!? File we opened is now gone!? */
330       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
331       if (GNUNET_YES !=
332           GNUNET_DISK_file_unlock (fd, 0,
333                                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
334         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
335       GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd));
336
337       return NULL;
338     }
339     if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
340       fs = 0;
341     if (fs < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))
342     {
343       /* maybe we got the read lock before the key generating
344        * process had a chance to get the write lock; give it up! */
345       if (GNUNET_YES !=
346           GNUNET_DISK_file_unlock (fd, 0,
347                                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
348         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
349       if (0 == ++cnt % 10)
350       {
351         LOG (GNUNET_ERROR_TYPE_ERROR,
352              _("When trying to read key file `%s' I found %u bytes but I need at least %u.\n"),
353              filename, (unsigned int) fs,
354              (unsigned int) sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
355         LOG (GNUNET_ERROR_TYPE_ERROR,
356              _("This may be ok if someone is currently generating a key.\n"));
357       }
358       short_wait ();                /* wait a bit longer! */
359       continue;
360     }
361     break;
362   }
363   fs = sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
364   priv = GNUNET_malloc (fs);
365   GNUNET_assert (fs == GNUNET_DISK_file_read (fd, priv, fs));
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, "fcntl", filename);
370   GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
371   return priv;
372 }
373
374
375 /**
376  * Create a new private key by reading our peer's key from
377  * the file specified in the configuration.
378  *
379  * @param cfg the configuration to use
380  * @return new private key, NULL on error (for example,
381  *   permission denied)
382  */
383 struct GNUNET_CRYPTO_EddsaPrivateKey *
384 GNUNET_CRYPTO_eddsa_key_create_from_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
385 {
386   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
387   char *fn;
388
389   if (GNUNET_OK !=
390       GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY", &fn))
391     return NULL;
392   priv = GNUNET_CRYPTO_eddsa_key_create_from_file (fn);
393   GNUNET_free (fn);
394   return priv;
395 }
396
397
398 /**
399  * Retrieve the identity of the host's peer.
400  *
401  * @param cfg configuration to use
402  * @param dst pointer to where to write the peer identity
403  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the identity
404  *         could not be retrieved
405  */
406 int
407 GNUNET_CRYPTO_get_peer_identity (const struct GNUNET_CONFIGURATION_Handle *cfg,
408                                  struct GNUNET_PeerIdentity *dst)
409 {
410   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
411
412   if (NULL == (priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg)))
413   {
414     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
415                 _("Could not load peer's private key\n"));
416     return GNUNET_SYSERR;
417   }
418   GNUNET_CRYPTO_eddsa_key_get_public (priv, &dst->public_key);
419   GNUNET_free (priv);
420   return GNUNET_OK;
421 }
422
423
424 /**
425  * Setup a key file for a peer given the name of the
426  * configuration file (!).  This function is used so that
427  * at a later point code can be certain that reading a
428  * key is fast (for example in time-dependent testcases).
429  *
430  * @param cfg_name name of the configuration file to use
431  */
432 void
433 GNUNET_CRYPTO_eddsa_setup_key (const char *cfg_name)
434 {
435   struct GNUNET_CONFIGURATION_Handle *cfg;
436   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
437
438   cfg = GNUNET_CONFIGURATION_create ();
439   (void) GNUNET_CONFIGURATION_load (cfg, cfg_name);
440   priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
441   if (NULL != priv)
442     GNUNET_free (priv);
443   GNUNET_CONFIGURATION_destroy (cfg);
444 }
445
446 /* end of crypto_ecc_setup.c */