Polish logic on initializing PGP keys.
- simplify interfaces - more robust
This commit is contained in:
parent
df8e254c34
commit
d338e725d5
6 changed files with 93 additions and 63 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)!
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,28 +165,74 @@ 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."])
|
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 {
|
if let key = importKey(from: keyPath) {
|
||||||
throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public 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."])
|
||||||
|
}
|
||||||
|
} 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()
|
let pgpKeyData = try Data(contentsOf: url)
|
||||||
Defaults[.pgpKeyID] = key.keyID!.shortKeyString
|
try pgpKeyData.write(to: URL(fileURLWithPath: pgpKeyLocalPath), options: .atomic)
|
||||||
if let gpgUser = key.users[0] as? PGPUser {
|
try initPGPKey(keyType)
|
||||||
Defaults[.pgpKeyUserID] = gpgUser.userID
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue