Merge branch 'cadet-new-options'
[oweals/gnunet.git] / src / util / crypto_random.c
1 /*
2      This file is part of GNUnet.  Copyright (C) 2001-2014 Christian Grothoff
3      (and other contributing authors)
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 /**
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_crypto_lib.h"
29 #include <gcrypt.h>
30
31 #define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-random", __VA_ARGS__)
32
33 #define LOG_STRERROR(kind, syscall) \
34   GNUNET_log_from_strerror (kind, "util-crypto-random", syscall)
35
36
37 /* TODO: ndurner, move this to plibc? */
38 /* The code is derived from glibc, obviously */
39 #if ! HAVE_RANDOM || ! HAVE_SRANDOM
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
53
54 static int32_t glibc_weak_rand32_state = 1;
55
56
57 void
58 glibc_weak_srand32 (int32_t s)
59 {
60   glibc_weak_rand32_state = s;
61 }
62
63
64 int32_t
65 glibc_weak_rand32 ()
66 {
67   int32_t val = glibc_weak_rand32_state;
68
69   val = ((glibc_weak_rand32_state * 1103515245) + 12345) & 0x7fffffff;
70   glibc_weak_rand32_state = val;
71   return val;
72 }
73 #endif
74
75 /**
76  * Create a cryptographically weak pseudo-random number in the interval of 0 to 1.
77  *
78  * @return number between 0 and 1.
79  */
80 static double
81 get_weak_random ()
82 {
83   return ((double) RANDOM () / RAND_MAX);
84 }
85
86
87 /**
88  * Seed a weak random generator. Only #GNUNET_CRYPTO_QUALITY_WEAK-mode generator
89  * can be seeded.
90  *
91  * @param seed the seed to use
92  */
93 void
94 GNUNET_CRYPTO_seed_weak_random (int32_t seed)
95 {
96   SRANDOM (seed);
97 }
98
99
100 /**
101  * @ingroup crypto
102  * Zero out @a buffer, securely against compiler optimizations.
103  * Used to delete key material.
104  *
105  * @param buffer the buffer to zap
106  * @param length buffer length
107  */
108 void
109 GNUNET_CRYPTO_zero_keys (void *buffer, size_t length)
110 {
111 #if HAVE_MEMSET_S
112   memset_s (buffer, length, 0, length);
113 #elif HAVE_EXPLICIT_BZERO
114   explicit_bzero (buffer, length);
115 #else
116   volatile unsigned char *p = buffer;
117   while (length--)
118     *p++ = 0;
119 #endif
120 }
121
122
123 /**
124  * @ingroup crypto
125  * Fill block with a random values.
126  *
127  * @param mode desired quality of the random number
128  * @param buffer the buffer to fill
129  * @param length buffer length
130  */
131 void
132 GNUNET_CRYPTO_random_block (enum GNUNET_CRYPTO_Quality mode,
133                             void *buffer,
134                             size_t length)
135 {
136 #ifdef gcry_fast_random_poll
137   static unsigned int invokeCount;
138 #endif
139   switch (mode)
140   {
141   case GNUNET_CRYPTO_QUALITY_STRONG:
142     /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
143 #ifdef gcry_fast_random_poll
144     if ((invokeCount++ % 256) == 0)
145       gcry_fast_random_poll ();
146 #endif
147     gcry_randomize (buffer, length, GCRY_STRONG_RANDOM);
148     return;
149   case GNUNET_CRYPTO_QUALITY_NONCE:
150     gcry_create_nonce (buffer, length);
151     return;
152   case GNUNET_CRYPTO_QUALITY_WEAK:
153     /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
154 #ifdef gcry_fast_random_poll
155     if ((invokeCount++ % 256) == 0)
156       gcry_fast_random_poll ();
157 #endif
158     gcry_randomize (buffer, length, GCRY_WEAK_RANDOM);
159     return;
160   default:
161     GNUNET_assert (0);
162   }
163 }
164
165
166 /**
167  * Produce a random unsigned 32-bit number modulo @a i.
168  *
169  * @param mode desired quality of the random number
170  * @param i the upper limit (exclusive) for the random number
171  * @return a random value in the interval [0,i[.
172  */
173 uint32_t
174 GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality mode, uint32_t i)
175 {
176 #ifdef gcry_fast_random_poll
177   static unsigned int invokeCount;
178 #endif
179   uint32_t ret;
180   uint32_t ul;
181
182   GNUNET_assert (i > 0);
183
184   switch (mode)
185   {
186   case GNUNET_CRYPTO_QUALITY_STRONG:
187     /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
188 #ifdef gcry_fast_random_poll
189     if ((invokeCount++ % 256) == 0)
190       gcry_fast_random_poll ();
191 #endif
192     ul = UINT32_MAX - (UINT32_MAX % i);
193     do
194     {
195       gcry_randomize ((unsigned char *) &ret,
196                       sizeof (uint32_t),
197                       GCRY_STRONG_RANDOM);
198     } while (ret >= ul);
199     return ret % i;
200   case GNUNET_CRYPTO_QUALITY_NONCE:
201     ul = UINT32_MAX - (UINT32_MAX % i);
202     do
203     {
204       gcry_create_nonce (&ret, sizeof (ret));
205     } while (ret >= ul);
206     return ret % i;
207   case GNUNET_CRYPTO_QUALITY_WEAK:
208     ret = i * get_weak_random ();
209     if (ret >= i)
210       ret = i - 1;
211     return ret;
212   default:
213     GNUNET_assert (0);
214   }
215   return 0;
216 }
217
218
219 /**
220  * Get an array with a random permutation of the
221  * numbers 0...n-1.
222  * @param mode #GNUNET_RANDOM_QUALITY_STRONG if the strong (but expensive)
223  *        PRNG should be used, #GNUNET_RANDOM_QUALITY_WEAK otherwise
224  * @param n the size of the array
225  * @return the permutation array (allocated from heap)
226  */
227 unsigned int *
228 GNUNET_CRYPTO_random_permute (enum GNUNET_CRYPTO_Quality mode, unsigned int n)
229 {
230   unsigned int *ret;
231   unsigned int i;
232   unsigned int tmp;
233   uint32_t x;
234
235   GNUNET_assert (n > 0);
236   ret = GNUNET_malloc (n * sizeof (unsigned int));
237   for (i = 0; i < n; i++)
238     ret[i] = i;
239   for (i = n - 1; i > 0; i--)
240   {
241     x = GNUNET_CRYPTO_random_u32 (mode, i + 1);
242     tmp = ret[x];
243     ret[x] = ret[i];
244     ret[i] = tmp;
245   }
246   return ret;
247 }
248
249
250 /**
251  * Generate random unsigned 64-bit value.
252  *
253  * @param mode desired quality of the random number
254  * @param max value returned will be in range [0,max) (exclusive)
255  * @return random 64-bit number
256  */
257 uint64_t
258 GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode, uint64_t max)
259 {
260   uint64_t ret;
261   uint64_t ul;
262
263   GNUNET_assert (max > 0);
264   switch (mode)
265   {
266   case GNUNET_CRYPTO_QUALITY_STRONG:
267     ul = UINT64_MAX - (UINT64_MAX % max);
268     do
269     {
270       gcry_randomize ((unsigned char *) &ret,
271                       sizeof (uint64_t),
272                       GCRY_STRONG_RANDOM);
273     } while (ret >= ul);
274     return ret % max;
275   case GNUNET_CRYPTO_QUALITY_NONCE:
276     ul = UINT64_MAX - (UINT64_MAX % max);
277     do
278     {
279       gcry_create_nonce (&ret, sizeof (ret));
280     } while (ret >= ul);
281
282     return ret % max;
283   case GNUNET_CRYPTO_QUALITY_WEAK:
284     ret = max * get_weak_random ();
285     if (ret >= max)
286       ret = max - 1;
287     return ret;
288   default:
289     GNUNET_assert (0);
290   }
291   return 0;
292 }
293
294
295 /**
296  * Allocation wrapper for libgcrypt, used to avoid bad locking
297  * strategy of libgcrypt implementation.
298  */
299 static void *
300 w_malloc (size_t n)
301 {
302   return calloc (n, 1);
303 }
304
305
306 /**
307  * Allocation wrapper for libgcrypt, used to avoid bad locking
308  * strategy of libgcrypt implementation.
309  */
310 static int
311 w_check (const void *p)
312 {
313   (void) p;
314   return 0; /* not secure memory */
315 }
316
317
318 /**
319  * Initialize libgcrypt.
320  */
321 void __attribute__ ((constructor)) GNUNET_CRYPTO_random_init ()
322 {
323   gcry_error_t rc;
324
325   if (! gcry_check_version (NEED_LIBGCRYPT_VERSION))
326   {
327     FPRINTF (
328       stderr,
329       _ ("libgcrypt has not the expected version (version %s is required).\n"),
330       NEED_LIBGCRYPT_VERSION);
331     GNUNET_assert (0);
332   }
333   /* set custom allocators */
334   gcry_set_allocation_handler (&w_malloc, &w_malloc, &w_check, &realloc, &free);
335   /* Disable use of secure memory */
336   if ((rc = gcry_control (GCRYCTL_DISABLE_SECMEM, 0)))
337     FPRINTF (stderr,
338              "Failed to set libgcrypt option %s: %s\n",
339              "DISABLE_SECMEM",
340              gcry_strerror (rc));
341   /* Otherwise gnunet-ecc takes forever to complete, besides
342      we are fine with "just" using GCRY_STRONG_RANDOM */
343   if ((rc = gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0)))
344     FPRINTF (stderr,
345              "Failed to set libgcrypt option %s: %s\n",
346              "ENABLE_QUICK_RANDOM",
347              gcry_strerror (rc));
348   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
349   gcry_fast_random_poll ();
350   GNUNET_CRYPTO_seed_weak_random (
351     time (NULL) ^
352     GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX));
353 }
354
355
356 /**
357  * Nicely shut down libgcrypt.
358  */
359 void __attribute__ ((destructor)) GNUNET_CRYPTO_random_fini ()
360 {
361   gcry_set_progress_handler (NULL, NULL);
362 #ifdef GCRYCTL_CLOSE_RANDOM_DEVICE
363   (void) gcry_control (GCRYCTL_CLOSE_RANDOM_DEVICE, 0);
364 #endif
365 }
366
367
368 /* end of crypto_random.c */