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

Revision 2568, 8.4 KB checked in by lowjoel, 2 years ago (diff)

Use shorthand for easier understanding.

  • 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        /// Constructor.
45        /// </summary>
46        public EntropyPoller()
47        {
48            //Create the pool.
49            Pool = new byte[sizeof(uint) << 7];
50
51            //Handle the Entropy Source Registered event.
52            Host.Instance.EntropySources.Registered += OnEntropySourceRegistered;
53
54            //Meanwhile, add all entropy sources already registered.
55            foreach (IEntropySource source in Host.Instance.EntropySources)
56                AddEntropySource(source);
57
58            //Then start the thread which maintains the pool.
59            Thread = new Thread(Main);
60            Thread.Start();
61        }
62
63        /// <summary>
64        /// The PRNG entropy thread. This thread will run in the background, getting
65        /// random data to be used for entropy. This will maintain the integrity
66        /// of generated data from the PRNGs.
67        /// </summary>
68        private void Main()
69        {
70            //Maintain the time we last provided entropy to the PRNGs. We will only
71            //provide entropy every 10 minutes.
72            DateTime lastAddedEntropy = DateTime.Now;
73            TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0);
74
75            while (Thread.ThreadState != System.Threading.ThreadState.AbortRequested)
76            {
77                lock (EntropySources)
78                    foreach (IEntropySource src in EntropySources)
79                    {
80                        byte[] entropy = src.GetEntropy();
81                        AddEntropy(entropy);
82                    }
83
84                //Sleep for a "random" period between roughly [2, 5) seconds from now
85                Thread.Sleep(2000 + (int)(DateTime.Now.Ticks % 2999));
86
87                //Send entropy to the PRNGs for new seeds.
88                DateTime now = DateTime.Now;
89                if (now - lastAddedEntropy > managerEntropySpan)
90                {
91                    Host.Instance.Prngs.AddEntropy(GetPool());
92                    lastAddedEntropy = now;
93                }
94            }
95        }
96
97        /// <summary>
98        /// Stops the execution of the thread.
99        /// </summary>
100        public void Abort()
101        {
102            Thread.Abort();
103        }
104
105        /// <summary>
106        /// Handles the OnEntropySourceRegistered event so we can register them with
107        /// ourselves.
108        /// </summary>
109        /// <param name="sender">The object which was registered.</param>
110        /// <param name="e">Event argument.</param>
111        private void OnEntropySourceRegistered(object sender, EventArgs e)
112        {
113            AddEntropySource((IEntropySource)sender);
114        }
115
116        /// <summary>
117        /// Adds a new Entropy Source to the Poller.
118        /// </summary>
119        /// <param name="source">The EntropySource object to add.</param>
120        public void AddEntropySource(IEntropySource source)
121        {
122            lock (EntropySources)
123                EntropySources.Add(source);
124
125            AddEntropy(source.GetPrimer());
126            MixPool();
127
128            //Apply "whitening" effect. Try to mix the pool using RIPEMD-160 to strengthen
129            //the cryptographic strength of the pool.
130            //There is a need to catch the InvalidOperationException because if Eraser is
131            //running under an OS with FIPS-compliance mode the RIPEMD-160 algorithm cannot
132            //be used.
133            try
134            {
135                using (HashAlgorithm hash = new RIPEMD160Managed())
136                    MixPool(hash);
137            }
138            catch (InvalidOperationException)
139            {
140            }
141        }
142
143        /// <summary>
144        /// Retrieves the current contents of the entropy pool.
145        /// </summary>
146        /// <returns>A byte array containing all the randomness currently found.</returns>
147        public byte[] GetPool()
148        {
149            //Mix and invert the pool
150            MixPool();
151            InvertPool();
152
153            //Return a safe copy
154            lock (PoolLock)
155            {
156                byte[] result = new byte[Pool.Length];
157                Pool.CopyTo(result, 0);
158
159                return result;
160            }
161        }
162
163        /// <summary>
164        /// Inverts the contents of the pool
165        /// </summary>
166        private void InvertPool()
167        {
168            lock (PoolLock)
169                unsafe
170                {
171                    fixed (byte* fPool = Pool)
172                    {
173                        uint* pPool = (uint*)fPool;
174                        uint poolLength = (uint)(Pool.Length / sizeof(uint));
175                        while (poolLength-- != 0)
176                            *pPool = (uint)(*pPool++ ^ uint.MaxValue);
177                    }
178                }
179        }
180
181        /// <summary>
182        /// Mixes the contents of the pool.
183        /// </summary>
184        private void MixPool(HashAlgorithm hash)
185        {
186            lock (PoolLock)
187            {
188                //Mix the last 128 bytes first.
189                const int mixBlockSize = 128;
190                int hashSize = hash.HashSize / 8;
191                hash.ComputeHash(Pool, Pool.Length - mixBlockSize, mixBlockSize).CopyTo(Pool, 0);
192
193                //Then mix the following bytes until wraparound is required
194                int i = 0;
195                for (; i < Pool.Length - hashSize; i += hashSize)
196                    Buffer.BlockCopy(hash.ComputeHash(Pool, i,
197                        Math.Min(mixBlockSize, Pool.Length - i)), 0, Pool, i,
198                        Math.Min(hashSize, Pool.Length - i));
199
200                //Mix the remaining blocks which require copying from the front
201                byte[] combinedBuffer = new byte[mixBlockSize];
202                for (; i < Pool.Length; i += hashSize)
203                {
204                    int remainder = Pool.Length - i;
205                    Buffer.BlockCopy(Pool, i, combinedBuffer, 0, remainder);
206                    Buffer.BlockCopy(Pool, 0, combinedBuffer, remainder,
207                        mixBlockSize - remainder);
208
209                    Buffer.BlockCopy(hash.ComputeHash(combinedBuffer, 0, mixBlockSize), 0,
210                        Pool, i, Math.Min(hashSize, remainder));
211                }
212            }
213        }
214
215        /// <summary>
216        /// Mixes the contents of the entropy pool using the currently specified default
217        /// algorithm.
218        /// </summary>
219        private void MixPool()
220        {
221            using (HashAlgorithm hash = new SHA1CryptoServiceProvider())
222                MixPool(hash);
223        }
224
225        /// <summary>
226        /// Adds data which is random to the pool
227        /// </summary>
228        /// <param name="entropy">An array of data which will be XORed with pool
229        /// contents.</param>
230        public unsafe void AddEntropy(byte[] entropy)
231        {
232            lock (PoolLock)
233                fixed (byte* pEntropy = entropy)
234                fixed (byte* pPool = Pool)
235                {
236                    int size = entropy.Length;
237                    byte* mpEntropy = pEntropy;
238                    while (size > 0)
239                    {
240                        //Bring the pool position back to the front if we are at our end
241                        if (PoolPosition >= Pool.Length)
242                        {
243                            PoolPosition = 0;
244                            MixPool();
245                        }
246
247                        int amountToMix = Math.Min(size, Pool.Length - PoolPosition);
248                        MemoryXor(pPool + PoolPosition, mpEntropy, amountToMix);
249                        mpEntropy += amountToMix;
250                        size -= amountToMix;
251                        PoolPosition += amountToMix;
252                    }
253                }
254        }
255
256        /// <summary>
257        /// XOR's memory a DWORD at a time.
258        /// </summary>
259        /// <param name="destination">The destination buffer to be XOR'ed</param>
260        /// <param name="source">The source buffer to XOR with</param>
261        /// <param name="size">The size of the source buffer</param>
262        private static unsafe void MemoryXor(byte* destination, byte* source, int size)
263        {
264            // XXX: Further optomisation
265            // check the memory bus frame
266            // use BYTE / WORD / DWORD as required         
267           
268            int wsize = size / sizeof(uint);
269            size -= wsize * sizeof(uint);
270            uint* d = (uint*)destination;
271            uint* s = (uint*)source;
272
273            while (wsize-- > 0)
274                *d++ ^= *s++;
275
276            if (size > 0)
277            {
278                byte* db = (byte*)d,
279                      ds = (byte*)s;
280                while (size-- > 0)
281                    *db++ ^= *ds++;
282            }
283        }
284
285        /// <summary>
286        /// The pool of data which we currently maintain.
287        /// </summary>
288        private byte[] Pool;
289
290        /// <summary>
291        /// The next position where entropy will be added to the pool.
292        /// </summary>
293        private int PoolPosition;
294
295        /// <summary>
296        /// The lock guarding the pool array and the current entropy addition index.
297        /// </summary>
298        private object PoolLock = new object();
299
300        /// <summary>
301        /// The thread object.
302        /// </summary>
303        private Thread Thread;
304
305        /// <summary>
306        /// The list of entropy sources registered with the Poller.
307        /// </summary>
308        private List<IEntropySource> EntropySources = new List<IEntropySource>();
309    }
310}
Note: See TracBrowser for help on using the repository browser.