nacl.util.encodeBase64 и nacl.util.decodeBase64

Криптографические примитивы в NaCl работают исключительно с бинарными данными — массивами байтов (Uint8Array). Однако большинство прикладных систем, особенно веб-приложения и API, оперируют текстовыми форматами: JSON, формы, строки URL и базы данных.

Base64 используется как способ безопасного преобразования произвольных бинарных данных в строку, состоящую из печатных символов. Это позволяет:

  • передавать ключи и шифротексты через JSON
  • сохранять криптографические данные в БД
  • выводить бинарные данные в логах и интерфейсах
  • обмениваться данными между клиентом и сервером без потерь

В контексте TweetNaCl.js Base64 — это вспомогательный слой, не влияющий на криптографию, но необходимый для интеграции.


Формат представления данных в TweetNaCl.js

Библиотека TweetNaCl.js использует собственный набор утилит в пространстве nacl.util, среди которых:

  • преобразование Uint8Array → Base64
  • преобразование Base64 → Uint8Array

Важно понимать: криптографические функции (box, secretbox, sign) не работают со строками. Любая строка Base64 — это лишь представление данных, а не их форма для вычислений.


nacl.util.encodeBase64

Функция преобразует бинарный массив в строку Base64.

Сигнатура

nacl.util.encodeBase64(uint8Array)

Поведение

  • Принимает Uint8Array
  • Возвращает строку Base64
  • Использует стандартный алфавит A–Z a–z 0–9 + /
  • Не добавляет padding =

Пример

const message = new TextEncoder().encode("hello");

const encoded = nacl.util.encodeBase64(message);

console.log(encoded);

Результат будет строкой Base64, например:

aGVsbG8

(в зависимости от длины данных)

Особенности

  • Потеря форматирования невозможна: каждый байт сохраняется
  • Результат всегда строка ASCII
  • Не зависит от кодировки исходного текста (важно: кодирование происходит ДО Base64)

nacl.util.decodeBase64

Функция выполняет обратное преобразование — из строки Base64 в бинарный массив.

Сигнатура

nacl.util.decodeBase64(base64String)

Поведение

  • Принимает строку Base64
  • Возвращает Uint8Array
  • Ожидает корректный Base64 без padding =
  • При ошибочном вводе выбрасывает исключение

Пример

const decoded = nacl.util.decodeBase64("aGVsbG8");

const text = new TextDecoder().decode(decoded);

console.log(text); // hello

Обработка ошибок и ограничения

decodeBase64 строго проверяет входную строку:

  • запрещены символы вне Base64 алфавита
  • длина строки должна соответствовать корректной Base64-структуре
  • недопустимые символы приводят к выбросу ошибки

Пример некорректного вызова:

nacl.util.decodeBase64("!!!invalid!!!");

Результат: исключение во время декодирования.


Отличие от atob / btoa

В браузерном API существуют встроенные функции:

  • atob()
  • btoa()

Однако они имеют ограничения:

1. Работа со строками, а не с байтами

btoa("hello") // работает только с Latin1

Если строка содержит UTF-8 символы — возникает ошибка.

2. TweetNaCl.js работает с Uint8Array

nacl.util.encodeBase64 и decodeBase64 предназначены для бинарных данных и не зависят от текстовой кодировки.

3. Криптографическая совместимость

Base64 в nacl.util используется для:

  • ключей (box.keyPair)
  • nonce
  • ciphertext
  • подписи

и гарантирует корректную двустороннюю совместимость с бинарными алгоритмами.


Практическое применение в криптографии

Кодирование ключей

const keyPair = nacl.box.keyPair();

const publicKeyBase64 = nacl.util.encodeBase64(keyPair.publicKey);
const secretKeyBase64 = nacl.util.encodeBase64(keyPair.secretKey);

Такой формат удобно хранить в базе данных или передавать через API.


Шифротекст

const message = nacl.util.decodeUTF8("секрет");
const nonce = nacl.randomBytes(nacl.box.nonceLength);

const encrypted = nacl.box(
  message,
  nonce,
  recipientPublicKey,
  senderSecretKey
);

const encodedCipher = nacl.util.encodeBase64(encrypted);
const encodedNonce = nacl.util.encodeBase64(nonce);

Декодирование и расшифровка

const cipher = nacl.util.decodeBase64(encodedCipher);
const nonce = nacl.util.decodeBase64(encodedNonce);

const decrypted = nacl.box.open(
  cipher,
  nonce,
  senderPublicKey,
  recipientSecretKey
);

const text = nacl.util.encodeUTF8(decrypted);

Представление nonce и бинарных значений

Nonce и подписи часто имеют фиксированную длину:

  • nonce: 24 байта (для box)
  • signature: 64 байта

Base64 позволяет компактно представлять их:

  • без потери информации
  • в строковом виде
  • пригодном для JSON

Типичные ошибки при использовании

1. Попытка кодировать строку напрямую

nacl.util.encodeBase64("hello");

Ошибка: ожидается Uint8Array, а не string.

Правильно:

nacl.util.encodeBase64(nacl.util.decodeUTF8("hello"));

2. Потеря UTF-8 кодировки

const bad = atob("..."); // приводит к искажению символов

В криптографических данных это недопустимо.


3. Хранение Base64 с padding

Некоторые внешние системы добавляют = в конце строки. decodeBase64 TweetNaCl.js может не ожидать padding, что приводит к ошибкам совместимости.


Внутренняя модель преобразования

Base64 в nacl.util работает как два этапа:

  1. Группировка байтов по 3
  2. Перевод в 4 символа Base64

И обратно:

  1. Разбиение на блоки по 4 символа
  2. Восстановление исходных 3 байт

Это делает представление примерно на 33% больше по размеру, чем оригинальные данные.


Контекст использования в архитектуре приложений

Base64 в криптографических системах обычно используется только на границах системы:

  • API слой (JSON)
  • localStorage
  • URL-параметры (реже)
  • логирование

Внутри криптографического процесса всегда остаётся Uint8Array, так как:

  • исключается накладной расход на преобразования
  • сохраняется точность байтовых операций
  • исключаются ошибки кодировок

Совместное использование с другими утилитами nacl.util

Типичный поток данных:

UTF-8 string
   ↓ nacl.util.decodeUTF8
Uint8Array
   ↓ nacl.box / sign / secretbox
Uint8Array (ciphertext)
   ↓ nacl.util.encodeBase64
Base64 string

И обратный процесс:

Base64 string
   ↓ decodeBase64
Uint8Array
   ↓ cryptographic open/decrypt
Uint8Array
   ↓ encodeUTF8
string