Fix / sending secret encryption + refactoring

This commit is contained in:
Valere 2020-03-23 16:27:17 +01:00
parent 4f70c40b1a
commit e36367c040
9 changed files with 150 additions and 180 deletions

View File

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

View File

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

View File

@ -25,6 +25,7 @@ enum class GossipingRequestState {
NONE,
PENDING,
REJECTED,
ACCEPTING,
ACCEPTED,
FAILED_TO_ACCEPTED,
// USER_REJECTED,

View File

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

View File

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

View File

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

View File

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

View File

@ -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 ?: "")
}
}

View File

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