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.
This commit is contained in:
Bob Sun 2017-02-28 12:25:52 +08:00
parent 1c45766b96
commit fa512e6c86
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
10 changed files with 138 additions and 37 deletions

View file

@ -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
}
}
}

View file

@ -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

View file

@ -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)

View file

@ -18,6 +18,26 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
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
var content: 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)
@ -91,17 +126,21 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
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)
}
}
}

View file

@ -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")

View file

@ -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!

View file

@ -33,6 +33,7 @@ extension DefaultsKeys {
static let passcodeKey = DefaultsKey<String?>("passcodeKey")
static let isHideUnknownOn = DefaultsKey<Bool>("isHideUnknownOn")
static let isRememberPassphraseOn = DefaultsKey<Bool>("isRememberPassphraseOn")
static let passwordGenerationMethod = DefaultsKey<String>("passwordGenerationMethod")

View file

@ -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
}

View file

@ -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

View file

@ -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")