From e0c9ceb4fd053268175c473d444700d0460166c0 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 9 Feb 2020 14:13:51 +0100 Subject: [PATCH] Add ability to import PGP keys from the Files app --- pass.xcodeproj/project.pbxproj | 4 + pass/Base.lproj/Main.storyboard | 140 ++++++++++++++++-- ...PGPKeyFileSettingTableViewController.swift | 106 +++++++++++++ .../SettingsTableViewController.swift | 3 + pass/de.lproj/Localizable.strings | 12 ++ pass/de.lproj/Main.strings | 18 +++ pass/en.lproj/Localizable.strings | 12 ++ pass/en.lproj/Main.strings | Bin 9589 -> 20638 bytes passKit/Helpers/DefaultsKeys.swift | 2 +- 9 files changed, 285 insertions(+), 12 deletions(-) create mode 100644 pass/Controllers/PGPKeyFileSettingTableViewController.swift 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 900e61c9ac0c465575656e01224e1c960bf97fbc..94c443ab29343c207ed0de7d74eebf0557f77b09 100644 GIT binary patch literal 20638 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 W`Wa`uhhQ!5K3fzPzGj@g3jYJumen=@ literal 9589 zcmbta%W~pM65abN6urC=>Glf)F)<5X4Vb6J!-kzC3Lz{cEQG=Q#@)`>&6i9GtCXdb zgt##gy*lm8lkdtZar65xrDkipo$_zx*Q7nxbel~rb{H|kw_NAfzmyfz7R(!4zRiNa zsf=pvHdNH!Kvf$3nmQYf8+B!Ih=)%P7Dc!lbwVQ$xD6!F?2#zt81ujQ zV=;Tdg}1h5u$61`kOXu?&-&7QrgYf1GEuvP_Q`qyw+ib0GNeV|R#J3kPiRV5q3Ko4 zYx``&mH8qa+*jXn%pGBVe%C-}FfW-QGXl3~*)t~!Q?l#ATC@$AuAo_d3t14jBCM37 zY4Oxqrla|Lj}d=LMGcNKsOW0Qga9%T1$jPIU0oUNgA$kPwNy7?QijS@~ z&ro&m`>sRtRMm_R!c_k5LC=JmSqgnZK+4gNAmyR4OH@zHr`p2-77Os)QOJY<`jk*X zIs4j|Wop4xpw12X{5ZJYLK*pPB6T&(g?STBwTL_dBsZxFN}fNMMvdA;ZI9{vsU3^J zp5Ra$2;U)}R z&Th$GEz4L-Y2C=5dy1dv9S&SM%ZgE=A(nAW=v^LJ=X zM63`%inAEiojF`8y1S=GUTf6?_6hF`QGp0Z6&OlVrtETWEO`BiW~H93%h3LWk9KfS z21k7>0whYJ;d<(kp*hN6JXBs)rRFXeJ@Z<&4uv-8%Z8wj0Es?D3pIgOd|%tp^C;k& zuYj4F#PtJU5`W~V_Uzc6O$(hPp?zuZ;PC|GjTiDEKz)l8P-5yf$9;=;yjE3 z0+K106;gY$(@|M0BAcio0~4AKRQJJ|9#ncK0;F`r6;R?tI)cvHij#EddWx=Di~X(P zZj}9w?|5fpl;A(C!RVG))V<$AeFHsb6CN66TVE7U$Xc!Zm&0$KpsA?IG9+W@a&@BWcE+LxB!N--sk7A|kCayQCHiu^) z?rIak6af-bTmj{~um@v9q+GmJy#%KnjGN+kMj){RQ$^GU8rT2+=Wp(id@PL&0}3yo z??q7}fS#VksO}5?O?J$4l8eWf-x!-8P;|s|3IYkwPzB|sKJEBwC*v>B`nH<7y)SpM6#6bLUv8BOrN;!jzDw`1%K`S$7VG2iiw)Uk{23f$PY!-{javvw|lFsS1A@uLDP9B&L88U+Ct<*&NG<(R_h>6Xw1V<^zF5?Iko(E9m5wSGttl*ywGc{SE!e zT5v}I&hDg+|Ccp*Yqy~?7w^JG1pE`$>{OrEF6kMJbbzt$!m0|x=ipf`=l}$ybSIVM zxg9lYEWe8ZU*Fd7^;Qqr5I}Du(L4^ij>M_nEMO}rHa-Pa1RxdFKpC^cI@mHt8M%AU zV8M9Vx(**S^t!d!_hDHCRTFn#1f<|*sk+{n=va+KfOQ^0*Wu~D97X^Ei9M2~bonC< zm!=r+Qr$!40Y)|9H3Eq_hAI;D*{4>@uD8@mFg5t%^(XWdfyChW5l`@?9?4?P(#)ED z6J}#DnxbtHkYmeN_TaIfOH;b+%8FiQV?j0hpHOYVb}hbuARrM&vXqX_Lg82TU@+et zuyh!W^)R9cNc|H!7*j*Jvt2!Zg<{?p3!duabJ=`&% zd)6|}-)WQ{^H(l?&|BU0!EeKEC0@}GNR)DhK57`vMDj$5MZzkUz%RmbD4G}nOhZx! zWz7#q5X_qEEG={Iog+e0Vy(t7XhLf&-r^BRgd#;3HHe0RUW#Htum^3}K0&o9Y7qep z0@aYm*htxZHxC7P{f760xQHWwM4=$nAN&-62hjIi+dda`^x^r!wzaXVFzCRlE8Z9o zkP1DnfD&i-*J#TOOS6^nN^?Hw2y;6Oyb|0u#K=TIqNXY+In87E$`ebU`+0(v2WlnQ z`-2?=0gP(K^H8>P=kCw=2EH9yuwKBiAo7TSl&MuTn^uipgO5Z5LVF|0Ics)Qs3J(fX!u$v|hs}#4b#D$qeSmULSb#txA}PA4 zL9`WL82w)?ozP?f2b&HQ%{ws#yV-$bTL$OhB^ 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 {