From 2e6fdfbd6641752a45702f02d8268382bcdb0c8b Mon Sep 17 00:00:00 2001 From: Joel-David Date: Sat, 15 Jun 2024 15:21:00 +0800 Subject: [PATCH] Fixed --- Chronos/App/MainAppView.swift | 8 ++-- .../App/Onboarding/PasswordSetupView.swift | 6 +-- .../Tokens/AddToken/AddManualTokenView.swift | 5 +-- .../Tabs/Tokens/AddToken/AddTokenView.swift | 11 +---- Chronos/App/Tabs/Tokens/TokensTab.swift | 14 +++--- Chronos/Database/ChronosCrypto.swift | 3 +- Chronos/Database/EncryptedToken.swift | 3 +- Chronos/Database/Vault.swift | 12 +++-- Chronos/Services/CryptoService.swift | 12 ++--- Chronos/Services/VaultService.swift | 45 +++++++++++++++++-- 10 files changed, 71 insertions(+), 48 deletions(-) diff --git a/Chronos/App/MainAppView.swift b/Chronos/App/MainAppView.swift index dfeeb1a..cb5e03c 100644 --- a/Chronos/App/MainAppView.swift +++ b/Chronos/App/MainAppView.swift @@ -9,16 +9,16 @@ struct MainAppView: View { @Environment(\.scenePhase) private var scenePhase @EnvironmentObject var loginStatus: LoginStatus - let stateService = Container.shared.stateService() - + private let stateService = Container.shared.stateService() + @Query(sort: \Vault.createdAt) private var vaults: [Vault] private var filteredVault: [Vault] { return vaults.compactMap { vault in - return vault.vaultId == stateService.getVaultId() ? vault : nil + vault.vaultId == stateService.getVaultId() ? vault : nil } } - + var body: some View { ZStack { TabView(selection: $currentTab) { diff --git a/Chronos/App/Onboarding/PasswordSetupView.swift b/Chronos/App/Onboarding/PasswordSetupView.swift index 1b08b7d..062d200 100644 --- a/Chronos/App/Onboarding/PasswordSetupView.swift +++ b/Chronos/App/Onboarding/PasswordSetupView.swift @@ -103,11 +103,11 @@ extension PasswordSetupView { func generateAndEncryptMasterKey() async { stateService.masterKey = try! cryptoService.generateRandomMasterKey() - let vault = vaultService.createVault()! - print("lol fk " + vault.vaultId!.uuidString) + let chronosCrypto = await cryptoService.wrapMasterKeyWithUserPassword(password: Array(password.utf8)) + + let vault = vaultService.createVault(chronosCrypto: chronosCrypto)! stateService.setVaultId(vaultId: vault.vaultId!) - await cryptoService.wrapMasterKeyWithUserPassword(vault: vault, password: Array(password.utf8)) nextBtnPressed = true } } diff --git a/Chronos/App/Tabs/Tokens/AddToken/AddManualTokenView.swift b/Chronos/App/Tabs/Tokens/AddToken/AddManualTokenView.swift index e25f911..73737df 100644 --- a/Chronos/App/Tabs/Tokens/AddToken/AddManualTokenView.swift +++ b/Chronos/App/Tabs/Tokens/AddToken/AddManualTokenView.swift @@ -2,7 +2,6 @@ import Factory import SwiftUI struct AddManualTokenView: View { - @Environment(\.modelContext) private var modelContext @Environment(\.dismiss) var dismiss @Environment(\.colorScheme) var colorScheme @@ -18,6 +17,7 @@ struct AddManualTokenView: View { @State private var period: Int = 30 let cryptoService = Container.shared.cryptoService() + let vaultService = Container.shared.vaultService() let parentDismiss: DismissAction @@ -127,8 +127,7 @@ struct AddManualTokenView: View { newToken.period = period let newEncToken = cryptoService.encryptToken(token: newToken) - modelContext.insert(newEncToken) - try? modelContext.save() + vaultService.insertEncryptedToken(newEncToken) parentDismiss() dismiss() diff --git a/Chronos/App/Tabs/Tokens/AddToken/AddTokenView.swift b/Chronos/App/Tabs/Tokens/AddToken/AddTokenView.swift index 3ec2dd5..517fd11 100644 --- a/Chronos/App/Tabs/Tokens/AddToken/AddTokenView.swift +++ b/Chronos/App/Tabs/Tokens/AddToken/AddTokenView.swift @@ -4,14 +4,13 @@ import Factory import SwiftUI struct AddTokenView: View { - @Environment(\.modelContext) private var modelContext - @Environment(\.dismiss) var dismiss @State private var unableToAccessCamera = false @State private var showTokenManualAddSheet = false let cryptoService = Container.shared.cryptoService() let otpService = Container.shared.otpService() + let vaultService = Container.shared.vaultService() var body: some View { VStack { @@ -94,10 +93,8 @@ struct AddTokenView: View { do { let newToken = try otpService.parseOtpAuthUrl(otpAuthStr: otpAuthStr) - let newEncToken = cryptoService.encryptToken(token: newToken) - modelContext.insert(newEncToken) - try modelContext.save() + vaultService.insertEncryptedToken(newEncToken) dismiss() @@ -122,7 +119,3 @@ struct AddTokenView: View { } } } - -#Preview { - AddTokenView() -} diff --git a/Chronos/App/Tabs/Tokens/TokensTab.swift b/Chronos/App/Tabs/Tokens/TokensTab.swift index 1a59bda..670fec4 100644 --- a/Chronos/App/Tabs/Tokens/TokensTab.swift +++ b/Chronos/App/Tabs/Tokens/TokensTab.swift @@ -10,7 +10,7 @@ struct TokenPair: Identifiable { } struct TokensTab: View { - @Query(sort: \EncryptedToken.createdAt) private var encryptedTokens: [EncryptedToken] + @Query(sort: \Vault.createdAt) private var vaults: [Vault] @State private var showTokenAddSheet = false @State private var showTokenUpdateSheet = false @@ -25,14 +25,14 @@ struct TokensTab: View { private var tokenPairs: [TokenPair] { let vaultId = stateService.getVaultId() + let vault = vaults.filter { $0.vaultId == vaultId } - return encryptedTokens.filter { $0.vault?.vaultId == vaultId } - .compactMap { encToken in - guard let decryptedToken = cryptoService.decryptToken(encryptedToken: encToken) else { - return nil - } - return TokenPair(id: encToken.id, token: decryptedToken, encToken: encToken) + return vault.first!.encryptedTokens!.compactMap { encToken in + guard let decryptedToken = cryptoService.decryptToken(encryptedToken: encToken) else { + return nil } + return TokenPair(id: encToken.id, token: decryptedToken, encToken: encToken) + } } var body: some View { diff --git a/Chronos/Database/ChronosCrypto.swift b/Chronos/Database/ChronosCrypto.swift index e0a4af3..2298f93 100644 --- a/Chronos/Database/ChronosCrypto.swift +++ b/Chronos/Database/ChronosCrypto.swift @@ -28,8 +28,7 @@ class ChronosCrypto { var passwordParams: PasswordParams? var kdfParams: KdfParams? - init(vault: Vault, key: [UInt8]? = nil, keyParams: KeyParams? = nil, passwordParams: PasswordParams? = nil, kdfParams: KdfParams? = nil) { - self.vault = vault + init(key: [UInt8], keyParams: KeyParams, passwordParams: PasswordParams, kdfParams: KdfParams) { self.key = key self.keyParams = keyParams self.passwordParams = passwordParams diff --git a/Chronos/Database/EncryptedToken.swift b/Chronos/Database/EncryptedToken.swift index 9e75789..77f334f 100644 --- a/Chronos/Database/EncryptedToken.swift +++ b/Chronos/Database/EncryptedToken.swift @@ -10,8 +10,7 @@ class EncryptedToken { var authenticationTag: [UInt8]? var createdAt: Date? - init(vault: Vault, encryptedTokenCiper: [UInt8]? = nil, iv: [UInt8]? = nil, authenticationTag: [UInt8]? = nil, createdAt: Date? = nil) { - self.vault = vault + init(encryptedTokenCiper: [UInt8], iv: [UInt8], authenticationTag: [UInt8], createdAt: Date) { self.encryptedTokenCiper = encryptedTokenCiper self.iv = iv self.authenticationTag = authenticationTag diff --git a/Chronos/Database/Vault.swift b/Chronos/Database/Vault.swift index c6146fa..631e92d 100644 --- a/Chronos/Database/Vault.swift +++ b/Chronos/Database/Vault.swift @@ -6,16 +6,14 @@ class Vault { var vaultId: UUID? var createdAt: Date? - @Relationship(deleteRule: .cascade, inverse: \ChronosCrypto.vault) - var chronosCryptos: [ChronosCrypto]? + @Relationship(deleteRule: .cascade) + var chronosCryptos: [ChronosCrypto]? = [] - @Relationship(deleteRule: .cascade, inverse: \EncryptedToken.vault) - var encryptedTokens: [EncryptedToken]? + @Relationship(deleteRule: .cascade) + var encryptedTokens: [EncryptedToken]? = [] - init(vaultId: UUID? = nil, createdAt: Date? = nil, chronosCryptos: [ChronosCrypto]? = nil, encryptedTokens: [EncryptedToken]? = nil) { + init(vaultId: UUID, createdAt: Date) { self.vaultId = vaultId self.createdAt = createdAt - self.chronosCryptos = chronosCryptos - self.encryptedTokens = encryptedTokens } } diff --git a/Chronos/Services/CryptoService.swift b/Chronos/Services/CryptoService.swift index 99b6339..c9f88bc 100644 --- a/Chronos/Services/CryptoService.swift +++ b/Chronos/Services/CryptoService.swift @@ -19,7 +19,7 @@ public class CryptoService { // scrypt paramaters - n: 2^17, r: 8, p: 1 private let kdfParams = KdfParams(type: 0, n: 1 << 17, r: 8, p: 1) - func wrapMasterKeyWithUserPassword(vault: Vault, password: [UInt8]) async { + func wrapMasterKeyWithUserPassword(password: [UInt8]) async -> ChronosCrypto { let passwordSalt = try! generateRandomSaltHexString() let passwordParams = PasswordParams(salt: passwordSalt) @@ -33,13 +33,9 @@ public class CryptoService { let keyParams = KeyParams(iv: iv, tag: encrypt.authenticationTag) - let newPasswordCrypto = ChronosCrypto(vault: vault, key: encrypt.cipherText, keyParams: keyParams, passwordParams: passwordParams, kdfParams: kdfParams) + let newPasswordCrypto = ChronosCrypto(key: encrypt.cipherText, keyParams: keyParams, passwordParams: passwordParams, kdfParams: kdfParams) - let context = ModelContext(swiftDataService.getModelContainer()) - - context.insert(newPasswordCrypto) - try context.save() - logger.info("Successfully created ChronosCrypto") + return newPasswordCrypto } catch { fatalError(error.localizedDescription) } @@ -103,7 +99,7 @@ extension CryptoService { let tokenJson = try JSONEncoder().encode(token) let encrypt = try AEADXChaCha20Poly1305.encrypt(Array(tokenJson), key: Array(stateService.masterKey), iv: iv, authenticationHeader: header) - return EncryptedToken(vault: vaultService.getVault()!, encryptedTokenCiper: encrypt.cipherText, iv: iv, authenticationTag: encrypt.authenticationTag, createdAt: Date()) + return EncryptedToken(encryptedTokenCiper: encrypt.cipherText, iv: iv, authenticationTag: encrypt.authenticationTag, createdAt: Date()) } catch { fatalError(error.localizedDescription) } diff --git a/Chronos/Services/VaultService.swift b/Chronos/Services/VaultService.swift index c364f16..79ed805 100644 --- a/Chronos/Services/VaultService.swift +++ b/Chronos/Services/VaultService.swift @@ -9,10 +9,13 @@ public class VaultService { private let stateService = Container.shared.stateService() private let swiftDataService = Container.shared.swiftDataService() - func createVault() -> Vault? { + func createVault(chronosCrypto: ChronosCrypto) -> Vault? { let context = ModelContext(swiftDataService.getModelContainer()) - let vault = Vault(vaultId: UUID(), createdAt: Date(), chronosCryptos: [], encryptedTokens: []) + let vault = Vault(vaultId: UUID(), createdAt: Date()) + + vault.chronosCryptos = [chronosCrypto] + context.insert(vault) do { @@ -25,7 +28,7 @@ public class VaultService { return nil } } - + // TODO(joeldavidw): Selects first vault for now. Selection page should be shown if there are more than one vault. func getFirstVault(isRestore: Bool) -> Vault? { let context = ModelContext(swiftDataService.getModelContainer(isRestore: isRestore)) @@ -61,3 +64,39 @@ public class VaultService { return vaultArr.first } } + +extension VaultService { + func insertEncryptedToken(_ encryptedToken: EncryptedToken) { + let context = ModelContext(swiftDataService.getModelContainer()) + + let vault = getVault()! + + vault.encryptedTokens?.append(encryptedToken) + + context.insert(vault) + + do { + try context.save() + logger.info("Successfully saved vault") + } catch { + logger.error("Failed to save context: \(error.localizedDescription)") + } + } + + func deleteEncryptedToken(_ encryptedToken: EncryptedToken) { + let context = ModelContext(swiftDataService.getModelContainer()) + + var vault = getVault()! + + vault.encryptedTokens?.append(encryptedToken) + + context.insert(vault) + + do { + try context.save() + logger.info("Successfully saved vault") + } catch { + logger.error("Failed to save context: \(error.localizedDescription)") + } + } +}