Nonce (number used once) в криптографических конструкциях
TweetNaCl.js / nacl.js выполняет роль уникального значения, которое
должно отличаться для каждого шифрования с одним и тем же ключом. В
режиме box он участвует в формировании уникального
контекста шифрования и напрямую влияет на безопасность всей схемы.
Nonce не является секретом, но обязан быть уникальным для каждой операции шифрования при одном и том же ключе. Его задача — гарантировать, что одинаковые сообщения, зашифрованные одним ключом, будут давать разные шифротексты. Это защищает от анализа повторяющихся паттернов и критически важно для предотвращения атак на повторное использование данных.
В TweetNaCl.js nonce имеет фиксированный размер — 24 байта. Любое отклонение от требований по длине приводит к некорректной работе или снижению безопасности.
Уникальность строго обязательна
Повтор nonce с тем же ключом полностью разрушает криптографическую стойкость. Даже один повтор открывает возможность восстановления исходных сообщений. Это не теоретическая угроза — в реальных системах повтор nonce приводит к компрометации всей переписки.
Nonce не обязан быть случайным, но обязан быть неповторяющимся
Случайная генерация допустима, но не гарантирует отсутствие коллизий. Поэтому в высоконагруженных системах предпочтительнее детерминированные схемы: счётчики, комбинации префиксов и инкрементов, либо распределённые генераторы с гарантией уникальности.
Запрещено переиспользование nonce с тем же ключом
Даже при шифровании разных сообщений повтор одного nonce делает возможным проведение атак на основе XOR-анализа шифротекстов. В конструкции stream cipher это приводит к восстановлению информации о сообщениях.
В TweetNaCl.js nonce обычно создаётся через криптографически стойкий генератор случайных чисел:
import nacl from 'tweetnacl';
nacl.randomBytes(24);
Однако использование случайных значений допустимо только при наличии уверенности в отсутствии повторов. В распределённых системах предпочтительнее комбинированные подходы:
Пример детерминированной схемы:
function createNonce(nodeId, counter) {
const nonce = new Uint8Array(24);
nonce.set(nodeId.slice(0, 8), 0);
const view = new DataView(nonce.buffer);
view.setBigUint64(16, BigInt(counter));
return nonce;
}
Такая схема обеспечивает уникальность при условии корректного управления состоянием счётчика.
Использование фиксированного nonce
const nonce = new Uint8Array(24);
Подобная практика делает все сообщения предсказуемыми и уязвимыми к анализу.
Повтор nonce при ретраях отправки
Повторная отправка сообщения с тем же nonce и ключом создаёт возможность восстановления исходного текста через анализ разницы шифротекстов.
Глобальный случайный nonce без контроля состояния
При высокой частоте шифрования возможны коллизии, особенно при длительной работе системы.
В системах с несколькими узлами проблема nonce становится сложнее. Основная цель — гарантировать глобальную уникальность без централизованного координатора.
Используются следующие стратегии:
Разделение пространства nonce
Каждый узел получает свой диапазон значений или уникальный префикс идентификатора.
Комбинация времени и счётчика
function generateNonce() {
const nonce = new Uint8Array(24);
const timestamp = Date.now();
const counter = localCounter++;
const view = new DataView(nonce.buffer);
view.setBigUint64(0, BigInt(timestamp));
view.setBigUint64(16, BigInt(counter));
return nonce;
}
Такая схема требует строгого контроля часов и атомарности счётчика.
В системах с долгоживущими ключами важно сохранять состояние счётчика или последнего использованного значения. Потеря состояния приводит к риску повторов после перезапуска приложения.
Часто применяются:
В конструкции
nacl.box(message, nonce, publicKey, secretKey) nonce
становится частью входных данных для функции, формирующей ключевой
поток. При одинаковом ключе изменение nonce полностью меняет результат
шифрования.
Это свойство делает nonce эквивалентом «вектора инициализации», но с более строгим требованием уникальности.
Повтор nonce приводит к генерации одинакового keystream в stream cipher. Это позволяет атакующему:
В некоторых сценариях компрометация одного nonce приводит к раскрытию всех сообщений, защищённых одним ключом.
Даже криптографически стойкие генераторы не гарантируют отсутствие повторов при масштабировании системы. Ошибка проектирования обычно связана не с качеством случайности, а с отсутствием контроля состояния.
По этой причине nonce не рассматривается как случайная величина, а как строго управляемый идентификатор операции шифрования.
Недетерминированные подходы:
Детерминированные подходы:
Nonce рассматривается как ресурс, который исчерпывается в пределах пары «ключ + система генерации». Любая архитектура должна исходить из того, что повтор невозможен на уровне логики системы, а не проверяется постфактум.