2 * rand.c: System.Security.Cryptography.RNGCryptoServiceProvider support
5 * Mark Crichton (crichton@gimp.org)
6 * Patrik Torstensson (p@rxc.se)
7 * Sebastien Pouliot (sebastien@ximian.com)
9 * (C) 2001 Ximian, Inc.
10 * (C) 2004 Novell (http://www.novell.com)
15 #include <sys/types.h>
20 #include <mono/metadata/object.h>
21 #include <mono/metadata/rand.h>
22 #include <mono/metadata/exception.h>
24 #if !defined(PLATFORM_WIN32)
25 #include <sys/socket.h>
30 get_entropy_from_server (const char *path, guchar *buf, int len)
35 struct sockaddr_un egd_addr;
37 file = socket (PF_UNIX, SOCK_STREAM, 0);
41 egd_addr.sun_family = AF_UNIX;
42 strncpy (egd_addr.sun_path, path, MONO_SIZEOF_SUNPATH - 1);
43 egd_addr.sun_path [MONO_SIZEOF_SUNPATH-1] = '\0';
44 ret = connect (file, (struct sockaddr *)&egd_addr, sizeof(egd_addr));
49 g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
50 mono_raise_exception (mono_get_exception_execution_engine ("Failed to open egd socket"));
57 request [0] = 2; /* block until daemon can return enough entropy */
58 request [1] = len < 255 ? len : 255;
60 int sent = write (file, request + count, 2 - count);
63 else if (errno == EINTR)
67 g_warning ("Send egd request failed %d", errno);
68 mono_raise_exception (mono_get_exception_execution_engine ("Failed to send request to egd socket"));
73 while (count != request [1]) {
75 received = read(file, buf + offset, request [1] - count);
79 } else if (received < 0 && errno == EINTR) {
83 g_warning ("Receive egd request failed %d", errno);
84 mono_raise_exception (mono_get_exception_execution_engine ("Failed to get response from egd socket"));
95 #if defined (PLATFORM_WIN32)
99 #ifndef PROV_INTEL_SEC
100 #define PROV_INTEL_SEC 22
102 #ifndef CRYPT_VERIFY_CONTEXT
103 #define CRYPT_VERIFY_CONTEXT 0xF0000000
107 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
109 HCRYPTPROV provider = 0;
111 /* There is no need to create a container for just random data,
112 so we can use CRYPT_VERIFY_CONTEXT (one call) see:
113 http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
115 /* We first try to use the Intel PIII RNG if drivers are present */
116 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT)) {
117 /* not a PIII or no drivers available, use default RSA CSP */
118 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT)) {
120 /* exception will be thrown in managed code */
124 /* seed the CSP with the supplied buffer (if present) */
125 if ((provider != 0) && (seed)) {
126 guint32 len = mono_array_length (seed);
127 guchar *buf = mono_array_addr (seed, guchar, 0);
128 /* the call we replace the seed with random - this isn't what is
129 expected from the class library user */
130 guchar *data = g_malloc (len);
132 memcpy (data, buf, len);
133 /* add seeding material to the RNG */
134 CryptGenRandom (provider, len, data);
135 /* zeroize and free */
136 memset (data, 0, len);
141 return (gpointer) provider;
145 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
147 HCRYPTPROV provider = (HCRYPTPROV) handle;
148 guint32 len = mono_array_length (arry);
149 guchar *buf = mono_array_addr (arry, guchar, 0);
151 if (!CryptGenRandom (provider, len, buf)) {
152 CryptReleaseContext (provider, 0);
153 /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
154 provider = ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (NULL);
155 if (!CryptGenRandom (provider, len, buf)) {
156 CryptReleaseContext (provider, 0);
158 /* exception will be thrown in managed code */
164 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle)
166 CryptReleaseContext ((HCRYPTPROV) handle, 0);
171 #ifndef NAME_DEV_URANDOM
172 #define NAME_DEV_URANDOM "/dev/urandom"
175 static gboolean egd = FALSE;
178 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
185 #if defined (NAME_DEV_URANDOM)
186 file = open (NAME_DEV_URANDOM, O_RDONLY);
189 #if defined (NAME_DEV_RANDOM)
191 file = open (NAME_DEV_RANDOM, O_RDONLY);
195 const char *socket_path = g_getenv("MONO_EGD_SOCKET");
196 egd = (socket_path != NULL);
200 /* if required exception will be thrown in managed code */
201 return ((file < 0) ? NULL : (gpointer) file);
205 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
207 gint file = (gint) handle;
208 guint32 len = mono_array_length (arry);
209 guchar *buf = mono_array_addr (arry, guchar, 0);
212 const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
213 /* exception will be thrown in managed code */
214 if (socket_path == NULL)
216 get_entropy_from_server (socket_path, mono_array_addr (arry, guchar, 0), mono_array_length (arry));
220 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
225 err = read (file, buf + count, len - count);
227 } while (err >= 0 && count < len);
230 g_warning("Entropy error! Error in read (%s).", strerror (errno));
231 /* exception will be thrown in managed code */
236 /* We do not support PRNG seeding right now but the class library is this */
242 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle)
245 close ((gint) handle);
248 #endif /* OS definition */