Pull to refresh

Comments 7

Есть несколько мыслей по оптимизации вашей схемы. У вас получается что клиент сможет отправить первый байты полезных данных (например имя api endpoint-a) только после полного раундтрипа? А что насчет сервера? Если сервер вычислил ключ шифрования уже на первом upd-пакете от клиента то значит он имеет теоретическую возможность отправить зашифрованные данные тут же не дожидаясь запроса от клиента, правильно? То есть сервер генерирует "ephemeral server public key" записывает его в udp-пакет который предназначен клиенту и тут же внутри этого же udp пакета следом записывает уже зашифрованные данные и тогда клиент получив кей-шару от сервера вычисляет симметрический ключ шифрования и дальше сможет расшифровать уже полученные данные от сервера.

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

И дальше появляются мысли как можно еще сильнее зарыться в оптимизацию сетевых раундтрипов - если имя api-ручки сервера не является таким уж и секретным то клиент может включить это имя (в незашифрованном виде) в тот свой первый udp-пакет и тогда сервер сможет выполнить полученный запрос и отправить уже зашифрованные данные клиенту уже в том первом udp-пакете который клиент получит от сервера.
И кстати тут же напрашивается идея что можно сэкономить драгоценное место в upd-пакете (1472 байт) и закодировать имя api-ручки в номере udp-порта - то есть сервер будет слушать не один порт а например тысячу сетевых портов по одному на каждую api-ручку, правда для этого нужно чтобы это зашифрованное соединение не было привязано не только к ip-аддресу и порту клиента (что в принципе и так является хорошей практикой чтобы например избежать установки нового соединения при смене клиентом wi-fi сети на мобильную сеть и наоборот) но и к порту сервера.

Ну и наконец появляется такая идея что этот "ephemeral server public key" можно вообще включить в бинарник приложения и тогда клиент сможет вычислить ключ для симметричного шифрования вообще без общения с сервером и тогда он сможет в своем первом upd-пакете (включив сначала свой "ephemeral public key" ) отправить серверу в зашифрованном виде как имя api-endpoint-а так и соответствующие данные.

Спасибо за размернутый фидбэк и советы.

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

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

Оптимизация раундтипов хорошая возможность для улучшения. Это очень важно для скорости запуска и бесшовных переподключений, но внедрение нулевого раундтипа приведет к сложностям в виде защиты от reply атак, что может быть критично. К тому же я описывал механизм востановления сессии, который не успел реализовать, который должен значительно сократить время повторного подключения. И действительно нужен ли нулевой раундтип в добавку к механизму возобновления сессии нужно будет проверять эксперементально, после его реализации. В первой версии протокола я оставлю текущую схему, но если эксперемент покажет критические зависания связанные с 1-RTT, придется его оптимизировать.

А запекание ephemeral ключа приведет к проблемам с PFS, а в данном случае для меня безопасность приоритетнее скорости

Нет формального обоснования - алгоритм в помойку

Несколько я знаю, в криптографии основное правило: если ты не эксперт, то пользуйся готовыми алгоритмами и протоколами

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

Я вот этот момент никогда понять не мог

Дело в том, что S — это результат математической операции, и использовать его «как есть» в качестве ключа небезопасно.

S мы можем получить, только если как-то утекли приватные ключи или как-то взломали сам алгоритм. А если у нас уже есть S, то мы точно так же можем применить KDF и получить нужные сессионные ключи. Тогда какая разница?

В начале тоже не понимал в чем вообще смысл этого "Лишнего шага", пока писал свой протокол как раз и разабрался в этом.

Суть в том, что S - результат заранее известной математической операции, по этому оно может иметь предсказуему структуру, иметь какой-то паттерн. Например, каждый второй бит S обязательно 0 (пример взят из воздуха). Из-за чего криптоанализ упрощается и надежность падает. Деревация ключа делает его чуть более непредсказуемым.

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

Sign up to leave a comment.

Articles