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

Allow envelopes to be decrypted but not decoded #137

Merged
merged 4 commits into from
Nov 15, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ class MessageTest {
keyMaterial = invitationv1.aes256GcmHkdfSha256.keyMaterial.toByteArray()
)
val decoded = MessageV2Builder.buildDecode(
message1,
id = "",
client = client,
message = message1,
keyMaterial = invitationv1.aes256GcmHkdfSha256.keyMaterial.toByteArray(),
topic = invitationv1.topic
)
Expand Down
13 changes: 13 additions & 0 deletions library/src/main/java/org/xmtp/android/library/Conversation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<DecryptedMessage> {
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) {
Expand Down
65 changes: 52 additions & 13 deletions library/src/main/java/org/xmtp/android/library/ConversationV1.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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<DecryptedMessage> {
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? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import org.xmtp.android.library.messages.Envelope
import org.xmtp.android.library.messages.EnvelopeBuilder
import org.xmtp.android.library.messages.Message
import org.xmtp.android.library.messages.MessageBuilder
import org.xmtp.android.library.messages.MessageV2
import org.xmtp.android.library.messages.MessageV2Builder
import org.xmtp.android.library.messages.Pagination
import org.xmtp.android.library.messages.PagingInfoSortDirection
Expand All @@ -22,6 +21,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(
Expand Down Expand Up @@ -77,6 +77,28 @@ data class ConversationV2(
}
}

fun decryptedMessages(
limit: Int? = null,
before: Date? = null,
after: Date? = null,
direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING,
): List<DecryptedMessage> {
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<DecodedMessage> = flow {
client.subscribe(listOf(topic)).mapNotNull { decodeEnvelopeOrNull(envelope = it) }.collect {
emit(it)
Expand All @@ -85,9 +107,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? {
Expand All @@ -99,9 +125,6 @@ data class ConversationV2(
}
}

fun decode(message: MessageV2): DecodedMessage =
MessageV2Builder.buildDecode(message, keyMaterial = keyMaterial, topic = topic)

fun <T> send(content: T, options: SendOptions? = null): String {
val preparedMessage = prepareMessage(content = content, options = options)
return send(preparedMessage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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
)
}

Expand Down
Loading