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.
Структура
| Поле | Тип | Описание |
|---|---|---|
db | Arc<Mutex<Connection>> | SQLite соединение |
current_identity | Option<Identity> | Кэшированная текущая identity (с account_root_pubkey: Option<[u8;32]>) |
pending_signing_key | Option<Zeroizing<[u8; 32]>> | Ed25519 ключ в памяти до сохранения в vault |
cached_signing_key | Arc<Mutex<Option<Zeroizing<Vec<u8>>>>> | Кэш signing key для фоновых операций |
Методы
| Метод | Сигнатура | Описание |
|---|---|---|
new | fn(db) -> Result<Self> | Инициализация + загрузка identity из БД |
create_identity | fn(&mut self, name) -> Result<(Identity, [u8; 32])> | Создание identity + Ed25519 ключевая пара |
get_identity | fn(&self) -> Option<&Identity> | Получить текущую identity |
has_identity | fn(&self) -> bool | Есть ли identity |
replace_identity | fn(&mut self, identity) -> Result<()> | Заменить identity (при паринге) |
update_identity_name | fn(&mut self, name) -> Result<()> | Обновить имя |
reset_identity | fn(&mut self) -> Result<(Identity, Vec<u8>)> | Сброс: удалить и создать новую |
set_pending_signing_key | fn(&mut self, key) | Сохранить ключ в памяти |
take_pending_signing_key | fn(&mut self) -> Option<(String, Zeroizing<[u8; 32]>)> | Забрать pending ключ |
load_identity_signing_key | fn(&self, vault, id) -> Result<Zeroizing<Vec<u8>>> | Загрузить ключ из vault |
| Account root | ||
set_account_root_pubkey | fn(&mut self, pk: [u8;32]) -> Result<()> | One-shot запись account_root_pubkey |
account_root_pubkey | fn(&self) -> Option<[u8;32]> | Чтение account_root_pubkey |
clear_account_root_pubkey | fn(&mut self) -> Result<()> | Destructive: занулить anchor (UX-flow «начать с нуля») |
| Authorization | ||
identity_proof | fn(&self) -> Option<IdentityProof> | Capability token для авторизации операций |
| Signing Key Cache | ||
materialize_signing_key | fn(&self, vault) -> Result<()> | Материализовать ключ из vault в кэш |
clear_cached_keys | fn(&self) | Очистить кэш ключей |
get_cached_signing_key | fn(&self) -> Result<Zeroizing<Vec<u8>>> | Получить ключ из кэша |
Зависимости
VaultManager— recovery-фраза + хранение legacy signing keyStrongholdManager— генерацияDeviceSignKey/DeviceBoxKeyвinit_first_device_on_unlockDeviceManager— запись device-cert и device-pubkey'ев после первого unlock'а- SQLite — таблица
identity(колонкаaccount_root_pubkey BLOB)
init_first_device_on_unlock
Файл:
backend/src/lib.rs(lifecycle hook на eventvault_unlocked)
Идемпотентный первый-device init, запускается на каждом unlock'е и сразу skip'ается если у identity уже есть account_root_pubkey.
Источники account_root_sk (по приоритету)
- Recovery-фраза во vault — основной путь для онбординга «новый аккаунт» / «restore from phrase».
RecoveryPhrase::derive_account_root_keypair()детерминированно мнит signing key. - Унаследованный 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.
Последовательность
- Sync probe: проверка
account_root_pubkey.is_some()→ silent skip. - Sync probe: чтение
device_id,transport_pubkeyиз текущей device-row. - Sync probe: выбор источника
account_root_sk(phrase → fallback на inherited signing_key). - Drop всех std-guard'ов.
- 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_sk→SignedDeviceCert
- Внутри Stronghold:
- Re-lock для persistence:
set_account_root_pubkey,set_device_pubkeys,set_device_cert. - Drop
account_root_sk.
Все ошибки логируются на WARN и не прерывают unlock-flow — legacy-flow продолжает работать.
IdentityProof
Capability token для авторизации деструктивных операций (удаление/расшаривание пространств).
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 операциях
// Пример использования на 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 из БД.
Логика
- Создаёт таблицу
identity(id, display_name, public_key, created_at) - Выполняет SELECT для загрузки существующей identity
- Если найдена — кэширует в
current_identity
create_identity
fn create_identity(&mut self, name: Option<String>) -> Result<(Identity, [u8; 32])>
Создаёт новую identity с Ed25519 ключевой парой. Возвращает identity и байты приватного ключа для сохранения в vault.
Параметры
| Имя | Тип | Описание |
|---|---|---|
name | Option<String> | Отображаемое имя (опционально) |
Возвращает
(Identity, [u8; 32]) — identity и 32 байта signing key
Логика
- Генерирует Ed25519 ключевую пару (secure random)
- ID = blake3(public_key)
- Сохраняет в БД (INSERT)
- Кэширует в
current_identity - Возвращает identity + private key bytes
Используется в:
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 из БД и вставляет новую.
Логика
- DELETE FROM identity
- INSERT новую identity
- Обновляет кэш
Используется в:
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 и создаёт новую. Используется при полном сбросе приложения.
Логика
- DELETE FROM identity
- Вызывает
create_identity(None) - Возвращает новую 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
Используется в:
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 заблокирован, ключ не материализован).
Используется в:
spawn_vault_cache_lifecycle(lib.rs),SharingManager.run_sharing_service(режим Pause)