diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 11f34d3..a586a02 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -95,6 +95,7 @@ 556EC3DA22335D3400934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; }; 556EC3DB22335D3D00934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; }; 8BA607EB4C9C8258741AC18C /* Pods_passExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14E955B67C88672AA3A40BA0 /* Pods_passExtension.framework */; }; + 9A652412244ABED400DA0A41 /* UIAlertActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A652411244ABED400DA0A41 /* UIAlertActionExtension.swift */; }; 9A8A8387402FCCCECB1232A4 /* Pods_passKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2B2F844061EFA534FE9506 /* Pods_passKitTests.framework */; }; 9AA710CA23939C68009E3213 /* GitCredentialPassword.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA710C923939C68009E3213 /* GitCredentialPassword.swift */; }; 9ADC954124418A5F0005402E /* PasswordStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */; }; @@ -347,6 +348,7 @@ 62DEE9943E0F2B8C79E3FC5B /* Pods-passExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-passExtension/Pods-passExtension.release.xcconfig"; sourceTree = ""; }; 64AA8DF9E73F39CCC3317247 /* Pods-passKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-passKit/Pods-passKit.release.xcconfig"; sourceTree = ""; }; 7CAD21E487234A0631B52E20 /* Pods-passKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-passKit/Pods-passKit.debug.xcconfig"; sourceTree = ""; }; + 9A652411244ABED400DA0A41 /* UIAlertActionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertActionExtension.swift; sourceTree = ""; }; 9AA710C923939C68009E3213 /* GitCredentialPassword.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitCredentialPassword.swift; sourceTree = ""; }; 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordStoreTest.swift; sourceTree = ""; }; A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurePasteboard.swift; sourceTree = ""; }; @@ -648,6 +650,14 @@ path = Crypto; sourceTree = ""; }; + 9A652410244ABEB800DA0A41 /* Extensions */ = { + isa = PBXGroup; + children = ( + 9A652411244ABED400DA0A41 /* UIAlertActionExtension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; A2168A801EFD431A005EA873 /* Controllers */ = { isa = PBXGroup; children = ( @@ -886,6 +896,7 @@ DC917BD51E2E8231000FDF54 /* pass */ = { isa = PBXGroup; children = ( + 9A652410244ABEB800DA0A41 /* Extensions */, DC19400C1E4B39400077E0A3 /* Controllers */, DC19400E1E4B3A610077E0A3 /* Helpers */, DC19400F1E4B3A9E0077E0A3 /* Views */, @@ -1530,6 +1541,7 @@ DC037CBF1E4ED4E100609409 /* TextViewTableViewCell.swift in Sources */, DCC441541E916382008A90C4 /* SSHKeyArmorImportTableViewController.swift in Sources */, 306D970E24091CDD006C0E2E /* SwitchTableViewCell.swift in Sources */, + 9A652412244ABED400DA0A41 /* UIAlertActionExtension.swift in Sources */, A2A61C201EEFABAD00CFE063 /* UtilsExtension.swift in Sources */, DC8963C01E38EEB900828B09 /* SSHKeyUrlImportTableViewController.swift in Sources */, 3066AD6823EE0D6500F65535 /* PGPKeyImporter.swift in Sources */, diff --git a/pass/Controllers/AdvancedSettingsTableViewController.swift b/pass/Controllers/AdvancedSettingsTableViewController.swift index 2275201..5dba6a1 100644 --- a/pass/Controllers/AdvancedSettingsTableViewController.swift +++ b/pass/Controllers/AdvancedSettingsTableViewController.swift @@ -56,7 +56,7 @@ class AdvancedSettingsTableViewController: UITableViewController { SVProgressHUD.showSuccess(withStatus: "Done".localize()) SVProgressHUD.dismiss(withDelay: 1) })) - alert.addAction(UIAlertAction(title: "Dismiss".localize(), style: UIAlertAction.Style.cancel, handler:nil)) + alert.addAction(UIAlertAction.dismiss()) self.present(alert, animated: true, completion: nil) } else if tableView.cellForRow(at: indexPath) == discardChangesTableViewCell { let alert = UIAlertController(title: "DiscardAllLocalChanges?".localize(), message: "DiscardExplanation.".localize(), preferredStyle: UIAlertController.Style.alert) @@ -72,7 +72,7 @@ class AdvancedSettingsTableViewController: UITableViewController { } })) - alert.addAction(UIAlertAction(title: "Dismiss".localize(), style: UIAlertAction.Style.cancel, handler:nil)) + alert.addAction(UIAlertAction.dismiss()) self.present(alert, animated: true, completion: nil) } } diff --git a/pass/Controllers/GitRepositorySettingsTableViewController.swift b/pass/Controllers/GitRepositorySettingsTableViewController.swift index 5f305c1..1e10023 100644 --- a/pass/Controllers/GitRepositorySettingsTableViewController.swift +++ b/pass/Controllers/GitRepositorySettingsTableViewController.swift @@ -147,7 +147,7 @@ class GitRepositorySettingsTableViewController: UITableViewController { alert.addAction(UIAlertAction(title: "Overwrite".localize(), style: .destructive) { _ in self.cloneAndSegueIfSuccess() }) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)) + alert.addAction(UIAlertAction.cancel()) return alert }() self.present(overwriteAlert, animated: true) @@ -271,7 +271,7 @@ class GitRepositorySettingsTableViewController: UITableViewController { self.gitAuthenticationMethod = .password }) } - optionMenu.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)) + optionMenu.addAction(UIAlertAction.cancel()) optionMenu.popoverPresentationController?.sourceView = authSSHKeyCell optionMenu.popoverPresentationController?.sourceRect = authSSHKeyCell.bounds present(optionMenu, animated: true) diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 95c8083..8152b27 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -87,7 +87,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni } } - @objc private func decryptThenShowPassword() { + @objc private func decryptThenShowPassword(keyID: String? = nil) { guard let passwordEntity = passwordEntity else { Utils.alert(title: "CannotShowPassword".localize(), message: "PasswordDoesNotExist".localize(), controller: self, handler: {(UIAlertAction) -> Void in self.navigationController!.popViewController(animated: true) @@ -98,15 +98,26 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni // decrypt password do { let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: self) - self.password = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: requestPGPKeyPassphrase) + self.password = try self.passwordStore.decrypt(passwordEntity: passwordEntity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase) + } catch AppError.PgpPrivateKeyNotFound(let key) { + DispatchQueue.main.async { + // alert: cancel or try again + let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.PgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction.cancelAndPopView(controller: self)) + let selectKey = UIAlertAction.selectKey(controller: self) { action in + self.decryptThenShowPassword(keyID: action.title) + } + alert.addAction(selectKey) + + self.present(alert, animated: true, completion: nil) + } + return } catch { DispatchQueue.main.async { // alert: cancel or try again - let alert = UIAlertController(title: "CannotShowPassword".localize(), message: error.localizedDescription, preferredStyle: UIAlertController.Style.alert) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertAction.Style.default) { _ in - self.navigationController!.popViewController(animated: true) - }) - alert.addAction(UIAlertAction(title: "TryAgain".localize(), style: UIAlertAction.Style.destructive) {_ in + let alert = UIAlertController(title: "CannotShowPassword".localize(), message: error.localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction.cancelAndPopView(controller: self)) + alert.addAction(UIAlertAction(title: "TryAgain".localize(), style: .default) {_ in self.decryptThenShowPassword() }) self.present(alert, animated: true, completion: nil) @@ -171,20 +182,39 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni @IBAction private func saveEditPassword(segue: UIStoryboardSegue) { if self.password!.changed != 0 { - SVProgressHUD.show(withStatus: "Saving".localize()) - do { - self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!) - self.setTableData() - self.tableView.reloadData() - SVProgressHUD.showSuccess(withStatus: "Success".localize()) - SVProgressHUD.dismiss(withDelay: 1) - } catch { - SVProgressHUD.showSuccess(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) - } + self.saveEditPassword(password: self.password!) } } + private func saveEditPassword(password: Password, keyID: String? = nil) { + SVProgressHUD.show(withStatus: "Saving".localize()) + do { + self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: password, keyID: keyID) + self.setTableData() + self.tableView.reloadData() + SVProgressHUD.showSuccess(withStatus: "Success".localize()) + SVProgressHUD.dismiss(withDelay: 1) + } catch AppError.PgpPublicKeyNotFound(let key) { + DispatchQueue.main.async { + // alert: cancel or select keys + SVProgressHUD.dismiss() + let alert = UIAlertController(title: "Cannot Edit Password", message: AppError.PgpPublicKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction.cancelAndPopView(controller: self)) + let selectKey = UIAlertAction.selectKey(controller: self) { action in + self.saveEditPassword(password: password, keyID: action.title) + } + alert.addAction(selectKey) + + self.present(alert, animated: true, completion: nil) + } + return + } catch { + DispatchQueue.main.async { + Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil) + } + } + } + @IBAction private func deletePassword(segue: UIStoryboardSegue) { do { try passwordStore.delete(passwordEntity: passwordEntity!) diff --git a/pass/Controllers/PasswordEditorTableViewController.swift b/pass/Controllers/PasswordEditorTableViewController.swift index bc54a69..7575ebc 100644 --- a/pass/Controllers/PasswordEditorTableViewController.swift +++ b/pass/Controllers/PasswordEditorTableViewController.swift @@ -251,7 +251,7 @@ class PasswordEditorTableViewController: UITableViewController { alert.addAction(UIAlertAction(title: "Delete".localize(), style: UIAlertAction.Style.destructive, handler: {[unowned self] (action) -> Void in self.performSegue(withIdentifier: "deletePasswordSegue", sender: self) })) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertAction.Style.cancel, handler:nil)) + alert.addAction(UIAlertAction.cancel()) self.present(alert, animated: true, completion: nil) } else if selectedCell == scanQRCodeCell { self.performSegue(withIdentifier: "showQRScannerSegue", sender: self) @@ -392,7 +392,7 @@ extension PasswordEditorTableViewController: FillPasswordTableViewCellDelegate { alert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive, handler: {_ in self.generateAndCopyPasswordNoOtpCheck() })) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertAction.Style.cancel, handler: nil)) + alert.addAction(UIAlertAction.cancel()) self.present(alert, animated: true, completion: nil) } else { self.generateAndCopyPasswordNoOtpCheck() @@ -442,7 +442,7 @@ extension PasswordEditorTableViewController: SFSafariViewControllerDelegate { // update cell manually, no need to call reloadData() self.fillPasswordCell?.setContent(content: generatedPassword) })) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertAction.Style.cancel, handler:nil)) + alert.addAction(UIAlertAction.cancel()) self.present(alert, animated: true, completion: nil) } } diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index 7f9027c..380ff41 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -139,21 +139,39 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } @IBAction func saveAddPassword(segue: UIStoryboardSegue) { if let controller = segue.source as? AddPasswordTableViewController { - SVProgressHUD.setDefaultMaskType(.black) - SVProgressHUD.setDefaultStyle(.light) - SVProgressHUD.show(withStatus: "Saving".localize()) - DispatchQueue.global(qos: .userInitiated).async { - do { - let _ = try self.passwordStore.add(password: controller.password!) - DispatchQueue.main.async { - // will trigger reloadTableView() by a notification - SVProgressHUD.showSuccess(withStatus: "Done".localize()) - SVProgressHUD.dismiss(withDelay: 1) - } - } catch { - DispatchQueue.main.async { - Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil) + addPassword(password: controller.password!) + } + } + + private func addPassword(password: Password, keyID: String? = nil) { + SVProgressHUD.setDefaultMaskType(.black) + SVProgressHUD.setDefaultStyle(.light) + SVProgressHUD.show(withStatus: "Saving".localize()) + DispatchQueue.global(qos: .userInitiated).async { + do { + let _ = try self.passwordStore.add(password: password, keyID: keyID) + DispatchQueue.main.async { + // will trigger reloadTableView() by a notification + SVProgressHUD.showSuccess(withStatus: "Done".localize()) + SVProgressHUD.dismiss(withDelay: 1) + } + } catch AppError.PgpPublicKeyNotFound(let key) { + DispatchQueue.main.async { + // alert: cancel or select keys + SVProgressHUD.dismiss() + let alert = UIAlertController(title: "Cannot Encrypt Password", message: AppError.PgpPublicKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction.cancelAndPopView(controller: self)) + let selectKey = UIAlertAction.selectKey(controller: self) { action in + self.addPassword(password: password, keyID: action.title) } + alert.addAction(selectKey) + + self.present(alert, animated: true, completion: nil) + } + return + } catch { + DispatchQueue.main.async { + Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil) } } } diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index f9017b4..a761085 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -170,7 +170,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele Defaults.pgpKeySource = nil }) } - optionMenu.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)) + optionMenu.addAction(UIAlertAction.cancel()) optionMenu.popoverPresentationController?.sourceView = pgpKeyTableViewCell optionMenu.popoverPresentationController?.sourceRect = pgpKeyTableViewCell.bounds present(optionMenu, animated: true) @@ -193,10 +193,9 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele self?.setPasscodeLock() } - let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil) optionMenu.addAction(removePasscodeAction) optionMenu.addAction(changePasscodeAction) - optionMenu.addAction(cancelAction) + optionMenu.addAction(UIAlertAction.cancel()) optionMenu.popoverPresentationController?.sourceView = passcodeTableViewCell optionMenu.popoverPresentationController?.sourceRect = passcodeTableViewCell.bounds self.present(optionMenu, animated: true, completion: nil) @@ -240,7 +239,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele saveAction.isEnabled = false // disable the Save button by default // cancel action - let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil) + let cancelAction = UIAlertAction.cancel() // present setPasscodeLockAlert?.addAction(saveAction) diff --git a/pass/Extensions/UIAlertActionExtension.swift b/pass/Extensions/UIAlertActionExtension.swift new file mode 100644 index 0000000..20445e7 --- /dev/null +++ b/pass/Extensions/UIAlertActionExtension.swift @@ -0,0 +1,44 @@ +// +// UIAlertActionExtension.swift +// passKit +// +// Created by Sun, Mingshen on 4/17/20. +// Copyright © 2020 Bob Sun. All rights reserved. +// + +import UIKit +import Foundation +import passKit + +extension UIAlertAction { + static func cancelAndPopView(controller: UIViewController) -> UIAlertAction { + UIAlertAction(title: "Cancel".localize(), style: .cancel) { _ in + controller.navigationController?.popViewController(animated: true) + } + } + + static func cancel() -> UIAlertAction { + cancel(with: "Cancel") + } + + static func dismiss() -> UIAlertAction { + cancel(with: "Dismiss") + } + + static func cancel(with title: String) -> UIAlertAction { + UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil) + } + + static func selectKey(controller: UIViewController, handler: ((UIAlertAction) -> Void)?) -> UIAlertAction { + UIAlertAction(title: "Select Key", style: .default) { _ in + let selectKeyAlert = UIAlertController(title: "Select from imported keys", message: nil, preferredStyle: .actionSheet) + try? PGPAgent.shared.getShortKeyID().forEach({ k in + let action = UIAlertAction(title: k, style: .default, handler: handler) + selectKeyAlert.addAction(action) + }) + selectKeyAlert.addAction(UIAlertAction.cancelAndPopView(controller: controller)) + controller.present(selectKeyAlert, animated: true, completion: nil) + } + } + +} diff --git a/passKit/Controllers/PasscodeLockViewController.swift b/passKit/Controllers/PasscodeLockViewController.swift index 6dbfb5a..9696574 100644 --- a/passKit/Controllers/PasscodeLockViewController.swift +++ b/passKit/Controllers/PasscodeLockViewController.swift @@ -213,7 +213,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { } } })) - alert.addAction(UIAlertAction(title: "Dismiss".localize(), style: UIAlertAction.Style.cancel, handler:nil)) + alert.addAction(UIAlertAction(title: "Dismiss".localize(), style: .cancel, handler: nil)) self.present(alert, animated: true, completion: nil) } diff --git a/passKit/Crypto/PGPAgent.swift b/passKit/Crypto/PGPAgent.swift index a3a5002..2e693ee 100644 --- a/passKit/Crypto/PGPAgent.swift +++ b/passKit/Crypto/PGPAgent.swift @@ -42,16 +42,12 @@ public class PGPAgent { public func getShortKeyID() throws -> [String] { try checkAndInit() - return pgpInterface?.shortKeyID ?? [] + return pgpInterface?.shortKeyID.sorted() ?? [] } public func decrypt(encryptedData: Data, keyID: String, requestPGPKeyPassphrase: (String) -> String) throws -> Data? { - // Remember the previous status and set the current status - let previousDecryptStatus = self.latestDecryptStatus - self.latestDecryptStatus = false // Init keys. try checkAndInit() - guard let pgpInterface = pgpInterface else { throw AppError.Decryption } @@ -60,6 +56,10 @@ public class PGPAgent { throw AppError.PgpPrivateKeyNotFound(keyID: keyID) } + // Remember the previous status and set the current status + let previousDecryptStatus = self.latestDecryptStatus + self.latestDecryptStatus = false + // Get the PGP key passphrase. var passphrase = "" if previousDecryptStatus == false { diff --git a/passKit/Models/PasswordStore.swift b/passKit/Models/PasswordStore.swift index db2a7a0..aa35fac 100644 --- a/passKit/Models/PasswordStore.swift +++ b/passKit/Models/PasswordStore.swift @@ -523,13 +523,13 @@ public class PasswordStore { return parentPasswordEntity } - public func add(password: Password) throws -> PasswordEntity? { + public func add(password: Password, keyID: String? = nil) throws -> PasswordEntity? { try createDirectoryTree(at: password.url) - let newPasswordEntity = try addPasswordEntities(password: password) let saveURL = storeURL.appendingPathComponent(password.url.path) - try self.encrypt(password: password).write(to: saveURL) + try self.encrypt(password: password, keyID: keyID).write(to: saveURL) try gitAdd(path: password.url.path) let _ = try gitCommit(message: "AddPassword.".localize(password.url.deletingPathExtension().path)) + let newPasswordEntity = try addPasswordEntities(password: password) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) return newPasswordEntity } @@ -543,13 +543,13 @@ public class PasswordStore { NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } - public func edit(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? { + public func edit(passwordEntity: PasswordEntity, password: Password, keyID: String? = nil) throws -> PasswordEntity? { var newPasswordEntity: PasswordEntity? = passwordEntity let url = try passwordEntity.getURL() if password.changed&PasswordChange.content.rawValue != 0 { let saveURL = storeURL.appendingPathComponent(url.path) - try self.encrypt(password: password).write(to: saveURL) + try self.encrypt(password: password, keyID: keyID).write(to: saveURL) try gitAdd(path: url.path) let _ = try gitCommit(message: "EditPassword.".localize(url.deletingPathExtension().path.removingPercentEncoding!)) newPasswordEntity = passwordEntity @@ -698,9 +698,9 @@ public class PasswordStore { return try storeRepository.localCommitsRelative(toRemoteBranch: remoteBranch) } - public func decrypt(passwordEntity: PasswordEntity, requestPGPKeyPassphrase: (String) -> String) throws -> Password? { + public func decrypt(passwordEntity: PasswordEntity, keyID: String? = nil, requestPGPKeyPassphrase: (String) -> String) throws -> Password? { let encryptedDataPath = storeURL.appendingPathComponent(passwordEntity.getPath()) - let keyID = findGPGID(from: encryptedDataPath) + let keyID = keyID ?? findGPGID(from: encryptedDataPath) let encryptedData = try Data(contentsOf: encryptedDataPath) guard let decryptedData = try PGPAgent.shared.decrypt(encryptedData: encryptedData, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase) else { throw AppError.Decryption @@ -710,9 +710,9 @@ public class PasswordStore { return Password(name: passwordEntity.getName(), url: url, plainText: plainText) } - public func encrypt(password: Password) throws -> Data { + public func encrypt(password: Password, keyID: String? = nil) throws -> Data { let encryptedDataPath = storeURL.appendingPathComponent(password.url.path) - let keyID = findGPGID(from: encryptedDataPath) + let keyID = keyID ?? findGPGID(from: encryptedDataPath) return try PGPAgent.shared.encrypt(plainData: password.plainData, keyID: keyID) }