Changeset 479 for branches/eraser6/Manager/PRNG.cs
- Timestamp:
- 11/9/2008 10:04:48 AM (5 years ago)
- File:
-
- 1 edited
-
branches/eraser6/Manager/PRNG.cs (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/eraser6/Manager/PRNG.cs
r478 r479 144 144 { 145 145 /// <summary> 146 /// Constructor. 147 /// </summary> 148 public PRNGManager() 149 { 150 entropyThread.AddEntropySource(new KernelEntropySource()); 151 } 152 153 /// <summary> 146 154 /// Retrieves all currently registered erasure methods. 147 155 /// </summary> … … 217 225 /// </summary> 218 226 class EntropyPoller 219 {220 public EntropyPoller()221 {222 //Then start the thread which maintains the pool.223 thread = new Thread(delegate()224 {225 this.Main();226 }227 );228 thread.Start();229 }230 231 /// <summary>232 /// The PRNG entropy thread. This thread will run in the background, getting233 /// random data to be used for entropy. This will maintain the integrity234 /// of generated data from the PRNGs.235 /// </summary>236 private void Main()237 {238 //This entropy thread will utilize a polling loop.239 DateTime lastAddedEntropy = DateTime.Now;240 TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0);241 Stopwatch st = new Stopwatch();242 243 while (thread.ThreadState != System.Threading.ThreadState.AbortRequested)244 {245 st.Start();246 {247 FastAddEntropy();248 SlowAddEntropy();249 }250 251 st.Stop();252 Thread.Sleep(2000 + (int)(st.ElapsedTicks % 2049L));253 st.Reset();254 255 //Send entropy to the PRNGs for new seeds.256 if (DateTime.Now - lastAddedEntropy > managerEntropySpan)257 ManagerLibrary.Instance.PRNGManager.AddEntropy(GetPool());258 }259 }260 261 /// <summary>262 /// Stops the execution of the thread.263 /// </summary>264 public void Abort()265 {266 thread.Abort();267 }268 269 /// <summary>270 /// The thread object.271 /// </summary>272 Thread thread;273 }274 275 /// <summary>276 /// Provides means of generating random entropy from the system, user data277 /// available from the kernel or dedicated harware.278 /// </summary>279 public class EntropySource280 227 { 281 228 /// <summary> … … 292 239 }; 293 240 294 public EntropySource() 241 /// <summary> 242 /// Constructor. 243 /// </summary> 244 public EntropyPoller() 295 245 { 296 246 //Create the pool. 297 247 pool = new byte[sizeof(uint) * 128]; 298 248 299 //Initialize the pool with some default information. 300 { 301 //Process startup information 302 KernelAPI.STARTUPINFO startupInfo = new KernelAPI.STARTUPINFO(); 303 KernelAPI.GetStartupInfo(out startupInfo); 304 AddEntropy(startupInfo); 305 306 //System information 307 KernelAPI.SYSTEM_INFO systemInfo = new KernelAPI.SYSTEM_INFO(); 308 KernelAPI.GetSystemInfo(out systemInfo); 309 AddEntropy(systemInfo); 310 311 FastAddEntropy(); 312 SlowAddEntropy(); 313 314 // set the default PRF algorithm 315 PRFAlgorithm = PRFAlgorithms.SHA512; 316 MixPool(); 317 } 318 319 // apply whitening effect 249 //Then start the thread which maintains the pool. 250 Thread = new Thread(delegate() 251 { 252 this.Main(); 253 } 254 ); 255 Thread.Start(); 256 } 257 258 /// <summary> 259 /// The PRNG entropy thread. This thread will run in the background, getting 260 /// random data to be used for entropy. This will maintain the integrity 261 /// of generated data from the PRNGs. 262 /// </summary> 263 private void Main() 264 { 265 //This entropy thread will utilize a polling loop. 266 DateTime lastAddedEntropy = DateTime.Now; 267 TimeSpan managerEntropySpan = new TimeSpan(0, 10, 0); 268 Stopwatch st = new Stopwatch(); 269 270 while (Thread.ThreadState != System.Threading.ThreadState.AbortRequested) 271 { 272 st.Start(); 273 lock (EntropySources) 274 foreach (EntropySource src in EntropySources) 275 { 276 byte[] entropy = src.GetEntropy(); 277 AddEntropy(entropy); 278 } 279 280 st.Stop(); 281 Thread.Sleep(2000 + (int)(st.ElapsedTicks % 2049L)); 282 st.Reset(); 283 284 //Send entropy to the PRNGs for new seeds. 285 if (DateTime.Now - lastAddedEntropy > managerEntropySpan) 286 ManagerLibrary.Instance.PRNGManager.AddEntropy(GetPool()); 287 } 288 } 289 290 /// <summary> 291 /// Stops the execution of the thread. 292 /// </summary> 293 public void Abort() 294 { 295 Thread.Abort(); 296 } 297 298 /// <summary> 299 /// Adds a new Entropy Source to the Poller. 300 /// </summary> 301 /// <param name="source">The EntropySource object to add.</param> 302 public void AddEntropySource(EntropySource source) 303 { 304 lock (EntropySources) 305 EntropySources.Add(source); 306 307 AddEntropy(source.GetPrimer()); 308 MixPool(); 309 310 //Apply whitening effect 320 311 PRFAlgorithm = PRFAlgorithms.RIPEMD160; 321 312 MixPool(); 322 323 // set back to default hash algorithm324 313 PRFAlgorithm = PRFAlgorithms.SHA512; 325 314 } … … 436 425 uint* d = (uint*)destination; 437 426 uint* s = (uint*)source; 438 427 439 428 while (wsize-- > 0) 440 429 *d++ ^= *s++; … … 447 436 *db++ ^= *ds++; 448 437 } 449 }450 451 /// <summary>452 /// Adds data which is random to the pool453 /// </summary>454 /// <typeparam name="T">Any value type</typeparam>455 /// <param name="entropy">A value which will be XORed with pool contents.</param>456 public unsafe void AddEntropy<T>(T entropy) where T : struct457 {458 try459 {460 int sizeofObject = Marshal.SizeOf(entropy);461 IntPtr memory = Marshal.AllocHGlobal(sizeofObject);462 try463 {464 Marshal.StructureToPtr(entropy, memory, false);465 byte[] dest = new byte[sizeofObject];466 467 //Copy the memory468 Marshal.Copy(memory, dest, 0, sizeofObject);469 470 //Add entropy471 AddEntropy(dest);472 }473 finally474 {475 Marshal.FreeHGlobal(memory);476 }477 }478 catch (OutOfMemoryException ex1)479 {480 // ignore this entropy source, we don't have enough memory!481 string ignored = ex1.Message;482 }483 }484 485 /// <summary>486 /// Adds entropy to the pool. The sources of the entropy data is queried487 /// quickly.488 /// </summary>489 private void FastAddEntropy()490 {491 //Add the free disk space to the pool492 AddEntropy(new DriveInfo(new DirectoryInfo(Environment.SystemDirectory).493 Root.FullName).TotalFreeSpace);494 495 //Miscellaneous window handles496 AddEntropy(UserAPI.GetCapture());497 AddEntropy(UserAPI.GetClipboardOwner());498 AddEntropy(UserAPI.GetClipboardViewer());499 AddEntropy(UserAPI.GetDesktopWindow());500 AddEntropy(UserAPI.GetForegroundWindow());501 AddEntropy(UserAPI.GetMessagePos());502 AddEntropy(UserAPI.GetMessageTime());503 AddEntropy(UserAPI.GetOpenClipboardWindow());504 AddEntropy(UserAPI.GetProcessWindowStation());505 AddEntropy(KernelAPI.GetCurrentProcessId());506 AddEntropy(KernelAPI.GetCurrentThreadId());507 AddEntropy(KernelAPI.GetProcessHeap());508 509 //The caret and cursor positions510 UserAPI.POINT point;511 UserAPI.GetCaretPos(out point);512 AddEntropy(point);513 UserAPI.GetCursorPos(out point);514 AddEntropy(point);515 516 //Amount of free memory517 KernelAPI.MEMORYSTATUSEX memoryStatus = new KernelAPI.MEMORYSTATUSEX();518 memoryStatus.dwLength = (uint)Marshal.SizeOf(memoryStatus);519 if (KernelAPI.GlobalMemoryStatusEx(ref memoryStatus))520 {521 AddEntropy(memoryStatus.ullAvailPhys);522 AddEntropy(memoryStatus.ullAvailVirtual);523 AddEntropy(memoryStatus);524 }525 526 //Thread execution times527 long creationTime, exitTime, kernelTime, userTime;528 if (KernelAPI.GetThreadTimes(KernelAPI.GetCurrentThread(), out creationTime,529 out exitTime, out kernelTime, out userTime))530 {531 AddEntropy(creationTime);532 AddEntropy(kernelTime);533 AddEntropy(userTime);534 }535 536 //Process execution times537 if (KernelAPI.GetProcessTimes(KernelAPI.GetCurrentProcess(), out creationTime,538 out exitTime, out kernelTime, out userTime))539 {540 AddEntropy(creationTime);541 AddEntropy(kernelTime);542 AddEntropy(userTime);543 }544 545 //Current system time546 AddEntropy(DateTime.Now.Ticks);547 548 //The high resolution performance counter549 long perfCount = 0;550 if (KernelAPI.QueryPerformanceCounter(out perfCount))551 AddEntropy(perfCount);552 553 //Ticks since start up554 uint tickCount = KernelAPI.GetTickCount();555 if (tickCount != 0)556 AddEntropy(tickCount);557 558 //CryptGenRandom559 byte[] cryptGenRandom = new byte[160];560 if (CryptAPI.CryptGenRandom(cryptGenRandom))561 AddEntropy(cryptGenRandom);562 }563 564 /// <summary>565 /// Adds entropy to the pool. The sources of the entropy data is queried566 /// relatively slowly compared to the FastAddEntropy function.567 /// </summary>568 private void SlowAddEntropy()569 {570 //NetAPI statistics571 unsafe572 {573 IntPtr netAPIStats = IntPtr.Zero;574 if (NetAPI.NetStatisticsGet(null, NetAPI.SERVICE_WORKSTATION,575 0, 0, out netAPIStats) == 0)576 {577 try578 {579 //Get the size of the buffer580 uint size = 0;581 NetAPI.NetApiBufferSize(netAPIStats, out size);582 byte[] entropy = new byte[size];583 584 //Copy the buffer585 Marshal.Copy(entropy, 0, netAPIStats, entropy.Length);586 587 //And add it to the pool588 AddEntropy(entropy);589 }590 finally591 {592 //Free the statistics buffer593 NetAPI.NetApiBufferFree(netAPIStats);594 }595 }596 }597 598 #if false599 //Get disk I/O statistics for all the hard drives600 for (int drive = 0; ; ++drive)601 {602 //Try to open the drive.603 using (SafeFileHandle hDevice = File.CreateFile(604 string.Format("\\\\.\\PhysicalDrive%d", drive), 0,605 File.FILE_SHARE_READ | File.FILE_SHARE_WRITE, IntPtr.Zero,606 File.OPEN_EXISTING, 0, IntPtr.Zero))607 {608 if (hDevice.IsInvalid)609 break;610 611 //This only works if the user has turned on the disk performance612 //counters with 'diskperf -y'. These counters are off by default613 if (File.DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,614 &diskPerformance, sizeof(DISK_PERFORMANCE), &uSize, NULL))615 {616 addEntropy(&diskPerformance, uSize);617 }618 }619 }620 #endif621 622 /*623 Query performance data. Because the Win32 version of this API (through624 registry) may be buggy, use the NT Native API instead.625 626 Scan the first 64 possible information types (we don't bother627 with increasing the buffer size as we do with the Win32628 version of the performance data read, we may miss a few classes629 but it's no big deal). In addition the returned size value for630 some classes is wrong (eg 23 and 24 return a size of 0) so we631 miss a few more things, but again it's no big deal. This scan632 typically yields around 20 pieces of data, there's nothing in633 the range 65...128 so chances are there won't be anything above634 there either.635 */636 uint dataWritten = 0;637 byte[] infoBuffer = new byte[65536];638 uint totalEntropy = 0;639 for (uint infoType = 0; infoType < 64; ++infoType)640 {641 uint result = NTAPI.NtQuerySystemInformation(infoType, infoBuffer,642 (uint)infoBuffer.Length, out dataWritten);643 644 if (result == 0 /*ERROR_SUCCESS*/ && dataWritten > 0)645 {646 byte[] entropy = new byte[dataWritten];647 Buffer.BlockCopy(infoBuffer, 0, entropy, 0, (int)dataWritten);648 AddEntropy(entropy);649 totalEntropy += dataWritten;650 }651 }652 653 AddEntropy(totalEntropy);654 655 //Finally, our good friend CryptGenRandom()656 byte[] cryptGenRandom = new byte[1536];657 if (CryptAPI.CryptGenRandom(cryptGenRandom))658 AddEntropy(cryptGenRandom);659 438 } 660 439 … … 704 483 /// PRF algorithm identifier 705 484 /// </summary> 706 private PRFAlgorithms PRFAlgorithm ;485 private PRFAlgorithms PRFAlgorithm = PRFAlgorithms.SHA512; 707 486 708 487 /// <summary> … … 720 499 /// </summary> 721 500 private object poolLock = new object(); 501 502 /// <summary> 503 /// The thread object. 504 /// </summary> 505 Thread Thread; 506 507 /// <summary> 508 /// The list of entropy sources registered with the Poller. 509 /// </summary> 510 private List<EntropySource> EntropySources = new List<EntropySource>(); 511 } 512 513 /// <summary> 514 /// Provides an abstract interface to allow multiple sources of entropy into 515 /// the EntropyPoller class. 516 /// </summary> 517 public abstract class EntropySource 518 { 519 /// <summary> 520 /// Constructor. 521 /// </summary> 522 public EntropySource() 523 { 524 } 525 526 /// <summary> 527 /// Gets a primer to add to the pool when this source is first initialised, to 528 /// further add entropy to the pool. 529 /// </summary> 530 /// <returns>A byte array containing the entropy.</returns> 531 public abstract byte[] GetPrimer(); 532 533 /// <summary> 534 /// Gets entropy from the entropy source. This will be called repetitively. 535 /// </summary> 536 /// <returns>A byte array containing the entropy.</returns> 537 public abstract byte[] GetEntropy(); 538 539 /// <summary> 540 /// Converts value types into a byte array. This is a helper function to allow 541 /// inherited classes to convert value types into byte arrays which can be 542 /// returned to the EntropyPoller class. 543 /// </summary> 544 /// <typeparam name="T">Any value type</typeparam> 545 /// <param name="entropy">A value which will be XORed with pool contents.</param> 546 protected unsafe static byte[] StructToBuffer<T>(T entropy) where T: struct 547 { 548 int sizeofObject = Marshal.SizeOf(entropy); 549 IntPtr memory = Marshal.AllocHGlobal(sizeofObject); 550 try 551 { 552 Marshal.StructureToPtr(entropy, memory, false); 553 byte[] dest = new byte[sizeofObject]; 554 555 //Copy the memory 556 Marshal.Copy(memory, dest, 0, sizeofObject); 557 return dest; 558 } 559 finally 560 { 561 Marshal.FreeHGlobal(memory); 562 } 563 } 564 } 565 566 /// <summary> 567 /// Provides means of generating random entropy from the system or user space 568 /// randomness. 569 /// </summary> 570 internal class KernelEntropySource : EntropySource 571 { 572 public override byte[] GetPrimer() 573 { 574 List<byte> result = new List<byte>(); 575 576 //Process startup information 577 KernelAPI.STARTUPINFO startupInfo = new KernelAPI.STARTUPINFO(); 578 KernelAPI.GetStartupInfo(out startupInfo); 579 result.AddRange(StructToBuffer(startupInfo)); 580 581 //System information 582 KernelAPI.SYSTEM_INFO systemInfo = new KernelAPI.SYSTEM_INFO(); 583 KernelAPI.GetSystemInfo(out systemInfo); 584 result.AddRange(StructToBuffer(systemInfo)); 585 586 result.AddRange(GetFastEntropy()); 587 result.AddRange(GetSlowEntropy()); 588 return result.ToArray(); 589 } 590 591 public override byte[] GetEntropy() 592 { 593 List<byte> result = new List<byte>(); 594 result.AddRange(GetFastEntropy()); 595 result.AddRange(GetSlowEntropy()); 596 597 return result.ToArray(); 598 } 599 600 /// <summary> 601 /// Retrieves entropy from quick sources. 602 /// </summary> 603 private byte[] GetFastEntropy() 604 { 605 List<byte> result = new List<byte>(); 606 607 //Add the free disk space to the pool 608 result.AddRange(StructToBuffer(new DriveInfo(new DirectoryInfo(Environment.SystemDirectory). 609 Root.FullName).TotalFreeSpace)); 610 611 //Miscellaneous window handles 612 result.AddRange(StructToBuffer(UserAPI.GetCapture())); 613 result.AddRange(StructToBuffer(UserAPI.GetClipboardOwner())); 614 result.AddRange(StructToBuffer(UserAPI.GetClipboardViewer())); 615 result.AddRange(StructToBuffer(UserAPI.GetDesktopWindow())); 616 result.AddRange(StructToBuffer(UserAPI.GetForegroundWindow())); 617 result.AddRange(StructToBuffer(UserAPI.GetMessagePos())); 618 result.AddRange(StructToBuffer(UserAPI.GetMessageTime())); 619 result.AddRange(StructToBuffer(UserAPI.GetOpenClipboardWindow())); 620 result.AddRange(StructToBuffer(UserAPI.GetProcessWindowStation())); 621 result.AddRange(StructToBuffer(KernelAPI.GetCurrentProcessId())); 622 result.AddRange(StructToBuffer(KernelAPI.GetCurrentThreadId())); 623 result.AddRange(StructToBuffer(KernelAPI.GetProcessHeap())); 624 625 //The caret and cursor positions 626 UserAPI.POINT point; 627 UserAPI.GetCaretPos(out point); 628 result.AddRange(StructToBuffer(point)); 629 UserAPI.GetCursorPos(out point); 630 result.AddRange(StructToBuffer(point)); 631 632 //Amount of free memory 633 KernelAPI.MEMORYSTATUSEX memoryStatus = new KernelAPI.MEMORYSTATUSEX(); 634 memoryStatus.dwLength = (uint)Marshal.SizeOf(memoryStatus); 635 if (KernelAPI.GlobalMemoryStatusEx(ref memoryStatus)) 636 { 637 result.AddRange(StructToBuffer(memoryStatus.ullAvailPhys)); 638 result.AddRange(StructToBuffer(memoryStatus.ullAvailVirtual)); 639 result.AddRange(StructToBuffer(memoryStatus)); 640 } 641 642 //Thread execution times 643 long creationTime, exitTime, kernelTime, userTime; 644 if (KernelAPI.GetThreadTimes(KernelAPI.GetCurrentThread(), out creationTime, 645 out exitTime, out kernelTime, out userTime)) 646 { 647 result.AddRange(StructToBuffer(creationTime)); 648 result.AddRange(StructToBuffer(kernelTime)); 649 result.AddRange(StructToBuffer(userTime)); 650 } 651 652 //Process execution times 653 if (KernelAPI.GetProcessTimes(KernelAPI.GetCurrentProcess(), out creationTime, 654 out exitTime, out kernelTime, out userTime)) 655 { 656 result.AddRange(StructToBuffer(creationTime)); 657 result.AddRange(StructToBuffer(kernelTime)); 658 result.AddRange(StructToBuffer(userTime)); 659 } 660 661 //Current system time 662 result.AddRange(StructToBuffer(DateTime.Now.Ticks)); 663 664 //The high resolution performance counter 665 long perfCount = 0; 666 if (KernelAPI.QueryPerformanceCounter(out perfCount)) 667 result.AddRange(StructToBuffer(perfCount)); 668 669 //Ticks since start up 670 uint tickCount = KernelAPI.GetTickCount(); 671 if (tickCount != 0) 672 result.AddRange(StructToBuffer(tickCount)); 673 674 //CryptGenRandom 675 byte[] cryptGenRandom = new byte[160]; 676 if (CryptAPI.CryptGenRandom(cryptGenRandom)) 677 result.AddRange(cryptGenRandom); 678 679 return result.ToArray(); 680 } 681 682 /// <summary> 683 /// Retrieves entropy from sources which are relatively slower than those from 684 /// the FastAddEntropy function. 685 /// </summary> 686 private byte[] GetSlowEntropy() 687 { 688 List<byte> result = new List<byte>(); 689 690 //NetAPI statistics 691 unsafe 692 { 693 IntPtr netAPIStats = IntPtr.Zero; 694 if (NetAPI.NetStatisticsGet(null, NetAPI.SERVICE_WORKSTATION, 695 0, 0, out netAPIStats) == 0) 696 { 697 try 698 { 699 //Get the size of the buffer 700 uint size = 0; 701 NetAPI.NetApiBufferSize(netAPIStats, out size); 702 byte[] entropy = new byte[size]; 703 704 //Copy the buffer 705 Marshal.Copy(entropy, 0, netAPIStats, entropy.Length); 706 707 //And add it to the pool 708 result.AddRange(entropy); 709 } 710 finally 711 { 712 //Free the statistics buffer 713 NetAPI.NetApiBufferFree(netAPIStats); 714 } 715 } 716 } 717 718 #if false 719 //Get disk I/O statistics for all the hard drives 720 for (int drive = 0; ; ++drive) 721 { 722 //Try to open the drive. 723 using (SafeFileHandle hDevice = File.CreateFile( 724 string.Format("\\\\.\\PhysicalDrive%d", drive), 0, 725 File.FILE_SHARE_READ | File.FILE_SHARE_WRITE, IntPtr.Zero, 726 File.OPEN_EXISTING, 0, IntPtr.Zero)) 727 { 728 if (hDevice.IsInvalid) 729 break; 730 731 //This only works if the user has turned on the disk performance 732 //counters with 'diskperf -y'. These counters are off by default 733 if (File.DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, 734 &diskPerformance, sizeof(DISK_PERFORMANCE), &uSize, NULL)) 735 { 736 addEntropy(&diskPerformance, uSize); 737 } 738 } 739 } 740 #endif 741 742 /* 743 Query performance data. Because the Win32 version of this API (through 744 registry) may be buggy, use the NT Native API instead. 745 746 Scan the first 64 possible information types (we don't bother 747 with increasing the buffer size as we do with the Win32 748 version of the performance data read, we may miss a few classes 749 but it's no big deal). In addition the returned size value for 750 some classes is wrong (eg 23 and 24 return a size of 0) so we 751 miss a few more things, but again it's no big deal. This scan 752 typically yields around 20 pieces of data, there's nothing in 753 the range 65...128 so chances are there won't be anything above 754 there either. 755 */ 756 uint dataWritten = 0; 757 byte[] infoBuffer = new byte[65536]; 758 uint totalEntropy = 0; 759 for (uint infoType = 0; infoType < 64; ++infoType) 760 { 761 uint sysInfo = NTAPI.NtQuerySystemInformation(infoType, infoBuffer, 762 (uint)infoBuffer.Length, out dataWritten); 763 764 if (sysInfo == 0 /*ERROR_SUCCESS*/ && dataWritten > 0) 765 { 766 byte[] entropy = new byte[dataWritten]; 767 Buffer.BlockCopy(infoBuffer, 0, entropy, 0, (int)dataWritten); 768 result.AddRange(entropy); 769 totalEntropy += dataWritten; 770 } 771 } 772 773 result.AddRange(StructToBuffer(totalEntropy)); 774 775 //Finally, our good friend CryptGenRandom() 776 byte[] cryptGenRandom = new byte[1536]; 777 if (CryptAPI.CryptGenRandom(cryptGenRandom)) 778 result.AddRange(cryptGenRandom); 779 780 return result.ToArray(); 781 } 722 782 } 723 783 }
Note: See TracChangeset
for help on using the changeset viewer.
