Любая криптографическая библиотека в JavaScript работает не со строками напрямую, а с байтовыми массивами. Причина проста: криптография оперирует последовательностями байтов фиксированного формата, тогда как строки в JavaScript — это абстракция над Unicode-символами.
В TweetNaCl.js (и совместимых реализациях
nacl.js) для преобразования между строками и байтами
используется модуль nacl.util, в котором ключевую роль
играют две функции:
nacl.util.decodeUTF8nacl.util.encodeUTF8Именно они обеспечивают корректное преобразование данных между человеческим текстом и криптографическим представлением.
UTF-8 — это переменной длины кодировка Unicode, где каждый символ может занимать от 1 до 4 байт. Она выбрана не случайно:
В контексте TweetNaCl.js UTF-8 используется как
универсальный мост между строками JavaScript и бинарными массивами
Uint8Array.
Функция преобразует строку JavaScript в массив байтов
Uint8Array.
nacl.util.decodeUTF8(string) -> Uint8Array
const message = "Hello, мир";
const bytes = nacl.util.decodeUTF8(message);
console.log(bytes);
// Uint8Array([...байты UTF-8...])
Большинство криптографических функций TweetNaCl.js
(например, nacl.box, nacl.secretbox,
nacl.sign) принимают только Uint8Array.
Это означает:
Пример:
const msg = nacl.util.decodeUTF8("secret message");
const encrypted = nacl.secretbox(msg, nonce, key);
При использовании decodeUTF8 важно учитывать:
Пример:
"а".length === 1
nacl.util.decodeUTF8("а").length === 2
Это критически важно при работе с криптографическими буферами, где размер данных имеет значение.
Функция выполняет обратное преобразование: из Uint8Array
в строку JavaScript.
nacl.util.encodeUTF8(Uint8Array) -> string
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const text = nacl.util.encodeUTF8(bytes);
console.log(text); // "Hello"
Частый сценарий — получение расшифрованных данных:
const decrypted = nacl.secretbox.open(ciphertext, nonce, key);
if (decrypted) {
const message = nacl.util.encodeUTF8(decrypted);
console.log(message);
}
Здесь:
decrypted — бинарный массивencodeUTF8 восстанавливает человекочитаемый текстЕсли в encodeUTF8 передать массив, не являющийся
валидным UTF-8, результат может быть:
�UTF-8 строго структурирован. Если данные не были изначально текстом (например, случайный бинарный массив), попытка декодирования:
nacl.util.encodeUTF8(randomBytes)
не гарантирует осмысленного результата.
Ключевая ошибка при работе с TweetNaCl.js — смешивание
типов данных.
nacl.secretbox("hello", nonce, key);
const msg = nacl.util.decodeUTF8("hello");
nacl.secretbox(msg, nonce, key);
Криптографические функции работают только с байтами.
В современных средах можно использовать встроенные API:
const encoder = new TextEncoder();
const bytes = encoder.encode("hello");
const decoder = new TextDecoder();
const text = decoder.decode(bytes);
Однако nacl.util сохраняется как:
Корректное использование UTF-8 преобразований напрямую влияет на:
Даже одно неверное преобразование строки в байты приводит к:
// кодирование строки в байты
const data = nacl.util.decodeUTF8("confidential");
// криптографическая операция
const encrypted = nacl.secretbox(data, nonce, key);
// обратное преобразование
const decrypted = nacl.secretbox.open(encrypted, nonce, key);
const text = decrypted ? nacl.util.encodeUTF8(decrypted) : null;
UTF-8 гарантирует поддержку любых языков:
const text = "Привет ? こんにちは";
const bytes = nacl.util.decodeUTF8(text);
const restored = nacl.util.encodeUTF8(bytes);
Строка сохраняется без потерь при корректном round-trip преобразовании.
nacl.util.decodeUTF8("") // Uint8Array([])
nacl.util.encodeUTF8(new Uint8Array([])) // ""
Это важно при обработке сообщений нулевой длины или пустых payload в протоколах.
Uint8ArrayЭти ошибки часто проявляются не сразу, а только при расшифровке или проверке подписи.