nacl.hash реализует SHA-512 (в TweetNaCl.js — именно
64-байтный хеш), и в экосистеме NaCl он используется не как
самостоятельный «универсальный хеш», а как базовый строительный блок для
композиции других операций. Его ключевая особенность —
детерминированность и устойчивость к малейшим изменениям входных данных,
что делает его удобным инструментом для связывания данных между
различными криптографическими примитивами.
В практическом коде Jav * aScript:
import nacl from 'tweetnacl';
const msg = new TextEncoder().encode('hello');
const digest = nacl.hash(msg);
digest всегда имеет фиксированную длину 64 байта,
независимо от размера входа.
Одно из наиболее частых применений nacl.hash —
построение промежуточных представлений данных, которые затем
используются в других примитивах:
В NaCl отсутствует полноценный KDF (Key Derivation Function), поэтому
nacl.hash часто используется как простая замена:
const seed = new TextEncoder().encode('password_or_secret');
const fullHash = nacl.hash(seed);
// первые 32 байта можно использовать как ключ
const key = fullHash.slice(0, 32);
Такой подход встречается при работе с nacl.secretbox,
где требуется симметричный ключ фиксированной длины.
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
const message = new TextEncoder().encode('data');
const box = nacl.secretbox(message, nonce, key);
Важно, что такая схема не является полноценной заменой PBKDF2/Argon2, но часто используется в легковесных протоколах.
Внутри nacl.sign уже используется хеширование сообщения,
но на уровне композиции иногда дополнительно хешируют данные до
подписи:
const message = new TextEncoder().encode('important message');
const prehash = nacl.hash(message);
const keyPair = nacl.sign.keyPair();
const signature = nacl.sign(prehash, keyPair.secretKey);
Такой подход встречается в системах, где:
Однако двойное хеширование может быть опасным при неправильном понимании доменной изоляции: разные данные могут привести к одинаковой предобработке.
nacl.hash и nacl.box (асимметричное
шифрование)В nacl.box ключи — это пара Curve25519 ключей, но
nacl.hash часто используется для:
const kp = nacl.box.keyPair();
const publicKeyHash = nacl.hash(kp.publicKey);
const fingerprint = publicKeyHash.slice(0, 16);
Такой fingerprint используется в:
Для предотвращения атак повторного использования ключей в разных контекстах:
const context = new TextEncoder().encode('chat-v1');
const combined = new Uint8Array(context.length + kp.publicKey.length);
combined.set(context);
combined.set(kp.publicKey, context.length);
const derived = nacl.hash(combined);
Хотя в NaCl nonce должен быть случайным, иногда в протоколах используется детерминированное построение nonce:
const msg = new TextEncoder().encode('message');
const base = nacl.randomBytes(16);
const nonceInput = new Uint8Array(base.length + msg.length);
nonceInput.set(base);
nonceInput.set(msg, base.length);
const nonceFull = nacl.hash(nonceInput);
const nonce = nonceFull.slice(0, nacl.secretbox.nonceLength);
Такая схема применяется в системах, где важно восстановление состояния или повторяемость шифрования, но она требует строгого контроля уникальности входов.
Когда несколько полей объединяются в одно сообщение,
nacl.hash используется для фиксации структуры:
const userId = new TextEncoder().encode('123');
const payload = new TextEncoder().encode('transfer:500');
const buffer = new Uint8Array(userId.length + payload.length);
buffer.set(userId);
buffer.set(payload, userId.length);
const commitment = nacl.hash(buffer);
Это позволяет:
Одна из ключевых практик — разделение доменов использования. Один и
тот же nacl.hash может давать разные результаты при
добавлении контекста:
const data = new TextEncoder().encode('value');
const h1 = nacl.hash(new Uint8Array([...new TextEncoder().encode('A:'), ...data]));
const h2 = nacl.hash(new Uint8Array([...new TextEncoder().encode('B:'), ...data]));
Даже одинаковые данные становятся несовместимыми между протоколами.
Это критично при:
Любое использование nacl.hash в реальных системах почти
всегда включает этап сериализации:
function serialize(obj) {
return new TextEncoder().encode(JSON.stringify(obj));
}
const obj = { from: 1, to: 2, amount: 100 };
const hash = nacl.hash(serialize(obj));
Проблема здесь заключается в том, что порядок полей JSON не гарантирован. Поэтому на практике применяется каноническая сериализация (sorted keys), иначе хеш перестаёт быть стабильным.
nacl.hash редко используется изолированно. Он выступает
промежуточным слоем в:
Простейший пример линейного связывания:
const h1 = nacl.hash(msg1);
const h2 = nacl.hash(new Uint8Array([...h1, ...msg2]));
const h3 = nacl.hash(new Uint8Array([...h2, ...msg3]));
Такой подход создаёт цепочку зависимостей, где изменение одного сообщения влияет на весь результат.
Комбинирование nacl.hash с другими функциями NaCl
требует учёта особенностей:
Особенно критично:
Вся философия связки nacl.hash с другими операциями
строится на принципе:
В результате nacl.hash становится не функцией «получить
хеш», а механизмом связывания состояний между криптографическими слоями,
где каждое преобразование фиксирует предыдущие шаги в неизменяемой
форме.