Тема 05

Поточные шифры: LFSR, RC4 и ChaCha20

Поточные шифры: LFSR, RC4 и ChaCha20

Блочный шифр обрабатывает данные порциями фиксированной длины, а поточный шифр (англ. stream cipher) вырабатывает последовательность бит — ключевой поток (англ. keystream) — и складывает её с сообщением побитово. Такая схема не требует дополнения, работает с данными произвольной длины и не задерживает выдачу: байт шифртекста появляется сразу, как только пришёл байт открытого текста. Именно поэтому поточные шифры применяются там, где важна задержка и объём: в мобильной связи, в спутниковых каналах, в TLS для больших потоков, в протоколах защищённой передачи медиа.

Идея стара как сама криптография с ключом, но её практическая судьба извилиста. Первые промышленные поточные шифры строились на регистрах сдвига с линейной обратной связью — они дёшевы в кремнии и хорошо изучены математически. Оказалось, что именно эта изученность и губит: линейность позволяет восстановить регистр по короткому фрагменту выхода. Следующее поколение — RC4, простой и быстрый, — прожило в протоколах три десятилетия и было запрещено после серии атак, показавших, что «статистически хороший» не значит «стойкий». Современный стандарт — ChaCha20 с его арифметикой сложения, вращения и XOR, — соединяет скорость программной реализации со стойкостью, к которой не применим линейный криптоанализ.

Тема устроена как разбор трёх поколений: сначала — общая схема и её требования, затем — LFSR с алгоритмом Берлекэмпа — Мэсси и взломом регистра по известному началу сообщения, далее — RC4 и его смещения, наконец — ChaCha20 по RFC 8439 с разбором тестового вектора. Завершает тему то, ради чего она, собственно, нужна: почему поточный шифр без аутентификации опасен (ковкость XOR) и почему повтор пары «ключ, nonce» уничтожает защиту полностью. Обе ошибки — не теоретические: на них построены атаки на WEP, на WPA2 и на несколько поколений TLS.

Паспорт темы. Нужно знать: темы 0, 2. Освоите: описывать LFSR и восстанавливать его по выходу; объяснять смещения RC4; реализовывать ChaCha20 по RFC 8439; формулировать требование уникальности пары «ключ, nonce»; показывать ковкость поточного шифрования. Проверяет: ЛР 5. Минимум: разделы 1–4. Углубление: раздел 5.

Идея поточного шифра

Ключевой поток и операция XOR

Пусть в распоряжении есть секретный ключ k и открытое значение nonce. Генератор потока (англ. keystream generator) вырабатывает последовательность s₁, s₂, s₃, …, и шифрование сводится к трём формулам: cᵢ = mᵢ ⊕ sᵢ, mᵢ = cᵢ ⊕ sᵢ и s = PRF(k, nonce).

Расшифрование совпадает с шифрованием, потому что XOR обратим сам по себе: mᵢ ⊕ sᵢ ⊕ sᵢ = mᵢ. Это достоинство и одновременно источник главной уязвимости: у операции нет никакой памяти и никакой нелинейности, поэтому всё, что нужно знать противнику, — ключевой поток. Схема ниже показывает поток в целом и подчёркивает, что секрет находится целиком в генераторе: как только поток известен, сообщение восстановлено независимо от длины ключа.

Схема поточного шифра
Ключ и nonce порождают ключевой поток, который складывается с сообщением побитово; секрет целиком сосредоточен в генераторе

Требования к потоку формулируются так. Во-первых, он должен быть неотличим от случайного для противника, не знающего ключ: иначе статистика выдаст структуру и сократит поиск ключа. Во-вторых, он не должен повторяться: если один и тот же поток применён к двум сообщениям, XOR шифртекстов даёт XOR открытых текстов, и ключ из уравнения исчезает. В-третьих, восстановление фрагмента потока не должно позволять восстановить ключ или остальную часть потока, — это свойство называют стойкостью к восстановлению состояния.

От блочного шифра поточный отличается и по модели применения. Блочный шифр в режиме CTR тоже превращается в поточный: он шифрует счётчик и получает блоки потока. Разница в том, что поточный шифр спроектирован для этого изначально — у него нет «режима», нет зависимости от длины блока и нет дополнения. В теме 7 будет показано, что CTR из блочного шифра требует тех же предосторожностей с nonce, что и ChaCha20, и ломается ровно так же.

Синхронные и самосинхронизирующиеся шифры

Синхронный поточный шифр (англ. synchronous stream cipher) вырабатывает поток независимо от сообщения: генератор видит только ключ и nonce, а открытый текст на него не влияет. ChaCha20, RC4 и все LFSR-конструкции из этой темы — синхронные. Их достоинство — отсутствие размножения ошибок: искажение одного бита шифртекста портит ровно один бит открытого текста, что важно для каналов с помехами. Недостаток — потеря синхронизации: если приёмник отстал на байт, весь дальнейший текст расшифровывается в мусор, пока синхронизация не восстановлена, и приходится передавать nonce или метку позиции заново.

Самосинхронизирующийся шифр (англ. self-synchronizing или asynchronous stream cipher) строит поток из предыдущих байт шифртекста. Здесь открытый текст входит в обратную связь, и приёмник «догоняет» передатчик автоматически: после потери n байт достаточно n байт, чтобы состояние восстановилось. Плата — размножение ошибок: один испорченный байт шифртекста портит несколько байт открытого текста. Исторически так работал режим CFB блочного шифра; в современной практике самосинхронизирующиеся схемы почти вытеснены AEAD-режимами, которые решают задачу иначе — обнаружением искажения вместо его прощения.

Практический вывод для выбора: если канал надёжен и потеря байт маловероятна (TLS поверх TCP), синхронная схема проще и быстрее; если канал теряет данные (спутник, радио), нужна либо самосинхронизация, либо кадрирование с явной нумерацией.

Требования к паре «ключ, nonce»

Главное требование ко всей конструкции формулируется одной строкой: пара (ключ, nonce) не должна повторяться ни при каких обстоятельствах. Не «желательно», а обязательно, потому что при повторе поток совпадает и схема теряет конфиденциальность мгновенно, без всякого криптоанализа.

Отсюда две практики. Первая — счётчик: если обе стороны знают номер сообщения, nonce выбирается как номер, и уникальность гарантирована структурой протокола. Вторая — случайный nonce достаточной длины: при 96-битном случайном nonce вероятность коллизии после 2^32 сообщений одного ключа составляет около 2^-33, что считается приемлемым, а после 2^48 — уже около 2^-1, то есть катастрофой. Именно поэтому в рекомендациях фигурируют пределы на число сообщений под одним ключом.

Важно различать роли ключа и nonce. Ключ — секрет, его знают только участники. Nonce — не секрет, он передаётся открыто, но должен быть уникален. Если nonce сделан секретным, это не добавляет стойкости, но создаёт иллюзию, что его можно переиспользовать незаметно. Если nonce уникален, но предсказуем, схема остаётся стойкой — предсказуемость опасна только в схемах, где nonce входит в ключ иначе (например, в GCM при неудачной реализации).

Регистры сдвига с линейной обратной связью

Устройство и период

Регистр сдвига с линейной обратной связью (англ. linear feedback shift register, LFSR) — это последовательность из L бит, которая на каждом такте сдвигается, а в освободившуюся позицию записывается XOR нескольких выбранных бит. Набор позиций, участвующих в обратной связи, называют отводами (англ. taps).

Формально последовательность удовлетворяет линейной рекуррентности порядка L:

s(n) = c₁·s(n−1) ⊕ c₂·s(n−2) ⊕ … ⊕ c_L·s(n−L)

Коэффициенты cᵢ задают многочлен связей C(x) = 1 + c₁x + … + c_L x^L. Если этот многочлен примитивен над GF(2), период последовательности максимален и равен 2^L − 1 — все ненулевые состояния регистра обходятся прежде, чем последовательность повторится. Именно поэтому LFSR так любят в железе: L триггеров и несколько XOR дают гигантский период при минимальной стоимости.

Схема ниже показывает устройство регистра с отводами и обратной связью.

Регистр сдвига с линейной обратной связью
На каждом такте регистр сдвигается, а в старшую позицию записывается XOR выбранных отводов; при примитивном многочлене связей период равен 2^L − 1

Реализация на Python проста, и её стоит написать, чтобы увидеть, насколько мало в ней «криптографии»:

def lfsr_sequence(taps, init, n):
    """taps — номера отводов (1 = предыдущий бит), init — начальное состояние."""
    s = list(init)
    while len(s) < n:
        b = 0
        for t in taps:
            b ^= s[len(s) - t]
        s.append(b)
    return s[:n]

Проверка периода — отдельная задача: последовательность нужно прогонять до повторения состояния, и для L = 20 это около миллиона шагов, а для L = 64 — уже недостижимо. Поэтому на практике максимальность периода не проверяют экспериментально, а обеспечивают выбором примитивного многочлена из таблицы.

Линейная сложность и алгоритм Берлекэмпа — Мэсси

Линейная сложность последовательности — длина самого короткого LFSR, который её порождает. Для случайной последовательности она примерно равна половине её длины, для LFSR-последовательности — равна L. Это и есть мера её предсказуемости.

Алгоритм Берлекэмпа — Мэсси находит минимальный многочлен связей по первым 2L битам последовательности за O(L²) операций 3. Идея: алгоритм последовательно наращивает предполагаемый регистр, и при первом расхождении предсказания с наблюдением корректирует многочлен так, чтобы исправить расхождение с минимальным увеличением длины. Классический результат Мэсси состоит в том, что такой жадный алгоритм оптимален: он всегда даёт регистр минимальной длины.

Практическое следствие для криптографии жёсткое: любой LFSR вскрывается по 2L битам выхода за квадратичное время. Для L = 64 это 128 бит ключевого потока и несколько тысяч операций — работа, которую машина выполняет незаметно. Схема «взять LFSR с большим периодом и считать его шифром» не даёт стойкости вообще: период 2^64 впечатляет, а стойкость измеряется не периодом, а линейной сложностью.

В лабораторной работе это демонстрируется на учебном письме: известен заголовок (От:, Кому:, Тема:), из него получается фрагмент ключевого потока длиной в сотни бит, алгоритм находит L = 53 и многочлен связей, после чего поток достраивается на всю длину письма и текст восстанавливается целиком. Никакого перебора ключей не происходит.

Почему LFSR непригоден и что строят вместо него

Односложный ответ «потому что линейный» недостаточен: линейность сама по себе не порок, AES тоже линеен над GF(2) в одном смысле и совершенно нелинеен в другом. Порок в том, что LFSR линеен над GF(2) по отношению к своему состоянию: выход — линейная функция состояния, а состояние — линейная функция предыдущего состояния. Значит, по выходу можно составить систему линейных уравнений и решить её.

Обходной путь — нелинейная комбинация нескольких LFSR. Фильтрующая функция берёт биты с выходов нескольких регистров и пропускает их через нелинейную булеву функцию; комбинирующая функция смешивает выходы с разными периодами. Такие схемы (например, A5/1 в GSM с тремя регистрами длин 19, 22 и 23 бита) выглядят убедительно, но их стойкость не доказана, а практика оказалась суровой: A5/1 был вскрыт, и атаки требуют лишь нескольких секунд известного ключевого потока, полученного по открытому каналу.

Современное решение — вообще отказаться от линейной структуры и строить поток из арифметических операций, нелинейных относительно GF(2): сложения по модулю 2^32, циклических сдвигов и XOR. Так устроены Salsa20 и ChaCha20, и именно отсутствие линейности делает линейный криптоанализ к ним неприменимым. Альтернатива — LFSR с нелинейной фильтрацией, сертифицированная в конкретном стандарте (например, «Кузнечик» и «Магма» в ГОСТ используют другие конструкции), но для новой разработки выбирают ARX.

RC4 и его смещения

Алгоритм

RC4 (англ. Rivest Cipher 4, также ARC4) появился в 1987 году как торговый секрет и был опубликован в 1994-м. Он состоит из двух частей: планировщика ключа (англ. key scheduling algorithm, KSA), который перемешивает 256-байтовый массив S ключом, и генератора (англ. pseudo-random generation algorithm, PRGA), который на каждом шаге меняет местами два элемента и выдаёт один байт.

def rc4_ksa(key):
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    return S


def rc4_prga(S, n):
    out = bytearray()
    i = j = 0
    for _ in range(n):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        out.append(S[(S[i] + S[j]) % 256])
    return bytes(out)

Алгоритм красив своей простотой: два счётчика, один массив, три сложения и обмен. Именно поэтому он был встроен во всё, что передавало данные: WEP, WPA-TKIP, SSL/TLS, Microsoft Office, PDF, Lotus Notes.

Смещения в ключевом потоке

Простота обернулась смещениями — отклонениями от равномерного распределения в первых байтах потока. Первый байт выдаёт себя сильнее всего: P(S[1] = 0) ≈ 2/256, то есть вдвое больше ожидаемого, и причина в том, что KSA не успевает перемешать массив — при i = 1 обмен затрагивает только что тронутые позиции. На этом смещении построена атака FMS против WEP 89.

Второй байт ведёт себя так же: P(Z₂ = 0) ≈ 2/256 5. Дальше распределение выравнивается, но не полностью: существуют долгоживущие смещения, которые накапливаются при большом объёме данных. Известные атаки перечислены в таблице.

АтакаЧто используетУсловиеРезультат
FMS (2001)смещение P(Z₁ = 0)известен IV и первый байт шифртекставосстановление ключа WEP по миллионам пакетов
Tews–Weinmann–Pyshkin (2007)корреляции KSAте же данные104-битный WEP-ключ за минуту 4
Fluhrer–McGrew (2000)долгоживущие смещениямного потоков с разными IVстатистическое различение шифртекста 5
AlFardan et al. (2013)смещения в начале потокаTLS с множеством сессийвосстановление cookie за 2^26 запросов 10
Biham–Carmeli (2008)внутреннее состояниеизвестен фрагмент состояниявосстановление ключа по состоянию

Обратите внимание на закономерность: все атаки опираются на статистику, а не на полное решение системы уравнений. Это типично для поточных шифров — малейшее смещение в потоке превращается в оракул, если противник может получать много шифртекстов под разными nonce.

Запрет в TLS и уроки

История RC4 в TLS показательна. В 2013 году атаки стали практическими, и RFC 7465 запретил наборы шифров с RC4 6. Но запрет был выпущен в 2015 году, а исключены наборы из популярных браузеров только к 2016-му; ещё несколько лет RC4 оставался включённым как «совместимый вариант» и включался при сбое рукопожатия. Это типичная динамика: алгоритм живёт в коде дольше, чем в стандартах.

Урок первый: статистические дефекты не лечатся увеличением ключа. RC4 с 256-битным ключом так же уязвим, как с 40-битным, потому что атака использует структуру KSA и первые байты потока, а не длину ключа.

Урок второй: отбрасывание первых байт потока (распространённая «заплатка» — отбросить 256 или 768 байт) уменьшает смещения, но не устраняет их и не даёт гарантий. Это ровно тот случай, когда инженерная мера маскирует проблему, оставляя систему в неизвестном состоянии.

Урок третий: «прошло все статистические тесты» — не аргумент. RC4 проходил тесты NIST и умирал от атак, которые эти тесты не проверяют, потому что они смотрят на распределение одного потока, а не на корреляции между потоками с разными IV.

ChaCha20

Состояние и четверть-раунд

ChaCha20 предложен Дэниелом Бернстайном в 2008 году как развитие Salsa20 2; семейство вошло в портфель eSTREAM и стало одним из двух рекомендованных поточных шифров 11. Состояние — матрица 4×4 из 32-битных слов, всего 16 слов:

константы  c₀ c₁ c₂ c₃     "expand 32-byte k"
ключ       k₀ k₁ k₂ k₃
ключ       k₄ k₅ k₆ k₇
счётчик    b₀ | nonce n₀ n₁ n₂

Первые четыре слова — константы, следующие восемь — ключ, последние четыре — счётчик (одно слово) и nonce (три слова). Число 20 в названии — число раундов: десять двойных раундов, каждый из которых состоит из четырёх четверть-раундов над столбцами и четырёх над диагоналями.

Четверть-раунд (англ. quarter round) — сердце конструкции. Он берёт четыре слова, выполняет четыре сложения по модулю 2^32, четыре XOR и четыре циклических сдвига:

def rotl32(x, n):
    return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF


def quarter_round(a, b, c, d):
    a = (a + b) & 0xFFFFFFFF; d ^= a; d = rotl32(d, 16)
    c = (c + d) & 0xFFFFFFFF; b ^= c; b = rotl32(b, 12)
    a = (a + b) & 0xFFFFFFFF; d ^= a; d = rotl32(d, 8)
    c = (c + d) & 0xFFFFFFFF; b ^= c; b = rotl32(b, 7)
    return a, b, c, d

Такая схема называется ARX — от англ. add, rotate, xor. Схема ниже показывает четверть-раунд как цепочку из четырёх шагов.

Четверть-раунд ChaCha20
Четыре сложения по модулю 2^32, четыре XOR и четыре циклических сдвига на 16, 12, 8 и 7 бит образуют нелинейное перемешивание

Почему это работает лучше RC4 и LFSR. Сложение по модулю 2^32 нелинейно относительно GF(2): перенос из младшего разряда в старший зависит от данных, и именно переносы разрушают линейные соотношения между битами. Циклические сдвиги на разные величины обеспечивают диффузию: бит, попавший в результат одного шага, за четыре шага влияет уже на несколько слов. После двадцати раундов каждый выходной бит зависит от всех входных.

Счётчик, nonce и число сообщений

Одно слово состояния — счётчик блоков, три слова — nonce. Для 96-битного nonce и 32-битного счётчика предельный объём одного сообщения равен 2^32 блоков по 64 байта, то есть 256 ГиБ. Это ограничение существенно: при переполнении счётчика поток начинает повторяться, и схема ломается ровно так же, как при повторе nonce. Реализации обязаны либо ограничивать длину сообщения, либо использовать вариант с 64-битным счётчиком и 64-битным nonce.

После двадцати раундов состояние складывается с исходным (это называется «финальным сложением») и выдаётся как 64 байта потока. Финальное сложение не декоративно: без него раундовая функция была бы обратима, и по выходу можно было бы пройти раунды назад к ключу.

Разбор тестового вектора

RFC 8439 приводит полный тестовый вектор для одного блока 1. Ключ — байты от 00 до 1f, nonce — 00 00 00 00 00 00 00 4a 00 00 00 00, счётчик равен 1. Открытый текст — строка из 114 байт, шифртекст по стандарту приведён рядом:

открытый текст
Ladies and Gentlemen of the class of '99: If I could offer you
only one tip for the future, sunscreen would be it.

шифртекст (RFC 8439, раздел 2.4.2)
6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0b
f91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d
807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937
365af90bbf74a35be6b40b8eedf2785e42874d

Сверка с библиотекой cryptography подтверждает эти значения побайтово:

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms

key = bytes(range(32))
nonce = (1).to_bytes(4, "little") + bytes.fromhex("000000000000004a00000000")
pt = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."

enc = Cipher(algorithms.ChaCha20(key, nonce), mode=None).encryptor()
print(enc.update(pt).hex())

Обратите внимание на несовпадение форматов: RFC 8439 передаёт счётчик отдельным параметром и 12-байтный nonce, а cryptography ожидает 16-байтное значение, в котором первые четыре байта — счётчик в little-endian. Такие расхождения в интерфейсах — обычная причина ошибок; правильный порядок действий — сверить с тестовым вектором, а не догадываться по названию параметра.

Ошибки применения

Ковкость и подмена битов

XOR линеен, и это делает поточный шифр ковким (англ. malleable): противник, не знающий ключа, может изменить открытый текст предсказуемым образом, изменив шифртекст в нужных позициях. Если он знает структуру сообщения, ему достаточно вычислить разность Δ = m ⊕ m' и применить c' = c ⊕ Δ.

Покажем на учебном «платёжном поручении». Пусть в сообщении есть поле сумма=1000.00, а противник хочет сумма=9000.00. Он не знает поток s, но знает, что c = m ⊕ s, а значит c' = m' ⊕ s = c ⊕ m ⊕ m'. Разность вычисляется по известному формату поля, и получатель увидит валидное по форме поручение с другой суммой. Схема ниже показывает это тождество.

Ковкость поточного шифра
Противник вычисляет разность двух открытых текстов и применяет её к шифртексту; поток сокращается, ключ в подмене не участвует

Защита — аутентификация: к шифртексту добавляется код аутентификации сообщения (MAC) или используется аутентифицированное шифрование (AEAD), которое проверяет целостность до выдачи открытого текста. Важен порядок: сначала проверить тег, потом расшифровывать. Обратный порядок даёт оракул, на котором построены атаки на TLS и SSH (подробно — в теме 9).

Повтор nonce и его последствия

Повтор пары «ключ, nonce» — самая разрушительная ошибка в поточной криптографии. Если два сообщения зашифрованы одним потоком, то c₁ ⊕ c₂ = (m₁ ⊕ s) ⊕ (m₂ ⊕ s) = m₁ ⊕ m₂.

Поток сокращается, и противник получает XOR двух открытых текстов. Для текстов на естественном языке этого достаточно: в ASCII пробел равен 0x20 и «обнуляет» регистр буквы, поэтому XOR отчётливо показывает границы слов; для русского текста в UTF-8 структура другая, но избыточность всё равно позволяет восстановление.

Схема ниже показывает сокращение потока при повторе nonce.

Повтор nonce
При одном и том же ключе и nonce потоки совпадают, и XOR двух шифртекстов равен XOR открытых текстов — конфиденциальность теряется полностью

Дальнейшие шаги зависят от того, что известно. Если известно одно из сообщений (например, стандартный заголовок протокола), второе восстанавливается целиком. Если известно частично, применяется перетаскивание по предположению: угадывается слово в одном тексте, проверяется осмысленность второго, гипотеза уточняется. Для двух текстов на одном языке это десятки минут ручной работы, для автоматической атаки — секунды.

В AEAD последствия тяжелее. В ChaCha20-Poly1305 ключ Poly1305 выводится из первого блока потока, поэтому повтор nonce означает повтор ключа аутентификации, и противник может подделать тег для любого сообщения с тем же nonce. В AES-GCM повтор nonce позволяет восстановить ключ аутентификации H и подделывать теги для всех сообщений. Именно это произошло в атаке KRACK на WPA2: принудительная реинициализация nonce превращала защищённый протокол в открытый 7.

Чем это лечится

Три меры, и все три обязательны в правильной реализации 12. Первая — AEAD вместо «шифр плюс отдельный MAC, собранный вручную»: библиотека проверяет порядок операций и не даёт расшифровать данные с неверным тегом. Вторая — дисциплина nonce: счётчик с явной проверкой на переполнение либо случайный nonce с ограничением на число сообщений под ключом. Третья — ротация ключей: даже при идеальной дисциплине стоит менять ключ задолго до исчерпания счётчика, потому что любая ошибка в учёте сообщений накапливается.

Полезно помнить, что все три меры — про целостность и уникальность, а не про «силу алгоритма». Поточный шифр с 256-битным ключом и повторяющимся nonce слабее, чем 128-битный AEAD с аккуратным счётчиком. Именно этот сдвиг внимания — от примитива к протоколу — и составляет главный практический вывод темы.

Итоги темы

Поточный шифр сводит шифрование к XOR с ключевым потоком, и вся его стойкость сосредоточена в генераторе потока. Синхронные схемы не размножают ошибки, но требуют точной синхронизации; самосинхронизирующиеся восстанавливаются сами, но размножают искажения. В обоих случаях пара «ключ, nonce» должна быть уникальной, иначе поток повторяется и конфиденциальность исчезает без всякого криптоанализа.

Первое поколение поточных шифров строилось на LFSR. Эти регистры дешевы, имеют огромный период и хорошо изучены — и именно поэтому непригодны: линейная структура позволяет восстановить регистр по 2L битам выхода алгоритмом Берлекэмпа — Мэсси за квадратичное время. Нелинейные комбинации нескольких LFSR не дают доказуемой стойкости, что подтвердила история A5/1.

RC4 прожил три десятилетия и был запрещён из-за статистических смещений в начале потока: атаки FMS, Tews–Weinmann–Pyshkin, AlFardan и другие показали, что «простой и быстрый» не означает «стойкий», а увеличение ключа не помогает, когда атака использует структуру алгоритма. Запрет в RFC 7465 закрыл тему, но код с RC4 встречался ещё годы.

ChaCha20 — современный стандарт: ARX-арифметика, нелинейная относительно GF(2), двадцать раундов, счётчик и nonce в состоянии, тестовый вектор RFC 8439 для побайтовой проверки. Его слабое место — не алгоритм, а дисциплина применения: переполнение счётчика и повтор nonce ломают и его.

Следующая тема переходит к блочным шифрам — DES и AES, — где данные обрабатываются порциями и где требование к nonce появляется снова, но уже в режимах: CBC, CTR, GCM. Ковкость XOR и повтор nonce там выглядят иначе, но приводят к тем же последствиям, а атака padding oracle в теме 7 покажет, как ошибка обработки превращает стойкий шифр в оракул расшифрования.

Литература

  1. Nir Y., Langley A. ChaCha20 and Poly1305 for IETF Protocols. — RFC 8439, IETF, 2018. https://www.rfc-editor.org/rfc/rfc8439
  2. Bernstein D. J. ChaCha, a Variant of Salsa20. — Workshop Record of SASC 2008: The State of the Art of Stream Ciphers, 2008. https://cr.yp.to/chacha/chacha-20080128.pdf
  3. Massey J. L. Shift-Register Synthesis and BCH Decoding. — IEEE Transactions on Information Theory, 1969, Т. 15, № 1, С. 122–127. DOI: 10.1109/TIT.1969.1054260
  4. Tews E., Weinmann R.-P., Pyshkin A. Breaking 104 Bit WEP in Less Than 60 Seconds. — Information Security Applications (WISA 2007), Springer, 2007, С. 188–202. DOI: 10.1007/978-3-540-77535-5_14
  5. Fluhrer S., McGrew D. Statistical Analysis of the Alleged RC4 Keystream Generator. — Fast Software Encryption (FSE 2000), Springer, 2001, С. 19–30. DOI: 10.1007/3-540-44706-7_2
  6. Popov A. Prohibiting RC4 Cipher Suites. — RFC 7465, IETF, 2015. https://www.rfc-editor.org/rfc/rfc7465
  7. Vanhoef M., Piessens F. Key Reinstallation Attacks: Forcing Nonce Reuse in WPA2. — Proceedings of the 2017 ACM SIGSAC Conference on Computer and Communications Security (CCS), 2017, С. 1313–1328. DOI: 10.1145/3133956.3134027
  8. Fluhrer S., Mantin I., Shamir A. Weaknesses in the Key Scheduling Algorithm of RC4. — Selected Areas in Cryptography (SAC 2001), Springer, 2001, С. 1–24. DOI: 10.1007/3-540-45537-X_1
  9. Borisov N., Goldberg I., Wagner D. Intercepting Mobile Communications: The Insecurity of 802.11. — Proceedings of the 7th Annual International Conference on Mobile Computing and Networking (MobiCom), 2001, С. 180–189. DOI: 10.1145/381677.381695
  10. AlFardan N. J., Bernstein D. J., Paterson K. G., Poettering B., Schuldt J. C. N. On the Security of RC4 in TLS. — 22nd USENIX Security Symposium, 2013, С. 327–338. https://www.usenix.org/conference/usenixsecurity13/technical-sessions/paper/alfardan
  11. Babbage S., et al. The eSTREAM Portfolio in 2012. — eSTREAM project report, 2012. https://www.ecrypt.eu.org/stream/
  12. Aumasson J.-P. Serious Cryptography: A Practical Introduction to Modern Encryption. 2nd ed. — No Starch Press, 2024.