Skip to content

Commit

Permalink
Merge pull request #7775 from element-hq/valere/analytics_expected_utds
Browse files Browse the repository at this point in the history
Analytics | Report expected UTDs as HistoricalMessage error
  • Loading branch information
BillCarsonFr authored Apr 11, 2024
2 parents d0890d5 + e266f7f commit 24fb540
Show file tree
Hide file tree
Showing 6 changed files with 477 additions and 21 deletions.
21 changes: 16 additions & 5 deletions Riot/Modules/Analytics/DecryptionFailure+Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,34 @@ extension DecryptionFailure {

public func toAnalyticsEvent() -> AnalyticsEvent.Error {

let timeToDecryptMillis: Int = if self.timeToDecrypt != nil {
Int(self.timeToDecrypt! * 1000)
let timeToDecryptMillis: Int = if let ttd = self.timeToDecrypt {
Int(ttd * 1000)
} else {
-1
}

let isHistoricalEvent = if let localAge = self.eventLocalAgeMillis {
localAge < 0
} else { false }

let errorName = if isHistoricalEvent && self.trustOwnIdentityAtTimeOfFailure == false {
AnalyticsEvent.Error.Name.HistoricalMessage
} else {
self.reason.errorName
}

return AnalyticsEvent.Error(
context: self.context,
cryptoModule: .Rust,
cryptoSDK: .Rust,
domain: .E2EE,

eventLocalAgeMillis: nil,
eventLocalAgeMillis: self.eventLocalAgeMillis,
isFederated: nil,
isMatrixDotOrg: nil,
name: self.reason.errorName,
name: errorName,
timeToDecryptMillis: timeToDecryptMillis,
userTrustsOwnIdentity: nil,
userTrustsOwnIdentity: self.trustOwnIdentityAtTimeOfFailure,
wasVisibleToUser: nil
)
}
Expand Down
5 changes: 5 additions & 0 deletions Riot/Modules/Analytics/DecryptionFailure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ import AnalyticsEvents
/// 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
var timeToDecrypt: TimeInterval?

/// Was the current cross-signing identity trusted at the time of decryption
var trustOwnIdentityAtTimeOfFailure: Bool?

var eventLocalAgeMillis: Int?

init(failedEventId: String, reason: DecryptionFailureReason, context: String, ts: TimeInterval) {
self.failedEventId = failedEventId
self.reason = reason
Expand Down
18 changes: 15 additions & 3 deletions Riot/Modules/Analytics/DecryptionFailureTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ class DecryptionFailureTracker: NSObject {
selector: #selector(eventDidDecrypt(_:)),
name: .mxEventDidDecrypt,
object: nil)

}

@objc
func reportUnableToDecryptError(forEvent event: MXEvent, withRoomState roomState: MXRoomState, myUser userId: String) {
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
Expand All @@ -82,6 +82,12 @@ class DecryptionFailureTracker: NSObject {

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 {
Expand All @@ -92,7 +98,13 @@ class DecryptionFailureTracker: NSObject {

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

reportedFailures[failedEventId] = DecryptionFailure(failedEventId: failedEventId, reason: reason, context: context, ts: self.timeProvider.nowTs())
let failure = DecryptionFailure(failedEventId: failedEventId, reason: reason, context: context, ts: self.timeProvider.nowTs())

failure.eventLocalAgeMillis = Int(exactly: eventRelativeAgeMillis)
failure.trustOwnIdentityAtTimeOfFailure = isSessionVerified

reportedFailures[failedEventId] = failure


// Start the ticker if needed. There is no need to have a ticker if no failures are tracked
if checkFailuresTimer == nil {
Expand Down
2 changes: 1 addition & 1 deletion Riot/Utils/EventFormatter.m
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ - (NSAttributedString *)unsafeAttributedStringFromEvent:(MXEvent *)event
{
// Track e2e failures
dispatch_async(dispatch_get_main_queue(), ^{
[[DecryptionFailureTracker sharedInstance] reportUnableToDecryptErrorForEvent:event withRoomState:roomState myUser:self->mxSession.myUser.userId];
[[DecryptionFailureTracker sharedInstance] reportUnableToDecryptErrorForEvent:event withRoomState:roomState mySession:self->mxSession];
});

if (event.decryptionError.code == MXDecryptingErrorUnknownInboundSessionIdCode)
Expand Down
Loading

0 comments on commit 24fb540

Please sign in to comment.