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)
19 #include "mono-rand.h"
20 #include "metadata/exception.h"
21 #include "metadata/object.h"
28 #ifndef PROV_INTEL_SEC
29 #define PROV_INTEL_SEC 22
31 #ifndef CRYPT_VERIFY_CONTEXT
32 #define CRYPT_VERIFY_CONTEXT 0xF0000000
38 /* FALSE == Local (instance) handle for randomness */
43 mono_rand_init (guchar *seed, gint seed_size)
45 HCRYPTPROV provider = 0;
47 /* There is no need to create a container for just random data,
48 * so we can use CRYPT_VERIFY_CONTEXT (one call) see:
49 * http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
51 /* We first try to use the Intel PIII RNG if drivers are present */
52 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT)) {
53 /* not a PIII or no drivers available, use default RSA CSP */
54 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT)) {
55 /* exception will be thrown in managed code */
60 /* seed the CSP with the supplied buffer (if present) */
61 if (provider != 0 && seed) {
62 /* the call we replace the seed with random - this isn't what is
63 * expected from the class library user */
64 guchar *data = g_malloc (seed_size);
66 memcpy (data, seed, seed_size);
67 /* add seeding material to the RNG */
68 CryptGenRandom (provider, seed_size, data);
69 /* zeroize and free */
70 memset (data, 0, seed_size);
75 return (gpointer) provider;
79 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
84 provider = (HCRYPTPROV) *handle;
86 if (!CryptGenRandom (provider, buffer_size, buffer)) {
87 CryptReleaseContext (provider, 0);
88 /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
89 provider = (HCRYPTPROV) mono_rand_init (NULL, 0);
90 if (!CryptGenRandom (provider, buffer_size, buffer)) {
91 /* exception will be thrown in managed code */
92 CryptReleaseContext (provider, 0);
101 mono_rand_close (gpointer handle)
103 CryptReleaseContext ((HCRYPTPROV) handle, 0);
106 #elif defined (HAVE_SYS_UN_H) && !defined(__native_client__)
111 #include <sys/socket.h>
112 #include <sys/types.h>
115 #ifndef NAME_DEV_URANDOM
116 #define NAME_DEV_URANDOM "/dev/urandom"
119 static gboolean use_egd = FALSE;
120 static gint file = -1;
123 get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size)
125 struct sockaddr_un egd_addr;
130 file = socket (PF_UNIX, SOCK_STREAM, 0);
134 egd_addr.sun_family = AF_UNIX;
135 strncpy (egd_addr.sun_path, path, sizeof (egd_addr.sun_path) - 1);
136 egd_addr.sun_path [sizeof (egd_addr.sun_path) - 1] = '\0';
137 ret = connect (file, (struct sockaddr*) &egd_addr, sizeof (egd_addr));
142 g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
143 mono_raise_exception (mono_get_exception_execution_engine ("Failed to open egd socket"));
146 while (buffer_size > 0) {
150 /* block until daemon can return enough entropy */
152 request [1] = buffer_size < 255 ? buffer_size : 255;
154 int sent = write (file, request + count, 2 - count);
157 } else if (errno == EINTR) {
161 g_warning ("Send egd request failed %d", errno);
162 mono_raise_exception (mono_get_exception_execution_engine ("Failed to send request to egd socket"));
167 while (count != request [1]) {
169 received = read (file, buffer + offset, request [1] - count);
173 } else if (received < 0 && errno == EINTR) {
177 g_warning ("Receive egd request failed %d", errno);
178 mono_raise_exception (mono_get_exception_execution_engine ("Failed to get response from egd socket"));
182 buffer_size -= request [1];
189 mono_rand_open (void)
191 if (use_egd || (file >= 0))
194 #ifdef NAME_DEV_URANDOM
195 file = open (NAME_DEV_URANDOM, O_RDONLY);
197 #ifdef NAME_DEV_RANDOM
199 file = open (NAME_DEV_RANDOM, O_RDONLY);
202 use_egd = g_getenv("MONO_EGD_SOCKET") != NULL;
208 mono_rand_init (guchar *seed, gint seed_size)
210 /* if required exception will be thrown in managed code */
211 return (!use_egd && file < 0) ? NULL : GINT_TO_POINTER (file);
215 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
220 const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
221 /* exception will be thrown in managed code */
222 if (socket_path == NULL) {
226 get_entropy_from_egd (socket_path, buffer, buffer_size);
228 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
233 err = read (file, buffer + count, buffer_size - count);
237 g_warning("Entropy error! Error in read (%s).", strerror (errno));
238 /* exception will be thrown in managed code */
242 } while (count < buffer_size);
248 mono_rand_close (gpointer provider)
258 mono_rand_open (void)
265 mono_rand_init (guchar *seed, gint seed_size)
271 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
276 if (buffer_size - count >= sizeof (gint32) && RAND_MAX >= 0xFFFFFFFF) {
277 *(gint32*) buffer = rand();
278 count += sizeof (gint32);
279 buffer += sizeof (gint32) / sizeof (guchar);
280 } else if (buffer_size - count >= sizeof (gint16) && RAND_MAX >= 0xFFFF) {
281 *(gint16*) buffer = rand();
282 count += sizeof (gint16);
283 buffer += sizeof (gint16) / sizeof (guchar);
284 } else if (buffer_size - count >= sizeof (gint8) && RAND_MAX >= 0xFF) {
285 *(gint8*) buffer = rand();
286 count += sizeof (gint8);
287 buffer += sizeof (gint8) / sizeof (guchar);
289 } while (count < buffer_size);
295 mono_rand_close (gpointer provider)
302 mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max)
305 if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32)))
308 *val = (guint32) (((gdouble) *val) / G_MAXUINT32 * (max - min) + min);
309 g_assert (*val >= min);
310 g_assert (*val <= max);