nacl.sign.detached: отделённая подпись

Механизм цифровой подписи в TweetNaCl.js основан на алгоритме Ed25519, который обеспечивает высокую скорость и криптографическую стойкость. В библиотеке реализованы два основных подхода к подписи данных: «attached» и «detached». В случае detached-подписи результатом является отдельный массив байт фиксированной длины, не содержащий исходное сообщение.

Подход detached используется там, где необходимо хранить или передавать подпись отдельно от данных, например в протоколах обмена сообщениями, API-запросах и системах верификации файлов.


Алгоритм Ed25519 и ключевая модель

Ed25519 относится к эллиптическим криптографическим схемам с использованием кривой Curve25519. В контексте подписи используются два ключа:

  • Secret key (закрытый ключ) — используется для создания подписи
  • Public key (открытый ключ) — используется для проверки подписи

TweetNaCl.js работает с ключами в бинарном формате Uint8Array, а не строками.


Генерация ключевой пары

Ключевая пара создаётся через nacl.sign.keyPair(), либо из заранее известного seed.

const nacl = require('tweetnacl');

// генерация новой пары ключей
const keyPair = nacl.sign.keyPair();

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

Также можно восстановить пару из seed:

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

Seed должен быть длиной 32 байта.


Подпись данных: nacl.sign.detached

Функция nacl.sign.detached создаёт криптографическую подпись без упаковки сообщения.

Сигнатура функции

nacl.sign.detached(message, secretKey)

Параметры

  • message — данные в виде Uint8Array
  • secretKey — закрытый ключ (64 байта)

Результат

  • Uint8Array длиной 64 байта (подпись Ed25519)

Пример создания подписи

const nacl = require('tweetnacl');

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

const keyPair = nacl.sign.keyPair();

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

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


Проверка подписи: nacl.sign.detached.verify

Проверка выполняется отдельно от сообщения, что и определяет модель detached.

Сигнатура функции

nacl.sign.detached.verify(message, signature, publicKey)

Параметры

  • message — исходное сообщение (Uint8Array)
  • signature — подпись (64 байта)
  • publicKey — открытый ключ (32 байта)

Результат

  • boolean:

    • true — подпись валидна
    • false — подпись не соответствует данным

Пример проверки подписи

const nacl = require('tweetnacl');

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

const keyPair = nacl.sign.keyPair();

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

const isValid = nacl.sign.detached.verify(
  message,
  signature,
  keyPair.publicKey
);

console.log(isValid); // true

Особенности работы с данными

1. Только бинарный формат

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

new TextEncoder().encode("text")

2. Размеры буферов

  • Public key: 32 байта
  • Secret key: 64 байта
  • Signature: 64 байта

Любое отклонение в размере приводит к некорректной работе функций.


Применение detached-подписи

Detached-подпись используется в сценариях, где важно разделение данных и метаданных:

  • Подписание JSON-объектов перед отправкой по API
  • Проверка целостности файлов
  • Подпись сообщений в чат-системах
  • Подтверждение транзакций в распределённых системах

Подпись структурированных данных

При работе с объектами данные необходимо сериализовать.

const data = {
  user: "alice",
  amount: 100
};

const message = new TextEncoder().encode(JSON.stringify(data));

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

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


Распространённые ошибки

Несовпадение кодировки

Даже незначительное изменение строки делает подпись недействительной:

  • пробелы
  • порядок ключей в JSON
  • разный UTF-8/UTF-16

Использование строк вместо Uint8Array

// неправильно
nacl.sign.detached("message", secretKey);

// правильно
nacl.sign.detached(new TextEncoder().encode("message"), secretKey);

Хранение и передача подписи

Подпись является бинарным массивом, поэтому для передачи по сети её обычно кодируют:

  • Base64
  • Hex

Пример с Base64:

const signatureBase64 = Buffer.from(signature).toString("base64");

Обратное преобразование:

const signature = Uint8Array.from(Buffer.from(signatureBase64, "base64"));

Проверка целостности данных в API

Detached-подпись часто применяется как защита запросов:

  1. Клиент формирует payload
  2. Сериализует данные
  3. Подписывает secretKey
  4. Отправляет { data, signature }
  5. Сервер проверяет через publicKey

Криптографическая устойчивость модели

Ed25519, используемый в TweetNaCl.js, обеспечивает:

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

Взаимосвязь с другими функциями nacl.sign

В библиотеке присутствует связанная функциональность:

  • nacl.sign.keyPair() — генерация ключей
  • nacl.sign.detached() — создание подписи
  • nacl.sign.detached.verify() — проверка подписи
  • nacl.sign() — attached-вариант (со встроенным сообщением)

Detached-вариант предпочтителен при необходимости независимого хранения подписи и данных.