Polish logic on initializing PGP keys.

- simplify interfaces
- more robust
This commit is contained in:
Bob Sun 2017-03-16 22:06:39 -07:00
parent df8e254c34
commit d338e725d5
6 changed files with 93 additions and 63 deletions

View file

@ -27,7 +27,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "saveAddPasswordSegue" { if identifier == "saveAddPasswordSegue" {
// check PGP key // check PGP key
if Defaults[.pgpKeyID] == nil { if PasswordStore.shared.privateKey == nil {
let alertTitle = "Cannot Add Password" let alertTitle = "Cannot Add Password"
let alertMessage = "PGP Key is not set. Please set your PGP Key first." let alertMessage = "PGP Key is not set. Please set your PGP Key first."
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)

View file

@ -283,7 +283,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
func copyToPasteboard(from indexPath: IndexPath) { 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) Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil)
return return
} }
@ -387,7 +387,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "showPasswordDetail" { 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) 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 { if let s = sender as? UITableViewCell {
let selectedIndexPath = tableView.indexPath(for: s)! let selectedIndexPath = tableView.indexPath(for: s)!

View file

@ -41,12 +41,10 @@ class SettingsTableViewController: UITableViewController {
SVProgressHUD.show(withStatus: "Fetching PGP Key") SVProgressHUD.show(withStatus: "Fetching PGP Key")
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do { do {
try PasswordStore.shared.initPGP(pgpPublicKeyURL: Defaults[.pgpPublicKeyURL]!, try PasswordStore.shared.initPGPKey(from: Defaults[.pgpPublicKeyURL]!, keyType: .public)
pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath, try PasswordStore.shared.initPGPKey(from: Defaults[.pgpPrivateKeyURL]!, keyType: .secret)
pgpPrivateKeyURL: Defaults[.pgpPrivateKeyURL]!,
pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath)
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] self.pgpKeyTableViewCell.detailTextLabel?.text = PasswordStore.shared.pgpKeyID
SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.showSuccess(withStatus: "Success")
SVProgressHUD.dismiss(withDelay: 1) 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) 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 { } catch {
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set"
Defaults[.pgpKeyID] = nil
SVProgressHUD.showError(withStatus: error.localizedDescription) SVProgressHUD.showError(withStatus: error.localizedDescription)
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
@ -76,19 +73,16 @@ class SettingsTableViewController: UITableViewController {
SVProgressHUD.show(withStatus: "Fetching PGP Key") SVProgressHUD.show(withStatus: "Fetching PGP Key")
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do { do {
try PasswordStore.shared.initPGP(pgpPublicKeyArmor: controller.armorPublicKeyTextView.text!, try PasswordStore.shared.initPGPKey(with: controller.armorPublicKeyTextView.text, keyType: .public)
pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath, try PasswordStore.shared.initPGPKey(with: controller.armorPrivateKeyTextView.text, keyType: .secret)
pgpPrivateKeyArmor: controller.armorPrivateKeyTextView.text!,
pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath)
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] self.pgpKeyTableViewCell.detailTextLabel?.text = PasswordStore.shared.pgpKeyID
SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.showSuccess(withStatus: "Success")
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
} catch { } catch {
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set"
Defaults[.pgpKeyID] = nil
SVProgressHUD.showError(withStatus: error.localizedDescription) SVProgressHUD.showError(withStatus: error.localizedDescription)
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
@ -193,10 +187,10 @@ class SettingsTableViewController: UITableViewController {
} }
private func setPGPKeyTableViewCellDetailText() { private func setPGPKeyTableViewCellDetailText() {
if Defaults[.pgpKeyID] == nil { if let pgpKeyID = PasswordStore.shared.pgpKeyID {
pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" pgpKeyTableViewCell.detailTextLabel?.text = pgpKeyID
} else { } else {
pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] pgpKeyTableViewCell.detailTextLabel?.text = "Not Set"
} }
} }
@ -343,10 +337,7 @@ class SettingsTableViewController: UITableViewController {
DispatchQueue.main.async { DispatchQueue.main.async {
try? PasswordStore.shared.initPGP( PasswordStore.shared.initPGPKeys()
pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath,
pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath
)
let key: PGPKey = PasswordStore.shared.getPgpPrivateKey() let key: PGPKey = PasswordStore.shared.getPgpPrivateKey()
Defaults[.pgpKeySource] = "file" Defaults[.pgpKeySource] = "file"
@ -357,7 +348,7 @@ class SettingsTableViewController: UITableViewController {
} }
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] self.pgpKeyTableViewCell.detailTextLabel?.text = PasswordStore.shared.pgpKeyID
} }
} }

View file

@ -17,8 +17,6 @@ extension DefaultsKeys {
static let pgpPublicKeyArmor = DefaultsKey<String?>("pgpPublicKeyArmor") static let pgpPublicKeyArmor = DefaultsKey<String?>("pgpPublicKeyArmor")
static let pgpPrivateKeyArmor = DefaultsKey<String?>("pgpPrivateKeyArmor") static let pgpPrivateKeyArmor = DefaultsKey<String?>("pgpPrivateKeyArmor")
static let pgpKeyID = DefaultsKey<String?>("pgpKeyID")
static let pgpKeyUserID = DefaultsKey<String?>("pgpKeyUserID")
static let gitRepositoryURL = DefaultsKey<URL?>("gitRepositoryURL") static let gitRepositoryURL = DefaultsKey<URL?>("gitRepositoryURL")
static let gitRepositoryAuthenticationMethod = DefaultsKey<String?>("gitRepositoryAuthenticationMethod") static let gitRepositoryAuthenticationMethod = DefaultsKey<String?>("gitRepositoryAuthenticationMethod")

View file

@ -79,7 +79,6 @@ class Utils {
Defaults.remove(.pgpPrivateKeyArmor) Defaults.remove(.pgpPrivateKeyArmor)
Defaults.remove(.pgpPrivateKeyURL) Defaults.remove(.pgpPrivateKeyURL)
Defaults.remove(.pgpPublicKeyURL) Defaults.remove(.pgpPublicKeyURL)
Defaults.remove(.pgpKeyID)
Utils.removeKeychain(name: ".pgpKeyPassphrase") Utils.removeKeychain(name: ".pgpKeyPassphrase")
} }

View file

@ -93,11 +93,23 @@ struct GitCredential {
class PasswordStore { class PasswordStore {
static let shared = PasswordStore() static let shared = PasswordStore()
let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)") let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)")
let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp") let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp")
var storeRepository: GTRepository? var storeRepository: GTRepository?
var gitCredential: GitCredential? 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 { var gitSignatureForNow: GTSignature {
get { get {
return GTSignature(name: Defaults[.gitRepositoryUsername]!, email: Defaults[.gitRepositoryUsername]!+"@passforios", time: Date())! return GTSignature(name: Defaults[.gitRepositoryUsername]!, email: Defaults[.gitRepositoryUsername]!+"@passforios", time: Date())!
@ -134,11 +146,7 @@ class PasswordStore {
} catch { } catch {
print(error) print(error)
} }
if Defaults[.pgpKeyID] != nil { initPGPKeys()
pgp.importKeys(fromFile: Globals.pgpPublicKeyPath, allowDuplicates: false)
pgp.importKeys(fromFile: Globals.pgpPrivateKeyPath, allowDuplicates: false)
}
if Defaults[.gitRepositoryAuthenticationMethod] == "Password" { if Defaults[.gitRepositoryAuthenticationMethod] == "Password" {
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitRepositoryUsername]!, password: Utils.getPasswordFromKeychain(name: "gitRepositoryPassword") ?? "")) gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitRepositoryUsername]!, password: Utils.getPasswordFromKeychain(name: "gitRepositoryPassword") ?? ""))
} else if Defaults[.gitRepositoryAuthenticationMethod] == "SSH Key"{ } else if Defaults[.gitRepositoryAuthenticationMethod] == "SSH Key"{
@ -157,30 +165,76 @@ class PasswordStore {
} }
func initPGP(pgpPublicKeyLocalPath: String, pgpPrivateKeyLocalPath: String) throws { public func initPGPKeys() {
let pgpPublicKeyData = NSData(contentsOfFile: pgpPublicKeyLocalPath)! as Data do {
if pgpPublicKeyData.count == 0 { try initPGPKey(.public)
throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public key."]) 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."])
} }
let pgpPrivateKeyData = NSData(contentsOfFile: pgpPrivateKeyLocalPath)! as Data
if pgpPrivateKeyData.count == 0 { public func initPGPKey(_ keyType: PGPKeyType) throws {
throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public key."]) 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."])
} }
pgp.importKeys(from: pgpPrivateKeyData, allowDuplicates: false)
if pgp.getKeysOf(.secret).count == 0 { if let key = importKey(from: keyPath) {
throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import seceret key."]) 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."])
} }
let key: PGPKey = getPgpPrivateKey() } else {
Defaults[.pgpKeyID] = key.keyID!.shortKeyString throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import key."])
if let gpgUser = key.users[0] as? PGPUser {
Defaults[.pgpKeyUserID] = gpgUser.userID
} }
} }
public func initPGPKey(from url: URL, keyType: PGPKeyType) throws{
var pgpKeyLocalPath = ""
if keyType == .public {
pgpKeyLocalPath = Globals.pgpPublicKeyPath
} else {
pgpKeyLocalPath = Globals.pgpPrivateKeyPath
}
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 { func getPgpPrivateKey() -> PGPKey {
return pgp.getKeysOf(.secret)[0] return pgp.getKeysOf(.secret)[0]
} }
@ -207,20 +261,6 @@ class PasswordStore {
return true 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, func cloneRepository(remoteRepoURL: URL,
credential: GitCredential, credential: GitCredential,
transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void, transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void,
@ -564,6 +604,8 @@ class PasswordStore {
} }
func erase() { func erase() {
publicKey = nil
privateKey = nil
Utils.removeFileIfExists(at: storeURL) Utils.removeFileIfExists(at: storeURL)
Utils.removeFileIfExists(at: tempStoreURL) Utils.removeFileIfExists(at: tempStoreURL)