From d338e725d5107774165ad71e4c799b809c18c97b Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Thu, 16 Mar 2017 22:06:39 -0700 Subject: [PATCH] Polish logic on initializing PGP keys. - simplify interfaces - more robust --- .../AddPasswordTableViewController.swift | 2 +- .../Controllers/PasswordsViewController.swift | 4 +- .../SettingsTableViewController.swift | 31 ++--- pass/Helpers/DefaultsKeys.swift | 2 - pass/Helpers/Utils.swift | 1 - pass/Models/PasswordStore.swift | 116 ++++++++++++------ 6 files changed, 93 insertions(+), 63 deletions(-) diff --git a/pass/Controllers/AddPasswordTableViewController.swift b/pass/Controllers/AddPasswordTableViewController.swift index 55119d8..65c8481 100644 --- a/pass/Controllers/AddPasswordTableViewController.swift +++ b/pass/Controllers/AddPasswordTableViewController.swift @@ -27,7 +27,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { if identifier == "saveAddPasswordSegue" { // check PGP key - if Defaults[.pgpKeyID] == nil { + if PasswordStore.shared.privateKey == nil { let alertTitle = "Cannot Add Password" let alertMessage = "PGP Key is not set. Please set your PGP Key first." Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index 1ef9648..f6c09d0 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -283,7 +283,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } func copyToPasteboard(from indexPath: IndexPath) { - guard Defaults[.pgpKeyID] != nil else { + guard PasswordStore.shared.privateKey != nil else { Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil) return } @@ -387,7 +387,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { if identifier == "showPasswordDetail" { - if Defaults[.pgpKeyID] == nil { + guard PasswordStore.shared.privateKey != nil else { Utils.alert(title: "Cannot Show Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil) if let s = sender as? UITableViewCell { let selectedIndexPath = tableView.indexPath(for: s)! diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 26bfba9..ddb93c2 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -41,12 +41,10 @@ class SettingsTableViewController: UITableViewController { SVProgressHUD.show(withStatus: "Fetching PGP Key") DispatchQueue.global(qos: .userInitiated).async { [unowned self] in do { - try PasswordStore.shared.initPGP(pgpPublicKeyURL: Defaults[.pgpPublicKeyURL]!, - pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath, - pgpPrivateKeyURL: Defaults[.pgpPrivateKeyURL]!, - pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath) + try PasswordStore.shared.initPGPKey(from: Defaults[.pgpPublicKeyURL]!, keyType: .public) + try PasswordStore.shared.initPGPKey(from: Defaults[.pgpPrivateKeyURL]!, keyType: .secret) DispatchQueue.main.async { - self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] + self.pgpKeyTableViewCell.detailTextLabel?.text = PasswordStore.shared.pgpKeyID SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.dismiss(withDelay: 1) Utils.alert(title: "Rememver to Remove the Key", message: "Remember to remove the key from the server.", controller: self, completion: nil) @@ -54,7 +52,6 @@ class SettingsTableViewController: UITableViewController { } catch { DispatchQueue.main.async { self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" - Defaults[.pgpKeyID] = nil SVProgressHUD.showError(withStatus: error.localizedDescription) SVProgressHUD.dismiss(withDelay: 1) } @@ -76,19 +73,16 @@ class SettingsTableViewController: UITableViewController { SVProgressHUD.show(withStatus: "Fetching PGP Key") DispatchQueue.global(qos: .userInitiated).async { [unowned self] in do { - try PasswordStore.shared.initPGP(pgpPublicKeyArmor: controller.armorPublicKeyTextView.text!, - pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath, - pgpPrivateKeyArmor: controller.armorPrivateKeyTextView.text!, - pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath) + try PasswordStore.shared.initPGPKey(with: controller.armorPublicKeyTextView.text, keyType: .public) + try PasswordStore.shared.initPGPKey(with: controller.armorPrivateKeyTextView.text, keyType: .secret) DispatchQueue.main.async { - self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] + self.pgpKeyTableViewCell.detailTextLabel?.text = PasswordStore.shared.pgpKeyID SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.dismiss(withDelay: 1) } } catch { DispatchQueue.main.async { self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" - Defaults[.pgpKeyID] = nil SVProgressHUD.showError(withStatus: error.localizedDescription) SVProgressHUD.dismiss(withDelay: 1) } @@ -193,10 +187,10 @@ class SettingsTableViewController: UITableViewController { } private func setPGPKeyTableViewCellDetailText() { - if Defaults[.pgpKeyID] == nil { - pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" + if let pgpKeyID = PasswordStore.shared.pgpKeyID { + pgpKeyTableViewCell.detailTextLabel?.text = pgpKeyID } else { - pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] + pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" } } @@ -343,10 +337,7 @@ class SettingsTableViewController: UITableViewController { DispatchQueue.main.async { - try? PasswordStore.shared.initPGP( - pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath, - pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath - ) + PasswordStore.shared.initPGPKeys() let key: PGPKey = PasswordStore.shared.getPgpPrivateKey() Defaults[.pgpKeySource] = "file" @@ -357,7 +348,7 @@ class SettingsTableViewController: UITableViewController { } SVProgressHUD.dismiss() - self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] + self.pgpKeyTableViewCell.detailTextLabel?.text = PasswordStore.shared.pgpKeyID } } diff --git a/pass/Helpers/DefaultsKeys.swift b/pass/Helpers/DefaultsKeys.swift index a658818..a97b1a3 100644 --- a/pass/Helpers/DefaultsKeys.swift +++ b/pass/Helpers/DefaultsKeys.swift @@ -17,8 +17,6 @@ extension DefaultsKeys { static let pgpPublicKeyArmor = DefaultsKey("pgpPublicKeyArmor") static let pgpPrivateKeyArmor = DefaultsKey("pgpPrivateKeyArmor") - static let pgpKeyID = DefaultsKey("pgpKeyID") - static let pgpKeyUserID = DefaultsKey("pgpKeyUserID") static let gitRepositoryURL = DefaultsKey("gitRepositoryURL") static let gitRepositoryAuthenticationMethod = DefaultsKey("gitRepositoryAuthenticationMethod") diff --git a/pass/Helpers/Utils.swift b/pass/Helpers/Utils.swift index 76902cc..1278f24 100644 --- a/pass/Helpers/Utils.swift +++ b/pass/Helpers/Utils.swift @@ -79,7 +79,6 @@ class Utils { Defaults.remove(.pgpPrivateKeyArmor) Defaults.remove(.pgpPrivateKeyURL) Defaults.remove(.pgpPublicKeyURL) - Defaults.remove(.pgpKeyID) Utils.removeKeychain(name: ".pgpKeyPassphrase") } diff --git a/pass/Models/PasswordStore.swift b/pass/Models/PasswordStore.swift index ddc2351..1d6bb0b 100644 --- a/pass/Models/PasswordStore.swift +++ b/pass/Models/PasswordStore.swift @@ -93,11 +93,23 @@ struct GitCredential { class PasswordStore { static let shared = PasswordStore() - let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)") let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp") + var storeRepository: GTRepository? var gitCredential: GitCredential? + var pgpKeyID: String? + var publicKey: PGPKey? { + didSet { + if publicKey != nil { + pgpKeyID = publicKey!.keyID!.shortKeyString + } else { + pgpKeyID = nil + } + } + } + var privateKey: PGPKey? + var gitSignatureForNow: GTSignature { get { return GTSignature(name: Defaults[.gitRepositoryUsername]!, email: Defaults[.gitRepositoryUsername]!+"@passforios", time: Date())! @@ -134,11 +146,7 @@ class PasswordStore { } catch { print(error) } - if Defaults[.pgpKeyID] != nil { - pgp.importKeys(fromFile: Globals.pgpPublicKeyPath, allowDuplicates: false) - pgp.importKeys(fromFile: Globals.pgpPrivateKeyPath, allowDuplicates: false) - - } + initPGPKeys() if Defaults[.gitRepositoryAuthenticationMethod] == "Password" { gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitRepositoryUsername]!, password: Utils.getPasswordFromKeychain(name: "gitRepositoryPassword") ?? "")) } else if Defaults[.gitRepositoryAuthenticationMethod] == "SSH Key"{ @@ -157,28 +165,74 @@ class PasswordStore { } - func initPGP(pgpPublicKeyLocalPath: String, pgpPrivateKeyLocalPath: String) throws { - let pgpPublicKeyData = NSData(contentsOfFile: pgpPublicKeyLocalPath)! as Data - if pgpPublicKeyData.count == 0 { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public key."]) + public func initPGPKeys() { + do { + try initPGPKey(.public) + try initPGPKey(.secret) + } catch { + print(error) } - pgp.importKeys(from: pgpPublicKeyData, allowDuplicates: false) - if pgp.getKeysOf(.public).count == 0 { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public key."]) + } + + public func initPGPKey(_ keyType: PGPKeyType) throws { + var keyPath = "" + switch keyType { + case .public: + keyPath = Globals.pgpPublicKeyPath + case .secret: + keyPath = Globals.pgpPrivateKeyPath + default: + throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import key."]) } - let pgpPrivateKeyData = NSData(contentsOfFile: pgpPrivateKeyLocalPath)! as Data - if pgpPrivateKeyData.count == 0 { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public key."]) + + if let key = importKey(from: keyPath) { + switch keyType { + case .public: + self.publicKey = key + case .secret: + self.privateKey = key + default: + throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import key."]) + } + } else { + throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import key."]) } - pgp.importKeys(from: pgpPrivateKeyData, allowDuplicates: false) - if pgp.getKeysOf(.secret).count == 0 { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import seceret key."]) + } + + public func initPGPKey(from url: URL, keyType: PGPKeyType) throws{ + var pgpKeyLocalPath = "" + if keyType == .public { + pgpKeyLocalPath = Globals.pgpPublicKeyPath + } else { + pgpKeyLocalPath = Globals.pgpPrivateKeyPath } - let key: PGPKey = getPgpPrivateKey() - Defaults[.pgpKeyID] = key.keyID!.shortKeyString - if let gpgUser = key.users[0] as? PGPUser { - Defaults[.pgpKeyUserID] = gpgUser.userID + let pgpKeyData = try Data(contentsOf: url) + try pgpKeyData.write(to: URL(fileURLWithPath: pgpKeyLocalPath), options: .atomic) + try initPGPKey(keyType) + } + + public func initPGPKey(with armorKey: String, keyType: PGPKeyType) throws { + var pgpKeyLocalPath = "" + if keyType == .public { + pgpKeyLocalPath = Globals.pgpPublicKeyPath + } else { + pgpKeyLocalPath = Globals.pgpPrivateKeyPath } + try armorKey.write(toFile: pgpKeyLocalPath, atomically: true, encoding: .ascii) + try initPGPKey(keyType) + } + + + private func importKey(from keyPath: String) -> PGPKey? { + let fm = FileManager.default + if fm.fileExists(atPath: keyPath) { + if let keys = pgp.importKeys(fromFile: keyPath, allowDuplicates: false) as? [PGPKey] { + if keys.count > 0 { + return keys[0] + } + } + } + return nil } func getPgpPrivateKey() -> PGPKey { @@ -207,20 +261,6 @@ class PasswordStore { return true } - func initPGP(pgpPublicKeyURL: URL, pgpPublicKeyLocalPath: String, pgpPrivateKeyURL: URL, pgpPrivateKeyLocalPath: String) throws { - let pgpPublicData = try Data(contentsOf: pgpPublicKeyURL) - try pgpPublicData.write(to: URL(fileURLWithPath: pgpPublicKeyLocalPath), options: .atomic) - let pgpPrivateData = try Data(contentsOf: pgpPrivateKeyURL) - try pgpPrivateData.write(to: URL(fileURLWithPath: pgpPrivateKeyLocalPath), options: .atomic) - try initPGP(pgpPublicKeyLocalPath: pgpPublicKeyLocalPath, pgpPrivateKeyLocalPath: pgpPrivateKeyLocalPath) - } - - func initPGP(pgpPublicKeyArmor: String, pgpPublicKeyLocalPath: String, pgpPrivateKeyArmor: String, pgpPrivateKeyLocalPath: String) throws { - try pgpPublicKeyArmor.write(toFile: pgpPublicKeyLocalPath, atomically: true, encoding: .ascii) - try pgpPrivateKeyArmor.write(toFile: pgpPrivateKeyLocalPath, atomically: true, encoding: .ascii) - try initPGP(pgpPublicKeyLocalPath: pgpPublicKeyLocalPath, pgpPrivateKeyLocalPath: pgpPrivateKeyLocalPath) - } - func cloneRepository(remoteRepoURL: URL, credential: GitCredential, transferProgressBlock: @escaping (UnsafePointer, UnsafeMutablePointer) -> Void, @@ -564,6 +604,8 @@ class PasswordStore { } func erase() { + publicKey = nil + privateKey = nil Utils.removeFileIfExists(at: storeURL) Utils.removeFileIfExists(at: tempStoreURL)