source: branches/eraser6/Manager/EntropySource.cs @ 553

Revision 553, 20.9 KB checked in by lowjoel, 6 years ago (diff)

Added svn:keywords

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