NaCl runtime fixes
[mono.git] / mono / metadata / rand.c
index c51c51c3e1861bb5f33617b79395b1169b5aa40b..4ed1203f85df3390b9d75bd5c597ec955c0c6dea 100644 (file)
@@ -1,12 +1,13 @@
 /*
  * rand.c: System.Security.Cryptography.RNGCryptoServiceProvider support
  *
- * Author:
+ * Authors:
  *      Mark Crichton (crichton@gimp.org)
  *      Patrik Torstensson (p@rxc.se)
+ *     Sebastien Pouliot (sebastien@ximian.com)
  *
- * (C) 2001 Ximian, Inc.
- *
+ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  */
 
 #include <config.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
 
 #include <mono/metadata/object.h>
 #include <mono/metadata/rand.h>
 #include <mono/metadata/exception.h>
 
-#if !defined(PLATFORM_WIN32)
+#if !defined(__native_client__) && !defined(HOST_WIN32)
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <errno.h>
@@ -91,104 +97,225 @@ get_entropy_from_server (const char *path, guchar *buf, int len)
 }
 #endif
 
-#if defined (PLATFORM_WIN32)
-
-#include <WinCrypt.h>
-\r
-static int s_providerInitialized = 0;\r
-static HCRYPTPROV s_provider;\r
-
-static HCRYPTPROV GetProvider()\r
-{\r
-    if (s_providerInitialized == 1)\r
-        return s_provider;\r
-\r
-    if (!CryptAcquireContext (&s_provider, NULL, NULL, PROV_RSA_FULL, 0))  \r
-    {\r
-        if (GetLastError() != NTE_BAD_KEYSET)\r
-            mono_raise_exception (mono_get_exception_execution_engine ("Failed to acquire crypt context"));\r
-\r
-               // Generate a new keyset if needed\r
-        if (!CryptAcquireContext (&s_provider, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))\r
-            mono_raise_exception (mono_get_exception_execution_engine ("Failed to acquire crypt context (new keyset)"));\r
-    }\r
-    \r
-    s_providerInitialized =  1;\r
-\r
-    return s_provider;\r
-}\r
-
-void ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_InternalGetBytes(MonoObject *self, MonoArray *arry)
+#if defined (HOST_WIN32)
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#ifndef PROV_INTEL_SEC
+#define PROV_INTEL_SEC         22
+#endif
+#ifndef CRYPT_VERIFY_CONTEXT
+#define CRYPT_VERIFY_CONTEXT   0xF0000000
+#endif
+
+MonoBoolean
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
+{
+       /* FALSE == Local (instance) handle for randomness */
+       return FALSE;
+}
+
+gpointer
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
 {
-    guint32 len;
-    guchar *buf;
+       HCRYPTPROV provider = 0;
 
-    len = mono_array_length (arry);
-    buf = mono_array_addr (arry, guchar, 0);
+       /* There is no need to create a container for just random data,
+          so we can use CRYPT_VERIFY_CONTEXT (one call) see: 
+          http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
 
-    if (0 == CryptGenRandom (GetProvider(), len, buf))
-       mono_raise_exception (mono_get_exception_execution_engine ("Failed to generate random bytes from CryptoAPI"));\r
+       /* We first try to use the Intel PIII RNG if drivers are present */
+       if (!CryptAcquireContext (&provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT)) {
+               /* not a PIII or no drivers available, use default RSA CSP */
+               if (!CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT)) {
+                       provider = 0;
+                       /* exception will be thrown in managed code */
+               }
+       }
+
+       /* seed the CSP with the supplied buffer (if present) */
+       if ((provider != 0) && (seed)) {
+               guint32 len = mono_array_length (seed);
+               guchar *buf = mono_array_addr (seed, guchar, 0);
+               /* the call we replace the seed with random - this isn't what is
+                  expected from the class library user */
+               guchar *data = g_malloc (len);
+               if (data) {
+                       memcpy (data, buf, len);
+                       /* add seeding material to the RNG */
+                       CryptGenRandom (provider, len, data);
+                       /* zeroize and free */
+                       memset (data, 0, len);
+                       g_free (data);
+               }
+       }
+
+       return (gpointer) provider;     
 }
 
+gpointer
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
+{
+       HCRYPTPROV provider = (HCRYPTPROV) handle;
+       guint32 len = mono_array_length (arry);
+       guchar *buf = mono_array_addr (arry, guchar, 0);
+
+       if (!CryptGenRandom (provider, len, buf)) {
+               CryptReleaseContext (provider, 0);
+               /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
+               provider = (HCRYPTPROV) ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (NULL);
+               if (!CryptGenRandom (provider, len, buf)) {
+                       CryptReleaseContext (provider, 0);
+                       provider = 0;
+                       /* exception will be thrown in managed code */
+               }
+       }
+       return (gpointer) provider;
+}
+
+void
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) 
+{
+       CryptReleaseContext ((HCRYPTPROV) handle, 0);
+}
+
+#elif defined (__native_client__)
+
+#include <time.h>
+
+MonoBoolean
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
+{
+       srand (time (NULL));
+       return TRUE;
+}
+
+gpointer
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
+{
+       return -1;
+}
+
+gpointer 
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
+{      
+       guint32 len = mono_array_length (arry);
+       guchar *buf = mono_array_addr (arry, guchar, 0);
+
+       /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
+       gint count = 0;
+       gint err;
+
+       do {
+               if (len - count >= sizeof (long))
+               {
+                       *(long*)buf = rand();
+                       count += sizeof (long);
+               }
+               else if (len - count >= sizeof (short))
+               {
+                       *(short*)buf = rand();
+                       count += sizeof (short);
+               }
+               else if (len - count >= sizeof (char))
+               {
+                       *buf = rand();
+                       count += sizeof (char);
+               }
+       } while (count < len);
+
+       return (gpointer)-1L;
+}
+
+void
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) 
+{
+}
 #else
 
 #ifndef NAME_DEV_URANDOM
 #define NAME_DEV_URANDOM "/dev/urandom"
 #endif
 
-void 
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_InternalGetBytes (MonoObject *self, MonoArray *arry)
-{
-    guint32 len;
-    gint file = -1;
-    gint err;
-    gint count;
-    guchar *buf;
+static gboolean egd = FALSE;
+static gint file = -1;
 
-    len = mono_array_length(arry);
-    buf = mono_array_addr(arry, guchar, 0);
+MonoBoolean
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
+{
+       if (egd || (file >= 0))
+               return TRUE;
 
 #if defined (NAME_DEV_URANDOM)
-    file = open (NAME_DEV_URANDOM, O_RDONLY);
+       file = open (NAME_DEV_URANDOM, O_RDONLY);
 #endif
 
 #if defined (NAME_DEV_RANDOM)
-    if (file < 0)
-           file = open (NAME_DEV_RANDOM, O_RDONLY);
+       if (file < 0)
+               file = open (NAME_DEV_RANDOM, O_RDONLY);
 #endif
 
-    if (file < 0) {
-        const char *socket_path = getenv("MONO_EGD_SOCKET");
+       if (file < 0) {
+               const char *socket_path = g_getenv("MONO_EGD_SOCKET");
+               egd = (socket_path != NULL);
+       }
 
-        if (socket_path == NULL)
-            mono_raise_exception (mono_get_exception_execution_engine ("Failed to open /dev/urandom or /dev/random device, or find egd socket"));
+       /* TRUE == Global handle for randomness */
+       return TRUE;
+}
 
-        get_entropy_from_server (socket_path, mono_array_addr(arry, guchar, 0), mono_array_length(arry));
-        return;
-    }
+gpointer
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
+{
+       /* if required exception will be thrown in managed code */
+       return ((!egd && (file < 0)) ? NULL : GINT_TO_POINTER (file));
+}
 
-    /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
-    count = 0;
-    do {
-           err = read(file, buf + count, len - count);
-           count += err;
-    } while (err >= 0 && count < len);
-    close(file);
-
-    if (err < 0) {
-        g_warning("Entropy error! Error in read (%s).", strerror (errno));
-        mono_raise_exception (mono_get_exception_execution_engine ("Failed to read a random byte from /dev/urandom or /dev/random device"));\r
-    }
+gpointer 
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
+{
+       gint file = GPOINTER_TO_INT (handle);
+       guint32 len = mono_array_length (arry);
+       guchar *buf = mono_array_addr (arry, guchar, 0);
 
-}
+       if (egd) {
+               const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
+               /* exception will be thrown in managed code */
+               if (socket_path == NULL)
+                       return NULL; 
+               get_entropy_from_server (socket_path, mono_array_addr (arry, guchar, 0), mono_array_length (arry));
+               return (gpointer) -1;
+       } else {
+               /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
+               gint count = 0;
+               gint err;
 
-#endif /* OS definition */
+               do {
+                       err = read (file, buf + count, len - count);
+                       if (err < 0) {
+                               if (errno == EINTR)
+                                       continue;
+                               break;
+                       }
+                       count += err;
+               } while (count < len);
+
+               if (err < 0) {
+                       g_warning("Entropy error! Error in read (%s).", strerror (errno));
+                       /* exception will be thrown in managed code */
+                       return NULL;
+               }
+       }
+
+       /* We do not support PRNG seeding right now but the class library is this */
+
+       return handle;  
+}
 
 void
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_Seed (MonoArray *seed)
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) 
 {
-       /* actually we do not support any PRNG requiring seeding right now but
-       the class library is ready for such possibility - so this empty 
-       function is needed (e.g. a new or modified runtime) */
 }
 
+#endif /* OS definition */