Skip to content

VaultManager

Файл: backend/src/vault/manager.rs

Криптографическое хранилище секретов с PIN-авторизацией. Обеспечивает шифрование/дешифровку через ChaCha20Poly1305, derive ключей через Argon2, rate limiting попыток PIN и activity-based автоблокировку.

ValidatedPin

PIN валидируется на границе (в методах setup_with_pin, unlock_with_pin, change_pin) через newtype ValidatedPin (файл: vault/pin_encryption.rs):

rust
pub struct ValidatedPin(Zeroizing<String>);

impl ValidatedPin {
    pub fn new(pin: &str) -> Result<Self> {
        // Ровно 4 ASCII-цифры, иначе ошибка
    }
    pub fn as_str(&self) -> &str { ... }
}
  • Конструируется один раз при входе в boundary-метод
  • Передаётся по ссылке (&ValidatedPin) во все внутренние функции (derive_key, encrypt, decrypt, wrap_master_key_with_pin, unwrap_master_key_with_pin)
  • Содержимое автоматически зануляется при drop (Zeroizing)

Структура

ПолеТипОписание
dbArc<Mutex<Connection>>SQLite соединение
master_keyArc<Mutex<Option<Zeroizing<[u8; 32]>>>>Master key (только когда разблокирован)
pin_encryptionPinEncryptionRate limiting и lockout PIN
session_stateArc<Mutex<SessionState>>Состояние сессии (auto-lock, background op mode)
event_busOption<EventBus>Для событий vault_locked и vault_unlocked

Методы

МетодСигнатураОписание
Setup
newfn(db) -> Result<Self>Инициализация + создание таблиц
set_event_busfn(&mut self, bus)Установить event bus
is_setupfn(&self) -> Result<bool>Настроен ли vault (есть vault_config)
setup_with_pinfn(&mut self, pin) -> Result<()>Быстрая настройка без recovery
setup_with_recovery_phrasefn(&mut self, phrase, pin) -> Result<()>Полная настройка
Lock/Unlock
unlock_with_pinfn(&mut self, pin) -> Result<UnlockResult>Разблокировка по PIN
unlock_with_recovery_phrasefn(&mut self, phrase) -> Result<()>Разблокировка по фразе
is_unlockedfn(&self) -> boolЧистая проверка (без side effects)
lockfn(&mut self) -> Result<()>Заблокировать (zeroize master key)
PIN
change_pinfn(&mut self, new_pin) -> Result<()>Сменить PIN
get_pin_statefn(&self) -> Result<PinState>Состояние попыток
reset_pin_statefn(&mut self) -> Result<()>Сбросить попытки
Secrets
save_secretfn(&self, id, data) -> Result<()>Зашифровать и сохранить
load_secretfn(&self, id) -> Result<Zeroizing<Vec<u8>>>Расшифровать и загрузить
has_secretfn(&self, id) -> boolПроверить наличие
delete_secretfn(&self, id) -> Result<()>Удалить секрет
list_secretsfn(&self) -> Result<Vec<String>>Список ID секретов
Recovery
save_encrypted_recovery_phrasefn(&self, words) -> Result<()>Зашифровать и сохранить фразу
has_encrypted_recovery_phrasefn(&self) -> boolЕсть ли фраза
load_recovery_phrasefn(&self) -> Result<Zeroizing<String>>Расшифровать фразу
delete_recovery_phrasefn(&self) -> Result<()>Удалить фразу
Session
touch_activityfn(&self)Обновить timestamp (с guard на locked)
check_and_lock_if_expiredfn(&self) -> boolПроверить таймаут и заблокировать
set_auto_lock_timeoutfn(&self, seconds)Таймаут автоблокировки (persist в БД)
get_auto_lock_timeoutfn(&self) -> u64Получить текущий таймаут
Background Operations
set_background_operation_modefn(&self, mode)Режим фоновых операций (persist в БД)
get_background_operation_modefn(&self) -> BackgroundOperationModeПолучить текущий режим
issue_scoped_keyfn(&self, secret_id) -> Result<ScopedKey>Выдать производный ключ для фоновой задачи
Admin
resetfn(&mut self) -> Result<()>Полный сброс vault

Зависимости

  • ChaCha20Poly1305 — симметричное шифрование секретов
  • Argon2 — derive ключей из PIN и recovery phrase
  • SQLite — таблицы vault_config, encrypted_secrets, vault_pin_state, vault_settings

setup_with_recovery_phrase

fn setup_with_recovery_phrase(&mut self, phrase: &RecoveryPhrase, pin: &str) -> Result<()>

Полная настройка vault: генерирует master key из recovery phrase через Argon2, оборачивает его PIN'ом.

Логика

  1. Derive master key из recovery phrase (Argon2id, random salt)
  2. Генерирует random nonce для ChaCha20Poly1305
  3. Derive PIN key из PIN (Argon2id, другой salt)
  4. Шифрует master key PIN key'ом (ChaCha20Poly1305)
  5. Сохраняет в БД: salt, nonce, wrapped_master_key
  6. Кэширует master key в памяти (vault разблокирован)
plantuml Diagram

Используется в: setup_vault_with_recovery_svc, setup_pin_svc


unlock_with_pin

fn unlock_with_pin(&mut self, pin: &str) -> Result<UnlockResult>

Разблокирует vault по PIN. Реализует rate limiting: после нескольких неудачных попыток — временная блокировка.

Возвращает

UnlockResult { success: bool, remaining_attempts: u32 }

Логика

  1. Проверяет lockout (не заблокирован ли по времени)
  2. Загружает vault_config из БД (salt, nonce, wrapped_master_key)
  3. Derive pin_key из PIN через Argon2
  4. Decrypt wrapped_master_key с pin_key
  5. Если успешно — кэширует master_key, сбрасывает failed_attempts
  6. Если ошибка — инкрементирует failed_attempts, проверяет lockout
plantuml Diagram

Используется в: unlock_vault_with_pin_svc, reset_pin_state_svc, reset_vault_svc


lock

fn lock(&mut self) -> Result<()>

Блокирует vault: зануляет master key в памяти, эмитит событие vault_locked.

Логика

  1. self.master_key = None (Zeroizing автоматически зануляет память)
  2. Эмитит vault_locked через event bus

Используется в: lock_vault_svc


is_unlocked

fn is_unlocked(&self) -> bool

Чистая проверка состояния: возвращает true если master key в памяти. Без побочных эффектов, без проверки таймаута. Можно вызывать из любого места.

Используется в: is_unlocked_svc, get_vault_status_svc, vault_heartbeat_svc


save_secret / load_secret

fn save_secret(&self, secret_id: &str, data: &[u8]) -> Result<()>fn load_secret(&self, secret_id: &str) -> Result<Zeroizing<Vec<u8>>>

Шифрование/дешифровка секретов с master key. Каждый секрет имеет индивидуальные salt и nonce. Операции атомарно захватывают master_key lock — нет TOCTOU и нет touch_activity() (операции с секретами не продлевают сессию).

save_secret

  1. Атомарно захватывает master_key lock (или ошибка "Vault is locked")
  2. Генерирует random salt + nonce
  3. Encrypt data с master_key + nonce (ChaCha20Poly1305)
  4. INSERT/REPLACE INTO encrypted_secrets

load_secret

  1. SELECT FROM encrypted_secrets WHERE secret_id = ?
  2. Атомарно захватывает master_key lock (или ошибка "Vault is locked")
  3. Decrypt encrypted_data
plantuml Diagram

Используется в: setup_pin_svc, sign_data_svc, внутренне при паринге


save_encrypted_recovery_phrase / load_recovery_phrase

Управление recovery phrase. Фраза шифруется master key'ом и хранится в vault_config.

  • save_encrypted_recovery_phrase — шифрует фразу и сохраняет в vault_config
  • load_recovery_phrase — расшифровывает и возвращает (vault должен быть unlocked)
  • delete_recovery_phrase — удаляет после подтверждения бэкапа

Используется в: setup_pin_svc, get_recovery_phrase_svc, confirm_recovery_backup_svc


touch_activity / check_and_lock_if_expired / set_auto_lock_timeout

Activity-based автоблокировка. Подробнее: Internals: Auto-lock, PIN Rate Limiting.

  • touch_activity() — обновляет timestamp последней активности. Не работает на locked vault (guard). Вызывается из 5 мест: POST /heartbeat, setup_with_pin, setup_with_recovery_phrase, unlock_with_pin, unlock_with_recovery_phrase
  • check_and_lock_if_expired() — проверяет таймаут и блокирует vault при истечении. Вызывается только background timer (каждые 5 секунд). Имеет ранний выход при locked vault
  • set_auto_lock_timeout(seconds) — устанавливает таймаут (0 = отключено). Persist в таблицу vault_settings
  • get_auto_lock_timeout() — возвращает текущий таймаут

Используется в: vault_heartbeat_svc, set_auto_lock_timeout_svc, get_vault_status_svc, auto-lock timer


set_background_operation_mode / get_background_operation_mode

fn set_background_operation_mode(&self, mode: BackgroundOperationMode)fn get_background_operation_mode(&self) -> BackgroundOperationMode

Управление режимом фоновых операций при заблокированном vault. Persist в таблицу vault_settings.

BackgroundOperationMode

ЗначениеОписание
Continue(default) Фоновая задача получает ScopedKey при старте, продолжает работу при auto-lock
PauseФоновая задача использует кэшированные ключи из менеджеров, при lock — пауза, при unlock — resume

Используется в: get_background_operation_mode_svc, set_background_operation_mode_svc, SharingManager.start_sharing


issue_scoped_key

fn issue_scoped_key(&self, secret_id: &str) -> Result<ScopedKey>

Выдаёт производный ключ (ScopedKey) для фоновой задачи. Содержит конкретный секрет (identity signing key, S3 creds и т.д.), не master key. ScopedKey живёт независимо от состояния vault — при auto-lock ключ остаётся рабочим. Зануляется (Zeroize) при drop.

Логика

  1. Загружает секрет через load_secret(secret_id)
  2. Оборачивает в ScopedKey с timestamp выдачи
  3. При drop — логирует время жизни ключа

Используется в: SharingManager.start_sharing (режим Continue)


reset

fn reset(&mut self) -> Result<()>

Полный сброс vault: удаляет все таблицы и данные.

Логика

  1. Зануляет master key
  2. DELETE FROM vault_config
  3. DELETE FROM encrypted_secrets
  4. DELETE FROM vault_pin_state

Используется в: reset_vault_svc