Clean up zombie SVR code

This commit is contained in:
Harry 2025-01-07 15:31:05 -08:00 committed by GitHub
parent 4c3b4e3a83
commit 45389b0c60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 162 additions and 248 deletions

View File

@ -1088,6 +1088,7 @@
66C2B1562A1400E8008DDE72 /* SVR2WebsocketConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C2B1552A1400E8008DDE72 /* SVR2WebsocketConfigurator.swift */; };
66C343D62C140BA7004C3D60 /* DraftQuotedReplyModel+ForSending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C343D52C140BA7004C3D60 /* DraftQuotedReplyModel+ForSending.swift */; };
66C3887E29CA537400E6DC00 /* RegistrationTransferProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C3887D29CA537400E6DC00 /* RegistrationTransferProgressViewController.swift */; };
66C59CED2D28AB060007B874 /* SVRKeyDeriverMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C59CEC2D28AB010007B874 /* SVRKeyDeriverMock.swift */; };
66C7952D2C9B78E900C13937 /* BackupAttachmentUploadStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C7952C2C9B78E200C13937 /* BackupAttachmentUploadStore.swift */; };
66C795302C9B83A200C13937 /* BackupAttachmentUploadStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C7952E2C9B837500C13937 /* BackupAttachmentUploadStoreTests.swift */; };
66CA4CE62B9FC761009A5ED8 /* AttachmentReference+RenderingFlag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66CA4CE52B9FC761009A5ED8 /* AttachmentReference+RenderingFlag.swift */; };
@ -4843,6 +4844,7 @@
66C336D02A994B97000F4F50 /* FailedStorySendDisplayController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedStorySendDisplayController.swift; sourceTree = "<group>"; };
66C343D52C140BA7004C3D60 /* DraftQuotedReplyModel+ForSending.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DraftQuotedReplyModel+ForSending.swift"; sourceTree = "<group>"; };
66C3887D29CA537400E6DC00 /* RegistrationTransferProgressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationTransferProgressViewController.swift; sourceTree = "<group>"; };
66C59CEC2D28AB010007B874 /* SVRKeyDeriverMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVRKeyDeriverMock.swift; sourceTree = "<group>"; };
66C7952C2C9B78E200C13937 /* BackupAttachmentUploadStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupAttachmentUploadStore.swift; sourceTree = "<group>"; };
66C7952E2C9B837500C13937 /* BackupAttachmentUploadStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupAttachmentUploadStoreTests.swift; sourceTree = "<group>"; };
66CA4CE52B9FC761009A5ED8 /* AttachmentReference+RenderingFlag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttachmentReference+RenderingFlag.swift"; sourceTree = "<group>"; };
@ -9275,6 +9277,7 @@
6673FF6F2978C40300F96CFD /* SVRAuthCredentialStorage.swift */,
6673FF712979B33800F96CFD /* SVRAuthCredentialStorageImpl.swift */,
D94AEB392D28837A00B03D7A /* SVRKeyDeriver.swift */,
66C59CEC2D28AB010007B874 /* SVRKeyDeriverMock.swift */,
66C2B1352A0DB02E008DDE72 /* SVRUtil.swift */,
);
path = SecureValueRecovery;
@ -17726,6 +17729,7 @@
6673FF722979B33800F96CFD /* SVRAuthCredentialStorageImpl.swift in Sources */,
C18E3C742AA0F8CE003D1CF1 /* SVRAuthCredentialStorageMock.swift in Sources */,
D94AEB3A2D28837F00B03D7A /* SVRKeyDeriver.swift in Sources */,
66C59CED2D28AB060007B874 /* SVRKeyDeriverMock.swift in Sources */,
6640DD632ACDD5DE00CE9A8C /* SVRLocalStorage.swift in Sources */,
66C2B1362A0DB02E008DDE72 /* SVRUtil.swift in Sources */,
F9C5CE2F289453B400548EEE /* SwiftSingletons.swift in Sources */,

View File

@ -23,6 +23,7 @@ class ProvisioningCoordinatorImpl: ProvisioningCoordinator {
private let signalService: OWSSignalServiceProtocol
private let storageServiceManager: StorageServiceManager
private let svr: SecureValueRecovery
private let svrKeyDeriver: SVRKeyDeriver
private let syncManager: Shims.SyncManager
private let threadStore: ThreadStore
private let tsAccountManager: TSAccountManager
@ -43,6 +44,7 @@ class ProvisioningCoordinatorImpl: ProvisioningCoordinator {
signalService: OWSSignalServiceProtocol,
storageServiceManager: StorageServiceManager,
svr: SecureValueRecovery,
svrKeyDeriver: SVRKeyDeriver,
syncManager: Shims.SyncManager,
threadStore: ThreadStore,
tsAccountManager: TSAccountManager,
@ -62,6 +64,7 @@ class ProvisioningCoordinatorImpl: ProvisioningCoordinator {
self.signalService = signalService
self.storageServiceManager = storageServiceManager
self.svr = svr
self.svrKeyDeriver = svrKeyDeriver
self.syncManager = syncManager
self.threadStore = threadStore
self.tsAccountManager = tsAccountManager
@ -467,9 +470,9 @@ class ProvisioningCoordinatorImpl: ProvisioningCoordinator {
// Don't bother with this field at all; just put explicit none.
let twoFaMode: AccountAttributes.TwoFactorAuthMode = .none
let registrationRecoveryPassword = svr.data(
let registrationRecoveryPassword = svrKeyDeriver.data(
for: .registrationRecoveryPassword,
transaction: tx
tx: tx
)?.canonicalStringRepresentation
let encryptedDeviceName = encryptedDeviceNameRaw.base64EncodedString()

View File

@ -74,6 +74,7 @@ class ProvisioningController: NSObject {
signalService: SSKEnvironment.shared.signalServiceRef,
storageServiceManager: SSKEnvironment.shared.storageServiceManagerRef,
svr: DependenciesBridge.shared.svr,
svrKeyDeriver: DependenciesBridge.shared.svrKeyDeriver,
syncManager: ProvisioningCoordinatorImpl.Wrappers.SyncManager(SSKEnvironment.shared.syncManagerRef),
threadStore: ThreadStoreImpl(),
tsAccountManager: DependenciesBridge.shared.tsAccountManager,

View File

@ -35,6 +35,7 @@ public struct RegistrationCoordinatorDependencies {
public let storageServiceRecordIkmCapabilityStore: StorageServiceRecordIkmCapabilityStore
public let storageServiceManager: StorageServiceManager
public let svr: SecureValueRecovery
public let svrKeyDeriver: SVRKeyDeriver
public let svrAuthCredentialStore: SVRAuthCredentialStorage
public let tsAccountManager: TSAccountManager
public let udManager: RegistrationCoordinatorImpl.Shims.UDManager
@ -73,6 +74,7 @@ public struct RegistrationCoordinatorDependencies {
storageServiceRecordIkmCapabilityStore: DependenciesBridge.shared.storageServiceRecordIkmCapabilityStore,
storageServiceManager: SSKEnvironment.shared.storageServiceManagerRef,
svr: DependenciesBridge.shared.svr,
svrKeyDeriver: DependenciesBridge.shared.svrKeyDeriver,
svrAuthCredentialStore: DependenciesBridge.shared.svrCredentialStorage,
tsAccountManager: DependenciesBridge.shared.tsAccountManager,
udManager: RegistrationCoordinatorImpl.Wrappers.UDManager(SSKEnvironment.shared.udManagerRef),

View File

@ -1599,17 +1599,17 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator {
}
private func loadLocalMasterKeyAndUpdateState(_ tx: DBWriteTransaction) {
let regRecoveryPw = deps.svr.data(
let regRecoveryPw = deps.svrKeyDeriver.data(
for: .registrationRecoveryPassword,
transaction: tx
tx: tx
)?.canonicalStringRepresentation
inMemoryState.regRecoveryPw = regRecoveryPw
if regRecoveryPw != nil {
updatePersistedState(tx) { $0.shouldSkipRegistrationSplash = true }
}
inMemoryState.reglockToken = deps.svr.data(
inMemoryState.reglockToken = deps.svrKeyDeriver.data(
for: .registrationLock,
transaction: tx
tx: tx
)?.canonicalStringRepresentation
// If we have a local master key, theres no need to restore after registration.
// (we will still back up though)

View File

@ -27,6 +27,7 @@ public class ProvisioningCoordinatorTest: XCTestCase {
private var signalServiceMock: OWSSignalServiceMock!
private var storageServiceManagerMock: FakeStorageServiceManager!
private var svrMock: SecureValueRecoveryMock!
private var svrKeyDeriverMock: SVRKeyDeriverMock!
private var syncManagerMock: Mocks.SyncManager!
private var threadStoreMock: MockThreadStore!
private var tsAccountManagerMock: MockTSAccountManager!
@ -54,6 +55,7 @@ public class ProvisioningCoordinatorTest: XCTestCase {
self.signalServiceMock = .init()
self.storageServiceManagerMock = .init()
self.svrMock = .init()
self.svrKeyDeriverMock = .init()
self.syncManagerMock = .init()
self.threadStoreMock = .init()
self.tsAccountManagerMock = .init()
@ -74,6 +76,7 @@ public class ProvisioningCoordinatorTest: XCTestCase {
signalService: signalServiceMock,
storageServiceManager: storageServiceManagerMock,
svr: svrMock,
svrKeyDeriver: svrKeyDeriverMock,
syncManager: syncManagerMock,
threadStore: threadStoreMock,
tsAccountManager: tsAccountManagerMock,

View File

@ -36,6 +36,7 @@ public class RegistrationCoordinatorTest {
private var sessionManager: RegistrationSessionManagerMock!
private var storageServiceManagerMock: FakeStorageServiceManager!
private var svr: SecureValueRecoveryMock!
private var svrKeyDeriver: SVRKeyDeriverMock!
private var svrAuthCredentialStore: SVRAuthCredentialStorageMock!
private var tsAccountManagerMock: MockTSAccountManager!
private var usernameApiClientMock: MockUsernameApiClient!
@ -61,6 +62,7 @@ public class RegistrationCoordinatorTest {
return mock
}()
svr = SecureValueRecoveryMock()
svrKeyDeriver = SVRKeyDeriverMock()
svrAuthCredentialStore = SVRAuthCredentialStorageMock()
mockMessagePipelineSupervisor = RegistrationCoordinatorImpl.TestMocks.MessagePipelineSupervisor()
mockMessageProcessor = RegistrationCoordinatorImpl.TestMocks.MessageProcessor()
@ -115,6 +117,7 @@ public class RegistrationCoordinatorTest {
storageServiceRecordIkmCapabilityStore: StorageServiceRecordIkmCapabilityStoreImpl(),
storageServiceManager: storageServiceManagerMock,
svr: svr,
svrKeyDeriver: svrKeyDeriver,
svrAuthCredentialStore: svrAuthCredentialStore,
tsAccountManager: tsAccountManagerMock,
udManager: RegistrationCoordinatorImpl.TestMocks.UDManager(),
@ -231,7 +234,7 @@ public class RegistrationCoordinatorTest {
ows2FAManagerMock.pinCodeMock = { Stubs.pinCode }
// Make SVR give us back a reg recovery password.
svr.dataGenerator = {
svrKeyDeriver.dataGenerator = {
switch $0 {
case .registrationRecoveryPassword:
return Stubs.regRecoveryPwData
@ -391,7 +394,7 @@ public class RegistrationCoordinatorTest {
ows2FAManagerMock.pinCodeMock = { Stubs.pinCode }
// Make SVR give us back a reg recovery password.
svr.dataGenerator = {
svrKeyDeriver.dataGenerator = {
switch $0 {
case .registrationRecoveryPassword:
return Stubs.regRecoveryPwData
@ -530,7 +533,7 @@ public class RegistrationCoordinatorTest {
ows2FAManagerMock.pinCodeMock = { Stubs.pinCode }
// Make SVR give us back a reg recovery password.
svr.dataGenerator = {
svrKeyDeriver.dataGenerator = {
switch $0 {
case .registrationRecoveryPassword:
@ -676,7 +679,7 @@ public class RegistrationCoordinatorTest {
ows2FAManagerMock.pinCodeMock = { Stubs.pinCode }
// Make SVR give us back a reg recovery password.
svr.dataGenerator = {
svrKeyDeriver.dataGenerator = {
switch $0 {
case .registrationRecoveryPassword:
return Stubs.regRecoveryPwData
@ -836,7 +839,7 @@ public class RegistrationCoordinatorTest {
ows2FAManagerMock.pinCodeMock = { Stubs.pinCode }
// Make SVR give us back a reg recovery password.
svr.dataGenerator = {
svrKeyDeriver.dataGenerator = {
switch $0 {
case .registrationRecoveryPassword:
return Stubs.regRecoveryPwData
@ -1114,7 +1117,7 @@ public class RegistrationCoordinatorTest {
}
// At t=1 it should get the latest credentials from SVR.
self.svr.dataGenerator = {
self.svrKeyDeriver.dataGenerator = {
#expect(self.scheduler.currentTime == 1)
switch $0 {
case .registrationRecoveryPassword:

View File

@ -199,7 +199,7 @@ extension OWSSyncManager: SyncManagerProtocol, SyncManagerProtocolSwift {
return owsFailDebug("Missing thread")
}
let storageServiceKey = DependenciesBridge.shared.svr.data(for: .storageService, transaction: tx.asV2Read)
let storageServiceKey = DependenciesBridge.shared.svrKeyDeriver.data(for: .storageService, tx: tx.asV2Read)
let masterKey = DependenciesBridge.shared.svr.masterKeyDataForKeysSyncMessage(tx: tx.asV2Read)
let mrbk = DependenciesBridge.shared.mrbkStore.getOrGenerateMediaRootBackupKey(tx: tx.asV2Write)
let syncKeysMessage = OWSSyncKeysMessage(
@ -227,12 +227,6 @@ extension OWSSyncManager: SyncManagerProtocol, SyncManagerProtocolSwift {
updateStorageService: true,
transaction: transaction.asV2Write
)
} else {
DependenciesBridge.shared.svr.storeSyncedStorageServiceKey(
data: syncMessage.storageService,
authedAccount: .implicit(),
transaction: transaction.asV2Write
)
}
try? DependenciesBridge.shared.mrbkStore.setMediaRootBackupKey(

View File

@ -138,6 +138,7 @@ public class DependenciesBridge {
public let storageServiceRecordIkmCapabilityStore: StorageServiceRecordIkmCapabilityStore
public let svr: SecureValueRecovery
public let svrCredentialStorage: SVRAuthCredentialStorage
public let svrKeyDeriver: SVRKeyDeriver
public let threadAssociatedDataStore: ThreadAssociatedDataStore
public let threadRemover: ThreadRemover
public let threadReplyInfoStore: ThreadReplyInfoStore
@ -253,6 +254,7 @@ public class DependenciesBridge {
storageServiceRecordIkmCapabilityStore: StorageServiceRecordIkmCapabilityStore,
svr: SecureValueRecovery,
svrCredentialStorage: SVRAuthCredentialStorage,
svrKeyDeriver: SVRKeyDeriver,
threadAssociatedDataStore: ThreadAssociatedDataStore,
threadRemover: ThreadRemover,
threadReplyInfoStore: ThreadReplyInfoStore,
@ -367,6 +369,7 @@ public class DependenciesBridge {
self.storageServiceRecordIkmCapabilityStore = storageServiceRecordIkmCapabilityStore
self.svr = svr
self.svrCredentialStorage = svrCredentialStorage
self.svrKeyDeriver = svrKeyDeriver
self.threadAssociatedDataStore = threadAssociatedDataStore
self.threadRemover = threadRemover
self.threadReplyInfoStore = threadReplyInfoStore

View File

@ -37,7 +37,7 @@ public class AppSetup {
let remoteConfigManager: (any RemoteConfigManager)?
let signalService: (any OWSSignalServiceProtocol)?
let storageServiceManager: (any StorageServiceManager)?
let svr: SecureValueRecovery?
let svrKeyDeriver: SVRKeyDeriver?
let syncManager: (any SyncManagerProtocol)?
let systemStoryManager: (any SystemStoryManagerProtocol)?
let versionedProfiles: (any VersionedProfilesSwift)?
@ -60,7 +60,7 @@ public class AppSetup {
remoteConfigManager: (any RemoteConfigManager)? = nil,
signalService: (any OWSSignalServiceProtocol)? = nil,
storageServiceManager: (any StorageServiceManager)? = nil,
svr: SecureValueRecovery? = nil,
svrKeyDeriver: SVRKeyDeriver? = nil,
syncManager: (any SyncManagerProtocol)? = nil,
systemStoryManager: (any SystemStoryManagerProtocol)? = nil,
versionedProfiles: (any VersionedProfilesSwift)? = nil,
@ -82,7 +82,7 @@ public class AppSetup {
self.remoteConfigManager = remoteConfigManager
self.signalService = signalService
self.storageServiceManager = storageServiceManager
self.svr = svr
self.svrKeyDeriver = svrKeyDeriver
self.syncManager = syncManager
self.systemStoryManager = systemStoryManager
self.versionedProfiles = versionedProfiles
@ -284,7 +284,9 @@ public class AppSetup {
let svrCredentialStorage = SVRAuthCredentialStorageImpl()
let svrLocalStorage = SVRLocalStorageImpl()
let svrKeyDeriver = SVRKeyDeriverImpl(localStorage: svrLocalStorage)
let svrKeyDeriver = testDependencies.svrKeyDeriver ?? SVRKeyDeriverImpl(
localStorage: svrLocalStorage
)
let accountAttributesUpdater = AccountAttributesUpdaterImpl(
accountAttributesGenerator: AccountAttributesGenerator(
@ -314,7 +316,7 @@ public class AppSetup {
tsAccountManager: tsAccountManager
)
let svr = testDependencies.svr ?? SecureValueRecovery2Impl(
let svr = SecureValueRecovery2Impl(
accountAttributesUpdater: accountAttributesUpdater,
appContext: SVR2.Wrappers.AppContext(),
appReadiness: appReadiness,
@ -335,7 +337,7 @@ public class AppSetup {
let mrbkStore = MediaRootBackupKeyStore()
let messageBackupKeyMaterial = MessageBackupKeyMaterialImpl(
mrbkStore: mrbkStore,
svr: svr
svrKeyDeriver: svrKeyDeriver
)
let messageBackupRequestManager = MessageBackupRequestManagerImpl(
dateProvider: dateProvider,
@ -1314,6 +1316,7 @@ public class AppSetup {
storageServiceRecordIkmCapabilityStore: storageServiceRecordIkmCapabilityStore,
svr: svr,
svrCredentialStorage: svrCredentialStorage,
svrKeyDeriver: svrKeyDeriver,
threadAssociatedDataStore: threadAssociatedDataStore,
threadRemover: threadRemover,
threadReplyInfoStore: threadReplyInfoStore,

View File

@ -7,15 +7,16 @@ import Foundation
public import LibSignalClient
public struct MessageBackupKeyMaterialImpl: MessageBackupKeyMaterial {
private let svr: SecureValueRecovery
private let mrbkStore: MediaRootBackupKeyStore
private let svrKeyDeriver: SVRKeyDeriver
public init(
mrbkStore: MediaRootBackupKeyStore,
svr: SecureValueRecovery
svrKeyDeriver: SVRKeyDeriver
) {
self.mrbkStore = mrbkStore
self.svr = svr
self.svrKeyDeriver = svrKeyDeriver
}
/// Get the root backup key used by the encryption mode. The key may be derived
@ -32,7 +33,7 @@ public struct MessageBackupKeyMaterialImpl: MessageBackupKeyMaterial {
}
resultData = backupKey
case .messages:
guard let backupKey = svr.data(for: .backupKey, transaction: tx) else {
guard let backupKey = svrKeyDeriver.data(for: .backupKey, tx: tx) else {
throw MessageBackupKeyMaterialError.missingMasterKey
}
guard backupKey.type == .backupKey else {

View File

@ -37,10 +37,6 @@ public class SecureValueRecoveryMock: SecureValueRecovery {
public var reglockToken: String?
public func acquireRegistrationLockForNewNumber(with pin: String, and auth: SVRAuthCredential) -> Promise<String> {
return .value(reglockToken!)
}
public var generateAndBackupKeysMock: ((_ pin: String, _ authMethod: SVR.AuthMethod) -> Promise<Void>)?
public func generateAndBackupKeys(pin: String, authMethod: SVR.AuthMethod) -> Promise<Void> {
@ -63,22 +59,6 @@ public class SecureValueRecoveryMock: SecureValueRecovery {
return .value(())
}
public func encrypt(
keyType: SVR.DerivedKey,
data: Data,
transaction: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult {
return .success(data)
}
public func decrypt(
keyType: SVR.DerivedKey,
encryptedData: Data,
transaction: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult {
return .success(encryptedData)
}
public func warmCaches() {
// Do nothing
}
@ -87,14 +67,6 @@ public class SecureValueRecoveryMock: SecureValueRecovery {
hasMasterKey = false
}
public func storeSyncedStorageServiceKey(
data: Data?,
authedAccount: AuthedAccount,
transaction: DBWriteTransaction
) {
// Do nothing
}
public var syncedMasterKey: Data?
public func storeSyncedMasterKey(
@ -142,20 +114,6 @@ public class SecureValueRecoveryMock: SecureValueRecovery {
) {
useDeviceLocalMasterKeyMock?(authedAccount)
}
public var dataGenerator: (SVR.DerivedKey) -> Data? = { _ in return nil }
public func data(for key: SVR.DerivedKey) -> Data? {
return dataGenerator(key)
}
public func data(for key: SVR.DerivedKey, transaction: DBReadTransaction) -> SVR.DerivedKeyData? {
return SVR.DerivedKeyData(dataGenerator(key), key)
}
public func isKeyAvailable(_ key: SVR.DerivedKey, transaction: DBReadTransaction) -> Bool {
return true
}
}
#endif

View File

@ -247,31 +247,6 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
// MARK: - Key Management
public func acquireRegistrationLockForNewNumber(with pin: String, and auth: SVRAuthCredential) -> Promise<String> {
Logger.info("")
return doRestore(pin: pin, authMethod: .svrAuth(auth, backup: nil)).then(on: scheduler) { restoreResult -> Promise<String> in
switch restoreResult {
case .success(let masterKey, _):
// Ignore whether we restored from an old enclave; we aren't backing up to the new enclave
// on this code path so its not safe to wipe the old one anyway.
guard let reglockToken = self.keyDeriver.deriveReglockKey(masterKey: masterKey)?.canonicalStringRepresentation else {
return .init(error: SVR.SVRError.assertion)
}
return .value(reglockToken)
case .backupMissing:
return .init(error: SVR.SVRError.backupMissing)
case .invalidPin(let remainingAttempts):
return .init(error: SVR.SVRError.invalidPin(remainingAttempts: remainingAttempts))
case .decryptionError, .serverError, .unretainedError:
return .init(error: SVR.SVRError.assertion)
case .networkError(let error):
return .init(error: error)
case .genericError(let error):
return .init(error: error)
}
}
}
public func generateAndBackupKeys(pin: String, authMethod: SVR.AuthMethod) -> Promise<Void> {
let promise: Promise<Data> = self.generateAndBackupKeys(pin: pin, authMethod: authMethod)
return promise.asVoid(on: schedulers.sync)
@ -383,74 +358,6 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
localStorage.clearKeys(transaction)
}
// MARK: - Master Key Encryption
public func encrypt(
keyType: SVR.DerivedKey,
data: Data,
transaction: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult {
guard let keyData = self.data(for: keyType, transaction: transaction) else {
return .masterKeyMissing
}
do {
return .success(try Aes256GcmEncryptedData.encrypt(data, key: keyData.rawData).concatenate())
} catch let error {
return .cryptographyError(error)
}
}
public func decrypt(
keyType: SVR.DerivedKey,
encryptedData: Data,
transaction: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult {
guard let keyData = self.data(for: keyType, transaction: transaction) else {
return .masterKeyMissing
}
do {
return .success(try Aes256GcmEncryptedData(concatenated: encryptedData).decrypt(key: keyData.rawData))
} catch let error {
return .cryptographyError(error)
}
}
// 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) {
Logger.info("")
guard tsAccountManager.registrationState(tx: transaction).isPrimaryDevice == false else {
owsFailDebug("Should not be storing synced keys on primary!")
return
}
guard let storageServiceKey = data else {
localStorage.setSyncedStorageServiceKey(nil, transaction)
return
}
if
let masterKey = localStorage.getMasterKey(transaction),
keyDeriver.deriveStorageServiceKey(masterKey: masterKey)?.rawData == storageServiceKey
{
// We already have a master key, it already produces this storage service key.
// Nothing needs to change.
return
}
// Otherwise we are either missing a master key or it doesn't match;
// in either case we want to nil out our master key and store the storage
// service key.
localStorage.setSyncedStorageServiceKey(data, transaction)
localStorage.setMasterKey(nil, transaction)
// Trigger a re-fetch of the storage manifest, our keys have changed
storageServiceManager.restoreOrCreateManifestIfNecessary(authedDevice: authedAccount.authedDevice(isPrimaryDevice: false))
}
public func storeSyncedMasterKey(
data: Data,
authedDevice: AuthedDevice,
@ -480,16 +387,6 @@ public class SecureValueRecovery2Impl: SecureValueRecovery {
localStorage.setMasterKey(nil, transaction)
}
// MARK: - Value Derivation
public func isKeyAvailable(_ key: SVR.DerivedKey, transaction: DBReadTransaction) -> Bool {
return data(for: key, transaction: transaction) != nil
}
public func data(for key: SVR.DerivedKey, transaction: any DBReadTransaction) -> SVR.DerivedKeyData? {
return keyDeriver.data(for: key, tx: transaction)
}
// MARK: - Backup/Expose Request
private lazy var kvStore = KeyValueStore(collection: "SecureValueRecovery2Impl")

View File

@ -9,13 +9,22 @@ import LibSignalClient
/// Responsible for deriving key data for keys downstream of the SVR master key.
public protocol SVRKeyDeriver {
func isKeyAvailable(_ key: SVR.DerivedKey, tx: DBReadTransaction) -> Bool
/// Reads and derives key data for the given key type.
func data(for key: SVR.DerivedKey, tx: DBReadTransaction) -> SVR.DerivedKeyData?
/// Derive a Storage Service key from pre-known master key data.
func deriveStorageServiceKey(masterKey: Data) -> SVR.DerivedKeyData?
/// Derive a reglock key/token from pre-known master key data.
func deriveReglockKey(masterKey: Data) -> SVR.DerivedKeyData?
func encrypt(
keyType: SVR.DerivedKey,
data: Data,
tx: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult
func decrypt(
keyType: SVR.DerivedKey,
encryptedData: Data,
tx: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult
}
struct SVRKeyDeriverImpl: SVRKeyDeriver {
@ -27,6 +36,10 @@ struct SVRKeyDeriverImpl: SVRKeyDeriver {
// MARK: -
public func isKeyAvailable(_ key: SVR.DerivedKey, tx: DBReadTransaction) -> Bool {
return data(for: key, tx: tx) != nil
}
func data(for key: SVR.DerivedKey, tx: DBReadTransaction) -> SVR.DerivedKeyData? {
func withMasterKey(_ handler: (Data) -> SVR.DerivedKeyData?) -> SVR.DerivedKeyData? {
guard let masterKey = self.localStorage.getMasterKey(tx) else {
@ -36,29 +49,16 @@ struct SVRKeyDeriverImpl: SVRKeyDeriver {
}
func withStorageServiceKey(_ handler: (SVR.DerivedKeyData) -> SVR.DerivedKeyData?) -> SVR.DerivedKeyData? {
// NEW: linked devices might have the master key now, synced from the primary.
// This was not the case before, and in fact we may still not have the master
// key if it hasn't been synced yet.
// So we should _first_ check if we have the master key, and if we don't
// fall back to the storage service key which has historically been synced.
if let masterKey = self.localStorage.getMasterKey(tx) {
guard let storageServiceKey = deriveStorageServiceKey(masterKey: masterKey) else {
return nil
}
return handler(storageServiceKey)
} else if
// TODO: By 10/2024, we can remove this check. Starting in 10/2023, we started sending
// master keys in syncs. A year later, any primary that has not yet delivered a master
// key must not have launched and is therefore deregistered; we are ok to ignore the
// storage service key and take the master key or bust.
let storageServiceKeyData = self.localStorage.getSyncedStorageServiceKey(tx),
let storageServiceKey = SVR.DerivedKeyData(storageServiceKeyData, .storageService)
{
return handler(storageServiceKey)
} else {
// We have no keys at all.
// Linked devices have the master key, synced from the primary.
// This was not the case historically (2023 and earlier), but since then
// we sync keys in provisioning and via sync message on app launch.
guard
let masterKey = self.localStorage.getMasterKey(tx),
let storageServiceKey = deriveStorageServiceKey(masterKey: masterKey)
else {
return nil
}
return handler(storageServiceKey)
}
switch key {
@ -108,6 +108,38 @@ struct SVRKeyDeriverImpl: SVRKeyDeriver {
let keyType: SVR.DerivedKey = .legacy_storageServiceRecord(identifier: identifier)
return SVR.DerivedKeyData(keyType: keyType, dataToDeriveFrom: storageServiceKey.rawData)
}
// MARK: -
public func encrypt(
keyType: SVR.DerivedKey,
data: Data,
tx: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult {
guard let keyData = self.data(for: keyType, tx: tx) else {
return .masterKeyMissing
}
do {
return .success(try Aes256GcmEncryptedData.encrypt(data, key: keyData.rawData).concatenate())
} catch let error {
return .cryptographyError(error)
}
}
public func decrypt(
keyType: SVR.DerivedKey,
encryptedData: Data,
tx: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult {
guard let keyData = self.data(for: keyType, tx: tx) else {
return .masterKeyMissing
}
do {
return .success(try Aes256GcmEncryptedData(concatenated: encryptedData).decrypt(key: keyData.rawData))
} catch let error {
return .cryptographyError(error)
}
}
}
private extension SVR.DerivedKeyData {

View File

@ -0,0 +1,43 @@
//
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
#if TESTABLE_BUILD
open class SVRKeyDeriverMock: SVRKeyDeriver {
public init() {}
public func encrypt(
keyType: SVR.DerivedKey,
data: Data,
tx: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult {
return .success(data)
}
public func decrypt(
keyType: SVR.DerivedKey,
encryptedData: Data,
tx: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult {
return .success(encryptedData)
}
public var dataGenerator: (SVR.DerivedKey) -> Data? = { _ in return nil }
public func data(for key: SVR.DerivedKey) -> Data? {
return dataGenerator(key)
}
public func data(for key: SVR.DerivedKey, tx: DBReadTransaction) -> SVR.DerivedKeyData? {
return SVR.DerivedKeyData(dataGenerator(key), key)
}
public func isKeyAvailable(_ key: SVR.DerivedKey, tx: DBReadTransaction) -> Bool {
return true
}
}
#endif

View File

@ -145,11 +145,6 @@ public protocol SecureValueRecovery {
/// Callback will happen on the main thread.
func verifyPin(_ pin: String, resultHandler: @escaping (Bool) -> Void)
// When changing number, we need to verify the PIN against the new number's SVR
// record in order to generate a registration lock token. It's important that this
// happens without touching any of the state we maintain around our account.
func acquireRegistrationLockForNewNumber(with pin: String, and auth: SVRAuthCredential) -> Promise<String>
/// Loads the users key, if any, from the SVR into the database.
func restoreKeys(pin: String, authMethod: SVR.AuthMethod) -> Guarantee<SVR.RestoreKeysResult>
@ -164,36 +159,12 @@ public protocol SecureValueRecovery {
/// they will not be able to be restored.
func deleteKeys() -> Promise<Void>
// MARK: - Master Key Encryption
func encrypt(
keyType: SVR.DerivedKey,
data: Data,
transaction: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult
func decrypt(
keyType: SVR.DerivedKey,
encryptedData: Data,
transaction: DBReadTransaction
) -> SVR.ApplyDerivedKeyResult
func warmCaches()
/// Removes the SVR keys locally from the device, they can still be
/// restored from the server if you know the pin.
func clearKeys(transaction: DBWriteTransaction)
// 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.
func storeSyncedStorageServiceKey(
data: Data?,
authedAccount: AuthedAccount,
transaction: DBWriteTransaction
)
func storeSyncedMasterKey(
data: Data,
authedDevice: AuthedDevice,
@ -210,8 +181,4 @@ public protocol SecureValueRecovery {
/// Rotate the master key and _don't_ back it up to the SVR server, in effect switching to a
/// local-only master key and disabling PIN usage for backup restoration.
func useDeviceLocalMasterKey(authedAccount: AuthedAccount, transaction: DBWriteTransaction)
func data(for key: SVR.DerivedKey, transaction: DBReadTransaction) -> SVR.DerivedKeyData?
func isKeyAvailable(_ key: SVR.DerivedKey, transaction: DBReadTransaction) -> Bool
}

View File

@ -678,7 +678,7 @@ class StorageServiceOperation {
Bool,
State?
) = SSKEnvironment.shared.databaseStorageRef.read { tx in
let isKeyAvailable = DependenciesBridge.shared.svr.isKeyAvailable(.storageService, transaction: tx.asV2Read)
let isKeyAvailable = DependenciesBridge.shared.svrKeyDeriver.isKeyAvailable(.storageService, tx: tx.asV2Read)
switch mode {
case .rotateManifest:

View File

@ -318,9 +318,9 @@ extension OWS2FAManager {
public func enableRegistrationLockV2() async throws {
let token = SSKEnvironment.shared.databaseStorageRef.read { tx in
return DependenciesBridge.shared.svr.data(
return DependenciesBridge.shared.svrKeyDeriver.data(
for: .registrationLock,
transaction: tx.asV2Read
tx: tx.asV2Read
)?.canonicalStringRepresentation
}
guard let token else {

View File

@ -211,10 +211,10 @@ public struct StorageService {
}
let decryptResult = SSKEnvironment.shared.databaseStorageRef.read(block: { tx in
return DependenciesBridge.shared.svr.decrypt(
return DependenciesBridge.shared.svrKeyDeriver.decrypt(
keyType: .storageServiceManifest(version: encryptedManifestContainer.version),
encryptedData: encryptedManifestContainer.value,
transaction: tx.asV2Read
tx: tx.asV2Read
)
})
switch decryptResult {
@ -280,10 +280,10 @@ public struct StorageService {
let encryptedManifestData: Data
let encryptResult = SSKEnvironment.shared.databaseStorageRef.read(block: { tx in
return DependenciesBridge.shared.svr.encrypt(
return DependenciesBridge.shared.svrKeyDeriver.encrypt(
keyType: .storageServiceManifest(version: manifest.version),
data: manifestData,
transaction: tx.asV2Read
tx: tx.asV2Read
)
})
switch encryptResult {
@ -322,10 +322,10 @@ public struct StorageService {
/// If we don't have a `recordIkm` yet, fall back to the
/// SVR-derived key.
let itemEncryptionResult = SSKEnvironment.shared.databaseStorageRef.read(block: { tx in
return DependenciesBridge.shared.svr.encrypt(
return DependenciesBridge.shared.svrKeyDeriver.encrypt(
keyType: .legacy_storageServiceRecord(identifier: item.identifier),
data: plaintextRecordData,
transaction: tx.asV2Read
tx: tx.asV2Read
)
})
switch itemEncryptionResult {
@ -388,10 +388,10 @@ public struct StorageService {
}
let decryptionResult = SSKEnvironment.shared.databaseStorageRef.read(block: { tx in
return DependenciesBridge.shared.svr.decrypt(
return DependenciesBridge.shared.svrKeyDeriver.decrypt(
keyType: .storageServiceManifest(version: encryptedManifestContainer.version),
encryptedData: encryptedManifestContainer.value,
transaction: tx.asV2Read
tx: tx.asV2Read
)
})
switch decryptionResult {
@ -501,10 +501,10 @@ public struct StorageService {
/// If we don't yet have a `recordIkm` set we should
/// continue using the SVR-derived record key.
let itemDecryptionResult = SSKEnvironment.shared.databaseStorageRef.read(block: { tx in
return DependenciesBridge.shared.svr.decrypt(
return DependenciesBridge.shared.svrKeyDeriver.decrypt(
keyType: .legacy_storageServiceRecord(identifier: itemIdentifier),
encryptedData: item.value,
transaction: tx.asV2Read
tx: tx.asV2Read
)
})
switch itemDecryptionResult {

View File

@ -451,9 +451,9 @@ class MessageBackupIntegrationTests: XCTestCase {
keychainStorage: MockKeychainStorage()
)
let svr = SecureValueRecoveryMock()
let svrKeyDeriver = SVRKeyDeriverMock()
let backupKey = Data(repeating: 8, count: 32)
svr.dataGenerator = { derivedKey in
svrKeyDeriver.dataGenerator = { derivedKey in
switch derivedKey {
case .backupKey:
return backupKey
@ -480,7 +480,7 @@ class MessageBackupIntegrationTests: XCTestCase {
backupAttachmentDownloadManager: BackupAttachmentDownloadManagerMock(),
dateProvider: dateProvider,
networkManager: CrashyMocks.MockNetworkManager(libsignalNet: nil),
svr: svr,
svrKeyDeriver: svrKeyDeriver,
webSocketFactory: CrashyMocks.MockWebSocketFactory()
)
).prepareDatabase().awaitable()