From fcc8961e46513b41bcdeccac62ba9f512fa26739 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sat, 18 Apr 2020 22:35:17 -0700 Subject: [PATCH] Fix fail-safe mechanism for other decryption scenarios --- pass.xcodeproj/project.pbxproj | 16 ++---- .../PasswordDetailTableViewController.swift | 9 +--- .../Controllers/PasswordsViewController.swift | 31 ++++++++--- pass/Extensions/UIAlertActionExtension.swift | 44 --------------- pass/Helpers/GitCredentialPassword.swift | 4 +- .../CredentialProviderViewController.swift | 29 +++++++--- .../Controllers/ExtensionViewController.swift | 34 ++++++++---- .../PasscodeLockViewController.swift | 2 +- passKit/Crypto/PGPAgent.swift | 9 +++- .../Extensions/UIAlertActionExtension.swift | 53 +++++++++++++++++++ passKit/Helpers/Utils.swift | 10 ++-- passKit/Models/Password.swift | 4 ++ passKit/Models/PasswordStore.swift | 2 +- passKitTests/Crypto/PGPAgentTest.swift | 2 +- passKitTests/Models/PasswordStoreTest.swift | 4 +- 15 files changed, 153 insertions(+), 100 deletions(-) delete mode 100644 pass/Extensions/UIAlertActionExtension.swift create mode 100644 passKit/Extensions/UIAlertActionExtension.swift diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index a586a02..91e3a16 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -95,7 +95,7 @@ 556EC3DA22335D3400934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; }; 556EC3DB22335D3D00934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; }; 8BA607EB4C9C8258741AC18C /* Pods_passExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14E955B67C88672AA3A40BA0 /* Pods_passExtension.framework */; }; - 9A652412244ABED400DA0A41 /* UIAlertActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A652411244ABED400DA0A41 /* UIAlertActionExtension.swift */; }; + 9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */; }; 9A8A8387402FCCCECB1232A4 /* Pods_passKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2B2F844061EFA534FE9506 /* Pods_passKitTests.framework */; }; 9AA710CA23939C68009E3213 /* GitCredentialPassword.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA710C923939C68009E3213 /* GitCredentialPassword.swift */; }; 9ADC954124418A5F0005402E /* PasswordStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */; }; @@ -348,7 +348,7 @@ 62DEE9943E0F2B8C79E3FC5B /* Pods-passExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-passExtension/Pods-passExtension.release.xcconfig"; sourceTree = ""; }; 64AA8DF9E73F39CCC3317247 /* Pods-passKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-passKit/Pods-passKit.release.xcconfig"; sourceTree = ""; }; 7CAD21E487234A0631B52E20 /* Pods-passKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-passKit/Pods-passKit.debug.xcconfig"; sourceTree = ""; }; - 9A652411244ABED400DA0A41 /* UIAlertActionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertActionExtension.swift; sourceTree = ""; }; + 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertActionExtension.swift; sourceTree = ""; }; 9AA710C923939C68009E3213 /* GitCredentialPassword.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitCredentialPassword.swift; sourceTree = ""; }; 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordStoreTest.swift; sourceTree = ""; }; A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurePasteboard.swift; sourceTree = ""; }; @@ -590,6 +590,7 @@ 30697C3721F63C990064FCAC /* UITextFieldExtension.swift */, 30697C3521F63C990064FCAC /* UIViewControllerExtension.swift */, 30697C3821F63C990064FCAC /* UIViewExtension.swift */, + 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */, ); path = Extensions; sourceTree = ""; @@ -650,14 +651,6 @@ path = Crypto; sourceTree = ""; }; - 9A652410244ABEB800DA0A41 /* Extensions */ = { - isa = PBXGroup; - children = ( - 9A652411244ABED400DA0A41 /* UIAlertActionExtension.swift */, - ); - path = Extensions; - sourceTree = ""; - }; A2168A801EFD431A005EA873 /* Controllers */ = { isa = PBXGroup; children = ( @@ -896,7 +889,6 @@ DC917BD51E2E8231000FDF54 /* pass */ = { isa = PBXGroup; children = ( - 9A652410244ABEB800DA0A41 /* Extensions */, DC19400C1E4B39400077E0A3 /* Controllers */, DC19400E1E4B3A610077E0A3 /* Helpers */, DC19400F1E4B3A9E0077E0A3 /* Views */, @@ -1449,6 +1441,7 @@ 30A1D2A221B2BC6F00E2D1F7 /* TokenBuilder.swift in Sources */, 30697C3C21F63C990064FCAC /* UITextFieldExtension.swift in Sources */, 302E85632125EE550031BA64 /* Constants.swift in Sources */, + 9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */, 301F6463216162550071A4CE /* AdditionField.swift in Sources */, 30697C3021F63C5A0064FCAC /* AppError.swift in Sources */, 30697C2B21F63C5A0064FCAC /* Globals.swift in Sources */, @@ -1541,7 +1534,6 @@ DC037CBF1E4ED4E100609409 /* TextViewTableViewCell.swift in Sources */, DCC441541E916382008A90C4 /* SSHKeyArmorImportTableViewController.swift in Sources */, 306D970E24091CDD006C0E2E /* SwitchTableViewCell.swift in Sources */, - 9A652412244ABED400DA0A41 /* UIAlertActionExtension.swift in Sources */, A2A61C201EEFABAD00CFE063 /* UtilsExtension.swift in Sources */, DC8963C01E38EEB900828B09 /* SSHKeyUrlImportTableViewController.swift in Sources */, 3066AD6823EE0D6500F65535 /* PGPKeyImporter.swift in Sources */, diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 8152b27..9c8d949 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -80,9 +80,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni super.viewWillAppear(animated) if self.shouldPopCurrentView { let alert = UIAlertController(title: "Notice".localize(), message: "PreviousChangesDiscarded.".localize(), preferredStyle: UIAlertController.Style.alert) - alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in - _ = self.navigationController?.popViewController(animated: true) - })) + alert.addAction(UIAlertAction.okAndPopView(controller: self)) self.present(alert, animated: true, completion: nil) } } @@ -99,6 +97,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni do { let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: self) self.password = try self.passwordStore.decrypt(passwordEntity: passwordEntity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase) + self.showPassword() } catch AppError.PgpPrivateKeyNotFound(let key) { DispatchQueue.main.async { // alert: cancel or try again @@ -111,7 +110,6 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni self.present(alert, animated: true, completion: nil) } - return } catch { DispatchQueue.main.async { // alert: cancel or try again @@ -122,10 +120,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni }) self.present(alert, animated: true, completion: nil) } - return } - // display password - self.showPassword() } } diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index 380ff41..50e2631 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -294,7 +294,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } self.reloadTableView(data: filteredPasswordsTableEntries, label: .unsynced) } - let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) + let cancelAction = UIAlertAction.cancel() ac.addAction(allAction) ac.addAction(unsyncedAction) @@ -453,32 +453,49 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV private func decryptThenCopyPassword(from indexPath: IndexPath) { guard PGPAgent.shared.isPrepared else { - Utils.alert(title: "CannotCopyPassword".localize(), message: "PgpKeyNotSet.".localize(), controller: self, completion: nil) + Utils.alert(title: "CannotCopyPassword".localize(), message: "PgpKeyNotSet.".localize(), controller: self) return } let passwordEntity = getPasswordEntry(by: indexPath).passwordEntity UIImpactFeedbackGenerator(style: .medium).impactOccurred() SVProgressHUD.dismiss() + self.decryptPassword(passwordEntity: passwordEntity) + } + + private func decryptPassword(passwordEntity: PasswordEntity, keyID: String? = nil) { DispatchQueue.global(qos: .userInteractive).async { - var decryptedPassword: Password? do { let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: self) - decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: requestPGPKeyPassphrase) + let decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase) + DispatchQueue.main.async { - SecurePasteboard.shared.copy(textToCopy: decryptedPassword?.password) + SecurePasteboard.shared.copy(textToCopy: decryptedPassword.password) SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultStyle(.dark) SVProgressHUD.showSuccess(withStatus: "PasswordCopiedToPasteboard.".localize()) SVProgressHUD.dismiss(withDelay: 0.6) } - } catch { + } catch AppError.PgpPrivateKeyNotFound(let key) { DispatchQueue.main.async { - Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self, completion: nil) + // alert: cancel or try again + let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.PgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction.cancelAndPopView(controller: self)) + let selectKey = UIAlertAction.selectKey(controller: self) { action in + self.decryptPassword(passwordEntity: passwordEntity, keyID: action.title) + } + alert.addAction(selectKey) + + self.present(alert, animated: true) + } + } catch { + DispatchQueue.main.async { + Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self) } } } } + private func generateSections(item: [PasswordTableEntry]) { let collation = UILocalizedIndexedCollation.current() let sectionTitles = collation.sectionIndexTitles diff --git a/pass/Extensions/UIAlertActionExtension.swift b/pass/Extensions/UIAlertActionExtension.swift deleted file mode 100644 index 20445e7..0000000 --- a/pass/Extensions/UIAlertActionExtension.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// UIAlertActionExtension.swift -// passKit -// -// Created by Sun, Mingshen on 4/17/20. -// Copyright © 2020 Bob Sun. All rights reserved. -// - -import UIKit -import Foundation -import passKit - -extension UIAlertAction { - static func cancelAndPopView(controller: UIViewController) -> UIAlertAction { - UIAlertAction(title: "Cancel".localize(), style: .cancel) { _ in - controller.navigationController?.popViewController(animated: true) - } - } - - static func cancel() -> UIAlertAction { - cancel(with: "Cancel") - } - - static func dismiss() -> UIAlertAction { - cancel(with: "Dismiss") - } - - static func cancel(with title: String) -> UIAlertAction { - UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil) - } - - static func selectKey(controller: UIViewController, handler: ((UIAlertAction) -> Void)?) -> UIAlertAction { - UIAlertAction(title: "Select Key", style: .default) { _ in - let selectKeyAlert = UIAlertController(title: "Select from imported keys", message: nil, preferredStyle: .actionSheet) - try? PGPAgent.shared.getShortKeyID().forEach({ k in - let action = UIAlertAction(title: k, style: .default, handler: handler) - selectKeyAlert.addAction(action) - }) - selectKeyAlert.addAction(UIAlertAction.cancelAndPopView(controller: controller)) - controller.present(selectKeyAlert, animated: true, completion: nil) - } - } - -} diff --git a/pass/Helpers/GitCredentialPassword.swift b/pass/Helpers/GitCredentialPassword.swift index 8302797..c07beef 100644 --- a/pass/Helpers/GitCredentialPassword.swift +++ b/pass/Helpers/GitCredentialPassword.swift @@ -31,11 +31,11 @@ public func requestGitCredentialPassword(credential: GitCredential.Credential, $0.text = lastPassword ?? "" $0.isSecureTextEntry = true } - alert.addAction(UIAlertAction(title: "Ok".localize(), style: .default) { _ in + alert.addAction(UIAlertAction.ok() { _ in password = alert.textFields?.first?.text sem.signal() }) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel) { _ in + alert.addAction(UIAlertAction.cancel() { _ in password = nil sem.signal() }) diff --git a/passAutoFillExtension/Controllers/CredentialProviderViewController.swift b/passAutoFillExtension/Controllers/CredentialProviderViewController.swift index 655ccc0..e2e8373 100644 --- a/passAutoFillExtension/Controllers/CredentialProviderViewController.swift +++ b/passAutoFillExtension/Controllers/CredentialProviderViewController.swift @@ -139,20 +139,35 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa let passwordEntity = entry.passwordEntity UIImpactFeedbackGenerator(style: .medium).impactOccurred() + self.decryptPassword(passwordEntity: passwordEntity) + } + + private func decryptPassword(passwordEntity: PasswordEntity, keyID: String? = nil) { DispatchQueue.global(qos: .userInteractive).async { - var decryptedPassword: Password? do { let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: self) - decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: requestPGPKeyPassphrase) - let username = decryptedPassword?.username ?? decryptedPassword?.login ?? "" - let password = decryptedPassword?.password ?? "" - DispatchQueue.main.async {// prepare a dictionary to return + let decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase) + + let username = decryptedPassword.getUsernameForCompletion() + let password = decryptedPassword.password + DispatchQueue.main.async { let passwordCredential = ASPasswordCredential(user: username, password: password) - self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil) + self.extensionContext.completeRequest(withSelectedCredential: passwordCredential) + } + } catch AppError.PgpPrivateKeyNotFound(let key) { + DispatchQueue.main.async { + let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.PgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction.cancelAndPopView(controller: self)) + let selectKey = UIAlertAction.selectKey(controller: self) { action in + self.decryptPassword(passwordEntity: passwordEntity, keyID: action.title) + } + alert.addAction(selectKey) + + self.present(alert, animated: true) } } catch { DispatchQueue.main.async { - Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self, completion: nil) + Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self) } } } diff --git a/passExtension/Controllers/ExtensionViewController.swift b/passExtension/Controllers/ExtensionViewController.swift index 9ffe6c9..bc39800 100644 --- a/passExtension/Controllers/ExtensionViewController.swift +++ b/passExtension/Controllers/ExtensionViewController.swift @@ -147,36 +147,52 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV let passwordEntity = entry.passwordEntity UIImpactFeedbackGenerator(style: .medium).impactOccurred() + self.decryptPassword(passwordEntity: passwordEntity) + } + + private func decryptPassword(passwordEntity: PasswordEntity, keyID: String? = nil) { DispatchQueue.global(qos: .userInteractive).async { - var decryptedPassword: Password? do { let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: self) - decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: requestPGPKeyPassphrase) - let username = decryptedPassword?.username ?? decryptedPassword?.login ?? decryptedPassword?.nameFromPath ?? "" - let password = decryptedPassword?.password ?? "" + let decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase) + + let username = decryptedPassword.getUsernameForCompletion() + let password = decryptedPassword.password DispatchQueue.main.async {// prepare a dictionary to return switch self.extensionAction { case .findLogin: let extensionItem = NSExtensionItem() var returnDictionary = [OnePasswordExtensionKey.usernameKey: username, OnePasswordExtensionKey.passwordKey: password] - if let totpPassword = decryptedPassword?.currentOtp { + if let totpPassword = decryptedPassword.currentOtp { returnDictionary[OnePasswordExtensionKey.totpKey] = totpPassword } extensionItem.attachments = [NSItemProvider(item: returnDictionary as NSSecureCoding, typeIdentifier: String(kUTTypePropertyList))] self.extensionContext!.completeRequest(returningItems: [extensionItem], completionHandler: nil) case .fillBrowser: - Utils.copyToPasteboard(textToCopy: decryptedPassword?.password) + Utils.copyToPasteboard(textToCopy: decryptedPassword.password) // return a dictionary for JavaScript for best-effor fill in let extensionItem = NSExtensionItem() let returnDictionary = [NSExtensionJavaScriptFinalizeArgumentKey : ["username": username, "password": password]] extensionItem.attachments = [NSItemProvider(item: returnDictionary as NSSecureCoding, typeIdentifier: String(kUTTypePropertyList))] - self.extensionContext!.completeRequest(returningItems: [extensionItem], completionHandler: nil) + self.extensionContext?.completeRequest(returningItems: [extensionItem], completionHandler: nil) default: - self.extensionContext!.completeRequest(returningItems: nil, completionHandler: nil) + self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } } - } catch { + } catch AppError.PgpPrivateKeyNotFound(let key) { + DispatchQueue.main.async { + // alert: cancel or try again + let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.PgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction.cancelAndPopView(controller: self)) + let selectKey = UIAlertAction.selectKey(controller: self) { action in + self.decryptPassword(passwordEntity: passwordEntity, keyID: action.title) + } + alert.addAction(selectKey) + + self.present(alert, animated: true, completion: nil) + } + } catch { DispatchQueue.main.async { Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self, completion: nil) } diff --git a/passKit/Controllers/PasscodeLockViewController.swift b/passKit/Controllers/PasscodeLockViewController.swift index 9696574..a1cee8b 100644 --- a/passKit/Controllers/PasscodeLockViewController.swift +++ b/passKit/Controllers/PasscodeLockViewController.swift @@ -213,7 +213,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { } } })) - alert.addAction(UIAlertAction(title: "Dismiss".localize(), style: .cancel, handler: nil)) + alert.addAction(UIAlertAction.dismiss()) self.present(alert, animated: true, completion: nil) } diff --git a/passKit/Crypto/PGPAgent.swift b/passKit/Crypto/PGPAgent.swift index 2e693ee..d2260a1 100644 --- a/passKit/Crypto/PGPAgent.swift +++ b/passKit/Crypto/PGPAgent.swift @@ -45,15 +45,20 @@ public class PGPAgent { return pgpInterface?.shortKeyID.sorted() ?? [] } - public func decrypt(encryptedData: Data, keyID: String, requestPGPKeyPassphrase: (String) -> String) throws -> Data? { + public func decrypt(encryptedData: Data, keyID: String, requestPGPKeyPassphrase: @escaping (String) -> String) throws -> Data? { // Init keys. try checkAndInit() guard let pgpInterface = pgpInterface else { throw AppError.Decryption } + var keyID = keyID; if !pgpInterface.containsPrivateKey(with: keyID) { - throw AppError.PgpPrivateKeyNotFound(keyID: keyID) + if pgpInterface.keyID.count == 1 { + keyID = pgpInterface.keyID.first! + } else { + throw AppError.PgpPrivateKeyNotFound(keyID: keyID) + } } // Remember the previous status and set the current status diff --git a/passKit/Extensions/UIAlertActionExtension.swift b/passKit/Extensions/UIAlertActionExtension.swift new file mode 100644 index 0000000..53f97d5 --- /dev/null +++ b/passKit/Extensions/UIAlertActionExtension.swift @@ -0,0 +1,53 @@ +// +// UIAlertActionExtension.swift +// passKit +// +// Created by Sun, Mingshen on 4/17/20. +// Copyright © 2020 Bob Sun. All rights reserved. +// + +import UIKit +import Foundation + +extension UIAlertAction { + public static func cancelAndPopView(controller: UIViewController) -> UIAlertAction { + return cancel() { _ in + controller.navigationController?.popViewController(animated: true) + } + } + + public static func cancel(handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction { + cancel(with: "Cancel", handler: handler) + } + + public static func dismiss(handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction { + cancel(with: "Dismiss", handler: handler) + } + + public static func cancel(with title: String, handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction { + UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: handler) + } + + public static func ok(handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction { + UIAlertAction(title: "Ok".localize(), style: .default, handler: handler) + } + + public static func okAndPopView(controller: UIViewController) -> UIAlertAction { + return ok() { _ in + controller.navigationController?.popViewController(animated: true) + } + } + + public static func selectKey(controller: UIViewController, handler: ((UIAlertAction) -> Void)?) -> UIAlertAction { + UIAlertAction(title: "Select Key", style: .default) { _ in + let selectKeyAlert = UIAlertController(title: "Select from imported keys", message: nil, preferredStyle: .actionSheet) + try? PGPAgent.shared.getShortKeyID().forEach({ k in + let action = UIAlertAction(title: k, style: .default, handler: handler) + selectKeyAlert.addAction(action) + }) + selectKeyAlert.addAction(UIAlertAction.cancelAndPopView(controller: controller)) + controller.present(selectKeyAlert, animated: true, completion: nil) + } + } + +} diff --git a/passKit/Helpers/Utils.swift b/passKit/Helpers/Utils.swift index ff26b3c..be8f0c6 100644 --- a/passKit/Helpers/Utils.swift +++ b/passKit/Helpers/Utils.swift @@ -47,15 +47,15 @@ public class Utils { let title = "Passphrase".localize() + " (\(keyID.suffix(8)))" let message = "FillInPgpPassphrase.".localize() let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in + alert.addAction(UIAlertAction.ok() { _ in passphrase = alert.textFields?.first?.text ?? "" sem.signal() - })) - alert.addTextField(configurationHandler: {(textField: UITextField!) in + }) + alert.addTextField() { textField in textField.text = AppKeychain.shared.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? "" textField.isSecureTextEntry = true - }) - controller.present(alert, animated: true, completion: nil) + } + controller.present(alert, animated: true) } let _ = sem.wait(timeout: DispatchTime.distantFuture) if Defaults.isRememberPGPPassphraseOn { diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index 093fa0c..37acd74 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -213,4 +213,8 @@ public class Password { // get and return the password return self.otpToken?.currentPassword } + + public func getUsernameForCompletion() -> String { + username ?? login ?? nameFromPath ?? "" + } } diff --git a/passKit/Models/PasswordStore.swift b/passKit/Models/PasswordStore.swift index aa35fac..256283c 100644 --- a/passKit/Models/PasswordStore.swift +++ b/passKit/Models/PasswordStore.swift @@ -698,7 +698,7 @@ public class PasswordStore { return try storeRepository.localCommitsRelative(toRemoteBranch: remoteBranch) } - public func decrypt(passwordEntity: PasswordEntity, keyID: String? = nil, requestPGPKeyPassphrase: (String) -> String) throws -> Password? { + public func decrypt(passwordEntity: PasswordEntity, keyID: String? = nil, requestPGPKeyPassphrase: @escaping (String) -> String) throws -> Password { let encryptedDataPath = storeURL.appendingPathComponent(passwordEntity.getPath()) let keyID = keyID ?? findGPGID(from: encryptedDataPath) let encryptedData = try Data(contentsOf: encryptedDataPath) diff --git a/passKitTests/Crypto/PGPAgentTest.swift b/passKitTests/Crypto/PGPAgentTest.swift index 9cade13..0da0a25 100644 --- a/passKitTests/Crypto/PGPAgentTest.swift +++ b/passKitTests/Crypto/PGPAgentTest.swift @@ -31,7 +31,7 @@ class PGPAgentTest: XCTestCase { super.tearDown() } - func basicEncryptDecrypt(using pgpAgent: PGPAgent, keyID: String, encryptKeyID: String? = nil, requestPassphrase: (String) -> String = requestPGPKeyPassphrase, encryptInArmored: Bool = true, encryptInArmoredNow: Bool = true) throws -> Data? { + func basicEncryptDecrypt(using pgpAgent: PGPAgent, keyID: String, encryptKeyID: String? = nil, requestPassphrase: @escaping (String) -> String = requestPGPKeyPassphrase, encryptInArmored: Bool = true, encryptInArmoredNow: Bool = true) throws -> Data? { passKit.Defaults.encryptInArmored = encryptInArmored let encryptedData = try pgpAgent.encrypt(plainData: testData, keyID: keyID) passKit.Defaults.encryptInArmored = encryptInArmoredNow diff --git a/passKitTests/Models/PasswordStoreTest.swift b/passKitTests/Models/PasswordStoreTest.swift index c42d9e8..6662f42 100644 --- a/passKitTests/Models/PasswordStoreTest.swift +++ b/passKitTests/Models/PasswordStoreTest.swift @@ -57,7 +57,7 @@ class PasswordStoreTest: XCTestCase { let testPassword = Password(name: "test", url: URL(string: "test.gpg")!, plainText: "testpassword") let testPasswordEntity = try passwordStore.add(password: testPassword)! - let testPasswordPlain = try passwordStore.decrypt(passwordEntity: testPasswordEntity, requestPGPKeyPassphrase: requestPGPKeyPassphrase )! + let testPasswordPlain = try passwordStore.decrypt(passwordEntity: testPasswordEntity, requestPGPKeyPassphrase: requestPGPKeyPassphrase ) XCTAssertEqual(testPasswordPlain.plainText, "testpassword") passwordStore.erase() @@ -65,7 +65,7 @@ class PasswordStoreTest: XCTestCase { private func decrypt(passwordStore: PasswordStore, path: String, passphrase: String) throws -> Password { let entity = passwordStore.getPasswordEntity(by: path, isDir: false)! - return try passwordStore.decrypt(passwordEntity: entity, requestPGPKeyPassphrase: requestPGPKeyPassphrase )! + return try passwordStore.decrypt(passwordEntity: entity, requestPGPKeyPassphrase: requestPGPKeyPassphrase ) }