Initial implementation of using YubiKey for decryption (#533)
This commit is contained in:
parent
13804b79e6
commit
955e50c3d3
23 changed files with 606 additions and 118 deletions
66
Gemfile.lock
66
Gemfile.lock
|
|
@ -2,16 +2,8 @@ GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.2)
|
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)
|
addressable (2.8.0)
|
||||||
public_suffix (>= 2.0.2, < 5.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)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.1.0)
|
aws-eventstream (1.1.0)
|
||||||
aws-partitions (1.345.0)
|
aws-partitions (1.345.0)
|
||||||
|
|
@ -31,48 +23,10 @@ GEM
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
babosa (1.0.3)
|
babosa (1.0.3)
|
||||||
claide (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)
|
colored (1.2)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
commander-fastlane (4.4.6)
|
commander-fastlane (4.4.6)
|
||||||
highline (~> 1.7.2)
|
highline (~> 1.7.2)
|
||||||
concurrent-ruby (1.1.7)
|
|
||||||
declarative (0.0.20)
|
declarative (0.0.20)
|
||||||
declarative-option (0.1.0)
|
declarative-option (0.1.0)
|
||||||
digest-crc (0.6.1)
|
digest-crc (0.6.1)
|
||||||
|
|
@ -81,9 +35,6 @@ GEM
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
dotenv (2.7.6)
|
dotenv (2.7.6)
|
||||||
emoji_regex (3.0.0)
|
emoji_regex (3.0.0)
|
||||||
escape (0.0.4)
|
|
||||||
ethon (0.12.0)
|
|
||||||
ffi (>= 1.3.0)
|
|
||||||
excon (0.75.0)
|
excon (0.75.0)
|
||||||
faraday (1.0.1)
|
faraday (1.0.1)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
|
|
@ -129,9 +80,6 @@ GEM
|
||||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||||
xcpretty (~> 0.3.0)
|
xcpretty (~> 0.3.0)
|
||||||
xcpretty-travis-formatter (>= 0.0.3)
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
ffi (1.13.1)
|
|
||||||
fourflusher (2.3.1)
|
|
||||||
fuzzy_match (2.0.4)
|
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-api-client (0.38.0)
|
google-api-client (0.38.0)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
|
|
@ -166,8 +114,6 @@ GEM
|
||||||
http-cookie (1.0.3)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (0.9.5)
|
|
||||||
concurrent-ruby (~> 1.0)
|
|
||||||
jmespath (1.4.0)
|
jmespath (1.4.0)
|
||||||
json (2.3.1)
|
json (2.3.1)
|
||||||
jwt (2.2.1)
|
jwt (2.2.1)
|
||||||
|
|
@ -177,12 +123,9 @@ GEM
|
||||||
mime-types-data (3.2020.0512)
|
mime-types-data (3.2020.0512)
|
||||||
mini_magick (4.10.1)
|
mini_magick (4.10.1)
|
||||||
mini_mime (1.0.2)
|
mini_mime (1.0.2)
|
||||||
minitest (5.14.2)
|
|
||||||
molinillo (0.6.6)
|
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.0.0)
|
multipart-post (2.0.0)
|
||||||
nanaimo (0.3.0)
|
nanaimo (0.3.0)
|
||||||
nap (1.1.0)
|
|
||||||
naturally (2.2.0)
|
naturally (2.2.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
os (1.1.0)
|
os (1.1.0)
|
||||||
|
|
@ -200,7 +143,6 @@ GEM
|
||||||
netrc (~> 0.8)
|
netrc (~> 0.8)
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rouge (2.0.7)
|
rouge (2.0.7)
|
||||||
ruby-macho (1.4.0)
|
|
||||||
rubyzip (2.3.0)
|
rubyzip (2.3.0)
|
||||||
security (0.1.3)
|
security (0.1.3)
|
||||||
signet (0.14.0)
|
signet (0.14.0)
|
||||||
|
|
@ -215,15 +157,10 @@ GEM
|
||||||
terminal-notifier (2.0.0)
|
terminal-notifier (2.0.0)
|
||||||
terminal-table (1.8.0)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
thread_safe (0.3.6)
|
|
||||||
tty-cursor (0.7.1)
|
tty-cursor (0.7.1)
|
||||||
tty-screen (0.8.1)
|
tty-screen (0.8.1)
|
||||||
tty-spinner (0.9.3)
|
tty-spinner (0.9.3)
|
||||||
tty-cursor (~> 0.7)
|
tty-cursor (~> 0.7)
|
||||||
typhoeus (1.4.0)
|
|
||||||
ethon (>= 0.9.0)
|
|
||||||
tzinfo (1.2.8)
|
|
||||||
thread_safe (~> 0.1)
|
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
|
|
@ -245,10 +182,9 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
cocoapods
|
|
||||||
fastlane
|
fastlane
|
||||||
rest-client
|
rest-client
|
||||||
xcodeproj
|
xcodeproj
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.1.4
|
2.2.25
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,10 @@
|
||||||
9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE426E5D1CE0052028E /* OneTimePassword */; };
|
9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE426E5D1CE0052028E /* OneTimePassword */; };
|
||||||
9A1D1CE726E5D2230052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE626E5D2230052028E /* OneTimePassword */; };
|
9A1D1CE726E5D2230052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE626E5D2230052028E /* OneTimePassword */; };
|
||||||
9A1F47FA26E5CF4B000C0E01 /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1F47F926E5CF4B000C0E01 /* 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 */; };
|
9A55C158259E785600FA8FD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDD1E2E8231000FDF54 /* Assets.xcassets */; };
|
||||||
9A55C15F259E785700FA8FD9 /* 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 */; };
|
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 */; };
|
9A58662925AAAA79006719C2 /* PasswordSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */; };
|
||||||
9A58664825AAAB7E006719C2 /* SearchPassword.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A5865EF25AA944B006719C2 /* SearchPassword.storyboard */; };
|
9A58664825AAAB7E006719C2 /* SearchPassword.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A5865EF25AA944B006719C2 /* SearchPassword.storyboard */; };
|
||||||
9A58665125AADB76006719C2 /* CredentialProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58665025AADB76006719C2 /* CredentialProvider.swift */; };
|
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 */; };
|
9A5D06EE25A56F0800FA59D4 /* PasswordTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EE1259EDD520027CE15 /* PasswordTableViewCell.swift */; };
|
||||||
9A5D06F525A56F0E00FA59D4 /* 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 */; };
|
9A5D070225A5769A00FA59D4 /* PasswordTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EE1259EDD520027CE15 /* PasswordTableViewCell.swift */; };
|
||||||
9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */; };
|
9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */; };
|
||||||
9A74D2E0277D2F8C00F7BC44 /* UIAlertControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A74D2DF277D2F8C00F7BC44 /* UIAlertControllerExtension.swift */; };
|
9A74D2E0277D2F8C00F7BC44 /* UIAlertControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A74D2DF277D2F8C00F7BC44 /* UIAlertControllerExtension.swift */; };
|
||||||
9A78A7CC277BECE80093222D /* SVProgressHUD.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30F6C1B327664C7200BE5AB2 /* SVProgressHUD.xcframework */; };
|
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 */; };
|
9A8F9EBD259EA4C50027CE15 /* PasswordsTableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EBC259EA4C50027CE15 /* PasswordsTableDataSource.swift */; };
|
||||||
9A8F9ECC259ECB410027CE15 /* PasswordSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */; };
|
9A8F9ECC259ECB410027CE15 /* PasswordSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */; };
|
||||||
9A8F9F4025A1A91F0027CE15 /* CredentialProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9F3F25A1A91F0027CE15 /* CredentialProvider.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, ); }; };
|
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, ); }; };
|
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 */; };
|
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 */; };
|
9ADAB21D26DDA52400900F10 /* Gopenpgp.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */; };
|
||||||
9ADC954124418A5F0005402E /* PasswordStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */; };
|
9ADC954124418A5F0005402E /* PasswordStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */; };
|
||||||
9AFC87D325B39FF3008D6060 /* PasswordNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFC87D225B39FF2008D6060 /* PasswordNavigationViewController.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 = "<group>"; };
|
9A1EF0B424C50E780074FEAC /* passBetaAutoFillExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaAutoFillExtension.entitlements; sourceTree = "<group>"; };
|
||||||
9A1EF0B524C50EE00074FEAC /* passBetaExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaExtension.entitlements; sourceTree = "<group>"; };
|
9A1EF0B524C50EE00074FEAC /* passBetaExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaExtension.entitlements; sourceTree = "<group>"; };
|
||||||
9A1EF0B624C50FEA0074FEAC /* passBetaShortcuts.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaShortcuts.entitlements; sourceTree = "<group>"; };
|
9A1EF0B624C50FEA0074FEAC /* passBetaShortcuts.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaShortcuts.entitlements; sourceTree = "<group>"; };
|
||||||
|
9A2C7D832783FF5200BD9AF3 /* YubiKeyConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YubiKeyConnection.swift; sourceTree = "<group>"; };
|
||||||
|
9A2C7D8A2784139200BD9AF3 /* YubiKeyAPDU.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YubiKeyAPDU.swift; sourceTree = "<group>"; };
|
||||||
9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordsViewController.swift; sourceTree = "<group>"; };
|
9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordsViewController.swift; sourceTree = "<group>"; };
|
||||||
9A5865EF25AA944B006719C2 /* SearchPassword.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SearchPassword.storyboard; sourceTree = "<group>"; };
|
9A5865EF25AA944B006719C2 /* SearchPassword.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SearchPassword.storyboard; sourceTree = "<group>"; };
|
||||||
9A58665025AADB76006719C2 /* CredentialProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProvider.swift; sourceTree = "<group>"; };
|
9A58665025AADB76006719C2 /* CredentialProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProvider.swift; sourceTree = "<group>"; };
|
||||||
|
9A5C6EEF2786C8710003F340 /* AlertPresenting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresenting.swift; sourceTree = "<group>"; };
|
||||||
9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertActionExtension.swift; sourceTree = "<group>"; };
|
9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertActionExtension.swift; sourceTree = "<group>"; };
|
||||||
9A74D2DF277D2F8C00F7BC44 /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertControllerExtension.swift; sourceTree = "<group>"; };
|
9A74D2DF277D2F8C00F7BC44 /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertControllerExtension.swift; sourceTree = "<group>"; };
|
||||||
9A8F9EBC259EA4C50027CE15 /* PasswordsTableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordsTableDataSource.swift; sourceTree = "<group>"; };
|
9A8F9EBC259EA4C50027CE15 /* PasswordsTableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordsTableDataSource.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -506,8 +519,10 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
9A5C6EF92786CE170003F340 /* YubiKit in Frameworks */,
|
||||||
9A996C6E26DEB99200A4485D /* passKit.framework in Frameworks */,
|
9A996C6E26DEB99200A4485D /* passKit.framework in Frameworks */,
|
||||||
30A3001A26DA697C002A734E /* SwiftyUserDefaults in Frameworks */,
|
30A3001A26DA697C002A734E /* SwiftyUserDefaults in Frameworks */,
|
||||||
|
9A5C6F042787F09D0003F340 /* Gopenpgp.xcframework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -521,6 +536,7 @@
|
||||||
9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */,
|
9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */,
|
||||||
30A3001626DA6697002A734E /* SwiftyUserDefaults in Frameworks */,
|
30A3001626DA6697002A734E /* SwiftyUserDefaults in Frameworks */,
|
||||||
3032DA5626DAF4E500A7728C /* ObjectivePGP in Frameworks */,
|
3032DA5626DAF4E500A7728C /* ObjectivePGP in Frameworks */,
|
||||||
|
9A2C7D862783FF9600BD9AF3 /* YubiKit in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -539,7 +555,10 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
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;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -560,6 +579,7 @@
|
||||||
9A996C5326DDF61F00A4485D /* Base32 in Frameworks */,
|
9A996C5326DDF61F00A4485D /* Base32 in Frameworks */,
|
||||||
9A78A7CC277BECE80093222D /* SVProgressHUD.xcframework in Frameworks */,
|
9A78A7CC277BECE80093222D /* SVProgressHUD.xcframework in Frameworks */,
|
||||||
3032DA5426DAF4C200A7728C /* ObjectivePGP in Frameworks */,
|
3032DA5426DAF4C200A7728C /* ObjectivePGP in Frameworks */,
|
||||||
|
9A2C7D822782CB2F00BD9AF3 /* YubiKit in Frameworks */,
|
||||||
3010CB6626DA500F008964D2 /* KeychainAccess in Frameworks */,
|
3010CB6626DA500F008964D2 /* KeychainAccess in Frameworks */,
|
||||||
9A996C5826DEB0D100A4485D /* passKit.framework in Frameworks */,
|
9A996C5826DEB0D100A4485D /* passKit.framework in Frameworks */,
|
||||||
30ED1777276F8842009BA876 /* ObjectiveGit in Frameworks */,
|
30ED1777276F8842009BA876 /* ObjectiveGit in Frameworks */,
|
||||||
|
|
@ -724,6 +744,14 @@
|
||||||
path = Services;
|
path = Services;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
9A5C6EF32786C9C00003F340 /* Protocols */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9A5C6EEF2786C8710003F340 /* AlertPresenting.swift */,
|
||||||
|
);
|
||||||
|
path = Protocols;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9A8F9EBB259EA4A80027CE15 /* Services */ = {
|
9A8F9EBB259EA4A80027CE15 /* Services */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -806,6 +834,7 @@
|
||||||
A26075791EEC6F34005DB03E /* passKit */ = {
|
A26075791EEC6F34005DB03E /* passKit */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
9A5C6EF32786C9C00003F340 /* Protocols */,
|
||||||
A2C532B9201DD07500DB9F53 /* Controllers */,
|
A2C532B9201DD07500DB9F53 /* Controllers */,
|
||||||
30CCA90C232584560048CA51 /* Crypto */,
|
30CCA90C232584560048CA51 /* Crypto */,
|
||||||
30B6AABA21F49095006B352D /* Extensions */,
|
30B6AABA21F49095006B352D /* Extensions */,
|
||||||
|
|
@ -877,6 +906,8 @@
|
||||||
A2F4E20F1EED7F0A0011986E /* Helpers */ = {
|
A2F4E20F1EED7F0A0011986E /* Helpers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
9A2C7D832783FF5200BD9AF3 /* YubiKeyConnection.swift */,
|
||||||
|
9A2C7D8A2784139200BD9AF3 /* YubiKeyAPDU.swift */,
|
||||||
30697C2921F63C590064FCAC /* AppError.swift */,
|
30697C2921F63C590064FCAC /* AppError.swift */,
|
||||||
302B2C9722C2BDE700D831EE /* AppKeychain.swift */,
|
302B2C9722C2BDE700D831EE /* AppKeychain.swift */,
|
||||||
3087574E2343E42A00B971A2 /* Colors.swift */,
|
3087574E2343E42A00B971A2 /* Colors.swift */,
|
||||||
|
|
@ -1075,6 +1106,7 @@
|
||||||
name = passAutoFillExtension;
|
name = passAutoFillExtension;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
30A3001926DA697C002A734E /* SwiftyUserDefaults */,
|
30A3001926DA697C002A734E /* SwiftyUserDefaults */,
|
||||||
|
9A5C6EF82786CE170003F340 /* YubiKit */,
|
||||||
);
|
);
|
||||||
productName = passAutoFillExtension;
|
productName = passAutoFillExtension;
|
||||||
productReference = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */;
|
productReference = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */;
|
||||||
|
|
@ -1100,6 +1132,7 @@
|
||||||
3032DA5526DAF4E500A7728C /* ObjectivePGP */,
|
3032DA5526DAF4E500A7728C /* ObjectivePGP */,
|
||||||
9A996C5626DDF65900A4485D /* Base32 */,
|
9A996C5626DDF65900A4485D /* Base32 */,
|
||||||
9A1D1CE426E5D1CE0052028E /* OneTimePassword */,
|
9A1D1CE426E5D1CE0052028E /* OneTimePassword */,
|
||||||
|
9A2C7D852783FF9600BD9AF3 /* YubiKit */,
|
||||||
);
|
);
|
||||||
productName = passKit;
|
productName = passKit;
|
||||||
productReference = A26075781EEC6F34005DB03E /* passKit.framework */;
|
productReference = A26075781EEC6F34005DB03E /* passKit.framework */;
|
||||||
|
|
@ -1142,6 +1175,10 @@
|
||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = passExtension;
|
name = passExtension;
|
||||||
|
packageProductDependencies = (
|
||||||
|
9A5C6EFA2786CE5E0003F340 /* YubiKit */,
|
||||||
|
9A5C6F072787F0C20003F340 /* SwiftyUserDefaults */,
|
||||||
|
);
|
||||||
productName = passExtension;
|
productName = passExtension;
|
||||||
productReference = A26700241EEC466A00176B8A /* passExtension.appex */;
|
productReference = A26700241EEC466A00176B8A /* passExtension.appex */;
|
||||||
productType = "com.apple.product-type.app-extension";
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
|
@ -1197,6 +1234,7 @@
|
||||||
9A996C5226DDF61F00A4485D /* Base32 */,
|
9A996C5226DDF61F00A4485D /* Base32 */,
|
||||||
9A1F47F926E5CF4B000C0E01 /* OneTimePassword */,
|
9A1F47F926E5CF4B000C0E01 /* OneTimePassword */,
|
||||||
30ED1776276F8842009BA876 /* ObjectiveGit */,
|
30ED1776276F8842009BA876 /* ObjectiveGit */,
|
||||||
|
9A2C7D812782CB2F00BD9AF3 /* YubiKit */,
|
||||||
);
|
);
|
||||||
productName = pass;
|
productName = pass;
|
||||||
productReference = DC917BD31E2E8231000FDF54 /* Pass.app */;
|
productReference = DC917BD31E2E8231000FDF54 /* Pass.app */;
|
||||||
|
|
@ -1300,6 +1338,7 @@
|
||||||
3032DA5226DAF4C200A7728C /* XCRemoteSwiftPackageReference "ObjectivePGP" */,
|
3032DA5226DAF4C200A7728C /* XCRemoteSwiftPackageReference "ObjectivePGP" */,
|
||||||
9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */,
|
9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */,
|
||||||
30ED1775276F8842009BA876 /* XCRemoteSwiftPackageReference "objective-git-swift-package" */,
|
30ED1775276F8842009BA876 /* XCRemoteSwiftPackageReference "objective-git-swift-package" */,
|
||||||
|
9A2C7D802782CB2F00BD9AF3 /* XCRemoteSwiftPackageReference "yubikit-ios" */,
|
||||||
);
|
);
|
||||||
productRefGroup = DC917BD41E2E8231000FDF54 /* Products */;
|
productRefGroup = DC917BD41E2E8231000FDF54 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
|
@ -1491,6 +1530,7 @@
|
||||||
30697C3C21F63C990064FCAC /* UITextFieldExtension.swift in Sources */,
|
30697C3C21F63C990064FCAC /* UITextFieldExtension.swift in Sources */,
|
||||||
302E85632125EE550031BA64 /* Constants.swift in Sources */,
|
302E85632125EE550031BA64 /* Constants.swift in Sources */,
|
||||||
9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */,
|
9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */,
|
||||||
|
9A2C7D842783FF5200BD9AF3 /* YubiKeyConnection.swift in Sources */,
|
||||||
301F6463216162550071A4CE /* AdditionField.swift in Sources */,
|
301F6463216162550071A4CE /* AdditionField.swift in Sources */,
|
||||||
30697C3021F63C5A0064FCAC /* AppError.swift in Sources */,
|
30697C3021F63C5A0064FCAC /* AppError.swift in Sources */,
|
||||||
30697C2B21F63C5A0064FCAC /* Globals.swift in Sources */,
|
30697C2B21F63C5A0064FCAC /* Globals.swift in Sources */,
|
||||||
|
|
@ -1513,6 +1553,7 @@
|
||||||
30B4C7BA24084AAA008B86F7 /* PasswordGenerator.swift in Sources */,
|
30B4C7BA24084AAA008B86F7 /* PasswordGenerator.swift in Sources */,
|
||||||
30A1D2A621B2D46100E2D1F7 /* OTPType.swift in Sources */,
|
30A1D2A621B2D46100E2D1F7 /* OTPType.swift in Sources */,
|
||||||
3032328E22CBD4CD009EBD9C /* CryptographicKeys.swift in Sources */,
|
3032328E22CBD4CD009EBD9C /* CryptographicKeys.swift in Sources */,
|
||||||
|
9A5C6EF42786CA5F0003F340 /* AlertPresenting.swift in Sources */,
|
||||||
30697C2A21F63C5A0064FCAC /* NotificationNames.swift in Sources */,
|
30697C2A21F63C5A0064FCAC /* NotificationNames.swift in Sources */,
|
||||||
30CCA91623258C380048CA51 /* PGPInterface.swift in Sources */,
|
30CCA91623258C380048CA51 /* PGPInterface.swift in Sources */,
|
||||||
30DAFD4A240985A7002456E7 /* Array+Slices.swift in Sources */,
|
30DAFD4A240985A7002456E7 /* Array+Slices.swift in Sources */,
|
||||||
|
|
@ -1527,6 +1568,7 @@
|
||||||
30697C3A21F63C990064FCAC /* UIViewControllerExtension.swift in Sources */,
|
30697C3A21F63C990064FCAC /* UIViewControllerExtension.swift in Sources */,
|
||||||
30697C2E21F63C5A0064FCAC /* Utils.swift in Sources */,
|
30697C2E21F63C5A0064FCAC /* Utils.swift in Sources */,
|
||||||
30CCA91823258E760048CA51 /* GopenPGPInterface.swift in Sources */,
|
30CCA91823258E760048CA51 /* GopenPGPInterface.swift in Sources */,
|
||||||
|
9A2C7D8B2784139200BD9AF3 /* YubiKeyAPDU.swift in Sources */,
|
||||||
30697C4521F63CAB0064FCAC /* Password.swift in Sources */,
|
30697C4521F63CAB0064FCAC /* Password.swift in Sources */,
|
||||||
30697C4421F63CAB0064FCAC /* PasswordEntity.swift in Sources */,
|
30697C4421F63CAB0064FCAC /* PasswordEntity.swift in Sources */,
|
||||||
30BAC8CD22E3BB9700438475 /* KeyStore.swift in Sources */,
|
30BAC8CD22E3BB9700438475 /* KeyStore.swift in Sources */,
|
||||||
|
|
@ -2796,6 +2838,14 @@
|
||||||
revision = 8d59e4abba762d0f1e9aed161081f7b3fe21daa0;
|
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 */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
|
@ -2874,6 +2924,31 @@
|
||||||
package = 9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */;
|
package = 9A1F47F826E5CF4B000C0E01 /* XCRemoteSwiftPackageReference "OneTimePassword" */;
|
||||||
productName = 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 */ = {
|
9A996C5226DDF61F00A4485D /* Base32 */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */;
|
package = 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */;
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,15 @@
|
||||||
"revision": "f66bcd04088582c8fbb5cb8554d577e303bae396",
|
"revision": "f66bcd04088582c8fbb5cb8554d577e303bae396",
|
||||||
"version": "5.3.0"
|
"version": "5.3.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "YubiKit",
|
||||||
|
"repositoryURL": "https://github.com/Yubico/yubikit-ios",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "7e75fe8f057acf9bf7ac134d375783a1d409f79e",
|
||||||
|
"version": "4.1.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UNUserNotificationCenter.current().delegate = NotificationCenterDispatcher.shared
|
UNUserNotificationCenter.current().delegate = NotificationCenterDispatcher.shared
|
||||||
|
#if !targetEnvironment(simulator)
|
||||||
|
_ = passKit.YubiKeyConnection.shared
|
||||||
|
#endif
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ extension PGPKeyArmorImportTableViewController: PGPKeyImporter {
|
||||||
Utils.alert(title: "CannotSave".localize(), message: "SetPublicKey.".localize(), controller: self, completion: nil)
|
Utils.alert(title: "CannotSave".localize(), message: "SetPublicKey.".localize(), controller: self, completion: nil)
|
||||||
return false
|
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)
|
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKey.".localize(), controller: self, completion: nil)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import passKit
|
import passKit
|
||||||
|
|
||||||
class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController {
|
class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController, AlertPresenting {
|
||||||
@IBOutlet var pgpPublicKeyFile: UITableViewCell!
|
@IBOutlet var pgpPublicKeyFile: UITableViewCell!
|
||||||
@IBOutlet var pgpPrivateKeyFile: UITableViewCell!
|
@IBOutlet var pgpPrivateKeyFile: UITableViewCell!
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController {
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
let cell = tableView.cellForRow(at: 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
|
cell?.isSelected = false
|
||||||
if cell == pgpPublicKeyFile {
|
if cell == pgpPublicKeyFile {
|
||||||
currentlyPicking = .public
|
currentlyPicking = .public
|
||||||
|
|
@ -71,7 +71,7 @@ extension PGPKeyFileImportTableViewController: UIDocumentPickerDelegate {
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
let message = "FileCannotBeImported.".localize(fileName) | "UnderlyingError".localize(error.localizedDescription)
|
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()
|
static let label = "LoadFromFiles".localize()
|
||||||
|
|
||||||
func isReadyToUse() -> Bool {
|
func isReadyToUse() -> Bool {
|
||||||
validate(key: publicKey) && validate(key: privateKey)
|
validate(key: publicKey) && (Defaults.isYubiKeyEnabled || validate(key: privateKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
func importKeys() throws {
|
func importKeys() throws {
|
||||||
guard let publicKey = publicKey, let privateKey = privateKey else {
|
if let publicKey = publicKey {
|
||||||
return
|
|
||||||
}
|
|
||||||
try KeyFileManager.PublicPGP.importKey(from: publicKey)
|
try KeyFileManager.PublicPGP.importKey(from: publicKey)
|
||||||
|
}
|
||||||
|
if let privateKey = privateKey {
|
||||||
try KeyFileManager.PrivatePGP.importKey(from: privateKey)
|
try KeyFileManager.PrivatePGP.importKey(from: privateKey)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func doAfterImport() {
|
func doAfterImport() {
|
||||||
Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromLocation.".localize(), controller: self)
|
presentAlert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromLocation.".localize())
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveImportedKeys() {
|
func saveImportedKeys() {
|
||||||
|
|
@ -102,7 +103,7 @@ extension PGPKeyFileImportTableViewController: PGPKeyImporter {
|
||||||
|
|
||||||
private func validate(key: String?) -> Bool {
|
private func validate(key: String?) -> Bool {
|
||||||
guard key != nil else {
|
guard key != nil else {
|
||||||
Utils.alert(title: "CannotSavePgpKey".localize(), message: "KeyFileNotSet.".localize(), controller: self)
|
presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "KeyFileNotSet.".localize())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
import passKit
|
import passKit
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController {
|
class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController, AlertPresenting {
|
||||||
@IBOutlet var pgpPublicKeyURLTextField: UITextField!
|
@IBOutlet var pgpPublicKeyURLTextField: UITextField!
|
||||||
@IBOutlet var pgpPrivateKeyURLTextField: UITextField!
|
@IBOutlet var pgpPrivateKeyURLTextField: UITextField!
|
||||||
|
|
||||||
|
|
@ -24,18 +24,11 @@ class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController {
|
||||||
|
|
||||||
@IBAction
|
@IBAction
|
||||||
private func save(_: Any) {
|
private func save(_: Any) {
|
||||||
guard let publicKeyURLText = pgpPublicKeyURLTextField.text,
|
pgpPublicKeyURL = validate(pgpKeyURLText: pgpPublicKeyURLTextField.text)
|
||||||
let publicKeyURL = URL(string: publicKeyURLText),
|
|
||||||
let privateKeyURLText = pgpPrivateKeyURLTextField.text,
|
if !Defaults.isYubiKeyEnabled {
|
||||||
let privateKeyURL = URL(string: privateKeyURLText) else {
|
pgpPrivateKeyURL = validate(pgpKeyURLText: pgpPrivateKeyURLTextField.text)
|
||||||
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)
|
|
||||||
}
|
|
||||||
pgpPrivateKeyURL = privateKeyURL
|
|
||||||
pgpPublicKeyURL = publicKeyURL
|
|
||||||
saveImportedKeys()
|
saveImportedKeys()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,35 +38,39 @@ extension PGPKeyURLImportTableViewController: PGPKeyImporter {
|
||||||
static let label = "DownloadFromUrl".localize()
|
static let label = "DownloadFromUrl".localize()
|
||||||
|
|
||||||
func isReadyToUse() -> Bool {
|
func isReadyToUse() -> Bool {
|
||||||
validate(pgpKeyURL: pgpPublicKeyURLTextField.text ?? "")
|
validate(pgpKeyURLText: pgpPublicKeyURLTextField.text) != nil
|
||||||
&& validate(pgpKeyURL: pgpPrivateKeyURLTextField.text ?? "")
|
&& (Defaults.isYubiKeyEnabled || validate(pgpKeyURLText: pgpPrivateKeyURLTextField.text ?? "") != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func importKeys() throws {
|
func importKeys() throws {
|
||||||
|
if let pgpPrivateKeyURL = pgpPrivateKeyURL {
|
||||||
Defaults.pgpPrivateKeyURL = pgpPrivateKeyURL
|
Defaults.pgpPrivateKeyURL = pgpPrivateKeyURL
|
||||||
Defaults.pgpPublicKeyURL = pgpPublicKeyURL
|
try KeyFileManager.PrivatePGP.importKey(from: pgpPrivateKeyURL)
|
||||||
|
}
|
||||||
|
|
||||||
try KeyFileManager.PublicPGP.importKey(from: Defaults.pgpPublicKeyURL!)
|
if let pgpPublicKeyURL = pgpPublicKeyURL {
|
||||||
try KeyFileManager.PrivatePGP.importKey(from: Defaults.pgpPrivateKeyURL!)
|
Defaults.pgpPublicKeyURL = pgpPublicKeyURL
|
||||||
|
try KeyFileManager.PublicPGP.importKey(from: pgpPublicKeyURL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doAfterImport() {
|
func doAfterImport() {
|
||||||
Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize(), controller: self)
|
presentAlert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize())
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveImportedKeys() {
|
func saveImportedKeys() {
|
||||||
performSegue(withIdentifier: "savePGPKeySegue", sender: self)
|
performSegue(withIdentifier: "savePGPKeySegue", sender: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func validate(pgpKeyURL: String) -> Bool {
|
private func validate(pgpKeyURLText: String?) -> URL? {
|
||||||
guard let url = URL(string: pgpKeyURL) else {
|
guard let pgpKeyURL = pgpKeyURLText, let url = URL(string: pgpKeyURL) else {
|
||||||
Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self)
|
presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize())
|
||||||
return false
|
return nil
|
||||||
}
|
}
|
||||||
guard url.scheme == "https" || url.scheme == "http" else {
|
guard url.scheme == "https" || url.scheme == "http" else {
|
||||||
Utils.alert(title: "CannotSavePgpKey".localize(), message: "UseEitherHttpsOrHttp.".localize(), controller: self)
|
presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "UseEitherHttpsOrHttp.".localize())
|
||||||
return false
|
return nil
|
||||||
}
|
}
|
||||||
return true
|
return url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,13 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import FavIcon
|
import FavIcon
|
||||||
|
import Gopenpgp
|
||||||
|
import passAutoFillExtension
|
||||||
import passKit
|
import passKit
|
||||||
import SVProgressHUD
|
import SVProgressHUD
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
|
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate, AlertPresenting {
|
||||||
var passwordEntity: PasswordEntity?
|
var passwordEntity: PasswordEntity?
|
||||||
private var password: Password?
|
private var password: Password?
|
||||||
private var passwordImage: UIImage?
|
private var passwordImage: UIImage?
|
||||||
|
|
@ -87,7 +89,15 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
decryptThenShowPassword()
|
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 {
|
guard let passwordEntity = passwordEntity else {
|
||||||
Utils.alert(title: "CannotShowPassword".localize(), message: "PasswordDoesNotExist".localize(), controller: self, completion: {
|
Utils.alert(title: "CannotShowPassword".localize(), message: "PasswordDoesNotExist".localize(), controller: self, completion: {
|
||||||
self.navigationController!.popViewController(animated: true)
|
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)
|
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.pgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
|
||||||
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
|
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
|
||||||
let selectKey = UIAlertAction.selectKey(controller: self) { action in
|
let selectKey = UIAlertAction.selectKey(controller: self) { action in
|
||||||
self.decryptThenShowPassword(keyID: action.title)
|
self.decryptThenShowPasswordLocalKey(keyID: action.title)
|
||||||
}
|
}
|
||||||
alert.addAction(selectKey)
|
alert.addAction(selectKey)
|
||||||
|
|
||||||
|
|
@ -119,7 +129,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
|
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
|
||||||
alert.addAction(
|
alert.addAction(
|
||||||
UIAlertAction(title: "TryAgain".localize(), style: .default) { _ in
|
UIAlertAction(title: "TryAgain".localize(), style: .default) { _ in
|
||||||
self.decryptThenShowPassword()
|
self.decryptThenShowPasswordLocalKey()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.present(alert, animated: true, completion: nil)
|
self.present(alert, animated: true, completion: nil)
|
||||||
|
|
@ -509,3 +519,66 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
tableView.deselectRow(at: indexPath, animated: true)
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -319,7 +319,7 @@ extension PasswordNavigationViewController {
|
||||||
|
|
||||||
override func shouldPerformSegue(withIdentifier identifier: String, sender _: Any?) -> Bool {
|
override func shouldPerformSegue(withIdentifier identifier: String, sender _: Any?) -> Bool {
|
||||||
if identifier == "showPasswordDetail" {
|
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)
|
Utils.alert(title: "CannotShowPassword".localize(), message: "PgpKeyNotSet.".localize(), controller: self)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,12 +87,16 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
||||||
|
|
||||||
private func setPGPKeyTableViewCellDetailText() {
|
private func setPGPKeyTableViewCellDetailText() {
|
||||||
var label = "NotSet".localize()
|
var label = "NotSet".localize()
|
||||||
|
|
||||||
let keyID = (try? PGPAgent.shared.getShortKeyID()) ?? []
|
let keyID = (try? PGPAgent.shared.getShortKeyID()) ?? []
|
||||||
if keyID.count == 1 {
|
if keyID.count == 1 {
|
||||||
label = keyID.first ?? ""
|
label = keyID.first ?? ""
|
||||||
} else if keyID.count > 1 {
|
} else if keyID.count > 1 {
|
||||||
label = "Multiple"
|
label = "Multiple"
|
||||||
}
|
}
|
||||||
|
if Defaults.isYubiKeyEnabled {
|
||||||
|
label += "+YubiKey"
|
||||||
|
}
|
||||||
pgpKeyTableViewCell.detailTextLabel?.text = label
|
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 {
|
if Defaults.pgpKeySource != nil {
|
||||||
optionMenu.addAction(
|
optionMenu.addAction(
|
||||||
UIAlertAction(title: "RemovePgpKeys".localize(), style: .destructive) { _ in
|
UIAlertAction(title: "RemovePgpKeys".localize(), style: .destructive) { _ in
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,6 @@
|
||||||
#define Objective_CBridgingHeader_h
|
#define Objective_CBridgingHeader_h
|
||||||
|
|
||||||
@import ObjectiveGit;
|
@import ObjectiveGit;
|
||||||
|
#import <YubiKit.h>
|
||||||
|
|
||||||
#endif /* Objective_CBridgingHeader_h */
|
#endif /* Objective_CBridgingHeader_h */
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,21 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
|
||||||
|
<array>
|
||||||
|
<string>A000000527471117</string>
|
||||||
|
<string>A0000006472F0001</string>
|
||||||
|
<string>A0000005272101</string>
|
||||||
|
<string>A000000308</string>
|
||||||
|
<string>D27600012401</string>
|
||||||
|
<string>A000000527200101</string>
|
||||||
|
</array>
|
||||||
|
<key>NFCReaderUsageDescription</key>
|
||||||
|
<string>The application needs access to NFC reading to communicate with your YubiKey.</string>
|
||||||
|
<key>UISupportedExternalAccessoryProtocols</key>
|
||||||
|
<array>
|
||||||
|
<string>com.yubico.ylp</string>
|
||||||
|
</array>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,28 @@
|
||||||
// Copyright © 2021 Bob Sun. All rights reserved.
|
// Copyright © 2021 Bob Sun. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CryptoTokenKit
|
||||||
|
import Gopenpgp
|
||||||
import passKit
|
import passKit
|
||||||
import SVProgressHUD
|
import SVProgressHUD
|
||||||
import UIKit
|
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 {
|
DispatchQueue.global(qos: .userInteractive).async {
|
||||||
do {
|
do {
|
||||||
let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: controller)
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.developer.nfc.readersession.formats</key>
|
||||||
|
<array>
|
||||||
|
<string>NDEF</string>
|
||||||
|
<string>TAG</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.developer.siri</key>
|
<key>com.apple.developer.siri</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.developer.nfc.readersession.formats</key>
|
||||||
|
<array>
|
||||||
|
<string>NDEF</string>
|
||||||
|
<string>TAG</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.developer.siri</key>
|
<key>com.apple.developer.siri</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,10 @@ public extension UIAlertController {
|
||||||
alert.addAction(UIAlertAction.cancel())
|
alert.addAction(UIAlertAction.cancel())
|
||||||
return alert
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
// Copyright © 2017 Bob Sun. All rights reserved.
|
// Copyright © 2017 Bob Sun. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
public enum AppError: Error, Equatable {
|
public enum AppError: Error, Equatable {
|
||||||
case repositoryNotSet
|
case repositoryNotSet
|
||||||
case repositoryRemoteBranchNotFound(branchName: String)
|
case repositoryRemoteBranchNotFound(branchName: String)
|
||||||
|
|
@ -18,13 +20,31 @@ public enum AppError: Error, Equatable {
|
||||||
case gitPushNotSuccessful
|
case gitPushNotSuccessful
|
||||||
case pgpPublicKeyNotFound(keyID: String)
|
case pgpPublicKeyNotFound(keyID: String)
|
||||||
case pgpPrivateKeyNotFound(keyID: String)
|
case pgpPrivateKeyNotFound(keyID: String)
|
||||||
|
case yubiKey(YubiKeyError)
|
||||||
|
case passwordFileNotFound(path: String)
|
||||||
case keyExpiredOrIncompatible
|
case keyExpiredOrIncompatible
|
||||||
case wrongPassphrase
|
case wrongPassphrase
|
||||||
case wrongPasswordFilename
|
case wrongPasswordFilename
|
||||||
case decryption
|
case decryption
|
||||||
case encryption
|
case encryption
|
||||||
case encoding
|
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 {
|
extension AppError: LocalizedError {
|
||||||
|
|
@ -36,6 +56,8 @@ extension AppError: LocalizedError {
|
||||||
return localizationKey.localize(name)
|
return localizationKey.localize(name)
|
||||||
case let .pgpPrivateKeyNotFound(keyID), let .pgpPublicKeyNotFound(keyID):
|
case let .pgpPrivateKeyNotFound(keyID), let .pgpPublicKeyNotFound(keyID):
|
||||||
return localizationKey.localize(keyID)
|
return localizationKey.localize(keyID)
|
||||||
|
case let .other(message):
|
||||||
|
return message.localize()
|
||||||
default:
|
default:
|
||||||
return localizationKey.localize()
|
return localizationKey.localize()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ public extension DefaultsKeys {
|
||||||
var pgpKeySource: DefaultsKey<KeySource?> { .init("pgpKeySource") }
|
var pgpKeySource: DefaultsKey<KeySource?> { .init("pgpKeySource") }
|
||||||
var pgpPublicKeyURL: DefaultsKey<URL?> { .init("pgpPublicKeyURL") }
|
var pgpPublicKeyURL: DefaultsKey<URL?> { .init("pgpPublicKeyURL") }
|
||||||
var pgpPrivateKeyURL: DefaultsKey<URL?> { .init("pgpPrivateKeyURL") }
|
var pgpPrivateKeyURL: DefaultsKey<URL?> { .init("pgpPrivateKeyURL") }
|
||||||
|
var isYubiKeyEnabled: DefaultsKey<Bool> { .init("isYubiKeyEnabled", defaultValue: false) }
|
||||||
|
|
||||||
// Keep them for legacy reasons.
|
// Keep them for legacy reasons.
|
||||||
var pgpPublicKeyArmor: DefaultsKey<String?> { .init("pgpPublicKeyArmor") }
|
var pgpPublicKeyArmor: DefaultsKey<String?> { .init("pgpPublicKeyArmor") }
|
||||||
|
|
|
||||||
54
passKit/Helpers/YubiKeyAPDU.swift
Normal file
54
passKit/Helpers/YubiKeyAPDU.swift
Normal file
|
|
@ -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))!
|
||||||
|
}
|
||||||
|
}
|
||||||
63
passKit/Helpers/YubiKeyConnection.swift
Normal file
63
passKit/Helpers/YubiKeyConnection.swift
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,7 @@ public extension PasswordEntity {
|
||||||
if let path = getPath().stringByAddingPercentEncodingForRFC3986(), let url = URL(string: path) {
|
if let path = getPath().stringByAddingPercentEncodingForRFC3986(), let url = URL(string: path) {
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
throw AppError.unknown
|
throw AppError.other(message: "cannot decode URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: define some getters to get core data, we need to consider
|
// XXX: define some getters to get core data, we need to consider
|
||||||
|
|
|
||||||
55
passKit/Protocols/AlertPresenting.swift
Normal file
55
passKit/Protocols/AlertPresenting.swift
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
set -euox pipefail
|
set -euox pipefail
|
||||||
|
|
||||||
GOPENPGP_VERSION="v2.1.10"
|
GOPENPGP_VERSION="passforios"
|
||||||
|
|
||||||
export GOPATH="$(pwd)/go"
|
export GOPATH="$(pwd)/go"
|
||||||
export PATH="$PATH:$GOPATH/bin"
|
export PATH="$PATH:$GOPATH/bin"
|
||||||
|
|
@ -18,13 +18,11 @@ go env -w GO111MODULE=auto
|
||||||
go get golang.org/x/mobile/cmd/gomobile
|
go get golang.org/x/mobile/cmd/gomobile
|
||||||
gomobile init
|
gomobile init
|
||||||
|
|
||||||
git clone --depth 1 --branch "$GOPENPGP_VERSION" https://github.com/ProtonMail/gopenpgp.git "$GOPENPGP_PATH"
|
git clone --depth 1 --branch "$GOPENPGP_VERSION" https://github.com/mssun/gopenpgp.git "$GOPENPGP_PATH"
|
||||||
|
|
||||||
git apply patch/gnu-dummy.patch --directory "$GOPENPGP_PATH"
|
|
||||||
|
|
||||||
sed -i '' 's/build android/echo "Skipping Android build."/g' "$GOPENPGP_PATH/build.sh"
|
sed -i '' 's/build android/echo "Skipping Android build."/g' "$GOPENPGP_PATH/build.sh"
|
||||||
|
|
||||||
(cd "$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"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue