Два месяца спустя: как pet-проект на мессенджере превратился в урок архитектурного мышления
Два месяца назад независимый разработчик опубликовал статью о своём open-source проекте - E2EE-мессенджере Chaos. Никто не ожидал, что материал наделает столько шума. Сейчас, спустя шестьдесят дней активной доработки, проект вырос в полноценную распределённую систему - и заодно наглядно показал, почему Signal создавали годами.
От простого чата к архитектурному ограничению
Задача поначалу казалась понятной. Есть спецификации X3DH и Double Ratchet, есть WebCrypto, есть документация. Собери аккуратно - и готово. Примерно в этот момент, как сам признаёт автор, где-то вдалеке начинает смеяться инженер из Signal.
Главный вывод, к которому пришёл разработчик: E2EE - это не отдельная функция encrypt() и не изолированный криптосервис. Это архитектурное ограничение, которое прорастает через всю систему. Если сервер не должен видеть plaintext, это немедленно затрагивает базу данных, WebSocket, авторизацию, восстановление сессий, preview сообщений и даже то, что отображается в списке чатов. Хочешь решить задачу на серверной стороне - сначала спроси себя: а должен ли сервер вообще знать ответ? Чаще всего - нет.
Double Ratchet: на бумаге красиво, в жизни больнее
Ключевое техническое обновление за эти два месяца - реализация полноценного DH Ratchet. В первой версии проекта была начальная установка сессии через X3DH и симметрическая цепочка ключей, но без DH-составляющей Double Ratchet не работает break-in recovery. Именно на это указали в комментариях - и автор прислушался.
Суть механизма проста на словах. Каждое сообщение использует новый message key, старые ключи постепенно обесцениваются. Когда собеседник отвечает новым DH-ключом, обе стороны пересчитывают root key и получают свежие цепочки. Если атакующий каким-то образом добрался до текущего chain key - после следующего DH-обмена это знание перестаёт ему помогать. Не панацея, но критически важное свойство протокола. Кстати, чем-то это напоминает принцип ротации в игровых системах безопасности - как, например, war prime в Warframe меняет механику доступа к контенту, вынуждая игрока каждый раз проходить новый путь.
На практике же сразу вылезает целый букет неудобных вопросов:
- что делать с out-of-order сообщениями
- сколько skipped message keys безопасно хранить
- как вести себя, если устройство долго было офлайн
- как не сломать multi-device при DH-обмене
- где заканчивается удобство и начинается снижение безопасности
Сервер должен быть «тупее» - и это комплимент
Один из самых показательных эпизодов проекта - история с preview последнего сообщения. В базе поле content выглядит как заглушка. Первая реакция: что-то сломалось. Потом приходит понимание: нет, именно так оно и должно работать.
В обычном мессенджере preview - это простой SQL-запрос. В E2EE-системе сервер хранит только зашифрованный конверт, а клиент сохраняет расшифрованный текст локально после получения. Архитектура становится неудобнее. Зато честнее.
Сегодня проект включает React-фронтенд, Electron-приложение, Spring Boot backend, STOMP/WebSocket, PostgreSQL, Redis, Docker Compose, Prometheus, Grafana, Loki, Caddy и Kubernetes-манифесты. Chaos - не конкурент Signal и не готовый продукт. Это живая лаборатория, в которой любое решение про ключи немедленно отзывается во всей остальной системе. И именно это не даёт проекту надоесть.