mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-01 11:46:57 +01:00
Add option to disable key gossip, clear key request on trust change
This commit is contained in:
parent
6a509ce22d
commit
1d948d6b20
@ -64,9 +64,6 @@ import java.util.concurrent.CountDownLatch
|
||||
@LargeTest
|
||||
class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
private val testHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
/**
|
||||
* Simple test that create an e2ee room.
|
||||
* Some new members are added, and a message is sent.
|
||||
@ -78,16 +75,24 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testSendingE2EEMessages() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val e2eRoomID = cryptoTestData.roomId
|
||||
|
||||
val aliceRoomPOV = aliceSession.getRoom(e2eRoomID)!!
|
||||
// we want to disable key gossiping to just check initial sending of keys
|
||||
aliceSession.cryptoService().enableKeyGossiping(false)
|
||||
cryptoTestData.secondSession?.cryptoService()?.enableKeyGossiping(false)
|
||||
|
||||
// add some more users and invite them
|
||||
val otherAccounts = listOf("benoit", "valere", "ganfra") // , "adam", "manu")
|
||||
.map {
|
||||
testHelper.createAccount(it, SessionTestParams(true))
|
||||
testHelper.createAccount(it, SessionTestParams(true)).also {
|
||||
it.cryptoService().enableKeyGossiping(false)
|
||||
}
|
||||
}
|
||||
|
||||
Log.v("#E2E TEST", "All accounts created")
|
||||
@ -101,18 +106,18 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
// All user should accept invite
|
||||
otherAccounts.forEach { otherSession ->
|
||||
waitForAndAcceptInviteInRoom(otherSession, e2eRoomID)
|
||||
waitForAndAcceptInviteInRoom(testHelper, otherSession, e2eRoomID)
|
||||
Log.v("#E2E TEST", "${otherSession.myUserId} joined room $e2eRoomID")
|
||||
}
|
||||
|
||||
// check that alice see them as joined (not really necessary?)
|
||||
ensureMembersHaveJoined(aliceSession, otherAccounts, e2eRoomID)
|
||||
ensureMembersHaveJoined(testHelper, aliceSession, otherAccounts, e2eRoomID)
|
||||
|
||||
Log.v("#E2E TEST", "All users have joined the room")
|
||||
Log.v("#E2E TEST", "Alice is sending the message")
|
||||
|
||||
val text = "This is my message"
|
||||
val sentEventId: String? = sendMessageInRoom(aliceRoomPOV, text)
|
||||
val sentEventId: String? = sendMessageInRoom(testHelper, aliceRoomPOV, text)
|
||||
// val sentEvent = testHelper.sendTextMessage(aliceRoomPOV, "Hello all", 1).first()
|
||||
Assert.assertTrue("Message should be sent", sentEventId != null)
|
||||
|
||||
@ -142,10 +147,10 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
newAccount.forEach {
|
||||
waitForAndAcceptInviteInRoom(it, e2eRoomID)
|
||||
waitForAndAcceptInviteInRoom(testHelper, it, e2eRoomID)
|
||||
}
|
||||
|
||||
ensureMembersHaveJoined(aliceSession, newAccount, e2eRoomID)
|
||||
ensureMembersHaveJoined(testHelper, aliceSession, newAccount, e2eRoomID)
|
||||
|
||||
// wait a bit
|
||||
testHelper.runBlockingTest {
|
||||
@ -170,7 +175,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
Log.v("#E2E TEST", "Alice sends a new message")
|
||||
|
||||
val secondMessage = "2 This is my message"
|
||||
val secondSentEventId: String? = sendMessageInRoom(aliceRoomPOV, secondMessage)
|
||||
val secondSentEventId: String? = sendMessageInRoom(testHelper, aliceRoomPOV, secondMessage)
|
||||
|
||||
// new members should be able to decrypt it
|
||||
newAccount.forEach { otherSession ->
|
||||
@ -194,6 +199,14 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKeyGossipingIsEnabledByDefault() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val session = testHelper.createAccount("alice", SessionTestParams(true))
|
||||
Assert.assertTrue("Key gossiping should be enabled by default", session.cryptoService().isKeyGossipingEnabled())
|
||||
testHelper.signOutAndClose(session)
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick test for basic key backup
|
||||
* 1. Create e2e between Alice and Bob
|
||||
@ -210,6 +223,9 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testBasicBackupImport() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val bobSession = cryptoTestData.secondSession!!
|
||||
@ -233,7 +249,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
val sentEventIds = mutableListOf<String>()
|
||||
val messagesText = listOf("1. Hello", "2. Bob", "3. Good morning")
|
||||
messagesText.forEach { text ->
|
||||
val sentEventId = sendMessageInRoom(aliceRoomPOV, text)!!.also {
|
||||
val sentEventId = sendMessageInRoom(testHelper, aliceRoomPOV, text)!!.also {
|
||||
sentEventIds.add(it)
|
||||
}
|
||||
|
||||
@ -327,6 +343,9 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testSimpleGossip() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val bobSession = cryptoTestData.secondSession!!
|
||||
@ -334,15 +353,13 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
val aliceRoomPOV = aliceSession.getRoom(e2eRoomID)!!
|
||||
|
||||
cryptoTestHelper.initializeCrossSigning(bobSession)
|
||||
|
||||
// let's send a few message to bob
|
||||
val sentEventIds = mutableListOf<String>()
|
||||
val messagesText = listOf("1. Hello", "2. Bob")
|
||||
|
||||
Log.v("#E2E TEST", "Alice sends some messages")
|
||||
messagesText.forEach { text ->
|
||||
val sentEventId = sendMessageInRoom(aliceRoomPOV, text)!!.also {
|
||||
val sentEventId = sendMessageInRoom(testHelper, aliceRoomPOV, text)!!.also {
|
||||
sentEventIds.add(it)
|
||||
}
|
||||
|
||||
@ -357,7 +374,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
// Ensure bob can decrypt
|
||||
ensureIsDecrypted(sentEventIds, bobSession, e2eRoomID)
|
||||
ensureIsDecrypted(testHelper, sentEventIds, bobSession, e2eRoomID)
|
||||
|
||||
// Let's now add a new bob session
|
||||
// Create a new session for bob
|
||||
@ -431,6 +448,9 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testForwardBetterKey() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val bobSessionWithBetterKey = cryptoTestData.secondSession!!
|
||||
@ -438,15 +458,13 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
val aliceRoomPOV = aliceSession.getRoom(e2eRoomID)!!
|
||||
|
||||
cryptoTestHelper.initializeCrossSigning(bobSessionWithBetterKey)
|
||||
|
||||
// let's send a few message to bob
|
||||
var firstEventId: String
|
||||
val firstMessage = "1. Hello"
|
||||
|
||||
Log.v("#E2E TEST", "Alice sends some messages")
|
||||
firstMessage.let { text ->
|
||||
firstEventId = sendMessageInRoom(aliceRoomPOV, text)!!
|
||||
firstEventId = sendMessageInRoom(testHelper, aliceRoomPOV, text)!!
|
||||
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
@ -459,7 +477,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
// Ensure bob can decrypt
|
||||
ensureIsDecrypted(listOf(firstEventId), bobSessionWithBetterKey, e2eRoomID)
|
||||
ensureIsDecrypted(testHelper, listOf(firstEventId), bobSessionWithBetterKey, e2eRoomID)
|
||||
|
||||
// Let's add a new unverified session from bob
|
||||
val newBobSession = testHelper.logIntoAccount(bobSessionWithBetterKey.myUserId, SessionTestParams(true))
|
||||
@ -474,7 +492,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
Log.v("#E2E TEST", "Alice sends some messages")
|
||||
secondMessage.let { text ->
|
||||
secondEventId = sendMessageInRoom(aliceRoomPOV, text)!!
|
||||
secondEventId = sendMessageInRoom(testHelper, aliceRoomPOV, text)!!
|
||||
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
@ -524,7 +542,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
.markedLocallyAsManuallyVerified(bobSessionWithBetterKey.myUserId, bobSessionWithBetterKey.sessionParams.deviceId!!)
|
||||
|
||||
// now let new session request
|
||||
newBobSession.cryptoService().requestRoomKeyForEvent(firstEventNewBobPov.root)
|
||||
newBobSession.cryptoService().reRequestRoomKeyForEvent(firstEventNewBobPov.root)
|
||||
|
||||
// We need to wait for the key request to be sent out and then a reply to be received
|
||||
|
||||
@ -552,11 +570,12 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
testHelper.signOutAndClose(bobSessionWithBetterKey)
|
||||
testHelper.signOutAndClose(newBobSession)
|
||||
}
|
||||
|
||||
private fun sendMessageInRoom(aliceRoomPOV: Room, text: String): String? {
|
||||
private fun sendMessageInRoom(testHelper: CommonTestHelper, aliceRoomPOV: Room, text: String): String? {
|
||||
aliceRoomPOV.sendTextMessage(text)
|
||||
var sentEventId: String? = null
|
||||
testHelper.waitWithLatch(4 * 60_000L) { latch ->
|
||||
@ -586,6 +605,9 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testSelfInteractiveVerificationAndGossip() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
val aliceSession = testHelper.createAccount("alice", SessionTestParams(true))
|
||||
cryptoTestHelper.bootstrapSecurity(aliceSession)
|
||||
|
||||
@ -614,16 +636,16 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
Log.d("##TEST", "exitsingPov: $tx")
|
||||
val sasTx = tx as OutgoingSasVerificationTransaction
|
||||
when (sasTx.uxState) {
|
||||
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||
// for the test we just accept?
|
||||
oldCode = sasTx.getDecimalCodeRepresentation()
|
||||
sasTx.userHasVerifiedShortCode()
|
||||
}
|
||||
OutgoingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||
OutgoingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||
// we can release this latch?
|
||||
oldCompleteLatch.countDown()
|
||||
}
|
||||
else -> Unit
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -647,20 +669,20 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
val sasTx = tx as IncomingSasVerificationTransaction
|
||||
when (sasTx.uxState) {
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
|
||||
// no need to accept as there was a request first it will auto accept
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||
if (matchOnce) {
|
||||
sasTx.userHasVerifiedShortCode()
|
||||
newCode = sasTx.getDecimalCodeRepresentation()
|
||||
matchOnce = false
|
||||
}
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||
newCompleteLatch.countDown()
|
||||
}
|
||||
else -> Unit
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -727,7 +749,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
testHelper.signOutAndClose(aliceNewSession)
|
||||
}
|
||||
|
||||
private fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List<Session>, e2eRoomID: String) {
|
||||
private fun ensureMembersHaveJoined(testHelper: CommonTestHelper, aliceSession: Session, otherAccounts: List<Session>, e2eRoomID: String) {
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
otherAccounts.map {
|
||||
@ -739,7 +761,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun waitForAndAcceptInviteInRoom(otherSession: Session, e2eRoomID: String) {
|
||||
private fun waitForAndAcceptInviteInRoom(testHelper: CommonTestHelper, otherSession: Session, e2eRoomID: String) {
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val roomSummary = otherSession.getRoomSummary(e2eRoomID)
|
||||
@ -751,7 +773,8 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
|
||||
testHelper.runBlockingTest(60_000) {
|
||||
// not sure why it's taking so long :/
|
||||
testHelper.runBlockingTest(90_000) {
|
||||
Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID")
|
||||
try {
|
||||
otherSession.roomService().joinRoom(e2eRoomID)
|
||||
@ -769,7 +792,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureIsDecrypted(sentEventIds: List<String>, session: Session, e2eRoomID: String) {
|
||||
private fun ensureIsDecrypted(testHelper: CommonTestHelper, sentEventIds: List<String>, session: Session, e2eRoomID: String) {
|
||||
testHelper.waitWithLatch { latch ->
|
||||
sentEventIds.forEach { sentEventId ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
|
@ -50,11 +50,11 @@ import org.matrix.android.sdk.internal.crypto.RequestResult
|
||||
@LargeTest
|
||||
class KeyShareTests : InstrumentedTest {
|
||||
|
||||
private val commonTestHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
@Test
|
||||
fun test_DoNotSelfShareIfNotTrusted() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
Log.v("TEST", "=======> AliceSession 1 is ${aliceSession.sessionParams.deviceId}")
|
||||
|
||||
@ -71,13 +71,17 @@ class KeyShareTests : InstrumentedTest {
|
||||
assertNotNull(room)
|
||||
Thread.sleep(4_000)
|
||||
assertTrue(room?.isEncrypted() == true)
|
||||
|
||||
val sentEvent = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first()
|
||||
val sentEventId = sentEvent.eventId
|
||||
val sentEventText = sentEvent.getLastMessageContent()?.body
|
||||
|
||||
// Open a new sessionx
|
||||
// Open a new session
|
||||
val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(false))
|
||||
// block key requesting for now as decrypt will send requests (room summary is trying to decrypt)
|
||||
aliceSession2.cryptoService().enableKeyGossiping(false)
|
||||
commonTestHelper.syncSession(aliceSession2)
|
||||
|
||||
val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
||||
Log.v("TEST", "=======> AliceSession 2 is ${aliceSession2.sessionParams.deviceId}")
|
||||
|
||||
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
|
||||
@ -95,7 +99,10 @@ class KeyShareTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
val outgoingRequestsBefore = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||
assertEquals("There should be no request as it's disabled", 0, outgoingRequestsBefore.size)
|
||||
|
||||
// Try to request
|
||||
aliceSession2.cryptoService().enableKeyGossiping(true)
|
||||
aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root)
|
||||
|
||||
val eventMegolmSessionId = receivedEvent.root.content.toModel<EncryptedEventContent>()?.sessionId
|
||||
@ -105,10 +112,6 @@ class KeyShareTests : InstrumentedTest {
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||
.filter { req ->
|
||||
// filter out request that was known before
|
||||
!outgoingRequestsBefore.any { req.requestId == it.requestId }
|
||||
}
|
||||
.let {
|
||||
val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
|
||||
outGoingRequestId = outgoing?.requestId
|
||||
@ -156,7 +159,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
val outgoing = aliceSession2.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.requestId == outGoingRequestId }
|
||||
val reply = outgoing?.results?.firstOrNull { it.userId == aliceSession.myUserId && it.fromDevice == aliceSession.sessionParams.deviceId }
|
||||
val resultCode = (reply?.result as? RequestResult.Failure)?.code
|
||||
resultCode == WithHeldCode.UNAUTHORISED
|
||||
resultCode == WithHeldCode.UNVERIFIED
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,6 +192,9 @@ class KeyShareTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun test_reShareIfWasIntendedToBeShared() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val roomFromAlice = aliceSession.getRoom(testData.roomId)!!
|
||||
@ -219,6 +225,9 @@ class KeyShareTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun test_reShareToUnverifiedIfWasIntendedToBeShared() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val roomFromAlice = aliceSession.getRoom(testData.roomId)!!
|
||||
@ -254,6 +263,9 @@ class KeyShareTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun test_reShareFromTheEarliestKnownIndexWithOwnVerifiedSession() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val bobSession = testData.secondSession!!
|
||||
@ -263,7 +275,9 @@ class KeyShareTests : InstrumentedTest {
|
||||
val sentEventMegolmSession = sentEvents.first().root.content.toModel<EncryptedEventContent>()!!.sessionId!!
|
||||
|
||||
// Let alice now add a new session
|
||||
val aliceNewSession = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
||||
val aliceNewSession = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(false))
|
||||
aliceNewSession.cryptoService().enableKeyGossiping(false)
|
||||
commonTestHelper.syncSession(aliceNewSession)
|
||||
|
||||
// we wait bob first session to be aware of that session?
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
@ -289,7 +303,8 @@ class KeyShareTests : InstrumentedTest {
|
||||
}
|
||||
assertEquals(sentEventMegolmSession, newEvent.root.content.toModel<EncryptedEventContent>()!!.sessionId)
|
||||
|
||||
// Request a first time, bob and alice should reply with unauthorized
|
||||
// Request a first time, bob should reply with unauthorized and alice should reply with unverified
|
||||
aliceNewSession.cryptoService().enableKeyGossiping(true)
|
||||
aliceNewSession.cryptoService().reRequestRoomKeyForEvent(newEvent.root)
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
@ -309,6 +324,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
val bobDeviceReply = outgoing?.results
|
||||
?.firstOrNull { it.userId == bobSession.myUserId && it.fromDevice == bobSession.sessionParams.deviceId }
|
||||
val result = bobDeviceReply?.result
|
||||
Log.v("TEST", "bob device result is $result")
|
||||
result != null && result is RequestResult.Success && result.chainIndex > 0
|
||||
}
|
||||
}
|
||||
@ -322,7 +338,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
.markedLocallyAsManuallyVerified(aliceNewSession.myUserId, aliceNewSession.sessionParams.deviceId!!)
|
||||
|
||||
// Let's now try to request
|
||||
aliceNewSession.cryptoService().reRequestRoomKeyForEvent(newEvent.root)
|
||||
aliceNewSession.cryptoService().reRequestRoomKeyForEvent(sentEvents.first().root)
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
@ -368,6 +384,9 @@ class KeyShareTests : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun test_dontCancelToEarly() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val bobSession = testData.secondSession!!
|
||||
|
@ -76,6 +76,15 @@ interface CryptoService {
|
||||
|
||||
fun setGlobalBlacklistUnverifiedDevices(block: Boolean)
|
||||
|
||||
/**
|
||||
* Enable or disable key gossiping.
|
||||
* Default is true.
|
||||
* If set to false this device won't send key_request nor will accept key forwarded
|
||||
*/
|
||||
fun enableKeyGossiping(enable: Boolean)
|
||||
|
||||
fun isKeyGossipingEnabled(): Boolean
|
||||
|
||||
fun setRoomUnBlacklistUnverifiedDevices(roomId: String)
|
||||
|
||||
fun getDeviceTrackingStatus(userId: String): Int
|
||||
|
@ -1080,6 +1080,12 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
cryptoStore.setGlobalBlacklistUnverifiedDevices(block)
|
||||
}
|
||||
|
||||
override fun enableKeyGossiping(enable: Boolean) {
|
||||
cryptoStore.enableKeyGossiping(enable)
|
||||
}
|
||||
|
||||
override fun isKeyGossipingEnabled() = cryptoStore.isKeyGossipingEnabled()
|
||||
|
||||
/**
|
||||
* Tells whether the client should ever send encrypted messages to unverified devices.
|
||||
* The default value is false.
|
||||
|
@ -23,18 +23,19 @@ import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.crypto.model.GossipingToDeviceObject
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyShareRequest
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest
|
||||
import org.matrix.android.sdk.api.util.fromBase64
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
@ -75,7 +76,7 @@ internal class OutgoingKeyRequestManager @Inject constructor(
|
||||
// We only have one active key request per session, so we don't request if it's already requested
|
||||
// But it could make sense to check more the backup, as it's evolving.
|
||||
// We keep a stack as we consider that the key requested last is more likely to be on screen?
|
||||
private val requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup = Stack<String>()
|
||||
private val requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup = Stack<Pair<String, String>>()
|
||||
|
||||
fun requestKeyForEvent(event: Event, force: Boolean) {
|
||||
val (targets, body) = getRoomKeyRequestTargetForEvent(event) ?: return
|
||||
@ -157,6 +158,20 @@ internal class OutgoingKeyRequestManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onSelfCrossSigningTrustChanged(newTrust: Boolean) {
|
||||
if (newTrust) {
|
||||
// we were previously not cross signed, but we are now
|
||||
// so there is now more chances to get better replies for existing request
|
||||
// Let's forget about sent request so that next time we try to decrypt we will resend requests
|
||||
// We don't resend all because we don't want to generate a bulk of traffic
|
||||
outgoingRequestScope.launch {
|
||||
sequencer.post {
|
||||
cryptoStore.deleteOutgoingRoomKeyRequestInState(OutgoingRoomKeyRequestState.SENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onRoomKeyForwarded(sessionId: String,
|
||||
algorithm: String,
|
||||
roomId: String,
|
||||
@ -274,6 +289,15 @@ internal class OutgoingKeyRequestManager @Inject constructor(
|
||||
}
|
||||
|
||||
private fun internalQueueRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>, fromIndex: Int, force: Boolean) {
|
||||
if (!cryptoStore.isKeyGossipingEnabled()) {
|
||||
// we might want to try backup?
|
||||
if (requestBody.roomId != null && requestBody.sessionId != null) {
|
||||
requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup.push(requestBody.roomId to requestBody.sessionId)
|
||||
}
|
||||
Timber.tag(loggerTag.value).d("discarding request for ${requestBody.sessionId} as gossiping is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
Timber.tag(loggerTag.value).d("Queueing key request for ${requestBody.sessionId} force:$force")
|
||||
val existing = cryptoStore.getOutgoingRoomKeyRequest(requestBody)
|
||||
Timber.tag(loggerTag.value).v("Queueing key request exiting is ${existing?.state}")
|
||||
@ -293,7 +317,9 @@ internal class OutgoingKeyRequestManager @Inject constructor(
|
||||
Timber.tag(loggerTag.value).d(".. force to request ${requestBody.sessionId}")
|
||||
cryptoStore.updateOutgoingRoomKeyRequestState(existing.requestId, OutgoingRoomKeyRequestState.CANCELLATION_PENDING_AND_WILL_RESEND)
|
||||
} else {
|
||||
requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup.push(existing.requestId)
|
||||
if (existing.roomId != null && existing.sessionId != null) {
|
||||
requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup.push(existing.roomId to existing.sessionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
OutgoingRoomKeyRequestState.CANCELLATION_PENDING -> {
|
||||
@ -343,10 +369,7 @@ internal class OutgoingKeyRequestManager @Inject constructor(
|
||||
var currentCalls = 0
|
||||
measureTimeMillis {
|
||||
while (requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup.isNotEmpty() && currentCalls < maxBackupCallsBySync) {
|
||||
requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup.pop().let {
|
||||
val req = cryptoStore.getOutgoingRoomKeyRequest(it)
|
||||
val sessionId = req?.sessionId ?: return@let
|
||||
val roomId = req.roomId ?: return@let
|
||||
requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup.pop().let { (roomId, sessionId) ->
|
||||
// we want to rate limit that somehow :/
|
||||
perSessionBackupQueryRateLimiter.tryFromBackupIfPossible(sessionId, roomId)
|
||||
}
|
||||
|
@ -54,10 +54,7 @@ internal class MXMegolmDecryption(
|
||||
|
||||
@Throws(MXCryptoError::class)
|
||||
override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
||||
// If cross signing is enabled, we don't send request until the keys are trusted
|
||||
// There could be a race effect here when xsigning is enabled, we should ensure that keys was downloaded once
|
||||
val requestOnFail = cryptoStore.getMyCrossSigningInfo()?.isTrusted() == true
|
||||
return decryptEvent(event, timeline, requestOnFail)
|
||||
return decryptEvent(event, timeline, true)
|
||||
}
|
||||
|
||||
@Throws(MXCryptoError::class)
|
||||
@ -164,6 +161,11 @@ internal class MXMegolmDecryption(
|
||||
return
|
||||
}
|
||||
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
||||
if (!cryptoStore.isKeyGossipingEnabled()) {
|
||||
Timber.tag(loggerTag.value)
|
||||
.i("onRoomKeyEvent(), ignore forward adding as per crypto config : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||
return
|
||||
}
|
||||
Timber.tag(loggerTag.value).i("onRoomKeyEvent(), forward adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()
|
||||
?: return
|
||||
|
@ -38,6 +38,8 @@ import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.fromBase64
|
||||
import org.matrix.android.sdk.internal.crypto.DeviceListManager
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UploadSignatureQueryBuilder
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.InitializeCrossSigningTask
|
||||
@ -70,6 +72,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val cryptoCoroutineScope: CoroutineScope,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
private val outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
private val updateTrustWorkerDataRepository: UpdateTrustWorkerDataRepository
|
||||
) : CrossSigningService,
|
||||
DeviceListManager.UserDevicesUpdateListener {
|
||||
@ -781,7 +784,8 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||
// If it's me, recheck trust of all users and devices?
|
||||
val users = ArrayList<String>()
|
||||
if (otherUserId == userId && currentTrust != trusted) {
|
||||
// reRequestAllPendingRoomKeyRequest()
|
||||
// notify key requester
|
||||
outgoingKeyRequestManager.onSelfCrossSigningTrustChanged(trusted)
|
||||
cryptoStore.updateUsersTrust {
|
||||
users.add(it)
|
||||
checkUserTrust(it).isVerified()
|
||||
@ -796,19 +800,4 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private fun reRequestAllPendingRoomKeyRequest() {
|
||||
// cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
// Timber.d("## CrossSigning - reRequest pending outgoing room key requests")
|
||||
// cryptoStore.getOutgoingRoomKeyRequests().forEach {
|
||||
// it.requestBody?.let { requestBody ->
|
||||
// if (cryptoStore.getInboundGroupSession(requestBody.sessionId ?: "", requestBody.senderKey ?: "") == null) {
|
||||
// outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody)
|
||||
// } else {
|
||||
// outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldCo
|
||||
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.OutgoingRoomKeyRequestState
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingRoomKeyRequestState
|
||||
import org.matrix.android.sdk.internal.crypto.model.AuditTrail
|
||||
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||
@ -81,6 +81,15 @@ internal interface IMXCryptoStore {
|
||||
*/
|
||||
fun setGlobalBlacklistUnverifiedDevices(block: Boolean)
|
||||
|
||||
/**
|
||||
* Enable or disable key gossiping.
|
||||
* Default is true.
|
||||
* If set to false this device won't send key_request nor will accept key forwarded
|
||||
*/
|
||||
fun enableKeyGossiping(enable: Boolean)
|
||||
|
||||
fun isKeyGossipingEnabled(): Boolean
|
||||
|
||||
/**
|
||||
* Provides the rooms ids list in which the messages are not encrypted for the unverified devices.
|
||||
*
|
||||
@ -386,6 +395,7 @@ internal interface IMXCryptoStore {
|
||||
event: Event)
|
||||
|
||||
fun deleteOutgoingRoomKeyRequest(requestId: String)
|
||||
fun deleteOutgoingRoomKeyRequestInState(state: OutgoingRoomKeyRequestState)
|
||||
|
||||
fun saveIncomingKeyRequestAuditTrail(
|
||||
requestId: String,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.store.db.migration
|
||||
|
||||
import io.realm.DynamicRealm
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.AuditTrailEntityFields
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyRequestReplyEntity
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyRequestReplyEntityFields
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntityFields
|
||||
@ -59,5 +60,12 @@ class MigrateCryptoTo016(realm: DynamicRealm) : RealmMigrator(realm, 15) {
|
||||
.addField(KeyRequestReplyEntityFields.SENDER_ID, String::class.java)
|
||||
.addField(KeyRequestReplyEntityFields.FROM_DEVICE, String::class.java)
|
||||
.addField(KeyRequestReplyEntityFields.EVENT_JSON, String::class.java)
|
||||
|
||||
realm.schema.get("CryptoMetadataEntity")
|
||||
?.addField(CryptoMetadataEntityFields.GLOBAL_ENABLE_KEY_REQUESTING_AND_SHARING, Boolean::class.java)
|
||||
?.transform {
|
||||
// set the default value to true
|
||||
it.setBoolean(CryptoMetadataEntityFields.GLOBAL_ENABLE_KEY_REQUESTING_AND_SHARING, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ internal open class CryptoMetadataEntity(
|
||||
var deviceSyncToken: String? = null,
|
||||
// Settings for blacklisting unverified devices.
|
||||
var globalBlacklistUnverifiedDevices: Boolean = false,
|
||||
// setting to enable or disable key gossiping
|
||||
var globalEnableKeyRequestingAndSharing: Boolean = true,
|
||||
// The keys backup version currently used. Null means no backup.
|
||||
var backupVersion: String? = null,
|
||||
|
||||
|
@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.session.room.membership.joining
|
||||
|
||||
import io.realm.RealmConfiguration
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.identity.model.SignInvitationResult
|
||||
import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure
|
||||
@ -52,6 +54,7 @@ internal class DefaultJoinRoomTask @Inject constructor(
|
||||
private val readMarkersTask: SetReadMarkersTask,
|
||||
@SessionDatabase
|
||||
private val realmConfiguration: RealmConfiguration,
|
||||
private val coroutineDispatcher: MatrixCoroutineDispatchers,
|
||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
) : JoinRoomTask {
|
||||
@ -68,11 +71,13 @@ internal class DefaultJoinRoomTask @Inject constructor(
|
||||
}
|
||||
val joinRoomResponse = try {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
roomAPI.join(
|
||||
roomIdOrAlias = params.roomIdOrAlias,
|
||||
viaServers = params.viaServers.take(3),
|
||||
params = extraParams
|
||||
)
|
||||
withContext(coroutineDispatcher.io) {
|
||||
roomAPI.join(
|
||||
roomIdOrAlias = params.roomIdOrAlias,
|
||||
viaServers = params.viaServers.take(3),
|
||||
params = extraParams
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
roomChangeMembershipStateDataSource.updateState(params.roomIdOrAlias, ChangeMembershipState.FailedJoining(failure))
|
||||
|
Loading…
x
Reference in New Issue
Block a user