Changeset 549 for branches/eraser6/Manager/PRNG.cs
- Timestamp:
- 11/14/2008 12:17:53 AM (4 years ago)
- File:
-
- 1 edited
-
branches/eraser6/Manager/PRNG.cs (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/eraser6/Manager/PRNG.cs
r502 r549 3 3 * Copyright 2008 The Eraser Project 4 4 * Original Author: Joel Low <lowjoel@users.sourceforge.net> 5 * Modified By: Kasra Nasiri <cjax@users.sourceforge.net> @10/7/20085 * Modified By: Kasra Nasiri <cjax@users.sourceforge.net> 6 6 * Modified By: 7 7 * … … 148 148 public PRNGManager() 149 149 { 150 entropyThread.AddEntropySource(new KernelEntropySource());151 150 } 152 151 … … 206 205 public static byte[] GetEntropy() 207 206 { 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 } 215 209 216 210 /// <summary> … … 219 213 private Dictionary<Guid, PRNG> prngs = new Dictionary<Guid, PRNG>(); 220 214 } 221 222 /// <summary>223 /// Provides an abstract interface to allow multiple sources of entropy into224 /// the EntropyPoller class.225 /// </summary>226 public abstract class EntropySource227 {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, to237 /// 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 of244 /// 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 of251 /// 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 allow264 /// inherited classes to convert value types into byte arrays which can be265 /// 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 : struct270 {271 int sizeofObject = Marshal.SizeOf(entropy);272 IntPtr memory = Marshal.AllocHGlobal(sizeofObject);273 try274 {275 Marshal.StructureToPtr(entropy, memory, false);276 byte[] dest = new byte[sizeofObject];277 278 //Copy the memory279 Marshal.Copy(memory, dest, 0, sizeofObject);280 return dest;281 }282 finally283 {284 Marshal.FreeHGlobal(memory);285 }286 }287 }288 289 /// <summary>290 /// Provides means of generating random entropy from the system or user space291 /// randomness.292 /// </summary>293 public class KernelEntropySource : EntropySource294 {295 public override byte[] GetPrimer()296 {297 List<byte> result = new List<byte>();298 299 //Process startup information300 KernelAPI.STARTUPINFO startupInfo = new KernelAPI.STARTUPINFO();301 KernelAPI.GetStartupInfo(out startupInfo);302 result.AddRange(StructToBuffer(startupInfo));303 304 //System information305 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 pool331 result.AddRange(StructToBuffer(new DriveInfo(new DirectoryInfo(Environment.SystemDirectory).332 Root.FullName).TotalFreeSpace));333 334 //Miscellaneous window handles335 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 positions349 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 memory356 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 times366 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 times376 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 time385 result.AddRange(StructToBuffer(DateTime.Now.Ticks));386 387 //The high resolution performance counter388 long perfCount = 0;389 if (KernelAPI.QueryPerformanceCounter(out perfCount))390 result.AddRange(StructToBuffer(perfCount));391 392 //Ticks since start up393 uint tickCount = KernelAPI.GetTickCount();394 if (tickCount != 0)395 result.AddRange(StructToBuffer(tickCount));396 397 //CryptGenRandom398 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 from407 /// the FastAddEntropy function.408 /// </summary>409 public override byte[] GetSlowEntropy()410 {411 List<byte> result = new List<byte>();412 413 //NetAPI statistics414 unsafe415 {416 IntPtr netAPIStats = IntPtr.Zero;417 if (NetAPI.NetStatisticsGet(null, NetAPI.SERVICE_WORKSTATION,418 0, 0, out netAPIStats) == 0)419 {420 try421 {422 //Get the size of the buffer423 uint size = 0;424 NetAPI.NetApiBufferSize(netAPIStats, out size);425 byte[] entropy = new byte[size];426 427 //Copy the buffer428 Marshal.Copy(entropy, 0, netAPIStats, entropy.Length);429 430 //And add it to the pool431 result.AddRange(entropy);432 }433 finally434 {435 //Free the statistics buffer436 NetAPI.NetApiBufferFree(netAPIStats);437 }438 }439 }440 441 #if false442 //Get disk I/O statistics for all the hard drives443 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 performance455 //counters with 'diskperf -y'. These counters are off by default456 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 #endif464 465 /*466 Query performance data. Because the Win32 version of this API (through467 registry) may be buggy, use the NT Native API instead.468 469 Scan the first 64 possible information types (we don't bother470 with increasing the buffer size as we do with the Win32471 version of the performance data read, we may miss a few classes472 but it's no big deal). In addition the returned size value for473 some classes is wrong (eg 23 and 24 return a size of 0) so we474 miss a few more things, but again it's no big deal. This scan475 typically yields around 20 pieces of data, there's nothing in476 the range 65...128 so chances are there won't be anything above477 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 of509 /// randomness at "regular" but "random" intervals510 /// </summary>511 public class EntropyPoller : KernelEntropySource512 {513 /// <summary>514 /// The algorithm used for mixing515 /// </summary>516 private enum PRFAlgorithms517 {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, getting545 /// random data to be used for entropy. This will maintain the integrity546 /// 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 avalanche567 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 effect597 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 pool609 MixPool();610 InvertPool();611 612 //Return a safe copy613 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 pool624 /// </summary>625 private void InvertPool()626 {627 lock (poolLock)628 unsafe629 {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 required653 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 front660 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 pool676 /// </summary>677 /// <param name="entropy">An array of data which will be XORed with pool678 /// 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 end690 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 handle728 /// </summary>729 private HashAlgorithm PRF730 {731 get732 {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 identifier770 /// </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 }798 215 }
Note: See TracChangeset
for help on using the changeset viewer.
