From 13804b79e6dd78a91e5b460c1910aa8a882e984a Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Fri, 7 Jan 2022 10:04:40 -0800 Subject: [PATCH 001/103] Version bump to 0.13.0 --- pass.xcodeproj/project.pbxproj | 30 +++++++++++++++--------------- passAutoFillExtension/Info.plist | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 15bacc56..09fe3ed1 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1798,7 +1798,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios.shortcuts; @@ -1833,7 +1833,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios.shortcuts; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1930,7 +1930,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "${inherited}"; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -1991,7 +1991,7 @@ "@executable_path/../../Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; OTHER_CFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER).find-login-action-extension"; @@ -2026,7 +2026,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MODULEMAP_FILE = ""; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -2091,7 +2091,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "me.mssun.passforiosbeta.auto-fill-credential-extension"; @@ -2127,7 +2127,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforiosbeta.shortcuts; @@ -2162,7 +2162,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "me.mssun.passforios.auto-fill-credential-extension"; @@ -2198,7 +2198,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "me.mssun.passforios.auto-fill-credential-extension"; PRODUCT_NAME = passAutoFillExtension; @@ -2231,7 +2231,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MODULEMAP_FILE = ""; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -2271,7 +2271,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; MODULEMAP_FILE = ""; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -2355,7 +2355,7 @@ "@executable_path/../../Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; OTHER_CFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER).find-login-action-extension"; PRODUCT_NAME = passExtension; @@ -2390,7 +2390,7 @@ "@executable_path/../../Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; OTHER_CFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER).find-login-action-extension"; PRODUCT_NAME = passExtension; @@ -2605,7 +2605,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "${inherited}"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; @@ -2639,7 +2639,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.12.0; + MARKETING_VERSION = 0.13.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "${inherited}"; OTHER_SWIFT_FLAGS = ""; diff --git a/passAutoFillExtension/Info.plist b/passAutoFillExtension/Info.plist index 7f13ea9c..d5967914 100644 --- a/passAutoFillExtension/Info.plist +++ b/passAutoFillExtension/Info.plist @@ -20,8 +20,6 @@ $(MARKETING_VERSION) CFBundleVersion 0 - NSFaceIDUsageDescription - Enable access to Face ID to unlock Pass. NSExtension NSExtensionMainStoryboard @@ -29,5 +27,7 @@ NSExtensionPointIdentifier com.apple.authentication-services-credential-provider-ui + NSFaceIDUsageDescription + Enable access to Face ID to unlock Pass. From 955e50c3d3611f65915060b9da412d242d07576a Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sun, 9 Jan 2022 21:38:39 -0800 Subject: [PATCH 002/103] Initial implementation of using YubiKey for decryption (#533) --- Gemfile.lock | 66 +------- pass.xcodeproj/project.pbxproj | 81 ++++++++- .../xcshareddata/swiftpm/Package.resolved | 9 + pass/AppDelegate.swift | 3 + ...PGPKeyArmorImportTableViewController.swift | 2 +- .../PGPKeyFIleImportTableViewController.swift | 21 +-- .../PGPKeyURLImportTableViewController.swift | 49 +++--- .../PasswordDetailTableViewController.swift | 81 ++++++++- .../PasswordNavigationViewController.swift | 2 +- .../SettingsTableViewController.swift | 11 ++ pass/Helpers/Objective-CBridgingHeader.h | 1 + pass/Info.plist | 15 ++ pass/Services/PasswordDecryptor.swift | 160 +++++++++++++++++- pass/pass.entitlements | 5 + pass/passBeta.entitlements | 5 + .../UIAlertControllerExtension.swift | 6 + passKit/Helpers/AppError.swift | 24 ++- passKit/Helpers/DefaultsKeys.swift | 1 + passKit/Helpers/YubiKeyAPDU.swift | 54 ++++++ passKit/Helpers/YubiKeyConnection.swift | 63 +++++++ passKit/Models/PasswordEntity.swift | 2 +- passKit/Protocols/AlertPresenting.swift | 55 ++++++ scripts/gopenpgp_build.sh | 8 +- 23 files changed, 606 insertions(+), 118 deletions(-) create mode 100644 passKit/Helpers/YubiKeyAPDU.swift create mode 100644 passKit/Helpers/YubiKeyConnection.swift create mode 100644 passKit/Protocols/AlertPresenting.swift diff --git a/Gemfile.lock b/Gemfile.lock index 247f8a0d..26cba895 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,16 +2,8 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (3.0.2) - activesupport (4.2.11.3) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) - algoliasearch (1.27.5) - httpclient (~> 2.8, >= 2.8.3) - json (>= 1.5.1) atomos (0.1.3) aws-eventstream (1.1.0) aws-partitions (1.345.0) @@ -31,48 +23,10 @@ GEM aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.3) claide (1.0.3) - cocoapods (1.9.3) - activesupport (>= 4.0.2, < 5) - claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.9.3) - cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.2.2, < 2.0) - cocoapods-plugins (>= 1.0.0, < 2.0) - cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.4.0, < 2.0) - cocoapods-try (>= 1.1.0, < 2.0) - colored2 (~> 3.1) - escape (~> 0.0.4) - fourflusher (>= 2.3.0, < 3.0) - gh_inspector (~> 1.0) - molinillo (~> 0.6.6) - nap (~> 1.0) - ruby-macho (~> 1.4) - xcodeproj (>= 1.14.0, < 2.0) - cocoapods-core (1.9.3) - activesupport (>= 4.0.2, < 6) - algoliasearch (~> 1.0) - concurrent-ruby (~> 1.1) - fuzzy_match (~> 2.0.4) - nap (~> 1.0) - netrc (~> 0.11) - typhoeus (~> 1.0) - cocoapods-deintegrate (1.0.4) - cocoapods-downloader (1.4.0) - cocoapods-plugins (1.0.0) - nap - cocoapods-search (1.0.0) - cocoapods-stats (1.1.0) - cocoapods-trunk (1.5.0) - nap (>= 0.8, < 2.0) - netrc (~> 0.11) - cocoapods-try (1.2.0) colored (1.2) colored2 (3.1.2) commander-fastlane (4.4.6) highline (~> 1.7.2) - concurrent-ruby (1.1.7) declarative (0.0.20) declarative-option (0.1.0) digest-crc (0.6.1) @@ -81,9 +35,6 @@ GEM unf (>= 0.0.5, < 1.0.0) dotenv (2.7.6) emoji_regex (3.0.0) - escape (0.0.4) - ethon (0.12.0) - ffi (>= 1.3.0) excon (0.75.0) faraday (1.0.1) multipart-post (>= 1.2, < 3) @@ -129,9 +80,6 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - ffi (1.13.1) - fourflusher (2.3.1) - fuzzy_match (2.0.4) gh_inspector (1.1.3) google-api-client (0.38.0) addressable (~> 2.5, >= 2.5.1) @@ -166,8 +114,6 @@ GEM http-cookie (1.0.3) domain_name (~> 0.5) httpclient (2.8.3) - i18n (0.9.5) - concurrent-ruby (~> 1.0) jmespath (1.4.0) json (2.3.1) jwt (2.2.1) @@ -177,12 +123,9 @@ GEM mime-types-data (3.2020.0512) mini_magick (4.10.1) mini_mime (1.0.2) - minitest (5.14.2) - molinillo (0.6.6) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) - nap (1.1.0) naturally (2.2.0) netrc (0.11.0) os (1.1.0) @@ -200,7 +143,6 @@ GEM netrc (~> 0.8) retriable (3.1.2) rouge (2.0.7) - ruby-macho (1.4.0) rubyzip (2.3.0) security (0.1.3) signet (0.14.0) @@ -215,15 +157,10 @@ GEM terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - thread_safe (0.3.6) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) tty-cursor (~> 0.7) - typhoeus (1.4.0) - ethon (>= 0.9.0) - tzinfo (1.2.8) - thread_safe (~> 0.1) uber (0.1.0) unf (0.1.4) unf_ext @@ -245,10 +182,9 @@ PLATFORMS ruby DEPENDENCIES - cocoapods fastlane rest-client xcodeproj BUNDLED WITH - 2.1.4 + 2.2.25 diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 09fe3ed1..0139052e 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -112,6 +112,10 @@ 9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE426E5D1CE0052028E /* OneTimePassword */; }; 9A1D1CE726E5D2230052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE626E5D2230052028E /* OneTimePassword */; }; 9A1F47FA26E5CF4B000C0E01 /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1F47F926E5CF4B000C0E01 /* OneTimePassword */; }; + 9A2C7D822782CB2F00BD9AF3 /* YubiKit in Frameworks */ = {isa = PBXBuildFile; productRef = 9A2C7D812782CB2F00BD9AF3 /* YubiKit */; }; + 9A2C7D842783FF5200BD9AF3 /* YubiKeyConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2C7D832783FF5200BD9AF3 /* YubiKeyConnection.swift */; }; + 9A2C7D862783FF9600BD9AF3 /* YubiKit in Frameworks */ = {isa = PBXBuildFile; productRef = 9A2C7D852783FF9600BD9AF3 /* YubiKit */; }; + 9A2C7D8B2784139200BD9AF3 /* YubiKeyAPDU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2C7D8A2784139200BD9AF3 /* YubiKeyAPDU.swift */; }; 9A55C158259E785600FA8FD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDD1E2E8231000FDF54 /* Assets.xcassets */; }; 9A55C15F259E785700FA8FD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDD1E2E8231000FDF54 /* Assets.xcassets */; }; 9A55C185259E8C5600FA8FD9 /* PasswordsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */; }; @@ -122,13 +126,20 @@ 9A58662925AAAA79006719C2 /* PasswordSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */; }; 9A58664825AAAB7E006719C2 /* SearchPassword.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A5865EF25AA944B006719C2 /* SearchPassword.storyboard */; }; 9A58665125AADB76006719C2 /* CredentialProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58665025AADB76006719C2 /* CredentialProvider.swift */; }; + 9A5C6EF42786CA5F0003F340 /* AlertPresenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5C6EEF2786C8710003F340 /* AlertPresenting.swift */; }; + 9A5C6EF92786CE170003F340 /* YubiKit in Frameworks */ = {isa = PBXBuildFile; productRef = 9A5C6EF82786CE170003F340 /* YubiKit */; }; + 9A5C6EFB2786CE5E0003F340 /* YubiKit in Frameworks */ = {isa = PBXBuildFile; productRef = 9A5C6EFA2786CE5E0003F340 /* YubiKit */; }; + 9A5C6EFF2787F0980003F340 /* Gopenpgp.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */; }; + 9A5C6F022787F09A0003F340 /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; }; + 9A5C6F042787F09D0003F340 /* Gopenpgp.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */; }; + 9A5C6F082787F0C20003F340 /* SwiftyUserDefaults in Frameworks */ = {isa = PBXBuildFile; productRef = 9A5C6F072787F0C20003F340 /* SwiftyUserDefaults */; }; 9A5D06EE25A56F0800FA59D4 /* PasswordTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EE1259EDD520027CE15 /* PasswordTableViewCell.swift */; }; 9A5D06F525A56F0E00FA59D4 /* PasswordTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EE1259EDD520027CE15 /* PasswordTableViewCell.swift */; }; 9A5D070225A5769A00FA59D4 /* PasswordTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EE1259EDD520027CE15 /* PasswordTableViewCell.swift */; }; 9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */; }; 9A74D2E0277D2F8C00F7BC44 /* UIAlertControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A74D2DF277D2F8C00F7BC44 /* UIAlertControllerExtension.swift */; }; 9A78A7CC277BECE80093222D /* SVProgressHUD.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30F6C1B327664C7200BE5AB2 /* SVProgressHUD.xcframework */; }; - 9A78A7CD277BECE80093222D /* SVProgressHUD.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 30F6C1B327664C7200BE5AB2 /* SVProgressHUD.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 9A78A7CD277BECE80093222D /* SVProgressHUD.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 30F6C1B327664C7200BE5AB2 /* SVProgressHUD.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A8F9EBD259EA4C50027CE15 /* PasswordsTableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EBC259EA4C50027CE15 /* PasswordsTableDataSource.swift */; }; 9A8F9ECC259ECB410027CE15 /* PasswordSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */; }; 9A8F9F4025A1A91F0027CE15 /* CredentialProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9F3F25A1A91F0027CE15 /* CredentialProvider.swift */; }; @@ -141,7 +152,6 @@ 9A996C6826DEB96B00A4485D /* passShortcuts.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 30A69945240EED5E00B7D967 /* passShortcuts.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9A996C6B26DEB97600A4485D /* passExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A26700241EEC466A00176B8A /* passExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9A996C6E26DEB99200A4485D /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; }; - 9A996C7126DEB99500A4485D /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; }; 9ADAB21D26DDA52400900F10 /* Gopenpgp.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */; }; 9ADC954124418A5F0005402E /* PasswordStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */; }; 9AFC87D325B39FF3008D6060 /* PasswordNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFC87D225B39FF2008D6060 /* PasswordNavigationViewController.swift */; }; @@ -407,9 +417,12 @@ 9A1EF0B424C50E780074FEAC /* passBetaAutoFillExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaAutoFillExtension.entitlements; sourceTree = ""; }; 9A1EF0B524C50EE00074FEAC /* passBetaExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaExtension.entitlements; sourceTree = ""; }; 9A1EF0B624C50FEA0074FEAC /* passBetaShortcuts.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaShortcuts.entitlements; sourceTree = ""; }; + 9A2C7D832783FF5200BD9AF3 /* YubiKeyConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YubiKeyConnection.swift; sourceTree = ""; }; + 9A2C7D8A2784139200BD9AF3 /* YubiKeyAPDU.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YubiKeyAPDU.swift; sourceTree = ""; }; 9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordsViewController.swift; sourceTree = ""; }; 9A5865EF25AA944B006719C2 /* SearchPassword.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SearchPassword.storyboard; sourceTree = ""; }; 9A58665025AADB76006719C2 /* CredentialProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProvider.swift; sourceTree = ""; }; + 9A5C6EEF2786C8710003F340 /* AlertPresenting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresenting.swift; sourceTree = ""; }; 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertActionExtension.swift; sourceTree = ""; }; 9A74D2DF277D2F8C00F7BC44 /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertControllerExtension.swift; sourceTree = ""; }; 9A8F9EBC259EA4C50027CE15 /* PasswordsTableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordsTableDataSource.swift; sourceTree = ""; }; @@ -506,8 +519,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9A5C6EF92786CE170003F340 /* YubiKit in Frameworks */, 9A996C6E26DEB99200A4485D /* passKit.framework in Frameworks */, 30A3001A26DA697C002A734E /* SwiftyUserDefaults in Frameworks */, + 9A5C6F042787F09D0003F340 /* Gopenpgp.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -521,6 +536,7 @@ 9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */, 30A3001626DA6697002A734E /* SwiftyUserDefaults in Frameworks */, 3032DA5626DAF4E500A7728C /* ObjectivePGP in Frameworks */, + 9A2C7D862783FF9600BD9AF3 /* YubiKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -539,7 +555,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9A996C7126DEB99500A4485D /* passKit.framework in Frameworks */, + 9A5C6F022787F09A0003F340 /* passKit.framework in Frameworks */, + 9A5C6EFB2786CE5E0003F340 /* YubiKit in Frameworks */, + 9A5C6EFF2787F0980003F340 /* Gopenpgp.xcframework in Frameworks */, + 9A5C6F082787F0C20003F340 /* SwiftyUserDefaults in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -560,6 +579,7 @@ 9A996C5326DDF61F00A4485D /* Base32 in Frameworks */, 9A78A7CC277BECE80093222D /* SVProgressHUD.xcframework in Frameworks */, 3032DA5426DAF4C200A7728C /* ObjectivePGP in Frameworks */, + 9A2C7D822782CB2F00BD9AF3 /* YubiKit in Frameworks */, 3010CB6626DA500F008964D2 /* KeychainAccess in Frameworks */, 9A996C5826DEB0D100A4485D /* passKit.framework in Frameworks */, 30ED1777276F8842009BA876 /* ObjectiveGit in Frameworks */, @@ -724,6 +744,14 @@ path = Services; sourceTree = ""; }; + 9A5C6EF32786C9C00003F340 /* Protocols */ = { + isa = PBXGroup; + children = ( + 9A5C6EEF2786C8710003F340 /* AlertPresenting.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 9A8F9EBB259EA4A80027CE15 /* Services */ = { isa = PBXGroup; children = ( @@ -806,6 +834,7 @@ A26075791EEC6F34005DB03E /* passKit */ = { isa = PBXGroup; children = ( + 9A5C6EF32786C9C00003F340 /* Protocols */, A2C532B9201DD07500DB9F53 /* Controllers */, 30CCA90C232584560048CA51 /* Crypto */, 30B6AABA21F49095006B352D /* Extensions */, @@ -877,6 +906,8 @@ A2F4E20F1EED7F0A0011986E /* Helpers */ = { isa = PBXGroup; children = ( + 9A2C7D832783FF5200BD9AF3 /* YubiKeyConnection.swift */, + 9A2C7D8A2784139200BD9AF3 /* YubiKeyAPDU.swift */, 30697C2921F63C590064FCAC /* AppError.swift */, 302B2C9722C2BDE700D831EE /* AppKeychain.swift */, 3087574E2343E42A00B971A2 /* Colors.swift */, @@ -1075,6 +1106,7 @@ name = passAutoFillExtension; packageProductDependencies = ( 30A3001926DA697C002A734E /* SwiftyUserDefaults */, + 9A5C6EF82786CE170003F340 /* YubiKit */, ); productName = passAutoFillExtension; productReference = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */; @@ -1100,6 +1132,7 @@ 3032DA5526DAF4E500A7728C /* ObjectivePGP */, 9A996C5626DDF65900A4485D /* Base32 */, 9A1D1CE426E5D1CE0052028E /* OneTimePassword */, + 9A2C7D852783FF9600BD9AF3 /* YubiKit */, ); productName = passKit; productReference = A26075781EEC6F34005DB03E /* passKit.framework */; @@ -1142,6 +1175,10 @@ dependencies = ( ); name = passExtension; + packageProductDependencies = ( + 9A5C6EFA2786CE5E0003F340 /* YubiKit */, + 9A5C6F072787F0C20003F340 /* SwiftyUserDefaults */, + ); productName = passExtension; productReference = A26700241EEC466A00176B8A /* passExtension.appex */; productType = "com.apple.product-type.app-extension"; @@ -1197,6 +1234,7 @@ 9A996C5226DDF61F00A4485D /* Base32 */, 9A1F47F926E5CF4B000C0E01 /* OneTimePassword */, 30ED1776276F8842009BA876 /* ObjectiveGit */, + 9A2C7D812782CB2F00BD9AF3 /* YubiKit */, ); productName = pass; productReference = DC917BD31E2E8231000FDF54 /* Pass.app */; @@ -1300,6 +1338,7 @@ 3032DA5226DAF4C200A7728C /* XCRemoteSwiftPackageReference "ObjectivePGP" */, 9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */, 30ED1775276F8842009BA876 /* XCRemoteSwiftPackageReference "objective-git-swift-package" */, + 9A2C7D802782CB2F00BD9AF3 /* XCRemoteSwiftPackageReference "yubikit-ios" */, ); productRefGroup = DC917BD41E2E8231000FDF54 /* Products */; projectDirPath = ""; @@ -1491,6 +1530,7 @@ 30697C3C21F63C990064FCAC /* UITextFieldExtension.swift in Sources */, 302E85632125EE550031BA64 /* Constants.swift in Sources */, 9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */, + 9A2C7D842783FF5200BD9AF3 /* YubiKeyConnection.swift in Sources */, 301F6463216162550071A4CE /* AdditionField.swift in Sources */, 30697C3021F63C5A0064FCAC /* AppError.swift in Sources */, 30697C2B21F63C5A0064FCAC /* Globals.swift in Sources */, @@ -1513,6 +1553,7 @@ 30B4C7BA24084AAA008B86F7 /* PasswordGenerator.swift in Sources */, 30A1D2A621B2D46100E2D1F7 /* OTPType.swift in Sources */, 3032328E22CBD4CD009EBD9C /* CryptographicKeys.swift in Sources */, + 9A5C6EF42786CA5F0003F340 /* AlertPresenting.swift in Sources */, 30697C2A21F63C5A0064FCAC /* NotificationNames.swift in Sources */, 30CCA91623258C380048CA51 /* PGPInterface.swift in Sources */, 30DAFD4A240985A7002456E7 /* Array+Slices.swift in Sources */, @@ -1527,6 +1568,7 @@ 30697C3A21F63C990064FCAC /* UIViewControllerExtension.swift in Sources */, 30697C2E21F63C5A0064FCAC /* Utils.swift in Sources */, 30CCA91823258E760048CA51 /* GopenPGPInterface.swift in Sources */, + 9A2C7D8B2784139200BD9AF3 /* YubiKeyAPDU.swift in Sources */, 30697C4521F63CAB0064FCAC /* Password.swift in Sources */, 30697C4421F63CAB0064FCAC /* PasswordEntity.swift in Sources */, 30BAC8CD22E3BB9700438475 /* KeyStore.swift in Sources */, @@ -2796,6 +2838,14 @@ revision = 8d59e4abba762d0f1e9aed161081f7b3fe21daa0; }; }; + 9A2C7D802782CB2F00BD9AF3 /* XCRemoteSwiftPackageReference "yubikit-ios" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Yubico/yubikit-ios"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -2874,6 +2924,31 @@ package = 9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */; productName = OneTimePassword; }; + 9A2C7D812782CB2F00BD9AF3 /* YubiKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9A2C7D802782CB2F00BD9AF3 /* XCRemoteSwiftPackageReference "yubikit-ios" */; + productName = YubiKit; + }; + 9A2C7D852783FF9600BD9AF3 /* YubiKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9A2C7D802782CB2F00BD9AF3 /* XCRemoteSwiftPackageReference "yubikit-ios" */; + productName = YubiKit; + }; + 9A5C6EF82786CE170003F340 /* YubiKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9A2C7D802782CB2F00BD9AF3 /* XCRemoteSwiftPackageReference "yubikit-ios" */; + productName = YubiKit; + }; + 9A5C6EFA2786CE5E0003F340 /* YubiKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9A2C7D802782CB2F00BD9AF3 /* XCRemoteSwiftPackageReference "yubikit-ios" */; + productName = YubiKit; + }; + 9A5C6F072787F0C20003F340 /* SwiftyUserDefaults */ = { + isa = XCSwiftPackageProductDependency; + package = 3010CB5E26DA4F87008964D2 /* XCRemoteSwiftPackageReference "SwiftyUserDefaults" */; + productName = SwiftyUserDefaults; + }; 9A996C5226DDF61F00A4485D /* Base32 */ = { isa = XCSwiftPackageProductDependency; package = 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */; diff --git a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 39a1af92..253dc27a 100644 --- a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -63,6 +63,15 @@ "revision": "f66bcd04088582c8fbb5cb8554d577e303bae396", "version": "5.3.0" } + }, + { + "package": "YubiKit", + "repositoryURL": "https://github.com/Yubico/yubikit-ios", + "state": { + "branch": null, + "revision": "7e75fe8f057acf9bf7ac134d375783a1d409f79e", + "version": "4.1.0" + } } ] }, diff --git a/pass/AppDelegate.swift b/pass/AppDelegate.swift index 4412adaf..a65ca6ee 100644 --- a/pass/AppDelegate.swift +++ b/pass/AppDelegate.swift @@ -35,6 +35,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } UNUserNotificationCenter.current().delegate = NotificationCenterDispatcher.shared + #if !targetEnvironment(simulator) + _ = passKit.YubiKeyConnection.shared + #endif return true } diff --git a/pass/Controllers/PGPKeyArmorImportTableViewController.swift b/pass/Controllers/PGPKeyArmorImportTableViewController.swift index ac407694..fef56a3a 100644 --- a/pass/Controllers/PGPKeyArmorImportTableViewController.swift +++ b/pass/Controllers/PGPKeyArmorImportTableViewController.swift @@ -101,7 +101,7 @@ extension PGPKeyArmorImportTableViewController: PGPKeyImporter { Utils.alert(title: "CannotSave".localize(), message: "SetPublicKey.".localize(), controller: self, completion: nil) return false } - guard !armorPrivateKeyTextView.text.isEmpty else { + guard Defaults.isYubiKeyEnabled || !armorPrivateKeyTextView.text.isEmpty else { Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKey.".localize(), controller: self, completion: nil) return false } diff --git a/pass/Controllers/PGPKeyFIleImportTableViewController.swift b/pass/Controllers/PGPKeyFIleImportTableViewController.swift index 6a6322a0..58dd834f 100644 --- a/pass/Controllers/PGPKeyFIleImportTableViewController.swift +++ b/pass/Controllers/PGPKeyFIleImportTableViewController.swift @@ -8,7 +8,7 @@ import passKit -class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController { +class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController, AlertPresenting { @IBOutlet var pgpPublicKeyFile: UITableViewCell! @IBOutlet var pgpPrivateKeyFile: UITableViewCell! @@ -25,7 +25,7 @@ class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let cell = tableView.cellForRow(at: indexPath) - let picker = UIDocumentPickerViewController(documentTypes: ["public.data"], in: .open) + let picker = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .open) cell?.isSelected = false if cell == pgpPublicKeyFile { currentlyPicking = .public @@ -71,7 +71,7 @@ extension PGPKeyFileImportTableViewController: UIDocumentPickerDelegate { } } catch { let message = "FileCannotBeImported.".localize(fileName) | "UnderlyingError".localize(error.localizedDescription) - Utils.alert(title: "CannotImportFile".localize(), message: message, controller: self) + presentFailureAlert(title: "CannotImportFile".localize(), message: message) } } } @@ -81,19 +81,20 @@ extension PGPKeyFileImportTableViewController: PGPKeyImporter { static let label = "LoadFromFiles".localize() func isReadyToUse() -> Bool { - validate(key: publicKey) && validate(key: privateKey) + validate(key: publicKey) && (Defaults.isYubiKeyEnabled || validate(key: privateKey)) } func importKeys() throws { - guard let publicKey = publicKey, let privateKey = privateKey else { - return + if let publicKey = publicKey { + try KeyFileManager.PublicPGP.importKey(from: publicKey) + } + if let privateKey = privateKey { + try KeyFileManager.PrivatePGP.importKey(from: privateKey) } - try KeyFileManager.PublicPGP.importKey(from: publicKey) - try KeyFileManager.PrivatePGP.importKey(from: privateKey) } func doAfterImport() { - Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromLocation.".localize(), controller: self) + presentAlert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromLocation.".localize()) } func saveImportedKeys() { @@ -102,7 +103,7 @@ extension PGPKeyFileImportTableViewController: PGPKeyImporter { private func validate(key: String?) -> Bool { guard key != nil else { - Utils.alert(title: "CannotSavePgpKey".localize(), message: "KeyFileNotSet.".localize(), controller: self) + presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "KeyFileNotSet.".localize()) return false } return true diff --git a/pass/Controllers/PGPKeyURLImportTableViewController.swift b/pass/Controllers/PGPKeyURLImportTableViewController.swift index 9f5e4f3c..30c3deca 100644 --- a/pass/Controllers/PGPKeyURLImportTableViewController.swift +++ b/pass/Controllers/PGPKeyURLImportTableViewController.swift @@ -9,7 +9,7 @@ import passKit import UIKit -class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController { +class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController, AlertPresenting { @IBOutlet var pgpPublicKeyURLTextField: UITextField! @IBOutlet var pgpPrivateKeyURLTextField: UITextField! @@ -24,18 +24,11 @@ class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController { @IBAction private func save(_: Any) { - guard let publicKeyURLText = pgpPublicKeyURLTextField.text, - let publicKeyURL = URL(string: publicKeyURLText), - let privateKeyURLText = pgpPrivateKeyURLTextField.text, - let privateKeyURL = URL(string: privateKeyURLText) else { - Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self) - return - } - if privateKeyURL.scheme?.lowercased() == "http" || publicKeyURL.scheme?.lowercased() == "http" { - Utils.alert(title: "HttpNotSecure".localize(), message: "ReallyUseHttp.".localize(), controller: self) + pgpPublicKeyURL = validate(pgpKeyURLText: pgpPublicKeyURLTextField.text) + + if !Defaults.isYubiKeyEnabled { + pgpPrivateKeyURL = validate(pgpKeyURLText: pgpPrivateKeyURLTextField.text) } - pgpPrivateKeyURL = privateKeyURL - pgpPublicKeyURL = publicKeyURL saveImportedKeys() } } @@ -45,35 +38,39 @@ extension PGPKeyURLImportTableViewController: PGPKeyImporter { static let label = "DownloadFromUrl".localize() func isReadyToUse() -> Bool { - validate(pgpKeyURL: pgpPublicKeyURLTextField.text ?? "") - && validate(pgpKeyURL: pgpPrivateKeyURLTextField.text ?? "") + validate(pgpKeyURLText: pgpPublicKeyURLTextField.text) != nil + && (Defaults.isYubiKeyEnabled || validate(pgpKeyURLText: pgpPrivateKeyURLTextField.text ?? "") != nil) } func importKeys() throws { - Defaults.pgpPrivateKeyURL = pgpPrivateKeyURL - Defaults.pgpPublicKeyURL = pgpPublicKeyURL + if let pgpPrivateKeyURL = pgpPrivateKeyURL { + Defaults.pgpPrivateKeyURL = pgpPrivateKeyURL + try KeyFileManager.PrivatePGP.importKey(from: pgpPrivateKeyURL) + } - try KeyFileManager.PublicPGP.importKey(from: Defaults.pgpPublicKeyURL!) - try KeyFileManager.PrivatePGP.importKey(from: Defaults.pgpPrivateKeyURL!) + if let pgpPublicKeyURL = pgpPublicKeyURL { + Defaults.pgpPublicKeyURL = pgpPublicKeyURL + try KeyFileManager.PublicPGP.importKey(from: pgpPublicKeyURL) + } } func doAfterImport() { - Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize(), controller: self) + presentAlert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize()) } func saveImportedKeys() { performSegue(withIdentifier: "savePGPKeySegue", sender: self) } - private func validate(pgpKeyURL: String) -> Bool { - guard let url = URL(string: pgpKeyURL) else { - Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self) - return false + private func validate(pgpKeyURLText: String?) -> URL? { + guard let pgpKeyURL = pgpKeyURLText, let url = URL(string: pgpKeyURL) else { + presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize()) + return nil } guard url.scheme == "https" || url.scheme == "http" else { - Utils.alert(title: "CannotSavePgpKey".localize(), message: "UseEitherHttpsOrHttp.".localize(), controller: self) - return false + presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "UseEitherHttpsOrHttp.".localize()) + return nil } - return true + return url } } diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index d5264999..cd1e4d48 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -7,11 +7,13 @@ // import FavIcon +import Gopenpgp +import passAutoFillExtension import passKit import SVProgressHUD import UIKit -class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate { +class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate, AlertPresenting { var passwordEntity: PasswordEntity? private var password: Password? private var passwordImage: UIImage? @@ -87,7 +89,15 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni decryptThenShowPassword() } - private func decryptThenShowPassword(keyID: String? = nil) { + private func decryptThenShowPassword() { + if Defaults.isYubiKeyEnabled { + decryptThenShowPasswordYubiKey() + } else { + decryptThenShowPasswordLocalKey() + } + } + + private func decryptThenShowPasswordLocalKey(keyID: String? = nil) { guard let passwordEntity = passwordEntity else { Utils.alert(title: "CannotShowPassword".localize(), message: "PasswordDoesNotExist".localize(), controller: self, completion: { self.navigationController!.popViewController(animated: true) @@ -106,7 +116,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni 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) + self.decryptThenShowPasswordLocalKey(keyID: action.title) } alert.addAction(selectKey) @@ -119,7 +129,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni alert.addAction(UIAlertAction.cancelAndPopView(controller: self)) alert.addAction( UIAlertAction(title: "TryAgain".localize(), style: .default) { _ in - self.decryptThenShowPassword() + self.decryptThenShowPasswordLocalKey() } ) self.present(alert, animated: true, completion: nil) @@ -509,3 +519,66 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni tableView.deselectRow(at: indexPath, animated: true) } } + +extension PasswordDetailTableViewController { + private func requestYubiKeyPIN(completion: @escaping (String) -> Void) { + let alert = UIAlertController(title: "YubiKey PIN", message: "Verify YubiKey OpenPGP PIN.", preferredStyle: .alert) + alert.addAction( + UIAlertAction.cancel { _ in + self.navigationController!.popViewController(animated: true) + } + ) + alert.addAction( + UIAlertAction.ok { _ in + let pin = alert.textFields?.first?.text ?? "" + completion(pin) + } + ) + alert.addTextField { textField in + textField.isSecureTextEntry = true + } + present(alert, animated: true) + } + + private func handleError(error: AppError) { + switch error { + case let .yubiKey(yubiKeyError): + let errorMessage = yubiKeyError.localizedDescription + if #available(iOS 13.0, *) { + YubiKitManager.shared.stopNFCConnection(withErrorMessage: errorMessage) + DispatchQueue.main.async { + self.navigationController?.popViewController(animated: true) + } + } else { + DispatchQueue.main.async { + self.presentFailureAlert(message: errorMessage) { _ in + self.navigationController?.popViewController(animated: true) + } + } + } + default: + DispatchQueue.main.async { + self.presentFailureAlert(message: error.localizedDescription) { _ in + self.navigationController?.popViewController(animated: true) + } + } + } + } + + private func handleCancellation(_: Error) { + DispatchQueue.main.async { + self.navigationController?.popViewController(animated: true) + } + } + + private func decryptThenShowPasswordYubiKey() { + guard let passwordEntity = passwordEntity else { + handleError(error: AppError.other(message: "PasswordDoesNotExist")) + return + } + Pass.yubiKeyDecrypt(passwordEntity: passwordEntity, requestPIN: requestYubiKeyPIN, errorHandler: handleError, cancellation: handleCancellation) { password in + self.password = password + self.showPassword() + } + } +} diff --git a/pass/Controllers/PasswordNavigationViewController.swift b/pass/Controllers/PasswordNavigationViewController.swift index 60dc7d46..7ac2839d 100644 --- a/pass/Controllers/PasswordNavigationViewController.swift +++ b/pass/Controllers/PasswordNavigationViewController.swift @@ -319,7 +319,7 @@ extension PasswordNavigationViewController { override func shouldPerformSegue(withIdentifier identifier: String, sender _: Any?) -> Bool { if identifier == "showPasswordDetail" { - guard PGPAgent.shared.isPrepared else { + guard Defaults.isYubiKeyEnabled || PGPAgent.shared.isPrepared else { Utils.alert(title: "CannotShowPassword".localize(), message: "PgpKeyNotSet.".localize(), controller: self) return false } diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 5463a2a0..1fe9aee2 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -87,12 +87,16 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele private func setPGPKeyTableViewCellDetailText() { var label = "NotSet".localize() + let keyID = (try? PGPAgent.shared.getShortKeyID()) ?? [] if keyID.count == 1 { label = keyID.first ?? "" } else if keyID.count > 1 { label = "Multiple" } + if Defaults.isYubiKeyEnabled { + label += "+YubiKey" + } pgpKeyTableViewCell.detailTextLabel?.text = label } @@ -180,6 +184,13 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele ) } + optionMenu.addAction( + UIAlertAction(title: Defaults.isYubiKeyEnabled ? "✓ YubiKey" : "YubiKey", style: .default) { _ in + Defaults.isYubiKeyEnabled.toggle() + self.setPGPKeyTableViewCellDetailText() + } + ) + if Defaults.pgpKeySource != nil { optionMenu.addAction( UIAlertAction(title: "RemovePgpKeys".localize(), style: .destructive) { _ in diff --git a/pass/Helpers/Objective-CBridgingHeader.h b/pass/Helpers/Objective-CBridgingHeader.h index ff807e53..3a71b73d 100644 --- a/pass/Helpers/Objective-CBridgingHeader.h +++ b/pass/Helpers/Objective-CBridgingHeader.h @@ -10,5 +10,6 @@ #define Objective_CBridgingHeader_h @import ObjectiveGit; +#import #endif /* Objective_CBridgingHeader_h */ diff --git a/pass/Info.plist b/pass/Info.plist index 7faefec2..f5a7106e 100644 --- a/pass/Info.plist +++ b/pass/Info.plist @@ -2,6 +2,21 @@ + com.apple.developer.nfc.readersession.iso7816.select-identifiers + + A000000527471117 + A0000006472F0001 + A0000005272101 + A000000308 + D27600012401 + A000000527200101 + + NFCReaderUsageDescription + The application needs access to NFC reading to communicate with your YubiKey. + UISupportedExternalAccessoryProtocols + + com.yubico.ylp + CFBundleDevelopmentRegion en CFBundleDisplayName diff --git a/pass/Services/PasswordDecryptor.swift b/pass/Services/PasswordDecryptor.swift index 4a9c6a1f..0b6d0679 100644 --- a/pass/Services/PasswordDecryptor.swift +++ b/pass/Services/PasswordDecryptor.swift @@ -6,11 +6,28 @@ // Copyright © 2021 Bob Sun. All rights reserved. // +import CryptoTokenKit +import Gopenpgp import passKit import SVProgressHUD import UIKit +import YubiKit -func decryptPassword(in controller: UIViewController, with passwordPath: String, using keyID: String? = nil, completion: @escaping ((Password) -> Void)) { +func decryptPassword( + in controller: UIViewController, + with passwordPath: String, + using keyID: String? = nil, + completion: @escaping ((Password) -> Void) +) { + // YubiKey is not supported in extension + if Defaults.isYubiKeyEnabled { + DispatchQueue.main.async { + let alert = UIAlertController(title: "Error", message: "YubiKey is not supported in extension, please use the Pass app instead.", preferredStyle: .alert) + alert.addAction(UIAlertAction.ok()) + controller.present(alert, animated: true) + } + return + } DispatchQueue.global(qos: .userInteractive).async { do { let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: controller) @@ -37,3 +54,144 @@ func decryptPassword(in controller: UIViewController, with passwordPath: String, } } } + +public typealias RequestPINAction = (@escaping (String) -> Void) -> Void + +let symmetricKeyIDNameDict: [UInt8: String] = [ + 2: "3des", + 3: "cast5", + 7: "aes128", + 8: "aes192", + 9: "aes256", +] + +private func isEncryptKeyAlgoRSA(_ applicationRelatedData: Data) -> Bool { + let tlv = TKBERTLVRecord.sequenceOfRecords(from: applicationRelatedData)! + // 0x73: Discretionary data objects + for record in TKBERTLVRecord.sequenceOfRecords(from: tlv.first!.value)! where record.tag == 0x73 { + // 0xC2: Algorithm attributes decryption, 0x01: RSA + for record2 in TKBERTLVRecord.sequenceOfRecords(from: record.value)! where record2.tag == 0xC2 && record2.value.first! == 0x01 { + return true + } + } + return false +} + +// swiftlint:disable cyclomatic_complexity +public func yubiKeyDecrypt( + passwordEntity: PasswordEntity, + requestPIN: @escaping RequestPINAction, + errorHandler: @escaping ((AppError) -> Void), + cancellation: @escaping ((_ error: Error) -> Void), + completion: @escaping ((Password) -> Void) +) { + let encryptedDataPath = PasswordStore.shared.storeURL.appendingPathComponent(passwordEntity.getPath()) + + guard let encryptedData = try? Data(contentsOf: encryptedDataPath) else { + errorHandler(AppError.other(message: "PasswordDoesNotExist".localize())) + return + } + + // swiftlint:disable closure_body_length + requestPIN { pin in + // swiftlint:disable closure_body_length + passKit.YubiKeyConnection.shared.connection(cancellation: cancellation) { connection in + guard let smartCard = connection.smartCardInterface else { + errorHandler(AppError.yubiKey(.connection(message: "Failed to get smart card interface."))) + return + } + + // 1. Select OpenPGP application + let selectOpenPGPAPDU = YubiKeyAPDU.selectOpenPGPApplication() + smartCard.selectApplication(selectOpenPGPAPDU) { _, error in + guard error == nil else { + errorHandler(AppError.yubiKey(.selectApplication(message: "Failed to select application."))) + return + } + + // 2. Verify PIN + let verifyApdu = YubiKeyAPDU.verify(password: pin) + smartCard.executeCommand(verifyApdu) { _, error in + guard error == nil else { + errorHandler(AppError.yubiKey(.verify(message: "Failed to verify PIN."))) + return + } + + let applicationRelatedDataApdu = YubiKeyAPDU.get_application_related_data() + smartCard.executeCommand(applicationRelatedDataApdu) { data, _ in + guard let data = data else { + errorHandler(AppError.yubiKey(.decipher(message: "Failed to get application related data."))) + return + } + + if !isEncryptKeyAlgoRSA(data) { + errorHandler(AppError.yubiKey(.decipher(message: "Encryption key algorithm is not supported. Supported algorithm: RSA."))) + return + } + + // 3. Decipher + let ciphertext = encryptedData + var error: NSError? + let message = CryptoNewPGPMessage(ciphertext) + guard let mpi1 = Gopenpgp.HelperPassGetEncryptedMPI1(message, &error) else { + errorHandler(AppError.yubiKey(.decipher(message: "Failed to get encrypted MPI."))) + return + } + + let decipherApdu = YubiKeyAPDU.decipher(data: mpi1) + smartCard.executeCommand(decipherApdu) { data, error in + guard let data = data else { + errorHandler(AppError.yubiKey(.decipher(message: "Failed to execute decipher."))) + return + } + + if #available(iOS 13.0, *) { + YubiKitManager.shared.stopNFCConnection() + } + guard let algoByte = data.first, let algo = symmetricKeyIDNameDict[algoByte] else { + errorHandler(AppError.yubiKey(.decipher(message: "Failed to new session key."))) + return + } + guard let session_key = Gopenpgp.CryptoNewSessionKeyFromToken(data[1 ..< data.count - 2], algo) else { + errorHandler(AppError.yubiKey(.decipher(message: "Failed to new session key."))) + return + } + + var error: NSError? + let message = CryptoNewPGPMessage(ciphertext) + + guard let plaintext = Gopenpgp.HelperPassDecryptWithSessionKey(message, session_key, &error)?.data else { + errorHandler(AppError.yubiKey(.decipher(message: "Failed to decrypt with session key."))) + return + } + + guard let plaintext_str = String(data: plaintext, encoding: .utf8) else { + errorHandler(AppError.yubiKey(.decipher(message: "Failed to convert plaintext to string."))) + return + } + + guard let password = try? Password(name: passwordEntity.getName(), url: passwordEntity.getURL(), plainText: plaintext_str) else { + errorHandler(AppError.yubiKey(.decipher(message: "Failed to construct password."))) + return + } + + completion(password) + } + } + } + } + } + } +} + +extension Data { + struct HexEncodingOptions: OptionSet { + let rawValue: Int + static let upperCase = HexEncodingOptions(rawValue: 1 << 0) + } + + func hexEncodedString(options: HexEncodingOptions = []) -> String { + let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx" + return map { String(format: format, $0) }.joined() + } +} diff --git a/pass/pass.entitlements b/pass/pass.entitlements index 1444b8a2..d9153414 100644 --- a/pass/pass.entitlements +++ b/pass/pass.entitlements @@ -4,6 +4,11 @@ com.apple.developer.authentication-services.autofill-credential-provider + com.apple.developer.nfc.readersession.formats + + NDEF + TAG + com.apple.developer.siri com.apple.security.application-groups diff --git a/pass/passBeta.entitlements b/pass/passBeta.entitlements index 5e2efc75..e34057c4 100644 --- a/pass/passBeta.entitlements +++ b/pass/passBeta.entitlements @@ -4,6 +4,11 @@ com.apple.developer.authentication-services.autofill-credential-provider + com.apple.developer.nfc.readersession.formats + + NDEF + TAG + com.apple.developer.siri com.apple.security.application-groups diff --git a/passKit/Extensions/UIAlertControllerExtension.swift b/passKit/Extensions/UIAlertControllerExtension.swift index 962c7074..a3617ff9 100644 --- a/passKit/Extensions/UIAlertControllerExtension.swift +++ b/passKit/Extensions/UIAlertControllerExtension.swift @@ -14,4 +14,10 @@ public extension UIAlertController { alert.addAction(UIAlertAction.cancel()) return alert } + + class func showErrorAlert(title: String, message: String, completion: ((UIAlertAction) -> Void)? = nil) -> UIAlertController { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction.ok(handler: completion)) + return alert + } } diff --git a/passKit/Helpers/AppError.swift b/passKit/Helpers/AppError.swift index 08dbacec..a8a4ca1c 100644 --- a/passKit/Helpers/AppError.swift +++ b/passKit/Helpers/AppError.swift @@ -6,6 +6,8 @@ // Copyright © 2017 Bob Sun. All rights reserved. // +import Foundation + public enum AppError: Error, Equatable { case repositoryNotSet case repositoryRemoteBranchNotFound(branchName: String) @@ -18,13 +20,31 @@ public enum AppError: Error, Equatable { case gitPushNotSuccessful case pgpPublicKeyNotFound(keyID: String) case pgpPrivateKeyNotFound(keyID: String) + case yubiKey(YubiKeyError) + case passwordFileNotFound(path: String) case keyExpiredOrIncompatible case wrongPassphrase case wrongPasswordFilename case decryption case encryption case encoding - case unknown + case other(message: String) +} + +public enum YubiKeyError: Error, Equatable { + case connection(message: String) + case selectApplication(message: String) + case verify(message: String) + case decipher(message: String) +} + +extension YubiKeyError: LocalizedError { + public var errorDescription: String? { + switch self { + case let .connection(message), let .decipher(message), let .selectApplication(message), let .verify(message): + return message + } + } } extension AppError: LocalizedError { @@ -36,6 +56,8 @@ extension AppError: LocalizedError { return localizationKey.localize(name) case let .pgpPrivateKeyNotFound(keyID), let .pgpPublicKeyNotFound(keyID): return localizationKey.localize(keyID) + case let .other(message): + return message.localize() default: return localizationKey.localize() } diff --git a/passKit/Helpers/DefaultsKeys.swift b/passKit/Helpers/DefaultsKeys.swift index a0bbf81a..a96b2066 100644 --- a/passKit/Helpers/DefaultsKeys.swift +++ b/passKit/Helpers/DefaultsKeys.swift @@ -26,6 +26,7 @@ public extension DefaultsKeys { var pgpKeySource: DefaultsKey { .init("pgpKeySource") } var pgpPublicKeyURL: DefaultsKey { .init("pgpPublicKeyURL") } var pgpPrivateKeyURL: DefaultsKey { .init("pgpPrivateKeyURL") } + var isYubiKeyEnabled: DefaultsKey { .init("isYubiKeyEnabled", defaultValue: false) } // Keep them for legacy reasons. var pgpPublicKeyArmor: DefaultsKey { .init("pgpPublicKeyArmor") } diff --git a/passKit/Helpers/YubiKeyAPDU.swift b/passKit/Helpers/YubiKeyAPDU.swift new file mode 100644 index 00000000..70821114 --- /dev/null +++ b/passKit/Helpers/YubiKeyAPDU.swift @@ -0,0 +1,54 @@ +// +// YubiKeyAPDU.swift +// passKit +// +// Copyright © 2022 Bob Sun. All rights reserved. +// + +import YubiKit + +public enum YubiKeyAPDU { + public static func selectOpenPGPApplication() -> YKFSelectApplicationAPDU { + let selectOpenPGPAPDU = YKFSelectApplicationAPDU(data: Data([0xD2, 0x76, 0x00, 0x01, 0x24, 0x01]))! + return selectOpenPGPAPDU + } + + public static func verify(password: String) -> YKFAPDU { + let pw1: [UInt8] = Array(password.utf8) + var apdu: [UInt8] = [] + apdu += [0x00] // CLA + apdu += [0x20] // INS: VERIFY + apdu += [0x00] // P1 + apdu += [0x82] // P2: PW1 + apdu += withUnsafeBytes(of: UInt8(pw1.count).bigEndian, Array.init) + apdu += pw1 + let verifyApdu = YKFAPDU(data: Data(apdu))! + return verifyApdu + } + + public static func decipher(data: Data) -> YKFAPDU { + var apdu: [UInt8] = [] + apdu += [0x00] // CLA + apdu += [0x2A, 0x80, 0x86] // INS, P1, P2: PSO.DECIPHER + // Lc, An extended Lc field consists of three bytes: + // one byte set to '00' followed by two bytes not set to '0000' (1 to 65535 dec.). + apdu += [0x00] + withUnsafeBytes(of: UInt16(data.count + 1).bigEndian, Array.init) + // Padding indicator byte (00) for RSA or (02) for AES followed by cryptogram Cipher DO 'A6' for ECDH + apdu += [0x00] + apdu += data + apdu += [0x02, 0x00] + let decipherApdu = YKFAPDU(data: Data(apdu))! + + return decipherApdu + } + + public static func get_application_related_data() -> YKFAPDU { + var apdu: [UInt8] = [] + apdu += [0x00] // CLA + apdu += [0xCA] // INS: GET DATA + apdu += [0x00] + apdu += [0x6E] // P2: application related data + apdu += [0x00] + return YKFAPDU(data: Data(apdu))! + } +} diff --git a/passKit/Helpers/YubiKeyConnection.swift b/passKit/Helpers/YubiKeyConnection.swift new file mode 100644 index 00000000..fe759407 --- /dev/null +++ b/passKit/Helpers/YubiKeyConnection.swift @@ -0,0 +1,63 @@ +// +// YubiKeyConnection.swift +// passKit +// +// Copyright © 2022 Bob Sun. All rights reserved. +// + +import Foundation +import YubiKit + +public class YubiKeyConnection: NSObject { + public static let shared = YubiKeyConnection() + + var accessoryConnection: YKFAccessoryConnection? + var nfcConnection: YKFNFCConnection? + var connectionCallback: ((_ connection: YKFConnectionProtocol) -> Void)? + var cancellationCallback: ((_ error: Error) -> Void)? + + override init() { + super.init() + YubiKitManager.shared.delegate = self + YubiKitManager.shared.startAccessoryConnection() + } + + public func connection(cancellation: @escaping (_ error: Error) -> Void, completion: @escaping (_ connection: YKFConnectionProtocol) -> Void) { + if let connection = accessoryConnection { + completion(connection) + } else { + connectionCallback = completion + if #available(iOSApplicationExtension 13.0, *) { + YubiKitManager.shared.startNFCConnection() + } + } + cancellationCallback = cancellation + } +} + +extension YubiKeyConnection: YKFManagerDelegate { + public func didConnectNFC(_ connection: YKFNFCConnection) { + nfcConnection = connection + if let callback = connectionCallback { + callback(connection) + } + } + + public func didDisconnectNFC(_: YKFNFCConnection, error _: Error?) { + nfcConnection = nil + } + + public func didConnectAccessory(_ connection: YKFAccessoryConnection) { + accessoryConnection = connection + } + + public func didDisconnectAccessory(_: YKFAccessoryConnection, error _: Error?) { + accessoryConnection = nil + } + + public func didFailConnectingNFC(_ error: Error) { + if let callback = cancellationCallback { + callback(error) + } + } +} diff --git a/passKit/Models/PasswordEntity.swift b/passKit/Models/PasswordEntity.swift index 84792b87..bbeb8616 100644 --- a/passKit/Models/PasswordEntity.swift +++ b/passKit/Models/PasswordEntity.swift @@ -39,7 +39,7 @@ public extension PasswordEntity { if let path = getPath().stringByAddingPercentEncodingForRFC3986(), let url = URL(string: path) { return url } - throw AppError.unknown + throw AppError.other(message: "cannot decode URL") } // XXX: define some getters to get core data, we need to consider diff --git a/passKit/Protocols/AlertPresenting.swift b/passKit/Protocols/AlertPresenting.swift new file mode 100644 index 00000000..ec745da5 --- /dev/null +++ b/passKit/Protocols/AlertPresenting.swift @@ -0,0 +1,55 @@ +// +// AlertPresenting.swift +// pass +// +// Copyright © 2022 Bob Sun. All rights reserved. +// + +import UIKit + +public typealias AlertAction = (UIAlertAction) -> Void + +public protocol AlertPresenting { + func presentAlert(title: String, message: String) + func presentFailureAlert(title: String?, message: String, action: AlertAction?) + func presentAlertWithAction(title: String, message: String, action: AlertAction?) +} + +public extension AlertPresenting where Self: UIViewController { + func presentAlert(title: String, message: String) { + presentAlert( + title: title, + message: message, + actions: [UIAlertAction(title: "OK", style: .cancel, handler: nil)] + ) + } + + // swiftlint:disable function_default_parameter_at_end + func presentFailureAlert(title: String? = nil, message: String, action: AlertAction? = nil) { + let title = title ?? "Error" + presentAlert( + title: title, + message: message, + actions: [UIAlertAction(title: "OK", style: .cancel, handler: action)] + ) + } + + func presentAlertWithAction(title: String, message: String, action: AlertAction?) { + presentAlert( + title: title, + message: message, + actions: [ + UIAlertAction(title: "Yes", style: .default, handler: action), + UIAlertAction(title: "No", style: .cancel, handler: nil), + ] + ) + } + + private func presentAlert(title: String, message: String, actions: [UIAlertAction] = []) { + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + actions.forEach { action in + alertController.addAction(action) + } + present(alertController, animated: true, completion: nil) + } +} diff --git a/scripts/gopenpgp_build.sh b/scripts/gopenpgp_build.sh index f1997e3c..f1bac2a9 100755 --- a/scripts/gopenpgp_build.sh +++ b/scripts/gopenpgp_build.sh @@ -2,7 +2,7 @@ set -euox pipefail -GOPENPGP_VERSION="v2.1.10" +GOPENPGP_VERSION="passforios" export GOPATH="$(pwd)/go" export PATH="$PATH:$GOPATH/bin" @@ -18,13 +18,11 @@ go env -w GO111MODULE=auto go get golang.org/x/mobile/cmd/gomobile gomobile init -git clone --depth 1 --branch "$GOPENPGP_VERSION" https://github.com/ProtonMail/gopenpgp.git "$GOPENPGP_PATH" - -git apply patch/gnu-dummy.patch --directory "$GOPENPGP_PATH" +git clone --depth 1 --branch "$GOPENPGP_VERSION" https://github.com/mssun/gopenpgp.git "$GOPENPGP_PATH" sed -i '' 's/build android/echo "Skipping Android build."/g' "$GOPENPGP_PATH/build.sh" (cd "$GOPENPGP_PATH" && ./build.sh) -cp -R "$GOPENPGP_PATH/dist/Gopenpgp.xcframework" "$OUTPUT_PATH" +cp -r "$GOPENPGP_PATH/dist/Gopenpgp.xcframework" "$OUTPUT_PATH" From 6d1b271af6bcca94d9ba2d39da744fe32090988d Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sun, 9 Jan 2022 22:12:41 -0800 Subject: [PATCH 003/103] Fix building issue in CI --- pass/Controllers/PasswordDetailTableViewController.swift | 2 +- pass/Helpers/Objective-CBridgingHeader.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index cd1e4d48..e39b5c61 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -576,7 +576,7 @@ extension PasswordDetailTableViewController { handleError(error: AppError.other(message: "PasswordDoesNotExist")) return } - Pass.yubiKeyDecrypt(passwordEntity: passwordEntity, requestPIN: requestYubiKeyPIN, errorHandler: handleError, cancellation: handleCancellation) { password in + yubiKeyDecrypt(passwordEntity: passwordEntity, requestPIN: requestYubiKeyPIN, errorHandler: handleError, cancellation: handleCancellation) { password in self.password = password self.showPassword() } diff --git a/pass/Helpers/Objective-CBridgingHeader.h b/pass/Helpers/Objective-CBridgingHeader.h index 3a71b73d..5a7c3e46 100644 --- a/pass/Helpers/Objective-CBridgingHeader.h +++ b/pass/Helpers/Objective-CBridgingHeader.h @@ -10,6 +10,6 @@ #define Objective_CBridgingHeader_h @import ObjectiveGit; -#import +#import "YubiKit.h" #endif /* Objective_CBridgingHeader_h */ From 11282928318f3887026b461f5f66f36bdfb8adf7 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Tue, 25 Jan 2022 07:52:12 +0100 Subject: [PATCH 004/103] Update SwiftLint to version 0.46.x (#541) --- scripts/swiftlint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/swiftlint.sh b/scripts/swiftlint.sh index e5888c3d..716aac5a 100755 --- a/scripts/swiftlint.sh +++ b/scripts/swiftlint.sh @@ -1,4 +1,4 @@ -SWIFTLINT_VERSION="0.45.*" +SWIFTLINT_VERSION="0.46.*" if [[ "${CI}" == "true" ]]; then echo "Running in a Continuous Integration environment. Linting is skipped." From 71260f52aa82b791e68a28870e629859c1803885 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sat, 5 Feb 2022 05:54:36 +0100 Subject: [PATCH 005/103] Update ObjectivePGP to version 0.99.2 (#544) --- pass.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 0139052e..aab2aeb5 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -2811,7 +2811,7 @@ repositoryURL = "https://github.com/krzyzanowskim/ObjectivePGP"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 0.17.0; + minimumVersion = 0.99.0; }; }; 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */ = { diff --git a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 253dc27a..2ad0c23c 100644 --- a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/krzyzanowskim/ObjectivePGP", "state": { "branch": null, - "revision": "a1ed56c067245fe5b818cbb722d70f3dd74ba213", - "version": "0.99.0" + "revision": "0dc7ca7ffe193095dc37456b0c75be167a2026f4", + "version": "0.99.2" } }, { From 2e951497adf1763d04649033990c8f966d07fbb2 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sat, 5 Feb 2022 20:01:39 +0100 Subject: [PATCH 006/103] Update ObjectiveGit to version 0.17-passforios (#545) --- pass.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index aab2aeb5..87a00b6a 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -2826,7 +2826,7 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SimplyDanny/objective-git-swift-package"; requirement = { - branch = "0.16-passforios"; + branch = "0.17-passforios"; kind = branch; }; }; diff --git a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 2ad0c23c..b3dc414f 100644 --- a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "package": "ObjectiveGit", "repositoryURL": "https://github.com/SimplyDanny/objective-git-swift-package", "state": { - "branch": "0.16-passforios", - "revision": "01c4fe0c410f51484119c087f9bb4a6d3837543b", + "branch": "0.17-passforios", + "revision": "553f09c836b4f90e447e0fe26633acc11c37cece", "version": null } }, From 90f1f54a21fe3f6d04d8e06682b52a5d549b7d99 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sun, 22 May 2022 16:26:00 -0700 Subject: [PATCH 007/103] Fix building issue --- Cartfile | 2 +- Cartfile.resolved | 2 +- README.md | 2 +- fastlane/Fastfile | 1 + pass.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/swiftpm/Package.resolved | 149 +++++++++--------- passKit/Helpers/DefaultsKeys.swift | 68 ++++++++ scripts/gopenpgp_build.sh | 16 +- 8 files changed, 158 insertions(+), 86 deletions(-) diff --git a/Cartfile b/Cartfile index 6a338049..93393ac2 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "SVProgressHUD/SVProgressHUD" +github "SVProgressHUD/SVProgressHUD" "master" diff --git a/Cartfile.resolved b/Cartfile.resolved index b60baa2c..46eb4064 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "SVProgressHUD/SVProgressHUD" "2.2.5" +github "SVProgressHUD/SVProgressHUD" "de1d4dba816a19454329031156b8788692bcfa2c" diff --git a/README.md b/README.md index 9af54373..ce94bd10 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ For more, please read the [wiki page](https://github.com/mssun/passforios/wiki). ## Building Pass for iOS 1. Install Carthage, Go, SwiftLint, and SwiftFormat: `brew install carthage go swiftlint swiftformat`. -2. Install dependencies via Carthage. Therefore, execute `carthage update` and `carthage bootstrap --platform iOS --use-xcframeworks` in the root directory of the project. +2. Install dependencies via Carthage. Therefore, execute `carthage bootstrap --platform iOS --use-xcframeworks` in the root directory of the project. 3. Run `./scripts/gopenpgp_build.sh` to build GopenPGP. 5. Open the `pass.xcodeproj` file in Xcode. 6. Build & Run. diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 06fdcbbb..2ebe5273 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -94,6 +94,7 @@ platform :ios do scheme: "pass", configuration: "Beta", skip_profile_detection: true, + export_method: "app-store", export_options: { method: "app-store", provisioningProfiles: { diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 87a00b6a..c663c283 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1116,9 +1116,9 @@ isa = PBXNativeTarget; buildConfigurationList = A260758F1EEC6F34005DB03E /* Build configuration list for PBXNativeTarget "passKit" */; buildPhases = ( + A26075751EEC6F34005DB03E /* Headers */, A26075731EEC6F34005DB03E /* Sources */, A26075741EEC6F34005DB03E /* Frameworks */, - A26075751EEC6F34005DB03E /* Headers */, A26075761EEC6F34005DB03E /* Resources */, ); buildRules = ( @@ -2811,7 +2811,7 @@ repositoryURL = "https://github.com/krzyzanowskim/ObjectivePGP"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 0.99.0; + minimumVersion = 0.99.2; }; }; 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */ = { diff --git a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b3dc414f..f3e4dfa3 100644 --- a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,79 +1,76 @@ { - "object": { - "pins": [ - { - "package": "Base32", - "repositoryURL": "https://github.com/mattrubin/Base32", - "state": { - "branch": "1.1.2+spm", - "revision": "d185e44c8b355d34d5c6c6ad502c60cba4599f69", - "version": null - } - }, - { - "package": "FavIcon", - "repositoryURL": "https://github.com/leonbreedt/FavIcon", - "state": { - "branch": null, - "revision": "5bf16aad3ea543891eaef7e8da2aa2f6bb29e6e4", - "version": "3.1.0" - } - }, - { - "package": "KeychainAccess", - "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess", - "state": { - "branch": null, - "revision": "84e546727d66f1adc5439debad16270d0fdd04e7", - "version": "4.2.2" - } - }, - { - "package": "ObjectiveGit", - "repositoryURL": "https://github.com/SimplyDanny/objective-git-swift-package", - "state": { - "branch": "0.17-passforios", - "revision": "553f09c836b4f90e447e0fe26633acc11c37cece", - "version": null - } - }, - { - "package": "ObjectivePGP", - "repositoryURL": "https://github.com/krzyzanowskim/ObjectivePGP", - "state": { - "branch": null, - "revision": "0dc7ca7ffe193095dc37456b0c75be167a2026f4", - "version": "0.99.2" - } - }, - { - "package": "OneTimePassword", - "repositoryURL": "https://github.com/mssun/OneTimePassword", - "state": { - "branch": null, - "revision": "8d59e4abba762d0f1e9aed161081f7b3fe21daa0", - "version": null - } - }, - { - "package": "SwiftyUserDefaults", - "repositoryURL": "https://github.com/sunshinejr/SwiftyUserDefaults", - "state": { - "branch": null, - "revision": "f66bcd04088582c8fbb5cb8554d577e303bae396", - "version": "5.3.0" - } - }, - { - "package": "YubiKit", - "repositoryURL": "https://github.com/Yubico/yubikit-ios", - "state": { - "branch": null, - "revision": "7e75fe8f057acf9bf7ac134d375783a1d409f79e", - "version": "4.1.0" - } + "pins" : [ + { + "identity" : "base32", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mattrubin/Base32", + "state" : { + "branch" : "1.1.2+spm", + "revision" : "d185e44c8b355d34d5c6c6ad502c60cba4599f69" } - ] - }, - "version": 1 + }, + { + "identity" : "favicon", + "kind" : "remoteSourceControl", + "location" : "https://github.com/leonbreedt/FavIcon", + "state" : { + "revision" : "5bf16aad3ea543891eaef7e8da2aa2f6bb29e6e4", + "version" : "3.1.0" + } + }, + { + "identity" : "keychainaccess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kishikawakatsumi/KeychainAccess", + "state" : { + "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7", + "version" : "4.2.2" + } + }, + { + "identity" : "objective-git-swift-package", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SimplyDanny/objective-git-swift-package", + "state" : { + "branch" : "0.17-passforios", + "revision" : "553f09c836b4f90e447e0fe26633acc11c37cece" + } + }, + { + "identity" : "objectivepgp", + "kind" : "remoteSourceControl", + "location" : "https://github.com/krzyzanowskim/ObjectivePGP", + "state" : { + "revision" : "0dc7ca7ffe193095dc37456b0c75be167a2026f4", + "version" : "0.99.2" + } + }, + { + "identity" : "onetimepassword", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mssun/OneTimePassword", + "state" : { + "revision" : "8d59e4abba762d0f1e9aed161081f7b3fe21daa0" + } + }, + { + "identity" : "swiftyuserdefaults", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sunshinejr/SwiftyUserDefaults", + "state" : { + "revision" : "f66bcd04088582c8fbb5cb8554d577e303bae396", + "version" : "5.3.0" + } + }, + { + "identity" : "yubikit-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Yubico/yubikit-ios", + "state" : { + "revision" : "7b19be11a362d9e52eac0d76b5d904560b9e5ea7", + "version" : "4.2.0" + } + } + ], + "version" : 2 } diff --git a/passKit/Helpers/DefaultsKeys.swift b/passKit/Helpers/DefaultsKeys.swift index a96b2066..30594226 100644 --- a/passKit/Helpers/DefaultsKeys.swift +++ b/passKit/Helpers/DefaultsKeys.swift @@ -9,6 +9,74 @@ import Foundation import SwiftyUserDefaults +// Workaround for Xcode 13: https://github.com/sunshinejr/SwiftyUserDefaults/issues/285 + +extension DefaultsSerializable { + public static var _defaultsArray: DefaultsArrayBridge<[T]> { return DefaultsArrayBridge() } +} +extension Date: DefaultsSerializable { + public static var _defaults: DefaultsObjectBridge { return DefaultsObjectBridge() } +} +extension String: DefaultsSerializable { + public static var _defaults: DefaultsStringBridge { return DefaultsStringBridge() } +} +extension Int: DefaultsSerializable { + public static var _defaults: DefaultsIntBridge { return DefaultsIntBridge() } +} +extension Double: DefaultsSerializable { + public static var _defaults: DefaultsDoubleBridge { return DefaultsDoubleBridge() } +} +extension Bool: DefaultsSerializable { + public static var _defaults: DefaultsBoolBridge { return DefaultsBoolBridge() } +} +extension Data: DefaultsSerializable { + public static var _defaults: DefaultsDataBridge { return DefaultsDataBridge() } +} + +extension URL: DefaultsSerializable { + #if os(Linux) + public static var _defaults: DefaultsKeyedArchiverBridge { return DefaultsKeyedArchiverBridge() } + #else + public static var _defaults: DefaultsUrlBridge { return DefaultsUrlBridge() } + #endif + public static var _defaultsArray: DefaultsKeyedArchiverBridge<[URL]> { return DefaultsKeyedArchiverBridge() } +} + +extension DefaultsSerializable where Self: Codable { + public static var _defaults: DefaultsCodableBridge { return DefaultsCodableBridge() } + public static var _defaultsArray: DefaultsCodableBridge<[Self]> { return DefaultsCodableBridge() } +} + +extension DefaultsSerializable where Self: RawRepresentable { + public static var _defaults: DefaultsRawRepresentableBridge { return DefaultsRawRepresentableBridge() } + public static var _defaultsArray: DefaultsRawRepresentableArrayBridge<[Self]> { return DefaultsRawRepresentableArrayBridge() } +} + +extension DefaultsSerializable where Self: NSCoding { + public static var _defaults: DefaultsKeyedArchiverBridge { return DefaultsKeyedArchiverBridge() } + public static var _defaultsArray: DefaultsKeyedArchiverBridge<[Self]> { return DefaultsKeyedArchiverBridge() } +} + +extension Dictionary: DefaultsSerializable where Key == String { + public typealias T = [Key: Value] + public typealias Bridge = DefaultsObjectBridge + public typealias ArrayBridge = DefaultsArrayBridge<[T]> + public static var _defaults: Bridge { return Bridge() } + public static var _defaultsArray: ArrayBridge { return ArrayBridge() } +} +extension Array: DefaultsSerializable where Element: DefaultsSerializable { + public typealias T = [Element.T] + public typealias Bridge = Element.ArrayBridge + public typealias ArrayBridge = DefaultsObjectBridge<[T]> + public static var _defaults: Bridge { + return Element._defaultsArray + } + public static var _defaultsArray: ArrayBridge { + fatalError("Multidimensional arrays are not supported yet") + } +} + + public var Defaults = DefaultsAdapter(defaults: UserDefaults(suiteName: Globals.groupIdentifier)!, keyStore: DefaultsKeys()) public enum KeySource: String, DefaultsSerializable { diff --git a/scripts/gopenpgp_build.sh b/scripts/gopenpgp_build.sh index f1bac2a9..6e531195 100755 --- a/scripts/gopenpgp_build.sh +++ b/scripts/gopenpgp_build.sh @@ -14,15 +14,21 @@ GOPENPGP_PATH="$CHECKOUT_PATH/gopenpgp" mkdir -p "$OUTPUT_PATH" mkdir -p "$CHECKOUT_PATH" -go env -w GO111MODULE=auto -go get golang.org/x/mobile/cmd/gomobile +go install golang.org/x/mobile/cmd/gomobile@latest gomobile init git clone --depth 1 --branch "$GOPENPGP_VERSION" https://github.com/mssun/gopenpgp.git "$GOPENPGP_PATH" -sed -i '' 's/build android/echo "Skipping Android build."/g' "$GOPENPGP_PATH/build.sh" - -(cd "$GOPENPGP_PATH" && ./build.sh) +pushd "$GOPENPGP_PATH" +mkdir -p dist +go mod download github.com/ProtonMail/go-crypto +gomobile bind -tags mobile -target ios -iosversion 12.0 -v -x -ldflags="-s -w" -o dist/Gopenpgp.xcframework \ + github.com/ProtonMail/gopenpgp/v2/crypto \ + github.com/ProtonMail/gopenpgp/v2/armor \ + github.com/ProtonMail/gopenpgp/v2/constants \ + github.com/ProtonMail/gopenpgp/v2/models \ + github.com/ProtonMail/gopenpgp/v2/subtle github.com/ProtonMail/gopenpgp/v2/helper +popd cp -r "$GOPENPGP_PATH/dist/Gopenpgp.xcframework" "$OUTPUT_PATH" From 687b67a6a29982d1c37346cafd88a7a69990370c Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sun, 22 May 2022 21:57:33 -0700 Subject: [PATCH 008/103] Fix GitHub Actions --- .github/workflows/deploying.yml | 6 +++--- .github/workflows/linting.yml | 4 ++-- .github/workflows/testing.yml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deploying.yml b/.github/workflows/deploying.yml index 4134da1b..c4c0d363 100644 --- a/.github/workflows/deploying.yml +++ b/.github/workflows/deploying.yml @@ -7,7 +7,7 @@ on: jobs: build: - runs-on: macos-11 + runs-on: macos-12 strategy: matrix: channel: ['beta', 'release'] @@ -39,11 +39,11 @@ jobs: bundle config path vendor/bundle bundle install --jobs 4 --retry 3 - name: Carthage - if: steps.carthage-cache.outputs.cache-hit != 'true' + if: ${{ steps.carthage-cache.outputs.cache-hit == false }} run: | carthage bootstrap --no-use-binaries --cache-builds --use-xcframeworks - name: GopenPGP - if: steps.gopenpgp-cache.outputs.cache-hit != 'true' + if: ${{ steps.gopenpgp-cache.outputs.cache-hit == false }} run: | export PATH="/usr/local/opt/go/bin:$PATH" ./scripts/gopenpgp_build.sh diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 067ea35f..ffa853b3 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -4,7 +4,7 @@ on: pull_request jobs: swiftformat: - runs-on: macos-latest + runs-on: macos-12 steps: - uses: actions/checkout@v2 - name: Installing packages @@ -20,7 +20,7 @@ jobs: swiftformat --lint . swiftlint: - runs-on: macos-latest + runs-on: macos-12 steps: - uses: actions/checkout@v2 - name: Installing packages diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 26f41237..dc518ca9 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: testing: - runs-on: macos-11 + runs-on: macos-12 steps: - uses: actions/checkout@v2 - name: Installing packages @@ -33,11 +33,11 @@ jobs: bundle config path vendor/bundle bundle install --jobs 4 --retry 3 - name: Carthage - if: steps.carthage-cache.outputs.cache-hit != 'true' + if: ${{ steps.carthage-cache.outputs.cache-hit == false }} run: | carthage bootstrap --no-use-binaries --cache-builds --use-xcframeworks - name: GopenPGP - if: steps.gopenpgp-cache.outputs.cache-hit != 'true' + if: ${{ steps.gopenpgp-cache.outputs.cache-hit == false }} run: ./scripts/gopenpgp_build.sh - name: Testing run: bundle exec fastlane test From a0a08073bc98831075d7711a57e6a4d676847d14 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sun, 22 May 2022 22:03:39 -0700 Subject: [PATCH 009/103] Fix GitHub Actions --- .github/workflows/deploying.yml | 4 ++++ .github/workflows/linting.yml | 8 ++++++++ .github/workflows/testing.yml | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/.github/workflows/deploying.yml b/.github/workflows/deploying.yml index c4c0d363..f24ae202 100644 --- a/.github/workflows/deploying.yml +++ b/.github/workflows/deploying.yml @@ -13,6 +13,10 @@ jobs: channel: ['beta', 'release'] steps: - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true - name: Installing packages run: | brew update diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index ffa853b3..96031ca9 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -7,6 +7,10 @@ jobs: runs-on: macos-12 steps: - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true - name: Installing packages run: | brew update @@ -23,6 +27,10 @@ jobs: runs-on: macos-12 steps: - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true - name: Installing packages run: | brew update diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index dc518ca9..53366e60 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -7,6 +7,10 @@ jobs: runs-on: macos-12 steps: - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true - name: Installing packages run: | brew update From e5d3b068969295f977856d5d6bf822b4a2b30a16 Mon Sep 17 00:00:00 2001 From: Tony Wang Date: Mon, 23 May 2022 13:23:25 +0800 Subject: [PATCH 010/103] fix error when importing key using http (#556) http url was not saved so it always failed --- pass/Controllers/SSHKeyURLImportTableViewController..swift | 1 + 1 file changed, 1 insertion(+) diff --git a/pass/Controllers/SSHKeyURLImportTableViewController..swift b/pass/Controllers/SSHKeyURLImportTableViewController..swift index e3bf6c76..d1cc1088 100644 --- a/pass/Controllers/SSHKeyURLImportTableViewController..swift +++ b/pass/Controllers/SSHKeyURLImportTableViewController..swift @@ -32,6 +32,7 @@ class SSHKeyURLImportTableViewController: AutoCellHeightUITableViewController { savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: .default) { _ in }) savePassphraseAlert.addAction( UIAlertAction(title: "Yes".localize(), style: .destructive) { _ in + self.sshPrivateKeyURL = privateKeyURL self.performSegue(withIdentifier: "importSSHKeySegue", sender: self) } ) From 6f5385fe4ac64bff6a3dd226f1d13eb397ea42f5 Mon Sep 17 00:00:00 2001 From: Bradley Walters Date: Sun, 22 May 2022 23:35:53 -0600 Subject: [PATCH 011/103] Force weak linking of CryptoTokenKit (#543) Apple's CryptoTokenKit is only present in iOS 13.0+ however it exports symbols with availability annotations going back to iOS 10.0. In the Pass app we have a deployment target of iOS 12.0. Apple's automatic weak linking system apparently only looks at the symbol-level availability annotations so it assumes the symbols we use will always be present (even though they won't pre-iOS-13). We can work around this issue by forcing weak linking using the "Optional" framework setting. (Note that this workaround would not work if CryptoTokenKit was used from a third-party swift package.) This is necessary to restore iOS 12 support after #533. For further history see https://github.com/mssun/passforios/issues/539 --- pass.xcodeproj/project.pbxproj | 8 ++++++++ pass/Services/PasswordDecryptor.swift | 19 ++++++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index c663c283..e07195c2 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -109,6 +109,9 @@ 556EC3D922335D2800934F9C /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 30BF5ED521ED2434000E4154 /* Localizable.stringsdict */; }; 556EC3DA22335D3400934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; }; 556EC3DB22335D3D00934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; }; + 5F9D7B0D27AF6F7500A8AB22 /* CryptoTokenKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 5F9D7B0E27AF6FCA00A8AB22 /* CryptoTokenKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 5F9D7B0F27AF6FD200A8AB22 /* CryptoTokenKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE426E5D1CE0052028E /* OneTimePassword */; }; 9A1D1CE726E5D2230052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE626E5D2230052028E /* OneTimePassword */; }; 9A1F47FA26E5CF4B000C0E01 /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1F47F926E5CF4B000C0E01 /* OneTimePassword */; }; @@ -413,6 +416,7 @@ 30EE3A1B241E98C6009FBB61 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Intents.strings; sourceTree = ""; }; 30F6C1B327664C7200BE5AB2 /* SVProgressHUD.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = SVProgressHUD.xcframework; path = Carthage/Build/SVProgressHUD.xcframework; sourceTree = ""; }; 30FD2F77214D9E0E005E0A92 /* ParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParserTest.swift; sourceTree = ""; }; + 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoTokenKit.framework; path = System/Library/Frameworks/CryptoTokenKit.framework; sourceTree = SDKROOT; }; 9A1EF0B324C50DD80074FEAC /* passBeta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBeta.entitlements; sourceTree = ""; }; 9A1EF0B424C50E780074FEAC /* passBetaAutoFillExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaAutoFillExtension.entitlements; sourceTree = ""; }; 9A1EF0B524C50EE00074FEAC /* passBetaExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaExtension.entitlements; sourceTree = ""; }; @@ -519,6 +523,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5F9D7B0E27AF6FCA00A8AB22 /* CryptoTokenKit.framework in Frameworks */, 9A5C6EF92786CE170003F340 /* YubiKit in Frameworks */, 9A996C6E26DEB99200A4485D /* passKit.framework in Frameworks */, 30A3001A26DA697C002A734E /* SwiftyUserDefaults in Frameworks */, @@ -555,6 +560,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5F9D7B0F27AF6FD200A8AB22 /* CryptoTokenKit.framework in Frameworks */, 9A5C6F022787F09A0003F340 /* passKit.framework in Frameworks */, 9A5C6EFB2786CE5E0003F340 /* YubiKit in Frameworks */, 9A5C6EFF2787F0980003F340 /* Gopenpgp.xcframework in Frameworks */, @@ -573,6 +579,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5F9D7B0D27AF6F7500A8AB22 /* CryptoTokenKit.framework in Frameworks */, 3010CB6026DA4F87008964D2 /* SwiftyUserDefaults in Frameworks */, 3010CB6326DA4FE9008964D2 /* FavIcon in Frameworks */, 9A1F47FA26E5CF4B000C0E01 /* OneTimePassword in Frameworks */, @@ -772,6 +779,7 @@ 9ADAB21926DDA4F600900F10 /* Frameworks */ = { isa = PBXGroup; children = ( + 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */, 30F6C1B327664C7200BE5AB2 /* SVProgressHUD.xcframework */, 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */, ); diff --git a/pass/Services/PasswordDecryptor.swift b/pass/Services/PasswordDecryptor.swift index 0b6d0679..0e0675a1 100644 --- a/pass/Services/PasswordDecryptor.swift +++ b/pass/Services/PasswordDecryptor.swift @@ -66,15 +66,20 @@ let symmetricKeyIDNameDict: [UInt8: String] = [ ] private func isEncryptKeyAlgoRSA(_ applicationRelatedData: Data) -> Bool { - let tlv = TKBERTLVRecord.sequenceOfRecords(from: applicationRelatedData)! - // 0x73: Discretionary data objects - for record in TKBERTLVRecord.sequenceOfRecords(from: tlv.first!.value)! where record.tag == 0x73 { - // 0xC2: Algorithm attributes decryption, 0x01: RSA - for record2 in TKBERTLVRecord.sequenceOfRecords(from: record.value)! where record2.tag == 0xC2 && record2.value.first! == 0x01 { - return true + if #available(iOS 13.0, *) { + let tlv = TKBERTLVRecord.sequenceOfRecords(from: applicationRelatedData)! + // 0x73: Discretionary data objects + for record in TKBERTLVRecord.sequenceOfRecords(from: tlv.first!.value)! where record.tag == 0x73 { + // 0xC2: Algorithm attributes decryption, 0x01: RSA + for record2 in TKBERTLVRecord.sequenceOfRecords(from: record.value)! where record2.tag == 0xC2 && record2.value.first! == 0x01 { + return true + } } + return false + } else { + // We need CryptoTokenKit (iOS 13.0+) to check if data is RSA, so fail open here. + return true } - return false } // swiftlint:disable cyclomatic_complexity From 440b0123f2a75d4276143c3dc23053994d49fa6e Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Mon, 23 May 2022 10:54:11 -0700 Subject: [PATCH 012/103] Fix building issues with XCode --- pass.xcodeproj/project.pbxproj | 4 +- .../PasswordDetailTableViewController.swift | 1 + pass/Helpers/Objective-CBridgingHeader.h | 1 - passKit/Helpers/DefaultsKeys.swift | 68 ++----------------- passKit/Helpers/SearchBarScope.swift | 2 - scripts/swiftformat.sh | 1 + scripts/swiftlint.sh | 4 +- 7 files changed, 13 insertions(+), 68 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index e07195c2..d090c396 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -150,7 +150,6 @@ 9A996C5726DDF65900A4485D /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 9A996C5626DDF65900A4485D /* Base32 */; }; 9A996C5826DEB0D100A4485D /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; }; 9A996C5926DEB0D200A4485D /* passKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A996C5E26DEB79E00A4485D /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; }; 9A996C6426DEB93F00A4485D /* passAutoFillExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9A996C6826DEB96B00A4485D /* passShortcuts.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 30A69945240EED5E00B7D967 /* passShortcuts.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9A996C6B26DEB97600A4485D /* passExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A26700241EEC466A00176B8A /* passExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -192,6 +191,7 @@ DC037CBC1E4DD47B00609409 /* TextFieldTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC037CBA1E4DD47B00609409 /* TextFieldTableViewCell.xib */; }; DC037CBF1E4ED4E100609409 /* TextViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC037CBD1E4ED4E100609409 /* TextViewTableViewCell.swift */; }; DC037CC01E4ED4E100609409 /* TextViewTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC037CBE1E4ED4E100609409 /* TextViewTableViewCell.xib */; }; + DC0F7692283C00220042DA74 /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; }; DC193FFA1E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC193FF91E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift */; }; DC3E64E61E656F11009A83DE /* CommitLogsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3E64E51E656F11009A83DE /* CommitLogsTableViewController.swift */; }; DC4914961E434301007FF592 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4914941E434301007FF592 /* LabelTableViewCell.swift */; }; @@ -514,7 +514,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9A996C5E26DEB79E00A4485D /* passKit.framework in Frameworks */, + DC0F7692283C00220042DA74 /* passKit.framework in Frameworks */, 30A3001826DA6974002A734E /* SwiftyUserDefaults in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index e39b5c61..3e7ad0f8 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -12,6 +12,7 @@ import passAutoFillExtension import passKit import SVProgressHUD import UIKit +import YubiKit class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate, AlertPresenting { var passwordEntity: PasswordEntity? diff --git a/pass/Helpers/Objective-CBridgingHeader.h b/pass/Helpers/Objective-CBridgingHeader.h index 5a7c3e46..ff807e53 100644 --- a/pass/Helpers/Objective-CBridgingHeader.h +++ b/pass/Helpers/Objective-CBridgingHeader.h @@ -10,6 +10,5 @@ #define Objective_CBridgingHeader_h @import ObjectiveGit; -#import "YubiKit.h" #endif /* Objective_CBridgingHeader_h */ diff --git a/passKit/Helpers/DefaultsKeys.swift b/passKit/Helpers/DefaultsKeys.swift index 30594226..1f31d3d4 100644 --- a/passKit/Helpers/DefaultsKeys.swift +++ b/passKit/Helpers/DefaultsKeys.swift @@ -11,72 +11,16 @@ import SwiftyUserDefaults // Workaround for Xcode 13: https://github.com/sunshinejr/SwiftyUserDefaults/issues/285 -extension DefaultsSerializable { - public static var _defaultsArray: DefaultsArrayBridge<[T]> { return DefaultsArrayBridge() } -} -extension Date: DefaultsSerializable { - public static var _defaults: DefaultsObjectBridge { return DefaultsObjectBridge() } -} -extension String: DefaultsSerializable { - public static var _defaults: DefaultsStringBridge { return DefaultsStringBridge() } -} -extension Int: DefaultsSerializable { - public static var _defaults: DefaultsIntBridge { return DefaultsIntBridge() } -} -extension Double: DefaultsSerializable { - public static var _defaults: DefaultsDoubleBridge { return DefaultsDoubleBridge() } -} -extension Bool: DefaultsSerializable { - public static var _defaults: DefaultsBoolBridge { return DefaultsBoolBridge() } -} -extension Data: DefaultsSerializable { - public static var _defaults: DefaultsDataBridge { return DefaultsDataBridge() } -} - -extension URL: DefaultsSerializable { - #if os(Linux) - public static var _defaults: DefaultsKeyedArchiverBridge { return DefaultsKeyedArchiverBridge() } - #else - public static var _defaults: DefaultsUrlBridge { return DefaultsUrlBridge() } - #endif - public static var _defaultsArray: DefaultsKeyedArchiverBridge<[URL]> { return DefaultsKeyedArchiverBridge() } +public extension DefaultsSerializable where Self: Codable { + typealias Bridge = DefaultsCodableBridge + typealias ArrayBridge = DefaultsCodableBridge<[Self]> } -extension DefaultsSerializable where Self: Codable { - public static var _defaults: DefaultsCodableBridge { return DefaultsCodableBridge() } - public static var _defaultsArray: DefaultsCodableBridge<[Self]> { return DefaultsCodableBridge() } +public extension DefaultsSerializable where Self: RawRepresentable { + typealias Bridge = DefaultsRawRepresentableBridge + typealias ArrayBridge = DefaultsRawRepresentableArrayBridge<[Self]> } -extension DefaultsSerializable where Self: RawRepresentable { - public static var _defaults: DefaultsRawRepresentableBridge { return DefaultsRawRepresentableBridge() } - public static var _defaultsArray: DefaultsRawRepresentableArrayBridge<[Self]> { return DefaultsRawRepresentableArrayBridge() } -} - -extension DefaultsSerializable where Self: NSCoding { - public static var _defaults: DefaultsKeyedArchiverBridge { return DefaultsKeyedArchiverBridge() } - public static var _defaultsArray: DefaultsKeyedArchiverBridge<[Self]> { return DefaultsKeyedArchiverBridge() } -} - -extension Dictionary: DefaultsSerializable where Key == String { - public typealias T = [Key: Value] - public typealias Bridge = DefaultsObjectBridge - public typealias ArrayBridge = DefaultsArrayBridge<[T]> - public static var _defaults: Bridge { return Bridge() } - public static var _defaultsArray: ArrayBridge { return ArrayBridge() } -} -extension Array: DefaultsSerializable where Element: DefaultsSerializable { - public typealias T = [Element.T] - public typealias Bridge = Element.ArrayBridge - public typealias ArrayBridge = DefaultsObjectBridge<[T]> - public static var _defaults: Bridge { - return Element._defaultsArray - } - public static var _defaultsArray: ArrayBridge { - fatalError("Multidimensional arrays are not supported yet") - } -} - - public var Defaults = DefaultsAdapter(defaults: UserDefaults(suiteName: Globals.groupIdentifier)!, keyStore: DefaultsKeys()) public enum KeySource: String, DefaultsSerializable { diff --git a/passKit/Helpers/SearchBarScope.swift b/passKit/Helpers/SearchBarScope.swift index b679f0f7..180e8666 100644 --- a/passKit/Helpers/SearchBarScope.swift +++ b/passKit/Helpers/SearchBarScope.swift @@ -6,8 +6,6 @@ // Copyright © 2019 Bob Sun. All rights reserved. // -import SwiftyUserDefaults - public enum SearchBarScope: Int { case current case all diff --git a/scripts/swiftformat.sh b/scripts/swiftformat.sh index 8c82e269..065354a6 100755 --- a/scripts/swiftformat.sh +++ b/scripts/swiftformat.sh @@ -1,3 +1,4 @@ +export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}" SWIFTFORMAT_VERSION="0.49.*" if [[ "${CI}" == "true" ]]; then diff --git a/scripts/swiftlint.sh b/scripts/swiftlint.sh index 716aac5a..4ef7412b 100755 --- a/scripts/swiftlint.sh +++ b/scripts/swiftlint.sh @@ -1,4 +1,6 @@ -SWIFTLINT_VERSION="0.46.*" +export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}" + +SWIFTLINT_VERSION="0.47.*" if [[ "${CI}" == "true" ]]; then echo "Running in a Continuous Integration environment. Linting is skipped." From 9b5b0eff1f02629305ac94ae6d693241aaf68f9e Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Mon, 23 May 2022 15:27:58 -0700 Subject: [PATCH 013/103] Exclude the vendor dir for swiftformat and swiftlint (#562) --- .swiftformat | 3 ++- .swiftlint.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.swiftformat b/.swiftformat index f3feea4b..11ffe6e7 100644 --- a/.swiftformat +++ b/.swiftformat @@ -5,7 +5,8 @@ --exclude \ Carthage, \ go, \ - Pods + Pods, \ + vendor ## Enabled rules diff --git a/.swiftlint.yml b/.swiftlint.yml index 2abbdd2b..ede56bca 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -6,6 +6,7 @@ excluded: - Carthage - go - Pods + - vendor ## Active rules From 0f98569d54b99982bdfe0c92e145fe7f358b3d81 Mon Sep 17 00:00:00 2001 From: Bradley Walters Date: Mon, 23 May 2022 16:28:09 -0600 Subject: [PATCH 014/103] Update ObjectiveGit to 0.18-passforios (#561) This should resolve an incompatibility with older iOS versions. --- pass.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index d090c396..db16c080 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -2834,7 +2834,7 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SimplyDanny/objective-git-swift-package"; requirement = { - branch = "0.17-passforios"; + branch = "0.18-passforios"; kind = branch; }; }; diff --git a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f3e4dfa3..e5fc5559 100644 --- a/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/pass.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SimplyDanny/objective-git-swift-package", "state" : { - "branch" : "0.17-passforios", - "revision" : "553f09c836b4f90e447e0fe26633acc11c37cece" + "branch" : "0.18-passforios", + "revision" : "752ea5689a1c0c0f9a8442df039e842a452cfcf0" } }, { From 01f808b7786ca534e0dd117d64e5054b1e05fec0 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Tue, 24 May 2022 18:05:51 -0700 Subject: [PATCH 015/103] Add footnote of using TestFlight for iOS 12 users. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ce94bd10..f3adfa4d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ Pass for iOS is available in App Store with the name "Pass - Password Store", an Download on the App Store

-You can also help us test beta versions through [TestFlight](https://testflight.apple.com/join/whK4zUFG). +You can also help us test beta versions through [TestFlight](https://testflight.apple.com/join/whK4zUFG) [^1]. + +[^1]: For iOS 12 users, you can download the TestFlight app by first "purchasing it" on a Mac using your Apple ID, then going to the purchased section of the App Store on your iOS device and downloading it from there. ## Features From 972cf20d34e9aeb1c7ac6b08415f7c082e3c70f7 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Tue, 31 May 2022 10:33:54 -0700 Subject: [PATCH 016/103] Fix umbrella header warnings (#563) --- pass.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index db16c080..1cef924e 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -206,6 +206,7 @@ DCA0499A1E335CC800522E8F /* GitRepositorySettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA049991E335CC800522E8F /* GitRepositorySettingsTableViewController.swift */; }; DCA0499C1E3362F400522E8F /* PGPKeyURLImportTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA0499B1E3362F400522E8F /* PGPKeyURLImportTableViewController.swift */; }; DCAAF7451E2FA66800AB94BC /* SettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */; }; + DCB0EC272846857E00EFEE10 /* Objective-CBridgingHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = A2BC54C71EEE5669001FAFBD /* Objective-CBridgingHeader.h */; }; DCC441521E8F6C06008A90C4 /* RawPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */; }; DCC441541E916382008A90C4 /* SSHKeyArmorImportTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441531E916382008A90C4 /* SSHKeyArmorImportTableViewController.swift */; }; DCD3C65E1EFB9BB400CBE842 /* SettingsSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD3C65D1EFB9BB400CBE842 /* SettingsSplitViewController.swift */; }; @@ -1072,6 +1073,7 @@ buildActionMask = 2147483647; files = ( A260758A1EEC6F34005DB03E /* passKit.h in Headers */, + DCB0EC272846857E00EFEE10 /* Objective-CBridgingHeader.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; From 4da22b0a80fc7ec84098d067f25128cba386ef20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 18:59:51 -0700 Subject: [PATCH 017/103] Bump jmespath from 1.4.0 to 1.6.1 (#564) Bumps [jmespath](https://github.com/trevorrowe/jmespath.rb) from 1.4.0 to 1.6.1. - [Release notes](https://github.com/trevorrowe/jmespath.rb/releases) - [Changelog](https://github.com/jmespath/jmespath.rb/blob/main/CHANGELOG.md) - [Commits](https://github.com/trevorrowe/jmespath.rb/compare/v1.4.0...v1.6.1) --- updated-dependencies: - dependency-name: jmespath dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 26cba895..de4e4d59 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -114,7 +114,7 @@ GEM http-cookie (1.0.3) domain_name (~> 0.5) httpclient (2.8.3) - jmespath (1.4.0) + jmespath (1.6.1) json (2.3.1) jwt (2.2.1) memoist (0.16.2) From ad105b3df1b76b7a7c7b24d5cd1403ea98a3d5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 16 Jun 2022 04:55:02 +0200 Subject: [PATCH 018/103] Enable some more SwitfLint rules (#565) --- .swiftlint.yml | 5 +++++ pass/Controllers/PasswordDetailTableViewController.swift | 3 ++- pass/Controllers/QRScannerController.swift | 9 ++++++--- .../SSHKeyURLImportTableViewController..swift | 3 ++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index ede56bca..61d2a402 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -11,6 +11,7 @@ excluded: ## Active rules only_rules: + - accessibility_label_for_image # - anonymous_argument_in_multiline_closure - anyobject_protocol - array_init @@ -27,6 +28,7 @@ only_rules: - collection_alignment - colon - comma + - comma_inheritance - comment_spacing - compiler_protocol_init - computed_accessors_order @@ -141,6 +143,7 @@ only_rules: - override_in_extension - pattern_matching_keywords # - prefer_nimble + - prefer_self_in_static_references - prefer_self_type_over_type_of_self - prefer_zero_over_explicit_init # - prefixed_toplevel_constant # Violations are mostly in test code. @@ -169,6 +172,7 @@ only_rules: # - required_deinit - required_enum_case - return_arrow_whitespace + - return_value_from_void_function - self_in_property_initialization - shorthand_operator - single_test_class @@ -193,6 +197,7 @@ only_rules: # - type_body_length # - type_contents_order - type_name + - unavailable_condition - unavailable_function - unneeded_break_in_switch - unneeded_parentheses_in_closure_argument diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 3e7ad0f8..90fdf8a3 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -386,9 +386,10 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni func openLink(to address: String?) { guard address != nil, let url = URL(string: formActualWebAddress(from: address!)) else { - return DispatchQueue.main.async { + DispatchQueue.main.async { Utils.alert(title: "Error".localize(), message: "CannotFindValidUrl".localize(), controller: self, completion: nil) } + return } SecurePasteboard.shared.copy(textToCopy: password?.password) UIApplication.shared.open(url, options: [:], completionHandler: nil) diff --git a/pass/Controllers/QRScannerController.swift b/pass/Controllers/QRScannerController.swift index 9e0f05e4..4693d663 100644 --- a/pass/Controllers/QRScannerController.swift +++ b/pass/Controllers/QRScannerController.swift @@ -90,13 +90,16 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg func metadataOutput(_: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from _: AVCaptureConnection) { guard let metadataObj = metadataObjects.first as? AVMetadataMachineReadableCodeObject else { - return setNotDetected() + setNotDetected() + return } guard supportedCodeTypes.contains(metadataObj.type) else { - return setNotDetected() + setNotDetected() + return } guard let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj) else { - return setNotDetected() + setNotDetected() + return } // draw a bounds on the found QR code diff --git a/pass/Controllers/SSHKeyURLImportTableViewController..swift b/pass/Controllers/SSHKeyURLImportTableViewController..swift index d1cc1088..f219f64d 100644 --- a/pass/Controllers/SSHKeyURLImportTableViewController..swift +++ b/pass/Controllers/SSHKeyURLImportTableViewController..swift @@ -36,7 +36,8 @@ class SSHKeyURLImportTableViewController: AutoCellHeightUITableViewController { self.performSegue(withIdentifier: "importSSHKeySegue", sender: self) } ) - return present(savePassphraseAlert, animated: true) + present(savePassphraseAlert, animated: true) + return } sshPrivateKeyURL = privateKeyURL performSegue(withIdentifier: "importSSHKeySegue", sender: self) From a6b05d20e058221fdd1b6db7d489c73c91611b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 30 Jul 2022 20:20:08 +0200 Subject: [PATCH 019/103] Store password repository details only if we are sure to clone/overwrite it (#571) --- ...itRepositorySettingsTableViewController.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pass/Controllers/GitRepositorySettingsTableViewController.swift b/pass/Controllers/GitRepositorySettingsTableViewController.swift index f50e9112..e37c7c48 100644 --- a/pass/Controllers/GitRepositorySettingsTableViewController.swift +++ b/pass/Controllers/GitRepositorySettingsTableViewController.swift @@ -122,7 +122,7 @@ class GitRepositorySettingsTableViewController: UITableViewController, PasswordA return } - guard let branchName = branchNameTextField.text, !branchName.trimmed.isEmpty else { + guard let branchName = branchNameTextField.text?.trimmed, !branchName.isEmpty else { Utils.alert(title: "CannotSave".localize(), message: "SpecifyBranchName.".localize(), controller: self) return } @@ -146,16 +146,14 @@ class GitRepositorySettingsTableViewController: UITableViewController, PasswordA } } - self.gitURL = gitURL - gitBranchName = branchName.trimmed - gitUsername = (gitURL.user ?? usernameTextField.text ?? "git").trimmed + let username = (gitURL.user ?? usernameTextField.text ?? "git").trimmed if passwordStore.repositoryExists() { let overwriteAlert: UIAlertController = { let alert = UIAlertController(title: "Overwrite?".localize(), message: "OperationWillOverwriteData.".localize(), preferredStyle: .alert) alert.addAction( UIAlertAction(title: "Overwrite".localize(), style: .destructive) { _ in - self.cloneAndSegueIfSuccess() + self.cloneAndSegueIfSuccess(url: gitURL, branch: branchName, username: username) } ) alert.addAction(UIAlertAction.cancel()) @@ -163,11 +161,15 @@ class GitRepositorySettingsTableViewController: UITableViewController, PasswordA }() present(overwriteAlert, animated: true) } else { - cloneAndSegueIfSuccess() + cloneAndSegueIfSuccess(url: gitURL, branch: branchName, username: username) } } - private func cloneAndSegueIfSuccess() { + private func cloneAndSegueIfSuccess(url: URL, branch: String, username: String) { + gitURL = url + gitBranchName = branch + gitUsername = username + // Remember git credential password/passphrase temporarily, ask whether users want this after a successful clone. Defaults.isRememberGitCredentialPassphraseOn = true DispatchQueue.global(qos: .userInitiated).async { From 5c62d4a3e33ec912bb700b9578730b15bb61800f Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sat, 30 Jul 2022 11:46:40 -0700 Subject: [PATCH 020/103] Update gem dependencies --- Gemfile.lock | 194 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 117 insertions(+), 77 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index de4e4d59..294cd73f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,57 +1,80 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.2) + CFPropertyList (3.0.5) + rexml addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) + artifactory (3.0.15) atomos (0.1.3) - aws-eventstream (1.1.0) - aws-partitions (1.345.0) - aws-sdk-core (3.104.3) + aws-eventstream (1.2.0) + aws-partitions (1.613.0) + aws-sdk-core (3.131.5) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) + aws-partitions (~> 1, >= 1.525.0) aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.36.0) - aws-sdk-core (~> 3, >= 3.99.0) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.58.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.75.0) - aws-sdk-core (~> 3, >= 3.104.1) + aws-sdk-s3 (1.114.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.1) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.5.1) aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.3) - claide (1.0.3) + babosa (1.0.4) + claide (1.1.0) colored (1.2) colored2 (3.1.2) - commander-fastlane (4.4.6) - highline (~> 1.7.2) + commander (4.6.0) + highline (~> 2.0.0) declarative (0.0.20) - declarative-option (0.1.0) - digest-crc (0.6.1) - rake (~> 13.0) + digest-crc (0.6.4) + rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) - emoji_regex (3.0.0) - excon (0.75.0) - faraday (1.0.1) - multipart-post (>= 1.2, < 3) - faraday-cookie_jar (0.0.6) - faraday (>= 0.7.4) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.92.4) + faraday (1.10.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) http-cookie (~> 1.0.0) - faraday_middleware (1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.0) - fastlane (2.153.1) + fastimage (2.2.6) + fastlane (2.208.0) CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) babosa (>= 1.0.3, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) colored - commander-fastlane (>= 4.4.6, < 5.0.0) + commander (~> 4.6) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) excon (>= 0.71.0, < 1.0.0) @@ -60,18 +83,20 @@ GEM faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.37.0, < 0.39.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) multipart-post (~> 2.0.0) + naturally (~> 2.2) + optparse (~> 0.1.1) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.3) simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) terminal-notifier (>= 2.0.0, < 3.0.0) terminal-table (>= 1.4.5, < 2.0.0) tty-screen (>= 0.6.3, < 1.0.0) @@ -81,60 +106,71 @@ GEM xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) gh_inspector (1.1.3) - google-api-client (0.38.0) + google-apis-androidpublisher_v3 (0.25.0) + google-apis-core (>= 0.7, < 2.a) + google-apis-core (0.7.0) addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.9) - httpclient (>= 2.8.1, < 3.0) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) mini_mime (~> 1.0) representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.12) - google-cloud-core (1.5.0) + retriable (>= 2.0, < 4.a) + rexml + webrick + google-apis-iamcredentials_v1 (0.13.0) + google-apis-core (>= 0.7, < 2.a) + google-apis-playcustomapp_v1 (0.10.0) + google-apis-core (>= 0.7, < 2.a) + google-apis-storage_v1 (0.18.0) + google-apis-core (>= 0.7, < 2.a) + google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.3.3) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.0.1) - google-cloud-storage (1.26.2) - addressable (~> 2.5) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.2.0) + google-cloud-storage (1.37.0) + addressable (~> 2.8) digest-crc (~> 0.4) - google-api-client (~> 0.33) - google-cloud-core (~> 1.2) - googleauth (~> 0.9) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.1) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (0.13.0) - faraday (>= 0.17.3, < 2.0) + googleauth (1.2.0) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.14) - highline (1.7.10) + signet (>= 0.16, < 2.a) + highline (2.0.3) http-accept (1.7.0) - http-cookie (1.0.3) + http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.1) - json (2.3.1) - jwt (2.2.1) + json (2.6.2) + jwt (2.4.1) memoist (0.16.2) - mime-types (3.3.1) + mime-types (3.4.1) mime-types-data (~> 3.2015) - mime-types-data (3.2020.0512) - mini_magick (4.10.1) - mini_mime (1.0.2) + mime-types-data (3.2022.0105) + mini_magick (4.11.0) + mini_mime (1.1.2) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) - naturally (2.2.0) + naturally (2.2.1) netrc (0.11.0) - os (1.1.0) - plist (3.5.0) - public_suffix (4.0.6) - rake (13.0.1) - representable (3.0.4) + optparse (0.1.1) + os (1.1.4) + plist (3.6.0) + public_suffix (4.0.7) + rake (13.0.6) + representable (3.2.0) declarative (< 0.1.0) - declarative-option (< 0.2.0) + trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) @@ -142,21 +178,23 @@ GEM mime-types (>= 1.16, < 4.0) netrc (~> 0.8) retriable (3.1.2) + rexml (3.2.5) rouge (2.0.7) - rubyzip (2.3.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) security (0.1.3) - signet (0.14.0) - addressable (~> 2.3) - faraday (>= 0.17.3, < 2.0) + signet (0.17.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) simctl (1.6.8) CFPropertyList naturally - slack-notifier (2.3.2) terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) + trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) @@ -164,18 +202,20 @@ GEM uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) + unf_ext (0.0.8.2) + unicode-display_width (1.8.0) + webrick (1.7.0) word_wrap (1.0.0) - xcodeproj (1.17.1) + xcodeproj (1.22.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) + rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.0) + xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) PLATFORMS @@ -187,4 +227,4 @@ DEPENDENCIES xcodeproj BUNDLED WITH - 2.2.25 + 2.3.19 From b6be91301722024bdfe76a598003550d0e3ab700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 1 Nov 2022 04:29:42 +0100 Subject: [PATCH 021/103] Update SwiftFormat to version 0.50.x (#577) --- .swiftformat | 9 +++++++++ scripts/swiftformat.sh | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.swiftformat b/.swiftformat index 11ffe6e7..8d02852b 100644 --- a/.swiftformat +++ b/.swiftformat @@ -15,6 +15,7 @@ andOperator, \ anyObjectProtocol, \ assertionFailures, \ + blankLineAfterImports, \ blankLinesAroundMark, \ blankLinesAtEndOfScope, \ blankLinesAtStartOfScope, \ @@ -30,6 +31,7 @@ enumNamespaces, \ extensionAccessControl, \ fileHeader, \ +# genericExtensions, \ hoistPatternLet, \ indent, \ initCoderUnavailable, \ @@ -40,6 +42,7 @@ modifierOrder, \ # markTypes, \ numberFormatting, \ +# opaqueGenericParameters, \ # organizeDeclarations, \ preferDouble, \ preferKeyPath, \ @@ -54,6 +57,7 @@ redundantLetError, \ redundantNilInit, \ redundantObjc, \ +# redundantOptionalBinding, \ redundantParens, \ redundantPattern, \ redundantRawValues, \ @@ -91,6 +95,7 @@ wrapConditionalBodies, \ # wrapEnumCases, \ # wrapMultilineStatementBraces, \ + wrapSingleLineComments, \ # wrapSwitchCases, \ yodaConditions @@ -132,6 +137,10 @@ --emptybraces no-space +### Enum namespaces: "always" (default) or "structs-only" + +--enumnamespaces always + ### Case of 'e' in numbers: "lowercase" or "uppercase" (default) --exponentcase lowercase diff --git a/scripts/swiftformat.sh b/scripts/swiftformat.sh index 065354a6..021720bf 100755 --- a/scripts/swiftformat.sh +++ b/scripts/swiftformat.sh @@ -1,5 +1,5 @@ export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}" -SWIFTFORMAT_VERSION="0.49.*" +SWIFTFORMAT_VERSION="0.50.*" if [[ "${CI}" == "true" ]]; then echo "Running in a Continuous Integration environment. Formatting is skipped." From 1307dc77c5a32fbf69ead5499c63cb4dba28cae3 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Mon, 31 Oct 2022 21:31:35 -0700 Subject: [PATCH 022/103] Fix CI building issue --- Gemfile.lock | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 294cd73f..0f3009fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,25 +3,25 @@ GEM specs: CFPropertyList (3.0.5) rexml - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.613.0) - aws-sdk-core (3.131.5) + aws-partitions (1.654.0) + aws-sdk-core (3.166.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.525.0) - aws-sigv4 (~> 1.1) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.58.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-kms (1.59.0) + aws-sdk-core (~> 3, >= 3.165.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.114.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-s3 (1.117.1) + aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) - aws-sigv4 (1.5.1) + aws-sigv4 (1.5.2) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.1.0) @@ -36,8 +36,8 @@ GEM unf (>= 0.0.5, < 1.0.0) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.92.4) - faraday (1.10.0) + excon (0.93.1) + faraday (1.10.2) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -66,7 +66,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.6) - fastlane (2.208.0) + fastlane (2.210.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -106,9 +106,9 @@ GEM xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.25.0) - google-apis-core (>= 0.7, < 2.a) - google-apis-core (0.7.0) + google-apis-androidpublisher_v3 (0.30.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-core (0.9.1) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -117,27 +117,27 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-iamcredentials_v1 (0.13.0) - google-apis-core (>= 0.7, < 2.a) - google-apis-playcustomapp_v1 (0.10.0) - google-apis-core (>= 0.7, < 2.a) - google-apis-storage_v1 (0.18.0) - google-apis-core (>= 0.7, < 2.a) + google-apis-iamcredentials_v1 (0.16.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-playcustomapp_v1 (0.12.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.2.0) - google-cloud-storage (1.37.0) + google-cloud-errors (1.3.0) + google-cloud-storage (1.43.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.2.0) + googleauth (1.3.0) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) @@ -151,7 +151,7 @@ GEM httpclient (2.8.3) jmespath (1.6.1) json (2.6.2) - jwt (2.4.1) + jwt (2.5.0) memoist (0.16.2) mime-types (3.4.1) mime-types-data (~> 3.2015) @@ -166,7 +166,7 @@ GEM optparse (0.1.1) os (1.1.4) plist (3.6.0) - public_suffix (4.0.7) + public_suffix (5.0.0) rake (13.0.6) representable (3.2.0) declarative (< 0.1.0) From 32fdea0206f29f1b790a02cb05dbfb1954bc24c2 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 31 Oct 2022 22:09:24 -0700 Subject: [PATCH 023/103] fix(extension) fires JS event upon editing input fields (#575) --- passExtension/passProcessor.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/passExtension/passProcessor.js b/passExtension/passProcessor.js index 5f9c7461..56da5f74 100644 --- a/passExtension/passProcessor.js +++ b/passExtension/passProcessor.js @@ -1,5 +1,16 @@ var PassProcessor = function() {}; +/** + * Dispatches a synthetic event of a given type on a given element. + * @param {string} type the event type to dispatch + * @param {Element} el the element upon which to dispatch it + */ +var dispatchEvent = function(type, el) { + var evt = document.createEvent('Event'); + evt.initEvent(type, true, true); + el.dispatchEvent(evt); +}; + PassProcessor.prototype = { run: function(arguments) { var url @@ -19,6 +30,8 @@ finalize: function(arguments) { if (passwordElement) { passwordElement.setAttribute('value', arguments["password"]) passwordElement.value = arguments["password"] + dispatchEvent("input", passwordElement) + dispatchEvent("change", passwordElement) } } @@ -27,6 +40,8 @@ finalize: function(arguments) { if (usernameElement) { usernameElement.setAttribute('value', arguments["username"]) usernameElement.value = arguments["username"] + dispatchEvent("input", usernameElement) + dispatchEvent("change", usernameElement) } } } From e2ba21587c37fd0bc354ede3f21254347995d0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 1 Nov 2022 19:47:26 +0100 Subject: [PATCH 024/103] Update SwiftLint to version 0.49.x (#576) --- scripts/swiftlint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/swiftlint.sh b/scripts/swiftlint.sh index 4ef7412b..aefdf847 100755 --- a/scripts/swiftlint.sh +++ b/scripts/swiftlint.sh @@ -1,6 +1,6 @@ export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}" -SWIFTLINT_VERSION="0.47.*" +SWIFTLINT_VERSION="0.49.*" if [[ "${CI}" == "true" ]]; then echo "Running in a Continuous Integration environment. Linting is skipped." From 31c6541f9e66251a5384da25cc2a9458db6e65b1 Mon Sep 17 00:00:00 2001 From: Mattia Borda Date: Mon, 12 Dec 2022 07:08:18 +0100 Subject: [PATCH 025/103] Add Italian translation (#589) - Translate InfoPlist.strings, Intents.strings, and Localizable.stringsdict - Translate Main.strings - Translate Localizable.strings --- pass/it.lproj/InfoPlist.strings | 10 + pass/it.lproj/Intents.strings | 30 +++ pass/it.lproj/Localizable.strings | 348 ++++++++++++++++++++++++++ pass/it.lproj/Localizable.stringsdict | 78 ++++++ pass/it.lproj/Main.strings | 261 +++++++++++++++++++ 5 files changed, 727 insertions(+) create mode 100644 pass/it.lproj/InfoPlist.strings create mode 100644 pass/it.lproj/Intents.strings create mode 100644 pass/it.lproj/Localizable.strings create mode 100644 pass/it.lproj/Localizable.stringsdict create mode 100644 pass/it.lproj/Main.strings diff --git a/pass/it.lproj/InfoPlist.strings b/pass/it.lproj/InfoPlist.strings new file mode 100644 index 00000000..7e575311 --- /dev/null +++ b/pass/it.lproj/InfoPlist.strings @@ -0,0 +1,10 @@ +/* + Localizable.strings + pass + + Created by Danny Moesch on 19.01.19. + Copyright © 2019 Bob Sun. All rights reserved. + */ + +"NSCameraUsageDescription" = "L'accesso alla fotocamera è richiesto per scansionare codici QR."; +"NSFaceIDUsageDescription" = "Attiva l'accesso al Face ID per sbloccare Pass."; diff --git a/pass/it.lproj/Intents.strings b/pass/it.lproj/Intents.strings new file mode 100644 index 00000000..47657278 --- /dev/null +++ b/pass/it.lproj/Intents.strings @@ -0,0 +1,30 @@ +"0r0NKK" = "Impossibile eseguire il push dei cambiamenti al repository remoto."; + +"2jFDLY" = "Sincronizzazione del repository con il server."; + +"H2uxch" = "Nessuna password è salvata per comunicare al repository remoto."; + +"HO0DF8" = "Nessuna password è salvata per comunicare al repository remoto."; + +"IhvhFk" = "Sincronizzazione completata."; + +"JthclV" = "Nessun repository locale."; + +"KFGEkd" = "Sincronizza repository"; + +"O9MV3m" = "La sincronizzazione è fallita."; + +"URQN5E" = "Sincronizzazione completata."; + +"k4AscG" = "Impossibile eseguire il pull dei cambiamenti dal repository remoto."; + +"kbRWJu" = "Impossibile eseguire il pull dei cambiamenti dal repository remoto."; + +"nuN5Fq" = "Sincronizza il repository."; + +"qr6SIW" = "Nessun repository locale."; + +"wB7FJn" = "La sincronizzazione è fallita."; + +"z8bZQR" = "Impossibile eseguire il push dei cambiamenti al repository remoto."; + diff --git a/pass/it.lproj/Localizable.strings b/pass/it.lproj/Localizable.strings new file mode 100644 index 00000000..a1416656 --- /dev/null +++ b/pass/it.lproj/Localizable.strings @@ -0,0 +1,348 @@ +/* + Localizable.strings + pass + + Created by Danny Moesch on 12.01.19. + Copyright © 2019 Bob Sun. All rights reserved. +*/ + +// General +"PassForIos" = "Pass per iOS"; +"PasswordStore" = "Password Store"; +"Passphrase" = "Password"; +"Password" = "Password"; +"Passwords" = "Password"; +"Passcode" = "Codice"; +"Apple" = "Apple"; +"Settings" = "Impostazioni"; +"Contributors" = "Collaboratori"; + +// OTP related +"TimeBased" = "A tempo"; +"HmacBased" = "Basato su HMAC"; +"None" = "Nessuno"; +"ExpiresIn" = "(scade tra %ds)"; +"OTPForPassword" = "One-time password per %@"; +"OTPHasBeenCopied" = "... copiata negli appunti"; +"CopyToPasteboard" = "Copia negli appunti"; +"AutoCopyOTP" = "Copia automaticamente OTP"; +"AutoCopyOTPExplanation." = "Dopo che nome utente e password sono stati riempiti automaticamente da Pass, viene mostrata una notifica con la one-time password, che può essere copiata negli appunti. Con questa opzione attiva la one-time password sarà copiata automaticamente."; + +// General (error) messages +"Error" = "Errore"; +"CannotSave" = "Impossibile salvare"; +"UnresolvedError" = "Errore non risolto %@"; +"MigrationError" = "Errore nella migrazione: %@"; +"UnderlyingError" = "Errore di base: %@"; +"ErrorSaving" = "Errore nel salvataggio: %@"; +"CannotCopyPassword" = "Impossibile copiare la password"; +"CannotAddPassword" = "Impossibile aggiungere la password"; +"WrongPassphrase" = "Password errata"; +"MakeSurePgpAndGitProperlySet." = "Assicurarsi che la chiave PGP e il server Git siano configurati correttamente."; +"RecoverySuggestion." = "Suggerimento: è stata rimossa la password errata, riprovare."; +"NSURLFileAllocatedSizeKeyShouldAlwaysReturnValue." = "Cosa? NSURLFileAllocatedSizeKey Dovrebbe sempre restituire un valore."; +"NoProperPassRepo." = "Il repository non contiene un file .gpg-id. Impostarlo correttamente eseguendo 'pass init', poi riprovare a caricarlo nell'app."; + +// Settings +"PasswordGeneratorFlavor" = "Stile"; +"RememberPgpKeyPassphrase" = "Ricorda la password di PGP"; +"RememberGitCredentialPassphrase" = "Ricorda la password di Git"; +"EnableGPGID" = "Attiva .gpg-id (Sperimentale)"; +"ShowFolders" = "Mostra cartelle"; +"HidePasswordImages" = "Nascondi immagini password"; +"HidePasswordImagesExplanation." = "Le immagini delle password sono caricate e mostrate in base all'URL associato. Attivando questa opzione tali icone non saranno caricate."; +"HideUnknownFields" = "Nascondi campi sconosciuti"; +"HideUnknownFieldsExplanation." = "È supportato solo il formato \"chiave: valore\" per i campi. Ai campi non supportati verrà assegnata la chiave \"Sconosciuto\". Attivando questa opzione non saranno mostrati i campi non supportati."; +"HideOtpFields" = "Nascondi campi OTP"; +"HideOtpFieldsExplanation." = "Attivando questa opzione saranno nascosti i campi relativi alle one time password (cioè %@)."; +"Random" = "Casuale"; +"RandomString" = "Stringa casuale"; +"ApplesKeychainStyle" = "Stile Apple Keychain"; +"XKCD" = "XKCD"; + +// Git +"FailedToFetchPasswords" = "Impossibile raccogliere le password"; +"FailedToFetchPasswordEntities" = "Impossibile raccogliere le password: %@"; +"FailedToInsertPasswordEntity" = "Impossibile aggiungere password: %@"; +"FailedToDeletePasswordEntity" = "Impossibile eliminare password: %@"; +"FailedToSavePasswordEntity" = "Impossibile salvare password: %@"; +"FailureToSaveContext" = "Impossibile salvare contesto: %@"; +"RepositoryRemoteBranchNotFoundError." = "Impossibile trovare il branch remoto %@."; +"RepositoryBranchNotFoundError." = "Il branch %@ non è stato trovato in questo repository."; +"KeyImportError." = "Impossibile importare la chiave."; +"FileNotFoundError." = "Impossibile leggere il file '%@'."; +"PasswordDuplicatedError." = "Impossibile aggiungere la password; la password è duplocata."; +"GitResetError." = "Impossibile identificare l'ultimo commit sincronizzato."; +"GitCreateSignatureError." = "Impossibile creare un autore/committer valido."; +"GitPushNotSuccessfulError." = "Impossibile eseguire il push delle modifiche locali. Assicurarsi che non ci siano modifiche senza commit nel repository remoto."; +"WrongPasswordFilenameError." = "Impossibile scrivere il file della password."; +"DecryptionError." = "Impossibile decriptare la password."; +"EncodingError." = "La chiave non ha una codifica ASCII."; +"UnknownError." = "Errore sconosciuto."; +"PrepareRepository" = "Preparazione del repository"; +"CheckingOutBranch" = "Controllo del branch '%@'"; +"WantToSaveGitCredential?" = "Salvare la password di Git?"; + +// Repository +"RepositoryNotSetError." = "Il repository Git non è impostato."; +"SetGitRepositoryUrl" = "Impostare l'URL del repository Git."; +"CannotFindUsername." = "Impossibile trovare il nome utente nell'URL del repository Git. Esempio di URL: ssh://git@server/path/to/repo.git."; +"CheckEnteredUsername." = "Controllare il nome utente inserito e il nome utente dell'URL del repository Git. Dovrebbero essere uguali."; +"UseEitherHttpsOrHttp." = "Usare HTTPS (consigliato) o HTTP."; +"SpecifySchema." = "Specificare lo schema del'URL del repository Git (HTTPS o SSH)."; +"Overwrite?" = "Sovrascrivere?"; +"Overwrite" = "Sovrascrivi"; +"OperationWillOverwriteData." = "Gli attuali dati di Password Store saranno sovrascritti. I dati del server remoto non subiranno modifiche."; +"DownloadFromUrl" = "Scarica da URL"; +"AsciiArmorEncryptedKey" = "Chiave con armatura ASCII"; +"ITunesFileSharing" = "Condivisione file iTunes"; +"Import" = "Importa"; +"Imported" = "Importata"; +"Tips" = "Consigli"; +"FillInGitAccountPassword." = "Inserire la password dell'account Git."; +"NoPasswordStore." = "Attualmente non c'è alcun dato di Password Store."; +"SyncingPasswordStore" = "Sincronizzazione Password Store"; +"PullingFromRemoteRepository" = "Esecuzione del pull dal repository remoto"; +"PushingToRemoteRepository" = "Esecuzione del push al repository remoto"; +"SpecifyBranchName." = "Specificare il nome del branch da usare."; + +// SSH +"FillInSshKeyPassphrase." = "Inserire la password della chiave SSH."; +"CannotSelectSshKey" = "Impossibile selezionare la chiave SSH"; +"PleaseSetupSshKeyFirst." = "Impostare la chiave SSH."; +"RemoveSShKeys" = "Rimuovi chiavi SSH di Git"; +"SetPrivateKeyUrl." = "Selezionare un URL per la chiave privata."; +"SshCopyPrivateKeyToPass." = "Copiare la chiave privata con armatura ASCII in Pass con il nome \"ssh_key\" (senza virgolette) con iTunes. Poi tornare qui e selezionare \"Condivisione file iTunes\" per completare."; + +// QR code scanning +"LookingForStartingFrame." = "Ricerca del frame iniziale."; +"TooManyQrCodes" = "Troppi codici QR"; +"ScanPrivateKeyQrCodes" = "Scansiona i codici QR della chiave privata"; +"CannotSaveSshKey" = "Impossibile salvare chiave SSH"; +"ScanPublicKey." = "Impossibile scansionare la chiave pubblica."; +"ScanPrivateKey." = "Impossibile scansionare la chiave privata."; +"ScanPrivateKeyQrCodes" = "Scansiona i codici QR della chiave privata"; +"ScanPublicKeyQrCodes" = "Scansiona i codici QR della chiave publica"; +"SetPrivateKey." = "Impostare prima la chiave privata."; +"SetPublicKey." = "Impostare prima la chiave pubblica."; +"NoQrCodeDetected." = "Nessun codice QR rilevato."; +"NoStringValue" = "Nessuna stringa"; +"CameraAccessDenied." = "L'accesso alla fotocamera è bloccato."; +"WarningToggleCameraPermissionsResetsApp." = "ATTENZIONE: Cambiare il permesso della fotocamera reimposta l'app! Salva le modifiche."; + +// PGP +"Decrypting" = "Decriptazione"; +"PgpKeyNotSet." = "La chiave PGP non è impostata. Impostare la chiave PGP."; +"FillInPgpPassphrase." = "Inserire la password della chiave PGP privata."; +"WantToSavePassphrase?" = "Salvare la password?"; +"CannotSavePgpKey" = "Impossibile salvare la chiave PGP"; +"SetPgpKeyUrlsFirst." = "Impostare URL validi per le chiavi PGP."; +"FetchingPgpKey" = "Caricamento della chiave PGP"; +"RememberToRemoveKey" = "Ricorda di eliminare le chiavi"; +"RememberToRemoveKeyFromServer." = "Ricorda di eliminare le chiavi dal server."; +"RemovePgpKeys" = "Elimina chiavi PGP"; +"PgpCopyPublicAndPrivateKeyToPass." = "Copiare la chiave pubblica e quella privata con armatura ASCII in Pass con i nomi \"gpg_key.pub\" e \"gpg_key\" (senza virgolette) con iTunes. Poi tornare qui e selezionare \"Condivisione file iTunes\" per completare."; +"KeyExpiredOrIncompatibleError." = "La chiave PGP pubblica potrebbe essere scaduta o non corrispondente alla chiave privata."; +"WrongPassphraseError." = "La password della chiave PGP segreta è errata."; +"PgpPublicKeyNotFoundError." = "Impossibile trovare la chiave PGP pubblica (%@)."; +"PgpPrivateKeyNotFoundError." = "Impossibile trovare la chiave PGP privata (%@)."; +"CannotImportFile" = "Impossibile importare il file"; +"LoadFromFiles" = "Carica da File"; +"FileCannotBeImported." = "Impossibile importare il file '%@'. Assicurarsi che si abbiano i permessi necessari per leggere la sua posizione."; +"RememberToRemoveKeyFromLocation." = "Ricorda di eliminare i file delle chiavi dalle loro posizioni esterne."; +"KeyFileNotSet." = "Per uno o più tipi di chiavi non è stato selezionato un file."; +"HttpNotSecure" = "HTTP non è sicuro"; +"ReallyUseHttp?" = "Il protocollo HTTP non è sicuro. Usarlo comunque per trasferire il file della chiave privata?"; + +// App passcode +"RemovePasscode" = "Disattiva codice"; +"ChangePasscode" = "Cambia codice"; +"SetPasscode" = "Imposta codice"; +"PasswordConfirmation" = "Conferma con la password"; +"FillInAppPasscode." = "Inserire il codice di Pass (almeno 4 caratteri)."; + +// Git signature +"NotSet" = "Non impostata"; +"InvalidNameOrEmail" = "Nome o email non validi"; + +// Erase password store +"ErasePasswordStoreData?" = "Eliminare i dati di Password Store?"; +"ErasePasswordStoreData" = "Elimina i dati di Password Store"; +"EraseExplanation." = "Verranno eliminati tutti i dati e le impostazioni locali. I dati nel server remoto non saranno modificati."; +"Erasing..." = "Eliminazione ..."; + +// Discard local changes +"DiscardAllLocalChanges?" = "Scartare le modifiche locali?"; +"DiscardAllLocalChanges" = "Scarta le modifiche locali"; +"DiscardExplanation." = "Scartare permanentemente tutte le modifiche effettuate alla copia locale dei dati di Password Store? L'azione è irreversibile."; +"Resetting..." = "Reimpostazione ..."; + +// Forget passcode +"ForgotYourPasscode?" = "Codice dimenticato?"; +"ResetPass" = "Reimposta Pass"; +"ResetPassExplanation." = "L'unica cosa che si può fare è reimpostare l'app. Verranno eliminati tutti i dati e le impostazioni locali. I dati di Password Store nel server remoto non saranno modificati."; + +// Time related +"Unknown" = "Sconosciuto"; +"JustNow" = "Adesso"; + +// Commit messages +"AddPassword." = "Aggiunta password per %@ con Pass per iOS."; +"RemovePassword." = "Rimossa password per %@ con Pass per iOS."; +"EditPassword." = "Modificata password per %@ con Pass per iOS."; +"RenamePassword." = "Rinominato %@ a %@ con Pass per iOS."; + +// Menu buttons +"Ok" = "OK"; +"Cancel" = "Annulla"; +"Dismiss" = "Ignora"; +"Done" = "Fatto"; +"Yes" = "Si"; +"No" = "No"; +"TryAgain" = "Riprova"; +"Delete" = "Elimina"; +"Back" = "Indietro"; +"Current" = "Attuale"; +"All" = "Tutti"; +"On" = "Attivo"; +"Off" = "Disattivato"; +"Save" = "Salva"; +"Remove" = "Elimina"; + +// Lock screen +"EnterPasscode" = "Inserire il codice di Pass"; +"Passcode" = "Codice"; +"TouchId" = "Touch ID"; +"FaceId" = "Face ID"; +"AuthenticationNeeded." = "È necessaria l'autenticazione per accedere a Pass."; + +// About repository +"AboutRepository" = "Informazioni sul repository"; +"ValueNotAvailable" = "Valore non disponibile"; +"Size" = "Dimensione"; +"LocalCommits" = "Commit locali"; +"LastSynced" = "Sincronizzato l'ultima volta"; +"Commits" = "Commit"; +"CommitLogs" = "Registri dei sommit"; +"SyncAgain?" = "Sincronizzare ancora?"; + +// About app +"Website" = "Sito web"; +"Help" = "Aiuto"; +"ContactDeveloper" = "Contatta lo sviluppatore"; +"OpenSourceComponents" = "Componenti open source"; +"SpecialThanks" = "Ringraziamenti speciali"; +"Acknowledgements" = "Riconoscimenti"; +"ValueNotAvailable" = "Valore non disponibile"; + +// External applications +"CannotOpenMail" = "Impossibile aprire l'app Mail"; +"CopiedEmail" = "Email %@ copiata"; +"HttpNotSupported." = "La connessione HTTP non è supportata."; + +// Password view +"Name" = "Nome"; +"Additions" = "Altro"; +"Notice" = "Avviso"; +"PreviousChangesDiscarded." = "Tutte le modifiche precedenti sono state scartate. Verranno mostrate le password attuali."; +"CannotShowPassword" = "Impossibile mostrare la password"; +"PasswordDoesNotExist" = "La password non esiste."; +"Saving" = "Salvataggio"; +"Success" = "Successo"; +"OneTimePassword" = "One-time password"; +"ShowRaw" = "Mostra in originale"; +"Reveal" = "Rivela"; +"Conceal" = "Nascondi"; +"NextPassword" = "Password successiva"; +"CopyAndOpen" = "Copia password e apri link"; +"GetNextPasswordOfNonHotp." = "Ottieni la password successiva per un campo non HOTP."; +"PasswordCopied" = "Password copiata"; +"CounterUpdated" = "Contatore aggiornato"; +"CannotFindValidUrl" = "Impossibile trovare un URL valido"; +"LastUpdated" = "Ultimo aggiornamento: %@"; +"PasswordCopiedToPasteboard." = "Password copiata. Gli appunti saranno cancellati tra 45 secondi."; + +// Password editor +"UseKeyValueFormat." = "Usare il formato \"chiave: valore\" per gli altri campi."; +"DeletePassword" = "Elimina password"; +"AddOneTimePassword" = "Aggiungi one-time password"; +"Length" = "Lunghezza"; +"VaryCases" = "Maiuscole casuali"; +"Digits" = "Cifre"; +"SpecialSymbols" = "Simboli speciali"; +"Groups" = "Organizza in gruppi"; +"DeletePassword?" = "Eliminare la password?"; +"OverwriteOtpConfiguration?" = "Sovrascrivere la configurazione della one-time password?"; +"ValidTokenUrl" = "Token dell'URL valido"; +"InvalidTokenUrl" = "Token dell'URL non valido"; +"FillInName." = "Inserire il nome."; +"RemovePrefix." = "La password non può cominciare con \"/\"."; +"PasswordNameInvalid." = "Il nome della password non è valido."; +"CannotParseFilename." = "Impossibile validare il nome del file. Controllare e semplificare il nome della password."; +"WannaUseIt?" = "Usarlo?"; +"SeemsLikeYouHaveCopiedSomething." = "Sembra che tu abbia copiato qualcosa."; +"FirstStringIs:" = "La prima stringa è:"; + +// Multiline strings +"SshAsciiArmorCopyExplanation." = "Il formato della chiave con armatura ASCII è simile a documenti non codificati e non al formato binario. Usare + + $ cat ~/.ssh/id_rsa + +per ottenere la chiave in questo formato. Gli appunti saranno cancellati 45s dopo aver incollato la chiave."; + +"SshAsciiArmorServerExplanation." = "Il formato della chiave con armatura ASCII è simile a documenti non codificati e non al formato binario. Usare + + $ cat ~/.ssh/id_rsa + +per ottenere la chiave in questo formato. Dopodiché, copiarla nel server delle chiavi sicuro."; + +"SshAsciiArmorFileExplanation." = "Il formato della chiave con armatura ASCII è simile a documenti non codificati e non al formato binario. Usare + + $ cat ~/.ssh/id_rsa + +per ottenere la chiave in questo formato. Dopodiché, copiarla in una posizione a cui File abbia accesso."; + +"GpgAsciiArmorCopyExplanation." = "GnuPG supporta l'opzione da riga di comando \"-a\" per generare le chiavi in un formato con armatura ASCII, simile a documenti non codificati e non al formato binario. Usare + + $ gpg --list-keys + +per identificare il KEY_ID della sottochiave di criptazione (E), poi usare + + $ gpg --export -a KEY_ID! > subkey.pub + $ gpg --export-secret-subkeys -a KEY_ID! > subkey + +per ottenere la sottochiave pubblica e quella privata di criptazione in questo formato. Verificare che nessuna sottochiave di autenticazione sia stata esportata per errore: + + $ gpg --show-key subkey + +Gli appunti saranno cancellati 45s dopo aver incollato."; + +"GpgAsciiArmorServerExplanation." = "GnuPG supporta l'opzione da riga di comando \"-a\" per generare le chiavi in un formato con armatura ASCII, simile a documenti non codificati e non al formato binario. Usare + + $ gpg --list-keys + +per identificare il KEY_ID della sottochiave di criptazione (E), poi usare + + $ gpg --export -a KEY_ID! > subkey.pub + $ gpg --export-secret-subkeys -a KEY_ID! > subkey + +per ottenere la sottochiave pubblica e quella privata di criptazione in questo formato. Verificare che nessuna sottochiave di autenticazione sia stata esportata per errore: + + $ gpg --show-key subkey + +Infine, copiarle nel server HTTP(S) (che può essere locale)."; + +"GpgAsciiArmorFileExplanation." = "GnuPG supporta l'opzione da riga di comando \"-a\" per generare le chiavi in un formato con armatura ASCII, simile a documenti non codificati e non al formato binario. Usare + + $ gpg --list-keys + +per identificare il KEY_ID della sottochiave di criptazione (E), poi usare + + $ gpg --export -a KEY_ID! > subkey.pub + $ gpg --export-secret-subkeys -a KEY_ID! > subkey + +per ottenere la sottochiave pubblica e quella privata di criptazione in questo formato. Verificare che nessuna sottochiave di autenticazione sia stata esportata per errore: + + $ gpg --show-key subkey + +Infine, copiarla in una posizione a cui File abbia accesso."; diff --git a/pass/it.lproj/Localizable.stringsdict b/pass/it.lproj/Localizable.stringsdict new file mode 100644 index 00000000..58659bb8 --- /dev/null +++ b/pass/it.lproj/Localizable.stringsdict @@ -0,0 +1,78 @@ + + + + + ScannedQrCodes(%d) + + NSStringLocalizedFormatKey + %#@codes@ + codes + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + zero + Nessun codice QR scansionato + one + Un codice QR scansionato + other + %d codici QR scansionati + + + DiscardedCommits(%d) + + NSStringLocalizedFormatKey + %#@commits@ + commits + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + zero + Nessun commit locale + one + Un commit scartato + other + %d commit scartati + + + HiddenFields(%d) + + NSStringLocalizedFormatKey + %#@fields@ + fields + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + zero + Nessun campo nascosto + one + Un campo nascosto + other + %d campi nascosti + + + WrongAttempts(%d) + + NSStringLocalizedFormatKey + %#@attempts@ + attempts + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + zero + Nessun tentativo errato + one + Un tentativo errato + other + %d tentativi errati + + + + diff --git a/pass/it.lproj/Main.strings b/pass/it.lproj/Main.strings new file mode 100644 index 00000000..b577f667 --- /dev/null +++ b/pass/it.lproj/Main.strings @@ -0,0 +1,261 @@ + +/* Class = "UITableViewSection"; headerTitle = "ASCII-ARMOR PRIVATE KEY"; ObjectID = "0RP-Jn-j5G"; */ +"0RP-Jn-j5G.headerTitle" = "CHIAVE PRIVATA ARMATURA ASCII"; + +/* Class = "UILabel"; text = "Not Set"; ObjectID = "2qr-d7-0SK"; */ +"2qr-d7-0SK.text" = "Non impostata"; + +/* Class = "UITextField"; placeholder = "Private Key URL"; ObjectID = "4iJ-oB-R1f"; */ +"4iJ-oB-R1f.placeholder" = "URL chiave privata"; + +/* Class = "UITabBarItem"; title = "Settings"; ObjectID = "6Xa-be-Z8g"; */ +"6Xa-be-Z8g.title" = "Impostazioni"; + +/* Class = "UILabel"; text = "Not Set"; ObjectID = "7lc-Vh-G9W"; */ +"7lc-Vh-G9W.text" = "Non impostato"; + +/* Class = "UILabel"; text = "Git Signature"; ObjectID = "87a-xY-AbR"; */ +"87a-xY-AbR.text" = "Firma Git"; + +/* Class = "UILabel"; text = "Bob Sun"; ObjectID = "8Dc-U9-AVf"; */ +"8Dc-U9-AVf.text" = "Bob Sun"; + +/* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "9yM-Mg-Cg8"; */ +"9yM-Mg-Cg8.title" = "Indietro"; + +/* Class = "UILabel"; text = "Select file ..."; ObjectID = "9KE-VA-cx2"; */ +"9KE-VA-cx2.text" = "Scegli file ..."; + +/* Class = "UILabel"; text = "Private Key URL"; ObjectID = "C2w-dd-roS"; */ +"C2w-dd-roS.text" = "URL chiave privata"; + +/* Class = "UINavigationItem"; title = "Password Store"; ObjectID = "Cio-ZG-zCS"; */ +"Cio-ZG-zCS.title" = "Password Store"; + +/* Class = "UITableViewSection"; headerTitle = "SECURITY"; ObjectID = "Cx7-e4-wCO"; */ +"Cx7-e4-wCO.headerTitle" = "SICUREZZA"; + +/* Class = "UINavigationItem"; title = "SSH Key"; ObjectID = "DrT-Wk-L5K"; */ +"DrT-Wk-L5K.title" = "Chiave SSH"; + +/* Class = "UITextField"; placeholder = "Git Repository URL"; ObjectID = "EVT-VU-sCi"; */ +"EVT-VU-sCi.placeholder" = "URL repository Git"; + +/* Class = "UILabel"; text = "Title"; ObjectID = "Eu3-i0-M5v"; */ +"Eu3-i0-M5v.text" = "Titolo"; + +/* Class = "UILabel"; text = "SSH Key"; ObjectID = "Ezz-76-a53"; */ +"Ezz-76-a53.text" = "Chiave SSH"; + +/* Class = "UILabel"; text = "2017/04/04"; ObjectID = "GLC-qL-55P"; */ +"GLC-qL-55P.text" = "04/04/2017"; + +/* Class = "UILabel"; text = "Off"; ObjectID = "HXb-ZX-HUv"; */ +"HXb-ZX-HUv.text" = "Disattivato"; + +/* Class = "UIBarButtonItem"; title = "About"; ObjectID = "HhH-eq-XdK"; */ +"HhH-eq-XdK.title" = "Informazioni"; + +/* Class = "UINavigationItem"; title = "Edit Password"; ObjectID = "Hj9-Iq-kId"; */ +"Hj9-Iq-kId.title" = "Modifica password"; + +/* Class = "UINavigationItem"; title = "Scan OTP QR Codes"; ObjectID = "Hlb-5I-bfE"; */ +"Hlb-5I-bfE.title" = "Scannerizza codici QR OTP"; + +/* Class = "UILabel"; text = "Name"; ObjectID = "Hlb-Zh-ega"; */ +"Hlb-Zh-ega.text" = "Nome"; + +/* Class = "UINavigationItem"; title = "Scan QR Code"; ObjectID = "JIs-3z-Tmr"; */ +"JIs-3z-Tmr.title" = "Scannerizza codice QR"; + +/* Class = "UILabel"; text = "Encrypt in ASCII-Armored"; ObjectID = "Jwg-mt-woS"; */ +"Jwg-mt-woS.text" = "Cripta con armatura ASCII"; + +/* Class = "UILabel"; text = "Erase All Password Store Data"; ObjectID = "K2K-Bx-g7Z"; */ +"K2K-Bx-g7Z.text" = "Elimina tutti i dati di Password Store"; + +/* Class = "UINavigationItem"; title = "Add Password"; ObjectID = "KOg-Gn-Buk"; */ +"KOg-Gn-Buk.title" = "Aggiungi password"; + +/* Class = "UILabel"; text = "Select file ..."; ObjectID = "Ka2-8Z-fwx"; */ +"Ka2-8Z-fwx.text" = "Scegli file ..."; + +/* Class = "UILabel"; text = "Edit password for baidu.com using Pass for iOS."; ObjectID = "L1p-Dm-Mnh"; */ +"L1p-Dm-Mnh.text" = "Modifica password per baidu.com con Pass per iOS."; + +/* Class = "UILabel"; text = "Password"; ObjectID = "LfQ-Af-j2O"; */ +"LfQ-Af-j2O.text" = "Password"; + +/* Class = "UILabel"; text = "ASCII-Armor Keys"; ObjectID = "M32-yr-IfE"; */ +"M32-yr-IfE.text" = "Chiavi armatura ASCII"; + +/* Class = "UILabel"; text = "Advanced"; ObjectID = "MKj-d0-8q3"; */ +"MKj-d0-8q3.text" = "Avanzate"; + +/* Class = "UITableViewSection"; headerTitle = "ASCII-Armor Public Key"; ObjectID = "MZz-mp-v5N"; */ +"MZz-mp-v5N.headerTitle" = "Chiave pubblica armatura ASCII"; + +/* Class = "UILabel"; text = "Not Set"; ObjectID = "Myq-fV-riz"; */ +"Myq-fV-riz.text" = "Non impostata"; + +/* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "New-sD-9Z1"; */ +"New-sD-9Z1.title" = "Indietro"; + +/* Class = "UINavigationItem"; title = "About"; ObjectID = "Pel-JD-Z5x"; */ +"Pel-JD-Z5x.title" = "Informazioni"; + +/* Class = "UILabel"; text = "Private Key URL"; ObjectID = "Qht-RC-Yeg"; */ +"Qht-RC-Yeg.text" = "URL chiave privata"; + +/* Class = "UITableViewSection"; headerTitle = "PRIVATE KEY"; ObjectID = "RFc-J6-hAe"; */ +"RFc-J6-hAe.headerTitle" = "CHIAVE PRIVATA"; + +/* Class = "UILabel"; text = "PGP Key"; ObjectID = "RR9-xr-9ko"; */ +"RR9-xr-9ko.text" = "Chiave PGP"; + +/* Class = "UILabel"; text = "Passcode Lock"; ObjectID = "RaZ-6t-0CU"; */ +"RaZ-6t-0CU.text" = "Codice di blocco"; + +/* Class = "UITextField"; placeholder = "Public Key URL"; ObjectID = "Rb8-zs-TGa"; */ +"Rb8-zs-TGa.placeholder" = "URL chiave pubblica"; + +/* Class = "UILabel"; text = "✓"; ObjectID = "Scc-5J-bu1"; */ +"Scc-5J-bu1.text" = "✓"; + +/* Class = "UITextField"; placeholder = "Username"; ObjectID = "TMg-Gk-7nG"; */ +"TMg-Gk-7nG.placeholder" = "Nome utente"; + +/* Class = "UILabel"; text = "scanner output"; ObjectID = "U8O-Md-w8e"; */ +"U8O-Md-w8e.text" = "Uscita dello scanner"; + +/* Class = "UITableViewSection"; headerTitle = "Branch Name"; ObjectID = "Uoy-58-5ug"; */ +"Uoy-58-5ug.headerTitle" = "Nome branch"; + +/* Class = "UINavigationItem"; title = "PGP Key"; ObjectID = "V4w-cf-d9g"; */ +"V4w-cf-d9g.title" = "Chiave PGP"; + +/* Class = "UITextField"; placeholder = "Branch Name"; ObjectID = "VVI-gJ-e37"; */ +"VVI-gJ-e37.placeholder" = "Nome branch"; + +/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "WH4-7R-4TQ"; */ +"WH4-7R-4TQ.title" = "Impostazioni"; + +/* Class = "UILabel"; text = "Import Keys From Files"; ObjectID = "XU8-Io-n0h"; */ +"XU8-Io-n0h.text" = "Importa chiavi da File"; + +/* Class = "UILabel"; text = "Select file ..."; ObjectID = "XVY-Dj-6Mx"; */ +"XVY-Dj-6Mx.text" = "Scegli file ..."; + +/* Class = "UITableViewSection"; headerTitle = "PUBLIC KEY"; ObjectID = "Y8H-cb-G2j"; */ +"Y8H-cb-G2j.headerTitle" = "CHIAVE PUBBLICA"; + +/* Class = "UITabBarItem"; title = "Settings"; ObjectID = "YLZ-cr-akY"; */ +"YLZ-cr-akY.title" = "Impostazioni"; + +/* Class = "UITextField"; placeholder = "email"; ObjectID = "YY9-za-MNV"; */ +"YY9-za-MNV.placeholder" = "email"; + +/* Class = "UINavigationItem"; title = "General"; ObjectID = "aAM-Iw-iBA"; */ +"aAM-Iw-iBA.title" = "Generali"; + +/* Class = "UINavigationItem"; title = "Recent Commit Log"; ObjectID = "aRM-mj-MAt"; */ +"aRM-mj-MAt.title" = "Log dei commit recenti"; + +/* Class = "UITableViewSection"; headerTitle = "Password Store Data"; ObjectID = "aVR-FE-jMg"; */ +"aVR-FE-jMg.headerTitle" = "Dati di Password Store"; + +/* Class = "UINavigationItem"; title = "Raw Password"; ObjectID = "c13-zM-tLf"; */ +"c13-zM-tLf.title" = "Password in originale"; + +/* Class = "UITextField"; placeholder = "Private Key URL"; ObjectID = "cGJ-1g-Ztc"; */ +"cGJ-1g-Ztc.placeholder" = "URL chiave privata"; + +/* Class = "UILabel"; text = "ASCII-Armor Keys"; ObjectID = "cMp-Iy-NUj"; */ +"cMp-Iy-NUj.text" = "Chiavi armatura ASCII"; + +/* Class = "UILabel"; text = "ASCII-Armor Keys"; ObjectID = "cd7-0l-AZW"; */ +"cd7-0l-AZW.text" = "Chiavi armatura ASCII"; + +/* Class = "UINavigationItem"; title = "About Repository"; ObjectID = "cjL-hB-P6y"; */ +"cjL-hB-P6y.title" = "Informazioni sul repository"; + +/* Class = "UILabel"; text = "General"; ObjectID = "dOt-Rj-vWD"; */ +"dOt-Rj-vWD.text" = "Generali"; + +/* Class = "UILabel"; text = "Public Key URL"; ObjectID = "dWi-eh-7Eq"; */ +"dWi-eh-7Eq.text" = "URL chiave pubblica"; + +/* Class = "UINavigationItem"; title = "Open Source Components"; ObjectID = "duN-f7-Rak"; */ +"duN-f7-Rak.title" = "Componenti open source"; + +/* Class = "UINavigationItem"; title = "PGP Key"; ObjectID = "eK3-bb-419"; */ +"eK3-bb-419.title" = "Chiave PGP"; + +/* Class = "UITableViewSection"; headerTitle = "Username"; ObjectID = "fRu-A2-SCk"; */ +"fRu-A2-SCk.headerTitle" = "Nome utente"; + +/* Class = "UITextField"; placeholder = "name"; ObjectID = "fa8-Vc-w8F"; */ +"fa8-Vc-w8F.placeholder" = "Nome"; + +/* Class = "UITabBarItem"; title = "Passwords"; ObjectID = "gNg-YT-cGW"; */ +"gNg-YT-cGW.title" = "Password"; + +/* Class = "UINavigationItem"; title = "SSH Key"; ObjectID = "gNq-BH-pj8"; */ +"gNq-BH-pj8.title" = "Chiave SSH"; + +/* Class = "UILabel"; text = "Password Repository"; ObjectID = "gWn-ib-STb"; */ +"gWn-ib-STb.text" = "Repository di Password"; + +/* Class = "UINavigationItem"; title = "Git Server"; ObjectID = "gXX-yl-9oj"; */ +"gXX-yl-9oj.title" = "Server Git"; + +/* Class = "UITableViewSection"; headerTitle = "Authentication Method"; ObjectID = "h0N-tI-shZ"; */ +"h0N-tI-shZ.headerTitle" = "Metodo di autenticazione"; + +/* Class = "UILabel"; text = "ASCII-Armor Keys"; ObjectID = "i78-t7-fP9"; */ +"i78-t7-fP9.text" = "Chiavi armatura ASCII"; + +/* Class = "UITableViewSection"; headerTitle = "Git Configuration"; ObjectID = "ihT-OG-HTv"; */ +"ihT-OG-HTv.headerTitle" = "Configurazione Git"; + +/* Class = "UILabel"; text = "scanner output"; ObjectID = "lOI-p4-BGb"; */ +"lOI-p4-BGb.text" = "Uscita dello scanner"; + +/* Class = "UILabel"; text = "Email"; ObjectID = "m25-Qy-XwU"; */ +"m25-Qy-XwU.text" = "Email"; + +/* Class = "UIViewController"; title = "Raw Password"; ObjectID = "mlB-OK-hLo"; */ +"mlB-OK-hLo.title" = "Password in originale"; + +/* Class = "UINavigationItem"; title = "Special Thanks"; ObjectID = "oKe-r8-1EF"; */ +"oKe-r8-1EF.title" = "Ringraziamenti speciali"; + +/* Class = "UILabel"; text = "About"; ObjectID = "oqz-Hr-RAl"; */ +"oqz-Hr-RAl.text" = "Informazioni"; + +/* Class = "UINavigationItem"; title = "Git Signature"; ObjectID = "pPi-jd-x5U"; */ +"pPi-jd-x5U.title" = "Firma Git"; + +/* Class = "UITableViewSection"; headerTitle = "Git Repository URL"; ObjectID = "pbe-W6-w4V"; */ +"pbe-W6-w4V.headerTitle" = "URL repository Git"; + +/* Class = "UIBarButtonItem"; title = "Clone"; ObjectID = "sgQ-zB-rxv"; */ +"sgQ-zB-rxv.title" = "Clona"; + +/* Class = "UITableViewSection"; headerTitle = "ASCII-ARMOR PRIVATE KEY"; ObjectID = "sxk-Yb-Y3x"; */ +"sxk-Yb-Y3x.headerTitle" = "CHIAVE PRIVATA ARMATURA ASCII"; + +/* Class = "UINavigationItem"; title = "Advanced"; ObjectID = "tc7-wf-hG7"; */ +"tc7-wf-hG7.title" = "Avanzate"; + +/* Class = "UITableViewSection"; headerTitle = "GPG Configuration"; ObjectID = "ugP-R2-9M7"; */ +"ugP-R2-9M7.headerTitle" = "Configurazione GPG"; + +/* Class = "UINavigationItem"; title = "PGP Key"; ObjectID = "waZ-gh-rQt"; */ +"waZ-gh-rQt.title" = "Chiave PGP"; + +/* Class = "UILabel"; text = "✓"; ObjectID = "wbx-rk-i8H"; */ +"wbx-rk-i8H.text" = "✓"; + +/* Class = "UILabel"; text = "Discard All Local Changes"; ObjectID = "zrl-v3-fxg"; */ +"zrl-v3-fxg.text" = "Scarta tutte le modifiche locali"; From 51ad0c2920098beca328d3a033fc7156c3d8bee8 Mon Sep 17 00:00:00 2001 From: Tony Wang Date: Fri, 20 Jan 2023 03:39:05 +0800 Subject: [PATCH 026/103] fix #540, duplicate entries in auto fill extension (#593) * fix #540, duplicate entries in auto fill extension `prepareCredentialList` is called twice, once before unlock and once after. If the lists are already prepared, i.e. not empty, skip this step. * fix format and lint --- .../GitRepositorySettingsTableViewController.swift | 1 + pass/Controllers/QRScannerController.swift | 9 +++++---- pass/Services/PasswordDecryptor.swift | 2 +- .../Services/PasswordsTableDataSource.swift | 4 ++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pass/Controllers/GitRepositorySettingsTableViewController.swift b/pass/Controllers/GitRepositorySettingsTableViewController.swift index e37c7c48..25411165 100644 --- a/pass/Controllers/GitRepositorySettingsTableViewController.swift +++ b/pass/Controllers/GitRepositorySettingsTableViewController.swift @@ -172,6 +172,7 @@ class GitRepositorySettingsTableViewController: UITableViewController, PasswordA // Remember git credential password/passphrase temporarily, ask whether users want this after a successful clone. Defaults.isRememberGitCredentialPassphraseOn = true + // swiftlint:disable:next closure_body_length DispatchQueue.global(qos: .userInitiated).async { do { let transferProgressBlock: (UnsafePointer, UnsafeMutablePointer) -> Void = { git_transfer_progress, _ in diff --git a/pass/Controllers/QRScannerController.swift b/pass/Controllers/QRScannerController.swift index 4693d663..cd9bc1b0 100644 --- a/pass/Controllers/QRScannerController.swift +++ b/pass/Controllers/QRScannerController.swift @@ -140,11 +140,12 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg preferredStyle: .alert ) alertController.addAction(UIAlertAction(title: "Cancel".localize(), style: .default)) - alertController.addAction(UIAlertAction(title: "Settings".localize(), style: .cancel) { _ in - if let url = URL(string: UIApplication.openSettingsURLString) { - UIApplication.shared.open(url) + alertController.addAction( + UIAlertAction(title: "Settings".localize(), style: .cancel) { _ in + if let url = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(url) + } } - } ) present(alertController, animated: true) } diff --git a/pass/Services/PasswordDecryptor.swift b/pass/Services/PasswordDecryptor.swift index 0e0675a1..c97a7c22 100644 --- a/pass/Services/PasswordDecryptor.swift +++ b/pass/Services/PasswordDecryptor.swift @@ -192,7 +192,7 @@ public func yubiKeyDecrypt( extension Data { struct HexEncodingOptions: OptionSet { let rawValue: Int - static let upperCase = HexEncodingOptions(rawValue: 1 << 0) + static let upperCase = Self(rawValue: 1 << 0) } func hexEncodedString(options: HexEncodingOptions = []) -> String { diff --git a/passAutoFillExtension/Services/PasswordsTableDataSource.swift b/passAutoFillExtension/Services/PasswordsTableDataSource.swift index f59bde49..ab4525b4 100644 --- a/passAutoFillExtension/Services/PasswordsTableDataSource.swift +++ b/passAutoFillExtension/Services/PasswordsTableDataSource.swift @@ -72,6 +72,10 @@ class PasswordsTableDataSource: NSObject, UITableViewDataSource { return } + guard suggestedPasswordsTableEntries.isEmpty, otherPasswordsTableEntries.isEmpty else { + return + } + for entry in passwordTableEntries { if entry.matches(text) { suggestedPasswordsTableEntries.append(entry) From 3ab334dd00401d087d9f6618c9bfce8570f54ea7 Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Sat, 21 Jan 2023 16:52:46 +0100 Subject: [PATCH 027/103] fix #488 - support user field for completion - a few other backends use `user` instead of `username` or `login` (e.g. gopass-jsonapi, keepass2csv importer, fpm2 importer) - shorter to type when extracting field separately (e.g. CLI/clipboard) tools --- pass/Controllers/PasswordDetailTableViewController.swift | 3 +++ passKit/Models/Password.swift | 7 ++++++- passKit/Parser/Constants.swift | 1 + passKitTests/Models/PasswordTest.swift | 5 ++++- passKitTests/Testbase/TestBase.swift | 1 + 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 90fdf8a3..804c614e 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -250,6 +250,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni if let username = password.username { section.item.append(Constants.USERNAME_KEYWORD => username) } + if let user = password.username { + section.item.append(Constants.USER_KEYWORD => user) + } if let login = password.login { section.item.append(Constants.LOGIN_KEYWORD => login) } diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index 9a2b03f2..a9dab8c8 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -51,6 +51,10 @@ public class Password { getAdditionValue(withKey: Constants.USERNAME_KEYWORD) } + public var user: String? { + getAdditionValue(withKey: Constants.USER_KEYWORD) + } + public var login: String? { getAdditionValue(withKey: Constants.LOGIN_KEYWORD) } @@ -121,6 +125,7 @@ public class Password { additions.filter { field in let title = field.title.lowercased() return title != Constants.USERNAME_KEYWORD + && title != Constants.USER_KEYWORD && title != Constants.LOGIN_KEYWORD && title != Constants.PASSWORD_KEYWORD && (!Constants.isUnknown(title) || !Defaults.isHideUnknownOn) @@ -215,6 +220,6 @@ public class Password { } public func getUsernameForCompletion() -> String { - username ?? login ?? nameFromPath ?? "" + username ?? user ?? login ?? nameFromPath ?? "" } } diff --git a/passKit/Parser/Constants.swift b/passKit/Parser/Constants.swift index 81f8bffe..42d99dd1 100644 --- a/passKit/Parser/Constants.swift +++ b/passKit/Parser/Constants.swift @@ -48,6 +48,7 @@ public enum Constants { public static let PASSWORD_KEYWORD = "password" public static let USERNAME_KEYWORD = "username" + public static let USER_KEYWORD = "user" public static let LOGIN_KEYWORD = "login" public static let URL_KEYWORD = "url" public static let UNKNOWN = "unknown" diff --git a/passKitTests/Models/PasswordTest.swift b/passKitTests/Models/PasswordTest.swift index dddc5825..6ee05a03 100644 --- a/passKitTests/Models/PasswordTest.swift +++ b/passKitTests/Models/PasswordTest.swift @@ -32,6 +32,7 @@ class PasswordTest: XCTestCase { XCTAssertEqual(password.numberOfUnknowns, 0) XCTAssertNil(password.username) + XCTAssertNil(password.user) XCTAssertNil(password.urlString) XCTAssertNil(password.login) @@ -53,7 +54,7 @@ class PasswordTest: XCTestCase { } func testSimplePasswordFile() { - let additions = SECURE_URL_FIELD | LOGIN_FIELD | USERNAME_FIELD | NOTE_FIELD + let additions = SECURE_URL_FIELD | LOGIN_FIELD | USERNAME_FIELD | USER_FIELD | NOTE_FIELD let fileContent = PASSWORD_STRING | additions let password = getPasswordObjectWith(content: fileContent) @@ -62,11 +63,13 @@ class PasswordTest: XCTestCase { XCTAssert(SECURE_URL_FIELD ∈ password) XCTAssert(LOGIN_FIELD ∉ password) XCTAssert(USERNAME_FIELD ∉ password) + XCTAssert(USER_FIELD ∉ password) XCTAssert(NOTE_FIELD ∈ password) XCTAssertEqual(password.urlString, SECURE_URL_FIELD.content) XCTAssertEqual(password.login, LOGIN_FIELD.content) XCTAssertEqual(password.username, USERNAME_FIELD.content) + XCTAssertEqual(password.user, USER_FIELD.content) } func testTwoPasswords() { diff --git a/passKitTests/Testbase/TestBase.swift b/passKitTests/Testbase/TestBase.swift index 5c816cb0..cae542d6 100644 --- a/passKitTests/Testbase/TestBase.swift +++ b/passKitTests/Testbase/TestBase.swift @@ -22,6 +22,7 @@ let SECURE_URL_FIELD = "url" => "https://secure.com" let INSECURE_URL_FIELD = "url" => "http://insecure.com" let LOGIN_FIELD = "login" => "login name" let USERNAME_FIELD = "username" => "微 分 方 程" +let USER_FIELD = "user" => "积 分 方 程" let NOTE_FIELD = "note" => "A NOTE" let HINT_FIELD = "some hints" => "äöüß // €³ %% −° && @²` | [{\\}],.<>" let TOTP_URL_FIELD = "otpauth" => "//totp/email@email.com?secret=abcd1234" From f6f862a6b06ef2970d5a8ea5db6e1f2740654f9f Mon Sep 17 00:00:00 2001 From: Tony Wang Date: Thu, 26 Jan 2023 23:00:32 +0800 Subject: [PATCH 028/103] refs #584, add operation to clear QuickType suggestion in settings --- pass/Base.lproj/Main.storyboard | 26 +++++++++++++++++-- .../AdvancedSettingsTableViewController.swift | 12 +++++++++ pass/en.lproj/Localizable.strings | 5 +++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/pass/Base.lproj/Main.storyboard b/pass/Base.lproj/Main.storyboard index 3a7afc73..8e140fd2 100644 --- a/pass/Base.lproj/Main.storyboard +++ b/pass/Base.lproj/Main.storyboard @@ -1141,17 +1141,38 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f + + + + + + + + + + + + + + + - + - + - + - + - +