Атаки на ECDSA при повторном использовании nonce

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

Формально подпись ECDSA строится на основе:

  • приватного ключа d
  • случайного nonce k
  • хеша сообщения z

Подпись состоит из пары значений (r, s):

  • **r = (k · G)_x mod n**
  • s = k⁻¹ (z + r · d) mod n

где:

  • G — генератор эллиптической кривой
  • n — порядок группы
  • k⁻¹ — обратный элемент по модулю n

Критическое свойство схемы: значение k должно быть полностью уникальным и непредсказуемым.


Почему повтор nonce полностью ломает безопасность

Повторное использование nonce при одинаковом или даже разном приватном ключе приводит к полной компрометации ключа.

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

  • s₁ = k⁻¹ (z₁ + r · d) mod n
  • s₂ = k⁻¹ (z₂ + r · d) mod n

Так как k и r одинаковы, можно вычесть уравнения:

s₁ - s₂ = k⁻¹ (z₁ - z₂)

Отсюда вычисляется nonce:

k = (z₁ - z₂) / (s₁ - s₂) mod n

После восстановления k приватный ключ вычисляется напрямую:

d = (s₁ · k - z₁) / r mod n

или аналогично через вторую подпись.

Ключевой факт: один повтор nonce полностью раскрывает приватный ключ без перебора.


Практические сценарии утечки nonce

Ошибки генерации случайных чисел

Наиболее частая причина — слабый или предсказуемый RNG.

Типичные проблемы:

  • использование Math.random()
  • повторное инициализация seed
  • отсутствие криптографического источника энтропии
  • баги в WebCrypto polyfill

В JavaScript это особенно критично в средах без надежного window.crypto.


Ошибки реализации протокола

Повтор nonce часто возникает из-за:

  • кеширования значения k для ускорения подписи
  • неправильного reuse state в объекте ECDSA
  • параллельных подписей с общим генератором nonce
  • копирования кода без понимания внутреннего состояния

Утечки через deterministic nonce

RFC6979 предлагает детерминированную генерацию k на основе HMAC:

  • k = HMAC(privkey, message hash)

Ошибки возникают, если:

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

Jsrsasign и ECDSA: где возникает риск

Библиотека Jsrsasign реализует ECDSA и поддерживает несколько режимов генерации nonce.

Типичный пример подписи:

const sig = new KJUR.crypto.Signature({ "alg": "SHA256withECDSA" });
sig.init(privateKeyPEM);
sig.updateString("message");
const hexSig = sig.sign();

В нормальном режиме библиотека:

  • использует криптографический RNG (если доступен)
  • либо deterministic nonce (RFC6979)

Однако риск появляется при неправильной конфигурации или модификации внутреннего генератора.


Опасные паттерны использования Jsrsasign

1. Переиспользование объекта Signature

const sig = new KJUR.crypto.Signature({ alg: "SHA256withECDSA" });
sig.init(privateKey);

// ошибка: повторное использование без полной переинициализации
sig.updateString("msg1");
const s1 = sig.sign();

sig.updateString("msg2");
const s2 = sig.sign();

Если внутреннее состояние nonce не сбрасывается корректно, возможен reuse k.


2. Кастомный источник nonce

Некоторые реализации пытаются оптимизировать подпись:

sig.setParameters({ k: customK });

Любая ручная подстановка nonce:

  • нарушает криптографическую модель ECDSA
  • делает возможным восстановление ключа при повторе

3. Параллельные подписи с общим RNG

В многопоточных или асинхронных системах:

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

Атака на практике: сценарий восстановления ключа

При наличии двух подписей:

  • (r, s₁) для сообщения m₁
  • (r, s₂) для сообщения m₂

и совпадающего r (признак одинакового nonce), атакующий вычисляет:

  1. хеши сообщений z₁, z₂
  2. восстанавливает k
  3. вычисляет приватный ключ d

Это не требует знания кривой или brute-force.


Детектирование повторного nonce

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

1. Проверка совпадения r

Если:

  • r₁ == r₂ при разных сообщениях

это почти всегда означает повтор nonce.


2. Анализ линейной зависимости

Если известны подписи, можно проверить:

  • (s₁ - s₂)⁻¹ · (z₁ - z₂)

на корректность распределения. Аномалии указывают на утечку.


3. Логирование entropy source

Важно контролировать:

  • источник случайности
  • отсутствие повторов seed
  • стабильность crypto API

Безопасная модель генерации nonce в Jsrsasign

Правильное поведение библиотеки:

  • использование window.crypto.getRandomValues
  • fallback на Node.js crypto.randomBytes
  • RFC6979 как резервный механизм

Важно, что deterministic nonce:

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

Критические свойства защиты

Для безопасности ECDSA необходимо:

  • nonce никогда не должен повторяться
  • nonce должен быть непредсказуем
  • state генератора должен быть изолирован
  • подписи должны выполняться последовательно или с независимыми RNG

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


Типичные причины компрометации в JS-окружении

JavaScript среда усиливает риск:

  • браузерные polyfill’ы WebCrypto
  • различия между Node.js и browser RNG
  • hot-reload состояния в SPA
  • повторное использование singleton крипто-объектов
  • слабые реализации в старых версиях библиотек

Свойства атаки при частичном повторе nonce

Даже частичное совпадение k (например, утечка старших бит) может:

  • сократить пространство поиска
  • позволить lattice-атаки (Hidden Number Problem)
  • привести к восстановлению ключа при нескольких подписях

ECDSA крайне чувствителен к утечкам entropy.


Последствия для систем на Jsrsasign

При неправильной работе с nonce:

  • компрометация TLS-подписей
  • подделка JWT с ECDSA
  • подмена цифровых подписей документов
  • полный доступ к ключам подписи API

Ошибки на уровне одного компонента распространяются на всю криптосистему.


Архитектурные принципы предотвращения

  • изоляция экземпляров подписывающего объекта
  • запрет внешнего управления nonce
  • использование только встроенных генераторов
  • отсутствие глобального состояния RNG
  • регулярная проверка уникальности r-значений

Поведение ECDSA полностью зависит от корректности генерации одного параметра. В контексте Jsrsasign любая модификация этого механизма превращает криптографическую подпись в линейное уравнение, решаемое за несколько шагов.