Test integration of rust shield states
This commit is contained in:
parent
5f185c51e7
commit
238d10d4cb
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c87e7bd9c5e7c623708958eebddbc58c26269b0c1d81e0dc3510a657a28a6515
|
||||
size 20719656
|
||||
oid sha256:c025a7047c3276b09f8cfaddc6323688b4c0174385148aa20f21080ba74d236d
|
||||
size 32306804
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.crypto
|
||||
|
||||
import android.util.Log
|
||||
import androidx.test.filters.LargeTest
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.FixMethodOrder
|
||||
|
@ -98,32 +99,34 @@ class E2eeConfigTest : InstrumentedTest {
|
|||
|
||||
val roomAlicePOV = cryptoTestData.firstSession.roomService().getRoom(cryptoTestData.roomId)!!
|
||||
|
||||
val beforeMessage = testHelper.sendTextMessage(roomAlicePOV, "you can read", 1).first()
|
||||
val beforeMessage = testHelper.sendMessageInRoom(roomAlicePOV, "you can read")
|
||||
|
||||
val roomBobPOV = cryptoTestData.secondSession!!.roomService().getRoom(cryptoTestData.roomId)!!
|
||||
// ensure other received
|
||||
testHelper.retryPeriodically {
|
||||
roomBobPOV.timelineService().getTimelineEvent(beforeMessage.eventId) != null
|
||||
}
|
||||
Log.v("#E2E TEST", "Wait for bob to get the message")
|
||||
testHelper.ensureMessage(roomBobPOV, beforeMessage) { true }
|
||||
|
||||
Log.v("#E2E TEST", "ensure bob Can Decrypt first message")
|
||||
cryptoTestHelper.ensureCanDecrypt(
|
||||
listOf(beforeMessage.eventId),
|
||||
listOf(beforeMessage),
|
||||
cryptoTestData.secondSession!!,
|
||||
cryptoTestData.roomId,
|
||||
listOf(beforeMessage.getLastMessageContent()!!.body)
|
||||
listOf("you can read")
|
||||
)
|
||||
|
||||
Log.v("#E2E TEST", "setRoomBlockUnverifiedDevices true")
|
||||
cryptoTestData.firstSession.cryptoService().setRoomBlockUnverifiedDevices(cryptoTestData.roomId, true)
|
||||
|
||||
val afterMessage = testHelper.sendTextMessage(roomAlicePOV, "you are blocked", 1).first()
|
||||
Log.v("#E2E TEST", "let alice send the message")
|
||||
val afterMessage = testHelper.sendMessageInRoom(roomAlicePOV, "you are blocked")
|
||||
|
||||
// ensure received
|
||||
testHelper.retryPeriodically {
|
||||
cryptoTestData.secondSession?.getRoom(cryptoTestData.roomId)?.timelineService()?.getTimelineEvent(afterMessage.eventId)?.root != null
|
||||
}
|
||||
|
||||
Log.v("#E2E TEST", "Ensure bob received second message")
|
||||
testHelper.ensureMessage(roomBobPOV, afterMessage) { true }
|
||||
|
||||
cryptoTestHelper.ensureCannotDecrypt(
|
||||
listOf(afterMessage.eventId),
|
||||
listOf(afterMessage),
|
||||
cryptoTestData.secondSession!!,
|
||||
cryptoTestData.roomId,
|
||||
MXCryptoError.ErrorType.KEYS_WITHHELD
|
||||
|
|
|
@ -81,7 +81,7 @@ class WithHeldTests : InstrumentedTest {
|
|||
val timelineEvent = testHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first()
|
||||
|
||||
// await for bob unverified session to get the message
|
||||
testHelper.retryPeriodically {
|
||||
testHelper.retryWithBackoff {
|
||||
bobUnverifiedSession.getRoom(roomId)?.getTimelineEvent(timelineEvent.eventId) != null
|
||||
}
|
||||
|
||||
|
@ -106,24 +106,26 @@ class WithHeldTests : InstrumentedTest {
|
|||
bobUnverifiedSession.cryptoService().decryptEvent(eventBobPOV.root, "")
|
||||
}
|
||||
|
||||
// Let's see if the reply we got from bob first session is unverified
|
||||
testHelper.retryPeriodically {
|
||||
bobUnverifiedSession.cryptoService().getOutgoingRoomKeyRequests()
|
||||
.firstOrNull { it.sessionId == megolmSessionId }
|
||||
?.results
|
||||
?.firstOrNull { it.fromDevice == bobSession.sessionParams.deviceId }
|
||||
?.result
|
||||
?.let {
|
||||
it as? RequestResult.Failure
|
||||
}
|
||||
?.code == WithHeldCode.UNVERIFIED
|
||||
if (bobUnverifiedSession.cryptoService().supportsForwardedKeyWiththeld()) {
|
||||
// Let's see if the reply we got from bob first session is unverified
|
||||
testHelper.retryWithBackoff {
|
||||
bobUnverifiedSession.cryptoService().getOutgoingRoomKeyRequests()
|
||||
.firstOrNull { it.sessionId == megolmSessionId }
|
||||
?.results
|
||||
?.firstOrNull { it.fromDevice == bobSession.sessionParams.deviceId }
|
||||
?.result
|
||||
?.let {
|
||||
it as? RequestResult.Failure
|
||||
}
|
||||
?.code == WithHeldCode.UNVERIFIED
|
||||
}
|
||||
}
|
||||
// enable back sending to unverified
|
||||
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(false)
|
||||
|
||||
val secondEvent = testHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first()
|
||||
|
||||
testHelper.retryPeriodically {
|
||||
testHelper.retryWithBackoff {
|
||||
val ev = bobUnverifiedSession.getRoom(roomId)?.getTimelineEvent(secondEvent.eventId)
|
||||
// wait until it's decrypted
|
||||
ev?.root?.getClearType() == EventType.MESSAGE
|
||||
|
@ -234,9 +236,19 @@ class WithHeldTests : InstrumentedTest {
|
|||
// initialize to force request keys if missing
|
||||
cryptoTestHelper.initializeCrossSigning(bobSecondSession)
|
||||
|
||||
// Trust bob second device from Alice POV
|
||||
aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId)
|
||||
bobSecondSession.cryptoService().crossSigningService().trustDevice(aliceSession.sessionParams.deviceId)
|
||||
// // wait until alice downloaded the new device
|
||||
// testHelper.retryWithBackoff {
|
||||
// aliceSession.cryptoService().getUserDevices(bobSession.myUserId).any { it.deviceId == bobSecondSession.sessionParams.deviceId}
|
||||
// }
|
||||
//
|
||||
// // Trust bob second device from Alice POV
|
||||
// aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId)
|
||||
//
|
||||
// // wait until bob downloaded alice device
|
||||
// testHelper.retryWithBackoff {
|
||||
// bobSecondSession.cryptoService().getUserDevices(aliceSession.myUserId).any { it.deviceId == aliceSession.sessionParams.deviceId}
|
||||
// }
|
||||
// bobSecondSession.cryptoService().crossSigningService().trustDevice(aliceSession.sessionParams.deviceId)
|
||||
|
||||
var sessionId: String? = null
|
||||
// Check that the
|
||||
|
@ -252,10 +264,23 @@ class WithHeldTests : InstrumentedTest {
|
|||
timeLineEvent != null
|
||||
}
|
||||
|
||||
// Check that bob second session requested the key
|
||||
testHelper.retryPeriodically {
|
||||
val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!)
|
||||
wc?.code == WithHeldCode.UNAUTHORISED
|
||||
|
||||
mustFail(
|
||||
message = "This session should not be able to decrypt",
|
||||
failureBlock = { failure ->
|
||||
val type = (failure as MXCryptoError.Base).errorType
|
||||
val technicalMessage = failure.technicalMessage
|
||||
Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
|
||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
|
||||
}
|
||||
) {
|
||||
val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimelineEvent(eventId)
|
||||
bobSecondSession.cryptoService().decryptEvent(timeLineEvent!!.root, "")
|
||||
}
|
||||
// // Check that bob second session requested the key
|
||||
// testHelper.retryPeriodically {
|
||||
// val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!)
|
||||
// wc?.code == WithHeldCode.UNAUTHORISED
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,9 @@ interface CryptoService {
|
|||
*/
|
||||
fun supportsShareKeysOnInvite(): Boolean
|
||||
|
||||
fun supportsKeyWithheld(): Boolean
|
||||
fun supportsForwardedKeyWiththeld(): Boolean
|
||||
|
||||
/**
|
||||
* As per MSC3061.
|
||||
* If true will make it possible to share part of e2ee room history
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
|||
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MessageVerificationState
|
||||
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
|
@ -77,6 +78,8 @@ import org.matrix.rustcomponents.sdk.crypto.MegolmV1BackupKey
|
|||
import org.matrix.rustcomponents.sdk.crypto.Request
|
||||
import org.matrix.rustcomponents.sdk.crypto.RequestType
|
||||
import org.matrix.rustcomponents.sdk.crypto.RoomKeyCounts
|
||||
import org.matrix.rustcomponents.sdk.crypto.ShieldColor
|
||||
import org.matrix.rustcomponents.sdk.crypto.ShieldState
|
||||
import org.matrix.rustcomponents.sdk.crypto.setLogger
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
@ -180,7 +183,7 @@ internal class OlmMachine @Inject constructor(
|
|||
|
||||
val crossSigningVerified = when (val ownIdentity = getIdentity(userId())) {
|
||||
is OwnUserIdentity -> ownIdentity.trustsOurOwnDevice()
|
||||
else -> false
|
||||
else -> false
|
||||
}
|
||||
|
||||
return CryptoDeviceInfo(
|
||||
|
@ -437,7 +440,7 @@ internal class OlmMachine @Inject constructor(
|
|||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||
}
|
||||
val serializedEvent = adapter.toJson(event)
|
||||
val decrypted = inner.decryptRoomEvent(serializedEvent, event.roomId, false)
|
||||
val decrypted = inner.decryptRoomEvent(serializedEvent, event.roomId, false, false)
|
||||
|
||||
val deserializationAdapter =
|
||||
moshi.adapter<JsonDict>(Map::class.java)
|
||||
|
@ -449,16 +452,20 @@ internal class OlmMachine @Inject constructor(
|
|||
senderCurve25519Key = decrypted.senderCurve25519Key,
|
||||
claimedEd25519Key = decrypted.claimedEd25519Key,
|
||||
forwardingCurve25519KeyChain = decrypted.forwardingCurve25519Chain,
|
||||
messageVerificationState = decrypted.verificationState.fromInner(),
|
||||
messageVerificationState = decrypted.shieldState.toVerificationState(),
|
||||
)
|
||||
} catch (throwable: Throwable) {
|
||||
val reThrow = when (throwable) {
|
||||
is DecryptionException.MissingRoomKey -> {
|
||||
MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, throwable.message.orEmpty())
|
||||
if (throwable.withheldCode != null) {
|
||||
MXCryptoError.Base(MXCryptoError.ErrorType.KEYS_WITHHELD, throwable.withheldCode!!)
|
||||
} else {
|
||||
MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, throwable.error)
|
||||
}
|
||||
}
|
||||
is DecryptionException.Megolm -> {
|
||||
// TODO check if it's the correct binding?
|
||||
MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_MESSAGE_INDEX, throwable.message.orEmpty())
|
||||
MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_MESSAGE_INDEX, throwable.error)
|
||||
}
|
||||
is DecryptionException.Identifier -> {
|
||||
MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT, MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)
|
||||
|
@ -480,6 +487,24 @@ internal class OlmMachine @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun ShieldState.toVerificationState(): MessageVerificationState? {
|
||||
return when (this.color) {
|
||||
ShieldColor.GREEN -> MessageVerificationState.VERIFIED
|
||||
ShieldColor.RED -> {
|
||||
when (this.message) {
|
||||
"Encrypted by an unverified device." -> MessageVerificationState.UN_SIGNED_DEVICE
|
||||
"Encrypted by a device not verified by its owner." -> MessageVerificationState.UN_SIGNED_DEVICE_OF_VERIFIED_USER
|
||||
"Encrypted by an unknown or deleted device." -> MessageVerificationState.UNKNOWN_DEVICE
|
||||
else -> MessageVerificationState.UN_SIGNED_DEVICE
|
||||
}
|
||||
}
|
||||
ShieldColor.GRAY -> {
|
||||
MessageVerificationState.UNSAFE_SOURCE
|
||||
}
|
||||
ShieldColor.NONE -> null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the room key that was used to encrypt the given undecrypted event.
|
||||
*
|
||||
|
|
|
@ -727,9 +727,13 @@ internal class RustCryptoService @Inject constructor(
|
|||
|
||||
override fun supportsShareKeysOnInvite() = false
|
||||
|
||||
override fun supportsKeyWithheld() = true
|
||||
override fun supportsForwardedKeyWiththeld() = false
|
||||
|
||||
|
||||
override fun enableShareKeyOnInvite(enable: Boolean) {
|
||||
if (enable) {
|
||||
TODO("Enable share key on invite not implemented")
|
||||
if (enable && !supportsShareKeysOnInvite()) {
|
||||
throw java.lang.UnsupportedOperationException("Enable share key on invite not implemented in rust");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -872,10 +876,12 @@ internal class RustCryptoService @Inject constructor(
|
|||
}
|
||||
|
||||
override fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int> {
|
||||
// TODO not exposed in rust?
|
||||
return cryptoStore.getSharedWithInfo(roomId, sessionId)
|
||||
}
|
||||
|
||||
override fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent? {
|
||||
// TODO not exposed in rust.
|
||||
return cryptoStore.getWithHeldMegolmSession(roomId, sessionId)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.internal.crypto
|
||||
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MessageVerificationState
|
||||
import org.matrix.rustcomponents.sdk.crypto.VerificationState as InnerVerificationState
|
||||
|
||||
fun InnerVerificationState.fromInner(): MessageVerificationState {
|
||||
return when (this) {
|
||||
InnerVerificationState.VERIFIED -> MessageVerificationState.VERIFIED
|
||||
InnerVerificationState.SIGNED_DEVICE_OF_UNVERIFIED_USER -> MessageVerificationState.SIGNED_DEVICE_OF_UNVERIFIED_USER
|
||||
InnerVerificationState.UN_SIGNED_DEVICE_OF_VERIFIED_USER -> MessageVerificationState.UN_SIGNED_DEVICE_OF_VERIFIED_USER
|
||||
InnerVerificationState.UN_SIGNED_DEVICE_OF_UNVERIFIED_USER -> MessageVerificationState.UN_SIGNED_DEVICE
|
||||
InnerVerificationState.UNKNOWN_DEVICE -> MessageVerificationState.UNKNOWN_DEVICE
|
||||
InnerVerificationState.UNSAFE_SOURCE -> MessageVerificationState.UNSAFE_SOURCE
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue