Механизм detached signature в TweetNaCl.js реализует
схему цифровой подписи Ed25519, где подпись хранится отдельно от
сообщения. Это принципиально отличает её от «упакованных» форматов, где
сообщение и подпись объединены в один буфер.
Функция nacl.sign.detached.verify выполняет
криптографическую проверку: соответствует ли переданная подпись
конкретному сообщению и публичному ключу.
Проверка происходит в одну сторону: восстановить приватный ключ или изменить сообщение так, чтобы подпись осталась валидной, вычислительно невозможно при корректной реализации алгоритма.
nacl.sign.detached.verify(message, signature, publicKey)
message: Uint8Array
Исходное сообщение в бинарном виде. Важно: проверка выполняется строго над байтами, а не строкой.
signature: Uint8Array (64 байта)
Отделённая цифровая подпись, сформированная функцией:
nacl.sign.detached(message, secretKey)
publicKey: Uint8Array (32 байта)
Публичный ключ Ed25519, соответствующий приватному ключу, которым была создана подпись.
boolean
true — подпись корректна, сообщение не изменено и
принадлежит владельцу ключаfalse — подпись невалидна, сообщение изменено или ключ
не соответствует подписиimport nacl from "tweetnacl";
// генерация ключевой пары
const keyPair = nacl.sign.keyPair();
// сообщение
const message = new TextEncoder().encode("secure message");
// подпись
const signature = nacl.sign.detached(message, keyPair.secretKey);
// проверка подписи
const isValid = nacl.sign.detached.verify(
message,
signature,
keyPair.publicKey
);
console.log(isValid); // true
Функция не выполняет расшифровку и не модифицирует данные. Она только сравнивает криптографическую целостность.
Подпись может прийти из сети, файла, базы данных — проверка всегда сводится к математическому сравнению.
Реализация TweetNaCl.js построена так, чтобы исключать утечки через timing attacks. Время выполнения не зависит от содержимого сообщения или подписи.
Алгоритм Ed25519 в режиме detached verification:
Важно: на уровне API никакие промежуточные значения не доступны.
// ❌ неправильно
nacl.sign.detached.verify(
"message",
signature,
publicKey
);
Правильно:
const message = new TextEncoder().encode("message");
Если подпись создавалась от UTF-8 байтов, а проверка идёт от другой
кодировки (например, Latin-1), результат всегда будет
false.
Подпись должна быть строго 64 байта:
signature.length === 64
Любое изменение даже одного байта делает подпись недействительной.
Публичный ключ должен соответствовать приватному ключу, которым
создана подпись. Проверка с чужим ключом всегда возвращает
false.
В реальных приложениях данные часто приходят не в виде
Uint8Array, а в виде:
function base64ToUint8Array(base64) {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
Использование:
const signature = base64ToUint8Array(sigBase64);
const publicKey = base64ToUint8Array(pubKeyBase64);
const message = new TextEncoder().encode(text);
const ok = nacl.sign.detached.verify(message, signature, publicKey);
Функция широко используется в прикладных системах:
Типичный безопасный поток:
nacl.sign.detached(message, signature, publicKey)nacl.sign.detached.verifyНевозможно проверить «часть подписи» или «часть сообщения».
Подпись не содержит информации о сообщении.
Хеширование выполняется внутри алгоритма Ed25519. Дополнительный
SHA-256 на стороне приложения меняет результат
проверки.
nacl.sign.detached| Операция | Назначение |
|---|---|
nacl.sign.detached |
создание подписи |
nacl.sign.detached.verify |
проверка подписи |
Обе функции являются парными и должны использовать один и тот же формат входных данных.
Функция не выбрасывает исключения при неверной подписи. Она всегда
возвращает false.
Это важно для безопасности:
async function verifyPayload(payload, publicKey) {
const message = new TextEncoder().encode(payload.message);
const signature = base64ToUint8Array(payload.signature);
return nacl.sign.detached.verify(
message,
signature,
publicKey
);
}
Даже одинаковая строка может иметь разные байтовые представления.
new TextEncoder().encode(new TextEncoder().encode(str))
даёт некорректный результат.
Любая модификация:
ломает проверку.
JavaScript строки не являются бинарным форматом.
TextEncoder всегда преобразует строку в UTF-8.
Это значит:
Для корректной работы проверки необходимо соблюдение трёх условий: