Парольная политика (ADR-013) и привязка экспорта к аккаунту (ADR-014)
ADR-013: PBKDF2-HMAC-SHA256 ≥600k итераций (цель 1M), параметры KDF версионируются рядом с блобом; пароль от 12 символов, длина важнее состава. ADR-014: парольная фраза экспорта отменена, ключ экспорта — HKDF от 32-байтового секрета аккаунта из ключевого блоба; архив .bare криптографически бесполезен вне аккаунта-владельца. Перекрёстно обновлены ADR-006, ADR-010, architecture, threat-model; в открытые вопросы добавлен серверный «перец» для блоба. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SW4ZXtKn5NjX8NH3Lhm2ND
This commit is contained in:
@@ -6,11 +6,11 @@
|
||||
|
||||
## Решение
|
||||
|
||||
Только WebCrypto. Идентичность пользователя — ECDH-пара P-256. Приватный ключ шифруется ключом, выведенным из пароля (PBKDF2 — единственный KDF в WebCrypto), и хранится на сервере как блоб. Чаты 1:1: ECDH shared secret → AES-GCM.
|
||||
Только WebCrypto. Идентичность пользователя — ECDH-пара P-256. Приватный ключ шифруется ключом, выведенным из пароля (PBKDF2 — единственный KDF в WebCrypto; параметры и парольная политика — ADR-013), и хранится на сервере как блоб. Рядом с приватным ключом в блобе — секрет аккаунта для экспорта истории (ADR-014). Чаты 1:1: ECDH shared secret → AES-GCM.
|
||||
|
||||
## Следствия
|
||||
|
||||
- Сервер видит только шифротекст — и блоба, и сообщений.
|
||||
- Сброс пароля невозможен by design: пароль — материал ключа. Забыл — значит забыл.
|
||||
- Мультидевайс-вход: новый девайс вводит пароль, скачивает блоб, расшифровывает ключ.
|
||||
- Стойкость блоба к оффлайн-перебору равна стойкости пароля.
|
||||
- Стойкость блоба к оффлайн-перебору равна стойкости пароля. Контрмеры — ADR-013.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# ADR-010: Перенос истории — только ручной экспорт/импорт
|
||||
|
||||
Частично заменён [ADR-014](014-export-account-binding.md): парольная фраза экспорта отменена, ключ экспорта выводится из секрета аккаунта. Принцип «перенос — только ручной экспорт/импорт» в силе.
|
||||
|
||||
## Контекст
|
||||
|
||||
История живёт на устройстве (ADR-009), но людям нужны перенос на новое устройство и склейка истории с нескольких. Облачная синхронизация сделала бы сервер архивом — non-goal.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# ADR-013: Парольная политика и параметры KDF
|
||||
|
||||
## Контекст
|
||||
|
||||
Ключевой блоб лежит на сервере, и его стойкость к оффлайн-перебору равна стойкости пароля (ADR-006). Раз пароль — материал ключа, его качество и цена перебора — не UX-детали, а параметры криптосистемы.
|
||||
|
||||
## Решение
|
||||
|
||||
- KDF: PBKDF2-HMAC-SHA256, не менее 600 000 итераций, целевое значение — 1 000 000.
|
||||
- Число итераций (поле версии KDF) хранится рядом с блобом; при входе клиент читает параметры из блоба. Так итерации можно повышать в будущем без миграции всех аккаунтов разом.
|
||||
- Минимальная длина пароля — 12 символов. Правил про регистры и спецсимволы нет: длина важнее состава. UI рекомендует парольную фразу из нескольких слов.
|
||||
- UI при регистрации явно проговаривает: пароль — это ключ шифрования, а не запись в базе; восстановления нет.
|
||||
|
||||
## Следствия
|
||||
|
||||
- Гарантия «оператор не читает сообщения» действует в пределах стойкости пароля пользователя; слабый пароль — слабое E2EE. Это осознанная цена парольного мультидевайса, зафиксировано в threat-model.
|
||||
- Перебор дорожает на порядки, но не отменяется: словарный пароль умирает при любых итерациях.
|
||||
- Параметры повышаются со временем: новые аккаунты получают их сразу, существующие — перешифровкой блоба при очередном входе.
|
||||
@@ -0,0 +1,23 @@
|
||||
# ADR-014: Экспорт привязан к аккаунту криптографически
|
||||
|
||||
Заменяет часть [ADR-010](010-export-import.md): парольной фразы экспорта больше нет. Принцип «перенос истории — только ручной экспорт/импорт» в силе.
|
||||
|
||||
## Контекст
|
||||
|
||||
Экспортный файл, зашифрованный отдельной парольной фразой, переносим кем угодно: файл плюс фраза — и чужой клиент импортирует чужую историю. Требование: архив должен быть бесполезен вне аккаунта-владельца. Проверка на уровне «клиент сверяет ник» неприемлема — клиент открытый, такая проверка тривиально выпиливается. Запрет должен быть криптографическим.
|
||||
|
||||
## Решение
|
||||
|
||||
- В ключевом блобе аккаунта, рядом с приватным ECDH-ключом, хранится случайный 32-байтовый секрет аккаунта. Он генерируется при регистрации, доступен любому устройству, вошедшему в аккаунт, и никогда не существует в открытом виде вне клиента.
|
||||
- Ключ конкретного экспорта: HKDF(секрет аккаунта, salt, info="bare-export-v1"), salt случайный на каждый экспорт. Шифрование — AES-GCM. Всё на WebCrypto: HKDF там есть нативно.
|
||||
- Отдельной парольной фразы для экспорта больше нет — сущность удалена.
|
||||
- Формат `.bare`: заголовок открытым текстом — magic, версия формата, salt, отпечаток публичного ключа владельца; далее шифротекст. Ник владельца в заголовок не пишется — лишняя утечка.
|
||||
- Импорт: клиент сверяет отпечаток со своим; при несовпадении показывает «архив создан другим аккаунтом» и не пытается расшифровать. Сверка — только UX-вежливость: реальная защита в том, что у чужого клиента нет секрета аккаунта и расшифровка невозможна в принципе.
|
||||
- Импорт по-прежнему идемпотентный merge по messageId.
|
||||
|
||||
## Следствия
|
||||
|
||||
- Архив открывается только устройствами, вошедшими в аккаунт-владелец. Потеря пароля — потеря ключей — архив нечитаем навсегда. Это последовательное продолжение принципа «забыл — значит забыл», а не новое ограничение.
|
||||
- Смена ключевой пары (например, перерегистрация под тем же ником) делает старые архивы нечитаемыми: секрет аккаунта другой.
|
||||
- Привязка защищает файл, а не содержимое от самого владельца: скриншоты и модифицированный клиент никуда не деваются, а у собеседников есть собственные копии тех же сообщений. «Аналоговая дыра» криптографией не закрывается — зафиксировано в threat-model.
|
||||
- Побочный плюс: исключено загрязнение чужого клиента историей чатов, в которых пользователь не участник.
|
||||
Reference in New Issue
Block a user