|
16 | 16 | #include <md4.h> |
17 | 17 | #include <md5.h> |
18 | 18 | #include <rc4.h> |
| 19 | +#include <aes-ctr.h> |
19 | 20 |
|
20 | 21 | static const unsigned char CRYPT_LMhash_Magic[8] = |
21 | 22 | { 'K', 'G', 'S', '!', '@', '#', '$', '%' }; |
@@ -585,63 +586,6 @@ BOOL WINAPI SystemFunction035(LPCSTR lpszDllFilePath) |
585 | 586 | return TRUE; |
586 | 587 | } |
587 | 588 |
|
588 | | -/****************************************************************************** |
589 | | - * SystemFunction036 (ADVAPI32.@) |
590 | | - * |
591 | | - * MSDN documents this function as RtlGenRandom and declares it in ntsecapi.h |
592 | | - * |
593 | | - * PARAMS |
594 | | - * pbBuffer [O] Pointer to memory to receive random bytes. |
595 | | - * dwLen [I] Number of random bytes to fetch. |
596 | | - * |
597 | | - * RETURNS |
598 | | - * Always TRUE in my tests |
599 | | - */ |
600 | | -BOOLEAN |
601 | | -WINAPI |
602 | | -SystemFunction036(PVOID pbBuffer, ULONG dwLen) |
603 | | -{ |
604 | | - //////////////////////////////////////////////////////////////// |
605 | | - //////////////////// B I G W A R N I N G !!! //////////////// |
606 | | - // This function will output numbers based on the tick count. // |
607 | | - // It will NOT OUTPUT CRYPTOGRAPHIC-SAFE RANDOM NUMBERS !!! // |
608 | | - //////////////////////////////////////////////////////////////// |
609 | | - |
610 | | - DWORD dwSeed; |
611 | | - PBYTE pBuffer; |
612 | | - ULONG uPseudoRandom; |
613 | | - LARGE_INTEGER time; |
614 | | - static ULONG uCounter = 17; |
615 | | - |
616 | | - if(!pbBuffer || !dwLen) |
617 | | - { |
618 | | - /* This function always returns TRUE, even if invalid parameters were passed. (verified under WinXP SP2) */ |
619 | | - return TRUE; |
620 | | - } |
621 | | - |
622 | | - /* Get the first seed from the performance counter */ |
623 | | - QueryPerformanceCounter(&time); |
624 | | - dwSeed = time.LowPart ^ time.HighPart ^ RtlUlongByteSwap(uCounter++); |
625 | | - |
626 | | - /* We will access the buffer bytewise */ |
627 | | - pBuffer = (PBYTE)pbBuffer; |
628 | | - |
629 | | - do |
630 | | - { |
631 | | - /* Use the pseudo random number generator RtlRandom, which outputs a 4-byte value and a new seed */ |
632 | | - uPseudoRandom = RtlRandom(&dwSeed); |
633 | | - |
634 | | - do |
635 | | - { |
636 | | - /* Get each byte from the pseudo random number and store it in the buffer */ |
637 | | - *pBuffer = (BYTE)(uPseudoRandom >> 8 * (dwLen % 3) & 0xFF); |
638 | | - ++pBuffer; |
639 | | - } while(--dwLen % 3); |
640 | | - } while(dwLen); |
641 | | - |
642 | | - return TRUE; |
643 | | -} |
644 | | - |
645 | 589 | HANDLE KsecDeviceHandle; |
646 | 590 |
|
647 | 591 | static |
@@ -729,6 +673,138 @@ KsecDeviceIoControl( |
729 | 673 | return Status; |
730 | 674 | } |
731 | 675 |
|
| 676 | +/* Reseed every 256 KB generated */ |
| 677 | +#define MAX_RNG_RESEED_INTERVAL (256 * 1024) |
| 678 | + |
| 679 | +/* Generate a maximum of 1KB on each iteration */ |
| 680 | +#define MAX_RNG_CHUNK_SIZE 1024 |
| 681 | + |
| 682 | +/* Initialize the counter for instant reseed */ |
| 683 | +static volatile LONG RngBytesGeneratedSinceReseed = MAX_RNG_RESEED_INTERVAL + 1; |
| 684 | + |
| 685 | +static AES_CTR_CTX RngContext; |
| 686 | +static RTL_SRWLOCK RngReseedLock; |
| 687 | + |
| 688 | +_Requires_lock_not_held_(RngReseedLock) |
| 689 | +static |
| 690 | +NTSTATUS |
| 691 | +RngReseed(VOID) |
| 692 | +{ |
| 693 | + BYTE Seed[32]; |
| 694 | + NTSTATUS Status; |
| 695 | + |
| 696 | + /* Acquire the reseed lock exclusively */ |
| 697 | + RtlAcquireSRWLockExclusive(&RngReseedLock); |
| 698 | + |
| 699 | + /* Check if another thread already reseeded the PRNG and at least half is left */ |
| 700 | + if (RngBytesGeneratedSinceReseed < (MAX_RNG_RESEED_INTERVAL / 2)) |
| 701 | + { |
| 702 | + Status = STATUS_SUCCESS; |
| 703 | + goto Exit; |
| 704 | + } |
| 705 | + |
| 706 | + /* Get new seed data from ksecdd */ |
| 707 | + Status = KsecDeviceIoControl(IOCTL_KSEC_RANDOM_FILL_BUFFER, NULL, 0, Seed, sizeof(Seed)); |
| 708 | + if (!NT_SUCCESS(Status)) |
| 709 | + { |
| 710 | + //ERR("Failed to get random data from ksecdd for reseeding the PRNG!\n"); |
| 711 | + goto Exit; |
| 712 | + } |
| 713 | + |
| 714 | + /* Reseed the PRNG */ |
| 715 | + AES_CTR_Init(&RngContext, Seed, sizeof(Seed)); |
| 716 | + |
| 717 | + /* Reset the reseed counter */ |
| 718 | + InterlockedExchange(&RngBytesGeneratedSinceReseed, 0); |
| 719 | + |
| 720 | + Status = STATUS_SUCCESS; |
| 721 | + |
| 722 | +Exit: |
| 723 | + /* Release the reseed lock */ |
| 724 | + RtlReleaseSRWLockExclusive(&RngReseedLock); |
| 725 | + |
| 726 | + /* Erase the seed data */ |
| 727 | + RtlSecureZeroMemory(Seed, sizeof(Seed)); |
| 728 | + |
| 729 | + return Status; |
| 730 | +} |
| 731 | + |
| 732 | +_Requires_shared_lock_held_(RngReseedLock) |
| 733 | +static |
| 734 | +BOOLEAN |
| 735 | +RngGenerateChunk(PVOID Buffer, ULONG Length) |
| 736 | +{ |
| 737 | + ASSERT(Length > 0); |
| 738 | + ASSERT(Length <= MAX_RNG_CHUNK_SIZE); |
| 739 | + |
| 740 | + /* Make sure we don't exceed the maximum number of bytes per reseed */ |
| 741 | + ULONG Count = InterlockedAdd(&RngBytesGeneratedSinceReseed, Length); |
| 742 | + if (Count >= MAX_RNG_RESEED_INTERVAL) |
| 743 | + { |
| 744 | + /* Release the shared lock before acquiring the exclusive lock for reseeding */ |
| 745 | + RtlReleaseSRWLockShared(&RngReseedLock); |
| 746 | + |
| 747 | + /* We have used up the maximum, reseed the RNG */ |
| 748 | + if (!RngReseed()) |
| 749 | + { |
| 750 | + return FALSE; |
| 751 | + } |
| 752 | + |
| 753 | + /* Reacquire the shared lock after reseeding */ |
| 754 | + RtlAcquireSRWLockShared(&RngReseedLock); |
| 755 | + } |
| 756 | + |
| 757 | + /* Generate the random data */ |
| 758 | + AES_CTR_GenRandom(&RngContext, Buffer, Length); |
| 759 | + |
| 760 | + return TRUE; |
| 761 | +} |
| 762 | + |
| 763 | + |
| 764 | +/****************************************************************************** |
| 765 | + * SystemFunction036 (ADVAPI32.@) |
| 766 | + * |
| 767 | + * MSDN documents this function as RtlGenRandom and declares it in ntsecapi.h |
| 768 | + * |
| 769 | + * PARAMS |
| 770 | + * pbBuffer [O] Pointer to memory to receive random bytes. |
| 771 | + * dwLen [I] Number of random bytes to fetch. |
| 772 | + * |
| 773 | + * RETURNS |
| 774 | + * Always TRUE in my tests |
| 775 | + */ |
| 776 | +BOOLEAN |
| 777 | +WINAPI |
| 778 | +SystemFunction036(PVOID pvBuffer, ULONG dwLen) |
| 779 | +{ |
| 780 | + if ((pvBuffer == NULL) || (dwLen == 0)) |
| 781 | + { |
| 782 | + return FALSE; |
| 783 | + } |
| 784 | + |
| 785 | + /* Acquire the reseed lock shared, so we can safely access the RNG state */ |
| 786 | + RtlAcquireSRWLockShared(&RngReseedLock); |
| 787 | + |
| 788 | + /* Generate chunks of bytes until the buffer is filled */ |
| 789 | + ULONG cjRemaining = dwLen; |
| 790 | + while (cjRemaining > 0) |
| 791 | + { |
| 792 | + ULONG cjChunkSize = min(cjRemaining, MAX_RNG_CHUNK_SIZE); |
| 793 | + if (!RngGenerateChunk(pvBuffer, cjChunkSize)) |
| 794 | + { |
| 795 | + return FALSE; |
| 796 | + } |
| 797 | + |
| 798 | + pvBuffer = (PBYTE)pvBuffer + cjChunkSize; |
| 799 | + cjRemaining -= cjChunkSize; |
| 800 | + } |
| 801 | + |
| 802 | + /* Release the shared lock */ |
| 803 | + RtlReleaseSRWLockShared(&RngReseedLock); |
| 804 | + |
| 805 | + return TRUE; |
| 806 | +} |
| 807 | + |
732 | 808 | /* |
733 | 809 | These functions have nearly identical prototypes to CryptProtectMemory and CryptUnprotectMemory, |
734 | 810 | in crypt32.dll. |
|
0 commit comments