Remove weird roundabout way of doing decryption

This commit is contained in:
Niels Andriesse
2021-05-13 15:27:08 +10:00
parent d83c257491
commit d9348c5442
12 changed files with 37 additions and 147 deletions

View File

@@ -2,13 +2,11 @@ package org.session.libsession.messaging
import android.content.Context
import org.session.libsession.database.MessageDataProvider
import org.session.libsignal.service.loki.api.crypto.SessionProtocol
class MessagingModuleConfiguration(
val context: Context,
val storage: StorageProtocol,
val messageDataProvider: MessageDataProvider,
val sessionProtocol: SessionProtocol
val messageDataProvider: MessageDataProvider
) {
companion object {
@@ -16,11 +14,10 @@ class MessagingModuleConfiguration(
fun configure(context: Context,
storage: StorageProtocol,
messageDataProvider: MessageDataProvider,
sessionProtocol: SessionProtocol
messageDataProvider: MessageDataProvider
) {
if (Companion::shared.isInitialized) { return }
shared = MessagingModuleConfiguration(context, storage, messageDataProvider, sessionProtocol)
shared = MessagingModuleConfiguration(context, storage, messageDataProvider)
}
}
}

View File

@@ -92,7 +92,7 @@ interface StorageProtocol {
fun removeLastDeletionServerId(room: String, server: String)
// Message Handling
fun isMessageDuplicated(timestamp: Long, sender: String): Boolean
fun isDuplicateMessage(timestamp: Long, sender: String): Boolean
fun getReceivedMessageTimestamps(): Set<Long>
fun addReceivedMessageTimestamp(timestamp: Long)
fun removeReceivedMessageTimestamps(timestamps: Set<Long>)

View File

@@ -0,0 +1,52 @@
package org.session.libsession.messaging.sending_receiving
import android.util.Log
import com.goterl.lazycode.lazysodium.LazySodiumAndroid
import com.goterl.lazycode.lazysodium.SodiumAndroid
import com.goterl.lazycode.lazysodium.interfaces.Box
import com.goterl.lazycode.lazysodium.interfaces.Sign
import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
import org.session.libsignal.service.loki.utilities.toHexString
import org.session.libsignal.utilities.Hex
object MessageDecrypter {
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
public fun decrypt(ciphertext: ByteArray, x25519KeyPair: ECKeyPair): Pair<ByteArray, String> {
val recipientX25519PrivateKey = x25519KeyPair.privateKey.serialize()
val recipientX25519PublicKey = Hex.fromStringCondensed(x25519KeyPair.hexEncodedPublicKey.removing05PrefixIfNeeded())
val signatureSize = Sign.BYTES
val ed25519PublicKeySize = Sign.PUBLICKEYBYTES
// 1. ) Decrypt the message
val plaintextWithMetadata = ByteArray(ciphertext.size - Box.SEALBYTES)
try {
sodium.cryptoBoxSealOpen(plaintextWithMetadata, ciphertext, ciphertext.size.toLong(), recipientX25519PublicKey, recipientX25519PrivateKey)
} catch (exception: Exception) {
Log.d("Loki", "Couldn't decrypt message due to error: $exception.")
throw MessageReceiver.Error.DecryptionFailed
}
if (plaintextWithMetadata.size <= (signatureSize + ed25519PublicKeySize)) { throw MessageReceiver.Error.DecryptionFailed }
// 2. ) Get the message parts
val signature = plaintextWithMetadata.sliceArray(plaintextWithMetadata.size - signatureSize until plaintextWithMetadata.size)
val senderED25519PublicKey = plaintextWithMetadata.sliceArray(plaintextWithMetadata.size - (signatureSize + ed25519PublicKeySize) until plaintextWithMetadata.size - signatureSize)
val plaintext = plaintextWithMetadata.sliceArray(0 until plaintextWithMetadata.size - (signatureSize + ed25519PublicKeySize))
// 3. ) Verify the signature
val verificationData = (plaintext + senderED25519PublicKey + recipientX25519PublicKey)
try {
val isValid = sodium.cryptoSignVerifyDetached(signature, verificationData, verificationData.size, senderED25519PublicKey)
if (!isValid) { throw MessageReceiver.Error.InvalidSignature }
} catch (exception: Exception) {
Log.d("Loki", "Couldn't verify message signature due to error: $exception.")
throw MessageReceiver.Error.InvalidSignature
}
// 4. ) Get the sender's X25519 public key
val senderX25519PublicKey = ByteArray(Sign.CURVE25519_PUBLICKEYBYTES)
sodium.convertPublicKeyEd25519ToCurve25519(senderX25519PublicKey, senderED25519PublicKey)
return Pair(plaintext, "05" + senderX25519PublicKey.toHexString())
}
}

View File

@@ -10,35 +10,25 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos
object MessageReceiver {
private val lastEncryptionKeyPairRequest = mutableMapOf<String, Long>()
internal sealed class Error(val description: String) : Exception(description) {
internal sealed class Error(message: String) : Exception(message) {
object DuplicateMessage: Error("Duplicate message.")
object InvalidMessage: Error("Invalid message.")
object UnknownMessage: Error("Unknown message type.")
object UnknownEnvelopeType: Error("Unknown envelope type.")
object NoUserX25519KeyPair: Error("Couldn't find user X25519 key pair.")
object NoUserED25519KeyPair: Error("Couldn't find user ED25519 key pair.")
object DecryptionFailed : Exception("Couldn't decrypt message.")
object InvalidSignature: Error("Invalid message signature.")
object NoData: Error("Received an empty envelope.")
object SenderBlocked: Error("Received a message from a blocked user.")
object NoThread: Error("Couldn't find thread for message.")
object SelfSend: Error("Message addressed at self.")
object ParsingFailed : Error("Couldn't parse ciphertext message.")
// Shared sender keys
object InvalidGroupPublicKey: Error("Invalid group public key.")
object NoGroupKeyPair: Error("Missing group key pair.")
internal val isRetryable: Boolean = when (this) {
is DuplicateMessage -> false
is InvalidMessage -> false
is UnknownMessage -> false
is UnknownEnvelopeType -> false
is InvalidSignature -> false
is NoData -> false
is NoThread -> false
is SenderBlocked -> false
is SelfSend -> false
is DuplicateMessage, is InvalidMessage, is UnknownMessage,
is UnknownEnvelopeType, is InvalidSignature, is NoData,
is SenderBlocked, is SelfSend -> false
else -> true
}
}
@@ -46,13 +36,15 @@ object MessageReceiver {
internal fun parse(data: ByteArray, openGroupServerID: Long?, isRetry: Boolean = false): Pair<Message, SignalServiceProtos.Content> {
val storage = MessagingModuleConfiguration.shared.storage
val userPublicKey = storage.getUserPublicKey()
val isOpenGroupMessage = openGroupServerID != null
val isOpenGroupMessage = (openGroupServerID != null)
// Parse the envelope
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
// 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.
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
val ciphertext = envelope.content ?: throw Error.NoData
var plaintext: ByteArray? = null
@@ -65,7 +57,7 @@ object MessageReceiver {
when (envelope.type) {
SignalServiceProtos.Envelope.Type.SESSION_MESSAGE -> {
val userX25519KeyPair = MessagingModuleConfiguration.shared.storage.getUserX25519KeyPair()
val decryptionResult = MessageReceiverDecryption.decryptWithSessionProtocol(ciphertext.toByteArray(), userX25519KeyPair)
val decryptionResult = MessageDecrypter.decrypt(ciphertext.toByteArray(), userX25519KeyPair)
plaintext = decryptionResult.first
sender = decryptionResult.second
}
@@ -81,7 +73,7 @@ object MessageReceiver {
var encryptionKeyPair = encryptionKeyPairs.removeLast()
fun decrypt() {
try {
val decryptionResult = MessageReceiverDecryption.decryptWithSessionProtocol(ciphertext.toByteArray(), encryptionKeyPair)
val decryptionResult = MessageDecrypter.decrypt(ciphertext.toByteArray(), encryptionKeyPair)
plaintext = decryptionResult.first
sender = decryptionResult.second
} catch (e: Exception) {
@@ -100,9 +92,9 @@ object MessageReceiver {
}
}
// 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
if (isBlock(sender!!)) throw Error.SenderBlocked
if (isBlocked(sender!!)) throw Error.SenderBlocked
// Parse the proto
val proto = SignalServiceProtos.Content.parseFrom(PushTransportDetails.getStrippedPaddingMessageBody(plaintext))
// Parse the message
@@ -113,7 +105,7 @@ object MessageReceiver {
ExpirationTimerUpdate.fromProto(proto) ?:
ConfigurationMessage.fromProto(proto) ?:
VisibleMessage.fromProto(proto) ?: throw Error.UnknownMessage
// Ignore self sends if needed
// Ignore self send if needed
if (!message.isSelfSendValid && sender == userPublicKey) throw Error.SelfSend
// Guard against control messages in open groups
if (isOpenGroupMessage && message !is VisibleMessage) throw Error.InvalidMessage

View File

@@ -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)
}
}

View File

@@ -34,7 +34,7 @@ import java.security.MessageDigest
import java.util.*
import kotlin.collections.ArrayList
internal fun MessageReceiver.isBlock(publicKey: String): Boolean {
internal fun MessageReceiver.isBlocked(publicKey: String): Boolean {
val context = MessagingModuleConfiguration.shared.context
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
return recipient.isBlocked
@@ -323,7 +323,7 @@ private fun MessageReceiver.handleClosedGroupEncryptionKeyPair(message: ClosedGr
// Find our wrapper and decrypt it if possible
val wrapper = kind.wrappers.firstOrNull { it.publicKey!! == userPublicKey } ?: return
val encryptedKeyPair = wrapper.encryptedKeyPair!!.toByteArray()
val plaintext = MessageReceiverDecryption.decryptWithSessionProtocol(encryptedKeyPair, userKeyPair).first
val plaintext = MessageDecrypter.decrypt(encryptedKeyPair, userKeyPair).first
// Parse it
val proto = SignalServiceProtos.KeyPair.parseFrom(plaintext)
val keyPair = ECKeyPair(DjbECPublicKey(proto.publicKey.toByteArray().removing05PrefixIfNeeded()), DjbECPrivateKey(proto.privateKey.toByteArray()))