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

Revision 1802, 17.7 KB checked in by lowjoel, 4 years ago (diff)

Merged the CodeReview? Branch back to trunk. (Finally!) Closes #275: Code Review.

  • 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
274            //CryptGenRandom
275            byte[] cryptGenRandom = new byte[160];
276            if (Security.Randomise(cryptGenRandom))
277                result.AddRange(cryptGenRandom);
278
279            return result.ToArray();
280        }
281
282        /// <summary>
283        /// Retrieves entropy from sources which are relatively slower than those from
284        /// the FastAddEntropy function.
285        /// </summary>
286        public override byte[] GetSlowEntropy()
287        {
288            List<byte> result = new List<byte>();
289
290            //NetAPI statistics
291            byte[] netApiStats = NetApi.NetStatisticsGet(null, NetApiService.Workstation, 0, 0);
292            if (netApiStats != null)
293                result.AddRange(netApiStats);
294
295#if false
296            //Get disk I/O statistics for all the hard drives
297            try
298            {
299                for (int drive = 0; ; ++drive)
300                {
301                    //Try to open the drive.
302                    StreamInfo info = new StreamInfo(string.Format(CultureInfo.InvariantCulture,
303                        "\\\\.\\PhysicalDrive{0}", drive));
304                    using (FileStream stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
305                    {
306                        SafeFileHandle device = stream.SafeFileHandle;
307                        KernelApi.DiskPerformanceInfo diskPerformance =
308                            KernelApi.QueryDiskPerformanceInfo(device);
309                        if (diskPerformance != null)
310                        {
311                            result.AddRange(StructToBuffer(diskPerformance.BytesRead));
312                            result.AddRange(StructToBuffer(diskPerformance.BytesWritten));
313                            result.AddRange(StructToBuffer(diskPerformance.IdleTime));
314                            result.AddRange(StructToBuffer(diskPerformance.QueryTime));
315                            result.AddRange(StructToBuffer(diskPerformance.QueueDepth));
316                            result.AddRange(StructToBuffer(diskPerformance.ReadCount));
317                            result.AddRange(StructToBuffer(diskPerformance.ReadTime));
318                            result.AddRange(StructToBuffer(diskPerformance.SplitCount));
319                            result.AddRange(StructToBuffer(diskPerformance.StorageDeviceNumber));
320                            result.AddRange(Encoding.UTF8.GetBytes(diskPerformance.StorageManagerName));
321                            result.AddRange(StructToBuffer(diskPerformance.WriteCount));
322                            result.AddRange(StructToBuffer(diskPerformance.WriteTime));
323                        }
324                    }
325                }
326            }
327            catch (FileNotFoundException)
328            {
329            }
330            catch (UnauthorizedAccessException)
331            {
332            }
333#endif
334            //Finally, our good friend CryptGenRandom()
335            byte[] cryptGenRandom = new byte[1536];
336            if (Security.Randomise(cryptGenRandom))
337                result.AddRange(cryptGenRandom);
338
339            return result.ToArray();
340        }
341    }
342
343    /// <summary>
344    /// A class which uses EntropyPoll class to fetch system data as a source of
345    /// randomness at "regular" but "random" intervals
346    /// </summary>
347    public class EntropyPoller
348    {
349        /// <summary>
350        /// The algorithm used for mixing
351        /// </summary>
352        private enum PRFAlgorithms
353        {
354            Md5,
355            Sha1,
356            Ripemd160,
357            Sha256,
358            Sha384,
359            Sha512,
360        };
361
362        /// <summary>
363        /// Constructor.
364        /// </summary>
365        public EntropyPoller()
366        {
367            //Create the pool.
368            pool = new byte[sizeof(uint) << 7];
369
370            //Then start the thread which maintains the pool.
371            Thread = new Thread(Main);
372            Thread.Start();
373        }
374
375        /// <summary>
376        /// The PRNG entropy thread. This thread will run in the background, getting
377        /// random data to be used for entropy. This will maintain the integrity
378        /// of generated data from the PRNGs.
379        /// </summary>
380        private void Main()
381        {
382            //This entropy thread will utilize a polling loop.
383            DateTime lastAddedEntropy = DateTime.Now;
384            TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0);
385            Stopwatch st = new Stopwatch();
386
387            while (Thread.ThreadState != System.Threading.ThreadState.AbortRequested)
388            {
389                st.Start();
390                lock (EntropySources)
391                    foreach (EntropySource src in EntropySources)
392                    {
393                        byte[] entropy = src.GetEntropy();
394                        AddEntropy(entropy);
395                    }
396
397                st.Stop();
398                // 2049 = bin '100000000001' ==> great avalanche
399                Thread.Sleep(2000 + (int)(st.ElapsedTicks % 2049L));
400                st.Reset();
401
402                // Send entropy to the PRNGs for new seeds.
403                if (DateTime.Now - lastAddedEntropy > managerEntropySpan)
404                    ManagerLibrary.Instance.PrngRegistrar.AddEntropy(GetPool());
405            }
406        }
407
408        /// <summary>
409        /// Stops the execution of the thread.
410        /// </summary>
411        public void Abort()
412        {
413            Thread.Abort();
414        }
415
416        /// <summary>
417        /// Adds a new Entropy Source to the Poller.
418        /// </summary>
419        /// <param name="source">The EntropySource object to add.</param>
420        public void AddEntropySource(EntropySource source)
421        {
422            lock (EntropySources)
423                EntropySources.Add(source);
424
425            AddEntropy(source.GetPrimer());
426            MixPool();
427
428            //Apply whitening effect
429            PRFAlgorithm = PRFAlgorithms.Ripemd160;
430            MixPool();
431            PRFAlgorithm = PRFAlgorithms.Sha512;
432        }
433
434        /// <summary>
435        /// Retrieves the current contents of the entropy pool.
436        /// </summary>
437        /// <returns>A byte array containing all the randomness currently found.</returns>
438        public byte[] GetPool()
439        {
440            //Mix and invert the pool
441            MixPool();
442            InvertPool();
443
444            //Return a safe copy
445            lock (poolLock)
446            {
447                byte[] result = new byte[pool.Length];
448                pool.CopyTo(result, 0);
449
450                return result;
451            }
452        }
453
454        /// <summary>
455        /// Inverts the contents of the pool
456        /// </summary>
457        private void InvertPool()
458        {
459            lock (poolLock)
460                unsafe
461                {
462                    fixed (byte* fPool = pool)
463                    {
464                        uint* pPool = (uint*)fPool;
465                        uint poolLength = (uint)(pool.Length / sizeof(uint));
466                        while (poolLength-- != 0)
467                            *pPool = (uint)(*pPool++ ^ uint.MaxValue);
468                    }
469                }
470        }
471
472        /// <summary>
473        /// Mixes the contents of the pool.
474        /// </summary>
475        private void MixPool()
476        {
477            lock (poolLock)
478            {
479                //Mix the last 128 bytes first.
480                const int mixBlockSize = 128;
481                int hashSize = PRF.HashSize / 8;
482                PRF.ComputeHash(pool, pool.Length - mixBlockSize, mixBlockSize).CopyTo(pool, 0);
483
484                //Then mix the following bytes until wraparound is required
485                int i = 0;
486                for (; i < pool.Length - hashSize; i += hashSize)
487                    Buffer.BlockCopy(PRF.ComputeHash(pool, i,
488                        i + mixBlockSize >= pool.Length ? pool.Length - i : mixBlockSize),
489                        0, pool, i, i + hashSize >= pool.Length ? pool.Length - i : hashSize);
490
491                //Mix the remaining blocks which require copying from the front
492                byte[] combinedBuffer = new byte[mixBlockSize];
493                for (; i < pool.Length; i += hashSize)
494                {
495                    Buffer.BlockCopy(pool, i, combinedBuffer, 0, pool.Length - i);
496
497                    Buffer.BlockCopy(pool, 0, combinedBuffer, pool.Length - i,
498                                mixBlockSize - (pool.Length - i));
499
500                    Buffer.BlockCopy(PRF.ComputeHash(combinedBuffer, 0, mixBlockSize), 0,
501                        pool, i, pool.Length - i > hashSize ? hashSize : pool.Length - i);
502                }
503            }
504        }
505
506        /// <summary>
507        /// Adds data which is random to the pool
508        /// </summary>
509        /// <param name="entropy">An array of data which will be XORed with pool
510        /// contents.</param>
511        public unsafe void AddEntropy(byte[] entropy)
512        {
513            lock (poolLock)
514                fixed (byte* pEntropy = entropy)
515                fixed (byte* pPool = pool)
516                {
517                    int size = entropy.Length;
518                    byte* mpEntropy = pEntropy;
519                    while (size > 0)
520                    {
521                        //Bring the pool position back to the front if we are at our end
522                        if (poolPosition >= pool.Length)
523                            poolPosition = 0;
524
525                        int amountToMix = Math.Min(size, pool.Length - poolPosition);
526                        MemoryXor(pPool + poolPosition, mpEntropy, amountToMix);
527                        mpEntropy = mpEntropy + amountToMix;
528                        size -= amountToMix;
529                    }
530                }
531        }
532
533        /// <summary>
534        /// XOR's memory a DWORD at a time.
535        /// </summary>
536        /// <param name="destination">The destination buffer to be XOR'ed</param>
537        /// <param name="source">The source buffer to XOR with</param>
538        /// <param name="size">The size of the source buffer</param>
539        private static unsafe void MemoryXor(byte* destination, byte* source, int size)
540        {
541            // XXX: Further optomisation
542            // check the memory bus frame
543            // use BYTE / WORD / DWORD as required         
544           
545            int wsize = size / sizeof(uint);
546            size -= wsize * sizeof(uint);
547            uint* d = (uint*)destination;
548            uint* s = (uint*)source;
549
550            while (wsize-- > 0)
551                *d++ ^= *s++;
552
553            if (size > 0)
554            {
555                byte* db = (byte*)d,
556                      ds = (byte*)s;
557                while (size-- > 0)
558                    *db++ ^= *ds++;
559            }
560        }
561
562        /// <summary>
563        /// PRF algorithm handle
564        /// </summary>
565        private HashAlgorithm PRF
566        {
567            get
568            {
569                Type type = null;
570                switch (PRFAlgorithm)
571                {
572                    case PRFAlgorithms.Md5:
573                        type = typeof(MD5CryptoServiceProvider);
574                        break;
575                    case PRFAlgorithms.Sha1:
576                        type = typeof(SHA1Managed);
577                        break;
578                    case PRFAlgorithms.Ripemd160:
579                        type = typeof(RIPEMD160Managed);
580                        break;
581                    case PRFAlgorithms.Sha256:
582                        type = typeof(SHA256Managed);
583                        break;
584                    case PRFAlgorithms.Sha384:
585                        type = typeof(SHA384Managed);
586                        break;
587                    default:
588                        type = typeof(SHA512Managed);
589                        break;
590                }
591
592                if (type.IsInstanceOfType(prfCache))
593                    return prfCache;
594                ConstructorInfo hashConstructor = type.GetConstructor(Type.EmptyTypes);
595                return prfCache = (HashAlgorithm)hashConstructor.Invoke(null);
596            }
597        }
598
599        /// <summary>
600        /// The last created PRF algorithm handle.
601        /// </summary>
602        private HashAlgorithm prfCache;
603
604        /// <summary>
605        /// PRF algorithm identifier
606        /// </summary>
607        private PRFAlgorithms PRFAlgorithm = PRFAlgorithms.Sha512;
608
609        /// <summary>
610        /// The pool of data which we currently maintain.
611        /// </summary>
612        private byte[] pool;
613
614        /// <summary>
615        /// The next position where entropy will be added to the pool.
616        /// </summary>
617        private int poolPosition;
618
619        /// <summary>
620        /// The lock guarding the pool array and the current entropy addition index.
621        /// </summary>
622        private object poolLock = new object();
623
624        /// <summary>
625        /// The thread object.
626        /// </summary>
627        private Thread Thread;
628
629        /// <summary>
630        /// The list of entropy sources registered with the Poller.
631        /// </summary>
632        private List<EntropySource> EntropySources = new List<EntropySource>();
633    }
634}
Note: See TracBrowser for help on using the repository browser.