Skip to content
This repository has been archived by the owner on Apr 12, 2022. It is now read-only.

Commit

Permalink
Implement the backup of the room keys (element-hq/riot-android#2642)
Browse files Browse the repository at this point in the history
  • Loading branch information
bmarty committed Dec 4, 2018
1 parent e23c638 commit 71e021a
Show file tree
Hide file tree
Showing 51 changed files with 3,681 additions and 281 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -7,6 +7,8 @@ Features:
Improvements:
- Any Account data element, even if the type is not known is persisted.
- The crypto store is now implemented using a Realm database. The existing file store will be migrated at first usage (#398)
- Upgrade olm-sdk.aar from version 2.3.0 to version 3.0.0
- Implement the backup of the room keys in the KeysBackup class (vector-im/riot-android#2642)

Bugfix:
- Room members who left are listed with the actual members (vector-im/riot-android#2744)
Expand All @@ -16,6 +18,7 @@ Bugfix:
API Change:
- new API in CallSoundsManager to allow client to play the specified Ringtone (vector-im/riot-android#827)
- IMXStore.storeAccountData() has been renamed to IMXStore.storeRoomAccountData()
- MXCrypto: importRoomKeys methods now return number of imported keys and number of total keys in the Callback.

Translations:
-
Expand Down
Binary file modified matrix-sdk/libs/olm-sdk.aar
Binary file not shown.
Expand Up @@ -281,7 +281,6 @@ public void onSuccess(Credentials credentials) {
IMXStore store = new MXFileStore(hs, false, context);

MXDataHandler dataHandler = new MXDataHandler(store, credentials);
// TODO Use sessionTestParam parameter when other PR will be merged
dataHandler.setLazyLoadingEnabled(sessionTestParams.getWithLazyLoading());

MXSession mxSession = new MXSession.Builder(hs, dataHandler, context)
Expand Down Expand Up @@ -373,8 +372,8 @@ public void clearAllSessions(List<MXSession> sessions) {
}

/**
* Clone a session
* // TODO Use this method where it should be (after merge of keys backup)
* Clone a session.
* It simulate that the user launches again the application with the same Credentials, contrary to login which will create a new DeviceId
*
* @param from the session to clone
* @return the duplicated session
Expand All @@ -383,10 +382,11 @@ public void clearAllSessions(List<MXSession> sessions) {
public MXSession createNewSession(@NonNull MXSession from, SessionTestParams sessionTestParams) throws InterruptedException {
final Context context = InstrumentationRegistry.getContext();

Credentials aliceCredentials = from.getCredentials();
HomeServerConnectionConfig hs = createHomeServerConfig(aliceCredentials);
Credentials credentials = from.getCredentials();
HomeServerConnectionConfig hs = createHomeServerConfig(credentials);
MXFileStore store = new MXFileStore(hs, false, context);
MXDataHandler dataHandler = new MXDataHandler(store, aliceCredentials);
MXDataHandler dataHandler = new MXDataHandler(store, credentials);
dataHandler.setLazyLoadingEnabled(sessionTestParams.getWithLazyLoading());
store.setDataHandler(dataHandler);
MXSession session2 = new MXSession.Builder(hs, dataHandler, context)
.withLegacyCryptoStore(sessionTestParams.getWithLegacyCryptoStore())
Expand Down
Expand Up @@ -18,12 +18,12 @@ package org.matrix.androidsdk.common

import android.os.SystemClock
import android.text.TextUtils
import org.junit.Assert
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Assert.*
import org.matrix.androidsdk.MXSession
import org.matrix.androidsdk.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.androidsdk.crypto.MXCryptoAlgorithms
import org.matrix.androidsdk.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import org.matrix.androidsdk.crypto.keysbackup.MegolmBackupAuthData
import org.matrix.androidsdk.crypto.keysbackup.MegolmBackupCreationInfo
import org.matrix.androidsdk.data.RoomState
import org.matrix.androidsdk.listeners.MXEventListener
import org.matrix.androidsdk.rest.model.Event
Expand All @@ -36,10 +36,10 @@ import java.util.concurrent.CountDownLatch
/**
* Synchronously enable crypto for the session and fail if it does not work
*/
fun MXSession.enableCrypto() {
val cryptoLatch = CountDownLatch(1)
enableCrypto(true, TestApiCallback(cryptoLatch))
cryptoLatch.await()
fun MXSession.enableCrypto(testHelper: CommonTestHelper) {
val latch = CountDownLatch(1)
enableCrypto(true, TestApiCallback(latch))
testHelper.await(latch)
}


Expand Down Expand Up @@ -80,7 +80,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
}
})
mTestHelper.await(lock0)
Assert.assertTrue(results.containsKey("enableCrypto"))
assertTrue(results.containsKey("enableCrypto"))

var roomId: String? = null
val lock1 = CountDownLatch(1)
Expand All @@ -93,7 +93,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
})

mTestHelper.await(lock1)
Assert.assertNotNull(roomId)
assertNotNull(roomId)

val room = aliceSession.dataHandler.getRoom(roomId)

Expand All @@ -105,7 +105,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
}
})
mTestHelper.await(lock2)
Assert.assertTrue(results.containsKey("enableEncryptionWithAlgorithm"))
assertTrue(results.containsKey("enableEncryptionWithAlgorithm"))

return CryptoTestData(aliceSession, roomId!!)
}
Expand Down Expand Up @@ -158,7 +158,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {

mTestHelper.await(lock1)

Assert.assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom"))
assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom"))

bobSession.dataHandler.removeListener(bobEventListener)

Expand All @@ -183,11 +183,11 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
mTestHelper.await(lock2)

// Ensure bob can send messages to the room
val roomFromBobPOV = bobSession.getDataHandler().getRoom(aliceRoomId)
assertNotNull(roomFromBobPOV.getState().getPowerLevels())
assertTrue(roomFromBobPOV.getState().getPowerLevels().maySendMessage(bobSession.getMyUserId()))
val roomFromBobPOV = bobSession.dataHandler.getRoom(aliceRoomId)
assertNotNull(roomFromBobPOV.state.powerLevels)
assertTrue(roomFromBobPOV.state.powerLevels.maySendMessage(bobSession.myUserId))

Assert.assertTrue(statuses.toString() + "", statuses.containsKey("AliceJoin"))
assertTrue(statuses.toString() + "", statuses.containsKey("AliceJoin"))

bobSession.dataHandler.removeListener(bobEventListener)

Expand Down Expand Up @@ -241,7 +241,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {

mTestHelper.await(lock1)

Assert.assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom"))
assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom"))

samSession.dataHandler.removeListener(samEventListener)

Expand All @@ -255,7 +255,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
})

mTestHelper.await(lock2)
Assert.assertTrue(statuses.containsKey("joinRoom"))
assertTrue(statuses.containsKey("joinRoom"))

// wait the initial sync
SystemClock.sleep(1000)
Expand Down Expand Up @@ -308,66 +308,84 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
// Alice sends a message
roomFromAlicePOV.sendEvent(buildTextEvent(messagesFromAlice[0], aliceSession, aliceRoomId), TestApiCallback<Void>(lock, true))
mTestHelper.await(lock)
Assert.assertTrue(results.containsKey("onToDeviceEvent"))
Assert.assertEquals(1, messagesReceivedByBobCount)
assertTrue(results.containsKey("onToDeviceEvent"))
assertEquals(1, messagesReceivedByBobCount)

// Bob send a message
lock = CountDownLatch(1)
roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob[0], bobSession, aliceRoomId), TestApiCallback<Void>(lock, true))
// android does not echo the messages sent from itself
messagesReceivedByBobCount++
mTestHelper.await(lock)
Assert.assertEquals(2, messagesReceivedByBobCount)
assertEquals(2, messagesReceivedByBobCount)

// Bob send a message
lock = CountDownLatch(1)
roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob[1], bobSession, aliceRoomId), TestApiCallback<Void>(lock, true))
// android does not echo the messages sent from itself
messagesReceivedByBobCount++
mTestHelper.await(lock)
Assert.assertEquals(3, messagesReceivedByBobCount)
assertEquals(3, messagesReceivedByBobCount)

// Bob send a message
lock = CountDownLatch(1)
roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob[2], bobSession, aliceRoomId), TestApiCallback<Void>(lock, true))
// android does not echo the messages sent from itself
messagesReceivedByBobCount++
mTestHelper.await(lock)
Assert.assertEquals(4, messagesReceivedByBobCount)
assertEquals(4, messagesReceivedByBobCount)

// Alice sends a message
lock = CountDownLatch(2)
roomFromAlicePOV.sendEvent(buildTextEvent(messagesFromAlice[1], aliceSession, aliceRoomId), TestApiCallback<Void>(lock, true))
mTestHelper.await(lock)
Assert.assertEquals(5, messagesReceivedByBobCount)
assertEquals(5, messagesReceivedByBobCount)

return cryptoTestData
}

fun checkEncryptedEvent(event: Event, roomId: String, clearMessage: String, senderSession: MXSession) {
Assert.assertEquals(Event.EVENT_TYPE_MESSAGE_ENCRYPTED, event.wireType)
Assert.assertNotNull(event.wireContent)
assertEquals(Event.EVENT_TYPE_MESSAGE_ENCRYPTED, event.wireType)
assertNotNull(event.wireContent)

val eventWireContent = event.wireContent.asJsonObject
Assert.assertNotNull(eventWireContent)
assertNotNull(eventWireContent)

Assert.assertNull(eventWireContent.get("body"))
Assert.assertEquals(MXCRYPTO_ALGORITHM_MEGOLM, eventWireContent.get("algorithm").asString)
assertNull(eventWireContent.get("body"))
assertEquals(MXCRYPTO_ALGORITHM_MEGOLM, eventWireContent.get("algorithm").asString)

Assert.assertNotNull(eventWireContent.get("ciphertext"))
Assert.assertNotNull(eventWireContent.get("session_id"))
Assert.assertNotNull(eventWireContent.get("sender_key"))
assertNotNull(eventWireContent.get("ciphertext"))
assertNotNull(eventWireContent.get("session_id"))
assertNotNull(eventWireContent.get("sender_key"))

Assert.assertEquals(senderSession.credentials.deviceId, eventWireContent.get("device_id").asString)
assertEquals(senderSession.credentials.deviceId, eventWireContent.get("device_id").asString)

Assert.assertNotNull(event.eventId)
Assert.assertEquals(roomId, event.roomId)
Assert.assertEquals(Event.EVENT_TYPE_MESSAGE, event.getType())
Assert.assertTrue(event.getAge() < 10000)
assertNotNull(event.eventId)
assertEquals(roomId, event.roomId)
assertEquals(Event.EVENT_TYPE_MESSAGE, event.getType())
assertTrue(event.getAge() < 10000)

val eventContent = event.contentAsJsonObject
Assert.assertNotNull(eventContent)
Assert.assertEquals(clearMessage, eventContent!!.get("body").asString)
Assert.assertEquals(senderSession.myUserId, event.sender)
assertNotNull(eventContent)
assertEquals(clearMessage, eventContent!!.get("body").asString)
assertEquals(senderSession.myUserId, event.sender)
}

fun createFakeMegolmBackupAuthData(): MegolmBackupAuthData {
return MegolmBackupAuthData(
publicKey = "abcdefg",
signatures = HashMap<String, Any>().apply {
this["something"] = HashMap<String, String>().apply {
this["ed25519:something"] = "hijklmnop"
}
}
)
}

fun createFakeMegolmBackupCreationInfo(): MegolmBackupCreationInfo {
return MegolmBackupCreationInfo().apply {
algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
authData = createFakeMegolmBackupAuthData()
}
}
}
Expand Up @@ -16,7 +16,7 @@

package org.matrix.androidsdk.common

class SessionTestParams @JvmOverloads constructor(val withInitialSync: Boolean = false,
val withCryptoEnabled: Boolean = false,
val withLazyLoading: Boolean = false,
val withLegacyCryptoStore: Boolean = true)
data class SessionTestParams @JvmOverloads constructor(val withInitialSync: Boolean = false,
val withCryptoEnabled: Boolean = false,
val withLazyLoading: Boolean = true,
val withLegacyCryptoStore: Boolean = false)
Expand Up @@ -17,12 +17,10 @@
package org.matrix.androidsdk.common

import android.support.annotation.CallSuper
import junit.framework.Assert

import org.junit.Assert.fail
import org.matrix.androidsdk.rest.callback.ApiCallback
import org.matrix.androidsdk.rest.model.MatrixError
import org.matrix.androidsdk.util.Log

import java.util.concurrent.CountDownLatch

/**
Expand All @@ -43,7 +41,7 @@ open class TestApiCallback<T> @JvmOverloads constructor(private val countDownLat
Log.e("TestApiCallback", e.message, e)

if (onlySuccessful) {
Assert.fail("onNetworkError " + e.localizedMessage)
fail("onNetworkError " + e.localizedMessage)
}

countDownLatch.countDown()
Expand All @@ -54,7 +52,7 @@ open class TestApiCallback<T> @JvmOverloads constructor(private val countDownLat
Log.e("TestApiCallback", e.message + " " + e.errcode)

if (onlySuccessful) {
Assert.fail("onMatrixError " + e.localizedMessage)
fail("onMatrixError " + e.localizedMessage)
}

countDownLatch.countDown()
Expand All @@ -65,7 +63,7 @@ open class TestApiCallback<T> @JvmOverloads constructor(private val countDownLat
Log.e("TestApiCallback", e.message, e)

if (onlySuccessful) {
Assert.fail("onUnexpectedError " + e.localizedMessage)
fail("onUnexpectedError " + e.localizedMessage)
}

countDownLatch.countDown()
Expand Down
@@ -0,0 +1,53 @@
/*
* Copyright 2018 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 org.matrix.androidsdk.common

import org.junit.Assert.*

/**
* Compare two lists and their content
*/
fun assertListEquals(list1: List<Any>?, list2: List<Any>?) {
if (list1 == null) {
assertNull(list2)
} else {
assertNotNull(list2)

assertEquals("List sizes must match", list1.size, list2!!.size)

for (i in list1.indices) {
assertEquals("Elements at index $i are not equal", list1[i], list2[i])
}
}
}

/**
* Compare two maps and their content
*/
fun assertDictEquals(dict1: Map<String, Any>?, dict2: Map<String, Any>?) {
if (dict1 == null) {
assertNull(dict2)
} else {
assertNotNull(dict2)

assertEquals("Map sizes must match", dict1.size, dict2!!.size)

for (i in dict1.keys) {
assertEquals("Values for key $i are not equal", dict1[i], dict2[i])
}
}
}

0 comments on commit 71e021a

Please sign in to comment.