Files
bundesmessenger-ios/Riot/Modules/Analytics/DecryptionFailureTracker.swift
T
JanNiklas Grabowski b298dedc22 chore: update from foss 1.11.19 (MESSENGER-6656)
Merge commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1' into feature/foss_update_1_11_19

* commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1':
  finish version++
  version++
  comments
  update submodule
  remove obsolete tests
  removed unused code
  update submodule
  fix
  Libolm removal
  update license macro
  update license
  Prepare for new sprint

# Conflicts:
#	Config/AppVersion.xcconfig
#	IDETemplateMacros.plist
#	LICENSE
#	README.md
#	Riot/Categories/MXSession+Riot.m
#	Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
#	Riot/Managers/KeyValueStorage/Extensions/Keychain.swift
#	Riot/Managers/KeyValueStorage/KeyValueStore.swift
#	Riot/Managers/KeyValueStorage/KeychainStore.swift
#	Riot/Managers/KeyValueStorage/MemoryStore.swift
#	Riot/Managers/PushNotification/PushNotificationService.m
#	Riot/Managers/Settings/RiotSettings.swift
#	Riot/Managers/Settings/Shared/RiotSharedSettings.swift
#	Riot/Modules/Analytics/AnalyticsUIElement.swift
#	Riot/Modules/Application/AppCoordinator.swift
#	Riot/Modules/Application/LegacyAppDelegate.h
#	Riot/Modules/Application/LegacyAppDelegate.m
#	Riot/Modules/Authentication/Legacy/AuthenticationViewController.h
#	Riot/Modules/Authentication/Legacy/AuthenticationViewController.m
#	Riot/Modules/Authentication/Legacy/Views/AuthInputsView.h
#	Riot/Modules/Authentication/Legacy/Views/AuthInputsView.m
#	Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
#	Riot/Modules/Common/Recents/RecentsViewController.m
#	Riot/Modules/Common/WebViewController/WebViewViewController.m
#	Riot/Modules/Contacts/Details/ContactDetailsViewController.m
#	Riot/Modules/Contacts/Views/ContactTableViewCell.m
#	Riot/Modules/Favorites/FavouritesViewController.h
#	Riot/Modules/Favorites/FavouritesViewController.m
#	Riot/Modules/GlobalSearch/UnifiedSearchViewController.m
#	Riot/Modules/People/PeopleViewController.h
#	Riot/Modules/People/PeopleViewController.m
#	Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift
#	Riot/Modules/Room/DataSources/RoomDataSource.m
#	Riot/Modules/Room/Files/RoomFilesViewController.m
#	Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m
#	Riot/Modules/Room/Members/RoomParticipantsViewController.m
#	Riot/Modules/Room/RoomViewController.m
#	Riot/Modules/Room/Settings/RoomSettingsViewController.m
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift
#	Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h
#	Riot/Modules/Rooms/RoomsViewController.h
#	Riot/Modules/Rooms/ShowDirectory/Cells/Network/DirectoryNetworkTableHeaderFooterView.swift
#	Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift
#	Riot/Modules/Rooms/ShowDirectory/PublicRoomsDirectoryViewModel.swift
#	Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift
#	Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift
#	Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift
#	Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift
#	Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModel.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModelType.swift
#	Riot/Modules/SetPinCode/PinCodePreferences.swift
#	Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift
#	Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m
#	Riot/Modules/Settings/Security/SecurityViewController.m
#	Riot/Modules/Settings/SettingsViewController.m
#	Riot/Modules/SplitView/SplitViewCoordinator.swift
#	Riot/Modules/SplitView/SplitViewCoordinatorType.swift
#	Riot/Modules/StartChat/StartChatViewController.m
#	Riot/Modules/TabBar/MasterTabBarController.h
#	Riot/Modules/TabBar/MasterTabBarController.m
#	Riot/Utils/EventFormatter.m
#	Riot/Utils/HTMLFormatter.swift
#	Riot/Utils/Tools.m
#	RiotNSE/NotificationService.swift
2024-10-18 15:45:54 +02:00

191 lines
6.7 KiB
Swift

//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
/* bwi: 6035 - ignore the Swift implementation and use the old objective-c code
import Foundation
// Protocol to get the current time. Used for easy testing
protocol TimeProvider {
func nowTs() -> TimeInterval
}
class DefaultTimeProvider: TimeProvider {
func nowTs() -> TimeInterval {
return Date.now.timeIntervalSince1970
}
}
@objc
class DecryptionFailureTracker: NSObject {
let GRACE_PERIOD: TimeInterval = 4
// Call `checkFailures` every `CHECK_INTERVAL`
let CHECK_INTERVAL: TimeInterval = 15
// The maximum time to wait for a late decryption before reporting as permanent UTD
let MAX_WAIT_FOR_LATE_DECRYPTION: TimeInterval = 60
@objc weak var delegate: E2EAnalytics?
// Reported failures
var reportedFailures = [String /* eventId */: DecryptionFailure]()
// Event ids of failures that were tracked previously
var trackedEvents = Set<String>()
var checkFailuresTimer: Timer?
@objc static let sharedInstance = DecryptionFailureTracker()
var timeProvider: TimeProvider = DefaultTimeProvider()
override init() {
super.init()
NotificationCenter.default.addObserver(self,
selector: #selector(eventDidDecrypt(_:)),
name: .mxEventDidDecrypt,
object: nil)
}
@objc
func reportUnableToDecryptError(forEvent event: MXEvent, withRoomState roomState: MXRoomState, mySession: MXSession) {
if reportedFailures[event.eventId] != nil || trackedEvents.contains(event.eventId) {
return
}
guard let userId = mySession.myUserId else { return }
// Filter out "expected" UTDs
// We cannot decrypt messages sent before the user joined the room
guard let myUser = roomState.members.member(withUserId: userId) else { return }
if myUser.membership != MXMembership.join {
return
}
guard let failedEventId = event.eventId else { return }
guard let error = event.decryptionError as? NSError else { return }
let eventOrigin = event.originServerTs
let deviceTimestamp = mySession.crypto.deviceCreationTs
// If negative it's an historical event relative to the current session
let eventRelativeAgeMillis = Int(eventOrigin) - Int(deviceTimestamp)
let isSessionVerified = mySession.crypto.crossSigning.canTrustCrossSigning
var reason = DecryptionFailureReason.unspecified
if error.code == MXDecryptingErrorUnknownInboundSessionIdCode.rawValue {
reason = DecryptionFailureReason.olmKeysNotSent
} else if error.code == MXDecryptingErrorOlmCode.rawValue {
reason = DecryptionFailureReason.olmIndexError
}
let context = String(format: "code: %ld, description: %@", error.code, event.decryptionError.localizedDescription)
let failure = DecryptionFailure(failedEventId: failedEventId, reason: reason, context: context, ts: self.timeProvider.nowTs())
failure.eventLocalAgeMillis = Int(exactly: eventRelativeAgeMillis)
failure.trustOwnIdentityAtTimeOfFailure = isSessionVerified
let myDomain = userId.components(separatedBy: ":").last
failure.isMatrixOrg = myDomain == "matrix.org"
if MXTools.isMatrixUserIdentifier(event.sender) {
let senderDomain = event.sender.components(separatedBy: ":").last
failure.isFederated = senderDomain != nil && senderDomain != myDomain
}
/// XXX for future work, as for now only the event formatter reports UTDs. That means that it's only UTD ~visible to users
failure.wasVisibleToUser = true
reportedFailures[failedEventId] = failure
// Start the ticker if needed. There is no need to have a ticker if no failures are tracked
if checkFailuresTimer == nil {
self.checkFailuresTimer = Timer.scheduledTimer(withTimeInterval: CHECK_INTERVAL, repeats: true) { [weak self] _ in
self?.checkFailures()
}
}
}
@objc
func dispatch() {
self.checkFailures()
}
@objc
func eventDidDecrypt(_ notification: Notification) {
guard let event = notification.object as? MXEvent else { return }
guard let reportedFailure = self.reportedFailures[event.eventId] else { return }
let now = self.timeProvider.nowTs()
let ellapsedTime = now - reportedFailure.ts
if ellapsedTime < 4 {
// event is graced
reportedFailures.removeValue(forKey: event.eventId)
} else {
// It's a late decrypt must be reported as a late decrypt
reportedFailure.timeToDecrypt = ellapsedTime
self.delegate?.trackE2EEError(reportedFailure)
}
// Remove from reported failures
self.trackedEvents.insert(event.eventId)
reportedFailures.removeValue(forKey: event.eventId)
// Check if we still need the ticker timer
if reportedFailures.isEmpty {
// Invalidate the current timer, nothing to check for
self.checkFailuresTimer?.invalidate()
self.checkFailuresTimer = nil
}
}
/**
Mark reported failures that occured before tsNow - GRACE_PERIOD as failures that should be
tracked.
*/
@objc
func checkFailures() {
guard let delegate = self.delegate else {return}
let tsNow = self.timeProvider.nowTs()
var failuresToCheck = [DecryptionFailure]()
for reportedFailure in self.reportedFailures.values {
let ellapsed = tsNow - reportedFailure.ts
if ellapsed > MAX_WAIT_FOR_LATE_DECRYPTION {
failuresToCheck.append(reportedFailure)
reportedFailure.timeToDecrypt = nil
reportedFailures.removeValue(forKey: reportedFailure.failedEventId)
trackedEvents.insert(reportedFailure.failedEventId)
}
}
for failure in failuresToCheck {
delegate.trackE2EEError(failure)
}
// Check if we still need the ticker timer
if reportedFailures.isEmpty {
// Invalidate the current timer, nothing to check for
self.checkFailuresTimer?.invalidate()
self.checkFailuresTimer = nil
}
}
}
*/