Атаки на реализацию ECDSA при слабом RNG

Слабый генератор случайных чисел в контексте ECDSA приводит к полной компрометации приватного ключа даже при корректной математической реализации алгоритма. В криптографии подпись ECDSA считается безопасной только при строгом выполнении одного критического условия — одноразовости и криптографической стойкости nonce k, используемого при генерации каждой подписи.


ECDSA формируется вокруг параметров эллиптической кривой с порядком группы n и приватного ключа d. Для подписи сообщения вычисляется хэш z и выбирается случайное значение k, после чего:

  • вычисляется точка R = k * G
  • r = R.x mod n
  • s = k⁻¹ (z + r·d) mod n

Подпись — это пара (r, s).

Ключевая уязвимость заключается в том, что значение k должно быть:

  • непредсказуемым
  • уникальным для каждой подписи
  • равномерно распределённым в диапазоне [1, n-1]

Любое отклонение разрушает безопасность схемы.


Последствия слабого RNG

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

Повтор nonce

Если два разных сообщения подписаны с одинаковым k, получаем:

  • s1 = k⁻¹ (z1 + r·d)
  • s2 = k⁻¹ (z2 + r·d)

Вычитая уравнения, можно выразить k, а затем восстановить приватный ключ:

d = (s1·z2 - s2·z1) / (r·(s2 - s1)) mod n

Это полностью разрушает ключевую пару.


SJCL и генерация случайности

В Stanford JavaScript Crypto Library (SJCL) используется модуль:

  • sjcl.random

Он строится на сборе энтропии из:

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

Пример инициализации:

sjcl.random.startCollectors();

И использование:

const k = sjcl.bn.random(n);

Проблема возникает, когда:

  • энтропия ещё не накоплена
  • приложение принудительно вызывает генерацию
  • браузерная среда ограничена (headless, embedded WebView)
  • используется ранний вызов encrypt/sign без прогрева RNG

Типовые ошибки интеграции SJCL

1. Использование до накопления энтропии

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

Если random ещё не собрал достаточный entropy pool, результат может быть предсказуемым.


2. Отсутствие блокировки генерации

SJCL позволяет проверку:

if (!sjcl.random.isReady()) {
    sjcl.random.addEventListener('ready', signData);
}

Игнорирование этого состояния приводит к деградации стойкости.


3. Использование fallback на Math.random()

В некоторых интеграциях разработчики заменяют источник случайности:

sjcl.random = Math.random;

Это полностью уничтожает криптографическую стойкость, так как Math.random():

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

Уязвимости браузерной среды

В JavaScript-криптографии часто возникает конфликт между:

  • window.crypto.getRandomValues() (криптографически стойкий источник)
  • sjcl.random (энтропийный пул с накоплением)

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

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

sjcl.random.setDefaultParanoia(6);

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


Предсказуемость nonce и атаки восстановления ключа

Даже частичная утечка k приводит к возможности восстановления d. Если известны несколько битов nonce или наблюдается bias (смещение распределения), применяются методы:

  • решётчатые атаки (lattice-based recovery)
  • анализ частичных nonce (partial nonce exposure)
  • корреляционные атаки при повторяющихся состояниях RNG

В JS-среде это особенно актуально из-за:

  • shared runtime состояния
  • JIT-оптимизаций
  • повторного использования seed в изолированных контекстах

RFC 6979 как альтернатива RNG

Для устранения зависимости от случайности применяется детерминированная генерация nonce:

  • RFC 6979
  • основан на HMAC-SHA256

В этом подходе k вычисляется как функция:

k = HMAC(privkey, hash(message))

Преимущество:

  • отсутствие зависимости от RNG
  • невозможность повторного nonce при одинаковом ключе и сообщении

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


Ошибки состояния entropy pool в SJCL

sjcl.random хранит внутренний пул энтропии. При неправильной эксплуатации возникают состояния:

  • недостаточная энтропия (pool starvation)
  • деградация качества случайных бит
  • блокирующие вызовы в неподходящий момент

Особенно критично в сценариях:

  • серверный JS (Node + browser polyfill)
  • Electron-приложения
  • IoT устройства с JS-движком

Признаки слабого RNG в криптографической подписи

В анализе подписей можно выявить:

  • повторяющиеся r значения
  • коррелированные подписи для разных сообщений
  • аномально низкая энтропия распределения nonce
  • линейные зависимости между компонентами (r, s)

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


Связь между реализацией и криптостойкостью

ECDSA как математическая конструкция остаётся корректной, но реализация в JavaScript через SJCL полностью зависит от качества:

  • инициализации sjcl.random
  • источников entropy
  • времени прогрева генератора
  • отсутствия fallback на небезопасные функции

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