Шифрование туннельного уровня через ChaCha

Proposal 153
Open
Author chisana
Created 2019-08-04
Last Updated 2019-08-05

Обзор

Это предложение основывается на изменениях из предложения 152: Туннели ECIES.

Только туннели, построенные через узлы, поддерживающие форматы BuildRequestRecord для туннелей ECIES-X25519, могут реализовать эту спецификацию.

Эта спецификация требует использования формата Tunnel Build Options для указания типа шифрования уровня туннеля и передачи ключей для уровня AEAD.

Цели

Целями данного предложения являются:

  • Замена AES256/ECB+CBC на ChaCha20 для установленного шифрования туннельного IV и уровня
  • Использование ChaCha20-Poly1305 для AEAD-защиты между узлами
  • Быть неотличимым от существующего шифрования уровня туннеля для участников, не использующих туннель
  • Не изменять общую длину туннельного сообщения

Обработка установленного туннельного сообщения

Этот раздел описывает изменения для:

  • Предобработки + шифрования на Исходящем и Входящем шлюзе
  • Шифрования + постобработки для участника
  • Шифрования + постобработки на Исходящей и Входящей конечной точке

Для получения обзора текущей обработки туннельных сообщений, см. Tunnel Implementation спецификацию.

Обсуждаются только изменения для маршрутизаторов, поддерживающих шифрование уровня через ChaCha20.

Не рассматриваются изменения для смешанных туннелей с шифрованием уровня через AES, пока не будет разработан безопасный протокол для преобразования 128-битного IV AES в 64-битный нонс ChaCha20. Фильтры Блума гарантируют уникальность для полного IV, но первая половина уникальных IV может быть идентичной.

Это означает, что шифрование уровня должно быть единообразным для всех узлов в туннеле и устанавливаться через опции построения туннеля в процессе его создания.

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

Упоминаемый в этом предложении nonceKey заменяет используемый в шифровании уровня через AES IVKey. Он генерируется с использованием того же KDF из предложения 152.

Шифрование AEAD сообщений от узла к узлу

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

Туннельные сообщения должны уменьшить длину внутреннего зашифрованного фрейма на 16 байт, чтобы разместить MAC Poly1305.

AEAD не может быть применен непосредственно к сообщениям, так как для исходящих туннелей требуется итеративное дешифрование. Итеративное дешифрование может быть достигнуто только с использованием ChaCha20 без AEAD.

+----+----+----+----+----+----+----+----+
  |    Tunnel ID      |   tunnelNonce     |
  +----+----+----+----+----+----+----+----+
  | tunnelNonce cont. |    obfsNonce      |
  +----+----+----+----+----+----+----+----+
  |  obfsNonce cont.  |                   |
  +----+----+----+----+                   +
  |                                       |
  +           Encrypted Data              +
  ~                                       ~
  |                                       |
  +                   +----+----+----+----+
  |                   |    Poly1305 MAC   |
  +----+----+----+----+                   +  
  |                                       |
  +                   +----+----+----+----+
  |                   |
  +----+----+----+----+

  Tunnel ID :: `TunnelId`
         4 байта
         ID следующего узла

  tunnelNonce ::
         8 байт
         нонс уровня туннеля

  obfsNonce ::
         8 байт
         нонс для шифрования нонса уровня туннеля

  Encrypted Data ::
         992 байта
         зашифрованное туннельное сообщение

  Poly1305 MAC ::
         16 байт

  общий размер: 1028 байт

Внутренние узлы (с предшествующими и следующими узлами) будут иметь два AEADKeys, один для дешифрования слоя AEAD предыдущего узла и шифрования слоя AEAD для следующего узла.

Таким образом, всем участникам внутреннего узла потребуется дополнительно 64 байта ключевого материала, включенные в их BuildRequestRecords.

Исходящей конечной точке и Входящему шлюзу потребуется только дополнительные 32 байта ключевых данных, поскольку они не шифруют сообщения уровня туннеля между друг другом.

Исходящий шлюз создает свой ключ outAEAD, который совпадает с ключом inAEAD первого исходящего узла.

Входящая конечная точка создает свой ключ inAEAD, который совпадает с ключом outAEAD финального входящего узла.

Внутренние узлы получают и inAEADKey, и outAEADKey, которые будут использоваться для AEAD дешифрования входящих сообщений и шифрования исходящих сообщений соответственно.

Как пример, в туннеле с внутренними узлами OBGW, A, B, OBEP:

  • inAEADKey A совпадает с outAEADKey OBGW
  • inAEADKey B совпадает с outAEADKey A
  • outAEADKey B совпадает с inAEADKey OBEP

Ключи уникальны для пар узлов, так что inAEADKey OBEP будет отличаться от inAEADKey A, outAEADKey A будет отличаться от outAEADKey B и т. д.

Обработка сообщений Шлюзом и Создателем туннеля

Шлюзы будут фрагментировать и объединять сообщения таким же образом, резервируя место для MAC Poly1305 после фрейма инструкции-фрагмента.

Внутренние I2NP сообщения, содержащие AEAD фреймы (включая MAC), могут быть разделены через фрагменты, но любые потерянные фрагменты приведут к несработке дешифрования AEAD (неудачная проверка MAC) в конечной точке.

Предобработка и шифрование на шлюзе

Когда туннели поддерживают шифрование уровня через ChaCha20, шлюзы будут генерировать два 64-битных нонса на набор сообщений.

Входящие туннели:

  • Шифруют IV и туннельное сообщение(я) с использованием ChaCha20

  • Используют 8-байтовые tunnelNonce и obfsNonce, учитывая срок действия туннелей

  • Используют 8-байтовый obfsNonce для шифрования tunnelNonce

  • Уничтожают туннель до 2^(64 - 1) - 1 наборов сообщений: 2^63 - 1 = 9,223,372,036,854,775,807

    • Ограничение на нонс установлено, чтобы избежать коллизии 64-битных нонсов
    • Ограничение на нонс практически невозможно когда-либо достичь, учитывая, что это было бы более 15,372,286,728,091,294 msg/sec за 10 минутные туннели
  • Настраивают фильтр Блума на разумное число ожидаемых элементов (128 msg/sec, 1024 msg/sec? TBD)

Входной шлюз (IBGW) туннеля обрабатывает сообщения, полученные из Исходящей Конечной Точки (OBEP) другого туннеля.

На этом этапе самая внешняя часть сообщения зашифрована с использованием шифрования транспорта от точки до точки. Заголовки сообщений I2NP видны, на уровне туннеля, для OBEP и IBGW. Внутренние сообщения I2NP упакованы в “Гарлик”-гвоздики, зашифрованные с использованием шифрования сессии от конца до конца.

IBGW предварительно обрабатывает сообщения в соответствующим образом сформатированные туннельные сообщения и шифрует следующим образом:


// IBGW генерирует случайные нонсы, удостоверяясь, что нет коллизий в своем фильтре Блума для каждого нонса
  tunnelNonce = Random(len = 64-bits)
  obfsNonce = Random(len = 64-bits)
  // IBGW шифрует каждое из предварительно обработанных туннельных сообщений через ChaCha20 с использованием своего tunnelNonce и layerKey
  encMsg = ChaCha20(msg = tunnel msg, nonce = tunnelNonce, key = layerKey)

  // ChaCha20-Poly1305 шифрует каждый фрейм зашифрованных данных сообщений с tunnelNonce и outAEADKey
  (encMsg, MAC) = ChaCha20-Poly1305-Encrypt(msg = encMsg, nonce = tunnelNonce, key = outAEADKey)

Формат туннельного сообщения немного изменится, используя два 8-байтных нонса вместо 16-байтного IV. obfsNonce, используемый для шифрования нонса, добавляется к 8-байтному tunnelNonce и шифруется каждым узлом, используя зашифрованный tunnelNonce и nonceKey данного узла.

После того как набор сообщений был предварительно дешифрован для каждого узла, Исходящий шлюз ChaCha20-Poly1305 AEAD шифрует шифрованную часть каждого туннельного сообщения с использованием tunnelNonce и своего outAEADKey.

Исходящие туннели:

  • Итеративно дешифруют туннельные сообщения
  • ChaCha20-Poly1305 шифруют предварительно дешифрованные фреймы зашифрованных туннельных сообщений
  • Используют те же правила для нонсов уровня, что и входящие туннели
  • Генерируют случайные нонсы один раз для каждого набора отправляемых туннельных сообщений


// Для каждого набора сообщений генерировать уникальные случайные нонсы
  tunnelNonce = Random(len = 64-bits)
  obfsNonce = Random(len = 64-bits)

  // Для каждого узла применить ChaCha20 к предыдущему tunnelNonce с текущим ключом IV узла
  tunnelNonce = ChaCha20(msg = prev. tunnelNonce, nonce = obfsNonce, key = hop's nonceKey)

  // Для каждого узла применить ChaCha20 "дешифрование" к туннельному сообщению с текущим tunnelNonce и layerKey узла
  decMsg = ChaCha20(msg = tunnel msg(s), nonce = tunnelNonce, key = hop's layerKey)

  // Для каждого узла применить ChaCha20 "дешифрование" к obfsNonce с зашифрованным tunnelNonce и nonceKey узла
  obfsNonce = ChaCha20(msg = obfsNonce, nonce = tunnelNonce, key = hop's nonceKey)

  // После обработки узла ChaCha20-Poly1305 шифрует каждый "дешифрованный" фрейм данных туннельного сообщения с first hop's encrypted tunnelNonce и inAEADKey
  (encMsg, MAC) = ChaCha20-Poly1305-Encrypt(msg = decMsg, nonce = first hop's encrypted tunnelNonce, key = first hop's inAEADKey / GW outAEADKey)

Обработка участников

Участники будут отслеживать увиденные сообщения таким же образом, используя исчезающие фильтры Блума.

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

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

Для проверки полученных tunnelNonce и obfsNonce участники проверяют каждый нонс индивидуально на наличие дубликатов в своем фильтре Блума.

После проверки участник:

  • ChaCha20-Poly1305 дешифрует каждый AEAD шифротекст туннельного сообщения с использованием полученного tunnelNonce и своего inAEADKey
  • Шифрует tunnelNonce с помощью nonceKey и полученного obfsNonce
  • Шифрует каждый фрейм зашифрованных данных туннельного сообщения с помощью зашифрованного tunnelNonce и своего layerKey
  • Шифрует každý фрейм зашифрованного туннельного сообщения с зашифрованным tunnelNonce и своим outAEADKey через ChaCha20-Poly1305
  • Шифрует obfsNonce с помощью nonceKey и зашифрованного tunnelNonce
  • Отправляет набор {nextTunnelId, зашифрованный (tunnelNonce || obfsNonce), AEAD шифротекст || MAC} следующему узлу.

// Для проверки узлы туннеля должны проверять уникальность каждого полученного нонса в фильтре Блума
  // После проверки развернуть AEAD фрейм(ы), расшифровав каждый фрейм туннельного сообщения
  // с полученным tunnelNonce и inAEADKey через ChaCha20-Poly1305 
  encTunMsg = ChaCha20-Poly1305-Decrypt(msg = received encMsg \|\| MAC, nonce = received tunnelNonce, key = inAEADKey)

  // Шифрует tunnelNonce с помощью obfsNonce и nonceKey узла через ChaCha20
  tunnelNonce = ChaCha20(msg = received tunnelNonce, nonce = received obfsNonce, key = nonceKey)

  // Шифрует каждый фрейм зашифрованных данных туннельного сообщения с зашифрованным tunnelNonce и layerKey узла через ChaCha20
  encMsg = ChaCha20(msg = encTunMsg, nonce = tunnelNonce, key = layerKey)

  // Для защиты AEAD, также шифрует каждый фрейм зашифрованных данных сообщения
  // с зашифрованным tunnelNonce и outAEADKey узла через ChaCha20-Poly1305
  (encMsg, MAC) = ChaCha20-Poly1305-Encrypt(msg = encMsg, nonce = tunnelNonce, key = outAEADKey)

  // Шифрует полученный obfsNonce с зашифрованным tunnelNonce и nonceKey узла через ChaCha20
  obfsNonce = ChaCha20(msg = obfsNonce, nonce = tunnelNonce, key = nonceKey)

Обработка на Входной конечной точке

Для туннелей, использующих ChaCha20, будет применяться следующая схема для дешифровки каждого туннельного сообщения:

  • Проверяет независимость полученных tunnelNonce и obfsNonce в его фильтре Блума
  • Дешифрует зашифрованный фрейм данных с использованием полученного tunnelNonce и inAEADKey через ChaCha20-Poly1305
  • Дешифрует зашифрованный фрейм данных с использованием полученного tunnelNonce и layerKey узла через ChaCha20
  • Дешифрует obfsNonce, используя nonceKey узла и полученный tunnelNonce, для получения предыдущего obfsNonce
  • Дешифрует полученный tunnelNonce, используя nonceKey узла и расшифрованный obfsNonce, для получения предыдущего tunnelNonce
  • Дешифрует зашифрованные данные с использованием расшифрованного tunnelNonce и layerKey предыдущего узла через ChaCha20
  • Повторяет шаги для дешифрования нонса и уровня для каждого узла в туннеле обратно до IBGW
  • Расшифрование AEAD фрейма требуется только в первом раунде

// Для первого раунда ChaCha20-Poly1305 дешифрует каждый фрейм зашифрованных данных + MAC 
  // используя полученный tunnelNonce и inAEADKey
  msg = encTunMsg \|\| MAC
  tunnelNonce = полученный tunnelNonce
  encTunMsg = ChaCha20-Poly1305-Decrypt(msg, nonce = tunnelNonce, key = inAEADKey)

  // Повторить для каждого узла в туннеле обратно до IBGW
  // Для каждого раунда ChaCha20 дешифрует каждое шифрование уровня узла на каждом фрейме зашифрованных данных сообщения
  // Заменить полученный tunnelNonce расшифрованным tunnelNonce предыдущего раунда для каждого узла
  decMsg = ChaCha20(msg = encTunMsg, nonce = tunnelNonce, key = layerKey)
  obfsNonce = ChaCha20(msg = obfsNonce, nonce = tunnelNonce, key = nonceKey)
  tunnelNonce = ChaCha20(msg = tunnelNonce, nonce = obfsNonce, key = nonceKey)

Анализ безопасности для ChaCha20+ChaCha20-Poly1305 при шифровании туннельного уровня

Переход от AES256/ECB+AES256/CBC к ChaCha20+ChaCha20-Poly1305 предоставляет ряд преимуществ и новых аспектов безопасности.

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

Неиспользование уникальных нонсов с тем же ключом для разных сообщений нарушает ChaCha20 и ChaCha20-Poly1305.

Использование прикрепленного obfsNonce позволяет IBEP дешифровать tunnelNonce для шифрования уровня каждого узла, восстанавливая предыдущий нонс.

obfsNonce наряду с tunnelNonce не раскрывает новую информацию туннельным узлам, поскольку obfsNonce зашифрован с использованием зашифрованного tunnelNonce. Это также позволяет IBEP восстановить предыдущий obfsNonce аналогично восстановлению tunnelNonce.

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

Существуют практические атаки оракула против AES256/ECB + AES256/CBC, когда ключ повторно используется (как в шифровании уровня туннеля).

Атаки оракула против AES256/ECB не будут работать из-за двойного шифрования, используемого, и шифрования по одному блоку (туннельному IV).

Атаки оракула на выравнивание против AES256/CBC тоже не будут работать, потому что выравнивание не используется. Если длина туннельного сообщения изменится на немодифицированные длины 16, AES256/CBC все равно не будет уязвим, так как дублирование IV отклоняется.

Обе атаки также блокируются отказом в многочисленных вызовах оракула с одинаковым IV, поскольку дублирование IV отклоняется.

Ссылки