Uint8Array является основным типом данных, с которым работает TweetNaCl.js. Все криптографические операции — подпись, шифрование, генерация ключей — выполняются над массивами байтов фиксированного формата. Любые внешние представления ключей, сообщений или nonce неизбежно требуют преобразования в Uint8Array и обратно.
В JavaScript бинарные данные чаще всего представлены через
Uint8Array, где каждый элемент — это число от 0 до 255.
Пример создания:
const bytes = new Uint8Array([104, 101, 108, 108, 111]);
Это строка "hello" в байтовом виде.
TweetNaCl.js не работает со строками напрямую в криптографических функциях. Любая строка должна быть преобразована в байты:
function stringToUint8Array(str) {
return new TextEncoder().encode(str);
}
function uint8ArrayToString(bytes) {
return new TextDecoder().decode(bytes);
}
Hex (шестнадцатеричное представление) часто используется для отображения ключей и хэшей. Каждый байт кодируется двумя символами.
function uint8ToHex(uint8) {
return Array.from(uint8)
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
Пример:
uint8ToHex(new Uint8Array([255, 16, 0]));
// "ff1000"
function hexToUint8(hex) {
if (hex.length % 2 !== 0) throw new Error("Invalid hex length");
const arr = new Uint8Array(hex.length / 2);
for (let i = 0; i < arr.length; i++) {
arr[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return arr;
}
Особенности:
[0-9a-fA-F]0x) должны удаляться заранееBase64 широко используется в Web API и при обмене криптографическими данными.
function uint8ToBase64(uint8) {
let binary = '';
for (let i = 0; i < uint8.length; i++) {
binary += String.fromCharCode(uint8[i]);
}
return btoa(binary);
}
function base64ToUint8(base64) {
const binary = atob(base64);
const arr = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
arr[i] = binary.charCodeAt(i);
}
return arr;
}
В криптографических протоколах часто применяется модифицированный Base64:
+ заменяется на -/ заменяется на _= убираетсяfunction base64UrlEncode(uint8) {
return uint8ToBase64(uint8)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
function base64UrlDecode(str) {
str = str.replace(/-/g, '+').replace(/_/g, '/');
while (str.length % 4) str += '=';
return base64ToUint8(str);
}
Base58 используется там, где важна человекочитаемость и отсутствие неоднозначных символов.
Алфавит:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
Исключены:
Base58 не является побайтовым кодированием, как hex или base64. Это представление большого числа в системе счисления 58.
const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
function uint8ToBase58(bytes) {
let num = BigInt('0x' + Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0'))
.join(''));
let result = '';
while (num > 0) {
const mod = num % 58n;
result = ALPHABET[Number(mod)] + result;
num = num / 58n;
}
// обработка ведущих нулей
for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
result = '1' + result;
}
return result;
}
function base58ToUint8(str) {
let num = 0n;
for (let i = 0; i < str.length; i++) {
num = num * 58n + BigInt(ALPHABET.indexOf(str[i]));
}
let hex = num.toString(16);
if (hex.length % 2) hex = '0' + hex;
let bytes = hexToUint8(hex);
// восстановление ведущих нулей
let leadingZeros = 0;
for (let i = 0; i < str.length && str[i] === '1'; i++) {
leadingZeros++;
}
return new Uint8Array([...new Uint8Array(leadingZeros), ...bytes]);
}
Библиотека ориентирована на строгость и минимализм:
Например:
nacl.sign(messageUint8, secretKeyUint8)
никаких строковых входов не допускается.
Hex и Base58 часто теряют ведущие нули при преобразовании числа.
Это критично для ключей:
00ab... != ab...
new TextEncoder().encode(hexString)
Это ошибка: hex — это представление, а не данные.
В браузере и Node.js используются разные API (atob,
Buffer).
Пример ключа:
const keyPair = nacl.sign.keyPair();
const publicKeyHex = uint8ToHex(keyPair.publicKey);
const secretKeyBase64 = uint8ToBase64(keyPair.secretKey);
Дальнейшее восстановление:
const publicKey = hexToUint8(publicKeyHex);
const secretKey = base64ToUint8(secretKeyBase64);
В криптографических системах обычно выделяют три слоя:
| Формат | Размер | Читаемость | Использование |
|---|---|---|---|
| Hex | 2× | высокая | отладка |
| Base64 | ~1.33× | средняя | API, Web |
| Base58 | ~1.37× | высокая | криптокошельки |
| Uint8Array | 1× | отсутствует | внутренняя работа |
Все корректные реализации должны обеспечивать строгую обратимость:
Uint8Array → Base64 → Uint8Array
Uint8Array → Hex → Uint8Array
Uint8Array → Base58 → Uint8Array
Любая потеря информации указывает на ошибку реализации, а не на ограничения формата.
Типичный поток работы с TweetNaCl.js:
строка → Uint8Array → криптография → Uint8Array → Base64/Hex/Base58
Каждый внешний формат существует только на границах системы: ввод, хранение, передача.
Внутри всегда остаётся бинарное представление без исключений.