diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 1995a8e..2ff17b4 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 301F6463216162550071A4CE /* AdditionField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 301F6462216162550071A4CE /* AdditionField.swift */; }; 301F6468216165290071A4CE /* ConstantsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 301F6467216165290071A4CE /* ConstantsTest.swift */; }; 301F646D216166AA0071A4CE /* AdditionFieldTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 301F646C216166AA0071A4CE /* AdditionFieldTest.swift */; }; + 302269B323E634B000F843A3 /* PGPKeyFileSettingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302269B223E634B000F843A3 /* PGPKeyFileSettingTableViewController.swift */; }; 302B2C9822C2BDE700D831EE /* AppKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B2C9722C2BDE700D831EE /* AppKeychain.swift */; }; 302E85612125ECC70031BA64 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302E85602125ECC70031BA64 /* Parser.swift */; }; 302E85632125EE550031BA64 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302E85622125EE550031BA64 /* Constants.swift */; }; @@ -233,6 +234,7 @@ 301F6467216165290071A4CE /* ConstantsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsTest.swift; sourceTree = ""; }; 301F646C216166AA0071A4CE /* AdditionFieldTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdditionFieldTest.swift; sourceTree = ""; }; 302202EE222F14E400555236 /* SearchBarScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarScope.swift; sourceTree = ""; }; + 302269B223E634B000F843A3 /* PGPKeyFileSettingTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PGPKeyFileSettingTableViewController.swift; sourceTree = ""; }; 302B2C9722C2BDE700D831EE /* AppKeychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKeychain.swift; sourceTree = ""; }; 302E85602125ECC70031BA64 /* Parser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = ""; }; 302E85622125EE550031BA64 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; @@ -717,6 +719,7 @@ DCFB77A81E502FF6008DE471 /* PasswordEditorTableViewController.swift */, DC5734AD1E439AD400D09270 /* PasswordsViewController.swift */, DC5F385A1E56AADB00C69ACA /* PGPKeyArmorSettingTableViewController.swift */, + 302269B223E634B000F843A3 /* PGPKeyFileSettingTableViewController.swift */, 3066AD6723EE0D6500F65535 /* PGPKeyImporter.swift */, DCA0499B1E3362F400522E8F /* PGPKeyUrlTableViewController.swift */, A2A7813E1E97DBD9001311F5 /* QRScannerController.swift */, @@ -1401,6 +1404,7 @@ 30C25DD721F4834D00BB27BB /* UILocalizedLabel.swift in Sources */, DC5734AE1E439AD400D09270 /* PasswordsViewController.swift in Sources */, 300713C52219D54100F553AC /* AutoCellHeightUITableViewController.swift in Sources */, + 302269B323E634B000F843A3 /* PGPKeyFileSettingTableViewController.swift in Sources */, DCD3C65E1EFB9BB400CBE842 /* SettingsSplitViewController.swift in Sources */, A20691F41F2A3D0E0096483D /* SecurePasteboard.swift in Sources */, DC3E64E61E656F11009A83DE /* CommitLogsTableViewController.swift in Sources */, diff --git a/pass/Base.lproj/Main.storyboard b/pass/Base.lproj/Main.storyboard index 3be8433..5fbb26a 100644 --- a/pass/Base.lproj/Main.storyboard +++ b/pass/Base.lproj/Main.storyboard @@ -241,6 +241,7 @@ + @@ -500,7 +501,7 @@ - + @@ -630,7 +631,7 @@ - + @@ -940,7 +941,7 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f - + @@ -1034,7 +1035,7 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f - + @@ -1075,7 +1076,7 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f - + @@ -1431,7 +1432,7 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f - + @@ -1449,7 +1450,7 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f - + @@ -1561,7 +1562,7 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f - + @@ -1676,7 +1677,7 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f @@ -1756,6 +1757,123 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1763,7 +1881,7 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f - + diff --git a/pass/Controllers/PGPKeyFileSettingTableViewController.swift b/pass/Controllers/PGPKeyFileSettingTableViewController.swift new file mode 100644 index 0000000..763f87a --- /dev/null +++ b/pass/Controllers/PGPKeyFileSettingTableViewController.swift @@ -0,0 +1,106 @@ +// +// PGPKeyFileSettingTableViewController.swift +// pass +// +// Created by Danny Moesch on 01.02.20. +// Copyright © 2020 Bob Sun. All rights reserved. +// + +import passKit + +class PGPKeyFileSettingTableViewController: AutoCellHeightUITableViewController { + + @IBOutlet weak var pgpPublicKeyFile: UITableViewCell! + @IBOutlet weak var pgpPrivateKeyFile: UITableViewCell! + + private let passwordStore = PasswordStore.shared + private let keychain = AppKeychain.shared + + private var publicKey: String? = nil + private var privateKey: String? = nil + + private enum KeyType { case none, `private`, `public` } + private var currentlyPicking = KeyType.none + + @IBAction func save(_ sender: Any) { + savePassphraseDialog() + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let cell = tableView.cellForRow(at: indexPath) + let picker = UIDocumentPickerViewController(documentTypes: ["public.data"], in: .import) + if cell == pgpPublicKeyFile { + currentlyPicking = .public + } else if cell == pgpPrivateKeyFile { + currentlyPicking = .private + } else { + return + } + picker.delegate = self + if #available(iOS 13.0, *) { + picker.shouldShowFileExtensions = true + } + present(picker, animated: true, completion: nil) + } +} + +extension PGPKeyFileSettingTableViewController: UIDocumentPickerDelegate { + + func documentPicker(_: UIDocumentPickerViewController, didPickDocumentsAt url: [URL]) { + guard let url = url.first else { + return + } + let fileName = url.lastPathComponent + do { + let fileContent = try String(contentsOf: url, encoding: .ascii) + switch currentlyPicking { + case .none: + return + case .public: + publicKey = fileContent + pgpPublicKeyFile.textLabel?.text = fileName + case .private: + privateKey = fileContent + pgpPrivateKeyFile.textLabel?.text = fileName + } + } catch { + Utils.alert(title: "CannotImportFile".localize(), message: "FileCannotBeImported.".localize(fileName), controller: self) + } + } +} + +extension PGPKeyFileSettingTableViewController: PGPKeyImporter { + + static let keySource = PGPKeySource.files + static let label = "LoadFromFiles".localize() + + func isReadyToUse() -> Bool { + return validate(key: publicKey) && validate(key: privateKey) + } + + func importKeys() throws { + guard let publicKey = publicKey, let privateKey = privateKey else { + return + } + Defaults.pgpKeySource = Self.keySource + + try KeyFileManager.PublicPgp.importKey(from: publicKey) + try KeyFileManager.PrivatePgp.importKey(from: privateKey) + } + + func doAfterImport() { + Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromLocation.".localize(), controller: self) + } + + func saveImportedKeys() { + self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) + } + + private func validate(key: String?) -> Bool { + guard key != nil else { + Utils.alert(title: "CannotSavePgpKey".localize(), message: "KeyFileNotSet.".localize(), controller: self) + return false + } + return true + } +} diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index ba3cc22..1cc4b48 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -138,6 +138,9 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele optionMenu.addAction(UIAlertAction(title: PGPKeyArmorSettingTableViewController.menuLabel, style: .default) { _ in self.performSegue(withIdentifier: "setPGPKeyByASCIISegue", sender: self) }) + optionMenu.addAction(UIAlertAction(title: PGPKeyFileSettingTableViewController.menuLabel, style: .default) { _ in + self.performSegue(withIdentifier: "setPGPKeyByFileSegue", sender: self) + }) if isReadyToUse() { optionMenu.addAction(UIAlertAction(title: "\(Self.menuLabel) (\("Import".localize()))", style: .default) { _ in diff --git a/pass/de.lproj/Localizable.strings b/pass/de.lproj/Localizable.strings index a1e9478..607bea5 100644 --- a/pass/de.lproj/Localizable.strings +++ b/pass/de.lproj/Localizable.strings @@ -134,6 +134,11 @@ "PgpCopyPublicAndPrivateKeyToPass." = "Kopiere den öffentlichen und den privaten Schlüssel im ASCII-Format verschlüsselt mit den Namen \"gpg_key.pub\" bzw. \"gpg_key\" über iTunes zu Pass. Danach können die Schlüssel über \"Datenaustausch über iTunes\" importiert werden."; "KeyExpiredOrIncompatibleError." = "Der öffentliche PGP-Schlüssel ist eventuell abgelaufen oder inkompatibel mit dem privaten Schlüssel."; "WrongPassphraseError." = "Das Passwort für den privaten PGP-Schlüssel ist falsch."; +"CannotImportFile" = "Fehler beim Importieren"; +"LoadFromFiles" = "Aus Datei laden"; +"FileCannotBeImported." = "Beim Importieren der Datei '%@' trat ein Fehler auf. Stelle bitte sicher, dass ihr Speicherort lesbar ist."; +"RememberToRemoveKeyFromLocation." = "Vergiss nicht, die Schlüsseldateien wieder von ihrem externen Speicherort zu entfernen."; +"KeyFileNotSet." = "Es wurde nicht für beide Schlüsseltypen eine Datei angegeben."; // App passcode "RemovePasscode" = "Passcode entfernen"; @@ -286,3 +291,10 @@ geben den öffentlichen und den privaten Schlüssel in diesem speziellen Format $ gpg --export-secret-keys -a KEY_ID geben den öffentlichen und den privaten Schlüssel in diesem speziellen Format aus. Kopiere sie so zu einem Key-Server."; + +"GpgAsciiArmorFileExplanation." = "GnuPG unterstützt die Kommandozeilenoption \"-a\", welche die Schlüssel im Format \"ASCII-Armor\" ausgibt. Es ist anders als das Binärformat eine einfache Zeichenkette. Die Befehle + + $ gpg --export -a KEY_ID + $ gpg --export-secret-keys -a KEY_ID + +geben den öffentlichen und den privaten Schlüssel in diesem speziellen Format aus. Kopiere sie so zu einem Speicherort, der für die Dateien-App zugänglich ist."; diff --git a/pass/de.lproj/Main.strings b/pass/de.lproj/Main.strings index 3cac894..b7a47be 100644 --- a/pass/de.lproj/Main.strings +++ b/pass/de.lproj/Main.strings @@ -74,6 +74,9 @@ /* Class = "UINavigationItem"; title = "Add Password"; ObjectID = "KOg-Gn-Buk"; */ "KOg-Gn-Buk.title" = "Passwort hinzufügen"; +/* Class = "UILabel"; text = "Select file ..."; ObjectID = "Ka2-8Z-fwx"; */ +"Ka2-8Z-fwx.text" = "Datei auswählen ..."; + /* Class = "UILabel"; text = "Edit password for baidu.com using Pass for iOS."; ObjectID = "L1p-Dm-Mnh"; */ "L1p-Dm-Mnh.text" = "Passwort für baidu.com wurde in Pass for iOS hinzugefügt."; @@ -101,6 +104,9 @@ /* Class = "UILabel"; text = "Private Key URL"; ObjectID = "Qht-RC-Yeg"; */ "Qht-RC-Yeg.text" = "URL des privaten Schlüssels"; +/* Class = "UITableViewSection"; headerTitle = "PRIVATE KEY"; ObjectID = "RFc-J6-hAe"; */ +"RFc-J6-hAe.headerTitle" = "PRIVATER SCHLÜSSEL"; + /* Class = "UILabel"; text = "PGP Key"; ObjectID = "RR9-xr-9ko"; */ "RR9-xr-9ko.text" = "PGP-Schlüssel"; @@ -131,6 +137,15 @@ /* Class = "UINavigationItem"; title = "Settings"; ObjectID = "WH4-7R-4TQ"; */ "WH4-7R-4TQ.title" = "Einstellungen"; +/* Class = "UILabel"; text = "Import Keys From Files"; ObjectID = "XU8-Io-n0h"; */ +"XU8-Io-n0h.text" = "Schlüssel aus Dateien importieren"; + +/* Class = "UILabel"; text = "Select file ..."; ObjectID = "XVY-Dj-6Mx"; */ +"XVY-Dj-6Mx.text" = "Datei auswählen ..."; + +/* Class = "UITableViewSection"; headerTitle = "PUBLIC KEY"; ObjectID = "Y8H-cb-G2j"; */ +"Y8H-cb-G2j.headerTitle" = "ÖFFENTLICHER SCHLÜSSEL"; + /* Class = "UITabBarItem"; title = "Settings"; ObjectID = "YLZ-cr-akY"; */ "YLZ-cr-akY.title" = "Einstellungen"; @@ -233,6 +248,9 @@ /* Class = "UITableViewSection"; headerTitle = "GPG Configuration"; ObjectID = "ugP-R2-9M7"; */ "ugP-R2-9M7.headerTitle" = "GPG-Konfiguration"; +/* Class = "UINavigationItem"; title = "PGP Key"; ObjectID = "waZ-gh-rQt"; */ +"waZ-gh-rQt.title" = "PGP-Schlüssel"; + /* Class = "UILabel"; text = "✓"; ObjectID = "wbx-rk-i8H"; */ "wbx-rk-i8H.text" = "✓"; diff --git a/pass/en.lproj/Localizable.strings b/pass/en.lproj/Localizable.strings index 198f8b8..96ad1b7 100644 --- a/pass/en.lproj/Localizable.strings +++ b/pass/en.lproj/Localizable.strings @@ -134,6 +134,11 @@ "PgpCopyPublicAndPrivateKeyToPass." = "Copy your ASCII-armored public and private keys to Pass with names \"gpg_key.pub\" and \"gpg_key\" (without quotes) via iTunes. Then come back and click \"iTunes File Sharing\" to finish."; "KeyExpiredOrIncompatibleError." = "PGP public key may be expired or incompatible with the private key."; "WrongPassphraseError." = "Passphrase of your PGP secret key is wrong."; +"CannotImportFile" = "Cannot Import File"; +"LoadFromFiles" = "Load From File"; +"FileCannotBeImported." = "An error occurred importing the file '%@'. Please make sure its location is readable."; +"RememberToRemoveKeyFromLocation." = "Remember to remove the key files from their external locations."; +"KeyFileNotSet." = "At least for one key type there is no file specified."; // App passcode "RemovePasscode" = "Remove Passcode"; @@ -286,3 +291,10 @@ to get the public and the private key in this specific format. The clipboard wil $ gpg --export-secret-keys -a KEY_ID to get the public and the private key in this specific format. Subsequently, copy them to your secured key server."; + +"GpgAsciiArmorFileExplanation." = "GnuPG supports the command-line option \"-a\" that causes output to be generated in an ASCII-armored format similar to unencoded documents rather than the binary format. Use + + $ gpg --export -a KEY_ID + $ gpg --export-secret-keys -a KEY_ID + +to get the public and the private key in this specific format. Subsequently, copy them to a location accessible by the Files app."; diff --git a/pass/en.lproj/Main.strings b/pass/en.lproj/Main.strings index 900e61c..94c443a 100644 Binary files a/pass/en.lproj/Main.strings and b/pass/en.lproj/Main.strings differ diff --git a/passKit/Helpers/DefaultsKeys.swift b/passKit/Helpers/DefaultsKeys.swift index 17b5ddb..3b13949 100644 --- a/passKit/Helpers/DefaultsKeys.swift +++ b/passKit/Helpers/DefaultsKeys.swift @@ -12,7 +12,7 @@ import SwiftyUserDefaults public var Defaults = DefaultsAdapter(defaults: UserDefaults(suiteName: Globals.groupIdentifier)!, keyStore: DefaultsKeys()) public enum PGPKeySource: String, DefaultsSerializable { - case url, armor, itunes + case url, armor, files, itunes } public enum GitAuthenticationMethod: String, DefaultsSerializable {