/*
* 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>
}
#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 */