Launch KBS into the sun
This commit is contained in:
parent
48eb400414
commit
e3d39d9fc0
@ -783,7 +783,6 @@
|
||||
6612780D2996BD0300A1D5A1 /* RegistrationCoordinatorTestShims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 661278092996BAB400A1D5A1 /* RegistrationCoordinatorTestShims.swift */; };
|
||||
661278112996BE0C00A1D5A1 /* TestSchedulers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 661278102996BE0C00A1D5A1 /* TestSchedulers.swift */; };
|
||||
661278132996BE3400A1D5A1 /* TestScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 661278122996BE3400A1D5A1 /* TestScheduler.swift */; };
|
||||
66138FAB2982EE5F002E0CFE /* KeyBackupServiceTestShims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66138FAA2982EE5F002E0CFE /* KeyBackupServiceTestShims.swift */; };
|
||||
66138FB6298326C7002E0CFE /* SecureValueRecovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66138FB5298326C7002E0CFE /* SecureValueRecovery.swift */; };
|
||||
661396AB28BD53EF00E0C4DF /* HiddenStoryHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 661396AA28BD53EF00E0C4DF /* HiddenStoryHeaderCell.swift */; };
|
||||
661396AD28BE74DC00E0C4DF /* ChainedPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 661396AC28BE74DC00E0C4DF /* ChainedPromise.swift */; };
|
||||
@ -797,7 +796,6 @@
|
||||
662AC92B2A4A4D04009E2D5F /* SpoilerAnimationTestController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662AC92A2A4A4D04009E2D5F /* SpoilerAnimationTestController.swift */; };
|
||||
662C44092A1567E4001F83E2 /* svr2.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662C44082A1567E4001F83E2 /* svr2.pb.swift */; };
|
||||
662C440B2A156DF7001F83E2 /* SecureValueRecovery2Impl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662C440A2A156DF7001F83E2 /* SecureValueRecovery2Impl.swift */; };
|
||||
662C440F2A17DB8A001F83E2 /* OrchestratingSVRImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662C440E2A17DB8A001F83E2 /* OrchestratingSVRImpl.swift */; };
|
||||
662C44172A1D21D7001F83E2 /* SecureValueRecovery2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662C44152A1D2101001F83E2 /* SecureValueRecovery2Tests.swift */; };
|
||||
663BA3182A4B8595004B9A43 /* SpoilerRenderState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663BA3172A4B8595004B9A43 /* SpoilerRenderState.swift */; };
|
||||
663BA31C2A4C9997004B9A43 /* safety-numbers.json in Resources */ = {isa = PBXBuildFile; fileRef = 663BA31B2A4C9997004B9A43 /* safety-numbers.json */; };
|
||||
@ -857,7 +855,6 @@
|
||||
6673FF702978C40300F96CFD /* SVRAuthCredentialStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6673FF6F2978C40300F96CFD /* SVRAuthCredentialStorage.swift */; };
|
||||
6673FF722979B33800F96CFD /* SVRAuthCredentialStorageImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6673FF712979B33800F96CFD /* SVRAuthCredentialStorageImpl.swift */; };
|
||||
6673FF752979F87500F96CFD /* SVRAuthCredentialStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6673FF742979F87500F96CFD /* SVRAuthCredentialStorageTests.swift */; };
|
||||
6673FF81297B3A5000F96CFD /* KeyBackupServiceShims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6673FF80297B3A5000F96CFD /* KeyBackupServiceShims.swift */; };
|
||||
6673FF87297B694C00F96CFD /* DB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6673FF86297B694C00F96CFD /* DB.swift */; };
|
||||
6673FF89297B6AF800F96CFD /* DBTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6673FF88297B6AF800F96CFD /* DBTransaction.swift */; };
|
||||
6673FF8B297B6FA800F96CFD /* SDSDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6673FF8A297B6FA800F96CFD /* SDSDB.swift */; };
|
||||
@ -915,8 +912,6 @@
|
||||
66C2B1362A0DB02E008DDE72 /* SVRUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B1352A0DB02E008DDE72 /* SVRUtil.swift */; };
|
||||
66C2B1382A0DB6A9008DDE72 /* SVRAuthCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B1372A0DB6A9008DDE72 /* SVRAuthCredential.swift */; };
|
||||
66C2B13D2A0E9116008DDE72 /* SVR2AuthCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B13C2A0E9116008DDE72 /* SVR2AuthCredential.swift */; };
|
||||
66C2B13F2A12B019008DDE72 /* KBSAuthCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B1392A0E8D91008DDE72 /* KBSAuthCredential.swift */; };
|
||||
66C2B1412A12B0E6008DDE72 /* OrchestratingSVRAuthCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B1402A12B0E6008DDE72 /* OrchestratingSVRAuthCredential.swift */; };
|
||||
66C2B1492A13E2A0008DDE72 /* SgxWebsocketConnectionFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B1482A13E2A0008DDE72 /* SgxWebsocketConnectionFactory.swift */; };
|
||||
66C2B14B2A13E2AC008DDE72 /* SgxWebsocketConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B14A2A13E2AC008DDE72 /* SgxWebsocketConnection.swift */; };
|
||||
66C2B14D2A13E2C7008DDE72 /* SgxWebsocketConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B14C2A13E2C7008DDE72 /* SgxWebsocketConfigurator.swift */; };
|
||||
@ -1574,22 +1569,18 @@
|
||||
F942624E289B1B5500460798 /* SDSDatabaseStorageObservationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261DF289B1B5400460798 /* SDSDatabaseStorageObservationTest.swift */; };
|
||||
F9426250289B1B5500460798 /* SSKSignedPreKeyStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261E1289B1B5400460798 /* SSKSignedPreKeyStoreTest.swift */; };
|
||||
F9426251289B1B5500460798 /* GroupModelsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261E3289B1B5400460798 /* GroupModelsTest.swift */; };
|
||||
F9426252289B1B5500460798 /* kbs_vectors.json in Resources */ = {isa = PBXBuildFile; fileRef = F94261E5289B1B5400460798 /* kbs_vectors.json */; };
|
||||
F9426253289B1B5500460798 /* OWSErrorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261E6289B1B5400460798 /* OWSErrorTest.swift */; };
|
||||
F9426254289B1B5500460798 /* FeatureFlagsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261E7289B1B5400460798 /* FeatureFlagsTests.swift */; };
|
||||
F9426255289B1B5500460798 /* UnfairLockTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261E8289B1B5400460798 /* UnfairLockTest.swift */; };
|
||||
F9426256289B1B5500460798 /* NSData+ImageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261E9289B1B5400460798 /* NSData+ImageTest.swift */; };
|
||||
F9426257289B1B5500460798 /* KeyBackupServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261EA289B1B5400460798 /* KeyBackupServiceTests.swift */; };
|
||||
F9426258289B1B5500460798 /* TSMessageStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F94261EB289B1B5400460798 /* TSMessageStorageTests.m */; };
|
||||
F9426259289B1B5500460798 /* RemoteConfigManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261EC289B1B5400460798 /* RemoteConfigManagerTests.swift */; };
|
||||
F942625B289B1B5500460798 /* OWSFormatTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261EE289B1B5400460798 /* OWSFormatTest.swift */; };
|
||||
F942625D289B1B5500460798 /* RefineryTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261F0289B1B5400460798 /* RefineryTest.swift */; };
|
||||
F942625F289B1B5500460798 /* LRUCacheTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261F2289B1B5400460798 /* LRUCacheTest.swift */; };
|
||||
F9426260289B1B5500460798 /* kbs_storage_service_encryption_vectors.json in Resources */ = {isa = PBXBuildFile; fileRef = F94261F3289B1B5400460798 /* kbs_storage_service_encryption_vectors.json */; };
|
||||
F9426261289B1B5500460798 /* AppVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261F4289B1B5400460798 /* AppVersionTests.swift */; };
|
||||
F9426263289B1B5500460798 /* DeviceNamesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261F6289B1B5400460798 /* DeviceNamesTest.swift */; };
|
||||
F9426265289B1B5500460798 /* Date+SSKTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261F8289B1B5400460798 /* Date+SSKTest.swift */; };
|
||||
F9426266289B1B5500460798 /* kbs_pin_sanitation_vectors.json in Resources */ = {isa = PBXBuildFile; fileRef = F94261F9289B1B5400460798 /* kbs_pin_sanitation_vectors.json */; };
|
||||
F9426267289B1B5500460798 /* DispatchQueue+OWSTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261FA289B1B5400460798 /* DispatchQueue+OWSTest.swift */; };
|
||||
F9426268289B1B5500460798 /* OWSOperationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261FB289B1B5400460798 /* OWSOperationTest.swift */; };
|
||||
F9426269289B1B5500460798 /* MathOWSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261FC289B1B5400460798 /* MathOWSTests.swift */; };
|
||||
@ -2226,7 +2217,6 @@
|
||||
F9C5CDEA289453B400548EEE /* TestModel.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5CB18289453B200548EEE /* TestModel.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F9C5CDEB289453B400548EEE /* JobQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CB19289453B200548EEE /* JobQueue.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
|
||||
F9C5CDEC289453B400548EEE /* FunctionalUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CB1A289453B200548EEE /* FunctionalUtil.m */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
|
||||
F9C5CDED289453B400548EEE /* KeyBackupServiceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CB1B289453B200548EEE /* KeyBackupServiceImpl.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
|
||||
F9C5CDEE289453B400548EEE /* SDS+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CB1C289453B200548EEE /* SDS+SSK.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
|
||||
F9C5CDEF289453B400548EEE /* PinnedThreadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CB1D289453B200548EEE /* PinnedThreadManager.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
|
||||
F9C5CDF1289453B400548EEE /* NSRegularExpression+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CB1F289453B200548EEE /* NSRegularExpression+SSK.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
|
||||
@ -3370,7 +3360,6 @@
|
||||
6612780B2996BC2900A1D5A1 /* InMemoryKeyValueStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemoryKeyValueStore.swift; sourceTree = "<group>"; };
|
||||
661278102996BE0C00A1D5A1 /* TestSchedulers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSchedulers.swift; sourceTree = "<group>"; };
|
||||
661278122996BE3400A1D5A1 /* TestScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestScheduler.swift; sourceTree = "<group>"; };
|
||||
66138FAA2982EE5F002E0CFE /* KeyBackupServiceTestShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupServiceTestShims.swift; sourceTree = "<group>"; };
|
||||
66138FAE2982F4C4002E0CFE /* MockDBV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDBV2.swift; sourceTree = "<group>"; };
|
||||
66138FB5298326C7002E0CFE /* SecureValueRecovery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureValueRecovery.swift; sourceTree = "<group>"; };
|
||||
661396AA28BD53EF00E0C4DF /* HiddenStoryHeaderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HiddenStoryHeaderCell.swift; sourceTree = "<group>"; };
|
||||
@ -3385,7 +3374,6 @@
|
||||
662AC92A2A4A4D04009E2D5F /* SpoilerAnimationTestController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpoilerAnimationTestController.swift; sourceTree = "<group>"; };
|
||||
662C44082A1567E4001F83E2 /* svr2.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = svr2.pb.swift; sourceTree = "<group>"; };
|
||||
662C440A2A156DF7001F83E2 /* SecureValueRecovery2Impl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureValueRecovery2Impl.swift; sourceTree = "<group>"; };
|
||||
662C440E2A17DB8A001F83E2 /* OrchestratingSVRImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrchestratingSVRImpl.swift; sourceTree = "<group>"; };
|
||||
662C44152A1D2101001F83E2 /* SecureValueRecovery2Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureValueRecovery2Tests.swift; sourceTree = "<group>"; };
|
||||
663BA3172A4B8595004B9A43 /* SpoilerRenderState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpoilerRenderState.swift; sourceTree = "<group>"; };
|
||||
663BA31B2A4C9997004B9A43 /* safety-numbers.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "safety-numbers.json"; sourceTree = "<group>"; };
|
||||
@ -3446,7 +3434,6 @@
|
||||
6673FF6F2978C40300F96CFD /* SVRAuthCredentialStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVRAuthCredentialStorage.swift; sourceTree = "<group>"; };
|
||||
6673FF712979B33800F96CFD /* SVRAuthCredentialStorageImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVRAuthCredentialStorageImpl.swift; sourceTree = "<group>"; };
|
||||
6673FF742979F87500F96CFD /* SVRAuthCredentialStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVRAuthCredentialStorageTests.swift; sourceTree = "<group>"; };
|
||||
6673FF80297B3A5000F96CFD /* KeyBackupServiceShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupServiceShims.swift; sourceTree = "<group>"; };
|
||||
6673FF86297B694C00F96CFD /* DB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DB.swift; sourceTree = "<group>"; };
|
||||
6673FF88297B6AF800F96CFD /* DBTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBTransaction.swift; sourceTree = "<group>"; };
|
||||
6673FF8A297B6FA800F96CFD /* SDSDB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDSDB.swift; sourceTree = "<group>"; };
|
||||
@ -3505,9 +3492,7 @@
|
||||
66C2B1302A05D28A008DDE72 /* TSRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRequest.swift; sourceTree = "<group>"; };
|
||||
66C2B1352A0DB02E008DDE72 /* SVRUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVRUtil.swift; sourceTree = "<group>"; };
|
||||
66C2B1372A0DB6A9008DDE72 /* SVRAuthCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVRAuthCredential.swift; sourceTree = "<group>"; };
|
||||
66C2B1392A0E8D91008DDE72 /* KBSAuthCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KBSAuthCredential.swift; sourceTree = "<group>"; };
|
||||
66C2B13C2A0E9116008DDE72 /* SVR2AuthCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVR2AuthCredential.swift; sourceTree = "<group>"; };
|
||||
66C2B1402A12B0E6008DDE72 /* OrchestratingSVRAuthCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrchestratingSVRAuthCredential.swift; sourceTree = "<group>"; };
|
||||
66C2B1482A13E2A0008DDE72 /* SgxWebsocketConnectionFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SgxWebsocketConnectionFactory.swift; sourceTree = "<group>"; };
|
||||
66C2B14A2A13E2AC008DDE72 /* SgxWebsocketConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SgxWebsocketConnection.swift; sourceTree = "<group>"; };
|
||||
66C2B14C2A13E2C7008DDE72 /* SgxWebsocketConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SgxWebsocketConfigurator.swift; sourceTree = "<group>"; };
|
||||
@ -4263,22 +4248,18 @@
|
||||
F94261DF289B1B5400460798 /* SDSDatabaseStorageObservationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SDSDatabaseStorageObservationTest.swift; sourceTree = "<group>"; };
|
||||
F94261E1289B1B5400460798 /* SSKSignedPreKeyStoreTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKSignedPreKeyStoreTest.swift; sourceTree = "<group>"; };
|
||||
F94261E3289B1B5400460798 /* GroupModelsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupModelsTest.swift; sourceTree = "<group>"; };
|
||||
F94261E5289B1B5400460798 /* kbs_vectors.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = kbs_vectors.json; sourceTree = "<group>"; };
|
||||
F94261E6289B1B5400460798 /* OWSErrorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSErrorTest.swift; sourceTree = "<group>"; };
|
||||
F94261E7289B1B5400460798 /* FeatureFlagsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureFlagsTests.swift; sourceTree = "<group>"; };
|
||||
F94261E8289B1B5400460798 /* UnfairLockTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnfairLockTest.swift; sourceTree = "<group>"; };
|
||||
F94261E9289B1B5400460798 /* NSData+ImageTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSData+ImageTest.swift"; sourceTree = "<group>"; };
|
||||
F94261EA289B1B5400460798 /* KeyBackupServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupServiceTests.swift; sourceTree = "<group>"; };
|
||||
F94261EB289B1B5400460798 /* TSMessageStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessageStorageTests.m; sourceTree = "<group>"; };
|
||||
F94261EC289B1B5400460798 /* RemoteConfigManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteConfigManagerTests.swift; sourceTree = "<group>"; };
|
||||
F94261EE289B1B5400460798 /* OWSFormatTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSFormatTest.swift; sourceTree = "<group>"; };
|
||||
F94261F0289B1B5400460798 /* RefineryTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefineryTest.swift; sourceTree = "<group>"; };
|
||||
F94261F2289B1B5400460798 /* LRUCacheTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LRUCacheTest.swift; sourceTree = "<group>"; };
|
||||
F94261F3289B1B5400460798 /* kbs_storage_service_encryption_vectors.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = kbs_storage_service_encryption_vectors.json; sourceTree = "<group>"; };
|
||||
F94261F4289B1B5400460798 /* AppVersionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppVersionTests.swift; sourceTree = "<group>"; };
|
||||
F94261F6289B1B5400460798 /* DeviceNamesTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceNamesTest.swift; sourceTree = "<group>"; };
|
||||
F94261F8289B1B5400460798 /* Date+SSKTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+SSKTest.swift"; sourceTree = "<group>"; };
|
||||
F94261F9289B1B5400460798 /* kbs_pin_sanitation_vectors.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = kbs_pin_sanitation_vectors.json; sourceTree = "<group>"; };
|
||||
F94261FA289B1B5400460798 /* DispatchQueue+OWSTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+OWSTest.swift"; sourceTree = "<group>"; };
|
||||
F94261FB289B1B5400460798 /* OWSOperationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSOperationTest.swift; sourceTree = "<group>"; };
|
||||
F94261FC289B1B5400460798 /* MathOWSTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MathOWSTests.swift; sourceTree = "<group>"; };
|
||||
@ -4915,7 +4896,6 @@
|
||||
F9C5CB18289453B200548EEE /* TestModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestModel.h; sourceTree = "<group>"; };
|
||||
F9C5CB19289453B200548EEE /* JobQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JobQueue.swift; sourceTree = "<group>"; };
|
||||
F9C5CB1A289453B200548EEE /* FunctionalUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FunctionalUtil.m; sourceTree = "<group>"; };
|
||||
F9C5CB1B289453B200548EEE /* KeyBackupServiceImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupServiceImpl.swift; sourceTree = "<group>"; };
|
||||
F9C5CB1C289453B200548EEE /* SDS+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SDS+SSK.swift"; sourceTree = "<group>"; };
|
||||
F9C5CB1D289453B200548EEE /* PinnedThreadManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinnedThreadManager.swift; sourceTree = "<group>"; };
|
||||
F9C5CB1F289453B200548EEE /* NSRegularExpression+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+SSK.swift"; sourceTree = "<group>"; };
|
||||
@ -6904,9 +6884,7 @@
|
||||
6673FF6A2978B5B900F96CFD /* SecureValueRecovery */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
66C2B1342A0DA16F008DDE72 /* KeyBackupService */,
|
||||
6640DD612ACDD5CD00CE9A8C /* LocalStorage */,
|
||||
66C2B13E2A0E9147008DDE72 /* Orchestrating */,
|
||||
66C2B13B2A0E9108008DDE72 /* SVR2 */,
|
||||
666654202AD0B03F00B23B32 /* MasterKeySyncManager.swift */,
|
||||
66138FB5298326C7002E0CFE /* SecureValueRecovery.swift */,
|
||||
@ -6919,18 +6897,6 @@
|
||||
path = SecureValueRecovery;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6673FF732979F7A400F96CFD /* KeyBackupService */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F94261F9289B1B5400460798 /* kbs_pin_sanitation_vectors.json */,
|
||||
F94261F3289B1B5400460798 /* kbs_storage_service_encryption_vectors.json */,
|
||||
F94261E5289B1B5400460798 /* kbs_vectors.json */,
|
||||
F94261EA289B1B5400460798 /* KeyBackupServiceTests.swift */,
|
||||
66138FAA2982EE5F002E0CFE /* KeyBackupServiceTestShims.swift */,
|
||||
);
|
||||
path = KeyBackupService;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6673FF82297B659D00F96CFD /* SDSKeyValueStore */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -7030,16 +6996,6 @@
|
||||
path = SpoilerRendering;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
66C2B1342A0DA16F008DDE72 /* KeyBackupService */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
66C2B1392A0E8D91008DDE72 /* KBSAuthCredential.swift */,
|
||||
F9C5CB1B289453B200548EEE /* KeyBackupServiceImpl.swift */,
|
||||
6673FF80297B3A5000F96CFD /* KeyBackupServiceShims.swift */,
|
||||
);
|
||||
path = KeyBackupService;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
66C2B13B2A0E9108008DDE72 /* SVR2 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -7051,19 +7007,9 @@
|
||||
path = SVR2;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
66C2B13E2A0E9147008DDE72 /* Orchestrating */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
66C2B1402A12B0E6008DDE72 /* OrchestratingSVRAuthCredential.swift */,
|
||||
662C440E2A17DB8A001F83E2 /* OrchestratingSVRImpl.swift */,
|
||||
);
|
||||
path = Orchestrating;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
66C2B1422A12E043008DDE72 /* SecureValueRecovery */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6673FF732979F7A400F96CFD /* KeyBackupService */,
|
||||
662C44142A1D20EB001F83E2 /* SVR2 */,
|
||||
6673FF742979F87500F96CFD /* SVRAuthCredentialStorageTests.swift */,
|
||||
);
|
||||
@ -10842,9 +10788,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F9426266289B1B5500460798 /* kbs_pin_sanitation_vectors.json in Resources */,
|
||||
F9426260289B1B5500460798 /* kbs_storage_service_encryption_vectors.json in Resources */,
|
||||
F9426252289B1B5500460798 /* kbs_vectors.json in Resources */,
|
||||
F942628B289B1B5600460798 /* sample-sticker.encrypted in Resources */,
|
||||
F942628C289B1B5600460798 /* sample-sticker.webp in Resources */,
|
||||
F908AA7D28CE629700472E68 /* test-apng.png in Resources */,
|
||||
@ -12542,11 +12485,8 @@
|
||||
F9C5CDEB289453B400548EEE /* JobQueue.swift in Sources */,
|
||||
D9AE0ADD2918B2960063488B /* JobRecord+Columns.swift in Sources */,
|
||||
D9B8541229137C150058F97B /* JobRecord.swift in Sources */,
|
||||
66C2B13F2A12B019008DDE72 /* KBSAuthCredential.swift in Sources */,
|
||||
F9C5CC93289453B300548EEE /* KeyBackup.pb.swift in Sources */,
|
||||
F9C5CC9B289453B300548EEE /* KeyBackupProto.swift in Sources */,
|
||||
F9C5CDED289453B400548EEE /* KeyBackupServiceImpl.swift in Sources */,
|
||||
6673FF81297B3A5000F96CFD /* KeyBackupServiceShims.swift in Sources */,
|
||||
6698FC0D297EFE21004EFC30 /* KeyValueStore.swift in Sources */,
|
||||
6698FC0F297F03FE004EFC30 /* KeyValueStoreFactory.swift in Sources */,
|
||||
F9C5CC1A289453B300548EEE /* KnownStickerPack+SDS.swift in Sources */,
|
||||
@ -12629,8 +12569,6 @@
|
||||
F9C5CE21289453B400548EEE /* NSUserDefaults+OWS.m in Sources */,
|
||||
F9C5CD1C289453B300548EEE /* ObservedDatabaseChanges.swift in Sources */,
|
||||
F9C5CE16289453B400548EEE /* OffMainThreadTimer.swift in Sources */,
|
||||
66C2B1412A12B0E6008DDE72 /* OrchestratingSVRAuthCredential.swift in Sources */,
|
||||
662C440F2A17DB8A001F83E2 /* OrchestratingSVRImpl.swift in Sources */,
|
||||
F9C5CE43289453B400548EEE /* OrderedDictionary.swift in Sources */,
|
||||
F9C5CE1E289453B400548EEE /* OrderedSet.swift in Sources */,
|
||||
F9C5CD9F289453B400548EEE /* OutageDetection.swift in Sources */,
|
||||
@ -13170,8 +13108,6 @@
|
||||
D979CC4C2AD4DECB006AAC49 /* IndividualCallRecordManagerTest.swift in Sources */,
|
||||
F942624D289B1B5500460798 /* InteractionFinderTest.swift in Sources */,
|
||||
D9B95A9629E6830B00D7CB95 /* JobRecordTest.swift in Sources */,
|
||||
F9426257289B1B5500460798 /* KeyBackupServiceTests.swift in Sources */,
|
||||
66138FAB2982EE5F002E0CFE /* KeyBackupServiceTestShims.swift in Sources */,
|
||||
D93EA1212A0596E400579C6F /* LearnMyOwnPniManagerTest.swift in Sources */,
|
||||
D9F399B42A96E54C001599EC /* LinkedDevicePniKeyManagerTest.swift in Sources */,
|
||||
50D5E2432980B53000899660 /* LinkValidatorTest.swift in Sources */,
|
||||
|
||||
@ -22,7 +22,6 @@ extension RegistrationCoordinatorImpl {
|
||||
public typealias ProfileManager = _RegistrationCoordinator_ProfileManagerShim
|
||||
public typealias PushRegistrationManager = _RegistrationCoordinator_PushRegistrationManagerShim
|
||||
public typealias ReceiptManager = _RegistrationCoordinator_ReceiptManagerShim
|
||||
public typealias RemoteConfig = _RegistrationCoordinator_RemoteConfigShim
|
||||
public typealias UDManager = _RegistrationCoordinator_UDManagerShim
|
||||
}
|
||||
public enum Wrappers {
|
||||
@ -36,7 +35,6 @@ extension RegistrationCoordinatorImpl {
|
||||
public typealias ProfileManager = _RegistrationCoordinator_ProfileManagerWrapper
|
||||
public typealias PushRegistrationManager = _RegistrationCoordinator_PushRegistrationManagerWrapper
|
||||
public typealias ReceiptManager = _RegistrationCoordinator_ReceiptManagerWrapper
|
||||
public typealias RemoteConfig = _RegistrationCoordinator_RemoteConfigWrapper
|
||||
public typealias UDManager = _RegistrationCoordinator_UDManagerWrapper
|
||||
}
|
||||
}
|
||||
@ -389,25 +387,6 @@ public class _RegistrationCoordinator_ReceiptManagerWrapper: _RegistrationCoordi
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RemoteConfig
|
||||
|
||||
public protocol _RegistrationCoordinator_RemoteConfigShim {
|
||||
|
||||
func refreshRemoteConfig(account: AuthedAccount) -> Promise<RemoteConfig.SVRConfiguration>
|
||||
}
|
||||
|
||||
public class _RegistrationCoordinator_RemoteConfigWrapper: _RegistrationCoordinator_RemoteConfigShim {
|
||||
|
||||
private let remoteConfig: RemoteConfigManager
|
||||
public init(_ remoteConfig: RemoteConfigManager) { self.remoteConfig = remoteConfig }
|
||||
|
||||
public func refreshRemoteConfig(account: AuthedAccount) -> Promise<RemoteConfig.SVRConfiguration> {
|
||||
return firstly(on: DispatchQueue.main) { [remoteConfig] in
|
||||
return remoteConfig.refresh(account: account)
|
||||
}.map(on: SyncScheduler(), \.svrConfiguration)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UDManager
|
||||
|
||||
public protocol _RegistrationCoordinator_UDManagerShim {
|
||||
|
||||
@ -25,7 +25,6 @@ public struct RegistrationCoordinatorDependencies {
|
||||
public let pushRegistrationManager: RegistrationCoordinatorImpl.Shims.PushRegistrationManager
|
||||
public let receiptManager: RegistrationCoordinatorImpl.Shims.ReceiptManager
|
||||
public let registrationStateChangeManager: RegistrationStateChangeManager
|
||||
public let remoteConfig: RegistrationCoordinatorImpl.Shims.RemoteConfig
|
||||
public let schedulers: Schedulers
|
||||
public let sessionManager: RegistrationSessionManager
|
||||
public let signalService: OWSSignalServiceProtocol
|
||||
@ -55,7 +54,6 @@ public struct RegistrationCoordinatorDependencies {
|
||||
pushRegistrationManager: RegistrationCoordinatorImpl.Wrappers.PushRegistrationManager(object.pushRegistrationManager),
|
||||
receiptManager: RegistrationCoordinatorImpl.Wrappers.ReceiptManager(object.receiptManager),
|
||||
registrationStateChangeManager: DependenciesBridge.shared.registrationStateChangeManager,
|
||||
remoteConfig: RegistrationCoordinatorImpl.Wrappers.RemoteConfig(object.remoteConfigManager),
|
||||
schedulers: DependenciesBridge.shared.schedulers,
|
||||
sessionManager: DependenciesBridge.shared.registrationSessionManager,
|
||||
signalService: object.signalService,
|
||||
|
||||
@ -10,58 +10,6 @@ extension RegistrationCoordinatorImpl {
|
||||
|
||||
enum Service {
|
||||
|
||||
enum KBSAuthCheckResponse {
|
||||
case success(RegistrationServiceResponses.KBSAuthCheckResponse)
|
||||
case networkError
|
||||
case genericError
|
||||
}
|
||||
|
||||
static func makeKBSAuthCheckRequest(
|
||||
e164: E164,
|
||||
candidateCredentials: [KBSAuthCredential],
|
||||
signalService: OWSSignalServiceProtocol,
|
||||
schedulers: Schedulers
|
||||
) -> Guarantee<KBSAuthCheckResponse> {
|
||||
let request = RegistrationRequestFactory.kbsAuthCredentialCheckRequest(
|
||||
e164: e164,
|
||||
credentials: candidateCredentials
|
||||
)
|
||||
return makeRequest(
|
||||
request,
|
||||
signalService: signalService,
|
||||
schedulers: schedulers,
|
||||
handler: self.handleKBSAuthCheckResponse(statusCode:retryAfterHeader:bodyData:),
|
||||
fallbackError: .genericError,
|
||||
networkFailureError: .networkError
|
||||
)
|
||||
}
|
||||
|
||||
private static func handleKBSAuthCheckResponse(
|
||||
statusCode: Int,
|
||||
retryAfterHeader: String?,
|
||||
bodyData: Data?
|
||||
) -> KBSAuthCheckResponse {
|
||||
let statusCode = RegistrationServiceResponses.KBSAuthCheckResponseCodes(rawValue: statusCode)
|
||||
switch statusCode {
|
||||
case .success:
|
||||
guard let bodyData else {
|
||||
Logger.warn("Got empty KBS auth check response")
|
||||
return .genericError
|
||||
}
|
||||
guard let response = try? JSONDecoder().decode(RegistrationServiceResponses.KBSAuthCheckResponse.self, from: bodyData) else {
|
||||
Logger.warn("Unable to parse KBS auth check response from response")
|
||||
return .genericError
|
||||
}
|
||||
|
||||
return .success(response)
|
||||
case .malformedRequest, .invalidJSON:
|
||||
Logger.error("Malformed kbs auth check request")
|
||||
return .genericError
|
||||
case .none, .unexpectedError:
|
||||
return .genericError
|
||||
}
|
||||
}
|
||||
|
||||
enum SVR2AuthCheckResponse {
|
||||
case success(RegistrationServiceResponses.SVR2AuthCheckResponse)
|
||||
case networkError
|
||||
|
||||
@ -501,7 +501,6 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
|
||||
// candidate credentials, which may not
|
||||
// be valid, or may not correspond with the current e164.
|
||||
var kbsAuthCredentialCandidates: [KBSAuthCredential]?
|
||||
var svr2AuthCredentialCandidates: [SVR2AuthCredential]?
|
||||
var svrAuthCredential: SVRAuthCredential?
|
||||
// If we had SVR backups before registration even began.
|
||||
@ -539,7 +538,6 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
// Every time we go through registration, we should back up our SVR master
|
||||
// secret's random bytes to SVR. Its safer to do this more than it is to do
|
||||
// it less, so keeping this state in memory.
|
||||
var svrRemoteConfig: RemoteConfig.SVRConfiguration?
|
||||
var hasBackedUpToSVR = false
|
||||
var didSkipSVRBackup = false
|
||||
var shouldBackUpToSVR: Bool {
|
||||
@ -665,49 +663,30 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
case none
|
||||
|
||||
/// We tried to register and got reglocked; we have to
|
||||
/// recover from SVR1/KBS with the credential given.
|
||||
/// recover from SVR2 with the credential given.
|
||||
case reglocked(credential: SVRAuthCredential, expirationDate: Date)
|
||||
|
||||
struct SVRAuthCredential: Codable, Equatable {
|
||||
let kbs: KBSAuthCredential?
|
||||
/// In a prior life, this object could contain either a KBS(SVR1) credential or an SVR2 credential.
|
||||
/// For backwards compatibility, therefore, the SVR2 credential might be nil.
|
||||
let svr2: SVR2AuthCredential?
|
||||
|
||||
private init(kbs: KBSAuthCredential?, svr2: SVR2AuthCredential?) {
|
||||
self.kbs = kbs
|
||||
private init(svr2: SVR2AuthCredential?) {
|
||||
self.svr2 = svr2
|
||||
}
|
||||
|
||||
init(kbs: KBSAuthCredential, svr2: SVR2AuthCredential) {
|
||||
self.kbs = kbs
|
||||
self.svr2 = svr2
|
||||
}
|
||||
|
||||
init(kbs: KBSAuthCredential) {
|
||||
self.kbs = kbs
|
||||
self.svr2 = nil
|
||||
}
|
||||
|
||||
init(svr2: SVR2AuthCredential) {
|
||||
self.kbs = nil
|
||||
self.svr2 = svr2
|
||||
}
|
||||
|
||||
#if TESTABLE_BUILD
|
||||
static func testOnly(kbs: KBSAuthCredential?, svr2: SVR2AuthCredential?) -> Self {
|
||||
return .init(kbs: kbs, svr2: svr2)
|
||||
static func testOnly(svr2: SVR2AuthCredential?) -> Self {
|
||||
return .init(svr2: svr2)
|
||||
}
|
||||
#endif
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
// Initially this field held a raw KBSAuthCredential; try to
|
||||
// decode that first.
|
||||
if let rawKbs = try? KBSAuthCredential(from: decoder) {
|
||||
self.kbs = rawKbs
|
||||
self.svr2 = nil
|
||||
return
|
||||
}
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.kbs = try container.decodeIfPresent(KBSAuthCredential.self, forKey: .kbs)
|
||||
self.svr2 = try container.decodeIfPresent(SVR2AuthCredential.self, forKey: .svr2)
|
||||
}
|
||||
}
|
||||
@ -1056,7 +1035,7 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
/// We might have un-verified SVR auth credentials
|
||||
/// synced from another device; first we need to check them
|
||||
/// with the server and then potentially go to the svrAuthCredential path.
|
||||
case svrAuthCredentialCandidates([SVR2AuthCredential], [KBSAuthCredential])
|
||||
case svrAuthCredentialCandidates([SVR2AuthCredential])
|
||||
/// Verifying via SMS code using a `RegistrationSession`.
|
||||
/// Used as a fallback if the above paths are unavailable or fail.
|
||||
case session(RegistrationSession)
|
||||
@ -1115,12 +1094,14 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
// or proceed to reg recovery pw (if it succeeds) we must wipe this state.
|
||||
return .svrAuthCredential(credential)
|
||||
}
|
||||
if inMemoryState.svr2AuthCredentialCandidates?.isEmpty == false || inMemoryState.kbsAuthCredentialCandidates?.isEmpty == false {
|
||||
if
|
||||
let svr2AuthCredentialCandidates = inMemoryState.svr2AuthCredentialCandidates,
|
||||
!svr2AuthCredentialCandidates.isEmpty
|
||||
{
|
||||
// If we have un-vetted candidates, try checking those first
|
||||
// and then going to the svrAuthCredential path if one is valid.
|
||||
return .svrAuthCredentialCandidates(
|
||||
inMemoryState.svr2AuthCredentialCandidates ?? [],
|
||||
inMemoryState.kbsAuthCredentialCandidates ?? []
|
||||
svr2AuthCredentialCandidates
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1140,10 +1121,9 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
return nextStepForRegRecoveryPasswordPath(regRecoveryPw: password)
|
||||
case .svrAuthCredential(let credential):
|
||||
return nextStepForSVRAuthCredentialPath(svrAuthCredential: credential)
|
||||
case .svrAuthCredentialCandidates(let svr2Candidates, let kbsCandidates):
|
||||
case .svrAuthCredentialCandidates(let svr2Candidates):
|
||||
return nextStepForSVRAuthCredentialCandidatesPath(
|
||||
svr2AuthCredentialCandidates: svr2Candidates,
|
||||
kbsAuthCredentialCandidates: kbsCandidates
|
||||
svr2AuthCredentialCandidates: svr2Candidates
|
||||
)
|
||||
case .session(let session):
|
||||
return nextStepForSessionPath(session)
|
||||
@ -1306,7 +1286,6 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
// Its possible we tried svr2 and kbs has the right info, or vice versa, but this is all
|
||||
// best effort anyway; just fall back to session-based registration.
|
||||
deps.svrAuthCredentialStore.removeSVR2CredentialsForCurrentUser(tx)
|
||||
deps.svrAuthCredentialStore.removeKBSCredentialsForCurrentUser(tx)
|
||||
// Clear the SVR master key locally; we failed reglock so we know its wrong
|
||||
// and useless anyway.
|
||||
deps.svr.clearKeys(transaction: tx)
|
||||
@ -1347,10 +1326,7 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
db.write { tx in
|
||||
// We do want to clear out any credentials permanently; we know we
|
||||
// have to use the session path so credentials aren't helpful.
|
||||
if let kbsCredential = inMemoryState.svrAuthCredential?.kbs {
|
||||
deps.svrAuthCredentialStore.deleteInvalidCredentials([kbsCredential], tx)
|
||||
}
|
||||
if let svr2Credential = inMemoryState.svrAuthCredential?.svr2 {
|
||||
if let svr2Credential = inMemoryState.svrAuthCredential {
|
||||
deps.svrAuthCredentialStore.deleteInvalidCredentials([svr2Credential], tx)
|
||||
}
|
||||
}
|
||||
@ -1409,10 +1385,6 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
if svr2AuthCredentialCandidates.isEmpty.negated {
|
||||
inMemoryState.svr2AuthCredentialCandidates = svr2AuthCredentialCandidates
|
||||
}
|
||||
let kbsAuthCredentialCandidates: [KBSAuthCredential] = deps.svrAuthCredentialStore.getAuthCredentials(tx)
|
||||
if kbsAuthCredentialCandidates.isEmpty.negated {
|
||||
inMemoryState.kbsAuthCredentialCandidates = kbsAuthCredentialCandidates
|
||||
}
|
||||
}
|
||||
|
||||
private func wipeInMemoryStateToPreventSVRPathAttempts() {
|
||||
@ -1423,7 +1395,6 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
// either so we shouldn't bother trying.
|
||||
inMemoryState.svrAuthCredential = nil
|
||||
inMemoryState.svr2AuthCredentialCandidates = nil
|
||||
inMemoryState.kbsAuthCredentialCandidates = nil
|
||||
}
|
||||
|
||||
// MARK: - SVR Auth Credential Pathway
|
||||
@ -1476,33 +1447,18 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
exitConfiguration: self.pinCodeEntryExitConfiguration()
|
||||
)))
|
||||
case .backupMissing:
|
||||
|
||||
switch credential {
|
||||
case .svr2Only:
|
||||
// If we failed with just svr2, and we have kbs credentials,
|
||||
// try again but with kbs credentials available.
|
||||
guard
|
||||
let kbsAuthCredentialCandidates = self.inMemoryState.kbsAuthCredentialCandidates?.nilIfEmpty,
|
||||
let e164 = self.persistedState.e164
|
||||
else {
|
||||
fallthrough
|
||||
// If we are unable to talk to SVR, it got wiped and we can't
|
||||
// recover. Give it all up and wipe our SVR info.
|
||||
self.wipeInMemoryStateToPreventSVRPathAttempts()
|
||||
self.inMemoryState.pinFromUser = nil
|
||||
self.db.write { tx in
|
||||
self.updatePersistedState(tx) {
|
||||
$0.hasGivenUpTryingToRestoreWithSVR = true
|
||||
}
|
||||
Logger.info("Checking KBS credential since we had SVR2 only.")
|
||||
return self.makeKBSAuthCredentialCheckRequest(kbsAuthCredentialCandidates: kbsAuthCredentialCandidates, e164: e164)
|
||||
case .both, .kbsOnly:
|
||||
// If we are unable to talk to SVR, it got wiped and we can't
|
||||
// recover. Give it all up and wipe our SVR info.
|
||||
self.wipeInMemoryStateToPreventSVRPathAttempts()
|
||||
self.inMemoryState.pinFromUser = nil
|
||||
self.db.write { tx in
|
||||
self.updatePersistedState(tx) {
|
||||
$0.hasGivenUpTryingToRestoreWithSVR = true
|
||||
}
|
||||
}
|
||||
return .value(.pinAttemptsExhaustedWithoutReglock(
|
||||
.init(mode: .restoringRegistrationRecoveryPassword)
|
||||
))
|
||||
}
|
||||
return .value(.pinAttemptsExhaustedWithoutReglock(
|
||||
.init(mode: .restoringRegistrationRecoveryPassword)
|
||||
))
|
||||
|
||||
case .networkError:
|
||||
if retriesLeft > 0 {
|
||||
@ -1512,39 +1468,13 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
retriesLeft: retriesLeft - 1
|
||||
)
|
||||
}
|
||||
return self.retryRestoreSVRMasterSecretWithKBSIfNeededForAuthCredentialPath(
|
||||
credential: credential,
|
||||
fallbackResult: .showErrorSheet(.networkError)
|
||||
)
|
||||
return .value(.showErrorSheet(.networkError))
|
||||
case .genericError:
|
||||
return self.retryRestoreSVRMasterSecretWithKBSIfNeededForAuthCredentialPath(
|
||||
credential: credential,
|
||||
fallbackResult: .showErrorSheet(.genericError)
|
||||
)
|
||||
return .value(.showErrorSheet(.genericError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func retryRestoreSVRMasterSecretWithKBSIfNeededForAuthCredentialPath(
|
||||
credential: SVRAuthCredential,
|
||||
fallbackResult: RegistrationStep
|
||||
) -> Guarantee<RegistrationStep> {
|
||||
switch credential {
|
||||
case .both, .kbsOnly:
|
||||
return .value(fallbackResult)
|
||||
case .svr2Only:
|
||||
break
|
||||
}
|
||||
guard
|
||||
let kbsAuthCredentialCandidates = inMemoryState.kbsAuthCredentialCandidates?.nilIfEmpty,
|
||||
let e164 = persistedState.e164
|
||||
else {
|
||||
return .value(fallbackResult)
|
||||
}
|
||||
Logger.info("Checking KBS credential since we had SVR2 only.")
|
||||
return makeKBSAuthCredentialCheckRequest(kbsAuthCredentialCandidates: kbsAuthCredentialCandidates, e164: e164)
|
||||
}
|
||||
|
||||
private func loadLocalMasterKeyAndUpdateState(_ tx: DBWriteTransaction) {
|
||||
let regRecoveryPw = deps.svr.data(
|
||||
for: .registrationRecoveryPassword,
|
||||
@ -1567,108 +1497,19 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
// MARK: - SVR Auth Credential Candidates Pathway
|
||||
|
||||
private func nextStepForSVRAuthCredentialCandidatesPath(
|
||||
svr2AuthCredentialCandidates: [SVR2AuthCredential],
|
||||
kbsAuthCredentialCandidates: [KBSAuthCredential]
|
||||
svr2AuthCredentialCandidates: [SVR2AuthCredential]
|
||||
) -> Guarantee<RegistrationStep> {
|
||||
guard let e164 = persistedState.e164 else {
|
||||
// If we haven't entered a phone number but we have auth
|
||||
// credential candidates to check, enter it now.
|
||||
return .value(.phoneNumberEntry(phoneNumberEntryState()))
|
||||
}
|
||||
if !svr2AuthCredentialCandidates.isEmpty {
|
||||
return makeSVR2AuthCredentialCheckRequest(
|
||||
svr2AuthCredentialCandidates: svr2AuthCredentialCandidates,
|
||||
e164: e164
|
||||
)
|
||||
} else {
|
||||
return makeKBSAuthCredentialCheckRequest(
|
||||
kbsAuthCredentialCandidates: kbsAuthCredentialCandidates,
|
||||
e164: e164
|
||||
)
|
||||
}
|
||||
return makeSVR2AuthCredentialCheckRequest(
|
||||
svr2AuthCredentialCandidates: svr2AuthCredentialCandidates,
|
||||
e164: e164
|
||||
)
|
||||
}
|
||||
|
||||
/// This call is KBS specific; not generic to SVR 1 or 2.
|
||||
private func makeKBSAuthCredentialCheckRequest(
|
||||
kbsAuthCredentialCandidates: [KBSAuthCredential],
|
||||
e164: E164,
|
||||
retriesLeft: Int = Constants.networkErrorRetries
|
||||
) -> Guarantee<RegistrationStep> {
|
||||
return Service.makeKBSAuthCheckRequest(
|
||||
e164: e164,
|
||||
candidateCredentials: kbsAuthCredentialCandidates,
|
||||
signalService: deps.signalService,
|
||||
schedulers: schedulers
|
||||
).then(on: schedulers.main) { [weak self] response in
|
||||
guard let self else {
|
||||
return unretainedSelfError()
|
||||
}
|
||||
return self.handleKBSAuthCredentialCheckResponse(
|
||||
response,
|
||||
kbsAuthCredentialCandidates: kbsAuthCredentialCandidates,
|
||||
e164: e164,
|
||||
retriesLeft: retriesLeft
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleKBSAuthCredentialCheckResponse(
|
||||
_ response: Service.KBSAuthCheckResponse,
|
||||
kbsAuthCredentialCandidates: [KBSAuthCredential],
|
||||
e164: E164,
|
||||
retriesLeft: Int
|
||||
) -> Guarantee<RegistrationStep> {
|
||||
var matchedCredential: KBSAuthCredential?
|
||||
var credentialsToDelete = [KBSAuthCredential]()
|
||||
switch response {
|
||||
case .networkError:
|
||||
if retriesLeft > 0 {
|
||||
return makeKBSAuthCredentialCheckRequest(
|
||||
kbsAuthCredentialCandidates: kbsAuthCredentialCandidates,
|
||||
e164: e164,
|
||||
retriesLeft: retriesLeft - 1
|
||||
)
|
||||
}
|
||||
self.inMemoryState.kbsAuthCredentialCandidates = nil
|
||||
return self.nextStep()
|
||||
case .genericError:
|
||||
// If we failed to verify, wipe the candidates so we don't try again
|
||||
// and keep going.
|
||||
self.inMemoryState.kbsAuthCredentialCandidates = nil
|
||||
return self.nextStep()
|
||||
case .success(let response):
|
||||
for candidate in kbsAuthCredentialCandidates {
|
||||
let result: RegistrationServiceResponses.KBSAuthCheckResponse.Result? = response.result(for: candidate)
|
||||
switch result {
|
||||
case .match:
|
||||
matchedCredential = candidate
|
||||
case .notMatch:
|
||||
// Still valid, keep it around but don't use it.
|
||||
continue
|
||||
case .invalid, .none:
|
||||
credentialsToDelete.append(candidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Wipe the candidates so we don't re-check them.
|
||||
self.inMemoryState.kbsAuthCredentialCandidates = nil
|
||||
// If this is nil, the next time we call `nextStepForSVRAuthCredentialPath`
|
||||
// will just return an empty promise.
|
||||
switch inMemoryState.svrAuthCredential {
|
||||
case .none, .kbsOnly:
|
||||
self.inMemoryState.svrAuthCredential = matchedCredential.map { .kbsOnly($0) }
|
||||
case .svr2Only(let svr2Credential), .both(_, let svr2Credential):
|
||||
if let matchedCredential {
|
||||
self.inMemoryState.svrAuthCredential = .both(matchedCredential, svr2Credential)
|
||||
}
|
||||
}
|
||||
self.db.write { tx in
|
||||
self.deps.svrAuthCredentialStore.deleteInvalidCredentials(credentialsToDelete, tx)
|
||||
}
|
||||
return self.nextStep()
|
||||
}
|
||||
|
||||
/// This call is SVR2 specific; not generic to SVR 1 or 2.
|
||||
private func makeSVR2AuthCredentialCheckRequest(
|
||||
svr2AuthCredentialCandidates: [SVR2AuthCredential],
|
||||
e164: E164,
|
||||
@ -1734,15 +1575,8 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
self.inMemoryState.svr2AuthCredentialCandidates = nil
|
||||
// If this is nil, the next time we call `nextStepForSVRAuthCredentialPath`
|
||||
// will just return an empty promise.
|
||||
switch inMemoryState.svrAuthCredential {
|
||||
case .none, .svr2Only:
|
||||
self.inMemoryState.svrAuthCredential = matchedCredential.map { .svr2Only($0) }
|
||||
case .kbsOnly(let kbsCredential), .both(let kbsCredential, _):
|
||||
if let matchedCredential {
|
||||
self.inMemoryState.svrAuthCredential = .both(kbsCredential, matchedCredential)
|
||||
}
|
||||
}
|
||||
self.inMemoryState.svrAuthCredential = matchedCredential.map { .svr2Only($0) }
|
||||
|
||||
self.inMemoryState.svrAuthCredential = matchedCredential
|
||||
self.db.write { tx in
|
||||
self.deps.svrAuthCredentialStore.deleteInvalidCredentials(credentialsToDelete, tx)
|
||||
}
|
||||
@ -1756,10 +1590,7 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
case .none:
|
||||
break
|
||||
case let .reglocked(svrAuthCredential, reglockExpirationDate):
|
||||
guard let orchestratedCredential = OrchestratingSVRAuthCredential.from(
|
||||
kbs: svrAuthCredential.kbs,
|
||||
svr2: svrAuthCredential.svr2
|
||||
) else {
|
||||
guard let svrAuthCredential = svrAuthCredential.svr2 else {
|
||||
// If we don't have a useable credential, we are stuck.
|
||||
db.write { tx in
|
||||
self.updatePersistedSessionState(session: session, tx) {
|
||||
@ -1772,7 +1603,7 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
return restoreSVRMasterSecretForSessionPathReglock(
|
||||
session: session,
|
||||
pin: pinFromUser,
|
||||
svrAuthCredential: orchestratedCredential,
|
||||
svrAuthCredential: svrAuthCredential,
|
||||
reglockExpirationDate: reglockExpirationDate
|
||||
)
|
||||
} else {
|
||||
@ -2042,8 +1873,10 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
// If we have already exhausted our SVR backup attempts, we are stuck.
|
||||
db.write { tx in
|
||||
// May as well store credentials, anyway.
|
||||
reglockFailure.kbsAuthCredential.map { deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername($0, tx) }
|
||||
reglockFailure.svr2AuthCredential.map { deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername($0, tx) }
|
||||
deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername(
|
||||
reglockFailure.svr2AuthCredential,
|
||||
tx
|
||||
)
|
||||
self.updatePersistedSessionState(session: sessionFromBeforeRequest, tx) {
|
||||
$0.reglockState = .waitingTimeout(expirationDate: reglockExpirationDate)
|
||||
}
|
||||
@ -2062,8 +1895,10 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
// that means the whole thing is stuck. wait out the reglock.
|
||||
db.write { tx in
|
||||
// May as well store credentials, anyway.
|
||||
reglockFailure.kbsAuthCredential.map { deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername($0, tx) }
|
||||
reglockFailure.svr2AuthCredential.map { deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername($0, tx) }
|
||||
deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername(
|
||||
reglockFailure.svr2AuthCredential,
|
||||
tx
|
||||
)
|
||||
self.updatePersistedSessionState(session: sessionFromBeforeRequest, tx) {
|
||||
$0.reglockState = .waitingTimeout(expirationDate: reglockExpirationDate)
|
||||
}
|
||||
@ -2074,33 +1909,14 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
return nextStep()
|
||||
|
||||
case .none, .v1:
|
||||
let jointCredential: PersistedState.SessionState.ReglockState.SVRAuthCredential
|
||||
let kbsAuthCredential = reglockFailure.kbsAuthCredential
|
||||
let svr2AuthCredential = reglockFailure.svr2AuthCredential
|
||||
if let kbsAuthCredential, let svr2AuthCredential {
|
||||
jointCredential = .init(kbs: kbsAuthCredential, svr2: svr2AuthCredential)
|
||||
} else if let kbsAuthCredential {
|
||||
jointCredential = .init(kbs: kbsAuthCredential)
|
||||
} else if let svr2AuthCredential {
|
||||
jointCredential = .init(svr2: svr2AuthCredential)
|
||||
} else {
|
||||
// If we have no credentials we can use, we are stuck. wait out the reglock.
|
||||
db.write { tx in
|
||||
self.updatePersistedSessionState(session: sessionFromBeforeRequest, tx) {
|
||||
$0.reglockState = .waitingTimeout(expirationDate: reglockExpirationDate)
|
||||
}
|
||||
self.updatePersistedState(tx) {
|
||||
$0.e164WithKnownReglockEnabled = sessionFromBeforeRequest.e164
|
||||
}
|
||||
}
|
||||
return nextStep()
|
||||
}
|
||||
let persistedCredential = PersistedState.SessionState.ReglockState.SVRAuthCredential(
|
||||
svr2: reglockFailure.svr2AuthCredential
|
||||
)
|
||||
db.write { tx in
|
||||
reglockFailure.kbsAuthCredential.map { deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername($0, tx) }
|
||||
reglockFailure.svr2AuthCredential.map { deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername($0, tx) }
|
||||
deps.svrAuthCredentialStore.storeAuthCredentialForCurrentUsername(reglockFailure.svr2AuthCredential, tx)
|
||||
self.updatePersistedSessionState(session: sessionFromBeforeRequest, tx) {
|
||||
$0.reglockState = .reglocked(
|
||||
credential: jointCredential,
|
||||
credential: persistedCredential,
|
||||
expirationDate: reglockExpirationDate
|
||||
)
|
||||
}
|
||||
@ -2811,11 +2627,7 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
break
|
||||
case .changingNumber:
|
||||
// Change number is different; we do a limited number of operations and then finalize.
|
||||
guard let svrRemoteConfig = inMemoryState.svrRemoteConfig else {
|
||||
return syncRemoteConfig(accountIdentity)
|
||||
}
|
||||
|
||||
if let stepGuarantee = performSVRBackupStepsIfNeeded(accountIdentity: accountIdentity, svrRemoteConfig: svrRemoteConfig) {
|
||||
if let stepGuarantee = performSVRBackupStepsIfNeeded(accountIdentity: accountIdentity) {
|
||||
return stepGuarantee
|
||||
}
|
||||
|
||||
@ -2885,11 +2697,7 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
guard let svrRemoteConfig = inMemoryState.svrRemoteConfig else {
|
||||
return self.syncRemoteConfig(accountIdentity)
|
||||
}
|
||||
|
||||
if let stepGuarantee = performSVRBackupStepsIfNeeded(accountIdentity: accountIdentity, svrRemoteConfig: svrRemoteConfig) {
|
||||
if let stepGuarantee = performSVRBackupStepsIfNeeded(accountIdentity: accountIdentity) {
|
||||
return stepGuarantee
|
||||
}
|
||||
|
||||
@ -3011,50 +2819,12 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
private func syncRemoteConfig(
|
||||
_ accountIdentity: AccountIdentity,
|
||||
retriesLeft: Int = Constants.networkErrorRetries
|
||||
) -> Guarantee<RegistrationStep> {
|
||||
Logger.info("")
|
||||
|
||||
return deps.remoteConfig.refreshRemoteConfig(account: accountIdentity.authedAccount)
|
||||
.then(on: schedulers.main) { [weak self] config in
|
||||
guard let strongSelf = self else {
|
||||
return unretainedSelfError()
|
||||
}
|
||||
strongSelf.inMemoryState.svrRemoteConfig = config
|
||||
return strongSelf.nextStep()
|
||||
}
|
||||
.recover(on: schedulers.main) { [weak self] error -> Guarantee<RegistrationStep> in
|
||||
guard let strongSelf = self else {
|
||||
return unretainedSelfError()
|
||||
}
|
||||
if error.isNetworkFailureOrTimeout {
|
||||
if retriesLeft > 0 {
|
||||
return strongSelf.syncRemoteConfig(
|
||||
accountIdentity,
|
||||
retriesLeft: retriesLeft - 1
|
||||
)
|
||||
} else {
|
||||
return .value(.showErrorSheet(.networkError))
|
||||
}
|
||||
} else if error.isPostRegDeregisteredError {
|
||||
return strongSelf.becameDeregisteredBeforeCompleting(accountIdentity: accountIdentity)
|
||||
}
|
||||
return .value(.showErrorSheet(.genericError))
|
||||
}
|
||||
}
|
||||
|
||||
// returns nil if no steps needed.
|
||||
private func performSVRBackupStepsIfNeeded(
|
||||
accountIdentity: AccountIdentity,
|
||||
svrRemoteConfig: RemoteConfig.SVRConfiguration
|
||||
accountIdentity: AccountIdentity
|
||||
) -> Guarantee<RegistrationStep>? {
|
||||
Logger.info("")
|
||||
|
||||
// Set remote configuration so we back up to the right place.
|
||||
(deps.svr as? OrchestratingSVRImpl)?.setRemoteConfiguration(svrRemoteConfig)
|
||||
|
||||
let isRestoringPinBackup: Bool = (
|
||||
accountIdentity.hasPreviouslyUsedSVR &&
|
||||
!persistedState.hasGivenUpTryingToRestoreWithSVR
|
||||
|
||||
@ -46,7 +46,6 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
private var pushRegistrationManagerMock: RegistrationCoordinatorImpl.TestMocks.PushRegistrationManager!
|
||||
private var receiptManagerMock: RegistrationCoordinatorImpl.TestMocks.ReceiptManager!
|
||||
private var registrationStateChangeManagerMock: MockRegistrationStateChangeManager!
|
||||
private var remoteConfigMock: RegistrationCoordinatorImpl.TestMocks.RemoteConfig!
|
||||
private var sessionManager: RegistrationSessionManagerMock!
|
||||
private var storageServiceManagerMock: FakeStorageServiceManager!
|
||||
private var svr: SecureValueRecoveryMock!
|
||||
@ -79,7 +78,6 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
pushRegistrationManagerMock = RegistrationCoordinatorImpl.TestMocks.PushRegistrationManager()
|
||||
receiptManagerMock = RegistrationCoordinatorImpl.TestMocks.ReceiptManager()
|
||||
registrationStateChangeManagerMock = MockRegistrationStateChangeManager()
|
||||
remoteConfigMock = RegistrationCoordinatorImpl.TestMocks.RemoteConfig()
|
||||
sessionManager = RegistrationSessionManagerMock()
|
||||
storageServiceManagerMock = FakeStorageServiceManager()
|
||||
tsAccountManagerMock = MockTSAccountManager(dateProvider: dateProvider)
|
||||
@ -112,7 +110,6 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
pushRegistrationManager: pushRegistrationManagerMock,
|
||||
receiptManager: receiptManagerMock,
|
||||
registrationStateChangeManager: registrationStateChangeManagerMock,
|
||||
remoteConfig: remoteConfigMock,
|
||||
schedulers: TestSchedulers(scheduler: scheduler),
|
||||
sessionManager: sessionManager,
|
||||
signalService: mockSignalService,
|
||||
@ -774,7 +771,6 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
statusCode: RegistrationServiceResponses.AccountCreationResponseCodes.reglockFailed.rawValue,
|
||||
bodyJson: RegistrationServiceResponses.RegistrationLockFailureResponse(
|
||||
timeRemainingMs: 10,
|
||||
kbsAuthCredential: Stubs.kbsAuthCredential,
|
||||
svr2AuthCredential: Stubs.svr2AuthCredential
|
||||
)
|
||||
)
|
||||
@ -1033,14 +1029,6 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
]
|
||||
svrAuthCredentialStore.svr2Dict = Dictionary(grouping: svr2CredentialCandidates, by: \.credential.username).mapValues { $0.first! }
|
||||
|
||||
let kbsCredentialCandidates: [KBSAuthCredential] = [
|
||||
Stubs.kbsAuthCredential,
|
||||
KBSAuthCredential(credential: RemoteAttestation.Auth(username: "aaaa", password: "abc")),
|
||||
KBSAuthCredential(credential: RemoteAttestation.Auth(username: "zzzz", password: "xyz")),
|
||||
KBSAuthCredential(credential: RemoteAttestation.Auth(username: "0000", password: "123"))
|
||||
]
|
||||
svrAuthCredentialStore.dict = Dictionary(grouping: kbsCredentialCandidates, by: \.credential.username).mapValues { $0.first! }
|
||||
|
||||
// Get past the opening.
|
||||
goThroughOpeningHappyPath(expectedNextStep: .phoneNumberEntry(Stubs.phoneNumberEntryState(mode: mode)))
|
||||
|
||||
@ -1053,7 +1041,7 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
mockURLSession.addResponse(TSRequestOWSURLSessionMock.Response(
|
||||
urlSuffix: expectedSVR2CheckRequest.url!.absoluteString,
|
||||
statusCode: 200,
|
||||
bodyJson: RegistrationServiceResponses.KBSAuthCheckResponse(matches: [
|
||||
bodyJson: RegistrationServiceResponses.SVR2AuthCheckResponse(matches: [
|
||||
"\(Stubs.svr2AuthCredential.credential.username):\(Stubs.svr2AuthCredential.credential.password)": .match,
|
||||
"aaaa:abc": .notMatch,
|
||||
"zzzz:xyz": .invalid,
|
||||
@ -1072,8 +1060,8 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
XCTAssertNotNil(remainingCredentials["aaaa"])
|
||||
XCTAssertNil(remainingCredentials["zzzz"])
|
||||
XCTAssertNil(remainingCredentials["0000"])
|
||||
// KBS should be untouched.
|
||||
XCTAssertNotNil(svrAuthCredentialStore.dict[Stubs.kbsAuthCredential.credential.username])
|
||||
// SVR should be untouched.
|
||||
XCTAssertNotNil(svrAuthCredentialStore.svr2Dict[Stubs.svr2AuthCredential.credential.username])
|
||||
|
||||
scheduler.stop()
|
||||
scheduler.adjustTime(to: 0)
|
||||
@ -1086,7 +1074,7 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
svr.restoreKeysMock = { pin, authMethod in
|
||||
XCTAssertEqual(self.scheduler.currentTime, 0)
|
||||
XCTAssertEqual(pin, Stubs.pinCode)
|
||||
XCTAssertEqual(authMethod, .svrAuth(.svr2Only(Stubs.svr2AuthCredential), backup: nil))
|
||||
XCTAssertEqual(authMethod, .svrAuth(Stubs.svr2AuthCredential, backup: nil))
|
||||
self.svr.hasMasterKey = true
|
||||
return self.scheduler.guarantee(resolvingWith: .success, atTime: 1)
|
||||
}
|
||||
@ -1179,7 +1167,7 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
XCTAssertEqual(self.scheduler.currentTime, 4)
|
||||
XCTAssertEqual(pin, Stubs.pinCode)
|
||||
XCTAssertEqual(authMethod, .svrAuth(
|
||||
.svr2Only(Stubs.svr2AuthCredential),
|
||||
Stubs.svr2AuthCredential,
|
||||
backup: .chatServerAuth(expectedAuthedAccount())
|
||||
))
|
||||
XCTAssertFalse(rotateMasterKey)
|
||||
@ -1229,13 +1217,13 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
setupDefaultAccountAttributes()
|
||||
|
||||
// Put some auth credentials in storage.
|
||||
let credentialCandidates: [KBSAuthCredential] = [
|
||||
Stubs.kbsAuthCredential,
|
||||
KBSAuthCredential(credential: RemoteAttestation.Auth(username: "aaaa", password: "abc")),
|
||||
KBSAuthCredential(credential: RemoteAttestation.Auth(username: "zzzz", password: "xyz")),
|
||||
KBSAuthCredential(credential: RemoteAttestation.Auth(username: "0000", password: "123"))
|
||||
let credentialCandidates: [SVR2AuthCredential] = [
|
||||
Stubs.svr2AuthCredential,
|
||||
SVR2AuthCredential(credential: RemoteAttestation.Auth(username: "aaaa", password: "abc")),
|
||||
SVR2AuthCredential(credential: RemoteAttestation.Auth(username: "zzzz", password: "xyz")),
|
||||
SVR2AuthCredential(credential: RemoteAttestation.Auth(username: "0000", password: "123"))
|
||||
]
|
||||
svrAuthCredentialStore.dict = Dictionary(grouping: credentialCandidates, by: \.credential.username).mapValues { $0.first! }
|
||||
svrAuthCredentialStore.svr2Dict = Dictionary(grouping: credentialCandidates, by: \.credential.username).mapValues { $0.first! }
|
||||
|
||||
// Get past the opening.
|
||||
goThroughOpeningHappyPath(expectedNextStep: .phoneNumberEntry(Stubs.phoneNumberEntryState(mode: mode)))
|
||||
@ -1247,16 +1235,16 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
let nextStep = coordinator.submitE164(Stubs.e164)
|
||||
|
||||
// Don't give back any matches at t=2, which means we will want to create a session as a fallback.
|
||||
let expectedKBSCheckRequest = RegistrationRequestFactory.kbsAuthCredentialCheckRequest(
|
||||
let expectedSVRCheckRequest = RegistrationRequestFactory.svr2AuthCredentialCheckRequest(
|
||||
e164: Stubs.e164,
|
||||
credentials: credentialCandidates
|
||||
)
|
||||
mockURLSession.addResponse(
|
||||
TSRequestOWSURLSessionMock.Response(
|
||||
urlSuffix: expectedKBSCheckRequest.url!.absoluteString,
|
||||
urlSuffix: expectedSVRCheckRequest.url!.absoluteString,
|
||||
statusCode: 200,
|
||||
bodyJson: RegistrationServiceResponses.KBSAuthCheckResponse(matches: [
|
||||
"\(Stubs.kbsAuthCredential.credential.username):\(Stubs.kbsAuthCredential.credential.password)": .notMatch,
|
||||
bodyJson: RegistrationServiceResponses.SVR2AuthCheckResponse(matches: [
|
||||
"\(Stubs.svr2AuthCredential.credential.username):\(Stubs.svr2AuthCredential.credential.password)": .notMatch,
|
||||
"aaaa:abc": .notMatch,
|
||||
"zzzz:xyz": .invalid,
|
||||
"0000:123": .unknown
|
||||
@ -1299,8 +1287,8 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
XCTAssertEqual(nextStep.value, .verificationCodeEntry(Stubs.verificationCodeEntryState(mode: self.mode)))
|
||||
|
||||
// We should have wipted the invalid and unknown credentials.
|
||||
let remainingCredentials = svrAuthCredentialStore.dict
|
||||
XCTAssertNotNil(remainingCredentials[Stubs.kbsAuthCredential.credential.username])
|
||||
let remainingCredentials = svrAuthCredentialStore.svr2Dict
|
||||
XCTAssertNotNil(remainingCredentials[Stubs.svr2AuthCredential.credential.username])
|
||||
XCTAssertNotNil(remainingCredentials["aaaa"])
|
||||
XCTAssertNil(remainingCredentials["zzzz"])
|
||||
XCTAssertNil(remainingCredentials["0000"])
|
||||
@ -1316,10 +1304,10 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
setupDefaultAccountAttributes()
|
||||
|
||||
// Put some auth credentials in storage.
|
||||
let credentialCandidates: [KBSAuthCredential] = [
|
||||
Stubs.kbsAuthCredential
|
||||
let credentialCandidates: [SVR2AuthCredential] = [
|
||||
Stubs.svr2AuthCredential
|
||||
]
|
||||
svrAuthCredentialStore.dict = Dictionary(grouping: credentialCandidates, by: \.credential.username).mapValues { $0.first! }
|
||||
svrAuthCredentialStore.svr2Dict = Dictionary(grouping: credentialCandidates, by: \.credential.username).mapValues { $0.first! }
|
||||
|
||||
// Get past the opening.
|
||||
goThroughOpeningHappyPath(expectedNextStep: .phoneNumberEntry(Stubs.phoneNumberEntryState(mode: mode)))
|
||||
@ -1334,16 +1322,16 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
var nextStep = coordinator.submitE164(originalE164)
|
||||
|
||||
// Don't give back any matches at t=2, which means we will want to create a session as a fallback.
|
||||
var expectedKBSCheckRequest = RegistrationRequestFactory.kbsAuthCredentialCheckRequest(
|
||||
var expectedSVRCheckRequest = RegistrationRequestFactory.svr2AuthCredentialCheckRequest(
|
||||
e164: originalE164,
|
||||
credentials: credentialCandidates
|
||||
)
|
||||
mockURLSession.addResponse(
|
||||
TSRequestOWSURLSessionMock.Response(
|
||||
urlSuffix: expectedKBSCheckRequest.url!.absoluteString,
|
||||
urlSuffix: expectedSVRCheckRequest.url!.absoluteString,
|
||||
statusCode: 200,
|
||||
bodyJson: RegistrationServiceResponses.KBSAuthCheckResponse(matches: [
|
||||
"\(Stubs.kbsAuthCredential.credential.username):\(Stubs.kbsAuthCredential.credential.password)": .notMatch
|
||||
bodyJson: RegistrationServiceResponses.SVR2AuthCheckResponse(matches: [
|
||||
"\(Stubs.svr2AuthCredential.credential.username):\(Stubs.svr2AuthCredential.credential.password)": .notMatch
|
||||
])
|
||||
),
|
||||
atTime: 2,
|
||||
@ -1383,8 +1371,8 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
XCTAssertEqual(nextStep.value, .verificationCodeEntry(Stubs.verificationCodeEntryState(mode: self.mode)))
|
||||
|
||||
// We should have wiped the invalid and unknown credentials.
|
||||
let remainingCredentials = svrAuthCredentialStore.dict
|
||||
XCTAssertNotNil(remainingCredentials[Stubs.kbsAuthCredential.credential.username])
|
||||
let remainingCredentials = svrAuthCredentialStore.svr2Dict
|
||||
XCTAssertNotNil(remainingCredentials[Stubs.svr2AuthCredential.credential.username])
|
||||
|
||||
// Now change the phone number; this should take us back to phone number entry.
|
||||
nextStep = coordinator.requestChangeE164()
|
||||
@ -1394,17 +1382,17 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
// Give it a phone number, which should cause it to check the auth credentials again.
|
||||
nextStep = coordinator.submitE164(changedE164)
|
||||
|
||||
// Give a match at t=5, so it registers via kbs auth credential.
|
||||
expectedKBSCheckRequest = RegistrationRequestFactory.kbsAuthCredentialCheckRequest(
|
||||
// Give a match at t=5, so it registers via SVR auth credential.
|
||||
expectedSVRCheckRequest = RegistrationRequestFactory.svr2AuthCredentialCheckRequest(
|
||||
e164: changedE164,
|
||||
credentials: credentialCandidates
|
||||
)
|
||||
mockURLSession.addResponse(
|
||||
TSRequestOWSURLSessionMock.Response(
|
||||
urlSuffix: expectedKBSCheckRequest.url!.absoluteString,
|
||||
urlSuffix: expectedSVRCheckRequest.url!.absoluteString,
|
||||
statusCode: 200,
|
||||
bodyJson: RegistrationServiceResponses.KBSAuthCheckResponse(matches: [
|
||||
"\(Stubs.kbsAuthCredential.credential.username):\(Stubs.kbsAuthCredential.credential.password)": .match
|
||||
bodyJson: RegistrationServiceResponses.SVR2AuthCheckResponse(matches: [
|
||||
"\(Stubs.svr2AuthCredential.credential.username):\(Stubs.svr2AuthCredential.credential.password)": .match
|
||||
])
|
||||
),
|
||||
atTime: 5,
|
||||
@ -3201,7 +3189,7 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
// )
|
||||
let reglockStateReglockedData = "7b227265676c6f636b6564223a7b2265787069726174696f6e44617465223a2d3937383239373230302c2263726564656e7469616c223a7b2263726564656e7469616c223a7b22757365726e616d65223a2261626364222c2270617373776f7264223a2278797a227d7d7d7d"
|
||||
XCTAssertEqual(
|
||||
ReglockState.reglocked(credential: .testOnly(kbs: Stubs.kbsAuthCredential, svr2: nil), expirationDate: reglockExpirationDate),
|
||||
ReglockState.reglocked(credential: .testOnly(svr2: nil), expirationDate: reglockExpirationDate),
|
||||
try decoder.decode(ReglockState.self, from: Data.data(fromHex: reglockStateReglockedData)!)
|
||||
)
|
||||
|
||||
@ -3214,7 +3202,7 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
// )
|
||||
let reglockStateReglockedSVR2Data = "7b227265676c6f636b6564223a7b2265787069726174696f6e44617465223a2d3937383239373230302c2263726564656e7469616c223a7b226b6273223a7b2263726564656e7469616c223a7b22757365726e616d65223a2261626364222c2270617373776f7264223a2278797a227d7d2c2273767232223a7b2263726564656e7469616c223a7b22757365726e616d65223a22787878222c2270617373776f7264223a22797979227d7d7d7d7d"
|
||||
XCTAssertEqual(
|
||||
ReglockState.reglocked(credential: .init(kbs: Stubs.kbsAuthCredential, svr2: Stubs.svr2AuthCredential), expirationDate: reglockExpirationDate),
|
||||
ReglockState.reglocked(credential: .init(svr2: Stubs.svr2AuthCredential), expirationDate: reglockExpirationDate),
|
||||
try decoder.decode(ReglockState.self, from: Data.data(fromHex: reglockStateReglockedSVR2Data)!)
|
||||
)
|
||||
|
||||
@ -3383,7 +3371,6 @@ public class RegistrationCoordinatorTest: XCTestCase {
|
||||
static let reglockData = Data(repeating: 7, count: 8)
|
||||
static var reglockToken: String { reglockData.hexadecimalString }
|
||||
|
||||
static let kbsAuthCredential = KBSAuthCredential(credential: RemoteAttestation.Auth(username: "abcd", password: "xyz"))
|
||||
static let svr2AuthCredential = SVR2AuthCredential(credential: RemoteAttestation.Auth(username: "xxx", password: "yyy"))
|
||||
|
||||
static let captchaToken = "captchaToken"
|
||||
@ -3693,3 +3680,11 @@ extension RegistrationMode {
|
||||
private class PreKeyError: Error {
|
||||
init() {}
|
||||
}
|
||||
|
||||
extension RegistrationServiceResponses.RegistrationLockFailureResponse: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(timeRemainingMs, forKey: .timeRemainingMs)
|
||||
try container.encodeIfPresent(svr2AuthCredential.credential, forKey: .svr2AuthCredential)
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,6 @@ extension RegistrationCoordinatorImpl {
|
||||
public typealias ProfileManager = _RegistrationCoordinator_ProfileManagerMock
|
||||
public typealias PushRegistrationManager = _RegistrationCoordinator_PushRegistrationManagerMock
|
||||
public typealias ReceiptManager = _RegistrationCoordinator_ReceiptManagerMock
|
||||
public typealias RemoteConfig = _RegistrationCoordinator_RemoteConfigMock
|
||||
public typealias UDManager = _RegistrationCoordinator_UDManagerMock
|
||||
}
|
||||
}
|
||||
@ -282,17 +281,6 @@ public class _RegistrationCoordinator_ReceiptManagerMock: _RegistrationCoordinat
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RemoteConfig
|
||||
|
||||
public class _RegistrationCoordinator_RemoteConfigMock: _RegistrationCoordinator_RemoteConfigShim {
|
||||
|
||||
public init() {}
|
||||
|
||||
public func refreshRemoteConfig(account: AuthedAccount) -> Promise<RemoteConfig.SVRConfiguration> {
|
||||
return .value(.mirroring)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UDManager
|
||||
|
||||
public class _RegistrationCoordinator_UDManagerMock: _RegistrationCoordinator_UDManagerShim {
|
||||
|
||||
@ -305,24 +305,21 @@ public class DependenciesBridge {
|
||||
)
|
||||
self.accountAttributesUpdater = accountAttributesUpdater
|
||||
|
||||
self.svr = OrchestratingSVRImpl(
|
||||
self.svr = SecureValueRecovery2Impl(
|
||||
accountAttributesUpdater: accountAttributesUpdater,
|
||||
appContext: CurrentAppContext(),
|
||||
appReadiness: SVR2.Wrappers.AppReadiness(),
|
||||
appVersion: appVersion,
|
||||
connectionFactory: SgxWebsocketConnectionFactoryImpl(websocketFactory: websocketFactory),
|
||||
credentialStorage: svrCredentialStorage,
|
||||
databaseStorage: db,
|
||||
db: db,
|
||||
keyValueStoreFactory: keyValueStoreFactory,
|
||||
remoteAttestation: SVR.Wrappers.RemoteAttestation(),
|
||||
schedulers: schedulers,
|
||||
signalService: signalService,
|
||||
storageServiceManager: storageServiceManager,
|
||||
svrLocalStorage: svrLocalStorage,
|
||||
syncManager: syncManager,
|
||||
tsAccountManager: tsAccountManager,
|
||||
tsConstants: tsConstants,
|
||||
twoFAManager: SVR.Wrappers.OWS2FAManager(ows2FAManager)
|
||||
twoFAManager: SVR2.Wrappers.OWS2FAManager(ows2FAManager)
|
||||
)
|
||||
|
||||
let interactionStore = InteractionStoreImpl()
|
||||
|
||||
@ -9,15 +9,6 @@ import Foundation
|
||||
|
||||
public class SVRAuthCredentialStorageMock: SVRAuthCredentialStorage {
|
||||
|
||||
public var dict: [String: KBSAuthCredential] {
|
||||
get { return kbsDict }
|
||||
set { kbsDict = newValue }
|
||||
}
|
||||
public var currentUsername: String? {
|
||||
get { return currentKBSUsername }
|
||||
set { currentKBSUsername = newValue }
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
||||
// MARK: - SVR2
|
||||
@ -49,35 +40,5 @@ public class SVRAuthCredentialStorageMock: SVRAuthCredentialStorage {
|
||||
guard let currentSVR2Username else { return }
|
||||
svr2Dict[currentSVR2Username] = nil
|
||||
}
|
||||
|
||||
// MARK: - KBS
|
||||
|
||||
public var currentKBSUsername: String?
|
||||
public var kbsDict = [String: KBSAuthCredential]()
|
||||
|
||||
public func storeAuthCredentialForCurrentUsername(_ auth: KBSAuthCredential, _ transaction: DBWriteTransaction) {
|
||||
kbsDict[auth.credential.username] = auth
|
||||
currentKBSUsername = auth.credential.username
|
||||
}
|
||||
|
||||
public func getAuthCredentials(_ transaction: DBReadTransaction) -> [KBSAuthCredential] {
|
||||
return Array(kbsDict.values)
|
||||
}
|
||||
|
||||
public func getAuthCredentialForCurrentUser(_ transaction: DBReadTransaction) -> KBSAuthCredential? {
|
||||
guard let currentUsername = currentKBSUsername else {
|
||||
return nil
|
||||
}
|
||||
return kbsDict[currentUsername]
|
||||
}
|
||||
|
||||
public func deleteInvalidCredentials(_ invalidCredentials: [KBSAuthCredential], _ transaction: DBWriteTransaction) {
|
||||
invalidCredentials.lazy.map(\.credential.username).forEach { kbsDict[$0] = nil }
|
||||
}
|
||||
|
||||
public func removeKBSCredentialsForCurrentUser(_ transaction: DBWriteTransaction) {
|
||||
guard let currentKBSUsername else { return }
|
||||
dict[currentKBSUsername] = nil
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
//
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Transparent wrapper that exists purely to make it clear to readers
|
||||
// that the credential must be for kbs, not an arbitrary RemoteAttestation.Auth.
|
||||
public struct KBSAuthCredential: Equatable, Codable {
|
||||
public let credential: RemoteAttestation.Auth
|
||||
|
||||
public init(credential: RemoteAttestation.Auth) {
|
||||
self.credential = credential
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
||||
//
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// This file has "shim" classes that allow KeyBackupServiceImpl to talk to
|
||||
// other classes which aren't protocolized and aren't stub-able in tests.
|
||||
// We can easily produce stub instances of these protocols in tests,
|
||||
// allowing us to control their behavior precisely.
|
||||
// This also allows us to bridge from "v2" database transaction types
|
||||
// which KeyBackupServiceImpl uses, to sds transaction types that these
|
||||
// older classes expect as parameters.
|
||||
//
|
||||
// Eventually, this whole file can be deleted, once:
|
||||
// 1) The classes involved have been protocolized (and stub instances
|
||||
// can be passed to KeyBackupServiceImpl's initializer in tests).
|
||||
// 2) The classes involved accept v2 transaction types.
|
||||
|
||||
// MARK: - Namespace
|
||||
|
||||
extension SVR {
|
||||
public enum Shims {
|
||||
public typealias OWS2FAManager = _KeyBackupServiceImpl_OWS2FAManagerShim
|
||||
public typealias RemoteAttestation = _KeyBackupServiceImpl_RemoteAttestationShim
|
||||
}
|
||||
|
||||
public enum Wrappers {
|
||||
public typealias OWS2FAManager = _KeyBackupServiceImpl_OWS2FAManagerWrapper
|
||||
public typealias RemoteAttestation = _KeyBackupServiceImpl_RemoteAttestationWrapper
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OWS2FAManager
|
||||
|
||||
public protocol _KeyBackupServiceImpl_OWS2FAManagerShim {
|
||||
func pinCode(transaction: DBReadTransaction) -> String?
|
||||
func markDisabled(transaction: DBWriteTransaction)
|
||||
}
|
||||
|
||||
public class _KeyBackupServiceImpl_OWS2FAManagerWrapper: SVR.Shims.OWS2FAManager {
|
||||
private let manager: OWS2FAManager
|
||||
public init(_ manager: OWS2FAManager) { self.manager = manager }
|
||||
|
||||
public func pinCode(transaction: DBReadTransaction) -> String? {
|
||||
return manager.pinCode(with: SDSDB.shimOnlyBridge(transaction))
|
||||
}
|
||||
|
||||
public func markDisabled(transaction: DBWriteTransaction) {
|
||||
manager.markDisabled(transaction: SDSDB.shimOnlyBridge(transaction))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RemoteAttestation
|
||||
|
||||
public protocol _KeyBackupServiceImpl_RemoteAttestationShim {
|
||||
func performForKeyBackup(
|
||||
authMethod: RemoteAttestation.KeyBackupAuthMethod,
|
||||
enclave: KeyBackupEnclave
|
||||
) -> Promise<RemoteAttestation>
|
||||
}
|
||||
|
||||
public class _KeyBackupServiceImpl_RemoteAttestationWrapper: _KeyBackupServiceImpl_RemoteAttestationShim {
|
||||
public func performForKeyBackup(
|
||||
authMethod: RemoteAttestation.KeyBackupAuthMethod,
|
||||
enclave: KeyBackupEnclave
|
||||
) -> Promise<RemoteAttestation> {
|
||||
return RemoteAttestation.performForKeyBackup(
|
||||
authMethod: authMethod,
|
||||
enclave: enclave
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -21,10 +21,10 @@ public protocol SVRLocalStorageInternal: SVRLocalStorage {
|
||||
// Linked devices get the storage service key and store it locally. The primary doesn't do this.
|
||||
func getSyncedStorageServiceKey(_ transaction: DBReadTransaction) -> Data?
|
||||
|
||||
func getSVR1EnclaveName(_ transaction: DBReadTransaction) -> String?
|
||||
|
||||
func getSVR2MrEnclaveStringValue(_ transaction: DBReadTransaction) -> String?
|
||||
|
||||
func hadSVR1Enclave(_ transaction: DBReadTransaction) -> Bool
|
||||
|
||||
// MARK: - Setters
|
||||
|
||||
func setIsMasterKeyBackedUp(_ value: Bool, _ transaction: DBWriteTransaction)
|
||||
@ -41,8 +41,6 @@ public protocol SVRLocalStorageInternal: SVRLocalStorage {
|
||||
// Linked devices get the backup key and store it locally. The primary doesn't do this.
|
||||
func setSyncedBackupKey(_ value: Data?, _ transaction: DBWriteTransaction)
|
||||
|
||||
func setSVR1EnclaveName(_ value: String?, _ transaction: DBWriteTransaction)
|
||||
|
||||
func setSVR2MrEnclaveStringValue(_ value: String?, _ transaction: DBWriteTransaction)
|
||||
|
||||
// MARK: - Clearing Keys
|
||||
@ -94,14 +92,14 @@ internal class SVRLocalStorageImpl: SVRLocalStorageInternal {
|
||||
return keyValueStore.getData(Keys.syncedStorageServiceKey, transaction: transaction)
|
||||
}
|
||||
|
||||
public func getSVR1EnclaveName(_ transaction: DBReadTransaction) -> String? {
|
||||
return keyValueStore.getString(Keys.svr1EnclaveName, transaction: transaction)
|
||||
}
|
||||
|
||||
public func getSVR2MrEnclaveStringValue(_ transaction: DBReadTransaction) -> String? {
|
||||
return keyValueStore.getString(Keys.svr2MrEnclaveStringValue, transaction: transaction)
|
||||
}
|
||||
|
||||
public func hadSVR1Enclave(_ transaction: DBReadTransaction) -> Bool {
|
||||
return keyValueStore.hasValue(Keys.legacy_svr1EnclaveName, transaction: transaction)
|
||||
}
|
||||
|
||||
// MARK: - Setters
|
||||
|
||||
public func setIsMasterKeyBackedUp(_ value: Bool, _ transaction: DBWriteTransaction) {
|
||||
@ -130,10 +128,6 @@ internal class SVRLocalStorageImpl: SVRLocalStorageInternal {
|
||||
keyValueStore.setData(value, key: Keys.syncedBackupKey, transaction: transaction)
|
||||
}
|
||||
|
||||
public func setSVR1EnclaveName(_ value: String?, _ transaction: DBWriteTransaction) {
|
||||
keyValueStore.setString(value, key: Keys.svr1EnclaveName, transaction: transaction)
|
||||
}
|
||||
|
||||
public func setSVR2MrEnclaveStringValue(_ value: String?, _ transaction: DBWriteTransaction) {
|
||||
keyValueStore.setString(value, key: Keys.svr2MrEnclaveStringValue, transaction: transaction)
|
||||
}
|
||||
@ -148,7 +142,7 @@ internal class SVRLocalStorageImpl: SVRLocalStorageInternal {
|
||||
Keys.encodedPINVerificationString,
|
||||
Keys.isMasterKeyBackedUp,
|
||||
Keys.syncedStorageServiceKey,
|
||||
Keys.svr1EnclaveName,
|
||||
Keys.legacy_svr1EnclaveName,
|
||||
Keys.svr2MrEnclaveStringValue
|
||||
],
|
||||
transaction: transaction
|
||||
@ -165,7 +159,8 @@ internal class SVRLocalStorageImpl: SVRLocalStorageInternal {
|
||||
static let isMasterKeyBackedUp = "isMasterKeyBackedUp"
|
||||
static let syncedStorageServiceKey = "Storage Service Encryption"
|
||||
static let syncedBackupKey = "Backup Key"
|
||||
static let svr1EnclaveName = "enclaveName"
|
||||
// Kept around because its existence indicates we had an svr1 backup.
|
||||
static let legacy_svr1EnclaveName = "enclaveName"
|
||||
static let svr2MrEnclaveStringValue = "svr2_mrenclaveStringValue"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
//
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum OrchestratingSVRAuthCredential: Equatable {
|
||||
case kbsOnly(KBSAuthCredential)
|
||||
case svr2Only(SVR2AuthCredential)
|
||||
case both(KBSAuthCredential, SVR2AuthCredential)
|
||||
|
||||
public static func from(kbs: KBSAuthCredential?, svr2: SVR2AuthCredential?) -> Self? {
|
||||
if let kbs, let svr2 {
|
||||
return .both(kbs, svr2)
|
||||
} else if let kbs {
|
||||
return .kbsOnly(kbs)
|
||||
} else if let svr2 {
|
||||
return .svr2Only(svr2)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var kbs: KBSAuthCredential? {
|
||||
switch self {
|
||||
case .kbsOnly(let kBSAuthCredential):
|
||||
return kBSAuthCredential
|
||||
case .svr2Only:
|
||||
return nil
|
||||
case .both(let kBSAuthCredential, _):
|
||||
return kBSAuthCredential
|
||||
}
|
||||
}
|
||||
|
||||
public var svr2: SVR2AuthCredential? {
|
||||
switch self {
|
||||
case .kbsOnly:
|
||||
return nil
|
||||
case .svr2Only(let sVR2AuthCredential):
|
||||
return sVR2AuthCredential
|
||||
case .both(_, let sVR2AuthCredential):
|
||||
return sVR2AuthCredential
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,658 +0,0 @@
|
||||
//
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Implementation of `SecureValueRecovery` that talks to KBS (SVR1) and/or SVR2
|
||||
/// depending on local and remote flags.
|
||||
/// Once we are fully migrated to SVR2 this class can be deleted.
|
||||
public class OrchestratingSVRImpl: SecureValueRecovery {
|
||||
|
||||
private let db: DB
|
||||
private let kvStore: KeyValueStore
|
||||
private let schedulers: Schedulers
|
||||
|
||||
public init(
|
||||
accountAttributesUpdater: AccountAttributesUpdater,
|
||||
appContext: AppContext,
|
||||
appReadiness: SVR2.Shims.AppReadiness,
|
||||
appVersion: AppVersion,
|
||||
connectionFactory: SgxWebsocketConnectionFactory,
|
||||
credentialStorage: SVRAuthCredentialStorage,
|
||||
databaseStorage: DB,
|
||||
keyValueStoreFactory: KeyValueStoreFactory,
|
||||
remoteAttestation: SVR.Shims.RemoteAttestation,
|
||||
schedulers: Schedulers,
|
||||
signalService: OWSSignalServiceProtocol,
|
||||
storageServiceManager: StorageServiceManager,
|
||||
svrLocalStorage: SVRLocalStorageInternal,
|
||||
syncManager: SyncManagerProtocolSwift,
|
||||
tsAccountManager: TSAccountManager,
|
||||
tsConstants: TSConstantsProtocol,
|
||||
twoFAManager: SVR.Shims.OWS2FAManager
|
||||
) {
|
||||
self.svr2 = SecureValueRecovery2Impl(
|
||||
accountAttributesUpdater: accountAttributesUpdater,
|
||||
appReadiness: appReadiness,
|
||||
appVersion: appVersion,
|
||||
connectionFactory: connectionFactory,
|
||||
credentialStorage: credentialStorage,
|
||||
db: databaseStorage,
|
||||
keyValueStoreFactory: keyValueStoreFactory,
|
||||
schedulers: schedulers,
|
||||
storageServiceManager: storageServiceManager,
|
||||
svrLocalStorage: svrLocalStorage,
|
||||
syncManager: syncManager,
|
||||
tsAccountManager: tsAccountManager,
|
||||
tsConstants: tsConstants,
|
||||
twoFAManager: twoFAManager
|
||||
)
|
||||
self.kbs = KeyBackupServiceImpl(
|
||||
appContext: appContext,
|
||||
credentialStorage: credentialStorage,
|
||||
databaseStorage: databaseStorage,
|
||||
keyValueStoreFactory: keyValueStoreFactory,
|
||||
remoteAttestation: remoteAttestation,
|
||||
schedulers: schedulers,
|
||||
signalService: signalService,
|
||||
storageServiceManager: storageServiceManager,
|
||||
svrLocalStorage: svrLocalStorage,
|
||||
syncManager: syncManager,
|
||||
tsAccountManager: tsAccountManager,
|
||||
tsConstants: tsConstants,
|
||||
twoFAManager: twoFAManager
|
||||
)
|
||||
self.db = databaseStorage
|
||||
self.kvStore = keyValueStoreFactory.keyValueStore(collection: "OrchestratingSVRImpl")
|
||||
self.schedulers = schedulers
|
||||
}
|
||||
|
||||
private let svr2: SecureValueRecovery2Impl
|
||||
private let kbs: KeyBackupServiceImpl
|
||||
|
||||
// MARK: - Remote Config
|
||||
|
||||
private var remoteSVRConfig: RemoteConfig.SVRConfiguration?
|
||||
|
||||
public func setRemoteConfiguration(_ config: RemoteConfig.SVRConfiguration) {
|
||||
self.remoteSVRConfig = config
|
||||
db.asyncWrite { tx in
|
||||
self.kvStore.setInt(config.rawValue, key: "svrConfig", transaction: tx)
|
||||
}
|
||||
}
|
||||
|
||||
private func getStoredRemoteConfiguration(tx: DBReadTransaction) -> RemoteConfig.SVRConfiguration? {
|
||||
guard let raw = kvStore.getInt("svrConfig", transaction: tx) else {
|
||||
return nil
|
||||
}
|
||||
return .init(rawValue: raw)
|
||||
}
|
||||
|
||||
// MARK: - Read/Write Strategy
|
||||
|
||||
private enum ReadStrategy {
|
||||
case kbsOnly(SVR.AuthMethod)
|
||||
case fallbackToKBSForNonInvalidPinFailure(kbsAuth: SVR.AuthMethod, svr2Auth: SVR.AuthMethod)
|
||||
case fallbackToKBSForNoBackupFailureOnly(kbsAuth: SVR.AuthMethod, svr2Auth: SVR.AuthMethod)
|
||||
case svr2Only(SVR.AuthMethod)
|
||||
case reportNoBackup
|
||||
}
|
||||
|
||||
private func readStrategy(for authMethod: SVR.AuthMethod) -> ReadStrategy {
|
||||
|
||||
let kbsAuth = authMethod.kbsCompatible
|
||||
let svr2Auth = authMethod.svr2Compatible
|
||||
func kbsOnlyIfPossible() -> ReadStrategy {
|
||||
return kbsAuth.map { .kbsOnly($0) } ?? .reportNoBackup
|
||||
}
|
||||
func svr2OnlyIfPossible() -> ReadStrategy {
|
||||
return svr2Auth.map { .svr2Only($0) } ?? .reportNoBackup
|
||||
}
|
||||
func fallbackToKBSForNonInvalidPinFailureIfPossible() -> ReadStrategy {
|
||||
if let kbsAuth, let svr2Auth {
|
||||
return .fallbackToKBSForNonInvalidPinFailure(kbsAuth: kbsAuth, svr2Auth: svr2Auth)
|
||||
} else if let kbsAuth {
|
||||
return .kbsOnly(kbsAuth)
|
||||
} else if let svr2Auth {
|
||||
return .svr2Only(svr2Auth)
|
||||
} else {
|
||||
return .reportNoBackup
|
||||
}
|
||||
}
|
||||
func fallbackToKBSForNoBackupFailureOnlyIfPossible() -> ReadStrategy {
|
||||
if let kbsAuth, let svr2Auth {
|
||||
return .fallbackToKBSForNoBackupFailureOnly(kbsAuth: kbsAuth, svr2Auth: svr2Auth)
|
||||
} else if let kbsAuth {
|
||||
return .kbsOnly(kbsAuth)
|
||||
} else if let svr2Auth {
|
||||
return .svr2Only(svr2Auth)
|
||||
} else {
|
||||
return .reportNoBackup
|
||||
}
|
||||
}
|
||||
|
||||
switch remoteSVRConfig {
|
||||
case .none:
|
||||
// No config; we are probably in registration.
|
||||
// Don't accept backups; if we get explicit credentials,
|
||||
// but don't get a kbs credential, don't try and fetch one
|
||||
// via chat service auth or whatever. Ditto for svr2.
|
||||
// If we have both, or are given non-explicit auth credentials,
|
||||
// we want to try SVR2 but fall back to KBS if _anything_ goes
|
||||
// wrong (besides an explicit wrong PIN).
|
||||
switch authMethod {
|
||||
case .svrAuth(let authCredential, _):
|
||||
if authCredential.kbs != nil, authCredential.svr2 != nil {
|
||||
return fallbackToKBSForNonInvalidPinFailureIfPossible()
|
||||
} else if authCredential.kbs != nil {
|
||||
return kbsOnlyIfPossible()
|
||||
} else if authCredential.svr2 != nil {
|
||||
return svr2OnlyIfPossible()
|
||||
} else {
|
||||
return .reportNoBackup
|
||||
}
|
||||
case .chatServerAuth, .implicit:
|
||||
return fallbackToKBSForNonInvalidPinFailureIfPossible()
|
||||
}
|
||||
case .kbsOnly:
|
||||
return kbsOnlyIfPossible()
|
||||
case .mirroring:
|
||||
return fallbackToKBSForNoBackupFailureOnlyIfPossible()
|
||||
case .svr2Only:
|
||||
return svr2OnlyIfPossible()
|
||||
}
|
||||
}
|
||||
|
||||
private enum WriteStrategy {
|
||||
case kbsOnly(SVR.AuthMethod)
|
||||
case mirroring(kbsAuth: SVR.AuthMethod, svr2Auth: SVR.AuthMethod)
|
||||
case svr2Only(SVR.AuthMethod)
|
||||
case reportGenericError
|
||||
}
|
||||
|
||||
private func writeStrategy(for authMethod: SVR.AuthMethod) -> WriteStrategy {
|
||||
|
||||
let kbsAuth = authMethod.kbsCompatible
|
||||
let svr2Auth = authMethod.svr2Compatible
|
||||
|
||||
func kbsOnlyIfPossible() -> WriteStrategy {
|
||||
return kbsAuth.map { .kbsOnly($0) } ?? .reportGenericError
|
||||
}
|
||||
func svr2OnlyIfPossible() -> WriteStrategy {
|
||||
return svr2Auth.map { .svr2Only($0) } ?? .reportGenericError
|
||||
}
|
||||
func mirroringIfPossible() -> WriteStrategy {
|
||||
if let kbsAuth, let svr2Auth {
|
||||
return .mirroring(kbsAuth: kbsAuth, svr2Auth: svr2Auth)
|
||||
} else if let kbsAuth {
|
||||
return .kbsOnly(kbsAuth)
|
||||
} else if let svr2Auth {
|
||||
return .svr2Only(svr2Auth)
|
||||
} else {
|
||||
return .reportGenericError
|
||||
}
|
||||
}
|
||||
|
||||
guard let remoteSVRConfig else {
|
||||
owsFailBeta("Should only be writing after setting remote config")
|
||||
return mirroringIfPossible()
|
||||
}
|
||||
switch remoteSVRConfig {
|
||||
case .kbsOnly:
|
||||
return kbsOnlyIfPossible()
|
||||
case .mirroring:
|
||||
return mirroringIfPossible()
|
||||
case .svr2Only:
|
||||
return svr2OnlyIfPossible()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Delegation
|
||||
|
||||
public func warmCaches() {
|
||||
self.remoteSVRConfig = db.read(block: self.getStoredRemoteConfiguration(tx:))
|
||||
|
||||
var shouldWarmKBS = false
|
||||
var shouldWarmSVR2 = false
|
||||
switch remoteSVRConfig {
|
||||
case .none:
|
||||
// Both of these no-op if not registered,
|
||||
// so its pretty safe to call without a remote config.
|
||||
shouldWarmKBS = true
|
||||
shouldWarmSVR2 = true
|
||||
case .kbsOnly:
|
||||
shouldWarmKBS = true
|
||||
case .svr2Only:
|
||||
shouldWarmSVR2 = true
|
||||
case .mirroring:
|
||||
shouldWarmKBS = true
|
||||
shouldWarmSVR2 = true
|
||||
}
|
||||
|
||||
if shouldWarmKBS {
|
||||
kbs.warmCaches()
|
||||
}
|
||||
if shouldWarmSVR2 {
|
||||
svr2.warmCaches()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Key Existence
|
||||
|
||||
public var hasMasterKey: Bool {
|
||||
return db.read(block: self.hasMasterKey(transaction:))
|
||||
}
|
||||
|
||||
public func hasMasterKey(transaction: DBReadTransaction) -> Bool {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .kbsOnly, .reportNoBackup:
|
||||
return kbs.hasMasterKey(transaction: transaction)
|
||||
case .svr2Only:
|
||||
return svr2.hasMasterKey(transaction: transaction)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Local only operation; no errors to differentiate.
|
||||
return svr2.hasMasterKey(transaction: transaction) || kbs.hasMasterKey(transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
public var hasBackedUpMasterKey: Bool {
|
||||
return db.read(block: self.hasBackedUpMasterKey(transaction:))
|
||||
}
|
||||
|
||||
public func hasBackedUpMasterKey(transaction: DBReadTransaction) -> Bool {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .kbsOnly, .reportNoBackup:
|
||||
return kbs.hasBackedUpMasterKey(transaction: transaction)
|
||||
case .svr2Only:
|
||||
return svr2.hasBackedUpMasterKey(transaction: transaction)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Local only operation; no errors to differentiate.
|
||||
return svr2.hasBackedUpMasterKey(transaction: transaction) || kbs.hasBackedUpMasterKey(transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
public func useDeviceLocalMasterKey(authedAccount: AuthedAccount, transaction: DBWriteTransaction) {
|
||||
// Local write; auth is irrelevant
|
||||
// (yes its confusing there's an authed account; thats just to trigger storage service ops,
|
||||
// not to use to talk to kbs/svr2)
|
||||
switch writeStrategy(for: .implicit) {
|
||||
case .kbsOnly, .reportGenericError:
|
||||
kbs.useDeviceLocalMasterKey(authedAccount: authedAccount, transaction: transaction)
|
||||
case .svr2Only:
|
||||
svr2.useDeviceLocalMasterKey(authedAccount: authedAccount, transaction: transaction)
|
||||
case .mirroring:
|
||||
kbs.useDeviceLocalMasterKey(authedAccount: authedAccount, transaction: transaction)
|
||||
svr2.useDeviceLocalMasterKey(authedAccount: authedAccount, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - PIN Management
|
||||
|
||||
public var currentPinType: SVR.PinType? {
|
||||
return db.read(block: self.currentPinType(transaction:))
|
||||
}
|
||||
|
||||
public func currentPinType(transaction: DBReadTransaction) -> SVR.PinType? {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .kbsOnly, .reportNoBackup:
|
||||
return kbs.currentPinType(transaction: transaction)
|
||||
case .svr2Only:
|
||||
return svr2.currentPinType(transaction: transaction)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Local only operation; no errors to differentiate.
|
||||
return svr2.currentPinType(transaction: transaction) ?? kbs.currentPinType(transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
public func verifyPin(_ pin: String, resultHandler: @escaping (Bool) -> Void) {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .kbsOnly, .reportNoBackup:
|
||||
kbs.verifyPin(pin, resultHandler: resultHandler)
|
||||
case .svr2Only:
|
||||
svr2.verifyPin(pin, resultHandler: resultHandler)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Return true if any return true.
|
||||
// Note this is a local operation; the different fallback strategies
|
||||
// are irrelevant here.
|
||||
svr2.verifyPin(pin) { [kbs] success in
|
||||
if success {
|
||||
resultHandler(true)
|
||||
} else {
|
||||
kbs.verifyPin(pin, resultHandler: resultHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Key Management
|
||||
|
||||
public func acquireRegistrationLockForNewNumber(with pin: String, and auth: SVRAuthCredential) -> Promise<String> {
|
||||
switch readStrategy(for: .svrAuth(auth, backup: nil)) {
|
||||
case .reportNoBackup:
|
||||
return .init(error: SVR.SVRError.backupMissing)
|
||||
case .kbsOnly:
|
||||
return kbs.acquireRegistrationLockForNewNumber(with: pin, and: auth)
|
||||
case .svr2Only:
|
||||
return svr2.acquireRegistrationLockForNewNumber(with: pin, and: auth)
|
||||
case .fallbackToKBSForNoBackupFailureOnly:
|
||||
return svr2.acquireRegistrationLockForNewNumber(with: pin, and: auth).recover(on: schedulers.main) { [kbs] error in
|
||||
if error.isBackupMissingError {
|
||||
return kbs.acquireRegistrationLockForNewNumber(with: pin, and: auth)
|
||||
} else {
|
||||
return .init(error: error)
|
||||
}
|
||||
}
|
||||
case .fallbackToKBSForNonInvalidPinFailure:
|
||||
return svr2.acquireRegistrationLockForNewNumber(with: pin, and: auth).recover(on: schedulers.main) { [kbs] error in
|
||||
if error.isInvalidPinError {
|
||||
return Promise<String>(error: error)
|
||||
} else {
|
||||
return kbs.acquireRegistrationLockForNewNumber(with: pin, and: auth)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func generateAndBackupKeys(pin: String, authMethod: SVR.AuthMethod, rotateMasterKey: Bool) -> Promise<Void> {
|
||||
switch writeStrategy(for: authMethod) {
|
||||
case .reportGenericError:
|
||||
return .init(error: SVR.SVRError.assertion)
|
||||
case .kbsOnly(let authMethod):
|
||||
return kbs.generateAndBackupKeys(pin: pin, authMethod: authMethod, rotateMasterKey: rotateMasterKey)
|
||||
case .svr2Only(let authMethod):
|
||||
return svr2.generateAndBackupKeys(pin: pin, authMethod: authMethod, rotateMasterKey: rotateMasterKey)
|
||||
case .mirroring(let kbsAuthMethod, let svr2AuthMethod):
|
||||
return svr2.generateAndBackupKeys(pin: pin, authMethod: svr2AuthMethod, rotateMasterKey: rotateMasterKey).then(on: schedulers.main) { [kbs] (masterKey: Data) in
|
||||
return kbs.generateAndBackupKeys(pin: pin, authMethod: kbsAuthMethod, masterKey: masterKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func restoreKeys(pin: String, authMethod: SVR.AuthMethod) -> Guarantee<SVR.RestoreKeysResult> {
|
||||
switch readStrategy(for: authMethod) {
|
||||
case .reportNoBackup:
|
||||
return .value(.backupMissing)
|
||||
case .kbsOnly(let authMethod):
|
||||
return kbs.restoreKeys(pin: pin, authMethod: authMethod)
|
||||
case .svr2Only(let authMethod):
|
||||
return svr2.restoreKeys(pin: pin, authMethod: authMethod)
|
||||
case .fallbackToKBSForNoBackupFailureOnly(let kbsAuthMethod, let svr2AuthMethod):
|
||||
return svr2.restoreKeys(pin: pin, authMethod: svr2AuthMethod).then(on: schedulers.main) { [kbs] result in
|
||||
switch result {
|
||||
case .success, .invalidPin, .genericError, .networkError:
|
||||
return .value(result)
|
||||
case .backupMissing:
|
||||
return kbs.restoreKeys(pin: pin, authMethod: kbsAuthMethod)
|
||||
}
|
||||
}
|
||||
case .fallbackToKBSForNonInvalidPinFailure(let kbsAuthMethod, let svr2AuthMethod):
|
||||
return svr2.restoreKeys(pin: pin, authMethod: svr2AuthMethod).then(on: schedulers.main) { [kbs] result in
|
||||
switch result {
|
||||
case .success, .invalidPin:
|
||||
return .value(result)
|
||||
case .backupMissing, .genericError, .networkError:
|
||||
return kbs.restoreKeys(pin: pin, authMethod: kbsAuthMethod)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func restoreKeysAndBackup(pin: String, authMethod: SVR.AuthMethod) -> Guarantee<SVR.RestoreKeysResult> {
|
||||
// We have to separate the read from the write, because each might fail.
|
||||
// A valid path here is reading from SVR succeeds, then we write that
|
||||
// restored value to both svr and kbs.
|
||||
return firstly(on: schedulers.sync) {
|
||||
return self.restoreKeys(pin: pin, authMethod: authMethod)
|
||||
}.then(on: schedulers.main) { [weak self] restoreKeysResult in
|
||||
guard let self else {
|
||||
return .value(.genericError(OWSAssertionError("unretained self")))
|
||||
}
|
||||
return self.generateAndBackupKeys(pin: pin, authMethod: authMethod, rotateMasterKey: false)
|
||||
.map(on: SyncScheduler()) {
|
||||
return restoreKeysResult
|
||||
}
|
||||
.recover(on: SyncScheduler()) { error in
|
||||
if error.isNetworkFailureOrTimeout {
|
||||
return .value(.networkError(error))
|
||||
}
|
||||
return .value(.genericError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func deleteKeys() -> Promise<Void> {
|
||||
// Auth is always implicit for this method.
|
||||
switch writeStrategy(for: .implicit) {
|
||||
case .reportGenericError:
|
||||
return .init(error: SVR.SVRError.assertion)
|
||||
case .kbsOnly:
|
||||
return kbs.deleteKeys()
|
||||
case .svr2Only:
|
||||
return svr2.deleteKeys()
|
||||
case .mirroring:
|
||||
return svr2.deleteKeys().then(on: schedulers.main) { [kbs] in
|
||||
return kbs.deleteKeys()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func clearKeys(transaction: DBWriteTransaction) {
|
||||
// This is a special kind of local write; we want to clear
|
||||
// all keys we have across both regardless of write strategy.
|
||||
svr2.clearKeys(transaction: transaction)
|
||||
kbs.clearKeys(transaction: transaction)
|
||||
}
|
||||
|
||||
// MARK: - Master Key Encryption
|
||||
|
||||
public func encrypt(
|
||||
keyType: SVR.DerivedKey,
|
||||
data: Data,
|
||||
transaction: DBReadTransaction
|
||||
) -> SVR.ApplyDerivedKeyResult {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .kbsOnly, .reportNoBackup:
|
||||
return kbs.encrypt(keyType: keyType, data: data, transaction: transaction)
|
||||
case .svr2Only:
|
||||
return svr2.encrypt(keyType: keyType, data: data, transaction: transaction)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Local only operation; no different error behavior.
|
||||
let svr2Result = svr2.encrypt(keyType: keyType, data: data, transaction: transaction)
|
||||
if svr2Result.isKeyMissingError {
|
||||
return kbs.encrypt(keyType: keyType, data: data, transaction: transaction)
|
||||
} else {
|
||||
return svr2Result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func decrypt(
|
||||
keyType: SVR.DerivedKey,
|
||||
encryptedData: Data,
|
||||
transaction: DBReadTransaction
|
||||
) -> SVR.ApplyDerivedKeyResult {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .kbsOnly, .reportNoBackup:
|
||||
return kbs.decrypt(keyType: keyType, encryptedData: encryptedData, transaction: transaction)
|
||||
case .svr2Only:
|
||||
return svr2.decrypt(keyType: keyType, encryptedData: encryptedData, transaction: transaction)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Local only operation; no different error behavior.
|
||||
let svr2Result = svr2.decrypt(keyType: keyType, encryptedData: encryptedData, transaction: transaction)
|
||||
if svr2Result.isKeyMissingError {
|
||||
return kbs.decrypt(keyType: keyType, encryptedData: encryptedData, transaction: transaction)
|
||||
} else {
|
||||
return svr2Result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: By 03/2024, we can remove this method. Starting in 10/2023, we started sending
|
||||
// master keys in syncs. 90 days later, all active primaries will be sending the master key.
|
||||
// 30 days after that all message queues will have been flushed, at which point sync messages
|
||||
// without a master key will be impossible.
|
||||
public func storeSyncedStorageServiceKey(
|
||||
data: Data?,
|
||||
authedAccount: AuthedAccount,
|
||||
transaction: DBWriteTransaction
|
||||
) {
|
||||
// This is a special kind of local write; it only writes to local state
|
||||
// and in fact doesn't affect svr1/2 at all since it only happens on linked
|
||||
// devices which never theselves talk to svr.
|
||||
// Ok to ignore write strategy and always write to both.
|
||||
|
||||
// NOTE: this will trigger multiple storage service syncs.
|
||||
// However, those happen asynchronously, and StorageService code
|
||||
// should dedupe the requests. So this should be fine in the
|
||||
// short term while we use both SVRs in parallel.
|
||||
kbs.storeSyncedStorageServiceKey(data: data, authedAccount: authedAccount, transaction: transaction)
|
||||
svr2.storeSyncedStorageServiceKey(data: data, authedAccount: authedAccount, transaction: transaction)
|
||||
}
|
||||
|
||||
public func storeSyncedMasterKey(
|
||||
data: Data,
|
||||
authedAccount: AuthedAccount,
|
||||
transaction: DBWriteTransaction
|
||||
) {
|
||||
// This is a special kind of local write; it only writes to local state
|
||||
// and in fact doesn't affect svr1/2 at all since it only happens on linked
|
||||
// devices which never theselves talk to svr.
|
||||
// Ok to ignore write strategy and always write to both.
|
||||
kbs.storeSyncedMasterKey(data: data, authedAccount: authedAccount, transaction: transaction)
|
||||
svr2.storeSyncedMasterKey(data: data, authedAccount: authedAccount, transaction: transaction)
|
||||
}
|
||||
|
||||
public func masterKeyDataForKeysSyncMessage(tx: DBReadTransaction) -> Data? {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .kbsOnly, .reportNoBackup:
|
||||
return kbs.masterKeyDataForKeysSyncMessage(tx: tx)
|
||||
case .svr2Only:
|
||||
return svr2.masterKeyDataForKeysSyncMessage(tx: tx)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Local only operation; no errors to differentiate.
|
||||
return svr2.masterKeyDataForKeysSyncMessage(tx: tx) ?? kbs.masterKeyDataForKeysSyncMessage(tx: tx)
|
||||
}
|
||||
}
|
||||
|
||||
public func clearSyncedStorageServiceKey(transaction: DBWriteTransaction) {
|
||||
// This is a special kind of local write; it only writes to local state
|
||||
// and in fact doesn't affect svr1/2 at all since it only happens on linked
|
||||
// devices which never theselves talk to svr.
|
||||
// Ok to ignore write strategy and always write to both.
|
||||
kbs.clearSyncedStorageServiceKey(transaction: transaction)
|
||||
svr2.clearSyncedStorageServiceKey(transaction: transaction)
|
||||
}
|
||||
|
||||
// MARK: - Value Derivation
|
||||
|
||||
public func data(for key: SVR.DerivedKey, transaction: DBReadTransaction) -> SVR.DerivedKeyData? {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .reportNoBackup:
|
||||
return nil
|
||||
case .kbsOnly:
|
||||
return kbs.data(for: key, transaction: transaction)
|
||||
case .svr2Only:
|
||||
return svr2.data(for: key, transaction: transaction)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Local only operation; no different error behavior.
|
||||
return svr2.data(for: key, transaction: transaction) ?? kbs.data(for: key, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
public func isKeyAvailable(_ key: SVR.DerivedKey) -> Bool {
|
||||
return db.read {
|
||||
return self.isKeyAvailable(key, transaction: $0)
|
||||
}
|
||||
}
|
||||
|
||||
public func isKeyAvailable(_ key: SVR.DerivedKey, transaction: DBReadTransaction) -> Bool {
|
||||
// Local read; auth is irrelevant
|
||||
switch readStrategy(for: .implicit) {
|
||||
case .reportNoBackup:
|
||||
return false
|
||||
case .kbsOnly:
|
||||
return kbs.isKeyAvailable(key, transaction: transaction)
|
||||
case .svr2Only:
|
||||
return svr2.isKeyAvailable(key, transaction: transaction)
|
||||
case .fallbackToKBSForNoBackupFailureOnly, .fallbackToKBSForNonInvalidPinFailure:
|
||||
// Local only operation; no different error behavior.
|
||||
return svr2.isKeyAvailable(key, transaction: transaction) || kbs.isKeyAvailable(key, transaction: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AuthMethod
|
||||
|
||||
extension SVR.AuthMethod {
|
||||
|
||||
var kbsCompatible: SVR.AuthMethod? {
|
||||
switch self {
|
||||
case .svrAuth(let authCredential, let backup):
|
||||
if authCredential.kbs != nil {
|
||||
return self
|
||||
}
|
||||
return backup?.kbsCompatible
|
||||
case .chatServerAuth, .implicit:
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
var svr2Compatible: SVR.AuthMethod? {
|
||||
switch self {
|
||||
case .svrAuth(let authCredential, let backup):
|
||||
if authCredential.svr2 != nil {
|
||||
return self
|
||||
}
|
||||
return backup?.svr2Compatible
|
||||
case .chatServerAuth, .implicit:
|
||||
return self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Errors
|
||||
|
||||
extension SVR.ApplyDerivedKeyResult {
|
||||
|
||||
fileprivate var isKeyMissingError: Bool {
|
||||
switch self {
|
||||
case .success:
|
||||
return false
|
||||
case .masterKeyMissing:
|
||||
return true
|
||||
case .cryptographyError:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Error {
|
||||
|
||||
fileprivate var isBackupMissingError: Bool {
|
||||
switch self as? SVR.SVRError {
|
||||
case .none, .assertion, .invalidPin:
|
||||
return false
|
||||
case .backupMissing:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var isInvalidPinError: Bool {
|
||||
switch self as? SVR.SVRError {
|
||||
case .none, .assertion, .backupMissing:
|
||||
return false
|
||||
case .invalidPin:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,18 +16,5 @@ public struct SVR2AuthCredential: Equatable, Codable {
|
||||
}
|
||||
|
||||
public enum SVR2 {
|
||||
/// An auth credential is needed to talk to the SVR server.
|
||||
/// This defines how we should get that auth credential
|
||||
public indirect enum AuthMethod: Equatable {
|
||||
/// Explicitly provide an auth credential to use directly with SVR2.
|
||||
/// note: if it fails, will fall back to the backup or implicit if unset.
|
||||
case svrAuth(SVR2AuthCredential, backup: AuthMethod?)
|
||||
/// Get an SVR2 auth credential from the chat server first with the
|
||||
/// provided credentials, then use it to talk to the SVR2 server.
|
||||
case chatServerAuth(AuthedAccount)
|
||||
/// Use whatever SVR2 auth credential we have cached; if unavailable or
|
||||
/// if invalid, falls back to getting a SVR2 auth credential from the chat server
|
||||
/// with the chat server auth credentials we have cached.
|
||||
case implicit
|
||||
}
|
||||
public typealias AuthMethod = SVR.AuthMethod
|
||||
}
|
||||
|
||||
@ -9,9 +9,11 @@ import LibSignalClient
|
||||
extension SVR2 {
|
||||
public enum Shims {
|
||||
public typealias AppReadiness = _SVR2_AppReadinessShim
|
||||
public typealias OWS2FAManager = _SVR2_OWS2FAManagerShim
|
||||
}
|
||||
public enum Wrappers {
|
||||
public typealias AppReadiness = _SVR2_AppReadinessWrapper
|
||||
public typealias OWS2FAManager = _SVR2_OWS2FAManagerWrapper
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +30,26 @@ public class _SVR2_AppReadinessWrapper: _SVR2_AppReadinessShim {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OWS2FAManager
|
||||
|
||||
public protocol _SVR2_OWS2FAManagerShim {
|
||||
func pinCode(transaction: DBReadTransaction) -> String?
|
||||
func markDisabled(transaction: DBWriteTransaction)
|
||||
}
|
||||
|
||||
public class _SVR2_OWS2FAManagerWrapper: SVR2.Shims.OWS2FAManager {
|
||||
private let manager: OWS2FAManager
|
||||
public init(_ manager: OWS2FAManager) { self.manager = manager }
|
||||
|
||||
public func pinCode(transaction: DBReadTransaction) -> String? {
|
||||
return manager.pinCode(with: SDSDB.shimOnlyBridge(transaction))
|
||||
}
|
||||
|
||||
public func markDisabled(transaction: DBWriteTransaction) {
|
||||
manager.markDisabled(transaction: SDSDB.shimOnlyBridge(transaction))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The below aren't shims/wrappers in the normal sense; they
|
||||
// wrap libsignal stuff that we will _always_ need to wrap.
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
private let syncManager: SyncManagerProtocolSwift
|
||||
private let tsAccountManager: TSAccountManager
|
||||
private let tsConstants: TSConstantsProtocol
|
||||
private let twoFAManager: SVR.Shims.OWS2FAManager
|
||||
private let twoFAManager: SVR2.Shims.OWS2FAManager
|
||||
|
||||
public convenience init(
|
||||
accountAttributesUpdater: AccountAttributesUpdater,
|
||||
@ -39,7 +39,7 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
syncManager: SyncManagerProtocolSwift,
|
||||
tsAccountManager: TSAccountManager,
|
||||
tsConstants: TSConstantsProtocol,
|
||||
twoFAManager: SVR.Shims.OWS2FAManager
|
||||
twoFAManager: SVR2.Shims.OWS2FAManager
|
||||
) {
|
||||
self.init(
|
||||
accountAttributesUpdater: accountAttributesUpdater,
|
||||
@ -77,7 +77,7 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
syncManager: SyncManagerProtocolSwift,
|
||||
tsAccountManager: TSAccountManager,
|
||||
tsConstants: TSConstantsProtocol,
|
||||
twoFAManager: SVR.Shims.OWS2FAManager
|
||||
twoFAManager: SVR2.Shims.OWS2FAManager
|
||||
) {
|
||||
self.accountAttributesUpdater = accountAttributesUpdater
|
||||
self.appReadiness = appReadiness
|
||||
@ -244,10 +244,6 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
|
||||
public func acquireRegistrationLockForNewNumber(with pin: String, and auth: SVRAuthCredential) -> Promise<String> {
|
||||
Logger.info("")
|
||||
guard let auth = auth.svr2 else {
|
||||
owsFailDebug("Invalid auth for svr2")
|
||||
return .init(error: SVR.SVRError.assertion)
|
||||
}
|
||||
return doRestore(pin: pin, authMethod: .svrAuth(auth, backup: nil)).then(on: scheduler) { restoreResult -> Promise<String> in
|
||||
switch restoreResult {
|
||||
case .success(let masterKey, _):
|
||||
@ -278,10 +274,6 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
|
||||
internal func generateAndBackupKeys(pin: String, authMethod: SVR.AuthMethod, rotateMasterKey: Bool) -> Promise<Data> {
|
||||
Logger.info("backing up, rotating master key? \(rotateMasterKey)")
|
||||
guard let authMethod = authMethod.svr2 else {
|
||||
owsFailDebug("Invalid auth for svr2")
|
||||
return .init(error: SVR.SVRError.assertion)
|
||||
}
|
||||
return firstly(on: scheduler) { [weak self] () -> Promise<Data> in
|
||||
guard let self else {
|
||||
return .init(error: SVR.SVRError.assertion)
|
||||
@ -298,10 +290,6 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
|
||||
public func restoreKeys(pin: String, authMethod: SVR.AuthMethod) -> Guarantee<SVR.RestoreKeysResult> {
|
||||
Logger.info("")
|
||||
guard let authMethod = authMethod.svr2 else {
|
||||
owsFailDebug("Invalid auth for svr2")
|
||||
return .value(.genericError(SVR.SVRError.assertion))
|
||||
}
|
||||
// When we restore, we remember which enclave it was from. On some future app startup, we check
|
||||
// this enclave, and migrate to a new one if available. This code path relies on that happening
|
||||
// asynchronously.
|
||||
@ -310,10 +298,7 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
|
||||
public func restoreKeysAndBackup(pin: String, authMethod: SVR.AuthMethod) -> Guarantee<SVR.RestoreKeysResult> {
|
||||
Logger.info("")
|
||||
guard let authMethod = authMethod.svr2 else {
|
||||
owsFailDebug("Invalid auth for svr2")
|
||||
return .value(.genericError(SVR.SVRError.assertion))
|
||||
}
|
||||
|
||||
return doRestore(pin: pin, authMethod: authMethod)
|
||||
.then(on: scheduler) { [weak self] restoreResult in
|
||||
switch restoreResult {
|
||||
@ -1240,7 +1225,7 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
/// If there is no migration needed, returns a success promise immediately.
|
||||
private func migrateEnclavesIfNecessary() -> Promise<Void> {
|
||||
return firstly(on: scheduler) { [weak self] () -> (String?, String, Data)? in
|
||||
return self?.db.read { tx in
|
||||
return self?.db.read { tx -> (String?, String, Data)? in
|
||||
guard
|
||||
let self,
|
||||
self.tsAccountManager.registrationState(tx: tx).isRegisteredPrimaryDevice,
|
||||
@ -1273,7 +1258,7 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
}
|
||||
if
|
||||
oldSVR2EnclaveString == nil,
|
||||
self.localStorage.getSVR1EnclaveName(tx) != nil
|
||||
self.localStorage.hadSVR1Enclave(tx)
|
||||
{
|
||||
// We have not ever backed up to svr2, but we had backed up
|
||||
// to svr1 (kbs), so we should migrate immediately.
|
||||
@ -1663,29 +1648,6 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension SVR.AuthMethod {
|
||||
|
||||
var svr2: SVR2.AuthMethod? {
|
||||
switch self {
|
||||
case .svrAuth(let authCredential, let backup):
|
||||
switch authCredential {
|
||||
case .svr2Only(let svr2AuthCredential):
|
||||
return .svrAuth(svr2AuthCredential, backup: backup?.svr2)
|
||||
case .both(_, let svr2AuthCredential):
|
||||
return .svrAuth(svr2AuthCredential, backup: backup?.svr2)
|
||||
case .kbsOnly:
|
||||
// We explicitly opted to kbs only, don't try
|
||||
// the backup (its probably a chat service auth credential).
|
||||
return nil
|
||||
}
|
||||
case .chatServerAuth(let authedAccount):
|
||||
return .chatServerAuth(authedAccount)
|
||||
case .implicit:
|
||||
return .implicit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension SVR2.AuthMethod {
|
||||
|
||||
var authedAccount: AuthedAccount {
|
||||
|
||||
@ -7,12 +7,5 @@ import Foundation
|
||||
|
||||
/// Changes the pointer to the SVR auth credential used in the rest of the app.
|
||||
///
|
||||
/// At first, we talk only to KBS (SVR1), and this credential just points to
|
||||
/// KBSAuthCredential as a result.
|
||||
///
|
||||
/// Once support for SVR2 is added, we will talk to _both_ KBS and SVR2;
|
||||
/// at that point this should point to OrchestratingSVRAuthCredential instead.
|
||||
///
|
||||
/// ~90 days later, we will stop talking to KBS entirely, and this should be changed
|
||||
/// to point to SVR2AuthCredential.
|
||||
public typealias SVRAuthCredential = OrchestratingSVRAuthCredential
|
||||
/// Currently we only talk to SVR2; eventually this may point to SVR3.
|
||||
public typealias SVRAuthCredential = SVR2AuthCredential
|
||||
|
||||
@ -42,41 +42,4 @@ public protocol SVRAuthCredentialStorage {
|
||||
/// Removes all credentials for the current user from storage.
|
||||
/// Called when we e.g. wipe all SVR info, or learn it has been wiped on the server.
|
||||
func removeSVR2CredentialsForCurrentUser(_ transaction: DBWriteTransaction)
|
||||
|
||||
// MARK: - KBS (SVR1)
|
||||
|
||||
// Everything below here can be cleanly removed alongisde KeyBackupServiceImpl
|
||||
// once KBS is deprecated in favor of SVR2.
|
||||
|
||||
/// Stores an `KBSAuthCredential` to both local storage and iCloud storage, overwriting
|
||||
/// any existing credentials for the same username.
|
||||
/// Also marks the credential as "current", using it for future KBS requests until a new credential
|
||||
/// is provided to this method.
|
||||
/// Stores up to `SVR.maxSVRAuthCredentialsBackedUp` credentials; if more are inserted,
|
||||
/// they are dropped in FIFO order.
|
||||
///
|
||||
/// Note multiple accounts can share an iCloud and so more than one user may be present in storage.
|
||||
/// iCloud storage is used to re-register on other devices without the need for OTP verification via phone number.
|
||||
func storeAuthCredentialForCurrentUsername(_ auth: KBSAuthCredential, _ transaction: DBWriteTransaction)
|
||||
|
||||
/// Gets all stored `KBSAuthCredential`s, from local disk if available, or iCloud otherwise.
|
||||
/// Credentials may be expired or invalid for a particular e164; callers should poke the registration
|
||||
/// server to validate them as a set.
|
||||
/// Returns up to `SVR.maxSVRAuthCredentialsBackedUp` credentials.
|
||||
func getAuthCredentials(_ transaction: DBReadTransaction) -> [KBSAuthCredential]
|
||||
|
||||
/// Gets the `KBSAuthCredential` that was most recently stored on this device.
|
||||
/// Returns nil if there are no backed up credentials, or if all credentials came from iCloud
|
||||
/// storage and therefore there is no concept of a "current" username.
|
||||
/// In such cases, the user should go through registration using the full list with
|
||||
/// `getAuthCredentials`.
|
||||
func getAuthCredentialForCurrentUser(_ transaction: DBReadTransaction) -> KBSAuthCredential?
|
||||
|
||||
/// Removes the provided credentials from storage.
|
||||
/// Should be called when the server tells the client the credential(s) are invalid.
|
||||
func deleteInvalidCredentials(_: [KBSAuthCredential], _ transaction: DBWriteTransaction)
|
||||
|
||||
/// Removes all KBS credentials for the current user from storage.
|
||||
/// Called when we e.g. wipe all SVR info, or learn it has been wiped on the server.
|
||||
func removeKBSCredentialsForCurrentUser(_ transaction: DBWriteTransaction)
|
||||
}
|
||||
|
||||
@ -55,46 +55,6 @@ public class SVRAuthCredentialStorageImpl: SVRAuthCredentialStorage {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - KBS (SVR1)
|
||||
|
||||
public func storeAuthCredentialForCurrentUsername(_ credential: KBSAuthCredential, _ transaction: DBWriteTransaction) {
|
||||
let credential = AuthCredential.from(credential)
|
||||
updateCredentials(for: .kbs, transaction) {
|
||||
// Sorting is handled by updateCredentials
|
||||
$0.append(credential)
|
||||
}
|
||||
setCurrentUsername(credential.username, for: .kbs, transaction)
|
||||
}
|
||||
|
||||
public func getAuthCredentials(_ transaction: DBReadTransaction) -> [KBSAuthCredential] {
|
||||
return consolidateLocalAndiCloud(for: .kbs, transaction).map { $0.toKBSCredential() }
|
||||
}
|
||||
|
||||
public func getAuthCredentialForCurrentUser(_ transaction: DBReadTransaction) -> KBSAuthCredential? {
|
||||
return consolidateLocalAndiCloud(for: .kbs, transaction).first(where: {
|
||||
return $0.username == currentUsername(for: .kbs, transaction)
|
||||
})?.toKBSCredential()
|
||||
}
|
||||
|
||||
public func deleteInvalidCredentials(_ invalidCredentials: [KBSAuthCredential], _ transaction: DBWriteTransaction) {
|
||||
updateCredentials(for: .kbs, transaction) {
|
||||
$0.removeAll(where: { existingCredential in
|
||||
invalidCredentials.contains(where: { invalidCredential in
|
||||
existingCredential.isEqual(to: invalidCredential)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public func removeKBSCredentialsForCurrentUser(_ transaction: DBWriteTransaction) {
|
||||
let currentUsername = currentUsername(for: .kbs, transaction)
|
||||
updateCredentials(for: .kbs, transaction) {
|
||||
$0.removeAll(where: { existingCredential in
|
||||
existingCredential.username == currentUsername
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Local Storage
|
||||
|
||||
// Also write any KBS auth credentials we know about to local, encrypted database storage,
|
||||
@ -239,13 +199,10 @@ public class SVRAuthCredentialStorageImpl: SVRAuthCredentialStorage {
|
||||
}
|
||||
|
||||
internal enum CredentialType: Hashable {
|
||||
case kbs
|
||||
case svr2
|
||||
|
||||
var iCloudCredentialsKey: String {
|
||||
switch self {
|
||||
case .kbs:
|
||||
return "signal_kbs_credentials"
|
||||
case .svr2:
|
||||
return "signal_svr2_credentials"
|
||||
}
|
||||
@ -253,8 +210,6 @@ public class SVRAuthCredentialStorageImpl: SVRAuthCredentialStorage {
|
||||
|
||||
var kvStoreCollectionName: String {
|
||||
switch self {
|
||||
case .kbs:
|
||||
return "KBSAuthCredential"
|
||||
case .svr2:
|
||||
return "SVR2AuthCredential"
|
||||
}
|
||||
@ -274,14 +229,6 @@ public class SVRAuthCredentialStorageImpl: SVRAuthCredentialStorage {
|
||||
)
|
||||
}
|
||||
|
||||
static func from(_ credential: KBSAuthCredential) -> Self {
|
||||
return AuthCredential(
|
||||
username: credential.credential.username,
|
||||
password: credential.credential.password,
|
||||
insertionTime: Date()
|
||||
)
|
||||
}
|
||||
|
||||
private var credential: RemoteAttestation.Auth {
|
||||
return RemoteAttestation.Auth(username: username, password: password)
|
||||
}
|
||||
@ -290,19 +237,6 @@ public class SVRAuthCredentialStorageImpl: SVRAuthCredentialStorage {
|
||||
return SVR2AuthCredential(credential: credential)
|
||||
}
|
||||
|
||||
func toKBSCredential() -> KBSAuthCredential {
|
||||
return KBSAuthCredential(credential: credential)
|
||||
}
|
||||
|
||||
func isEqual(to credential: KBSAuthCredential) -> Bool {
|
||||
// Compare everything but the insertion time, make that equal.
|
||||
return AuthCredential(
|
||||
username: credential.credential.username,
|
||||
password: credential.credential.password,
|
||||
insertionTime: self.insertionTime
|
||||
) == self
|
||||
}
|
||||
|
||||
func isEqual(to credential: SVR2AuthCredential) -> Bool {
|
||||
// Compare everything but the insertion time, make that equal.
|
||||
return AuthCredential(
|
||||
|
||||
@ -169,33 +169,6 @@ public enum RegistrationRequestFactory {
|
||||
return result
|
||||
}
|
||||
|
||||
// MARK: - KBS Auth Check
|
||||
|
||||
public static func kbsAuthCredentialCheckRequest(
|
||||
e164: E164,
|
||||
credentials: [KBSAuthCredential]
|
||||
) -> TSRequest {
|
||||
owsAssertDebug(!credentials.isEmpty)
|
||||
|
||||
let urlPathComponents = URLPathComponents(
|
||||
["v1", "backup", "auth", "check"]
|
||||
)
|
||||
var urlComponents = URLComponents()
|
||||
urlComponents.percentEncodedPath = urlPathComponents.percentEncoded
|
||||
let url = urlComponents.url!
|
||||
|
||||
let parameters: [String: Any] = [
|
||||
"number": e164.stringValue,
|
||||
"passwords": credentials.map {
|
||||
"\($0.credential.username):\($0.credential.password)"
|
||||
}
|
||||
]
|
||||
|
||||
let result = TSRequest(url: url, method: "POST", parameters: parameters)
|
||||
result.shouldHaveAuthorizationHeaders = false
|
||||
return result
|
||||
}
|
||||
|
||||
// MARK: - SVR2 Auth Check
|
||||
|
||||
public static func svr2AuthCredentialCheckRequest(
|
||||
|
||||
@ -166,43 +166,6 @@ public enum RegistrationServiceResponses {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - KBS Auth Check
|
||||
|
||||
public enum KBSAuthCheckResponseCodes: Int, UnknownEnumCodable {
|
||||
/// Success. Response body has `KBSAuthCheckResponse` object.
|
||||
case success = 200
|
||||
/// The server couldn't parse the set of credentials.
|
||||
case malformedRequest = 422
|
||||
/// The POST request body is not valid JSON.
|
||||
case invalidJSON = 400
|
||||
case unexpectedError = -1
|
||||
|
||||
static public var unknown: Self { .unexpectedError }
|
||||
}
|
||||
|
||||
public struct KBSAuthCheckResponse: Codable {
|
||||
public let matches: [String: Result]
|
||||
|
||||
public enum Result: String, UnknownEnumCodable {
|
||||
/// At most one credential will be marked as a `match` per request.
|
||||
/// Clients should use this credential when re-registering the associated phone number.
|
||||
case match
|
||||
/// The provided credential is valid and should be retained by the client,
|
||||
/// but cannot be used to re-register the provided number.
|
||||
case notMatch = "not-match"
|
||||
/// Indicates that the credential may not be used to re-register any phone number and should be discarded.
|
||||
case invalid
|
||||
|
||||
// Server API explicitly says clients should treat unrecognized values as invalid.
|
||||
static public var unknown: Self { return .invalid }
|
||||
}
|
||||
|
||||
public func result(for credential: KBSAuthCredential) -> Result? {
|
||||
let key = "\(credential.credential.username):\(credential.credential.password)"
|
||||
return matches[key]
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SVR2 Auth Check
|
||||
|
||||
public enum SVR2AuthCheckResponseCodes: Int, UnknownEnumCodable {
|
||||
@ -341,57 +304,33 @@ public enum RegistrationServiceResponses {
|
||||
}
|
||||
}
|
||||
|
||||
public struct RegistrationLockFailureResponse: Codable {
|
||||
public struct RegistrationLockFailureResponse: Decodable {
|
||||
/// Time remaining until the registration lock expires and the account
|
||||
/// can be taken over.
|
||||
public let timeRemainingMs: Int
|
||||
/// A credential with which the client can talk to KBS server to
|
||||
/// recover the KBS master key, and from it the reglock token,
|
||||
/// using the user's PIN.
|
||||
/// NOTE: this is NOT an SVR2 credential.
|
||||
public let kbsAuthCredential: KBSAuthCredential?
|
||||
/// A credential with which the client can talk to SVR2 server to
|
||||
/// recover the SVR master key, and from it the reglock token,
|
||||
/// using the user's PIN.
|
||||
/// NOTE: this is NOT an KBS/SVR1 credential.
|
||||
public let svr2AuthCredential: SVR2AuthCredential?
|
||||
public let svr2AuthCredential: SVR2AuthCredential
|
||||
|
||||
public enum CodingKeys: String, CodingKey {
|
||||
case timeRemainingMs = "timeRemaining"
|
||||
case kbsAuthCredential = "backupCredentials"
|
||||
case svr2AuthCredential = "svr2Credentials"
|
||||
}
|
||||
|
||||
public init(
|
||||
timeRemainingMs: Int,
|
||||
kbsAuthCredential: KBSAuthCredential,
|
||||
svr2AuthCredential: SVR2AuthCredential
|
||||
) {
|
||||
self.timeRemainingMs = timeRemainingMs
|
||||
self.kbsAuthCredential = kbsAuthCredential
|
||||
self.svr2AuthCredential = svr2AuthCredential
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
timeRemainingMs = try container.decode(Int.self, forKey: .timeRemainingMs)
|
||||
if let credential = try container.decodeIfPresent(RemoteAttestation.Auth.self, forKey: .kbsAuthCredential) {
|
||||
kbsAuthCredential = KBSAuthCredential(credential: credential)
|
||||
} else {
|
||||
self.kbsAuthCredential = nil
|
||||
}
|
||||
if let svr2Credential = try container.decodeIfPresent(RemoteAttestation.Auth.self, forKey: .svr2AuthCredential) {
|
||||
self.svr2AuthCredential = SVR2AuthCredential(credential: svr2Credential)
|
||||
} else {
|
||||
self.svr2AuthCredential = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(timeRemainingMs, forKey: .timeRemainingMs)
|
||||
try container.encodeIfPresent(kbsAuthCredential?.credential, forKey: .kbsAuthCredential)
|
||||
try container.encodeIfPresent(svr2AuthCredential?.credential, forKey: .svr2AuthCredential)
|
||||
let svr2Credential = try container.decode(RemoteAttestation.Auth.self, forKey: .svr2AuthCredential)
|
||||
self.svr2AuthCredential = .init(credential: svr2Credential)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -235,27 +235,6 @@ public class RemoteConfig: BaseFlags {
|
||||
getUInt32Value(forFlag: .maxNicknameLength, defaultValue: 32)
|
||||
}
|
||||
|
||||
/// NOTE: serialized, be careful changing raw values.
|
||||
public enum SVRConfiguration: Int {
|
||||
/// Exclusively read/write to KBS (as if SVR2 didn't exist)
|
||||
case kbsOnly = 0
|
||||
/// Write to SVR2 and then KBS. Require both to succeed.
|
||||
/// Read from SVR2 first; if it has no backups, read from KBS.
|
||||
case mirroring = 1
|
||||
/// Exclusively read/write to SVR2.
|
||||
case svr2Only = 2
|
||||
}
|
||||
|
||||
public var svrConfiguration: SVRConfiguration {
|
||||
if isEnabled(.exclusiveSVR2) {
|
||||
return .svr2Only
|
||||
}
|
||||
if isEnabled(.stopMirroringToSVR2Override) {
|
||||
return .kbsOnly
|
||||
}
|
||||
return .mirroring
|
||||
}
|
||||
|
||||
static var tryToReturnAcisWithoutUaks: Bool {
|
||||
if !FeatureFlags.phoneNumberIdentifiers {
|
||||
return true
|
||||
@ -533,8 +512,6 @@ private struct Flags {
|
||||
// marked true regardless of the remote state.
|
||||
enum StickyIsEnabledFlags: String, FlagType {
|
||||
case uuidSafetyNumbers
|
||||
case stopMirroringToSVR2Override
|
||||
case exclusiveSVR2
|
||||
}
|
||||
|
||||
// Values defined in this array will update while the app is running,
|
||||
@ -542,8 +519,6 @@ private struct Flags {
|
||||
// wait for an app restart.
|
||||
enum HotSwappableIsEnabledFlags: String, FlagType {
|
||||
case barrierFsyncKillSwitch
|
||||
case stopMirroringToSVR2Override
|
||||
case exclusiveSVR2
|
||||
}
|
||||
|
||||
// We filter the received config down to just the supported flags.
|
||||
@ -573,8 +548,6 @@ private struct Flags {
|
||||
case paypalMonthlyDonationKillSwitch
|
||||
case enableAutoAPNSRotation
|
||||
case ringrtcNwPathMonitorTrialKillSwitch
|
||||
case stopMirroringToSVR2Override
|
||||
case exclusiveSVR2
|
||||
case cdsDisableCompatibilityMode
|
||||
}
|
||||
|
||||
@ -804,7 +777,6 @@ public class ServiceRemoteConfigManager: RemoteConfigManager {
|
||||
timeGatedFlags: timeGatedFlags,
|
||||
account: .implicit()
|
||||
)
|
||||
(DependenciesBridge.shared.svr as? OrchestratingSVRImpl)?.setRemoteConfiguration(remoteConfig.svrConfiguration)
|
||||
}
|
||||
warmSecondaryCaches(isEnabledFlags: isEnabledFlags, valueFlags: valueFlags, isUsingBarrierFsync: isUsingBarrierFsync)
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@ import SignalCoreKit
|
||||
@testable import SignalServiceKit
|
||||
import XCTest
|
||||
|
||||
struct FakeError: Error {}
|
||||
|
||||
public class TestSchedulerTest: XCTestCase {
|
||||
|
||||
func test_fulfill() {
|
||||
|
||||
@ -1,62 +0,0 @@
|
||||
//
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@testable import SignalServiceKit
|
||||
|
||||
// MARK: - Namespace
|
||||
|
||||
extension SVR {
|
||||
public enum TestMocks {
|
||||
public typealias OWS2FAManager = _KeyBackupServiceImpl_OWS2FAManagerTestMock
|
||||
public typealias RemoteAttestation = _KeyBackupServiceImpl_RemoteAttestationMock
|
||||
public typealias URLSession = _KeyBackupServiceImpl_OWSURLSessionMock
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OWS2FAManager
|
||||
|
||||
public class _KeyBackupServiceImpl_OWS2FAManagerTestMock: SVR.Shims.OWS2FAManager {
|
||||
public init() {}
|
||||
|
||||
public var pinCode: String!
|
||||
|
||||
public func pinCode(transaction: DBReadTransaction) -> String? {
|
||||
return pinCode
|
||||
}
|
||||
|
||||
public func markDisabled(transaction: DBWriteTransaction) {}
|
||||
}
|
||||
|
||||
// MARK: - RemoteAttestation
|
||||
|
||||
public class _KeyBackupServiceImpl_RemoteAttestationMock: SVR.Shims.RemoteAttestation {
|
||||
public init() {}
|
||||
|
||||
var authMethodInputs = [RemoteAttestation.KeyBackupAuthMethod]()
|
||||
var enclaveInputs = [KeyBackupEnclave]()
|
||||
|
||||
var promisesToReturn = [Promise<RemoteAttestation>]()
|
||||
|
||||
public func performForKeyBackup(
|
||||
authMethod: RemoteAttestation.KeyBackupAuthMethod,
|
||||
enclave: KeyBackupEnclave
|
||||
) -> Promise<RemoteAttestation> {
|
||||
authMethodInputs.append(authMethod)
|
||||
enclaveInputs.append(enclave)
|
||||
return promisesToReturn.remove(at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OWSURLSession
|
||||
|
||||
public class _KeyBackupServiceImpl_OWSURLSessionMock: BaseOWSURLSessionMock {
|
||||
|
||||
public var promiseForTSRequestBlock: ((TSRequest) -> Promise<HTTPResponse>)?
|
||||
|
||||
public override func promiseForTSRequest(_ rawRequest: TSRequest) -> Promise<HTTPResponse> {
|
||||
return promiseForTSRequestBlock!(rawRequest)
|
||||
}
|
||||
}
|
||||
@ -1,333 +0,0 @@
|
||||
//
|
||||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
@testable import SignalServiceKit
|
||||
|
||||
class KeyBackupServiceTests: XCTestCase {
|
||||
|
||||
private var db: MockDB!
|
||||
private var keyBackupService: KeyBackupServiceImpl!
|
||||
|
||||
private var credentialStorage: SVRAuthCredentialStorageMock!
|
||||
private var remoteAttestation: SVR.TestMocks.RemoteAttestation!
|
||||
private var mockSignalService: OWSSignalServiceMock!
|
||||
private var tsConstants: TSConstantsProtocol!
|
||||
private var scheduler: TestScheduler!
|
||||
|
||||
override func setUp() {
|
||||
self.db = MockDB()
|
||||
self.credentialStorage = SVRAuthCredentialStorageMock()
|
||||
self.remoteAttestation = SVR.TestMocks.RemoteAttestation()
|
||||
self.mockSignalService = OWSSignalServiceMock()
|
||||
self.tsConstants = TSConstants.shared
|
||||
self.scheduler = TestScheduler()
|
||||
|
||||
let kvStore = InMemoryKeyValueStoreFactory()
|
||||
let localStorage = SVRLocalStorageImpl(keyValueStoreFactory: kvStore)
|
||||
|
||||
// Start the scheduler so everything executes synchronously.
|
||||
self.scheduler.start()
|
||||
self.keyBackupService = KeyBackupServiceImpl(
|
||||
appContext: TestAppContext(),
|
||||
credentialStorage: credentialStorage,
|
||||
databaseStorage: db,
|
||||
keyValueStoreFactory: kvStore,
|
||||
remoteAttestation: remoteAttestation,
|
||||
schedulers: TestSchedulers(scheduler: scheduler),
|
||||
signalService: mockSignalService,
|
||||
storageServiceManager: FakeStorageServiceManager(),
|
||||
svrLocalStorage: localStorage,
|
||||
syncManager: OWSMockSyncManager(),
|
||||
tsAccountManager: MockTSAccountManager(),
|
||||
tsConstants: tsConstants,
|
||||
twoFAManager: SVR.TestMocks.OWS2FAManager()
|
||||
)
|
||||
}
|
||||
|
||||
lazy var decoder: JSONDecoder = {
|
||||
let decoder = JSONDecoder()
|
||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
decoder.dataDecodingStrategy = .custom { decoder in
|
||||
let container = try decoder.singleValueContainer()
|
||||
let string = try container.decode(String.self)
|
||||
guard let data = Data.data(fromHex: string) else { throw OWSAssertionError("Invalid data") }
|
||||
return data
|
||||
}
|
||||
return decoder
|
||||
}()
|
||||
|
||||
func test_vectors() throws {
|
||||
struct Vector: Codable {
|
||||
let pin: String
|
||||
let backupId: Data
|
||||
let argon2Hash: Data
|
||||
let masterKey: Data
|
||||
let kbsAccessKey: Data
|
||||
let ivAndCipher: Data
|
||||
let registrationLock: String
|
||||
}
|
||||
|
||||
let vectorsUrl = Bundle(for: type(of: self)).url(forResource: "kbs_vectors", withExtension: "json")!
|
||||
let jsonData = try Data(contentsOf: vectorsUrl)
|
||||
let vectors = try decoder.decode([Vector].self, from: jsonData)
|
||||
|
||||
for vector in vectors {
|
||||
let (encryptionKey, accessKey) = try SVRUtil.deriveSVR1EncryptionKeyAndAccessKey(pin: vector.pin, backupId: vector.backupId)
|
||||
|
||||
XCTAssertEqual(vector.argon2Hash, encryptionKey + accessKey)
|
||||
XCTAssertEqual(vector.kbsAccessKey, accessKey)
|
||||
|
||||
let ivAndCipher = try keyBackupService.encryptMasterKey(vector.masterKey, encryptionKey: encryptionKey)
|
||||
|
||||
XCTAssertEqual(vector.ivAndCipher, ivAndCipher)
|
||||
|
||||
let decryptedMasterKey = try keyBackupService.decryptMasterKey(ivAndCipher, encryptionKey: encryptionKey)
|
||||
|
||||
XCTAssertEqual(vector.masterKey, decryptedMasterKey)
|
||||
|
||||
db.write { transaction in
|
||||
keyBackupService.store(
|
||||
masterKey: vector.masterKey,
|
||||
isMasterKeyBackedUp: true,
|
||||
pinType: .init(forPin: vector.pin),
|
||||
encodedVerificationString: "",
|
||||
enclaveName: "",
|
||||
authedAccount: .implicit(),
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
|
||||
let registrationLockToken = db.read {
|
||||
keyBackupService.data(for: .registrationLock, transaction: $0)?.canonicalStringRepresentation
|
||||
}
|
||||
|
||||
XCTAssertEqual(vector.registrationLock, registrationLockToken)
|
||||
}
|
||||
}
|
||||
|
||||
func test_pinNormalization() throws {
|
||||
struct Vector: Codable {
|
||||
let name: String
|
||||
let pin: String
|
||||
let bytes: Data
|
||||
}
|
||||
|
||||
let vectorsUrl = Bundle(for: type(of: self)).url(forResource: "kbs_pin_sanitation_vectors", withExtension: "json")!
|
||||
let jsonData = try Data(contentsOf: vectorsUrl)
|
||||
let vectors = try decoder.decode([Vector].self, from: jsonData)
|
||||
|
||||
for vector in vectors {
|
||||
let normalizedPin = SVRUtil.normalizePin(vector.pin)
|
||||
XCTAssertEqual(vector.bytes, normalizedPin.data(using: .utf8)!, vector.name)
|
||||
}
|
||||
}
|
||||
|
||||
func test_storageServiceEncryption() throws {
|
||||
struct Vector: Codable {
|
||||
enum VectorType: String, Codable {
|
||||
case storageServiceManifest
|
||||
case storageServiceRecord
|
||||
}
|
||||
let type: VectorType
|
||||
|
||||
enum VectorMode: String, Codable {
|
||||
case local
|
||||
case synced
|
||||
}
|
||||
let mode: VectorMode
|
||||
|
||||
let masterKeyData: Data?
|
||||
let storageServiceKeyData: Data
|
||||
let derivedKeyData: Data
|
||||
let associatedValueData: Data
|
||||
let rawData: Data
|
||||
let encryptedData: Data
|
||||
|
||||
var derivedKey: SVR.DerivedKey {
|
||||
switch type {
|
||||
case .storageServiceRecord:
|
||||
return .storageServiceRecord(identifier: StorageService.StorageIdentifier(data: associatedValueData, type: .contact))
|
||||
case .storageServiceManifest:
|
||||
return .storageServiceManifest(version: associatedValueData.withUnsafeBytes { $0.pointee })
|
||||
}
|
||||
}
|
||||
|
||||
func storeKey(keyBackupService: KeyBackupServiceImpl, transaction: DBWriteTransaction) {
|
||||
keyBackupService.clearKeys(transaction: transaction)
|
||||
switch mode {
|
||||
case .local:
|
||||
keyBackupService.store(
|
||||
masterKey: masterKeyData!,
|
||||
isMasterKeyBackedUp: true,
|
||||
pinType: .numeric,
|
||||
encodedVerificationString: "",
|
||||
enclaveName: "",
|
||||
authedAccount: .implicit(),
|
||||
transaction: transaction
|
||||
)
|
||||
case .synced:
|
||||
keyBackupService.storeSyncedStorageServiceKey(
|
||||
data: storageServiceKeyData,
|
||||
authedAccount: .implicit(),
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let vectorsUrl = Bundle(for: type(of: self)).url(forResource: "kbs_storage_service_encryption_vectors", withExtension: "json")!
|
||||
let jsonData = try Data(contentsOf: vectorsUrl)
|
||||
let vectors = try decoder.decode([Vector].self, from: jsonData)
|
||||
|
||||
for vector in vectors {
|
||||
db.write { vector.storeKey(keyBackupService: keyBackupService, transaction: $0) }
|
||||
|
||||
let hasMasterKey = db.read {
|
||||
keyBackupService.hasMasterKey(transaction: $0)
|
||||
}
|
||||
XCTAssertEqual(hasMasterKey, vector.masterKeyData != nil)
|
||||
|
||||
db.read { tx in
|
||||
XCTAssertEqual(vector.derivedKeyData, keyBackupService.data(for: vector.derivedKey, transaction: tx)?.rawData)
|
||||
XCTAssertEqual(vector.storageServiceKeyData, keyBackupService.data(for: .storageService, transaction: tx)?.rawData)
|
||||
}
|
||||
|
||||
let encryptedData: Data
|
||||
switch db.read(block: { tx in
|
||||
keyBackupService.encrypt(keyType: vector.derivedKey, data: vector.rawData, transaction: tx)
|
||||
}) {
|
||||
case .success(let data):
|
||||
encryptedData = data
|
||||
case .masterKeyMissing:
|
||||
XCTFail("Missing master key")
|
||||
return
|
||||
case .cryptographyError(let error):
|
||||
XCTFail("Failed to encrypt: \(error)")
|
||||
return
|
||||
}
|
||||
switch db.read(block: { tx in
|
||||
keyBackupService.decrypt(keyType: vector.derivedKey, encryptedData: encryptedData, transaction: tx)
|
||||
}) {
|
||||
case .success(let decryptedData):
|
||||
XCTAssertEqual(vector.rawData, decryptedData)
|
||||
case .masterKeyMissing:
|
||||
XCTFail("Missing master key")
|
||||
case .cryptographyError(let error):
|
||||
XCTFail("Failed to decrypt: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func test_kbsCredentialStorage() throws {
|
||||
let firstCredential = KBSAuthCredential(credential: .init(username: "abc", password: "123"))
|
||||
remoteAttestation.promisesToReturn.append(.value(fakeRemoteAttestation(firstCredential)))
|
||||
|
||||
// Try once without auth, and with a success response.
|
||||
var promise: Promise<RemoteAttestation> = keyBackupService.performRemoteAttestation(auth: .implicit, enclave: tsConstants.keyBackupEnclave)
|
||||
promise.observe(on: scheduler) {
|
||||
switch $0 {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
XCTFail("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// Input should be empty.
|
||||
XCTAssertEqual(remoteAttestation.authMethodInputs, [.chatServerImplicitCredentials])
|
||||
|
||||
// Check that auth has been stored.
|
||||
XCTAssertEqual(credentialStorage.kbsDict[firstCredential.credential.username], firstCredential)
|
||||
XCTAssertEqual(credentialStorage.currentKBSUsername, firstCredential.credential.username)
|
||||
|
||||
// Reset for a second round, which should reuse the existing auth credential.
|
||||
// Note that as of time of writing, the real RemoteAttestation just hands back whatever
|
||||
// auth you gave it in the response, but we don't need to assume that and should be able to
|
||||
// handle situations where it gets a fresh auth credential for whatever reason, in which
|
||||
// case we should overwrite the credential we have. This tests for that.
|
||||
let secondCredential = KBSAuthCredential(credential: .init(username: "abc", password: "456"))
|
||||
remoteAttestation.authMethodInputs = []
|
||||
remoteAttestation.promisesToReturn.append(.value(fakeRemoteAttestation(secondCredential)))
|
||||
|
||||
promise = keyBackupService.performRemoteAttestation(auth: .implicit, enclave: tsConstants.keyBackupEnclave)
|
||||
promise.observe(on: scheduler) {
|
||||
switch $0 {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
XCTFail("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// Should have used existing auth.
|
||||
XCTAssertEqual(remoteAttestation.authMethodInputs, [.kbsAuth(firstCredential.credential)])
|
||||
|
||||
// The new credential should've been stored.
|
||||
XCTAssertEqual(credentialStorage.kbsDict[secondCredential.credential.username], secondCredential)
|
||||
XCTAssertEqual(credentialStorage.currentKBSUsername, secondCredential.credential.username)
|
||||
|
||||
// Reset for a third round, which should reuse the existing auth credential.
|
||||
let thirdCredential = KBSAuthCredential(credential: .init(username: "def", password: "789"))
|
||||
remoteAttestation.authMethodInputs = []
|
||||
// Fail one request then accept the next
|
||||
remoteAttestation.promisesToReturn.append(.init(error: FakeError()))
|
||||
remoteAttestation.promisesToReturn.append(.value(fakeRemoteAttestation(thirdCredential)))
|
||||
|
||||
promise = keyBackupService.performRemoteAttestation(auth: .implicit, enclave: tsConstants.keyBackupEnclave)
|
||||
promise.observe(on: scheduler) {
|
||||
switch $0 {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
XCTFail("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// Should have used existing auth.
|
||||
XCTAssertEqual(remoteAttestation.authMethodInputs, [.kbsAuth(secondCredential.credential), .chatServerImplicitCredentials])
|
||||
|
||||
// The new credential should've been stored.
|
||||
XCTAssertEqual(credentialStorage.kbsDict[thirdCredential.credential.username], thirdCredential)
|
||||
// The previous credential should've been wiped.
|
||||
XCTAssertNil(credentialStorage.kbsDict[secondCredential.credential.username])
|
||||
XCTAssertEqual(credentialStorage.currentKBSUsername, thirdCredential.credential.username)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
func AssertPin(
|
||||
_ pin: String,
|
||||
isValid expectedResult: Bool,
|
||||
_ message: String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) {
|
||||
let expectation = XCTestExpectation(description: "Verify Pin")
|
||||
keyBackupService.verifyPin(pin) { isValid in
|
||||
XCTAssertEqual(isValid, expectedResult, message, file: file, line: line)
|
||||
expectation.fulfill()
|
||||
}
|
||||
wait(for: [expectation], timeout: 5)
|
||||
}
|
||||
|
||||
func fakeRemoteAttestation(_ credential: KBSAuthCredential) -> RemoteAttestation {
|
||||
return RemoteAttestation(
|
||||
cookies: [],
|
||||
keys: try! .init(
|
||||
clientEphemeralKeyPair: Curve25519.generateKeyPair(),
|
||||
serverEphemeralPublic: try! Curve25519.generateKeyPair().ecPublicKey().keyData,
|
||||
serverStaticPublic: try! Curve25519.generateKeyPair().ecPublicKey().keyData
|
||||
),
|
||||
requestId: Data(repeating: 1, count: 10),
|
||||
enclaveName: tsConstants.keyBackupEnclave.name,
|
||||
auth: .init(username: credential.credential.username, password: credential.credential.password)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct FakeError: Error {}
|
||||
@ -1,77 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Empty",
|
||||
"pin": "",
|
||||
"bytes": ""
|
||||
},
|
||||
{
|
||||
"name": "Normal",
|
||||
"pin": "password",
|
||||
"bytes": "70617373776f7264"
|
||||
},
|
||||
{
|
||||
"name": "Trailing space",
|
||||
"pin": "password ",
|
||||
"bytes": "70617373776f7264"
|
||||
},
|
||||
{
|
||||
"name": "Leading and trailing spaces",
|
||||
"pin": " password ",
|
||||
"bytes": "70617373776f7264"
|
||||
},
|
||||
{
|
||||
"name": "Space in word",
|
||||
"pin": "pass word",
|
||||
"bytes": "7061737320776f7264"
|
||||
},
|
||||
{
|
||||
"name": "Leading and trailing spaces and space in word",
|
||||
"pin": " pass word ",
|
||||
"bytes": "7061737320776f7264"
|
||||
},
|
||||
{
|
||||
"name": "Arabic digits",
|
||||
"pin": "12345",
|
||||
"bytes": "3132333435"
|
||||
},
|
||||
{
|
||||
"name": "Leading and trailing spaces around digits",
|
||||
"pin": " 12345 ",
|
||||
"bytes": "3132333435"
|
||||
},
|
||||
{
|
||||
"name": "Non-arabic digits",
|
||||
"pin": "١٢٣٤٥",
|
||||
"bytes": "3132333435"
|
||||
},
|
||||
{
|
||||
"name": "Mixed digits",
|
||||
"pin": "١٢٣4٥",
|
||||
"bytes": "3132333435"
|
||||
},
|
||||
{
|
||||
"name": "Non-arabic digits with non-digit",
|
||||
"pin": "١٢٣٤٥A",
|
||||
"bytes": "d9a1d9a2d9a3d9a4d9a541"
|
||||
},
|
||||
{
|
||||
"name": "NFKD Test, Double Char",
|
||||
"pin": "Ä",
|
||||
"bytes": "41cc88"
|
||||
},
|
||||
{
|
||||
"name": "NFKD Test, Single Char",
|
||||
"pin": "Ä",
|
||||
"bytes": "41cc88"
|
||||
},
|
||||
{
|
||||
"name": "Leading and trailing spaces around non-arabic digits",
|
||||
"pin": " ١٢٣٤٥ ",
|
||||
"bytes": "3132333435"
|
||||
},
|
||||
{
|
||||
"name": "Space in non-arabic digits",
|
||||
"pin": "١٢٣ ٤٥٦",
|
||||
"bytes": "d9a1d9a2d9a320d9a4d9a5d9a6"
|
||||
}
|
||||
]
|
||||
@ -1,42 +0,0 @@
|
||||
[
|
||||
{
|
||||
"type":"storageServiceManifest",
|
||||
"mode":"local",
|
||||
"master_key_data":"f759cd3924a5f0c5dede73d57936fb473b04baad331bd7682075d4571e68ffcb",
|
||||
"storage_service_key_data":"4c64a39de2c29b7eabf68ef973244fbab548c515456bf520073564f471195dcd",
|
||||
"derived_key_data":"afd5317afeee3b8bafd10169c3aa6b29d2c832576bd4aee504ebb243f505e946",
|
||||
"associated_value_data":"01",
|
||||
"raw_data":"b6f16aa0591732e339b7e99cdd5fd6586a1c285c9d66876947fd82f66ed99757301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b",
|
||||
"encrypted_data":"d9c5a6d39c9e466796ccce14df7be4adb25001cdc137eee8c1ee984583d282a63488ed7eaaf4aceb61dc9b0299eaa61177b960b290a8fe3e4c04555377797373ced8ee91b3f77eb9e2ec006a79ff44f0a08ff700058d89277ced08c2"
|
||||
},
|
||||
{
|
||||
"type":"storageServiceManifest",
|
||||
"mode":"synced",
|
||||
"master_key_data":null,
|
||||
"storage_service_key_data":"f759cd3924a5f0c5dede73d57936fb473b04baad331bd7682075d4571e68ffcb",
|
||||
"derived_key_data":"88c14532cc5166b63e5bf2ef70489d077b122810d1f5280885186e37d281cd73",
|
||||
"associated_value_data":"34",
|
||||
"raw_data":"b6f16aa0591732e339b7e99cdd5fd6586a1c285c9d66876947fd82f66ed99757301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b",
|
||||
"encrypted_data":"62a256c858f2afc0dd56be06ac6683cb91ca64c6d328bebb046dcda6287ede20204d17b7c93081471e8ac4bffb5c7e07c64f167143028aa5cafb3759408f71d4e39785f8c9d9c64588a940cad4b19e187c059081c2a69262a4623667"
|
||||
},
|
||||
{
|
||||
"type":"storageServiceRecord",
|
||||
"mode":"local",
|
||||
"master_key_data":"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"storage_service_key_data":"47d3b98d12d2f826f27f2d8c63ba0e0cad6019675659623a6c3dac297d1b0889",
|
||||
"derived_key_data":"3b4797f8c2c99496842909a227d0ad0cd31a2314d7130896612cd46e62092489",
|
||||
"associated_value_data":"ee34ff53e87d35a632431fc7efd9d4e6",
|
||||
"raw_data":"b6f16aa0591732e339b7e99cdd5fd6586a1c285c9d66876947fd82f66ed99757301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b",
|
||||
"encrypted_data":"1df93d68ed70016bb1607a9a68e1ab2905b4e59f11427b14fefedbcf67a54517ef8de088b7611cc2e17c7a599fe08e844c7939331e0b573f4f75177955771bc40955d80426c338eced46933da4b104ff9b69e0b8379f51bee7f6375c"
|
||||
},
|
||||
{
|
||||
"type":"storageServiceRecord",
|
||||
"mode":"synced",
|
||||
"master_key_data":null,
|
||||
"storage_service_key_data":"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"derived_key_data":"c4cfd3a64a1f5baa1837e2301402026cd1c96ab03b5910ac729c5159aa965264",
|
||||
"associated_value_data":"b0615811719db052705dc40f5b702343",
|
||||
"raw_data":"b6f16aa0591732e339b7e99cdd5fd6586a1c285c9d66876947fd82f66ed99757301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b",
|
||||
"encrypted_data":"cb8a2bbca377ba349b1de4c25e9fbfffb1cb6f5d64b129584fba57083e1eba43318890aa8c12b153229a39b0e1091f9adbc50c81bd255934777f54a80f4213f15c2e0a33c586bc5af4861f2fe677c1fef85ec44a053e1599feb8f5aa"
|
||||
},
|
||||
]
|
||||
@ -1,38 +0,0 @@
|
||||
[
|
||||
{
|
||||
"pin":"password",
|
||||
"backup_id":"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
|
||||
"argon2_hash":"44652df80490fc66bb864a9e638b2f7dc9e20649671dd66bbb9c37bee2bfecf1ab7e8499d21f80a6600b3b9ee349ac6d72c07e3359fe885a934ba7aa844429f8",
|
||||
"master_key":"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"kbs_access_key":"ab7e8499d21f80a6600b3b9ee349ac6d72c07e3359fe885a934ba7aa844429f8",
|
||||
"iv_and_cipher":"3f33ce58eb25b40436592a30eae2a8fabab1899095f4e2fba6e2d0dc43b4a2d9cac5a3931748522393951e0e54dec769",
|
||||
"registration_lock":"2bf7988224ba35d3554966c65e8dc8c54974b034bdd44cabfd3f15fdb185e3c6"
|
||||
},
|
||||
{
|
||||
"pin":"anotherpassword",
|
||||
"backup_id":"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"argon2_hash":"b6f16aa0591732e339b7e99cdd5fd6586a1c285c9d66876947fd82f66ed99757301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b",
|
||||
"master_key":"88a787415a2ecd79da0d1016a82a27c5c695c9a19b88b0aa1d35683280aa9a67",
|
||||
"kbs_access_key":"301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b",
|
||||
"iv_and_cipher":"9d9b05402ea39c17ff1c9298c8a0e86784a352aa02a74943bf8bcf07ec0f4b574a5b786ad0182c8d308d9eb06538b8c9",
|
||||
"registration_lock":"4a458afa1b07493b23ee9b3f287b70416b2388ca39b5b8c27b4b7585bf73f413"
|
||||
},
|
||||
{
|
||||
"pin": " Pass६örd ",
|
||||
"backup_id": "cba811749042b303a6a7efa5ccd160aea5e3ea243c8d2692bd13d515732f51a8",
|
||||
"argon2_hash": "54853336a666eb66fbe8eb7a224b3ad3f457991ed74895b22ea9c6a4a46eda40ab645acdccc1652a48a34b2ac6926340ff35c03034013f68760f20013f028dd8",
|
||||
"master_key": "9571f3fde1e58588ba49bcf82be1b301ca3859a6f59076f79a8f47181ef952bf",
|
||||
"kbs_access_key": "ab645acdccc1652a48a34b2ac6926340ff35c03034013f68760f20013f028dd8",
|
||||
"iv_and_cipher": "11c0ba1834db15e47c172f6c987c64bd4cfc69c6047dd67a022afeec0165a10943f204d5b8f37b3cb7bab21c6dfc39c8",
|
||||
"registration_lock": "577939bccb2b6638c39222d5a97998a867c5e154e30b82cc120f2dd07a3de987"
|
||||
},
|
||||
{
|
||||
"pin": " ६१८ ",
|
||||
"backup_id": "717dc111a98423a57196512606822fca646c653facd037c10728f14ba0be2ab3",
|
||||
"argon2_hash": "8e9082faa57488f9ef98a6423c416a9974fb4d7c6bfc5b4f24de8e93afea199cd2fedabd0d4c17a371491c9722578843a26be3b4923e28d452ab2fc5491e794b",
|
||||
"master_key": "0432d735b32f66d0e3a70d4f9cc821a8529521a4937d26b987715d8eff4e4c54",
|
||||
"kbs_access_key": "d2fedabd0d4c17a371491c9722578843a26be3b4923e28d452ab2fc5491e794b",
|
||||
"iv_and_cipher": "877ef871ef1fc668401c717ef21aa12e8523579fb1ff4474b76f28c2293537c80cc7569996c9e0229bea7f378e3a824e",
|
||||
"registration_lock": "23a75cb1df1a87df45cc2ed167c2bdc85ab1220b847c88761b0005cac907fce5"
|
||||
}
|
||||
]
|
||||
@ -47,7 +47,7 @@ class SVR2ConcurrencyTests: XCTestCase {
|
||||
syncManager: OWSMockSyncManager(),
|
||||
tsAccountManager: MockTSAccountManager(),
|
||||
tsConstants: TSConstants.shared,
|
||||
twoFAManager: SVR.TestMocks.OWS2FAManager()
|
||||
twoFAManager: SVR2.TestMocks.OWS2FAManager()
|
||||
)
|
||||
}
|
||||
|
||||
@ -345,7 +345,7 @@ class SVR2ConcurrencyTests: XCTestCase {
|
||||
syncManager: OWSMockSyncManager(),
|
||||
tsAccountManager: MockTSAccountManager(),
|
||||
tsConstants: TSConstants.shared,
|
||||
twoFAManager: SVR.TestMocks.OWS2FAManager()
|
||||
twoFAManager: SVR2.TestMocks.OWS2FAManager()
|
||||
)
|
||||
firstBackupPromise = svr.generateAndBackupKeys(pin: "1234", authMethod: .implicit, rotateMasterKey: false)
|
||||
secondBackupPromise = svr.generateAndBackupKeys(pin: "1234", authMethod: .implicit, rotateMasterKey: false)
|
||||
|
||||
@ -17,7 +17,7 @@ class SecureValueRecovery2Tests: XCTestCase {
|
||||
private var scheduler: TestScheduler!
|
||||
|
||||
private var mockAccountAttributesUpdater: MockAccountAttributesUpdater!
|
||||
private var mock2FAManager: SVR.TestMocks.OWS2FAManager!
|
||||
private var mock2FAManager: SVR2.TestMocks.OWS2FAManager!
|
||||
private var keyValueStoreFactory: InMemoryKeyValueStoreFactory!
|
||||
private var localStorage: SVRLocalStorageImpl!
|
||||
private var mockConnectionFactory: MockSgxWebsocketConnectionFactory!
|
||||
@ -32,7 +32,7 @@ class SecureValueRecovery2Tests: XCTestCase {
|
||||
// Start the scheduler so everything executes synchronously.
|
||||
self.scheduler.start()
|
||||
|
||||
mock2FAManager = SVR.TestMocks.OWS2FAManager()
|
||||
mock2FAManager = SVR2.TestMocks.OWS2FAManager()
|
||||
keyValueStoreFactory = InMemoryKeyValueStoreFactory()
|
||||
localStorage = .init(keyValueStoreFactory: keyValueStoreFactory)
|
||||
|
||||
@ -318,3 +318,23 @@ class SecureValueRecovery2Tests: XCTestCase {
|
||||
XCTAssertFalse(SVRUtil.verifyPIN(pin: "notAPassword", againstEncodedPINVerificationString: argon2EncodedString))
|
||||
}
|
||||
}
|
||||
|
||||
extension SVR2 {
|
||||
public enum TestMocks {
|
||||
public typealias OWS2FAManager = _SVR2_OWS2FAManagerTestMock
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OWS2FAManager
|
||||
|
||||
public class _SVR2_OWS2FAManagerTestMock: SVR2.Shims.OWS2FAManager {
|
||||
public init() {}
|
||||
|
||||
public var pinCode: String!
|
||||
|
||||
public func pinCode(transaction: DBReadTransaction) -> String? {
|
||||
return pinCode
|
||||
}
|
||||
|
||||
public func markDisabled(transaction: DBWriteTransaction) {}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user