ArrayBuffer и TypedArray как основной формат данных

Web Crypto API опирается на строгую работу с бинарными данными. Почти все операции — от хеширования до шифрования — принимают и возвращают значения в виде ArrayBuffer или представлений на его основе. Это связано с тем, что криптография работает не со строками и объектами высокого уровня, а с последовательностями байтов фиксированной структуры.

ArrayBuffer представляет собой фиксированный блок памяти без привязки к формату интерпретации. Он не содержит методов чтения или записи данных напрямую — это только «сырой» контейнер.

const buffer = new ArrayBuffer(16); // 16 байт памяти

Сам по себе ArrayBuffer бесполезен без слоя интерпретации. Именно для этого используются типизированные массивы.


TypedArray как слой интерпретации бинарной памяти

TypedArray — это набор представлений над ArrayBuffer, позволяющий интерпретировать бинарные данные как числа различных типов.

Наиболее часто в Web Crypto используется:

  • Uint8Array — массив 8-битных беззнаковых чисел
  • Int8Array
  • Uint32Array
  • DataView — гибкий доступ к данным с контролем порядка байтов
const buffer = new ArrayBuffer(4);
const view = new Uint8Array(buffer);

view[0] = 255;
view[1] = 16;
view[2] = 0;
view[3] = 128;

Uint8Array является де-факто стандартом для передачи данных в Web Crypto API, поскольку криптографические алгоритмы оперируют байтами.


Причина использования бинарного формата в Web Crypto API

Криптографические операции требуют строгого контроля над:

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

Строки в JavaScript не подходят для этих целей из-за:

  • UTF-16 кодирования внутри движка
  • неявных преобразований
  • зависимости от локали при некоторых операциях

Поэтому Web Crypto API использует только бинарные структуры:

  • ArrayBuffer
  • TypedArray
  • DataView

Преобразование строк в бинарные данные

Для работы с текстом используется TextEncoder и TextDecoder. Они обеспечивают предсказуемое преобразование строк в байтовые массивы и обратно.

const encoder = new TextEncoder();
const data = encoder.encode("Hello");

console.log(data); // Uint8Array

Результатом является Uint8Array, который напрямую совместим с Web Crypto API.

Обратное преобразование:

const decoder = new TextDecoder();
const text = decoder.decode(data);

Использование ArrayBuffer в Web Crypto API

Практически все методы crypto.subtle принимают ArrayBuffer или TypedArray.

Хеширование (SHA-256)

const encoder = new TextEncoder();
const data = encoder.encode("message");

const hashBuffer = await crypto.subtle.digest("SHA-256", data);

Результат digest — это ArrayBuffer, содержащий 32 байта хеша.

Для дальнейшей работы его обычно преобразуют:

const hashArray = Array.from(new Uint8Array(hashBuffer));

Шифрование и работа с AES-GCM

Алгоритмы симметричного шифрования также используют бинарные данные.

const encoder = new TextEncoder();
const keyMaterial = encoder.encode("secret-key");

Генерация ключа:

const key = await crypto.subtle.importKey(
  "raw",
  keyMaterial,
  { name: "AES-GCM" },
  false,
  ["encrypt", "decrypt"]
);

Шифрование:

const iv = crypto.getRandomValues(new Uint8Array(12));

const encrypted = await crypto.subtle.encrypt(
  {
    name: "AES-GCM",
    iv
  },
  key,
  encoder.encode("confidential data")
);

Здесь:

  • ivUint8Array
  • входные данные — Uint8Array
  • результат — ArrayBuffer

Преобразование между ArrayBuffer и TypedArray

ArrayBuffer не содержит методов доступа к данным, поэтому всегда используется обёртка.

const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);

TypedArray не копирует данные, а создаёт представление над той же областью памяти.


Различие slice и subarray

При работе с бинарными данными важно учитывать различие:

const original = new Uint8Array([1, 2, 3, 4]);

const sub = original.subarray(1, 3);
const copy = original.slice(1, 3);
  • subarray — создаёт новое представление без копирования
  • slice — создаёт копию данных

В криптографических операциях предпочтителен subarray для экономии памяти и повышения производительности.


Endianness и DataView

Некоторые криптографические и низкоуровневые операции требуют контроля порядка байтов.

DataView позволяет явно задавать endianess:

const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);

view.setUint16(0, 0x1234, true); // little-endian
view.setUint16(2, 0x1234, false); // big-endian

Это важно при реализации протоколов или совместимости с внешними системами.


Генерация случайных данных

Web Crypto API предоставляет криптографически стойкий генератор:

const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);

Результат — Uint8Array, который может использоваться как IV, соль или ключевой материал.


Передача данных между операциями Web Crypto

Типичный поток данных выглядит следующим образом:

  1. строка → TextEncoderUint8Array
  2. Uint8Arraycrypto.subtle операция
  3. результат → ArrayBuffer
  4. ArrayBufferUint8Array
  5. при необходимости → строка через TextDecoder

Типичные ошибки при работе с ArrayBuffer

Потеря данных при копировании

const a = new Uint8Array([1,2,3]);
const b = a.buffer;

b не является копией — это тот же буфер памяти.

Некорректное преобразование типов

crypto.subtle.digest("SHA-256", "text"); // ошибка

Строка недопустима — требуется ArrayBuffer или TypedArray.


Производительность и zero-copy подход

Web Crypto API спроектирован вокруг минимизации копирования данных. Использование:

  • subarray
  • прямых ArrayBuffer
  • повторного использования буферов

позволяет снизить нагрузку на GC и ускорить криптографические операции.


Совместимость типов внутри криптографического потока

Операция Вход Выход
digest ArrayBuffer / TypedArray ArrayBuffer
encrypt ArrayBuffer / TypedArray ArrayBuffer
decrypt ArrayBuffer / TypedArray ArrayBuffer
generateKey параметры CryptoKey

Практическая модель данных Web Crypto

Вся работа строится вокруг одной концепции:

любые данные → Uint8Array → ArrayBuffer → обратно Uint8Array

Эта модель обеспечивает:

  • предсказуемость
  • отсутствие неоднозначной кодировки
  • совместимость между алгоритмами
  • высокую производительность