Stop caching SVRB auth credentials

This commit is contained in:
Sasha Weiss 2026-05-29 11:18:26 -07:00 committed by GitHub
parent 8f0c315ad7
commit 65f577efed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 39 additions and 165 deletions

View File

@ -107,39 +107,22 @@ extension BackupImportSource {
logger: PrefixedLogger,
) async throws -> MessageBackupKey {
switch self {
case let .remote(key, noneSource):
case let .remote(key, nonceSource):
let forwardSecrecyToken: BackupForwardSecrecyToken?
switch noneSource {
switch nonceSource {
case let .provisioningMessage(token):
forwardSecrecyToken = token
case let .svrB(metadataHeader, chatAuth):
do {
forwardSecrecyToken = try await self.fetchForwardSecrecyTokenFromSvr(
key: key,
metadataHeader: metadataHeader,
chatAuth: chatAuth,
forceRefreshSVRBAuthCredential: false,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,
nonceStore: nonceStore,
logger: logger,
)
} catch SignalError.webSocketError {
// This may represent an "expired auth" error from the SVRB
// servers. Try again, force-refreshing credentials.
forwardSecrecyToken = try await self.fetchForwardSecrecyTokenFromSvr(
key: key,
metadataHeader: metadataHeader,
chatAuth: chatAuth,
forceRefreshSVRBAuthCredential: true,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,
nonceStore: nonceStore,
logger: logger,
)
}
forwardSecrecyToken = try await self.fetchForwardSecrecyTokenFromSvr(
key: key,
metadataHeader: metadataHeader,
chatAuth: chatAuth,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,
nonceStore: nonceStore,
logger: logger,
)
}
return try MessageBackupKey(
@ -161,29 +144,17 @@ extension BackupImportSource {
key: MessageRootBackupKey,
metadataHeader: BackupNonce.MetadataHeader,
chatAuth: ChatServiceAuth,
forceRefreshSVRBAuthCredential: Bool,
backupRequestManager: BackupRequestManager,
db: any DB,
libsignalNet: LibSignalClient.Net,
nonceStore: BackupNonceMetadataStore,
logger: PrefixedLogger,
) async throws -> BackupForwardSecrecyToken {
let svrBAuth: LibSignalClient.Auth
do {
svrBAuth = try await backupRequestManager.fetchSVRBAuthCredential(
key: key,
chatServiceAuth: chatAuth,
forceRefresh: forceRefreshSVRBAuthCredential,
logger: logger,
)
} catch let error as CancellationError {
throw error
} catch let error where error.isNetworkFailureOrTimeout {
throw error
} catch let error {
owsFailDebug("Permanently failed to fetch svrB auth! \(error)")
throw SVRBError.unrecoverable
}
let svrBAuth = try await backupRequestManager.fetchSVRBAuthCredential(
key: key,
chatServiceAuth: chatAuth,
logger: logger,
)
let svrB = libsignalNet.svrB(auth: svrBAuth)
@ -212,7 +183,6 @@ extension BackupImportSource {
key: key,
metadataHeader: metadataHeader,
chatAuth: chatAuth,
forceRefreshSVRBAuthCredential: false,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,
@ -273,29 +243,14 @@ extension BackupExportPurpose {
) async throws -> EncryptionMetadata {
switch self {
case let .remoteExport(key, chatAuth):
do {
return try await storeEncryptionMetadataToSVRB(
key: key,
chatAuth: chatAuth,
forceRefreshSVRBAuthCredential: false,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,
nonceStore: nonceStore,
)
} catch SignalError.webSocketError {
// This may represent an "expired auth" error from the SVRB
// servers. Try again, force-refreshing credentials.
return try await storeEncryptionMetadataToSVRB(
key: key,
chatAuth: chatAuth,
forceRefreshSVRBAuthCredential: true,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,
nonceStore: nonceStore,
)
}
return try await storeEncryptionMetadataToSVRB(
key: key,
chatAuth: chatAuth,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,
nonceStore: nonceStore,
)
case let .linkNsync(ephemeralKey, aci):
let backupId = ephemeralKey.deriveBackupId(aci: aci)
let encryptionKey = try MessageBackupKey(
@ -315,28 +270,16 @@ extension BackupExportPurpose {
private func storeEncryptionMetadataToSVRB(
key: MessageRootBackupKey,
chatAuth: ChatServiceAuth,
forceRefreshSVRBAuthCredential: Bool,
backupRequestManager: BackupRequestManager,
db: any DB,
libsignalNet: LibSignalClient.Net,
nonceStore: BackupNonceMetadataStore,
) async throws -> EncryptionMetadata {
let svrBAuth: LibSignalClient.Auth
do {
svrBAuth = try await backupRequestManager.fetchSVRBAuthCredential(
key: key,
chatServiceAuth: chatAuth,
forceRefresh: forceRefreshSVRBAuthCredential,
logger: logger,
)
} catch let error as CancellationError {
throw error
} catch let error where error.isNetworkFailureOrTimeout {
throw error
} catch let error {
owsFailDebug("Permanently failed to fetch svrB auth. \(error)")
throw SVRBError.unrecoverable
}
let svrBAuth = try await backupRequestManager.fetchSVRBAuthCredential(
key: key,
chatServiceAuth: chatAuth,
logger: logger,
)
let svrB = libsignalNet.svrB(auth: svrBAuth)
@ -374,7 +317,6 @@ extension BackupExportPurpose {
return try await storeEncryptionMetadataToSVRB(
key: key,
chatAuth: chatAuth,
forceRefreshSVRBAuthCredential: false,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,
@ -387,7 +329,6 @@ extension BackupExportPurpose {
return try await storeEncryptionMetadataToSVRB(
key: key,
chatAuth: chatAuth,
forceRefreshSVRBAuthCredential: false,
backupRequestManager: backupRequestManager,
db: db,
libsignalNet: libsignalNet,

View File

@ -154,7 +154,6 @@ public protocol BackupRequestManager {
func fetchSVRBAuthCredential(
key: MessageRootBackupKey,
chatServiceAuth auth: ChatServiceAuth,
forceRefresh: Bool,
logger: PrefixedLogger,
) async throws -> LibSignalClient.Auth
}
@ -485,13 +484,11 @@ public struct BackupRequestManagerImpl: BackupRequestManager {
public func fetchSVRBAuthCredential(
key: MessageRootBackupKey,
chatServiceAuth auth: ChatServiceAuth,
forceRefresh: Bool,
logger: PrefixedLogger,
) async throws -> LibSignalClient.Auth {
return try await backupAuthCredentialManager.fetchSVRBAuthCredential(
key: key,
chatServiceAuth: auth,
forceRefresh: forceRefresh,
logger: logger,
)
}
@ -679,7 +676,6 @@ public class BackupRequestManagerMock: BackupRequestManager {
public func fetchSVRBAuthCredential(
key: SignalServiceKit.MessageRootBackupKey,
chatServiceAuth auth: SignalServiceKit.ChatServiceAuth,
forceRefresh: Bool,
logger: PrefixedLogger,
) async throws -> LibSignalClient.Auth {
return LibSignalClient.Auth(username: "", password: "")

View File

@ -329,6 +329,7 @@ public class GRDBSchemaMigrator {
case dropAttachmentContentTypeAUTrigger
case addOrphanedAttachmentTimestamp
case migrateSecureValueRecovery
case wipeCachedSVRBAuthCredentials
// NOTE: Every time we add a migration id, consider
// incrementing grdbSchemaVersionLatest.
@ -5169,6 +5170,14 @@ public class GRDBSchemaMigrator {
return .success(())
}
migrator.registerMigration(.wipeCachedSVRBAuthCredentials) { tx in
try tx.database.execute(sql: """
DELETE FROM keyvalue
WHERE collection = 'SVR🐝AuthCredential'
""")
return .success(())
}
// MARK: - Schema Migration Insertion Point
}

View File

@ -11,14 +11,12 @@ public class AuthCredentialStore {
private let groupAuthCredentialStore: KeyValueStore
private let backupMessagesAuthCredentialStore: KeyValueStore
private let backupMediaAuthCredentialStore: KeyValueStore
private let svrBAuthCredentialStore: KeyValueStore
public init() {
self.callLinkAuthCredentialStore = KeyValueStore(collection: "CallLinkAuthCredential")
self.groupAuthCredentialStore = KeyValueStore(collection: "GroupsV2Impl.authCredentialStoreStore")
self.backupMessagesAuthCredentialStore = KeyValueStore(collection: "BackupAuthCredential")
self.backupMediaAuthCredentialStore = KeyValueStore(collection: "MediaAuthCredential")
self.svrBAuthCredentialStore = KeyValueStore(collection: "SVR🐝AuthCredential")
}
private static func callLinkAuthCredentialKey(for redemptionTime: UInt64) -> String {
@ -136,49 +134,6 @@ public class AuthCredentialStore {
)
}
static let svrBAuthCredentialExpirationTime: TimeInterval = .day - .hour
static let svrBAuthCredentialExpiryDateKey = "svr🐝AuthCredentialTimestampKey"
static let svrBAuthCredentialUsernameKey = "svr🐝AuthCredentialUsernameKey"
static let svrBAuthCredentialPasswordKey = "svr🐝AuthCredentialPasswordKey"
func svrBAuthCredential(
now: Date,
tx: DBReadTransaction,
) -> LibSignalClient.Auth? {
guard
let username = svrBAuthCredentialStore.getString(Self.svrBAuthCredentialUsernameKey, transaction: tx),
let password = svrBAuthCredentialStore.getString(Self.svrBAuthCredentialPasswordKey, transaction: tx),
let expiryDate = svrBAuthCredentialStore.getDate(Self.svrBAuthCredentialExpiryDateKey, transaction: tx)
else {
return nil
}
guard expiryDate > now else {
return nil
}
return Auth(
username: username,
password: password,
)
}
func setSVRBAuthCredential(
_ credential: LibSignalClient.Auth?,
now: Date,
tx: DBWriteTransaction,
) {
guard let credential else {
svrBAuthCredentialStore.removeAll(transaction: tx)
return
}
svrBAuthCredentialStore.setString(credential.username, key: Self.svrBAuthCredentialUsernameKey, transaction: tx)
svrBAuthCredentialStore.setString(credential.password, key: Self.svrBAuthCredentialPasswordKey, transaction: tx)
svrBAuthCredentialStore.setDate(
now.addingTimeInterval(Self.svrBAuthCredentialExpirationTime),
key: Self.svrBAuthCredentialExpiryDateKey,
transaction: tx,
)
}
func removeAllBackupAuthCredentials(ofType credentialType: BackupAuthCredentialType, tx: DBWriteTransaction) {
let store: KeyValueStore = switch credentialType {
case .media: backupMediaAuthCredentialStore
@ -186,13 +141,6 @@ public class AuthCredentialStore {
}
store.removeAll(transaction: tx)
switch credentialType {
case .media:
break
case .messages:
svrBAuthCredentialStore.removeAll(transaction: tx)
}
}
public func removeAllBackupAuthCredentials(tx: DBWriteTransaction) {

View File

@ -52,7 +52,6 @@ public protocol BackupAuthCredentialManager {
func fetchSVRBAuthCredential(
key: MessageRootBackupKey,
chatServiceAuth: ChatServiceAuth,
forceRefresh: Bool,
logger: PrefixedLogger,
) async throws -> LibSignalClient.Auth
}
@ -189,14 +188,12 @@ class BackupAuthCredentialManagerImpl: BackupAuthCredentialManager {
func fetchSVRBAuthCredential(
key: MessageRootBackupKey,
chatServiceAuth auth: ChatServiceAuth,
forceRefresh: Bool,
logger: PrefixedLogger,
) async throws -> LibSignalClient.Auth {
try await serialTaskQueue.run {
try await _fetchSVRBAuthCredential(
key: key,
chatServiceAuth: auth,
forceRefresh: forceRefresh,
logger: logger,
)
}
@ -205,20 +202,8 @@ class BackupAuthCredentialManagerImpl: BackupAuthCredentialManager {
private func _fetchSVRBAuthCredential(
key: MessageRootBackupKey,
chatServiceAuth auth: ChatServiceAuth,
forceRefresh: Bool,
logger: PrefixedLogger,
) async throws -> LibSignalClient.Auth {
let now = dateProvider()
if
!forceRefresh,
let cachedCredential = db.read(block: { tx in
authCredentialStore.svrBAuthCredential(now: now, tx: tx)
})
{
return cachedCredential
}
let backupServiceAuth = try await _fetchBackupServiceAuth(
key: key,
localAci: key.aci,
@ -238,10 +223,6 @@ class BackupAuthCredentialManagerImpl: BackupAuthCredentialManager {
password: receivedSVRBAuthCredential.password,
)
await db.awaitableWrite { tx in
authCredentialStore.setSVRBAuthCredential(svrBAuth, now: now, tx: tx)
}
return svrBAuth
}

View File

@ -198,7 +198,6 @@ class _AttachmentUploadManager_BackupRequestManagerMock: BackupRequestManager {
func fetchSVRBAuthCredential(
key: SignalServiceKit.MessageRootBackupKey,
chatServiceAuth auth: SignalServiceKit.ChatServiceAuth,
forceRefresh: Bool,
logger: PrefixedLogger,
) async throws -> LibSignalClient.Auth {
return LibSignalClient.Auth(username: "", password: "")