mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Fix / sending secret encryption + refactoring
This commit is contained in:
parent
4f70c40b1a
commit
e36367c040
@ -282,10 +282,10 @@ class CommonTestHelper(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun waitWithLatch(block: (CountDownLatch) -> Unit) {
|
||||
fun waitWithLatch(timout: Long? = TestConstants.timeOutMillis, block: (CountDownLatch) -> Unit) {
|
||||
val latch = CountDownLatch(1)
|
||||
block(latch)
|
||||
await(latch)
|
||||
await(latch, timout)
|
||||
}
|
||||
|
||||
// Transform a method with a MatrixCallback to a synchronous method
|
||||
|
@ -216,7 +216,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
|
||||
aliceVerificationService1.addListener(object : VerificationService.Listener {
|
||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||
Log.d("TEST", "AA: tx incoming?:${tx.isIncoming} state ${tx.state}")
|
||||
Log.d("#TEST", "AA: tx incoming?:${tx.isIncoming} state ${tx.state}")
|
||||
if (tx is SasVerificationTransaction) {
|
||||
if (tx.state == VerificationTxState.OnStarted) {
|
||||
(tx as IncomingSasVerificationTransaction).performAccept()
|
||||
@ -231,7 +231,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
|
||||
aliceVerificationService2.addListener(object : VerificationService.Listener {
|
||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||
Log.d("TEST", "BB: tx incoming?:${tx.isIncoming} state ${tx.state}")
|
||||
Log.d("#TEST", "BB: tx incoming?:${tx.isIncoming} state ${tx.state}")
|
||||
if (tx is SasVerificationTransaction) {
|
||||
if (tx.state == VerificationTxState.ShortCodeReady) {
|
||||
session2ShortCode = tx.getDecimalCodeRepresentation()
|
||||
@ -246,42 +246,26 @@ class KeyShareTests : InstrumentedTest {
|
||||
?: "", txId)
|
||||
|
||||
|
||||
waitWithLatch { latch ->
|
||||
retryPeriodicallyWithLatch(latch) {
|
||||
mTestHelper.waitWithLatch { latch ->
|
||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.credentials.deviceId ?: "")?.isVerified == true
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull(session1ShortCode)
|
||||
Log.d("TEST", "session1ShortCode: $session1ShortCode")
|
||||
Log.d("#TEST", "session1ShortCode: $session1ShortCode")
|
||||
assertNotNull(session2ShortCode)
|
||||
Log.d("TEST", "session2ShortCode: $session2ShortCode")
|
||||
Log.d("#TEST", "session2ShortCode: $session2ShortCode")
|
||||
assertEquals(session1ShortCode, session2ShortCode)
|
||||
|
||||
// SSK and USK private keys should have been shared
|
||||
|
||||
waitWithLatch(300_000) { latch ->
|
||||
retryPeriodicallyWithLatch(latch) {
|
||||
mTestHelper.waitWithLatch(60_000) { latch ->
|
||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
Log.d("#TEST", "CAN XS :${ aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}")
|
||||
aliceSession2.cryptoService().crossSigningService().canCrossSign()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
|
||||
GlobalScope.launch {
|
||||
while (true) {
|
||||
delay(1000)
|
||||
if (condition()) {
|
||||
latch.countDown()
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun waitWithLatch(timeout: Long? = null, block: (CountDownLatch) -> Unit) {
|
||||
val latch = CountDownLatch(1)
|
||||
block(latch)
|
||||
mTestHelper.await(latch, timeout)
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ enum class GossipingRequestState {
|
||||
NONE,
|
||||
PENDING,
|
||||
REJECTED,
|
||||
ACCEPTING,
|
||||
ACCEPTED,
|
||||
FAILED_TO_ACCEPTED,
|
||||
// USER_REJECTED,
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import androidx.work.BackoffPolicy
|
||||
import androidx.work.Data
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.ListenableWorker
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.util.CancelableWork
|
||||
import im.vector.matrix.android.internal.worker.startChain
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
internal class GossipingWorkManager @Inject constructor(
|
||||
private val workManagerProvider: WorkManagerProvider
|
||||
) {
|
||||
|
||||
inline fun <reified W : ListenableWorker> createWork(data: Data, startChain: Boolean): OneTimeWorkRequest {
|
||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<W>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.startChain(startChain)
|
||||
.setInputData(data)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
// Prevent sending queue to stay broken after app restart
|
||||
// The unique queue id will stay the same as long as this object is instanciated
|
||||
val queueSuffixApp = System.currentTimeMillis()
|
||||
|
||||
fun postWork(workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND): Cancelable {
|
||||
workManagerProvider.workManager
|
||||
.beginUniqueWork(this::class.java.name + "_$queueSuffixApp", policy, workRequest)
|
||||
.enqueue()
|
||||
|
||||
return CancelableWork(workManagerProvider.workManager, workRequest.id)
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.auth.data.sessionId
|
||||
import im.vector.matrix.android.api.crypto.MXCryptoConfig
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
@ -27,16 +28,19 @@ import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.GossipingDefaultContent
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.GossipingToDeviceObject
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.di.SessionId
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
@SessionId private val sessionId: String,
|
||||
private val credentials: Credentials,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val cryptoConfig: MXCryptoConfig,
|
||||
private val secretSecretCryptoProvider: ShareSecretCryptoProvider,
|
||||
private val gossipingWorkManager: GossipingWorkManager,
|
||||
private val roomDecryptorProvider: RoomDecryptorProvider) {
|
||||
|
||||
// list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations
|
||||
@ -84,7 +88,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
* @param event the announcement event.
|
||||
*/
|
||||
fun onGossipingRequestEvent(event: Event) {
|
||||
Timber.v("## onGossipingRequestEvent type ${event.type} from user ${event.senderId}")
|
||||
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} from user ${event.senderId}")
|
||||
val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>()
|
||||
val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
||||
when (roomKeyShare?.action) {
|
||||
@ -93,7 +97,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
IncomingSecretShareRequest.fromEvent(event)?.let {
|
||||
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
|
||||
// ignore, it was sent by me as *
|
||||
Timber.v("## onGossipingRequestEvent type ${event.type} ignore remote echo")
|
||||
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
|
||||
} else {
|
||||
// save in DB
|
||||
cryptoStore.storeIncomingGossipingRequest(it, ageLocalTs)
|
||||
@ -104,7 +108,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
IncomingRoomKeyRequest.fromEvent(event)?.let {
|
||||
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
|
||||
// ignore, it was sent by me as *
|
||||
Timber.v("## onGossipingRequestEvent type ${event.type} ignore remote echo")
|
||||
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
|
||||
} else {
|
||||
cryptoStore.storeIncomingGossipingRequest(it, ageLocalTs)
|
||||
receivedGossipingRequests.add(it)
|
||||
@ -118,7 +122,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Timber.e("## onGossipingRequestEvent() : unsupported action ${roomKeyShare?.action}")
|
||||
Timber.e("## GOSSIP onGossipingRequestEvent() : unsupported action ${roomKeyShare?.action}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,7 +133,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
* It must be called on CryptoThread
|
||||
*/
|
||||
fun processReceivedGossipingRequests() {
|
||||
Timber.v("## processReceivedGossipingRequests()")
|
||||
Timber.v("## GOSSIP processReceivedGossipingRequests()")
|
||||
|
||||
val roomKeyRequestsToProcess = receivedGossipingRequests.toList()
|
||||
receivedGossipingRequests.clear()
|
||||
@ -151,7 +155,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
}
|
||||
|
||||
receivedRequestCancellations?.forEach { request ->
|
||||
Timber.v("## processReceivedGossipingRequests() : m.room_key_request cancellation $request")
|
||||
Timber.v("## GOSSIP processReceivedGossipingRequests() : m.room_key_request cancellation $request")
|
||||
// we should probably only notify the app of cancellations we told it
|
||||
// about, but we don't currently have a record of that, so we just pass
|
||||
// everything through.
|
||||
@ -180,10 +184,10 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
val roomId = body!!.roomId
|
||||
val alg = body.algorithm
|
||||
|
||||
Timber.v("## processIncomingRoomKeyRequest from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}")
|
||||
Timber.v("## GOSSIP processIncomingRoomKeyRequest from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}")
|
||||
if (userId == null || credentials.userId != userId) {
|
||||
// TODO: determine if we sent this device the keys already: in
|
||||
Timber.w("## processReceivedGossipingRequests() : Ignoring room key request from other user for now")
|
||||
Timber.w("## GOSSIP processReceivedGossipingRequests() : Ignoring room key request from other user for now")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
return
|
||||
}
|
||||
@ -192,18 +196,18 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
// the keys for the requested events, and can drop the requests.
|
||||
val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg)
|
||||
if (null == decryptor) {
|
||||
Timber.w("## processReceivedGossipingRequests() : room key request for unknown $alg in room $roomId")
|
||||
Timber.w("## GOSSIP processReceivedGossipingRequests() : room key request for unknown $alg in room $roomId")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
return
|
||||
}
|
||||
if (!decryptor.hasKeysForKeyRequest(request)) {
|
||||
Timber.w("## processReceivedGossipingRequests() : room key request for unknown session ${body.sessionId!!}")
|
||||
Timber.w("## GOSSIP processReceivedGossipingRequests() : room key request for unknown session ${body.sessionId!!}")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
return
|
||||
}
|
||||
|
||||
if (credentials.deviceId == deviceId && credentials.userId == userId) {
|
||||
Timber.v("## processReceivedGossipingRequests() : oneself device - ignored")
|
||||
Timber.v("## GOSSIP processReceivedGossipingRequests() : oneself device - ignored")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
return
|
||||
}
|
||||
@ -218,13 +222,13 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
val device = cryptoStore.getUserDevice(userId, deviceId!!)
|
||||
if (device != null) {
|
||||
if (device.isVerified) {
|
||||
Timber.v("## processReceivedGossipingRequests() : device is already verified: sharing keys")
|
||||
Timber.v("## GOSSIP processReceivedGossipingRequests() : device is already verified: sharing keys")
|
||||
request.share?.run()
|
||||
return
|
||||
}
|
||||
|
||||
if (device.isBlocked) {
|
||||
Timber.v("## processReceivedGossipingRequests() : device is blocked -> ignored")
|
||||
Timber.v("## GOSSIP processReceivedGossipingRequests() : device is blocked -> ignored")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
return
|
||||
}
|
||||
@ -245,30 +249,30 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
private fun processIncomingSecretShareRequest(request: IncomingSecretShareRequest) {
|
||||
val secretName = request.secretName ?: return Unit.also {
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
Timber.v("## processIncomingSecretShareRequest() : Missing secret name")
|
||||
Timber.v("## GOSSIP processIncomingSecretShareRequest() : Missing secret name")
|
||||
}
|
||||
|
||||
val userId = request.userId
|
||||
if (userId == null || credentials.userId != userId) {
|
||||
Timber.e("## processIncomingSecretShareRequest() : Ignoring secret share request from other users")
|
||||
Timber.e("## GOSSIP processIncomingSecretShareRequest() : Ignoring secret share request from other users")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
return
|
||||
}
|
||||
|
||||
val deviceId = request.deviceId
|
||||
?: return Unit.also {
|
||||
Timber.e("## processIncomingSecretShareRequest() : Malformed request, no ")
|
||||
Timber.e("## GOSSIP processIncomingSecretShareRequest() : Malformed request, no ")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
}
|
||||
|
||||
val device = cryptoStore.getUserDevice(userId, deviceId)
|
||||
?: return Unit.also {
|
||||
Timber.e("## processIncomingSecretShareRequest() : Received secret share request from unknown device ${request.deviceId}")
|
||||
Timber.e("## GOSSIP processIncomingSecretShareRequest() : Received secret share request from unknown device ${request.deviceId}")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
}
|
||||
|
||||
if (!device.isVerified || device.isBlocked) {
|
||||
Timber.v("## processIncomingSecretShareRequest() : Ignoring secret share request from untrusted/blocked session $device")
|
||||
Timber.v("## GOSSIP processIncomingSecretShareRequest() : Ignoring secret share request from untrusted/blocked session $device")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
return
|
||||
}
|
||||
@ -281,13 +285,20 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
USER_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.user
|
||||
else -> null
|
||||
}?.let { secretValue ->
|
||||
// TODO check if locally trusted and not outdated
|
||||
Timber.i("## processIncomingSecretShareRequest() : Sharing secret $secretName with $device locally trusted")
|
||||
Timber.i("## GOSSIP processIncomingSecretShareRequest() : Sharing secret $secretName with $device locally trusted")
|
||||
if (isDeviceLocallyVerified == true && hasBeenVerifiedLessThanFiveMinutesFromNow(deviceId)) {
|
||||
secretSecretCryptoProvider.shareSecretWithDevice(request, secretValue)
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
|
||||
|
||||
val params = SendGossipWorker.Params(
|
||||
sessionId = sessionId,
|
||||
secretValue = secretValue,
|
||||
request = request
|
||||
)
|
||||
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING)
|
||||
val workRequest = gossipingWorkManager.createWork<SendGossipWorker>(WorkerParamsFactory.toData(params), true)
|
||||
gossipingWorkManager.postWork(workRequest)
|
||||
} else {
|
||||
Timber.v("## processIncomingSecretShareRequest() : Can't share secret $secretName with $device, verification too old")
|
||||
Timber.v("## GOSSIP processIncomingSecretShareRequest() : Can't share secret $secretName with $device, verification too old")
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
}
|
||||
return
|
||||
@ -298,7 +309,16 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
}
|
||||
|
||||
request.share = { secretValue ->
|
||||
secretSecretCryptoProvider.shareSecretWithDevice(request, secretValue)
|
||||
|
||||
val params = SendGossipWorker.Params(
|
||||
sessionId = userId,
|
||||
secretValue = secretValue,
|
||||
request = request
|
||||
)
|
||||
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTING)
|
||||
val workRequest = gossipingWorkManager.createWork<SendGossipWorker>(WorkerParamsFactory.toData(params), true)
|
||||
gossipingWorkManager.postWork(workRequest)
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
|
||||
}
|
||||
|
||||
@ -333,7 +353,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
return
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## onRoomKeyRequest() failed")
|
||||
Timber.e(e, "## GOSSIP onRoomKeyRequest() failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -352,7 +372,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
try {
|
||||
listener.onRoomKeyRequestCancellation(request)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## onRoomKeyRequestCancellation() failed")
|
||||
Timber.e(e, "## GOSSIP onRoomKeyRequestCancellation() failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,27 +17,17 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import androidx.work.BackoffPolicy
|
||||
import androidx.work.Data
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.ListenableWorker
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.di.SessionId
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.util.CancelableWork
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import im.vector.matrix.android.internal.worker.startChain
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
@ -46,7 +36,7 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val cryptoCoroutineScope: CoroutineScope,
|
||||
private val workManagerProvider: WorkManagerProvider) {
|
||||
private val gossipingWorkManager: GossipingWorkManager) {
|
||||
|
||||
/**
|
||||
* Send off a room key request, if we haven't already done so.
|
||||
@ -65,7 +55,7 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
cryptoStore.getOrAddOutgoingRoomKeyRequest(requestBody, recipients)?.let {
|
||||
// Don't resend if it's already done, you need to cancel first (reRequest)
|
||||
if (it.state == OutgoingGossipingRequestState.SENDING || it.state == OutgoingGossipingRequestState.SENT) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : we already request for that session: $it")
|
||||
Timber.v("## GOSSIP sendOutgoingRoomKeyRequest() : we already request for that session: $it")
|
||||
return@launch
|
||||
}
|
||||
|
||||
@ -82,7 +72,7 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
cryptoStore.getOrAddOutgoingSecretShareRequest(secretName, recipients)?.let {
|
||||
// TODO check if there is already one that is being sent?
|
||||
if (it.state == OutgoingGossipingRequestState.SENDING || it.state == OutgoingGossipingRequestState.SENT) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : we already request for that session: $it")
|
||||
Timber.v("## GOSSIP sendSecretShareRequest() : we already request for that session: $it")
|
||||
return@launch
|
||||
}
|
||||
|
||||
@ -123,7 +113,7 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
val req = cryptoStore.getOutgoingRoomKeyRequest(requestBody)
|
||||
?: // no request was made for this key
|
||||
return Unit.also {
|
||||
Timber.v("## cancelRoomKeyRequest() Unknown request")
|
||||
Timber.v("## GOSSIP cancelRoomKeyRequest() Unknown request")
|
||||
}
|
||||
|
||||
sendOutgoingRoomKeyRequestCancellation(req, andResend)
|
||||
@ -135,7 +125,7 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
* @param request the request
|
||||
*/
|
||||
private fun sendOutgoingGossipingRequest(request: OutgoingGossipingRequest) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : Requesting keys $request")
|
||||
Timber.v("## GOSSIP sendOutgoingRoomKeyRequest() : Requesting keys $request")
|
||||
|
||||
val params = SendGossipRequestWorker.Params(
|
||||
sessionId = sessionId,
|
||||
@ -143,8 +133,8 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
secretShareRequest = request as? OutgoingSecretRequest
|
||||
)
|
||||
cryptoStore.updateOutgoingGossipingRequestState(request.requestId, OutgoingGossipingRequestState.SENDING)
|
||||
val workRequest = createWork<SendGossipRequestWorker>(WorkerParamsFactory.toData(params), true)
|
||||
postWork(workRequest)
|
||||
val workRequest = gossipingWorkManager.createWork<SendGossipRequestWorker>(WorkerParamsFactory.toData(params), true)
|
||||
gossipingWorkManager.postWork(workRequest)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,33 +147,16 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
val params = CancelGossipRequestWorker.Params.fromRequest(sessionId, request)
|
||||
cryptoStore.updateOutgoingGossipingRequestState(request.requestId, OutgoingGossipingRequestState.CANCELLING)
|
||||
|
||||
val workRequest = createWork<CancelGossipRequestWorker>(WorkerParamsFactory.toData(params), true)
|
||||
postWork(workRequest)
|
||||
val workRequest = gossipingWorkManager.createWork<CancelGossipRequestWorker>(WorkerParamsFactory.toData(params), true)
|
||||
gossipingWorkManager.postWork(workRequest)
|
||||
|
||||
if (resend) {
|
||||
val reSendParams = SendGossipRequestWorker.Params(
|
||||
sessionId = sessionId,
|
||||
keyShareRequest = request.copy(requestId = LocalEcho.createLocalEchoId())
|
||||
)
|
||||
val reSendWorkRequest = createWork<SendGossipRequestWorker>(WorkerParamsFactory.toData(reSendParams), true)
|
||||
postWork(reSendWorkRequest)
|
||||
val reSendWorkRequest = gossipingWorkManager.createWork<SendGossipRequestWorker>(WorkerParamsFactory.toData(reSendParams), true)
|
||||
gossipingWorkManager.postWork(reSendWorkRequest)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified W : ListenableWorker> createWork(data: Data, startChain: Boolean): OneTimeWorkRequest {
|
||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<W>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.startChain(startChain)
|
||||
.setInputData(data)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun postWork(workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND): Cancelable {
|
||||
workManagerProvider.workManager
|
||||
.beginUniqueWork(this::class.java.name, policy, workRequest)
|
||||
.enqueue()
|
||||
|
||||
return CancelableWork(workManagerProvider.workManager, workRequest.id)
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
||||
import im.vector.matrix.android.api.session.events.model.toContent
|
||||
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
||||
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.event.SecretSendEventContent
|
||||
@ -53,8 +54,8 @@ internal class SendGossipWorker(context: Context,
|
||||
@Inject lateinit var cryptoStore: IMXCryptoStore
|
||||
@Inject lateinit var eventBus: EventBus
|
||||
@Inject lateinit var credentials: Credentials
|
||||
// @Inject lateinit var secretSecretCryptoProvider: ShareSecretCryptoProvider
|
||||
@Inject lateinit var messageEncrypter: MessageEncrypter
|
||||
@Inject lateinit var ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val errorOutputData = Data.Builder().putBoolean("failed", true).build()
|
||||
@ -84,11 +85,26 @@ internal class SendGossipWorker(context: Context,
|
||||
Timber.e("Unknown deviceInfo, cannot send message, sessionId: ${params.request.deviceId}")
|
||||
}
|
||||
|
||||
val payloadJson = mutableMapOf<String, Any>("type" to EventType.SEND_SECRET)
|
||||
payloadJson["content"] = toDeviceContent.toContent()
|
||||
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
|
||||
val devicesByUser = mapOf(requestingUserId to listOf(deviceInfo))
|
||||
val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
val olmSessionResult = usersDeviceMap.getObject(requestingUserId, requestingDeviceId)
|
||||
if (olmSessionResult?.sessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
return Result.success(errorOutputData).also {
|
||||
cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.FAILED_TO_ACCEPTED)
|
||||
Timber.e("no session with this device, probably because there were no one-time keys.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val payloadJson = mapOf(
|
||||
"type" to EventType.SEND_SECRET,
|
||||
"content" to toDeviceContent.toContent()
|
||||
)
|
||||
|
||||
try {
|
||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
sendToDeviceMap.setObject(requestingUserId, requestingDeviceId, encodedPayload)
|
||||
@ -108,7 +124,7 @@ internal class SendGossipWorker(context: Context,
|
||||
try {
|
||||
sendToDeviceTask.execute(
|
||||
SendToDeviceTask.Params(
|
||||
eventType = eventType,
|
||||
eventType = EventType.ENCRYPTED,
|
||||
contentMap = sendToDeviceMap,
|
||||
transactionId = localId
|
||||
)
|
||||
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toContent
|
||||
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.event.SecretSendEventContent
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class ShareSecretCryptoProvider @Inject constructor(
|
||||
val messageEncrypter: MessageEncrypter,
|
||||
val sendToDeviceTask: SendToDeviceTask,
|
||||
val deviceListManager: DeviceListManager,
|
||||
@UserId val myUserId: String,
|
||||
private val olmDecryptionFactory: MXOlmDecryptionFactory,
|
||||
val cryptoCoroutineScope: CoroutineScope,
|
||||
val cryptoStore: IMXCryptoStore,
|
||||
val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||
) {
|
||||
fun shareSecretWithDevice(request: IncomingSecretShareRequest, secretValue: String) {
|
||||
val userId = request.userId ?: return
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
// runCatching { deviceListManager.downloadKeys(listOf(userId), false) }
|
||||
// .mapCatching {
|
||||
val deviceId = request.deviceId
|
||||
val deviceInfo = cryptoStore.getUserDevice(userId, deviceId ?: "") ?: return@launch
|
||||
|
||||
|
||||
Timber.i("## shareSecretWithDevice() : sharing secret ${request.secretName} with device $userId:$deviceId")
|
||||
|
||||
val payloadJson = mutableMapOf<String, Any>("type" to EventType.SEND_SECRET)
|
||||
val secretContent = SecretSendEventContent(
|
||||
requestId = request.requestId ?: "",
|
||||
secretValue = secretValue
|
||||
)
|
||||
payloadJson["content"] = secretContent.toContent()
|
||||
|
||||
cryptoStore.saveGossipingEvent(Event(
|
||||
type = EventType.SEND_SECRET,
|
||||
content = secretContent.toContent(),
|
||||
senderId = myUserId
|
||||
))
|
||||
|
||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||
Timber.i("## shareSecretWithDevice() : sending to $userId:$deviceId")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
fun decryptEvent(event: Event): MXEventDecryptionResult {
|
||||
return olmDecryptionFactory.create().decryptEvent(event, ShareSecretCryptoProvider::class.java.name ?: "")
|
||||
}
|
||||
}
|
@ -596,7 +596,8 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||
}
|
||||
|
||||
override fun canCrossSign(): Boolean {
|
||||
return cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null
|
||||
return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null
|
||||
&& cryptoStore.getCrossSigningPrivateKeys()?.user != null
|
||||
}
|
||||
|
||||
override fun trustUser(otherUserId: String, callback: MatrixCallback<Unit>) {
|
||||
|
Loading…
Reference in New Issue
Block a user