Большинство руководств по шифрованию сводятся к совету «используй AES-256-GCM». Но в mesh-сети угрозы иные: ключи постоянно меняются, сообщения приходят не по порядку, а некоторые узлы могут быть скомпрометированы. Нужен AEAD-шифр, который быстр, портативен и хорошо сочетается с выводом ключей.
GhostWire использует XChaCha20-Poly1305 в качестве основного шифра. Вот почему и как это работает на практике.
Почему не AES-256-GCM?
AES быстр на устройствах с аппаратным ускорением AES-NI. Но GhostWire работает на ESP32, Raspberry Pi, старых ноутбуках и телефонах — не везде есть аппаратный AES.
ChaCha20-Poly1305:
- Постоянное время выполнения на всех платформах — нет уязвимостей по сторонним каналам.
- Быстр в программной реализации — часто обгоняет AES на устройствах без AES-NI.
- Проще реализация — меньше шансов ошибиться.
XChaCha20-Poly1305 расширяет стандартный вариант, используя 192-битный nonce (против 96 бит у обычного ChaCha20-Poly1305). Это критично для mesh-сетей, где на одном ключе могут шифроваться миллионы сообщений — 96-битный nonce исчерпался бы после ~2^32 сообщений.
Процесс шифрования
Вот как происходит шифрование сообщения в GhostWire:
use chacha20poly1305::{XChaCha20Poly1305, KeyInit, aead::Aead}; use chacha20poly1305::aead::OsRng; use x25519_dalek::{EphemeralSecret, PublicKey}; // 1. Обмен ключами (X25519 ECDH) let secret = EphemeralSecret::random_from_rng(OsRng); let public = PublicKey::from(&secret); // 2. Вычисление общего секрета let shared = secret.diffie_hellman(&peer_public); // 3. Вывод ключа шифрования из общего секрета let key = hkdf_expand(shared.as_bytes(), b"ghostwire-v1"); // 4. Шифрование XChaCha20-Poly1305 let cipher = XChaCha20Poly1305::new(key.into()); let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng); let ciphertext = cipher.encrypt(&nonce, plaintext)?; // 5. Отправка: nonce || ciphertext || tagNonce генерируется случайно (не последовательно), так как в mesh-сети сообщения могут приходить не по порядку. При 192-битном случайном nonce вероятность коллизии пренебрежимо мала даже при миллионах сообщений.
Прямая секретность с помощью Ratchet
Статические ключи — уязвимость: если узел скомпрометирован, все прошлые сообщения раскрыты. GhostWire реализует паттерн Double Ratchet (вдохновлён Signal) для сессионных ключей:
Session Key = HKDF(previous_key, message_number)Каждое сообщение выводит новый ключ из предыдущего. Компрометация текущего ключа не раскрывает прошлые сообщения — предыдущие ключи удаляются после использования.
struct RatchetState { root_key: [u8; 32], chain_key: [u8; 32], message_number: u32, } impl RatchetState { fn next_message_key(&mut self) -> [u8; 32] { let key = hkdf_expand(&self.chain_key, &self.message_number.to_le_bytes()); self.chain_key = hkdf_expand(&self.chain_key, b"chain"); self.message_number += 1; key } fn advance_chain(&mut self, new_chain_key: [u8; 32]) { self.root_key = hkdf_expand(&self.root_key, &new_chain_key); self.chain_key = new_chain_key; } }Это обеспечивает прямую секретность (прошлые сообщения в безопасности) и секретность будущих (компрометация одного ключа не раскрывает будущие, если происходит шаг ratchet).
Постквантовый гибрид
XChaCha20-Poly1305 устойчив к квантовым атакам (не основан на факторизации или дискретном логарифме). Но обмен ключами X25519 — нет: квантовый компьютер сможет вычислить общий секрет.
GhostWire решает это с помощью гибридного обмена ключами:
shared_secret = X25519(my_secret, peer_public) || ML-KEM-768(my_secret, peer_public)Оба общих секрета (классический и постквантовый) объединяются и подаются на вход HKDF. Злоумышленнику нужно взломать оба, чтобы получить ключ. Если ML-KEM-768 падёт под квантовыми вычислениями, X25519 всё ещё защищает (и наоборот).
fn hybrid_shared_secret( x25519_secret: &EphemeralSecret, x25519_public: &PublicKey, mlkem_ciphertext: &[u8], ) -> [u8; 64] { let classical = x25519_secret.diffie_hellman(x25519_public); let pq = mlkem_decaps(mlkem_secret, mlkem_ciphertext); let mut combined = [0u8; 64]; combined[..32].copy_from_slice(classical.as_bytes()); combined[32..].copy_from_slice(&pq); combined }Производительность на ограниченных устройствах
Мы провели замеры на ESP32-S3 (240 МГц, без AES-NI):
| Операция | XChaCha20-Poly1305 | AES-256-GCM |
|---|---|---|
| Шифрование 1 КБ | 12 мкс | 18 мкс |
| Шифрование 64 КБ | 0,8 мс | 1,1 мс |
| Вывод ключа | 45 мкс | 45 мкс |
ChaCha20 оказался примерно на 33% быстрее на этой платформе. На x86 с AES-NI выигрывает AES, но GhostWire не ориентирован на дата-центры.
Что дальше
В следующей статье мы рассмотрим луковую маршрутизацию Sphinx — как GhostWire скрывает метаданные сообщений и личность отправителя с помощью многослойного шифрования.
GitHub: github.com/Phantomojo/GhostWire-secure-mesh-communication
Сайт: ghostwire.cc
Создано в Найроби, под эгидой RVC.
Комментарии (0)
Войдите, чтобы комментировать.
Пока нет комментариев. Будьте первым.