Skip to content

Commit e266f7f

Browse files
committed
Analytics | Report expected UTDs as HistoricalMessage error
1 parent d0890d5 commit e266f7f

File tree

6 files changed

+477
-21
lines changed

6 files changed

+477
-21
lines changed

Riot/Modules/Analytics/DecryptionFailure+Analytics.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,34 @@ extension DecryptionFailure {
2121

2222
public func toAnalyticsEvent() -> AnalyticsEvent.Error {
2323

24-
let timeToDecryptMillis: Int = if self.timeToDecrypt != nil {
25-
Int(self.timeToDecrypt! * 1000)
24+
let timeToDecryptMillis: Int = if let ttd = self.timeToDecrypt {
25+
Int(ttd * 1000)
2626
} else {
2727
-1
2828
}
29+
30+
let isHistoricalEvent = if let localAge = self.eventLocalAgeMillis {
31+
localAge < 0
32+
} else { false }
33+
34+
let errorName = if isHistoricalEvent && self.trustOwnIdentityAtTimeOfFailure == false {
35+
AnalyticsEvent.Error.Name.HistoricalMessage
36+
} else {
37+
self.reason.errorName
38+
}
39+
2940
return AnalyticsEvent.Error(
3041
context: self.context,
3142
cryptoModule: .Rust,
3243
cryptoSDK: .Rust,
3344
domain: .E2EE,
3445

35-
eventLocalAgeMillis: nil,
46+
eventLocalAgeMillis: self.eventLocalAgeMillis,
3647
isFederated: nil,
3748
isMatrixDotOrg: nil,
38-
name: self.reason.errorName,
49+
name: errorName,
3950
timeToDecryptMillis: timeToDecryptMillis,
40-
userTrustsOwnIdentity: nil,
51+
userTrustsOwnIdentity: self.trustOwnIdentityAtTimeOfFailure,
4152
wasVisibleToUser: nil
4253
)
4354
}

Riot/Modules/Analytics/DecryptionFailure.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ import AnalyticsEvents
4747
/// UTDs can be permanent or temporary. If temporary, this field will contain the time it took to decrypt the message in milliseconds. If permanent should be nil
4848
var timeToDecrypt: TimeInterval?
4949

50+
/// Was the current cross-signing identity trusted at the time of decryption
51+
var trustOwnIdentityAtTimeOfFailure: Bool?
52+
53+
var eventLocalAgeMillis: Int?
54+
5055
init(failedEventId: String, reason: DecryptionFailureReason, context: String, ts: TimeInterval) {
5156
self.failedEventId = failedEventId
5257
self.reason = reason

Riot/Modules/Analytics/DecryptionFailureTracker.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ class DecryptionFailureTracker: NSObject {
6262
selector: #selector(eventDidDecrypt(_:)),
6363
name: .mxEventDidDecrypt,
6464
object: nil)
65-
6665
}
6766

6867
@objc
69-
func reportUnableToDecryptError(forEvent event: MXEvent, withRoomState roomState: MXRoomState, myUser userId: String) {
68+
func reportUnableToDecryptError(forEvent event: MXEvent, withRoomState roomState: MXRoomState, mySession: MXSession) {
7069
if reportedFailures[event.eventId] != nil || trackedEvents.contains(event.eventId) {
7170
return
7271
}
72+
guard let userId = mySession.myUserId else { return }
7373

7474
// Filter out "expected" UTDs
7575
// We cannot decrypt messages sent before the user joined the room
@@ -82,6 +82,12 @@ class DecryptionFailureTracker: NSObject {
8282

8383
guard let error = event.decryptionError as? NSError else { return }
8484

85+
let eventOrigin = event.originServerTs
86+
let deviceTimestamp = mySession.crypto.deviceCreationTs
87+
// If negative it's an historical event relative to the current session
88+
let eventRelativeAgeMillis = Int(eventOrigin) - Int(deviceTimestamp)
89+
let isSessionVerified = mySession.crypto.crossSigning.canTrustCrossSigning
90+
8591
var reason = DecryptionFailureReason.unspecified
8692

8793
if error.code == MXDecryptingErrorUnknownInboundSessionIdCode.rawValue {
@@ -92,7 +98,13 @@ class DecryptionFailureTracker: NSObject {
9298

9399
let context = String(format: "code: %ld, description: %@", error.code, event.decryptionError.localizedDescription)
94100

95-
reportedFailures[failedEventId] = DecryptionFailure(failedEventId: failedEventId, reason: reason, context: context, ts: self.timeProvider.nowTs())
101+
let failure = DecryptionFailure(failedEventId: failedEventId, reason: reason, context: context, ts: self.timeProvider.nowTs())
102+
103+
failure.eventLocalAgeMillis = Int(exactly: eventRelativeAgeMillis)
104+
failure.trustOwnIdentityAtTimeOfFailure = isSessionVerified
105+
106+
reportedFailures[failedEventId] = failure
107+
96108

97109
// Start the ticker if needed. There is no need to have a ticker if no failures are tracked
98110
if checkFailuresTimer == nil {

Riot/Utils/EventFormatter.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ - (NSAttributedString *)unsafeAttributedStringFromEvent:(MXEvent *)event
334334
{
335335
// Track e2e failures
336336
dispatch_async(dispatch_get_main_queue(), ^{
337-
[[DecryptionFailureTracker sharedInstance] reportUnableToDecryptErrorForEvent:event withRoomState:roomState myUser:self->mxSession.myUser.userId];
337+
[[DecryptionFailureTracker sharedInstance] reportUnableToDecryptErrorForEvent:event withRoomState:roomState mySession:self->mxSession];
338338
});
339339

340340
if (event.decryptionError.code == MXDecryptingErrorUnknownInboundSessionIdCode)

0 commit comments

Comments
 (0)