TM-SGNL-iOS/SignalServiceKit/SecureValueRecovery/MasterKeySyncManager.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

143 lines
4.6 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
public protocol MasterKeySyncManager {
/// Runs startup jobs required to sync the master key to linked devices.
///
/// Historically linked devices did not have the master key; now that
/// we have started including it in the Keys SyncMessage, we have to
/// do some work to make sure that sync message happens proactively.
///
/// On a primary device, this will trigger a one-time Keys SyncMessage if
/// we haven't already sent one.
///
/// On a linked device, this will send a sync keys request if we don't have
/// a master key available locally.
func runStartupJobs(tx: DBWriteTransaction)
}
class MasterKeySyncManagerImpl: MasterKeySyncManager {
private enum StoreConstants {
static let collectionName = "MasterKeyOneTimeSyncManager"
static let hasDistributedMasterKey = "hasSyncedMasterKey"
static let lastKeysSyncRequestMessageDateKey = "lastKeysSyncRequestMessageDateKey"
}
private let logger = PrefixedLogger(prefix: "MKSM")
private let dateProvider: DateProvider
private let keyValueStore: KeyValueStore
private let svr: SecureValueRecovery
private let syncManager: Shims.SyncManager
private let tsAccountManager: TSAccountManager
init(
dateProvider: @escaping DateProvider,
svr: SecureValueRecovery,
syncManager: Shims.SyncManager,
tsAccountManager: TSAccountManager
) {
self.dateProvider = dateProvider
self.keyValueStore = KeyValueStore(collection: StoreConstants.collectionName)
self.svr = svr
self.syncManager = syncManager
self.tsAccountManager = tsAccountManager
}
func runStartupJobs(tx: DBWriteTransaction) {
switch tsAccountManager.registrationState(tx: tx) {
case .registered:
runStartupJobsForPrimaryDevice(tx: tx)
case .provisioned:
runStartupJobsForLinkedDevice(tx: tx)
case .delinked, .deregistered, .unregistered, .transferred,
.transferringIncoming, .transferringLinkedOutgoing,
.transferringPrimaryOutgoing,
.reregistering, .relinking:
logger.info("Skipping; not registered")
return
}
}
private func runStartupJobsForPrimaryDevice(tx: DBWriteTransaction) {
guard !keyValueStore.getBool(
StoreConstants.hasDistributedMasterKey,
defaultValue: false,
transaction: tx
) else {
return
}
logger.info("Sending one-time keys sync message.")
syncManager.sendKeysSyncMessage(tx: tx)
self.keyValueStore.setBool(
true,
key: StoreConstants.hasDistributedMasterKey,
transaction: tx
)
}
private func runStartupJobsForLinkedDevice(tx: DBWriteTransaction) {
if svr.hasMasterKey(transaction: tx) {
// No need to sync; we have the master key.
return
}
let lastRequestDate = keyValueStore.getDate(
StoreConstants.lastKeysSyncRequestMessageDateKey,
transaction: tx
) ?? .distantPast
guard dateProvider().timeIntervalSince(lastRequestDate) >= 60 * 60 * 24 else {
logger.info("Skipping keys sync request; too soon since last request.")
return
}
logger.info("Requesting keys sync message")
syncManager.sendKeysSyncRequestMessage(tx: tx)
keyValueStore.setDate(
dateProvider(),
key: StoreConstants.lastKeysSyncRequestMessageDateKey,
transaction: tx
)
}
}
// MARK: - Dependencies
extension MasterKeySyncManagerImpl {
enum Shims {
public typealias SyncManager = _MasterKeySyncManagerImpl_SyncManager_Shim
}
enum Wrappers {
public typealias SyncManager = _MasterKeySyncManagerImpl_SyncManager_Wrapper
}
}
// MARK: SyncManager
protocol _MasterKeySyncManagerImpl_SyncManager_Shim {
func sendKeysSyncMessage(tx: DBWriteTransaction)
func sendKeysSyncRequestMessage(tx: DBWriteTransaction)
}
class _MasterKeySyncManagerImpl_SyncManager_Wrapper: _MasterKeySyncManagerImpl_SyncManager_Shim {
private let syncManager: SyncManagerProtocolSwift
init(_ syncManager: SyncManagerProtocolSwift) {
self.syncManager = syncManager
}
func sendKeysSyncMessage(tx: DBWriteTransaction) {
syncManager.sendKeysSyncMessage(tx: SDSDB.shimOnlyBridge(tx))
}
func sendKeysSyncRequestMessage(tx: DBWriteTransaction) {
syncManager.sendKeysSyncRequestMessage(transaction: SDSDB.shimOnlyBridge(tx))
}
}