montana/Монтана-iOS/Sources/MontanaApp/Identity/IdentityManager.swift

69 lines
2.4 KiB
Swift
Raw Normal View History

import Foundation
import SwiftUI
/// Минимальный держатель identity для первой итерации.
/// Полные derived keys (ML-DSA + ML-KEM) Phase 2 (FFI к Rust).
struct Identity: Equatable {
let masterSeed: Data
let accountIdHex: String
let mnemonic: String?
}
@MainActor
final class IdentityManager: ObservableObject {
@Published private(set) var identity: Identity?
private let storageKey = "MontanaIdentity_v1"
init() {}
/// Загрузить existing identity из Keychain (если есть).
func loadIfPresent() {
guard let data = KeychainStore.read(key: storageKey),
let stored = try? JSONDecoder().decode(StoredIdentity.self, from: data) else {
return
}
identity = Identity(
masterSeed: stored.masterSeed,
accountIdHex: stored.accountIdHex,
mnemonic: nil
)
}
/// Создать новую identity из случайной мнемоники.
func createNew() throws -> String {
let mnemonic = Mnemonic.generate()
try install(mnemonic: mnemonic)
return mnemonic
}
/// Восстановить identity из существующей мнемоники.
func restore(from mnemonic: String) throws {
try install(mnemonic: mnemonic)
}
/// Стереть identity локально (для тестов / переустановки).
func wipe() {
KeychainStore.delete(key: storageKey)
identity = nil
}
private func install(mnemonic: String) throws {
let masterSeed = try Mnemonic.decodeAndDerive(mnemonic: mnemonic)
// Placeholder account_id = SHA-256("mt-account-placeholder" || masterSeed[0..16])
// Реальный account_id = SHA-256("mt-account" || pubkey_suite || ml_dsa_pubkey) Phase 2
let accountIdHex = HashUtil.sha256Hex(prefix: "mt-account-placeholder", body: masterSeed.prefix(16))
let stored = StoredIdentity(masterSeed: masterSeed, accountIdHex: accountIdHex)
let data = try JSONEncoder().encode(stored)
KeychainStore.write(key: storageKey, value: data)
identity = Identity(masterSeed: masterSeed, accountIdHex: accountIdHex, mnemonic: mnemonic)
}
}
private struct StoredIdentity: Codable {
let masterSeed: Data
let accountIdHex: String
}