Проверка достаточности накопленной энтропии

В Stanford JavaScript Crypto Library генерация криптографически стойких случайных значений основана на глобальном генераторе sjcl.random. Его устойчивость напрямую зависит от качества и количества накопленной энтропии. Энтропия здесь понимается как мера непредсказуемости входных данных, используемых для инициализации внутреннего состояния генератора псевдослучайных чисел.

Ключевая особенность модели заключается в том, что генератор не считается готовым к криптографически безопасной работе до достижения определённого порога энтропии. Это защищает от ситуации, когда недостаточно случайные данные приводят к предсказуемым ключам, IV или nonce.

Источники энтропии

sjcl.random в браузерной среде собирает энтропию из событий пользовательской активности и системных данных. Основные источники:

  • движение мыши
  • нажатия клавиш
  • события сенсорного ввода
  • изменения времени между событиями
  • системные таймеры (Date.now, performance.now)
  • дополнительные внешние источники, если они явно подключены через addEntropy

Каждое событие преобразуется в числовую форму и добавляется в пул энтропии с оценкой количества битов случайности.

sjcl.random.addEntropy(data, 32, "mouse");

Во втором аргументе указывается оценка количества бит энтропии, что критически важно: библиотека не вычисляет истинную энтропию, а опирается на доверенную оценку источника.

Модель накопления

Внутреннее состояние sjcl.random представляет собой пул энтропии, который постепенно заполняется поступающими данными. Эти данные не используются напрямую для генерации чисел, а перемешиваются в состоянии генератора.

Каждое добавление энтропии проходит через хеш-функции (обычно SHA-256), что обеспечивает диффузию и предотвращает восстановление исходных событий.

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

Порог готовности и уровни паранойи

Генератор имеет параметр уровня безопасности — paranoia level, который определяет, сколько энтропии необходимо накопить для начала генерации безопасных чисел.

sjcl.random.setDefaultParanoia(10);

Чем выше уровень, тем больше энтропии требуется. Это напрямую влияет на задержку доступности генератора.

Типичная градация:

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

Система не блокирует выполнение кода, но может временно выдавать предупреждение о недостаточной готовности.

Методы проверки: isReady, getProgress

Основной механизм контроля состояния энтропии реализован через метод isReady.

if (sjcl.random.isReady(10)) {
    const bytes = sjcl.random.randomWords(8);
}

Метод принимает уровень паранойи и возвращает:

  • 1 — достаточно энтропии накоплено
  • 0 — энтропия недостаточна
  • отрицательные значения — генератор ещё не инициализирован

Дополнительно используется getProgress, позволяющий оценить процент готовности.

const progress = sjcl.random.getProgress(10);

Результат обычно выражается в диапазоне от 0 до 1, где:

  • 0 — полное отсутствие накопленной энтропии
  • 1 — достаточный уровень для выбранного paranoia level

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

События генератора

sjcl.random поддерживает событийную модель. Это позволяет реагировать на момент достижения достаточного уровня энтропии.

sjcl.random.addEventListener("seeded", function () {
    const key = sjcl.random.randomWords(8);
});

Событие seeded вызывается один раз при переходе состояния в “готово”. Это важный механизм для асинхронных приложений, где генерация ключей не должна выполняться до завершения накопления энтропии.

Дополнительно может использоваться событие изменения состояния, позволяющее отслеживать постепенное заполнение пула.

Практика проверки перед криптографическими операциями

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

Типичный паттерн:

function secureRandomBytes(n) {
    if (!sjcl.random.isReady(10)) {
        throw new Error("Недостаточно энтропии");
    }
    return sjcl.random.randomWords(n);
}

В более устойчивых системах вместо исключения применяется отложенное выполнение:

function waitForEntropy(callback) {
    if (sjcl.random.isReady(10)) {
        callback();
    } else {
        sjcl.random.addEventListener("seeded", callback);
    }
}

Такой подход предотвращает использование слабого состояния генератора.

Динамическая нехватка энтропии в браузерах

В браузерной среде проблема недостатка энтропии особенно заметна при:

  • автоматизированных тестах без пользовательского ввода
  • серверных рендерах (SSR)
  • headless-окружениях
  • раннем старте приложения

В этих условиях пул энтропии может оставаться пустым длительное время. Для компенсации используется:

  • искусственное добавление энтропии через addEntropy
  • использование криптографически стойких источников окружения (если доступны)
  • задержка инициализации криптографических модулей
sjcl.random.addEntropy(Date.now(), 32, "init-time");

Однако такие источники не всегда эквивалентны реальной пользовательской энтропии, и их вклад ограничен.

Ошибки и типичные проблемы

На практике проверка достаточности энтропии часто игнорируется, что приводит к ряду проблем:

  • генерация ключей до полной инициализации пула
  • зависимость от слабых источников случайности
  • отсутствие подписки на событие seeded
  • некорректная оценка paranoia level

Особенно критична ситуация, когда randomWords вызывается сразу после загрузки страницы без проверки состояния:

const key = sjcl.random.randomWords(8);

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

Корректная модель всегда предполагает явную проверку состояния генератора перед использованием его выхода в безопасности-критичных операциях.