This commit is contained in:
Ryan ZHAO 2021-01-14 13:28:25 +11:00
commit 8e8abcbe81
6 changed files with 74 additions and 19 deletions

View File

@ -18,12 +18,12 @@ import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.GroupRecord
import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.session.libsignal.libsignal.util.KeyHelper import org.session.libsignal.libsignal.util.KeyHelper
import org.session.libsignal.libsignal.util.guava.Optional import org.session.libsignal.libsignal.util.guava.Optional
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.api.messages.SignalServiceGroup
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.loki.api.opengroups.PublicChat
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase
@ -46,6 +46,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return Pair(userPublicKey, userPrivateKey) return Pair(userPublicKey, userPrivateKey)
} }
override fun getUserX25519KeyPair(): ECKeyPair {
return DatabaseFactory.getLokiAPIDatabase(context).getUserX25519KeyPair()
}
override fun getUserDisplayName(): String? { override fun getUserDisplayName(): String? {
return TextSecurePreferences.getProfileName(context) return TextSecurePreferences.getProfileName(context)
} }
@ -270,6 +274,18 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
mmsDB.markAsSent(infoMessageID, true) mmsDB.markAsSent(infoMessageID, true)
} }
override fun isClosedGroup(publicKey: String): Boolean {
TODO("Not yet implemented")
}
override fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): MutableList<ECKeyPair> {
TODO("Not yet implemented")
}
override fun getLatestClosedGroupEncryptionKeyPair(groupPublicKey: String): ECKeyPair {
TODO("Not yet implemented")
}
override fun setProfileSharing(address: Address, value: Boolean) { override fun setProfileSharing(address: Address, value: Boolean) {
val recipient = Recipient.from(context, address, false) val recipient = Recipient.from(context, address, false)
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, value) DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, value)

View File

@ -27,6 +27,7 @@ interface StorageProtocol {
// General // General
fun getUserPublicKey(): String? fun getUserPublicKey(): String?
fun getUserKeyPair(): Pair<String, ByteArray>? fun getUserKeyPair(): Pair<String, ByteArray>?
fun getUserX25519KeyPair(): ECKeyPair
fun getUserDisplayName(): String? fun getUserDisplayName(): String?
fun getUserProfileKey(): ByteArray? fun getUserProfileKey(): ByteArray?
fun getUserProfilePictureURL(): String? fun getUserProfilePictureURL(): String?
@ -101,6 +102,9 @@ interface StorageProtocol {
name: String, members: Collection<String>, admins: Collection<String>) name: String, members: Collection<String>, admins: Collection<String>)
fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceProtos.GroupContext.Type, name: String, fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceProtos.GroupContext.Type, name: String,
members: Collection<String>, admins: Collection<String>, threadID: Long) members: Collection<String>, admins: Collection<String>, threadID: Long)
fun isClosedGroup(publicKey: String): Boolean //TODO
fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): MutableList<ECKeyPair> //TODO
fun getLatestClosedGroupEncryptionKeyPair(groupPublicKey: String): ECKeyPair //TODO
// Settings // Settings
fun setProfileSharing(address: Address, value: Boolean) fun setProfileSharing(address: Address, value: Boolean)

View File

@ -7,8 +7,10 @@ import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
import org.session.libsession.messaging.messages.control.ReadReceipt import org.session.libsession.messaging.messages.control.ReadReceipt
import org.session.libsession.messaging.messages.control.TypingIndicator import org.session.libsession.messaging.messages.control.TypingIndicator
import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
import java.lang.Error
object MessageReceiver { object MessageReceiver {
internal sealed class Error(val description: String) : Exception() { internal sealed class Error(val description: String) : Exception() {
@ -16,7 +18,8 @@ object MessageReceiver {
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 NoUserPublicKey: Error("Couldn't find user key pair.") object NoUserX25519KeyPair: Error("Couldn't find user X25519 key pair.")
object NoUserED25519KeyPair: Error("Couldn't find user ED25519 key pair.")
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.")
@ -24,7 +27,7 @@ object MessageReceiver {
object ParsingFailed : Error("Couldn't parse ciphertext message.") 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 NoGroupPrivateKey: Error("Missing group private key.") object NoGroupKeyPair: Error("Missing group key pair.")
object SharedSecretGenerationFailed: Error("Couldn't generate a shared secret.") object SharedSecretGenerationFailed: Error("Couldn't generate a shared secret.")
internal val isRetryable: Boolean = when (this) { internal val isRetryable: Boolean = when (this) {
@ -47,8 +50,9 @@ object MessageReceiver {
if (storage.getReceivedMessageTimestamps().contains(envelope.timestamp)) throw Error.DuplicateMessage if (storage.getReceivedMessageTimestamps().contains(envelope.timestamp)) throw Error.DuplicateMessage
storage.addReceivedMessageTimestamp(envelope.timestamp) storage.addReceivedMessageTimestamp(envelope.timestamp)
// Decrypt the contents // Decrypt the contents
val plaintext: ByteArray val ciphertext = envelope.content ?: throw Error.NoData
val sender: String var plaintext: ByteArray? = null
var sender: String? = null
var groupPublicKey: String? = null var groupPublicKey: String? = null
if (isOpenGroupMessage) { if (isOpenGroupMessage) {
plaintext = envelope.content.toByteArray() plaintext = envelope.content.toByteArray()
@ -56,20 +60,43 @@ object MessageReceiver {
} else { } else {
when (envelope.type) { when (envelope.type) {
SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER -> { SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER -> {
val decryptionResult = MessageReceiverDecryption.decryptWithSessionProtocol(envelope) val userX25519KeyPair = MessagingConfiguration.shared.storage.getUserX25519KeyPair() ?: throw Error.NoUserX25519KeyPair
val decryptionResult = MessageReceiverDecryption.decryptWithSessionProtocol(ciphertext.toByteArray(), userX25519KeyPair)
plaintext = decryptionResult.first plaintext = decryptionResult.first
sender = decryptionResult.second sender = decryptionResult.second
} }
SignalServiceProtos.Envelope.Type.CLOSED_GROUP_CIPHERTEXT -> { SignalServiceProtos.Envelope.Type.CLOSED_GROUP_CIPHERTEXT -> {
val decryptionResult = MessageReceiverDecryption.decryptWithSharedSenderKeys(envelope) val hexEncodedGroupPublicKey = envelope.source
if (hexEncodedGroupPublicKey == null || MessagingConfiguration.shared.storage.isClosedGroup(hexEncodedGroupPublicKey)) {
throw Error.InvalidGroupPublicKey
}
val encryptionKeyPairs = MessagingConfiguration.shared.storage.getClosedGroupEncryptionKeyPairs(hexEncodedGroupPublicKey)
if (encryptionKeyPairs.isEmpty()) { throw Error.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.removeLast()
fun decrypt() {
try {
val decryptionResult = MessageReceiverDecryption.decryptWithSessionProtocol(ciphertext.toByteArray(), encryptionKeyPair)
plaintext = decryptionResult.first plaintext = decryptionResult.first
sender = decryptionResult.second sender = decryptionResult.second
} catch (e: Exception) {
if (encryptionKeyPairs.isNotEmpty()) {
encryptionKeyPair = encryptionKeyPairs.removeLast()
decrypt()
} else {
throw e
}
}
}
decrypt()
groupPublicKey = envelope.source
} }
else -> throw Error.UnknownEnvelopeType else -> throw Error.UnknownEnvelopeType
} }
} }
// 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 (isBlock(sender!!)) throw Error.SenderBlocked
// Ignore self sends // Ignore self sends
if (sender == userPublicKey) throw Error.SelfSend if (sender == userPublicKey) throw Error.SelfSend
// Parse the proto // Parse the proto

View File

@ -4,6 +4,7 @@ import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsession.messaging.sending_receiving.MessageReceiver.Error import org.session.libsession.messaging.sending_receiving.MessageReceiver.Error
import org.session.libsession.utilities.AESGCM import org.session.libsession.utilities.AESGCM
import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.GroupUtil
import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.whispersystems.curve25519.Curve25519 import org.whispersystems.curve25519.Curve25519
@ -32,11 +33,11 @@ object MessageReceiverDecryption {
return Pair(ByteArray(1), result.sender) // TODO: Return real plaintext return Pair(ByteArray(1), result.sender) // TODO: Return real plaintext
}*/ }*/
internal fun decryptWithSessionProtocol(envelope: SignalServiceProtos.Envelope): Pair<ByteArray, String> { internal fun decryptWithSessionProtocol(ciphertext: ByteArray, x25519KeyPair: ECKeyPair): Pair<ByteArray, String> {
return MessagingConfiguration.shared.sessionProtocol.decrypt(SignalServiceEnvelope(envelope)) return MessagingConfiguration.shared.sessionProtocol.decrypt(ciphertext, x25519KeyPair)
} }
internal fun decryptWithSharedSenderKeys(envelope: SignalServiceProtos.Envelope): Pair<ByteArray, String> { /*internal fun decryptWithSharedSenderKeys(envelope: SignalServiceProtos.Envelope): Pair<ByteArray, String> {
// 1. ) Check preconditions // 1. ) Check preconditions
val groupPublicKey = envelope.source val groupPublicKey = envelope.source
if (!GroupUtil.isClosedGroup(groupPublicKey)) { throw Error.InvalidGroupPublicKey } if (!GroupUtil.isClosedGroup(groupPublicKey)) { throw Error.InvalidGroupPublicKey }
@ -61,5 +62,5 @@ object MessageReceiverDecryption {
val plaintext = SharedSenderKeysImplementation.shared.decrypt(closedGroupCiphertextMessage.ivAndCiphertext, groupPublicKey, senderPublicKey, closedGroupCiphertextMessage.keyIndex) val plaintext = SharedSenderKeysImplementation.shared.decrypt(closedGroupCiphertextMessage.ivAndCiphertext, groupPublicKey, senderPublicKey, closedGroupCiphertextMessage.keyIndex)
// 6. ) Return // 6. ) Return
return Pair(plaintext, senderPublicKey) return Pair(plaintext, senderPublicKey)
} }*/
} }

View File

@ -23,6 +23,7 @@ import org.session.libsignal.service.api.messages.SignalServiceAttachment
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.internal.util.Base64 import org.session.libsignal.service.internal.util.Base64
import org.session.libsignal.service.loki.api.crypto.ProofOfWork import org.session.libsignal.service.loki.api.crypto.ProofOfWork
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
object MessageSender { object MessageSender {
@ -32,7 +33,10 @@ object MessageSender {
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 ProofOfWorkCalculationFailed : Error("Proof of work calculation failed.")
object NoUserPublicKey : Error("Couldn't find user key pair.") object NoUserX25519KeyPair : Error("Couldn't find user X25519 key pair.")
object NoUserED25519KeyPair : Error("Couldn't find user ED25519 key pair.")
object SigningFailed : Error("Couldn't sign message.")
object EncryptionFailed : Error("Couldn't encrypt message.")
// 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.")
@ -71,7 +75,7 @@ object MessageSender {
var snodeMessage: SnodeMessage? = null var snodeMessage: SnodeMessage? = null
// 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 */ message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() } /* Visible messages will already have their sent timestamp set */
message.sender = storage.getUserPublicKey() message.sender = userPublicKey
try { try {
when (destination) { when (destination) {
is Destination.Contact -> message.recipient = destination.publicKey is Destination.Contact -> message.recipient = destination.publicKey
@ -117,7 +121,10 @@ object MessageSender {
val ciphertext: ByteArray val ciphertext: ByteArray
when (destination) { when (destination) {
is Destination.Contact -> ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, destination.publicKey) is Destination.Contact -> ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, destination.publicKey)
is Destination.ClosedGroup -> ciphertext = MessageSenderEncryption.encryptWithSharedSenderKeys(plaintext, destination.groupPublicKey) is Destination.ClosedGroup -> {
val encryptionKeyPair = MessagingConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair(destination.groupPublicKey)
ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, encryptionKeyPair.hexEncodedPublicKey)
}
is Destination.OpenGroup -> throw preconditionFailure is Destination.OpenGroup -> throw preconditionFailure
} }
// Wrap the result // Wrap the result

View File

@ -38,7 +38,7 @@ object MessageSenderEncryption {
return MessagingConfiguration.shared.sessionProtocol.encrypt(plaintext, recipientPublicKey) return MessagingConfiguration.shared.sessionProtocol.encrypt(plaintext, recipientPublicKey)
} }
internal fun encryptWithSharedSenderKeys(plaintext: ByteArray, groupPublicKey: String): ByteArray { /*internal fun encryptWithSharedSenderKeys(plaintext: ByteArray, groupPublicKey: String): ByteArray {
// 1. ) Encrypt the data with the user's sender key // 1. ) Encrypt the data with the user's sender key
val userPublicKey = MessagingConfiguration.shared.storage.getUserPublicKey() ?: throw Error.NoUserPublicKey val userPublicKey = MessagingConfiguration.shared.storage.getUserPublicKey() ?: throw Error.NoUserPublicKey
val ciphertextAndKeyIndex = SharedSenderKeysImplementation.shared.encrypt(plaintext, groupPublicKey, userPublicKey) val ciphertextAndKeyIndex = SharedSenderKeysImplementation.shared.encrypt(plaintext, groupPublicKey, userPublicKey)
@ -52,5 +52,5 @@ object MessageSenderEncryption {
.setCiphertext(ByteString.copyFrom(intermediate.ciphertext)) .setCiphertext(ByteString.copyFrom(intermediate.ciphertext))
.setEphemeralPublicKey(ByteString.copyFrom(intermediate.ephemeralPublicKey)) .setEphemeralPublicKey(ByteString.copyFrom(intermediate.ephemeralPublicKey))
.build().toByteArray() .build().toByteArray()
} }*/
} }