Skip to content

Commit 18f5c84

Browse files
authored
Allow envelopes to be decrypted but not decoded (#137)
* add matching decryption code to swift * fix up the lint issues * fix up the tests
1 parent c260ec3 commit 18f5c84

File tree

7 files changed

+151
-27
lines changed

7 files changed

+151
-27
lines changed

library/src/androidTest/java/org/xmtp/android/library/MessageTest.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ class MessageTest {
9494
keyMaterial = invitationv1.aes256GcmHkdfSha256.keyMaterial.toByteArray()
9595
)
9696
val decoded = MessageV2Builder.buildDecode(
97-
message1,
97+
id = "",
98+
client = client,
99+
message = message1,
98100
keyMaterial = invitationv1.aes256GcmHkdfSha256.keyMaterial.toByteArray(),
99101
topic = invitationv1.topic
100102
)

library/src/main/java/org/xmtp/android/library/Conversation.kt

+13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.xmtp.proto.keystore.api.v1.Keystore.TopicMap.TopicData
1010
import org.xmtp.proto.message.api.v1.MessageApiOuterClass
1111
import org.xmtp.proto.message.contents.Invitation
1212
import org.xmtp.proto.message.contents.Invitation.InvitationV1.Aes256gcmHkdfsha256
13+
import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage
1314
import java.util.Date
1415

1516
sealed class Conversation {
@@ -180,6 +181,18 @@ sealed class Conversation {
180181
}
181182
}
182183

184+
fun decryptedMessages(
185+
limit: Int? = null,
186+
before: Date? = null,
187+
after: Date? = null,
188+
direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING,
189+
): List<DecryptedMessage> {
190+
return when (this) {
191+
is V1 -> conversationV1.decryptedMessages(limit, before, after, direction)
192+
is V2 -> conversationV2.decryptedMessages(limit, before, after, direction)
193+
}
194+
}
195+
183196
val client: Client
184197
get() {
185198
return when (this) {

library/src/main/java/org/xmtp/android/library/ConversationV1.kt

+52-13
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.xmtp.android.library.messages.sentAt
2222
import org.xmtp.android.library.messages.toPublicKeyBundle
2323
import org.xmtp.android.library.messages.walletAddress
2424
import org.xmtp.proto.message.api.v1.MessageApiOuterClass
25+
import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage
2526
import java.util.Date
2627

2728
data class ConversationV1(
@@ -56,21 +57,59 @@ data class ConversationV1(
5657
}
5758
}
5859

59-
fun decode(envelope: Envelope): DecodedMessage {
60-
val message = Message.parseFrom(envelope.message)
61-
val decrypted = message.v1.decrypt(client.privateKeyBundleV1)
62-
val encodedMessage = EncodedContent.parseFrom(decrypted)
63-
val header = message.v1.header
64-
val decoded = DecodedMessage(
65-
topic = envelope.contentTopic,
66-
encodedContent = encodedMessage,
67-
senderAddress = header.sender.walletAddress,
68-
sent = message.v1.sentAt
69-
)
60+
fun decryptedMessages(
61+
limit: Int? = null,
62+
before: Date? = null,
63+
after: Date? = null,
64+
direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING,
65+
): List<DecryptedMessage> {
66+
val pagination =
67+
Pagination(limit = limit, before = before, after = after, direction = direction)
68+
69+
val envelopes = runBlocking {
70+
client.apiClient.envelopes(
71+
topic = Topic.directMessageV1(client.address, peerAddress).description,
72+
pagination = pagination
73+
)
74+
}
7075

71-
decoded.id = generateId(envelope)
76+
return envelopes.map { decrypt(it) }
77+
}
7278

73-
return decoded
79+
fun decrypt(envelope: Envelope): DecryptedMessage {
80+
try {
81+
val message = Message.parseFrom(envelope.message)
82+
val decrypted = message.v1.decrypt(client.privateKeyBundleV1)
83+
84+
val encodedMessage = EncodedContent.parseFrom(decrypted)
85+
val header = message.v1.header
86+
87+
return DecryptedMessage(
88+
id = generateId(envelope),
89+
encodedContent = encodedMessage,
90+
senderAddress = header.sender.walletAddress,
91+
sentAt = message.v1.sentAt
92+
)
93+
} catch (e: Exception) {
94+
throw XMTPException("Error decrypting message", e)
95+
}
96+
}
97+
98+
fun decode(envelope: Envelope): DecodedMessage {
99+
try {
100+
val decryptedMessage = decrypt(envelope)
101+
102+
return DecodedMessage(
103+
id = generateId(envelope),
104+
client = client,
105+
topic = envelope.contentTopic,
106+
encodedContent = decryptedMessage.encodedContent,
107+
senderAddress = decryptedMessage.senderAddress,
108+
sent = decryptedMessage.sentAt
109+
)
110+
} catch (e: Exception) {
111+
throw XMTPException("Error decoding message", e)
112+
}
74113
}
75114

76115
private fun decodeOrNull(envelope: Envelope): DecodedMessage? {

library/src/main/java/org/xmtp/android/library/ConversationV2.kt

+30-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import org.xmtp.android.library.messages.Envelope
1313
import org.xmtp.android.library.messages.EnvelopeBuilder
1414
import org.xmtp.android.library.messages.Message
1515
import org.xmtp.android.library.messages.MessageBuilder
16-
import org.xmtp.android.library.messages.MessageV2
1716
import org.xmtp.android.library.messages.MessageV2Builder
1817
import org.xmtp.android.library.messages.Pagination
1918
import org.xmtp.android.library.messages.PagingInfoSortDirection
@@ -22,6 +21,7 @@ import org.xmtp.android.library.messages.getPublicKeyBundle
2221
import org.xmtp.android.library.messages.walletAddress
2322
import org.xmtp.proto.message.api.v1.MessageApiOuterClass
2423
import org.xmtp.proto.message.contents.Invitation
24+
import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage
2525
import java.util.Date
2626

2727
data class ConversationV2(
@@ -77,6 +77,28 @@ data class ConversationV2(
7777
}
7878
}
7979

80+
fun decryptedMessages(
81+
limit: Int? = null,
82+
before: Date? = null,
83+
after: Date? = null,
84+
direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING,
85+
): List<DecryptedMessage> {
86+
val pagination =
87+
Pagination(limit = limit, before = before, after = after, direction = direction)
88+
val envelopes = runBlocking { client.apiClient.envelopes(topic, pagination) }
89+
90+
return envelopes.map { envelope ->
91+
val message = Message.parseFrom(envelope.message)
92+
MessageV2Builder.buildDecrypt(
93+
id = generateId(envelope = envelope),
94+
topic,
95+
message.v2,
96+
keyMaterial,
97+
client
98+
)
99+
}
100+
}
101+
80102
fun streamMessages(): Flow<DecodedMessage> = flow {
81103
client.subscribe(listOf(topic)).mapNotNull { decodeEnvelopeOrNull(envelope = it) }.collect {
82104
emit(it)
@@ -85,9 +107,13 @@ data class ConversationV2(
85107

86108
fun decodeEnvelope(envelope: Envelope): DecodedMessage {
87109
val message = Message.parseFrom(envelope.message)
88-
val decoded = decode(message.v2)
89-
decoded.id = generateId(envelope)
90-
return decoded
110+
return MessageV2Builder.buildDecode(
111+
generateId(envelope = envelope),
112+
topic = topic,
113+
message.v2,
114+
keyMaterial = keyMaterial,
115+
client = client
116+
)
91117
}
92118

93119
private fun decodeEnvelopeOrNull(envelope: Envelope): DecodedMessage? {
@@ -99,9 +125,6 @@ data class ConversationV2(
99125
}
100126
}
101127

102-
fun decode(message: MessageV2): DecodedMessage =
103-
MessageV2Builder.buildDecode(message, keyMaterial = keyMaterial, topic = topic)
104-
105128
fun <T> send(content: T, options: SendOptions? = null): String {
106129
val preparedMessage = prepareMessage(content = content, options = options)
107130
return send(preparedMessage)

library/src/main/java/org/xmtp/android/library/DecodedMessage.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,18 @@ import org.xmtp.proto.message.contents.Content
66
import java.util.Date
77

88
data class DecodedMessage(
9+
var id: String = "",
10+
val client: Client,
911
var topic: String,
1012
var encodedContent: Content.EncodedContent,
1113
var senderAddress: String,
1214
var sent: Date
1315
) {
14-
var id: String = ""
1516
companion object {
16-
fun preview(topic: String, body: String, senderAddress: String, sent: Date): DecodedMessage {
17+
fun preview(client: Client, topic: String, body: String, senderAddress: String, sent: Date): DecodedMessage {
1718
val encoded = TextCodec().encode(content = body)
1819
return DecodedMessage(
20+
client = client,
1921
topic = topic,
2022
encodedContent = encoded,
2123
senderAddress = senderAddress,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package uniffi.xmtp_dh.org.xmtp.android.library.messages
2+
3+
import org.xmtp.android.library.codecs.EncodedContent
4+
import java.util.Date
5+
6+
data class DecryptedMessage(
7+
var id: String,
8+
var encodedContent: EncodedContent,
9+
var senderAddress: String,
10+
var sentAt: Date,
11+
var topic: String = ""
12+
)

library/src/main/java/org/xmtp/android/library/messages/MessageV2.kt

+37-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.xmtp.android.library.DecodedMessage
1111
import org.xmtp.android.library.KeyUtil
1212
import org.xmtp.android.library.XMTPException
1313
import org.xmtp.android.library.codecs.EncodedContent
14+
import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage
1415
import java.math.BigInteger
1516
import java.util.Date
1617

@@ -25,7 +26,36 @@ class MessageV2Builder {
2526
}.build()
2627
}
2728

28-
fun buildDecode(message: MessageV2, keyMaterial: ByteArray, topic: String): DecodedMessage {
29+
fun buildDecode(
30+
id: String,
31+
topic: String,
32+
message: MessageV2,
33+
keyMaterial: ByteArray,
34+
client: Client,
35+
): DecodedMessage {
36+
try {
37+
val decryptedMessage = buildDecrypt(id, topic, message, keyMaterial, client)
38+
39+
return DecodedMessage(
40+
id = id,
41+
client = client,
42+
topic = decryptedMessage.topic,
43+
encodedContent = decryptedMessage.encodedContent,
44+
senderAddress = decryptedMessage.senderAddress,
45+
sent = decryptedMessage.sentAt
46+
)
47+
} catch (e: Exception) {
48+
throw XMTPException("Error decoding message", e)
49+
}
50+
}
51+
52+
fun buildDecrypt(
53+
id: String,
54+
topic: String,
55+
message: MessageV2,
56+
keyMaterial: ByteArray,
57+
client: Client,
58+
): DecryptedMessage {
2959
val decrypted =
3060
Crypto.decrypt(keyMaterial, message.ciphertext, message.headerBytes.toByteArray())
3161
val signed = SignedContent.parseFrom(decrypted)
@@ -68,16 +98,19 @@ class MessageV2Builder {
6898
if (key.walletAddress != (PublicKeyBuilder.buildFromSignedPublicKey(signed.sender.preKey).walletAddress)) {
6999
throw XMTPException("Invalid signature")
70100
}
101+
71102
val encodedMessage = EncodedContent.parseFrom(signed.payload)
72103
val header = MessageHeaderV2.parseFrom(message.headerBytes)
73104
if (header.topic != topic) {
74105
throw XMTPException("Topic mismatch")
75106
}
76-
return DecodedMessage(
77-
topic = header.topic,
107+
108+
return DecryptedMessage(
109+
id = id,
78110
encodedContent = encodedMessage,
79111
senderAddress = signed.sender.walletAddress,
80-
sent = Date(header.createdNs / 1_000_000)
112+
sentAt = Date(header.createdNs / 1_000_000),
113+
topic = topic
81114
)
82115
}
83116

0 commit comments

Comments
 (0)