source: branches/eraser6/CodeReview/Eraser.Manager/EntropySource.cs @ 1681

Revision 1681, 20.3 KB checked in by lowjoel, 5 years ago (diff)

Bring the CodeReview? branch up to date with the current trunk at r1680

  • 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
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 EntropySourceManager
129    {
130        /// <summary>
131        /// Constructor.
132        /// </summary>
133        public EntropySourceManager()
134        {
135            entropyThread.AddEntropySource(new KernelEntropySource());
136        }
137
138        /// <summary>
139        /// Retrieves all currently registered erasure methods.
140        /// </summary>
141        /// <returns>A mutable list, with an instance of each EntropySource.</returns>
142        public static Dictionary<Guid, EntropySource> Items
143        {
144            get
145            {
146                lock (ManagerLibrary.Instance.EntropySourceManager.sources)
147                    return ManagerLibrary.Instance.EntropySourceManager.sources;
148            }
149        }
150
151        /// <summary>
152        /// Retrieves the instance of the EntropySource with the given GUID.
153        /// </summary>
154        /// <param name="value">The GUID of the EntropySource.</param>
155        /// <returns>The EntropySource instance.</returns>
156        public static EntropySource GetInstance(Guid value)
157        {
158            lock (ManagerLibrary.Instance.EntropySourceManager.sources)
159            {
160                if (!ManagerLibrary.Instance.EntropySourceManager.sources.ContainsKey(value))
161                    throw new EntropySourceNotFoundException(value);
162                return ManagerLibrary.Instance.EntropySourceManager.sources[value];
163            }
164        }
165
166        /// <summary>
167        /// Allows plugins to register EntropySources with the main program. Thread-safe.
168        /// </summary>
169        /// <param name="source">The source of entropy to add.</param>
170        public static void Register(EntropySource source)
171        {
172            EntropySourceManager manager = ManagerLibrary.Instance.EntropySourceManager;
173            lock (ManagerLibrary.Instance.EntropySourceManager.sources)
174                manager.sources.Add(source.Guid, source);
175            manager.entropyThread.AddEntropySource(source);
176
177            OnEntropySourceRegistered(new EntropySourceRegistrationEventArgs(source.Guid));
178        }
179
180        /// <summary>
181        /// Calls the EntropySourceRegistered event handlers.
182        /// </summary>
183        private static void OnEntropySourceRegistered(EntropySourceRegistrationEventArgs e)
184        {
185            if (EntropySourceRegistered != null)
186                EntropySourceRegistered(ManagerLibrary.Instance.EntropySourceManager, e);
187        }
188
189        /// <summary>
190        /// Gets the entropy poller instance associated with this manager.
191        /// </summary>
192        public EntropyPoller Poller
193        {
194            get
195            {
196                return entropyThread;
197            }
198        }
199
200        /// <summary>
201        /// Global static instance of the EntropySourceRegisteredFunction,
202        /// called whenever the EntropySourceManager.Register is invoked.
203        /// </summary>
204        public static EventHandler<EntropySourceRegistrationEventArgs>
205            EntropySourceRegistered { get; set; }
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    public class EntropySourceRegistrationEventArgs : EventArgs
219    {
220        /// <summary>
221        /// Constructor.
222        /// </summary>
223        /// <param name="value">The GUID of the newly registered/unregistered entropy
224        /// source.</param>
225        public EntropySourceRegistrationEventArgs(Guid value)
226        {
227            Guid = value;
228        }
229
230        /// <summary>
231        /// The GUID of the newly registered/unregistered entropy source.
232        /// </summary>
233        public Guid Guid { get; private set; }
234    }
235       
236    /// <summary>
237    /// Provides means of generating random entropy from the system or user space
238    /// randomness.
239    /// This class is hardcoded into the Manager Library as we need at least one
240    /// instance of such behaviour within our system. The other classes could be
241    /// implemented as plugins, managed by EntropySourceManager.
242    /// </summary>
243    public class KernelEntropySource : EntropySource
244    {
245        public override byte[] GetPrimer()
246        {
247            List<byte> result = new List<byte>();
248
249            //Process information
250            result.AddRange(StructToBuffer(Process.GetCurrentProcess().Id));
251            result.AddRange(StructToBuffer(Process.GetCurrentProcess().StartTime.Ticks));
252
253            result.AddRange(GetFastEntropy());
254            result.AddRange(GetSlowEntropy());
255            return result.ToArray();
256        }
257
258        public override Guid Guid
259        {
260            get
261            {
262                return new Guid("{11EDCECF-AD81-4e50-A73D-B9CF1F813093}");
263            }
264        }
265
266        public override string Name
267        {
268            get
269            {
270                return "Kernel Entropy Source";
271            }
272        }
273
274        public override byte[] GetEntropy()
275        {
276            List<byte> result = new List<byte>();
277            result.AddRange(GetFastEntropy());
278            result.AddRange(GetSlowEntropy());
279
280            return result.ToArray();
281        }
282
283        /// <summary>
284        /// Retrieves entropy from quick sources.
285        /// </summary>
286        public override byte[] GetFastEntropy()
287        {
288            List<byte> result = new List<byte>();
289
290            //Add the free disk space to the pool
291            result.AddRange(StructToBuffer(new DriveInfo(new DirectoryInfo(Environment.SystemDirectory).
292                Root.FullName).TotalFreeSpace));
293
294            //Miscellaneous window handles
295            result.AddRange(StructToBuffer(UserApi.MessagePos));
296            result.AddRange(StructToBuffer(UserApi.MessageTime));
297
298            //The caret and cursor positions
299            result.AddRange(StructToBuffer(UserApi.CaretPos));
300            result.AddRange(StructToBuffer(Cursor.Position));
301
302            //Currently running threads (dynamic, but not very)
303            Process currProcess = Process.GetCurrentProcess();
304            foreach (ProcessThread thread in currProcess.Threads)
305                result.AddRange(StructToBuffer(thread.Id));
306
307            //Various process statistics
308            result.AddRange(StructToBuffer(currProcess.VirtualMemorySize64));
309            result.AddRange(StructToBuffer(currProcess.MaxWorkingSet));
310            result.AddRange(StructToBuffer(currProcess.MinWorkingSet));
311            result.AddRange(StructToBuffer(currProcess.NonpagedSystemMemorySize64));
312            result.AddRange(StructToBuffer(currProcess.PagedMemorySize64));
313            result.AddRange(StructToBuffer(currProcess.PagedSystemMemorySize64));
314            result.AddRange(StructToBuffer(currProcess.PeakPagedMemorySize64));
315            result.AddRange(StructToBuffer(currProcess.PeakVirtualMemorySize64));
316            result.AddRange(StructToBuffer(currProcess.PeakWorkingSet64));
317            result.AddRange(StructToBuffer(currProcess.PrivateMemorySize64));
318            result.AddRange(StructToBuffer(currProcess.WorkingSet64));
319            result.AddRange(StructToBuffer(currProcess.HandleCount));
320
321            //Amount of free memory
322            ComputerInfo computerInfo = new ComputerInfo();
323            result.AddRange(StructToBuffer(computerInfo.AvailablePhysicalMemory));
324            result.AddRange(StructToBuffer(computerInfo.AvailableVirtualMemory));
325
326            //Process execution times
327            result.AddRange(StructToBuffer(currProcess.TotalProcessorTime));
328            result.AddRange(StructToBuffer(currProcess.UserProcessorTime));
329            result.AddRange(StructToBuffer(currProcess.PrivilegedProcessorTime));
330
331            //Thread execution times
332            foreach (ProcessThread thread in currProcess.Threads)
333            {
334                try
335                {
336                    result.AddRange(StructToBuffer(thread.TotalProcessorTime));
337                    result.AddRange(StructToBuffer(thread.UserProcessorTime));
338                    result.AddRange(StructToBuffer(thread.PrivilegedProcessorTime));
339                }
340                catch (InvalidOperationException)
341                {
342                    //Caught when the thread has exited in the middle of the foreach.
343                }
344                catch (System.ComponentModel.Win32Exception e)
345                {
346                    if (e.NativeErrorCode != Win32ErrorCode.AccessDenied)
347                        throw;
348                }
349            }
350
351            //Current system time
352            result.AddRange(StructToBuffer(DateTime.Now.Ticks));
353
354            //The high resolution performance counter
355            result.AddRange(StructToBuffer(SystemInfo.PerformanceCounter));
356
357            //Ticks since start up
358            result.AddRange(StructToBuffer(Environment.TickCount));
359
360            //CryptGenRandom
361            byte[] cryptGenRandom = new byte[160];
362            if (Security.Randomise(cryptGenRandom))
363                result.AddRange(cryptGenRandom);
364
365            return result.ToArray();
366        }
367
368        /// <summary>
369        /// Retrieves entropy from sources which are relatively slower than those from
370        /// the FastAddEntropy function.
371        /// </summary>
372        public override byte[] GetSlowEntropy()
373        {
374            List<byte> result = new List<byte>();
375
376            //NetAPI statistics
377            byte[] netApiStats = NetApi.NetStatisticsGet(null, NetApiService.Workstation, 0, 0);
378            if (netApiStats != null)
379                result.AddRange(netApiStats);
380
381#if false
382            //Get disk I/O statistics for all the hard drives
383            try
384            {
385                for (int drive = 0; ; ++drive)
386                {
387                    //Try to open the drive.
388                    StreamInfo info = new StreamInfo(string.Format(CultureInfo.InvariantCulture,
389                        "\\\\.\\PhysicalDrive{0}", drive));
390                    using (FileStream stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
391                    {
392                        SafeFileHandle device = stream.SafeFileHandle;
393                        KernelApi.DiskPerformanceInfo diskPerformance =
394                            KernelApi.QueryDiskPerformanceInfo(device);
395                        if (diskPerformance != null)
396                        {
397                            result.AddRange(StructToBuffer(diskPerformance.BytesRead));
398                            result.AddRange(StructToBuffer(diskPerformance.BytesWritten));
399                            result.AddRange(StructToBuffer(diskPerformance.IdleTime));
400                            result.AddRange(StructToBuffer(diskPerformance.QueryTime));
401                            result.AddRange(StructToBuffer(diskPerformance.QueueDepth));
402                            result.AddRange(StructToBuffer(diskPerformance.ReadCount));
403                            result.AddRange(StructToBuffer(diskPerformance.ReadTime));
404                            result.AddRange(StructToBuffer(diskPerformance.SplitCount));
405                            result.AddRange(StructToBuffer(diskPerformance.StorageDeviceNumber));
406                            result.AddRange(Encoding.UTF8.GetBytes(diskPerformance.StorageManagerName));
407                            result.AddRange(StructToBuffer(diskPerformance.WriteCount));
408                            result.AddRange(StructToBuffer(diskPerformance.WriteTime));
409                        }
410                    }
411                }
412            }
413            catch (FileNotFoundException)
414            {
415            }
416            catch (UnauthorizedAccessException)
417            {
418            }
419#endif
420            //Finally, our good friend CryptGenRandom()
421            byte[] cryptGenRandom = new byte[1536];
422            if (Security.Randomise(cryptGenRandom))
423                result.AddRange(cryptGenRandom);
424
425            return result.ToArray();
426        }
427    }
428
429    /// <summary>
430    /// A class which uses EntropyPoll class to fetch system data as a source of
431    /// randomness at "regular" but "random" intervals
432    /// </summary>
433    public class EntropyPoller
434    {
435        /// <summary>
436        /// The algorithm used for mixing
437        /// </summary>
438        private enum PRFAlgorithms
439        {
440            Md5,
441            Sha1,
442            Ripemd160,
443            Sha256,
444            Sha384,
445            Sha512,
446        };
447
448        /// <summary>
449        /// Constructor.
450        /// </summary>
451        public EntropyPoller()
452        {
453            //Create the pool.
454            pool = new byte[sizeof(uint) << 7];
455
456            //Then start the thread which maintains the pool.
457            Thread = new Thread(Main);
458            Thread.Start();
459        }
460
461        /// <summary>
462        /// The PRNG entropy thread. This thread will run in the background, getting
463        /// random data to be used for entropy. This will maintain the integrity
464        /// of generated data from the PRNGs.
465        /// </summary>
466        private void Main()
467        {
468            //This entropy thread will utilize a polling loop.
469            DateTime lastAddedEntropy = DateTime.Now;
470            TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0);
471            Stopwatch st = new Stopwatch();
472
473            while (Thread.ThreadState != System.Threading.ThreadState.AbortRequested)
474            {
475                st.Start();
476                lock (EntropySources)
477                    foreach (EntropySource src in EntropySources)
478                    {
479                        byte[] entropy = src.GetEntropy();
480                        AddEntropy(entropy);
481                    }
482
483                st.Stop();
484                // 2049 = bin '100000000001' ==> great avalanche
485                Thread.Sleep(2000 + (int)(st.ElapsedTicks % 2049L));
486                st.Reset();
487
488                // Send entropy to the PRNGs for new seeds.
489                if (DateTime.Now - lastAddedEntropy > managerEntropySpan)
490                    ManagerLibrary.Instance.PRNGManager.AddEntropy(GetPool());
491            }
492        }
493
494        /// <summary>
495        /// Stops the execution of the thread.
496        /// </summary>
497        public void Abort()
498        {
499            Thread.Abort();
500        }
501
502        /// <summary>
503        /// Adds a new Entropy Source to the Poller.
504        /// </summary>
505        /// <param name="source">The EntropySource object to add.</param>
506        public void AddEntropySource(EntropySource source)
507        {
508            lock (EntropySources)
509                EntropySources.Add(source);
510
511            AddEntropy(source.GetPrimer());
512            MixPool();
513
514            //Apply whitening effect
515            PRFAlgorithm = PRFAlgorithms.Ripemd160;
516            MixPool();
517            PRFAlgorithm = PRFAlgorithms.Sha512;
518        }
519
520        /// <summary>
521        /// Retrieves the current contents of the entropy pool.
522        /// </summary>
523        /// <returns>A byte array containing all the randomness currently found.</returns>
524        public byte[] GetPool()
525        {
526            //Mix and invert the pool
527            MixPool();
528            InvertPool();
529
530            //Return a safe copy
531            lock (poolLock)
532            {
533                byte[] result = new byte[pool.Length];
534                pool.CopyTo(result, 0);
535
536                return result;
537            }
538        }
539
540        /// <summary>
541        /// Inverts the contents of the pool
542        /// </summary>
543        private void InvertPool()
544        {
545            lock (poolLock)
546                unsafe
547                {
548                    fixed (byte* fPool = pool)
549                    {
550                        uint* pPool = (uint*)fPool;
551                        uint poolLength = (uint)(pool.Length / sizeof(uint));
552                        while (poolLength-- != 0)
553                            *pPool = (uint)(*pPool++ ^ uint.MaxValue);
554                    }
555                }
556        }
557
558        /// <summary>
559        /// Mixes the contents of the pool.
560        /// </summary>
561        private void MixPool()
562        {
563            lock (poolLock)
564            {
565                //Mix the last 128 bytes first.
566                const int mixBlockSize = 128;
567                int hashSize = PRF.HashSize / 8;
568                PRF.ComputeHash(pool, pool.Length - mixBlockSize, mixBlockSize).CopyTo(pool, 0);
569
570                //Then mix the following bytes until wraparound is required
571                int i = 0;
572                for (; i < pool.Length - hashSize; i += hashSize)
573                    Buffer.BlockCopy(PRF.ComputeHash(pool, i,
574                        i + mixBlockSize >= pool.Length ? pool.Length - i : mixBlockSize),
575                        0, pool, i, i + hashSize >= pool.Length ? pool.Length - i : hashSize);
576
577                //Mix the remaining blocks which require copying from the front
578                byte[] combinedBuffer = new byte[mixBlockSize];
579                for (; i < pool.Length; i += hashSize)
580                {
581                    Buffer.BlockCopy(pool, i, combinedBuffer, 0, pool.Length - i);
582
583                    Buffer.BlockCopy(pool, 0, combinedBuffer, pool.Length - i,
584                                mixBlockSize - (pool.Length - i));
585
586                    Buffer.BlockCopy(PRF.ComputeHash(combinedBuffer, 0, mixBlockSize), 0,
587                        pool, i, pool.Length - i > hashSize ? hashSize : pool.Length - i);
588                }
589            }
590        }
591
592        /// <summary>
593        /// Adds data which is random to the pool
594        /// </summary>
595        /// <param name="entropy">An array of data which will be XORed with pool
596        /// contents.</param>
597        public unsafe void AddEntropy(byte[] entropy)
598        {
599            lock (poolLock)
600                fixed (byte* pEntropy = entropy)
601                fixed (byte* pPool = pool)
602                {
603                    int size = entropy.Length;
604                    byte* mpEntropy = pEntropy;
605                    while (size > 0)
606                    {
607                        //Bring the pool position back to the front if we are at our end
608                        if (poolPosition >= pool.Length)
609                            poolPosition = 0;
610
611                        int amountToMix = Math.Min(size, pool.Length - poolPosition);
612                        MemoryXor(pPool + poolPosition, mpEntropy, amountToMix);
613                        mpEntropy = mpEntropy + amountToMix;
614                        size -= amountToMix;
615                    }
616                }
617        }
618
619        /// <summary>
620        /// XOR's memory a DWORD at a time.
621        /// </summary>
622        /// <param name="destination">The destination buffer to be XOR'ed</param>
623        /// <param name="source">The source buffer to XOR with</param>
624        /// <param name="size">The size of the source buffer</param>
625        private static unsafe void MemoryXor(byte* destination, byte* source, int size)
626        {
627            // XXX: Further optomisation
628            // check the memory bus frame
629            // use BYTE / WORD / DWORD as required         
630           
631            int wsize = size / sizeof(uint);
632            size -= wsize * sizeof(uint);
633            uint* d = (uint*)destination;
634            uint* s = (uint*)source;
635
636            while (wsize-- > 0)
637                *d++ ^= *s++;
638
639            if (size > 0)
640            {
641                byte* db = (byte*)d,
642                      ds = (byte*)s;
643                while (size-- > 0)
644                    *db++ ^= *ds++;
645            }
646        }
647
648        /// <summary>
649        /// PRF algorithm handle
650        /// </summary>
651        private HashAlgorithm PRF
652        {
653            get
654            {
655                Type type = null;
656                switch (PRFAlgorithm)
657                {
658                    case PRFAlgorithms.Md5:
659                        type = typeof(MD5CryptoServiceProvider);
660                        break;
661                    case PRFAlgorithms.Sha1:
662                        type = typeof(SHA1Managed);
663                        break;
664                    case PRFAlgorithms.Ripemd160:
665                        type = typeof(RIPEMD160Managed);
666                        break;
667                    case PRFAlgorithms.Sha256:
668                        type = typeof(SHA256Managed);
669                        break;
670                    case PRFAlgorithms.Sha384:
671                        type = typeof(SHA384Managed);
672                        break;
673                    default:
674                        type = typeof(SHA512Managed);
675                        break;
676                }
677
678                if (type.IsInstanceOfType(prfCache))
679                    return prfCache;
680                ConstructorInfo hashConstructor = type.GetConstructor(Type.EmptyTypes);
681                return prfCache = (HashAlgorithm)hashConstructor.Invoke(null);
682            }
683        }
684
685        /// <summary>
686        /// The last created PRF algorithm handle.
687        /// </summary>
688        private HashAlgorithm prfCache;
689
690        /// <summary>
691        /// PRF algorithm identifier
692        /// </summary>
693        private PRFAlgorithms PRFAlgorithm = PRFAlgorithms.Sha512;
694
695        /// <summary>
696        /// The pool of data which we currently maintain.
697        /// </summary>
698        private byte[] pool;
699
700        /// <summary>
701        /// The next position where entropy will be added to the pool.
702        /// </summary>
703        private int poolPosition;
704
705        /// <summary>
706        /// The lock guarding the pool array and the current entropy addition index.
707        /// </summary>
708        private object poolLock = new object();
709
710        /// <summary>
711        /// The thread object.
712        /// </summary>
713        private Thread Thread;
714
715        /// <summary>
716        /// The list of entropy sources registered with the Poller.
717        /// </summary>
718        private List<EntropySource> EntropySources = new List<EntropySource>();
719    }
720}
Note: See TracBrowser for help on using the repository browser.