Featured image of post Как создать «случайность» по заданному алгоритму?: Наука и криптография непредсказуемости

Как создать «случайность» по заданному алгоритму?: Наука и криптография непредсказуемости

Как детерминированные компьютеры генерируют случайные числа и почему наблюдение всего 624 выходов вихря Мерсенна (MT19937) позволяет на 100% предсказать все будущие и прошлые числа? Полное руководство: ГПСЧ против ГСЧ, линейные конгруэнтные генераторы (LCG) и катастрофа 15 плоскостей IBM RANDU, MT19937, PCG64, Xoshiro256++, теорема Яо, криптостойкие КГПСЧ (ChaCha20, AES-CTR_DRBG, бэкдор АНБ Dual_EC_DRBG), тепловой шум, мин-энтропия и уязвимость Debian OpenSSL.

Введение: «Состояние греха» фон Неймана и парадокс детерминированной случайности

В 1951 году Джон фон Нейман произнёс фразу, навсегда вошедшую в историю информатики: «Всякий, кто рассматривает арифметические методы получения случайных цифр, разумеется, пребывает в состоянии греха (in a state of sin)». Цифровой компьютер — это строго детерминированный конечный автомат: при заданном начальном состоянии и инструкции каждый следующий бит предопределён на 100%. Тем не менее от моделирования методом Монте-Карло в ядерной физике и процедурной генерации миров в играх до ключей TLS, SSH и подписей ECDSA современная цивилизация потребляет триллионы случайных чисел каждую секунду.

Как жёсткий алгоритм может порождать непредсказуемость? И почему статистически безупречный генератор вроде вихря Мерсенна (MT19937) оказывается катастрофически уязвимым при генерации токенов сброса пароля, позволяя атакующему по 624 последовательным выходам на 100% предсказать все будущие и прошлые значения?


Глава 1: Математическая анатомия ГПСЧ ── Зерно (Seed), конечное пространство состояний и принцип Дирихле

Формально генератор псевдослучайных чисел (ГПСЧ / PRNG) — это детерминированный автомат \((\mathcal{S}, \mathcal{O}, f, g, s_0)\) с конечным внутренним пространством состояний \(\mathcal{S}\) размера \(|\mathcal{S}| = 2^b\), начальным зерном (Seed) \(s_0 \in \mathcal{S}\), функцией перехода \(f: \mathcal{S} \to \mathcal{S}\) и функцией вывода \(g: \mathcal{S} \to \mathcal{O}\):

\[s_{i+1} = f(s_i), \qquad r_i = g(s_i) \qquad \left(|\mathcal{S}| = 2^b \implies \text{Period } P \le 2^b\right)\]

Как только зерно \(s_0\) зафиксировано, вся последовательность \(r_0, r_1, r_2, \dots\) полностью предопределена. Эта воспроизводимость бесценна при отладке научных расчётов. Однако по принципу Дирихле (Pigeonhole Principle) автомат неизбежно повторит одно из предыдущих состояний \(s_{k+P} = s_k\) не более чем через \(2^b\) шагов, замкнувшись в периодический цикл периода \(P \le 2^b\).

  flowchart LR
    S0["Начальное зерно s0"] --> S1["Состояние s1 = f(s0)"]
    S1 --> S2["Состояние s2 = f(s1)"]
    S2 -. "Предпериод (Tail)" .-> Sk["Вход в цикл sk"]
    Sk --> Sk1["Состояние s(k+1)"]
    Sk1 -. "Периодический цикл P <= 2^b" .-> Skm1["Состояние s(k+P-1)"]
    Skm1 -- "f(s(k+P-1)) = sk" --> Sk

Предложенный фон Нейманом в 1949 году метод серединных квадратов (Middle-Square Method) (возведение \(2k\)-значного числа в квадрат и извлечение средних \(2k\) цифр) без алгебраической структуры быстро вырождается в 0000 или короткие циклы длины 4.


Глава 2: Линейные конгруэнтные генераторы (LCG) и теорема Марсальи ── Трагедия 15 плоскостей IBM RANDU

В 1949 году Д. Г. Лемер предложил линейный конгруэнтный генератор (LCG): \(X_{n+1} = (a X_n + c) \bmod m\). Согласно теореме Халла — Добелла (1962), LCG достигает полного периода \(P = m\) тогда и только тогда, когда: (1) \(\gcd(c, m) = 1\), (2) каждый простой делитель \(p\) числа \(m\) делит \(a-1\), и (3) если \(4 \mid m\), то \(4 \mid (a-1)\).

В 1968 году статистик Джордж Марсалья (George Marsaglia) опубликовал знаменитую статью «Random numbers fall mainly in the planes», доказав, что \(d\)-мерные точки \((X_n, \dots, X_{n+d-1})\) любого LCG ложатся не более чем на \((d! \cdot m)^{1/d}\) параллельных гиперплоскостей кристаллической решётки!

Самым печально известным примером стал стандартный генератор RANDU корпорации IBM (\(m = 2^{31}, c = 0, a = 65539 = 2^{16} + 3\)) на мейнфреймах System/360:

\[X_{n+1} = 65539 X_n \bmod 2^{31} = (2^{16} + 3) X_n \bmod 2^{31}\]\[X_{n+2} \equiv (2^{16}+3)^2 X_n \equiv \left(\underbrace{2^{32}}_{\equiv 0} + 6\cdot 2^{16} + 9\right) X_n \equiv 6 X_{n+1} - 9 X_n \pmod{2^{31}}\]\[\implies 9 u_n - 6 u_{n+1} + u_{n+2} = k \in \{-5, -4, \dots, 9\} \quad (\text{15 Parallel Planes!})\]

Из-за тождества \(X_{n+2} \equiv 6 X_{n+1} - 9 X_n \pmod{2^{31}}\) все 3D-точки RANDU в единичном кубе оказались заперты всего на 15 параллельных плоскостях с огромными пустыми промежутками, что исказило тысячи расчётов методом Монте-Карло в 1960–70-х годах!


Глава 3: Вихрь Мерсенна (MT19937) и современные статистические ГПСЧ (PCG64, Xoshiro256++)

В 1997 году японские математики Макото Мацумото и Такудзи Нисимура создали вихрь Мерсенна (MT19937) на основе скрученного обобщённого регистра сдвига с обратной связью (TGFSR) над полем \(\mathbb{F}_2 = \{0, 1\}\) (стандарт в Python random, R, MATLAB, C++ std::mt19937):

  1. Гигантский период и 623-мерная равномерность: Массив состояния из \(624\) 32-битных слов (\(19\,937\) активных бит) обеспечивает период, равный простому числу Мерсенна \(P = 2^{19937} - 1 \approx 4{,}315 \times 10^{6001}\), и строгую равномерность распределения вплоть до размерности 623!
  2. Закалка (Tempering): Перед выводом каждое слово состояния \(x\) умножается на обратимую битовую матрицу \(32\times 32\) над \(\mathbb{F}_2\):
\[\begin{aligned} y_1 &= x \oplus ((x \gg 11) \mathbin{\&} \texttt{0xFFFFFFFF}) \\ y_2 &= y_1 \oplus ((y_1 \ll 7) \mathbin{\&} \texttt{0x9D2C5680}) \\ y_3 &= y_2 \oplus ((y_2 \ll 15) \mathbin{\&} \texttt{0xEFC60000}) \\ z &= y_3 \oplus (y_3 \gg 18) \end{aligned}\]

Чтобы преодолеть большой объём состояния MT19937 (\(2{,}5\text{ КБ}\)) и провал двух тестов на \(\mathbb{F}_2\)-линейность в пакете TestU01 (BigCrush), в 2010-х годах были созданы PCG64 (Мелисса О’Нил, 2014: 128-битный LCG с вращением битов, зависящим от данных; стандарт в NumPy) и Xoshiro256++ (Блэкман и Винья, 2018: 256-битное состояние с нелинейным скремблером; используется в Rust и Java), проходящие 100% тестов BigCrush.


Глава 4: Почему статистическое совершенство \(\neq\) криптостойкость ── Взлом MT19937 по 624 выходам и КГПСЧ

4.1 Обращение Tempering: клонирование MT19937 по 624 выходам

Использование MT19937 для генерации токенов сессий или паролей — фатальная ошибка: каждый из четырёх шагов Tempering является обратимой линейной биекцией над \(\mathbb{F}_2^{32}\) за время \(O(1)\)!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def untemper(z: int) -> int:
    # Invert MT19937 32-bit output z back to internal state word x in O(1)
    y3 = z ^ (z >> 18)
    y2 = y3 ^ ((y3 << 15) & 0xEFC60000)
    y1 = y2
    for _ in range(4):
        y1 = y2 ^ ((y1 << 7) & 0x9D2C5680)
    x = y1
    for _ in range(2):
        x = y1 ^ (x >> 11)
    return x & 0xFFFFFFFF

Атакующий, увидевший всего 624 последовательных 32-битных выхода (\(2\,496\text{ байт}\)) сервера на MT19937, применяет untemper(z) к каждому числу и на 100% восстанавливает внутренний массив из 624 слов! С этого момента он может со 100%-ной точностью предсказывать все будущие числа и вычислять прошлые.

4.2 Теорема Эндрю Яо (1982) и криптографически стойкие генераторы (КГПСЧ / CSPRNG)

В криптографии необходим криптографически стойкий ГПСЧ (CSPRNG), удовлетворяющий трём аксиомам:

  1. Непредсказуемость следующего бита \(\iff\) Вычислительная неотличимость (Теорема Яо, 1982 / Блюм — Микали): Никакой полиномиальный алгоритм \(\mathcal{A}\), зная первые \(k\) бит, не может угадать бит \(r_{k+1}\) существенно лучше, чем подбрасыванием монеты (\(1/2\)):
\[\left| \Pr\left[\mathcal{A}(r_1, r_2, \dots, r_k) = r_{k+1}\right] - \frac{1}{2} \right| \le \text{negl}(\lambda)\]
  1. Прямая секретность (Forward Secrecy / Защита от ретроспективного взлома): После каждой выдачи внутренний ключ немедленно перезаписывается односторонней функцией (Fast-Key-Erasure), поэтому даже при утечке текущего состояния невозможно восстановить прошлые ключи.
  2. Обратная секретность (Backward Secrecy / Защита от предсказания): Периодическое подмешивание свежей аппаратной энтропии (Reseeding) восстанавливает безопасность после компрометации.
  flowchart TD
    subgraph Insecure ["Статистический ГПСЧ (LCG / MT19937 / Xoshiro)"]
        StateM["Внутреннее состояние s_i (624 слова)"] -- "Обратимый F2-линейный Tempering" --> OutM["Выход r_i"]
        OutM -- "untemper() клонирует состояние по 624 выходам!" --> StateM
    end
    subgraph Secure ["Криптостойкий КГПСЧ (ChaCha20 / AES-CTR_DRBG)"]
        Entropy["Аппаратные источники энтропии<br/>(Тепловой шум / RDSEED)"] -- "Периодический Reseeding (Backward Secrecy)" --> StateC["256-битный секретный ключ и счётчик"]
        StateC -- "Односторонний шифр (ChaCha20 / AES)<br/>[Обращение требует 2^256 операций!]" --> OutC["Криптостойкий поток r_i"]
        StateC -- "Fast-Key-Erasure Key' = PRF(Key)<br/>[Forward Secrecy!]" --> StateC_Next["Обновлённый ключ Key'"]
    end

Современные CSPRNG строятся на потоковых и блочных шифрах ChaCha20-RNG (ядро Linux, OpenBSD, Rust) и AES-CTR_DRBG (NIST SP 800-90A), либо на теоретико-числовой задаче факторизации в Blum-Blum-Shub (\(x_{n+1} = x_n^2 \bmod M\)). Напротив, стандартизированный NIST в 2006 году генератор на эллиптических кривых Dual_EC_DRBG содержал клептографический бэкдор АНБ (подтверждённый документами Сноудена в 2013 году: знание секретного дискретного логарифма \(d\) в \(P = d \cdot Q\) позволяло по 32 байтам выхода вычислить внутреннее состояние и расшифровать TLS-трафик).

Класс генератораПримеры алгоритмовРазмер состоянияПериод \(P\)Тест BigCrushКриптостойкостьПрименение
Классический LCGRANDU, C rand()32–64 бит\(\le 2^{64}\)Провал (Гиперплоскости)ВзломанУстаревший код
Современный стат. ГПСЧMT19937, PCG64, Xoshiro256++128 бит – 2,5 КБ\(2^{128} \sim 2^{19937}-1\)ПроходитВзломан (624 выхода / Z3)Монте-Карло, симуляции, игры
Криптостойкий (CSPRNG)ChaCha20-RNG, AES-CTR_DRBG256–512 бит\(\ge 2^{256}\)100% ПроходитБезопасен (Forward/Backward)Ключи TLS/SSH, пароли, токены
Физический ГСЧ (TRNG)Тепловой шум, лавинный пробой, генератор (RDSEED)Физический континуумАпериодичен (\(\infty\))После отбеливанияИстинная физическая энтропияИнициализация и Reseeding CSPRNG

Глава 5: Физические генераторы (TRNG), мин-энтропия (\(H_\infty\)) и пулы энтропии ОС

Начальное зерно CSPRNG поступает от аппаратных генераторов истинно случайных чисел (TRNG / HRNG), улавливающих физический хаос: тепловой шум Джонсона — Найквиста в проводниках, квантовый дробовой шум лавинного пробоя стабилитронов и фазовый джиттер кольцевых осцилляторов (инструкции RDSEED/RDRAND в процессорах Intel/AMD). В криптографии (NIST SP 800-90B) непредсказуемость в худшем случае оценивается через мин-энтропию (\(H_\infty\)), а не среднюю энтропию Шеннона \(H_1\):

\[V_{\text{rms}} = \sqrt{4 k_B T R \Delta f}, \qquad H_\infty(X) = -\log_2 \left(\max_{x} P(X = x)\right) \le H_1(X)\]

«Сырой» физический шум проходит отбеливание (Whitening) методом фон Неймана или криптографическими хеш-экстракторами (BLAKE2s в ядре Linux согласно Leftover Hash Lemma) перед подачей в CSPRNG.

В современном Linux (5.6+) /dev/random и /dev/urandom используют один и тот же ядерный CSPRNG на базе ChaCha20; разработчикам следует всегда вызывать getrandom(2) (или Python secrets, JS crypto.getRandomValues(), Rust OsRng/ThreadRng, Go crypto/rand). Катастрофа Debian OpenSSL 2008 года (CVE-2008-0166) — когда из-за закомментированной ради Valgrind строки энтропия всех генерируемых в мире ключей RSA/SSH на два года сжалась до 15-битного идентификатора процесса PID (\(32\,768\) вариантов!) — и ошибка со статическим одноразовым числом \(k\) в ECDSA на Sony PlayStation 3 (2010) напоминают железный закон: стойкость любой криптосистемы никогда не превышает энтропию генератора случайных чисел, создавшего её ключи.

comments powered by Disqus