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 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
15 #include <sys/types.h>
25 #include <mono/metadata/object.h>
26 #include <mono/metadata/rand.h>
27 #include <mono/metadata/exception.h>
29 #if !defined(HOST_WIN32)
30 #include <sys/socket.h>
35 get_entropy_from_server (const char *path
, guchar
*buf
, int len
)
40 struct sockaddr_un egd_addr
;
42 file
= socket (PF_UNIX
, SOCK_STREAM
, 0);
46 egd_addr
.sun_family
= AF_UNIX
;
47 strncpy (egd_addr
.sun_path
, path
, MONO_SIZEOF_SUNPATH
- 1);
48 egd_addr
.sun_path
[MONO_SIZEOF_SUNPATH
-1] = '\0';
49 ret
= connect (file
, (struct sockaddr
*)&egd_addr
, sizeof(egd_addr
));
54 g_warning ("Entropy problem! Can't create or connect to egd socket %s", path
);
55 mono_raise_exception (mono_get_exception_execution_engine ("Failed to open egd socket"));
62 request
[0] = 2; /* block until daemon can return enough entropy */
63 request
[1] = len
< 255 ? len
: 255;
65 int sent
= write (file
, request
+ count
, 2 - count
);
68 else if (errno
== EINTR
)
72 g_warning ("Send egd request failed %d", errno
);
73 mono_raise_exception (mono_get_exception_execution_engine ("Failed to send request to egd socket"));
78 while (count
!= request
[1]) {
80 received
= read(file
, buf
+ offset
, request
[1] - count
);
84 } else if (received
< 0 && errno
== EINTR
) {
88 g_warning ("Receive egd request failed %d", errno
);
89 mono_raise_exception (mono_get_exception_execution_engine ("Failed to get response from egd socket"));
100 #if defined (HOST_WIN32)
103 #include <wincrypt.h>
105 #ifndef PROV_INTEL_SEC
106 #define PROV_INTEL_SEC 22
108 #ifndef CRYPT_VERIFY_CONTEXT
109 #define CRYPT_VERIFY_CONTEXT 0xF0000000
113 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
115 /* FALSE == Local (instance) handle for randomness */
120 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray
*seed
)
122 HCRYPTPROV provider
= 0;
124 /* There is no need to create a container for just random data,
125 so we can use CRYPT_VERIFY_CONTEXT (one call) see:
126 http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
128 /* We first try to use the Intel PIII RNG if drivers are present */
129 if (!CryptAcquireContext (&provider
, NULL
, NULL
, PROV_INTEL_SEC
, CRYPT_VERIFY_CONTEXT
)) {
130 /* not a PIII or no drivers available, use default RSA CSP */
131 if (!CryptAcquireContext (&provider
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_VERIFY_CONTEXT
)) {
133 /* exception will be thrown in managed code */
137 /* seed the CSP with the supplied buffer (if present) */
138 if ((provider
!= 0) && (seed
)) {
139 guint32 len
= mono_array_length (seed
);
140 guchar
*buf
= mono_array_addr (seed
, guchar
, 0);
141 /* the call we replace the seed with random - this isn't what is
142 expected from the class library user */
143 guchar
*data
= g_malloc (len
);
145 memcpy (data
, buf
, len
);
146 /* add seeding material to the RNG */
147 CryptGenRandom (provider
, len
, data
);
148 /* zeroize and free */
149 memset (data
, 0, len
);
154 return (gpointer
) provider
;
158 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle
, MonoArray
*arry
)
160 HCRYPTPROV provider
= (HCRYPTPROV
) handle
;
161 guint32 len
= mono_array_length (arry
);
162 guchar
*buf
= mono_array_addr (arry
, guchar
, 0);
164 if (!CryptGenRandom (provider
, len
, buf
)) {
165 CryptReleaseContext (provider
, 0);
166 /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
167 provider
= (HCRYPTPROV
) ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (NULL
);
168 if (!CryptGenRandom (provider
, len
, buf
)) {
169 CryptReleaseContext (provider
, 0);
171 /* exception will be thrown in managed code */
174 return (gpointer
) provider
;
178 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle
)
180 CryptReleaseContext ((HCRYPTPROV
) handle
, 0);
185 #ifndef NAME_DEV_URANDOM
186 #define NAME_DEV_URANDOM "/dev/urandom"
189 static gboolean egd
= FALSE
;
190 static gint file
= -1;
193 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
195 if (egd
|| (file
>= 0))
198 #if defined (NAME_DEV_URANDOM)
199 file
= open (NAME_DEV_URANDOM
, O_RDONLY
);
202 #if defined (NAME_DEV_RANDOM)
204 file
= open (NAME_DEV_RANDOM
, O_RDONLY
);
208 const char *socket_path
= g_getenv("MONO_EGD_SOCKET");
209 egd
= (socket_path
!= NULL
);
212 /* TRUE == Global handle for randomness */
217 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray
*seed
)
219 /* if required exception will be thrown in managed code */
220 return ((!egd
&& (file
< 0)) ? NULL
: GINT_TO_POINTER (file
));
224 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle
, MonoArray
*arry
)
226 gint file
= GPOINTER_TO_INT (handle
);
227 guint32 len
= mono_array_length (arry
);
228 guchar
*buf
= mono_array_addr (arry
, guchar
, 0);
231 const char *socket_path
= g_getenv ("MONO_EGD_SOCKET");
232 /* exception will be thrown in managed code */
233 if (socket_path
== NULL
)
235 get_entropy_from_server (socket_path
, mono_array_addr (arry
, guchar
, 0), mono_array_length (arry
));
236 return (gpointer
) -1;
238 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
243 err
= read (file
, buf
+ count
, len
- count
);
250 } while (count
< len
);
253 g_warning("Entropy error! Error in read (%s).", strerror (errno
));
254 /* exception will be thrown in managed code */
259 /* We do not support PRNG seeding right now but the class library is this */
265 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle
)
269 #endif /* OS definition */