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 * Copyright (C) 2004-2005 Novell, Inc (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)
100 #ifndef PROV_INTEL_SEC
101 #define PROV_INTEL_SEC 22
103 #ifndef CRYPT_VERIFY_CONTEXT
104 #define CRYPT_VERIFY_CONTEXT 0xF0000000
108 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
110 /* FALSE == Local (instance) handle for randomness */
115 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray
*seed
)
117 HCRYPTPROV provider
= 0;
119 /* There is no need to create a container for just random data,
120 so we can use CRYPT_VERIFY_CONTEXT (one call) see:
121 http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
123 /* We first try to use the Intel PIII RNG if drivers are present */
124 if (!CryptAcquireContext (&provider
, NULL
, NULL
, PROV_INTEL_SEC
, CRYPT_VERIFY_CONTEXT
)) {
125 /* not a PIII or no drivers available, use default RSA CSP */
126 if (!CryptAcquireContext (&provider
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_VERIFY_CONTEXT
)) {
128 /* exception will be thrown in managed code */
132 /* seed the CSP with the supplied buffer (if present) */
133 if ((provider
!= 0) && (seed
)) {
134 guint32 len
= mono_array_length (seed
);
135 guchar
*buf
= mono_array_addr (seed
, guchar
, 0);
136 /* the call we replace the seed with random - this isn't what is
137 expected from the class library user */
138 guchar
*data
= g_malloc (len
);
140 memcpy (data
, buf
, len
);
141 /* add seeding material to the RNG */
142 CryptGenRandom (provider
, len
, data
);
143 /* zeroize and free */
144 memset (data
, 0, len
);
149 return (gpointer
) provider
;
153 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle
, MonoArray
*arry
)
155 HCRYPTPROV provider
= (HCRYPTPROV
) handle
;
156 guint32 len
= mono_array_length (arry
);
157 guchar
*buf
= mono_array_addr (arry
, guchar
, 0);
159 if (!CryptGenRandom (provider
, len
, buf
)) {
160 CryptReleaseContext (provider
, 0);
161 /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
162 provider
= (HCRYPTPROV
) ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (NULL
);
163 if (!CryptGenRandom (provider
, len
, buf
)) {
164 CryptReleaseContext (provider
, 0);
166 /* exception will be thrown in managed code */
169 return (gpointer
) provider
;
173 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle
)
175 CryptReleaseContext ((HCRYPTPROV
) handle
, 0);
180 #ifndef NAME_DEV_URANDOM
181 #define NAME_DEV_URANDOM "/dev/urandom"
184 static gboolean egd
= FALSE
;
185 static gint file
= -1;
188 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
190 if (egd
|| (file
>= 0))
193 #if defined (NAME_DEV_URANDOM)
194 file
= open (NAME_DEV_URANDOM
, O_RDONLY
);
197 #if defined (NAME_DEV_RANDOM)
199 file
= open (NAME_DEV_RANDOM
, O_RDONLY
);
203 const char *socket_path
= g_getenv("MONO_EGD_SOCKET");
204 egd
= (socket_path
!= NULL
);
207 /* TRUE == Global handle for randomness */
212 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray
*seed
)
214 /* if required exception will be thrown in managed code */
215 return ((!egd
&& (file
< 0)) ? NULL
: GINT_TO_POINTER (file
));
219 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle
, MonoArray
*arry
)
221 gint file
= GPOINTER_TO_INT (handle
);
222 guint32 len
= mono_array_length (arry
);
223 guchar
*buf
= mono_array_addr (arry
, guchar
, 0);
226 const char *socket_path
= g_getenv ("MONO_EGD_SOCKET");
227 /* exception will be thrown in managed code */
228 if (socket_path
== NULL
)
230 get_entropy_from_server (socket_path
, mono_array_addr (arry
, guchar
, 0), mono_array_length (arry
));
231 return (gpointer
) -1;
233 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
238 err
= read (file
, buf
+ count
, len
- count
);
245 } while (count
< len
);
248 g_warning("Entropy error! Error in read (%s).", strerror (errno
));
249 /* exception will be thrown in managed code */
254 /* We do not support PRNG seeding right now but the class library is this */
260 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle
)
264 #endif /* OS definition */