Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show Encryption Authenticity warnings on messages in the timeline. #3051

Merged
merged 3 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; };
02F4FAE40AF63A1941FD3BBA /* NotificationCenterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B7F8EE25775DE2A305CBB5 /* NotificationCenterProtocol.swift */; };
037006FB6DF1374F94E4058D /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */; };
03CDCA6243F89B194E3FAD17 /* EncryptionAuthenticity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955336CBD5ED73C792D1F580 /* EncryptionAuthenticity.swift */; };
0437765FF480249486893CC7 /* ScreenTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.swift */; };
044DD8F80231BC30570F7965 /* UserDiscoveryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AAD845E53B0C8B5E0812C2 /* UserDiscoveryService.swift */; };
04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422724361B6555364C43281E /* RoomHeaderView.swift */; };
Expand Down Expand Up @@ -1771,6 +1772,7 @@
94028A227645FA880B966211 /* WaveformSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveformSource.swift; sourceTree = "<group>"; };
94D670124FC3E84F23A62CCF /* APNSPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APNSPayload.swift; sourceTree = "<group>"; };
9501D11B4258DFA33BA3B40F /* ServerSelectionScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenModels.swift; sourceTree = "<group>"; };
955336CBD5ED73C792D1F580 /* EncryptionAuthenticity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionAuthenticity.swift; sourceTree = "<group>"; };
95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSRegularExpresion.swift; sourceTree = "<group>"; };
969694F67E844FCA51F7E051 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
96C4762F8D6112E43117DB2F /* CustomStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStringConvertible.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3516,6 +3518,7 @@
children = (
B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */,
96C4762F8D6112E43117DB2F /* CustomStringConvertible.swift */,
955336CBD5ED73C792D1F580 /* EncryptionAuthenticity.swift */,
314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */,
5DE8D25D6A91030175D52A20 /* RoomTimelineItemProperties.swift */,
BE89A8BD65CCE3FCC925CA14 /* TimelineItemReplyDetails.swift */,
Expand Down Expand Up @@ -6198,6 +6201,7 @@
8B1D5CE017EEC734CF5FE130 /* Encodable.swift in Sources */,
4C5A638DAA8AF64565BA4866 /* EncryptedRoomTimelineItem.swift in Sources */,
B5903E48CF43259836BF2DBF /* EncryptedRoomTimelineView.swift in Sources */,
03CDCA6243F89B194E3FAD17 /* EncryptionAuthenticity.swift in Sources */,
FBD402E3170EB1ED0D1AA672 /* EncryptionKeyProvider.swift in Sources */,
46A6DB0F78FB399BD59E2D41 /* EncryptionKeyProviderProtocol.swift in Sources */,
0C6DF318E9C8F6461E6ABDE7 /* EncryptionResetPasswordScreen.swift in Sources */,
Expand Down Expand Up @@ -7553,7 +7557,7 @@
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
requirement = {
kind = exactVersion;
version = 1.0.30;
version = 1.0.31;
};
};
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
"state" : {
"revision" : "bc534e15fa0749d668b201b923ee57204afb868a",
"version" : "1.0.30"
"revision" : "8e2b4049fb492dcf5b0c796784b7aa7a3c099943",
"version" : "1.0.31"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@
"error_some_messages_have_not_been_sent" = "Some messages have not been sent";
"error_unknown" = "Sorry, an error occurred";
"event_shield_reason_authenticity_not_guaranteed" = "The authenticity of this encrypted message can't be guaranteed on this device.";
"event_shield_reason_sent_in_clear" = "Sent in clear.";
"event_shield_reason_unknown_device" = "Encrypted by an unknown or deleted device.";
"event_shield_reason_unsigned_device" = "Encrypted by a device not verified by its owner.";
"event_shield_reason_unverified_identity" = "Encrypted by an unverified user.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
/* Used for testing */
"untranslated" = "Untranslated";

// MARK: - Shields

"send_info_not_encrypted" = "Not encrypted";

// MARK: - Soft logout

"soft_logout_signin_title" = "Sign in";
Expand Down
4 changes: 4 additions & 0 deletions ElementX/Sources/Application/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ final class AppSettings {
case publicSearchEnabled
case fuzzyRoomListSearchEnabled
case pinningEnabled
case timelineItemAuthenticityEnabled
}

private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
Expand Down Expand Up @@ -284,6 +285,9 @@ final class AppSettings {

@UserPreference(key: UserDefaultsKeys.pinningEnabled, defaultValue: false, storageType: .userDefaults(store))
var pinningEnabled

@UserPreference(key: UserDefaultsKeys.timelineItemAuthenticityEnabled, defaultValue: false, storageType: .userDefaults(store))
var timelineItemAuthenticityEnabled

#endif

Expand Down
2 changes: 2 additions & 0 deletions ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let userID = userSession.clientProxy.userID

let timelineItemFactory = RoomTimelineItemFactory(userID: userID,
encryptionAuthenticityEnabled: appSettings.timelineItemAuthenticityEnabled,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))

Expand Down Expand Up @@ -1033,6 +1034,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let userID = userSession.clientProxy.userID

let timelineItemFactory = RoomTimelineItemFactory(userID: userID,
encryptionAuthenticityEnabled: appSettings.timelineItemAuthenticityEnabled,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))

Expand Down
2 changes: 0 additions & 2 deletions ElementX/Sources/Generated/Strings+Untranslated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import Foundation
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
internal enum UntranslatedL10n {
/// Not encrypted
internal static var sendInfoNotEncrypted: String { return UntranslatedL10n.tr("Untranslated", "send_info_not_encrypted") }
/// Clear all data currently stored on this device?
/// Sign in again to access your account data and messages.
internal static var softLogoutClearDataDialogContent: String { return UntranslatedL10n.tr("Untranslated", "soft_logout_clear_data_dialog_content") }
Expand Down
2 changes: 2 additions & 0 deletions ElementX/Sources/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,8 @@ internal enum L10n {
internal static var errorUnknown: String { return L10n.tr("Localizable", "error_unknown") }
/// The authenticity of this encrypted message can't be guaranteed on this device.
internal static var eventShieldReasonAuthenticityNotGuaranteed: String { return L10n.tr("Localizable", "event_shield_reason_authenticity_not_guaranteed") }
/// Sent in clear.
internal static var eventShieldReasonSentInClear: String { return L10n.tr("Localizable", "event_shield_reason_sent_in_clear") }
/// Encrypted by an unknown or deleted device.
internal static var eventShieldReasonUnknownDevice: String { return L10n.tr("Localizable", "event_shield_reason_unknown_device") }
/// Encrypted by a device not verified by its owner.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
.previewDisplayName("Replies")
threads
.previewDisplayName("Thread decorator")
encryptionAuthenticity
.previewDisplayName("Encryption Indicators")
}

// These akwats include a reply
Expand Down Expand Up @@ -477,4 +479,80 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
}
.environmentObject(viewModel.context)
}

static var encryptionAuthenticity: some View {
VStack(spacing: 0) {
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
canBeRepliedTo: true,
isThreaded: false,
sender: .init(id: "whoever"),
content: .init(body: "A long message that should be on multiple lines."),
properties: RoomTimelineItemProperties(encryptionAuthenticity: .unsignedDevice(color: .red))),
groupStyle: .single))

RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
canBeRepliedTo: true,
isThreaded: false,
sender: .init(id: "whoever"),
content: .init(body: "A long message that should be on multiple lines."),
properties: RoomTimelineItemProperties(isEdited: true,
encryptionAuthenticity: .unsignedDevice(color: .red))),
groupStyle: .single))

RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
timestamp: "10:42",
isOutgoing: false,
isEditable: false,
canBeRepliedTo: true,
isThreaded: false,
sender: .init(id: "whoever"),
content: .init(body: "Short message"),
properties: RoomTimelineItemProperties(encryptionAuthenticity: .unknownDevice(color: .red))),
groupStyle: .first))

RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
timestamp: "10:42",
isOutgoing: false,
isEditable: false,
canBeRepliedTo: true,
isThreaded: false,
sender: .init(id: "whoever"),
content: .init(body: "Message goes Here"),
properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))),
groupStyle: .last))

ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random,
timestamp: "Now",
isOutgoing: false,
isEditable: false,
canBeRepliedTo: true,
isThreaded: false,
sender: .init(id: "Bob"),
content: .init(body: "Some other image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil),

properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))))

VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
canBeRepliedTo: true,
isThreaded: true,
sender: .init(id: ""),
content: .init(body: "audio.ogg",
duration: 100,
waveform: EstimatedWaveform.mockWaveform,
source: nil,
contentType: nil),
properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))),
playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), duration: 10, waveform: EstimatedWaveform.mockWaveform))
}
.environmentObject(viewModel.context)
}
}
Loading
Loading