Yesterday

Мессенджер мечты: каким он должен быть

Каждый раз, когда я открываю очередной «защищённый» мессенджер, меня не покидает ощущение компромисса. Signal требует номер телефона и заблокирован в РФ. Telegram хранит переписку на серверах и тоже привязывается к телефону, как и WhatsApp, который принадлежит экстремистской Meta и сливает подробные метаданные. Session и SimpleX тормозят и тоже заблокированы РКН. Briar разряжает батарею за два часа, а ситуацию с блокировкой, откровенно говоря, даже не пытался выяснить.

Что остаётся в такой ситуации, если не мечтать? Каким должен быть мессенджер, который не заставляет выбирать между безопасностью, удобством и приватностью?

Судя по всему, подобные мысли приходят в голову не только мне. Недавно на Хабре появилась статья о разработке P2P-мессенджера ntied на Rust — с E2E-шифрованием, прямыми UDP-соединениями и голосовыми звонками. Проект интересный, но подход отличается от того, что описываю я. К этому сравнению вернусь в конце.

Философия: безопасность — не фича, а фундамент

Большинство мессенджеров проектируются так: сначала функциональность, потом — «а давайте добавим шифрование». Это путь в никуда. Безопасность, прикрученная сбоку, всегда будет дырявой.

Правильный подход — security by design (безопасность на уровне архитектуры). Каждое архитектурное решение принимается с вопросом: «А что, если атакующий контролирует сеть? А если у него есть доступ к серверу? А если он украл телефон?»

Два человека сформулировали это лучше всех:

  • Эдсгер Дейкстра: «Простота — необходимое условие надёжности». Меньше кода — меньше багов — меньше уязвимостей. Каждая лишняя строка — потенциальная дыра.
  • Дэниел Бернстайн: «Криптография должна быть скучной». Никаких экспериментов, никаких «инновационных» алгоритмов. Только проверенные временем и аудитами решения.

Эти принципы пронизывают всё, что описано ниже. Если где-то приходится выбирать между «красиво» и «просто» — выбираем простое. Если между «своя реализация» и «проверенная библиотека» — берём библиотеку.

Идентичность: никаких привязок к личности

Регистрация в идеальном мессенджере выглядит так: приложение генерирует криптографический ключ, хеширует публичную часть — и это ваш ID. Всё. Никакого номера телефона, email, имени. Случайная строка вроде AXQF7-BMKP2-DNJW9-RTVC4.

Почему это важно? Номер телефона — это не просто идентификатор, это связь с вашей реальной личностью. Оператор знает, кто вы. Государство знает, кто вы. Любая утечка базы данных мессенджера раскрывает не абстрактные ID, а реальных людей.

Потеряли seed-фразу — потеряли аккаунт. Это не баг, это фича. Невозможно «восстановить пароль через SMS», потому что нет никакого центрального сервера, который что-то о вас знает. Нет сервера — нечего взломать, нечего запросить по решению суда, нечего изъять при обыске в офисе.

Seed-фраза — 24 слова, которые вы записываете на бумагу и храните в надёжном месте. Это единственный способ восстановить доступ. Кажется неудобным? Возможно. Но это цена настоящей приватности.

Шифрование: не декларированное, а правильное E2E

End-to-end шифрование сейчас заявляют все. Но между «у нас есть E2E» и «наше E2E невозможно сломать» — пропасть.

Выбор алгоритмов

Принцип Бернстайна: берём только то, что проверено годами и аудитами.

XChaCha20-Poly1305 для симметричного шифрования. Почему не AES? AES быстр только с аппаратным ускорением (AES-NI). Без него — медленный и уязвимый к timing-атакам. ChaCha20 одинаково быстр везде: на сервере, на телефоне, на умных часах. А расширенная версия XChaCha20 использует 192-битный nonce вместо 96-битного, что практически исключает риск коллизий даже при огромном количестве сообщений.

X25519 для обмена ключами. Эллиптическая кривая Curve25519, разработанная тем самым Бернстайном. Быстрая, безопасная, устойчивая к timing-атакам. 128 бит безопасности — пока достаточно для любых практических целей.

BLAKE3 для хеширования. Современная замена SHA-2: быстрее, параллелизуемая, криптографически стойкая. Один алгоритм для всех задач — KDF, MAC, хеширование.

Постквантовая защита

Квантовые компьютеры пока не умеют ломать криптографию. Но когда-нибудь научатся. Проблема в том, что злоумышленник может записывать зашифрованный трафик сегодня, чтобы расшифровать его через 10–20 лет, когда квантовые компьютеры станут достаточно мощными. Это называется harvest now, decrypt later (собирай сейчас, расшифруешь потом).

Решение — гибридная криптография. Каждый обмен ключами использует и классический X25519, и постквантовый ML-KEM (он же Kyber, стандартизированный NIST в 2024 году). Результаты комбинируются через XOR или HKDF.

Если квантовый компьютер сломает X25519 — ML-KEM защитит. Если в ML-KEM найдут уязвимость (алгоритм всё-таки молодой) — классика спасёт. Атакующий должен сломать оба алгоритма одновременно.

Double Ratchet (двойной храповик): почему один ключ — это мало

Допустим, Алиса и Боб обменялись ключами и шифруют сообщения. Что произойдёт, если атакующий получит этот ключ? Он расшифрует всё: прошлые сообщения, будущие сообщения.

Double Ratchet решает эту проблему. Ключ не один — он постоянно меняется. После каждого сообщения текущий ключ уничтожается, а новый выводится криптографически. Это даёт два важных свойства:

  • Forward secrecy (прямая секретность): компрометация текущего ключа не раскрывает прошлые сообщения. Ключи, которыми они были зашифрованы, уже не существуют.
  • Post-compromise security (восстановление после компрометации): даже если атакующий получил ключ, через несколько сообщений он «потеряет след». Новые DH-обмены создадут ключи, которых у него нет.

Это не теоретическая конструкция — Signal Protocol работает именно так. Мы берём проверенное решение.

Отрицаемость: криптографическое «это не я»

Обычные цифровые подписи доказывают авторство. Если Алиса подписала сообщение своим приватным ключом, Боб может показать эту подпись третьей стороне (суду, журналисту) и доказать: «Это написала Алиса».

В идеальном мессенджере это недопустимо. Нужна отрицаемая аутентификация: Боб может проверить, что сообщение от Алисы, но не может доказать это никому другому.

Как это работает? Вместо подписи используется MAC (Message Authentication Code), вычисленный на общем секрете. Боб знает этот секрет — значит, он сам мог создать такой же MAC. Любой, кто может проверить подлинность, может и подделать. Криптографического доказательства авторства не существует.

Архитектура: никаких центральных серверов

Сервер — это точка отказа и точка атаки. Сервер можно взломать, изъять, заставить сотрудничать. В идеальном мессенджере серверов нет.

Как это работает

DHT (Distributed Hash Table) — распределённая хеш-таблица для поиска контактов. Работает как в BitTorrent: тысячи узлов хранят фрагменты информации, и ни один не знает всей картины. Хотите найти собеседника по его публичному ключу? Запрос уходит в сеть и возвращается с адресами, где он доступен.

Прямые соединения — когда возможно, клиенты связываются напрямую. NAT hole punching, STUN, ICE — техники для пробивания файрволов и NAT без посредников. Ваше сообщение идёт прямо к собеседнику, минуя любые серверы.

Relay-ноды (ретрансляторы) — волонтёрские узлы для случаев, когда прямое соединение невозможно, или когда собеседник оффлайн. Relay хранит зашифрованные сообщения и пересылает их при появлении получателя. Важно: relay видит только зашифрованные блобы. Он не знает ни содержимого, ни кто отправитель, ни кто получатель.

Mix-сеть (сеть перемешивания) — для параноидального режима. Сообщение проходит через цепочку узлов, на каждом перешифровывается, задерживается на случайное время, перемешивается с другими сообщениями. Даже если атакующий контролирует часть сети и видит весь трафик, он не может связать входящие сообщения с исходящими.

Защита метаданных

Обычно шифрование скрывает содержимое, но не метаданные. «Алиса писала Бобу в 3 часа ночи каждый день в течение месяца» — это информация, даже если сами сообщения недоступны.

Но с этим можно и нужно бороться. Например, вот так:

  • Padding (выравнивание): все сообщения приводятся к одинаковому размеру. Отправили «Привет» или роман — снаружи выглядит одинаково.
  • Cover traffic (маскировочный трафик): постоянный поток фиктивных сообщений. Реальные сообщения неотличимы от шума.
  • Timing jitter (временной джиттер): случайные задержки перед отправкой. Нельзя скоррелировать по времени.

Да, это тратит батарею и трафик. Поэтому — опционально, для тех, кому нужно.

Устойчивость к блокировкам

В России блокируют мессенджеры. В Китае их блокируют ещё жёстче. В Иране отключают интернет целиком. Идеальный мессенджер должен работать везде.

Маскировка трафика

Трафик мессенджера должен быть неотличим от обычного HTTPS. DPI (Deep Packet Inspection) анализирует паттерны — значит, нужно имитировать паттерны браузера:

  • TLS fingerprint randomization (рандомизация отпечатка TLS): TLS-хендшейк выглядит как Chrome или Firefox. JA3/JA4-отпечатки рандомизируются.
  • HTTP-мимикрия: трафик упаковывается в легитимные HTTP/2 или HTTP/3 потоки с правильными заголовками.
  • Domain fronting (фронтирование домена): соединение идёт через популярный CDN. Снаружи выглядит как запрос к google.com или cloudflare.com.

Pluggable Transports (подключаемые транспорты)

Если один способ заблокировали — переключаемся на другой. Автоматически.

  1. Прямой QUIC — самый эффективный, пробуем первым.
  2. WebSocket через HTTPS — проходит через большинство прокси.
  3. Domain fronting через CDN — для серьёзной цензуры.
  4. Tor bridges — для совсем жёстких случаев.
  5. Mesh через Bluetooth/Wi-Fi Direct — когда интернета нет вообще.

Клиент пробует варианты и выбирает работающий. Пользователь не должен разбираться в настройках — просто «работает» или «не работает».

Компактность и производительность

Electron — это Chromium, который весит 150+ мегабайт и ест 300+ мегабайт RAM на пустом окне. Для мессенджера, который должен работать в фоне 24/7, это неприемлемо.

Идеальный мессенджер:

  • исполняемый файл 3–5 мегабайт;
  • потребление памяти 30–50 мегабайт;
  • запуск за полсекунды;
  • минимальное влияние на батарею.

Как этого достичь? Никакого JavaScript в клиенте. Ядро на компилируемом языке (Nim или Rust), UI на нативных фреймворках каждой платформы: GTK на Linux, AppKit на macOS, SwiftUI на iOS, Jetpack Compose на Android.

Для веба — WebAssembly. Криптография и протокол выполняются в WASM, JavaScript только для glue-кода и отрисовки.

Аутентификация: не пароль, а ключи

В идеальном мессенджере нет «логина на сервер». Вы доказываете свою личность криптографически — подписывая сообщения приватным ключом. Это уже passwordless (беспарольная аутентификация) по определению.

Но как защитить сам приватный ключ на устройстве?

Обязательная многофакторная аутентификация

Одного PIN-кода недостаточно. Украли телефон, подсмотрели PIN — и всё скомпрометировано.

Решение — схема 2-из-3 на базе Shamir Secret Sharing (разделение секрета по схеме Шамира). Master seed разбивается на три части, каждая шифруется своим фактором:

  • Часть A: PIN или пароль (что вы знаете).
  • Часть B: аппаратный ключ — YubiKey, Secure Enclave, TPM (что вы имеете).
  • Часть C: recovery-фраза (что вы можете восстановить).

Чтобы разблокировать приложение, нужны любые два из трёх:

  • PIN + аппаратный ключ — обычный вход;
  • PIN + recovery — если потеряли аппаратный ключ;
  • аппаратный ключ + recovery — если забыли PIN.

Одного фактора недостаточно. Украденный телефон без PIN и без YubiKey бесполезен.

Верификация контактов

Как убедиться, что вы общаетесь с настоящим Бобом, а не с атакующим, который перехватил соединение?

Safety numbers (коды безопасности): хеш от публичных ключей обоих собеседников. Отображается как 12 групп цифр или как последовательность эмодзи. При личной встрече сравниваете числа — если совпадают, MITM-атаки (атаки посредника) нет.

QR-коды: один сканирует QR другого и наоборот. Быстрее, чем диктовать цифры.

Голосовая верификация: произносите эмодзи вслух по телефону. Если голос знакомый и эмодзи совпадают — всё в порядке.

Защита при принуждении

Вас задержали и требуют разблокировать телефон. Что делать?

Паник-пароль

Обычный PIN: 1234 — открывает реальный профиль. Паник-PIN: 4321 — открывает «чистый» профиль с котиками. Настоящие ключи тихо уничтожаются в фоне.

Вариант жёстче: паник-PIN мгновенно стирает все данные и показывает сообщение «ошибка расшифровки базы данных».

Скрытый профиль

Два пароля — два профиля в одном приложении. Пароль A открывает профиль A с невинной перепиской. Пароль B открывает настоящий профиль B.

Без пароля B невозможно даже доказать, что профиль B существует. Файл базы данных выглядит одинаково — просто зашифрованные данные.

Триггеры уничтожения

  • N неудачных попыток ввода пароля — wipe (полное уничтожение данных).
  • Удалённая команда по секретному слову от доверенного контакта.
  • Отсутствие «сигнала жизни» (не открывали приложение N дней) — wipe (стирание).
  • Выход из разрешённой географической зоны.

Важно: уничтожение должно быть надёжным. Не просто удаление файлов, а перезапись. На SSD — crypto-erase (криптографическое стирание). В RAM — зануление перед освобождением.

Режимы паранойи

Не всем нужен одинаковый уровень защиты. Журналисту в Иране нужно одно, а вам для переписки с друзьями — другое. Один размер не подходит всем.

Normal mode — для повседневного использования:

  • E2E-шифрование (всегда, без компромиссов);
  • минимальный padding;
  • история хранится локально;
  • обычные таймауты ключей;
  • экономия батареи.

Paranoid mode — для журналистов, активистов, whistleblowers (информаторов):

  • агрессивный padding — все сообщения одного размера;
  • постоянный cover traffic;
  • маршрутизация через Tor обязательна;
  • короткие таймауты ключей (ratchet каждые 5 минут);
  • автоудаление истории через N часов;
  • запрет скриншотов.

Extreme mode — для однократных анонимных коммуникаций:

  • история не сохраняется вообще;
  • ключи удаляются после сессии;
  • клиент не запоминает контакты;
  • каждый запуск — новая identity (опционально);
  • только mesh или Tor, никаких прямых соединений.

Переключается в настройках. Можно задать режим по умолчанию для конкретного контакта.

Множественные личности

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

Каждая идентичность:

  • использует отдельный seed;
  • подключается через разные Tor-цепочки;
  • использует разные relay-ноды;
  • имеет разные паттерны активности.

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

UX: безопасность не должна убивать удобство

Самый защищённый мессенджер бесполезен, если им неудобно пользоваться. Люди просто вернутся в Telegram — и будут правы. Безопасность ценой юзабилити не работает.

Поэтому обязательные фичи:

  • Поиск по истории — локальный, по зашифрованному индексу (SQLite FTS5). Никуда не отправляется, работает мгновенно.
  • Реакции на сообщения — да, эмодзи тоже E2E-шифруются.
  • Цитирование и ответы — reply-to с контекстом, опционально треды.
  • Вложения — фото, видео, файлы. Превью зашифрованы отдельно, скачиваются по запросу.
  • Голосовые и видеозвонки — E2E-шифрованные, с forward secrecy (прямой секретностью).
  • Уведомления — push без содержимого («новое сообщение»), расшифровка при открытии клиента.
  • Статусы доставки — «доставлено», «прочитано» — опционально, opt-in (по желанию) для обеих сторон.

Прозрачные обновления

Обычная подпись кода не защищает от целевых атак. Спецслужбы могут заставить разработчика выпустить «специальную» версию для конкретного пользователя или региона — с бэкдором внутри. Подпись будет валидной.

Решение — Certificate Transparency (прозрачность сертификатов) для релизов. Каждая версия записывается в публичный append-only лог (журнал только для добавления). Клиент при обновлении проверяет:

  1. Подпись разработчика — валидна?
  2. Этот билд есть в публичном логе?
  3. Лог консистентен (нет форков)?

Если билда нет в логе — отказ от обновления. Независимые мониторы следят за логом и поднимают тревогу при появлении подозрительных релизов.

Это не паранойя — это реальная защита от целевых атак на уровне поставки ПО.

Почему Nim, а не Rust

Rust — отличный язык с гарантиями безопасности памяти на уровне компилятора. Но для этого проекта есть аргументы в пользу Nim.

Главный инсайт: криптографию мы не пишем сами. Берём аудированный libsodium (30 000 строк C, используется везде — от Signal до WhatsApp) и статически линкуем. Это снимает главный аргумент за Rust — его криптографическую экосистему.

Что остаётся:

  • Nim компилируется за секунды, Rust — за минуты. За день разработки это сотни итераций вместо десятков.
  • Синтаксис Nim проще, код читабельнее. Меньше когнитивной нагрузки при аудите.
  • Nim компилируется в C. Можно использовать любой C-компилятор, все его оптимизации, легко интегрировать C-библиотеки.
  • Бинарники меньше.

Для кода в стиле Бернстайна — чем проще, тем лучше. Lifetimes и borrow checker Rust — мощные инструменты, но они добавляют сложность. В мессенджере эта сложность редко окупается.

При этом Rust остаётся хорошим выбором для WASM-модуля веб-версии — там его экосистема действительно более зрелая.

Реалистичность

Всё описанное технически реализуемо. Каждый компонент существует в виде работающих open-source проектов:

  • libsodium — криптографические примитивы, аудирован многократно;
  • liboqs — постквантовые алгоритмы (ML-KEM, ML-DSA);
  • libp2p — P2P-сетевой стек;
  • Nym — mix-сеть для анонимности;
  • SQLCipher — зашифрованная SQLite для локального хранилища;
  • Tor — луковая маршрутизация;
  • Sigsum — прозрачные логи для обновлений.

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

Сравнение с ntied

В начале я упоминал проект ntied — P2P-мессенджер на Rust. Автор (Иван Удовин) проделал серьёзную работу: рабочий прототип с голосовыми звонками, E2E-шифрование, прямые UDP-соединения. Но архитектурные решения отличаются:

Криптография: ntied использует NIST-кривые (p256, ECDSA, AES-GCM). Это валидный выбор, но я предпочитаю алгоритмы Бернстайна (Curve25519, XChaCha20-Poly1305) — они проще, быстрее и устойчивее к timing-атакам. Постквантовой защиты в ntied нет.

Архитектура: ntied использует вспомогательный сервер для NAT traversal — он хранит соответствие «публичный ключ → UDP-адрес». Это точка централизации: сервер знает, кто онлайн и когда. В моей модели эту роль выполняет DHT — никакой единой точки, которая видит всех.

Модель угроз: ntied фокусируется на базовом E2E-шифровании. В моём варианте — упор на защиту метаданных, устойчивость к блокировкам, режимы паранойи, постквантовую криптографию. Разные задачи — разные решения.

Язык: автор выбрал Rust, я склоняюсь к Nim + libsodium. Оба варианта имеют право на жизнь. Rust даёт гарантии на уровне компилятора, Nim — простоту и скорость разработки. Для криптографического ядра это менее критично, если используются аудированные C-библиотеки.

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

Оценка ресурсов

Сколько стоит построить такой мессенджер? Попробую дать реалистичную оценку.

Команда: 6–8 человек на полную занятость:

  • 2 криптографа / специалиста по безопасности;
  • 2 системных программиста (сетевой стек, P2P);
  • 2 мобильных разработчика (iOS + Android);
  • 1 десктоп-разработчик (Linux, macOS, Windows);
  • 1 специалист по UX/UI.

Сроки по этапам:

  • криптографическое ядро и протокол: 4–6 месяцев;
  • P2P-сеть с DHT и relay: 4–6 месяцев;
  • антиблокировочный слой (подключаемые транспорты): 3–4 месяца;
  • десктоп-клиенты (3 платформы): 6–8 месяцев;
  • мобильные клиенты (iOS + Android): 8–10 месяцев;
  • голосовые и видеозвонки: 4–6 месяцев;
  • внешний аудит безопасности: 2–3 месяца;
  • бета-тестирование и стабилизация: 4–6 месяцев.

Многое можно делать параллельно. Реалистичный срок до публичного релиза: 2–3 года.

Бюджет: при средней зарплате $8–12K/месяц на человека (учитывая уровень экспертизы) — $1,5–3 млн на разработку. Плюс $200–500K на аудиты (минимум два независимых). Плюс инфраструктура, юридические вопросы, поддержка.

Итого: $2–4 млн и 2–3 года для MVP с полным набором функций и профессиональным аудитом.

Это не космические деньги. Signal Foundation тратит $50 млн в год. Wire привлёк $100 млн инвестиций. Для правильно спроектированного мессенджера без legacy-багажа и с чёткой архитектурой — вполне подъёмная сумма.

Главный вопрос не в деньгах. Главный вопрос — найти людей, которые понимают, зачем это нужно, и готовы делать правильно, а не быстро.


Пока это только мечта. Но иногда мечты становятся кодом. А код иногда меняет мир.