Ignore:
Timestamp:
11/14/2008 12:17:53 AM (6 years ago)
Author:
cjax
Message:

Additional entropy source management

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/eraser6/Manager/PRNG.cs

    r502 r549  
    33 * Copyright 2008 The Eraser Project 
    44 * Original Author: Joel Low <lowjoel@users.sourceforge.net> 
    5  * Modified By: Kasra Nasiri <cjax@users.sourceforge.net> @10/7/2008 
     5 * Modified By: Kasra Nasiri <cjax@users.sourceforge.net> 
    66 * Modified By: 
    77 *  
     
    148148        public PRNGManager() 
    149149        { 
    150             entropyThread.AddEntropySource(new KernelEntropySource()); 
    151150        } 
    152151 
     
    206205        public static byte[] GetEntropy() 
    207206        { 
    208             return ManagerLibrary.Instance.PRNGManager.entropyThread.GetPool(); 
    209         } 
    210          
    211         /// <summary> 
    212         /// The entropy thread gathering entropy for the RNGs. 
    213         /// </summary> 
    214         internal EntropyPoller entropyThread = new EntropyPoller(); 
     207            return ManagerLibrary.Instance.EntropySourceManager.Poller.GetPool(); 
     208        } 
    215209 
    216210        /// <summary> 
     
    219213        private Dictionary<Guid, PRNG> prngs = new Dictionary<Guid, PRNG>(); 
    220214    } 
    221  
    222     /// <summary> 
    223     /// Provides an abstract interface to allow multiple sources of entropy into 
    224     /// the EntropyPoller class. 
    225     /// </summary> 
    226     public abstract class EntropySource 
    227     { 
    228         /// <summary> 
    229         /// Constructor. 
    230         /// </summary> 
    231         public EntropySource() 
    232         { 
    233         } 
    234  
    235         /// <summary> 
    236         /// Gets a primer to add to the pool when this source is first initialised, to 
    237         /// further add entropy to the pool. 
    238         /// </summary> 
    239         /// <returns>A byte array containing the entropy.</returns> 
    240         public abstract byte[] GetPrimer(); 
    241          
    242         /// <summary> 
    243         /// Retrieve entropy from a source which will have slow rate of 
    244         /// entropy polling. 
    245         /// </summary> 
    246         /// <returns></returns> 
    247         public abstract byte[] GetSlowEntropy(); 
    248  
    249         /// <summary> 
    250         /// Retrieve entropy from a soruce which will have a fast rate of  
    251         /// entropy polling. 
    252         /// </summary> 
    253         /// <returns></returns> 
    254         public abstract byte[] GetFastEntropy(); 
    255  
    256         /// <summary> 
    257         /// Gets entropy from the entropy source. This will be called repetitively. 
    258         /// </summary> 
    259         /// <returns>A byte array containing the entropy, both slow rate and fast rate.</returns> 
    260         public abstract byte[] GetEntropy(); 
    261                  
    262         /// <summary> 
    263         /// Converts value types into a byte array. This is a helper function to allow 
    264         /// inherited classes to convert value types into byte arrays which can be 
    265         /// returned to the EntropyPoller class. 
    266         /// </summary> 
    267         /// <typeparam name="T">Any value type</typeparam> 
    268         /// <param name="entropy">A value which will be XORed with pool contents.</param> 
    269         protected unsafe static byte[] StructToBuffer<T>(T entropy) where T : struct 
    270         { 
    271             int sizeofObject = Marshal.SizeOf(entropy); 
    272             IntPtr memory = Marshal.AllocHGlobal(sizeofObject); 
    273             try 
    274             { 
    275                 Marshal.StructureToPtr(entropy, memory, false); 
    276                 byte[] dest = new byte[sizeofObject]; 
    277  
    278                 //Copy the memory 
    279                 Marshal.Copy(memory, dest, 0, sizeofObject); 
    280                 return dest; 
    281             } 
    282             finally 
    283             { 
    284                 Marshal.FreeHGlobal(memory); 
    285             } 
    286         } 
    287     } 
    288  
    289     /// <summary> 
    290     /// Provides means of generating random entropy from the system or user space 
    291     /// randomness. 
    292     /// </summary> 
    293     public class KernelEntropySource : EntropySource 
    294     { 
    295         public override byte[] GetPrimer() 
    296         { 
    297             List<byte> result = new List<byte>(); 
    298  
    299             //Process startup information 
    300             KernelAPI.STARTUPINFO startupInfo = new KernelAPI.STARTUPINFO(); 
    301             KernelAPI.GetStartupInfo(out startupInfo); 
    302             result.AddRange(StructToBuffer(startupInfo)); 
    303  
    304             //System information 
    305             KernelAPI.SYSTEM_INFO systemInfo = new KernelAPI.SYSTEM_INFO(); 
    306             KernelAPI.GetSystemInfo(out systemInfo); 
    307             result.AddRange(StructToBuffer(systemInfo)); 
    308  
    309             result.AddRange(GetFastEntropy()); 
    310             result.AddRange(GetSlowEntropy()); 
    311             return result.ToArray(); 
    312         } 
    313  
    314         public override byte[] GetEntropy() 
    315         { 
    316             List<byte> result = new List<byte>(); 
    317             result.AddRange(GetFastEntropy()); 
    318             result.AddRange(GetSlowEntropy()); 
    319  
    320             return result.ToArray(); 
    321         } 
    322  
    323         /// <summary> 
    324         /// Retrieves entropy from quick sources. 
    325         /// </summary> 
    326         public override byte[] GetFastEntropy() 
    327         { 
    328             List<byte> result = new List<byte>(); 
    329  
    330             //Add the free disk space to the pool 
    331             result.AddRange(StructToBuffer(new DriveInfo(new DirectoryInfo(Environment.SystemDirectory). 
    332                 Root.FullName).TotalFreeSpace)); 
    333  
    334             //Miscellaneous window handles 
    335             result.AddRange(StructToBuffer(UserAPI.GetCapture())); 
    336             result.AddRange(StructToBuffer(UserAPI.GetClipboardOwner())); 
    337             result.AddRange(StructToBuffer(UserAPI.GetClipboardViewer())); 
    338             result.AddRange(StructToBuffer(UserAPI.GetDesktopWindow())); 
    339             result.AddRange(StructToBuffer(UserAPI.GetForegroundWindow())); 
    340             result.AddRange(StructToBuffer(UserAPI.GetMessagePos())); 
    341             result.AddRange(StructToBuffer(UserAPI.GetMessageTime())); 
    342             result.AddRange(StructToBuffer(UserAPI.GetOpenClipboardWindow())); 
    343             result.AddRange(StructToBuffer(UserAPI.GetProcessWindowStation())); 
    344             result.AddRange(StructToBuffer(KernelAPI.GetCurrentProcessId())); 
    345             result.AddRange(StructToBuffer(KernelAPI.GetCurrentThreadId())); 
    346             result.AddRange(StructToBuffer(KernelAPI.GetProcessHeap())); 
    347  
    348             //The caret and cursor positions 
    349             UserAPI.POINT point; 
    350             UserAPI.GetCaretPos(out point); 
    351             result.AddRange(StructToBuffer(point)); 
    352             UserAPI.GetCursorPos(out point); 
    353             result.AddRange(StructToBuffer(point)); 
    354  
    355             //Amount of free memory 
    356             KernelAPI.MEMORYSTATUSEX memoryStatus = new KernelAPI.MEMORYSTATUSEX(); 
    357             memoryStatus.dwLength = (uint)Marshal.SizeOf(memoryStatus); 
    358             if (KernelAPI.GlobalMemoryStatusEx(ref memoryStatus)) 
    359             { 
    360                 result.AddRange(StructToBuffer(memoryStatus.ullAvailPhys)); 
    361                 result.AddRange(StructToBuffer(memoryStatus.ullAvailVirtual)); 
    362                 result.AddRange(StructToBuffer(memoryStatus)); 
    363             } 
    364  
    365             //Thread execution times 
    366             long creationTime, exitTime, kernelTime, userTime; 
    367             if (KernelAPI.GetThreadTimes(KernelAPI.GetCurrentThread(), out creationTime, 
    368                 out exitTime, out kernelTime, out userTime)) 
    369             { 
    370                 result.AddRange(StructToBuffer(creationTime)); 
    371                 result.AddRange(StructToBuffer(kernelTime)); 
    372                 result.AddRange(StructToBuffer(userTime)); 
    373             } 
    374  
    375             //Process execution times 
    376             if (KernelAPI.GetProcessTimes(KernelAPI.GetCurrentProcess(), out creationTime, 
    377                 out exitTime, out kernelTime, out userTime)) 
    378             { 
    379                 result.AddRange(StructToBuffer(creationTime)); 
    380                 result.AddRange(StructToBuffer(kernelTime)); 
    381                 result.AddRange(StructToBuffer(userTime)); 
    382             } 
    383  
    384             //Current system time 
    385             result.AddRange(StructToBuffer(DateTime.Now.Ticks)); 
    386  
    387             //The high resolution performance counter 
    388             long perfCount = 0; 
    389             if (KernelAPI.QueryPerformanceCounter(out perfCount)) 
    390                 result.AddRange(StructToBuffer(perfCount)); 
    391  
    392             //Ticks since start up 
    393             uint tickCount = KernelAPI.GetTickCount(); 
    394             if (tickCount != 0) 
    395                 result.AddRange(StructToBuffer(tickCount)); 
    396  
    397             //CryptGenRandom 
    398             byte[] cryptGenRandom = new byte[160]; 
    399             if (CryptAPI.CryptGenRandom(cryptGenRandom)) 
    400                 result.AddRange(cryptGenRandom); 
    401  
    402             return result.ToArray(); 
    403         } 
    404  
    405         /// <summary> 
    406         /// Retrieves entropy from sources which are relatively slower than those from 
    407         /// the FastAddEntropy function. 
    408         /// </summary> 
    409         public override byte[] GetSlowEntropy() 
    410         { 
    411             List<byte> result = new List<byte>(); 
    412  
    413             //NetAPI statistics 
    414             unsafe 
    415             { 
    416                 IntPtr netAPIStats = IntPtr.Zero; 
    417                 if (NetAPI.NetStatisticsGet(null, NetAPI.SERVICE_WORKSTATION, 
    418                     0, 0, out netAPIStats) == 0) 
    419                 { 
    420                     try 
    421                     { 
    422                         //Get the size of the buffer 
    423                         uint size = 0; 
    424                         NetAPI.NetApiBufferSize(netAPIStats, out size); 
    425                         byte[] entropy = new byte[size]; 
    426  
    427                         //Copy the buffer 
    428                         Marshal.Copy(entropy, 0, netAPIStats, entropy.Length); 
    429  
    430                         //And add it to the pool 
    431                         result.AddRange(entropy); 
    432                     } 
    433                     finally 
    434                     { 
    435                         //Free the statistics buffer 
    436                         NetAPI.NetApiBufferFree(netAPIStats); 
    437                     } 
    438                 } 
    439             } 
    440  
    441 #if false 
    442             //Get disk I/O statistics for all the hard drives 
    443             for (int drive = 0; ; ++drive) 
    444             { 
    445                 //Try to open the drive. 
    446                 using (SafeFileHandle hDevice = File.CreateFile( 
    447                     string.Format("\\\\.\\PhysicalDrive%d", drive), 0, 
    448                     File.FILE_SHARE_READ | File.FILE_SHARE_WRITE, IntPtr.Zero, 
    449                     File.OPEN_EXISTING, 0, IntPtr.Zero)) 
    450                 { 
    451                     if (hDevice.IsInvalid) 
    452                         break; 
    453  
    454                     //This only works if the user has turned on the disk performance 
    455                     //counters with 'diskperf -y'. These counters are off by default 
    456                     if (File.DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, 
    457                         &diskPerformance, sizeof(DISK_PERFORMANCE), &uSize, NULL)) 
    458                     { 
    459                         addEntropy(&diskPerformance, uSize); 
    460                     } 
    461                 } 
    462             } 
    463 #endif 
    464  
    465             /* 
    466              Query performance data. Because the Win32 version of this API (through 
    467              registry) may be buggy, use the NT Native API instead. 
    468               
    469              Scan the first 64 possible information types (we don't bother 
    470              with increasing the buffer size as we do with the Win32 
    471              version of the performance data read, we may miss a few classes 
    472              but it's no big deal).  In addition the returned size value for 
    473              some classes is wrong (eg 23 and 24 return a size of 0) so we 
    474              miss a few more things, but again it's no big deal.  This scan 
    475              typically yields around 20 pieces of data, there's nothing in 
    476              the range 65...128 so chances are there won't be anything above 
    477              there either. 
    478             */ 
    479             uint dataWritten = 0; 
    480             byte[] infoBuffer = new byte[65536]; 
    481             uint totalEntropy = 0; 
    482             for (uint infoType = 0; infoType < 64; ++infoType) 
    483             { 
    484                 uint sysInfo = NTAPI.NtQuerySystemInformation(infoType, infoBuffer, 
    485                     (uint)infoBuffer.Length, out dataWritten); 
    486  
    487                 if (sysInfo == 0 /*ERROR_SUCCESS*/ && dataWritten > 0) 
    488                 { 
    489                     byte[] entropy = new byte[dataWritten]; 
    490                     Buffer.BlockCopy(infoBuffer, 0, entropy, 0, (int)dataWritten); 
    491                     result.AddRange(entropy); 
    492                     totalEntropy += dataWritten; 
    493                 } 
    494             } 
    495  
    496             result.AddRange(StructToBuffer(totalEntropy)); 
    497  
    498             //Finally, our good friend CryptGenRandom() 
    499             byte[] cryptGenRandom = new byte[1536]; 
    500             if (CryptAPI.CryptGenRandom(cryptGenRandom)) 
    501                 result.AddRange(cryptGenRandom); 
    502  
    503             return result.ToArray(); 
    504         } 
    505     } 
    506  
    507     /// <summary> 
    508     /// A class which uses EntropyPoll class to fetch system data as a source of 
    509     /// randomness at "regular" but "random" intervals 
    510     /// </summary> 
    511     public class EntropyPoller : KernelEntropySource 
    512     { 
    513         /// <summary> 
    514         /// The algorithm used for mixing 
    515         /// </summary> 
    516         private enum PRFAlgorithms 
    517         { 
    518             MD5, 
    519             SHA1, 
    520             RIPEMD160, 
    521             SHA256, 
    522             SHA384, 
    523             SHA512, 
    524         }; 
    525  
    526         /// <summary> 
    527         /// Constructor. 
    528         /// </summary> 
    529         public EntropyPoller() 
    530         { 
    531             //Create the pool. 
    532             pool = new byte[sizeof(uint) * 128]; 
    533  
    534             //Then start the thread which maintains the pool. 
    535             Thread = new Thread(delegate() 
    536                 { 
    537                     this.Main(); 
    538                 } 
    539             ); 
    540             Thread.Start(); 
    541         } 
    542  
    543         /// <summary> 
    544         /// The PRNG entropy thread. This thread will run in the background, getting 
    545         /// random data to be used for entropy. This will maintain the integrity 
    546         /// of generated data from the PRNGs. 
    547         /// </summary> 
    548         private void Main() 
    549         { 
    550             //This entropy thread will utilize a polling loop. 
    551             DateTime lastAddedEntropy = DateTime.Now; 
    552             TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0); 
    553             Stopwatch st = new Stopwatch(); 
    554  
    555             while (Thread.ThreadState != System.Threading.ThreadState.AbortRequested) 
    556             { 
    557                 st.Start(); 
    558                 lock (EntropySources) 
    559                     foreach (EntropySource src in EntropySources) 
    560                     { 
    561                         byte[] entropy = src.GetEntropy(); 
    562                         AddEntropy(entropy); 
    563                     } 
    564                  
    565                 st.Stop(); 
    566                 // 2049 = bin '100000000001', great avalanche 
    567                 Thread.Sleep(2000 + (int)(st.ElapsedTicks % 2049L)); 
    568                 st.Reset(); 
    569  
    570                 // Send entropy to the PRNGs for new seeds. 
    571                 if (DateTime.Now - lastAddedEntropy > managerEntropySpan) 
    572                     ManagerLibrary.Instance.PRNGManager.AddEntropy(GetPool()); 
    573             } 
    574         } 
    575          
    576         /// <summary> 
    577         /// Stops the execution of the thread. 
    578         /// </summary> 
    579         public void Abort() 
    580         { 
    581             Thread.Abort(); 
    582         } 
    583  
    584         /// <summary> 
    585         /// Adds a new Entropy Source to the Poller. 
    586         /// </summary> 
    587         /// <param name="source">The EntropySource object to add.</param> 
    588         public void AddEntropySource(EntropySource source) 
    589         { 
    590             lock (EntropySources) 
    591                 EntropySources.Add(source); 
    592  
    593             AddEntropy(source.GetPrimer()); 
    594             MixPool(); 
    595  
    596             //Apply whitening effect 
    597             PRFAlgorithm = PRFAlgorithms.RIPEMD160; 
    598             MixPool(); 
    599             PRFAlgorithm = PRFAlgorithms.SHA512; 
    600         } 
    601  
    602         /// <summary> 
    603         /// Retrieves the current contents of the entropy pool. 
    604         /// </summary> 
    605         /// <returns>A byte array containing all the randomness currently found.</returns> 
    606         public byte[] GetPool() 
    607         { 
    608             //Mix and invert the pool 
    609             MixPool(); 
    610             InvertPool(); 
    611  
    612             //Return a safe copy 
    613             lock (pool) 
    614             { 
    615                 byte[] result = new byte[pool.Length]; 
    616                 pool.CopyTo(result, 0); 
    617  
    618                 return result; 
    619             } 
    620         } 
    621  
    622         /// <summary> 
    623         /// Inverts the contents of the pool 
    624         /// </summary> 
    625         private void InvertPool() 
    626         { 
    627             lock (poolLock) 
    628                 unsafe 
    629                 { 
    630                     fixed (byte* fPool = pool) 
    631                     { 
    632                         uint* pPool = (uint*)fPool; 
    633                         uint poolLength = (uint)(pool.Length / sizeof(uint)); 
    634                         while (poolLength-- != 0) 
    635                             *pPool = (uint)(*pPool++ ^ unchecked((uint)-1)); 
    636                     } 
    637                 } 
    638         } 
    639  
    640         /// <summary> 
    641         /// Mixes the contents of the pool. 
    642         /// </summary> 
    643         private void MixPool() 
    644         { 
    645             lock (poolLock) 
    646             { 
    647                 //Mix the last 128 bytes first. 
    648                 const int mixBlockSize = 128; 
    649                 int hashSize = PRF.HashSize / 8; 
    650                 PRF.ComputeHash(pool, pool.Length - mixBlockSize, mixBlockSize).CopyTo(pool, 0); 
    651  
    652                 //Then mix the following bytes until wraparound is required 
    653                 int i = 0; 
    654                 for (; i < pool.Length - hashSize; i += hashSize) 
    655                     Buffer.BlockCopy(PRF.ComputeHash(pool, i, 
    656                         i + mixBlockSize >= pool.Length ? pool.Length - i : mixBlockSize), 
    657                         0, pool, i, i + hashSize >= pool.Length ? pool.Length - i : hashSize); 
    658  
    659                 //Mix the remaining blocks which require copying from the front 
    660                 byte[] combinedBuffer = new byte[mixBlockSize]; 
    661                 for (; i < pool.Length; i += hashSize) 
    662                 { 
    663                     Buffer.BlockCopy(pool, i, combinedBuffer, 0, pool.Length - i); 
    664  
    665                     Buffer.BlockCopy(pool, 0, combinedBuffer, pool.Length - i, 
    666                                 mixBlockSize - (pool.Length - i)); 
    667  
    668                     Buffer.BlockCopy(PRF.ComputeHash(combinedBuffer, 0, mixBlockSize), 0, 
    669                         pool, i, pool.Length - i > hashSize ? hashSize : pool.Length - i); 
    670                 } 
    671             } 
    672         } 
    673  
    674         /// <summary> 
    675         /// Adds data which is random to the pool 
    676         /// </summary> 
    677         /// <param name="entropy">An array of data which will be XORed with pool 
    678         /// contents.</param> 
    679         public unsafe void AddEntropy(byte[] entropy) 
    680         { 
    681             lock (poolLock) 
    682                 fixed (byte* pEntropy = entropy) 
    683                 fixed (byte* pPool = pool) 
    684                 { 
    685                     int size = entropy.Length; 
    686                     byte* mpEntropy = pEntropy; 
    687                     while (size > 0) 
    688                     { 
    689                         //Bring the pool position back to the front if we are at our end 
    690                         if (poolPosition >= pool.Length) 
    691                             poolPosition = 0; 
    692  
    693                         int amountToMix = Math.Min(size, pool.Length - poolPosition); 
    694                         MemoryXor(pPool + poolPosition, mpEntropy, amountToMix); 
    695                         mpEntropy = mpEntropy + amountToMix; 
    696                         size -= amountToMix; 
    697                     } 
    698                 } 
    699         } 
    700  
    701         /// <summary> 
    702         /// XOR's memory a DWORD at a time. 
    703         /// </summary> 
    704         /// <param name="destination">The destination buffer to be XOR'ed</param> 
    705         /// <param name="source">The source buffer to XOR with</param> 
    706         /// <param name="size">The size of the source buffer</param> 
    707         private static unsafe void MemoryXor(byte* destination, byte* source, int size) 
    708         { 
    709             int wsize = size / sizeof(uint); 
    710             size -= wsize * sizeof(uint); 
    711             uint* d = (uint*)destination; 
    712             uint* s = (uint*)source; 
    713  
    714             while (wsize-- > 0) 
    715                 *d++ ^= *s++; 
    716  
    717             if (size > 0) 
    718             { 
    719                 byte* db = (byte*)d, 
    720                       ds = (byte*)s; 
    721                 while (size-- > 0) 
    722                     *db++ ^= *ds++; 
    723             } 
    724         } 
    725  
    726         /// <summary> 
    727         /// PRF algorithm handle 
    728         /// </summary> 
    729         private HashAlgorithm PRF 
    730         { 
    731             get 
    732             { 
    733                 Type type = null; 
    734                 switch (PRFAlgorithm) 
    735                 { 
    736                     case PRFAlgorithms.MD5: 
    737                         type = typeof(MD5CryptoServiceProvider); 
    738                         break; 
    739                     case PRFAlgorithms.SHA1: 
    740                         type = typeof(SHA1Managed); 
    741                         break; 
    742                     case PRFAlgorithms.RIPEMD160: 
    743                         type = typeof(RIPEMD160Managed); 
    744                         break; 
    745                     case PRFAlgorithms.SHA256: 
    746                         type = typeof(SHA256Managed); 
    747                         break; 
    748                     case PRFAlgorithms.SHA384: 
    749                         type = typeof(SHA384Managed); 
    750                         break; 
    751                     default: 
    752                         type = typeof(SHA512Managed); 
    753                         break; 
    754                 } 
    755  
    756                 if (type.IsInstanceOfType(prfCache)) 
    757                     return prfCache; 
    758                 ConstructorInfo hashConstructor = type.GetConstructor(Type.EmptyTypes); 
    759                 return prfCache = (HashAlgorithm)hashConstructor.Invoke(null); 
    760             } 
    761         } 
    762  
    763         /// <summary> 
    764         /// The last created PRF algorithm handle. 
    765         /// </summary> 
    766         private HashAlgorithm prfCache; 
    767  
    768         /// <summary> 
    769         /// PRF algorithm identifier 
    770         /// </summary> 
    771         private PRFAlgorithms PRFAlgorithm = PRFAlgorithms.SHA512; 
    772  
    773         /// <summary> 
    774         /// The pool of data which we currently maintain. 
    775         /// </summary> 
    776         private byte[] pool; 
    777  
    778         /// <summary> 
    779         /// The next position where entropy will be added to the pool. 
    780         /// </summary> 
    781         private int poolPosition = 0; 
    782  
    783         /// <summary> 
    784         /// The lock guarding the pool array and the current entropy addition index. 
    785         /// </summary> 
    786         private object poolLock = new object(); 
    787  
    788         /// <summary> 
    789         /// The thread object. 
    790         /// </summary> 
    791         Thread Thread; 
    792  
    793         /// <summary> 
    794         /// The list of entropy sources registered with the Poller. 
    795         /// </summary> 
    796         private List<EntropySource> EntropySources = new List<EntropySource>(); 
    797     } 
    798215} 
Note: See TracChangeset for help on using the changeset viewer.