nacl.sign.keyPair: генерация ключей подписи

В библиотеке TweetNaCl.js реализована схема цифровой подписи Ed25519, предоставляющая компактную и высокоскоростную криптографию на основе эллиптических кривых. Основной точкой входа для создания ключей является функция nacl.sign.keyPair.

Базовый механизм генерации

Функция генерирует пару ключей, состоящую из:

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

Вызов без аргументов приводит к генерации криптографически стойкой случайной пары:

import nacl from "tweetnacl";

const keyPair = nacl.sign.keyPair();

const publicKey = keyPair.publicKey;
const secretKey = keyPair.secretKey;

Обе сущности представлены в виде Uint8Array фиксированной длины:

  • publicKey: 32 байта
  • secretKey: 64 байта

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


Внутренняя структура ключей Ed25519

Алгоритм Ed25519 основан на детерминированной генерации ключевого материала из случайного 32-байтового seed. В процессе формируется расширенный секрет, из которого вычисляются:

  • хешированная форма приватного ключа
  • публичная точка на кривой Edwards25519

TweetNaCl.js скрывает эти преобразования, предоставляя готовый интерфейс через keyPair.


Генерация ключей из seed

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

const seed = crypto.getRandomValues(new Uint8Array(32));

const keyPair = nacl.sign.keyPair.fromSeed(seed);

Особенности использования seed:

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

Полученная пара обладает теми же характеристиками:

const publicKey = keyPair.publicKey;
const secretKey = keyPair.secretKey;

Различия между keyPair и keyPair.fromSeed

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

nacl.sign.keyPair()

  • использует внутренний CSPRNG (криптографически безопасный генератор случайных чисел)
  • не требует входных данных
  • результат непредсказуем

nacl.sign.keyPair.fromSeed(seed)

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

Представление данных и бинарный формат

Все ключи представлены в бинарном виде (Uint8Array), что исключает необходимость дополнительного кодирования на уровне библиотеки.

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

const base64PublicKey = btoa(String.fromCharCode(...publicKey));
const base64SecretKey = btoa(String.fromCharCode(...secretKey));

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


Поведение secretKey в Ed25519

Закрытый ключ в TweetNaCl.js имеет удвоенный размер по сравнению с математическим определением ключа Ed25519. Структура включает:

  • 32 байта — расширенный секрет (hash seed)
  • 32 байта — соответствующий публичный ключ

Это упрощает операции подписи, поскольку повторное вычисление публичного ключа не требуется.


Генерация и безопасность случайных чисел

При вызове nacl.sign.keyPair() критически важно наличие корректного источника энтропии. В браузерной среде используется:

  • window.crypto.getRandomValues

В Node.js аналогично:

  • crypto.randomBytes (внутри реализации библиотеки или через полифил)

Слабая энтропия приводит к полной компрометации ключевой пары, поскольку алгоритм Ed25519 детерминирован после получения seed.


Типичные ошибки при работе с ключами

Повторное использование seed

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

Случайное усечение массива

Любое изменение длины seed (например, 16 байт вместо 32) делает ключевую пару некорректной или непредсказуемо изменённой.

Сериализация без контроля формата

Преобразование Uint8Array через строковые функции без явного кодирования приводит к потере данных:

String.fromCharCode(...secretKey) // потенциально опасно при хранении

Связь с операциями подписи

Сгенерированная пара используется в следующих функциях:

const message = new TextEncoder().encode("data");

const signature = nacl.sign(message, secretKey);

const verified = nacl.sign.open(signature, publicKey);

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


Поведение при повторной генерации

Каждый вызов nacl.sign.keyPair() приводит к созданию новой независимой пары. Повторяемость отсутствует, если не используется fromSeed.

Это свойство критично для:

  • генерации идентичностей
  • распределённых систем
  • криптографических протоколов с восстановлением ключей

Использование в архитектуре приложений

Ключевая пара часто используется как основа идентификации субъекта в системах:

  • распределённые протоколы аутентификации
  • peer-to-peer сети
  • цифровые подписи сообщений
  • хранение приватных идентичностей

Открытый ключ выступает как публичный идентификатор, закрытый — как средство контроля доступа к действиям подписи.