CTR (Counter) режим относится к потоковым режимам шифрования и превращает блочный алгоритм AES в потоковый шифр, где каждый блок данных обрабатывается независимо, а результат зависит от комбинации ключа и счётчика.
Ключевая идея заключается в том, что вместо прямого шифрования блока данных AES используется результат шифрования специального значения — комбинации nonce и возрастающего счётчика. Полученный поток псевдослучайных байт затем применяется к данным через операцию XOR.
Алгоритм строится вокруг генерации ключевого потока:
Формально:
plaintext ⊕ AES(key, counter_i) = ciphertext_i
Именно отсутствие прямой зависимости между блоками делает CTR режим параллелизуемым.
В Web Crypto API режим AES-CTR задаётся через объект:
{
name: "AES-CTR",
counter: Uint8Array,
length: number
}
16-байтовый буфер, представляющий начальное состояние счётчика.
Он состоит из двух логических частей:
Важно: библиотека не генерирует nonce автоматически — его обязан задать разработчик.
Количество бит, отведённых под счётчик.
Чем больше длина счётчика, тем меньше пространство nonce, и наоборот.
WebCrypto требует явного создания ключа через
SubtleCrypto.
const key = await crypto.subtle.generateKey(
{
name: "AES-CTR",
length: 256
},
true,
["encrypt", "decrypt"]
);
Особенности:
const encoder = new TextEncoder();
const data = encoder.encode("Секретное сообщение");
const counter = crypto.getRandomValues(new Uint8Array(16));
const encrypted = await crypto.subtle.encrypt(
{
name: "AES-CTR",
counter: counter,
length: 64
},
key,
data
);
Результат:
CTR не различает операции шифрования и расшифрования:
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-CTR",
counter: counter,
length: 64
},
key,
encrypted
);
const decoded = new TextDecoder().decode(decrypted);
Ключевые требования:
Любое отклонение приводит к полностью некорректному результату.
CTR режим можно рассматривать как генератор псевдослучайного потока байт:
Однако Web Crypto API не предоставляет встроенного stream API для AES-CTR, поэтому потоковую обработку приходится реализовывать вручную:
Самая опасная ошибка при работе с CTR — повторное использование одинакового counter с тем же ключом.
Если:
то ключевой поток полностью совпадает.
Это приводит к:
ciphertext1 ⊕ ciphertext2 = plaintext1 ⊕ plaintext2
В результате возможно восстановление исходных данных без знания ключа.
Поэтому:
Обычно 16 байт разбиваются так:
Пример:
const counter = new Uint8Array(16);
crypto.getRandomValues(counter.subarray(0, 12));
counter[15] = 1;
При шифровании больших данных счётчик увеличивается автоматически внутри алгоритма.
CTR режим обладает важным свойством:
Это делает его предпочтительным для:
В отличие от CBC, где каждый блок зависит от предыдущего, CTR не имеет цепочки зависимостей.
Несколько важных деталей:
encrypt — независимая операцияТакже стоит учитывать:
CTR обеспечивает только конфиденциальность, но не целостность.
Это означает:
Пример эффекта:
если изменить байт ciphertext, изменится ровно соответствующий байт plaintext
Поэтому CTR почти всегда комбинируется с:
Режим используется, когда требуется:
Он часто встречается в:
При этом почти всегда требует дополнительного слоя аутентификации данных.