Implement the new protocol
parent
455471b20e
commit
06f547dc88
@ -1,31 +0,0 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||
|
||||
class EphemeralMessage private constructor(val data: Map<*, *>) {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun create(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey ))
|
||||
|
||||
@JvmStatic
|
||||
fun createDeviceUnlinkingRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "unpairingRequest" to true ))
|
||||
|
||||
@JvmStatic
|
||||
fun createSessionRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "friendRequest" to true, "sessionRequest" to true ))
|
||||
|
||||
internal fun parse(serialized: String): EphemeralMessage {
|
||||
val data = JsonUtil.fromJson(serialized, Map::class.java) ?: throw IllegalArgumentException("Couldn't parse string to JSON")
|
||||
return EphemeralMessage(data)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> get(key: String, defaultValue: T): T {
|
||||
return data[key] as? T ?: defaultValue
|
||||
}
|
||||
|
||||
fun serialize(): String {
|
||||
return JsonUtil.toJson(data)
|
||||
}
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||
import org.thoughtcrime.securesms.logging.Log
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class PushEphemeralMessageSendJob private constructor(parameters: Parameters, private val message: EphemeralMessage) : BaseJob(parameters) {
|
||||
|
||||
companion object {
|
||||
private const val KEY_MESSAGE = "message"
|
||||
const val KEY = "PushBackgroundMessageSendJob"
|
||||
}
|
||||
|
||||
constructor(message: EphemeralMessage) : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(1)
|
||||
.build(),
|
||||
message)
|
||||
|
||||
override fun serialize(): Data {
|
||||
return Data.Builder()
|
||||
.putString(KEY_MESSAGE, message.serialize())
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun getFactoryKey(): String { return KEY }
|
||||
|
||||
public override fun onRun() {
|
||||
val recipient = message.get<String?>("recipient", null) ?: throw IllegalStateException()
|
||||
val dataMessage = SignalServiceDataMessage.newBuilder().withTimestamp(System.currentTimeMillis())
|
||||
// Attach a pre key bundle if needed
|
||||
if (message.get("friendRequest", false)) {
|
||||
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient)
|
||||
dataMessage.withPreKeyBundle(bundle)
|
||||
}
|
||||
// Set flags if needed (these are mutually exclusive)
|
||||
when {
|
||||
message.get("unpairingRequest", false) -> dataMessage.asDeviceUnlinkingRequest(true)
|
||||
message.get("sessionRequest", false) -> dataMessage.asSessionRequest(true)
|
||||
}
|
||||
// Send the message
|
||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||
val address = SignalServiceAddress(recipient)
|
||||
try {
|
||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(recipient), false))
|
||||
messageSender.sendMessage(0, address, udAccess, dataMessage.build()) // The message ID doesn't matter
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to send background message to: $recipient due to error: $e.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onShouldRetry(e: Exception): Boolean {
|
||||
// Disable since we have our own retrying
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCanceled() { }
|
||||
|
||||
class Factory : Job.Factory<PushEphemeralMessageSendJob> {
|
||||
|
||||
override fun create(parameters: Parameters, data: Data): PushEphemeralMessageSendJob {
|
||||
try {
|
||||
val messageJSON = data.getString(KEY_MESSAGE)
|
||||
return PushEphemeralMessageSendJob(parameters, EphemeralMessage.parse(messageJSON))
|
||||
} catch (e: IOException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||
import org.thoughtcrime.securesms.logging.Log
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities
|
||||
import java.io.IOException
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class PushSessionRequestMessageSendJob private constructor(parameters: Parameters, private val publicKey: String) : BaseJob(parameters) {
|
||||
|
||||
companion object {
|
||||
const val KEY = "PushSessionRequestMessageSendJob"
|
||||
}
|
||||
|
||||
constructor(publicKey: String) : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(1)
|
||||
.build(),
|
||||
publicKey)
|
||||
|
||||
override fun serialize(): Data {
|
||||
return Data.Builder().putString("publicKey", publicKey).build()
|
||||
}
|
||||
|
||||
override fun getFactoryKey(): String { return KEY }
|
||||
|
||||
public override fun onRun() {
|
||||
// Prepare
|
||||
val contentMessage = SignalServiceProtos.Content.newBuilder()
|
||||
// Attach the pre key bundle message
|
||||
val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder()
|
||||
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return
|
||||
preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize())
|
||||
preKeyBundleMessage.deviceId = preKeyBundle.deviceId
|
||||
preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId
|
||||
preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId
|
||||
preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize())
|
||||
preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize())
|
||||
preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature)
|
||||
contentMessage.preKeyBundleMessage = preKeyBundleMessage.build()
|
||||
// Attach the null message
|
||||
val nullMessage = SignalServiceProtos.NullMessage.newBuilder()
|
||||
val sr = SecureRandom()
|
||||
val paddingSize = sr.nextInt(512)
|
||||
val padding = ByteArray(paddingSize)
|
||||
sr.nextBytes(padding)
|
||||
nullMessage.padding = ByteString.copyFrom(padding)
|
||||
contentMessage.nullMessage = nullMessage.build()
|
||||
// Send the result
|
||||
val serializedContentMessage = contentMessage.build().toByteArray()
|
||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||
val address = SignalServiceAddress(publicKey)
|
||||
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
|
||||
val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest)
|
||||
try {
|
||||
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
|
||||
Date().time, serializedContentMessage, false, ttl, false,
|
||||
true, false, false)
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to send session request to: $publicKey due to error: $e.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onShouldRetry(e: Exception): Boolean {
|
||||
// Disable since we have our own retrying
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCanceled() { }
|
||||
|
||||
class Factory : Job.Factory<PushSessionRequestMessageSendJob> {
|
||||
|
||||
override fun create(parameters: Parameters, data: Data): PushSessionRequestMessageSendJob {
|
||||
try {
|
||||
val publicKey = data.getString("publicKey")
|
||||
return PushSessionRequestMessageSendJob(parameters, publicKey)
|
||||
} catch (e: IOException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue