mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-26 08:40:45 +00:00
Clean
This commit is contained in:
parent
ea71d285b7
commit
a2c886468d
@ -191,7 +191,7 @@ object ClosedGroupsProtocolV2 {
|
|||||||
}
|
}
|
||||||
if (userPublicKey in admins) {
|
if (userPublicKey in admins) {
|
||||||
// send current encryption key to the latest added members
|
// send current encryption key to the latest added members
|
||||||
val encryptionKeyPair = pendingKeyPair[groupPublicKey]?.orNull()
|
val encryptionKeyPair = pendingKeyPairs[groupPublicKey]?.orNull()
|
||||||
?: apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)
|
?: apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)
|
||||||
if (encryptionKeyPair == null) {
|
if (encryptionKeyPair == null) {
|
||||||
Log.d("Loki", "Couldn't get encryption key pair for closed group.")
|
Log.d("Loki", "Couldn't get encryption key pair for closed group.")
|
||||||
|
@ -15,6 +15,14 @@ object MessageDecrypter {
|
|||||||
|
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts `ciphertext` using the Session protocol and `x25519KeyPair`.
|
||||||
|
*
|
||||||
|
* @param ciphertext the data to decrypt.
|
||||||
|
* @param x25519KeyPair the key pair to use for decryption. This could be the current user's key pair, or the key pair of a closed group.
|
||||||
|
*
|
||||||
|
* @return the padded plaintext.
|
||||||
|
*/
|
||||||
public fun decrypt(ciphertext: ByteArray, x25519KeyPair: ECKeyPair): Pair<ByteArray, String> {
|
public fun decrypt(ciphertext: ByteArray, x25519KeyPair: ECKeyPair): Pair<ByteArray, String> {
|
||||||
val recipientX25519PrivateKey = x25519KeyPair.privateKey.serialize()
|
val recipientX25519PrivateKey = x25519KeyPair.privateKey.serialize()
|
||||||
val recipientX25519PublicKey = Hex.fromStringCondensed(x25519KeyPair.hexEncodedPublicKey.removing05PrefixIfNeeded())
|
val recipientX25519PublicKey = Hex.fromStringCondensed(x25519KeyPair.hexEncodedPublicKey.removing05PrefixIfNeeded())
|
||||||
|
@ -13,7 +13,7 @@ import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
|||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
|
|
||||||
object MessageSenderEncryption {
|
object MessageEncrypter {
|
||||||
|
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ object MessageSenderEncryption {
|
|||||||
*
|
*
|
||||||
* @return the encrypted message.
|
* @return the encrypted message.
|
||||||
*/
|
*/
|
||||||
internal fun encryptWithSessionProtocol(plaintext: ByteArray, recipientHexEncodedX25519PublicKey: String): ByteArray{
|
internal fun encrypt(plaintext: ByteArray, recipientHexEncodedX25519PublicKey: String): ByteArray{
|
||||||
val context = MessagingModuleConfiguration.shared.context
|
val context = MessagingModuleConfiguration.shared.context
|
||||||
val userED25519KeyPair = KeyPairUtilities.getUserED25519KeyPair(context) ?: throw Error.NoUserED25519KeyPair
|
val userED25519KeyPair = KeyPairUtilities.getUserED25519KeyPair(context) ?: throw Error.NoUserED25519KeyPair
|
||||||
val recipientX25519PublicKey = Hex.fromStringCondensed(recipientHexEncodedX25519PublicKey.removing05PrefixIfNeeded())
|
val recipientX25519PublicKey = Hex.fromStringCondensed(recipientHexEncodedX25519PublicKey.removing05PrefixIfNeeded())
|
@ -21,7 +21,6 @@ object MessageReceiver {
|
|||||||
object SenderBlocked: Error("Received a message from a blocked user.")
|
object SenderBlocked: Error("Received a message from a blocked user.")
|
||||||
object NoThread: Error("Couldn't find thread for message.")
|
object NoThread: Error("Couldn't find thread for message.")
|
||||||
object SelfSend: Error("Message addressed at self.")
|
object SelfSend: Error("Message addressed at self.")
|
||||||
// Shared sender keys
|
|
||||||
object InvalidGroupPublicKey: Error("Invalid group public key.")
|
object InvalidGroupPublicKey: Error("Invalid group public key.")
|
||||||
object NoGroupKeyPair: Error("Missing group key pair.")
|
object NoGroupKeyPair: Error("Missing group key pair.")
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
|||||||
import org.session.libsession.messaging.messages.visible.*
|
import org.session.libsession.messaging.messages.visible.*
|
||||||
import org.session.libsession.messaging.open_groups.*
|
import org.session.libsession.messaging.open_groups.*
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
|
||||||
import org.session.libsession.messaging.utilities.MessageWrapper
|
import org.session.libsession.messaging.utilities.MessageWrapper
|
||||||
import org.session.libsession.snode.RawResponsePromise
|
import org.session.libsession.snode.RawResponsePromise
|
||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
@ -27,6 +26,7 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
||||||
import org.session.libsignal.utilities.Base64
|
import org.session.libsignal.utilities.Base64
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
|
import java.lang.IllegalStateException
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment
|
||||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview as SignalLinkPreview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview as SignalLinkPreview
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as SignalQuote
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as SignalQuote
|
||||||
@ -37,8 +37,6 @@ object MessageSender {
|
|||||||
sealed class Error(val description: String) : Exception(description) {
|
sealed class Error(val description: String) : Exception(description) {
|
||||||
object InvalidMessage : Error("Invalid message.")
|
object InvalidMessage : Error("Invalid message.")
|
||||||
object ProtoConversionFailed : Error("Couldn't convert message to proto.")
|
object ProtoConversionFailed : Error("Couldn't convert message to proto.")
|
||||||
object ProofOfWorkCalculationFailed : Error("Proof of work calculation failed.")
|
|
||||||
object NoUserX25519KeyPair : Error("Couldn't find user X25519 key pair.")
|
|
||||||
object NoUserED25519KeyPair : Error("Couldn't find user ED25519 key pair.")
|
object NoUserED25519KeyPair : Error("Couldn't find user ED25519 key pair.")
|
||||||
object SigningFailed : Error("Couldn't sign message.")
|
object SigningFailed : Error("Couldn't sign message.")
|
||||||
object EncryptionFailed : Error("Couldn't encrypt message.")
|
object EncryptionFailed : Error("Couldn't encrypt message.")
|
||||||
@ -46,17 +44,10 @@ object MessageSender {
|
|||||||
// Closed groups
|
// Closed groups
|
||||||
object NoThread : Error("Couldn't find a thread associated with the given group public key.")
|
object NoThread : Error("Couldn't find a thread associated with the given group public key.")
|
||||||
object NoKeyPair: Error("Couldn't find a private key associated with the given group public key.")
|
object NoKeyPair: Error("Couldn't find a private key associated with the given group public key.")
|
||||||
object NoPrivateKey : Error("Couldn't find a private key associated with the given group public key.")
|
|
||||||
object InvalidClosedGroupUpdate : Error("Invalid group update.")
|
object InvalidClosedGroupUpdate : Error("Invalid group update.")
|
||||||
|
|
||||||
// Precondition
|
|
||||||
class PreconditionFailure(val reason: String): Error(reason)
|
|
||||||
|
|
||||||
internal val isRetryable: Boolean = when (this) {
|
internal val isRetryable: Boolean = when (this) {
|
||||||
is InvalidMessage -> false
|
is InvalidMessage, ProtoConversionFailed, InvalidClosedGroupUpdate -> false
|
||||||
is ProtoConversionFailed -> false
|
|
||||||
is ProofOfWorkCalculationFailed -> false
|
|
||||||
is InvalidClosedGroupUpdate -> false
|
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,7 +67,9 @@ object MessageSender {
|
|||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val userPublicKey = storage.getUserPublicKey()
|
val userPublicKey = storage.getUserPublicKey()
|
||||||
// Set the timestamp, sender and recipient
|
// Set the timestamp, sender and recipient
|
||||||
message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() } /* Visible messages will already have their sent timestamp set */
|
if (message.sentTimestamp == null) {
|
||||||
|
message.sentTimestamp = System.currentTimeMillis() // Visible messages will already have their sent timestamp set
|
||||||
|
}
|
||||||
message.sender = userPublicKey
|
message.sender = userPublicKey
|
||||||
val isSelfSend = (message.recipient == userPublicKey)
|
val isSelfSend = (message.recipient == userPublicKey)
|
||||||
// Set the failure handler (need it here already for precondition failure handling)
|
// Set the failure handler (need it here already for precondition failure handling)
|
||||||
@ -91,8 +84,7 @@ object MessageSender {
|
|||||||
when (destination) {
|
when (destination) {
|
||||||
is Destination.Contact -> message.recipient = destination.publicKey
|
is Destination.Contact -> message.recipient = destination.publicKey
|
||||||
is Destination.ClosedGroup -> message.recipient = destination.groupPublicKey
|
is Destination.ClosedGroup -> message.recipient = destination.groupPublicKey
|
||||||
is Destination.OpenGroup,
|
is Destination.OpenGroup, is Destination.OpenGroupV2 -> throw IllegalStateException("Destination should not be an open group.")
|
||||||
is Destination.OpenGroupV2 -> throw Error.PreconditionFailure("Destination should not be open groups!")
|
|
||||||
}
|
}
|
||||||
// Validate the message
|
// Validate the message
|
||||||
if (!message.isValid()) { throw Error.InvalidMessage }
|
if (!message.isValid()) { throw Error.InvalidMessage }
|
||||||
@ -125,13 +117,12 @@ object MessageSender {
|
|||||||
// Encrypt the serialized protobuf
|
// Encrypt the serialized protobuf
|
||||||
val ciphertext: ByteArray
|
val ciphertext: ByteArray
|
||||||
when (destination) {
|
when (destination) {
|
||||||
is Destination.Contact -> ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, destination.publicKey)
|
is Destination.Contact -> ciphertext = MessageEncrypter.encrypt(plaintext, destination.publicKey)
|
||||||
is Destination.ClosedGroup -> {
|
is Destination.ClosedGroup -> {
|
||||||
val encryptionKeyPair = MessagingModuleConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair(destination.groupPublicKey)!!
|
val encryptionKeyPair = MessagingModuleConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair(destination.groupPublicKey)!!
|
||||||
ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, encryptionKeyPair.hexEncodedPublicKey)
|
ciphertext = MessageEncrypter.encrypt(plaintext, encryptionKeyPair.hexEncodedPublicKey)
|
||||||
}
|
}
|
||||||
is Destination.OpenGroup,
|
is Destination.OpenGroup, is Destination.OpenGroupV2 -> throw IllegalStateException("Destination should not be open group.")
|
||||||
is Destination.OpenGroupV2 -> throw Error.PreconditionFailure("Destination should not be open groups!")
|
|
||||||
}
|
}
|
||||||
// Wrap the result
|
// Wrap the result
|
||||||
val kind: SignalServiceProtos.Envelope.Type
|
val kind: SignalServiceProtos.Envelope.Type
|
||||||
@ -145,8 +136,7 @@ object MessageSender {
|
|||||||
kind = SignalServiceProtos.Envelope.Type.CLOSED_GROUP_MESSAGE
|
kind = SignalServiceProtos.Envelope.Type.CLOSED_GROUP_MESSAGE
|
||||||
senderPublicKey = destination.groupPublicKey
|
senderPublicKey = destination.groupPublicKey
|
||||||
}
|
}
|
||||||
is Destination.OpenGroup,
|
is Destination.OpenGroup, is Destination.OpenGroupV2 -> throw IllegalStateException("Destination should not be open group.")
|
||||||
is Destination.OpenGroupV2 -> throw Error.PreconditionFailure("Destination should not be open groups!")
|
|
||||||
}
|
}
|
||||||
val wrappedMessage = MessageWrapper.wrap(kind, message.sentTimestamp!!, senderPublicKey, ciphertext)
|
val wrappedMessage = MessageWrapper.wrap(kind, message.sentTimestamp!!, senderPublicKey, ciphertext)
|
||||||
// Send the result
|
// Send the result
|
||||||
@ -201,7 +191,9 @@ object MessageSender {
|
|||||||
private fun sendToOpenGroupDestination(destination: Destination, message: Message): Promise<Unit, Exception> {
|
private fun sendToOpenGroupDestination(destination: Destination, message: Message): Promise<Unit, Exception> {
|
||||||
val deferred = deferred<Unit, Exception>()
|
val deferred = deferred<Unit, Exception>()
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() }
|
if (message.sentTimestamp == null) {
|
||||||
|
message.sentTimestamp = System.currentTimeMillis()
|
||||||
|
}
|
||||||
message.sender = storage.getUserPublicKey()
|
message.sender = storage.getUserPublicKey()
|
||||||
// Set the failure handler (need it here already for precondition failure handling)
|
// Set the failure handler (need it here already for precondition failure handling)
|
||||||
fun handleFailure(error: Exception) {
|
fun handleFailure(error: Exception) {
|
||||||
@ -210,18 +202,15 @@ object MessageSender {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
when (destination) {
|
when (destination) {
|
||||||
is Destination.Contact -> throw Error.PreconditionFailure("Destination should not be contacts!")
|
is Destination.Contact, is Destination.ClosedGroup -> throw IllegalStateException("Invalid destination.")
|
||||||
is Destination.ClosedGroup -> throw Error.PreconditionFailure("Destination should not be closed groups!")
|
|
||||||
is Destination.OpenGroup -> {
|
is Destination.OpenGroup -> {
|
||||||
message.recipient = "${destination.server}.${destination.channel}"
|
message.recipient = "${destination.server}.${destination.channel}"
|
||||||
val server = destination.server
|
val server = destination.server
|
||||||
val channel = destination.channel
|
val channel = destination.channel
|
||||||
|
|
||||||
// Validate the message
|
// Validate the message
|
||||||
if (message !is VisibleMessage || !message.isValid()) {
|
if (message !is VisibleMessage || !message.isValid()) {
|
||||||
throw Error.InvalidMessage
|
throw Error.InvalidMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the message to an open group message
|
// Convert the message to an open group message
|
||||||
val openGroupMessage = OpenGroupMessage.from(message, server) ?: run {
|
val openGroupMessage = OpenGroupMessage.from(message, server) ?: run {
|
||||||
throw Error.InvalidMessage
|
throw Error.InvalidMessage
|
||||||
@ -239,7 +228,6 @@ object MessageSender {
|
|||||||
message.recipient = "${destination.server}.${destination.room}"
|
message.recipient = "${destination.server}.${destination.room}"
|
||||||
val server = destination.server
|
val server = destination.server
|
||||||
val room = destination.room
|
val room = destination.room
|
||||||
|
|
||||||
// Attach the user's profile if needed
|
// Attach the user's profile if needed
|
||||||
if (message is VisibleMessage) {
|
if (message is VisibleMessage) {
|
||||||
val displayName = storage.getUserDisplayName()!!
|
val displayName = storage.getUserDisplayName()!!
|
||||||
@ -251,12 +239,10 @@ object MessageSender {
|
|||||||
message.profile = Profile(displayName)
|
message.profile = Profile(displayName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the message
|
// Validate the message
|
||||||
if (message !is VisibleMessage || !message.isValid()) {
|
if (message !is VisibleMessage || !message.isValid()) {
|
||||||
throw Error.InvalidMessage
|
throw Error.InvalidMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
val proto = message.toProto()!!
|
val proto = message.toProto()!!
|
||||||
val plaintext = PushTransportDetails.getPaddedMessageBody(proto.toByteArray())
|
val plaintext = PushTransportDetails.getPaddedMessageBody(proto.toByteArray())
|
||||||
val openGroupMessage = OpenGroupMessageV2(
|
val openGroupMessage = OpenGroupMessageV2(
|
||||||
@ -264,7 +250,6 @@ object MessageSender {
|
|||||||
sentTimestamp = message.sentTimestamp!!,
|
sentTimestamp = message.sentTimestamp!!,
|
||||||
base64EncodedData = Base64.encodeBytes(plaintext),
|
base64EncodedData = Base64.encodeBytes(plaintext),
|
||||||
)
|
)
|
||||||
|
|
||||||
OpenGroupAPIV2.send(openGroupMessage,room,server).success {
|
OpenGroupAPIV2.send(openGroupMessage,room,server).success {
|
||||||
message.openGroupServerMessageID = it.serverID
|
message.openGroupServerMessageID = it.serverID
|
||||||
handleSuccessfulMessageSend(message, destination)
|
handleSuccessfulMessageSend(message, destination)
|
||||||
@ -272,7 +257,6 @@ object MessageSender {
|
|||||||
}.fail {
|
}.fail {
|
||||||
handleFailure(it)
|
handleFailure(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
@ -285,7 +269,7 @@ object MessageSender {
|
|||||||
fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false) {
|
fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false) {
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val userPublicKey = storage.getUserPublicKey()!!
|
val userPublicKey = storage.getUserPublicKey()!!
|
||||||
val messageId = storage.getMessageIdInDatabase(message.sentTimestamp!!, message.sender?:userPublicKey) ?: return
|
val messageID = storage.getMessageIdInDatabase(message.sentTimestamp!!, message.sender?:userPublicKey) ?: return
|
||||||
// Ignore future self-sends
|
// Ignore future self-sends
|
||||||
storage.addReceivedMessageTimestamp(message.sentTimestamp!!)
|
storage.addReceivedMessageTimestamp(message.sentTimestamp!!)
|
||||||
// Track the open group server message ID
|
// Track the open group server message ID
|
||||||
@ -293,7 +277,7 @@ object MessageSender {
|
|||||||
val encoded = GroupUtil.getEncodedOpenGroupID("${destination.server}.${destination.room}".toByteArray())
|
val encoded = GroupUtil.getEncodedOpenGroupID("${destination.server}.${destination.room}".toByteArray())
|
||||||
val threadID = storage.getThreadIdFor(Address.fromSerialized(encoded))
|
val threadID = storage.getThreadIdFor(Address.fromSerialized(encoded))
|
||||||
if (threadID != null && threadID >= 0) {
|
if (threadID != null && threadID >= 0) {
|
||||||
storage.setOpenGroupServerMessageID(messageId, message.openGroupServerMessageID!!, threadID, !(message as VisibleMessage).isMediaMessage())
|
storage.setOpenGroupServerMessageID(messageID, message.openGroupServerMessageID!!, threadID, !(message as VisibleMessage).isMediaMessage())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Mark the message as sent
|
// Mark the message as sent
|
||||||
@ -323,16 +307,16 @@ object MessageSender {
|
|||||||
// Convenience
|
// Convenience
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun send(message: VisibleMessage, address: Address, attachments: List<SignalAttachment>, quote: SignalQuote?, linkPreview: SignalLinkPreview?) {
|
fun send(message: VisibleMessage, address: Address, attachments: List<SignalAttachment>, quote: SignalQuote?, linkPreview: SignalLinkPreview?) {
|
||||||
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
||||||
val attachmentIDs = dataProvider.getAttachmentIDsFor(message.id!!)
|
val attachmentIDs = messageDataProvider.getAttachmentIDsFor(message.id!!)
|
||||||
message.attachmentIDs.addAll(attachmentIDs)
|
message.attachmentIDs.addAll(attachmentIDs)
|
||||||
message.quote = Quote.from(quote)
|
message.quote = Quote.from(quote)
|
||||||
message.linkPreview = LinkPreview.from(linkPreview)
|
message.linkPreview = LinkPreview.from(linkPreview)
|
||||||
message.linkPreview?.let {
|
message.linkPreview?.let { linkPreview ->
|
||||||
if (it.attachmentID == null) {
|
if (linkPreview.attachmentID == null) {
|
||||||
dataProvider.getLinkPreviewAttachmentIDFor(message.id!!)?.let {
|
messageDataProvider.getLinkPreviewAttachmentIDFor(message.id!!)?.let { attachmentID ->
|
||||||
message.linkPreview!!.attachmentID = it
|
message.linkPreview!!.attachmentID = attachmentID
|
||||||
message.attachmentIDs.remove(it)
|
message.attachmentIDs.remove(attachmentID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ import java.util.*
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
const val groupSizeLimit = 100
|
const val groupSizeLimit = 100
|
||||||
val pendingKeyPair = ConcurrentHashMap<String, Optional<ECKeyPair>>()
|
|
||||||
|
val pendingKeyPairs = ConcurrentHashMap<String, Optional<ECKeyPair>>()
|
||||||
|
|
||||||
fun MessageSender.create(name: String, members: Collection<String>): Promise<String, Exception> {
|
fun MessageSender.create(name: String, members: Collection<String>): Promise<String, Exception> {
|
||||||
val deferred = deferred<String, Exception>()
|
val deferred = deferred<String, Exception>()
|
||||||
@ -179,13 +180,11 @@ fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List<St
|
|||||||
Log.d("Loki", "Can't remove admin from closed group unless the group is destroyed entirely.")
|
Log.d("Loki", "Can't remove admin from closed group unless the group is destroyed entirely.")
|
||||||
throw Error.InvalidClosedGroupUpdate
|
throw Error.InvalidClosedGroupUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the new group members
|
// Save the new group members
|
||||||
storage.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) })
|
storage.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) })
|
||||||
// Update the zombie list
|
// Update the zombie list
|
||||||
val oldZombies = storage.getZombieMember(groupID)
|
val oldZombies = storage.getZombieMember(groupID)
|
||||||
storage.updateZombieMembers(groupID, oldZombies.minus(membersToRemove).map { Address.fromSerialized(it) })
|
storage.updateZombieMembers(groupID, oldZombies.minus(membersToRemove).map { Address.fromSerialized(it) })
|
||||||
|
|
||||||
val removeMembersAsData = membersToRemove.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) }
|
val removeMembersAsData = membersToRemove.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) }
|
||||||
val name = group.title
|
val name = group.title
|
||||||
// Send the update to the group
|
// Send the update to the group
|
||||||
@ -194,17 +193,14 @@ fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List<St
|
|||||||
val closedGroupControlMessage = ClosedGroupControlMessage(memberUpdateKind)
|
val closedGroupControlMessage = ClosedGroupControlMessage(memberUpdateKind)
|
||||||
closedGroupControlMessage.sentTimestamp = sentTime
|
closedGroupControlMessage.sentTimestamp = sentTime
|
||||||
send(closedGroupControlMessage, Address.fromSerialized(groupID))
|
send(closedGroupControlMessage, Address.fromSerialized(groupID))
|
||||||
|
// Send the new encryption key pair to the remaining group members.
|
||||||
// Send the new encryption key pair to the remaining group members
|
// At this stage we know the user is admin, no need to test.
|
||||||
// At this stage we know the user is admin, no need to test
|
|
||||||
generateAndSendNewEncryptionKeyPair(groupPublicKey, updatedMembers)
|
generateAndSendNewEncryptionKeyPair(groupPublicKey, updatedMembers)
|
||||||
// Notify the user
|
// Notify the user
|
||||||
|
// We don't display zombie members in the notification as users have already been notified when those members left
|
||||||
// Insert an outgoing notification
|
|
||||||
// we don't display zombie members in the notification as users have already been notified when those members left
|
|
||||||
val notificationMembers = membersToRemove.minus(oldZombies)
|
val notificationMembers = membersToRemove.minus(oldZombies)
|
||||||
if (notificationMembers.isNotEmpty()) {
|
if (notificationMembers.isNotEmpty()) {
|
||||||
// no notification to display when only zombies have been removed
|
// No notification to display when only zombies have been removed
|
||||||
val infoType = SignalServiceGroup.Type.MEMBER_REMOVED
|
val infoType = SignalServiceGroup.Type.MEMBER_REMOVED
|
||||||
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
|
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
|
||||||
storage.insertOutgoingInfoMessage(context, groupID, infoType, name, notificationMembers, admins, threadID, sentTime)
|
storage.insertOutgoingInfoMessage(context, groupID, infoType, name, notificationMembers, admins, threadID, sentTime)
|
||||||
@ -259,16 +255,16 @@ fun MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey: String, ta
|
|||||||
}
|
}
|
||||||
// Generate the new encryption key pair
|
// Generate the new encryption key pair
|
||||||
val newKeyPair = Curve.generateKeyPair()
|
val newKeyPair = Curve.generateKeyPair()
|
||||||
// replace call will not succeed if no value already set
|
// Replace call will not succeed if no value already set
|
||||||
pendingKeyPair.putIfAbsent(groupPublicKey,Optional.absent())
|
pendingKeyPairs.putIfAbsent(groupPublicKey,Optional.absent())
|
||||||
do {
|
do {
|
||||||
// make sure we set the pendingKeyPair or wait until it is not null
|
// Make sure we set the pending key pair or wait until it is not null
|
||||||
} while (!pendingKeyPair.replace(groupPublicKey,Optional.absent(),Optional.fromNullable(newKeyPair)))
|
} while (!pendingKeyPairs.replace(groupPublicKey,Optional.absent(),Optional.fromNullable(newKeyPair)))
|
||||||
// Distribute it
|
// Distribute it
|
||||||
sendEncryptionKeyPair(groupPublicKey, newKeyPair, targetMembers)?.success {
|
sendEncryptionKeyPair(groupPublicKey, newKeyPair, targetMembers)?.success {
|
||||||
// Store it * after * having sent out the message to the group
|
// Store it * after * having sent out the message to the group
|
||||||
storage.addClosedGroupEncryptionKeyPair(newKeyPair, groupPublicKey)
|
storage.addClosedGroupEncryptionKeyPair(newKeyPair, groupPublicKey)
|
||||||
pendingKeyPair[groupPublicKey] = Optional.absent()
|
pendingKeyPairs[groupPublicKey] = Optional.absent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +275,7 @@ fun MessageSender.sendEncryptionKeyPair(groupPublicKey: String, newKeyPair: ECKe
|
|||||||
proto.privateKey = ByteString.copyFrom(newKeyPair.privateKey.serialize())
|
proto.privateKey = ByteString.copyFrom(newKeyPair.privateKey.serialize())
|
||||||
val plaintext = proto.build().toByteArray()
|
val plaintext = proto.build().toByteArray()
|
||||||
val wrappers = targetMembers.map { publicKey ->
|
val wrappers = targetMembers.map { publicKey ->
|
||||||
val ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, publicKey)
|
val ciphertext = MessageEncrypter.encrypt(plaintext, publicKey)
|
||||||
ClosedGroupControlMessage.KeyPairWrapper(publicKey, ByteString.copyFrom(ciphertext))
|
ClosedGroupControlMessage.KeyPairWrapper(publicKey, ByteString.copyFrom(ciphertext))
|
||||||
}
|
}
|
||||||
val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), wrappers)
|
val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), wrappers)
|
||||||
@ -307,14 +303,14 @@ fun MessageSender.sendLatestEncryptionKeyPair(publicKey: String, groupPublicKey:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Get the latest encryption key pair
|
// Get the latest encryption key pair
|
||||||
val encryptionKeyPair = pendingKeyPair[groupPublicKey]?.orNull()
|
val encryptionKeyPair = pendingKeyPairs[groupPublicKey]?.orNull()
|
||||||
?: storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: return
|
?: storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: return
|
||||||
// Send it
|
// Send it
|
||||||
val proto = SignalServiceProtos.KeyPair.newBuilder()
|
val proto = SignalServiceProtos.KeyPair.newBuilder()
|
||||||
proto.publicKey = ByteString.copyFrom(encryptionKeyPair.publicKey.serialize().removing05PrefixIfNeeded())
|
proto.publicKey = ByteString.copyFrom(encryptionKeyPair.publicKey.serialize().removing05PrefixIfNeeded())
|
||||||
proto.privateKey = ByteString.copyFrom(encryptionKeyPair.privateKey.serialize())
|
proto.privateKey = ByteString.copyFrom(encryptionKeyPair.privateKey.serialize())
|
||||||
val plaintext = proto.build().toByteArray()
|
val plaintext = proto.build().toByteArray()
|
||||||
val ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, publicKey)
|
val ciphertext = MessageEncrypter.encrypt(plaintext, publicKey)
|
||||||
Log.d("Loki", "Sending latest encryption key pair to: $publicKey.")
|
Log.d("Loki", "Sending latest encryption key pair to: $publicKey.")
|
||||||
val wrapper = ClosedGroupControlMessage.KeyPairWrapper(publicKey, ByteString.copyFrom(ciphertext))
|
val wrapper = ClosedGroupControlMessage.KeyPairWrapper(publicKey, ByteString.copyFrom(ciphertext))
|
||||||
val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), listOf(wrapper))
|
val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), listOf(wrapper))
|
||||||
|
@ -401,7 +401,7 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo
|
|||||||
//
|
//
|
||||||
// Without the code below, the added member(s) would never get the key pair that was generated by the admin when they saw
|
// Without the code below, the added member(s) would never get the key pair that was generated by the admin when they saw
|
||||||
// the member removed message.
|
// the member removed message.
|
||||||
val encryptionKeyPair = pendingKeyPair[groupPublicKey]?.orNull()
|
val encryptionKeyPair = pendingKeyPairs[groupPublicKey]?.orNull()
|
||||||
?: storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)
|
?: storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)
|
||||||
if (encryptionKeyPair == null) {
|
if (encryptionKeyPair == null) {
|
||||||
android.util.Log.d("Loki", "Couldn't get encryption key pair for closed group.")
|
android.util.Log.d("Loki", "Couldn't get encryption key pair for closed group.")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user