-try with finished
[oweals/gnunet.git] / src / util / crypto_random.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
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 2, 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19
20 */
21
22 /**
23  * @file util/crypto_random.c
24  * @brief functions to gather random numbers
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_crypto_lib.h"
30 #include "gnunet_os_lib.h"
31 #include <gcrypt.h>
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
34
35 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
36
37 /* TODO: ndurner, move this to plibc? */
38 /* The code is derived from glibc, obviously */
39 #if MINGW
40 #ifdef RANDOM
41 #undef RANDOM
42 #endif
43 #ifdef SRANDOM
44 #undef SRANDOM
45 #endif
46 #define RANDOM() glibc_weak_rand32()
47 #define SRANDOM(s) glibc_weak_srand32(s)
48 static int32_t glibc_weak_rand32_state = 1;
49
50 void
51 glibc_weak_srand32 (int32_t s)
52 {
53   glibc_weak_rand32_state = s;
54 }
55
56 int32_t
57 glibc_weak_rand32 ()
58 {
59   int32_t val = glibc_weak_rand32_state;
60
61   val = ((glibc_weak_rand32_state * 1103515245) + 12345) & 0x7fffffff;
62   glibc_weak_rand32_state = val;
63   return val;
64 }
65 #endif
66
67 /**
68  * Create a cryptographically weak pseudo-random number in the interval of 0 to 1.
69  *
70  * @return number between 0 and 1.
71  */
72 static double
73 weak_random ()
74 {
75   return ((double) RANDOM () / RAND_MAX);
76 }
77
78 /**
79  * Seed a weak random generator. Only GNUNET_CRYPTO_QUALITY_WEAK-mode generator
80  * can be seeded.
81  *
82  * @param seed the seed to use
83  */
84 void
85 GNUNET_CRYPTO_seed_weak_random (int32_t seed)
86 {
87   SRANDOM (seed);
88 }
89
90 /**
91  * Produce a random value.
92  *
93  * @param mode desired quality of the random number
94  * @param i the upper limit (exclusive) for the random number
95  * @return a random value in the interval [0,i[.
96  */
97 uint32_t
98 GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality mode, uint32_t i)
99 {
100 #ifdef gcry_fast_random_poll
101   static unsigned int invokeCount;
102 #endif
103   uint32_t ret;
104   uint32_t ul;
105
106   GNUNET_assert (i > 0);
107
108   switch (mode)
109   {
110   case GNUNET_CRYPTO_QUALITY_STRONG:
111     /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
112 #ifdef gcry_fast_random_poll
113     if ((invokeCount++ % 256) == 0)
114       gcry_fast_random_poll ();
115 #endif
116     ul = UINT32_MAX - (UINT32_MAX % i);
117     do
118     {
119       gcry_randomize ((unsigned char *) &ret, sizeof (uint32_t),
120                       GCRY_STRONG_RANDOM);
121     }
122     while (ret >= ul);
123     return ret % i;
124   case GNUNET_CRYPTO_QUALITY_NONCE:
125     ul = UINT32_MAX - (UINT32_MAX % i);
126     do
127     {
128       gcry_create_nonce (&ret, sizeof (ret));
129     }
130     while (ret >= ul);
131     return ret % i;
132   case GNUNET_CRYPTO_QUALITY_WEAK:
133     ret = i * weak_random ();
134     if (ret >= i)
135       ret = i - 1;
136     return ret;
137   default:
138     GNUNET_assert (0);
139   }
140   return 0;
141 }
142
143
144 /**
145  * Get an array with a random permutation of the
146  * numbers 0...n-1.
147  * @param mode GNUNET_RANDOM_QUALITY_STRONG if the strong (but expensive)
148  *        PRNG should be used, GNUNET_RANDOM_QUALITY_WEAK otherwise
149  * @param n the size of the array
150  * @return the permutation array (allocated from heap)
151  */
152 unsigned int *
153 GNUNET_CRYPTO_random_permute (enum GNUNET_CRYPTO_Quality mode, unsigned int n)
154 {
155   unsigned int *ret;
156   unsigned int i;
157   unsigned int tmp;
158   uint32_t x;
159
160   GNUNET_assert (n > 0);
161   ret = GNUNET_malloc (n * sizeof (unsigned int));
162   for (i = 0; i < n; i++)
163     ret[i] = i;
164   for (i = n - 1; i > 0; i--)
165   {
166     x = GNUNET_CRYPTO_random_u32 (mode, i + 1);
167     tmp = ret[x];
168     ret[x] = ret[i];
169     ret[i] = tmp;
170   }
171   return ret;
172 }
173
174 /**
175  * Random on unsigned 64-bit values.
176  *
177  *
178  * @param mode desired quality of the random number
179  * @param max value returned will be in range [0,max) (exclusive)
180  * @return random 64-bit number
181  */
182 uint64_t
183 GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode, uint64_t max)
184 {
185   uint64_t ret;
186   uint64_t ul;
187
188   GNUNET_assert (max > 0);
189   switch (mode)
190   {
191   case GNUNET_CRYPTO_QUALITY_STRONG:
192     ul = UINT64_MAX - (UINT64_MAX % max);
193     do
194     {
195       gcry_randomize ((unsigned char *) &ret, sizeof (uint64_t),
196                       GCRY_STRONG_RANDOM);
197     }
198     while (ret >= ul);
199     return ret % max;
200   case GNUNET_CRYPTO_QUALITY_NONCE:
201     ul = UINT64_MAX - (UINT64_MAX % max);
202     do
203     {
204       gcry_create_nonce (&ret, sizeof (ret));
205     }
206     while (ret >= ul);
207
208     return ret % max;
209   case GNUNET_CRYPTO_QUALITY_WEAK:
210     ret = max * weak_random ();
211     if (ret >= max)
212       ret = max - 1;
213     return ret;
214   default:
215     GNUNET_assert (0);
216   }
217   return 0;
218 }
219
220 /**
221  * This function should only be called in testcases
222  * where strong entropy gathering is not desired
223  * (for example, for hostkey generation).
224  */
225 void
226 GNUNET_CRYPTO_random_disable_entropy_gathering ()
227 {
228   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
229 }
230
231
232 /**
233  * Process ID of the "find" process that we use for
234  * entropy gathering.
235  */
236 static struct GNUNET_OS_Process *genproc;
237
238 /**
239  * Function called by libgcrypt whenever we are
240  * blocked gathering entropy.
241  */
242 static void
243 entropy_generator (void *cls, const char *what, int printchar, int current,
244                    int total)
245 {
246   unsigned long code;
247   enum GNUNET_OS_ProcessStatusType type;
248   int ret;
249
250   if (0 != strcmp (what, "need_entropy"))
251     return;
252   if (current == total)
253   {
254     if (genproc != NULL)
255     {
256       if (0 != GNUNET_OS_process_kill (genproc, SIGTERM))
257         LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "kill");
258       GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (genproc));
259       GNUNET_OS_process_close (genproc);
260       genproc = NULL;
261     }
262     return;
263   }
264   if (genproc != NULL)
265   {
266     ret = GNUNET_OS_process_status (genproc, &type, &code);
267     if (ret == GNUNET_NO)
268       return;                   /* still running */
269     if (ret == GNUNET_SYSERR)
270     {
271       GNUNET_break (0);
272       return;
273     }
274     if (0 != GNUNET_OS_process_kill (genproc, SIGTERM))
275       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "kill");
276     GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (genproc));
277     GNUNET_OS_process_close (genproc);
278     genproc = NULL;
279   }
280   LOG (GNUNET_ERROR_TYPE_INFO, _("Starting `%s' process to generate entropy\n"),
281        "find");
282   genproc =
283       GNUNET_OS_start_process (NULL, NULL, "sh", "sh", "-c",
284                                "exec find / -mount -type f -exec cp {} /dev/null \\; 2>/dev/null",
285                                NULL);
286 }
287
288
289 static void
290 killfind ()
291 {
292   if (genproc != NULL)
293   {
294     GNUNET_OS_process_kill (genproc, SIGKILL);
295     GNUNET_OS_process_close (genproc);
296     genproc = NULL;
297   }
298 }
299
300
301 void __attribute__ ((constructor)) GNUNET_CRYPTO_random_init ()
302 {
303   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
304   if (!gcry_check_version (GCRYPT_VERSION))
305   {
306     FPRINTF (stderr,
307              _
308              ("libgcrypt has not the expected version (version %s is required).\n"),
309              GCRYPT_VERSION);
310     GNUNET_abort ();
311   }
312 #ifdef GCRYCTL_INITIALIZATION_FINISHED
313   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
314 #endif
315 #ifdef gcry_fast_random_poll
316   gcry_fast_random_poll ();
317 #endif
318   gcry_set_progress_handler (&entropy_generator, NULL);
319   atexit (&killfind);
320   GNUNET_CRYPTO_seed_weak_random (time (NULL) ^
321                                   GNUNET_CRYPTO_random_u32
322                                   (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX));
323 }
324
325
326 void __attribute__ ((destructor)) GNUNET_CRYPTO_random_fini ()
327 {
328   gcry_set_progress_handler (NULL, NULL);
329 }
330
331
332
333 /* end of crypto_random.c */