From 90709675a3f08630af3c2955cddcb25aa0d1e2bf Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sun, 19 Feb 2017 22:10:36 +0800 Subject: [PATCH] use keychain to store pgp passphrase and git password --- Cartfile | 2 ++ pass.xcodeproj/project.pbxproj | 10 ++++-- .../GitServerSettingTableViewController.swift | 10 +++--- ...GPKeyArmorSettingTableViewController.swift | 2 +- .../PGPKeySettingTableViewController.swift | 3 +- ...epositorySettingsTableViewController.swift | 6 ++-- .../SettingsTableViewController.swift | 6 ++-- pass/Helpers/Utils.swift | 32 +++++++++++++++++++ pass/Models/PasswordEntity.swift | 2 +- pass/Models/PasswordStore.swift | 32 +++++++++++++++++-- 10 files changed, 86 insertions(+), 19 deletions(-) diff --git a/Cartfile b/Cartfile index 2d8a890..bfd1d63 100644 --- a/Cartfile +++ b/Cartfile @@ -3,3 +3,5 @@ github "radex/SwiftyUserDefaults" github "mssun/objective-git" "master" github "zahlz/SwiftPasscodeLock" "master" github "bitserf/FavIcon" +github "kishikawakatsumi/KeychainAccess" "master" + diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 89f96ab..274c392 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ DCA0499A1E335CC800522E8F /* GitServerSettingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA049991E335CC800522E8F /* GitServerSettingTableViewController.swift */; }; DCA0499C1E3362F400522E8F /* PGPKeySettingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA0499B1E3362F400522E8F /* PGPKeySettingTableViewController.swift */; }; DCA0499E1E33BAC100522E8F /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA0499D1E33BAC100522E8F /* Globals.swift */; }; + DCA742DA1E599ED400D54E16 /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA742D91E599ED400D54E16 /* KeychainAccess.framework */; }; DCAAF7451E2FA66800AB94BC /* SettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */; }; DCC277D21E30D6EA00402246 /* pass.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DCC408C81E30BA1300F29B0E /* pass.xcdatamodeld */; }; DCC408A41E2FCC9E00F29B0E /* PasswordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */; }; @@ -104,6 +105,7 @@ DCA049991E335CC800522E8F /* GitServerSettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitServerSettingTableViewController.swift; sourceTree = ""; }; DCA0499B1E3362F400522E8F /* PGPKeySettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PGPKeySettingTableViewController.swift; sourceTree = ""; }; DCA0499D1E33BAC100522E8F /* Globals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Globals.swift; sourceTree = ""; }; + DCA742D91E599ED400D54E16 /* KeychainAccess.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainAccess.framework; path = Carthage/Build/iOS/KeychainAccess.framework; sourceTree = ""; }; DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = ""; }; DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordStore.swift; sourceTree = ""; }; DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = ""; }; @@ -129,6 +131,7 @@ DC917BEF1E2F38C5000FDF54 /* Result.framework in Frameworks */, DC037CAC1E4C1C7100609409 /* FavIcon.framework in Frameworks */, DCC408C71E307DBB00F29B0E /* SVProgressHUD.framework in Frameworks */, + DCA742DA1E599ED400D54E16 /* KeychainAccess.framework in Frameworks */, DC193FFC1E49E0340077E0A3 /* PasscodeLock.framework in Frameworks */, DC1208581E35EBE60042942E /* ObjectiveGit.framework in Frameworks */, DCA049961E3357E000522E8F /* SwiftyUserDefaults.framework in Frameworks */, @@ -255,6 +258,7 @@ DC917BED1E2F38C4000FDF54 /* Frameworks */ = { isa = PBXGroup; children = ( + DCA742D91E599ED400D54E16 /* KeychainAccess.framework */, DC037CAB1E4C1C7100609409 /* FavIcon.framework */, DC193FFB1E49E0340077E0A3 /* PasscodeLock.framework */, DC1208571E35EBE60042942E /* ObjectiveGit.framework */, @@ -277,7 +281,7 @@ DC917BCF1E2E8231000FDF54 /* Sources */, DC917BD01E2E8231000FDF54 /* Frameworks */, DC917BD11E2E8231000FDF54 /* Resources */, - DC917BEC1E2F3659000FDF54 /* ShellScript */, + DC917BEC1E2F3659000FDF54 /* Run Script */, 58F20B5DB8A41D610AF2145E /* [CP] Embed Pods Frameworks */, D5AA0953A6BB441CE2390DE3 /* [CP] Copy Pods Resources */, ); @@ -390,7 +394,7 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-pass/Pods-pass-resources.sh\"\n"; showEnvVarsInLog = 0; }; - DC917BEC1E2F3659000FDF54 /* ShellScript */ = { + DC917BEC1E2F3659000FDF54 /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 12; files = ( @@ -402,7 +406,9 @@ "$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework", "$(SRCROOT)/Carthage/Build/iOS/PasscodeLock.framework", "$(SRCROOT)/Carthage/Build/iOS/FavIcon.framework", + "$(SRCROOT)/Carthage/Build/iOS/KeychainAccess.framework", ); + name = "Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; diff --git a/pass/Controllers/GitServerSettingTableViewController.swift b/pass/Controllers/GitServerSettingTableViewController.swift index 2ae349b..802e238 100644 --- a/pass/Controllers/GitServerSettingTableViewController.swift +++ b/pass/Controllers/GitServerSettingTableViewController.swift @@ -13,10 +13,8 @@ class GitServerSettingTableViewController: UITableViewController { @IBOutlet weak var gitRepositoryURLTextField: UITextField! @IBOutlet weak var usernameTextField: UITextField! -// @IBOutlet weak var passwordTextField: UITextField! @IBOutlet weak var authenticationTableViewCell: UITableViewCell! - -// var password: String? + var password: String? var authenticationMethod = Defaults[.gitRepositoryAuthenticationMethod] @@ -27,8 +25,8 @@ class GitServerSettingTableViewController: UITableViewController { gitRepositoryURLTextField.text = url.absoluteString } usernameTextField.text = Defaults[.gitRepositoryUsername] -// passwordTextField.text = Defaults[.gitRepositoryPassword] authenticationTableViewCell.detailTextLabel?.text = authenticationMethod + password = PasswordStore.shared.gitRepositoryPassword } override func viewDidAppear(_ animated: Bool) { @@ -74,13 +72,13 @@ class GitServerSettingTableViewController: UITableViewController { if authenticationMethod == "Password" { let alert = UIAlertController(title: "Password", message: "Please fill in the password of your Git account.", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in - Defaults[.gitRepositoryPassword] = alert.textFields?.first?.text + self.password = alert.textFields!.first!.text if self.shouldPerformSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) { self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) } })) alert.addTextField(configurationHandler: {(textField: UITextField!) in - textField.text = Defaults[.gitRepositoryPassword] + textField.text = self.password textField.isSecureTextEntry = true }) self.present(alert, animated: true, completion: nil) diff --git a/pass/Controllers/PGPKeyArmorSettingTableViewController.swift b/pass/Controllers/PGPKeyArmorSettingTableViewController.swift index d0637de..445a3ae 100644 --- a/pass/Controllers/PGPKeyArmorSettingTableViewController.swift +++ b/pass/Controllers/PGPKeyArmorSettingTableViewController.swift @@ -18,7 +18,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController { super.viewDidLoad() armorPublicKeyTextView.text = Defaults[.pgpPublicKeyArmor] armorPrivateKeyTextView.text = Defaults[.pgpPrivateKeyArmor] - pgpPassphrase = Defaults[.pgpKeyPassphrase] + pgpPassphrase = PasswordStore.shared.pgpKeyPassphrase } @IBAction func save(_ sender: Any) { diff --git a/pass/Controllers/PGPKeySettingTableViewController.swift b/pass/Controllers/PGPKeySettingTableViewController.swift index b8b7c72..8c195e4 100644 --- a/pass/Controllers/PGPKeySettingTableViewController.swift +++ b/pass/Controllers/PGPKeySettingTableViewController.swift @@ -17,9 +17,10 @@ class PGPKeySettingTableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() + tableView.rowHeight = UITableViewAutomaticDimension pgpPublicKeyURLTextField.text = Defaults[.pgpPublicKeyURL]?.absoluteString pgpPrivateKeyURLTextField.text = Defaults[.pgpPrivateKeyURL]?.absoluteString - pgpPassphrase = Defaults[.pgpKeyPassphrase] + pgpPassphrase = PasswordStore.shared.pgpKeyPassphrase } override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { diff --git a/pass/Controllers/PasswordRepositorySettingsTableViewController.swift b/pass/Controllers/PasswordRepositorySettingsTableViewController.swift index fd7c5fa..e05125f 100644 --- a/pass/Controllers/PasswordRepositorySettingsTableViewController.swift +++ b/pass/Controllers/PasswordRepositorySettingsTableViewController.swift @@ -43,14 +43,14 @@ class PasswordRepositorySettingsTableViewController: BasicStaticTableViewControl if let controller = segue.source as? GitServerSettingTableViewController { let gitRepostiroyURL = controller.gitRepositoryURLTextField.text! let username = controller.usernameTextField.text! - let password = Defaults[.gitRepositoryPassword] + let password = controller.password let auth = controller.authenticationMethod if Defaults[.gitRepositoryURL] == nil || Defaults[.gitRepositoryURL]!.absoluteString != gitRepostiroyURL || auth != Defaults[.gitRepositoryAuthenticationMethod] || username != Defaults[.gitRepositoryUsername] || - password != Defaults[.gitRepositoryPassword] { + password != PasswordStore.shared.gitRepositoryPassword { SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultStyle(.light) @@ -82,7 +82,7 @@ class PasswordRepositorySettingsTableViewController: BasicStaticTableViewControl NotificationCenter.default.post(Notification(name: Notification.Name("passwordUpdated"))) Defaults[.gitRepositoryURL] = URL(string: gitRepostiroyURL) Defaults[.gitRepositoryUsername] = username - Defaults[.gitRepositoryPassword] = password + PasswordStore.shared.gitRepositoryPassword = password Defaults[.gitRepositoryAuthenticationMethod] = auth Defaults[.gitRepositoryPasswordAttempts] = 0 SVProgressHUD.showSuccess(withStatus: "Done") diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 3efc0f7..6595e95 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -27,7 +27,7 @@ class SettingsTableViewController: UITableViewController { if let controller = segue.source as? PGPKeySettingTableViewController { Defaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!) Defaults[.pgpPublicKeyURL] = URL(string: controller.pgpPublicKeyURLTextField.text!) - Defaults[.pgpKeyPassphrase] = controller.pgpPassphrase + PasswordStore.shared.pgpKeyPassphrase = controller.pgpPassphrase Defaults[.pgpKeySource] = "url" SVProgressHUD.setDefaultMaskType(.black) @@ -57,7 +57,9 @@ class SettingsTableViewController: UITableViewController { } else if let controller = segue.source as? PGPKeyArmorSettingTableViewController { Defaults[.pgpKeySource] = "armor" - Defaults[.pgpKeyPassphrase] = controller.pgpPassphrase + PasswordStore.shared.pgpKeyPassphrase = controller.pgpPassphrase + Utils.addPasswrodToKeychain(name: "pgpKeyPassphrase", password: controller.pgpPassphrase!) + Defaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text! Defaults[.pgpPrivateKeyArmor] = controller.armorPrivateKeyTextView.text! diff --git a/pass/Helpers/Utils.swift b/pass/Helpers/Utils.swift index 7877a5a..1502852 100644 --- a/pass/Helpers/Utils.swift +++ b/pass/Helpers/Utils.swift @@ -8,6 +8,7 @@ import Foundation import SwiftyUserDefaults +import KeychainAccess import UIKit class Utils { @@ -70,6 +71,37 @@ class Utils { Defaults.remove(.pgpPublicKeyURL) Defaults.remove(.pgpKeyID) } + + static func getPasswordFromKeychain(name: String) -> String? { + let keychain = Keychain(service: "me.mssun.passforios") + do { + return try keychain.getString(name) + } catch { + print(error) + } + return nil + } + + static func addPasswrodToKeychain(name: String, password: String) { + let keychain = Keychain(service: "me.mssun.passforios") + keychain[name] = password + } + static func removeKeychain(name: String) { + let keychain = Keychain(service: "me.mssun.passforios") + do { + try keychain.remove(name) + } catch { + print(error) + } + } + static func removeAllKeychain() { + let keychain = Keychain(service: "me.mssun.passforios") + do { + try keychain.removeAll() + } catch { + print(error) + } + } } // https://gist.github.com/NikolaiRuhe/eeb135d20c84a7097516 diff --git a/pass/Models/PasswordEntity.swift b/pass/Models/PasswordEntity.swift index c99bc51..b952918 100644 --- a/pass/Models/PasswordEntity.swift +++ b/pass/Models/PasswordEntity.swift @@ -14,7 +14,7 @@ extension PasswordEntity { 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: Defaults[.pgpKeyPassphrase]) + let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: PasswordStore.shared.pgpKeyPassphrase!) 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 c0a7496..6267342 100644 --- a/pass/Models/PasswordStore.swift +++ b/pass/Models/PasswordStore.swift @@ -41,7 +41,7 @@ struct GitCredential { let alert = UIAlertController(title: "Password", message: "Please fill in the password of your Git account.", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in newPassword = alert.textFields!.first!.text! - Defaults[.gitRepositoryPassword] = newPassword + PasswordStore.shared.gitRepositoryPassword = newPassword sem.signal() })) if Defaults[.gitRepositoryPasswordAttempts] == 3 { @@ -51,7 +51,7 @@ struct GitCredential { }) } alert.addTextField(configurationHandler: {(textField: UITextField!) in - textField.text = Defaults[.gitRepositoryPassword] + textField.text = PasswordStore.shared.gitRepositoryPassword textField.isSecureTextEntry = true }) topController.present(alert, animated: true, completion: nil) @@ -83,6 +83,25 @@ class PasswordStore { let pgp: ObjectivePGP = ObjectivePGP() + var pgpKeyPassphrase: String? { + didSet { + if pgpKeyPassphrase != nil { + Utils.addPasswrodToKeychain(name: "pgpKeyPassphrase", password: pgpKeyPassphrase!) + } else { + Utils.removeKeychain(name: "pgpKeyPassphrase") + } + } + } + var gitRepositoryPassword: String? { + didSet { + if gitRepositoryPassword != nil { + Utils.addPasswrodToKeychain(name: "gitRepositoryPassword", password: gitRepositoryPassword!) + } else { + Utils.removeKeychain(name: "gitRepositoryPassword") + } + } + } + let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext @@ -100,14 +119,17 @@ class PasswordStore { } if Defaults[.gitRepositoryAuthenticationMethod] == "Password" { - gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitRepositoryUsername]!, password: Defaults[.gitRepositoryPassword]!)) + gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitRepositoryUsername]!, password: PasswordStore.shared.gitRepositoryPassword!)) } else if Defaults[.gitRepositoryAuthenticationMethod] == "SSH Key"{ gitCredential = GitCredential(credential: GitCredential.Credential.ssh(userName: Defaults[.gitRepositoryUsername]!, password: Defaults[.gitRepositorySSHPrivateKeyPassphrase]!, publicKeyFile: Globals.sshPublicKeyURL, privateKeyFile: Globals.sshPrivateKeyURL)) } else { gitCredential = nil } + pgpKeyPassphrase = Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase") + gitRepositoryPassword = Utils.getPasswordFromKeychain(name: "gitRepositoryPassword") } + func initPGP(pgpPublicKeyLocalPath: String, pgpPrivateKeyLocalPath: String) throws { pgp.importKeys(fromFile: pgpPublicKeyLocalPath, allowDuplicates: false) if pgp.getKeysOf(.public).count == 0 { @@ -442,6 +464,10 @@ class PasswordStore { Utils.removeFileIfExists(at: Globals.sshPrivateKeyURL) Utils.removeFileIfExists(at: Globals.sshPublicKeyURL) + Utils.removeAllKeychain() + pgpKeyPassphrase = nil + gitRepositoryPassword = nil + deleteCoreData(entityName: "PasswordEntity") deleteCoreData(entityName: "PasswordCategoryEntity")