Каждый раз, когда браузер открывает защищённое соединение с банковским сайтом, а Windows создаёт новый ключ шифрования для диска, где-то внутри операционной системы происходит незаметная, но критически важная операция - рождение случайного числа. От того, насколько это число действительно непредсказуемо, зависит, сможет ли злоумышленник подобрать сеансовый ключ, вскрыть зашифрованный архив или подделать цифровую подпись. Устройство этого механизма в Windows устроено гораздо сложнее, чем кажется на первый взгляд: система смешивает данные из десятка независимых источников, прогоняет их через криптографический алгоритм и лишь после этого отдаёт программе байты, которые можно назвать случайными.
Зачем криптографии нужны числа, которые нельзя угадать
Обычный генератор случайных чисел, встроенный в язык программирования или библиотеку компилятора, создаётся для игр, симуляций и тестов. Его задача - выдавать числа, которые статистически похожи на случайные и равномерно распределены. Для криптографии этого недостаточно. Ключ шифрования, вектор инициализации, соль для пароля или одноразовый номер сессии должны обладать другим свойством: их невозможно предсказать заранее, даже если атакующий знает алгоритм генерации и видел предыдущие выходные значения.
Разница проявляется в реальных атаках. Если разработчик игры использует слабый генератор для случайного появления врагов, худшее, что произойдёт, - предсказуемый игровой процесс. Если тот же слабый генератор применить для создания ключа TLS-сессии на банковском сайте, злоумышленник, зная алгоритм и хотя бы приблизительное время запуска программы, может восстановить ключ и прочитать зашифрованный трафик. Именно поэтому в Windows для криптографических задач используется не обычный псевдослучайный генератор, а криптографически стойкий - CSPRNG, чьё внутреннее состояние защищено от восстановления по выходным данным.
Путь от нажатия клавиши до случайного байта в системе
Формирование случайного числа в Windows устроено как многоуровневая воронка, куда стекается энтропия из разных источников, а затем перемешивается и уплотняется криптографическим алгоритмом. Источники энтропии, которые собирает система, различаются по надёжности и частоте обновления:
- время прерываний процессора - при каждом аппаратном прерывании система считывает значение счётчика меток времени TSC и записывает его в компактный внутренний буфер, это основной и самый частый источник новой энтропии;
- модуль доверенной платформы TPM - выдаёт около 40 байт случайных данных при загрузке компьютера и порядка 64 байт при каждой переинициализации, но из-за аппаратных ограничений не может делать это чаще одного раза в 40 минут;
- аппаратные инструкции процессора RDRAND и RDSEED - обращение напрямую к встроенному в кристалл источнику случайности, доступному на современных чипах Intel и AMD;
- файл начального значения в реестре системы - хранит промежуточное состояние генератора между перезагрузками, чтобы система не начинала с нуля после каждого включения.
Все эти данные не используются напрямую как ключи. Вместо этого они служат сырьём для внутреннего пула энтропии, который периодически перемешивается и подаётся на вход детерминированного алгоритма, отвечающего уже за равномерность и непредсказуемость итоговой последовательности бит.
Алгоритм CTR_DRBG и стандарт NIST SP800-90 внутри Windows
С выхода Windows Vista с пакетом обновления 1 система использует детерминированный генератор случайных бит на основе блочного шифра AES в режиме счётчика, который в криптографии обозначается аббревиатурой CTR_DRBG. Этот алгоритм описан в стандарте американского Национального института стандартов и технологий NIST SP800-90 и построен на простой идее: у генератора есть внутреннее секретное состояние, оно шифруется алгоритмом AES, а результат шифрования и становится порцией случайных байт для приложения. После каждой выдачи внутреннее состояние обновляется, поэтому знание одной порции выходных данных не позволяет восстановить ни предыдущие, ни следующие значения.
Такой подход решает сразу две задачи. Во-первых, он превращает ограниченный запас настоящей физической энтропии, собранной из прерываний, TPM и аппаратных генераторов, в практически неисчерпаемый поток криптографически стойких байт. Во-вторых, он делает вычислительно нереальной попытку восстановить внутреннее состояние генератора по перехваченным выходным данным, поскольку для этого атакующему пришлось бы обратить операцию шифрования AES без знания ключа.
Обращаться к этому механизму приложения могут через несколько программных интерфейсов, появлявшихся в разное время. Старейший из них - функция CryptGenRandom из библиотеки CryptoAPI, которую Microsoft рекомендовала использовать во всех Win32-программах, где нужна случайность. Сегодня эта функция считается устаревшей, а её место заняла BCryptGenRandom из современной криптографической библиотеки CNG, дополненная внутренними функциями ProcessPrng и SystemPrng для более узких сценариев.
Уязвимость 2007 года и как она изменила архитектуру генератора
История генератора случайных чисел в Windows не была безоблачной. В 2007 году группа исследователей из Еврейского университета в Иерусалиме под руководством Бенни Пинкаса опубликовала работу с анализом реализации CryptGenRandom в Windows 2000. Оказалось, что генератор в этой версии системы работал в пользовательском режиме, а не в режиме ядра, а значит доступ к его внутреннему состоянию можно было получить даже без административных привилегий на заражённой машине. Определив состояние генератора в один момент времени, исследователи научились предсказывать последующие выдаваемые числа - серьёзная проблема для системы, отвечающей за формирование ключей шифрования и цифровых подписей.
Microsoft подтвердила, что схожая проблема присутствует и в Windows XP, но отсутствует в Windows Vista, где генератор уже был перенесён на более защищённую архитектуру. Исправление для Windows XP компания выпустила вместе с третьим пакетом обновлений в середине 2008 года. Этот случай стал наглядной иллюстрацией того, что криптографический генератор недостаточно просто спроектировать по надёжному алгоритму - критически важно, в каком режиме процессора он выполняется, кто может прочитать его внутреннее состояние и насколько изолирован этот процесс от остальной системы.
Аппаратные инструкции RDRAND и RDSEED в процессорах Intel и AMD
Начиная с процессоров архитектуры Ivy Bridge, представленных Intel в 2012 году, в наборе команд x86 появилась инструкция RDRAND, дающая программе прямой доступ к встроенному в кристалл аппаратному генератору случайных чисел. Технология получила внутреннее название Bull Mountain и официальное - Intel Secure Key. Источник случайности здесь физический: пара логических инверторов на кристалле, соединённых так, что теоретически могут бесконечно долго оставаться в одинаковом промежуточном состоянии, но на практике любое тепловое колебание атомов кремния выводит схему в одно из двух устойчивых положений, и именно это непредсказуемое переключение становится источником случайного бита.
Позже, начиная с процессоров архитектуры Broadwell, к RDRAND добавилась инструкция RDSEED, дающая более прямой доступ к необработанному аппаратному источнику случайности, тогда как RDRAND дополнительно пропускает данные через программный генератор CTR_DRBG для сглаживания и ускорения выдачи. AMD добавила поддержку обеих инструкций в 2015 году, а позже аналогичные механизмы появились и в архитектуре ARM. Полученные от процессора данные не подаются напрямую в качестве ключей шифрования, а подмешиваются в общий пул энтропии Windows наравне с данными прерываний и TPM, что снижает риск от возможных недостатков конкретной аппаратной реализации.
Почему разработчики Linux и FreeBSD не доверяют RDRAND полностью
Аппаратная случайность процессора устроена как закрытая система: программист не может заглянуть внутрь кристалла и проверить, что именно происходит на пути от теплового шума транзисторов до итогового бита на выходе инструкции. Эта непрозрачность в 2013 году привела к громкому решению разработчиков FreeBSD - они отказались полагаться на RDRAND и аналогичную инструкцию Padlock как на основной источник случайности для системного генератора, сославшись на невозможность независимо проверить отсутствие скрытых закладок в аппаратной реализации. Похожей позиции придерживается и ядро Linux: инструкции RDRAND и RDSEED там используются, но лишь как один из нескольких источников, подмешиваемых в общий пул, а не как единственный источник истины.
Опасения оказались не только теоретическими. В октябре 2025 года один из инженеров, работающих над серверной инфраструктурой в крупной технологической компании, сообщил разработчикам ядра Linux об ошибке в процессорах AMD с архитектурой Zen 5: инструкция RDSEED примерно в одном случае из десяти неверно сообщала об успешном получении случайных данных и на деле возвращала нулевые байты вместо энтропии. Ядро оперативно получило исправление, отключающее использование RDSEED на затронутых процессорах до выхода обновлённого микрокода. Этот эпизод показывает, что даже спустя более десяти лет после появления аппаратных генераторов случайности индустрия по-прежнему относится к ним как к одному из многих источников, а не как к безусловно надёжному фундаменту всей криптографической системы.
Современные интерфейсы BCryptGenRandom, ProcessPrng и SystemPrng
В актуальных версиях Windows 10 и Windows 11 основным способом получить криптографически стойкие случайные данные остаётся функция BCryptGenRandom из библиотеки CNG. По умолчанию она реализует уже упомянутый алгоритм CTR_DRBG в соответствии со стандартом NIST SP800-90, а вызывать её можно как из пользовательского режима, так и из режима ядра, что снимает проблему, из-за которой в 2007 году пострадала Windows 2000. Наряду с ней в системе существуют внутренние функции ProcessPrng и SystemPrng, обслуживающие более узкие сценарии внутри самой операционной системы и её компонентов.
Старая функция CryptGenRandom по-прежнему присутствует в системе ради совместимости со старым программным обеспечением, но в официальной документации помечена как устаревшая, и новым программам рекомендуется использовать BCryptGenRandom. Для разработчика на практике это означает, что если приложение создаёт ключ шифрования, генерирует токен сессии или формирует соль для хранения пароля, ему стоит обращаться именно к современному интерфейсу CNG, а не полагаться на устаревшие или тем более на обычные библиотечные функции случайных чисел, не предназначенные для криптографии.
Что это значит на практике для разработчиков и пользователей
Понимание внутреннего устройства генератора случайных чисел важно не только для криптографов. Разработчику приложения, который выбирает между стандартной функцией случайных чисел языка программирования и криптографическим API операционной системы, стоит помнить: обычный генератор оптимизирован для скорости и статистической равномерности, а не для устойчивости к предсказанию, и использовать его для ключей, паролей или токенов сессии небезопасно, даже если результат выглядит достаточно хаотичным на глаз. Правильный выбор - явно обращаться к BCryptGenRandom или аналогичным криптографическим интерфейсам, которые уже учитывают всю многоуровневую архитектуру энтропии, описанную выше.
Пользователю операционной системы вся эта цепочка остаётся полностью незаметной: ни при создании пароля, ни при подключении к защищённому сайту не появляется индикатор того, что в этот момент система опрашивает счётчик прерываний, ждёт данных TPM и подмешивает выдачу инструкции RDRAND в алгоритм CTR_DRBG. Именно эта незаметность и есть признак того, что механизм спроектирован правильно: критическая инфраструктура безопасности должна работать в фоне, не требуя от человека никаких действий, но при этом оставаться устойчивой к анализу и попыткам восстановить её внутреннее состояние. История с уязвимостью в Windows 2000 показала, к чему приводит нарушение этого принципа, а переход на архитектуру, основанную на стандарте NIST SP800-90 и защищённую от чтения состояния из пользовательского режима, стал прямым следствием того урока.