Закрыты все открытые вопросы проектирования. Пароль не покидает клиент (два ключа из мастера), TOFU для публичных ключей, устройства и конверт, атомарный rekey комнат, регистрация и контакты, схема SQLite и драйвер без cgo, сессии и CSRF, деплой через nginx+systemd, правила пушей, айдентика «Скобы» на системном mono. Иконки PWA в web/icons. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015DbCjVfTFq4ZFG8juD45YJ
3.7 KiB
3.7 KiB
ADR-015: Пароль не покидает клиент — два ключа из одного мастера
Контекст
ADR-005 и ADR-006 используют один пароль и для серверной аутентификации (Argon2id), и как материал ключа шифрования блоба. Если клиент отправляет пароль на сервер в открытом виде, оператор, логирующий тела запросов, получает материал ключа — и обещание «оператор не читает сообщения» рушится на первом же входе. Кроме того, ADR-013 требует повышать число итераций KDF без миграции всех аккаунтов разом, а смена пароля числится открытым вопросом.
Решение
- Пароль никогда не отправляется на сервер. Клиент выводит мастер-ключ:
master = PBKDF2-HMAC-SHA256(NFC(пароль), salt = SHA-256("bare-v1:" + nick), iterations, 256 бит). Соль детерминированная — известна до входа без запроса к серверу. - Из мастера через HKDF-SHA256 выводятся два независимых ключа:
authKey = HKDF(master, info="bare-auth-v1")— 32 байта, уходит на сервер как «пароль»;kek = HKDF(master, info="bare-kek-v1")— AES-GCM-256, шифрует ключевой блоб и сервер его не видит. - Сервер хранит
argon2id(authKey). Argon2id остаётся (ADR-002, ADR-005): он защищает дамп базы от использованияauthKeyкак готового пароля для входа. - Перед входом клиент спрашивает
GET /api/kdf?nick=и получает число итераций. Для несуществующего ника сервер отвечает текущим целевым значением — ответ не раскрывает существование ника. - Повышение итераций и смена пароля — одна и та же операция
POST /api/password: клиент, имея пароль в памяти, выводит новыйauthKey, перешифровывает блоб новымkekи отправляет оба вместе со старымauthKeyдля подтверждения. Сервер заменяет хеш и блоб атомарно. Смена пароля входит в v1. - Смена пароля по желанию пользователя завершает остальные сессии (
logoutOthers: true); автоматическое повышение итераций — нет.
Следствия
- Пассивный оператор не получает материал ключа ни при регистрации, ни при входе. Модель угроз становится честной.
- Соль из ника означает, что перерегистрация под тем же ником с тем же паролем даёт тот же мастер. Ключевая пара при этом новая — старые архивы нечитаемы (ADR-014), мастер это не спасает.
- PBKDF2 выполняется один раз на вход; на слабом телефоне 1 000 000 итераций — до нескольких секунд. UI показывает «вычисляем ключ».
- Вопрос «смена пароля в v1 или позже» закрыт: в v1.