Signal-iOS/SignalServiceKit/Account/IdentityKeyChecker.swift
2025-07-01 09:11:08 -05:00

98 lines
3.2 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import LibSignalClient
protocol IdentityKeyChecker {
func serverHasSameKeyAsLocal(for identity: OWSIdentity, localIdentifier: ServiceId) async throws -> Bool
}
class IdentityKeyCheckerImpl: IdentityKeyChecker {
private let db: any DB
private let identityManager: Shims.IdentityManager
private let profileFetcher: Shims.ProfileFetcher
init(
db: any DB,
identityManager: Shims.IdentityManager,
profileFetcher: Shims.ProfileFetcher,
) {
self.db = db
self.identityManager = identityManager
self.profileFetcher = profileFetcher
}
func serverHasSameKeyAsLocal(for identity: OWSIdentity, localIdentifier: ServiceId) async throws -> Bool {
owsPrecondition((identity == .aci && localIdentifier is Aci) || (identity == .pni && localIdentifier is Pni))
let remoteIdentityKey = try await self.profileFetcher.fetchIdentityPublicKey(serviceId: localIdentifier)
let localIdentityKey = self.db.read(block: { tx -> IdentityKey? in
return self.identityManager.identityKeyPair(for: identity, tx: tx)?.keyPair.identityKey
})
return remoteIdentityKey == localIdentityKey
}
}
// MARK: - Dependencies
extension IdentityKeyCheckerImpl {
enum Shims {
typealias IdentityManager = _IdentityKeyCheckerImpl_IdentityManager_Shim
typealias ProfileFetcher = _IdentityKeyCheckerImpl_ProfileFetcher_Shim
}
enum Wrappers {
typealias IdentityManager = _IdentityKeyCheckerImpl_IdentityManager_Wrapper
typealias ProfileFetcher = _IdentityKeyCheckerImpl_ProfileFetcher_Wrapper
}
}
// MARK: IdentityManager
protocol _IdentityKeyCheckerImpl_IdentityManager_Shim {
func identityKeyPair(for identity: OWSIdentity, tx: DBReadTransaction) -> ECKeyPair?
}
class _IdentityKeyCheckerImpl_IdentityManager_Wrapper: _IdentityKeyCheckerImpl_IdentityManager_Shim {
private let identityManager: OWSIdentityManager
init(_ identityManager: OWSIdentityManager) {
self.identityManager = identityManager
}
func identityKeyPair(for identity: OWSIdentity, tx: DBReadTransaction) -> ECKeyPair? {
return identityManager.identityKeyPair(for: identity, tx: tx)
}
}
// MARK: ProfileFetcher
protocol _IdentityKeyCheckerImpl_ProfileFetcher_Shim {
func fetchIdentityPublicKey(serviceId: ServiceId) async throws -> IdentityKey
}
class _IdentityKeyCheckerImpl_ProfileFetcher_Wrapper: _IdentityKeyCheckerImpl_ProfileFetcher_Shim {
private let networkManager: NetworkManager
init(networkManager: NetworkManager) {
self.networkManager = networkManager
}
func fetchIdentityPublicKey(serviceId: ServiceId) async throws -> IdentityKey {
let request = OWSRequestFactory.getUnversionedProfileRequest(
serviceId: serviceId,
auth: .identified(.implicit())
)
let response = try await networkManager.asyncRequest(request)
struct Response: Decodable {
var identityKey: Data
}
let decodedResponse = try JSONDecoder().decode(Response.self, from: (response.responseBodyData ?? Data()))
return try IdentityKey(bytes: decodedResponse.identityKey)
}
}