Skip to content

IdentityManager

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

Управление идентичностью пользователя. Каждое приложение Kontinuum имеет одну активную identity, у которой есть стабильный account_root_pubkey — Ed25519 pubkey, выведенный из recovery-фразы; на нём якорится cert-chain устройств (см. DeviceManager).

Identity-key model

После редизайна:

  • account_root_sk детерминированно выводится из recovery-фразы через blake3::derive_key("kontinuum account-root ed25519", master_key) (vault::recovery::derive_account_root_keypair). Живёт только в памяти при первом-device init / paired-joiner inheritance, сразу после применения никуда не персистится.
  • account_root_pubkey = derive_account_root_keypair().verifying_key() — записан в колонку identity.account_root_pubkey (one-shot), служит anchor'ом для всего cert-chain'а устройств.
  • account_id = blake3(account_root_pubkey) — стабильный идентификатор аккаунта для cross-device и cross-acquaintance операций.

Legacy signing_key (Ed25519 на identity-уровне) сохраняется во vault для backward-compat с meet/sharing-flow'ами, которые ещё не переехали на cert-chain provenance. Новый код signing'а должен использовать SecretType::DeviceSignKey через StrongholdManager.

Структура

ПолеТипОписание
dbArc<Mutex<Connection>>SQLite соединение
current_identityOption<Identity>Кэшированная текущая identity (с account_root_pubkey: Option<[u8;32]>)
pending_signing_keyOption<Zeroizing<[u8; 32]>>Ed25519 ключ в памяти до сохранения в vault
cached_signing_keyArc<Mutex<Option<Zeroizing<Vec<u8>>>>>Кэш signing key для фоновых операций

Методы

МетодСигнатураОписание
newfn(db) -> Result<Self>Инициализация + загрузка identity из БД
create_identityfn(&mut self, name) -> Result<(Identity, [u8; 32])>Создание identity + Ed25519 ключевая пара
get_identityfn(&self) -> Option<&Identity>Получить текущую identity
has_identityfn(&self) -> boolЕсть ли identity
replace_identityfn(&mut self, identity) -> Result<()>Заменить identity (при паринге)
update_identity_namefn(&mut self, name) -> Result<()>Обновить имя
reset_identityfn(&mut self) -> Result<(Identity, Vec<u8>)>Сброс: удалить и создать новую
set_pending_signing_keyfn(&mut self, key)Сохранить ключ в памяти
take_pending_signing_keyfn(&mut self) -> Option<(String, Zeroizing<[u8; 32]>)>Забрать pending ключ
load_identity_signing_keyfn(&self, vault, id) -> Result<Zeroizing<Vec<u8>>>Загрузить ключ из vault
Account root
set_account_root_pubkeyfn(&mut self, pk: [u8;32]) -> Result<()>One-shot запись account_root_pubkey
account_root_pubkeyfn(&self) -> Option<[u8;32]>Чтение account_root_pubkey
clear_account_root_pubkeyfn(&mut self) -> Result<()>Destructive: занулить anchor (UX-flow «начать с нуля»)
Authorization
identity_prooffn(&self) -> Option<IdentityProof>Capability token для авторизации операций
Signing Key Cache
materialize_signing_keyfn(&self, vault) -> Result<()>Материализовать ключ из vault в кэш
clear_cached_keysfn(&self)Очистить кэш ключей
get_cached_signing_keyfn(&self) -> Result<Zeroizing<Vec<u8>>>Получить ключ из кэша

Зависимости

  • VaultManager — recovery-фраза + хранение legacy signing key
  • StrongholdManager — генерация DeviceSignKey/DeviceBoxKey в init_first_device_on_unlock
  • DeviceManager — запись device-cert и device-pubkey'ев после первого unlock'а
  • SQLite — таблица identity (колонка account_root_pubkey BLOB)

init_first_device_on_unlock

Файл: backend/src/lib.rs (lifecycle hook на event vault_unlocked)

Идемпотентный первый-device init, запускается на каждом unlock'е и сразу skip'ается если у identity уже есть account_root_pubkey.

Источники account_root_sk (по приоритету)

  1. Recovery-фраза во vault — основной путь для онбординга «новый аккаунт» / «restore from phrase». RecoveryPhrase::derive_account_root_keypair() детерминированно мнит signing key.
  2. Унаследованный legacy signing_key — paired-joiner flow (slice 11). Если identity row уже установлен через apply_paired_identity и в vault лежит identity_signing_key:{id}, эти байты используются как account_root_sk. Joiner становится sibling-device под тем же account_root.

Последовательность

  1. Sync probe: проверка account_root_pubkey.is_some() → silent skip.
  2. Sync probe: чтение device_id, transport_pubkey из текущей device-row.
  3. Sync probe: выбор источника account_root_sk (phrase → fallback на inherited signing_key).
  4. Drop всех std-guard'ов.
  5. Async: issue_first_device_artifacts_from_sk(account_root_sk, stronghold, password, transport_pk)
    • Внутри Stronghold: generate_device_keypair(DeviceSignKey)device_sign_pubkey
    • Внутри Stronghold: generate_device_keypair(DeviceBoxKey)device_box_pubkey
    • Сборка DeviceCertBody (account_id + три pubkey'я + issued_at + parent=None)
    • Подпись body через account_root_skSignedDeviceCert
  6. Re-lock для persistence: set_account_root_pubkey, set_device_pubkeys, set_device_cert.
  7. Drop account_root_sk.

Все ошибки логируются на WARN и не прерывают unlock-flow — legacy-flow продолжает работать.



IdentityProof

Capability token для авторизации деструктивных операций (удаление/расшаривание пространств).

rust
pub struct IdentityProof {
    identity_id: IdentityId,  // private field
}

impl IdentityProof {
    pub(crate) fn new(id: IdentityId) -> Self { ... }  // только внутри крейта
    pub fn identity_id(&self) -> &IdentityId { ... }
}
  • Создаётся только через IdentityManager::identity_proof() — гарантирует, что identity существует
  • Конструктор pub(crate) — внешний код не может подделать proof
  • Используется в SpaceManager::delete_space_authorized и sharing операциях
rust
// Пример использования на boundary (в сервисе):
let proof = identity_manager.lock().unwrap()
    .identity_proof()
    .ok_or("No active identity")?;
space_manager.lock().unwrap()
    .delete_space_authorized(space_id, &proof)?;

new

fn new(db: Arc<Mutex<Connection>>) -> Result<Self>

Инициализирует менеджер: создаёт таблицу identity (если не существует) и загружает текущую identity из БД.

Логика

  1. Создаёт таблицу identity (id, display_name, public_key, created_at)
  2. Выполняет SELECT для загрузки существующей identity
  3. Если найдена — кэширует в current_identity
plantuml Diagram

create_identity

fn create_identity(&mut self, name: Option<String>) -> Result<(Identity, [u8; 32])>

Создаёт новую identity с Ed25519 ключевой парой. Возвращает identity и байты приватного ключа для сохранения в vault.

Параметры

ИмяТипОписание
nameOption<String>Отображаемое имя (опционально)

Возвращает

(Identity, [u8; 32]) — identity и 32 байта signing key

Логика

  1. Генерирует Ed25519 ключевую пару (secure random)
  2. ID = blake3(public_key)
  3. Сохраняет в БД (INSERT)
  4. Кэширует в current_identity
  5. Возвращает identity + private key bytes
plantuml Diagram

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


get_identity / has_identity

fn get_identity(&self) -> Option<&Identity>fn has_identity(&self) -> bool

Геттеры для текущей identity. Работают с кэшем в памяти (без обращения к БД).

Используется в: get_identity_svc, create_space_svc, list_user_spaces_svc, list_devices_svc


replace_identity

fn replace_identity(&mut self, identity: Identity) -> Result<()>

Заменяет текущую identity на полученную от другого устройства при паринге. Удаляет старую identity из БД и вставляет новую.

Логика

  1. DELETE FROM identity
  2. INSERT новую identity
  3. Обновляет кэш
plantuml Diagram

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


update_identity_name

fn update_identity_name(&mut self, name: String) -> Result<()>

Обновляет отображаемое имя identity в БД и кэше.

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


reset_identity

fn reset_identity(&mut self) -> Result<(Identity, Vec<u8>)>

Удаляет текущую identity и создаёт новую. Используется при полном сбросе приложения.

Логика

  1. DELETE FROM identity
  2. Вызывает create_identity(None)
  3. Возвращает новую identity + signing key bytes

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


set_pending_signing_key / take_pending_signing_key

fn set_pending_signing_key(&mut self, key: Zeroizing<[u8; 32]>)fn take_pending_signing_key(&mut self) -> Option<(String, Zeroizing<[u8; 32]>)>

Механизм отложенного сохранения signing key. Если vault заблокирован при создании identity, ключ хранится в памяти до разблокировки vault.

  • set_pending_signing_key — вызывается при создании identity, если vault заблокирован
  • take_pending_signing_key — вызывается при разблокировке vault для flush ключа в vault
plantuml Diagram

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


load_identity_signing_key

fn load_identity_signing_key(&self, vault: &VaultManager, identity_id: &str) -> Result<Zeroizing<Vec<u8>>>

Загружает signing key из vault по identity ID. Используется для криптографических операций (подпись сообщений).

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


materialize_signing_key / clear_cached_keys / get_cached_signing_key

Кэш signing key для фоновых операций. Позволяет фоновым задачам (sharing, sync) получать signing key без обращения к vault.

materialize_signing_key

fn materialize_signing_key(&self, vault: &VaultManager) -> Result<()>

Загружает signing key из vault в кэш. Вызывается при разблокировке vault (событие vault_unlocked) через spawn_vault_cache_lifecycle.

clear_cached_keys

fn clear_cached_keys(&self)

Очищает кэш ключей. Вызывается при блокировке vault (событие vault_locked) через spawn_vault_cache_lifecycle.

get_cached_signing_key

fn get_cached_signing_key(&self) -> Result<Zeroizing<Vec<u8>>>

Возвращает signing key из кэша без обращения к vault. Возвращает ошибку, если кэш пуст (vault заблокирован, ключ не материализован).

plantuml Diagram

Используется в: spawn_vault_cache_lifecycle (lib.rs), SharingManager.run_sharing_service (режим Pause)