Refactor YubiKey decryptor (#663)
- Add YKFSmartCardInterface extension to simplify smart card related calls - Use async/await to rewrite callback closures - Update YubiKeyConnection - Better error handling
This commit is contained in:
parent
fc35805565
commit
a410c9480a
9 changed files with 344 additions and 320 deletions
|
|
@ -117,9 +117,7 @@
|
||||||
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 */; };
|
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 */; };
|
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 */; };
|
||||||
|
|
@ -132,8 +130,6 @@
|
||||||
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 */; };
|
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 */; };
|
9A5C6EFF2787F0980003F340 /* Gopenpgp.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */; };
|
||||||
9A5C6F022787F09A0003F340 /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; };
|
9A5C6F022787F09A0003F340 /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; };
|
||||||
9A5C6F042787F09D0003F340 /* Gopenpgp.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */; };
|
9A5C6F042787F09D0003F340 /* Gopenpgp.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */; };
|
||||||
|
|
@ -199,6 +195,8 @@
|
||||||
DC4914961E434301007FF592 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4914941E434301007FF592 /* LabelTableViewCell.swift */; };
|
DC4914961E434301007FF592 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4914941E434301007FF592 /* LabelTableViewCell.swift */; };
|
||||||
DC4914991E434600007FF592 /* PasswordDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4914981E434600007FF592 /* PasswordDetailTableViewController.swift */; };
|
DC4914991E434600007FF592 /* PasswordDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4914981E434600007FF592 /* PasswordDetailTableViewController.swift */; };
|
||||||
DC5F385B1E56AADB00C69ACA /* PGPKeyArmorImportTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC5F385A1E56AADB00C69ACA /* PGPKeyArmorImportTableViewController.swift */; };
|
DC5F385B1E56AADB00C69ACA /* PGPKeyArmorImportTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC5F385A1E56AADB00C69ACA /* PGPKeyArmorImportTableViewController.swift */; };
|
||||||
|
DC7CBBBD2D0FA3F2003BB4D2 /* YubiKit in Frameworks */ = {isa = PBXBuildFile; productRef = DC7CBBBC2D0FA3F2003BB4D2 /* YubiKit */; };
|
||||||
|
DC7CBBBF2D0FAC92003BB4D2 /* YKFSmartCardInterfaceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7CBBBE2D0FAC8E003BB4D2 /* YKFSmartCardInterfaceExtension.swift */; };
|
||||||
DC8963C01E38EEB900828B09 /* SSHKeyURLImportTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC8963BF1E38EEB900828B09 /* SSHKeyURLImportTableViewController.swift */; };
|
DC8963C01E38EEB900828B09 /* SSHKeyURLImportTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC8963BF1E38EEB900828B09 /* SSHKeyURLImportTableViewController.swift */; };
|
||||||
DC917BD71E2E8231000FDF54 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC917BD61E2E8231000FDF54 /* AppDelegate.swift */; };
|
DC917BD71E2E8231000FDF54 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC917BD61E2E8231000FDF54 /* AppDelegate.swift */; };
|
||||||
DC917BDC1E2E8231000FDF54 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDA1E2E8231000FDF54 /* Main.storyboard */; };
|
DC917BDC1E2E8231000FDF54 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDA1E2E8231000FDF54 /* Main.storyboard */; };
|
||||||
|
|
@ -495,6 +493,7 @@
|
||||||
DC4914941E434301007FF592 /* LabelTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelTableViewCell.swift; sourceTree = "<group>"; };
|
DC4914941E434301007FF592 /* LabelTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
DC4914981E434600007FF592 /* PasswordDetailTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordDetailTableViewController.swift; sourceTree = "<group>"; };
|
DC4914981E434600007FF592 /* PasswordDetailTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordDetailTableViewController.swift; sourceTree = "<group>"; };
|
||||||
DC5F385A1E56AADB00C69ACA /* PGPKeyArmorImportTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PGPKeyArmorImportTableViewController.swift; sourceTree = "<group>"; };
|
DC5F385A1E56AADB00C69ACA /* PGPKeyArmorImportTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PGPKeyArmorImportTableViewController.swift; sourceTree = "<group>"; };
|
||||||
|
DC7CBBBE2D0FAC8E003BB4D2 /* YKFSmartCardInterfaceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YKFSmartCardInterfaceExtension.swift; sourceTree = "<group>"; };
|
||||||
DC8963BF1E38EEB900828B09 /* SSHKeyURLImportTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSHKeyURLImportTableViewController.swift; sourceTree = "<group>"; };
|
DC8963BF1E38EEB900828B09 /* SSHKeyURLImportTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSHKeyURLImportTableViewController.swift; sourceTree = "<group>"; };
|
||||||
DC917BD31E2E8231000FDF54 /* Pass.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pass.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
DC917BD31E2E8231000FDF54 /* Pass.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pass.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DC917BD61E2E8231000FDF54 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
DC917BD61E2E8231000FDF54 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -533,7 +532,6 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5F9D7B0E27AF6FCA00A8AB22 /* CryptoTokenKit.framework in Frameworks */,
|
5F9D7B0E27AF6FCA00A8AB22 /* CryptoTokenKit.framework in Frameworks */,
|
||||||
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 */,
|
9A5C6F042787F09D0003F340 /* Gopenpgp.xcframework in Frameworks */,
|
||||||
|
|
@ -544,6 +542,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
DC7CBBBD2D0FA3F2003BB4D2 /* YubiKit in Frameworks */,
|
||||||
9ADAB21D26DDA52400900F10 /* Gopenpgp.xcframework in Frameworks */,
|
9ADAB21D26DDA52400900F10 /* Gopenpgp.xcframework in Frameworks */,
|
||||||
30A3001426DA6692002A734E /* KeychainAccess in Frameworks */,
|
30A3001426DA6692002A734E /* KeychainAccess in Frameworks */,
|
||||||
9A996C5726DDF65900A4485D /* Base32 in Frameworks */,
|
9A996C5726DDF65900A4485D /* Base32 in Frameworks */,
|
||||||
|
|
@ -551,7 +550,6 @@
|
||||||
30333B2D2CF9252E008A2EA2 /* SVProgressHUD in Frameworks */,
|
30333B2D2CF9252E008A2EA2 /* SVProgressHUD 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;
|
||||||
};
|
};
|
||||||
|
|
@ -572,7 +570,6 @@
|
||||||
files = (
|
files = (
|
||||||
5F9D7B0F27AF6FD200A8AB22 /* CryptoTokenKit.framework in Frameworks */,
|
5F9D7B0F27AF6FD200A8AB22 /* CryptoTokenKit.framework in Frameworks */,
|
||||||
9A5C6F022787F09A0003F340 /* passKit.framework in Frameworks */,
|
9A5C6F022787F09A0003F340 /* passKit.framework in Frameworks */,
|
||||||
9A5C6EFB2786CE5E0003F340 /* YubiKit in Frameworks */,
|
|
||||||
9A5C6EFF2787F0980003F340 /* Gopenpgp.xcframework in Frameworks */,
|
9A5C6EFF2787F0980003F340 /* Gopenpgp.xcframework in Frameworks */,
|
||||||
9A5C6F082787F0C20003F340 /* SwiftyUserDefaults in Frameworks */,
|
9A5C6F082787F0C20003F340 /* SwiftyUserDefaults in Frameworks */,
|
||||||
);
|
);
|
||||||
|
|
@ -596,7 +593,6 @@
|
||||||
9A1F47FA26E5CF4B000C0E01 /* OneTimePassword in Frameworks */,
|
9A1F47FA26E5CF4B000C0E01 /* OneTimePassword in Frameworks */,
|
||||||
9A996C5326DDF61F00A4485D /* Base32 in Frameworks */,
|
9A996C5326DDF61F00A4485D /* Base32 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 */,
|
||||||
|
|
@ -683,6 +679,7 @@
|
||||||
30B6AABA21F49095006B352D /* Extensions */ = {
|
30B6AABA21F49095006B352D /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DC7CBBBE2D0FAC8E003BB4D2 /* YKFSmartCardInterfaceExtension.swift */,
|
||||||
30DAFD49240985A7002456E7 /* Array+Slices.swift */,
|
30DAFD49240985A7002456E7 /* Array+Slices.swift */,
|
||||||
30CCA90A2325119C0048CA51 /* Data+Mutable.swift */,
|
30CCA90A2325119C0048CA51 /* Data+Mutable.swift */,
|
||||||
30697C3621F63C990064FCAC /* String+Localization.swift */,
|
30697C3621F63C990064FCAC /* String+Localization.swift */,
|
||||||
|
|
@ -1127,7 +1124,6 @@
|
||||||
name = passAutoFillExtension;
|
name = passAutoFillExtension;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
30A3001926DA697C002A734E /* SwiftyUserDefaults */,
|
30A3001926DA697C002A734E /* SwiftyUserDefaults */,
|
||||||
9A5C6EF82786CE170003F340 /* YubiKit */,
|
|
||||||
);
|
);
|
||||||
productName = passAutoFillExtension;
|
productName = passAutoFillExtension;
|
||||||
productReference = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */;
|
productReference = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */;
|
||||||
|
|
@ -1153,8 +1149,8 @@
|
||||||
3032DA5526DAF4E500A7728C /* ObjectivePGP */,
|
3032DA5526DAF4E500A7728C /* ObjectivePGP */,
|
||||||
9A996C5626DDF65900A4485D /* Base32 */,
|
9A996C5626DDF65900A4485D /* Base32 */,
|
||||||
9A1D1CE426E5D1CE0052028E /* OneTimePassword */,
|
9A1D1CE426E5D1CE0052028E /* OneTimePassword */,
|
||||||
9A2C7D852783FF9600BD9AF3 /* YubiKit */,
|
|
||||||
30333B2C2CF9252E008A2EA2 /* SVProgressHUD */,
|
30333B2C2CF9252E008A2EA2 /* SVProgressHUD */,
|
||||||
|
DC7CBBBC2D0FA3F2003BB4D2 /* YubiKit */,
|
||||||
);
|
);
|
||||||
productName = passKit;
|
productName = passKit;
|
||||||
productReference = A26075781EEC6F34005DB03E /* passKit.framework */;
|
productReference = A26075781EEC6F34005DB03E /* passKit.framework */;
|
||||||
|
|
@ -1198,7 +1194,6 @@
|
||||||
);
|
);
|
||||||
name = passExtension;
|
name = passExtension;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
9A5C6EFA2786CE5E0003F340 /* YubiKit */,
|
|
||||||
9A5C6F072787F0C20003F340 /* SwiftyUserDefaults */,
|
9A5C6F072787F0C20003F340 /* SwiftyUserDefaults */,
|
||||||
);
|
);
|
||||||
productName = passExtension;
|
productName = passExtension;
|
||||||
|
|
@ -1256,7 +1251,6 @@
|
||||||
9A996C5226DDF61F00A4485D /* Base32 */,
|
9A996C5226DDF61F00A4485D /* Base32 */,
|
||||||
9A1F47F926E5CF4B000C0E01 /* OneTimePassword */,
|
9A1F47F926E5CF4B000C0E01 /* OneTimePassword */,
|
||||||
30ED1776276F8842009BA876 /* ObjectiveGit */,
|
30ED1776276F8842009BA876 /* ObjectiveGit */,
|
||||||
9A2C7D812782CB2F00BD9AF3 /* YubiKit */,
|
|
||||||
30333B2A2CF924DC008A2EA2 /* SVProgressHUD */,
|
30333B2A2CF924DC008A2EA2 /* SVProgressHUD */,
|
||||||
);
|
);
|
||||||
productName = pass;
|
productName = pass;
|
||||||
|
|
@ -1362,10 +1356,10 @@
|
||||||
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" */,
|
|
||||||
307CA2322CF348260099F6DE /* XCRemoteSwiftPackageReference "SwiftFormat" */,
|
307CA2322CF348260099F6DE /* XCRemoteSwiftPackageReference "SwiftFormat" */,
|
||||||
307CB7522CF9219100D0931F /* XCRemoteSwiftPackageReference "SVProgressHUD" */,
|
307CB7522CF9219100D0931F /* XCRemoteSwiftPackageReference "SVProgressHUD" */,
|
||||||
30333B292CF922D9008A2EA2 /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */,
|
30333B292CF922D9008A2EA2 /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */,
|
||||||
|
DC7CBBBB2D0FA3F2003BB4D2 /* XCRemoteSwiftPackageReference "yubikit-ios" */,
|
||||||
);
|
);
|
||||||
productRefGroup = DC917BD41E2E8231000FDF54 /* Products */;
|
productRefGroup = DC917BD41E2E8231000FDF54 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
|
@ -1568,6 +1562,7 @@
|
||||||
A26075AD1EEC7125005DB03E /* pass.xcdatamodeld in Sources */,
|
A26075AD1EEC7125005DB03E /* pass.xcdatamodeld in Sources */,
|
||||||
30697C2D21F63C5A0064FCAC /* PasswordGeneratorFlavor.swift in Sources */,
|
30697C2D21F63C5A0064FCAC /* PasswordGeneratorFlavor.swift in Sources */,
|
||||||
308C273A2279F9CB0016D0E2 /* SearchBarScope.swift in Sources */,
|
308C273A2279F9CB0016D0E2 /* SearchBarScope.swift in Sources */,
|
||||||
|
DC7CBBBF2D0FAC92003BB4D2 /* YKFSmartCardInterfaceExtension.swift in Sources */,
|
||||||
30697C2F21F63C5A0064FCAC /* DefaultsKeys.swift in Sources */,
|
30697C2F21F63C5A0064FCAC /* DefaultsKeys.swift in Sources */,
|
||||||
30A1D2A821B2D53200E2D1F7 /* PasswordChange.swift in Sources */,
|
30A1D2A821B2D53200E2D1F7 /* PasswordChange.swift in Sources */,
|
||||||
30697C3E21F63C990064FCAC /* String+Utilities.swift in Sources */,
|
30697C3E21F63C990064FCAC /* String+Utilities.swift in Sources */,
|
||||||
|
|
@ -2932,12 +2927,12 @@
|
||||||
revision = 8d59e4abba762d0f1e9aed161081f7b3fe21daa0;
|
revision = 8d59e4abba762d0f1e9aed161081f7b3fe21daa0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
9A2C7D802782CB2F00BD9AF3 /* XCRemoteSwiftPackageReference "yubikit-ios" */ = {
|
DC7CBBBB2D0FA3F2003BB4D2 /* XCRemoteSwiftPackageReference "yubikit-ios" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/Yubico/yubikit-ios";
|
repositoryURL = "https://github.com/Yubico/yubikit-ios.git";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = upToNextMajorVersion;
|
kind = upToNextMajorVersion;
|
||||||
minimumVersion = 4.0.0;
|
minimumVersion = 4.6.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
@ -3028,26 +3023,6 @@
|
||||||
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 */ = {
|
9A5C6F072787F0C20003F340 /* SwiftyUserDefaults */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 3010CB5E26DA4F87008964D2 /* XCRemoteSwiftPackageReference "SwiftyUserDefaults" */;
|
package = 3010CB5E26DA4F87008964D2 /* XCRemoteSwiftPackageReference "SwiftyUserDefaults" */;
|
||||||
|
|
@ -3063,6 +3038,11 @@
|
||||||
package = 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */;
|
package = 30A3000C26DA62F4002A734E /* XCRemoteSwiftPackageReference "Base32" */;
|
||||||
productName = Base32;
|
productName = Base32;
|
||||||
};
|
};
|
||||||
|
DC7CBBBC2D0FA3F2003BB4D2 /* YubiKit */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = DC7CBBBB2D0FA3F2003BB4D2 /* XCRemoteSwiftPackageReference "yubikit-ios" */;
|
||||||
|
productName = YubiKit;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
|
||||||
/* Begin XCVersionGroup section */
|
/* Begin XCVersionGroup section */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"originHash" : "057d32000b3ab83e442b489c5bd052eed01930beee0100cb576baf0e541800df",
|
"originHash" : "728388097d1b3fa9558ae377b694945a2a9f0b6c670226aaf4de00602155a938",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "base32",
|
"identity" : "base32",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
|
||||||
<device id="retina6_0" orientation="portrait" appearance="light"/>
|
<device id="retina6_0" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
@ -81,11 +81,11 @@
|
||||||
<rect key="frame" x="0.0" y="18" width="390" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="18" width="390" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="55g-T3-9ak" id="dKn-cO-EJa">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="55g-T3-9ak" id="dKn-cO-EJa">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="371.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="0.0" width="331.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="General" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="dOt-Rj-vWD">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="General" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="dOt-Rj-vWD">
|
||||||
<rect key="frame" x="8" y="0.0" width="355.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="8" y="0.0" width="315.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
<rect key="frame" x="0.0" y="61.666667938232422" width="390" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="61.666667938232422" width="390" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2rc-ZW-XKd" id="CpT-zb-QEP">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2rc-ZW-XKd" id="CpT-zb-QEP">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="371.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="0.0" width="331.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Password Repository" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="gWn-ib-STb">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Password Repository" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="gWn-ib-STb">
|
||||||
|
|
@ -112,7 +112,7 @@
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="Myq-fV-riz">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="Myq-fV-riz">
|
||||||
<rect key="frame" x="306.33333333333337" y="11.999999999999998" width="57.333333333333336" height="20.333333333333332"/>
|
<rect key="frame" x="266.33333333333337" y="11.999999999999998" width="57.333333333333336" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||||
|
|
@ -128,18 +128,18 @@
|
||||||
<rect key="frame" x="0.0" y="105.33333587646484" width="390" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="105.33333587646484" width="390" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1ze-MS-Xbj" id="W7U-oL-hOh">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1ze-MS-Xbj" id="W7U-oL-hOh">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="371.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="0.0" width="359.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="PGP Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="RR9-xr-9ko">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="PGP Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="RR9-xr-9ko">
|
||||||
<rect key="frame" x="8" y="11.999999999999998" width="65.666666666666671" height="20.333333333333332"/>
|
<rect key="frame" x="20" y="11.999999999999998" width="65.666666666666671" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="7lc-Vh-G9W">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="7lc-Vh-G9W">
|
||||||
<rect key="frame" x="306.33333333333337" y="11.999999999999998" width="57.333333333333336" height="20.333333333333332"/>
|
<rect key="frame" x="294.33333333333337" y="11.999999999999998" width="57.333333333333336" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||||
|
|
@ -156,7 +156,7 @@
|
||||||
<rect key="frame" x="0.0" y="205.00000381469729" width="390" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="205.00000381469729" width="390" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="6Y0-mj-qhA" id="qlv-tQ-Xmc">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="6Y0-mj-qhA" id="qlv-tQ-Xmc">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="371.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="0.0" width="331.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Passcode Lock" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="RaZ-6t-0CU">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Passcode Lock" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="RaZ-6t-0CU">
|
||||||
|
|
@ -167,7 +167,7 @@
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Off" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="HXb-ZX-HUv">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Off" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="HXb-ZX-HUv">
|
||||||
<rect key="frame" x="339.33333333333337" y="11.999999999999998" width="24.333333333333332" height="20.333333333333332"/>
|
<rect key="frame" x="299.33333333333337" y="11.999999999999998" width="24.333333333333332" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||||
|
|
@ -184,11 +184,11 @@
|
||||||
<rect key="frame" x="0.0" y="284.66667175292969" width="390" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="284.66667175292969" width="390" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="tQN-gu-iRe" id="Xs0-LN-r43">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="tQN-gu-iRe" id="Xs0-LN-r43">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="371.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="0.0" width="331.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Advanced" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="MKj-d0-8q3">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Advanced" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="MKj-d0-8q3">
|
||||||
<rect key="frame" x="8" y="0.0" width="355.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="8" y="0.0" width="315.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
|
|
@ -208,11 +208,11 @@
|
||||||
<rect key="frame" x="0.0" y="364.33333969116211" width="390" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="364.33333969116211" width="390" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="osS-xk-WRP" id="G6j-ij-rNr">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="osS-xk-WRP" id="G6j-ij-rNr">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="371.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="0.0" width="359.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="About" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="oqz-Hr-RAl">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="About" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="oqz-Hr-RAl">
|
||||||
<rect key="frame" x="8" y="0.0" width="355.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="20" y="0.0" width="331.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
|
|
@ -402,14 +402,14 @@
|
||||||
<tableViewSection headerTitle="Authentication Method" id="h0N-tI-shZ">
|
<tableViewSection headerTitle="Authentication Method" id="h0N-tI-shZ">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="KrP-nb-haa">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="KrP-nb-haa">
|
||||||
<rect key="frame" x="0.0" y="356.33332633972168" width="390" height="43"/>
|
<rect key="frame" x="0.0" y="356.33332633972168" width="390" height="43.333332061767578"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="KrP-nb-haa" id="1uB-oE-kfI">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="KrP-nb-haa" id="1uB-oE-kfI">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43"/>
|
<rect key="frame" x="0.0" y="0.0" width="390" height="43.333332061767578"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LfQ-Af-j2O">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LfQ-Af-j2O">
|
||||||
<rect key="frame" x="74" y="11" width="256" height="21"/>
|
<rect key="frame" x="74" y="10.999999999999998" width="256" height="21.333333333333329"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
|
@ -434,14 +434,14 @@
|
||||||
<inset key="separatorInset" minX="62" minY="0.0" maxX="0.0" maxY="0.0"/>
|
<inset key="separatorInset" minX="62" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" accessoryType="detailButton" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="Qmt-bo-CuJ">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" accessoryType="detailButton" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="Qmt-bo-CuJ">
|
||||||
<rect key="frame" x="0.0" y="399.33332633972168" width="390" height="43"/>
|
<rect key="frame" x="0.0" y="399.66665840148926" width="390" height="43.333332061767578"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="Qmt-bo-CuJ" id="p3u-8b-h3U">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="Qmt-bo-CuJ" id="p3u-8b-h3U">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="358" height="43"/>
|
<rect key="frame" x="0.0" y="0.0" width="358" height="43.333332061767578"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SSH Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ezz-76-a53">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SSH Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ezz-76-a53">
|
||||||
<rect key="frame" x="74" y="11" width="223" height="21"/>
|
<rect key="frame" x="74" y="10.999999999999998" width="223" height="21.333333333333329"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
|
@ -840,11 +840,11 @@
|
||||||
<viewControllerLayoutGuide type="bottom" id="7JD-uM-IyS"/>
|
<viewControllerLayoutGuide type="bottom" id="7JD-uM-IyS"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="5YZ-6i-LST">
|
<view key="view" contentMode="scaleToFill" id="5YZ-6i-LST">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="390" height="787"/>
|
<rect key="frame" x="0.0" y="0.0" width="390" height="834"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3dt-Ph-4As">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3dt-Ph-4As">
|
||||||
<rect key="frame" x="16" y="76" width="358" height="669"/>
|
<rect key="frame" x="16" y="76" width="358" height="750"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<attributedString key="attributedText">
|
<attributedString key="attributedText">
|
||||||
<fragment>
|
<fragment>
|
||||||
|
|
@ -1106,18 +1106,18 @@ Secret Question 1: What is your childhood best friend's most bizarre superhero f
|
||||||
<rect key="frame" x="0.0" y="154.99999809265137" width="390" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="154.99999809265137" width="390" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="SVj-jD-qPT" id="HaO-5w-qZt">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="SVj-jD-qPT" id="HaO-5w-qZt">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="359.66666666666669" height="43.666667938232422"/>
|
<rect key="frame" x="0.0" y="0.0" width="371.66666666666669" height="43.666667938232422"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Git Signature" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="87a-xY-AbR">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Git Signature" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="87a-xY-AbR">
|
||||||
<rect key="frame" x="20" y="11.999999999999998" width="99" height="20.333333333333332"/>
|
<rect key="frame" x="8" y="11.999999999999998" width="99" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="2qr-d7-0SK">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="2qr-d7-0SK">
|
||||||
<rect key="frame" x="294.33333333333337" y="11.999999999999998" width="57.333333333333336" height="20.333333333333332"/>
|
<rect key="frame" x="306.33333333333337" y="11.999999999999998" width="57.333333333333336" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
case name, main, addition, misc
|
case name, main, addition, misc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var passwordYubiKeyDecryptor = PasswordYubiKeyDecryptor()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
|
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
|
||||||
|
|
@ -559,12 +561,11 @@ extension PasswordDetailTableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func requestYubiKeyPIN(completion: @escaping (String) -> Void, cancellation: @escaping () -> Void) {
|
private func requestYubiKeyPIN(completion: @escaping (String) -> Void) {
|
||||||
let alert = UIAlertController(title: "YubiKey PIN", message: "Verify YubiKey OpenPGP PIN.", preferredStyle: .alert)
|
let alert = UIAlertController(title: "YubiKey PIN", message: "Verify YubiKey OpenPGP PIN.", preferredStyle: .alert)
|
||||||
alert.addAction(
|
alert.addAction(
|
||||||
UIAlertAction.cancel { _ in
|
UIAlertAction.cancel { _ in
|
||||||
self.navigationController!.popViewController(animated: true)
|
self.navigationController!.popViewController(animated: true)
|
||||||
cancellation()
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
alert.addAction(
|
alert.addAction(
|
||||||
|
|
@ -580,25 +581,10 @@ extension PasswordDetailTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleError(error: AppError) {
|
private func handleError(error: AppError) {
|
||||||
switch error {
|
DispatchQueue.main.async {
|
||||||
case let .yubiKey(yubiKeyError):
|
self.presentFailureAlert(message: error.localizedDescription) { _ in
|
||||||
let errorMessage = yubiKeyError.localizedDescription
|
|
||||||
YubiKitManager.shared.stopNFCConnection(withErrorMessage: errorMessage)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.navigationController?.popViewController(animated: true)
|
self.navigationController?.popViewController(animated: true)
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.presentFailureAlert(message: error.localizedDescription) { _ in
|
|
||||||
self.navigationController?.popViewController(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleCancellation() {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.navigationController?.popViewController(animated: true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -607,9 +593,30 @@ extension PasswordDetailTableViewController {
|
||||||
handleError(error: AppError.other(message: "PasswordDoesNotExist"))
|
handleError(error: AppError.other(message: "PasswordDoesNotExist"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
yubiKeyDecrypt(passwordEntity: passwordEntity, requestPIN: requestYubiKeyPIN, errorHandler: handleError, cancellation: handleCancellation) { password in
|
let encryptedDataPath = PasswordStore.shared.storeURL.appendingPathComponent(passwordEntity.getPath())
|
||||||
self.password = password
|
|
||||||
self.showPassword()
|
guard let encryptedData = try? Data(contentsOf: encryptedDataPath) else {
|
||||||
|
handleError(error: AppError.other(message: "PasswordDoesNotExist"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
requestYubiKeyPIN { [self] pin in
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
let decryptedData = try await passwordYubiKeyDecryptor.yubiKeyDecrypt(encryptedData: encryptedData, pin: pin)
|
||||||
|
guard let decryptedDataString = String(data: decryptedData, encoding: .utf8) else {
|
||||||
|
throw AppError.yubiKey(.decipher(message: "Failed to convert plaintext to string."))
|
||||||
|
}
|
||||||
|
guard let password = try? Password(name: passwordEntity.getName(), url: passwordEntity.getURL(), plainText: decryptedDataString) else {
|
||||||
|
throw AppError.yubiKey(.decipher(message: "Failed to construct password."))
|
||||||
|
}
|
||||||
|
self.password = password
|
||||||
|
self.showPassword()
|
||||||
|
} catch let error as AppError {
|
||||||
|
handleError(error: error)
|
||||||
|
} catch {
|
||||||
|
handleError(error: AppError.yubiKey(.other(message: error.localizedDescription)))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,176 +55,61 @@ func decryptPassword(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public typealias RequestPINAction = (@escaping (String) -> Void, @escaping () -> Void) -> Void
|
class PasswordYubiKeyDecryptor {
|
||||||
|
let yubiKeyConnection = YubiKeyConnection()
|
||||||
|
|
||||||
let symmetricKeyIDNameDict: [UInt8: String] = [
|
func didDisconnect(completion: @escaping (_ connection: YKFConnectionProtocol?, _ error: Error?) -> Void) {
|
||||||
2: "3des",
|
yubiKeyConnection.didDisconnect(handler: completion)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
private func getCapabilities(_ applicationRelatedData: Data) -> (Bool, Bool) {
|
private func handleError(error: Error, forConnection connection: YKFConnectionProtocol) -> Error {
|
||||||
let tlv = TKBERTLVRecord.sequenceOfRecords(from: applicationRelatedData)!
|
if (connection as? YKFNFCConnection) != nil {
|
||||||
// 0x5f52: Historical Bytes
|
YubiKitManager.shared.stopNFCConnection(withErrorMessage: error.localizedDescription)
|
||||||
for record in TKBERTLVRecord.sequenceOfRecords(from: tlv.first!.value)! where record.tag == 0x5F52 {
|
return error // Dont pass on the error since we display it in the NFC modal
|
||||||
let historical = record.value
|
|
||||||
if historical.count < 4 {
|
|
||||||
// log_error ("warning: historical bytes are too short\n");
|
|
||||||
return (false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if historical[0] != 0 {
|
|
||||||
// log_error ("warning: bad category indicator in historical bytes\n");
|
|
||||||
return (false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
let dos = historical[1 ..< historical.endIndex - 3]
|
|
||||||
for record2 in TKCompactTLVRecord.sequenceOfRecords(from: dos)! where record2.tag == 7 && record2.value.count == 3 {
|
|
||||||
let cmd_chaining = (record2.value[2] & 0x80) != 0
|
|
||||||
let ext_lc_le = (record2.value[2] & 0x40) != 0
|
|
||||||
return (cmd_chaining, ext_lc_le)
|
|
||||||
}
|
}
|
||||||
|
return error
|
||||||
}
|
}
|
||||||
return (false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func yubiKeyDecrypt(
|
func yubiKeyDecrypt(encryptedData: Data, pin: String) async throws -> Data {
|
||||||
passwordEntity: PasswordEntity,
|
let connection = await yubiKeyConnection.startConnection()
|
||||||
requestPIN: @escaping RequestPINAction,
|
|
||||||
errorHandler: @escaping ((AppError) -> Void),
|
|
||||||
cancellation: @escaping (() -> Void),
|
|
||||||
completion: @escaping ((Password) -> Void)
|
|
||||||
) {
|
|
||||||
Task {
|
|
||||||
do {
|
do {
|
||||||
let encryptedDataPath = PasswordStore.shared.storeURL.appendingPathComponent(passwordEntity.getPath())
|
|
||||||
|
|
||||||
guard let encryptedData = try? Data(contentsOf: encryptedDataPath) else {
|
|
||||||
errorHandler(AppError.other(message: "PasswordDoesNotExist".localize()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let pin = await readPin(requestPIN: requestPIN) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let connection = try? await getConnection() else {
|
|
||||||
cancellation()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let smartCard = connection.smartCardInterface else {
|
guard let smartCard = connection.smartCardInterface else {
|
||||||
throw AppError.yubiKey(.connection(message: "Failed to get smart card interface."))
|
throw AppError.yubiKey(.connection(message: "Failed to get smart card interface."))
|
||||||
}
|
}
|
||||||
|
do {
|
||||||
try await selectOpenPGPApplication(smartCard: smartCard)
|
try await smartCard.selectOpenPGPApplication()
|
||||||
|
} catch {
|
||||||
try await verifyPin(smartCard: smartCard, pin: pin)
|
throw AppError.yubiKey(.connection(message: "Failed to select OpenPGP application"))
|
||||||
|
|
||||||
guard let applicationRelatedData = try await getApplicationRelatedData(smartCard: smartCard) else {
|
|
||||||
throw AppError.yubiKey(.decipher(message: "Failed to get application related data."))
|
|
||||||
}
|
}
|
||||||
|
do {
|
||||||
if !isEncryptKeyAlgoRSA(applicationRelatedData) {
|
try await smartCard.verify(password: pin)
|
||||||
throw AppError.yubiKey(.decipher(message: "Encryption key algorithm is not supported. Supported algorithm: RSA."))
|
} catch {
|
||||||
|
throw AppError.yubiKey(.connection(message: "Failed to verify PIN"))
|
||||||
}
|
}
|
||||||
|
guard let deciphered = try? await smartCard.decipher(ciphertext: encryptedData) else {
|
||||||
let (cmd_chaining, _) = getCapabilities(applicationRelatedData)
|
throw AppError.yubiKey(.connection(message: "Failed to dicipher data"))
|
||||||
|
|
||||||
let deciphered = try await decipher(smartCard: smartCard, ciphertext: encryptedData, chained: cmd_chaining)
|
|
||||||
|
|
||||||
YubiKitManager.shared.stopNFCConnection()
|
|
||||||
|
|
||||||
let plaintext = try decryptPassword(deciphered: deciphered, ciphertext: encryptedData)
|
|
||||||
guard let password = try? Password(name: passwordEntity.getName(), url: passwordEntity.getURL(), plainText: plaintext) else {
|
|
||||||
throw AppError.yubiKey(.decipher(message: "Failed to construct password."))
|
|
||||||
}
|
}
|
||||||
|
let decryptedData = try decryptData(deciphered: deciphered, ciphertext: encryptedData)
|
||||||
completion(password)
|
if (connection as? YKFNFCConnection) != nil {
|
||||||
} catch let error as AppError {
|
YubiKitManager.shared.stopNFCConnection()
|
||||||
errorHandler(error)
|
}
|
||||||
|
return decryptedData
|
||||||
} catch {
|
} catch {
|
||||||
errorHandler(AppError.other(message: String(describing: error)))
|
throw handleError(error: error, forConnection: connection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readPin(requestPIN: @escaping RequestPINAction) async -> String? {
|
private func decryptData(deciphered: Data, ciphertext: Data) throws -> Data {
|
||||||
await withCheckedContinuation { (continuation: CheckedContinuation<String?, Never>) in
|
let symmetricKeyIDNameDict: [UInt8: String] = [
|
||||||
DispatchQueue.main.async {
|
2: "3des",
|
||||||
requestPIN({ pin in continuation.resume(returning: pin) }, { continuation.resume(returning: nil) })
|
3: "cast5",
|
||||||
}
|
7: "aes128",
|
||||||
}
|
8: "aes192",
|
||||||
}
|
9: "aes256",
|
||||||
|
]
|
||||||
|
|
||||||
func getConnection() async throws -> YKFConnectionProtocol? {
|
|
||||||
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<YKFConnectionProtocol?, Error>) in
|
|
||||||
passKit.YubiKeyConnection.shared.connection(cancellation: { error in
|
|
||||||
continuation.resume(throwing: error)
|
|
||||||
}, completion: { connection in
|
|
||||||
continuation.resume(returning: connection)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func selectOpenPGPApplication(smartCard: YKFSmartCardInterface) async throws {
|
|
||||||
if await withCheckedContinuation({ (continuation: CheckedContinuation<Error?, Never>) in
|
|
||||||
smartCard.selectApplication(YubiKeyAPDU.selectOpenPGPApplication()) { _, error in
|
|
||||||
continuation.resume(returning: error)
|
|
||||||
}
|
|
||||||
}) != nil {
|
|
||||||
throw AppError.yubiKey(.selectApplication(message: "Failed to select application."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getApplicationRelatedData(smartCard: YKFSmartCardInterface) async throws -> Data? {
|
|
||||||
try await executeCommandAsync(smartCard: smartCard, apdu: YubiKeyAPDU.get_application_related_data())
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyPin(smartCard: YKFSmartCardInterface, pin: String) async throws {
|
|
||||||
if await withCheckedContinuation({ (continuation: CheckedContinuation<Error?, Never>) in
|
|
||||||
smartCard.executeCommand(YubiKeyAPDU.verify(password: pin)) { _, error in
|
|
||||||
continuation.resume(returning: error)
|
|
||||||
}}) != nil {
|
|
||||||
throw AppError.yubiKey(.selectApplication(message: "Failed to verify PIN."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decipher(smartCard: YKFSmartCardInterface, ciphertext: Data, chained: Bool) async throws -> Data {
|
|
||||||
var error: NSError?
|
|
||||||
let message = createPGPMessage(from: ciphertext)
|
|
||||||
guard let mpi1 = Gopenpgp.HelperPassGetEncryptedMPI1(message, &error) else {
|
|
||||||
throw AppError.yubiKey(.decipher(message: "Failed to get encrypted MPI."))
|
|
||||||
}
|
|
||||||
|
|
||||||
let apdus = chained ? YubiKeyAPDU.decipherChained(data: mpi1) : YubiKeyAPDU.decipherExtended(data: mpi1)
|
|
||||||
|
|
||||||
for (idx, apdu) in apdus.enumerated() {
|
|
||||||
let data = try await executeCommandAsync(smartCard: smartCard, apdu: apdu)
|
|
||||||
// the last response must have the data
|
|
||||||
if idx == apdus.endIndex - 1, let data {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw AppError.yubiKey(.verify(message: "Failed to execute decipher."))
|
|
||||||
}
|
|
||||||
|
|
||||||
func decryptPassword(deciphered: Data, ciphertext: Data) throws -> String {
|
|
||||||
let message = createPGPMessage(from: ciphertext)
|
let message = createPGPMessage(from: ciphertext)
|
||||||
|
|
||||||
guard let algoByte = deciphered.first, let algo = symmetricKeyIDNameDict[algoByte] else {
|
guard let algoByte = deciphered.first, let algo = symmetricKeyIDNameDict[algoByte] else {
|
||||||
|
|
@ -240,33 +125,5 @@ func decryptPassword(deciphered: Data, ciphertext: Data) throws -> String {
|
||||||
throw AppError.yubiKey(.decipher(message: "Failed to decrypt with session key: \(String(describing: error))"))
|
throw AppError.yubiKey(.decipher(message: "Failed to decrypt with session key: \(String(describing: error))"))
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let plaintext_str = String(data: plaintext, encoding: .utf8) else {
|
return plaintext
|
||||||
throw AppError.yubiKey(.decipher(message: "Failed to convert plaintext to string."))
|
|
||||||
}
|
|
||||||
|
|
||||||
return plaintext_str
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeCommandAsync(smartCard: YKFSmartCardInterface, apdu: YKFAPDU) async throws -> Data? {
|
|
||||||
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Data?, Error>) in
|
|
||||||
smartCard.executeCommand(apdu) { data, error in
|
|
||||||
if let error {
|
|
||||||
continuation.resume(throwing: error)
|
|
||||||
} else {
|
|
||||||
continuation.resume(returning: data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Data {
|
|
||||||
struct HexEncodingOptions: OptionSet {
|
|
||||||
let rawValue: Int
|
|
||||||
static let upperCase = Self(rawValue: 1 << 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hexEncodedString(options: HexEncodingOptions = []) -> String {
|
|
||||||
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
|
|
||||||
return map { String(format: format, $0) }.joined()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
84
passKit/Extensions/YKFSmartCardInterfaceExtension.swift
Normal file
84
passKit/Extensions/YKFSmartCardInterfaceExtension.swift
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
//
|
||||||
|
// YKFSmartCardInterfaceExtension.swift
|
||||||
|
// pass
|
||||||
|
//
|
||||||
|
// Created by Mingshen Sun on 12/15/24.
|
||||||
|
// Copyright © 2024 Bob Sun. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CryptoTokenKit
|
||||||
|
import Gopenpgp
|
||||||
|
import YubiKit
|
||||||
|
|
||||||
|
public enum Algorithm {
|
||||||
|
case rsa
|
||||||
|
case others
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ApplicationRelatedData {
|
||||||
|
public let isCommandChaining: Bool
|
||||||
|
public let decryptionAlgorithm: Algorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension YKFSmartCardInterface {
|
||||||
|
func selectOpenPGPApplication() async throws {
|
||||||
|
try await selectApplication(YubiKeyAPDU.selectOpenPGPApplication())
|
||||||
|
}
|
||||||
|
|
||||||
|
func verify(password: String) async throws {
|
||||||
|
try await executeCommand(YubiKeyAPDU.verify(password: password))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getApplicationRelatedData() async throws -> ApplicationRelatedData {
|
||||||
|
let data = try await executeCommand(YubiKeyAPDU.getApplicationRelatedData())
|
||||||
|
var isCommandChaining = false
|
||||||
|
var algorithm = Algorithm.others
|
||||||
|
let tlv = TKBERTLVRecord.sequenceOfRecords(from: data)!
|
||||||
|
for record in TKBERTLVRecord.sequenceOfRecords(from: tlv.first!.value)! {
|
||||||
|
if record.tag == 0x5F52 { // 0x5f52: Historical Bytes
|
||||||
|
let historical = record.value
|
||||||
|
if historical.count < 4 {
|
||||||
|
isCommandChaining = false
|
||||||
|
}
|
||||||
|
if historical[0] != 0 {
|
||||||
|
isCommandChaining = false
|
||||||
|
}
|
||||||
|
let dos = historical[1 ..< historical.endIndex - 3]
|
||||||
|
for record2 in TKCompactTLVRecord.sequenceOfRecords(from: dos)! where record2.tag == 7 && record2.value.count == 3 {
|
||||||
|
isCommandChaining = (record2.value[2] & 0x80) != 0
|
||||||
|
}
|
||||||
|
} else if record.tag == 0x73 { // 0x73: Discretionary data objects
|
||||||
|
// 0xC2: Algorithm attributes decryption, 0x01: RSA
|
||||||
|
for record2 in TKBERTLVRecord.sequenceOfRecords(from: record.value)! where record2.tag == 0xC2 && record2.value.first! == 0x01 {
|
||||||
|
algorithm = .rsa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ApplicationRelatedData(isCommandChaining: isCommandChaining, decryptionAlgorithm: algorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decipher(ciphertext: Data) async throws -> Data {
|
||||||
|
let applicationRelatedData = try await getApplicationRelatedData()
|
||||||
|
guard applicationRelatedData.decryptionAlgorithm == .rsa else {
|
||||||
|
throw AppError.yubiKey(.decipher(message: "Encryption key algorithm is not supported. Supported algorithm: RSA."))
|
||||||
|
}
|
||||||
|
|
||||||
|
var error: NSError?
|
||||||
|
let message = createPGPMessage(from: ciphertext)
|
||||||
|
guard let mpi1 = Gopenpgp.HelperPassGetEncryptedMPI1(message, &error) else {
|
||||||
|
throw AppError.yubiKey(.decipher(message: "Failed to get encrypted MPI."))
|
||||||
|
}
|
||||||
|
|
||||||
|
let apdus = applicationRelatedData.isCommandChaining ? YubiKeyAPDU.decipherChained(data: mpi1) : YubiKeyAPDU.decipherExtended(data: mpi1)
|
||||||
|
|
||||||
|
for (idx, apdu) in apdus.enumerated() {
|
||||||
|
let data = try await executeCommand(apdu)
|
||||||
|
// the last response must have the data
|
||||||
|
if idx == apdus.endIndex - 1 {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw AppError.yubiKey(.verify(message: "Failed to execute decipher."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,12 +36,13 @@ public enum YubiKeyError: Error, Equatable {
|
||||||
case selectApplication(message: String)
|
case selectApplication(message: String)
|
||||||
case verify(message: String)
|
case verify(message: String)
|
||||||
case decipher(message: String)
|
case decipher(message: String)
|
||||||
|
case other(message: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension YubiKeyError: LocalizedError {
|
extension YubiKeyError: LocalizedError {
|
||||||
public var errorDescription: String? {
|
public var errorDescription: String? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .connection(message), let .decipher(message), let .selectApplication(message), let .verify(message):
|
case let .connection(message), let .decipher(message), let .other(message), let .selectApplication(message), let .verify(message):
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,6 +57,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 .yubiKey(error):
|
||||||
|
return error.errorDescription
|
||||||
case let .other(message):
|
case let .other(message):
|
||||||
return message.localize()
|
return message.localize()
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -13,30 +13,12 @@ public enum YubiKeyAPDU {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func verify(password: String) -> YKFAPDU {
|
public static func verify(password: String) -> YKFAPDU {
|
||||||
let pw1: [UInt8] = Array(password.utf8)
|
YKFAPDU(cla: 0x00, ins: 0x20, p1: 0x00, p2: 0x82, data: Data(password.utf8), type: .extended)!
|
||||||
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
|
|
||||||
return YKFAPDU(data: Data(apdu))!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func decipherExtended(data: Data) -> [YKFAPDU] {
|
public static func decipherExtended(data: Data) -> [YKFAPDU] {
|
||||||
var apdu: [UInt8] = []
|
let apdu = YKFAPDU(cla: 0x00, ins: 0x2A, p1: 0x80, p2: 0x86, data: data, type: .extended)!
|
||||||
apdu += [0x00] // CLA (last or only command of a chain)
|
return [apdu]
|
||||||
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]
|
|
||||||
|
|
||||||
return [YKFAPDU(data: Data(apdu))!]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func decipherChained(data: Data) -> [YKFAPDU] {
|
public static func decipherChained(data: Data) -> [YKFAPDU] {
|
||||||
|
|
@ -63,14 +45,8 @@ public enum YubiKeyAPDU {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func get_application_related_data() -> YKFAPDU {
|
public static func getApplicationRelatedData() -> YKFAPDU {
|
||||||
var apdu: [UInt8] = []
|
YKFAPDU(cla: 0x00, ins: 0xCA, p1: 0x00, p2: 0x6E, data: Data(), type: .short)!
|
||||||
apdu += [0x00] // CLA
|
|
||||||
apdu += [0xCA] // INS: GET DATA
|
|
||||||
apdu += [0x00]
|
|
||||||
apdu += [0x6E] // P2: application related data
|
|
||||||
apdu += [0x00]
|
|
||||||
return YKFAPDU(data: Data(apdu))!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func chunk(data: Data) -> [[UInt8]] {
|
static func chunk(data: Data) -> [[UInt8]] {
|
||||||
|
|
|
||||||
|
|
@ -2,62 +2,179 @@
|
||||||
// YubiKeyConnection.swift
|
// YubiKeyConnection.swift
|
||||||
// passKit
|
// passKit
|
||||||
//
|
//
|
||||||
// Copyright © 2022 Bob Sun. All rights reserved.
|
// Copyright (C) 2024 Mingshen Sun.
|
||||||
|
//
|
||||||
|
// This file is part of yubioath-ios, modified from the original.
|
||||||
|
// Original code Copyright Yubico 2022.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import YubiKit
|
import YubiKit
|
||||||
|
|
||||||
public class YubiKeyConnection: NSObject {
|
public class YubiKeyConnection: NSObject {
|
||||||
public static let shared = YubiKeyConnection()
|
override public init() {
|
||||||
|
|
||||||
var accessoryConnection: YKFAccessoryConnection?
|
|
||||||
var nfcConnection: YKFNFCConnection?
|
|
||||||
var connectionCallback: ((_ connection: YKFConnectionProtocol) -> Void)?
|
|
||||||
var cancellationCallback: ((_ error: Error) -> Void)?
|
|
||||||
|
|
||||||
override init() {
|
|
||||||
super.init()
|
super.init()
|
||||||
if YubiKitDeviceCapabilities.supportsISO7816NFCTags {
|
YubiKitManager.shared.delegate = self
|
||||||
YubiKitManager.shared.delegate = self
|
|
||||||
YubiKitManager.shared.startAccessoryConnection()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func connection(cancellation: @escaping (_ error: Error) -> Void, completion: @escaping (_ connection: YKFConnectionProtocol) -> Void) {
|
deinit {}
|
||||||
|
|
||||||
|
var connection: YKFConnectionProtocol? {
|
||||||
|
accessoryConnection ?? smartCardConnection ?? nfcConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
private var nfcConnection: YKFNFCConnection?
|
||||||
|
private var smartCardConnection: YKFSmartCardConnection?
|
||||||
|
private var accessoryConnection: YKFAccessoryConnection?
|
||||||
|
|
||||||
|
private var connectionCallback: ((_ connection: YKFConnectionProtocol) -> Void)?
|
||||||
|
private var disconnectionCallback: ((_ connection: YKFConnectionProtocol?, _ error: Error?) -> Void)?
|
||||||
|
|
||||||
|
private var accessoryConnectionCallback: ((_ connection: YKFAccessoryConnection?) -> Void)?
|
||||||
|
private var nfcConnectionCallback: ((_ connection: YKFNFCConnection?) -> Void)?
|
||||||
|
private var smartCardConnectionCallback: ((_ connection: YKFSmartCardConnection?) -> Void)?
|
||||||
|
|
||||||
|
public func startConnection(completion: @escaping (_ connection: YKFConnectionProtocol) -> Void) {
|
||||||
|
YubiKitManager.shared.delegate = self
|
||||||
|
|
||||||
if let connection = accessoryConnection {
|
if let connection = accessoryConnection {
|
||||||
completion(connection)
|
completion(connection)
|
||||||
|
} else if let connection = smartCardConnection {
|
||||||
|
completion(connection)
|
||||||
|
} else if let connection = nfcConnection {
|
||||||
|
completion(connection)
|
||||||
} else {
|
} else {
|
||||||
connectionCallback = completion
|
connectionCallback = completion
|
||||||
YubiKitManager.shared.startNFCConnection()
|
if YubiKitDeviceCapabilities.supportsISO7816NFCTags {
|
||||||
|
YubiKitManager.shared.startNFCConnection()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cancellationCallback = cancellation
|
}
|
||||||
|
|
||||||
|
public func startConnection() async -> YKFConnectionProtocol {
|
||||||
|
await withCheckedContinuation { continuation in
|
||||||
|
self.startConnection { connection in
|
||||||
|
continuation.resume(with: Result.success(connection))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startWiredConnection(completion: @escaping (_ connection: YKFConnectionProtocol) -> Void) {
|
||||||
|
connectionCallback = completion
|
||||||
|
YubiKitManager.shared.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessoryConnection(handler: @escaping (_ connection: YKFAccessoryConnection?) -> Void) {
|
||||||
|
if let connection = accessoryConnection {
|
||||||
|
handler(connection)
|
||||||
|
} else {
|
||||||
|
accessoryConnectionCallback = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func smartCardConnection(handler: @escaping (_ connection: YKFSmartCardConnection?) -> Void) {
|
||||||
|
if let connection = smartCardConnection {
|
||||||
|
handler(connection)
|
||||||
|
} else {
|
||||||
|
smartCardConnectionCallback = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nfcConnection(handler: @escaping (_ connection: YKFNFCConnection?) -> Void) {
|
||||||
|
if let connection = nfcConnection {
|
||||||
|
handler(connection)
|
||||||
|
} else {
|
||||||
|
nfcConnectionCallback = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func stop() {
|
||||||
|
if #available(iOSApplicationExtension 16.0, *) {
|
||||||
|
smartCardConnection?.stop()
|
||||||
|
}
|
||||||
|
accessoryConnection?.stop()
|
||||||
|
nfcConnection?.stop()
|
||||||
|
// stop() returns immediately but closing the connection will take a few cycles so we need to wait to make sure it's closed before restarting.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
|
if YubiKitDeviceCapabilities.supportsMFIAccessoryKey {
|
||||||
|
YubiKitManager.shared.startAccessoryConnection()
|
||||||
|
}
|
||||||
|
if YubiKitDeviceCapabilities.supportsSmartCardOverUSBC, #available(iOSApplicationExtension 16.0, *) {
|
||||||
|
YubiKitManager.shared.startSmartCardConnection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func didDisconnect(handler: @escaping (_ connection: YKFConnectionProtocol?, _ error: Error?) -> Void) {
|
||||||
|
disconnectionCallback = handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension YubiKeyConnection: YKFManagerDelegate {
|
extension YubiKeyConnection: YKFManagerDelegate {
|
||||||
public func didConnectNFC(_ connection: YKFNFCConnection) {
|
public func didConnectNFC(_ connection: YKFNFCConnection) {
|
||||||
nfcConnection = connection
|
nfcConnection = connection
|
||||||
if let callback = connectionCallback {
|
nfcConnectionCallback?(connection)
|
||||||
callback(connection)
|
nfcConnectionCallback = nil
|
||||||
}
|
connectionCallback?(connection)
|
||||||
|
connectionCallback = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func didDisconnectNFC(_: YKFNFCConnection, error _: Error?) {
|
public func didDisconnectNFC(_ connection: YKFNFCConnection, error: Error?) {
|
||||||
nfcConnection = nil
|
nfcConnection = nil
|
||||||
|
nfcConnectionCallback = nil
|
||||||
|
connectionCallback = nil
|
||||||
|
disconnectionCallback?(connection, error)
|
||||||
|
disconnectionCallback = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func didFailConnectingNFC(_ error: Error) {
|
||||||
|
nfcConnectionCallback = nil
|
||||||
|
connectionCallback = nil
|
||||||
|
disconnectionCallback?(nil, error)
|
||||||
|
disconnectionCallback = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func didConnectAccessory(_ connection: YKFAccessoryConnection) {
|
public func didConnectAccessory(_ connection: YKFAccessoryConnection) {
|
||||||
accessoryConnection = connection
|
accessoryConnection = connection
|
||||||
|
accessoryConnectionCallback?(connection)
|
||||||
|
accessoryConnectionCallback = nil
|
||||||
|
connectionCallback?(connection)
|
||||||
|
connectionCallback = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func didDisconnectAccessory(_: YKFAccessoryConnection, error _: Error?) {
|
public func didDisconnectAccessory(_ connection: YKFAccessoryConnection, error: Error?) {
|
||||||
accessoryConnection = nil
|
accessoryConnection = nil
|
||||||
|
accessoryConnectionCallback = nil
|
||||||
|
connectionCallback = nil
|
||||||
|
disconnectionCallback?(connection, error)
|
||||||
|
disconnectionCallback = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func didFailConnectingNFC(_ error: Error) {
|
public func didConnectSmartCard(_ connection: YKFSmartCardConnection) {
|
||||||
if let callback = cancellationCallback {
|
smartCardConnection = connection
|
||||||
callback(error)
|
smartCardConnectionCallback?(connection)
|
||||||
}
|
smartCardConnectionCallback = nil
|
||||||
|
connectionCallback?(connection)
|
||||||
|
connectionCallback = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func didDisconnectSmartCard(_ connection: YKFSmartCardConnection, error: Error?) {
|
||||||
|
smartCardConnection = nil
|
||||||
|
smartCardConnectionCallback = nil
|
||||||
|
connectionCallback = nil
|
||||||
|
disconnectionCallback?(connection, error)
|
||||||
|
disconnectionCallback = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue