From 132b74173e6d5c878ac5a37d144be200c890e31e Mon Sep 17 00:00:00 2001 From: Roma Sosnovsky Date: Thu, 30 Sep 2021 13:31:01 +0300 Subject: [PATCH 1/5] issue #441 remove PassPhraseObject model --- FlowCrypt.xcodeproj/project.pbxproj | 4 -- .../Setup/SetupBackupsViewController.swift | 23 +++++---- .../Setup/SetupEKMKeyViewController.swift | 2 +- .../SetupGenerateKeyViewController.swift | 13 +++-- ...anuallyEnterPassPhraseViewController.swift | 20 +------- .../Encrypted Storage/EncryptedStorage.swift | 36 ++------------ .../EncryptedStorageProtocols.swift | 4 +- .../Mail Provider/Imap/Imap+Other.swift | 2 +- .../Mail Provider/Imap/Imap+messages.swift | 4 +- .../Mail Provider/Imap/Imap+session.swift | 4 +- .../Imap+folders.swift | 2 +- .../Key Services/KeyDataStorage.swift | 8 ++-- .../Services/Key Services/KeyService.swift | 24 ++++++---- .../Key Services/PassPhraseService.swift | 20 ++------ FlowCrypt/Models/Realm Models/KeyInfo.swift | 4 +- .../Realm Models/PassPhraseObject.swift | 48 ------------------- 16 files changed, 62 insertions(+), 156 deletions(-) delete mode 100644 FlowCrypt/Models/Realm Models/PassPhraseObject.swift diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index f411bc6a2..e28e44f06 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -185,7 +185,6 @@ 9FC41183268118B1004C0A69 /* EmailProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC7EBC1266EBE0100F3BF5D /* EmailProviderMock.swift */; }; 9FC413182683C492004C0A69 /* InMemoryPassPhraseStorageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC413172683C491004C0A69 /* InMemoryPassPhraseStorageTest.swift */; }; 9FC413442683C912004C0A69 /* GmailServiceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC413432683C912004C0A69 /* GmailServiceTest.swift */; }; - 9FC7EAB3266A404D00F3BF5D /* PassPhraseObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC7EAB2266A404D00F3BF5D /* PassPhraseObject.swift */; }; 9FC7EB76266EB67B00F3BF5D /* EncryptedStorageProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC7EB75266EB67B00F3BF5D /* EncryptedStorageProtocols.swift */; }; 9FC7EBAA266EBD3700F3BF5D /* InMemoryPassPhraseStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC7EBA9266EBD3700F3BF5D /* InMemoryPassPhraseStorage.swift */; }; 9FD364862381EFCB00657302 /* SetupManuallyEnterPassPhraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD364852381EFCB00657302 /* SetupManuallyEnterPassPhraseViewController.swift */; }; @@ -611,7 +610,6 @@ 9FC411892681191D004C0A69 /* PromiseTestExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PromiseTestExtension.swift; sourceTree = ""; }; 9FC413172683C491004C0A69 /* InMemoryPassPhraseStorageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemoryPassPhraseStorageTest.swift; sourceTree = ""; }; 9FC413432683C912004C0A69 /* GmailServiceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GmailServiceTest.swift; sourceTree = ""; }; - 9FC7EAB2266A404D00F3BF5D /* PassPhraseObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassPhraseObject.swift; sourceTree = ""; }; 9FC7EB75266EB67B00F3BF5D /* EncryptedStorageProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedStorageProtocols.swift; sourceTree = ""; }; 9FC7EBA2266EB95300F3BF5D /* PassPhraseStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassPhraseStorageTests.swift; sourceTree = ""; }; 9FC7EBA9266EBD3700F3BF5D /* InMemoryPassPhraseStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemoryPassPhraseStorage.swift; sourceTree = ""; }; @@ -1088,7 +1086,6 @@ 9F1B49E02624E19D00420472 /* Realm Models */ = { isa = PBXGroup; children = ( - 9FC7EAB2266A404D00F3BF5D /* PassPhraseObject.swift */, D2F41372243CC7990066AFB5 /* UserObject.swift */, D27B911C24EFE806002DF0A1 /* ContactObject.swift */, D2E26F6924F25AB800612AF1 /* KeyAlgoObject.swift */, @@ -2684,7 +2681,6 @@ D29AFFF92409767F00C1387D /* GoogleContactsResponse.swift in Sources */, 9F003D6125E1B4ED00EB38C0 /* TrashFolderProvider.swift in Sources */, 9FF0671025520D7100FCC9E6 /* MessageGateway.swift in Sources */, - 9FC7EAB3266A404D00F3BF5D /* PassPhraseObject.swift in Sources */, 9F31AB8C23298B3F00CF87EA /* Imap+retry.swift in Sources */, 9F79229426696B9300DA3D80 /* KeyDataStorage.swift in Sources */, 9F82D352256D74FA0069A702 /* InboxViewContainerController.swift in Sources */, diff --git a/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift b/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift index b12ead124..3df8241e8 100644 --- a/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift @@ -144,17 +144,22 @@ extension SetupBackupsViewController { return } - // save pass phrase - matchingKeyBackups - .map { - PassPhrase(value: passPhrase, fingerprints: $0.fingerprints) - } - .forEach { - passPhraseService.savePassPhrase(with: $0, inStorage: shouldStorePassPhrase) - } + if !shouldStorePassPhrase { + // save pass phrase + matchingKeyBackups + .map { + PassPhrase(value: passPhrase, fingerprints: $0.fingerprints) + } + .forEach { + passPhraseService.savePassPhrase(with: $0, inStorage: shouldStorePassPhrase) + } + } // save keys - keyStorage.addKeys(keyDetails: Array(matchingKeyBackups), source: .backup, for: user.email) + keyStorage.addKeys(keyDetails: Array(matchingKeyBackups), + passPhrase: shouldStorePassPhrase ? passPhrase : nil, + source: .backup, + for: user.email) moveToMainFlow() } diff --git a/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift index 4e092c59f..6aadcc800 100644 --- a/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift @@ -87,7 +87,7 @@ extension SetupEKMKeyViewController { passphrase: passPhrase ) let parsedKey = try self.core.parseKeys(armoredOrBinary: encryptedPrv.encryptedKey.data()) - self.keyStorage.addKeys(keyDetails: parsedKey.keyDetails, source: .ekm, for: self.user.email) + self.keyStorage.addKeys(keyDetails: parsedKey.keyDetails, passPhrase: passPhrase, source: .ekm, for: self.user.email) allFingerprints.append(contentsOf: parsedKey.keyDetails.flatMap { $0.fingerprints }) } } diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index 85eccc497..da33a4a76 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -87,10 +87,15 @@ extension SetupGenerateKeyViewController { try awaitPromise(self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user)) - let passPhrase = PassPhrase(value: passPhrase, fingerprints: encryptedPrv.key.fingerprints) - - self.keyStorage.addKeys(keyDetails: [encryptedPrv.key], source: .generated, for: self.user.email) - self.passPhraseService.savePassPhrase(with: passPhrase, inStorage: self.shouldStorePassPhrase) + self.keyStorage.addKeys(keyDetails: [encryptedPrv.key], + passPhrase: self.shouldStorePassPhrase ? passPhrase: nil, + source: .generated, + for: self.user.email) + + if !self.shouldStorePassPhrase { + let passPhrase = PassPhrase(value: passPhrase, fingerprints: encryptedPrv.key.fingerprints) + self.passPhraseService.savePassPhrase(with: passPhrase, inStorage: false) + } let updateKey = self.attester.updateKey( email: userId.email, diff --git a/FlowCrypt/Controllers/Setup/SetupManuallyEnterPassPhraseViewController.swift b/FlowCrypt/Controllers/Setup/SetupManuallyEnterPassPhraseViewController.swift index e24bedc9f..3bf630281 100644 --- a/FlowCrypt/Controllers/Setup/SetupManuallyEnterPassPhraseViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupManuallyEnterPassPhraseViewController.swift @@ -241,24 +241,8 @@ extension SetupManuallyEnterPassPhraseViewController { let keysToUpdate = Array(Set(existedKeys).intersection(fetchedKeys)) let newKeysToAdd = Array(Set(fetchedKeys).subtracting(existedKeys)) - keysStorage.addKeys(keyDetails: newKeysToAdd, source: .imported, for: email) - keysStorage.updateKeys(keyDetails: keysToUpdate, source: .imported, for: email) - - keysToUpdate - .map { - PassPhrase(value: passPhrase, fingerprints: $0.fingerprints) - } - .forEach { - passPhraseService.updatePassPhrase(with: $0, inStorage: shouldStorePassPhrase) - } - - newKeysToAdd - .map { - PassPhrase(value: passPhrase, fingerprints: $0.fingerprints) - } - .forEach { - passPhraseService.savePassPhrase(with: $0, inStorage: shouldStorePassPhrase) - } + keysStorage.addKeys(keyDetails: newKeysToAdd, passPhrase: passPhrase, source: .imported, for: email) + keysStorage.updateKeys(keyDetails: keysToUpdate, passPhrase: passPhrase, source: .imported, for: email) hideSpinner() diff --git a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift index b211983fb..541e56b21 100644 --- a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift +++ b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift @@ -110,8 +110,6 @@ extension EncryptedStorage: LogOutHandler { .filter { $0.email == email } let keys = storage.objects(KeyInfo.self) .filter { $0.account == email } - let passPhrases = storage.objects(PassPhraseObject.self) - .filter { keys.map(\.primaryFingerprint).contains($0.primaryFingerprint) } let sessions = storage.objects(SessionObject.self) .filter { $0.email == email } let clientConfigurations = storage.objects(ClientConfigurationObject.self) @@ -120,7 +118,6 @@ extension EncryptedStorage: LogOutHandler { try storage.write { storage.delete(keys) storage.delete(sessions) - storage.delete(passPhrases) storage.delete(clientConfigurations) storage.delete(userToDelete) } @@ -159,26 +156,26 @@ extension EncryptedStorage { // MARK: - Keys extension EncryptedStorage { - func addKeys(keyDetails: [KeyDetails], source: KeySource, for email: String) { + func addKeys(keyDetails: [KeyDetails], passPhrase: String?, source: KeySource, for email: String) { guard let user = storage.objects(UserObject.self).first(where: { $0.email == email }) else { logger.logError("Can't find user with given email to add keys. User should be already saved") return } try! storage.write { for key in keyDetails { - storage.add(try! KeyInfo(key, source: source, user: user)) + storage.add(try! KeyInfo(key, passphrase: passPhrase, source: source, user: user)) } } } - func updateKeys(keyDetails: [KeyDetails], source: KeySource, for email: String) { + func updateKeys(keyDetails: [KeyDetails], passPhrase: String?, source: KeySource, for email: String) { guard let user = getUserObject(for: email) else { logger.logError("Can't find user with given email to update keys. User should be already saved") return } try! storage.write { for key in keyDetails { - storage.add(try! KeyInfo(key, source: source, user: user), update: .all) + storage.add(try! KeyInfo(key, passphrase: passPhrase, source: source, user: user), update: .all) } } } @@ -206,31 +203,6 @@ extension EncryptedStorage { } } -// MARK: - PassPhrase -extension EncryptedStorage: PassPhraseStorageType { - func save(passPhrase: PassPhrase) { - try! storage.write { - storage.add(PassPhraseObject(passPhrase)) - } - } - - func update(passPhrase: PassPhrase) { - try! storage.write { - storage.add(PassPhraseObject(passPhrase), update: .all) - } - } - - func remove(passPhrase: PassPhrase) { - try! storage.write { - storage.delete(PassPhraseObject(passPhrase)) - } - } - - func getPassPhrases() -> [PassPhrase] { - Array(storage.objects(PassPhraseObject.self)).map(PassPhrase.init) - } -} - // MARK: - User extension EncryptedStorage { var activeUser: UserObject? { diff --git a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorageProtocols.swift b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorageProtocols.swift index 046f7773b..ca44fef12 100644 --- a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorageProtocols.swift +++ b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorageProtocols.swift @@ -9,8 +9,8 @@ import Foundation protocol KeyStorageType { - func addKeys(keyDetails: [KeyDetails], source: KeySource, for email: String) - func updateKeys(keyDetails: [KeyDetails], source: KeySource, for email: String) + func addKeys(keyDetails: [KeyDetails], passPhrase: String?, source: KeySource, for email: String) + func updateKeys(keyDetails: [KeyDetails], passPhrase: String?, source: KeySource, for email: String) func publicKey() -> String? func keysInfo() -> [KeyInfo] } diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+Other.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+Other.swift index 30a41a83c..c0917f73d 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+Other.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+Other.swift @@ -46,7 +46,7 @@ extension Imap { self.fetchMessage(in: folder, kind: kind, uids: uids) }) else { return } - guard let messages = msgs as? [MCOIMAPMessage] else { + guard let messages = msgs else { return reject(AppErr.cast("[MCOIMAPMessage]")) } return resolve(messages) diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+messages.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+messages.swift index 340212bd6..b8d5e64ae 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+messages.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+messages.swift @@ -27,7 +27,7 @@ extension Imap { .start { error, messages, _ in // original method sig has 3 args, finalize expects 2 args self.finalize("fetchMsgsByNumber", resolve, reject, retry: { self.fetchMsgsByNumber(for: folder, kind: kind, set: set) - })(error, messages as? [MCOIMAPMessage]) + })(error, messages) } } } @@ -49,7 +49,7 @@ extension Imap { .start { error, messages, _ in // original method sig has 3 args, finalize expects 2 args self.finalize("fetchMessagesByUIDOperation", resolve, reject, retry: { self.fetchMessagesByUIDOperation(for: folder, kind: kind, set: set) - })(error, messages as? [MCOIMAPMessage]) + })(error, messages) } } } diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift index e45108369..9a47cb026 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift @@ -83,7 +83,7 @@ extension Imap { extension MCOIMAPSession { func log() -> Self { - connectionLogger = { connectionID, type, data in + connectionLogger = { _, type, data in guard let data = data, let string = String(data: data, encoding: .utf8) else { return } Logger.nested("IMAP").logInfo("\(type):\(string)") } @@ -93,7 +93,7 @@ extension MCOIMAPSession { extension MCOSMTPSession { func log() -> Self { - connectionLogger = { connectionID, type, data in + connectionLogger = { _, type, data in guard let data = data, let string = String(data: data, encoding: .utf8) else { return } Logger.nested("SMTP").logInfo("\(type):\(string)") } diff --git a/FlowCrypt/Functionality/Services/Folders Services/RemoteFoldersProviderType/Imap+folders.swift b/FlowCrypt/Functionality/Services/Folders Services/RemoteFoldersProviderType/Imap+folders.swift index 3c974e57a..e6efba8a3 100644 --- a/FlowCrypt/Functionality/Services/Folders Services/RemoteFoldersProviderType/Imap+folders.swift +++ b/FlowCrypt/Functionality/Services/Folders Services/RemoteFoldersProviderType/Imap+folders.swift @@ -25,7 +25,7 @@ extension Imap: RemoteFoldersProviderType { reject(ImapError.providerError(error)) return } - guard let folders = value as? [MCOIMAPFolder] else { + guard let folders = value else { return reject(AppErr.cast("[MCOIMAPFolder]")) } diff --git a/FlowCrypt/Functionality/Services/Key Services/KeyDataStorage.swift b/FlowCrypt/Functionality/Services/Key Services/KeyDataStorage.swift index 42f01c647..d92cf1369 100644 --- a/FlowCrypt/Functionality/Services/Key Services/KeyDataStorage.swift +++ b/FlowCrypt/Functionality/Services/Key Services/KeyDataStorage.swift @@ -22,8 +22,8 @@ final class KeyDataStorage { } extension KeyDataStorage: KeyStorageType { - func updateKeys(keyDetails: [KeyDetails], source: KeySource, for email: String) { - encryptedStorage.updateKeys(keyDetails: keyDetails, source: source, for: email) + func updateKeys(keyDetails: [KeyDetails], passPhrase: String?, source: KeySource, for email: String) { + encryptedStorage.updateKeys(keyDetails: keyDetails, passPhrase: passPhrase, source: source, for: email) } func publicKey() -> String? { @@ -34,7 +34,7 @@ extension KeyDataStorage: KeyStorageType { encryptedStorage.keysInfo() } - func addKeys(keyDetails: [KeyDetails], source: KeySource, for email: String) { - encryptedStorage.addKeys(keyDetails: keyDetails, source: source, for: email) + func addKeys(keyDetails: [KeyDetails], passPhrase: String?, source: KeySource, for email: String) { + encryptedStorage.addKeys(keyDetails: keyDetails, passPhrase: passPhrase, source: source, for: email) } } diff --git a/FlowCrypt/Functionality/Services/Key Services/KeyService.swift b/FlowCrypt/Functionality/Services/Key Services/KeyService.swift index f2b11b1a0..c77ee432e 100644 --- a/FlowCrypt/Functionality/Services/Key Services/KeyService.swift +++ b/FlowCrypt/Functionality/Services/Key Services/KeyService.swift @@ -66,9 +66,11 @@ final class KeyService: KeyServiceType { let keysInfo = storage.keysInfo() .filter { $0.account == email } + let keysWithPassPhrases = keysInfo.filter { $0.passphrase != nil } + let storedPassPhrases = passPhraseService.getPassPhrases() - if passPhrase == nil, storedPassPhrases.isEmpty { + if passPhrase == nil, storedPassPhrases.isEmpty, keysWithPassPhrases.isEmpty { // in case there are no pass phrases in storage/memory // and user did not enter a pass phrase yet return .failure(.missedPassPhrase) @@ -100,19 +102,21 @@ final class KeyService: KeyServiceType { } // append keys to ensure with a pass phrase - if let passPhrase = passPhrase { - let keysToEnsure = keysInfo.map { - PrvKeyInfo( - private: $0.private, - longid: $0.primaryLongid, - passphrase: passPhrase, - fingerprints: Array($0.allFingerprints) - ) + let keysToEnsure: [PrvKeyInfo] = keysInfo.compactMap { + guard let passPhraseValue = $0.passphrase ?? passPhrase else { + return nil } - privateKeys.append(contentsOf: keysToEnsure) + return PrvKeyInfo( + private: $0.private, + longid: $0.primaryLongid, + passphrase: passPhraseValue, + fingerprints: Array($0.allFingerprints) + ) } + privateKeys.append(contentsOf: keysToEnsure) + return .success(privateKeys) } } diff --git a/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift b/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift index 63b51993b..7415a0423 100644 --- a/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift +++ b/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift @@ -58,45 +58,31 @@ final class PassPhraseService: PassPhraseServiceType { private lazy var logger = Logger.nested(Self.self) let currentUserEmail: String? - let encryptedStorage: PassPhraseStorageType let inMemoryStorage: PassPhraseStorageType init( - encryptedStorage: PassPhraseStorageType = EncryptedStorage(), localStorage: PassPhraseStorageType = InMemoryPassPhraseStorage(), emailProvider: EmailProviderType = DataService.shared ) { - self.encryptedStorage = encryptedStorage self.inMemoryStorage = localStorage self.currentUserEmail = emailProvider.email } func savePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) { - if inStorage { - logger.logInfo("Save passphrase to storage") - encryptedStorage.save(passPhrase: passPhrase) - } else { + if !inStorage { logger.logInfo("Save passphrase in memory") inMemoryStorage.save(passPhrase: passPhrase) - - let alreadySaved = encryptedStorage.getPassPhrases() - - if alreadySaved.contains(where: { $0.primaryFingerprint == passPhrase.primaryFingerprint }) { - encryptedStorage.remove(passPhrase: passPhrase) - } } } func updatePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) { - if inStorage { - encryptedStorage.update(passPhrase: passPhrase) - } else { + if !inStorage { inMemoryStorage.save(passPhrase: passPhrase) } } func getPassPhrases() -> [PassPhrase] { - encryptedStorage.getPassPhrases() + inMemoryStorage.getPassPhrases() + inMemoryStorage.getPassPhrases() } } diff --git a/FlowCrypt/Models/Realm Models/KeyInfo.swift b/FlowCrypt/Models/Realm Models/KeyInfo.swift index a0d65c50c..ca7880bdc 100644 --- a/FlowCrypt/Models/Realm Models/KeyInfo.swift +++ b/FlowCrypt/Models/Realm Models/KeyInfo.swift @@ -31,10 +31,11 @@ final class KeyInfo: Object { let allFingerprints = List() let allLongids = List() + @objc dynamic var passphrase: String? @objc dynamic var source: String = "" @objc dynamic var user: UserObject! - convenience init(_ keyDetails: KeyDetails, source: KeySource, user: UserObject) throws { + convenience init(_ keyDetails: KeyDetails, passphrase: String?, source: KeySource, user: UserObject) throws { self.init() guard let privateKey = keyDetails.private else { @@ -51,6 +52,7 @@ final class KeyInfo: Object { self.`public` = keyDetails.public self.allFingerprints.append(objectsIn: keyDetails.ids.map(\.fingerprint)) self.allLongids.append(objectsIn: keyDetails.ids.map(\.longid)) + self.passphrase = passphrase self.source = source.rawValue self.user = user } diff --git a/FlowCrypt/Models/Realm Models/PassPhraseObject.swift b/FlowCrypt/Models/Realm Models/PassPhraseObject.swift deleted file mode 100644 index 78f1443f2..000000000 --- a/FlowCrypt/Models/Realm Models/PassPhraseObject.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// PassPhraseInfo.swift -// FlowCrypt -// -// Created by Anton Kharchevskyi on 04.06.2021. -// Copyright © 2017-present FlowCrypt a. s. All rights reserved. -// - -import Foundation -import RealmSwift - -/// PassPhrase object to store in Realm -final class PassPhraseObject: Object { - @objc dynamic var value: String = "" - let allFingerprints = List() - - convenience init( - value: String = "", - fingerprints: [String] - ) { - self.init() - self.value = value - self.allFingerprints.append(objectsIn: fingerprints) - } - - override class func primaryKey() -> String? { - "value" - } -} - -// MARK: - Convenience -extension PassPhraseObject { - var primaryFingerprint: String { - allFingerprints[0] - } - - convenience init(_ passPhrase: PassPhrase) { - self.init(value: passPhrase.value, fingerprints: passPhrase.fingerprints) - } -} - -extension PassPhrase { - init(object: PassPhraseObject) { - self.value = object.value - self.fingerprints = Array(object.allFingerprints) - self.date = nil - } -} From f9953a89bcd067187cb45c6ac656d725785ca4b0 Mon Sep 17 00:00:00 2001 From: Roma Sosnovsky Date: Thu, 30 Sep 2021 15:37:16 +0300 Subject: [PATCH 2/5] issue #441 update tests --- .../Setup/SetupEKMKeyViewController.swift | 12 +++- .../InMemoryPassPhraseStorage.swift | 16 ------ .../Key Services/PassPhraseService.swift | 9 --- .../Core/Models/KeyInfoTests.swift | 8 +-- .../InMemoryPassPhraseStorageTest.swift | 15 ----- .../PassPhraseStorageTests.swift | 57 +++---------------- FlowCryptAppTests/Mocks/KeyStorageMock.swift | 4 +- 7 files changed, 23 insertions(+), 98 deletions(-) diff --git a/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift index 6aadcc800..5877cfd72 100644 --- a/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift @@ -87,12 +87,18 @@ extension SetupEKMKeyViewController { passphrase: passPhrase ) let parsedKey = try self.core.parseKeys(armoredOrBinary: encryptedPrv.encryptedKey.data()) - self.keyStorage.addKeys(keyDetails: parsedKey.keyDetails, passPhrase: passPhrase, source: .ekm, for: self.user.email) + self.keyStorage.addKeys(keyDetails: parsedKey.keyDetails, + passPhrase: self.shouldStorePassPhrase ? passPhrase : nil, + source: .ekm, + for: self.user.email) allFingerprints.append(contentsOf: parsedKey.keyDetails.flatMap { $0.fingerprints }) } } - let passPhrase = PassPhrase(value: passPhrase, fingerprints: allFingerprints.unique()) - self.passPhraseService.savePassPhrase(with: passPhrase, inStorage: self.shouldStorePassPhrase) + + if !self.shouldStorePassPhrase { + let passPhrase = PassPhrase(value: passPhrase, fingerprints: allFingerprints.unique()) + self.passPhraseService.savePassPhrase(with: passPhrase, inStorage: self.shouldStorePassPhrase) + } } .then(on: .main) { [weak self] in self?.hideSpinner() diff --git a/FlowCrypt/Functionality/Services/Key Services/InMemoryPassPhraseStorage.swift b/FlowCrypt/Functionality/Services/Key Services/InMemoryPassPhraseStorage.swift index 13e5d2c2b..193b889ed 100644 --- a/FlowCrypt/Functionality/Services/Key Services/InMemoryPassPhraseStorage.swift +++ b/FlowCrypt/Functionality/Services/Key Services/InMemoryPassPhraseStorage.swift @@ -29,15 +29,6 @@ final class InMemoryPassPhraseStorage: PassPhraseStorageType { passPhraseProvider.save(passPhrase: passPhraseToSave) } - func update(passPhrase: PassPhrase) { - let passPhraseToSave = passPhrase.withUpdatedDate() - passPhraseProvider.save(passPhrase: passPhraseToSave) - } - - func remove(passPhrase: PassPhrase) { - passPhraseProvider.removePassPhrases(with: passPhrase) - } - func getPassPhrases() -> [PassPhrase] { passPhraseProvider.passPhrases .compactMap { passPhrase -> PassPhrase? in @@ -70,7 +61,6 @@ final class InMemoryPassPhraseStorage: PassPhraseStorageType { protocol InMemoryPassPhraseProviderType { var passPhrases: Set { get } func save(passPhrase: PassPhrase) - func removePassPhrases(with objects: PassPhrase) } /// - Warning: - should be shared instance @@ -85,10 +75,4 @@ final class InMemoryPassPhraseProvider: InMemoryPassPhraseProviderType { func save(passPhrase: PassPhrase) { passPhrases.insert(passPhrase) } - - func removePassPhrases(with objects: PassPhrase) { - if passPhrases.contains(objects) { - passPhrases.remove(objects) - } - } } diff --git a/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift b/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift index 7415a0423..2e88549ca 100644 --- a/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift +++ b/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift @@ -41,8 +41,6 @@ struct PassPhrase: Codable, Hashable, Equatable { // MARK: - Pass Phrase Storage protocol PassPhraseStorageType { func save(passPhrase: PassPhrase) - func update(passPhrase: PassPhrase) - func remove(passPhrase: PassPhrase) func getPassPhrases() -> [PassPhrase] } @@ -51,7 +49,6 @@ protocol PassPhraseStorageType { protocol PassPhraseServiceType { func getPassPhrases() -> [PassPhrase] func savePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) - func updatePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) } final class PassPhraseService: PassPhraseServiceType { @@ -76,12 +73,6 @@ final class PassPhraseService: PassPhraseServiceType { } } - func updatePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) { - if !inStorage { - inMemoryStorage.save(passPhrase: passPhrase) - } - } - func getPassPhrases() -> [PassPhrase] { inMemoryStorage.getPassPhrases() } diff --git a/FlowCryptAppTests/Core/Models/KeyInfoTests.swift b/FlowCryptAppTests/Core/Models/KeyInfoTests.swift index bb6235392..16b4616bf 100644 --- a/FlowCryptAppTests/Core/Models/KeyInfoTests.swift +++ b/FlowCryptAppTests/Core/Models/KeyInfoTests.swift @@ -28,7 +28,7 @@ class KeyInfoTests: XCTestCase { ) var thrownError: Error? - XCTAssertThrowsError(try KeyInfo(keyDetail, source: .backup, user: user)) { error in + XCTAssertThrowsError(try KeyInfo(keyDetail, passphrase: nil, source: .backup, user: user)) { error in thrownError = error } @@ -50,7 +50,7 @@ class KeyInfoTests: XCTestCase { ) var thrownError: Error? - XCTAssertThrowsError(try KeyInfo(keyDetail, source: .backup, user: user)) { error in + XCTAssertThrowsError(try KeyInfo(keyDetail, passphrase: nil, source: .backup, user: user)) { error in thrownError = error } @@ -70,7 +70,7 @@ class KeyInfoTests: XCTestCase { ) var thrownError: Error? - XCTAssertThrowsError(try KeyInfo(keyDetail, source: .backup, user: user)) { error in + XCTAssertThrowsError(try KeyInfo(keyDetail, passphrase: nil, source: .backup, user: user)) { error in thrownError = error } @@ -93,7 +93,7 @@ class KeyInfoTests: XCTestCase { algo: nil ) - let key = try KeyInfo(keyDetail, source: .backup, user: user) + let key = try KeyInfo(keyDetail, passphrase: nil, source: .backup, user: user) XCTAssertTrue(key.private == "private") XCTAssertTrue(key.public == "public") diff --git a/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift b/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift index 8b5bf3d3a..b111f2e3e 100644 --- a/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift +++ b/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift @@ -31,21 +31,6 @@ class InMemoryPassPhraseStorageTest: XCTestCase { } } - func testUpdatePassPhraseUpdatesDate() { - let pass = PassPhrase(value: "A", fingerprints: ["11","12"]) - sut.update(passPhrase: pass) - passPhraseProvider.passPhrases.forEach { - XCTAssertNotNil($0.date) - } - } - - func testRemovePassPhrase() { - let pass = PassPhrase(value: "A", fingerprints: ["11","12"]) - sut.save(passPhrase: pass) - sut.remove(passPhrase: pass) - XCTAssertTrue(passPhraseProvider.passPhrases.isEmpty) - } - func testGetPassPhrases() { XCTAssertTrue(sut.getPassPhrases().isEmpty) diff --git a/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/PassPhraseStorageTests.swift b/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/PassPhraseStorageTests.swift index 68d3adccc..dc491fcea 100644 --- a/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/PassPhraseStorageTests.swift +++ b/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/PassPhraseStorageTests.swift @@ -12,25 +12,20 @@ import XCTest class PassPhraseStorageTests: XCTestCase { var sut: PassPhraseService! - var encryptedStorage: PassPhraseStorageMock! var inMemoryStorage: PassPhraseStorageMock! var emailProvider: EmailProviderMock! override func setUp() { emailProvider = EmailProviderMock() - encryptedStorage = PassPhraseStorageMock() inMemoryStorage = PassPhraseStorageMock() sut = PassPhraseService( - encryptedStorage: encryptedStorage, localStorage: inMemoryStorage, emailProvider: emailProvider ) } func testGetPassPhrasesWhenEmpty() { - // no pass phrases in storage - encryptedStorage.getPassPhrasesResult = { [] } // no pass phrases in localStorage inMemoryStorage.getPassPhrasesResult = { [] } @@ -39,7 +34,7 @@ class PassPhraseStorageTests: XCTestCase { XCTAssertTrue(result.isEmpty) } - func testGetValidPassPhraseFromStorage() { + func testGetValidPassPhraseFromLocalStorage() { let passPhrase1 = PassPhrase( value: "some", fingerprints: ["11","12"] @@ -48,16 +43,15 @@ class PassPhraseStorageTests: XCTestCase { value: "some", fingerprints: ["21","22"] ) - - encryptedStorage.getPassPhrasesResult = { [passPhrase1] } + // no pass phrases in localStorage - inMemoryStorage.getPassPhrasesResult = { [] } + inMemoryStorage.getPassPhrasesResult = { [passPhrase1] } var result = sut.getPassPhrases() XCTAssertTrue(result.count == 1) - encryptedStorage.getPassPhrasesResult = { + inMemoryStorage.getPassPhrasesResult = { [passPhrase1, passPhrase2] } @@ -67,8 +61,6 @@ class PassPhraseStorageTests: XCTestCase { } func testGetValidPassPhraseInLocalStorage() { - encryptedStorage.getPassPhrasesResult = { [] } - let savedDate = Date() let localPassPhrase = PassPhrase( value: "value", @@ -84,19 +76,7 @@ class PassPhraseStorageTests: XCTestCase { XCTAssertTrue(result.isNotEmpty) } - func testBothStorageContainsValidPassPhrase() { - let passPhrase1 = PassPhrase( - value: "some", - fingerprints: ["A123"] - ) - let passPhrase2 = PassPhrase( - value: "some", - fingerprints: ["A123"] - ) - encryptedStorage.getPassPhrasesResult = { - [passPhrase1, passPhrase2] - } - + func testLocalStorageContainsValidPassPhrase() { let savedDate = Date() let localPassPhrase = PassPhrase( value: "value", @@ -107,7 +87,7 @@ class PassPhraseStorageTests: XCTestCase { inMemoryStorage.getPassPhrasesResult = { [localPassPhrase] } let result = sut.getPassPhrases() - XCTAssertTrue(result.count == 3) + XCTAssertTrue(result.count == 1) } func testSavePassPhraseInStorage() { @@ -117,21 +97,6 @@ class PassPhraseStorageTests: XCTestCase { expectation.expectedFulfillmentCount = 1 expectation.isInverted = true - // encrypted storage contains pass phrase which should be saved locally - encryptedStorage.getPassPhrasesResult = { - [ - PassPhrase(value: "pass", fingerprints: ["fingerprint 1", "adfnhjfg"]) - ] - } - - - // encrypted storage should not contains pass phrase which user decide to save locally - encryptedStorage.isRemovePassPhraseResult = { passPhraseToRemove in - if passPhraseToRemove.primaryFingerprint == "fingerprint 1" { - expectation.fulfill() - } - } - sut.savePassPhrase(with: passPhraseToSave, inStorage: true) XCTAssertFalse(inMemoryStorage.saveResult != nil ) @@ -144,14 +109,7 @@ class PassPhraseStorageTests: XCTestCase { let expectation = XCTestExpectation() expectation.isInverted = true - - // encrypted storage is empty - encryptedStorage.getPassPhrasesResult = { [ ] } - - encryptedStorage.isRemovePassPhraseResult = { _ in - expectation.fulfill() - } - + sut.savePassPhrase(with: passPhraseToSave, inStorage: true) XCTAssertFalse(inMemoryStorage.saveResult != nil ) @@ -181,6 +139,7 @@ extension KeyInfo { users: [], algo: nil ), + passphrase: nil, source: .backup, user: UserObject( name: "name", diff --git a/FlowCryptAppTests/Mocks/KeyStorageMock.swift b/FlowCryptAppTests/Mocks/KeyStorageMock.swift index a3bfa3191..d651ace5f 100644 --- a/FlowCryptAppTests/Mocks/KeyStorageMock.swift +++ b/FlowCryptAppTests/Mocks/KeyStorageMock.swift @@ -10,11 +10,11 @@ import Foundation @testable import FlowCrypt class KeyStorageMock: KeyStorageType { - func addKeys(keyDetails: [KeyDetails], source: KeySource, for email: String) { + func addKeys(keyDetails: [KeyDetails], passPhrase: String?, source: KeySource, for email: String) { } - func updateKeys(keyDetails: [KeyDetails], source: KeySource, for email: String) { + func updateKeys(keyDetails: [KeyDetails], passPhrase: String?, source: KeySource, for email: String) { } From 600f1d25805ff85233aca1870afb16b517105e7c Mon Sep 17 00:00:00 2001 From: Roma Sosnovsky Date: Thu, 30 Sep 2021 16:44:53 +0300 Subject: [PATCH 3/5] issue #441 add passphrase check to tests --- FlowCryptAppTests/Core/Models/KeyInfoTests.swift | 3 ++- .../InMemoryPassPhraseStorageTest.swift | 6 ------ Podfile.lock | 14 +++++++------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/FlowCryptAppTests/Core/Models/KeyInfoTests.swift b/FlowCryptAppTests/Core/Models/KeyInfoTests.swift index 16b4616bf..f21ee1605 100644 --- a/FlowCryptAppTests/Core/Models/KeyInfoTests.swift +++ b/FlowCryptAppTests/Core/Models/KeyInfoTests.swift @@ -93,12 +93,13 @@ class KeyInfoTests: XCTestCase { algo: nil ) - let key = try KeyInfo(keyDetail, passphrase: nil, source: .backup, user: user) + let key = try KeyInfo(keyDetail, passphrase: "123", source: .backup, user: user) XCTAssertTrue(key.private == "private") XCTAssertTrue(key.public == "public") XCTAssertTrue(Array(key.allFingerprints) == ["f1", "f2", "f3"]) XCTAssertTrue(Array(key.allLongids) == ["l1", "l2", "l3"]) + XCTAssertTrue(key.passphrase == "123") XCTAssertTrue(key.source == "backup") XCTAssertTrue(key.user == user) XCTAssertTrue(key.primaryFingerprint == "f1") diff --git a/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift b/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift index b111f2e3e..759335ed8 100644 --- a/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift +++ b/FlowCryptAppTests/Functionallity/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift @@ -58,10 +58,4 @@ class InMemoryPassPhraseProviderMock: InMemoryPassPhraseProviderType { func save(passPhrase: PassPhrase) { passPhrases.insert(passPhrase) } - - func removePassPhrases(with objects: PassPhrase) { - if passPhrases.contains(objects) { - passPhrases.remove(objects) - } - } } diff --git a/Podfile.lock b/Podfile.lock index 3ca541b17..ba69805f0 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -43,11 +43,11 @@ PODS: - PromisesObjC (2.0.0) - PromisesSwift (2.0.0): - PromisesObjC (= 2.0.0) - - Realm (10.15.1): - - Realm/Headers (= 10.15.1) - - Realm/Headers (10.15.1) - - RealmSwift (10.15.1): - - Realm (= 10.15.1) + - Realm (10.16.0): + - Realm/Headers (= 10.16.0) + - Realm/Headers (10.16.0) + - RealmSwift (10.16.0): + - Realm (= 10.16.0) - SwiftFormat/CLI (0.48.11) - SwiftLint (0.44.0) - SwiftyRSA (1.7.0): @@ -130,8 +130,8 @@ SPEC CHECKSUMS: PINRemoteImage: f1295b29f8c5e640e25335a1b2bd9d805171bd01 PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 PromisesSwift: e0b2a6433469efb0b83a2b84c62a2abab8e5e5d4 - Realm: fbcbde2620d51623cc5b18e28f5b165c39abef52 - RealmSwift: e4bea8e42efad3d543aea23b56504069c6faaea2 + Realm: b6027801398f3743fc222f096faa85281b506e6c + RealmSwift: b02a3d0e4947955da960b642c98ad3a461fc4e70 SwiftFormat: 938e5865a118c49d63c7a290ddad86335f9e585f SwiftLint: e96c0a8c770c7ebbc4d36c55baf9096bb65c4584 SwiftyRSA: 8c6dd1ea7db1b8dc4fb517a202f88bb1354bc2c6 From 78c1f7d48937167a7a0108f86237bb54a25d4238 Mon Sep 17 00:00:00 2001 From: Roma Sosnovsky Date: Fri, 1 Oct 2021 13:23:14 +0300 Subject: [PATCH 4/5] issue #441 add back encrypted storage for passphrases --- ...anuallyEnterPassPhraseViewController.swift | 19 ++++ .../Encrypted Storage/EncryptedStorage.swift | 28 +++++ .../InMemoryPassPhraseStorage.swift | 16 +++ .../Services/Key Services/KeyService.swift | 24 ++-- .../Key Services/PassPhraseService.swift | 36 +++++- .../InMemoryPassPhraseStorageTest.swift | 21 ++++ .../PassPhraseStorageTests.swift | 106 ++++++++++++------ 7 files changed, 202 insertions(+), 48 deletions(-) diff --git a/FlowCrypt/Controllers/Setup/SetupManuallyEnterPassPhraseViewController.swift b/FlowCrypt/Controllers/Setup/SetupManuallyEnterPassPhraseViewController.swift index 3bf630281..f0c9f2a08 100644 --- a/FlowCrypt/Controllers/Setup/SetupManuallyEnterPassPhraseViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupManuallyEnterPassPhraseViewController.swift @@ -244,6 +244,25 @@ extension SetupManuallyEnterPassPhraseViewController { keysStorage.addKeys(keyDetails: newKeysToAdd, passPhrase: passPhrase, source: .imported, for: email) keysStorage.updateKeys(keyDetails: keysToUpdate, passPhrase: passPhrase, source: .imported, for: email) + if !shouldStorePassPhrase { + keysToUpdate + .map { + PassPhrase(value: passPhrase, fingerprints: $0.fingerprints) + } + .forEach { + passPhraseService.updatePassPhrase(with: $0, inStorage: shouldStorePassPhrase) + } + + newKeysToAdd + .map { + PassPhrase(value: passPhrase, fingerprints: $0.fingerprints) + } + .forEach { + passPhraseService.savePassPhrase(with: $0, inStorage: shouldStorePassPhrase) + } + } + + hideSpinner() let updated = keysToUpdate.count diff --git a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift index 541e56b21..3be3512c4 100644 --- a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift +++ b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift @@ -180,6 +180,15 @@ extension EncryptedStorage { } } + func updateKeys(with primaryFingerprint: String, passphrase: String?) { + let keys = keysInfo() + .filter { $0.primaryFingerprint == primaryFingerprint } + + try! storage.write { + keys.map { $0.passphrase = passphrase } + } + } + func keysInfo() -> [KeyInfo] { let result = storage.objects(KeyInfo.self) return Array(result) @@ -203,6 +212,25 @@ extension EncryptedStorage { } } +// MARK: - PassPhrase +extension EncryptedStorage: PassPhraseStorageType { + func save(passPhrase: PassPhrase) { + updateKeys(with: passPhrase.primaryFingerprint, passphrase: passPhrase.value) + } + + func update(passPhrase: PassPhrase) { + updateKeys(with: passPhrase.primaryFingerprint, passphrase: passPhrase.value) + } + + func remove(passPhrase: PassPhrase) { + updateKeys(with: passPhrase.primaryFingerprint, passphrase: nil) + } + + func getPassPhrases() -> [PassPhrase] { + keysInfo().compactMap(PassPhrase.init) + } +} + // MARK: - User extension EncryptedStorage { var activeUser: UserObject? { diff --git a/FlowCrypt/Functionality/Services/Key Services/InMemoryPassPhraseStorage.swift b/FlowCrypt/Functionality/Services/Key Services/InMemoryPassPhraseStorage.swift index 193b889ed..13e5d2c2b 100644 --- a/FlowCrypt/Functionality/Services/Key Services/InMemoryPassPhraseStorage.swift +++ b/FlowCrypt/Functionality/Services/Key Services/InMemoryPassPhraseStorage.swift @@ -29,6 +29,15 @@ final class InMemoryPassPhraseStorage: PassPhraseStorageType { passPhraseProvider.save(passPhrase: passPhraseToSave) } + func update(passPhrase: PassPhrase) { + let passPhraseToSave = passPhrase.withUpdatedDate() + passPhraseProvider.save(passPhrase: passPhraseToSave) + } + + func remove(passPhrase: PassPhrase) { + passPhraseProvider.removePassPhrases(with: passPhrase) + } + func getPassPhrases() -> [PassPhrase] { passPhraseProvider.passPhrases .compactMap { passPhrase -> PassPhrase? in @@ -61,6 +70,7 @@ final class InMemoryPassPhraseStorage: PassPhraseStorageType { protocol InMemoryPassPhraseProviderType { var passPhrases: Set { get } func save(passPhrase: PassPhrase) + func removePassPhrases(with objects: PassPhrase) } /// - Warning: - should be shared instance @@ -75,4 +85,10 @@ final class InMemoryPassPhraseProvider: InMemoryPassPhraseProviderType { func save(passPhrase: PassPhrase) { passPhrases.insert(passPhrase) } + + func removePassPhrases(with objects: PassPhrase) { + if passPhrases.contains(objects) { + passPhrases.remove(objects) + } + } } diff --git a/FlowCrypt/Functionality/Services/Key Services/KeyService.swift b/FlowCrypt/Functionality/Services/Key Services/KeyService.swift index c77ee432e..f2b11b1a0 100644 --- a/FlowCrypt/Functionality/Services/Key Services/KeyService.swift +++ b/FlowCrypt/Functionality/Services/Key Services/KeyService.swift @@ -66,11 +66,9 @@ final class KeyService: KeyServiceType { let keysInfo = storage.keysInfo() .filter { $0.account == email } - let keysWithPassPhrases = keysInfo.filter { $0.passphrase != nil } - let storedPassPhrases = passPhraseService.getPassPhrases() - if passPhrase == nil, storedPassPhrases.isEmpty, keysWithPassPhrases.isEmpty { + if passPhrase == nil, storedPassPhrases.isEmpty { // in case there are no pass phrases in storage/memory // and user did not enter a pass phrase yet return .failure(.missedPassPhrase) @@ -102,21 +100,19 @@ final class KeyService: KeyServiceType { } // append keys to ensure with a pass phrase - let keysToEnsure: [PrvKeyInfo] = keysInfo.compactMap { - guard let passPhraseValue = $0.passphrase ?? passPhrase else { - return nil + if let passPhrase = passPhrase { + let keysToEnsure = keysInfo.map { + PrvKeyInfo( + private: $0.private, + longid: $0.primaryLongid, + passphrase: passPhrase, + fingerprints: Array($0.allFingerprints) + ) } - return PrvKeyInfo( - private: $0.private, - longid: $0.primaryLongid, - passphrase: passPhraseValue, - fingerprints: Array($0.allFingerprints) - ) + privateKeys.append(contentsOf: keysToEnsure) } - privateKeys.append(contentsOf: keysToEnsure) - return .success(privateKeys) } } diff --git a/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift b/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift index 2e88549ca..fdfee264b 100644 --- a/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift +++ b/FlowCrypt/Functionality/Services/Key Services/PassPhraseService.swift @@ -38,9 +38,20 @@ struct PassPhrase: Codable, Hashable, Equatable { } } +extension PassPhrase { + init?(keyInfo: KeyInfo) { + guard let passphrase = keyInfo.passphrase else { return nil } + + self.init(value: passphrase, + fingerprints: Array(keyInfo.allFingerprints)) + } +} + // MARK: - Pass Phrase Storage protocol PassPhraseStorageType { func save(passPhrase: PassPhrase) + func update(passPhrase: PassPhrase) + func remove(passPhrase: PassPhrase) func getPassPhrases() -> [PassPhrase] } @@ -49,31 +60,52 @@ protocol PassPhraseStorageType { protocol PassPhraseServiceType { func getPassPhrases() -> [PassPhrase] func savePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) + func updatePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) } final class PassPhraseService: PassPhraseServiceType { private lazy var logger = Logger.nested(Self.self) let currentUserEmail: String? + let encryptedStorage: PassPhraseStorageType let inMemoryStorage: PassPhraseStorageType init( + encryptedStorage: PassPhraseStorageType = EncryptedStorage(), localStorage: PassPhraseStorageType = InMemoryPassPhraseStorage(), emailProvider: EmailProviderType = DataService.shared ) { + self.encryptedStorage = encryptedStorage self.inMemoryStorage = localStorage self.currentUserEmail = emailProvider.email } func savePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) { - if !inStorage { + if inStorage { + logger.logInfo("Save passphrase to storage") + encryptedStorage.save(passPhrase: passPhrase) + } else { logger.logInfo("Save passphrase in memory") inMemoryStorage.save(passPhrase: passPhrase) + + let alreadySaved = encryptedStorage.getPassPhrases() + + if alreadySaved.contains(where: { $0.primaryFingerprint == passPhrase.primaryFingerprint }) { + encryptedStorage.remove(passPhrase: passPhrase) + } + } + } + + func updatePassPhrase(with passPhrase: PassPhrase, inStorage: Bool) { + if inStorage { + encryptedStorage.update(passPhrase: passPhrase) + } else { + inMemoryStorage.save(passPhrase: passPhrase) } } func getPassPhrases() -> [PassPhrase] { - inMemoryStorage.getPassPhrases() + encryptedStorage.getPassPhrases() + inMemoryStorage.getPassPhrases() } } diff --git a/FlowCryptAppTests/Functionality/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift b/FlowCryptAppTests/Functionality/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift index 759335ed8..7f034f1a7 100644 --- a/FlowCryptAppTests/Functionality/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift +++ b/FlowCryptAppTests/Functionality/Services/PassPhraseStorageTests/InMemoryPassPhraseStorageTest.swift @@ -30,6 +30,21 @@ class InMemoryPassPhraseStorageTest: XCTestCase { XCTAssertNotNil($0.date) } } + + func testUpdatePassPhraseUpdatesDate() { + let pass = PassPhrase(value: "A", fingerprints: ["11","12"]) + sut.update(passPhrase: pass) + passPhraseProvider.passPhrases.forEach { + XCTAssertNotNil($0.date) + } + } + + func testRemovePassPhrase() { + let pass = PassPhrase(value: "A", fingerprints: ["11","12"]) + sut.save(passPhrase: pass) + sut.remove(passPhrase: pass) + XCTAssertTrue(passPhraseProvider.passPhrases.isEmpty) + } func testGetPassPhrases() { XCTAssertTrue(sut.getPassPhrases().isEmpty) @@ -58,4 +73,10 @@ class InMemoryPassPhraseProviderMock: InMemoryPassPhraseProviderType { func save(passPhrase: PassPhrase) { passPhrases.insert(passPhrase) } + + func removePassPhrases(with objects: PassPhrase) { + if passPhrases.contains(objects) { + passPhrases.remove(objects) + } + } } diff --git a/FlowCryptAppTests/Functionality/Services/PassPhraseStorageTests/PassPhraseStorageTests.swift b/FlowCryptAppTests/Functionality/Services/PassPhraseStorageTests/PassPhraseStorageTests.swift index dc491fcea..298460ce2 100644 --- a/FlowCryptAppTests/Functionality/Services/PassPhraseStorageTests/PassPhraseStorageTests.swift +++ b/FlowCryptAppTests/Functionality/Services/PassPhraseStorageTests/PassPhraseStorageTests.swift @@ -10,31 +10,36 @@ import XCTest @testable import FlowCrypt class PassPhraseStorageTests: XCTestCase { - + var sut: PassPhraseService! + var encryptedStorage: PassPhraseStorageMock! var inMemoryStorage: PassPhraseStorageMock! var emailProvider: EmailProviderMock! - + override func setUp() { emailProvider = EmailProviderMock() + encryptedStorage = PassPhraseStorageMock() inMemoryStorage = PassPhraseStorageMock() - + sut = PassPhraseService( + encryptedStorage: encryptedStorage, localStorage: inMemoryStorage, emailProvider: emailProvider ) } - + func testGetPassPhrasesWhenEmpty() { + // no pass phrases in storage + encryptedStorage.getPassPhrasesResult = { [] } // no pass phrases in localStorage inMemoryStorage.getPassPhrasesResult = { [] } - + let result = sut.getPassPhrases() - + XCTAssertTrue(result.isEmpty) } - - func testGetValidPassPhraseFromLocalStorage() { + + func testGetValidPassPhraseFromStorage() { let passPhrase1 = PassPhrase( value: "some", fingerprints: ["11","12"] @@ -44,23 +49,26 @@ class PassPhraseStorageTests: XCTestCase { fingerprints: ["21","22"] ) + encryptedStorage.getPassPhrasesResult = { [passPhrase1] } // no pass phrases in localStorage - inMemoryStorage.getPassPhrasesResult = { [passPhrase1] } - + inMemoryStorage.getPassPhrasesResult = { [] } + var result = sut.getPassPhrases() - + XCTAssertTrue(result.count == 1) - - inMemoryStorage.getPassPhrasesResult = { + + encryptedStorage.getPassPhrasesResult = { [passPhrase1, passPhrase2] } - + result = sut.getPassPhrases() - + XCTAssertTrue(result.count == 2) } - + func testGetValidPassPhraseInLocalStorage() { + encryptedStorage.getPassPhrasesResult = { [] } + let savedDate = Date() let localPassPhrase = PassPhrase( value: "value", @@ -68,52 +76,86 @@ class PassPhraseStorageTests: XCTestCase { date: savedDate ) inMemoryStorage.getPassPhrasesResult = { [localPassPhrase] } - + // current timeout = 2 sleep(1) - + let result = sut.getPassPhrases() XCTAssertTrue(result.isNotEmpty) } - - func testLocalStorageContainsValidPassPhrase() { + + func testBothStorageContainsValidPassPhrase() { + let passPhrase1 = PassPhrase( + value: "some", + fingerprints: ["A123"] + ) + let passPhrase2 = PassPhrase( + value: "some", + fingerprints: ["A123"] + ) + encryptedStorage.getPassPhrasesResult = { + [passPhrase1, passPhrase2] + } + let savedDate = Date() let localPassPhrase = PassPhrase( value: "value", fingerprints: ["123444"], date: savedDate ) - + inMemoryStorage.getPassPhrasesResult = { [localPassPhrase] } - + let result = sut.getPassPhrases() - XCTAssertTrue(result.count == 1) + XCTAssertTrue(result.count == 3) } - + func testSavePassPhraseInStorage() { let passPhraseToSave = PassPhrase(value: "pass", fingerprints: ["fingerprint 1", "123333"]) - + let expectation = XCTestExpectation() expectation.expectedFulfillmentCount = 1 expectation.isInverted = true - + + // encrypted storage contains pass phrase which should be saved locally + encryptedStorage.getPassPhrasesResult = { + [ + PassPhrase(value: "pass", fingerprints: ["fingerprint 1", "adfnhjfg"]) + ] + } + + + // encrypted storage should not contains pass phrase which user decide to save locally + encryptedStorage.isRemovePassPhraseResult = { passPhraseToRemove in + if passPhraseToRemove.primaryFingerprint == "fingerprint 1" { + expectation.fulfill() + } + } + sut.savePassPhrase(with: passPhraseToSave, inStorage: true) - + XCTAssertFalse(inMemoryStorage.saveResult != nil ) - + wait(for: [expectation], timeout: 0.1, enforceOrder: false) } - + func testSavePassPhraseInStorageWithoutAnyPassPhrases() { let passPhraseToSave = PassPhrase(value: "pass", fingerprints: ["fingerprint 1", "123333"]) - + let expectation = XCTestExpectation() expectation.isInverted = true + // encrypted storage is empty + encryptedStorage.getPassPhrasesResult = { [ ] } + + encryptedStorage.isRemovePassPhraseResult = { _ in + expectation.fulfill() + } + sut.savePassPhrase(with: passPhraseToSave, inStorage: true) - + XCTAssertFalse(inMemoryStorage.saveResult != nil ) - + wait(for: [expectation], timeout: 0.1, enforceOrder: false) } From d4c85938f1fb9421e56a786eb1c6b13d22597ad1 Mon Sep 17 00:00:00 2001 From: Roma Sosnovsky Date: Fri, 1 Oct 2021 13:54:29 +0300 Subject: [PATCH 5/5] issue #441 update keysToEnsure handling in KeyService --- .../Services/Key Services/KeyService.swift | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/FlowCrypt/Functionality/Services/Key Services/KeyService.swift b/FlowCrypt/Functionality/Services/Key Services/KeyService.swift index f2b11b1a0..b35accdbc 100644 --- a/FlowCrypt/Functionality/Services/Key Services/KeyService.swift +++ b/FlowCrypt/Functionality/Services/Key Services/KeyService.swift @@ -100,19 +100,19 @@ final class KeyService: KeyServiceType { } // append keys to ensure with a pass phrase - if let passPhrase = passPhrase { - let keysToEnsure = keysInfo.map { - PrvKeyInfo( - private: $0.private, - longid: $0.primaryLongid, - passphrase: passPhrase, - fingerprints: Array($0.allFingerprints) - ) - } - - privateKeys.append(contentsOf: keysToEnsure) + let keysToEnsure: [PrvKeyInfo] = keysInfo.compactMap { + guard let passPhraseValue = $0.passphrase ?? passPhrase else { return nil } + + return PrvKeyInfo( + private: $0.private, + longid: $0.primaryLongid, + passphrase: passPhraseValue, + fingerprints: Array($0.allFingerprints) + ) } + privateKeys.append(contentsOf: keysToEnsure) + return .success(privateKeys) } }