source: branches/eraser6/Manager/PRNG.cs @ 479

Revision 479, 22.1 KB checked in by lowjoel, 6 years ago (diff)

Fixed Kaz's implementation, and implemented his proposal of using EntropySources? to allow extensibility in future to hardware entropy sources, etc.

  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By: Kasra Nasiri <cjax@users.sourceforge.net> @10/7/2008
6 * Modified By:
7 *
8 * This file is part of Eraser.
9 *
10 * Eraser is free software: you can redistribute it and/or modify it under the
11 * terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 3 of the License, or (at your option) any later
13 * version.
14 *
15 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
16 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 *
19 * A copy of the GNU General Public License can be found at
20 * <http://www.gnu.org/licenses/>.
21 */
22
23using System;
24using System.Collections.Generic;
25using System.Text;
26
27using System.Threading;
28using System.Security.Cryptography;
29using System.Runtime.InteropServices;
30using System.Diagnostics;
31using System.Reflection;
32using System.IO;
33using Microsoft.Win32.SafeHandles;
34using Eraser.Util;
35
36namespace Eraser.Manager
37{
38    /// <summary>
39    /// An interface class for all pseudorandom number generators used for the
40    /// random data erase passes.
41    /// </summary>
42    public abstract class PRNG : Random
43    {
44        public override string ToString()
45        {
46            return Name;
47        }
48
49        /// <summary>
50        /// The name of this erase pass, used for display in the UI
51        /// </summary>
52        public abstract string Name
53        {
54            get;
55        }
56
57        /// <summary>
58        /// The GUID for this PRNG.
59        /// </summary>
60        public abstract Guid GUID
61        {
62            get;
63        }
64
65        /// <summary>
66        /// Reseeds the PRNG. This can be called by inherited classes, but its most
67        /// important function is to provide new seeds regularly. The PRNGManager
68        /// will call this function once in a whle to maintain the quality of
69        /// generated numbers.
70        /// </summary>
71        /// <param name="seed">An arbitrary length of information that will be
72        /// used to reseed the PRNG</param>
73        protected internal abstract void Reseed(byte[] seed);
74
75        #region Random members
76        public override int Next(int maxValue)
77        {
78            if (maxValue == 0)
79                return 0;
80            return Next() % maxValue;
81        }
82
83        public override int Next(int minValue, int maxValue)
84        {
85            if (minValue > maxValue)
86                throw new ArgumentOutOfRangeException("minValue", minValue, "minValue is greater than maxValue");
87            else if (minValue == maxValue)
88                return minValue;
89            return (Next() % (maxValue - minValue)) + minValue;
90        }
91
92        public unsafe override int Next()
93        {
94            //Declare a return variable
95            int result;
96            int* fResult = &result;
97
98            //Get the random-valued bytes to fill the int.
99            byte[] rand = new byte[sizeof(int)];
100            NextBytes(rand);
101
102            //Copy the random buffer into the int.
103            fixed (byte* fRand = rand)
104            {
105                byte* pResult = (byte*)fResult;
106                byte* pRand = fRand;
107                for (int i = 0; i != sizeof(int); ++i)
108                    *pResult++ = *pRand++;
109            }
110
111            return Math.Abs(result);
112        }
113
114        protected unsafe override double Sample()
115        {
116            //Declare a return variable
117            double result;
118            double* fResult = &result;
119
120            //Get the random-valued bytes to fill the int.
121            byte[] rand = new byte[sizeof(double)];
122            NextBytes(rand);
123
124            //Copy the random buffer into the int.
125            fixed (byte* fRand = rand)
126            {
127                byte* pResult = (byte*)fResult;
128                byte* pRand = fRand;
129                for (int i = 0; i != sizeof(double); ++i)
130                    *pResult++ = *pRand++;
131            }
132
133            return result;
134        }
135
136        public abstract override void NextBytes(byte[] buffer);
137        #endregion
138    }
139
140    /// <summary>
141    /// Class managing all the PRNG algorithms.
142    /// </summary>
143    public class PRNGManager
144    {
145        /// <summary>
146        /// Constructor.
147        /// </summary>
148        public PRNGManager()
149        {
150            entropyThread.AddEntropySource(new KernelEntropySource());
151        }
152
153        /// <summary>
154        /// Retrieves all currently registered erasure methods.
155        /// </summary>
156        /// <returns>A mutable list, with an instance of each PRNG.</returns>
157        public static Dictionary<Guid, PRNG> GetAll()
158        {
159            lock (ManagerLibrary.Instance.PRNGManager.prngs)
160                return ManagerLibrary.Instance.PRNGManager.prngs;
161        }
162
163        /// <summary>
164        /// Retrieves the instance of the PRNG with the given GUID.
165        /// </summary>
166        /// <param name="guid">The GUID of the PRNG.</param>
167        /// <returns>The PRNG instance.</returns>
168        public static PRNG GetInstance(Guid guid)
169        {
170            try
171            {
172                lock (ManagerLibrary.Instance.PRNGManager.prngs)
173                    return ManagerLibrary.Instance.PRNGManager.prngs[guid];
174            }
175            catch (KeyNotFoundException)
176            {
177                throw new FatalException("PRNG not found: " + guid.ToString());
178            }
179        }
180
181        /// <summary>
182        /// Allows plug-ins to register PRNGs with the main program. Thread-safe.
183        /// </summary>
184        /// <param name="method"></param>
185        public static void Register(PRNG prng)
186        {
187            lock (ManagerLibrary.Instance.PRNGManager.prngs)
188                ManagerLibrary.Instance.PRNGManager.prngs.Add(prng.GUID, prng);
189        }
190
191        /// <summary>
192        /// Allows the EntropyThread to get entropy to the PRNG functions as seeds.
193        /// </summary>
194        /// <param name="entropy">An array of bytes, being entropy for the PRNG.</param>
195        internal void AddEntropy(byte[] entropy)
196        {
197            lock (ManagerLibrary.Instance.PRNGManager.prngs)
198                foreach (PRNG prng in prngs.Values)
199                    prng.Reseed(entropy);
200        }
201
202        /// <summary>
203        /// Gets entropy from the EntropyThread.
204        /// </summary>
205        /// <returns>A buffer of arbitrary length containing random information.</returns>
206        public static byte[] GetEntropy()
207        {
208            return ManagerLibrary.Instance.PRNGManager.entropyThread.GetPool();
209        }
210       
211        /// <summary>
212        /// The entropy thread gathering entropy for the RNGs.
213        /// </summary>
214        internal EntropyPoller entropyThread = new EntropyPoller();
215
216        /// <summary>
217        /// The list of currently registered erasure methods.
218        /// </summary>
219        private Dictionary<Guid, PRNG> prngs = new Dictionary<Guid, PRNG>();
220    }
221
222    /// <summary>
223    /// A class which uses EntropyPoll class to fetch system data as a source of
224    /// randomness at "regular" but "random" intervals
225    /// </summary>
226    class EntropyPoller
227    {
228        /// <summary>
229        /// The algorithm used for mixing
230        /// </summary>
231        private enum PRFAlgorithms
232        {
233            MD5,
234            SHA1,
235            RIPEMD160,
236            SHA256,
237            SHA384,
238            SHA512,
239        };
240
241        /// <summary>
242        /// Constructor.
243        /// </summary>
244        public EntropyPoller()
245        {
246            //Create the pool.
247            pool = new byte[sizeof(uint) * 128];
248
249            //Then start the thread which maintains the pool.
250            Thread = new Thread(delegate()
251                {
252                    this.Main();
253                }
254            );
255            Thread.Start();
256        }
257
258        /// <summary>
259        /// The PRNG entropy thread. This thread will run in the background, getting
260        /// random data to be used for entropy. This will maintain the integrity
261        /// of generated data from the PRNGs.
262        /// </summary>
263        private void Main()
264        {
265            //This entropy thread will utilize a polling loop.
266            DateTime lastAddedEntropy = DateTime.Now;
267            TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0);
268            Stopwatch st = new Stopwatch();
269
270            while (Thread.ThreadState != System.Threading.ThreadState.AbortRequested)
271            {
272                st.Start();
273                lock (EntropySources)
274                    foreach (EntropySource src in EntropySources)
275                    {
276                        byte[] entropy = src.GetEntropy();
277                        AddEntropy(entropy);
278                    }
279               
280                st.Stop(); 
281                Thread.Sleep(2000 + (int)(st.ElapsedTicks % 2049L));
282                st.Reset();
283
284                //Send entropy to the PRNGs for new seeds.
285                if (DateTime.Now - lastAddedEntropy > managerEntropySpan)
286                    ManagerLibrary.Instance.PRNGManager.AddEntropy(GetPool());
287            }
288        }
289       
290        /// <summary>
291        /// Stops the execution of the thread.
292        /// </summary>
293        public void Abort()
294        {
295            Thread.Abort();
296        }
297
298        /// <summary>
299        /// Adds a new Entropy Source to the Poller.
300        /// </summary>
301        /// <param name="source">The EntropySource object to add.</param>
302        public void AddEntropySource(EntropySource source)
303        {
304            lock (EntropySources)
305                EntropySources.Add(source);
306
307            AddEntropy(source.GetPrimer());
308            MixPool();
309
310            //Apply whitening effect
311            PRFAlgorithm = PRFAlgorithms.RIPEMD160;
312            MixPool();
313            PRFAlgorithm = PRFAlgorithms.SHA512;
314        }
315
316        /// <summary>
317        /// Retrieves the current contents of the entropy pool.
318        /// </summary>
319        /// <returns>A byte array containing all the randomness currently found.</returns>
320        public byte[] GetPool()
321        {
322            //Mix and invert the pool
323            MixPool();
324            InvertPool();
325
326            //Return a safe copy
327            lock (pool)
328            {
329                byte[] result = new byte[pool.Length];
330                pool.CopyTo(result, 0);
331
332                return result;
333            }
334        }
335
336        /// <summary>
337        /// Inverts the contents of the pool
338        /// </summary>
339        private void InvertPool()
340        {
341            lock (poolLock)
342                unsafe
343                {
344                    fixed (byte* fPool = pool)
345                    {
346                        uint* pPool = (uint*)fPool;
347                        uint poolLength = (uint)(pool.Length / sizeof(uint));
348                        while (poolLength-- != 0)
349                            *pPool = (uint)(*pPool++ ^ unchecked((uint)-1));
350                    }
351                }
352        }
353
354        /// <summary>
355        /// Mixes the contents of the pool.
356        /// </summary>
357        private void MixPool()
358        {
359            lock (poolLock)
360            {
361                //Mix the last 128 bytes first.
362                const int mixBlockSize = 128;
363                int hashSize = PRF.HashSize / 8;
364                PRF.ComputeHash(pool, pool.Length - mixBlockSize, mixBlockSize).CopyTo(pool, 0);
365
366                //Then mix the following bytes until wraparound is required
367                int i = 0;
368                for (; i < pool.Length - hashSize; i += hashSize)
369                    Buffer.BlockCopy(PRF.ComputeHash(pool, i,
370                        i + mixBlockSize >= pool.Length ? pool.Length - i : mixBlockSize),
371                        0, pool, i, i + hashSize >= pool.Length ? pool.Length - i : hashSize);
372
373                //Mix the remaining blocks which require copying from the front
374                byte[] combinedBuffer = new byte[mixBlockSize];
375                for (; i < pool.Length; i += hashSize)
376                {
377                    Buffer.BlockCopy(pool, i, combinedBuffer, 0, pool.Length - i);
378
379                    Buffer.BlockCopy(pool, 0, combinedBuffer, pool.Length - i,
380                                mixBlockSize - (pool.Length - i));
381
382                    Buffer.BlockCopy(PRF.ComputeHash(combinedBuffer, 0, mixBlockSize), 0,
383                        pool, i, pool.Length - i > hashSize ? hashSize : pool.Length - i);
384                }
385            }
386        }
387
388        /// <summary>
389        /// Adds data which is random to the pool
390        /// </summary>
391        /// <param name="entropy">An array of data which will be XORed with pool
392        /// contents.</param>
393        public unsafe void AddEntropy(byte[] entropy)
394        {
395            lock (poolLock)
396                fixed (byte* pEntropy = entropy)
397                fixed (byte* pPool = pool)
398                {
399                    int size = entropy.Length;
400                    byte* mpEntropy = pEntropy;
401                    while (size > 0)
402                    {
403                        //Bring the pool position back to the front if we are at our end
404                        if (poolPosition >= pool.Length)
405                            poolPosition = 0;
406
407                        int amountToMix = Math.Min(size, pool.Length - poolPosition);
408                        MemoryXor(pPool + poolPosition, mpEntropy, amountToMix);
409                        mpEntropy = mpEntropy + amountToMix;
410                        size -= amountToMix;
411                    }
412                }
413        }
414
415        /// <summary>
416        /// XOR's memory a DWORD at a time.
417        /// </summary>
418        /// <param name="destination">The destination buffer to be XOR'ed</param>
419        /// <param name="source">The source buffer to XOR with</param>
420        /// <param name="size">The size of the source buffer</param>
421        private static unsafe void MemoryXor(byte* destination, byte* source, int size)
422        {
423            int wsize = size / sizeof(uint);
424            size -= wsize * sizeof(uint);
425            uint* d = (uint*)destination;
426            uint* s = (uint*)source;
427
428            while (wsize-- > 0)
429                *d++ ^= *s++;
430
431            if (size > 0)
432            {
433                byte* db = (byte*)d,
434                      ds = (byte*)s;
435                while (size-- > 0)
436                    *db++ ^= *ds++;
437            }
438        }
439
440        /// <summary>
441        /// PRF algorithm handle
442        /// </summary>
443        private HashAlgorithm PRF
444        {
445            get
446            {
447                Type type = null;
448                switch (PRFAlgorithm)
449                {
450                    case PRFAlgorithms.MD5:
451                        type = typeof(MD5CryptoServiceProvider);
452                        break;
453                    case PRFAlgorithms.SHA1:
454                        type = typeof(SHA1Managed);
455                        break;
456                    case PRFAlgorithms.RIPEMD160:
457                        type = typeof(RIPEMD160Managed);
458                        break;
459                    case PRFAlgorithms.SHA256:
460                        type = typeof(SHA256Managed);
461                        break;
462                    case PRFAlgorithms.SHA384:
463                        type = typeof(SHA384Managed);
464                        break;
465                    default:
466                        type = typeof(SHA512Managed);
467                        break;
468                }
469
470                if (type.IsInstanceOfType(prfCache))
471                    return prfCache;
472                ConstructorInfo hashConstructor = type.GetConstructor(Type.EmptyTypes);
473                return prfCache = (HashAlgorithm)hashConstructor.Invoke(null);
474            }
475        }
476
477        /// <summary>
478        /// The last created PRF algorithm handle.
479        /// </summary>
480        private HashAlgorithm prfCache;
481
482        /// <summary>
483        /// PRF algorithm identifier
484        /// </summary>
485        private PRFAlgorithms PRFAlgorithm = PRFAlgorithms.SHA512;
486
487        /// <summary>
488        /// The pool of data which we currently maintain.
489        /// </summary>
490        private byte[] pool;
491
492        /// <summary>
493        /// The next position where entropy will be added to the pool.
494        /// </summary>
495        private int poolPosition = 0;
496
497        /// <summary>
498        /// The lock guarding the pool array and the current entropy addition index.
499        /// </summary>
500        private object poolLock = new object();
501
502        /// <summary>
503        /// The thread object.
504        /// </summary>
505        Thread Thread;
506
507        /// <summary>
508        /// The list of entropy sources registered with the Poller.
509        /// </summary>
510        private List<EntropySource> EntropySources = new List<EntropySource>();
511    }
512   
513    /// <summary>
514    /// Provides an abstract interface to allow multiple sources of entropy into
515    /// the EntropyPoller class.
516    /// </summary>
517    public abstract class EntropySource
518    {
519        /// <summary>
520        /// Constructor.
521        /// </summary>
522        public EntropySource()
523        {
524        }
525
526        /// <summary>
527        /// Gets a primer to add to the pool when this source is first initialised, to
528        /// further add entropy to the pool.
529        /// </summary>
530        /// <returns>A byte array containing the entropy.</returns>
531        public abstract byte[] GetPrimer();
532
533        /// <summary>
534        /// Gets entropy from the entropy source. This will be called repetitively.
535        /// </summary>
536        /// <returns>A byte array containing the entropy.</returns>
537        public abstract byte[] GetEntropy();
538
539        /// <summary>
540        /// Converts value types into a byte array. This is a helper function to allow
541        /// inherited classes to convert value types into byte arrays which can be
542        /// returned to the EntropyPoller class.
543        /// </summary>
544        /// <typeparam name="T">Any value type</typeparam>
545        /// <param name="entropy">A value which will be XORed with pool contents.</param>
546        protected unsafe static byte[] StructToBuffer<T>(T entropy) where T: struct
547        {
548            int sizeofObject = Marshal.SizeOf(entropy);
549            IntPtr memory = Marshal.AllocHGlobal(sizeofObject);
550            try
551            {
552                Marshal.StructureToPtr(entropy, memory, false);
553                byte[] dest = new byte[sizeofObject];
554
555                //Copy the memory
556                Marshal.Copy(memory, dest, 0, sizeofObject);
557                return dest;
558            }
559            finally
560            {
561                Marshal.FreeHGlobal(memory);
562            }
563        }
564    }
565
566    /// <summary>
567    /// Provides means of generating random entropy from the system or user space
568    /// randomness.
569    /// </summary>
570    internal class KernelEntropySource : EntropySource
571    {
572        public override byte[] GetPrimer()
573        {
574            List<byte> result = new List<byte>();
575
576            //Process startup information
577            KernelAPI.STARTUPINFO startupInfo = new KernelAPI.STARTUPINFO();
578            KernelAPI.GetStartupInfo(out startupInfo);
579            result.AddRange(StructToBuffer(startupInfo));
580
581            //System information
582            KernelAPI.SYSTEM_INFO systemInfo = new KernelAPI.SYSTEM_INFO();
583            KernelAPI.GetSystemInfo(out systemInfo);
584            result.AddRange(StructToBuffer(systemInfo));
585
586            result.AddRange(GetFastEntropy());
587            result.AddRange(GetSlowEntropy());
588            return result.ToArray();
589        }
590
591        public override byte[] GetEntropy()
592        {
593            List<byte> result = new List<byte>();
594            result.AddRange(GetFastEntropy());
595            result.AddRange(GetSlowEntropy());
596           
597            return result.ToArray();
598        }
599
600        /// <summary>
601        /// Retrieves entropy from quick sources.
602        /// </summary>
603        private byte[] GetFastEntropy()
604        {
605            List<byte> result = new List<byte>();
606
607            //Add the free disk space to the pool
608            result.AddRange(StructToBuffer(new DriveInfo(new DirectoryInfo(Environment.SystemDirectory).
609                Root.FullName).TotalFreeSpace));
610
611            //Miscellaneous window handles
612            result.AddRange(StructToBuffer(UserAPI.GetCapture()));
613            result.AddRange(StructToBuffer(UserAPI.GetClipboardOwner()));
614            result.AddRange(StructToBuffer(UserAPI.GetClipboardViewer()));
615            result.AddRange(StructToBuffer(UserAPI.GetDesktopWindow()));
616            result.AddRange(StructToBuffer(UserAPI.GetForegroundWindow()));
617            result.AddRange(StructToBuffer(UserAPI.GetMessagePos()));
618            result.AddRange(StructToBuffer(UserAPI.GetMessageTime()));
619            result.AddRange(StructToBuffer(UserAPI.GetOpenClipboardWindow()));
620            result.AddRange(StructToBuffer(UserAPI.GetProcessWindowStation()));
621            result.AddRange(StructToBuffer(KernelAPI.GetCurrentProcessId()));
622            result.AddRange(StructToBuffer(KernelAPI.GetCurrentThreadId()));
623            result.AddRange(StructToBuffer(KernelAPI.GetProcessHeap()));
624
625            //The caret and cursor positions
626            UserAPI.POINT point;
627            UserAPI.GetCaretPos(out point);
628            result.AddRange(StructToBuffer(point));
629            UserAPI.GetCursorPos(out point);
630            result.AddRange(StructToBuffer(point));
631
632            //Amount of free memory
633            KernelAPI.MEMORYSTATUSEX memoryStatus = new KernelAPI.MEMORYSTATUSEX();
634            memoryStatus.dwLength = (uint)Marshal.SizeOf(memoryStatus);
635            if (KernelAPI.GlobalMemoryStatusEx(ref memoryStatus))
636            {
637                result.AddRange(StructToBuffer(memoryStatus.ullAvailPhys));
638                result.AddRange(StructToBuffer(memoryStatus.ullAvailVirtual));
639                result.AddRange(StructToBuffer(memoryStatus));
640            }
641
642            //Thread execution times
643            long creationTime, exitTime, kernelTime, userTime;
644            if (KernelAPI.GetThreadTimes(KernelAPI.GetCurrentThread(), out creationTime,
645                out exitTime, out kernelTime, out userTime))
646            {
647                result.AddRange(StructToBuffer(creationTime));
648                result.AddRange(StructToBuffer(kernelTime));
649                result.AddRange(StructToBuffer(userTime));
650            }
651
652            //Process execution times
653            if (KernelAPI.GetProcessTimes(KernelAPI.GetCurrentProcess(), out creationTime,
654                out exitTime, out kernelTime, out userTime))
655            {
656                result.AddRange(StructToBuffer(creationTime));
657                result.AddRange(StructToBuffer(kernelTime));
658                result.AddRange(StructToBuffer(userTime));
659            }
660
661            //Current system time
662            result.AddRange(StructToBuffer(DateTime.Now.Ticks));
663
664            //The high resolution performance counter
665            long perfCount = 0;
666            if (KernelAPI.QueryPerformanceCounter(out perfCount))
667                result.AddRange(StructToBuffer(perfCount));
668
669            //Ticks since start up
670            uint tickCount = KernelAPI.GetTickCount();
671            if (tickCount != 0)
672                result.AddRange(StructToBuffer(tickCount));
673
674            //CryptGenRandom
675            byte[] cryptGenRandom = new byte[160];
676            if (CryptAPI.CryptGenRandom(cryptGenRandom))
677                result.AddRange(cryptGenRandom);
678
679            return result.ToArray();
680        }
681
682        /// <summary>
683        /// Retrieves entropy from sources which are relatively slower than those from
684        /// the FastAddEntropy function.
685        /// </summary>
686        private byte[] GetSlowEntropy()
687        {
688            List<byte> result = new List<byte>();
689
690            //NetAPI statistics
691            unsafe
692            {
693                IntPtr netAPIStats = IntPtr.Zero;
694                if (NetAPI.NetStatisticsGet(null, NetAPI.SERVICE_WORKSTATION,
695                    0, 0, out netAPIStats) == 0)
696                {
697                    try
698                    {
699                        //Get the size of the buffer
700                        uint size = 0;
701                        NetAPI.NetApiBufferSize(netAPIStats, out size);
702                        byte[] entropy = new byte[size];
703
704                        //Copy the buffer
705                        Marshal.Copy(entropy, 0, netAPIStats, entropy.Length);
706
707                        //And add it to the pool
708                        result.AddRange(entropy);
709                    }
710                    finally
711                    {
712                        //Free the statistics buffer
713                        NetAPI.NetApiBufferFree(netAPIStats);
714                    }
715                }
716            }
717
718#if false
719            //Get disk I/O statistics for all the hard drives
720            for (int drive = 0; ; ++drive)
721            {
722                //Try to open the drive.
723                using (SafeFileHandle hDevice = File.CreateFile(
724                    string.Format("\\\\.\\PhysicalDrive%d", drive), 0,
725                    File.FILE_SHARE_READ | File.FILE_SHARE_WRITE, IntPtr.Zero,
726                    File.OPEN_EXISTING, 0, IntPtr.Zero))
727                {
728                    if (hDevice.IsInvalid)
729                        break;
730
731                    //This only works if the user has turned on the disk performance
732                    //counters with 'diskperf -y'. These counters are off by default
733                    if (File.DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
734                        &diskPerformance, sizeof(DISK_PERFORMANCE), &uSize, NULL))
735                    {
736                        addEntropy(&diskPerformance, uSize);
737                    }
738                }
739            }
740#endif
741
742            /*
743             Query performance data. Because the Win32 version of this API (through
744             registry) may be buggy, use the NT Native API instead.
745             
746             Scan the first 64 possible information types (we don't bother
747             with increasing the buffer size as we do with the Win32
748             version of the performance data read, we may miss a few classes
749             but it's no big deal).  In addition the returned size value for
750             some classes is wrong (eg 23 and 24 return a size of 0) so we
751             miss a few more things, but again it's no big deal.  This scan
752             typically yields around 20 pieces of data, there's nothing in
753             the range 65...128 so chances are there won't be anything above
754             there either.
755            */
756            uint dataWritten = 0;
757            byte[] infoBuffer = new byte[65536];
758            uint totalEntropy = 0;
759            for (uint infoType = 0; infoType < 64; ++infoType)
760            {
761                uint sysInfo = NTAPI.NtQuerySystemInformation(infoType, infoBuffer,
762                    (uint)infoBuffer.Length, out dataWritten);
763
764                if (sysInfo == 0 /*ERROR_SUCCESS*/ && dataWritten > 0)
765                {
766                    byte[] entropy = new byte[dataWritten];
767                    Buffer.BlockCopy(infoBuffer, 0, entropy, 0, (int)dataWritten);
768                    result.AddRange(entropy);
769                    totalEntropy += dataWritten;
770                }
771            }
772
773            result.AddRange(StructToBuffer(totalEntropy));
774
775            //Finally, our good friend CryptGenRandom()
776            byte[] cryptGenRandom = new byte[1536];
777            if (CryptAPI.CryptGenRandom(cryptGenRandom))
778                result.AddRange(cryptGenRandom);
779
780            return result.ToArray();
781        }
782    }
783}
Note: See TracBrowser for help on using the repository browser.