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

Новые записи netDB

Proposal 123
Открыть
Author zzz, str4d, orignal
Created 2016-01-16
Last Updated 2020-07-18
Supercedes: 110, 120, 121, 122

Статус

Части этого предложения завершены и реализованы в версиях 0.9.38 и 0.9.39.
Общие структуры, I2CP, I2NP и другие спецификации
обновлены, чтобы отразить поддерживаемые изменения.

Завершённые части всё ещё могут быть подвергнуты незначительным изменениям.
Другие части предложения всё ещё находятся в разработке
и могут быть существенно пересмотрены.

Поиск сервисов (типы 9 и 11) имеет низкий приоритет,
не запланирован и может быть вынесен в отдельное предложение.

Обзор

Это обновление и объединение следующих 4 предложений:

  • 110 LS2
  • 120 Meta LS2 для массового мультихоминга
  • 121 Зашифрованный LS2
  • 122 Неаутентифицированный поиск сервисов (аникастинг)

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

Следующие предложения частично связаны:

  • 140 Невидимый мультихоминг (несовместим с этим предложением)
  • 142 Новый шаблон криптографии (для новых симметричных криптосистем)
  • 144 ECIES-X25519-AEAD-Ratchet
  • 145 ECIES-P256
  • 146 Red25519
  • 148 EdDSA-BLAKE2b-Ed25519
  • 149 B32 для зашифрованного LS2
  • 150 Протокол Garlic Farm
  • 151 ECDSA Blinding

Предложение

Это предложение определяет 5 новых типов DatabaseEntry и процесс
их хранения и извлечения из сетевой базы данных,
а также метод подписи и проверки этих подписей.

Цели

  • Обратная совместимость
  • LS2 должен работать со старым стилем мультихоминга
  • Не требуется новая криптография или примитивы для поддержки
  • Сохранение разделения криптографии и подписи; поддержка всех текущих и будущих версий
  • Поддержка опциональных оффлайн-ключей для подписи
  • Уменьшение точности временных меток для снижения фингерпринтинга
  • Включение новых криптосистем для назначений
  • Поддержка массового мультихоминга
  • Исправление нескольких проблем с существующим зашифрованным LS
  • Опциональное маскирование (blinding) для уменьшения видимости floodfill-узлами
  • Зашифрованный LS поддерживает как одноключевую, так и многоключевую с возможностью отзыва схему
  • Поиск сервисов для упрощения поиска outproxy, DHT-загрузки приложений и других задач
  • Не нарушать работу существующих систем, зависящих от 32-байтовых хешей назначений, например, bittorrent
  • Добавить гибкость в leasesets через свойства, как в routerinfos
  • Поместить опубликованную временную метку и переменное время истечения в заголовок, чтобы это работало даже
    если содержимое зашифровано (не выводить временную метку из самого раннего лиза)
  • Все новые типы находятся в том же пространстве DHT и в тех же местах, что и существующие leasesets,
    чтобы пользователи могли перейти со старого LS на LS2,
    или переключаться между LS2, Meta и Encrypted,
    не меняя назначение или хеш.
  • Существующее назначение может быть переведено на оффлайн-ключи,
    или обратно на онлайн-ключи, без изменения назначения или хеша.

Нецели / Вне области

  • Новый алгоритм вращения DHT или генерация общего случайного числа
  • Конкретный новый тип шифрования и end-to-end схема шифрования
    будут определены в отдельном предложении.
    Никакая новая криптография здесь не определяется и не обсуждается.
  • Новое шифрование для RIs или построения туннелей.
    Будет в отдельном предложении.
  • Методы шифрования, передачи и приёма сообщений I2NP DLM / DSM / DSRM.
    Не меняются.
  • Как генерировать и поддерживать Meta, включая межузловую коммуникацию, управление, отказоустойчивость и координацию.
    Поддержка может быть добавлена в I2CP, i2pcontrol или новый протокол.
    Это может быть стандартизировано или нет.
  • Как реализовать и управлять туннелями с длительным сроком действия или отменять существующие туннели.
    Это крайне сложно, и без этого невозможно реализовать разумное плавное завершение работы.
  • Изменения модели угроз
  • Формат хранения оффлайн, или методы хранения/извлечения/обмена данными.
  • Детали реализации здесь не обсуждаются и остаются на усмотрение каждого проекта.

Обоснование

LS2 добавляет поля для изменения типа шифрования и будущих изменений протокола.

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

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

Запись сервиса и Список сервисов обеспечивают anycast-сервисы, такие как поиск имён
и загрузка DHT.

Типы данных NetDB

Номера типов используются в сообщениях I2NP Database Lookup/Store.

Колонка end-to-end указывает, отправляются ли запросы/ответы назначению в сообщении Garlic.

Существующие типы:

NetDB DataLookup TypeStore Type
any0any
LS11
RI20
exploratory3DSRM

Новые типы:

NetDB DataLookup TypeStore TypeStd. LS2 Header?Sent end-to-end?
LS213yesyes
Encrypted LS215nono
Meta LS217yesno
Service Recordn/a9yesno
Service List411nono

Примечания

  • Типы поиска в настоящее время используют биты 3-2 в сообщении Database Lookup.
    Любые дополнительные типы потребуют использования бита 4.

  • Все типы хранения нечётные, так как старшие биты в поле типа сообщения Database Store
    игнорируются старыми маршрутизаторами.
    Мы предпочитаем, чтобы разбор завершался с ошибкой как LS, а не как сжатый RI.

  • Должен ли тип быть явным, неявным или ни тем, ни другим в данных, охватываемых подписью?

Процесс поиска/хранения

Типы 3, 5 и 7 могут быть возвращены в ответ на стандартный запрос leaseset (тип 1).
Тип 9 никогда не возвращается в ответ на запрос.
Тип 11 возвращается в ответ на новый тип поиска сервиса (тип 11).

Только тип 3 может быть отправлен в сообщении Garlic от клиента к клиенту.

Формат

Типы 3, 7 и 9 имеют общий формат::

Стандартный заголовок LS2

  • как определено ниже

Часть, специфичная для типа

  • как определено ниже для каждого типа

Стандартная подпись LS2:

  • Длина определяется типом подписи ключа подписи

Тип 5 (зашифрованный) не начинается с назначения и имеет
другой формат. См. ниже.

Тип 11 (Service List) — это агрегация нескольких Service Records и имеет
другой формат. См. ниже.

Рассмотрение вопросов конфиденциальности/безопасности

TBD

Стандартный заголовок LS2

Типы 3, 7 и 9 используют стандартный заголовок LS2, определённый ниже:

Формат

Стандартный заголовок LS2:
  - Тип (1 байт)
    Фактически не входит в заголовок, но является частью данных, охватываемых подписью.
    Берётся из поля в сообщении Database Store.
  - Назначение (387+ байт)
  - Опубликованная временная метка (4 байта, big endian, секунды с эпохи, переполнение в 2106)
  - Истекает (2 байта, big endian) (смещение от опубликованной временной метки в секундах, максимум 18.2 часа)
  - Флаги (2 байта)
    Порядок битов: 15 14 ... 3 2 1 0
    Бит 0: Если 0, нет оффлайн-ключей; если 1, есть оффлайн-ключи
    Бит 1: Если 0, стандартный опубликованный leaseset.
           Если 1, неопубликованный leaseset. Не должен рассылаться, публиковаться или
           отправляться в ответ на запрос. Если этот leaseset истекает, не запрашивать новый
           в netdb, если не установлен бит 2.
    Бит 2: Если 0, стандартный опубликованный leaseset.
           Если 1, этот незашифрованный leaseset будет замаскирован и зашифрован при публикации.
           Если этот leaseset истекает, запрашивать замаскированное местоположение в netdb для нового.
           Если этот бит установлен в 1, бит 1 также должен быть установлен в 1.
           Начиная с версии 0.9.42.
    Биты 3-15: устанавливаются в 0 для совместимости с будущим использованием
  - Если флаг указывает на оффлайн-ключи, секция оффлайн-подписи:
    Временная метка истечения (4 байта, big endian, секунды с эпохи, переполнение в 2106)
    Тип временной подписи (2 байта, big endian)
    Временный публичный ключ подписи (длина определяется типом подписи)
    Подпись временной метки истечения, типа временной подписи и публичного ключа,
    выполненная публичным ключом назначения,
    длина определяется типом подписи публичного ключа назначения.
    Эта секция может и должна генерироваться оффлайн.

Обоснование

  • Неопубликованный/опубликованный: Для использования при отправке сообщения Database Store end-to-end,
    отправляющий маршрутизатор может указать, что этот leaseset не должен
    отправляться другим. В настоящее время мы используем эвристику для поддержания этого состояния.

  • Опубликованный: Заменяет сложную логику определения ‘версии’ leaseset.
    В настоящее время версия — это срок действия последнего истекающего лиза,
    и маршрутизатор, публикующий leaseset, который только удаляет старый лиз,
    должен увеличить этот срок как минимум на 1 мс.

  • Истекает: Позволяет сроку действия записи netdb быть раньше, чем у
    его последнего истекающего leaseset. Может быть не полезно для LS2, где ожидается, что leasesets
    будут иметь максимальный срок действия 11 минут, но
    для других новых типов это необходимо (см. Meta LS и Service Record ниже).

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

Проблемы

  • Можно ещё больше уменьшить точность временной метки (10 минут?), но тогда нужно добавить
    номер версии. Это может нарушить мультихоминг, если у нас нет порядкового шифрования?
    Вероятно, нельзя обойтись без временных меток вообще.

  • Альтернатива: 3-байтовая временная метка (эпоха / 10 минут), 1-байтовая версия, 2-байтовый срок истечения

  • Является ли тип явным или неявным в данных / подписи? “Доменные” константы для подписи?

Примечания

  • Маршрутизаторы не должны публиковать LS чаще одного раза в секунду.
    Если они это делают, они должны искусственно увеличить опубликованную временную метку на 1
    по сравнению с ранее опубликованным LS.

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

  • Оффлайн-ключи и подпись подходят только для долгоживущих назначений,
    т.е. серверов, а не клиентов.

Новые типы DatabaseEntry

LeaseSet 2

Изменения по сравнению с существующим LeaseSet:

  • Добавлены опубликованная временная метка, временная метка истечения, флаги и свойства
  • Добавлен тип шифрования
  • Удалён ключ отзыва

Поиск с помощью
Стандартного флага LS (1)
Хранение с помощью
Типа стандартного LS2 (3)
Хранение в
Хеше назначения
Этот хеш затем используется для генерации ежедневного “ключа маршрутизации”, как в LS1
Типичное истечение
10 минут, как в обычном LS.
Публикуется
Назначением

Формат

Стандартный заголовок LS2, как указано выше

  Специфичная часть типа LS2
  - Свойства (Сопоставление, как указано в спецификации общих структур, 2 нулевых байта, если нет)
  - Количество секций ключей, следующих за (1 байт, максимум TBD)
  - Секции ключей:
    - Тип шифрования (2 байта, big endian)
    - Длина ключа шифрования (2 байта, big endian)
      Это явно указано, чтобы floodfill-узлы могли разбирать LS2 с неизвестными типами шифрования.
    - Ключ шифрования (указанное количество байт)
  - Количество lease2s (1 байт)
  - Lease2s (по 40 байт каждый)
    Это лизы, но с 4-байтовым вместо 8-байтового срока истечения,  
    секунды с эпохи (переполнение в 2106)

  Стандартная подпись LS2:
  - Подпись
    Если флаг указывает на оффлайн-ключи, подпись выполняется временным публичным ключом,  
    иначе — публичным ключом назначения  
    Длина определяется типом подписи ключа подписи  
    Подпись всего вышеперечисленного.

Обоснование

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

  • Несколько пар тип/публичный ключ шифрования
    для упрощения перехода на новые типы шифрования. Другой способ —
    публиковать несколько leasesets, возможно, используя одни и те же туннели,
    как мы делаем сейчас для назначений DSA и EdDSA.
    Определение входящего типа шифрования на туннеле
    может быть выполнено с помощью существующего механизма session tag,
    и/или пробного расшифрования с каждым ключом. Длина входящих
    сообщений также может дать подсказку.

Обсуждение

Это предложение продолжает использовать публичный ключ в leaseset как
ключ end-to-end шифрования и оставляет поле публичного ключа в
назначении неиспользуемым, как сейчас. Тип шифрования не указан
в сертификате ключа назначения, он останется 0.

Отклонённая альтернатива — указать тип шифрования в сертификате ключа назначения,
использовать публичный ключ в назначении и не использовать публичный ключ
в leaseset. Мы не планируем этого делать.

Преимущества LS2:

  • Местоположение фактического публичного ключа не меняется.
  • Тип шифрования или публичный ключ могут меняться без изменения назначения.
  • Удаляется неиспользуемое поле отзыва
  • Базовая совместимость с другими типами DatabaseEntry в этом предложении
  • Позволяет использовать несколько типов шифрования

Недостатки LS2:

  • Местоположение публичного ключа и типа шифрования отличается от RouterInfo
  • Сохраняется неиспользуемый публичный ключ в leaseset
  • Требует реализации по всей сети; в альтернативе могут использоваться экспериментальные
    типы шифрования, если разрешены floodfill-узлами
    (но см. связанные предложения 136 и 137 о поддержке экспериментальных типов подписей).
    Альтернативное предложение может быть проще в реализации и тестировании для экспериментальных типов шифрования.

Новые проблемы шифрования

Часть этого выходит за рамки данного предложения,
но пока помещаем заметки здесь, так как у нас ещё нет
отдельного предложения по шифрованию.
См. также предложения по ECIES 144 и 145.

  • Тип шифрования представляет комбинацию
    кривой, длины ключа и end-to-end схемы,
    включая KDF и MAC, если есть.

  • Мы включили поле длины ключа, чтобы LS2 был
    разбираемым и проверяемым floodfill-узлом даже для неизвестных типов шифрования.

  • Первым новым типом шифрования, который будет предложен, вероятно, будет
    ECIES/X25519. Как он используется end-to-end
    (либо слегка изменённая версия ElGamal/AES+SessionTag,
    либо что-то совершенно новое, например, ChaCha/Poly) будет определено
    в одном или нескольких отдельных предложениях.
    См. также предложения по ECIES 144 и 145.

Примечания

  • 8-байтовый срок истечения в лизах изменён на 4 байта.

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

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

Зашифрованный LS2

Цели:

  • Добавить маскирование (blinding)
  • Разрешить несколько типов подписей
  • Не требовать новых криптографических примитивов
  • Опционально шифровать для каждого получателя, отзываемо
  • Поддерживать шифрование только стандартного LS2 и Meta LS2

Зашифрованный LS2 никогда не отправляется в end-to-end сообщении Garlic.
Используйте стандартный LS2, как указано выше.

Изменения по сравнению с существующим зашифрованным LeaseSet:

  • Зашифровать всё целиком для безопасности
  • Надёжно шифровать, а не только с помощью AES.
  • Шифровать для каждого получателя

Поиск с помощью
Стандартного флага LS (1)
Хранение с помощью
Типа зашифрованного LS2 (5)
Хранение в
Хеше типа маскированной подписи и маскированного публичного ключа
Два байта типа подписи (big endian, например, 0x000b) || маскированный публичный ключ
Этот хеш затем используется для генерации ежедневного “ключа маршрутизации”, как в LS1
Типичное истечение
10 минут, как в обычном LS, или часы, как в meta LS.
Публикуется
Назначением

Определения

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

CSRNG(n) n-байтовый вывод из криптографически безопасного генератора случайных чисел.

Помимо требования, что CSRNG криптографически безопасен (и, следовательно,  
подходит для генерации ключевого материала), он ДОЛЖЕН быть безопасным  
для использования n-байтового вывода в качестве ключевого материала, когда байтовые последовательности непосредственно  
перед и после него раскрыты в сети (например, в виде соли или зашифрованного заполнения). Реализации, полагающиеся на потенциально ненадёжный источник, должны хешировать  
любой вывод, который будет раскрыт в сети. См. [PRNG references](http://projectbullrun.org/dual-ec/ext-rand.html) и [Tor dev discussion](https://lists.torproject.org/pipermail/tor-dev/2015-November/009954.html).

H(p, d) Функция хеширования SHA-256, принимающая персонализированную строку p и данные d, и
производящая выход длиной 32 байта.

Используйте SHA-256 следующим образом::

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

STREAM Потоковый шифр ChaCha20, как указано в RFC 7539 Section 2.4 , с начальным счётчиком
установленным в 1. S_KEY_LEN = 32 и S_IV_LEN = 12.

ENCRYPT(k, iv, plaintext)
    Шифрует открытый текст с помощью ключа шифра k и nonce iv, который ДОЛЖЕН быть уникальным для  
    ключа k. Возвращает шифротекст того же размера, что и открытый текст.

    Весь шифротекст должен быть неотличим от случайного, если ключ секретен.

DECRYPT(k, iv, ciphertext)
    Расшифровывает шифротекст с помощью ключа шифра k и nonce iv. Возвращает открытый текст.

SIG Схема подписи RedDSA (соответствует SigType 11) с маскированием ключа.
Имеет следующие функции:

DERIVE_PUBLIC(privkey)
    Возвращает публичный ключ, соответствующий данному приватному ключу.

SIGN(privkey, m)
    Возвращает подпись приватным ключом privkey по данному сообщению m.

VERIFY(pubkey, m, sig)
    Проверяет подпись sig по публичному ключу pubkey и сообщению m. Возвращает  
    true, если подпись действительна, иначе false.

Также должна поддерживать следующие операции маскирования ключа:

GENERATE_ALPHA(data, secret)
    Генерирует alpha для тех, кто знает данные и опциональный секрет.  
    Результат должен быть идентично распределён, как приватные ключи.

BLIND_PRIVKEY(privkey, alpha)
    Маскирует приватный ключ, используя секрет alpha.

BLIND_PUBKEY(pubkey, alpha)
    Маскирует публичный ключ, используя секрет alpha.  
    Для данной пары ключей (privkey, pubkey) выполняется следующее соотношение::

        BLIND_PUBKEY(pubkey, alpha) ==
        DERIVE_PUBLIC(BLIND_PRIVKEY(privkey, alpha))

DH Система согласования общего ключа X25519. Приватные ключи длиной 32 байта, публичные ключи длиной 32
байта, производит выход длиной 32 байта. Имеет следующие
функции:

GENERATE_PRIVATE()
    Генерирует новый приватный ключ.

DERIVE_PUBLIC(privkey)
    Возвращает публичный ключ, соответствующий данному приватному ключу.

DH(privkey, pubkey)
    Генерирует общий секрет из данных приватного и публичного ключей.

HKDF(salt, ikm, info, n) Криптографическая функция выработки ключа, которая принимает некоторый входной ключевой материал ikm (который
должен иметь хорошую энтропию, но не обязательно быть равномерно случайной строкой), соль
длиной 32 байта и контекстно-зависимое значение ‘info’, и производит выход
длиной n байт, пригодный для использования в качестве ключевого материала.

Используйте HKDF, как указано в [RFC 5869](https://tools.ietf.org/html/rfc5869), используя функцию HMAC с хешем SHA-256,  
как указано в [RFC 2104](https://tools.ietf.org/html/rfc2104). Это означает, что SALT_LEN максимум 32 байта.

Формат

Формат зашифрованного LS2 состоит из трёх вложенных слоёв:

  • Внешний слой, содержащий необходимую открытую информацию для хранения и извлечения.
  • Средний слой, отвечающий за аутентификацию клиента.
  • Внутренний слой, содержащий фактические данные LS2.

Общий формат выглядит так::

Данные слоя 0 + Enc(данные слоя 1 + Enc(данные слоя 2)) + Подпись

Обратите внимание, что зашифрованный LS2 маскируется. Назначение не находится в заголовке.
Место хранения в DHT — SHA-256(тип подписи || маскированный публичный ключ), и вращается ежедневно.

НЕ использует стандартный заголовок LS2, указанный выше.

Слой 0 (внешний)

Тип 1 байт

Фактически не входит в заголовок, но является частью данных, охватываемых подписью.  
Берётся из поля в сообщении Database Store.

Тип публичного ключа с маскированием 2 байта, big endian
Это всегда будет тип 11, идентифицирующий маскированный ключ Red25519.

Маскированный публичный ключ Длина определяется типом подписи

Опубликованная временная метка 4 байта, big endian

Секунды с эпохи, переполнение в 2106

Истекает 2 байта, big endian

Смещение от опубликованной временной метки в секундах, максимум 18.2 часа

Флаги 2 байта

Порядок битов: 15 14 ... 3 2 1 0

Бит 0: Если 0, нет оффлайн-ключей; если 1, есть оффлайн-ключи

Другие биты: устанавливаются в 0 для совместимости с будущим использованием

Данные временного ключа Присутствуют, если флаг указывает на оффлайн-ключи

Временная метка истечения
    4 байта, big endian

    Секунды с эпохи, переполнение в 2106

Тип временной подписи
    2 байта, big endian

Временный публичный ключ подписи
    Длина определяется типом подписи

Подпись
    Длина определяется типом подписи маскированного публичного ключа

    По временной метке истечения, типу временной подписи и временному публичному ключу.

    Проверяется с помощью маскированного публичного ключа.

lenOuterCiphertext 2 байта, big endian

outerCiphertext lenOuterCiphertext байт

Зашифрованные данные слоя 1. См. ниже алгоритмы выработки ключа и шифрования.

Подпись Длина определяется типом подписи используемого ключа подписи

Подпись всего вышеперечисленного.

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

Слой 1 (средний)

Флаги 1 байт

Порядок битов: 76543210

Бит 0: 0 для всех, 1 для персонального клиента, далее следует секция аутентификации

Биты 3-1: Схема аутентификации, только если бит 0 установлен в 1 для персонального клиента, иначе 000  
          000: Аутентификация клиента по DH (или без аутентификации на уровне клиента)  
          001: Аутентификация клиента по PSK

Биты 7-4: Не используются, устанавливаются в 0 для будущей совместимости

Данные аутентификации по DH Присутствуют, если бит флага 0 установлен в 1, а биты флага 3-1 установлены в 000.

ephemeralPublicKey
    32 байта

clients
    2 байта, big endian

    Количество записей authClient, следующих далее, по 40 байт каждая

authClient
    Данные авторизации для одного клиента.  
    См. ниже алгоритм авторизации на уровне клиента.

    clientID_i
        8 байт

    clientCookie_i
        32 байта

Данные аутентификации по PSK Присутствуют, если бит флага 0 установлен в 1, а биты флага 3-1 установлены в 001.

authSalt
    32 байта

clients
    2 байта, big endian

    Количество записей authClient, следующих далее, по 40 байт каждая

authClient
    Данные авторизации для одного клиента.  
    См. ниже алгоритм авторизации на уровне клиента.

    clientID_i
        8 байт

    clientCookie_i
        32 байта

innerCiphertext Длина определяется lenOuterCiphertext (оставшиеся данные)

Зашифрованные данные слоя 2. См. ниже алгоритмы выработки ключа и шифрования.

Слой 2 (внутренний)

Тип 1 байт

Либо 3 (LS2), либо 7 (Meta LS2)

Данные Данные LeaseSet2 для данного типа.

Включают заголовок и подпись.

Выведение ключа маскирования

Мы используем следующую схему для маскирования ключа,
основанную на Ed25519 и ZCash RedDSA .
Подписи Re25519 делаются по кривой Ed25519 с использованием SHA-512 для хеширования.

Мы не используем Tor’s rend-spec-v3.txt appendix A.2 ,
который имеет схожие цели проектирования, потому что его маскированные публичные ключи
могут находиться вне подгруппы простого порядка, с неизвестными последствиями для безопасности.

Цели

  • Публичный ключ подписи в немаскированном назначении должен быть
    Ed25519 (тип подписи 7) или Red25519 (тип подписи 11);
    другие типы подписей не поддерживаются
  • Если публичный ключ подписи оффлайн, временный публичный ключ подписи также должен быть Ed25519
  • Маскирование вычислительно просто
  • Использование существующих криптографических примитивов
  • Маскированные публичные ключи не могут быть раскрыты
  • Маскированные публичные ключи должны находиться на кривой Ed25519 и в подгруппе простого порядка
  • Должно быть известно публичный ключ подписи назначения
    (полное назначение не требуется) для вывода маскированного публичного ключа
  • Опционально предоставить дополнительный секрет, необходимый для вывода маскированного публичного ключа

Безопасность

Безопасность схемы маскирования требует, чтобы
распределение alpha было таким же, как у немаскированных приватных ключей.
Однако, когда мы маскируем приватный ключ Ed25519 (тип подписи 7)
в приватный ключ Red25519 (тип подписи 11), распределение отличается.
Чтобы соответствовать требованиям zcash section 4.1.6.1 ,
Red25519 (тип подписи 11) должен использоваться и для немаскированных ключей, чтобы
“комбинация переупорядоченного публичного ключа и подписей
под этим ключом не раскрывала ключ, из которого он был переупорядочен”.
Мы разрешаем тип 7 для существующих назначений, но рекомендуем
тип 11 для новых назначений, которые будут зашифрованы.

Определения

B Базовая точка (генератор) Ed25519 2^255 - 19, как в Ed25519

L Порядок Ed25519 2^252 + 27742317777372353535851937790883648493
как в Ed25519

DERIVE_PUBLIC(a) Преобразует приватный ключ в публичный, как в Ed25519 (умножение на G)

alpha 32-байтовое случайное число, известное тем, кто знает назначение.

GENERATE_ALPHA(destination, date, secret) Генерирует alpha для текущей даты, для тех, кто знает назначение и секрет.
Результат должен быть идентично распределён, как приватные ключи Ed25519.

a Немаскированный 32-байтовый приватный ключ EdDSA или RedDSA, используемый для подписи назначения

A Немаскированный 32-байтовый публичный ключ EdDSA или RedDSA в назначении,
= DERIVE_PUBLIC(a), как в Ed25519

a’ Маскированный 32-байтовый приватный ключ EdDSA, используемый для подписи зашифрованного leaseset
Это действительный приватный ключ EdDSA.

A’ Маскированный 32-байтовый публичный ключ EdDSA в назначении,
может быть сгенерирован с помощью DERIVE_PUBLIC(a’), или из A и alpha.
Это действительный публичный ключ EdDSA, на кривой и в подгруппе простого порядка.

LEOS2IP(x) Переворачивает порядок байтов входа в little-endian

H*(x) 32 байта = (LEOS2IP(SHA512(x))) mod B, то же, что и в Ed25519 hash-and-reduce

Расчёты маскирования

Новый секрет alpha и маскированные ключи должны генерироваться каждый день (UTC).
Секрет alpha и маскированные ключи рассчитываются следующим образом.

GENERATE_ALPHA(destination, date, secret), для всех сторон:

// GENERATE_ALPHA(destination, date, secret)

  // секрет опционален, иначе нулевой длины
  A = публичный ключ подписи назначения
  stA = тип подписи A, 2 байта big endian (0x0007 или 0x000b)
  stA' = тип подписи маскированного публичного ключа A', 2 байта big endian (0x000b)
  keydata = A || stA || stA'
  datestring = 8 байт ASCII YYYYMMDD из текущей даты UTC
  secret = строка в кодировке UTF-8
  seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64)
  // рассматривать seed как 64-байтовое значение little-endian
  alpha = seed mod L

BLIND_PRIVKEY(), для владельца, публикующего leaseset:

// BLIND_PRIVKEY()

  alpha = GENERATE_ALPHA(destination, date, secret)
  // Если для приватного ключа Ed25519 (тип 7)
  seed = приватный ключ подписи назначения
  a = левая половина SHA512(seed) и зажата, как обычно для Ed25519
  // иначе, для приватного ключа Red25519 (тип 11)
  a = приватный ключ подписи назначения
  // Сложение с использованием скалярной арифметики
  маскированный приватный ключ подписи = a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L
  маскированный публичный ключ подписи = A' = DERIVE_PUBLIC(a')

BLIND_PUBKEY(), для клиентов, получающих leaseset:

// BLIND_PUBKEY()

  alpha = GENERATE_ALPHA(destination, date, secret)
  A = публичный ключ подписи назначения
  // Сложение с использованием элементов группы (точек на кривой)
  маскированный публичный ключ = A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha)

Оба метода расчёта A’ дают одинаковый результат, как и требуется.

Подпись

Немаскированный leaseset подписывается немаскированным приватным ключом Ed25519 или Red25519
и проверяется с помощью немаскированного публичного ключа Ed25519 или Red25519 (типы подписи 7 или 11) как обычно.

Если публичный ключ подписи оффлайн,
немаскированный leaseset подписывается немаскированным временным приватным ключом Ed25519 или Red25519
и проверяется с помощью немаскированного публичного ключа Ed25519 или Red25519 (типы подписи 7 или 11) как обычно.
См. ниже дополнительные примечания об оффлайн-ключах для зашифрованных leasesets.

Для подписи зашифрованного leaseset мы используем Red25519, основанный на RedDSA
для подписи и проверки с маскированными ключами.
Подписи Red25519 делаются по кривой Ed25519 с использованием SHA-512 для хеширования.

Red25519 идентичен стандартному Ed25519, за исключением указанных ниже различий.

Расчёты подписи/проверки

Внешняя часть зашифрованного leaseset использует ключи и подписи Red25519.

Red25519 почти идентичен Ed25519. Есть два различия:

Приватные ключи Red25519 генерируются из случайных чисел и затем должны быть приведены по модулю L, где L определено выше.
Приватные ключи Ed25519 генерируются из случайных чисел и затем “зажимаются” с помощью
побитовой маски для байтов 0 и 31. Это не делается для Red25519.
Функции GENERATE_ALPHA() и BLIND_PRIVKEY(), определённые выше, генерируют правильные
приватные ключи Red25519 с использованием mod L.

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

Подпись:

T = 80 случайных байт
  r = H*(T || publickey || message)
  // остальное то же, что и в Ed25519

Проверка:

// то же, что и в Ed25519

Шифрование и обработка

Выведение подучреждений

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

A = публичный ключ подписи назначения
  stA = тип подписи A, 2 байта big endian (0x0007 или 0x000b)
  stA' = тип подписи A', 2 байта big endian (0x000b)
  keydata = A || stA || stA'
  credential = H("credential", keydata)

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

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

subcredential = H("subcredential", credential || blindedPublicKey)

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

Шифрование слоя 1

Сначала готовится вход для процесса выработки ключа:

outerInput = subcredential || publishedTimestamp

Затем генерируется случайная соль:

outerSalt = CSRNG(32)

Затем выводится ключ, используемый для шифрования слоя 1:

keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
  outerKey = keys[0:31]
  outerIV = keys[32:43]

Наконец, открытый текст слоя 1 шифруется и сериализуется:

outerCiphertext = outerSalt || ENCRYPT(outerKey, outerIV, outerPlaintext)

Расшифрование слоя 1

Соль парсится из шифротекста слоя 1:

outerSalt = outerCiphertext[0:31]

Затем выводится ключ, используемый для шифрования слоя 1:

outerInput = subcredential || publishedTimestamp
  keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
  outerKey = keys[0:31]
  outerIV = keys[32:43]

Наконец, шифротекст слоя 1 расшифровывается:

outerPlaintext = DECRYPT(outerKey, outerIV, outerCiphertext[32:end])

Шифрование слоя 2

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

Шифрование продолжается аналогично слою 1:

innerInput = authCookie || subcredential || publishedTimestamp
  innerSalt = CSRNG(32)
  keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44)
  innerKey = keys[0:31]
  innerIV = keys[32:43]
  innerCiphertext = innerSalt || ENCRYPT(innerKey, innerIV, innerPlaintext)

Расшифрование слоя 2

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

Расшифрование продолжается аналогично слою 1:

innerInput = authCookie || subcredential || publishedTimestamp
  innerSalt = innerCiphertext[0:31]
  keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44)
  innerKey = keys[0:31]
  innerIV = keys[32:43]
  innerPlaintext = DECRYPT(innerKey, innerIV, innerCiphertext[32:end])

Авторизация на уровне клиента

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

Есть два варианта реализации авторизации на уровне клиента:

Авторизация клиента по DH

Каждый клиент генерирует пару ключей DH [csk_i, cpk_i] и отправляет публичный ключ cpk_i
на сервер.

Обработка сервером ^^^^^^^^^^^^^^^^^ Сервер генерирует новый authCookie и эфемерную пару ключей DH:

authCookie = CSRNG(32)
  esk = GENERATE_PRIVATE()
  epk = DERIVE_PUBLIC(esk)

Затем для каждого авторизованного клиента сервер шифрует authCookie его публичным ключом:

sharedSecret = DH(esk, cpk_i)
  authInput = sharedSecret || cpk_i || subcredential || publishedTimestamp
  okm = HKDF(epk, authInput, "ELS2_XCA", 52)
  clientKey_i = okm[0:31]
  clientIV_i = okm[32:43]
  clientID_i = okm[44:51]
  clientCookie_i = ENCRYPT(clientKey_i, clientIV_i, authCookie)

Сервер помещает каждую пару [clientID_i, clientCookie_i] в слой 1
зашифрованного LS2 вместе с epk.

Обработка клиентом ^^^^^^^^^^^^^^^^^ Клиент использует свой приватный ключ для вывода ожидаемого идентификатора клиента clientID_i,
ключа шифрования clientKey_i и IV шифрования clientIV_i:

sharedSecret = DH(csk_i, epk)
  authInput = sharedSecret || cpk_i || subcredential || publishedTimestamp
  okm = HKDF(epk, authInput, "ELS2_XCA", 52)
  clientKey_i = okm[0:31]
  clientIV_i = okm[32:43]
  clientID_i = okm[44:51]

Затем клиент ищет в данных авторизации слоя 1 запись, содержащую
clientID_i. Если совпадающая запись существует, клиент расшифровывает её, чтобы получить
authCookie:

authCookie = DECRYPT(clientKey_i, clientIV_i, clientCookie_i)

Авторизация клиента по предварительно разделяемому ключу (PSK)

Каждый клиент генерирует секретный 32-байтовый ключ psk_i и отправляет его на сервер.
В качестве альтернативы, сервер может сгенерировать секретный ключ и отправить его одному или нескольким клиентам.

Обработка сервером ^^^^^^^^^^^^^^^^^ Сервер генерирует новый authCookie и соль:

authCookie = CSRNG(32)
  authSalt = CSRNG(32)

Затем для каждого авторизованного клиента сервер шифрует authCookie его предварительно разделяемым ключом:

authInput = psk_i || subcredential || publishedTimestamp
  okm = HKDF(authSalt, authInput, "ELS2PSKA", 52)
  clientKey_i = okm[0:31]
  clientIV_i = okm[32:43]
  clientID_i = okm[44:51]
  clientCookie_i = ENCRYPT(clientKey_i, clientIV_i, authCookie)

Сервер помещает каждую пару [clientID_i, clientCookie_i] в слой 1
зашифрованного LS2 вместе с authSalt.

Обработка клиентом ^^^^^^^^^^^^^^^^^ Клиент использует свой предварительно разделяемый ключ для вывода ожидаемого идентификатора клиента clientID_i,
ключа шифрования clientKey_i и IV шифрования clientIV_i:

authInput = psk_i || subcredential || publishedTimestamp
  okm = HKDF(authSalt, authInput, "ELS2PSKA", 52)
  clientKey_i = okm[0:31]
  clientIV_i = okm[32:43]
  clientID_i = okm[44:51]

Затем клиент ищет в данных авторизации слоя 1 запись, содержащую
clientID_i. Если совпадающая запись существует, клиент расшифровывает её, чтобы получить
authCookie:

authCookie = DECRYPT(clientKey_i, clientIV_i, clientCookie_i)

Рассмотрение вопросов безопасности

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

Серверы ДОЛЖНЫ перемешивать порядок клиентов каждый раз при генерации зашифрованного LS2, чтобы
предотвратить клиентов от определения их позиции в списке и вывода, когда другие клиенты были добавлены или отозваны.

Сервер МОЖЕТ выбрать скрытие количества подписанных клиентов, вставляя случайные записи в список данных авторизации.

Преимущества авторизации клиента по DH ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  • Безопасность схемы не зависит исключительно от внеполосного обмена материалом ключа клиента.
    Приватный ключ клиента никогда не должен покидать его устройство, и поэтому злоумышленник, способный перехватить внеполосный обмен, но не способный взломать алгоритм DH, не может расшифровать зашифрованный LS2 или определить, как долго клиенту разрешён доступ.

Недостатки авторизации клиента по DH ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  • Требует N + 1 операций DH на стороне сервера для N клиентов.
  • Требует одной операции DH на стороне клиента.
  • Требует, чтобы клиент сгенерировал секретный ключ.

Преимущества авторизации клиента по PSK ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

Недостатки авторизации клиента по PSK ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

Зашифрованный LS с адресами Base 32

См. предложение 149.

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

Зашифрованный LS с оффлайн-ключами

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

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

В этом предложении не определён формат файла для упаковки нескольких временных и
маскированных ключей и предоставления их клиенту или маршрутизатору.
В этом предложении не определено расширение протокола I2CP для поддержки
зашифрованных leasesets с оффлайн-ключами.

Примечания

  • Сервис, использующий зашифрованные leasesets, будет публиковать зашифрованную версию на
    floodfill-узлах. Однако, для эффективности, он будет отправлять незашифрованные leasesets клиентам в
    обёрнутом сообщении garlic, после аутентификации (например, по белому списку).

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

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

  • ChaCha20 был выбран вместо AES. Хотя скорости схожи при наличии аппаратной поддержки AES,
    ChaCha20 в 2,5-3 раза быстрее, когда аппаратная поддержка AES отсутствует, например, на недорогих ARM-устройствах.

  • Нам недостаточно важно быстродействие, чтобы использовать ключевой BLAKE2b. Он имеет достаточный размер выхода, чтобы вместить наибольшее n, которое нам нужно (или мы можем вызывать его один раз для каждого желаемого ключа с аргументом счётчика). BLAKE2b намного быстрее SHA-256, и
    ключевой BLAKE2b сократил бы общее количество вызовов хеш-функции.
    Однако, см. предложение 148, где предлагается перейти на BLAKE2b по другим причинам.
    См. Secure key derivation performance .

Meta LS2

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

Meta LS2 является вершиной, и возможно, промежуточными узлами,
деревообразной структуры.
Он содержит несколько записей, каждая из которых указывает на LS, LS2 или другой Meta LS2
для поддержки массового мультихоминга.
Meta LS2 может содержать смесь записей LS, LS2 и Meta LS2.
Листья дерева всегда являются LS или LS2.
Дерево является DAG; циклы запрещены; клиенты, выполняющие поиск, должны обнаруживать и
отказываться следовать по циклам.

Meta LS2 может иметь гораздо более длительный срок истечения, чем стандартный LS или LS2.
Верхний уровень может иметь срок истечения на несколько часов после даты публикации.
Максимальное время истечения будет обеспечиваться floodfill-узлами и клиентами, и определяется позже.

Использование Meta LS2 — это массовый мультихоминг, но без дополнительной
защиты от корреляции маршрутизаторов с leasesets (во время перезагрузки маршрутизатора), чем
предоставляется сейчас с LS или LS2.
Это эквивалентно “facebook” сценарию, которому, вероятно, не нужна
защита от корреляции. Этот сценарий, вероятно, нуждается в оффлайн-ключах,
которые предоставляются в стандартном заголовке на каждом узле дерева.

Протокол бэкенда для координации между маршрутизаторами-листьями, промежуточными и главными подписантами Meta LS
здесь не указан. Требования крайне просты — просто проверить, что пир работает,
и публиковать новый LS каждые несколько часов. Единственная сложность — выбор новых
публикователей для верхнего или промежуточного уровня Meta LS при сбое.

Смешанные leasesets, где лизы от нескольких маршрутизаторов объединяются, подписываются и публикуются
в одном leaseset, документируются в предложении 140, “невидимый мультихоминг”.
Это предложение неприемлемо в написанном виде, потому что потоковые соединения не будут
“прилипать” к одному маршрутизатору, см. http://zzz.i 2p/topics/2335 .

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

Чтобы избежать перегрузки floodfill-узла для верхнего уровня Meta LS, срок истечения должен
быть не менее нескольких часов. Клиенты должны кэшировать верхний уровень Meta LS и сохранять
его между перезагрузками, если он не истёк.

Нам нужно определить алгоритм для клиентов, чтобы обходить дерево, включая резервные варианты,
чтобы использование было распределено. Некоторая функция от хеш-расстояния, стоимости и случайности.
Если узел имеет и LS/LS2, и Meta LS, нам нужно знать, когда разрешено
использовать эти leasesets, а когда продолжать обход дерева.

Поиск с помощью
Стандартного флага LS (1)
Хранение с помощью
Типа Meta LS2 (7)
Хранение в
Хеше назначения
Этот хеш затем используется для генерации ежедневного “ключа маршрутизации”, как в LS1
Типичное истечение
Часы. Максимум 18.2 часа (65535 секунд)
Публикуется
“главным” назначением или координатором, или промежуточными координаторами

Формат

Стандартный заголовок LS2, как указано выше

  Специфичная часть типа Meta LS2
  - Свойства (Сопоставление, как указано в спецификации общих структур, 2 нулевых байта, если нет)
  - Количество записей (1 байт) Максимум TBD
  - Записи. Каждая запись содержит: (40 байт)
    - Хеш (32 байта)
    - Флаги (2 байта)
      TBD. Установите все в ноль для совместимости с будущим использованием.
    - Тип (1 байт) Тип LS, на который он ссылается;  
      1 для LS, 3 для LS2, 5 для зашифрованного, 7 для meta, 0 для неизвестного.
    - Стоимость (приоритет) (1 байт)
    - Истекает (4 байта) (4 байта, big endian, секунды с эпохи, переполнение в 2106)
  - Количество отзывов (1 байт) Максимум TBD
  - Отзывы: Каждый отзыв содержит: (32 байта)
    - Хеш (32 байта)

  Стандартная подпись LS2:
  - Подпись (40+ байт)
    Подпись всего вышеперечисленного.

Флаги и свойства: для будущего использования

Примечания

  • Рас