Применение nacl.box.after и nacl.box.open.after

Функции nacl.box.after и nacl.box.open.after предназначены для работы с уже вычисленным общим секретом между двумя сторонами. В основе лежит тот же механизм асимметричного шифрования, что и в nacl.box, однако ключевая оптимизация заключается в том, что операция вычисления общего ключа (shared key) выполняется один раз, а затем переиспользуется.

Это особенно важно в сценариях:

  • длительных сессий связи (например, WebSocket)
  • обмена большим количеством сообщений между одними и теми же участниками
  • систем с высокой нагрузкой, где каждая криптографическая операция имеет значение

Вычисление общего ключа: nacl.box.before

Перед использованием after-версий необходимо получить общий ключ:

const sharedKey = nacl.box.before(theirPublicKey, mySecretKey);

Особенности:

  • результат — Uint8Array длиной 32 байта
  • вычисление основано на алгоритме X25519 (ECDH)
  • порядок аргументов важен: сначала публичный ключ другой стороны, затем собственный секретный

После этого обе стороны, имея соответствующие ключи, получают одинаковый sharedKey.


Шифрование с nacl.box.after

const encrypted = nacl.box.after(message, nonce, sharedKey);

Параметры:

  • messageUint8Array, исходные данные
  • nonce — уникальный 24-байтовый вектор
  • sharedKey — заранее вычисленный общий ключ

Результат:

  • зашифрованное сообщение с встроенной аутентификацией (MAC)

Ключевые моменты:

  • nonce не должен повторяться для одного и того же ключа
  • шифрование использует XSalsa20 + Poly1305
  • структура результата аналогична nacl.box

Расшифровка с nacl.box.open.after

const decrypted = nacl.box.open.after(encrypted, nonce, sharedKey);

Возвращает:

  • Uint8Array с исходным сообщением при успешной проверке
  • null, если проверка подлинности не прошла

Особенности:

  • проверка MAC выполняется автоматически
  • любые изменения в ciphertext приводят к null

Сравнение с nacl.box / nacl.box.open

Характеристика box box.after
Вычисление shared key каждый вызов один раз
Производительность ниже выше при множественных операциях
Удобство проще API требует подготовки
Контроль меньше больше

Практический сценарий: обмен сообщениями

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

const alice = nacl.box.keyPair();
const bob = nacl.box.keyPair();

Вычисление общего ключа

const aliceShared = nacl.box.before(bob.publicKey, alice.secretKey);
const bobShared = nacl.box.before(alice.publicKey, bob.secretKey);

aliceShared и bobShared будут идентичны.


Шифрование сообщения

const nonce = nacl.randomBytes(nacl.box.nonceLength);
const message = new TextEncoder().encode("Привет");

const encrypted = nacl.box.after(message, nonce, aliceShared);

Расшифровка сообщения

const decrypted = nacl.box.open.after(encrypted, nonce, bobShared);

if (decrypted) {
    console.log(new TextDecoder().decode(decrypted));
}

Управление nonce

nonce играет критическую роль:

  • длина строго 24 байта
  • должен быть уникальным для каждой операции
  • допустимо передавать вместе с ciphertext

Распространённый подход:

const nonce = nacl.randomBytes(24);

или использование счётчиков (в контролируемых системах).


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

После вычисления sharedKey можно:

  • хранить его в памяти в рамках сессии
  • использовать для множества сообщений
  • избегать повторных дорогих операций ECDH

Это превращает асимметричную схему в эффективную симметричную внутри сессии.


Безопасность и ограничения

Важно учитывать:

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

Не рекомендуется:

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

Производительность

Основной выигрыш достигается за счёт:

  • исключения повторных операций X25519
  • ускорения при массовой отправке сообщений

Типичный сценарий:

  • 1 раз before
  • N раз after

Внутреннее устройство

nacl.box.after фактически использует:

  • уже вычисленный shared key
  • потоковый шифр XSalsa20
  • MAC Poly1305

Это эквивалентно:

sharedKey = X25519(...)
cipher = XSalsa20(sharedKey, nonce)
mac = Poly1305(ciphertext)

Когда использовать after

Подходит для:

  • чат-приложений
  • защищённых каналов (например, WebRTC data channels)
  • API-клиентов с постоянной авторизацией
  • IoT-устройств с ограниченными ресурсами

Не подходит, если:

  • сообщения единичные
  • нет устойчивого канала между сторонами
  • ключи постоянно меняются

Типичная архитектура

  1. Обмен публичными ключами
  2. Вычисление shared key (before)
  3. Обмен сообщениями через after / open.after
  4. Обновление ключей при необходимости

Частые ошибки

1. Перепутаны ключи при before

// неправильно
nacl.box.before(myPublicKey, theirSecretKey)

2. Повторный nonce

// опасно
const nonce = new Uint8Array(24); // всегда нули

3. Игнорирование null при расшифровке

// ошибка
const msg = nacl.box.open.after(...);
// без проверки msg

Итоговое понимание

nacl.box.after и nacl.box.open.after — это оптимизированный слой поверх стандартного box, превращающий асимметричное шифрование в эффективный симметричный поток внутри защищённой сессии.