Безопасность
Безопасность в csend построена на одной простой идее: кодовая фраза — это единственный секрет, и она никогда не передаётся по сети. Даже если кто-то слушает весь трафик в Wi-Fi сети, он не сможет прочитать ваши файлы.
Что выводится из кодовой фразы
Из одной фразы вроде 3-royal-mount-dance программа вычисляет две криптографические производные и сохраняет саму фразу для отображения:
| Что | Функция | Тип | Назначение |
|---|---|---|---|
| Room ID | code_to_room_id() | Криптографическая производная (BLAKE3 hash) | Проверка фразы при P2P-рукопожатии (Handshake) |
| Encryption Key | code_to_encryption_key() | Криптографическая производная (BLAKE3 KDF) | Шифровать и расшифровывать файлы |
| Сама фраза | — | Оригинал (без преобразования) | Показать отправителю, чтобы он сказал её получателю |
Все три значения не передаются по сети: Room ID используется только в P2P Handshake (внутри Noise-зашифрованного канала), Encryption Key вообще не покидает устройство, а фраза показывается только на экране отправителя.
Генерация кодовой фразы
Фраза генерируется случайно при каждой отправке:
формат: {цифра}-{слово}-{слово}-{слово}
пример: 7-castle-river-bloom| Часть | Диапазон | Зачем |
|---|---|---|
| Цифра | 1–9 | Быстрая визуальная проверка |
| Слова | 256 вариантов каждое | Обеспечивают случайность |
Количество комбинаций: 9 × 256 × 256 × 256 = 150 994 944 (больше 150 миллионов). Угадать фразу перебором за разумное время невозможно.
Словарь содержит 256 простых английских слов из разных категорий (животные, цвета, погода...), чтобы их было легко произнести и запомнить: apple, brave, castle, dream...
Хеширование — BLAKE3
BLAKE3 — это криптографическая хеш-функция. Она превращает любые данные в строку фиксированной длины. Представь мясорубку: что бы ты ни положил внутрь, на выходе всегда фарш. А из фарша невозможно собрать обратно кусок мяса.
Room ID
pub fn code_to_room_id(code: &str) -> String {
// 1. Хешируем фразу → 32 байта
let hash = blake3::hash(code.as_bytes());
// 2. Берём первые 16 hex-символов
hash.to_hex()[..16].to_string()
}Пример: "3-royal-mount-dance" → "a3f8b2c1e9d04567"
Room ID не передаётся в Discovery — обнаружение работает без него. Room ID используется только в P2P-рукопожатии (Handshake { code_hash }): получатель отправляет хеш введённой фразы, а отправитель сверяет его со своим. Так оба убеждаются, что знают одну и ту же фразу.
Encryption Key (KDF)
pub fn code_to_encryption_key(code: &str) -> [u8; 32] {
blake3::derive_key("kontinuum-send-encryption-v1", code.as_bytes())
}KDF (Key Derivation Function) — специальный режим BLAKE3, который выводит ключ из пароля. Контекстная строка "kontinuum-send-encryption-v1" гарантирует, что даже одинаковые фразы в разных приложениях дадут разные ключи.
Почему два разных вывода? Room ID и Encryption Key используют разные алгоритмы BLAKE3 (
hashvsderive_key), поэтому знание Room ID не помогает узнать Encryption Key.
Шифрование файлов — ChaCha20Poly1305
ChaCha20Poly1305 — это AEAD-шифр (Authenticated Encryption with Associated Data). Он одновременно:
- Шифрует данные (никто не прочитает без ключа)
- Аутентифицирует данные (никто не подменит без ключа)
Nonce — уникальный номер для каждого чанка
Шифр ChaCha20 требует nonce (number used once) — уникальное число для каждого шифрования. Если использовать один и тот же nonce дважды с одним ключом, шифрование ломается.
В csend nonce детерминированный — вычисляется из позиции чанка:
nonce (12 байт) = file_index (4 байта LE) || offset (8 байт LE)Почему это безопасно?
- Каждый чанк имеет уникальную пару
(file_index, offset) - Значит, каждый nonce уникален в рамках одной передачи
- Ключ тоже уникален (выводится из случайной фразы)
Риск повторного использования фразы: Если одну и ту же кодовую фразу использовать для двух разных передач, ключ и nonce'ы совпадут для чанков с одинаковыми позициями. Это теоретически ослабляет шифрование (XOR двух шифротекстов = XOR двух открытых текстов). На практике это маловероятно, потому что фраза генерируется случайно при каждой отправке и используется один раз.
Размер данных после шифрования
Каждый зашифрованный чанк на 16 байт больше оригинала. Эти 16 байт — аутентификационный тег (Poly1305 MAC). Тег позволяет получателю проверить, что данные не были изменены.
Исходный чанк: 256 КиБ = 262 144 байт
Зашифрованный: 262 144 + 16 = 262 160 байтМодель угроз
Что произойдёт при разных атаках:
| Угроза | Защита |
|---|---|
| Перехват трафика | Два уровня: Noise шифрует канал (заголовки, метаданные), ChaCha20Poly1305 шифрует файлы |
| Подмена данных | Poly1305 аутентификация (тег не совпадёт → ошибка) |
| Угадывание фразы | 150+ млн комбинаций, онлайн-проверка медленная (~1 сек на попытку) |
| Повтор чанка (replay) | Детерминированный nonce + идемпотентная запись: повторный чанк перезапишет те же байты |
| Утечка кодовой фразы | Ни фраза, ни её хеш не передаются в Discovery |
| Утечка списка файлов | Имена и размеры видны в Discovery (трейдофф: удобство vs приватность) |
Два уровня шифрования: libp2p Noise шифрует весь транспорт — пассивный наблюдатель не видит даже типы сообщений (Handshake, Chunk и т.д.). Однако Discovery-сообщения (имена файлов, размеры) отправляются всем пирам в сети как часть протокола обнаружения, поэтому любой участник сети с
csendих получит. ChaCha20 — второй барьер: даже если Noise будет скомпрометирован, содержимое файлов останется защищённым.
Что НЕ защищено
Честность важна — вот что csend не скрывает:
- Имена и размеры файлов — передаются открыто в Discovery и Offer
- Имя устройства — видно всем в сети
- Факт передачи — наблюдатель знает, что файлы передаются
- Размер трафика — по объёму данных можно оценить, сколько файлов передано
Это осознанный компромисс: полная анонимность усложнила бы обнаружение устройств.