Skip to content

Commit

Permalink
refactor: process conversation proteus/MLS message add event - WPB-10…
Browse files Browse the repository at this point in the history
…174 (#2164)
  • Loading branch information
jullianm authored Dec 13, 2024
1 parent 7511a48 commit af0ad4c
Show file tree
Hide file tree
Showing 75 changed files with 3,422 additions and 531 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,17 @@ struct ConversationMLSMessageAddEventDecoder {
forKey: .payload
)

let timestamp = try container.decodeIfPresent(
UTCTimeMillis.self,
forKey: .timestamp
)

return ConversationMLSMessageAddEvent(
conversationID: conversationID,
senderID: senderID,
subconversation: subconversation,
message: payload.text
message: payload.text,
timestamp: timestamp?.date
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ struct ConversationProteusMessageAddEventDecoder {
conversationID: conversationID,
senderID: senderID,
timestamp: timestamp.date,
message: .ciphertext(payload.text),
externalData: payload.data.map { .ciphertext($0) },
message: .init(encryptedMessage: payload.text),
externalData: payload.data.map { .init(encryptedMessage: $0) },
messageSenderClientID: payload.sender,
messageRecipientClientID: payload.recipient
)
Expand Down
6 changes: 3 additions & 3 deletions WireAPI/Sources/WireAPI/Models/Messaging/MessageContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ import Foundation
/// The contents of a message, typically as a base-64 encoded
/// Protobuf string.

public enum MessageContent: Equatable, Codable, Sendable {
public struct MessageContent: Equatable, Codable, Sendable {

/// Encrypted message content.

case ciphertext(String)
public let encryptedMessage: String

/// Unencrypted message content.

case plaintext(String)
public var decryptedMessage: String?

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ import Foundation

public struct ConversationMLSMessageAddEvent: Equatable, Codable, Sendable {

public struct DecryptedMessage: Equatable, Codable, Sendable {

public let message: String

public let senderClientID: String?

public init(
message: String,
senderClientID: String?
) {
self.message = message
self.senderClientID = senderClientID
}
}

/// The id of the conversation.

public let conversationID: ConversationID
Expand All @@ -42,4 +57,13 @@ public struct ConversationMLSMessageAddEvent: Equatable, Codable, Sendable {

public let message: String

/// The date the message was received.

public let timestamp: Date?

/// The decrypted current message + decrypted buffered messages
/// along with the related sender client ID for each message.

public var decryptedMessages: [DecryptedMessage] = []

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/list-connections/"
- method: post
- body: {"size":500}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
curl \
"/v7/conversations/213248A1-5499-418F-8173-5010D1C1E506/code"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
curl \
--request POST \
--header "Content-Type: application/json" \
--data "{\"size\":500}" \
"/v7/conversations/list-ids/"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
curl \
--request POST \
--header "Content-Type: application/json" \
--data "{\"qualified_ids\":[{\"domain\":\"wire.com\",\"id\":\"213248A1-5499-418F-8173-5010D1C1E506\"}]}" \
"/v7/conversations/list"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
curl \
"/v7/one2one-conversations/domain.com/99db9768-04e3-4b5d-9268-831b6a25c4ab"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/feature-configs"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/self"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/self/supported-protocols"
- method: put
- body: {"supported_protocols":["mls"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/teams/213248a1-5499-418f-8173-5010d1c1e506/legalhold/302c59b0-037c-4b0f-a3ed-ccdbfb4cfe2c"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/teams/213248a1-5499-418f-8173-5010d1c1e506/members?maxResults=2000"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/teams/213248a1-5499-418f-8173-5010d1c1e506"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/teams/213248a1-5499-418f-8173-5010d1c1e506/conversations/roles"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
curl \
"/v7/notifications/last?client=abcd1234"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
curl \
"/v7/notifications?client=abcd1234&since=d7f7f946-c4da-4300-998d-5aeba8affeee&size=500"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
curl \
"/v7/notifications?client=abcd1234&since=2eeeb5e4-df85-4aef-9eb2-289981f086ab&size=500"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
curl \
"/v7/clients"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/properties/labels"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/properties/WIRE_TYPING_INDICATOR_MODE"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/properties/WIRE_RECEIPT_MODE"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/users/example.com/213248a1-5499-418f-8173-5010d1c1e506"
- method: get
- body: none
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- HTTPRequest
- path: "/v7/list-users"
- method: post
- body: {"qualified_ids":[{"domain":"example.com","id":"213248A1-5499-418F-8173-5010D1C1E506"},{"domain":"example.com","id":"302C59B0-037C-4B0F-A3ED-CCDBFB4CFE2C"},{"domain":"example.com","id":"7E23727B-D612-4123-88C0-57E311A7E5A3"}]}
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ private enum Scaffolding {
conversationID: conversationID,
senderID: senderID,
timestamp: timestamp,
message: .ciphertext("foo"),
externalData: .ciphertext("bar"),
message: .init(encryptedMessage: "foo"),
externalData: .init(encryptedMessage: "bar"),
messageSenderClientID: "abc123",
messageRecipientClientID: "def456"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,8 @@ final class ConversationEventDecodingTests: XCTestCase {
conversationID: conversationID,
senderID: senderID,
subconversation: "subconversation",
message: "message"
message: "message",
timestamp: fractionalDate(from: "2024-06-04T15:03:07.598Z")
)

static let mlsWelcomeEvent = ConversationMLSWelcomeEvent(
Expand All @@ -471,8 +472,8 @@ final class ConversationEventDecodingTests: XCTestCase {
conversationID: conversationID,
senderID: senderID,
timestamp: timestamp,
message: .ciphertext("foo"),
externalData: .ciphertext("bar"),
message: .init(encryptedMessage: "foo"),
externalData: .init(encryptedMessage: "bar"),
messageSenderClientID: "abc123",
messageRecipientClientID: "def456"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"subconv": "subconversation",
"data": {
"text": "message"
}
},
"time": "2024-06-04T15:03:07.598Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//
// Wire
// Copyright (C) 2024 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import Foundation
import WireAPI
import WireDataModel
import WireLogging

// sourcery: AutoMockable
/// Decrypt MLS messages.
protocol MLSMessageDecryptorProtocol {

/// Decrypt a MLS message.
///
/// - Parameter eventData: A payload containing the encrypted message.
/// - Returns: The payload containing the decrypted message.

func decryptedEventData(
from eventData: ConversationMLSMessageAddEvent
) async throws -> ConversationMLSMessageAddEvent

}

struct MLSMessageDecryptor: MLSMessageDecryptorProtocol {

let mlsDecryptionService: any MLSDecryptionServiceInterface
let mlsService: any MLSServiceInterface
let conversationLocalStore: any ConversationLocalStoreProtocol

func decryptedEventData(
from eventData: ConversationMLSMessageAddEvent
) async throws -> ConversationMLSMessageAddEvent {
let conversationID = eventData.conversationID

guard let mlsConversation = await conversationLocalStore.fetchConversation(
id: conversationID.uuid,
domain: conversationID.domain
) else {
throw MLSMessageDecryptorError.conversationNotFound
}

guard let (mlsGroupID, isMLSReady) = await conversationLocalStore.mlsConversationInfo(
conversation: mlsConversation
) else {
// MLS conversation should have a group id.
throw MLSMessageDecryptorError.missingMLSGroupID
}

guard isMLSReady else {
throw MLSMessageDecryptorError.mlsConversationNotReady
}

let decryptionResults = await decryptMLSMessage(
message: eventData.message,
mlsGroupID: mlsGroupID,
subconversation: eventData.subconversation
)

let decryptedMessages = await processMLSMessageDecryptionResults(
decryptionResults,
mlsConversation: mlsConversation,
senderID: eventData.senderID.uuid,
senderDomain: eventData.senderID.domain,
date: eventData.timestamp
)

var decryptedEvent = eventData
decryptedEvent.decryptedMessages = decryptedMessages

return decryptedEvent
}

private func decryptMLSMessage(
message: String,
mlsGroupID: MLSGroupID,
subconversation: String?
) async -> [MLSDecryptResult] {
do {
let subconvType = subconversation != nil ? SubgroupType(rawValue: subconversation!) : nil

let results = try await mlsDecryptionService.decrypt(
message: message,
for: mlsGroupID,
subconversationType: subconvType
)

if results.isEmpty {
WireLogger.mls.info(
"successfully decrypted mls message but no result was returned"
)

return []
}

return results

} catch {
WireLogger.mls.error(
"failed to decrypt mls message: \(String(describing: error))"
)

return []
}
}

private func processMLSMessageDecryptionResults(
_ results: [MLSDecryptResult],
mlsConversation: ZMConversation,
senderID: UUID,
senderDomain: String,
date: Date?
) async -> [ConversationMLSMessageAddEvent.DecryptedMessage] {
var decryptedMessages: [ConversationMLSMessageAddEvent.DecryptedMessage] = []

for result in results {
switch result {
case let .message(decryptedData, senderClientID):
let mlsDecryptedMessage = decryptedData.base64EncodedString()
decryptedMessages.append(
.init(
message: mlsDecryptedMessage,
senderClientID: senderClientID
)
)

case let .proposal(commitDelay):
await conversationLocalStore.commitPendingProposals(
conversation: mlsConversation,
date: date ?? .now,
commitDelay: commitDelay
)
}
}

return decryptedMessages
}

}
Loading

0 comments on commit af0ad4c

Please sign in to comment.