Для инженеров Устройство — достаточно подробно, чтобы его оценить

Как устроен Mirage.

Что делает криптография, что инфраструктура видит и чего не видит, и как движок встраивается в другой продукт. Прямо о том, какие части готовы, а какие — нет.

Сообщение внутри двух слоёв защиты: запечатано сессией, затем передаётся внутри зашифрованного QUIC-туннеля. привет ВНЕШНИЙ СЛОЙ QUIC · TLS 1.3 ВНУТРЕННЯЯ ПЕЧАТЬ ключ на сообщение СОДЕРЖИМОЕ видно только на концах
Модель угроз

Начнём с того, чего система не делает.

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

Против чего работает

  • Провайдер или оператор сети, читающий содержимое сообщений или звонков
  • Оператор relay, читающий что-либо из того, что он передаёт, — включая нас
  • Попытка прочитать ваш список контактов на наших серверах: его там нет
  • Расшифровка прошлого трафика после того, как ключ позже скомпрометирован
  • Подмена сообщения в пути: она обнаруживается, а не просто маловероятна

От чего не защищает

  • Скомпрометированное устройство: вредоносная программа или программа, считывающая экран, на вашем же устройстве
  • Собеседник, который всегда может сохранить или переслать то, что вы отправили
  • Глобальный противник, сопоставляющий тайминги по всей сети
  • Человек, получивший вашу фразу восстановления
  • Наблюдатель на вашем локальном канале, который узнаёт, что вы вообще подключены
Внешнего аудита пока не было. Устройство описано здесь, чтобы его можно было проверить, а не потому, что его уже проверили. Относитесь к нему как к протоколу, который стоит изучить, а не как к уже заверенному.
Идентичность

Аккаунта нет — есть только ключ.

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

Идентичность спроектирована в два уровня, чтобы потеря устройства не означала потерю аккаунта. Корневой ключ (Ed25519) никогда ничего не шифрует; его единственная задача — подписывать версионированный roster, список устройств, которые сейчас принадлежат вам. Украденное устройство отзывается публикацией новой версии roster. Несколько устройств — в работе

Проверка устроена так, как должна: сверьте отпечаток по независимому каналу. Приложение показывает его короткими группами, чтобы его можно было прочитать вслух. Если вы потеряете все устройства, аккаунт вернёт фраза восстановления BIP-39.

Сессии

Ключи движутся вперёд, никогда — назад.

Разговор начинается с согласования ключей X3DH поверх X25519 — оно позволяет отправить первое сообщение, даже пока собеседник офлайн. Дальше сессия работает на Double Ratchet: каждое сообщение продвигает ключевой материал, поэтому ключ, украденный сегодня, не открывает вчерашний трафик, а сессия восстанавливается, как только скомпрометированный ключ выходит из употребления.

У звонков свой ratchet, отдельный от текстового, потому что медиаключи ротируются в другом ритме. Содержимое запечатывается ChaCha20-Poly1305 или AES-256-GCM. Оба — аутентифицированное шифрование, поэтому подмена обнаруживается, а не просто маловероятна.

Установка соединения

Несколько маршрутов наперегонки.

NAT — причина, по которой большинство P2P-программ незаметно откатываются на сервер. Mirage пробует несколько стратегий сразу и берёт ту, что сработает первой, поэтому прямой путь используется всегда, когда он существует.

Напрямую к пиру

Если адрес уже известен из обмена пирами (peer exchange), QUIC-соединение открывается сразу, и сервер не участвует.

Знакомство через mesh ранняя стадия

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

Hole punching через relay

Relay синхронизирует UDP-рандеву двух сторон. Он их знакомит, но саму сессию не переносит.

Через relay, по-прежнему запечатано

Когда прямого пути нет, relay пересылает пакеты, которые не может открыть. Фолбэк на TCP/443 для сетей, которые полностью режут UDP, — в планах

Инфраструктура

Что relay видит и чего не видит.

Relay нужен для обнаружения, обхода NAT и хранения сообщений для тех, кто офлайн. Аккаунтов пользователей он не хранит, а всё, что он передаёт, запечатано ещё до того, как к нему попадёт. Вот честная опись.

Содержимое сообщений и звонковникогда
Закрытые ключиникогда
Ваш список контактовникогда
Факт подключения с адресада
Размер и время пересылаемых пакетовда
Что для кого-то ждёт запечатанный конвертда

Строки «да» — вот о чём стоит спорить, и именно поэтому устройство так настойчиво стремится к прямым соединениям: то, что relay никогда не передаёт, у него никогда нельзя потребовать выдать.

Доставка офлайн

Запечатанные конверты у того, кто не может их открыть.

Если получатель офлайн, сообщение шифруется в конверт и оставляется у relay. Конверт аутентифицирован, поэтому хранитель не может его изменить, и непрозрачен, поэтому хранитель не может его прочитать. Он доставляется, когда получатель возвращается; relay узнаёт только, что что-то ждало.

Сейчас конверты хранятся в памяти relay до суток. Хранение их на десктопных узлах mesh вместо relay и зашифрованный бэкап истории спроектированы и в работе

Сетевая устойчивость

Сделано для сетей, которые мешают.

Всё идёт поверх QUIC на UDP/443 с TLS 1.3 внутри, поэтому сертификаты и метаданные сессии не видны анализатору на пути. Служебный трафик (обмен ключами, обнаружение пиров, присутствие) идёт в том же туннеле, что и сообщения. Второго канала, за которым можно следить, нет.

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

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

Комнаты и группы

Одна идея для чатов и звонков: комната.

Текстовый разговор и звонок внутри движка — один и тот же объект: комната. Внутри QUIC-соединения каждая комната использует независимые потоки — один для управления, один для исходящего медиа и отдельный входящий поток на каждого участника, — поэтому один медленный пир не может застопорить всех остальных.

Маршрутизаторы слепы: они пересылают запечатанные пакеты, не имея ключей ни к одному из них. Чаты и звонки один на один уже доступны. Небольшие групповые звонки используют общий ключ, согласованный между участниками, и находятся в тестировании для групп до восьми человек. Групповые чаты — в работе

Управление групповыми ключами переводится на MLS (RFC 9420) через мост на Rust вокруг openmls. Мост построен и прошёл нагрузочное тестирование; подключение его к движку — работа, которая идёт сейчас.

Движок

Пять слоёв, ничего на диске.

Всё описанное выше живёт в ядре на C++20. Интерфейс поверх него заменяем; десктопный мессенджер — просто первое, что на нём стоит.

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

Mirage Engine

То же ядро — внутри вашего продукта.

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

Версионированный C ABI

Вызывается из чего угодно с FFI: Node, Python, Go, Rust, C#, Swift. В разработке ABI 2.0 — с владением через контекст и семантическим версионированием; именно за эту поверхность мы возьмём на себя обязательства.

Управление через колбэки

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

Хранение и UI — ваши

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

Ваша инфраструктура

Relay — небольшой сервис на Go в одном контейнере. Запустите его в своей сети и своей юрисдикции; в нём нет пользовательских аккаунтов, которые пришлось бы переносить или выдавать по судебному запросу.

/* ABI 2.0 (в разработке): так выглядит интеграция, имена ещё могут измениться */
mirage_abi_version(…);        /* major должен совпадать, minor — не меньше вашего */
mirage_context_create(…);     /* один движок на контекст, несколько на процесс */
mirage_set_callbacks(…);      /* сообщения, комнаты, звонки, медиа */
mirage_room_create(…);
mirage_room_send_text(…);
mirage_room_leave(…);
mirage_context_stop(…);
mirage_context_destroy(…);

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

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

Хотите разобрать устройство вместе?

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

Записаться на технический разбор

Или прочитайте о программе дизайн-партнёров.