Шифрование транспортного уровня в виртуальных частных сетях требует симметричных шифров, сочетающих высокую пропускную способность с надежной устойчивостью к микроархитектурным побочным каналам процессора. Долгие годы стандартом в корпоративных протоколах, таких как IPsec и OpenVPN, оставался алгоритм AES в режиме счетчика с аутентификацией Галуа (AES-GCM). Однако на аппаратных платформах, лишенных специализированных инструкций, AES-GCM демонстрирует снижение производительности и создает риски утечки данных.

Когда программная реализация выполняет раунды AES на встраиваемых маршрутизаторах, мобильных чипах или шлюзах без поддержки инструкций AES-NI или ARMv8 Crypto, вычисления полагаются на таблицы подстановок (S-boxes). Обращения к памяти создают колебания времени доступа к кэшу, позволяя злоумышленнику восстановить секретный ключ через анализ задержек. Кроме того, умножение GHASH в поле Галуа $\mathbb{F}_{2^{128}}$ создает высокую вычислительную нагрузку без поддержки умножителя без переноса (PCLMULQDQ).

Протокол WireGuard устранил зависимость от аппаратного блока AES, полностью стандартизировав использование ChaCha20-Poly1305 AEAD (RFC 8439). За счет архитектуры ARX (Add-Rotate-XOR) и математического клампинга ключей этот алгоритм обеспечивает стабильную работу с константным временем выполнения на любых типах процессоров.

Криптография без аппаратного ускорения AES

Чтобы понять причины отказа современных VPN от AES-GCM на разнородном оборудовании, рассмотрим работу блочных шифров. В основе AES лежит сеть подстановок-перестановок. Нелинейная операция SubBytes преобразует 8-битные входные значения через таблицу замен Rijndael S-box.

В программных реализациях разработчики объединяют эти операции в четыре таблицы поиска размером по 1 КБ каждая (Te0, Te1, Te2, Te3):

// Программный раунд AES с обращением к таблицам S-box
state[0] = Te0[(s0 >> 24)       ] ^ Te1[(s1 >> 16) & 0xff] ^ 
           Te2[(s2 >>  8) & 0xff] ^ Te3[(s3      ) & 0xff] ^ rk[0];

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

Шифр ChaCha20 полностью исключает обращения к таблицам в памяти. Это потоковый шифр, построенный на операциях ARX: сложение по модулю $2^{32}$ (Addition), циклический сдвиг на фиксированное число битов (Rotation) и побитовое исключающее ИЛИ (XOR). Инструкции ARX выполняются за фиксированное число тактов на стандартных АЛУ, что делает шифр неуязвимым для атак по времени кэша.

Архитектура ARX и матрица состояния ChaCha20 4x4

Шифр ChaCha20, созданный Дэниелом Бернштейном, работает с 512-битным внутренним состоянием, представленным в виде матрицы 4x4 из шестнадцати 32-битных беззнаковых слов:

+---------------+---------------+---------------+---------------+
|  Constant 0   |  Constant 1   |  Constant 2   |  Constant 3   |
|  0x61707865   |  0x3320646e   |  0x79622d32   |  0x6b206574   |
+---------------+---------------+---------------+---------------+
|  Key Word 0   |  Key Word 1   |  Key Word 2   |  Key Word 3   |
+---------------+---------------+---------------+---------------+
|  Key Word 4   |  Key Word 5   |  Key Word 6   |  Key Word 7   |
+---------------+---------------+---------------+---------------+
| Block Counter |    Nonce 0    |    Nonce 1    |    Nonce 2    |
+---------------+---------------+---------------+---------------+

Структура матрицы обеспечивает математическую строгость:

  • Слова 0–3: фиксированная 128-битная константа "expand 32-byte k", исключающая нулевое состояние матрицы.
  • Слова 4–11: 256-битный симметричный сессионный ключ.
  • Слово 12: 32-битный счетчик блоков, увеличивающийся для каждого 64-байтного блока гаммы.
  • Слова 13–15: 96-битный одноразовый номер (nonce).

Преобразование состояния основано на функции четверти раунда QR(a, b, c, d):

// Функция четверти раунда ChaCha20 Quarter-Round
#define ROTL32(v, n) (((v) << (n)) | ((v) >> (32 - (n))))
#define QR(a, b, c, d) { \
    a += b; d ^= a; d = ROTL32(d, 16); \
    c += d; b ^= c; b = ROTL32(b, 12); \
    a += b; d ^= a; d = ROTL32(d,  8); \
    c += d; b ^= c; b = ROTL32(d,  7); \
}

Полный цикл состоит из 20 раундов (10 раундов по столбцам и 10 раундов по диагоналям). После 20 раундов полученное состояние складывается со стартовой матрицей, а результат складывается по модулю 2 с 64 байтами открытого текста.

Поскольку функция QR задействует только регистры процессора и простые инструкции АЛУ, в ней отсутствуют условные переходы, зависящие от данных, и обращения к памяти.

Одноразовый аутентификатор Poly1305 и клампинг ключа

Одной конфиденциальности недостаточно: без аутентификации злоумышленник может модифицировать трафик или внедрять поддельные пакеты. ChaCha20 дополняется кодом аутентификации Poly1305, формируя схему AEAD.

Poly1305 представляет собой полиномиальный аутентификатор Картера-Вегмана над простым полем $\mathbb{F}_{2^{130}-5}$. Для вычисления имитовставки сообщение разбивается на 16-байтные блоки, каждый из которых выступает коэффициентом полинома:

$$A(r) = \left( \sum_{i=1}^{L} c_i \cdot r^{L-i+1} \right) \pmod{2^{130}-5} + s \pmod{2^{128}}$$

Одноразовая 256-битная пара ключей $(r, s)$ вырабатывается из нулевого блока гаммы, сгенерированного ChaCha20 для конкретного пакета:

  • Слова 0–3 формируют 128-битный ключ $r$.
  • Слова 4–7 формируют 128-битный секрет $s$.

Для исключения переполнения разрядов при умножении ключ $r$ проходит процедуру клампинга:

// Клампинг ключа Poly1305 согласно стандарту RFC 8439
r[3] &= 15;  r[7] &= 15;  r[11] &= 15; r[15] &= 15;
r[4] &= 252; r[8] &= 252; r[12] &= 252;

Клампинг обнуляет 22 бита в 16-байтном ключе $r$. Это математически ограничивает величину промежуточных произведений ($r < 2^{124}$), позволяя 32- и 64-битным регистрам вычислять остаток от деления сдвигами и сложением без межразрядного переноса.

Кадрирование пакетов WireGuard в сравнении с RFC 8439

Спецификация WireGuard оптимизирует кадрирование данных для сетевого туннелирования:

Формат RFC 8439:      [ Счетчик 32 бит ] [ Случайный Nonce 96 бит ] -> Аутентификация AAD + шифротекста
Формат WireGuard:     [ Заполнение 32 нуля ] [ Счетчик 64 бит ]     -> Счетчик в открытом заголовке

В реализации WireGuard:

  1. 64-битный счетчик: Заголовок пакета данных содержит открытый 64-битный счетчик (receiver_index и counter). Он начинается с 0 и строго инкрементируется с каждым отправленным пакетом.
  2. Формирование 96-битного Nonce: 96-битный nonce образуется помещением 32 нулевых битов в старшую часть и 64-битного счетчика в младшую часть в формате little-endian.
  3. Лимиты ротации ключей: Повторное использование nonce с одним ключом разрушает безопасность Poly1305, раскрывая ключ $r$. Для исключения этого WireGuard принудительно обновляет сессионные ключи после передачи $2^{60}$ пакетов или каждые 120 секунд.

Дополнительные аутентифицируемые данные (AAD) в штатных пакетах данных не передаются, поскольку 16-байтный тег Poly1305 защищает полезную нагрузку, а индекс получателя верифицируется через таблицу Cryptokey Routing.

Векторное ускорение SIMD: AVX2, AVX-512 и ARM NEON

Ключевое архитектурное преимущество ChaCha20 — параллелизуемость. В отличие от режимов сцепления блоков, блоки гаммы ChaCha20 рассчитываются независимо друг от друга.

Это позволяет задействовать векторные регистры процессора:

  • ARM NEON: 128-битные регистры на мобильных процессорах обрабатывают две четверти раунда одновременно.
  • x86_64 AVX2: 256-битные регистры параллельно генерируют четыре 64-байтных блока (256 байт гаммы за итерацию).
  • x86_64 AVX-512: 512-битные регистры обрабатывают восемь блоков одновременно (512 байт за итерацию).

Поскольку векторные инструкции выполняются с детерминированной задержкой, скорость обработки превышает 3 ГБ/с на ядро процессора без малейших рисков утечек по сторонним каналам.

Оптимизация обработки пакетов и безопасность памяти в RiceGuard

В сетевых шлюзах с высокой нагрузкой RiceGuard реализует многопоточную конвейерную обработку ChaCha20-Poly1305:

  1. Многоядерное распределение: Интеграция с подсистемой padata распределяет входящие сетевые пакеты по всем ядрам ЦП с последующей упорядоченной доставкой в сетевой стек.
  2. Константная проверка тега: Тег Poly1305 сверяется функцией crypto_memneq. Если имитовставка неверна, пакет отбрасывается до начала расшифрования полезной нагрузки.
  3. Очистка буферов памяти: Промежуточные подключи ($r, s$) очищаются в памяти функцией memzero_explicit сразу после завершения валидации.

Благодаря структуре ARX, константному времени вычислений и контролю памяти ChaCha20-Poly1305 формирует высокоскоростной криптографический фундамент современных VPN.

Скачать VPNRice