Peering

Peering / Блог

Что релей Peering может и чего не может видеть

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

Что он делает

У релея три задачи. Он знакомит два устройства, которые не могут связаться напрямую, копирует байты между ними, когда они всё ещё не могут, и отправляет push-уведомления от имени Mac, например «вашему агенту для программирования нужен ответ». Это небольшая программа на Rust, и держать её задачи небольшими и есть замысел.

Что он знает

Честность в этом вопросе часть замысла. Чтобы выполнять свою работу, релей видит:

Политика конфиденциальности перечисляет всё это полностью. Ничто из этого не является содержимым вашего сеанса.

Чего он не видит

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

Управляющий канал это TLS между устройствами

Когда сеанс идёт через релей, iPhone выполняет рукопожатие TLS 1.3 с Mac сквозь релей. Релей копирует зашифрованные записи, не завершая TLS, поэтому у него никогда нет ключа сеанса. Оба конца проходят проверку подлинности ключами, которые запомнили при связывании, так что релей не может подставить себя, не вызвав отказа iPhone в соединении.

Ключ видео никогда не проходит через релей

Видео идёт другой дорогой, по UDP, поэтому ему нужен свой ключ. Когда сеанс готов, Mac создаёт 32 случайных байта и отправляет их на iPhone внутри потока TLS. Релей несёт этот поток и не может его открыть. Затем каждое направление выводит свой ключ через HKDF-SHA256, и каждая датаграмма запечатывается ChaCha20-Poly1305 вместе со счётчиком.

key = HKDF-SHA256(secret, salt = "pl.dmq.peering.video-datagram", info = direction)
datagram = token | counter | ciphertext | tag

16-байтный токен в начале это единственное, что читает релей. Это подсказка для маршрутизации, которая говорит, к какому сеансу относится датаграмма, и релей убирает её перед пересылкой. Всё после неё это шифртекст.

Экран блокировки iPhone с двумя уведомлениями Peering: терминал завершил работу и терминалу нужно подтверждение.
Push-уведомления несут короткий текст и кодированное состояние. Ваш экран никогда не входит в их состав.

Push-уведомления и Live Activity

Когда iPhone спит, Mac просит релей разбудить его push-сообщением Apple. Содержимое этого сообщения намеренно скудное: короткий заголовок и текст для уведомления, а для обновлений Live Activity только кодированное состояние: идентификаторы, состояния, счётчики и время. Mac никогда не отправляет релею ваш экран или вывод терминала. О подтверждениях для агентов читайте в статье уведомления для агентов для программирования.

Доказательства вместо аккаунтов

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

Проверьте сами

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

Вопросы

Может ли релей Peering видеть мой экран?

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

Что релей Peering знает обо мне?

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