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