element-ios/Riot/Managers/UISIAutoReporter/UISIDetector.swift

116 lines
4.2 KiB
Swift
Raw Normal View History

//
2022-03-03 17:29:41 +00:00
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import MatrixSDK
import Foundation
protocol UISIDetectorDelegate: AnyObject {
var reciprocateToDeviceEventType: String { get }
func uisiDetected(source: UISIDetectedMessage)
2022-03-03 17:29:41 +00:00
func uisiReciprocateRequest(source: MXEvent)
}
struct UISIDetectedMessage {
2022-03-03 17:29:41 +00:00
let eventId: String
let roomId: String
let senderUserId: String
let senderDeviceId: String
let senderKey: String
let sessionId: String
static func fromEvent(event: MXEvent) -> UISIDetectedMessage {
return UISIDetectedMessage(
2022-03-03 17:29:41 +00:00
eventId: event.eventId ?? "",
roomId: event.roomId,
2022-03-03 17:29:41 +00:00
senderUserId: event.sender,
senderDeviceId: event.wireContent["device_id"] as? String ?? "",
senderKey: event.wireContent["sender_key"] as? String ?? "",
sessionId: event.wireContent["session_id"] as? String ?? ""
2022-03-03 17:29:41 +00:00
)
}
}
/// Detects decryption errors that occur and don't recover within a grace period.
/// see `UISIDetectorDelegate` for listening to detections.
2022-03-03 17:29:41 +00:00
class UISIDetector: MXLiveEventListener {
weak var delegate: UISIDetectorDelegate?
var enabled = false
var initialSyncCompleted = false
private var trackedUISIs = [String: DispatchSourceTimer]()
2022-03-03 17:29:41 +00:00
private let dispatchQueue = DispatchQueue(label: "io.element.UISIDetector.queue")
private static let gracePeriodSeconds = 30
2022-03-03 17:29:41 +00:00
// MARK: - Public
func onSessionStateChanged(state: MXSessionState) {
2022-03-03 17:29:41 +00:00
dispatchQueue.async {
self.initialSyncCompleted = state == .running
2022-03-03 17:29:41 +00:00
}
}
func onLiveEventDecryptionAttempted(event: MXEvent, result: MXEventDecryptionResult) {
guard enabled, let eventId = event.eventId, let roomId = event.roomId else { return }
2022-03-03 17:29:41 +00:00
dispatchQueue.async {
let trackedId = Self.trackedEventId(roomId: eventId, eventId: roomId)
if let timer = self.trackedUISIs[trackedId],
result.clearEvent != nil {
// successfully decrypted during grace period, cancel timer.
self.trackedUISIs[trackedId] = nil
timer.cancel()
return
}
guard self.initialSyncCompleted,
result.clearEvent == nil
else { return }
// track uisi and report it only if it is not decrypted before grade period ends
let timer = DispatchSource.makeTimerSource(queue: self.dispatchQueue)
timer.schedule(deadline: .now() + .seconds(Self.gracePeriodSeconds))
timer.setEventHandler { [weak self] in
guard let self = self else { return }
self.trackedUISIs[trackedId] = nil
MXLog.verbose("[UISIDetector] onLiveEventDecryptionAttempted: Timeout on \(eventId)")
self.triggerUISI(source: UISIDetectedMessage.fromEvent(event: event))
2022-03-03 17:29:41 +00:00
}
self.trackedUISIs[trackedId] = timer
timer.activate()
2022-03-03 17:29:41 +00:00
}
}
func onLiveToDeviceEvent(event: MXEvent) {
guard enabled, event.type == delegate?.reciprocateToDeviceEventType else { return }
delegate?.uisiReciprocateRequest(source: event)
}
// MARK: - Private
private func triggerUISI(source: UISIDetectedMessage) {
2022-03-03 17:29:41 +00:00
guard enabled else { return }
MXLog.info("[UISIDetector] triggerUISI: Unable To Decrypt \(source)")
2022-03-03 17:29:41 +00:00
self.delegate?.uisiDetected(source: source)
}
// MARK: - Static
private static func trackedEventId(roomId: String, eventId: String) -> String {
2022-03-03 17:29:41 +00:00
return "\(roomId)-\(eventId)"
}
}