source: trunk/eraser/Eraser.Manager/EntropyPoller.cs @ 2516

Revision 2516, 8.3 KB checked in by lowjoel, 2 years ago (diff)

Update copyrights to this year.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Rev URL
Line 
1/*
2 * $Id$
3 * Copyright 2008-2012 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.Threading;
27using System.Security.Cryptography;
28using System.Runtime.InteropServices;
29using System.Diagnostics;
30using System.Reflection;
31
32using Eraser.Plugins;
33using Eraser.Plugins.ExtensionPoints;
34
35namespace Eraser.Manager
36{
37    /// <summary>
38    /// A class which uses EntropyPoll class to fetch system data as a source of
39    /// randomness at "regular" but "random" intervals
40    /// </summary>
41    public class EntropyPoller
42    {
43        /// <summary>
44        /// The algorithm used for mixing
45        /// </summary>
46        private enum PRFAlgorithms
47        {
48            Md5,
49            Sha1,
50            Ripemd160,
51            Sha256,
52            Sha384,
53            Sha512,
54        };
55
56        /// <summary>
57        /// Constructor.
58        /// </summary>
59        public EntropyPoller()
60        {
61            //Create the pool.
62            pool = new byte[sizeof(uint) << 7];
63
64            //Then start the thread which maintains the pool.
65            Thread = new Thread(Main);
66            Thread.Start();
67        }
68
69        /// <summary>
70        /// The PRNG entropy thread. This thread will run in the background, getting
71        /// random data to be used for entropy. This will maintain the integrity
72        /// of generated data from the PRNGs.
73        /// </summary>
74        private void Main()
75        {
76            //This entropy thread will utilize a polling loop.
77            DateTime lastAddedEntropy = DateTime.Now;
78            TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0);
79            Stopwatch st = new Stopwatch();
80
81            while (Thread.ThreadState != System.Threading.ThreadState.AbortRequested)
82            {
83                st.Start();
84                lock (EntropySources)
85                    foreach (IEntropySource src in EntropySources)
86                    {
87                        byte[] entropy = src.GetEntropy();
88                        AddEntropy(entropy);
89                    }
90
91                st.Stop();
92                // 2049 = bin '100000000001' ==> great avalanche
93                Thread.Sleep(2000 + (int)(st.ElapsedTicks % 2049L));
94                st.Reset();
95
96                // Send entropy to the PRNGs for new seeds.
97                if (DateTime.Now - lastAddedEntropy > managerEntropySpan)
98                    Host.Instance.Prngs.AddEntropy(GetPool());
99            }
100        }
101
102        /// <summary>
103        /// Stops the execution of the thread.
104        /// </summary>
105        public void Abort()
106        {
107            Thread.Abort();
108        }
109
110        /// <summary>
111        /// Adds a new Entropy Source to the Poller.
112        /// </summary>
113        /// <param name="source">The EntropySource object to add.</param>
114        public void AddEntropySource(IEntropySource source)
115        {
116            lock (EntropySources)
117                EntropySources.Add(source);
118
119            AddEntropy(source.GetPrimer());
120            MixPool();
121
122            //Apply whitening effect
123            PRFAlgorithm = PRFAlgorithms.Ripemd160;
124            MixPool();
125            PRFAlgorithm = PRFAlgorithms.Sha512;
126        }
127
128        /// <summary>
129        /// Retrieves the current contents of the entropy pool.
130        /// </summary>
131        /// <returns>A byte array containing all the randomness currently found.</returns>
132        public byte[] GetPool()
133        {
134            //Mix and invert the pool
135            MixPool();
136            InvertPool();
137
138            //Return a safe copy
139            lock (poolLock)
140            {
141                byte[] result = new byte[pool.Length];
142                pool.CopyTo(result, 0);
143
144                return result;
145            }
146        }
147
148        /// <summary>
149        /// Inverts the contents of the pool
150        /// </summary>
151        private void InvertPool()
152        {
153            lock (poolLock)
154                unsafe
155                {
156                    fixed (byte* fPool = pool)
157                    {
158                        uint* pPool = (uint*)fPool;
159                        uint poolLength = (uint)(pool.Length / sizeof(uint));
160                        while (poolLength-- != 0)
161                            *pPool = (uint)(*pPool++ ^ uint.MaxValue);
162                    }
163                }
164        }
165
166        /// <summary>
167        /// Mixes the contents of the pool.
168        /// </summary>
169        private void MixPool()
170        {
171            lock (poolLock)
172            {
173                //Mix the last 128 bytes first.
174                const int mixBlockSize = 128;
175                int hashSize = PRF.HashSize / 8;
176                PRF.ComputeHash(pool, pool.Length - mixBlockSize, mixBlockSize).CopyTo(pool, 0);
177
178                //Then mix the following bytes until wraparound is required
179                int i = 0;
180                for (; i < pool.Length - hashSize; i += hashSize)
181                    Buffer.BlockCopy(PRF.ComputeHash(pool, i,
182                        i + mixBlockSize >= pool.Length ? pool.Length - i : mixBlockSize),
183                        0, pool, i, i + hashSize >= pool.Length ? pool.Length - i : hashSize);
184
185                //Mix the remaining blocks which require copying from the front
186                byte[] combinedBuffer = new byte[mixBlockSize];
187                for (; i < pool.Length; i += hashSize)
188                {
189                    Buffer.BlockCopy(pool, i, combinedBuffer, 0, pool.Length - i);
190
191                    Buffer.BlockCopy(pool, 0, combinedBuffer, pool.Length - i,
192                                mixBlockSize - (pool.Length - i));
193
194                    Buffer.BlockCopy(PRF.ComputeHash(combinedBuffer, 0, mixBlockSize), 0,
195                        pool, i, pool.Length - i > hashSize ? hashSize : pool.Length - i);
196                }
197            }
198        }
199
200        /// <summary>
201        /// Adds data which is random to the pool
202        /// </summary>
203        /// <param name="entropy">An array of data which will be XORed with pool
204        /// contents.</param>
205        public unsafe void AddEntropy(byte[] entropy)
206        {
207            lock (poolLock)
208                fixed (byte* pEntropy = entropy)
209                fixed (byte* pPool = pool)
210                {
211                    int size = entropy.Length;
212                    byte* mpEntropy = pEntropy;
213                    while (size > 0)
214                    {
215                        //Bring the pool position back to the front if we are at our end
216                        if (poolPosition >= pool.Length)
217                            poolPosition = 0;
218
219                        int amountToMix = Math.Min(size, pool.Length - poolPosition);
220                        MemoryXor(pPool + poolPosition, mpEntropy, amountToMix);
221                        mpEntropy = mpEntropy + amountToMix;
222                        size -= amountToMix;
223                    }
224                }
225        }
226
227        /// <summary>
228        /// XOR's memory a DWORD at a time.
229        /// </summary>
230        /// <param name="destination">The destination buffer to be XOR'ed</param>
231        /// <param name="source">The source buffer to XOR with</param>
232        /// <param name="size">The size of the source buffer</param>
233        private static unsafe void MemoryXor(byte* destination, byte* source, int size)
234        {
235            // XXX: Further optomisation
236            // check the memory bus frame
237            // use BYTE / WORD / DWORD as required         
238           
239            int wsize = size / sizeof(uint);
240            size -= wsize * sizeof(uint);
241            uint* d = (uint*)destination;
242            uint* s = (uint*)source;
243
244            while (wsize-- > 0)
245                *d++ ^= *s++;
246
247            if (size > 0)
248            {
249                byte* db = (byte*)d,
250                      ds = (byte*)s;
251                while (size-- > 0)
252                    *db++ ^= *ds++;
253            }
254        }
255
256        /// <summary>
257        /// PRF algorithm handle
258        /// </summary>
259        private HashAlgorithm PRF
260        {
261            get
262            {
263                Type type = null;
264                switch (PRFAlgorithm)
265                {
266                    case PRFAlgorithms.Md5:
267                        type = typeof(MD5CryptoServiceProvider);
268                        break;
269                    case PRFAlgorithms.Sha1:
270                        type = typeof(SHA1Managed);
271                        break;
272                    case PRFAlgorithms.Ripemd160:
273                        type = typeof(RIPEMD160Managed);
274                        break;
275                    case PRFAlgorithms.Sha256:
276                        type = typeof(SHA256Managed);
277                        break;
278                    case PRFAlgorithms.Sha384:
279                        type = typeof(SHA384Managed);
280                        break;
281                    default:
282                        type = typeof(SHA512Managed);
283                        break;
284                }
285
286                if (type.IsInstanceOfType(prfCache))
287                    return prfCache;
288                ConstructorInfo hashConstructor = type.GetConstructor(Type.EmptyTypes);
289                return prfCache = (HashAlgorithm)hashConstructor.Invoke(null);
290            }
291        }
292
293        /// <summary>
294        /// The last created PRF algorithm handle.
295        /// </summary>
296        private HashAlgorithm prfCache;
297
298        /// <summary>
299        /// PRF algorithm identifier
300        /// </summary>
301        private PRFAlgorithms PRFAlgorithm = PRFAlgorithms.Sha512;
302
303        /// <summary>
304        /// The pool of data which we currently maintain.
305        /// </summary>
306        private byte[] pool;
307
308        /// <summary>
309        /// The next position where entropy will be added to the pool.
310        /// </summary>
311        private int poolPosition;
312
313        /// <summary>
314        /// The lock guarding the pool array and the current entropy addition index.
315        /// </summary>
316        private object poolLock = new object();
317
318        /// <summary>
319        /// The thread object.
320        /// </summary>
321        private Thread Thread;
322
323        /// <summary>
324        /// The list of entropy sources registered with the Poller.
325        /// </summary>
326        private List<IEntropySource> EntropySources = new List<IEntropySource>();
327    }
328}
Note: See TracBrowser for help on using the repository browser.