Entropy pool и накопление энтропии

В основе генерации криптографически стойких случайных чисел в Stanford Javascript Crypto Library лежит механизм накопления энтропии. Он реализован через систему энтропийных пулов, которые постепенно заполняются данными из различных источников неопределённости среды исполнения JavaScript.

Архитектура энтропийных пулов

В SJCL используется несколько параллельных пулов энтропии, обычно 32. Такая структура заимствована из подходов криптографической библиотеки Fortuna.

Каждый пул представляет собой:

  • массив чисел (обычно 32-битных слов)
  • накопитель энтропии без немедленной компрессии
  • независимую единицу смешивания данных

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

Пулам назначается индекс, зависящий от количества уже добавленных энтропийных событий. Обычно используется следующий принцип распределения:

  • первое событие → pool 0
  • второе событие → pool 1
  • тридцать второе → pool 31
  • тридцать третье → снова pool 0

Такое распределение обеспечивает постепенное смешивание информации с разной «глубиной истории».


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

SJCL не полагается на единственный источник случайности. Вместо этого библиотека агрегирует данные из множества каналов среды браузера или Node.js.

Типичные источники:

1. Пользовательские события

Наиболее важный источник в браузере:

  • движения мыши
  • клики
  • прокрутка
  • нажатия клавиш
  • касания на мобильных устройствах

Каждое событие преобразуется в набор чисел:

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

2. Тайминговые различия

Микроскопические различия во времени исполнения:

  • интервалы между событиями
  • jitter планировщика событий JavaScript
  • различия между вызовами requestAnimationFrame

3. Системные параметры

Менее надёжные, но используемые как дополнительный шум:

  • разрешение экрана
  • языковые настройки
  • параметры браузера
  • список шрифтов (косвенно через измерения)

4. Встроенные криптографические источники

Если доступны:

  • window.crypto.getRandomValues
  • crypto API в Node.js

Они могут использоваться как усилитель энтропии или первичный источник в окружениях с низкой интерактивностью.


Механизм добавления энтропии

Основной интерфейс накопления данных реализован через функцию:

sjcl.random.addEntropy(data, estimatedEntropyBits, source)

Параметры:

  • data — числовой массив или одиночное значение
  • estimatedEntropyBits — оценка количества случайных бит
  • source — идентификатор источника

Каждое добавление проходит несколько этапов:

1. Оценка энтропии

Система не может точно измерить случайность, поэтому используется эвристика. Например:

  • движение мыши → 0.5–2 бита на событие
  • нажатие клавиши → 1–3 бита
  • таймер → значительно меньше

Эти оценки консервативны, чтобы избежать переоценки качества данных.

2. Распределение по пулам

Данные распределяются циклически:

  • пул выбирается по глобальному счётчику событий
  • счётчик увеличивается при каждом добавлении

3. Смешивание внутри пула

Каждое значение добавляется в массив пула, но не используется сразу. Это предотвращает локальные предсказуемые зависимости.


Коллекторы энтропии

SJCL использует автоматические сборщики (entropy collectors), которые подключаются к DOM или runtime.

Типичная инициализация включает:

  • обработчики mousemove
  • mousedown, mouseup
  • keydown, keyup
  • touchstart, touchmove

Каждый обработчик извлекает «сырые» данные и передаёт их в систему энтропии.

Пример логики:

function mouseEvent(e) {
    var x = e.clientX;
    var y = e.clientY;
    var t = (new Date()).getTime();

    sjcl.random.addEntropy([x, y, t], 2, "mouse");
}

Важно, что SJCL не хранит полную историю событий — только агрегированную информацию.


Оценка готовности генератора

Состояние системы энтропии определяется через:

sjcl.random.isReady(threshold)

Где threshold — требуемое количество бит.

Система проходит несколько уровней:

  • 0 бит — не готова
  • 128 бит — минимально допустимо
  • 192+ бит — криптографически безопасно для большинства задач

Также используется:

sjcl.random.getProgress(threshold)

возвращающая долю накопленной энтропии.


Процесс сидирования генератора

Когда накоплено достаточное количество энтропии, происходит инициализация PRNG.

Внутренне SJCL использует Fortuna-подобный механизм:

  • несколько пулов хэшируются независимо
  • результаты комбинируются
  • формируется seed для AES-based генератора

Процесс происходит лениво:

  • генератор не пересеивается при каждом событии
  • пересидирование выполняется периодически или при запросе случайных чисел

Генерация случайных чисел

После инициализации используется функция:

sjcl.random.randomWords(n, paranoia)
  • n — количество 32-битных слов
  • paranoia — уровень требуемой безопасности

Если энтропии недостаточно, функция может:

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

Уровни паранойи (paranoia levels)

SJCL вводит концепцию уровней безопасности:

  • 0 — допустимы слабые источники
  • 1–2 — стандартная криптография
  • 3+ — максимальная безопасность, требующая значительной энтропии

Чем выше уровень, тем больше требуется накопленных бит перед генерацией.


Защита от атак на энтропию

Основные угрозы:

1. Предсказуемые события пользователя

Если злоумышленник знает поведение пользователя, он может частично восстановить входные данные. Поэтому SJCL:

  • комбинирует множество источников
  • не полагается на один тип событий

2. Ранняя инициализация

Если генератор используется до накопления энтропии, результат может быть слабым. Для этого применяется:

  • блокировка до достижения порога
  • проверка readiness перед генерацией

3. Переиспользование состояния

Fortuna-подобная архитектура предотвращает повторное использование одних и тех же seed-значений без пересидирования.


Поведение в средах без пользовательского ввода

В серверных средах или headless-окружениях отсутствуют мышь и клавиатура. В этом случае SJCL использует альтернативные источники:

  • системный PRNG (если доступен)
  • таймеры высокого разрешения
  • внешние seed-значения, переданные вручную

При этом энтропийный пул заполняется медленнее и может требовать явной инициализации.


Внутреннее устройство пулов

Каждый пул представляет собой:

  • массив чисел фиксированной длины
  • хешируемую структуру перед извлечением seed

Перед использованием данные проходят через криптографическую функцию (обычно SHA-256 или аналог в SJCL), что обеспечивает:

  • диффузию данных
  • невозможность восстановления входных событий
  • устойчивость к частичным утечкам

Роль накопления энтропии в безопасности SJCL

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

  • стойкость ключей шифрования
  • безопасность IV и nonce
  • устойчивость протоколов (например, AES-GCM)

Недостаточная энтропия приводит к:

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

Поведение при переполнении и перераспределении

Пулам не требуется очистка в классическом смысле. Вместо этого используется:

  • циклическое распределение событий
  • периодическое пересидирование PRNG
  • хэширование накопленных данных

Это предотвращает:

  • переполнение памяти
  • деградацию качества энтропии со временем

Взаимодействие с Math.random

В некоторых конфигурациях SJCL может заменять стандартный Math.random:

  • через обёртку безопасного генератора
  • либо полностью отключая встроенный PRNG браузера

Это обеспечивает единообразное поведение генерации случайных чисел во всей системе.


Итоговая логика работы энтропийного контура

Вся система может быть представлена как поток:

  • сбор событий среды
  • преобразование в числовые данные
  • распределение по пулам
  • накопление статистической неопределённости
  • криптографическое хеширование
  • формирование seed
  • запуск PRNG
  • генерация безопасных случайных чисел