Обзор
Данный документ определяет ослепление, шифрование и расшифровку зашифрованных leaseset. Для структуры зашифрованного leaseset см. спецификацию общих структур . Для справочной информации о зашифрованных leaseset см. предложение 123 . Для использования в netdb см. документацию netdb.
Определения
Мы определяем следующие функции, соответствующие криптографическим блокам, используемым для зашифрованного LS2:
CSRNG(n) : n-байтовый выход из криптографически стойкого генератора случайных чисел.
Помимо требования к CSRNG быть криптографически стойким (и таким образом подходящим для генерации ключевого материала), он ДОЛЖЕН быть безопасным для использования некоторого n-байтового выхода в качестве ключевого материала, когда байтовые последовательности, непосредственно предшествующие и следующие за ним, раскрываются в сети (например, в соли или зашифрованном заполнении). Реализации, полагающиеся на потенциально недоверенный источник, должны хешировать любой вывод, который будет раскрыт в сети PRNG-REFS .
H(p, d) : хеш-функция SHA-256, которая принимает строку персонализации p и данные d, и выдает выходные данные длиной 32 байта.
Используйте SHA-256 следующим образом:
H(p, d) := SHA-256(p || d)
STREAM : Потоковый шифр ChaCha20, как указано в RFC-7539-S2.4 , с начальным счетчиком, установленным в 1. S_KEY_LEN = 32 и S_IV_LEN = 12.
ENCRYPT(k, iv, plaintext) : Шифрует открытый текст, используя ключ шифрования k и одноразовый номер iv, который ДОЛЖЕН быть уникальным для ключа k. Возвращает шифротекст того же размера, что и открытый текст. Весь шифротекст должен быть неотличим от случайного, если ключ является секретным.
DECRYPT(k, iv, ciphertext) : Расшифровывает зашифрованный текст, используя ключ шифрования k и nonce iv. Возвращает открытый текст.
SIG : Схема цифровой подписи Red25519 (соответствующая SigType 11) с ослеплением ключей. Она имеет следующие функции:
DERIVE_PUBLIC(privkey) : Возвращает публичный ключ, соответствующий данному приватному ключу.
SIGN(privkey, m) : Возвращает подпись приватным ключом privkey для заданного сообщения m.
VERIFY(pubkey, m, sig) : Проверяет подпись sig относительно открытого ключа pubkey и сообщения m. Возвращает true, если подпись действительна, иначе false.
Он также должен поддерживать следующие операции слепого ослепления ключей:
GENERATE_ALPHA(data, secret) : Генерирует альфу для тех, кто знает данные и опциональный секрет. Результат должен быть идентично распределен как приватные ключи.
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 , применяя хеш-функцию HMAC SHA-256 согласно спецификации RFC-2104 . Это означает, что SALT_LEN составляет максимум 32 байта.
Формат
Зашифрованный формат LS2 состоит из трех вложенных уровней:
- Внешний слой, содержащий необходимую информацию в открытом тексте для хранения и извлечения.
- Средний слой, который обрабатывает аутентификацию клиента.
- Внутренний слой, который содержит фактические данные LS2.
Общий формат выглядит следующим образом:
Layer 0 data + Enc(layer 1 data + Enc(layer 2 data)) + Signature
Обратите внимание, что зашифрованный LS2 является затуманенным. Destination не находится в заголовке. Место хранения в DHT определяется как SHA-256(тип подписи || затуманенный публичный ключ) и изменяется ежедневно.
НЕ использует стандартный заголовок LS2, указанный выше.
Слой 0 (внешний)
Тип : 1 байт
На самом деле не в заголовке, но часть данных, покрываемых подписью. Взять из поля в сообщении Database Store Message.
Тип подписи скрытого публичного ключа : 2 байта, big endian
Это всегда будет тип 11, идентифицирующий ослепленный ключ Red25519.
Blinded Public Key : Длина определяется типом подписи
Временная метка публикации : 4 байта, big endian
Секунды с начала эпохи, переполняется в 2106 году
Expires : 2 байта, big endian
Смещение от опубликованной временной метки в секундах, максимум 18.2 часа
Флаги : 2 байта
Порядок битов: 15 14 … 3 2 1 0
- Бит 0: Если 0, нет offline ключей; если 1, есть offline ключи
- Другие биты: установить в 0 для совместимости с будущими применениями
Данные временного ключа : Присутствуют, если флаг указывает на автономные ключи
- Временная метка истечения : 4 байта, big endian. Секунды с начала эпохи, переполняется в 2106 году
- Тип временной подписи : 2 байта, big endian
- Временный открытый ключ подписи : Длина определяется типом подписи
- Подпись : Длина определяется типом подписи скрытого открытого ключа. Над временной меткой истечения, типом временной подписи и временным открытым ключом. Проверяется скрытым открытым ключом.
lenOuterCiphertext : 2 байта, big endian
outerCiphertext : lenOuterCiphertext байт
Зашифрованные данные уровня 1. См. ниже алгоритмы выведения ключей и шифрования.
Signature : Длина, определяемая типом подписи используемого ключа подписи
Подпись распространяется на всё вышеперечисленное. Если флаг указывает на офлайн ключи, подпись проверяется с помощью временного публичного ключа. В противном случае подпись проверяется с помощью скрытого публичного ключа.
Уровень 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 ZCASH . Подписи Red25519 работают на кривой Ed25519, используя SHA-512 для хеширования.
Мы не используем приложение A.2 из rend-spec-v3.txt Tor TOR-REND-SPEC-V3 , которое имеет схожие цели проектирования, поскольку его ослепленные открытые ключи могут выходить за пределы подгруппы простого порядка, что имеет неизвестные последствия для безопасности.
Цели
- Подписывающий публичный ключ в неослепленном назначении должен быть Ed25519 (тип подписи 7) или Red25519 (тип подписи 11); другие типы подписей не поддерживаются
- Если подписывающий публичный ключ находится в офлайне, временный подписывающий публичный ключ также должен быть Ed25519
- Ослепление является вычислительно простым
- Использовать существующие криптографические примитивы
- Ослепленные публичные ключи нельзя разослепить
- Ослепленные публичные ключи должны находиться на кривой Ed25519 и в подгруппе простого порядка
- Необходимо знать подписывающий публичный ключ назначения (полное назначение не требуется) для получения ослепленного публичного ключа
- Опционально предоставить дополнительный секрет, необходимый для получения ослепленного публичного ключа
Безопасность
Безопасность схемы блайндинга требует, чтобы распределение альфы было таким же, как у неблайндированных приватных ключей. Однако, когда мы блайндируем приватный ключ Ed25519 (тип подписи 7) в приватный ключ Red25519 (тип подписи 11), распределение отличается. Чтобы соответствовать требованиям раздела 4.1.6.1 zcash ZCASH , Red25519 (тип подписи 11) должен использоваться также для неблайндированных ключей, чтобы “комбинация повторно рандомизированного публичного ключа и подписи(-ей) под этим ключом не раскрывала ключ, из которого он был повторно рандомизирован.” Мы разрешаем тип 7 для существующих назначений, но рекомендуем тип 11 для новых назначений, которые будут зашифрованы.
Определения
B : Базовая точка (генератор) Ed25519 2^255 - 19 как в ED25519-REFS
L : Порядок Ed25519 2^252 + 27742317777372353535851937790883648493 как в ED25519-REFS
DERIVE_PUBLIC(a) : Преобразовать приватный ключ в публичный, как в Ed25519 (умножить на G)
alpha : 32-байтовое случайное число, известное тем, кто знает назначение.
GENERATE_ALPHA(destination, date, secret) : Генерирует альфа для текущей даты для тех, кто знает назначение и секрет. Результат должен быть идентично распределен как приватные ключи Ed25519.
a : Неослепленный 32-байтовый приватный ключ подписи EdDSA или RedDSA, используемый для подписи назначения
A : Незашифрованный 32-байтный открытый ключ подписи EdDSA или RedDSA в назначении, = DERIVE_PUBLIC(a), как в Ed25519
a’ : Ослеплённый 32-байтовый закрытый ключ подписи EdDSA, используемый для подписи зашифрованного leaseset. Это действительный закрытый ключ EdDSA.
A’ : Ослепленный 32-байтный открытый ключ подписи EdDSA в Destination, может быть сгенерирован с помощью DERIVE_PUBLIC(a’) или из A и alpha. Это действительный открытый ключ EdDSA, находящийся на кривой и в подгруппе простого порядка.
LEOS2IP(x) : Переставить порядок входных байтов в little-endian
H*(x) : 32 байта = (LEOS2IP(SHA512(x))) mod B, то же самое, что и в Ed25519 hash-and-reduce
Вычисления ослепления
Новые секретные альфа-ключи и слепые ключи должны генерироваться каждый день (UTC).
Секретная альфа и ослепленные ключи вычисляются следующим образом:
GENERATE_ALPHA(destination, date, secret), для всех сторон:
// secret is optional, else zero-length
A = destination's signing public key
stA = signature type of A, 2 bytes big endian (0x0007 or 0x000b)
stA' = signature type of blinded public key A', 2 bytes big endian (0x000b)
keydata = A || stA || stA'
datestring = 8 bytes ASCII YYYYMMDD from the current date UTC
secret = UTF-8 encoded string
seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64)
// treat seed as a 64 byte little-endian value
alpha = seed mod L
BLIND_PRIVKEY(), для владельца, публикующего leaseset:
alpha = GENERATE_ALPHA(destination, date, secret)
// If for a Ed25519 private key (type 7)
seed = destination's signing private key
a = left half of SHA512(seed) and clamped as usual for Ed25519
// else for a Red25519 private key (type 11)
a = destination's signing private key
// Addition using scalar arithmetic
blinded signing private key = a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L
blinded signing public key = A' = DERIVE_PUBLIC(a')
BLIND_PUBKEY(), для клиентов, получающих leaseset:
alpha = GENERATE_ALPHA(destination, date, secret)
A = destination's signing public key
// Addition using group elements (points on the curve)
blinded public key = A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha)
Оба метода вычисления A’ дают одинаковый результат, как и требуется.
Подписывание
Неослепленный leaseSet подписывается неослепленным закрытым ключом подписи Ed25519 или Red25519 и проверяется с помощью неослепленного открытого ключа подписи Ed25519 или Red25519 (типы подписей 7 или 11) как обычно.
Если подписывающий публичный ключ находится офлайн, незашифрованный leaseset подписывается незашифрованным временным приватным ключом подписи Ed25519 или Red25519 и проверяется незашифрованным временным публичным ключом подписи Ed25519 или Red25519 (типы подписей 7 или 11) как обычно. См. дополнительные заметки ниже об офлайн ключах для зашифрованных leaseset’ов.
Для подписи зашифрованного leaseSet мы используем Red25519 на основе RedDSA ZCASH для подписи и проверки с помощью скрытых ключей. Подписи 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 отличается, даже при подписи одних и тех же данных одним и тем же ключом.
Signing:
T = 80 random bytes
r = H*(T || publickey || message)
(rest is the same as in Ed25519)
Verification:
Same as for Ed25519
Шифрование и обработка
Выведение подучетных данных
В рамках процесса сокрытия нам необходимо обеспечить, чтобы зашифрованный LS2 мог быть расшифрован только тем, кто знает соответствующий открытый ключ подписи Destination. Полный Destination не требуется. Для достижения этого мы выводим credential из открытого ключа подписи:
A = destination's signing public key
stA = signature type of A, 2 bytes big endian (0x0007 or 0x000b)
stA' = signature type of A', 2 bytes big endian (0x000b)
keydata = A || stA || stA'
credential = H("credential", keydata)
Строка персонализации гарантирует, что учетные данные не будут конфликтовать с любым хешем, используемым в качестве ключа поиска DHT, например с обычным хешем Destination.
Для данного замаскированного ключа мы можем затем вывести подучетные данные:
subcredential = H("subcredential", credential || blindedPublicKey)
Подучетные данные включаются в процессы вывода ключей ниже, что связывает эти ключи со знанием открытого ключа подписи Destination.
Шифрование уровня 1
Сначала подготавливается входные данные для процесса выведения ключа:
outerInput = subcredential || publishedTimestamp
Далее генерируется случайная соль:
outerSalt = CSRNG(32)
Затем выводится ключ, используемый для шифрования слоя 1:
keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
outerKey = keys[0:31]
outerIV = keys[32:43]
Наконец, открытый текст первого уровня шифруется и сериализуется:
outerCiphertext = outerSalt || ENCRYPT(outerKey, outerIV, outerPlaintext)
Расшифровка первого уровня
Salt извлекается из зашифрованного текста уровня 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])
Авторизация по клиентам
Когда для Destination включена авторизация клиентов, сервер ведет список клиентов, которым он разрешает расшифровывать зашифрованные данные LS2. Данные, хранимые для каждого клиента, зависят от механизма авторизации и включают некоторую форму ключевого материала, который каждый клиент генерирует и отправляет серверу через защищенный внеполосный механизм.
Существует две альтернативы для реализации авторизации для каждого клиента:
Авторизация клиента DH
Каждый клиент генерирует DH keypair [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 и вектора инициализации 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)
Авторизация клиента с предварительно распределённым ключом
Каждый клиент генерирует секретный 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 и вектора инициализации шифрования 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)
Соображения безопасности
Оба описанных выше механизма авторизации клиентов обеспечивают конфиденциальность членства клиентов. Сущность, которая знает только Destination, может видеть количество подписанных клиентов в любой момент времени, но не может отследить, какие клиенты добавляются или отзываются.
Серверы ДОЛЖНЫ рандомизировать порядок клиентов каждый раз при генерации зашифрованного LS2, чтобы предотвратить возможность клиентов узнать свою позицию в списке и делать выводы о том, когда другие клиенты были добавлены или отозваны.
Сервер МОЖЕТ скрыть количество подписанных клиентов, вставив случайные записи в список данных авторизации.
Преимущества клиентской авторизации DH
- Безопасность схемы не зависит исключительно от внеполосного обмена ключевым материалом клиента. Приватный ключ клиента никогда не покидает его устройство, поэтому злоумышленник, который может перехватить внеполосный обмен, но не может взломать алгоритм DH, не сможет расшифровать зашифрованный LS2 или определить, на какое время клиенту предоставляется доступ.
Недостатки клиентской авторизации DH
- Требует N + 1 DH операций на стороне сервера для N клиентов.
- Требует одну DH операцию на стороне клиента.
- Требует от клиента генерации секретного ключа.
Преимущества авторизации клиента по PSK
- Не требует операций DH.
- Позволяет серверу генерировать секретный ключ.
- Позволяет серверу использовать один и тот же ключ с несколькими клиентами, если это необходимо.
Недостатки авторизации клиента по PSK
- Безопасность схемы критически зависит от внеполосного обмена ключевым материалом клиента. Противник, который перехватит обмен для конкретного клиента, может расшифровать любой последующий зашифрованный LS2, для которого этот клиент авторизован, а также определить, когда доступ клиента отозван.
Зашифрованный LS с адресами Base 32
Вы не можете использовать традиционный base 32 адрес для зашифрованного LS2, поскольку он содержит только хеш назначения. Он не предоставляет незашифрованный публичный ключ. Поэтому одного base 32 адреса недостаточно. Клиенту нужен либо полный destination (который содержит публичный ключ), либо сам публичный ключ. Если у клиента есть полный destination в адресной книге, и адресная книга поддерживает обратный поиск по хешу, то публичный ключ может быть получен.
Поэтому нам нужен новый формат, который помещает открытый ключ вместо хеша в base32 адрес. Этот формат также должен содержать тип подписи открытого ключа и тип подписи схемы блайндинга. Общие требования составляют 32 + 3 = 35 байт, что требует 56 символов в base 32, или больше для более длинных типов открытых ключей.
data = ((1 byte flags || 1 byte unblinded sigtype || 1 byte blinded sigtype) XOR checksum) || 32 byte pubkey
address = Base32Encode(data) || ".b32.i2p"
Мы используем тот же суффикс “.b32.i2p”, что и для традиционных base 32 адресов. Адреса для зашифрованных leaseSet идентифицируются по 56 закодированным символам (35 декодированных байт) по сравнению с 52 символами (32 байта) для традиционных base 32 адресов. Пять неиспользуемых битов в конце b32 должны быть равны 0.
Вы не можете использовать зашифрованный LS2 для bittorrent из-за компактных ответов announce, которые составляют 32 байта. Эти 32 байта содержат только хеш. В них нет места для указания того, что leaseSet зашифрован, или типов подписи.
Смотрите спецификацию именования или предложение 149 для получения дополнительной информации о новом формате.
Зашифрованный LS с автономными ключами
Для зашифрованных leaseSet с автономными ключами слепые приватные ключи также должны генерироваться в автономном режиме, по одному на каждый день.
Поскольку опциональный блок offline подписи находится в открытой части зашифрованного leaseset, любой, кто сканирует floodfill узлы, может использовать это для отслеживания leaseset (но не для его расшифровки) в течение нескольких дней. Чтобы предотвратить это, владелец ключей должен также генерировать новые временные ключи для каждого дня. И временные, и ослепленные ключи могут быть сгенерированы заранее и переданы в router пакетом.
Не определен формат файла для упаковки множественных временных и скрытых ключей и предоставления их клиенту или router. Не определено расширение протокола I2CP для поддержки зашифрованных leaseSet с офлайн-ключами.
Заметки
- Сервис, использующий зашифрованные leaseSet, будет публиковать зашифрованную версию в floodfill. Однако для эффективности он будет отправлять незашифрованные leaseSet клиентам в обернутом garlic сообщении после аутентификации (например, через белый список).
- Floodfill могут ограничивать максимальный размер разумным значением для предотвращения злоупотреблений.
- После расшифровки следует выполнить несколько проверок, включая соответствие внутренней временной метки и срока действия тем, что указаны на верхнем уровне.
- ChaCha20 был выбран вместо AES. Хотя скорость схожа при наличии аппаратной поддержки AES, ChaCha20 работает в 2,5-3 раза быстрее, когда аппаратная поддержка AES отсутствует, например, на устройствах ARM нижнего сегмента.
Справочные материалы
- [ED25519-REFS] “Высокоскоростные высокобезопасные подписи” авторов Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe, and Bo-Yin Yang. http://cr.yp.to/papers.html#ed25519
- [KEYBLIND-PROOF] https://lists.torproject.org/pipermail/tor-dev/2013-December/005943.html
- [KEYBLIND-REFS] https://trac.torproject.org/projects/tor/ticket/8106 и https://lists.torproject.org/pipermail/tor-dev/2012-September/004026.html
- [PRNG-REFS] http://projectbullrun.org/dual-ec/ext-rand.html и https://lists.torproject.org/pipermail/tor-dev/2015-November/009954.html
- [RFC-2104] https://tools.ietf.org/html/rfc2104
- [RFC-4880-S5.1] https://tools.ietf.org/html/rfc4880#section-5.1
- [RFC-5869] https://tools.ietf.org/html/rfc5869
- [RFC-7539-S2.4] https://tools.ietf.org/html/rfc7539#section-2.4
- [TOR-REND-SPEC-V3] https://spec.torproject.org/rend-spec-v3
- [ZCASH] https://github.com/zcash/zips/tree/master/protocol/protocol.pdf