2007-04-06 Andreas Faerber <andreas.faerber@web.de>
[mono.git] / mono / metadata / rand.c
blob28e4b8716022d292b3251a7b774e2b5ccbdf43a8
1 /*
2 * rand.c: System.Security.Cryptography.RNGCryptoServiceProvider support
4 * Authors:
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)
13 #include <config.h>
14 #include <glib.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <unistd.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>
26 #include <sys/un.h>
27 #include <errno.h>
29 static void
30 get_entropy_from_server (const char *path, guchar *buf, int len)
32 int file;
33 gint ret;
34 guint offset = 0;
35 struct sockaddr_un egd_addr;
37 file = socket (PF_UNIX, SOCK_STREAM, 0);
38 if (file < 0)
39 ret = -1;
40 else {
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));
46 if (ret == -1) {
47 if (file >= 0)
48 close (file);
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"));
53 while (len > 0) {
54 guchar request[2];
55 gint count = 0;
57 request [0] = 2; /* block until daemon can return enough entropy */
58 request [1] = len < 255 ? len : 255;
59 while (count < 2) {
60 int sent = write (file, request + count, 2 - count);
61 if (sent >= 0)
62 count += sent;
63 else if (errno == EINTR)
64 continue;
65 else {
66 close (file);
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"));
72 count = 0;
73 while (count != request [1]) {
74 int received;
75 received = read(file, buf + offset, request [1] - count);
76 if (received > 0) {
77 count += received;
78 offset += received;
79 } else if (received < 0 && errno == EINTR) {
80 continue;
81 } else {
82 close (file);
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"));
88 len -= request [1];
91 close (file);
93 #endif
95 #if defined (PLATFORM_WIN32)
97 #include <windows.h>
98 #include <wincrypt.h>
100 #ifndef PROV_INTEL_SEC
101 #define PROV_INTEL_SEC 22
102 #endif
103 #ifndef CRYPT_VERIFY_CONTEXT
104 #define CRYPT_VERIFY_CONTEXT 0xF0000000
105 #endif
107 MonoBoolean
108 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
110 /* FALSE == Local (instance) handle for randomness */
111 return FALSE;
114 gpointer
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)) {
127 provider = 0;
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);
139 if (data) {
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);
145 g_free (data);
149 return (gpointer) provider;
152 gpointer
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);
165 provider = 0;
166 /* exception will be thrown in managed code */
169 return (gpointer) provider;
172 void
173 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle)
175 CryptReleaseContext ((HCRYPTPROV) handle, 0);
178 #else
180 #ifndef NAME_DEV_URANDOM
181 #define NAME_DEV_URANDOM "/dev/urandom"
182 #endif
184 static gboolean egd = FALSE;
185 static gint file = -1;
187 MonoBoolean
188 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
190 if (egd || (file >= 0))
191 return TRUE;
193 #if defined (NAME_DEV_URANDOM)
194 file = open (NAME_DEV_URANDOM, O_RDONLY);
195 #endif
197 #if defined (NAME_DEV_RANDOM)
198 if (file < 0)
199 file = open (NAME_DEV_RANDOM, O_RDONLY);
200 #endif
202 if (file < 0) {
203 const char *socket_path = g_getenv("MONO_EGD_SOCKET");
204 egd = (socket_path != NULL);
207 /* TRUE == Global handle for randomness */
208 return TRUE;
211 gpointer
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));
218 gpointer
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);
225 if (egd) {
226 const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
227 /* exception will be thrown in managed code */
228 if (socket_path == NULL)
229 return NULL;
230 get_entropy_from_server (socket_path, mono_array_addr (arry, guchar, 0), mono_array_length (arry));
231 return (gpointer) -1;
232 } else {
233 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
234 gint count = 0;
235 gint err;
237 do {
238 err = read (file, buf + count, len - count);
239 if (err < 0) {
240 if (errno == EINTR)
241 continue;
242 break;
244 count += err;
245 } while (count < len);
247 if (err < 0) {
248 g_warning("Entropy error! Error in read (%s).", strerror (errno));
249 /* exception will be thrown in managed code */
250 return NULL;
254 /* We do not support PRNG seeding right now but the class library is this */
256 return handle;
259 void
260 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle)
264 #endif /* OS definition */