Как работает CSPRNG под капотом

CSPRNG (Cryptographically Secure Pseudo-Random Number Generator) в криптографических библиотеках вроде TweetNaCl.js играет ключевую роль: от качества случайности зависит стойкость ключей, одноразовых чисел (nonce), соли и всех криптографических примитивов.

В основе любого CSPRNG лежит не математическая «случайность» в классическом смысле, а физическая энтропия операционной системы. Современные ОС собирают шум из множества источников:

  • тайминги прерываний и системных вызовов
  • дрожание CPU и кэш-промахи
  • ввод с клавиатуры и мыши
  • сетевые события
  • аппаратные источники (RDRAND, TPM, secure enclave)

Эти данные поступают в системный пул энтропии, где проходят смешивание (whitening), чтобы устранить статистические зависимости.

Далее формируется состояние генератора, из которого уже выводятся криптографически стойкие последовательности.


Как браузер и Node.js получают случайность

TweetNaCl.js не реализует собственный генератор случайных чисел — он опирается на платформенные CSPRNG.

В браузере

Основной механизм:

  • crypto.getRandomValues()

Он работает поверх Web Crypto API и обращается напрямую к CSPRNG операционной системы. Внутри браузера это обычно:

  • Windows: BCryptGenRandom / CNG
  • Linux: getrandom() / /dev/urandom
  • macOS: SecRandomCopyBytes

Важно: этот интерфейс синхронный и предназначен специально для криптографии.

В Node.js

Используется:

  • require('crypto').randomBytes()

Внутри Node.js это также обёртка над системным CSPRNG (OpenSSL + системные вызовы ОС).


Как TweetNaCl.js использует CSPRNG

В библиотеке NaCl (и её JavaScript-реализации TweetNaCl.js) случайные значения требуются в нескольких местах:

  • генерация ключевых пар
  • создание nonce (одноразовых векторов)
  • соль для KDF (key derivation functions)
  • внутренние криптографические операции протоколов

Типичный вызов:

const nacl = require('tweetnacl');

const keyPair = nacl.box.keyPair();

Внутри keyPair() вызывается генерация 32 байт случайных данных через:

  • nacl.randomBytes(n)

А уже эта функция делегирует вызов платформенному CSPRNG.


Почему Math.random() непригоден

В JavaScript существует встроенный генератор:

Math.random()

Он:

  • не криптографический
  • детерминированный (PRNG с внутренним состоянием)
  • может быть предсказан при частичной компрометации состояния
  • оптимизирован для скорости, а не для безопасности

CSPRNG отличается принципиально:

  • выход неотличим от истинной случайности
  • невозможно восстановить состояние по выходу
  • стойкость к «next-bit prediction»

Внутренний принцип работы CSPRNG

Большинство современных CSPRNG строятся по схеме DRBG (Deterministic Random Bit Generator), описанной в NIST SP 800-90A.

Обобщённая модель выглядит так:

  1. Получение энтропии из системы
  2. Инициализация внутреннего состояния (seed)
  3. Генерация псевдослучайного потока
  4. Периодический reseed (добавление новой энтропии)
  5. Обновление состояния после каждого вывода

Упрощённая модель:

state₀ = seed(entropy)

stateₙ₊₁ = f(stateₙ, counter, key)

outputₙ = g(stateₙ)

Функции f и g построены на криптографических примитивах:

  • AES-CTR DRBG (AES в режиме счётчика)
  • HMAC-DRBG (на основе HMAC-SHA256)
  • ChaCha20-based RNG (в современных системах)

Почему криптографическая стойкость не равна «хаотичности»

Важно различать:

  • статистическую случайность (равномерность распределения)
  • криптографическую стойкость (непредсказуемость)

CSPRNG гарантирует:

  • даже при известной части выходных данных невозможно восстановить будущие значения
  • прошлые значения не восстанавливаются (forward secrecy в генерации)
  • отсутствие коротких циклов и корреляций

Роль reseed и защита состояния

Один из ключевых элементов CSPRNG — периодическое обновление состояния.

Если злоумышленник получает доступ к состоянию генератора в момент времени T, без reseed он может:

  • восстановить все будущие значения

Поэтому современные CSPRNG:

  • регулярно смешивают новое системное entropy
  • используют внутренние счётчики и nonce
  • применяют односторонние функции (hash/HMAC)

Применение в TweetNaCl.js: nonce и ключи

В криптографических протоколах NaCl особую роль играют nonce.

Например:

const nonce = nacl.randomBytes(24);

Здесь важно:

  • nonce должен быть уникальным
  • повтор nonce с тем же ключом полностью ломает безопасность (особенно в stream cipher конструкциях)

CSPRNG обеспечивает:

  • отсутствие повторов (с вероятностной точки зрения)
  • непредсказуемость значения

Почему нельзя «самому сделать рандом»

Типичная ошибка — попытка создать псевдослучайный генератор вручную:

let seed = Date.now();

function badRandom() {
  seed = (seed * 9301 + 49297) % 233280;
  return seed / 233280;
}

Проблема:

  • линейная структура легко восстанавливается
  • предсказуемость после нескольких наблюдений
  • отсутствие внешней энтропии

CSPRNG решает это фундаментально: внутреннее состояние не раскрывается через выход.


Связь с криптографической безопасностью NaCl

TweetNaCl.js построен на принципе:

  • минимальная поверхность атаки
  • использование проверенных примитивов
  • отказ от собственной реализации сложных компонентов, включая RNG

Поэтому генерация случайности делегируется:

  • браузеру
  • операционной системе
  • криптографическим API платформы

Это снижает риск:

  • ошибок в реализации DRBG
  • утечек состояния
  • слабых сидов

Что происходит при вызове crypto.getRandomValues

На уровне системы:

  1. Запрос из JavaScript через WebCrypto
  2. Переход в браузерный C++ слой
  3. Вызов OS CSPRNG
  4. Извлечение байтов из системного пула
  5. Возврат буфера в JS ArrayBuffer

Важный момент: JS-код не имеет доступа к внутреннему состоянию генератора.


Практическая модель безопасности

CSPRNG считается безопасным, если выполняются условия:

  • отсутствие корреляции между выходами
  • невозможность отличить от истинной случайности за полиномиальное время
  • стойкость к partial state exposure (в пределах модели)

TweetNaCl.js полагается на эти свойства как на базовую предпосылку всей криптосистемы.


Итоговая архитектурная роль CSPRNG в стекe NaCl

В контексте криптографической системы:

  • CSPRNG = фундамент доверия к ключам
  • OS entropy pool = источник неопределённости
  • WebCrypto / Node crypto = безопасный интерфейс
  • TweetNaCl.js = потребитель случайных байтов, не реализующий генератор

Любая деградация CSPRNG автоматически приводит к компрометации:

  • ключей
  • шифрования
  • подписей
  • протоколов обмена сообщениями