diff --git a/pass/Controllers/GitSSHKeyArmorSettingTableViewController.swift b/pass/Controllers/GitSSHKeyArmorSettingTableViewController.swift index 38db5f0..1e24747 100644 --- a/pass/Controllers/GitSSHKeyArmorSettingTableViewController.swift +++ b/pass/Controllers/GitSSHKeyArmorSettingTableViewController.swift @@ -71,7 +71,7 @@ class GitSSHKeyArmorSettingTableViewController: AutoCellHeightUITableViewControl override func viewDidLoad() { super.viewDidLoad() - armorPrivateKeyTextView.text = SharedDefaults[.gitSSHPrivateKeyArmor] + armorPrivateKeyTextView.text = AppKeychain.get(for: SshKey.PRIVATE.getKeychainKey()) armorPrivateKeyTextView.delegate = self scanPrivateKeyCell?.textLabel?.text = "ScanPrivateKeyQrCodes".localize() @@ -81,7 +81,6 @@ class GitSSHKeyArmorSettingTableViewController: AutoCellHeightUITableViewControl } @IBAction func doneButtonTapped(_ sender: Any) { - SharedDefaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text do { try passwordStore.initGitSSHKey(with: armorPrivateKeyTextView.text) } catch { diff --git a/pass/Controllers/GitServerSettingTableViewController.swift b/pass/Controllers/GitServerSettingTableViewController.swift index 11dfdf3..a390982 100644 --- a/pass/Controllers/GitServerSettingTableViewController.swift +++ b/pass/Controllers/GitServerSettingTableViewController.swift @@ -43,7 +43,7 @@ class GitServerSettingTableViewController: UITableViewController { super.viewWillAppear(animated) // Grey out ssh option if ssh_key is not present if let sshLabel = sshLabel { - sshLabel.isEnabled = passwordStore.gitSSHKeyExists() + sshLabel.isEnabled = AppKeychain.contains(key: SshKey.PRIVATE.getKeychainKey()) } } override func viewDidLoad() { @@ -86,13 +86,14 @@ class GitServerSettingTableViewController: UITableViewController { SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.show(withStatus: "PrepareRepository".localize()) var gitCredential: GitCredential - if auth == "Password" { + let privateKey: String? = AppKeychain.get(for: SshKey.PRIVATE.getKeychainKey()) + if auth == "Password" || privateKey == nil { gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: username)) } else { gitCredential = GitCredential( credential: GitCredential.Credential.ssh( userName: username, - privateKeyFile: Globals.gitSSHPrivateKeyURL + privateKey: privateKey! ) ) } @@ -159,7 +160,7 @@ class GitServerSettingTableViewController: UITableViewController { authenticationMethod = "Password" } else if cell == authSSHKeyCell { - if !passwordStore.gitSSHKeyExists() { + if !AppKeychain.contains(key: SshKey.PRIVATE.getKeychainKey()) { Utils.alert(title: "CannotSelectSshKey".localize(), message: "PleaseSetupSshKeyFirst.".localize(), controller: self, completion: nil) authenticationMethod = "Password" } else { @@ -235,7 +236,7 @@ class GitServerSettingTableViewController: UITableViewController { optionMenu.addAction(urlAction) optionMenu.addAction(armorAction) - if passwordStore.gitSSHKeyExists(inFileSharing: true) { + if KeyFileManager.PrivateSsh.doesKeyFileExist() { // might keys updated via iTunes, or downloaded/pasted inside the app fileActionTitle.append(" (\("Import".localize()))") let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index c9aa608..ec78fac 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -139,13 +139,14 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.show(withStatus: "SyncingPasswordStore".localize()) var gitCredential: GitCredential - if SharedDefaults[.gitAuthenticationMethod] == "Password" { + let privateKey: String? = AppKeychain.get(for: SshKey.PRIVATE.getKeychainKey()) + if SharedDefaults[.gitAuthenticationMethod] == "Password" || privateKey == nil { gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: SharedDefaults[.gitUsername]!)) } else { gitCredential = GitCredential( credential: GitCredential.Credential.ssh( userName: SharedDefaults[.gitUsername]!, - privateKeyFile: Globals.gitSSHPrivateKeyURL + privateKey: privateKey! ) ) } diff --git a/passKit/Helpers/AppKeychain.swift b/passKit/Helpers/AppKeychain.swift index d2b6b1c..d063e7c 100644 --- a/passKit/Helpers/AppKeychain.swift +++ b/passKit/Helpers/AppKeychain.swift @@ -22,6 +22,10 @@ public class AppKeychain { keychain[key] = string } + public static func contains(key: String) -> Bool { + return (try? keychain.contains(key)) ?? false + } + public static func get(for key: String) -> Data? { return try? keychain.getData(key) } diff --git a/passKit/Helpers/CryptographicKeys.swift b/passKit/Helpers/CryptographicKeys.swift index b7fae05..addf5f3 100644 --- a/passKit/Helpers/CryptographicKeys.swift +++ b/passKit/Helpers/CryptographicKeys.swift @@ -34,3 +34,20 @@ public enum PgpKey: CryptographicKey { } } +public enum SshKey: CryptographicKey { + case PRIVATE + + public func getKeychainKey() -> String { + switch self { + case .PRIVATE: + return "sshPrivateKey" + } + } + + public func getFileSharingPath() -> String { + switch self { + case .PRIVATE: + return Globals.iTunesFileSharingSSHPrivate + } + } +} diff --git a/passKit/Helpers/DefaultsKeys.swift b/passKit/Helpers/DefaultsKeys.swift index 4df77b0..37e26c7 100644 --- a/passKit/Helpers/DefaultsKeys.swift +++ b/passKit/Helpers/DefaultsKeys.swift @@ -19,6 +19,7 @@ public extension DefaultsKeys { // Keep them for legacy reasons. static let pgpPublicKeyArmor = DefaultsKey("pgpPublicKeyArmor") static let pgpPrivateKeyArmor = DefaultsKey("pgpPrivateKeyArmor") + static let gitSSHPrivateKeyArmor = DefaultsKey("gitSSHPrivateKeyArmor") static let gitURL = DefaultsKey("gitURL") static let gitAuthenticationMethod = DefaultsKey("gitAuthenticationMethod") @@ -26,7 +27,6 @@ public extension DefaultsKeys { static let gitBranchName = DefaultsKey("gitBranchName", defaultValue: "master") static let gitSSHPrivateKeyURL = DefaultsKey("gitSSHPrivateKeyURL") static let gitSSHKeySource = DefaultsKey("gitSSHKeySource") - static let gitSSHPrivateKeyArmor = DefaultsKey("gitSSHPrivateKeyArmor") static let gitSignatureName = DefaultsKey("gitSignatureName") static let gitSignatureEmail = DefaultsKey("gitSignatureEmail") diff --git a/passKit/Helpers/KeyFileManager.swift b/passKit/Helpers/KeyFileManager.swift index dbf1609..3070472 100644 --- a/passKit/Helpers/KeyFileManager.swift +++ b/passKit/Helpers/KeyFileManager.swift @@ -11,6 +11,7 @@ public class KeyFileManager { public static let PublicPgp = KeyFileManager(keyType: PgpKey.PUBLIC) public static let PrivatePgp = KeyFileManager(keyType: PgpKey.PRIVATE) + public static let PrivateSsh = KeyFileManager(keyType: SshKey.PRIVATE) private let keyType: CryptographicKey private let keyPath: String diff --git a/passKit/Models/GitCredential.swift b/passKit/Models/GitCredential.swift index 8a7d6a0..ee311fb 100644 --- a/passKit/Models/GitCredential.swift +++ b/passKit/Models/GitCredential.swift @@ -17,7 +17,7 @@ public struct GitCredential { public enum Credential { case http(userName: String) - case ssh(userName: String, privateKeyFile: URL) + case ssh(userName: String, privateKey: String) } public init(credential: Credential) { @@ -48,7 +48,7 @@ public struct GitCredential { } attempts += 1 credential = try? GTCredential(userName: userName, password: lastPassword!) - case let .ssh(userName, privateKeyFile): + case let .ssh(userName, privateKey): if attempts > 0 { // The passphrase seems correct, but the previous authentification failed. return nil @@ -65,7 +65,7 @@ public struct GitCredential { } } attempts += 1 - credential = try? GTCredential(userName: userName, publicKeyURL: nil, privateKeyURL: privateKeyFile, passphrase: lastPassword!) + credential = try? GTCredential(userName: userName, publicKeyString: nil, privateKeyString: privateKey, passphrase: lastPassword!) } return credential } diff --git a/passKit/Models/PasswordStore.swift b/passKit/Models/PasswordStore.swift index 55be226..8d0fdf0 100644 --- a/passKit/Models/PasswordStore.swift +++ b/passKit/Models/PasswordStore.swift @@ -142,7 +142,7 @@ public class PasswordStore { private func migrateIfNeeded() { // migrate happens only if the repository was cloned and pgp keys were set up using earlier versions - let needMigration = !pgpKeyExists() && !gitSSHKeyExists() && !fm.fileExists(atPath: Globals.repositoryPath) && fm.fileExists(atPath: Globals.repositoryPathLegacy) + let needMigration = !pgpKeyExists() && !fm.fileExists(atPath: Globals.gitSSHPrivateKeyPath) && !fm.fileExists(atPath: Globals.repositoryPath) && fm.fileExists(atPath: Globals.repositoryPathLegacy) guard needMigration == true else { return } @@ -190,21 +190,19 @@ public class PasswordStore { do { try KeyFileManager(keyType: PgpKey.PUBLIC, keyPath: Globals.pgpPublicKeyPath).importKeyAndDeleteFile() try KeyFileManager(keyType: PgpKey.PRIVATE, keyPath: Globals.pgpPrivateKeyPath).importKeyAndDeleteFile() + try KeyFileManager(keyType: SshKey.PRIVATE, keyPath: Globals.gitSSHPrivateKeyPath).importKeyAndDeleteFile() SharedDefaults.remove(.pgpPublicKeyArmor) SharedDefaults.remove(.pgpPrivateKeyArmor) + SharedDefaults.remove(.gitSSHPrivateKeyArmor) SharedDefaults[.pgpKeySource] = "file" + SharedDefaults[.gitSSHKeySource] = "file" } catch { print("MigrationError".localize(error)) } } - enum SSHKeyType { - case `public`, secret - } - public func initGitSSHKey(with armorKey: String) throws { - let keyPath = Globals.gitSSHPrivateKeyPath - try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii) + AppKeychain.add(string: armorKey, for: SshKey.PRIVATE.getKeychainKey()) } public func initPGPKeys() throws { @@ -851,17 +849,11 @@ public class PasswordStore { public func removeGitSSHKeys() { try? fm.removeItem(atPath: Globals.gitSSHPrivateKeyPath) + Defaults.remove(.gitSSHKeySource) Defaults.remove(.gitSSHPrivateKeyArmor) Defaults.remove(.gitSSHPrivateKeyURL) - self.gitSSHPrivateKeyPassphrase = nil - } - - public func gitSSHKeyExists(inFileSharing: Bool = false) -> Bool { - if inFileSharing == false { - return fm.fileExists(atPath: Globals.gitSSHPrivateKeyPath) - } else { - return fm.fileExists(atPath: Globals.iTunesFileSharingSSHPrivate) - } + AppKeychain.removeContent(for: SshKey.PRIVATE.getKeychainKey()) + gitSSHPrivateKeyPassphrase = nil } public func pgpKeyExists(inFileSharing: Bool = false) -> Bool { @@ -873,7 +865,7 @@ public class PasswordStore { } public func gitSSHKeyImportFromFileSharing() throws { - try fm.moveItem(atPath: Globals.iTunesFileSharingSSHPrivate, toPath: Globals.gitSSHPrivateKeyPath) + try KeyFileManager.PrivateSsh.importKeyAndDeleteFile() } public func pgpKeyImportFromFileSharing() throws {