Merge pull request #216 from ilkerde/master
[mono.git] / mcs / class / corlib / System.Security.Cryptography / RNGCryptoServiceProvider.cs
index bf71a10bb17b4f1d15b67fd7a5f4bcd2cd33be90..e0ffb2e8e8b50e5b0ef58b367a8f29fedc23b188 100644 (file)
@@ -1,9 +1,31 @@
 //
 // System.Security.Cryptography.RNGCryptoServiceProvider
 //
-// Author: Mark Crichton (crichton@gimp.org)
+// Authors:
+//     Mark Crichton (crichton@gimp.org)
+//     Sebastien Pouliot (sebastien@ximian.com)
 //
 // (C) 2002
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
 // "In the beginning there was Chaos,
 // Great Power without form."
 // -- The Verrah Rubicon of Verena, Book One
 
-using System;
+using System.Globalization;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
 
 namespace System.Security.Cryptography {
        
-       public class RNGCryptoServiceProvider : RandomNumberGenerator {
-               
-               [MonoTODO]
-               public RNGCryptoServiceProvider () {
-                       // This will get some meaning when I figure out what the other
-                       // three constructors do.
+#if !NET_2_1
+       [ComVisible (true)]
+#endif
+       public sealed class RNGCryptoServiceProvider : RandomNumberGenerator {
+               private static object _lock;
+               private IntPtr _handle;
+
+               static RNGCryptoServiceProvider ()
+               {
+                       if (RngOpen ())
+                               _lock = new object ();
                }
-               
-               [MonoTODO]
-               public RNGCryptoServiceProvider (byte[] rgb) {
-                       // Ok, not called by app code... someone must call it, though.
+
+               public RNGCryptoServiceProvider ()
+               {
+                       _handle = RngInitialize (null);
+                       Check ();
                }
-               
-               [MonoTODO]
-               public RNGCryptoServiceProvider (CspParameters cspParams) {
-                       // Why do I have this feeling this is the MS CryptAPI...
+#if !NET_2_1
+               public RNGCryptoServiceProvider (byte[] rgb)
+               {
+                       _handle = RngInitialize (rgb);
+                       Check ();
                }
                
-               [MonoTODO]
-               public RNGCryptoServiceProvider (string str) {
-                       // More !application code.  Interesting...
+               public RNGCryptoServiceProvider (CspParameters cspParams)
+               {
+                       // CSP selection isn't supported but we still return 
+                       // random data (no exception) for compatibility
+                       _handle = RngInitialize (null);
+                       Check ();
                }
                
+               public RNGCryptoServiceProvider (string str) 
+               {
+                       if (str == null)
+                               _handle = RngInitialize (null);
+                       else
+                               _handle = RngInitialize (Encoding.UTF8.GetBytes (str));
+                       Check ();
+               }
+#endif
+               private void Check () 
+               {
+                       if (_handle == IntPtr.Zero) {
+                               throw new CryptographicException (
+                                       Locale.GetText ("Couldn't access random source."));
+                       }
+               }
+
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               public extern override void GetBytes (byte[] data);
+               private static extern bool RngOpen ();
                
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               public extern override void GetNonZeroBytes (byte[] data);
+               private static extern IntPtr RngInitialize (byte[] seed);
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               private static extern IntPtr RngGetBytes (IntPtr handle, byte[] data);
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               private static extern void RngClose (IntPtr handle);
+               
+               public override void GetBytes (byte[] data) 
+               {
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
+                       if (_lock == null) {
+                               _handle = RngGetBytes (_handle, data);
+                       } else {
+                               // using a global handle for randomness
+                               lock (_lock) {
+                                       _handle = RngGetBytes (_handle, data);
+                               }
+                       }
+                       Check ();
+               }
                
-               ~RNGCryptoServiceProvider () {
-                       // FIN?
+               public override void GetNonZeroBytes (byte[] data) 
+               {
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
+                       byte[] random = new byte [data.Length * 2];
+                       int i = 0;
+                       // one pass should be enough but hey this is random ;-)
+                       while (i < data.Length) {
+                               _handle = RngGetBytes (_handle, random);
+                               Check ();
+                               for (int j=0; j < random.Length; j++) {
+                                       if (i == data.Length)
+                                               break;
+                                       if (random [j] != 0)
+                                               data [i++] = random [j];
+                               }
+                       }
+               }
+               
+               ~RNGCryptoServiceProvider () 
+               {
+                       if (_handle != IntPtr.Zero) {
+                               RngClose (_handle);
+                               _handle = IntPtr.Zero;
+                       }
+               }
+
+#if NET_4_0 || MOONLIGHT
+               protected override void Dispose (bool disposing)
+               {
+                       base.Dispose (disposing);
                }
+#endif
        }
 }