diff --git a/pass/Controllers/GeneralSettingsTableViewController.swift b/pass/Controllers/GeneralSettingsTableViewController.swift index cb6eb4b..6ea155d 100644 --- a/pass/Controllers/GeneralSettingsTableViewController.swift +++ b/pass/Controllers/GeneralSettingsTableViewController.swift @@ -176,7 +176,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { @objc func rememberPGPPassphraseSwitchAction(_ sender: Any?) { Defaults.isRememberPGPPassphraseOn = rememberPGPPassphraseSwitch.isOn if rememberPGPPassphraseSwitch.isOn == false { - AppKeychain.shared.removeContent(for: Globals.pgpKeyPassphrase) + AppKeychain.shared.removeAllContent(withPrefix: Globals.pgpKeyPassphrase) } } diff --git a/pass/Controllers/PGPKeyArmorImportTableViewController.swift b/pass/Controllers/PGPKeyArmorImportTableViewController.swift index 61998ee..ed025ae 100644 --- a/pass/Controllers/PGPKeyArmorImportTableViewController.swift +++ b/pass/Controllers/PGPKeyArmorImportTableViewController.swift @@ -15,7 +15,10 @@ class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController, @IBOutlet weak var armorPrivateKeyTextView: UITextView! @IBOutlet weak var scanPublicKeyCell: UITableViewCell! @IBOutlet weak var scanPrivateKeyCell: UITableViewCell! - + + var armorPublicKey: String? + var armorPrivateKey: String? + class ScannedPGPKey { enum KeyType { case publicKey, privateKey @@ -74,7 +77,9 @@ class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController, } @IBAction func save(_ sender: Any) { - savePassphraseDialog() + armorPublicKey = armorPublicKeyTextView.text + armorPrivateKey = armorPrivateKeyTextView.text + self.saveImportedKeys() } func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { @@ -144,8 +149,8 @@ extension PGPKeyArmorImportTableViewController: PGPKeyImporter { } func importKeys() throws { - try KeyFileManager.PublicPgp.importKey(from: armorPublicKeyTextView.text ?? "") - try KeyFileManager.PrivatePgp.importKey(from: armorPrivateKeyTextView.text ?? "") + try KeyFileManager.PublicPgp.importKey(from: armorPublicKey ?? "") + try KeyFileManager.PrivatePgp.importKey(from: armorPrivateKey ?? "") } func saveImportedKeys() { diff --git a/pass/Controllers/PGPKeyFIleImportTableViewController.swift b/pass/Controllers/PGPKeyFIleImportTableViewController.swift index a6c938f..5016fda 100644 --- a/pass/Controllers/PGPKeyFIleImportTableViewController.swift +++ b/pass/Controllers/PGPKeyFIleImportTableViewController.swift @@ -20,7 +20,7 @@ class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController { private var currentlyPicking = KeyType.none @IBAction func save(_ sender: Any) { - savePassphraseDialog() + self.saveImportedKeys() } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { diff --git a/pass/Controllers/PGPKeyImporter.swift b/pass/Controllers/PGPKeyImporter.swift index 9fc71ba..8f60dd0 100644 --- a/pass/Controllers/PGPKeyImporter.swift +++ b/pass/Controllers/PGPKeyImporter.swift @@ -25,35 +25,3 @@ extension PGPKeyImporter { } } - -extension PGPKeyImporter where Self: UIViewController { - - func savePassphraseDialog() { - guard self.isReadyToUse() else { - return - } - let savePassphraseAlert = UIAlertController(title: "Passphrase".localize(), message: "WantToSavePassphrase?".localize(), preferredStyle: .alert) - // Do not save the key's passphrase. - savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: .default) { _ in - AppKeychain.shared.removeContent(for: Globals.pgpKeyPassphrase) - Defaults.isRememberPGPPassphraseOn = false - self.saveImportedKeys() - }) - // Save the key's passphrase. - savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: .destructive) { _ in - // Ask for the passphrase. - let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "Ok".localize(), style: .default) { _ in - AppKeychain.shared.add(string: alert.textFields?.first?.text, for: Globals.pgpKeyPassphrase) - Defaults.isRememberPGPPassphraseOn = true - self.saveImportedKeys() - }) - alert.addTextField { textField in - textField.text = AppKeychain.shared.get(for: Globals.pgpKeyPassphrase) - textField.isSecureTextEntry = true - } - self.present(alert, animated: true) - }) - present(savePassphraseAlert, animated: true) - } -} diff --git a/pass/Controllers/PGPKeyUrlImportTableViewController.swift b/pass/Controllers/PGPKeyUrlImportTableViewController.swift index ca44fb8..86a25cb 100644 --- a/pass/Controllers/PGPKeyUrlImportTableViewController.swift +++ b/pass/Controllers/PGPKeyUrlImportTableViewController.swift @@ -14,6 +14,9 @@ class PGPKeyUrlImportTableViewController: AutoCellHeightUITableViewController { @IBOutlet weak var pgpPublicKeyURLTextField: UITextField! @IBOutlet weak var pgpPrivateKeyURLTextField: UITextField! + var pgpPrivateKeyURL: URL? + var pgpPublicKeyURL: URL? + override func viewDidLoad() { super.viewDidLoad() pgpPublicKeyURLTextField.text = Defaults.pgpPublicKeyURL?.absoluteString @@ -21,23 +24,19 @@ class PGPKeyUrlImportTableViewController: AutoCellHeightUITableViewController { } @IBAction func save(_ sender: Any) { - let publicKeyUrl = pgpPublicKeyURLTextField.text - if publicKeyUrl == nil || publicKeyUrl!.trimmed.isEmpty { - return savePassphraseDialog() + guard let publicKeyURLText = pgpPublicKeyURLTextField.text, + let publicKeyURL = URL(string: publicKeyURLText), + let privateKeyURLText = pgpPrivateKeyURLTextField.text, + let privateKeyURL = URL(string: privateKeyURLText) else { + Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self) + return } - if getScheme(from: pgpPrivateKeyURLTextField.text?.trimmed) == "http" { - let savePassphraseAlert = UIAlertController(title: "HttpNotSecure".localize(), message: "ReallyUseHttp?".localize(), preferredStyle: .alert) - savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: .default) { _ in }) - savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: .destructive) { _ in - self.savePassphraseDialog() - }) - return present(savePassphraseAlert, animated: true) + if privateKeyURL.scheme?.lowercased() == "http" || publicKeyURL.scheme?.lowercased() == "http" { + Utils.alert(title: "HttpNotSecure".localize(), message: "ReallyUseHttp.".localize(), controller: self) } - return savePassphraseDialog() - } - - private func getScheme(from url: String?) -> String? { - return url.flatMap(URL.init(string:))?.scheme + pgpPrivateKeyURL = privateKeyURL + pgpPublicKeyURL = publicKeyURL + self.saveImportedKeys() } } @@ -47,13 +46,13 @@ extension PGPKeyUrlImportTableViewController: PGPKeyImporter { static let label = "DownloadFromUrl".localize() func isReadyToUse() -> Bool { - return validate(pgpKeyUrl: pgpPublicKeyURLTextField.text) - && validate(pgpKeyUrl: pgpPrivateKeyURLTextField.text) + return validate(pgpKeyUrl: pgpPublicKeyURLTextField.text ?? "") + && validate(pgpKeyUrl: pgpPrivateKeyURLTextField.text ?? "") } func importKeys() throws { - Defaults.pgpPrivateKeyURL = URL(string: pgpPrivateKeyURLTextField.text!.trimmed) - Defaults.pgpPublicKeyURL = URL(string: pgpPublicKeyURLTextField.text!.trimmed) + Defaults.pgpPrivateKeyURL = pgpPrivateKeyURL + Defaults.pgpPublicKeyURL = pgpPublicKeyURL try KeyFileManager.PublicPgp.importKey(from: Defaults.pgpPublicKeyURL!) try KeyFileManager.PrivatePgp.importKey(from: Defaults.pgpPrivateKeyURL!) @@ -67,12 +66,12 @@ extension PGPKeyUrlImportTableViewController: PGPKeyImporter { performSegue(withIdentifier: "savePGPKeySegue", sender: self) } - private func validate(pgpKeyUrl: String?) -> Bool { - guard let scheme = getScheme(from: pgpKeyUrl) else { + private func validate(pgpKeyUrl: String) -> Bool { + guard let url = URL(string: pgpKeyUrl) else { Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self) return false } - guard scheme == "https" || scheme == "http" else { + guard url.scheme == "https" || url.scheme == "http" else { Utils.alert(title: "CannotSavePgpKey".localize(), message: "UseEitherHttpsOrHttp.".localize(), controller: self) return false } diff --git a/pass/Controllers/SSHKeyArmorImportTableViewController.swift b/pass/Controllers/SSHKeyArmorImportTableViewController.swift index 6a16520..15c286f 100644 --- a/pass/Controllers/SSHKeyArmorImportTableViewController.swift +++ b/pass/Controllers/SSHKeyArmorImportTableViewController.swift @@ -15,6 +15,7 @@ class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController, @IBOutlet weak var scanPrivateKeyCell: UITableViewCell! var gitSSHPrivateKeyPassphrase: String? + var armorPrivateKey: String? class ScannedSSHKey { var segments = [String]() @@ -59,6 +60,7 @@ class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController, } @IBAction func doneButtonTapped(_ sender: Any) { + armorPrivateKey = armorPrivateKeyTextView.text performSegue(withIdentifier: "importSSHKeySegue", sender: self) } @@ -121,6 +123,6 @@ extension SSHKeyArmorImportTableViewController: KeyImporter { } func importKeys() throws { - try KeyFileManager.PrivateSsh.importKey(from: armorPrivateKeyTextView.text ?? "") + try KeyFileManager.PrivateSsh.importKey(from: armorPrivateKey ?? "") } } diff --git a/pass/Controllers/SSHKeyUrlImportTableViewController.swift b/pass/Controllers/SSHKeyUrlImportTableViewController.swift index 8dbc4c4..4e09ac2 100644 --- a/pass/Controllers/SSHKeyUrlImportTableViewController.swift +++ b/pass/Controllers/SSHKeyUrlImportTableViewController.swift @@ -13,13 +13,21 @@ class SSHKeyUrlImportTableViewController: AutoCellHeightUITableViewController { @IBOutlet weak var privateKeyURLTextField: UITextField! + var sshPrivateKeyURL: URL? + override func viewDidLoad() { super.viewDidLoad() privateKeyURLTextField.text = Defaults.gitSSHPrivateKeyURL?.absoluteString } @IBAction func doneButtonTapped(_ sender: UIButton) { - if getScheme(from: privateKeyURLTextField.text?.trimmed) == "http" { + guard let text = privateKeyURLTextField.text, + let privateKeyURL = URL(string: text) else { + Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKeyUrl.".localize(), controller: self) + return + } + + if privateKeyURL.scheme?.lowercased() == "http" { let savePassphraseAlert = UIAlertController(title: "HttpNotSecure".localize(), message: "ReallyUseHttp?".localize(), preferredStyle: .alert) savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: .default) { _ in }) savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: .destructive) { _ in @@ -27,12 +35,9 @@ class SSHKeyUrlImportTableViewController: AutoCellHeightUITableViewController { }) return present(savePassphraseAlert, animated: true) } + sshPrivateKeyURL = privateKeyURL performSegue(withIdentifier: "importSSHKeySegue", sender: self) } - - private func getScheme(from url: String?) -> String? { - return url.flatMap(URL.init(string:))?.scheme - } } extension SSHKeyUrlImportTableViewController: KeyImporter { @@ -41,11 +46,11 @@ extension SSHKeyUrlImportTableViewController: KeyImporter { static let label = "DownloadFromUrl".localize() func isReadyToUse() -> Bool { - guard let scheme = getScheme(from: privateKeyURLTextField.text?.trimmed) else { + guard let url = sshPrivateKeyURL else { Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKeyUrl.".localize(), controller: self) return false } - guard scheme == "https" || scheme == "http" else { + guard url.scheme == "https" || url.scheme == "http" else { Utils.alert(title: "CannotSave".localize(), message: "UseEitherHttpsOrHttp.".localize(), controller: self) return false } @@ -53,8 +58,7 @@ extension SSHKeyUrlImportTableViewController: KeyImporter { } func importKeys() throws { - Defaults.gitSSHPrivateKeyURL = URL(string: privateKeyURLTextField.text!.trimmed) - + Defaults.gitSSHPrivateKeyURL = sshPrivateKeyURL try KeyFileManager.PrivateSsh.importKey(from: Defaults.gitSSHPrivateKeyURL!) } } diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 6edff60..5440022 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -83,7 +83,14 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele } private func setPGPKeyTableViewCellDetailText() { - pgpKeyTableViewCell.detailTextLabel?.text = try? PGPAgent.shared.getShortKeyId() ?? "NotSet".localize() + var label = "NotSet".localize() + let keyID = (try? PGPAgent.shared.getShortKeyID()) ?? [] + if keyID.count == 1 { + label = keyID.first ?? "" + } else if keyID.count > 1 { + label = "Multiple" + } + pgpKeyTableViewCell.detailTextLabel?.text = label } private func setPasswordRepositoryTableViewCellDetailText() { @@ -141,9 +148,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele }) if isReadyToUse() { - optionMenu.addAction(UIAlertAction(title: "\(Self.menuLabel) (\("Import".localize()))", style: .default) { _ in - self.savePassphraseDialog() - }) + optionMenu.addAction(UIAlertAction(title: "\(Self.menuLabel) (\("Import".localize()))", style: .default)) } else { optionMenu.addAction(UIAlertAction(title: "\(Self.menuLabel) (\("Tips".localize()))", style: .default) { _ in let title = "Tips".localize() diff --git a/passKit/Crypto/GopenPgp.swift b/passKit/Crypto/GopenPgp.swift index ee05b25..589352d 100644 --- a/passKit/Crypto/GopenPgp.swift +++ b/passKit/Crypto/GopenPgp.swift @@ -52,13 +52,14 @@ struct GopenPgp: PgpInterface { for line in str.splitByNewline() { if line.trimmed.uppercased().hasPrefix("-----BEGIN PGP") { key = "" - key += line + "\n" + key += line } else if line.trimmed.uppercased().hasPrefix("-----END PGP") { key += line keys.append(key) } else { - key += line + "\n" + key += line } + key += "\n" } return keys } @@ -114,14 +115,12 @@ struct GopenPgp: PgpInterface { return encryptedData.getBinary()! } - var keyId: String { - let fingerprint = publicKeys.first?.key ?? "" - return String(fingerprint).uppercased() + var keyID: [String] { + return publicKeys.keys.map({ $0.uppercased() }) } - var shortKeyId: String { - let fingerprint = publicKeys.first?.key ?? "" - return String(fingerprint.suffix(8)).uppercased() + var shortKeyID: [String] { + return publicKeys.keys.map({ $0.suffix(8).uppercased()}) } private func createPgpMessage(from encryptedData: Data) -> CryptoPGPMessage? { diff --git a/passKit/Crypto/ObjectivePgp.swift b/passKit/Crypto/ObjectivePgp.swift index 58795f9..8262867 100644 --- a/passKit/Crypto/ObjectivePgp.swift +++ b/passKit/Crypto/ObjectivePgp.swift @@ -42,11 +42,11 @@ struct ObjectivePgp: PgpInterface { return encryptedData } - var keyId: String { - return publicKey.keyID.longIdentifier + var keyID: [String] { + return keyring.keys.map({ $0.keyID.longIdentifier }) } - var shortKeyId: String { - return publicKey.keyID.shortIdentifier + var shortKeyID: [String] { + return keyring.keys.map({ $0.keyID.shortIdentifier }) } } diff --git a/passKit/Crypto/PGPAgent.swift b/passKit/Crypto/PGPAgent.swift index aac9bf9..cdd8430 100644 --- a/passKit/Crypto/PGPAgent.swift +++ b/passKit/Crypto/PGPAgent.swift @@ -35,14 +35,14 @@ public class PGPAgent { pgpInterface = nil } - public func getKeyId() throws -> String? { + public func getKeyID() throws -> [String] { try checkAndInit() - return pgpInterface?.keyId + return pgpInterface?.keyID ?? [] } - public func getShortKeyId() throws -> String? { + public func getShortKeyID() throws -> [String] { try checkAndInit() - return pgpInterface?.shortKeyId + return pgpInterface?.shortKeyID ?? [] } public func decrypt(encryptedData: Data, keyID: String, requestPGPKeyPassphrase: (String) -> String) throws -> Data? { @@ -56,7 +56,7 @@ public class PGPAgent { if previousDecryptStatus == false { passphrase = requestPGPKeyPassphrase(keyID) } else { - passphrase = keyStore.get(for: Globals.pgpKeyPassphrase) ?? requestPGPKeyPassphrase(keyID) + passphrase = keyStore.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? requestPGPKeyPassphrase(keyID) } // Decrypt. guard let result = try pgpInterface!.decrypt(encryptedData: encryptedData, keyID: keyID, passphrase: passphrase) else { diff --git a/passKit/Crypto/PgpInterface.swift b/passKit/Crypto/PgpInterface.swift index 3a0567e..2a3af66 100644 --- a/passKit/Crypto/PgpInterface.swift +++ b/passKit/Crypto/PgpInterface.swift @@ -12,7 +12,7 @@ protocol PgpInterface { func encrypt(plainData: Data, keyID: String) throws -> Data - var keyId: String { get } + var keyID: [String] { get } - var shortKeyId: String { get } + var shortKeyID: [String] { get } } diff --git a/passKit/Helpers/AppKeychain.swift b/passKit/Helpers/AppKeychain.swift index 8f70492..a71c3c0 100644 --- a/passKit/Helpers/AppKeychain.swift +++ b/passKit/Helpers/AppKeychain.swift @@ -43,4 +43,16 @@ public class AppKeychain: KeyStore { public func removeAllContent() { try? keychain.removeAll() } + + public func removeAllContent(withPrefix prefix: String) { + for k in keychain.allKeys() { + if k.hasPrefix(prefix) { + try? keychain.remove(k) + } + } + } + + public static func getPGPKeyPassphraseKey(keyID: String) -> String { + Globals.pgpKeyPassphrase + "-" + keyID + } } diff --git a/passKit/Helpers/Utils.swift b/passKit/Helpers/Utils.swift index d5cd1fe..ff26b3c 100644 --- a/passKit/Helpers/Utils.swift +++ b/passKit/Helpers/Utils.swift @@ -40,24 +40,26 @@ public class Utils { } public static func createRequestPGPKeyPassphraseHandler(controller: UIViewController) -> (String) -> String { - return { keyID in + return { keyID in let sem = DispatchSemaphore(value: 0) var passphrase = "" DispatchQueue.main.async { - let alert = UIAlertController(title: "Passphrase".localize() + " (\(keyID.suffix(8)))", message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertController.Style.alert) + let title = "Passphrase".localize() + " (\(keyID.suffix(8)))" + let message = "FillInPgpPassphrase.".localize() + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in - passphrase = alert.textFields!.first!.text! + passphrase = alert.textFields?.first?.text ?? "" sem.signal() })) alert.addTextField(configurationHandler: {(textField: UITextField!) in - textField.text = AppKeychain.shared.get(for: Globals.pgpKeyPassphrase) ?? "" + textField.text = AppKeychain.shared.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? "" textField.isSecureTextEntry = true }) controller.present(alert, animated: true, completion: nil) } let _ = sem.wait(timeout: DispatchTime.distantFuture) if Defaults.isRememberPGPPassphraseOn { - AppKeychain.shared.add(string: passphrase, for: Globals.pgpKeyPassphrase) + AppKeychain.shared.add(string: passphrase, for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) } return passphrase } diff --git a/passKitTests/Crypto/PGPAgentTest.swift b/passKitTests/Crypto/PGPAgentTest.swift index 76427a1..61186f8 100644 --- a/passKitTests/Crypto/PGPAgentTest.swift +++ b/passKitTests/Crypto/PGPAgentTest.swift @@ -73,7 +73,7 @@ class PGPAgentTest: XCTestCase { try KeyFileManager(keyType: PgpKey.PRIVATE, keyPath: "", keyHandler: keychain.add).importKey(from: keyTriple.privateKey) XCTAssert(pgpAgent.isPrepared) try pgpAgent.initKeys() - XCTAssert(try pgpAgent.getKeyId()!.lowercased().hasSuffix(keyTriple.fingerprint)) + XCTAssert(try pgpAgent.getKeyID().first!.lowercased().hasSuffix(keyTriple.fingerprint)) try [ (true, true), (true, false), (false, true), (false, false) ].forEach{ a, b in