Собрал Chrome-расширение, которое шифрует сообщения так, что они выглядят как обычный текст на естественном языке. А потом заметил: seed-фразы криптокошельков используют ровно тот же приём «байты → слова». Мысли о том, что если их совместить.
Если есть система, в которой никто не может прочитать сообщение, и есть система генерации ключей от кошелька — почему бы их не совместить?
Сделал за один вечер расширение для Chrome — StealthChat. Идея простая: шифруешь сообщение перед отправкой в любом веб-мессенджере, но вместо нечитаемой абракадабры (U2FsdGVkX1+3xK9...) собеседник и платформа видят обычное предложение на естественном языке. Не «зашифрованный текст с маркерами», а буквально предложение, которое ничем не выделяется в переписке.
Дальше — уже вдогонку, во время написания кодировщика байтов в слова — пришла мысль: а ведь ровно этим же трюком пользуются криптокошельки для seed-фраз. Только они это не прячут, а наоборот — выставляют напоказ. Об этом пост.
Технически — расширение под Manifest V3, без единой внешней зависимости, весь крипто — через встроенный Web Crypto API.
Пайплайн отправки сообщения:
"Meet me at 5 near the subway"
│
▼
Compress deflate-raw, только если реально сжимает
│
▼
Encrypt AES-256-GCM, случайный 12-байтный IV
│
▼
Package протокол v2: версия + флаги + session ID + IV + шифротекст
│
▼
Encode байты → предложения (7 языков)
│
▼
"Alice bravely arranged several ancient anchor despite morning.
Quinn calmly blocked both bitter beacon during evening."
Ключевая часть — последний шаг. Байты пакуются в поток по 5 бит, и каждая 5-битная группа (0–31) выбирает слово из категории на 32 слова. Шаблон предложения фиксированный: Имя наречие глагол артикль прилагательное существительное предлог время. — восемь слов, 40 бит, 5 байт данных за одно предложение. Поддерживается 7 языков (en, ru, uk, de, be, fa, kk), декодер сам определяет язык по первому слову.
Обмен ключами — ECDH P-256: каждая сторона генерирует пару ключей, кодирует публичный ключ в те же 14 предложений (не в base64!) и отправляет их прямо в чат. Общий секрет считается через ECDH, AES-ключ выводится через HKDF-SHA256. Плюс ротация ключей каждые 50 сообщений для forward secrecy и fingerprint-верификация 4×4 hex-сеткой — чтобы проверить, что ключ никто не подменил по дороге.
Платформа-мессенджер в итоге видит только обычные предложения. Ни маркеров, ни подозрительных символов, ни очевидного «это шифротекст».
Механизм «байты → человекочитаемые слова по словарю» — не моё изобретение, это классическая идея. И самое массовое её применение сегодня — BIP39, стандарт, по которому почти все криптокошельки генерируют seed-фразу из 12 или 24 слов.
Оба механизма решают одну и ту же задачу — превратить случайные байты энтропии в форму, которую можно записать, прочитать вслух, скопировать вручную — но решают её противоположными способами:
| StealthChat | BIP39 (seed-фразы) | |
|---|---|---|
| Что кодируется | зашифрованный текст / публичный ключ | энтропия для приватного ключа |
| Бит на слово | 5 бит | 11 бит |
| Размер словаря | 32 слова на категорию (8 категорий) | 2048 слов (один список) |
| Структура | грамматическое предложение по шаблону | набор слов без связи между собой |
| Цель маскировки | выглядеть как обычная переписка | распознаваться как seed-фраза (для импорта в кошелёк) |
| Целевая аудитория | никто не должен заподозрить, что это данные | владелец должен точно знать, что это ключ |
Вот тут и разница в философии. StealthChat специально делает результат неотличимым от обычного текста — это весь смысл. А BIP39-фраза, наоборот, обязана быть узнаваемой: 12–24 слова подряд без знаков препинания — это то, что сразу считывается человеком (и, что важнее, вредоносным ПО) как «здесь seed-фраза». Скрин-скраперы, кейлоггеры и малварь, которая сканирует фото в галерее на телефоне на предмет 12/24-словных комбинаций — существуют именно потому, что формат легко детектируется.
Отсюда и мысль: а что если взять маскировку из одной системы и применить к другой?
1. Seed-фраза, которая выглядит как обычное предложение.
Взять энтропию BIP39 (те самые 128/256 бит) и прогнать через кодировщик в духе StealthChat — грамматический шаблон вместо голого списка слов. Вместо abandon ability able about above absent absorb... получаем что-то в духе «Алиса твердо описал этих древний бухта вместо февраль» — предложение, которое можно хранить в заметках, отправить себе в чат или буквально приклеить стикером на холодильник, и оно не будет триггерить ни один автоматический сканер seed-фраз, потому что структурно не похоже на список из словаря BIP39.
2. Передача бэкапа кошелька через сам канал StealthChat. Раз в расширении уже есть end-to-end шифрованный канал, который маскирует данные под обычный текст — почему бы не использовать его, чтобы переслать зашифрованный бэкап seed-фразы себе на второе устройство или доверенному контакту? Платформа-мессенджер видит очередное «обычное сообщение», а не кусок крайне чувствительных данных.
3. Переиспользовать уже написанный крипто-пайплайн под HD-деривацию ключей. В StealthChat уже есть ECDH + HKDF-SHA256 для вывода ключей — концептуально это близко к тому, как HD-кошельки (BIP32) выводят дочерние ключи из мастер-ключа по пути деривации. Готовый код для генерации и деривации ключей есть, осталось скрестить с логикой path-based деривации.
Чтобы не пересказывать это как готовое решение — сразу проблемные места:
crypto.js и навесить на него генерацию адресов не получится.StealthChat как есть — рабочее расширение: шифрует, кодирует в естественный текст на 7 языках, поддерживает ECDH-обмен ключами и ротацию для forward secrecy. Исходники здесь: github.com/BazZziliuS/StealthChat.
А идея скрестить его с генерацией ключей кошелька — пока именно идея, а не фича. Но совпадение в том, что обе системы по сути решают одну задачу («упаковать случайные байты в форму, понятную человеку»), просто с противоположными целями — спрятать vs выставить напоказ — кажется мне достаточно интересным, чтобы его записать. Если у кого-то есть мысли, почему это плохая или хорошая идея — welcome в issues репозитория.