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

Revision 2566, 7.7 KB checked in by lowjoel, 2 years ago (diff)

Merged revisions 2563 and 2565 from Eraser 6.0:

  • Remove the unused PRFAlgorithms enum.
  • Removed the capability for switchable algorithms at runtime, and changed the default pool mixing function to SHA-1 since under FIPS compliance mode on Windows XP the SHA-2 family of hashes are not available. RIPEMD-160 is likewise not available. As such, under FIPS compliance mode, we will only use SHA-1, even on later OSes, and make primer mixing with RIPEMD-160 unavailable.
  • The sleep time between polls is now variable between 2 and 5 seconds. The previous one tended to pick the same amounts of time since it would effectively make one loop double the time it took to gather entropy from all entropy sources.
  • Only push entropy to the PRNGs every 10 minutes

Addresses #406

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