5 * Mark Crichton (crichton@gimp.org)
6 * Patrik Torstensson (p@rxc.se)
7 * Sebastien Pouliot (sebastien@ximian.com)
8 * Ludovic Henry (ludovic.henry@xamarin.com)
10 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
11 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
12 * Copyright 2001 Xamarin, Inc (http://www.novell.com)
20 #include "mono-error.h"
21 #include "mono-error-internals.h"
22 #include "mono-rand.h"
23 #include "mono-threads.h"
24 #include "metadata/exception.h"
25 #include "metadata/object.h"
32 #ifndef PROV_INTEL_SEC
33 #define PROV_INTEL_SEC 22
35 #ifndef CRYPT_VERIFY_CONTEXT
36 #define CRYPT_VERIFY_CONTEXT 0xF0000000
42 * Returns: True if random source is global, false if mono_rand_init can be called repeatedly to get randomness instances.
44 * Initializes entire RNG system. Must be called once per process before calling mono_rand_init.
54 * @seed: A string containing seed data
55 * @seed_size: Length of seed string
57 * Returns: On success, a non-NULL handle which can be used to fetch random data from mono_rand_try_get_bytes. On failure, NULL.
59 * Initializes an RNG client.
62 mono_rand_init (guchar *seed, gint seed_size)
64 HCRYPTPROV provider = 0;
66 /* There is no need to create a container for just random data,
67 * so we can use CRYPT_VERIFY_CONTEXT (one call) see:
68 * http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
70 /* We first try to use the Intel PIII RNG if drivers are present */
71 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT)) {
72 /* not a PIII or no drivers available, use default RSA CSP */
73 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT)) {
74 /* exception will be thrown in managed code */
79 /* seed the CSP with the supplied buffer (if present) */
80 if (provider != 0 && seed) {
81 /* the call we replace the seed with random - this isn't what is
82 * expected from the class library user */
83 guchar *data = g_malloc (seed_size);
85 memcpy (data, seed, seed_size);
86 /* add seeding material to the RNG */
87 CryptGenRandom (provider, seed_size, data);
88 /* zeroize and free */
89 memset (data, 0, seed_size);
94 return (gpointer) provider;
98 * mono_rand_try_get_bytes:
99 * @handle: A pointer to an RNG handle. Handle is set to NULL on failure.
100 * @buffer: A buffer into which to write random data.
101 * @buffer_size: Number of bytes to write into buffer.
102 * @error: Set on error.
104 * Returns: FALSE on failure and sets @error, TRUE on success.
106 * Extracts bytes from an RNG handle.
109 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
113 mono_error_init (error);
116 provider = (HCRYPTPROV) *handle;
118 if (!CryptGenRandom (provider, buffer_size, buffer)) {
119 CryptReleaseContext (provider, 0);
120 /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
121 provider = (HCRYPTPROV) mono_rand_init (NULL, 0);
122 if (!CryptGenRandom (provider, buffer_size, buffer)) {
123 /* exception will be thrown in managed code */
124 CryptReleaseContext (provider, 0);
126 mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Failed to gen random bytes (%d)", GetLastError ());
135 * @handle: An RNG handle.
136 * @buffer: A buffer into which to write random data.
137 * @buffer_size: Number of bytes to write into buffer.
139 * Releases an RNG handle.
142 mono_rand_close (gpointer handle)
144 CryptReleaseContext ((HCRYPTPROV) handle, 0);
147 #elif defined (HAVE_SYS_UN_H) && !defined(__native_client__)
152 #include <sys/socket.h>
153 #include <sys/types.h>
156 #ifndef NAME_DEV_URANDOM
157 #define NAME_DEV_URANDOM "/dev/urandom"
160 static gboolean use_egd = FALSE;
161 static gint file = -1;
164 get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size, MonoError *error)
166 struct sockaddr_un egd_addr;
172 mono_error_init (error);
174 file = socket (PF_UNIX, SOCK_STREAM, 0);
179 egd_addr.sun_family = AF_UNIX;
180 strncpy (egd_addr.sun_path, path, sizeof (egd_addr.sun_path) - 1);
181 egd_addr.sun_path [sizeof (egd_addr.sun_path) - 1] = '\0';
182 ret = connect (file, (struct sockaddr*) &egd_addr, sizeof (egd_addr));
188 g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
189 mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Failed to open egd socket %s: %s", path, strerror (err));
193 while (buffer_size > 0) {
197 /* block until daemon can return enough entropy */
199 request [1] = buffer_size < 255 ? buffer_size : 255;
201 int sent = write (file, request + count, 2 - count);
205 } else if (err == EINTR) {
209 g_warning ("Send egd request failed %d", err);
210 mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Failed to send request to egd socket: %s", strerror (err));
216 while (count != request [1]) {
218 received = read (file, buffer + offset, request [1] - count);
223 } else if (received < 0 && err == EINTR) {
227 g_warning ("Receive egd request failed %d", err);
228 mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Failed to get response from egd socket: %s", strerror(err));
233 buffer_size -= request [1];
240 mono_rand_open (void)
242 static gint32 status = 0;
243 if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) {
245 mono_thread_info_yield ();
249 #ifdef NAME_DEV_URANDOM
250 file = open (NAME_DEV_URANDOM, O_RDONLY);
252 #ifdef NAME_DEV_RANDOM
254 file = open (NAME_DEV_RANDOM, O_RDONLY);
257 use_egd = g_getenv("MONO_EGD_SOCKET") != NULL;
265 mono_rand_init (guchar *seed, gint seed_size)
267 // file < 0 is expected in the egd case
268 return (!use_egd && file < 0) ? NULL : GINT_TO_POINTER (file);
272 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
276 mono_error_init (error);
279 const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
280 /* exception will be thrown in managed code */
281 if (socket_path == NULL) {
285 get_entropy_from_egd (socket_path, buffer, buffer_size, error);
287 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
292 err = read (file, buffer + count, buffer_size - count);
296 g_warning("Entropy error! Error in read (%s).", strerror (errno));
297 /* exception will be thrown in managed code */
298 mono_error_set_generic_error (error, "System", "ExecutionEngineException", "Entropy error! Error in read (%s).", strerror (errno));
302 } while (count < buffer_size);
308 mono_rand_close (gpointer provider)
318 mono_rand_open (void)
320 static gint32 status = 0;
321 if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) {
323 mono_thread_info_yield ();
335 mono_rand_init (guchar *seed, gint seed_size)
337 return "srand"; // NULL will be interpreted as failure; return arbitrary nonzero pointer
341 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
345 mono_error_init (error);
348 if (buffer_size - count >= sizeof (gint32) && RAND_MAX >= 0xFFFFFFFF) {
349 *(gint32*) buffer = rand();
350 count += sizeof (gint32);
351 buffer += sizeof (gint32) / sizeof (guchar);
352 } else if (buffer_size - count >= sizeof (gint16) && RAND_MAX >= 0xFFFF) {
353 *(gint16*) buffer = rand();
354 count += sizeof (gint16);
355 buffer += sizeof (gint16) / sizeof (guchar);
356 } else if (buffer_size - count >= sizeof (gint8) && RAND_MAX >= 0xFF) {
357 *(gint8*) buffer = rand();
358 count += sizeof (gint8);
359 buffer += sizeof (gint8) / sizeof (guchar);
361 } while (count < buffer_size);
367 mono_rand_close (gpointer provider)
374 * mono_rand_try_get_uint32:
375 * @handle: A pointer to an RNG handle. Handle is set to NULL on failure.
376 * @val: A pointer to a 32-bit unsigned int, to which the result will be written.
377 * @min: Result will be greater than or equal to this value.
378 * @max: Result will be less than or equal to this value.
380 * Returns: FALSE on failure, TRUE on success.
382 * Extracts one 32-bit unsigned int from an RNG handle.
385 mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max, MonoError *error)
388 if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32), error))
391 double randomDouble = ((gdouble) *val) / ( ((double)G_MAXUINT32) + 1 ); // Range is [0,1)
392 *val = (guint32) (randomDouble * (max - min + 1) + min);
394 g_assert (*val >= min);
395 g_assert (*val <= max);