Доказательство · открытый код
Не верьте на слово — проверьте
Любой мессенджер говорит «мы не читаем ваши сообщения». Мы решили не просить верить, а дать проверить: код открыт, тесты запускает любой, звонок можно перехватить Wireshark и убедиться самому.
Коротко
- Каждое сообщение запечатывается на телефоне отправителя сразу двумя алгоритмами: X25519 и постквантовым ML-KEM-1024 (Kyber). Чтобы прочитать, нужно взломать оба.
- Сервер получает адрес ящика и шифротекст. Ни текста, ни отправителя, ни получателя в нём нет.
- Звук и видео шифруются DTLS-SRTP между двумя телефонами. Ретранслятор пересылает пакеты, которые не может открыть.
- Всё это проверяется: код на GitHub, 7 тестов запускаются одной командой, звонок проверяется в Wireshark.
Сообщения: как запечатывается каждое

Текст сначала шифруется сессией Double Ratchet — у каждой переписки свои ключи, и они меняются с каждым сообщением. Потом вместе с данными отправителя он упаковывается во внутренний пакет — так отправитель оказывается внутри шифра.
Этот пакет запечатывается получателю: на каждое сообщение создаётся новый одноразовый ключ X25519 и делается инкапсуляция ML-KEM-1024. Оба секрета сводятся в один ключ через HKDF-SHA256, а пакет закрывается AES-256-GCM. Результат дополняется до фиксированного размера, чтобы длина не выдавала содержимое.
Закрытые ключи получателя создаются на его телефоне и не покидают его. Без них конверт не открыть — ни нам, ни хостингу, ни тому, кто скопирует базу сервера.
fn seal(to: &Recipient, plaintext: &[u8]) -> Sealed {
let ephemeral_private = random::<32>();
let ephemeral_public = x25519_public_key(ephemeral_private.clone()).unwrap();
let ss1 = x25519_shared_secret(ephemeral_private, to.x25519_public.clone()).unwrap();
let enc = mlkem1024_encapsulate(to.kyber_public.clone()).unwrap();
let iv = random::<12>();
let box_ = aes256_gcm_encrypt(key(ss1, enc.shared_secret), iv.clone(), plaintext.to_vec(), vec![]).unwrap();
Sealed { ephemeral_public, kyber_ciphertext: enc.ciphertext, iv, box_ }
}Что видит сервер
Мы специально показываем и то, что сервер видит. Доказательство, которое скрывает неудобное, — это реклама.
7 тестов, которые запускает любой
В репозитории лежит файл tests/proof.rs. Он воспроизводит ту самую печать, которой закрывается каждое сообщение, и проверяет каждое свойство отдельным тестом. Вот главный — постквантовая гарантия:
#[test]
fn breaking_only_x25519_is_not_enough() {
let bob = new_recipient();
let eve = new_recipient();
let sealed = seal(&bob, MESSAGE);
assert_eq!(open(&bob.x25519_private, &eve.kyber_private, &sealed), None);
}Этот тест моделирует будущий квантовый компьютер, который взломал X25519. Даже тогда сообщение не открыть без ключа Kyber, а он устойчив к квантовым атакам. Отдельные тесты проверяют: чужие ключи не подходят, изменение хоть одного бита отклоняется, одинаковый текст дважды даёт несвязанные шифротексты.
Проверьте сами — три команды
Нужен только Rust (rustup.rs). Сборка и тесты идут на Linux, macOS и Windows.
git clone https://github.com/Vahe327/UMBERA
cd UMBERA/core
cargo test --test proofrunning 7 tests
test the_recipient_can_read_the_message ... ok
test the_ciphertext_does_not_contain_the_message ... ok
test anyone_with_other_keys_cannot_read_it ... ok
test breaking_only_x25519_is_not_enough ... ok
test breaking_only_kyber_is_not_enough ... ok
test a_tampered_message_is_rejected ... ok
test every_message_is_sealed_with_fresh_keys ... ok
test result: ok. 7 passed; 0 failedЗвонки: ключи только у двух телефонов

Звук и видео шифруются DTLS-SRTP — это шифрование, встроенное в WebRTC. Рукопожатие DTLS идёт прямо между двумя телефонами, поэтому ключи разговора рождаются на телефонах и никуда не уходят.
Если прямое соединение невозможно, пакеты идут через ретранслятор (TURN). Он не участвует в рукопожатии, ключей не имеет и пересылает только зашифрованные пакеты.
Подслушать можно было бы одним способом: подменить отпечатки DTLS при установке звонка (атака «человек посередине»). Поэтому в Umbera установка звонка шифруется той же схемой, что и сообщения, и подписывается звонящим. Сервер не может ни прочитать отпечатки, ни изменить их, не сломав подпись.
Групповые звонки соединяют участников напрямую друг с другом. Сервера, который смешивает или декодирует звук, нет.
val result = messageEncryption.encryptMessage(
content = data,
recipientId = peerUserId,
recipientPublicKey = peerKeys.first,
recipientX25519PublicKey = peerKeys.second,
type = "signaling",
sequenceNumber = 0
)if (!Secp256k1.verify(ciphertext, sig, senderPublicKey)) {
throw SecurityException("Invalid message signature")
}Проверка звонка в Wireshark
- Подключите телефон к Wi-Fi, который вы контролируете, и запишите его трафик Wireshark — например, на ноутбуке, раздающем интернет.
- Сделайте звонок в Umbera.
- Поставьте фильтр
dtls || rtp || srtp. - Вы увидите рукопожатие DTLS и дальше только зашифрованные пакеты SRTP. Ни звука, ни видео Wireshark из них не достанет, открытого RTP нет.
Честные допущения
Доказательство честно ровно настолько, насколько честны его условия. Вот они:
Что опубликовано
Криптографическое ядро на Rust (те же библиотеки RustCrypto, dalek и OpenMLS, что и в приложениях) и протокольный слой на Kotlin — в том виде, в каком он работает в приложениях Android и iOS. Плюс полная спецификация протокола.
Сервер и интерфейс приложения в репозиторий не входят. Сервер получает только то, что этот код уже зашифровал, поэтому для проверки утверждений он не нужен.
Лицензия PolyForm Strict 1.0.0: читать, собирать, запускать тесты и проверять может любой.
Нашли уязвимость?
Если вы сможете опровергнуть хотя бы одно утверждение, мы хотим узнать об этом первыми. Как сообщить — в SECURITY.md репозитория. Отвечаем на каждое сообщение.
Приватность, которую можно проверить
Без номера телефона и почты. Постквантовое шифрование каждого сообщения.