Замена PRNG на внешний источник случайности

В библиотеке SJCL (Stanford Javascript Crypto Library) генератор псевдослучайных чисел (PRNG) является критически важным компонентом, поскольку от качества энтропии зависит стойкость всех криптографических операций: генерации ключей, nonce, salt и других случайных значений.

По умолчанию SJCL использует собственный PRNG, основанный на состоянии внутреннего генератора и накоплении энтропии из браузерных событий. Однако в ряде архитектур требуется замена или усиление источника случайности внешними системами: аппаратными генераторами, серверной энтропией, WebCrypto API или специализированными криптографическими сервисами.

В основе лежит объект sjcl.random, который управляет:

  • пулом энтропии (entropy pool)
  • сбором случайных событий
  • состоянием генератора
  • выдачей байтов через randomWords()

Ключевой момент: SJCL не полагается на один источник, а смешивает данные из разных источников в общий пул.

sjcl.random.addEntropy(data, strength, source)
  • data — числовое значение или массив
  • strength — оценка количества бит энтропии
  • source — идентификатор источника

Причины замены PRNG на внешний источник

Замена или расширение PRNG требуется в следующих случаях:

  • серверное окружение без пользовательских событий (Node.js)
  • необходимость FIPS-совместимой генерации
  • использование аппаратных модулей HSM
  • централизованный криптосервис генерации ключей
  • аудитируемая криптографическая система
  • снижение зависимости от браузерной модели энтропии

В таких системах стандартный браузерный шум (mouse move, keyboard events) отсутствует или недостаточен.

Базовая стратегия интеграции внешней энтропии

SJCL не предоставляет прямого механизма «замены PRNG», но позволяет полностью переопределить источник энтропии через добавление высококачественных внешних данных.

Основной метод:

sjcl.random.addEntropy(externalBytes, 256, "external-source");

Где externalBytes должен представлять криптографически стойкий набор данных.

Использование WebCrypto как внешнего источника

В современных браузерах стандартным источником является:

crypto.getRandomValues(new Uint32Array(n))

Интеграция в SJCL:

function injectWebCryptoEntropy() {
    const arr = new Uint32Array(32);
    crypto.getRandomValues(arr);

    sjcl.random.addEntropy(arr, 1024, "webcrypto");
}

Особенность: WebCrypto уже считается CSPRNG, поэтому его энтропия может рассматриваться как максимально надёжная.

Замена PRNG через внешнюю инициализацию пула

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

sjcl.random.startCollectors = function () {
    // отключение стандартных сборщиков
};

Далее система полностью переходит на внешнее наполнение:

async function initEntropyFromServer() {
    const response = await fetch("/api/random");
    const data = await response.arrayBuffer();

    sjcl.random.addEntropy(new Uint8Array(data), 2048, "server");
}

Серверный генератор энтропии

На сервере часто используется комбинация источников:

  • /dev/urandom
  • аппаратный RNG
  • криптографические сервисы HSM
  • комбинация системных шумов

Пример API-ответа:

{
  "random": "base64-encoded-bytes"
}

Клиентская обработка:

function base64ToBytes(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);

    for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
    }

    return bytes;
}

Инжекция:

const entropy = base64ToBytes(response.random);
sjcl.random.addEntropy(entropy, entropy.length * 8, "server-api");

Полное отключение браузерных источников

В высокозащищённых системах стандартные источники событий могут считаться недостоверными или непредсказуемыми по качеству.

Отключение:

sjcl.random.events = {};
sjcl.random.isReady = function () {
    return true;
};

Далее генерация становится строго управляемой внешними данными.

Использование аппаратных источников (HSM / TPM)

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

  • HSM (Hardware Security Module)
  • TPM (Trusted Platform Module)

Доступ осуществляется через backend:

async function getHSMEntropy() {
    const res = await fetch("/hsm/random");
    const buffer = await res.arrayBuffer();

    sjcl.random.addEntropy(new Uint8Array(buffer), 4096, "hsm");
}

Особенность таких источников — сертифицированная криптостойкость.

Контроль качества энтропии

SJCL использует оценку качества через параметр strength. При работе с внешними источниками важно корректно задавать уровень:

  • WebCrypto: ~256–1024 бит на вызов
  • HSM: 4096+ бит
  • серверный /dev/urandom: зависит от реализации
  • пользовательские источники: требуют осторожной оценки

Пример комбинирования:

sjcl.random.addEntropy(webEntropy, 1024, "webcrypto");
sjcl.random.addEntropy(serverEntropy, 2048, "server");
sjcl.random.addEntropy(hsmEntropy, 4096, "hsm");

Форсирование готовности генератора

SJCL блокирует выдачу случайных чисел, если считает энтропию недостаточной. При внешнем управлении можно контролировать готовность:

while (!sjcl.random.isReady()) {
    await injectEntropy();
}

После этого становится доступно:

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

Полная замена логики PRNG через внешний CSPRNG

В крайних архитектурах PRNG SJCL фактически не используется как генератор, а превращается в интерфейс хранения энтропии.

Реализация внешнего генератора:

async function externalRandomWords(n) {
    const res = await fetch(`/api/rng?n=${n}`);
    const buffer = await res.arrayBuffer();
    const view = new Uint32Array(buffer);

    return Array.from(view);
}

И синхронизация с SJCL:

function syncExternalToSJCL(n) {
    externalRandomWords(n).then(words => {
        sjcl.random.addEntropy(words, words.length * 32, "external-csprng");
    });
}

Смешивание источников и защита от компрометации

Безопасная практика заключается в обязательном смешивании нескольких источников:

  • браузерный WebCrypto
  • серверная энтропия
  • аппаратный RNG
  • локальные события (если доступны)

Это снижает риск полного компрометации одного источника.

function mixEntropy(sources) {
    sources.forEach(src => {
        sjcl.random.addEntropy(src.data, src.strength, src.name);
    });
}

Особенности состояния внутреннего пула

SJCL использует внутренний пул состояний, который:

  • аккумулирует энтропию
  • перемешивается при достижении порога
  • обновляет состояние PRNG

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

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

Корректная архитектура внешней замены обычно выглядит так:

  1. отключение браузерных событий
  2. инициализация WebCrypto
  3. загрузка серверной энтропии
  4. добавление аппаратного источника (если есть)
  5. проверка готовности sjcl.random.isReady()
  6. только затем генерация данных

Эта схема обеспечивает предсказуемость и криптографическую стойкость генерации без зависимости от внутренних механизмов браузера