source: trunk/eraser6/Eraser.Manager/EntropySource.cs @ 1832

Revision 1832, 16.7 KB checked in by lowjoel, 5 years ago (diff)

Removed CryptGenRandom? for a few reasons:

  • The only use was in the Entropy gathering code. We default to using RNGCryptoServiceProvider which is already a CSPRNG.
  • Since RNGCryptoServiceProvider doesn't accept seeds, we can't seed it with our entropy
  • CryptGenRandom? involved a lot of setting up -- certain computers do not have it and would result in a crash
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By: Kasra Nassiri <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.Globalization;
27using System.Threading;
28using System.Security.Cryptography;
29using System.Runtime.InteropServices;
30using System.Diagnostics;
31using System.Reflection;
32using System.IO;
33
34using System.Windows.Forms;
35using Microsoft.VisualBasic.Devices;
36using Microsoft.Win32.SafeHandles;
37using Eraser.Util;
38
39namespace Eraser.Manager
40{
41    /// <summary>
42    /// Provides an abstract interface to allow multiple sources of entropy into
43    /// the EntropyPoller class.
44    /// </summary>
45    public abstract class EntropySource : IRegisterable
46    {
47        /// <summary>
48        /// Constructor.
49        /// </summary>
50        protected EntropySource()
51        {
52        }
53
54        /// <summary>
55        /// The name of the entropy source
56        /// </summary>
57        public abstract string Name
58        {
59            get;
60        }
61
62        /// <summary>
63        /// The guid representing this entropy source
64        /// </summary>
65        public abstract Guid Guid
66        {
67            get;
68        }
69
70        /// <summary>
71        /// Gets a primer to add to the pool when this source is first initialised, to
72        /// further add entropy to the pool.
73        /// </summary>
74        /// <returns>A byte array containing the entropy.</returns>
75        public abstract byte[] GetPrimer();
76
77        /// <summary>
78        /// Retrieve entropy from a source which will have slow rate of
79        /// entropy polling.
80        /// </summary>
81        /// <returns></returns>
82        public abstract byte[] GetSlowEntropy();
83
84        /// <summary>
85        /// Retrieve entropy from a soruce which will have a fast rate of
86        /// entropy polling.
87        /// </summary>
88        /// <returns></returns>
89        public abstract byte[] GetFastEntropy();
90
91        /// <summary>
92        /// Gets entropy from the entropy source. This will be called repetitively.
93        /// </summary>
94        /// <returns>A byte array containing the entropy, both slow rate and fast rate.</returns>
95        public abstract byte[] GetEntropy();
96
97        /// <summary>
98        /// Converts value types into a byte array. This is a helper function to allow
99        /// inherited classes to convert value types into byte arrays which can be
100        /// returned to the EntropyPoller class.
101        /// </summary>
102        /// <typeparam name="T">Any value type</typeparam>
103        /// <param name="entropy">A value which will be XORed with pool contents.</param>
104        protected static byte[] StructToBuffer<T>(T entropy) where T : struct
105        {
106            int sizeofObject = Marshal.SizeOf(entropy);
107            IntPtr memory = Marshal.AllocHGlobal(sizeofObject);
108            try
109            {
110                Marshal.StructureToPtr(entropy, memory, false);
111                byte[] dest = new byte[sizeofObject];
112
113                //Copy the memory
114                Marshal.Copy(memory, dest, 0, sizeofObject);
115                return dest;
116            }
117            finally
118            {
119                Marshal.FreeHGlobal(memory);
120            }
121        }
122    }
123
124    /// <summary>
125    /// A class which manages all of the instances of the EntropySources
126    /// available. Plugins could register their entropy sources via this class.
127    /// </summary>
128    public class EntropySourceRegistrar : Registrar<EntropySource>
129    {
130        /// <summary>
131        /// Constructor.
132        /// </summary>
133        internal EntropySourceRegistrar()
134        {
135            Poller = new EntropyPoller();
136            Poller.AddEntropySource(new KernelEntropySource());
137        }
138
139        /// <summary>
140        /// Gets the entropy poller instance associated with this manager.
141        /// </summary>
142        public EntropyPoller Poller { get; private set; }
143       
144        /// <summary>
145        /// The list of currently registered Entropy Sources.
146        /// </summary>
147        private Dictionary<Guid, EntropySource> sources = new Dictionary<Guid, EntropySource>();
148    };
149       
150    /// <summary>
151    /// Provides means of generating random entropy from the system or user space
152    /// randomness.
153    /// This class is hardcoded into the Manager Library as we need at least one
154    /// instance of such behaviour within our system. The other classes could be
155    /// implemented as plugins, managed by EntropySourceManager.
156    /// </summary>
157    public class KernelEntropySource : EntropySource
158    {
159        public override byte[] GetPrimer()
160        {
161            List<byte> result = new List<byte>();
162
163            //Process information
164            result.AddRange(StructToBuffer(Process.GetCurrentProcess().Id));
165            result.AddRange(StructToBuffer(Process.GetCurrentProcess().StartTime.Ticks));
166
167            result.AddRange(GetFastEntropy());
168            result.AddRange(GetSlowEntropy());
169            return result.ToArray();
170        }
171
172        public override Guid Guid
173        {
174            get
175            {
176                return new Guid("{11EDCECF-AD81-4e50-A73D-B9CF1F813093}");
177            }
178        }
179
180        public override string Name
181        {
182            get
183            {
184                return "Kernel Entropy Source";
185            }
186        }
187
188        public override byte[] GetEntropy()
189        {
190            List<byte> result = new List<byte>();
191            result.AddRange(GetFastEntropy());
192            result.AddRange(GetSlowEntropy());
193
194            return result.ToArray();
195        }
196
197        /// <summary>
198        /// Retrieves entropy from quick sources.
199        /// </summary>
200        public override byte[] GetFastEntropy()
201        {
202            List<byte> result = new List<byte>();
203
204            //Add the free disk space to the pool
205            result.AddRange(StructToBuffer(new DriveInfo(new DirectoryInfo(Environment.SystemDirectory).
206                Root.FullName).TotalFreeSpace));
207
208            //Miscellaneous window handles
209            result.AddRange(StructToBuffer(UserApi.MessagePos));
210            result.AddRange(StructToBuffer(UserApi.MessageTime));
211
212            //The caret and cursor positions
213            result.AddRange(StructToBuffer(UserApi.CaretPos));
214            result.AddRange(StructToBuffer(Cursor.Position));
215
216            //Currently running threads (dynamic, but not very)
217            Process currProcess = Process.GetCurrentProcess();
218            foreach (ProcessThread thread in currProcess.Threads)
219                result.AddRange(StructToBuffer(thread.Id));
220
221            //Various process statistics
222            result.AddRange(StructToBuffer(currProcess.VirtualMemorySize64));
223            result.AddRange(StructToBuffer(currProcess.MaxWorkingSet));
224            result.AddRange(StructToBuffer(currProcess.MinWorkingSet));
225            result.AddRange(StructToBuffer(currProcess.NonpagedSystemMemorySize64));
226            result.AddRange(StructToBuffer(currProcess.PagedMemorySize64));
227            result.AddRange(StructToBuffer(currProcess.PagedSystemMemorySize64));
228            result.AddRange(StructToBuffer(currProcess.PeakPagedMemorySize64));
229            result.AddRange(StructToBuffer(currProcess.PeakVirtualMemorySize64));
230            result.AddRange(StructToBuffer(currProcess.PeakWorkingSet64));
231            result.AddRange(StructToBuffer(currProcess.PrivateMemorySize64));
232            result.AddRange(StructToBuffer(currProcess.WorkingSet64));
233            result.AddRange(StructToBuffer(currProcess.HandleCount));
234
235            //Amount of free memory
236            ComputerInfo computerInfo = new ComputerInfo();
237            result.AddRange(StructToBuffer(computerInfo.AvailablePhysicalMemory));
238            result.AddRange(StructToBuffer(computerInfo.AvailableVirtualMemory));
239
240            //Process execution times
241            result.AddRange(StructToBuffer(currProcess.TotalProcessorTime));
242            result.AddRange(StructToBuffer(currProcess.UserProcessorTime));
243            result.AddRange(StructToBuffer(currProcess.PrivilegedProcessorTime));
244
245            //Thread execution times
246            foreach (ProcessThread thread in currProcess.Threads)
247            {
248                try
249                {
250                    result.AddRange(StructToBuffer(thread.TotalProcessorTime));
251                    result.AddRange(StructToBuffer(thread.UserProcessorTime));
252                    result.AddRange(StructToBuffer(thread.PrivilegedProcessorTime));
253                }
254                catch (InvalidOperationException)
255                {
256                    //Caught when the thread has exited in the middle of the foreach.
257                }
258                catch (System.ComponentModel.Win32Exception e)
259                {
260                    if (e.NativeErrorCode != Win32ErrorCode.AccessDenied)
261                        throw;
262                }
263            }
264
265            //Current system time
266            result.AddRange(StructToBuffer(DateTime.Now.Ticks));
267
268            //The high resolution performance counter
269            result.AddRange(StructToBuffer(SystemInfo.PerformanceCounter));
270
271            //Ticks since start up
272            result.AddRange(StructToBuffer(Environment.TickCount));
273            return result.ToArray();
274        }
275
276        /// <summary>
277        /// Retrieves entropy from sources which are relatively slower than those from
278        /// the FastAddEntropy function.
279        /// </summary>
280        public override byte[] GetSlowEntropy()
281        {
282            List<byte> result = new List<byte>();
283
284            //NetAPI statistics
285            byte[] netApiStats = NetApi.NetStatisticsGet(null, NetApiService.Workstation, 0, 0);
286            if (netApiStats != null)
287                result.AddRange(netApiStats);
288
289#false
290            foreach (VolumeInfo info in VolumeInfo.Volumes)
291            {
292                /*DiskPerformanceInfo performance = info.Performance;
293                result.AddRange(StructToBuffer(performance.BytesRead));
294                result.AddRange(StructToBuffer(performance.BytesWritten));
295                result.AddRange(StructToBuffer(performance.IdleTime));
296                result.AddRange(StructToBuffer(performance.QueryTime));
297                result.AddRange(StructToBuffer(performance.QueueDepth));
298                result.AddRange(StructToBuffer(performance.ReadCount));
299                result.AddRange(StructToBuffer(performance.ReadTime));
300                result.AddRange(StructToBuffer(performance.SplitCount));
301                result.AddRange(StructToBuffer(performance.WriteCount));
302                result.AddRange(StructToBuffer(performance.WriteTime));*/
303            }
304#endif
305
306            return result.ToArray();
307        }
308    }
309
310    /// <summary>
311    /// A class which uses EntropyPoll class to fetch system data as a source of
312    /// randomness at "regular" but "random" intervals
313    /// </summary>
314    public class EntropyPoller
315    {
316        /// <summary>
317        /// The algorithm used for mixing
318        /// </summary>
319        private enum PRFAlgorithms
320        {
321            Md5,
322            Sha1,
323            Ripemd160,
324            Sha256,
325            Sha384,
326            Sha512,
327        };
328
329        /// <summary>
330        /// Constructor.
331        /// </summary>
332        public EntropyPoller()
333        {
334            //Create the pool.
335            pool = new byte[sizeof(uint) << 7];
336
337            //Then start the thread which maintains the pool.
338            Thread = new Thread(Main);
339            Thread.Start();
340        }
341
342        /// <summary>
343        /// The PRNG entropy thread. This thread will run in the background, getting
344        /// random data to be used for entropy. This will maintain the integrity
345        /// of generated data from the PRNGs.
346        /// </summary>
347        private void Main()
348        {
349            //This entropy thread will utilize a polling loop.
350            DateTime lastAddedEntropy = DateTime.Now;
351            TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0);
352            Stopwatch st = new Stopwatch();
353
354            while (Thread.ThreadState != System.Threading.ThreadState.AbortRequested)
355            {
356                st.Start();
357                lock (EntropySources)
358                    foreach (EntropySource src in EntropySources)
359                    {
360                        byte[] entropy = src.GetEntropy();
361                        AddEntropy(entropy);
362                    }
363
364                st.Stop();
365                // 2049 = bin '100000000001' ==> great avalanche
366                Thread.Sleep(2000 + (int)(st.ElapsedTicks % 2049L));
367                st.Reset();
368
369                // Send entropy to the PRNGs for new seeds.
370                if (DateTime.Now - lastAddedEntropy > managerEntropySpan)
371                    ManagerLibrary.Instance.PrngRegistrar.AddEntropy(GetPool());
372            }
373        }
374
375        /// <summary>
376        /// Stops the execution of the thread.
377        /// </summary>
378        public void Abort()
379        {
380            Thread.Abort();
381        }
382
383        /// <summary>
384        /// Adds a new Entropy Source to the Poller.
385        /// </summary>
386        /// <param name="source">The EntropySource object to add.</param>
387        public void AddEntropySource(EntropySource source)
388        {
389            lock (EntropySources)
390                EntropySources.Add(source);
391
392            AddEntropy(source.GetPrimer());
393            MixPool();
394
395            //Apply whitening effect
396            PRFAlgorithm = PRFAlgorithms.Ripemd160;
397            MixPool();
398            PRFAlgorithm = PRFAlgorithms.Sha512;
399        }
400
401        /// <summary>
402        /// Retrieves the current contents of the entropy pool.
403        /// </summary>
404        /// <returns>A byte array containing all the randomness currently found.</returns>
405        public byte[] GetPool()
406        {
407            //Mix and invert the pool
408            MixPool();
409            InvertPool();
410
411            //Return a safe copy
412            lock (poolLock)
413            {
414                byte[] result = new byte[pool.Length];
415                pool.CopyTo(result, 0);
416
417                return result;
418            }
419        }
420
421        /// <summary>
422        /// Inverts the contents of the pool
423        /// </summary>
424        private void InvertPool()
425        {
426            lock (poolLock)
427                unsafe
428                {
429                    fixed (byte* fPool = pool)
430                    {
431                        uint* pPool = (uint*)fPool;
432                        uint poolLength = (uint)(pool.Length / sizeof(uint));
433                        while (poolLength-- != 0)
434                            *pPool = (uint)(*pPool++ ^ uint.MaxValue);
435                    }
436                }
437        }
438
439        /// <summary>
440        /// Mixes the contents of the pool.
441        /// </summary>
442        private void MixPool()
443        {
444            lock (poolLock)
445            {
446                //Mix the last 128 bytes first.
447                const int mixBlockSize = 128;
448                int hashSize = PRF.HashSize / 8;
449                PRF.ComputeHash(pool, pool.Length - mixBlockSize, mixBlockSize).CopyTo(pool, 0);
450
451                //Then mix the following bytes until wraparound is required
452                int i = 0;
453                for (; i < pool.Length - hashSize; i += hashSize)
454                    Buffer.BlockCopy(PRF.ComputeHash(pool, i,
455                        i + mixBlockSize >= pool.Length ? pool.Length - i : mixBlockSize),
456                        0, pool, i, i + hashSize >= pool.Length ? pool.Length - i : hashSize);
457
458                //Mix the remaining blocks which require copying from the front
459                byte[] combinedBuffer = new byte[mixBlockSize];
460                for (; i < pool.Length; i += hashSize)
461                {
462                    Buffer.BlockCopy(pool, i, combinedBuffer, 0, pool.Length - i);
463
464                    Buffer.BlockCopy(pool, 0, combinedBuffer, pool.Length - i,
465                                mixBlockSize - (pool.Length - i));
466
467                    Buffer.BlockCopy(PRF.ComputeHash(combinedBuffer, 0, mixBlockSize), 0,
468                        pool, i, pool.Length - i > hashSize ? hashSize : pool.Length - i);
469                }
470            }
471        }
472
473        /// <summary>
474        /// Adds data which is random to the pool
475        /// </summary>
476        /// <param name="entropy">An array of data which will be XORed with pool
477        /// contents.</param>
478        public unsafe void AddEntropy(byte[] entropy)
479        {
480            lock (poolLock)
481                fixed (byte* pEntropy = entropy)
482                fixed (byte* pPool = pool)
483                {
484                    int size = entropy.Length;
485                    byte* mpEntropy = pEntropy;
486                    while (size > 0)
487                    {
488                        //Bring the pool position back to the front if we are at our end
489                        if (poolPosition >= pool.Length)
490                            poolPosition = 0;
491
492                        int amountToMix = Math.Min(size, pool.Length - poolPosition);
493                        MemoryXor(pPool + poolPosition, mpEntropy, amountToMix);
494                        mpEntropy = mpEntropy + amountToMix;
495                        size -= amountToMix;
496                    }
497                }
498        }
499
500        /// <summary>
501        /// XOR's memory a DWORD at a time.
502        /// </summary>
503        /// <param name="destination">The destination buffer to be XOR'ed</param>
504        /// <param name="source">The source buffer to XOR with</param>
505        /// <param name="size">The size of the source buffer</param>
506        private static unsafe void MemoryXor(byte* destination, byte* source, int size)
507        {
508            // XXX: Further optomisation
509            // check the memory bus frame
510            // use BYTE / WORD / DWORD as required         
511           
512            int wsize = size / sizeof(uint);
513            size -= wsize * sizeof(uint);
514            uint* d = (uint*)destination;
515            uint* s = (uint*)source;
516
517            while (wsize-- > 0)
518                *d++ ^= *s++;
519
520            if (size > 0)
521            {
522                byte* db = (byte*)d,
523                      ds = (byte*)s;
524                while (size-- > 0)
525                    *db++ ^= *ds++;
526            }
527        }
528
529        /// <summary>
530        /// PRF algorithm handle
531        /// </summary>
532        private HashAlgorithm PRF
533        {
534            get
535            {
536                Type type = null;
537                switch (PRFAlgorithm)
538                {
539                    case PRFAlgorithms.Md5:
540                        type = typeof(MD5CryptoServiceProvider);
541                        break;
542                    case PRFAlgorithms.Sha1:
543                        type = typeof(SHA1Managed);
544                        break;
545                    case PRFAlgorithms.Ripemd160:
546                        type = typeof(RIPEMD160Managed);
547                        break;
548                    case PRFAlgorithms.Sha256:
549                        type = typeof(SHA256Managed);
550                        break;
551                    case PRFAlgorithms.Sha384:
552                        type = typeof(SHA384Managed);
553                        break;
554                    default:
555                        type = typeof(SHA512Managed);
556                        break;
557                }
558
559                if (type.IsInstanceOfType(prfCache))
560                    return prfCache;
561                ConstructorInfo hashConstructor = type.GetConstructor(Type.EmptyTypes);
562                return prfCache = (HashAlgorithm)hashConstructor.Invoke(null);
563            }
564        }
565
566        /// <summary>
567        /// The last created PRF algorithm handle.
568        /// </summary>
569        private HashAlgorithm prfCache;
570
571        /// <summary>
572        /// PRF algorithm identifier
573        /// </summary>
574        private PRFAlgorithms PRFAlgorithm = PRFAlgorithms.Sha512;
575
576        /// <summary>
577        /// The pool of data which we currently maintain.
578        /// </summary>
579        private byte[] pool;
580
581        /// <summary>
582        /// The next position where entropy will be added to the pool.
583        /// </summary>
584        private int poolPosition;
585
586        /// <summary>
587        /// The lock guarding the pool array and the current entropy addition index.
588        /// </summary>
589        private object poolLock = new object();
590
591        /// <summary>
592        /// The thread object.
593        /// </summary>
594        private Thread Thread;
595
596        /// <summary>
597        /// The list of entropy sources registered with the Poller.
598        /// </summary>
599        private List<EntropySource> EntropySources = new List<EntropySource>();
600    }
601}
Note: See TracBrowser for help on using the repository browser.