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)
13 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
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"
28 // Windows specific implementation in mono-rand-windows.c
29 #elif defined (HAVE_SYS_UN_H)
34 #include <sys/socket.h>
35 #include <sys/types.h>
38 #ifdef HAVE_SYS_RANDOM_H
39 #include <sys/random.h>
42 #ifndef NAME_DEV_URANDOM
43 #define NAME_DEV_URANDOM "/dev/urandom"
46 static gboolean use_egd
= FALSE
;
47 static gint file
= -1;
50 /* Fill buffer with buffer_size random bytes generated by getrandom():
52 - Return 0 if getrandom() is not available (failed with ENOSYS or EPERM)
54 getrandom() is retried if it failed with EINTR: interrupted by a signal. */
56 mono_getrandom (guchar
*buffer
, gssize buffer_size
, gint flags
, MonoError
*error
)
58 g_assert (buffer
|| !buffer_size
);
60 static gboolean getrandom_fail
;
65 /* Read until the buffer is filled. This may block if random pool isn't initialized. */
66 while (buffer_size
> 0) {
67 gssize
const err
= getrandom (buffer
, buffer_size
, flags
);
71 if (errno
== ENOSYS
|| errno
== EPERM
) {
72 getrandom_fail
= TRUE
;
75 g_warning ("Entropy error! Error in getrandom (%s).", strerror (errno
));
76 /* exception will be thrown in managed code */
77 mono_error_set_execution_engine (error
, "Entropy error! Error in getrandom (%s).", strerror (errno
));
85 #endif /* HAVE_GETRANDOM */
87 #ifdef HAVE_GETENTROPY
88 /* Fill buffer with buffer_size random bytes generated by getentropy():
90 - Return 0 if getentropy() is not available (failed with ENOSYS or EPERM)
92 getentropy() is retried if it failed with EINTR: interrupted by a signal. */
94 mono_getentropy (guchar
*buffer
, gssize buffer_size
, MonoError
*error
)
96 g_assert (buffer
|| !buffer_size
);
98 static gboolean getentropy_fail
;
103 /* Read until the buffer is filled. This may block if random pool isn't initialized. */
104 while (buffer_size
> 0) {
105 gssize
const len
= MIN (buffer_size
, 256);
106 gint
const err
= getentropy (buffer
, len
);
110 if (errno
== ENOSYS
|| errno
== EPERM
) {
111 getentropy_fail
= TRUE
;
114 g_warning ("Entropy error! Error in getentropy (%s).", strerror (errno
));
115 /* exception will be thrown in managed code */
116 mono_error_set_execution_engine (error
, "Entropy error! Error in getentropy (%s).", strerror (errno
));
124 #endif /* HAVE_GETENTROPY */
127 get_entropy_from_egd (const char *path
, guchar
*buffer
, gssize buffer_size
, MonoError
*error
)
129 g_assert (buffer
|| !buffer_size
);
131 struct sockaddr_un egd_addr
;
137 socket_fd
= socket (PF_UNIX
, SOCK_STREAM
, 0);
142 egd_addr
.sun_family
= AF_UNIX
;
143 memcpy (egd_addr
.sun_path
, path
, sizeof (egd_addr
.sun_path
) - 1);
144 egd_addr
.sun_path
[sizeof (egd_addr
.sun_path
) - 1] = '\0';
145 ret
= connect (socket_fd
, (struct sockaddr
*) &egd_addr
, sizeof (egd_addr
));
151 g_warning ("Entropy problem! Can't create or connect to egd socket %s", path
);
152 mono_error_set_execution_engine (error
, "Failed to open egd socket %s: %s", path
, strerror (err
));
156 while (buffer_size
> 0) {
160 /* block until daemon can return enough entropy */
162 request
[1] = buffer_size
< 255 ? buffer_size
: 255;
164 int sent
= write (socket_fd
, request
+ count
, 2 - count
);
168 } else if (err
== EINTR
) {
172 g_warning ("Send egd request failed %d", err
);
173 mono_error_set_execution_engine (error
, "Failed to send request to egd socket: %s", strerror (err
));
179 while (count
!= request
[1]) {
181 received
= read (socket_fd
, buffer
+ offset
, request
[1] - count
);
186 } else if (received
< 0 && err
== EINTR
) {
190 g_warning ("Receive egd request failed %d", err
);
191 mono_error_set_execution_engine (error
, "Failed to get response from egd socket: %s", strerror(err
));
196 buffer_size
-= request
[1];
203 mono_rand_open (void)
205 static gint32 status
;
206 if (status
!= 0 || mono_atomic_cas_i32 (&status
, 1, 0) != 0) {
208 mono_thread_info_yield ();
212 #ifdef NAME_DEV_URANDOM
214 file
= open (NAME_DEV_URANDOM
, O_RDONLY
);
216 #ifdef NAME_DEV_RANDOM
218 file
= open (NAME_DEV_RANDOM
, O_RDONLY
);
221 use_egd
= g_hasenv ("MONO_EGD_SOCKET");
229 mono_rand_init (const guchar
*seed
, gssize seed_size
)
231 // file < 0 is expected in the egd case
232 return (!use_egd
&& file
< 0) ? (gpointer
)NULL
: GINT_TO_POINTER (file
);
236 mono_rand_try_get_bytes (gpointer
*handle
, guchar
*buffer
, gssize buffer_size
, MonoError
*error
)
238 g_assert (buffer
|| !buffer_size
);
243 #if defined(HAVE_GETRANDOM) || defined(HAVE_GETENTROPY)
244 #ifdef HAVE_GETRANDOM
245 gint
const res
= mono_getrandom (buffer
, buffer_size
, 0, error
);
247 gint
const res
= mono_getentropy (buffer
, buffer_size
, error
);
255 /* getrandom() or getentropy() function is not available: failed with
256 ENOSYS or EPERM. Fall back on reading from /dev/urandom. */
260 char *socket_path
= g_getenv ("MONO_EGD_SOCKET");
261 /* exception will be thrown in managed code */
262 if (socket_path
== NULL
) {
266 get_entropy_from_egd (socket_path
, buffer
, buffer_size
, error
);
267 g_free (socket_path
);
269 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
270 while (buffer_size
> 0) {
271 gssize
const err
= read (file
, buffer
, buffer_size
);
275 g_warning("Entropy error! Error in read (%s).", strerror (errno
));
276 /* exception will be thrown in managed code */
277 mono_error_set_execution_engine (error
, "Entropy error! Error in read (%s).", strerror (errno
));
288 mono_rand_close (gpointer provider
)
298 mono_rand_open (void)
300 static gint32 status
;
301 if (status
!= 0 || mono_atomic_cas_i32 (&status
, 1, 0) != 0) {
303 mono_thread_info_yield ();
315 mono_rand_init (const guchar
*seed
, gssize seed_size
)
317 return "srand"; // NULL will be interpreted as failure; return arbitrary nonzero pointer
321 mono_rand_try_get_bytes (gpointer
*handle
, guchar
*buffer
, gssize buffer_size
, MonoError
*error
)
323 // This functions is not used on any mainstream platform, perhaps not at all.
325 g_assert (buffer
|| !buffer_size
);
329 g_static_assert (RAND_MAX
>= 0xFF);
331 while (buffer_size
> 0) {
332 int const i
= rand ();
334 if (buffer_size
>= (j
= 4) && RAND_MAX
>= 0xFFFFFFFF)
335 *(gint32
*) buffer
= i
;
336 else if (buffer_size
>= (j
= 2) && RAND_MAX
>= 0xFFFF)
337 *(gint16
*) buffer
= i
;
340 *(gint8
*) buffer
= i
;
350 mono_rand_close (gpointer provider
)
357 * mono_rand_try_get_uint32:
358 * \param handle A pointer to an RNG handle. Handle is set to NULL on failure.
359 * \param val A pointer to a 32-bit unsigned int, to which the result will be written.
360 * \param min Result will be greater than or equal to this value.
361 * \param max Result will be less than or equal to this value.
362 * Extracts one 32-bit unsigned int from an RNG handle.
363 * \returns FALSE on failure, TRUE on success.
366 mono_rand_try_get_uint32 (gpointer
*handle
, guint32
*val
, guint32 min
, guint32 max
, MonoError
*error
)
369 if (!mono_rand_try_get_bytes (handle
, (guchar
*) val
, sizeof (guint32
), error
))
372 double randomDouble
= ((gdouble
) *val
) / ( ((double)G_MAXUINT32
) + 1 ); // Range is [0,1)
373 *val
= (guint32
) (randomDouble
* (max
- min
+ 1) + min
);
375 g_assert (*val
>= min
);
376 g_assert (*val
<= max
);