diff --git a/pass/Controllers/GeneralSettingsTableViewController.swift b/pass/Controllers/GeneralSettingsTableViewController.swift index edfeadd..8b29182 100644 --- a/pass/Controllers/GeneralSettingsTableViewController.swift +++ b/pass/Controllers/GeneralSettingsTableViewController.swift @@ -11,7 +11,21 @@ import SwiftyUserDefaults class GeneralSettingsTableViewController: BasicStaticTableViewController { - let hideUnknownSwitch = UISwitch() + let hideUnknownSwitch: UISwitch = { + let uiSwitch = UISwitch() + uiSwitch.onTintColor = Globals.blue + uiSwitch.sizeToFit() + uiSwitch.addTarget(self, action: #selector(hideUnknownSwitchAction(_:)), for: UIControlEvents.valueChanged) + return uiSwitch + }() + let rememberPassphraseSwitch: UISwitch = { + let uiSwitch = UISwitch() + uiSwitch.onTintColor = Globals.blue + uiSwitch.sizeToFit() + uiSwitch.addTarget(self, action: #selector(rememberPassphraseSwitchAction(_:)), for: UIControlEvents.valueChanged) + uiSwitch.isOn = Defaults[.isRememberPassphraseOn] + return uiSwitch + }() override func viewDidLoad() { navigationItemTitle = "General" @@ -20,7 +34,10 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { [[.title: "About Repository", .action: "segue", .link: "showAboutRepositorySegue"],], // section 1 - [[.title: "Hide Unknown Fields", .action: "none",],], + [ + [.title: "Remember Phassphrase", .action: "none",], + [.title: "Hide Unknown Fields", .action: "none",], + ], ] super.viewDidLoad() @@ -31,8 +48,6 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { let cell = super.tableView(tableView, cellForRowAt: indexPath) if cell.textLabel?.text == "Hide Unknown Fields" { cell.accessoryType = .none - hideUnknownSwitch.onTintColor = UIColor(displayP3Red: 0, green: 122.0/255, blue: 1, alpha: 1) - hideUnknownSwitch.sizeToFit() let detailButton = UIButton(type: .detailDisclosure) hideUnknownSwitch.frame = CGRect(x: detailButton.bounds.width+10, y: 0, width: hideUnknownSwitch.bounds.width, height: hideUnknownSwitch.bounds.height) detailButton.frame = CGRect(x: 0, y: 5, width: detailButton.bounds.width, height: detailButton.bounds.height) @@ -42,8 +57,11 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { accessoryView.addSubview(hideUnknownSwitch) cell.accessoryView = accessoryView cell.selectionStyle = .none - hideUnknownSwitch.addTarget(self, action: #selector(hideUnknownSwitchAction(_:)), for: UIControlEvents.valueChanged) hideUnknownSwitch.isOn = Defaults[.isHideUnknownOn] + } else if cell.textLabel?.text == "Remember Phassphrase" { + cell.accessoryType = .none + cell.selectionStyle = .none + cell.accessoryView = rememberPassphraseSwitch } return cell } @@ -58,4 +76,11 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { Defaults[.isHideUnknownOn] = hideUnknownSwitch.isOn } + func rememberPassphraseSwitchAction(_ sender: Any?) { + Defaults[.isRememberPassphraseOn] = rememberPassphraseSwitch.isOn + if rememberPassphraseSwitch.isOn == false { + PasswordStore.shared.pgpKeyPassphrase = nil + } + } + } diff --git a/pass/Controllers/PGPKeyArmorSettingTableViewController.swift b/pass/Controllers/PGPKeyArmorSettingTableViewController.swift index 445a3ae..e335755 100644 --- a/pass/Controllers/PGPKeyArmorSettingTableViewController.swift +++ b/pass/Controllers/PGPKeyArmorSettingTableViewController.swift @@ -21,11 +21,25 @@ class PGPKeyArmorSettingTableViewController: UITableViewController { pgpPassphrase = PasswordStore.shared.pgpKeyPassphrase } + private func createSavePassphraseAlert() -> UIAlertController { + let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later decryption?", preferredStyle: UIAlertControllerStyle.alert) + savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in + Defaults[.isRememberPassphraseOn] = false + self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) + }) + savePassphraseAlert.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.destructive) {_ in + Defaults[.isRememberPassphraseOn] = true + self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) + }) + return savePassphraseAlert + } + @IBAction func save(_ sender: Any) { - let alert = UIAlertController(title: "Phassphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) + let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in self.pgpPassphrase = alert.textFields?.first?.text - self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) + let savePassphraseAlert = self.createSavePassphraseAlert() + self.present(savePassphraseAlert, animated: true, completion: nil) })) alert.addTextField(configurationHandler: {(textField: UITextField!) in textField.text = self.pgpPassphrase diff --git a/pass/Controllers/PGPKeySettingTableViewController.swift b/pass/Controllers/PGPKeySettingTableViewController.swift index 8c195e4..17d0ea3 100644 --- a/pass/Controllers/PGPKeySettingTableViewController.swift +++ b/pass/Controllers/PGPKeySettingTableViewController.swift @@ -51,7 +51,7 @@ class PGPKeySettingTableViewController: UITableViewController { } @IBAction func save(_ sender: Any) { - let alert = UIAlertController(title: "Phassphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) + let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in self.pgpPassphrase = alert.textFields?.first?.text self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 7272ee7..6878f15 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -17,6 +17,26 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni var passwordCategoryText = "" var password: Password? var passwordImage: UIImage? + + let indicatorLable: UILabel = { + let label = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 21)) + label.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height * 0.382 + 22) + label.backgroundColor = UIColor.clear + label.textColor = UIColor.gray + label.text = "decrypting password" + label.textAlignment = .center + label.font = UIFont.preferredFont(forTextStyle: .footnote) + return label + }() + + let indicator: UIActivityIndicatorView = { + let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray) + indicator.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height * 0.382) + return indicator + }() + + let editUIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit(_:))) + struct TableCell { var title: String @@ -54,19 +74,11 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni tableView.contentInset = UIEdgeInsetsMake(-36, 0, 0, 0); tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = 52 - let indicatorLable = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 21)) - indicatorLable.center = CGPoint(x: view.frame.size.width / 2, y: view.frame.size.height * 0.382 + 22) - indicatorLable.backgroundColor = UIColor.clear - indicatorLable.textColor = UIColor.gray - indicatorLable.text = "decrypting password" - indicatorLable.textAlignment = .center - indicatorLable.font = UIFont.preferredFont(forTextStyle: .footnote) - let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray) - indicator.center = CGPoint(x: view.frame.size.width / 2, y: view.frame.size.height * 0.382) + + indicator.startAnimating() tableView.addSubview(indicator) tableView.addSubview(indicatorLable) - let editUIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit(_:))) editUIBarButtonItem.isEnabled = false navigationItem.rightBarButtonItem = editUIBarButtonItem @@ -75,10 +87,33 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni passwordImage = image } + var passphrase = "" + if Defaults[.isRememberPassphraseOn] && PasswordStore.shared.pgpKeyPassphrase != nil { + passphrase = PasswordStore.shared.pgpKeyPassphrase! + self.decryptThenShowPassword(passphrase: passphrase) + } else { + let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) + alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in + passphrase = alert.textFields!.first!.text! + self.decryptThenShowPassword(passphrase: passphrase) + })) + alert.addTextField(configurationHandler: {(textField: UITextField!) in + textField.text = "" + textField.isSecureTextEntry = true + }) + self.present(alert, animated: true, completion: nil) + } + + } + + func decryptThenShowPassword(passphrase: String) { + if Defaults[.isRememberPassphraseOn] { + PasswordStore.shared.pgpKeyPassphrase = passphrase + } DispatchQueue.global(qos: .userInitiated).async { do { - self.password = try self.passwordEntity!.decrypt()! + self.password = try self.passwordEntity!.decrypt(passphrase: passphrase)! } catch { DispatchQueue.main.async { let alert = UIAlertController(title: "Cannot Show Password", message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.alert) @@ -89,19 +124,23 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni } return } - + let password = self.password! - self.setTableData() DispatchQueue.main.async { [weak self] in - self?.tableView.reloadData() - indicator.stopAnimating() - indicatorLable.isHidden = true - editUIBarButtonItem.isEnabled = true - if let url = password.getURL() { - if self?.passwordEntity?.image == nil{ - self?.updatePasswordImage(url: url) - } - } + self?.showPassword(password: password) + } + } + } + + func showPassword(password: Password) { + setTableData() + self.tableView.reloadData() + indicator.stopAnimating() + indicatorLable.isHidden = true + editUIBarButtonItem.isEnabled = true + if let url = password.getURL() { + if self.passwordEntity?.image == nil{ + self.updatePasswordImage(url: url) } } } diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index 97ee79f..1e3dba4 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -190,13 +190,33 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV password = passwordEntities![index] } UIImpactFeedbackGenerator(style: .medium).impactOccurred() + var passphrase = "" + if Defaults[.isRememberPassphraseOn] && PasswordStore.shared.pgpKeyPassphrase != nil { + passphrase = PasswordStore.shared.pgpKeyPassphrase! + self.decryptThenCopyPassword(passwordEntity: password, passphrase: passphrase) + } else { + let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) + alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in + passphrase = alert.textFields!.first!.text! + self.decryptThenCopyPassword(passwordEntity: password, passphrase: passphrase) + })) + alert.addTextField(configurationHandler: {(textField: UITextField!) in + textField.text = "" + textField.isSecureTextEntry = true + }) + self.present(alert, animated: true, completion: nil) + } + + } + + func decryptThenCopyPassword(passwordEntity: PasswordEntity, passphrase: String) { SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultStyle(.dark) SVProgressHUD.show(withStatus: "Decrypting") DispatchQueue.global(qos: .userInteractive).async { var decryptedPassword: Password? do { - decryptedPassword = try password.decrypt()! + decryptedPassword = try passwordEntity.decrypt(passphrase: passphrase)! DispatchQueue.main.async { Utils.copyToPasteboard(textToCopy: decryptedPassword?.password) SVProgressHUD.showSuccess(withStatus: "Password Copied") diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 2394c62..f9d9504 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -59,7 +59,9 @@ class SettingsTableViewController: UITableViewController { } else if let controller = segue.source as? PGPKeyArmorSettingTableViewController { Defaults[.pgpKeySource] = "armor" PasswordStore.shared.pgpKeyPassphrase = controller.pgpPassphrase - Utils.addPasswrodToKeychain(name: "pgpKeyPassphrase", password: controller.pgpPassphrase!) + if Defaults[.isRememberPassphraseOn] { + Utils.addPasswrodToKeychain(name: "pgpKeyPassphrase", password: controller.pgpPassphrase!) + } Defaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text! Defaults[.pgpPrivateKeyArmor] = controller.armorPrivateKeyTextView.text! diff --git a/pass/Helpers/DefaultsKeys.swift b/pass/Helpers/DefaultsKeys.swift index ae3f6f6..b77c685 100644 --- a/pass/Helpers/DefaultsKeys.swift +++ b/pass/Helpers/DefaultsKeys.swift @@ -33,6 +33,7 @@ extension DefaultsKeys { static let passcodeKey = DefaultsKey("passcodeKey") static let isHideUnknownOn = DefaultsKey("isHideUnknownOn") + static let isRememberPassphraseOn = DefaultsKey("isRememberPassphraseOn") static let passwordGenerationMethod = DefaultsKey("passwordGenerationMethod") diff --git a/pass/Helpers/Utils.swift b/pass/Helpers/Utils.swift index bef1307..e0f0ead 100644 --- a/pass/Helpers/Utils.swift +++ b/pass/Helpers/Utils.swift @@ -93,7 +93,7 @@ class Utils { return nil } - static func addPasswrodToKeychain(name: String, password: String) { + static func addPasswrodToKeychain(name: String, password: String?) { let keychain = Keychain(service: "me.mssun.passforios") keychain[name] = password } diff --git a/pass/Models/PasswordEntity.swift b/pass/Models/PasswordEntity.swift index b952918..b9bb7c7 100644 --- a/pass/Models/PasswordEntity.swift +++ b/pass/Models/PasswordEntity.swift @@ -10,11 +10,11 @@ import Foundation import SwiftyUserDefaults extension PasswordEntity { - func decrypt() throws -> Password? { + func decrypt(passphrase: String) throws -> Password? { var password: Password? let encryptedDataPath = URL(fileURLWithPath: "\(Globals.repositoryPath)/\(rawPath!)") let encryptedData = try Data(contentsOf: encryptedDataPath) - let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: PasswordStore.shared.pgpKeyPassphrase!) + let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: passphrase) let plainText = String(data: decryptedData, encoding: .ascii) ?? "" password = Password(name: name!, plainText: plainText) return password diff --git a/pass/Models/PasswordStore.swift b/pass/Models/PasswordStore.swift index 137ff86..915c11a 100644 --- a/pass/Models/PasswordStore.swift +++ b/pass/Models/PasswordStore.swift @@ -103,7 +103,7 @@ class PasswordStore { var pgpKeyPassphrase: String? { set { - Utils.addPasswrodToKeychain(name: "pgpKeyPassphrase", password: newValue!) + Utils.addPasswrodToKeychain(name: "pgpKeyPassphrase", password: newValue) } get { return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase") @@ -111,7 +111,7 @@ class PasswordStore { } var gitRepositoryPassword: String? { set { - Utils.addPasswrodToKeychain(name: "gitRepositoryPassword", password: newValue!) + Utils.addPasswrodToKeychain(name: "gitRepositoryPassword", password: newValue) } get { return Utils.getPasswordFromKeychain(name: "gitRepositoryPassword")