mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-03 15:05:24 +00:00
Remove weird roundabout way of doing decryption
This commit is contained in:
parent
d83c257491
commit
d9348c5442
@ -70,7 +70,6 @@ import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
|||||||
import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
|
import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
|
||||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
||||||
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
||||||
import org.thoughtcrime.securesms.loki.api.SessionProtocolImpl;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||||
@ -178,8 +177,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
MessagingModuleConfiguration.Companion.configure(this,
|
MessagingModuleConfiguration.Companion.configure(this,
|
||||||
DatabaseFactory.getStorage(this),
|
DatabaseFactory.getStorage(this),
|
||||||
DatabaseFactory.getAttachmentProvider(this),
|
DatabaseFactory.getAttachmentProvider(this));
|
||||||
new SessionProtocolImpl(this));
|
|
||||||
SnodeModule.Companion.configure(apiDB, broadcaster);
|
SnodeModule.Companion.configure(apiDB, broadcaster);
|
||||||
if (userPublicKey != null) {
|
if (userPublicKey != null) {
|
||||||
MentionsManager.Companion.configureIfNeeded(userPublicKey, userDB);
|
MentionsManager.Companion.configureIfNeeded(userPublicKey, userDB);
|
||||||
|
@ -347,7 +347,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(group, server)
|
DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(group, server)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isMessageDuplicated(timestamp: Long, sender: String): Boolean {
|
override fun isDuplicateMessage(timestamp: Long, sender: String): Boolean {
|
||||||
return getReceivedMessageTimestamps().contains(timestamp)
|
return getReceivedMessageTimestamps().contains(timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import network.loki.messenger.R
|
|||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
import nl.komponents.kovenant.all
|
import nl.komponents.kovenant.all
|
||||||
import nl.komponents.kovenant.ui.alwaysUi
|
import nl.komponents.kovenant.ui.alwaysUi
|
||||||
|
import nl.komponents.kovenant.ui.successUi
|
||||||
import org.session.libsession.messaging.avatars.AvatarHelper
|
import org.session.libsession.messaging.avatars.AvatarHelper
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
@ -189,7 +190,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
promises.add(ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this))
|
promises.add(ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this))
|
||||||
}
|
}
|
||||||
val compoundPromise = all(promises)
|
val compoundPromise = all(promises)
|
||||||
compoundPromise.success {
|
compoundPromise.successUi { // Do this on the UI thread so that it happens before the alwaysUi clause below
|
||||||
if (isUpdatingProfilePicture && profilePicture != null) {
|
if (isUpdatingProfilePicture && profilePicture != null) {
|
||||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
||||||
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
||||||
@ -206,7 +207,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
btnGroupNameDisplay.text = displayName
|
btnGroupNameDisplay.text = displayName
|
||||||
}
|
}
|
||||||
if (isUpdatingProfilePicture && profilePicture != null) {
|
if (isUpdatingProfilePicture && profilePicture != null) {
|
||||||
profilePictureView.recycle() // clear cached image before update tje profilePictureView
|
profilePictureView.recycle() // Clear the cached image before updating
|
||||||
profilePictureView.update()
|
profilePictureView.update()
|
||||||
}
|
}
|
||||||
displayNameToBeUploaded = null
|
displayNameToBeUploaded = null
|
||||||
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.loki.protocol
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.protobuf.ByteString
|
import com.google.protobuf.ByteString
|
||||||
|
import org.session.libsession.messaging.sending_receiving.*
|
||||||
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
|
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
|
||||||
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
|
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
||||||
@ -15,12 +16,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
|
|||||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager
|
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager
|
||||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager.ClosedGroupOperation
|
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager.ClosedGroupOperation
|
||||||
import org.thoughtcrime.securesms.loki.api.SessionProtocolImpl
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase
|
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
|
||||||
import org.session.libsession.messaging.sending_receiving.generateAndSendNewEncryptionKeyPair
|
|
||||||
import org.session.libsession.messaging.sending_receiving.pendingKeyPair
|
|
||||||
import org.session.libsession.messaging.sending_receiving.sendEncryptionKeyPair
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.threads.GroupRecord
|
import org.session.libsession.messaging.threads.GroupRecord
|
||||||
@ -330,7 +326,7 @@ object ClosedGroupsProtocolV2 {
|
|||||||
// Find our wrapper and decrypt it if possible
|
// Find our wrapper and decrypt it if possible
|
||||||
val wrapper = closedGroupUpdate.wrappersList.firstOrNull { it.publicKey.toByteArray().toHexString() == userPublicKey } ?: return
|
val wrapper = closedGroupUpdate.wrappersList.firstOrNull { it.publicKey.toByteArray().toHexString() == userPublicKey } ?: return
|
||||||
val encryptedKeyPair = wrapper.encryptedKeyPair.toByteArray()
|
val encryptedKeyPair = wrapper.encryptedKeyPair.toByteArray()
|
||||||
val plaintext = SessionProtocolImpl(context).decrypt(encryptedKeyPair, userKeyPair).first
|
val plaintext = MessageDecrypter.decrypt(encryptedKeyPair, userKeyPair).first
|
||||||
// Parse it
|
// Parse it
|
||||||
val proto = SignalServiceProtos.KeyPair.parseFrom(plaintext)
|
val proto = SignalServiceProtos.KeyPair.parseFrom(plaintext)
|
||||||
val keyPair = ECKeyPair(DjbECPublicKey(proto.publicKey.toByteArray().removing05PrefixIfNeeded()), DjbECPrivateKey(proto.privateKey.toByteArray()))
|
val keyPair = ECKeyPair(DjbECPublicKey(proto.publicKey.toByteArray().removing05PrefixIfNeeded()), DjbECPrivateKey(proto.privateKey.toByteArray()))
|
||||||
|
@ -2,13 +2,11 @@ package org.session.libsession.messaging
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.session.libsession.database.MessageDataProvider
|
import org.session.libsession.database.MessageDataProvider
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocol
|
|
||||||
|
|
||||||
class MessagingModuleConfiguration(
|
class MessagingModuleConfiguration(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
val storage: StorageProtocol,
|
val storage: StorageProtocol,
|
||||||
val messageDataProvider: MessageDataProvider,
|
val messageDataProvider: MessageDataProvider
|
||||||
val sessionProtocol: SessionProtocol
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -16,11 +14,10 @@ class MessagingModuleConfiguration(
|
|||||||
|
|
||||||
fun configure(context: Context,
|
fun configure(context: Context,
|
||||||
storage: StorageProtocol,
|
storage: StorageProtocol,
|
||||||
messageDataProvider: MessageDataProvider,
|
messageDataProvider: MessageDataProvider
|
||||||
sessionProtocol: SessionProtocol
|
|
||||||
) {
|
) {
|
||||||
if (Companion::shared.isInitialized) { return }
|
if (Companion::shared.isInitialized) { return }
|
||||||
shared = MessagingModuleConfiguration(context, storage, messageDataProvider, sessionProtocol)
|
shared = MessagingModuleConfiguration(context, storage, messageDataProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -92,7 +92,7 @@ interface StorageProtocol {
|
|||||||
fun removeLastDeletionServerId(room: String, server: String)
|
fun removeLastDeletionServerId(room: String, server: String)
|
||||||
|
|
||||||
// Message Handling
|
// Message Handling
|
||||||
fun isMessageDuplicated(timestamp: Long, sender: String): Boolean
|
fun isDuplicateMessage(timestamp: Long, sender: String): Boolean
|
||||||
fun getReceivedMessageTimestamps(): Set<Long>
|
fun getReceivedMessageTimestamps(): Set<Long>
|
||||||
fun addReceivedMessageTimestamp(timestamp: Long)
|
fun addReceivedMessageTimestamp(timestamp: Long)
|
||||||
fun removeReceivedMessageTimestamps(timestamps: Set<Long>)
|
fun removeReceivedMessageTimestamps(timestamps: Set<Long>)
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
package org.thoughtcrime.securesms.loki.api
|
package org.session.libsession.messaging.sending_receiving
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.goterl.lazycode.lazysodium.LazySodiumAndroid
|
import com.goterl.lazycode.lazysodium.LazySodiumAndroid
|
||||||
import com.goterl.lazycode.lazysodium.SodiumAndroid
|
import com.goterl.lazycode.lazysodium.SodiumAndroid
|
||||||
import com.goterl.lazycode.lazysodium.interfaces.Box
|
import com.goterl.lazycode.lazysodium.interfaces.Box
|
||||||
import com.goterl.lazycode.lazysodium.interfaces.Sign
|
import com.goterl.lazycode.lazysodium.interfaces.Sign
|
||||||
|
|
||||||
import org.session.libsignal.utilities.Hex
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocol
|
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
||||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||||
import org.session.libsignal.service.loki.utilities.toHexString
|
import org.session.libsignal.service.loki.utilities.toHexString
|
||||||
import org.session.libsession.utilities.KeyPairUtilities
|
import org.session.libsignal.utilities.Hex
|
||||||
|
|
||||||
class SessionProtocolImpl(private val context: Context) : SessionProtocol {
|
object MessageDecrypter {
|
||||||
|
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
||||||
|
|
||||||
override 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())
|
||||||
val signatureSize = Sign.BYTES
|
val signatureSize = Sign.BYTES
|
||||||
@ -32,9 +27,9 @@ class SessionProtocolImpl(private val context: Context) : SessionProtocol {
|
|||||||
sodium.cryptoBoxSealOpen(plaintextWithMetadata, ciphertext, ciphertext.size.toLong(), recipientX25519PublicKey, recipientX25519PrivateKey)
|
sodium.cryptoBoxSealOpen(plaintextWithMetadata, ciphertext, ciphertext.size.toLong(), recipientX25519PublicKey, recipientX25519PrivateKey)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.d("Loki", "Couldn't decrypt message due to error: $exception.")
|
Log.d("Loki", "Couldn't decrypt message due to error: $exception.")
|
||||||
throw SessionProtocol.Exception.DecryptionFailed
|
throw MessageReceiver.Error.DecryptionFailed
|
||||||
}
|
}
|
||||||
if (plaintextWithMetadata.size <= (signatureSize + ed25519PublicKeySize)) { throw SessionProtocol.Exception.DecryptionFailed }
|
if (plaintextWithMetadata.size <= (signatureSize + ed25519PublicKeySize)) { throw MessageReceiver.Error.DecryptionFailed }
|
||||||
// 2. ) Get the message parts
|
// 2. ) Get the message parts
|
||||||
val signature = plaintextWithMetadata.sliceArray(plaintextWithMetadata.size - signatureSize until plaintextWithMetadata.size)
|
val signature = plaintextWithMetadata.sliceArray(plaintextWithMetadata.size - signatureSize until plaintextWithMetadata.size)
|
||||||
val senderED25519PublicKey = plaintextWithMetadata.sliceArray(plaintextWithMetadata.size - (signatureSize + ed25519PublicKeySize) until plaintextWithMetadata.size - signatureSize)
|
val senderED25519PublicKey = plaintextWithMetadata.sliceArray(plaintextWithMetadata.size - (signatureSize + ed25519PublicKeySize) until plaintextWithMetadata.size - signatureSize)
|
||||||
@ -43,10 +38,10 @@ class SessionProtocolImpl(private val context: Context) : SessionProtocol {
|
|||||||
val verificationData = (plaintext + senderED25519PublicKey + recipientX25519PublicKey)
|
val verificationData = (plaintext + senderED25519PublicKey + recipientX25519PublicKey)
|
||||||
try {
|
try {
|
||||||
val isValid = sodium.cryptoSignVerifyDetached(signature, verificationData, verificationData.size, senderED25519PublicKey)
|
val isValid = sodium.cryptoSignVerifyDetached(signature, verificationData, verificationData.size, senderED25519PublicKey)
|
||||||
if (!isValid) { throw SessionProtocol.Exception.InvalidSignature }
|
if (!isValid) { throw MessageReceiver.Error.InvalidSignature }
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.d("Loki", "Couldn't verify message signature due to error: $exception.")
|
Log.d("Loki", "Couldn't verify message signature due to error: $exception.")
|
||||||
throw SessionProtocol.Exception.InvalidSignature
|
throw MessageReceiver.Error.InvalidSignature
|
||||||
}
|
}
|
||||||
// 4. ) Get the sender's X25519 public key
|
// 4. ) Get the sender's X25519 public key
|
||||||
val senderX25519PublicKey = ByteArray(Sign.CURVE25519_PUBLICKEYBYTES)
|
val senderX25519PublicKey = ByteArray(Sign.CURVE25519_PUBLICKEYBYTES)
|
@ -10,35 +10,25 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|||||||
|
|
||||||
object MessageReceiver {
|
object MessageReceiver {
|
||||||
|
|
||||||
private val lastEncryptionKeyPairRequest = mutableMapOf<String, Long>()
|
internal sealed class Error(message: String) : Exception(message) {
|
||||||
|
|
||||||
internal sealed class Error(val description: String) : Exception(description) {
|
|
||||||
object DuplicateMessage: Error("Duplicate message.")
|
object DuplicateMessage: Error("Duplicate message.")
|
||||||
object InvalidMessage: Error("Invalid message.")
|
object InvalidMessage: Error("Invalid message.")
|
||||||
object UnknownMessage: Error("Unknown message type.")
|
object UnknownMessage: Error("Unknown message type.")
|
||||||
object UnknownEnvelopeType: Error("Unknown envelope type.")
|
object UnknownEnvelopeType: Error("Unknown envelope type.")
|
||||||
object NoUserX25519KeyPair: Error("Couldn't find user X25519 key pair.")
|
object DecryptionFailed : Exception("Couldn't decrypt message.")
|
||||||
object NoUserED25519KeyPair: Error("Couldn't find user ED25519 key pair.")
|
|
||||||
object InvalidSignature: Error("Invalid message signature.")
|
object InvalidSignature: Error("Invalid message signature.")
|
||||||
object NoData: Error("Received an empty envelope.")
|
object NoData: Error("Received an empty envelope.")
|
||||||
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.")
|
||||||
object ParsingFailed : Error("Couldn't parse ciphertext message.")
|
|
||||||
// Shared sender keys
|
// 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.")
|
||||||
|
|
||||||
internal val isRetryable: Boolean = when (this) {
|
internal val isRetryable: Boolean = when (this) {
|
||||||
is DuplicateMessage -> false
|
is DuplicateMessage, is InvalidMessage, is UnknownMessage,
|
||||||
is InvalidMessage -> false
|
is UnknownEnvelopeType, is InvalidSignature, is NoData,
|
||||||
is UnknownMessage -> false
|
is SenderBlocked, is SelfSend -> false
|
||||||
is UnknownEnvelopeType -> false
|
|
||||||
is InvalidSignature -> false
|
|
||||||
is NoData -> false
|
|
||||||
is NoThread -> false
|
|
||||||
is SenderBlocked -> false
|
|
||||||
is SelfSend -> false
|
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,13 +36,15 @@ object MessageReceiver {
|
|||||||
internal fun parse(data: ByteArray, openGroupServerID: Long?, isRetry: Boolean = false): Pair<Message, SignalServiceProtos.Content> {
|
internal fun parse(data: ByteArray, openGroupServerID: Long?, isRetry: Boolean = false): Pair<Message, SignalServiceProtos.Content> {
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val userPublicKey = storage.getUserPublicKey()
|
val userPublicKey = storage.getUserPublicKey()
|
||||||
val isOpenGroupMessage = openGroupServerID != null
|
val isOpenGroupMessage = (openGroupServerID != null)
|
||||||
// Parse the envelope
|
// Parse the envelope
|
||||||
val envelope = SignalServiceProtos.Envelope.parseFrom(data)
|
val envelope = SignalServiceProtos.Envelope.parseFrom(data)
|
||||||
// If the message failed to process the first time around we retry it later (if the error is retryable). In this case the timestamp
|
// If the message failed to process the first time around we retry it later (if the error is retryable). In this case the timestamp
|
||||||
// will already be in the database but we don't want to treat the message as a duplicate. The isRetry flag is a simple workaround
|
// will already be in the database but we don't want to treat the message as a duplicate. The isRetry flag is a simple workaround
|
||||||
// for this issue.
|
// for this issue.
|
||||||
if (storage.isMessageDuplicated(envelope.timestamp, GroupUtil.doubleEncodeGroupID(envelope.source)) && !isRetry) throw Error.DuplicateMessage
|
if (storage.isDuplicateMessage(envelope.timestamp, GroupUtil.doubleEncodeGroupID(envelope.source)) && !isRetry) {
|
||||||
|
throw Error.DuplicateMessage
|
||||||
|
}
|
||||||
// Decrypt the contents
|
// Decrypt the contents
|
||||||
val ciphertext = envelope.content ?: throw Error.NoData
|
val ciphertext = envelope.content ?: throw Error.NoData
|
||||||
var plaintext: ByteArray? = null
|
var plaintext: ByteArray? = null
|
||||||
@ -65,7 +57,7 @@ object MessageReceiver {
|
|||||||
when (envelope.type) {
|
when (envelope.type) {
|
||||||
SignalServiceProtos.Envelope.Type.SESSION_MESSAGE -> {
|
SignalServiceProtos.Envelope.Type.SESSION_MESSAGE -> {
|
||||||
val userX25519KeyPair = MessagingModuleConfiguration.shared.storage.getUserX25519KeyPair()
|
val userX25519KeyPair = MessagingModuleConfiguration.shared.storage.getUserX25519KeyPair()
|
||||||
val decryptionResult = MessageReceiverDecryption.decryptWithSessionProtocol(ciphertext.toByteArray(), userX25519KeyPair)
|
val decryptionResult = MessageDecrypter.decrypt(ciphertext.toByteArray(), userX25519KeyPair)
|
||||||
plaintext = decryptionResult.first
|
plaintext = decryptionResult.first
|
||||||
sender = decryptionResult.second
|
sender = decryptionResult.second
|
||||||
}
|
}
|
||||||
@ -81,7 +73,7 @@ object MessageReceiver {
|
|||||||
var encryptionKeyPair = encryptionKeyPairs.removeLast()
|
var encryptionKeyPair = encryptionKeyPairs.removeLast()
|
||||||
fun decrypt() {
|
fun decrypt() {
|
||||||
try {
|
try {
|
||||||
val decryptionResult = MessageReceiverDecryption.decryptWithSessionProtocol(ciphertext.toByteArray(), encryptionKeyPair)
|
val decryptionResult = MessageDecrypter.decrypt(ciphertext.toByteArray(), encryptionKeyPair)
|
||||||
plaintext = decryptionResult.first
|
plaintext = decryptionResult.first
|
||||||
sender = decryptionResult.second
|
sender = decryptionResult.second
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -100,9 +92,9 @@ object MessageReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't process the envelope any further if the message has been handled already
|
// Don't process the envelope any further if the message has been handled already
|
||||||
if (storage.isMessageDuplicated(envelope.timestamp, sender!!) && !isRetry) throw Error.DuplicateMessage
|
if (storage.isDuplicateMessage(envelope.timestamp, sender!!) && !isRetry) throw Error.DuplicateMessage
|
||||||
// Don't process the envelope any further if the sender is blocked
|
// Don't process the envelope any further if the sender is blocked
|
||||||
if (isBlock(sender!!)) throw Error.SenderBlocked
|
if (isBlocked(sender!!)) throw Error.SenderBlocked
|
||||||
// Parse the proto
|
// Parse the proto
|
||||||
val proto = SignalServiceProtos.Content.parseFrom(PushTransportDetails.getStrippedPaddingMessageBody(plaintext))
|
val proto = SignalServiceProtos.Content.parseFrom(PushTransportDetails.getStrippedPaddingMessageBody(plaintext))
|
||||||
// Parse the message
|
// Parse the message
|
||||||
@ -113,7 +105,7 @@ object MessageReceiver {
|
|||||||
ExpirationTimerUpdate.fromProto(proto) ?:
|
ExpirationTimerUpdate.fromProto(proto) ?:
|
||||||
ConfigurationMessage.fromProto(proto) ?:
|
ConfigurationMessage.fromProto(proto) ?:
|
||||||
VisibleMessage.fromProto(proto) ?: throw Error.UnknownMessage
|
VisibleMessage.fromProto(proto) ?: throw Error.UnknownMessage
|
||||||
// Ignore self sends if needed
|
// Ignore self send if needed
|
||||||
if (!message.isSelfSendValid && sender == userPublicKey) throw Error.SelfSend
|
if (!message.isSelfSendValid && sender == userPublicKey) throw Error.SelfSend
|
||||||
// Guard against control messages in open groups
|
// Guard against control messages in open groups
|
||||||
if (isOpenGroupMessage && message !is VisibleMessage) throw Error.InvalidMessage
|
if (isOpenGroupMessage && message !is VisibleMessage) throw Error.InvalidMessage
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package org.session.libsession.messaging.sending_receiving
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
|
||||||
|
|
||||||
object MessageReceiverDecryption {
|
|
||||||
|
|
||||||
internal fun decryptWithSessionProtocol(ciphertext: ByteArray, x25519KeyPair: ECKeyPair): Pair<ByteArray, String> {
|
|
||||||
return MessagingModuleConfiguration.shared.sessionProtocol.decrypt(ciphertext, x25519KeyPair)
|
|
||||||
}
|
|
||||||
}
|
|
@ -34,7 +34,7 @@ import java.security.MessageDigest
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
internal fun MessageReceiver.isBlock(publicKey: String): Boolean {
|
internal fun MessageReceiver.isBlocked(publicKey: String): Boolean {
|
||||||
val context = MessagingModuleConfiguration.shared.context
|
val context = MessagingModuleConfiguration.shared.context
|
||||||
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
||||||
return recipient.isBlocked
|
return recipient.isBlocked
|
||||||
@ -323,7 +323,7 @@ private fun MessageReceiver.handleClosedGroupEncryptionKeyPair(message: ClosedGr
|
|||||||
// Find our wrapper and decrypt it if possible
|
// Find our wrapper and decrypt it if possible
|
||||||
val wrapper = kind.wrappers.firstOrNull { it.publicKey!! == userPublicKey } ?: return
|
val wrapper = kind.wrappers.firstOrNull { it.publicKey!! == userPublicKey } ?: return
|
||||||
val encryptedKeyPair = wrapper.encryptedKeyPair!!.toByteArray()
|
val encryptedKeyPair = wrapper.encryptedKeyPair!!.toByteArray()
|
||||||
val plaintext = MessageReceiverDecryption.decryptWithSessionProtocol(encryptedKeyPair, userKeyPair).first
|
val plaintext = MessageDecrypter.decrypt(encryptedKeyPair, userKeyPair).first
|
||||||
// Parse it
|
// Parse it
|
||||||
val proto = SignalServiceProtos.KeyPair.parseFrom(plaintext)
|
val proto = SignalServiceProtos.KeyPair.parseFrom(plaintext)
|
||||||
val keyPair = ECKeyPair(DjbECPublicKey(proto.publicKey.toByteArray().removing05PrefixIfNeeded()), DjbECPrivateKey(proto.privateKey.toByteArray()))
|
val keyPair = ECKeyPair(DjbECPublicKey(proto.publicKey.toByteArray().removing05PrefixIfNeeded()), DjbECPrivateKey(proto.privateKey.toByteArray()))
|
||||||
|
@ -31,8 +31,6 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos.Content;
|
|||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage;
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocol;
|
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocolUtilities;
|
|
||||||
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol;
|
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -51,13 +49,10 @@ public class SignalServiceCipher {
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static final String TAG = SignalServiceCipher.class.getSimpleName();
|
private static final String TAG = SignalServiceCipher.class.getSimpleName();
|
||||||
|
|
||||||
private final SessionProtocol sessionProtocolImpl;
|
|
||||||
private final LokiAPIDatabaseProtocol apiDB;
|
private final LokiAPIDatabaseProtocol apiDB;
|
||||||
|
|
||||||
public SignalServiceCipher(SessionProtocol sessionProtocolImpl,
|
public SignalServiceCipher(LokiAPIDatabaseProtocol apiDB)
|
||||||
LokiAPIDatabaseProtocol apiDB)
|
|
||||||
{
|
{
|
||||||
this.sessionProtocolImpl = sessionProtocolImpl;
|
|
||||||
this.apiDB = apiDB;
|
this.apiDB = apiDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,27 +120,7 @@ public class SignalServiceCipher {
|
|||||||
|
|
||||||
protected Plaintext decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) throws InvalidMetadataMessageException
|
protected Plaintext decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) throws InvalidMetadataMessageException
|
||||||
{
|
{
|
||||||
byte[] paddedMessage;
|
throw new IllegalStateException("This shouldn't be used anymore");
|
||||||
Metadata metadata;
|
|
||||||
|
|
||||||
if (envelope.isClosedGroupCiphertext()) {
|
|
||||||
String groupPublicKey = envelope.getSource();
|
|
||||||
kotlin.Pair<byte[], String> plaintextAndSenderPublicKey = SessionProtocolUtilities.INSTANCE.decryptClosedGroupCiphertext(ciphertext, groupPublicKey, apiDB, sessionProtocolImpl);
|
|
||||||
paddedMessage = plaintextAndSenderPublicKey.getFirst();
|
|
||||||
String senderPublicKey = plaintextAndSenderPublicKey.getSecond();
|
|
||||||
metadata = new Metadata(senderPublicKey, 1, envelope.getTimestamp(), false);
|
|
||||||
} else if (envelope.isUnidentifiedSender()) {
|
|
||||||
ECKeyPair userX25519KeyPair = apiDB.getUserX25519KeyPair();
|
|
||||||
kotlin.Pair<byte[], String> plaintextAndSenderPublicKey = sessionProtocolImpl.decrypt(ciphertext, userX25519KeyPair);
|
|
||||||
paddedMessage = plaintextAndSenderPublicKey.getFirst();
|
|
||||||
String senderPublicKey = plaintextAndSenderPublicKey.getSecond();
|
|
||||||
metadata = new Metadata(senderPublicKey, 1, envelope.getTimestamp(), false);
|
|
||||||
} else {
|
|
||||||
throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType());
|
|
||||||
}
|
|
||||||
byte[] data = PushTransportDetails.getStrippedPaddingMessageBody(paddedMessage);
|
|
||||||
|
|
||||||
return new Plaintext(metadata, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceDataMessage createSignalServiceMessage(Metadata metadata, DataMessage content) throws ProtocolInvalidMessageException {
|
private SignalServiceDataMessage createSignalServiceMessage(Metadata metadata, DataMessage content) throws ProtocolInvalidMessageException {
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
package org.session.libsignal.service.loki.api.crypto
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
|
||||||
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol
|
|
||||||
|
|
||||||
interface SessionProtocol {
|
|
||||||
|
|
||||||
sealed class Exception(val description: String) : kotlin.Exception(description) {
|
|
||||||
// Encryption
|
|
||||||
object NoUserED25519KeyPair : Exception("Couldn't find user ED25519 key pair.")
|
|
||||||
object SigningFailed : Exception("Couldn't sign message.")
|
|
||||||
object EncryptionFailed : Exception("Couldn't encrypt message.")
|
|
||||||
// Decryption
|
|
||||||
object NoData : Exception("Received an empty envelope.")
|
|
||||||
object InvalidGroupPublicKey : Exception("Invalid group public key.")
|
|
||||||
object NoGroupKeyPair : Exception("Missing group key pair.")
|
|
||||||
object DecryptionFailed : Exception("Couldn't decrypt message.")
|
|
||||||
object InvalidSignature : Exception("Invalid message signature.")
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
fun decrypt(ciphertext: ByteArray, x25519KeyPair: ECKeyPair): Pair<ByteArray, String>
|
|
||||||
}
|
|
||||||
|
|
||||||
object SessionProtocolUtilities {
|
|
||||||
|
|
||||||
fun decryptClosedGroupCiphertext(ciphertext: ByteArray, groupPublicKey: String, apiDB: LokiAPIDatabaseProtocol, sessionProtocolImpl: SessionProtocol): Pair<ByteArray, String> {
|
|
||||||
val encryptionKeyPairs = apiDB.getClosedGroupEncryptionKeyPairs(groupPublicKey).toMutableList()
|
|
||||||
if (encryptionKeyPairs.isEmpty()) { throw SessionProtocol.Exception.NoGroupKeyPair }
|
|
||||||
// Loop through all known group key pairs in reverse order (i.e. try the latest key pair first (which'll more than
|
|
||||||
// likely be the one we want) but try older ones in case that didn't work)
|
|
||||||
var encryptionKeyPair = encryptionKeyPairs.removeAt(encryptionKeyPairs.lastIndex)
|
|
||||||
fun decrypt(): Pair<ByteArray, String> {
|
|
||||||
try {
|
|
||||||
return sessionProtocolImpl.decrypt(ciphertext, encryptionKeyPair)
|
|
||||||
} catch(exception: Exception) {
|
|
||||||
if (encryptionKeyPairs.isNotEmpty()) {
|
|
||||||
encryptionKeyPair = encryptionKeyPairs.removeAt(encryptionKeyPairs.lastIndex)
|
|
||||||
return decrypt()
|
|
||||||
} else {
|
|
||||||
throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return decrypt()
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user