From fa512e6c8602838fc659206a5db84f3cca849881 Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Tue, 28 Feb 2017 12:25:52 +0800 Subject: [PATCH] Add switch to turn on/off remembering passphrase If the switch is on, users need to type passphrase of secret key once. The key will be stored in the Keychain. If the switch is off, users have to fill in passphrase for each decryption including show password detail and long press to copy. --- .../GeneralSettingsTableViewController.swift | 35 ++++++-- ...GPKeyArmorSettingTableViewController.swift | 18 +++- .../PGPKeySettingTableViewController.swift | 2 +- .../PasswordDetailTableViewController.swift | 83 ++++++++++++++----- .../Controllers/PasswordsViewController.swift | 22 ++++- .../SettingsTableViewController.swift | 4 +- pass/Helpers/DefaultsKeys.swift | 1 + pass/Helpers/Utils.swift | 2 +- pass/Models/PasswordEntity.swift | 4 +- pass/Models/PasswordStore.swift | 4 +- 10 files changed, 138 insertions(+), 37 deletions(-) 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")