From 9d6a2cb873bbe7f086c732d97eeef73cce93defd Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 14 Nov 2023 21:42:49 -0800 Subject: [PATCH] add matching decryption code to swift --- .../org/xmtp/android/library/Conversation.kt | 13 ++++ .../xmtp/android/library/ConversationV1.kt | 65 +++++++++++++++---- .../xmtp/android/library/ConversationV2.kt | 36 ++++++++-- .../xmtp/android/library/DecodedMessage.kt | 6 +- .../library/messages/DecryptedMessage.kt | 12 ++++ .../android/library/messages/MessageV2.kt | 41 ++++++++++-- 6 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 library/src/main/java/org/xmtp/android/library/messages/DecryptedMessage.kt diff --git a/library/src/main/java/org/xmtp/android/library/Conversation.kt b/library/src/main/java/org/xmtp/android/library/Conversation.kt index dd7e8c261..08acd50a9 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversation.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversation.kt @@ -10,6 +10,7 @@ import org.xmtp.proto.keystore.api.v1.Keystore.TopicMap.TopicData import org.xmtp.proto.message.api.v1.MessageApiOuterClass import org.xmtp.proto.message.contents.Invitation import org.xmtp.proto.message.contents.Invitation.InvitationV1.Aes256gcmHkdfsha256 +import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage import java.util.Date sealed class Conversation { @@ -180,6 +181,18 @@ sealed class Conversation { } } + fun decryptedMessages( + limit: Int? = null, + before: Date? = null, + after: Date? = null, + direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING, + ): List { + return when (this) { + is V1 -> conversationV1.decryptedMessages(limit, before, after, direction) + is V2 -> conversationV2.decryptedMessages(limit, before, after, direction) + } + } + val client: Client get() { return when (this) { diff --git a/library/src/main/java/org/xmtp/android/library/ConversationV1.kt b/library/src/main/java/org/xmtp/android/library/ConversationV1.kt index 8cd10cb50..23ae829a6 100644 --- a/library/src/main/java/org/xmtp/android/library/ConversationV1.kt +++ b/library/src/main/java/org/xmtp/android/library/ConversationV1.kt @@ -22,6 +22,7 @@ import org.xmtp.android.library.messages.sentAt import org.xmtp.android.library.messages.toPublicKeyBundle import org.xmtp.android.library.messages.walletAddress import org.xmtp.proto.message.api.v1.MessageApiOuterClass +import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage import java.util.Date data class ConversationV1( @@ -56,21 +57,59 @@ data class ConversationV1( } } - fun decode(envelope: Envelope): DecodedMessage { - val message = Message.parseFrom(envelope.message) - val decrypted = message.v1.decrypt(client.privateKeyBundleV1) - val encodedMessage = EncodedContent.parseFrom(decrypted) - val header = message.v1.header - val decoded = DecodedMessage( - topic = envelope.contentTopic, - encodedContent = encodedMessage, - senderAddress = header.sender.walletAddress, - sent = message.v1.sentAt - ) + fun decryptedMessages( + limit: Int? = null, + before: Date? = null, + after: Date? = null, + direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING, + ): List { + val pagination = + Pagination(limit = limit, before = before, after = after, direction = direction) + + val envelopes = runBlocking { + client.apiClient.envelopes( + topic = Topic.directMessageV1(client.address, peerAddress).description, + pagination = pagination + ) + } - decoded.id = generateId(envelope) + return envelopes.map { decrypt(it) } + } - return decoded + fun decrypt(envelope: Envelope): DecryptedMessage { + try { + val message = Message.parseFrom(envelope.message) + val decrypted = message.v1.decrypt(client.privateKeyBundleV1) + + val encodedMessage = EncodedContent.parseFrom(decrypted) + val header = message.v1.header + + return DecryptedMessage( + id = generateId(envelope), + encodedContent = encodedMessage, + senderAddress = header.sender.walletAddress, + sentAt = message.v1.sentAt + ) + } catch (e: Exception) { + throw XMTPException("Error decrypting message", e) + } + } + + fun decode(envelope: Envelope): DecodedMessage { + try { + val decryptedMessage = decrypt(envelope) + + return DecodedMessage( + id = generateId(envelope), + client = client, + topic = envelope.contentTopic, + encodedContent = decryptedMessage.encodedContent, + senderAddress = decryptedMessage.senderAddress, + sent = decryptedMessage.sentAt + ) + } catch (e: Exception) { + throw XMTPException("Error decoding message", e) + } } private fun decodeOrNull(envelope: Envelope): DecodedMessage? { diff --git a/library/src/main/java/org/xmtp/android/library/ConversationV2.kt b/library/src/main/java/org/xmtp/android/library/ConversationV2.kt index 1e4388b36..13832a2a9 100644 --- a/library/src/main/java/org/xmtp/android/library/ConversationV2.kt +++ b/library/src/main/java/org/xmtp/android/library/ConversationV2.kt @@ -22,6 +22,7 @@ import org.xmtp.android.library.messages.getPublicKeyBundle import org.xmtp.android.library.messages.walletAddress import org.xmtp.proto.message.api.v1.MessageApiOuterClass import org.xmtp.proto.message.contents.Invitation +import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage import java.util.Date data class ConversationV2( @@ -77,6 +78,28 @@ data class ConversationV2( } } + fun decryptedMessages( + limit: Int? = null, + before: Date? = null, + after: Date? = null, + direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING, + ): List { + val pagination = + Pagination(limit = limit, before = before, after = after, direction = direction) + val envelopes = runBlocking { client.apiClient.envelopes(topic, pagination) } + + return envelopes.map { envelope -> + val message = Message.parseFrom(envelope.message) + MessageV2Builder.buildDecrypt( + id = generateId(envelope = envelope), + topic, + message.v2, + keyMaterial, + client + ) + } + } + fun streamMessages(): Flow = flow { client.subscribe(listOf(topic)).mapNotNull { decodeEnvelopeOrNull(envelope = it) }.collect { emit(it) @@ -85,9 +108,13 @@ data class ConversationV2( fun decodeEnvelope(envelope: Envelope): DecodedMessage { val message = Message.parseFrom(envelope.message) - val decoded = decode(message.v2) - decoded.id = generateId(envelope) - return decoded + return MessageV2Builder.buildDecode( + generateId(envelope = envelope), + topic = topic, + message.v2, + keyMaterial = keyMaterial, + client = client + ) } private fun decodeEnvelopeOrNull(envelope: Envelope): DecodedMessage? { @@ -99,9 +126,6 @@ data class ConversationV2( } } - fun decode(message: MessageV2): DecodedMessage = - MessageV2Builder.buildDecode(message, keyMaterial = keyMaterial, topic = topic) - fun send(content: T, options: SendOptions? = null): String { val preparedMessage = prepareMessage(content = content, options = options) return send(preparedMessage) diff --git a/library/src/main/java/org/xmtp/android/library/DecodedMessage.kt b/library/src/main/java/org/xmtp/android/library/DecodedMessage.kt index 6b5122ea6..eb80cd770 100644 --- a/library/src/main/java/org/xmtp/android/library/DecodedMessage.kt +++ b/library/src/main/java/org/xmtp/android/library/DecodedMessage.kt @@ -6,16 +6,18 @@ import org.xmtp.proto.message.contents.Content import java.util.Date data class DecodedMessage( + var id: String = "", + val client: Client, var topic: String, var encodedContent: Content.EncodedContent, var senderAddress: String, var sent: Date ) { - var id: String = "" companion object { - fun preview(topic: String, body: String, senderAddress: String, sent: Date): DecodedMessage { + fun preview(client: Client, topic: String, body: String, senderAddress: String, sent: Date): DecodedMessage { val encoded = TextCodec().encode(content = body) return DecodedMessage( + client = client, topic = topic, encodedContent = encoded, senderAddress = senderAddress, diff --git a/library/src/main/java/org/xmtp/android/library/messages/DecryptedMessage.kt b/library/src/main/java/org/xmtp/android/library/messages/DecryptedMessage.kt new file mode 100644 index 000000000..39cdacf80 --- /dev/null +++ b/library/src/main/java/org/xmtp/android/library/messages/DecryptedMessage.kt @@ -0,0 +1,12 @@ +package uniffi.xmtp_dh.org.xmtp.android.library.messages + +import org.xmtp.android.library.codecs.EncodedContent +import java.util.Date + +data class DecryptedMessage( + var id: String, + var encodedContent: EncodedContent, + var senderAddress: String, + var sentAt: Date, + var topic: String = "" +) \ No newline at end of file diff --git a/library/src/main/java/org/xmtp/android/library/messages/MessageV2.kt b/library/src/main/java/org/xmtp/android/library/messages/MessageV2.kt index 8cd528664..50185f05f 100644 --- a/library/src/main/java/org/xmtp/android/library/messages/MessageV2.kt +++ b/library/src/main/java/org/xmtp/android/library/messages/MessageV2.kt @@ -11,6 +11,7 @@ import org.xmtp.android.library.DecodedMessage import org.xmtp.android.library.KeyUtil import org.xmtp.android.library.XMTPException import org.xmtp.android.library.codecs.EncodedContent +import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage import java.math.BigInteger import java.util.Date @@ -25,7 +26,36 @@ class MessageV2Builder { }.build() } - fun buildDecode(message: MessageV2, keyMaterial: ByteArray, topic: String): DecodedMessage { + fun buildDecode( + id: String, + topic: String, + message: MessageV2, + keyMaterial: ByteArray, + client: Client, + ): DecodedMessage { + try { + val decryptedMessage = buildDecrypt(id, topic, message, keyMaterial, client) + + return DecodedMessage( + id = id, + client = client, + topic = decryptedMessage.topic, + encodedContent = decryptedMessage.encodedContent, + senderAddress = decryptedMessage.senderAddress, + sent = decryptedMessage.sentAt + ) + } catch (e: Exception) { + throw XMTPException("Error decoding message", e) + } + } + + fun buildDecrypt( + id: String, + topic: String, + message: MessageV2, + keyMaterial: ByteArray, + client: Client, + ): DecryptedMessage { val decrypted = Crypto.decrypt(keyMaterial, message.ciphertext, message.headerBytes.toByteArray()) val signed = SignedContent.parseFrom(decrypted) @@ -68,16 +98,19 @@ class MessageV2Builder { if (key.walletAddress != (PublicKeyBuilder.buildFromSignedPublicKey(signed.sender.preKey).walletAddress)) { throw XMTPException("Invalid signature") } + val encodedMessage = EncodedContent.parseFrom(signed.payload) val header = MessageHeaderV2.parseFrom(message.headerBytes) if (header.topic != topic) { throw XMTPException("Topic mismatch") } - return DecodedMessage( - topic = header.topic, + + return DecryptedMessage( + id = id, encodedContent = encodedMessage, senderAddress = signed.sender.walletAddress, - sent = Date(header.createdNs / 1_000_000) + sentAt = Date(header.createdNs / 1_000_000), + topic = topic ) }