Содержание статьи
Что такое OMEMO
OMEMO рекурсивно расшифровывается как OMEMO Multi-End Message and Object Encryption. Протокол был разработан в 2015 году. А в 2016 году была представлена его экспериментальная спецификация XEP-0384. OMEMO заинтересовал меня еще тогда, но нормальная его поддержка у клиентов появилась сравнительно недавно.
Я попробовал множество клиентов из официального списка. Каждый отдельно описывать я здесь не буду, скажу лишь, что в Linux нормально работал Dino IM и отвратительно работал Profanity. В Windows выбора особо нет — либо Gajim, либо веб‑версии других клиентов. На «маке» я ни один не тестировал за отсутствием собственно «мака». Однако Gajim работает на всех платформах, и у него оказался самый приятный интерфейс. В частности, есть механика «слепого доверия», при которой тебе не надо вручную регистрировать фингерпринт собеседника.
Почему не OTR?
Протокол Off-the-Record Messaging (OTR) долгое время был стандартом шифрования в Jabber. OMEMO выгодно отличается тем, что предоставляет возможность отправки офлайн‑сообщений (то есть тех, что придут пользователю, когда он появится в сети) и поддерживает шифрованные групповые чаты. Также он позволяет одновременно использовать несколько устройств с одним аккаунтом и на всех расшифровывать отправленные тебе сообщения!
Как и в OTR, в OMEMO необходимо вручную авторизовать фингерпринт собеседника, чтобы избежать MITM. Лучше всего проверять его через сторонние приложения. В отличие от OTR, OMEMO не предоставляет правдоподобного отрицания — все сообщения криптографически подписаны, что гарантирует проверку их авторства.
Однако OTR — протокол откровенно старый. Шифрование переписок стоит поперек горла у многих, и чем больше прошло времени, тем больше вероятность того, что протокол кем‑то скомпрометирован. Кем‑то, кто умеет годами об этом никому не рассказывать и только тихонько читать «зашифрованные» сообщения. Я не предлагаю отказываться от PGP или OTR. Но, как мне кажется, так же как и пароли, протоколы стоит время от времени менять.
Разбор протокола
Основной документ, описывающий протокол, — это XEP-0384. Но за техническими деталями он отсылает к спецификациям X3DH и Double Ratchet на сайте Signal. Фактически OMEMO — это имплементация протокола Signal поверх протокола XMPP. OMEMO активно использует расширение PubSub для публикации открытой части своих ключей на сервере Jabber и для запроса ключей собеседника, даже когда он находится вне сети.
Публикация ключей
Сразу после входа в сеть клиент Jabber публикует список своих устройств.
<iq xmlns="jabber:client" type="set" id="effde33f-f129-4904-a418-2f114903fb8c"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="eu.siacs.conversations.axolotl.devicelist"> <item id="current"> <list xmlns="eu.siacs.conversations.axolotl"> <device id="839508582" /> </list> </item> </publish> <publish-options> <x xmlns="jabber:x:data" type="submit"> <field var="FORM_TYPE" type="hidden"> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var="pubsub#persist_items"> <value>true</value> </field> <field var="pubsub#access_model"> <value>open</value> </field> </x> </publish-options> </pubsub></iq>
Протокол использует имя axolotl
— это старое название алгоритма Double Ratchet. Клиент сообщает, что у него есть одно устройство с номером 839508582. Каждый клиент Jabber считается отдельным «устройством», даже если они запущены в одной системе. Номер устройства генерируется на стороне клиента как случайное число UInt32.
<iq xmlns="jabber:client" type="set" id="dd3eabcb-2dd6-4e30-9a71-e82dcbff5846"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="eu.siacs.conversations.axolotl.bundles:839508582"> <item id="current"> <bundle xmlns="eu.siacs.conversations.axolotl"> <signedPreKeyPublic signedPreKeyId="1000768793">BS7b3OBON2Rrb9dNgiTnVtqj1RWPbKeXYkOHCrTwEXh3</signedPreKeyPublic> <signedPreKeySignature>PbekCtW80zwFdkXfxY16x51wyMiv15XI2jp8UxTWSQJ/KxJ9siqxihRymThvwfX0UE/n2HBTe3KBX6LFIkz0BA==</signedPreKeySignature> <identityKey>BSqKZLn8CEltZPLZtHJMWXpTgahV+BbArttWE0OIaqwK</identityKey> <prekeys> <preKeyPublic preKeyId="1">BfCa28WEYBKyKfVQ5tOc0Ml0AYkbkwhrcirZbX1+bb4n</preKeyPublic> <!-- (...) 98 ключей --> <preKeyPublic preKeyId="100">BcVt7CO5PuU5wGkMWDMoHZLbzDuqlo8DghHehZ5Nmm4j</preKeyPublic> </prekeys> </bundle> </item> </publish> <publish-options> <x xmlns="jabber:x:data" type="submit"> <field var="FORM_TYPE" type="hidden"> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var="pubsub#persist_items"> <value>true</value> </field> <field var="pubsub#access_model"> <value>open</value> </field> </x> </publish-options> </pubsub></iq>
Дальше клиент публикует актуальный Bundle — специальную структуру с набором открытых ключей. Они нужны для старта сессии OMEMO. Обрати внимание, Bundle указывает на номер конкретного устройства 839508582. Таким образом, для каждого устройства на сервере хранится уникальный набор ключей. Разумеется, это только публичные части ключей, закрытая часть остается у клиента.
Ключи делятся на три типа: долгоживущий, среднесрочный и одноразовый.
identityKey
Долгоживущий публичный ключ Curve25519. Одновременно выступает уникальным идентификатором каждого устройства. Из него формируется fingerprint — отпечаток устройства, доверие к которому устанавливается вручную.
Спецификация просит создавать fingerprint как хеш SHA-256 от публичного ключа identityKey
. Однако фактическая реализация клиента ведет себя иначе!
identityKey B2E2A1FE74930C82C60BF72F61EEB2C5DD070CD06769DC7676CB6F63AF27AA11
fingerprint 05B2E2A1FE74930C82C6D8F72F61EEB2C5DD07DCD06769DC7676CB6F63AF27AA
Сравнив ключ с отпечатком, видим, что это тот же ключ, но в другом виде. При экспорте ключа он сохраняется в специальном формате с префиксом 0x05
. В исходниках Gajim эта константа называется DJB_TYPE
. То есть префикс указывает на тип ключа. DJB расшифровывается как Daniel J. Bernstein. Это человек, впервые описавший криптографическую кривую Curve25519. Скорее всего, такой формат ключей выбран для совместимости с протоколом Signal.
signedPreKeyPublic
Ключ для начала сессии. Стандарт требует обновлять его примерно раз в неделю. Обрати внимание: у ключа есть уникальный номер 1000768793.
signedPreKeySignature
Подпись ключа signedPreKeyPublic
через алгоритм XEdDSA. Подписывается закрытой частью ключа identityKey.
prekeys
Пул одноразовых публичных ключей. Каждый ключ используется лишь однажды при создании сессии, после чего удаляется. Обычно клиент публикует сто одноразовых ключей в одном бандле.
Получение ключей собеседника
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее