ECIES-X25519-AEAD-Ratchet

Proposal 144
Закрыто
Author zzz, chisana, orignal
Created 2018-11-22
Last Updated 2025-03-05
Target Version 0.9.46
Implemented In 0.9.46

Примечание

Развертывание и тестирование сети в процессе. Возможны незначительные изменения. См. SPEC для официальной спецификации.

Следующие функции не реализованы по состоянию на версию 0.9.46:

  • Блоки MessageNumbers, Options и Termination
  • Ответы протокольного уровня
  • Нулевой статический ключ
  • Многоадресная рассылка

Обзор

Это предложение для первого нового типа сквозного шифрования с самого начала I2P, чтобы заменить ElGamal/AES+SessionTags Elg-AES.

Это основано на предыдущих работах следующим образом:

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

Дизайн будет использовать Noise handshake и фазу данных, включающую двойной храповик Signal.

Все ссылки на Signal и Noise в данном предложении приводятся исключительно для справочной информации. Знание протоколов Signal и Noise не требуется для понимания или реализации данного предложения.

Current ElGamal Uses

Для обзора, публичные ключи ElGamal размером 256 байт могут быть найдены в следующих структурах данных. См. спецификацию общих структур.

  • В Router Identity Это ключ шифрования маршрутизатора.

  • В Destination Публичный ключ destination использовался для старого i2cp-to-i2cp шифрования, которое было отключено в версии 0.6, в настоящее время не используется, за исключением IV для шифрования LeaseSet, что является устаревшим. Вместо этого используется публичный ключ в LeaseSet.

  • В LeaseSet Это ключ шифрования назначения.

  • В LS2 Это ключ шифрования назначения.

EncTypes in Key Certs

В качестве напоминания, мы добавили поддержку типов шифрования, когда добавляли поддержку типов подписи. Поле типа шифрования всегда равно нулю как в Destinations, так и в RouterIdentities. Стоит ли это когда-либо изменить — пока не определено. Обратитесь к спецификации общих структур Common Structures.

Текущее использование ElGamal

В качестве обзора, мы используем ElGamal для:

  1. Сообщения Tunnel Build (ключ находится в RouterIdentity) Замена не рассматривается в данном предложении. См. предложение 152 Proposal 152.

  2. Шифрование router-to-router сообщений netDb и других I2NP сообщений (Ключ находится в RouterIdentity) Зависит от данного предложения. Требует предложения для 1) также, или размещения ключа в опциях RI.

  3. Клиентское сквозное шифрование ElGamal+AES/SessionTag (ключ находится в LeaseSet, ключ Destination не используется) Замена ПОКРЫВАЕТСЯ данным предложением.

  4. Эфемерный DH для NTCP1 и SSU Замена не рассматривается в данном предложении. См. предложение 111 для NTCP2. Нет текущего предложения для SSU2.

EncTypes в Key Certs

  • Обратная совместимость
  • Требует и основывается на LS2 (предложение 123)
  • Использование новой криптографии или примитивов, добавленных для NTCP2 (предложение 111)
  • Не требуется новая криптография или примитивы для поддержки
  • Сохранение разделения криптографии и подписи; поддержка всех текущих и будущих версий
  • Включение новой криптографии для назначений
  • Включение новой криптографии для router’ов, но только для garlic сообщений - построение tunnel’ей было бы отдельным предложением
  • Не нарушать ничего, что полагается на 32-байтовые бинарные хеши назначений, например bittorrent
  • Сохранение доставки сообщений 0-RTT с использованием ephemeral-static DH
  • Не требовать буферизации / постановки в очередь сообщений на этом уровне протокола; продолжать поддерживать неограниченную доставку сообщений в обоих направлениях без ожидания ответа
  • Переход на ephemeral-ephemeral DH после 1 RTT
  • Сохранение обработки сообщений, поступивших не по порядку
  • Сохранение 256-битной безопасности
  • Добавление прямой секретности
  • Добавление аутентификации (AEAD)
  • Гораздо более эффективное использование CPU по сравнению с ElGamal
  • Не полагаться на Java jbigi для эффективности DH
  • Минимизация операций DH
  • Гораздо более эффективное использование пропускной способности по сравнению с ElGamal (блок ElGamal 514 байт)
  • Поддержка новой и старой криптографии в одном tunnel’е при желании
  • Получатель может эффективно отличать новую от старой криптографии, поступающей по одному tunnel’у
  • Другие не могут отличить новую от старой или будущей криптографии
  • Исключение классификации длины новых против существующих сессий (поддержка padding’а)
  • Не требуется новых I2NP сообщений
  • Замена контрольной суммы SHA-256 в AES payload на AEAD
  • Поддержка привязки сессий передачи и приема, чтобы подтверждения могли происходить внутри протокола, а не только внеполосно. Это также позволит ответам иметь прямую секретность немедленно.
  • Включение сквозного шифрования определенных сообщений (хранилища RouterInfo), которые мы в настоящее время не шифруем из-за нагрузки на CPU.
  • Не изменять I2NP Garlic Message или формат инструкций доставки Garlic Message.
  • Исключение неиспользуемых или избыточных полей в форматах Garlic Clove Set и Clove.

Устранить несколько проблем с session tags, включая:

  • Невозможность использовать AES до первого ответа
  • Ненадежность и зависания при предположении доставки тегов
  • Неэффективное использование пропускной способности, особенно при первой доставке
  • Огромная неэффективность использования пространства для хранения тегов
  • Огромные накладные расходы пропускной способности для доставки тегов
  • Высокая сложность, трудность в реализации
  • Трудность настройки для различных случаев использования (streaming против datagram, сервер против клиент, высокая против низкой пропускная способность)
  • Уязвимости исчерпания памяти из-за доставки тегов

Использование асимметричной криптографии

  • Изменения формата LS2 (предложение 123 выполнено)
  • Новый алгоритм ротации DHT или генерация общего случайного числа
  • Новое шифрование для построения туннелей. См. предложение 152 Proposal 152.
  • Новое шифрование для шифрования слоя туннелей. См. предложение 153 Proposal 153.
  • Методы шифрования, передачи и получения I2NP DLM / DSM / DSRM сообщений. Без изменений.
  • Связь LS1-к-LS2 или ElGamal/AES-к-данному-предложению не поддерживается. Данное предложение представляет собой двунаправленный протокол. Назначения могут обрабатывать обратную совместимость, публикуя два leaseSet используя те же туннели, или поместить оба типа шифрования в LS2.
  • Изменения модели угроз
  • Детали реализации здесь не обсуждаются и остаются на усмотрение каждого проекта.
  • (Оптимистично) Добавить расширения или хуки для поддержки multicast

Цели

ElGamal/AES+SessionTag был нашим единственным end-to-end протоколом около 15 лет, практически без изменений в протоколе. Сейчас существуют криптографические примитивы, которые работают быстрее. Нам необходимо усилить безопасность протокола. Мы также разработали эвристические стратегии и обходные пути для минимизации накладных расходов памяти и пропускной способности протокола, но эти стратегии хрупкие, сложные в настройке и делают протокол еще более подверженным сбоям, что приводит к разрыву сессии.

Примерно за тот же период времени спецификация ElGamal/AES+SessionTag и сопутствующая документация описывали, насколько затратной по пропускной способности является доставка session tags, и предлагали заменить доставку session tag на “синхронизированный PRNG”. Синхронизированный PRNG детерминированно генерирует одинаковые теги на обеих сторонах, выведенные из общего начального значения. Синхронизированный PRNG также можно назвать “ratchet”. Данное предложение (наконец) специфицирует этот ratchet механизм и исключает доставку тегов.

Используя ratchet (синхронизированный PRNG) для генерации тегов сессии, мы устраняем накладные расходы на отправку тегов сессии в сообщении New Session и последующих сообщениях при необходимости. Для типичного набора тегов из 32 тегов это составляет 1КБ. Это также исключает хранение тегов сессии на стороне отправителя, тем самым сокращая требования к памяти вдвое.

Полное двустороннее рукопожатие, аналогичное паттерну Noise IK, необходимо для предотвращения атак Key Compromise Impersonation (KCI). См. таблицу “Payload Security Properties” в Noise в NOISE. Для получения дополнительной информации о KCI см. статью https://www.usenix.org/system/files/conference/woot15/woot15-paper-hlauschek.pdf

Не-цели / Вне области применения

Модель угроз несколько отличается от модели для NTCP2 (предложение 111). Узлы MitM являются OBEP и IBGW и предполагается, что они имеют полный обзор текущей или исторической глобальной NetDB, сговариваясь с floodfill.

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

Detailed Proposal

Данное предложение определяет новый сквозной протокол для замены ElGamal/AES+SessionTags. Дизайн будет использовать хендшейк Noise и фазу данных, включающую двойной ratchet от Signal.

Обоснование

Необходимо перепроектировать пять частей протокола:

    1. Форматы контейнеров для новых и существующих сессий заменяются новыми форматами.
    1. ElGamal (256-байтовые публичные ключи, 128-байтовые приватные ключи) заменяется на ECIES-X25519 (32-байтовые публичные и приватные ключи)
    1. AES заменяется на AEAD_ChaCha20_Poly1305 (сокращенно ChaChaPoly ниже)
    1. SessionTags будут заменены на ratchets, что по сути является криптографическим синхронизированным PRNG.
    1. AES payload, как определено в спецификации ElGamal/AES+SessionTags, заменяется блочным форматом, аналогичным используемому в NTCP2.

Каждое из пяти изменений имеет свой раздел ниже.

Модель угроз

Существующие реализации I2P router потребуют реализации следующих стандартных криптографических примитивов, которые не требуются для текущих протоколов I2P:

  • ECIES (но это по сути X25519)
  • Elligator2

Существующие реализации I2P router, которые еще не реализовали NTCP2 (Proposal 111), также потребуют реализации для:

  • Генерация ключей X25519 и DH
  • AEAD_ChaCha20_Poly1305 (сокращенно ChaChaPoly ниже)
  • HKDF

Crypto Type

Тип криптографии (используемый в LS2) равен 4. Это указывает на 32-байтовый публичный ключ X25519 в формате little-endian и сквозной протокол, определенный здесь.

Тип шифрования 0 — это ElGamal. Типы шифрования 1-3 зарезервированы для ECIES-ECDH-AES-SessionTag, см. предложение 145 Предложение 145.

Обзор криптографического дизайна

Данное предложение предоставляет требования, основанные на Noise Protocol Framework NOISE (Ревизия 34, 2018-07-11). Noise имеет схожие свойства с протоколом Station-To-Station STS, который является основой для протокола SSU. В терминологии Noise, Алиса является инициатором, а Боб — отвечающим.

Данное предложение основано на протоколе Noise Noise_IK_25519_ChaChaPoly_SHA256. (Фактический идентификатор для начальной функции выведения ключей — “Noise_IKelg2_25519_ChaChaPoly_SHA256” для обозначения расширений I2P — см. раздел KDF 1 ниже) Этот протокол Noise использует следующие примитивы:

  • Interactive Handshake Pattern: IK Алиса немедленно передает свой статический ключ Бобу (I) Алиса уже знает статический ключ Боба (K)

  • Односторонний паттерн рукопожатия: N Alice не передает свой статический ключ Bob (N)

  • DH Function: X25519 X25519 DH с длиной ключа 32 байта, как указано в RFC-7748.

  • Cipher Function: ChaChaPoly AEAD_CHACHA20_POLY1305 как указано в RFC-7539 раздел 2.8. 12-байтовый nonce, с первыми 4 байтами установленными в ноль. Идентично тому, что используется в NTCP2.

  • Hash Function: SHA256 Стандартный 32-байтный хеш, уже широко используемый в I2P.

Новые криптографические примитивы для I2P

Данное предложение определяет следующие улучшения для Noise_IK_25519_ChaChaPoly_SHA256. Они в целом следуют рекомендациям в разделе 13 NOISE.

  1. Ключи cleartext ephemeral кодируются с помощью Elligator2.

  2. Ответ начинается с тега в открытом тексте.

  3. Формат полезной нагрузки определён для сообщений 1, 2 и фазы данных. Конечно, это не определено в Noise.

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

Тип шифрования

Рукопожатия используют шаблоны рукопожатий Noise.

Используется следующее соответствие букв:

  • e = одноразовый эфемерный ключ
  • s = статический ключ
  • p = полезная нагрузка сообщения

Одноразовые и несвязанные сессии похожи на паттерн Noise N.


<- s
  ...
  e es p ->

Связанные сессии подобны шаблону Noise IK.


<- s
  ...
  e es s ss p ->
  <- tag e ee se
  <- p
  p ->

Фреймворк протокола Noise

Текущий протокол ElGamal/AES+SessionTag является однонаправленным. На этом уровне получатель не знает, откуда пришло сообщение. Исходящие и входящие сессии не связаны между собой. Подтверждения передаются по внешнему каналу с использованием DeliveryStatusMessage (обернутого в GarlicMessage) в clove.

В однонаправленном протоколе есть существенная неэффективность. Любой ответ также должен использовать дорогостоящее сообщение ‘New Session’. Это приводит к повышенному использованию пропускной способности, ЦП и памяти.

Существуют также уязвимости безопасности в однонаправленном протоколе. Все сессии основаны на эфемерно-статическом DH. Без обратного пути у Боба нет способа “переключить” свой статический ключ на эфемерный ключ. Не зная, откуда пришло сообщение, невозможно использовать полученный эфемерный ключ для исходящих сообщений, поэтому первоначальный ответ также использует эфемерно-статический DH.

Для данного предложения мы определяем два механизма создания двунаправленного протокола - “сопряжение” и “привязка”. Эти механизмы обеспечивают повышенную эффективность и безопасность.

Дополнения к фреймворку

Как и в случае с ElGamal/AES+SessionTags, все входящие и исходящие сессии должны находиться в определенном контексте — либо в контексте router’а, либо в контексте конкретного локального назначения. В Java I2P этот контекст называется Session Key Manager.

Сессии не должны разделяться между контекстами, так как это позволило бы провести корреляцию между различными локальными направлениями или между локальным направлением и router’ом.

Когда данное назначение поддерживает как ElGamal/AES+SessionTags, так и данное предложение, оба типа сессий могут использовать общий контекст. См. раздел 1c) ниже.

Паттерны рукопожатий

Когда исходящая сессия создается у инициатора (Alice), новая входящая сессия создается и связывается с исходящей сессией, если только не ожидается ответа (например, сырые датаграммы).

Новая входящая сессия всегда связывается с новой исходящей сессией, если только не запрашивается ответ (например, необработанные датаграммы).

Если запрашивается ответ и он привязан к удаленному назначению или router, эта новая исходящая сессия привязывается к этому назначению или router и заменяет любую предыдущую исходящую сессию к этому назначению или router.

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

Сессии

Существует только одна исходящая сессия к данному адресату или router. Может существовать несколько текущих входящих сессий от данного адресата или router. Обычно, когда создается новая входящая сессия и на этой сессии получен трафик (что служит в качестве ACK), все остальные будут помечены для истечения срока действия относительно быстро, в течение минуты или около того. Проверяется значение предыдущих отправленных сообщений (PN), и если в предыдущей входящей сессии нет неполученных сообщений (в пределах размера окна), предыдущая сессия может быть удалена немедленно.

Когда исходящая сессия создается у инициатора (Alice), она привязывается к удаленному Destination (Bob), и любая парная входящая сессия также будет привязана к удаленному Destination. По мере того как сессии проходят ratchet, они продолжают оставаться привязанными к удаленному Destination.

Когда входящая сессия создается у получателя (Bob), она может быть привязана к удаленному Destination (Alice) по выбору Alice. Если Alice включает информацию о привязке (свой статический ключ) в сообщение New Session, сессия будет привязана к этому destination, и исходящая сессия будет создана и привязана к тому же Destination. По мере того как сессии ratchet, они продолжают оставаться привязанными к удаленному Destination.

Контекст сессии

Для обычного случая потоковой передачи мы ожидаем, что Алиса и Боб будут использовать протокол следующим образом:

  • Алиса связывает свою новую исходящую сессию с новой входящей сессией, обе привязаны к конечному пункту назначения (Боб).
  • Алиса включает информацию о связывании и подпись, а также запрос на ответ, в сообщение New Session, отправленное Бобу.
  • Боб связывает свою новую входящую сессию с новой исходящей сессией, обе привязаны к конечному пункту назначения (Алиса).
  • Боб отправляет ответ (подтверждение) Алисе в связанной сессии с ratchet к новому DH ключу.
  • Алиса выполняет ratchet к новой исходящей сессии с новым ключом Боба, связанной с существующей входящей сессией.

Привязывая входящую сессию к удаленному Destination и сопоставляя входящую сессию с исходящей сессией, привязанной к тому же Destination, мы получаем два основных преимущества:

  1. Первоначальный ответ от Боба к Алисе использует эфемерный-эфемерный DH

  2. После того как Алиса получает ответ Боба и выполняет ratchet, все последующие сообщения от Алисы к Бобу используют ephemeral-ephemeral DH.

Связывание входящих и исходящих сессий

В ElGamal/AES+SessionTags, когда LeaseSet упаковывается как garlic clove, или доставляются теги, отправляющий router запрашивает ACK. Это отдельный garlic clove, содержащий сообщение DeliveryStatus. Для дополнительной безопасности сообщение DeliveryStatus оборачивается в Garlic Message. Этот механизм является внеполосным с точки зрения протокола.

В новом протоколе, поскольку входящие и исходящие сессии спарены, мы можем иметь ACK внутри полосы. Отдельный clove не требуется.

Явное подтверждение (ACK) — это просто сообщение Existing Session без блока I2NP. Однако в большинстве случаев явного подтверждения можно избежать, поскольку существует обратный трафик. Для реализаций может быть желательно подождать короткое время (возможно, сто миллисекунд) перед отправкой явного подтверждения, чтобы дать уровню потоков или приложений время на ответ.

Реализации также должны будут отложить отправку любых ACK до тех пор, пока не будет обработан блок I2NP, поскольку Garlic Message может содержать Database Store Message с lease set. Актуальный lease set будет необходим для маршрутизации ACK, а удалённое назначение (содержащееся в lease set) будет необходимо для проверки связывающего статического ключа.

Привязка сессий и назначений

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

Преимущества привязки и сопряжения

Будет определено позже

Подтверждения сообщений

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

ZEROLEN

zero-length byte array

CSRNG(n)

n-byte output from a cryptographically-secure random number generator.

H(p, d)

SHA-256 hash function that takes a personalization string p and data d, and
produces an output of length 32 bytes.
As defined in [NOISE](https://noiseprotocol.org/noise.html).
|| below means append.

Use SHA-256 as follows::

    H(p, d) := SHA-256(p || d)

MixHash(d)

SHA-256 hash function that takes a previous hash h and new data d,
and produces an output of length 32 bytes.
|| below means append.

Use SHA-256 as follows::

    MixHash(d) := h = SHA-256(h || d)

ПОТОК

The ChaCha20/Poly1305 AEAD as specified in [RFC-7539](https://tools.ietf.org/html/rfc7539).
S_KEY_LEN = 32 and S_IV_LEN = 12.

ENCRYPT(k, n, plaintext, ad)
    Encrypts plaintext using the cipher key k, and nonce n which MUST be unique for
    the key k.
    Associated data ad is optional.
    Returns a ciphertext that is the size of the plaintext + 16 bytes for the HMAC.

    The entire ciphertext must be indistinguishable from random if the key is secret.

DECRYPT(k, n, ciphertext, ad)
    Decrypts ciphertext using the cipher key k, and nonce n.
    Associated data ad is optional.
    Returns the plaintext.

DH

X25519 public key agreement system. Private keys of 32 bytes, public keys of 32
bytes, produces outputs of 32 bytes. It has the following
functions:

GENERATE_PRIVATE()
    Generates a new private key.

DERIVE_PUBLIC(privkey)
    Returns the public key corresponding to the given private key.

GENERATE_PRIVATE_ELG2()
    Generates a new private key that maps to a public key suitable for Elligator2 encoding.
    Note that half of the randomly-generated private keys will not be suitable and must be discarded.

ENCODE_ELG2(pubkey)
    Returns the Elligator2-encoded public key corresponding to the given public key (inverse mapping).
    Encoded keys are little endian.
    Encoded key must be 256 bits indistinguishable from random data.
    See Elligator2 section below for specification.

DECODE_ELG2(pubkey)
    Returns the public key corresponding to the given Elligator2-encoded public key.
    See Elligator2 section below for specification.

DH(privkey, pubkey)
    Generates a shared secret from the given private and public keys.

HKDF(salt, ikm, info, n)

A cryptographic key derivation function which takes some input key material ikm (which
should have good entropy but is not required to be a uniformly random string), a salt
of length 32 bytes, and a context-specific 'info' value, and produces an output
of n bytes suitable for use as key material.

Use HKDF as specified in [RFC-5869](https://tools.ietf.org/html/rfc5869), using the HMAC hash function SHA-256
as specified in [RFC-2104](https://tools.ietf.org/html/rfc2104). This means that SALT_LEN is 32 bytes max.

MixKey(d)

Use HKDF() with a previous chainKey and new data d, and
sets the new chainKey and k.
As defined in [NOISE](https://noiseprotocol.org/noise.html).

Use HKDF as follows::

    MixKey(d) := output = HKDF(chainKey, d, "", 64)
                 chainKey = output[0:31]
                 k = output[32:63]

Тайм-ауты сессий

Многоадресная передача

Сообщение Garlic, как указано в I2NP, имеет следующий вид. Поскольку цель дизайна заключается в том, чтобы промежуточные узлы не могли отличить новую криптографию от старой, этот формат не может изменяться, даже несмотря на то, что поле длины является избыточным. Формат показан с полным 16-байтовым заголовком, хотя фактический заголовок может иметь другой формат в зависимости от используемого транспорта.

После расшифровки данные содержат серию Garlic Cloves и дополнительные данные, также известные как Clove Set.

См. I2NP для получения подробной информации и полной спецификации.


+----+----+----+----+----+----+----+----+
  |type|      msg_id       |  expiration
  +----+----+----+----+----+----+----+----+
                           |  size   |chks|
  +----+----+----+----+----+----+----+----+
  |      length       |                   |
  +----+----+----+----+                   +
  |          encrypted data               |
  ~                                       ~
  ~                                       ~
  |                                       |
  +----+----+----+----+----+----+----+----+

Определения

Текущий формат сообщений, используемый уже более 15 лет, — это ElGamal/AES+SessionTags. В ElGamal/AES+SessionTags существует два формата сообщений:

  1. Новая сессия: - 514-байтовый блок ElGamal - блок AES (минимум 128 байт, кратно 16)

  2. Существующая сессия: - 32-байтовый Session Tag - AES блок (минимум 128 байт, кратно 16)

Минимальное дополнение до 128 реализовано в Java I2P, но не принудительно проверяется при получении.

Эти сообщения инкапсулированы в I2NP garlic сообщение, которое содержит поле длины, поэтому длина известна.

Обратите внимание, что не определено заполнение до длины, не кратной 16, поэтому New Session всегда (mod 16 == 2), а Existing Session всегда (mod 16 == 0). Нам нужно это исправить.

Получатель сначала пытается найти первые 32 байта как Session Tag. Если найден, он расшифровывает блок AES. Если не найден, и данные имеют длину не менее (514+16), он пытается расшифровать блок ElGamal, и в случае успеха расшифровывает блок AES.

1) Формат сообщения

В Signal Double Ratchet заголовок содержит:

  • DH: Текущий открытый ключ ratchet
  • PN: Длина предыдущей цепи сообщений
  • N: Номер сообщения

«Цепочки отправки» Signal примерно эквивалентны нашим наборам тегов. Используя тег сессии, мы можем исключить большую часть этого.

В New Session мы помещаем только публичный ключ в незашифрованный заголовок.

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

В новых и существующих сессиях PN и N находятся в зашифрованном теле сообщения.

В Signal всё постоянно движется по ratchet-механизму. Новый DH публичный ключ требует от получателя выполнить ratchet и отправить новый публичный ключ обратно, что также служит подтверждением получения публичного ключа. Для нас это было бы слишком много DH операций. Поэтому мы разделяем подтверждение полученного ключа и передачу нового публичного ключа. Любое сообщение, использующее session tag, сгенерированный из нового DH публичного ключа, является подтверждением. Мы передаём новый публичный ключ только тогда, когда хотим выполнить rekey.

Максимальное количество сообщений перед тем, как DH должен выполнить ratchet, составляет 65535.

При доставке ключа сессии мы выводим из него “Набор Тегов”, а не доставляем теги сессии отдельно. Набор Тегов может содержать до 65536 тегов. Однако получатели должны реализовывать стратегию “предварительного просмотра”, а не генерировать все возможные теги одновременно. Генерируйте не более N тегов после последнего корректно полученного тега. N может составлять максимум 128, но 32 или даже меньше может быть лучшим выбором.

Обзор текущего формата сообщений

Новый сессионный одноразовый публичный ключ (32 байта) Зашифрованные данные и MAC (оставшиеся байты)

Сообщение New Session может содержать или не содержать статический открытый ключ отправителя. Если он включён, обратная сессия привязывается к этому ключу. Статический ключ следует включать, если ожидаются ответы, т.е. для потокового режима и датаграмм с возможностью ответа. Он не должен включаться для обычных датаграмм.

Сообщение New Session похоже на односторонний шаблон Noise NOISE “N” (если статический ключ не отправляется), или на двусторонний шаблон “IK” (если статический ключ отправляется).

Обзор формата зашифрованных данных

Длина составляет 96 + длина полезной нагрузки. Зашифрованный формат:


+----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |   New Session Ephemeral Public Key    |
  +             32 bytes                  +
  |     Encoded with Elligator2           |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +         Static Key                    +
  |       ChaCha20 encrypted data         |
  +            32 bytes                   +
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +    (MAC) for Static Key Section       +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +            Payload Section            +
  |       ChaCha20 encrypted data         |
  ~                                       ~
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +         (MAC) for Payload Section     +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+

  Public Key :: 32 bytes, little endian, Elligator2, cleartext

  Static Key encrypted data :: 32 bytes

  Payload Section encrypted data :: remaining data minus 16 bytes

  MAC :: Poly1305 message authentication code, 16 bytes

Новые теги сессий и сравнение с Signal

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

1a) Формат новой сессии

После расшифровки, статический ключ X25519 Алисы, 32 байта.

1b) Новый формат сессии (с привязкой)

Зашифрованная длина — это остаток данных. Расшифрованная длина на 16 меньше зашифрованной длины. Полезная нагрузка должна содержать блок DateTime и обычно содержит один или несколько блоков Garlic Clove. См. раздел полезной нагрузки ниже для формата и дополнительных требований.

Новый эфемерный ключ сессии

Если ответ не требуется, статический ключ не отправляется.

Длина составляет 96 + длина полезной нагрузки. Зашифрованный формат:


+----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |   New Session Ephemeral Public Key    |
  +             32 bytes                  +
  |     Encoded with Elligator2           |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +           Flags Section               +
  |       ChaCha20 encrypted data         |
  +            32 bytes                   +
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +         (MAC) for above section       +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +            Payload Section            +
  |       ChaCha20 encrypted data         |
  ~                                       ~
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +         (MAC) for Payload Section     +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+

  Public Key :: 32 bytes, little endian, Elligator2, cleartext

  Flags Section encrypted data :: 32 bytes

  Payload Section encrypted data :: remaining data minus 16 bytes

  MAC :: Poly1305 message authentication code, 16 bytes

Статический ключ

Эфемерный ключ Алисы. Эфемерный ключ состоит из 32 байт, закодированных с помощью Elligator2, little endian. Этот ключ никогда не переиспользуется; новый ключ генерируется для каждого сообщения, включая повторные передачи.

Полезная нагрузка

Секция Flags не содержит ничего. Она всегда составляет 32 байта, поскольку должна иметь ту же длину, что и статический ключ для сообщений New Session с привязкой. Bob определяет, является ли это статическим ключом или секцией flags, проверяя, равны ли все 32 байта нулю.

TODO нужны ли здесь какие-либо флаги?

1c) Новый формат сессии (без привязки)

Зашифрованная длина — это оставшаяся часть данных. Расшифрованная длина на 16 меньше зашифрованной длины. Полезная нагрузка должна содержать блок DateTime и обычно будет содержать один или несколько блоков Garlic Clove. Смотрите раздел полезной нагрузки ниже для формата и дополнительных требований.

Новый эфемерный ключ сессии

Если ожидается отправка только одного сообщения, настройка сессии или статический ключ не требуются.

Длина составляет 96 + длина полезной нагрузки. Зашифрованный формат:


+----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |       Ephemeral Public Key            |
  +             32 bytes                  +
  |     Encoded with Elligator2           |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +           Flags Section               +
  |       ChaCha20 encrypted data         |
  +            32 bytes                   +
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +         (MAC) for above section       +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +            Payload Section            +
  |       ChaCha20 encrypted data         |
  ~                                       ~
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +         (MAC) for Payload Section     +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+

  Public Key :: 32 bytes, little endian, Elligator2, cleartext

  Flags Section encrypted data :: 32 bytes

  Payload Section encrypted data :: remaining data minus 16 bytes

  MAC :: Poly1305 message authentication code, 16 bytes

Раздел флагов Расшифрованные данные

Одноразовый ключ составляет 32 байта, закодированный с помощью Elligator2, little endian. Этот ключ никогда не используется повторно; новый ключ генерируется для каждого сообщения, включая повторные передачи.

Полезная нагрузка

Секция Flags не содержит ничего. Она всегда составляет 32 байта, поскольку должна иметь такую же длину, как статический ключ для сообщений New Session с привязкой. Bob определяет, является ли это статическим ключом или секцией flags, проверяя, являются ли все 32 байта нулями.

TODO какие-то флаги нужны здесь?


+----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |                                       |
  +             All zeros                 +
  |              32 bytes                 |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+

  zeros:: All zeros, 32 bytes.

1d) Одноразовый формат (без привязки или сессии)

Зашифрованная длина — это оставшаяся часть данных. Расшифрованная длина на 16 меньше зашифрованной длины. Полезная нагрузка должна содержать блок DateTime и обычно содержит один или несколько блоков Garlic Clove. См. раздел полезной нагрузки ниже для формата и дополнительных требований.

Одноразовый ключ новой сессии

Раздел флагов Расшифрованные данные

Это стандартный NOISE для IK с измененным именем протокола. Обратите внимание, что мы используем один и тот же инициализатор как для паттерна IK (связанные сессии), так и для паттерна N (несвязанные сессии).

Название протокола изменено по двум причинам. Во-первых, чтобы указать, что эфемерные ключи кодируются с помощью Elligator2, и во-вторых, чтобы указать, что MixHash() вызывается перед вторым сообщением для смешивания значения тега.


This is the "e" message pattern:

  // Define protocol_name.
  Set protocol_name = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"
   (40 bytes, US-ASCII encoded, no NULL termination).

  // Define Hash h = 32 bytes
  h = SHA256(protocol_name);

  Define ck = 32 byte chaining key. Copy the h data to ck.
  Set chainKey = h

  // MixHash(null prologue)
  h = SHA256(h);

  // up until here, can all be precalculated by Alice for all outgoing connections

Полезная нагрузка


This is the "e" message pattern:

  // Bob's X25519 static keys
  // bpk is published in leaseset
  bsk = GENERATE_PRIVATE()
  bpk = DERIVE_PUBLIC(bsk)

  // Bob static public key
  // MixHash(bpk)
  // || below means append
  h = SHA256(h || bpk);

  // up until here, can all be precalculated by Bob for all incoming connections

  // Alice's X25519 ephemeral keys
  aesk = GENERATE_PRIVATE_ELG2()
  aepk = DERIVE_PUBLIC(aesk)

  // Alice ephemeral public key
  // MixHash(aepk)
  // || below means append
  h = SHA256(h || aepk);

  // h is used as the associated data for the AEAD in the New Session Message
  // Retain the Hash h for the New Session Reply KDF
  // eapk is sent in cleartext in the
  // beginning of the New Session message
  elg2_aepk = ENCODE_ELG2(aepk)
  // As decoded by Bob
  aepk = DECODE_ELG2(elg2_aepk)

  End of "e" message pattern.

  This is the "es" message pattern:

  // Noise es
  sharedSecret = DH(aesk, bpk) = DH(bsk, aepk)

  // MixKey(DH())
  //[chainKey, k] = MixKey(sharedSecret)
  // ChaChaPoly parameters to encrypt/decrypt
  keydata = HKDF(chainKey, sharedSecret, "", 64)
  chainKey = keydata[0:31]

  // AEAD parameters
  k = keydata[32:63]
  n = 0
  ad = h
  ciphertext = ENCRYPT(k, n, flags/static key section, ad)

  End of "es" message pattern.

  This is the "s" message pattern:

  // MixHash(ciphertext)
  // Save for Payload section KDF
  h = SHA256(h || ciphertext)

  // Alice's X25519 static keys
  ask = GENERATE_PRIVATE()
  apk = DERIVE_PUBLIC(ask)

  End of "s" message pattern.

1f) KDF для сообщения новой сессии


This is the "ss" message pattern:

  // Noise ss
  sharedSecret = DH(ask, bpk) = DH(bsk, apk)

  // MixKey(DH())
  //[chainKey, k] = MixKey(sharedSecret)
  // ChaChaPoly parameters to encrypt/decrypt
  // chainKey from Static Key Section
  Set sharedSecret = X25519 DH result
  keydata = HKDF(chainKey, sharedSecret, "", 64)
  chainKey = keydata[0:31]

  // AEAD parameters
  k = keydata[32:63]
  n = 0
  ad = h
  ciphertext = ENCRYPT(k, n, payload, ad)

  End of "ss" message pattern.

  // MixHash(ciphertext)
  // Save for New Session Reply KDF
  h = SHA256(h || ciphertext)

KDF для начального ChainKey

Обратите внимание, что это паттерн Noise “N”, но мы используем тот же инициализатор “IK”, что и для связанных сессий.

Сообщения New Session не могут быть идентифицированы как содержащие статический ключ Alice или нет, пока статический ключ не будет расшифрован и проверен, чтобы определить, содержит ли он все нули. Поэтому получатель должен использовать конечный автомат “IK” для всех сообщений New Session. Если статический ключ состоит из всех нулей, шаблон сообщения “ss” должен быть пропущен.


chainKey = from Flags/Static key section
  k = from Flags/Static key section
  n = 1
  ad = h from Flags/Static key section
  ciphertext = ENCRYPT(k, n, payload, ad)

KDF для содержимого зашифрованной секции флагов/статического ключа

В ответ на одно сообщение New Session может быть отправлено одно или несколько сообщений New Session Reply. Каждый ответ предваряется тегом, который генерируется из TagSet для сессии.

Ответ New Session состоит из двух частей. Первая часть — это завершение handshake Noise IK с добавленным в начало тегом. Длина первой части составляет 56 байт. Вторая часть — это полезная нагрузка фазы данных. Длина второй части составляет 16 + длина полезной нагрузки.

Общая длина составляет 72 + длина полезной нагрузки. Зашифрованный формат:


+----+----+----+----+----+----+----+----+
  |       Session Tag   8 bytes           |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +        Ephemeral Public Key           +
  |                                       |
  +            32 bytes                   +
  |     Encoded with Elligator2           |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +  (MAC) for Key Section (no data)      +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +            Payload Section            +
  |       ChaCha20 encrypted data         |
  ~                                       ~
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +         (MAC) for Payload Section     +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+

  Tag :: 8 bytes, cleartext

  Public Key :: 32 bytes, little endian, Elligator2, cleartext

  MAC :: Poly1305 message authentication code, 16 bytes
         Note: The ChaCha20 plaintext data is empty (ZEROLEN)

  Payload Section encrypted data :: remaining data minus 16 bytes

  MAC :: Poly1305 message authentication code, 16 bytes

KDF для секции полезной нагрузки (со статическим ключом Alice)

Тег генерируется в Session Tags KDF, как инициализировано в DH Initialization KDF ниже. Это связывает ответ с сессией. Session Key из DH Initialization не используется.

KDF для секции полезной нагрузки (без статического ключа Alice)

Эфемерный ключ Боба. Эфемерный ключ состоит из 32 байт, закодированных с помощью Elligator2, little endian. Этот ключ никогда не используется повторно; новый ключ генерируется для каждого сообщения, включая повторные передачи.

1g) Формат ответа New Session Reply

Зашифрованная длина — это оставшаяся часть данных. Расшифрованная длина на 16 меньше зашифрованной длины. Полезная нагрузка обычно содержит один или более блоков Garlic Clove. См. раздел полезной нагрузки ниже для формата и дополнительных требований.

Тег сессии

Один или несколько тегов создаются из TagSet, который инициализируется с использованием KDF ниже, используя chainKey из сообщения New Session.


// Generate tagset
  tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
  tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)

Ответ нового сеанса с эфемерным ключом


// Keys from the New Session message
  // Alice's X25519 keys
  // apk and aepk are sent in original New Session message
  // ask = Alice private static key
  // apk = Alice public static key
  // aesk = Alice ephemeral private key
  // aepk = Alice ephemeral public key
  // Bob's X25519 static keys
  // bsk = Bob private static key
  // bpk = Bob public static key

  // Generate the tag
  tagsetEntry = tagset_nsr.GET_NEXT_ENTRY()
  tag = tagsetEntry.SESSION_TAG

  // MixHash(tag)
  h = SHA256(h || tag)

  This is the "e" message pattern:

  // Bob's X25519 ephemeral keys
  besk = GENERATE_PRIVATE_ELG2()
  bepk = DERIVE_PUBLIC(besk)

  // Bob's ephemeral public key
  // MixHash(bepk)
  // || below means append
  h = SHA256(h || bepk);

  // elg2_bepk is sent in cleartext in the
  // beginning of the New Session message
  elg2_bepk = ENCODE_ELG2(bepk)
  // As decoded by Bob
  bepk = DECODE_ELG2(elg2_bepk)

  End of "e" message pattern.

  This is the "ee" message pattern:

  // MixKey(DH())
  //[chainKey, k] = MixKey(sharedSecret)
  // ChaChaPoly parameters to encrypt/decrypt
  // chainKey from original New Session Payload Section
  sharedSecret = DH(aesk, bepk) = DH(besk, aepk)
  keydata = HKDF(chainKey, sharedSecret, "", 32)
  chainKey = keydata[0:31]

  End of "ee" message pattern.

  This is the "se" message pattern:

  // MixKey(DH())
  //[chainKey, k] = MixKey(sharedSecret)
  sharedSecret = DH(ask, bepk) = DH(besk, apk)
  keydata = HKDF(chainKey, sharedSecret, "", 64)
  chainKey = keydata[0:31]

  // AEAD parameters
  k = keydata[32:63]
  n = 0
  ad = h
  ciphertext = ENCRYPT(k, n, ZEROLEN, ad)

  End of "se" message pattern.

  // MixHash(ciphertext)
  h = SHA256(h || ciphertext)

  chainKey is used in the ratchet below.

Полезная нагрузка

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


// split()
  keydata = HKDF(chainKey, ZEROLEN, "", 64)
  k_ab = keydata[0:31]
  k_ba = keydata[32:63]
  tagset_ab = DH_INITIALIZE(chainKey, k_ab)
  tagset_ba = DH_INITIALIZE(chainKey, k_ba)

  // AEAD parameters for New Session Reply payload
  k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
  n = 0
  ad = h
  ciphertext = ENCRYPT(k, n, payload, ad)

KDF для Reply TagSet

В ответ может быть отправлено несколько сообщений NSR, каждое с уникальными эфемерными ключами, в зависимости от размера ответа.

Алиса и Боб должны использовать новые эфемерные ключи для каждого сообщения NS и NSR.

Алиса должна получить одно из NSR сообщений Боба перед отправкой сообщений Existing Session (ES), а Боб должен получить ES сообщение от Алисы перед отправкой ES сообщений.

chainKey и k из NSR Payload Section Боба используются в качестве входных данных для начальных ES DH Ratchets (в обоих направлениях, см. DH Ratchet KDF).

Боб должен сохранять только Существующие Сессии для ES-сообщений, полученных от Алисы. Любые другие созданные входящие и исходящие сессии (для множественных NSR) должны быть немедленно уничтожены после получения первого ES-сообщения от Алисы для данной сессии.

KDF для зашифрованного содержимого секции Reply Key

Тег сессии (8 байт) Зашифрованные данные и MAC (см. раздел 3 ниже)

KDF для зашифрованного содержимого раздела полезной нагрузки

Зашифровано:


+----+----+----+----+----+----+----+----+
  |       Session Tag                     |
  +----+----+----+----+----+----+----+----+
  |                                       |
  +            Payload Section            +
  |       ChaCha20 encrypted data         |
  ~                                       ~
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +              (MAC)                    +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+

  Session Tag :: 8 bytes, cleartext

  Payload Section encrypted data :: remaining data minus 16 bytes

  MAC :: Poly1305 message authentication code, 16 bytes

Примечания

Зашифрованная длина - это остаток данных. Расшифрованная длина на 16 меньше зашифрованной длины. См. раздел полезной нагрузки ниже для формата и требований.

KDF

See AEAD section below.

  // AEAD parameters for Existing Session payload
  k = The 32-byte session key associated with this session tag
  n = The message number N in the current chain, as retrieved from the associated Session Tag.
  ad = The session tag, 8 bytes
  ciphertext = ENCRYPT(k, n, payload, ad)

1h) Формат существующей сессии

Формат: 32-байтовые публичные и приватные ключи, little-endian.

Обоснование: Используется в NTCP2.

Формат

В стандартных рукопожатиях Noise первые сообщения рукопожатия в каждом направлении начинаются с эфемерных ключей, которые передаются открытым текстом. Поскольку валидные ключи X25519 можно отличить от случайных данных, злоумышленник посередине может отличить эти сообщения от сообщений Existing Session, которые начинаются со случайных тегов сессии. В NTCP2 (Proposal 111) мы использовали низкозатратную функцию XOR с использованием внеполосного статического ключа для обфускации ключа. Однако модель угроз здесь отличается; мы не хотим позволять любому MitM использовать какие-либо средства для подтверждения назначения трафика или для различения первых сообщений рукопожатия от сообщений Existing Session.

Поэтому используется Elligator2 для преобразования эфемерных ключей в сообщениях New Session и New Session Reply, чтобы они были неотличимы от равномерно случайных строк.

Полезная нагрузка

32-байтовые публичные и приватные ключи. Закодированные ключи имеют порядок байтов little endian.

Как определено в Elligator2, закодированные ключи неотличимы от 254 случайных битов. Нам требуется 256 случайных битов (32 байта). Поэтому кодирование и декодирование определяются следующим образом:

Кодирование:


ENCODE_ELG2() Definition

  // Encode as defined in Elligator2 specification
  encodedKey = encode(pubkey)
  // OR in 2 random bits to MSB
  randomByte = CSRNG(1)
  encodedKey[31] |= (randomByte & 0xc0)

Декодирование:


DECODE_ELG2() Definition

  // Mask out 2 random bits from MSB
  encodedKey[31] &= 0x3f
  // Decode as defined in Elligator2 specification
  pubkey = decode(encodedKey)

2) ECIES-X25519

Требуется для предотвращения классификации трафика со стороны OBEP и IBGW.

2a) Elligator2

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

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

Генератор выполняет функцию ENCODE_ELG2() для определения пригодности. Поэтому генератор должен сохранять результат ENCODE_ELG2(), чтобы не пришлось вычислять его снова.

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

Формат

AEAD с использованием ChaCha20 и Poly1305, как в NTCP2. Это соответствует RFC-7539, который также аналогично используется в TLS RFC-7905.

Обоснование

Входные данные для функций шифрования/дешифрования блока AEAD в сообщении New Session:


k :: 32 byte cipher key
       See New Session and New Session Reply KDFs above.

  n :: Counter-based nonce, 12 bytes.
       n = 0

  ad :: Associated data, 32 bytes.
        The SHA256 hash of the preceding data, as output from mixHash()

  data :: Plaintext data, 0 or more bytes

Примечания

Входные данные для функций шифрования/дешифрования блока AEAD в сообщении Existing Session:


k :: 32 byte session key
       As looked up from the accompanying session tag.

  n :: Counter-based nonce, 12 bytes.
       Starts at 0 and incremented for each message when transmitting.
       For the receiver, the value
       as looked up from the accompanying session tag.
       First four bytes are always zero.
       Last eight bytes are the message number (n), little-endian encoded.
       Maximum value is 65535.
       Session must be ratcheted when N reaches that value.
       Higher values must never be used.

  ad :: Associated data
        The session tag

  data :: Plaintext data, 0 or more bytes

3) AEAD (ChaChaPoly)

Выходные данные функции шифрования, входные данные функции расшифровки:


+----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |       ChaCha20 encrypted data         |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 Message Authentication Code |
  +              (MAC)                    +
  |             16 bytes                  |
  +----+----+----+----+----+----+----+----+

  encrypted data :: Same size as plaintext data, 0 - 65519 bytes

  MAC :: Poly1305 message authentication code, 16 bytes

Входные данные New Session и New Session Reply

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

  • Ключ для шифра (256 бит) согласовывается с помощью SHA256 KDF. Детали KDF для каждого сообщения описаны в отдельных разделах ниже.

  • Фреймы ChaChaPoly имеют известный размер, поскольку они инкапсулированы в сообщение данных I2NP.

  • Для всех сообщений паддинг находится внутри фрейма аутентифицированных данных.

Входные данные существующей сессии

Все полученные данные, которые не прошли проверку AEAD, должны быть отброшены. Ответ не возвращается.

Зашифрованный формат

Используется в NTCP2.

Примечания

Мы по-прежнему используем session tags, как и раньше, но теперь мы используем ratchets для их генерации. Session tags также имели опцию повторного создания ключей, которую мы так и не реализовали. Так что это как double ratchet, но мы так и не сделали второй.

Здесь мы определяем нечто похожее на Double Ratchet от Signal. Теги сессии генерируются детерминированно и идентично на стороне получателя и отправителя.

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

Мы не используем шифрование заголовков, как указано (и опционально) в Signal, вместо этого мы используем теги сессий.

Используя DH ratchet, мы достигаем прямой секретности (forward secrecy), которая никогда не была реализована в ElGamal/AES+SessionTags.

Примечание: Одноразовый открытый ключ New Session не является частью ratchet, его единственная функция — зашифровать начальный DH ratchet ключ Алисы.

Обработка ошибок AEAD

Double Ratchet обрабатывает потерянные или поступившие не по порядку сообщения, включая в каждый заголовок сообщения тег. Получатель ищет индекс тега, это номер сообщения N. Если сообщение содержит блок Message Number со значением PN, получатель может удалить любые теги выше этого значения в предыдущем наборе тегов, сохраняя при этом пропущенные теги из предыдущего набора тегов на случай, если пропущенные сообщения поступят позже.

Обоснование

Мы определяем следующие структуры данных и функции для реализации этих ratchet.

TAGSET_ENTRY

A single entry in a TAGSET.

INDEX
    An integer index, starting with 0

SESSION_TAG
    An identifier to go out on the wire, 8 bytes

SESSION_KEY
    A symmetric key, never goes on the wire, 32 bytes

НАБОР ТЕГОВ

A collection of TAGSET_ENTRIES.

CREATE(key, n)
    Generate a new TAGSET using initial cryptographic key material of 32 bytes.
    The associated session identifier is provided.
    The initial number of of tags to create is specified; this is generally 0 or 1
    for an outgoing session.
    LAST_INDEX = -1
    EXTEND(n) is called.

EXTEND(n)
    Generate n more TAGSET_ENTRIES by calling EXTEND() n times.

EXTEND()
    Generate one more TAGSET_ENTRY, unless the maximum number SESSION_TAGS have
    already been generated.
    If LAST_INDEX is greater than or equal to 65535, return.
    ++ LAST_INDEX
    Create a new TAGSET_ENTRY with the LAST_INDEX value and the calculated SESSION_TAG.
    Calls RATCHET_TAG() and (optionally) RATCHET_KEY().
    For inbound sessions, the calculation of the SESSION_KEY may
    be deferred and calculated in GET_SESSION_KEY().
    Calls EXPIRE()

EXPIRE()
    Remove tags and keys that are too old, or if the TAGSET size exceeds some limit.

RATCHET_TAG()
    Calculates the next SESSION_TAG based on the last SESSION_TAG.

RATCHET_KEY()
    Calculates the next SESSION_KEY based on the last SESSION_KEY.

SESSION
    The associated session.

CREATION_TIME
    When the TAGSET was created.

LAST_INDEX
    The last TAGSET_ENTRY INDEX generated by EXTEND().

GET_NEXT_ENTRY()
    Used for outgoing sessions only.
    EXTEND(1) is called if there are no remaining TAGSET_ENTRIES.
    If EXTEND(1) did nothing, the max of 65535 TAGSETS have been used,
    and return an error.
    Returns the next unused TAGSET_ENTRY.

GET_SESSION_KEY(sessionTag)
    Used for incoming sessions only.
    Returns the TAGSET_ENTRY containing the sessionTag.
    If found, the TAGSET_ENTRY is removed.
    If the SESSION_KEY calculation was deferred, it is calculated now.
    If there are few TAGSET_ENTRIES remaining, EXTEND(n) is called.

4) Храповики

Ratchets, но не так быстро, как Signal. Мы отделяем подтверждение получения ключа от генерации нового ключа. При обычном использовании Алиса и Боб будут каждый выполнять ratchet (дважды) немедленно в New Session, но больше ratchet выполнять не будут.

Обратите внимание, что ratchet предназначен для одного направления и генерирует цепочку ratchet для New Session tag / ключа сообщения для этого направления. Для генерации ключей для обоих направлений необходимо выполнить ratchet дважды.

Вы делаете ratchet каждый раз, когда генерируете и отправляете новый ключ. Вы делаете ratchet каждый раз, когда получаете новый ключ.

Алиса выполняет один ratchet при создании несвязанной исходящей сессии, она не создает входящую сессию (несвязанная сессия не подразумевает ответа).

Bob выполняет ratchet один раз при создании несвязанной входящей сессии и не создает соответствующую исходящую сессию (несвязанная сессия не поддерживает ответы).

Алиса продолжает отправлять сообщения New Session (NS) Бобу до получения одного из сообщений New Session Reply (NSR) от Боба. Затем она использует результаты KDF секции Payload из NSR в качестве входных данных для механизмов ratchet сессии (см. DH Ratchet KDF) и начинает отправлять сообщения Existing Session (ES).

Для каждого полученного сообщения NS Боб создает новую входящую сессию, используя результаты KDF секции полезной нагрузки ответа в качестве входных данных для нового входящего и исходящего ES DH Ratchet.

Для каждого требуемого ответа Боб отправляет Алисе NSR сообщение с ответом в полезной нагрузке. Обязательно, чтобы Боб использовал новые эфемерные ключи для каждого NSR.

Боб должен получить ES сообщение от Алисы на одной из входящих сессий, прежде чем создавать и отправлять ES сообщения на соответствующей исходящей сессии.

Алиса должна использовать таймер для получения NSR-сообщения от Боба. Если время таймера истекает, сессия должна быть удалена.

Чтобы избежать атаки KCI и/или истощения ресурсов, при которой злоумышленник отбрасывает NSR ответы Боба, заставляя Алису продолжать отправлять NS сообщения, Алиса должна избегать запуска новых сессий к Бобу после определённого количества повторных попыток из-за истечения таймера.

Алиса и Боб выполняют DH ratchet для каждого полученного блока NextKey.

Алиса и Боб каждый генерируют новые наборы тегов и два симметричных ключа после каждого DH ratchet. Для каждого нового ES сообщения в данном направлении, Алиса и Боб продвигают теги сессии и симметричные ключи ratchet.

Частота DH ratchets после первоначального handshake зависит от реализации. Хотя протокол устанавливает ограничение в 65535 сообщений до того, как потребуется ratchet, более частое ratcheting (основанное на количестве сообщений, прошедшем времени или и том, и другом) может обеспечить дополнительную безопасность.

После финального handshake KDF на связанных сессиях, Bob и Alice должны выполнить функцию Noise Split() на результирующем CipherState для создания независимых симметричных ключей и ключей цепочки тегов для входящих и исходящих сессий.

KEY AND TAG SET IDS

Номера ID ключей и наборов тегов используются для идентификации ключей и наборов тегов. ID ключей используются в блоках NextKey для идентификации отправленного или используемого ключа. ID наборов тегов используются (вместе с номером сообщения) в блоках ACK для идентификации подтверждаемого сообщения. ID как ключей, так и наборов тегов применяются к наборам тегов для одного направления. Номера ID ключей и наборов тегов должны быть последовательными.

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

Для начала DH ratchet отправитель передает новый блок NextKey с идентификатором ключа 0. Получатель отвечает новым блоком NextKey с идентификатором ключа 0. Затем отправитель начинает использовать новый набор тегов с идентификатором набора тегов 1.

Последующие наборы тегов генерируются аналогично. Для всех наборов тегов, используемых после обменов NextKey, номер набора тегов равен (1 + идентификатор ключа Alice + идентификатор ключа Bob).

Идентификаторы ключей и наборов тегов начинаются с 0 и увеличиваются последовательно. Максимальный идентификатор набора тегов — 65535. Максимальный идентификатор ключа — 32767. Когда набор тегов почти исчерпан, отправитель набора тегов должен инициировать обмен NextKey. Когда набор тегов 65535 почти исчерпан, отправитель набора тегов должен инициировать новую сессию, отправив сообщение New Session.

При максимальном размере потокового сообщения 1730 и при условии отсутствия повторных передач, теоретический максимальный объем передачи данных с использованием одного набора тегов составляет 1730 * 65536 ~= 108 МБ. Фактический максимум будет ниже из-за повторных передач.

Теоретический максимум передачи данных со всеми 65536 доступными наборами тегов, до того как сессия должна быть отброшена и заменена, составляет 64K * 108 МБ ~= 6,9 ТБ.

DH RATCHET MESSAGE FLOW

Следующий обмен ключами для набора тегов должен быть инициирован отправителем этих тегов (владельцем исходящего набора тегов). Получатель (владелец входящего набора тегов) ответит. При типичном HTTP GET трафике на прикладном уровне Боб отправит больше сообщений и будет выполнять ratchet первым, инициируя обмен ключами; диаграмма ниже это показывает. Когда Алиса выполняет ratchet, то же самое происходит в обратном порядке.

Первый набор тегов, используемый после рукопожатия NS/NSR, это набор тегов 0. Когда набор тегов 0 почти исчерпан, новые ключи должны быть обменены в обоих направлениях для создания набора тегов 1. После этого новый ключ отправляется только в одном направлении.

Для создания набора тегов 2 отправитель тега посылает новый ключ, а получатель тега отправляет ID своего старого ключа в качестве подтверждения. Обе стороны выполняют DH.

Для создания набора тегов 3 отправитель тега отправляет идентификатор своего старого ключа и запрашивает новый ключ у получателя тега. Обе стороны выполняют DH.

Последующие наборы тегов генерируются так же, как наборы тегов 2 и 3. Номер набора тегов равен (1 + идентификатор ключа отправителя + идентификатор ключа получателя).


Tag Sender                    Tag Receiver

                   ... use tag set #0 ...


  (Tagset #0 almost empty)
  (generate new key #0)

  Next Key, forward, request reverse, with key #0  -------->
  (repeat until next key received)

                              (generate new key #0, do DH, create IB Tagset #1)

          <-------------      Next Key, reverse, with key #0
                              (repeat until tag received on new tagset)

  (do DH, create OB Tagset #1)


                   ... use tag set #1 ...


  (Tagset #1 almost empty)
  (generate new key #1)

  Next Key, forward, with key #1        -------->
  (repeat until next key received)

                              (reuse key #0, do DH, create IB Tagset #2)

          <--------------     Next Key, reverse, id 0
                              (repeat until tag received on new tagset)

  (do DH, create OB Tagset #2)


                   ... use tag set #2 ...


  (Tagset #2 almost empty)
  (reuse key #1)

  Next Key, forward, request reverse, id 1  -------->
  (repeat until next key received)

                              (generate new key #1, do DH, create IB Tagset #3)

          <--------------     Next Key, reverse, with key #1

  (do DH, create OB Tagset #3)
  (reuse key #1, do DH, create IB Tagset #3)



                   ... use tag set #3 ...



       After tag set 3, repeat the above
       patterns as shown for tag sets 2 and 3.

       To create a new even-numbered tag set, the sender sends a new key
       to the receiver. The receiver sends his old key ID
       back as an acknowledgement.

       To create a new odd-numbered tag set, the sender sends a reverse request
       to the receiver. The receiver sends a new reverse key to the sender.

После завершения DH ratchet для исходящего набора тегов и создания нового исходящего набора тегов, он должен использоваться немедленно, а старый исходящий набор тегов может быть удален.

После завершения DH ratchet для входящего tagset и создания нового входящего tagset, получатель должен прослушивать теги в обоих tagsets и удалить старый tagset через короткое время, примерно через 3 минуты.

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

New Tag Set IDSender key IDRcvr key ID
0n/an/a
10 *0 *
21 *0
311 *
42 *1
522 *
6553432767 *32766
655353276732767 *
Номера ID наборов ключей и тегов должны быть последовательными.

DH INITIALIZATION KDF

Это определение DH_INITIALIZE(rootKey, k) для одного направления. Оно создает tagset и “следующий корневой ключ”, который будет использоваться для последующего DH ratchet при необходимости.

Мы используем DH инициализацию в трех местах. Во-первых, мы используем ее для генерации набора тегов для New Session Replies. Во-вторых, мы используем ее для генерации двух наборов тегов, по одному для каждого направления, для использования в сообщениях Existing Session. Наконец, мы используем ее после DH Ratchet для генерации нового набора тегов в одном направлении для дополнительных сообщений Existing Session.


Inputs:
  1) rootKey = chainKey from Payload Section
  2) k from the New Session KDF or split()

  // KDF_RK(rk, dh_out)
  keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)

  // Output 1: The next Root Key (KDF input for the next DH ratchet)
  nextRootKey = keydata[0:31]
  // Output 2: The chain key to initialize the new
  // session tag and symmetric key ratchets
  // for the tag set
  ck = keydata[32:63]

  // session tag and symmetric key chain keys
  keydata = HKDF(ck, ZEROLEN, "TagAndKeyGenKeys", 64)
  sessTag_ck = keydata[0:31]
  symmKey_ck = keydata[32:63]

DH RATCHET KDF

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



// Tag sender generates new X25519 ephemeral keys
  // and sends rapk to tag receiver in a NextKey block
  rask = GENERATE_PRIVATE()
  rapk = DERIVE_PUBLIC(rask)
  
  // Tag receiver generates new X25519 ephemeral keys
  // and sends rbpk to Tag sender in a NextKey block
  rbsk = GENERATE_PRIVATE()
  rbpk = DERIVE_PUBLIC(rbsk)

  sharedSecret = DH(rask, rbpk) = DH(rbsk, rapk)
  tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
  rootKey = nextRootKey // from previous tagset in this direction
  newTagSet = DH_INITIALIZE(rootKey, tagsetKey)

Номера сообщений

Ratchets для каждого сообщения, как в Signal. Ratchet тега сессии синхронизирован с ratchet симметричного ключа, но ratchet ключа получателя может “отставать”, чтобы сэкономить память.

Передатчик выполняет ratchet один раз для каждого передаваемого сообщения. Дополнительные теги хранить не требуется. Передатчик также должен вести счетчик для ‘N’ — номера сообщения в текущей цепи. Значение ‘N’ включается в отправляемое сообщение. См. определение блока Message Number.

Получатель должен продвинуть ratchet вперед на максимальный размер окна и сохранить теги в “наборе тегов”, который связан с сессией. После получения сохраненный тег может быть отброшен, и если нет предыдущих неполученных тегов, окно может быть продвинуто. Получатель должен хранить значение ‘N’, связанное с каждым тегом сессии, и проверять, что номер в отправленном сообщении соответствует этому значению. См. определение блока Message Number.

KDF

Это определение RATCHET_TAG().


Inputs:
  1) Session Tag Chain key sessTag_ck
     First time: output from DH ratchet
     Subsequent times: output from previous session tag ratchet

  Generated:
  2) input_key_material = SESSTAG_CONSTANT
     Must be unique for this tag set (generated from chain key),
     so that the sequence isn't predictable, since session tags
     go out on the wire in plaintext.

  Outputs:
  1) N (the current session tag number)
  2) the session tag (and symmetric key, probably)
  3) the next Session Tag Chain Key (KDF input for the next session tag ratchet)

  Initialization:
  keydata = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
  // Output 1: Next chain key
  sessTag_chainKey = keydata[0:31]
  // Output 2: The constant
  SESSTAG_CONSTANT = keydata[32:63]

  // KDF_ST(ck, constant)
  keydata_0 = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
  // Output 1: Next chain key
  sessTag_chainKey_0 = keydata_0[0:31]
  // Output 2: The session tag
  // or more if tag is longer than 8 bytes
  tag_0 = keydata_0[32:39]

  // repeat as necessary to get to tag_n
  keydata_n = HKDF(sessTag_chainKey_(n-1), SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
  // Output 1: Next chain key
  sessTag_chainKey_n = keydata_n[0:31]
  // Output 2: The session tag
  // or more if tag is longer than 8 bytes
  tag_n = keydata_n[32:39]

Пример реализации

Ratchets для каждого сообщения, как в Signal. Каждый симметричный ключ имеет связанный номер сообщения и тег сессии. Ratchet ключа сессии синхронизирован с симметричным ratchet тега, но ratchet ключа получателя может “отставать”, чтобы сэкономить память.

Передатчик делает один поворот ratchet для каждого отправленного сообщения. Дополнительные ключи хранить не требуется.

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

Для эффективности механизмы session tag и симметричного ключа разделены, чтобы механизм session tag мог опережать механизм симметричного ключа. Это также обеспечивает дополнительную безопасность, поскольку session tag передаются по сети.

KDF

Это определение RATCHET_KEY().


Inputs:
  1) Symmetric Key Chain key symmKey_ck
     First time: output from DH ratchet
     Subsequent times: output from previous symmetric key ratchet

  Generated:
  2) input_key_material = SYMMKEY_CONSTANT = ZEROLEN
     No need for uniqueness. Symmetric keys never go out on the wire.
     TODO: Set a constant anyway?

  Outputs:
  1) N (the current session key number)
  2) the session key
  3) the next Symmetric Key Chain Key (KDF input for the next symmetric key ratchet)

  // KDF_CK(ck, constant)
  SYMMKEY_CONSTANT = ZEROLEN
  // Output 1: Next chain key
  keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
  symmKey_chainKey_0 = keydata_0[0:31]
  // Output 2: The symmetric key
  k_0 = keydata_0[32:63]

  // repeat as necessary to get to k[n]
  keydata_n = HKDF(symmKey_chainKey_(n-1), SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
  // Output 1: Next chain key
  symmKey_chainKey_n = keydata_n[0:31]
  // Output 2: The symmetric key
  k_n = keydata_n[32:63]

4a) DH Ratchet

Это заменяет формат секции AES, определённый в спецификации ElGamal/AES+SessionTags.

Используется тот же формат блока, который определён в спецификации NTCP2. Отдельные типы блоков определяются по-разному.

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

Payload Section Decrypted data

Зашифрованная длина — это оставшаяся часть данных. Расшифрованная длина на 16 меньше зашифрованной длины. Поддерживаются все типы блоков. Типичное содержимое включает следующие блоки:

Payload Block TypeType NumberBlock Length
DateTime07
Termination (TBD)49 typ.
Options (TBD)521+
Message Number (TBD)6TBD
Next Key73 or 35
ACK84 typ.
ACK Request93
Garlic Clove11varies
Padding254varies

Unencrypted data

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

Для расширяемости получатели ДОЛЖНЫ игнорировать блоки с неизвестными номерами типов и рассматривать их как заполнение.

Зашифрованные данные имеют максимальный размер 65535 байт, включая 16-байтовый заголовок аутентификации, поэтому максимальный размер незашифрованных данных составляет 65519 байт.

(Тег аутентификации Poly1305 не показан):


+----+----+----+----+----+----+----+----+
  |blk |  size   |       data             |
  +----+----+----+                        +
  |                                       |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+
  |blk |  size   |       data             |
  +----+----+----+                        +
  |                                       |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+
  ~               .   .   .               ~

  blk :: 1 byte
         0 datetime
         1-3 reserved
         4 termination
         5 options
         6 previous message number
         7 next session key
         8 ack
         9 ack request
         10 reserved
         11 Garlic Clove
         224-253 reserved for experimental features
         254 for padding
         255 reserved for future extension
  size :: 2 bytes, big endian, size of data to follow, 0 - 65516
  data :: the data

  Maximum ChaChaPoly frame is 65535 bytes.
  Poly1305 tag is 16 bytes
  Maximum total block size is 65519 bytes
  Maximum single block size is 65519 bytes
  Block type is 1 byte
  Block length is 2 bytes
  Maximum single block data size is 65516 bytes.

Block Ordering Rules

В сообщении New Session блок DateTime является обязательным и должен быть первым блоком.

Другие разрешенные блоки:

  • Garlic Clove (тип 11)
  • Параметры (тип 5)
  • Заполнение (тип 254)

В сообщении New Session Reply блоки не требуются.

Другие разрешенные блоки:

  • Garlic Clove (тип 11)
  • Параметры (тип 5)
  • Заполнение (тип 254)

Никакие другие блоки не разрешены. Дополнение, если присутствует, должно быть последним блоком.

В сообщении Existing Session блоки не требуются, и порядок не определен, за исключением следующих требований:

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

В одном кадре может быть несколько блоков Garlic Clove. В одном кадре может быть до двух блоков Next Key. Несколько блоков Padding не допускается в одном кадре. Другие типы блоков, вероятно, не будут иметь несколько блоков в одном кадре, но это не запрещено.

DateTime

Срок истечения. Помогает предотвратить повторные ответы. Боб должен проверить, что сообщение является свежим, используя данную временную метку. Боб должен реализовать фильтр Блума или другой механизм для предотвращения атак повторного воспроизведения, если время действительно. Обычно включается только в сообщения New Session.


+----+----+----+----+----+----+----+
  | 0  |    4    |     timestamp     |
  +----+----+----+----+----+----+----+

  blk :: 0
  size :: 2 bytes, big endian, value = 4
  timestamp :: Unix timestamp, unsigned seconds.
               Wraps around in 2106

4b) Храповик тегов сессии

Один расшифрованный Garlic Clove, как указано в I2NP, с изменениями для удаления полей, которые не используются или являются избыточными. Предупреждение: Этот формат значительно отличается от формата для ElGamal/AES. Каждый clove является отдельным блоком полезной нагрузки. Garlic Cloves не могут быть фрагментированы между блоками или между фреймами ChaChaPoly.


+----+----+----+----+----+----+----+----+
  | 11 |  size   |                        |
  +----+----+----+                        +
  |      Delivery Instructions            |
  ~                                       ~
  ~                                       ~
  |                                       |
  +----+----+----+----+----+----+----+----+
  |type|  Message_ID       | Expiration   
  +----+----+----+----+----+----+----+----+
       |      I2NP Message body           |
  +----+                                  +
  ~                                       ~
  ~                                       ~
  |                                       |
  +----+----+----+----+----+----+----+----+

  size :: size of all data to follow

  Delivery Instructions :: As specified in
         the Garlic Clove section of [I2NP](/docs/specs/i2np/).
         Length varies but is typically 1, 33, or 37 bytes

  type :: I2NP message type

  Message_ID :: 4 byte `Integer` I2NP message ID

  Expiration :: 4 bytes, seconds since the epoch

Примечания:

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

  • Формат Clove Set, указанный в I2NP, не используется. Каждый clove содержится в своем собственном блоке.

  • Заголовок I2NP сообщения составляет 9 байт, с идентичным форматом тому, что используется в NTCP2.

  • Certificate, Message ID и Expiration из определения Garlic Message в I2NP не включены.

  • Certificate, Clove ID и Expiration из определения Garlic Clove в I2NP не включены.

Обоснование:

  • Сертификаты никогда не использовались.
  • Отдельные ID сообщений и clove ID никогда не использовались.
  • Отдельные сроки истечения никогда не использовались.
  • Общая экономия по сравнению со старыми форматами Clove Set и Clove составляет приблизительно 35 байт для 1 clove, 54 байта для 2 clove и 73 байта для 3 clove.
  • Формат блоков является расширяемым, и любые новые поля могут быть добавлены как новые типы блоков.

Termination

Реализация необязательна. Закрыть сессию. Это должен быть последний блок без padding в кадре. Больше сообщений в этой сессии отправлено не будет.

Не разрешено в NS или NSR. Включается только в сообщения Existing Session.


+----+----+----+----+----+----+----+----+
  | 4  |  size   | rsn|     addl data     |
  +----+----+----+----+                   +
  ~               .   .   .               ~
  +----+----+----+----+----+----+----+----+

  blk :: 4
  size :: 2 bytes, big endian, value = 1 or more
  rsn :: reason, 1 byte:
         0: normal close or unspecified
         1: termination received
         others: optional, impementation-specific
  addl data :: optional, 0 or more bytes, for future expansion, debugging,
               or reason text.
               Format unspecified and may vary based on reason code.

4c) Симметричный ключевой храповик

НЕ РЕАЛИЗОВАНО, для дальнейшего изучения. Передавать обновленные опции. Опции включают различные параметры для сессии. Смотрите раздел “Анализ длины тега сессии” ниже для получения дополнительной информации.

Блок опций может иметь переменную длину, поскольку может присутствовать more_options.


+----+----+----+----+----+----+----+----+
  | 5  |  size   |ver |flg |STL |STimeout |
  +----+----+----+----+----+----+----+----+
  |  SOTW   |  RITW   |tmin|tmax|rmin|rmax|
  +----+----+----+----+----+----+----+----+
  |  tdmy   |  rdmy   |  tdelay |  rdelay |
  +----+----+----+----+----+----+----+----+
  |              more_options             |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+

  blk :: 5
  size :: 2 bytes, big endian, size of options to follow, 21 bytes minimum
  ver :: Protocol version, must be 0
  flg :: 1 byte flags
         bits 7-0: Unused, set to 0 for future compatibility
  STL :: Session tag length (must be 8), other values unimplemented
  STimeout :: Session idle timeout (seconds), big endian
  SOTW :: Sender Outbound Tag Window, 2 bytes big endian
  RITW :: Receiver Inbound Tag Window 2 bytes big endian

  tmin, tmax, rmin, rmax :: requested padding limits
      tmin and rmin are for desired resistance to traffic analysis.
      tmax and rmax are for bandwidth limits.
      tmin and tmax are the transmit limits for the router sending this options block.
      rmin and rmax are the receive limits for the router sending this options block.
      Each is a 4.4 fixed-point float representing 0 to 15.9375
      (or think of it as an unsigned 8-bit integer divided by 16.0).
      This is the ratio of padding to data. Examples:
      Value of 0x00 means no padding
      Value of 0x01 means add 6 percent padding
      Value of 0x10 means add 100 percent padding
      Value of 0x80 means add 800 percent (8x) padding
      Alice and Bob will negotiate the minimum and maximum in each direction.
      These are guidelines, there is no enforcement.
      Sender should honor receiver's maximum.
      Sender may or may not honor receiver's minimum, within bandwidth constraints.

  tdmy: Max dummy traffic willing to send, 2 bytes big endian, bytes/sec average
  rdmy: Requested dummy traffic, 2 bytes big endian, bytes/sec average
  tdelay: Max intra-message delay willing to insert, 2 bytes big endian, msec average
  rdelay: Requested intra-message delay, 2 bytes big endian, msec average

  more_options :: Format undefined, for future use

SOTW — это рекомендация отправителя получателю относительно окна входящих тегов получателя (максимальное упреждение). RITW — это заявление отправителя об окне входящих тегов (максимальное упреждение), которое он планирует использовать. Каждая сторона затем устанавливает или корректирует упреждение на основе некоторого минимума, максимума или другого расчета.

Примечания:

  • Поддержка нестандартной длины тегов сессии, надеюсь, никогда не потребуется.
  • Окно тегов - это MAX_SKIP в документации Signal.

Проблемы:

  • Согласование опций находится в разработке (TBD).
  • Значения по умолчанию находятся в разработке (TBD).
  • Опции заполнения и задержки скопированы из NTCP2, но эти опции там не были полностью реализованы или изучены.

Message Numbers

Реализация необязательна. Длина (количество отправленных сообщений) в предыдущем наборе тегов (PN). Получатель может немедленно удалить теги с номерами выше PN из предыдущего набора тегов. Получатель может удалить теги с номерами меньше или равными PN из предыдущего набора тегов через короткое время (например, 2 минуты).


+----+----+----+----+----+
  | 6  |  size   |  PN    |
 +----+----+----+----+----+

  blk :: 6
  size :: 2
  PN :: 2 bytes big endian. The index of the last tag sent in the previous tag set.

Примечания:

  • Максимальный PN равен 65535.
  • Определения PN равны определению Signal, минус один. Это аналогично тому, что делает Signal, но в Signal PN и N находятся в заголовке. Здесь они находятся в зашифрованном теле сообщения.
  • Не отправляйте этот блок в наборе тегов 0, поскольку предыдущего набора тегов не было.

5) Полезная нагрузка

Следующий ключ DH ratchet находится в полезной нагрузке и является необязательным. Мы не выполняем ratchet каждый раз. (Это отличается от signal, где он находится в заголовке и отправляется каждый раз)

Для первого ratchet, Key ID = 0.

Не разрешено в NS или NSR. Включается только в сообщения существующих сессий.


+----+----+----+----+----+----+----+----+
  | 7  |  size   |flag|  key ID |         |
  +----+----+----+----+----+----+         +
  |                                       |
  +                                       +
  |     Next DH Ratchet Public Key        |
  +                                       +
  |                                       |
  +                             +----+----+
  |                             |
  +----+----+----+----+----+----+

  blk :: 7
  size :: 3 or 35
  flag :: 1 byte flags
          bit order: 76543210
          bit 0: 1 for key present, 0 for no key present
          bit 1: 1 for reverse key, 0 for forward key
          bit 2: 1 to request reverse key, 0 for no request
                 only set if bit 1 is 0
          bits 7-2: Unused, set to 0 for future compatibility
  key ID :: The key ID of this key. 2 bytes, big endian
            0 - 32767
  Public Key :: The next X25519 public key, 32 bytes, little endian
                Only if bit 0 is 1

Примечания:

  • Key ID — это инкрементный счетчик для локального ключа, используемого для данного набора тегов, начинающийся с 0.
  • ID не должен изменяться, если не изменяется ключ.
  • Это может быть не строго необходимо, но полезно для отладки. Signal не использует key ID.
  • Максимальный Key ID равен 32767.
  • В редких случаях, когда наборы тегов в обоих направлениях изменяются одновременно, кадр будет содержать два блока Next Key — один для прямого ключа и один для обратного ключа.
  • Номера ID ключей и наборов тегов должны быть последовательными.
  • Подробности см. в разделе DH Ratchet выше.

Раздел полезной нагрузки Расшифрованные данные

Это отправляется только в том случае, если был получен блок запроса подтверждения. Может присутствовать несколько подтверждений для подтверждения нескольких сообщений.

Не разрешено в NS или NSR. Включается только в сообщения Existing Session.

+----+----+----+----+----+----+----+----+
  | 8  |  size   |tagsetid |   N     |    |
  +----+----+----+----+----+----+----+    +
  |             more acks                 |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+

  blk :: 8
  size :: 4 * number of acks to follow, minimum 1 ack
  for each ack:
  tagsetid :: 2 bytes, big endian, from the message being acked
  N :: 2 bytes, big endian, from the message being acked

Примечания:

  • ID набора тегов и N уникально идентифицируют подтверждаемое сообщение.
  • В первых наборах тегов, используемых для сессии в каждом направлении, ID набора тегов равен 0.
  • NextKey блоки не были отправлены, поэтому ID ключей отсутствуют.
  • Для всех наборов тегов, используемых после обмена NextKey, номер набора тегов равен (1 + ID ключа Alice + ID ключа Bob).

Незашифрованные данные

Запросить подтверждение в полосе передачи. Для замены внеполосного сообщения DeliveryStatus в Garlic Clove.

Если запрашивается явное подтверждение, текущий ID набора тегов и номер сообщения (N) возвращаются в блоке подтверждения.

Не разрешено в NS или NSR. Включается только в сообщения Existing Session.


+----+----+----+----+
  |  9 |  size   |flg |
  +----+----+----+----+

  blk :: 9
  size :: 1
  flg :: 1 byte flags
         bits 7-0: Unused, set to 0 for future compatibility

Правила упорядочивания блоков

Все заполнение находится внутри AEAD-фреймов. TODO Заполнение внутри AEAD должно приблизительно соответствовать согласованным параметрам. TODO Alice отправила свои запрошенные параметры tx/rx min/max в сообщении NS. TODO Bob отправил свои запрошенные параметры tx/rx min/max в сообщении NSR. Обновленные опции могут быть отправлены во время фазы данных. См. информацию о блоке опций выше.

Если присутствует, этот блок должен быть последним в кадре.


+----+----+----+----+----+----+----+----+
  |254 |  size   |      padding           |
  +----+----+----+                        +
  |                                       |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+

  blk :: 254
  size :: 2 bytes, big endian, 0-65516
  padding :: zeros or random data

Примечания:

  • Заполнение нулями допустимо, так как данные будут зашифрованы.
  • Стратегии заполнения пока не определены.
  • Фреймы, содержащие только заполнение, разрешены.
  • По умолчанию заполнение составляет 0-15 байт.
  • См. блок options для согласования параметра заполнения
  • См. блок options для параметров минимального/максимального заполнения
  • Реакция router на нарушение согласованного заполнения зависит от реализации.

Дата и время

Реализации должны игнорировать неизвестные типы блоков для обеспечения прямой совместимости.

Зубчик Garlic

  • Длина заполнения должна определяться либо для каждого сообщения отдельно на основе оценок распределения длин, либо должны добавляться случайные задержки. Эти контрмеры должны быть включены для противодействия DPI, поскольку размеры сообщений иначе раскрыли бы, что I2P трафик передается транспортным протоколом. Точная схема заполнения является областью будущей работы, Приложение A предоставляет больше информации по этой теме.

Typical Usage Patterns

Завершение

Это наиболее типичный случай использования, и большинство случаев потокового использования не-HTTP будут идентичны этому случаю. Отправляется небольшое начальное сообщение, следует ответ, и дополнительные сообщения отправляются в обоих направлениях.

HTTP GET запрос обычно помещается в одно I2NP сообщение. Alice отправляет небольшой запрос с одним новым Session сообщением, включая reply leaseset. Alice включает немедленный ratchet к новому ключу. Включает подпись для привязки к назначению. Подтверждение не запрашивается.

Боб выполняет ratchet немедленно.

Алиса выполняет ratchet немедленно.

Продолжает работу с этими сессиями.


Alice                           Bob

  New Session (1b)     ------------------->
  with ephemeral key 1
  with static key for binding
  with next key
  with bundled HTTP GET
  with bundled LS
  without bundled Delivery Status Message

  any retransmissions, same as above

  following messages may arrive in any order:

  <--------------     New Session Reply (1g)
                      with Bob ephemeral key 1
                      with bundled HTTP reply part 1

  <--------------     New Session Reply (1g)
                      with Bob ephemeral key 2
                      with bundled HTTP reply part 2

  <--------------     New Session Reply (1g)
                      with Bob ephemeral key 3
                      with bundled HTTP reply part 3

  After reception of any of these messages,
  Alice switches to use Existing Session messages,
  creates a new inbound + outbound session pair,
  and ratchets.


  Existing Session     ------------------->
  with bundled streaming ack


  Existing Session     ------------------->
  with bundled streaming ack


  After reception of any of these messages,
  Bob switches to use Existing Session messages.


  <--------------     Existing Session
                      with bundled HTTP reply part 4


  Existing Session     ------------------->
  with bundled streaming ack

  <--------------     Existing Session
                      with bundled HTTP reply part 5

Опции

У Алисы есть три варианта:

  1. Отправить только первое сообщение (размер окна = 1), как в HTTP GET. Не рекомендуется.

  2. Отправлять до окна потоковой передачи, но используя тот же открытый ключ в открытом виде, закодированный Elligator2. Все сообщения содержат один и тот же следующий открытый ключ (ratchet). Это будет видно для OBGW/IBEP, поскольку все они начинаются с одного и того же открытого текста. Процесс протекает как в 1). Не рекомендуется.

  3. Рекомендуемая реализация. Отправлять до размера окна потока, но используя разный Elligator2-кодированный открытый публичный ключ (сессия) для каждого. Все сообщения содержат один и тот же следующий публичный ключ (ratchet). Это не будет видно для OBGW/IBEP, поскольку все они начинаются с разного открытого текста. Боб должен распознать, что все они содержат один и тот же следующий публичный ключ, и ответить всем с тем же ratchet. Алиса использует этот следующий публичный ключ и продолжает.

Поток сообщений варианта 3:


Alice                           Bob

  New Session (1b)     ------------------->
  with ephemeral key 1
  with static key for binding
  with bundled HTTP POST part 1
  with bundled LS
  without bundled Delivery Status Message


  New Session (1b)     ------------------->
  with ephemeral key 2
  with static key for binding
  with bundled HTTP POST part 2
  with bundled LS
  without bundled Delivery Status Message


  New Session (1b)     ------------------->
  with ephemeral key 3
  with static key for binding
  with bundled HTTP POST part 3
  with bundled LS
  without bundled Delivery Status Message


  following messages can arrive in any order:

  <--------------     New Session Reply (1g)
                      with Bob ephemeral key 1
                      with bundled streaming ack

  <--------------     New Session Reply (1g)
                      with Bob ephemeral key 2
                      with bundled streaming ack

  After reception of any of these messages,
  Alice switches to use Existing Session messages,
  creates a new inbound + outbound session pair,
  and ratchets.


  following messages can arrive in any order:


  Existing Session     ------------------->
  with bundled HTTP POST part 4

  Existing Session     ------------------->
  with next key
  with bundled HTTP POST part 5


  After reception of any of these messages,
  Bob switches to use Existing Session messages.


  <--------------     Existing Session
                      with bundled streaming ack

  After reception of any of this message,
  Alice switches to use Existing Session messages,
  and Alice ratchets.


  Existing Session     ------------------->
  with next key
  with bundled HTTP POST part 4

  after reception of this message, Bob ratchets

  Existing Session     ------------------->
  with next key
  with bundled HTTP POST part 5

  <--------------     Existing Session
                      with bundled streaming ack

Номера сообщений

Одно сообщение с ожиданием одного ответа. Могут быть отправлены дополнительные сообщения или ответы.

Аналогично HTTP GET, но с меньшими опциями для размера окна тегов сессии и времени жизни. Возможно, не запрашивать ratchet.


Alice                           Bob

  New Session (1b)     ------------------->
  with static key for binding
  with next key
  with bundled repliable datagram
  with bundled LS
  without bundled Delivery Status Message


  <--------------     New Session Reply (1g)
                      with Bob ephemeral key
                      with bundled reply part 1

  <--------------     New Session Reply (1g)
                      with Bob ephemeral key
                      with bundled reply part 2

  After reception of either message,
  Alice switches to use Existing Session messages,
  and ratchets.

  If the Existing Session message arrives first,
  Alice ratchets on the existing inbound and outbound
  sessions.

  When the New Session Reply arrives, Alice
  sets the existing inbound session to expire,
  creates a new inbound and outbound session,
  and sends Existing Session messages on
  the new outbound session.

  Alice keeps the expiring inbound session
  around for a while to process the Existing Session
  message sent to Alice.
  If all expected original Existing Session message replies
  have been processed, Alice can expire the original
  inbound session immediately.

  if there are any other messages:

  Existing Session     ------------------->
  with bundled message

  Existing Session     ------------------->
  with bundled streaming ack

  <--------------     Existing Session
                      with bundled message

Следующий публичный ключ DH Ratchet

Множественные анонимные сообщения, без ожидания ответов.

В данном сценарии Алиса запрашивает сессию, но без привязки. Отправляется сообщение новой сессии. Reply LS не включается в пакет. Reply DSM включается в пакет (это единственный случай использования, который требует включенных в пакет DSM). Next key не включается. Reply или ratchet не запрашиваются. Ratchet не отправляется. Опции устанавливают окно session tags в ноль.


Alice                           Bob

  New Session (1c)     ------------------->
  with bundled message
  without bundled LS
  with bundled Delivery Status Message 1

  New Session (1c)     ------------------->
  with bundled message
  without bundled LS
  with bundled Delivery Status Message 2

  New Session (1c)     ------------------->
  with bundled message
  without bundled LS
  with bundled Delivery Status Message 3
 
  following messages can arrive in any order:

  <--------------     Delivery Status Message 1

  <--------------     Delivery Status Message 2

  <--------------     Delivery Status Message 3

  After reception of any of these messages,
  Alice switches to use Existing Session messages.

  Existing Session     ------------------->

  Existing Session     ------------------->

  Existing Session     ------------------->

Подтверждение

Одиночное анонимное сообщение без ожидания ответа.

Одноразовое сообщение отправлено. Никакие ответные LS или DSM не включены. Следующий ключ не включен. Ответ или ratchet не запрашивается. Ratchet не отправляется. Параметры устанавливают окно тегов сессии в ноль.


Alice                           Bob

  One-Time Message (1d)   ------------------->
  with bundled message
  without bundled LS
  without bundled Delivery Status Message

Запрос подтверждения

Долговременные сессии могут выполнить ratchet или запросить ratchet в любое время для поддержания прямой секретности с этого момента времени. Сессии должны выполнить ratchet при приближении к лимиту отправленных сообщений на сессию (65535).

Implementation Considerations

Заполнение

Как и в случае с существующим протоколом ElGamal/AES+SessionTag, реализации должны ограничивать хранение session tag и защищаться от атак исчерпания памяти.

Некоторые рекомендуемые стратегии включают:

  • Жёсткий лимит на количество хранимых меток сеанса
  • Агрессивное истечение неактивных входящих сеансов при нехватке памяти
  • Ограничение на количество входящих сеансов, привязанных к одному удалённому адресату
  • Адаптивное уменьшение окна меток сеанса и удаление старых неиспользуемых меток при нехватке памяти
  • Отказ от ratchet по запросу при нехватке памяти

Другие типы блоков

Рекомендуемые параметры и таймауты:

  • Размер tagset NSR: 12 tsmin и tsmax
  • Размер tagset ES 0: tsmin 24, tsmax 160
  • Размер tagset ES (1+): 160 tsmin и tsmax
  • Таймаут tagset NSR: 3 минуты для получателя
  • Таймаут tagset ES: 8 минут для отправителя, 10 минут для получателя
  • Удаление предыдущего tagset ES через: 3 минуты
  • Опережающий просмотр tagset для тега N: min(tsmax, tsmin + N/4)
  • Обрезка tagset позади тега N: min(tsmax, tsmin + N/4) / 2
  • Отправка следующего ключа на теге: TBD
  • Отправка следующего ключа после времени жизни tagset: TBD
  • Замена сессии при получении NS после: 3 минуты
  • Максимальное расхождение часов: от -5 минут до +2 минут
  • Длительность фильтра повторов NS: 5 минут
  • Размер заполнения: 0-15 байт (другие стратегии TBD)

Будущая работа

Ниже приведены рекомендации по классификации входящих сообщений.

X25519 Only

В туннеле, который используется исключительно с этим протоколом, выполняйте идентификацию так же, как это делается в настоящее время с ElGamal/AES+SessionTags:

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

Если не найден, обрабатывайте исходные данные как открытый ключ DH и nonce. Выполните операцию DH и указанную KDF, и попытайтесь расшифровать оставшиеся данные.

HTTP GET

В туннеле, который поддерживает как этот протокол, так и ElGamal/AES+SessionTags, классифицируйте входящие сообщения следующим образом:

Из-за недостатка в спецификации ElGamal/AES+SessionTags, блок AES не дополняется до случайной длины, не кратной 16. Поэтому длина сообщений Existing Session по модулю 16 всегда равна 0, а длина сообщений New Session по модулю 16 всегда равна 2 (поскольку блок ElGamal имеет длину 514 байт).

Если длина по модулю 16 не равна 0 или 2, обрабатывайте исходные данные как session tag и выполните поиск session tag. Если найден, расшифруйте, используя сохраненные данные, связанные с этим session tag.

Если не найден, и длина по модулю 16 не равна 0 или 2, рассматривать исходные данные как открытый ключ DH и nonce. Выполнить операцию DH и указанную KDF, и попытаться расшифровать оставшиеся данные. (основываясь на относительном составе трафика и относительных затратах операций X25519 и ElGamal DH, этот шаг может быть выполнен последним)

В противном случае, если длина по модулю 16 равна 0, обрабатывайте исходные данные как тег сеанса ElGamal/AES и выполните поиск тега сеанса. Если найден, расшифруйте, используя сохранённые данные, связанные с этим тегом сеанса.

Если не найдено, и данные составляют не менее 642 (514 + 128) байтов в длину, и длина по модулю 16 равна 2, рассматривайте исходные данные как блок ElGamal. Попытайтесь расшифровать оставшиеся данные.

Обратите внимание, что если спецификация ElGamal/AES+SessionTag будет обновлена для поддержки заполнения, не кратного 16, то потребуется делать это по-другому.

HTTP POST

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

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

Получение сообщений NS и NSR требует ответа; получение блоков ACK Request и Next Key также требует ответа.

Сложная реализация может запустить таймер при получении одного из этих сообщений, которое требует ответа, и сгенерировать “пустой” ответ (без блока Garlic Clove) на уровне ECIES, если обратный трафик не отправляется в течение короткого периода времени (например, 1 секунда).

Также может быть целесообразным установить еще более короткий таймаут для ответов на сообщения NS и NSR, чтобы как можно быстрее перенести трафик на эффективные сообщения ES.

Analysis

Отвечаемая датаграмма

Накладные расходы на сообщения для первых двух сообщений в каждом направлении составляют следующее. Это предполагает только одно сообщение в каждом направлении перед ACK, или что любые дополнительные сообщения отправляются спекулятивно как сообщения Existing Session. Если нет спекулятивных подтверждений доставленных тегов сессии, накладные расходы старого протокола намного выше.

Для анализа нового протокола не предполагается заполнение. Не предполагается объединённый leaseSet.

Множественные необработанные датаграммы

Сообщение новой сессии, одинаковое в каждом направлении:


ElGamal block:
  514 bytes

  AES block:
  - 2 byte tag count
  - 1024 bytes of tags (32 typical)
  - 4 byte payload size
  - 32 byte hash of payload
  - 1 byte flags
  - 1 byte clove count
  - 33 byte Garlic deliv. inst.
  - 16 byte I2NP header
  - 15 byte clove cert, id, exp.
  - 15 byte msg cert, id, exp.
  - 0 byte padding assuming 1936 byte message
  1143 total

  Total:
  1657 bytes

Существующие сообщения сессии, одинаковые в каждом направлении:


AES block:
  - 32 byte session tag
  - 2 byte tag count
  - 4 byte payload size
  - 32 byte hash of payload
  - 1 byte flags
  - 1 byte clove count
  - 33 byte Garlic deliv. inst.
  - 16 byte I2NP header
  - 15 byte msg cert, id, exp.
  - 15 byte clove cert, id, exp.
  - 0 byte padding assuming 1936 byte message
  151 total
Four message total (two each direction)
  3616 bytes overhead

Одиночная Raw-дейтаграмма

Сообщение Alice-to-Bob New Session:


- 32 byte ephemeral public key
  - 32 byte static public key
  - 16 byte Poly1305 MAC
  - 7 byte DateTime block
  - 3 byte Garlic block overhead
  - 9 byte I2NP header
  - 33 byte Garlic deliv. inst.
  - 16 byte Poly1305 MAC

  Total:
  148 bytes overhead

Сообщение ответа новой сессии от Bob к Alice:


- 8 byte session tag
  - 32 byte ephemeral public key
  - 16 byte Poly1305 MAC
  - 3 byte Garlic block overhead
  - 9 byte I2NP header
  - 33 byte Garlic deliv. inst.
  - 16 byte Poly1305 MAC

  Total:
  117 bytes overhead

Существующие сообщения сессии, одинаковые в каждом направлении:


- 8 byte session tag
  - 3 byte Garlic block overhead
  - 9 byte I2NP header
  - 33 byte Garlic deliv. inst.
  - 16 byte Poly1305 MAC

  Total:
  69 bytes

Долгоживущие сессии

Всего четыре сообщения (по два в каждом направлении):


372 bytes
  90% (approx. 10x) reduction compared to ElGamal/AES+SessionTags

Только handshake:


ElGamal: 1657 + 1657 = 3314 bytes
  Ratchet: 148 _ 117 = 265 bytes
  92% (approx. 12x) reduction compared to ElGamal/AES+SessionTags

Долгосрочный итог (исключая handshake-соединения):

ElGamal: 151 + 32 byte tag sent previously = 183 bytes
  Ratchet: 69 bytes
  64% (approx. 3x) reduction compared to ElGamal/AES+SessionTags

CPU

TODO обновить этот раздел после стабилизации предложения.

Следующие криптографические операции требуются каждой стороной для обмена сообщениями New Session и New Session Reply:

  • HMAC-SHA256: 3 на HKDF, всего TBD
  • ChaChaPoly: по 2 каждому
  • Генерация ключей X25519: 2 для Alice, 1 для Bob
  • X25519 DH: по 3 каждому
  • Проверка подписи: 1 (Bob)

Алиса вычисляет 5 ECDH на связанную сессию (минимум), 2 для каждого NS сообщения Бобу, и 3 для каждого NSR сообщения Боба.

Боб также вычисляет 6 ECDH на связанную сессию: 3 для каждого NS сообщения Алисы и 3 для каждого из своих NSR сообщений.

Следующие криптографические операции требуются каждой стороной для каждого сообщения Existing Session:

  • HKDF: 2
  • ChaChaPoly: 1

Защита

Текущая длина тега сессии составляет 32 байта. Мы пока не нашли никакого обоснования для этой длины, но продолжаем исследовать архивы. Приведенное выше предложение определяет новую длину тега как 8 байт. Анализ, обосновывающий 8-байтовый тег, выглядит следующим образом:

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

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

Цель состоит в том, чтобы выбрать длину тега сессии, которая достаточно велика для минимизации риска коллизий, но достаточно мала для минимизации использования памяти.

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

В худшем случае предположим загруженный сервер с 64 новыми входящими сессиями в секунду. Предположим время жизни тега входящей сессии 15 минут (как сейчас, вероятно, следует уменьшить). Предположим окно тега входящей сессии равное 32. 64 * 15 * 60 * 32 = 1,843,200 тегов. Текущий максимум входящих тегов в Java I2P составляет 750,000 и, насколько нам известно, никогда не был достигнут.

Целевой показатель коллизий тегов сессий 1 на миллион (1e-6), вероятно, является достаточным. Вероятность потери сообщения по пути из-за перегрузки сети значительно выше этого показателя.

Ссылка: https://en.wikipedia.org/wiki/Birthday_paradox раздел с таблицей вероятностей.

С 32-байтовыми session tag (256 бит) пространство session tag составляет 1.2e77. Вероятность коллизии с вероятностью 1e-18 требует 4.8e29 записей. Вероятность коллизии с вероятностью 1e-6 требует 4.8e35 записей. 1.8 миллиона tag по 32 байта каждый составляет примерно 59 МБ общего объема.

С 16-байтовыми session tags (128 бит) пространство session tag составляет 3.4e38. Вероятность коллизии с вероятностью 1e-18 требует 2.6e10 записей. Вероятность коллизии с вероятностью 1e-6 требует 2.6e16 записей. 1.8 миллиона тегов по 16 байт каждый составляет около 30 МБ в общей сложности.

С 8-байтными session tags (64 бита) пространство session tag составляет 1.8e19. Вероятность коллизии с вероятностью 1e-18 требует 6.1 записей. Вероятность коллизии с вероятностью 1e-6 требует 6.1e6 (6,100,000) записей. 1.8 миллиона тегов по 8 байт каждый составляет около 15 МБ общего объема.

6,1 миллиона активных тегов - это более чем в 3 раза больше нашей оценки наихудшего сценария в 1,8 миллиона тегов. Таким образом, вероятность коллизии составила бы менее одного на миллион. Поэтому мы заключаем, что 8-байтовых session tags достаточно. Это приводит к 4-кратному сокращению места для хранения в дополнение к 2-кратному сокращению из-за того, что transmit tags не сохраняются. Таким образом, у нас будет 8-кратное сокращение использования памяти session tag по сравнению с ElGamal/AES+SessionTags.

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

Реализации должны, как минимум, распознавать коллизии тегов сессии, корректно их обрабатывать и вести логи или подсчет количества коллизий. Хотя это по-прежнему крайне маловероятно, они будут гораздо более вероятными, чем при использовании ElGamal/AES+SessionTags, и могут реально произойти.

Параметры

Используя в два раза больше сессий в секунду (128) и в два раза больший tag window (64), мы получаем в 4 раза больше тегов (7.4 миллиона). Максимум для вероятности коллизии один на миллион составляет 6.1 миллиона тегов. 12-байтовые (или даже 10-байтовые) теги обеспечили бы огромный запас прочности.

Однако является ли шанс коллизии один на миллион хорошей целью? Намного больший, чем вероятность быть отброшенным по пути, не представляет большой пользы. Цель ложных срабатываний для Java DecayingBloomFilter составляет примерно 1 к 10,000, но даже 1 к 1000 не вызывает серьёзных опасений. Снижая цель до 1 к 10,000, остаётся достаточный запас с 8-байтовыми тегами.

Классификация

Отправитель генерирует теги и ключи на лету, поэтому хранение не требуется. Это сокращает общие требования к хранилищу вдвое по сравнению с ElGamal/AES. Теги ECIES составляют 8 байт вместо 32 для ElGamal/AES. Это сокращает общие требования к хранилищу еще в 4 раза. Сессионные ключи для каждого тега не хранятся на получателе, за исключением «пропусков», которые минимальны при разумных скоростях потерь.

Сокращение времени истечения тегов на 33% обеспечивает еще 33% экономии при условии коротких сессий.

Таким образом, общая экономия места по сравнению с ElGamal/AES составляет коэффициент 10,7, или 92%.

Только X25519

Поиск в базе данных из ECIES-адресатов: См. Предложение 154, теперь включено в I2NP для релиза 0.9.46.

Данное предложение требует поддержки LS2 для публикации открытого ключа X25519 с leaseSet. Никаких изменений в спецификации LS2 в I2NP не требуется. Вся поддержка была спроектирована, специфицирована и реализована в Предложении 123, которое было внедрено в версии 0.9.38.

X25519 с общим доступом с ElGamal/AES+SessionTags

Никаких. Данное предложение требует поддержки LS2 и установки свойства в опциях I2CP для активации. Никаких изменений в спецификациях I2CP не требуется. Вся поддержка была спроектирована, специфицирована и реализована в Предложении 123, внедрённом в версии 0.9.38.

Опция, необходимая для включения ECIES, представляет собой единственное свойство I2CP для I2CP, BOB, SAM или i2ptunnel.

Типичные значения: i2cp.leaseSetEncType=4 только для ECIES или i2cp.leaseSetEncType=4,0 для двойных ключей ECIES и ElGamal.

Ответы на уровне протокола

Этот раздел скопирован из Предложения 123.

Опция в SessionConfig Mapping:

  i2cp.leaseSetEncType=nnn[,nnn]  The encryption types to be used.
                                  0: ElGamal
                                  1-3: See proposal 145
                                  4: This proposal.

Create Leaseset2 Message

Это предложение требует LS2, который поддерживается начиная с версии 0.9.38. Никаких изменений в спецификации I2CP не требуется. Вся поддержка была спроектирована, специфицирована и реализована в Предложении 123, внедрённом в версии 0.9.38.

Накладные расходы

Любой router, поддерживающий LS2 с двойными ключами (версия 0.9.38 или выше), должен поддерживать подключение к точкам назначения с двойными ключами.

ECIES-only адресаты потребуют, чтобы большинство floodfill было обновлено до версии 0.9.46 для получения зашифрованных ответов на запросы. См. Proposal 154.

Destination-ы только с ECIES могут подключаться только к другим destination-ам, которые также используют только ECIES или имеют двойные ключи.