From 80bb7b2b3f2286f3a6c0f1945c07ac316ae4904b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Dec 2018 11:06:26 +0100 Subject: [PATCH 01/11] Version++ --- CHANGES.rst | 27 +++++++++++++++++++++++++++ matrix-sdk/build.gradle | 4 ++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9c8872f17..c45449095 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,30 @@ +Changes to Matrix Android SDK in 0.9.15 (2018-XX-XX) +======================================================= + +Features: + - + +Improvements: + - + +Bugfix: + - + +API Change: + - + +Translations: + - + +Others: + - + +Build: + - + +Test: + - + Changes to Matrix Android SDK in 0.9.14 (2018-12-13) ======================================================= diff --git a/matrix-sdk/build.gradle b/matrix-sdk/build.gradle index b57971609..d80bcf29e 100644 --- a/matrix-sdk/build.gradle +++ b/matrix-sdk/build.gradle @@ -26,8 +26,8 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 26 - versionCode 914 - versionName "0.9.14" + versionCode 915 + versionName "0.9.15-dev" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } From 17e9fcdb2bb8922fb83aa1ecc70e695cee59d528 Mon Sep 17 00:00:00 2001 From: Martin Kamleithner Date: Thu, 20 Dec 2018 14:14:35 +0100 Subject: [PATCH 02/11] ignore all whitespace, including newline, in recovery key --- .../main/java/org/matrix/androidsdk/crypto/util/RecoveryKey.kt | 2 +- .../java/org/matrix/androidsdk/crypto/util/RecoveryKeyTest.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/util/RecoveryKey.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/util/RecoveryKey.kt index 5f3b29062..aaf4c8c37 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/util/RecoveryKey.kt +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/util/RecoveryKey.kt @@ -81,7 +81,7 @@ fun extractCurveKeyFromRecoveryKey(recoveryKey: String?): ByteArray? { } // Remove any space - val spaceFreeRecoveryKey = recoveryKey.replace(" ".toRegex(), "") + val spaceFreeRecoveryKey = recoveryKey.replace("""\s""".toRegex(), "") val b58DecodedKey = base58decode(spaceFreeRecoveryKey) diff --git a/matrix-sdk/src/test/java/org/matrix/androidsdk/crypto/util/RecoveryKeyTest.kt b/matrix-sdk/src/test/java/org/matrix/androidsdk/crypto/util/RecoveryKeyTest.kt index bd491c922..3b3f5eb5a 100644 --- a/matrix-sdk/src/test/java/org/matrix/androidsdk/crypto/util/RecoveryKeyTest.kt +++ b/matrix-sdk/src/test/java/org/matrix/androidsdk/crypto/util/RecoveryKeyTest.kt @@ -32,6 +32,9 @@ class RecoveryKeyTest { // Space should be ignored Assert.assertTrue(isValidRecoveryKey("EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d")) + + // All whitespace should be ignored + Assert.assertTrue(isValidRecoveryKey("EsTc LW2K PGiF wKEA 3As5 g5c4\r\nBXwk qeeJ ZJV8 Q9fu gUMN UE4d")) } @Test From b83675ea5f594cd79d53adc958934b962b481c3b Mon Sep 17 00:00:00 2001 From: Martin Kamleithner Date: Thu, 20 Dec 2018 14:32:34 +0100 Subject: [PATCH 03/11] add changes to Changes.rst --- CHANGES.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c45449095..947d2f808 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -25,6 +25,33 @@ Build: Test: - +Changes to Matrix Android SDK in 0.9.16 (2018-12-20) +======================================================= + +Features: + - + +Improvements: + - isValidRecoveryKey() ignores now all whitespace characters, not only spaces + +Bugfix: + - + +API Change: + - + +Translations: + - + +Others: + - + +Build: + - + +Test: + - New test for recovery key with newlines in it + Changes to Matrix Android SDK in 0.9.14 (2018-12-13) ======================================================= From c121d8f1113388000842dab0f60868f323882a06 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Dec 2018 15:51:55 +0100 Subject: [PATCH 04/11] cleanup --- CHANGES.rst | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 947d2f808..bef0c420c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,33 +1,6 @@ Changes to Matrix Android SDK in 0.9.15 (2018-XX-XX) ======================================================= -Features: - - - -Improvements: - - - -Bugfix: - - - -API Change: - - - -Translations: - - - -Others: - - - -Build: - - - -Test: - - - -Changes to Matrix Android SDK in 0.9.16 (2018-12-20) -======================================================= - Features: - From c20963357e07268825bfaeec8a8d257f93473eb1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Dec 2018 16:35:16 +0100 Subject: [PATCH 05/11] Ensure there is no ghost device in the Realm crypto store (Fixes vector-im/riot-android#2784) --- CHANGES.rst | 2 +- .../data/cryptostore/db/RealmCryptoStore.kt | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index bef0c420c..ce3c51339 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,7 +8,7 @@ Improvements: - isValidRecoveryKey() ignores now all whitespace characters, not only spaces Bugfix: - - + - Ensure there is no ghost device in the Realm crypto store (vector-im/riot-android#2784) API Change: - diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStore.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStore.kt index 2eab15983..2d5944b06 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStore.kt +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStore.kt @@ -186,8 +186,9 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC putDeviceInfo(deviceInfo) } - // TODO Test if it is not added twice - user.devices.add(deviceInfoEntity) + if (!user.devices.contains(deviceInfoEntity)) { + user.devices.add(deviceInfoEntity) + } } } @@ -221,18 +222,20 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC if (userId == null) { return } - doRealmTransaction(realmConfiguration) { r -> + doRealmTransaction(realmConfiguration) { realm -> if (devices == null) { // Remove the user - UserEntity.delete(r, userId) + UserEntity.delete(realm, userId) } else { - UserEntity.getOrCreate(r, userId) + UserEntity.getOrCreate(realm, userId) .let { u -> // Add the devices - u.devices.clear() + // Ensure all other devices are deleted + u.devices.deleteAllFromRealm() + u.devices.addAll( devices.map { - DeviceInfoEntity.getOrCreate(r, userId, it.value.deviceId).apply { + DeviceInfoEntity.getOrCreate(realm, userId, it.value.deviceId).apply { deviceId = it.value.deviceId identityKey = it.value.identityKey() putDeviceInfo(it.value) From 3acf1da0af87019b61d40fec79954f66337d7f81 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Dec 2018 12:44:58 +0100 Subject: [PATCH 06/11] Migration is reserved for database schema change. Prefer using importation --- ...nTest.kt => CryptoStoreImportationTest.kt} | 52 +++++++++---------- .../data/cryptostore/MXFileCryptoStore.java | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) rename matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/{CryptoStoreMigrationTest.kt => CryptoStoreImportationTest.kt} (92%) diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreMigrationTest.kt b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreImportationTest.kt similarity index 92% rename from matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreMigrationTest.kt rename to matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreImportationTest.kt index 7d3db0173..9a0fd3657 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreMigrationTest.kt +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreImportationTest.kt @@ -38,7 +38,7 @@ import org.matrix.olm.OlmSession import java.util.concurrent.CountDownLatch @FixMethodOrder(MethodSorters.JVM) -class CryptoStoreMigrationTest { +class CryptoStoreImportationTest { private val mTestHelper = CommonTestHelper() private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) @@ -48,8 +48,8 @@ class CryptoStoreMigrationTest { private val sessionTestParamRealm = SessionTestParams(withInitialSync = true, withCryptoEnabled = true, withLegacyCryptoStore = false) @Test - fun test_migrationEmptyStore() { - testMigration( + fun test_importationEmptyStore() { + testImportation( doOnFileStore = { // Nothing to do for this test }, @@ -61,10 +61,10 @@ class CryptoStoreMigrationTest { } @Test - fun test_migrationOlmAccount() { + fun test_importationOlmAccount() { val olmAccount = OlmAccount() - testMigration( + testImportation( doOnFileStore = { it.storeAccount(olmAccount) }, @@ -77,8 +77,8 @@ class CryptoStoreMigrationTest { } @Test - fun test_migrationRooms() { - testMigration( + fun test_importationRooms() { + testImportation( doOnFileStore = { it.storeRoomAlgorithm("roomId1", "algo1") it.storeRoomAlgorithm("roomId2", "algo2") @@ -94,12 +94,12 @@ class CryptoStoreMigrationTest { } @Test - fun test_migrationUsers() { + fun test_importationUsers() { val deviceTrackingStatus = HashMap().apply { put("userId1", MXDeviceList.TRACKING_STATUS_DOWNLOAD_IN_PROGRESS) } - testMigration( + testImportation( doOnFileStore = { it.storeUserDevice("userId1", MXDeviceInfo().apply { deviceId = "deviceId1" @@ -118,7 +118,7 @@ class CryptoStoreMigrationTest { } @Test - fun test_migrationOutgoingRoomKeyRequest() { + fun test_importationOutgoingRoomKeyRequest() { val request = OutgoingRoomKeyRequest( // Request body HashMap().apply { @@ -136,7 +136,7 @@ class CryptoStoreMigrationTest { mCancellationTxnId = "mCancellationTxnId" } - testMigration( + testImportation( doOnFileStore = { it.getOrAddOutgoingRoomKeyRequest(request) }, @@ -154,7 +154,7 @@ class CryptoStoreMigrationTest { } @Test - fun test_migrationIncomingRoomKeyRequest() { + fun test_importationIncomingRoomKeyRequest() { val request = IncomingRoomKeyRequest().apply { mUserId = "userId" mDeviceId = "DeviceId" @@ -167,7 +167,7 @@ class CryptoStoreMigrationTest { } } - testMigration( + testImportation( doOnFileStore = { it.storeIncomingRoomKeyRequest(request) }, @@ -187,12 +187,12 @@ class CryptoStoreMigrationTest { } @Test - fun test_migrationOlmSessions() { + fun test_importationOlmSessions() { val session = OlmSession() val sessionId = session.sessionIdentifier() - testMigration( + testImportation( doOnFileStore = { it.storeSession(session, "deviceID") }, @@ -210,8 +210,8 @@ class CryptoStoreMigrationTest { } @Test - fun test_migrationInboundGroupSessions() { - // This is tested in test_integration_migrationInboundGroupSession + fun test_importationInboundGroupSessions() { + // This is tested in test_integration_importationInboundGroupSession } /* ========================================================================================== @@ -219,7 +219,7 @@ class CryptoStoreMigrationTest { * ========================================================================================== */ @Test - fun test_integration_migrationEmptyStore() { + fun test_integration_importationEmptyStore() { Log.e(LOG_TAG, "test01_testCryptoNoDeviceId") // Create an account using the file store @@ -232,10 +232,10 @@ class CryptoStoreMigrationTest { assertNotNull(bobSession.credentials.deviceId) - // Open again the session, with the Realm store. It will trigger the migration + // Open again the session, with the Realm store. It will trigger the importation val bobSession2 = mTestHelper.createNewSession(bobSession, sessionTestParamRealm) - // Migration should be ok + // Importation should be ok assertNotNull(bobSession2.crypto) assertNotNull(bobSession2.crypto?.cryptoStore) assertTrue(bobSession2.crypto?.cryptoStore is RealmCryptoStore) @@ -248,8 +248,8 @@ class CryptoStoreMigrationTest { } @Test - fun test_integration_migrationInboundGroupSession() { - Log.e(LOG_TAG, "test_integration_migrationInboundGroupSession") + fun test_integration_importationInboundGroupSession() { + Log.e(LOG_TAG, "test_integration_importationInboundGroupSession") val context = InstrumentationRegistry.getContext() val results = java.util.HashMap() @@ -345,8 +345,8 @@ class CryptoStoreMigrationTest { * Private * ========================================================================================== */ - private fun testMigration(doOnFileStore: (IMXCryptoStore) -> Unit, - checkOnRealmStore: (IMXCryptoStore) -> Unit) { + private fun testImportation(doOnFileStore: (IMXCryptoStore) -> Unit, + checkOnRealmStore: (IMXCryptoStore) -> Unit) { val context = InstrumentationRegistry.getContext() val credentials = cryptoStoreHelper.createCredential() @@ -359,7 +359,7 @@ class CryptoStoreMigrationTest { // Let each test do what they want to configure the file store doOnFileStore.invoke(fileCryptoStore) - // It will trigger the migration + // It will trigger the importation val realmCryptoStore = RealmCryptoStore() realmCryptoStore.initWithCredentials(context, credentials) @@ -374,6 +374,6 @@ class CryptoStoreMigrationTest { } companion object { - private const val LOG_TAG = "CryptoStoreMigrationTest" + private const val LOG_TAG = "CryptoStoreImportationTest" } } \ No newline at end of file diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/MXFileCryptoStore.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/MXFileCryptoStore.java index 3109ec65d..c36adedf2 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/MXFileCryptoStore.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/MXFileCryptoStore.java @@ -1817,7 +1817,7 @@ private void loadIncomingRoomKeyRequests() { } /* ========================================================================================== - * Accessors for the Realm migration + * Accessors for the Realm importation * ========================================================================================== */ public Map getRoomsAlgorithms() { From feb7d97292c2ec31046e561e90826e4710bc60f3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Dec 2018 11:25:27 +0100 Subject: [PATCH 07/11] MXCrypto: Use the last olm session that got a message (Fix vector-im/riot-android#2772) --- CHANGES.rst | 2 +- .../crypto/CryptoStoreImportationTest.kt | 12 +++- .../androidsdk/crypto/CryptoStoreTest.kt | 52 ++++++++++++++++ .../matrix/androidsdk/crypto/MXOlmDevice.java | 62 ++++++++++--------- .../androidsdk/crypto/data/MXOlmSession.kt | 36 +++++++++++ .../data/cryptostore/IMXCryptoStore.java | 19 ++++-- .../data/cryptostore/MXFileCryptoStore.java | 37 +++++------ .../cryptostore/db/CryptoFileStoreImporter.kt | 1 + .../data/cryptostore/db/RealmCryptoStore.kt | 36 +++++++---- .../db/RealmCryptoStoreMigration.kt | 17 ++++- .../cryptostore/db/model/OlmSessionEntity.kt | 3 +- 11 files changed, 205 insertions(+), 72 deletions(-) create mode 100644 matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSession.kt diff --git a/CHANGES.rst b/CHANGES.rst index bef0c420c..3ec996fc1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,7 +8,7 @@ Improvements: - isValidRecoveryKey() ignores now all whitespace characters, not only spaces Bugfix: - - + - MXCrypto: Use the last olm session that got a message (vector-im/riot-android#2772). API Change: - diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreImportationTest.kt b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreImportationTest.kt index 9a0fd3657..141a4f649 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreImportationTest.kt +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreImportationTest.kt @@ -19,11 +19,13 @@ package org.matrix.androidsdk.crypto import android.support.test.InstrumentationRegistry import android.text.TextUtils import org.junit.Assert.* +import org.junit.Before import org.junit.FixMethodOrder import org.junit.Test import org.junit.runners.MethodSorters import org.matrix.androidsdk.common.* import org.matrix.androidsdk.crypto.data.MXDeviceInfo +import org.matrix.androidsdk.crypto.data.MXOlmSession import org.matrix.androidsdk.data.RoomState import org.matrix.androidsdk.data.cryptostore.IMXCryptoStore import org.matrix.androidsdk.data.cryptostore.MXFileCryptoStore @@ -34,6 +36,7 @@ import org.matrix.androidsdk.rest.model.Event import org.matrix.androidsdk.rest.model.crypto.RoomKeyRequestBody import org.matrix.androidsdk.util.Log import org.matrix.olm.OlmAccount +import org.matrix.olm.OlmManager import org.matrix.olm.OlmSession import java.util.concurrent.CountDownLatch @@ -47,6 +50,11 @@ class CryptoStoreImportationTest { private val sessionTestParamLegacy = SessionTestParams(withInitialSync = true, withCryptoEnabled = true, withLegacyCryptoStore = true) private val sessionTestParamRealm = SessionTestParams(withInitialSync = true, withCryptoEnabled = true, withLegacyCryptoStore = false) + @Before + fun ensureLibLoaded() { + OlmManager() + } + @Test fun test_importationEmptyStore() { testImportation( @@ -194,7 +202,7 @@ class CryptoStoreImportationTest { testImportation( doOnFileStore = { - it.storeSession(session, "deviceID") + it.storeSession(MXOlmSession(session), "deviceID") }, checkOnRealmStore = { val sessionsFromRealm = it.getDeviceSessionIds("deviceID") @@ -205,7 +213,7 @@ class CryptoStoreImportationTest { val sessionFromRealm = it.getDeviceSession(sessionId, "deviceID") assertNotNull(sessionFromRealm) - assertEquals(sessionId, sessionFromRealm?.sessionIdentifier()) + assertEquals(sessionId, sessionFromRealm?.olmSession?.sessionIdentifier()) }) } diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt index f8e1b9b9d..f7d73fbc7 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt @@ -18,7 +18,12 @@ package org.matrix.androidsdk.crypto import org.junit.Assert.* import org.junit.Test +import org.matrix.androidsdk.crypto.data.MXOlmSession import org.matrix.androidsdk.data.cryptostore.IMXCryptoStore +import org.matrix.olm.OlmManager +import org.matrix.olm.OlmSession + +private const val DUMMY_DEVICE_KEY = "DeviceKey" class CryptoStoreTest { @@ -50,6 +55,53 @@ class CryptoStoreTest { cryptoStore.deleteStore() } + @Test + fun test_lastSessionUsed() { + // Ensure Olm is initialized + OlmManager() + + val cryptoStore: IMXCryptoStore = cryptoStoreHelper.createStore(true) + + assertNull(cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + + val firstOlmSession = OlmSession() + val firstSessionId = firstOlmSession.sessionIdentifier() + val firstMxOlmSession = MXOlmSession(firstOlmSession) + + cryptoStore.storeSession(firstMxOlmSession, DUMMY_DEVICE_KEY) + + assertEquals(firstSessionId, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + + val secondOlmSession = OlmSession() + val secondSessionId = secondOlmSession.sessionIdentifier() + val secondMxOlmSession = MXOlmSession(secondOlmSession) + + cryptoStore.storeSession(secondMxOlmSession, DUMMY_DEVICE_KEY) + + // Ensure sessionIds are distinct + // TODO: this test fails, so the whole test is a bit useless... + // assertNotEquals(firstSessionId, secondSessionId) + + // Note: we cannot be sure what will be the result of getLastUsedSessionId() here + + secondMxOlmSession.onMessageReceived() + cryptoStore.storeSession(secondMxOlmSession, DUMMY_DEVICE_KEY) + + // Second Id is returned now + assertEquals(secondSessionId, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + + Thread.sleep(200) + + firstMxOlmSession.onMessageReceived() + cryptoStore.storeSession(firstMxOlmSession, DUMMY_DEVICE_KEY) + + // First Id is returned now + assertEquals(firstSessionId, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + + // Cleanup + firstOlmSession.releaseSession() + secondOlmSession.releaseSession() + } companion object { private const val LOG_TAG = "CryptoStoreTest" diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXOlmDevice.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXOlmDevice.java index b9561886e..697d612c2 100755 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXOlmDevice.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXOlmDevice.java @@ -24,6 +24,7 @@ import org.matrix.androidsdk.crypto.algorithms.MXDecryptionResult; import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2; +import org.matrix.androidsdk.crypto.data.MXOlmSession; import org.matrix.androidsdk.data.cryptostore.IMXCryptoStore; import org.matrix.androidsdk.util.JsonUtils; import org.matrix.androidsdk.util.Log; @@ -35,8 +36,6 @@ import org.matrix.olm.OlmUtility; import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -228,7 +227,7 @@ public void generateOneTimeKeys(int numKeys) { * * @param theirIdentityKey the remote user's Curve25519 identity key * @param theirOneTimeKey the remote user's one-time Curve25519 key - * @return the session id for the outbound session. @TODO OLMSession? + * @return the session id for the outbound session. */ public String createOutboundSession(String theirIdentityKey, String theirOneTimeKey) { Log.d(LOG_TAG, "## createOutboundSession() ; theirIdentityKey " + theirIdentityKey + " theirOneTimeKey " + theirOneTimeKey); @@ -237,7 +236,15 @@ public String createOutboundSession(String theirIdentityKey, String theirOneTime try { olmSession = new OlmSession(); olmSession.initOutboundSession(mOlmAccount, theirIdentityKey, theirOneTimeKey); - mStore.storeSession(olmSession, theirIdentityKey); + + MXOlmSession mxOlmSession = new MXOlmSession(olmSession, 0); + + // Pretend we've received a message at this point, otherwise + // if we try to send a message to the device, it won't use + // this session + mxOlmSession.onMessageReceived(); + + mStore.storeSession(mxOlmSession, theirIdentityKey); String sessionIdentifier = olmSession.sessionIdentifier(); @@ -302,7 +309,12 @@ public Map createInboundSession(String theirDeviceIdentityKey, i try { payloadString = olmSession.decryptMessage(olmMessage); - mStore.storeSession(olmSession, theirDeviceIdentityKey); + + MXOlmSession mxOlmSession = new MXOlmSession(olmSession, 0); + // This counts as a received message: set last received message time to now + mxOlmSession.onMessageReceived(); + + mStore.storeSession(mxOlmSession, theirDeviceIdentityKey); } catch (Exception e) { Log.e(LOG_TAG, "## createInboundSession() : decryptMessage failed " + e.getMessage(), e); } @@ -345,19 +357,11 @@ public Set getSessionIds(String theirDeviceIdentityKey) { * Get the right olm session id for encrypting messages to the given identity key. * * @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. - * @return the session id, or nil if no established session. + * @return the session id, or null if no established session. */ + @Nullable public String getSessionId(String theirDeviceIdentityKey) { - String sessionId = null; - Set sessionIds = getSessionIds(theirDeviceIdentityKey); - - if ((null != sessionIds) && (0 != sessionIds.size())) { - List sessionIdsList = new ArrayList<>(sessionIds); - Collections.sort(sessionIdsList); - sessionId = sessionIdsList.get(0); - } - - return sessionId; + return mStore.getLastUsedSessionId(theirDeviceIdentityKey); } /** @@ -371,15 +375,15 @@ public String getSessionId(String theirDeviceIdentityKey) { public Map encryptMessage(String theirDeviceIdentityKey, String sessionId, String payloadString) { Map res = null; OlmMessage olmMessage; - OlmSession olmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId); + MXOlmSession mxOlmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId); - if (null != olmSession) { + if (mxOlmSession != null) { try { - Log.d(LOG_TAG, "## encryptMessage() : olmSession.sessionIdentifier: " + olmSession.sessionIdentifier()); + Log.d(LOG_TAG, "## encryptMessage() : olmSession.sessionIdentifier: " + sessionId); //Log.d(LOG_TAG, "## encryptMessage() : payloadString: " + payloadString); - olmMessage = olmSession.encryptMessage(payloadString); - mStore.storeSession(olmSession, theirDeviceIdentityKey); + olmMessage = mxOlmSession.getOlmSession().encryptMessage(payloadString); + mStore.storeSession(mxOlmSession, theirDeviceIdentityKey); res = new HashMap<>(); res.put("body", olmMessage.mCipherText); @@ -404,16 +408,17 @@ public Map encryptMessage(String theirDeviceIdentityKey, String public String decryptMessage(String ciphertext, int messageType, String sessionId, String theirDeviceIdentityKey) { String payloadString = null; - OlmSession olmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId); + MXOlmSession mxOlmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId); - if (null != olmSession) { + if (null != mxOlmSession) { OlmMessage olmMessage = new OlmMessage(); olmMessage.mCipherText = ciphertext; olmMessage.mType = messageType; try { - payloadString = olmSession.decryptMessage(olmMessage); - mStore.storeSession(olmSession, theirDeviceIdentityKey); + payloadString = mxOlmSession.getOlmSession().decryptMessage(olmMessage); + mxOlmSession.onMessageReceived(); + mStore.storeSession(mxOlmSession, theirDeviceIdentityKey); } catch (Exception e) { Log.e(LOG_TAG, "## decryptMessage() : decryptMessage failed " + e.getMessage(), e); } @@ -436,8 +441,8 @@ public boolean matchesSession(String theirDeviceIdentityKey, String sessionId, i return false; } - OlmSession olmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId); - return (null != olmSession) && olmSession.matchesInboundSession(ciphertext); + MXOlmSession mxOlmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId); + return (null != mxOlmSession) && mxOlmSession.getOlmSession().matchesInboundSession(ciphertext); } @@ -757,7 +762,8 @@ public String sha256(String message) { * @param sessionId the session Id * @return the olm session */ - private OlmSession getSessionForDevice(String theirDeviceIdentityKey, String sessionId) { + @Nullable + private MXOlmSession getSessionForDevice(String theirDeviceIdentityKey, String sessionId) { // sanity check if (!TextUtils.isEmpty(theirDeviceIdentityKey) && !TextUtils.isEmpty(sessionId)) { return mStore.getDeviceSession(sessionId, theirDeviceIdentityKey); diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSession.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSession.kt new file mode 100644 index 000000000..fb51741a2 --- /dev/null +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSession.kt @@ -0,0 +1,36 @@ +/* + * 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.crypto.data + +import org.matrix.olm.OlmSession + +/** + * Encapsulate a OlmSession and a last received message Timestamp + */ +data class MXOlmSession( + // The associated olm session. + val olmSession: OlmSession, + // Timestamp at which the session last received a message. + var lastReceivedMessageTs: Long = 0) { + + /** + * Notify that a message has been received on this olm session so that it updates `lastReceivedMessageTs` + */ + fun onMessageReceived() { + lastReceivedMessageTs = System.currentTimeMillis() + } +} \ No newline at end of file diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/IMXCryptoStore.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/IMXCryptoStore.java index 57a485bb5..9083f8096 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/IMXCryptoStore.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/IMXCryptoStore.java @@ -24,9 +24,9 @@ import org.matrix.androidsdk.crypto.OutgoingRoomKeyRequest; import org.matrix.androidsdk.crypto.data.MXDeviceInfo; import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2; +import org.matrix.androidsdk.crypto.data.MXOlmSession; import org.matrix.androidsdk.rest.model.login.Credentials; import org.matrix.olm.OlmAccount; -import org.matrix.olm.OlmSession; import java.util.List; import java.util.Map; @@ -159,7 +159,7 @@ public interface IMXCryptoStore { * @param session the end-to-end session. * @param deviceKey the public key of the other device. */ - void storeSession(OlmSession session, String deviceKey); + void storeSession(MXOlmSession session, String deviceKey); /** * Retrieve the end-to-end session ids between the logged-in user and another @@ -172,7 +172,7 @@ public interface IMXCryptoStore { Set getDeviceSessionIds(String deviceKey); /** - * Retrieve the end-to-end sessions between the logged-in user and another + * Retrieve an end-to-end session between the logged-in user and another * device. * * @param sessionId the session Id. @@ -180,7 +180,16 @@ public interface IMXCryptoStore { * @return The Base64 end-to-end session, or null if not found */ @Nullable - OlmSession getDeviceSession(String sessionId, String deviceKey); + MXOlmSession getDeviceSession(String sessionId, String deviceKey); + + /** + * Retrieve the last used sessionId, regarding `lastReceivedMessageTs`, or null if no session exist + * + * @param deviceKey the public key of the other device. + * @return last used sessionId, or null if not found + */ + @Nullable + String getLastUsedSessionId(String deviceKey); /** * Store an inbound group session. @@ -305,7 +314,7 @@ public interface IMXCryptoStore { * Get the tracking status of a specified userId devices. * * @param userId the user id - * @param defaultValue the default avlue + * @param defaultValue the default value * @return the tracking status */ int getDeviceTrackingStatus(String userId, int defaultValue); diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/MXFileCryptoStore.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/MXFileCryptoStore.java index c36adedf2..e21ef6bd7 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/MXFileCryptoStore.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/MXFileCryptoStore.java @@ -28,6 +28,7 @@ import org.matrix.androidsdk.crypto.data.MXDeviceInfo; import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession; import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2; +import org.matrix.androidsdk.crypto.data.MXOlmSession; import org.matrix.androidsdk.crypto.data.MXUsersDevicesMap; import org.matrix.androidsdk.rest.model.login.Credentials; import org.matrix.androidsdk.util.CompatUtil; @@ -727,8 +728,9 @@ public void saveDeviceTrackingStatuses(Map deviceTrackingStatus saveDeviceTrackingStatuses(); } + // Note: we keep this code for Unit test only. @Override - public void storeSession(final OlmSession olmSession, final String deviceKey) { + public void storeSession(final MXOlmSession olmSession, final String deviceKey) { if (!mIsReady) { Log.e(LOG_TAG, "## storeSession() : the store is not ready"); return; @@ -738,7 +740,7 @@ public void storeSession(final OlmSession olmSession, final String deviceKey) { if (null != olmSession) { try { - sessionIdentifier = olmSession.sessionIdentifier(); + sessionIdentifier = olmSession.getOlmSession().sessionIdentifier(); } catch (Exception e) { Log.e(LOG_TAG, "## storeSession : session.sessionIdentifier() failed " + e.getMessage(), e); } @@ -753,11 +755,11 @@ public void storeSession(final OlmSession olmSession, final String deviceKey) { OlmSession prevOlmSession = mOlmSessions.get(deviceKey).get(sessionIdentifier); // test if the session is a new one - if (olmSession != prevOlmSession) { + if (olmSession.getOlmSession() != prevOlmSession) { if (null != prevOlmSession) { prevOlmSession.releaseSession(); } - mOlmSessions.get(deviceKey).put(sessionIdentifier, olmSession); + mOlmSessions.get(deviceKey).put(sessionIdentifier, olmSession.getOlmSession()); } } @@ -767,7 +769,7 @@ public void storeSession(final OlmSession olmSession, final String deviceKey) { keyFolder.mkdir(); } - storeObject(olmSession, keyFolder, encodeFilename(sessionIdentifier), "Store olm session " + deviceKey + " " + sessionIdentifier); + storeObject(olmSession.getOlmSession(), keyFolder, encodeFilename(sessionIdentifier), "Store olm session " + deviceKey + " " + sessionIdentifier); } } @@ -796,24 +798,15 @@ public Set getDeviceSessionIds(String deviceKey) { @Nullable @Override - public OlmSession getDeviceSession(String sessionId, String deviceKey) { - if (!mIsReady) { - Log.e(LOG_TAG, "## storeSession() : the store is not ready"); - return null; - } - - if (null != deviceKey) { - Map map; - - synchronized (mOlmSessionsLock) { - map = mOlmSessions.get(deviceKey); - } - - if (map != null) { - return map.get(sessionId); - } - } + public MXOlmSession getDeviceSession(String sessionId, String deviceKey) { + // No op + return null; + } + @Nullable + @Override + public String getLastUsedSessionId(String deviceKey) { + // No op return null; } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/CryptoFileStoreImporter.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/CryptoFileStoreImporter.kt index 04947e777..af992a175 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/CryptoFileStoreImporter.kt +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/CryptoFileStoreImporter.kt @@ -187,6 +187,7 @@ internal class CryptoFileStoreImporter(private val enableFileEncryption: Boolean deviceKey = deviceKeyToMap.key sessionId = olmSessionIdToOlmSession.key putOlmSession(olmSessionIdToOlmSession.value) + lastReceivedMessageTs = 0 } } } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStore.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStore.kt index 2eab15983..256dbf85c 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStore.kt +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStore.kt @@ -20,11 +20,13 @@ import android.content.Context import android.text.TextUtils import io.realm.Realm import io.realm.RealmConfiguration +import io.realm.Sort import io.realm.kotlin.where import org.matrix.androidsdk.crypto.IncomingRoomKeyRequest import org.matrix.androidsdk.crypto.OutgoingRoomKeyRequest import org.matrix.androidsdk.crypto.data.MXDeviceInfo import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2 +import org.matrix.androidsdk.crypto.data.MXOlmSession import org.matrix.androidsdk.data.cryptostore.IMXCryptoStore import org.matrix.androidsdk.data.cryptostore.db.model.* import org.matrix.androidsdk.data.cryptostore.db.query.delete @@ -34,7 +36,6 @@ import org.matrix.androidsdk.rest.model.login.Credentials import org.matrix.androidsdk.util.Log import org.matrix.olm.OlmAccount import org.matrix.olm.OlmException -import org.matrix.olm.OlmSession import java.io.File import kotlin.collections.set @@ -49,7 +50,7 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC private var olmAccount: OlmAccount? = null // Cache for OlmSession, to release them properly - private val olmSessionsToRelease = HashMap() + private val olmSessionsToRelease = HashMap() // Cache for InboundGroupSession, to release them properly private val inboundGroupSessionToRelease = HashMap() @@ -131,7 +132,7 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC override fun close() { olmSessionsToRelease.forEach { - it.value.releaseSession() + it.value.olmSession.releaseSession() } olmSessionsToRelease.clear() @@ -269,7 +270,7 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC ?.algorithm } - override fun storeSession(session: OlmSession?, deviceKey: String?) { + override fun storeSession(session: MXOlmSession?, deviceKey: String?) { if (session == null || deviceKey == null) { return } @@ -277,7 +278,7 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC var sessionIdentifier: String? = null try { - sessionIdentifier = session.sessionIdentifier() + sessionIdentifier = session.olmSession.sessionIdentifier() } catch (e: OlmException) { Log.e(LOG_TAG, "## storeSession() : sessionIdentifier failed " + e.message, e) } @@ -286,8 +287,8 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC val key = OlmSessionEntity.createPrimaryKey(sessionIdentifier, deviceKey) // Release memory of previously known session, if it is not the same one - if (olmSessionsToRelease[key] != session) { - olmSessionsToRelease[key]?.releaseSession() + if (olmSessionsToRelease[key]?.olmSession != session.olmSession) { + olmSessionsToRelease[key]?.olmSession?.releaseSession() } olmSessionsToRelease[key] = session @@ -297,7 +298,8 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC primaryKey = key sessionId = sessionIdentifier this.deviceKey = deviceKey - putOlmSession(session) + putOlmSession(session.olmSession) + lastReceivedMessageTs = session.lastReceivedMessageTs } it.insertOrUpdate(realmOlmSession) @@ -305,7 +307,7 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC } } - override fun getDeviceSession(sessionId: String?, deviceKey: String?): OlmSession? { + override fun getDeviceSession(sessionId: String?, deviceKey: String?): MXOlmSession? { if (sessionId == null || deviceKey == null) { return null } @@ -319,15 +321,27 @@ class RealmCryptoStore(private val enableFileEncryption: Boolean = false) : IMXC .equalTo(OlmSessionEntityFields.PRIMARY_KEY, key) .findFirst() } - ?.getOlmSession() ?.let { - olmSessionsToRelease[key] = it + val olmSession = it.getOlmSession() + if (olmSession != null && it.sessionId != null) { + olmSessionsToRelease[key] = MXOlmSession(olmSession, it.lastReceivedMessageTs) + } } } return olmSessionsToRelease[key] } + override fun getLastUsedSessionId(deviceKey: String?): String? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey) + .sort(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, Sort.DESCENDING) + .findFirst() + } + ?.sessionId + } + override fun getDeviceSessionIds(deviceKey: String?): MutableSet { return doRealmQueryAndCopyList(realmConfiguration) { it.where() diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStoreMigration.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStoreMigration.kt index a750a25d4..aebc70e60 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/RealmCryptoStoreMigration.kt @@ -18,14 +18,27 @@ package org.matrix.androidsdk.data.cryptostore.db import io.realm.DynamicRealm import io.realm.RealmMigration +import org.matrix.androidsdk.data.cryptostore.db.model.OlmSessionEntityFields import org.matrix.androidsdk.util.Log internal object RealmCryptoStoreMigration : RealmMigration { - const val LOG_TAG = "RealmCryptoStoreMigration" - const val CRYPTO_STORE_SCHEMA_VERSION = 0L + private const val LOG_TAG = "RealmCryptoStoreMigration" + + const val CRYPTO_STORE_SCHEMA_VERSION = 1L override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Log.d(LOG_TAG, "Migrating Realm Crypto from $oldVersion to $newVersion") + + if (oldVersion <= 0) { + Log.d(LOG_TAG, "Step 0 -> 1") + Log.d(LOG_TAG, "Add field lastReceivedMessageTs (Long) and set the value to 0") + + realm.schema.get("OlmSessionEntity") + ?.addField(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, Long::class.java) + ?.transform { + it.setLong(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, 0) + } + } } } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/model/OlmSessionEntity.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/model/OlmSessionEntity.kt index 089def188..5a43d2c5d 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/model/OlmSessionEntity.kt +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/cryptostore/db/model/OlmSessionEntity.kt @@ -28,7 +28,8 @@ fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, deviceKey: St open class OlmSessionEntity(@PrimaryKey var primaryKey: String = "", var sessionId: String? = null, var deviceKey: String? = null, - var olmSessionData: String? = null) + var olmSessionData: String? = null, + var lastReceivedMessageTs: Long = 0) : RealmObject() { fun getOlmSession(): OlmSession? { From 4e35377e88b852c63b397356d5c4ad011e592413 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Dec 2018 16:53:51 +0100 Subject: [PATCH 08/11] Fix issue on test thanks to uhoreg --- .../androidsdk/crypto/CryptoStoreTest.kt | 18 ++++++++++++++++-- .../org/matrix/androidsdk/crypto/MXCrypto.java | 3 ++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt index f7d73fbc7..d41744960 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt @@ -20,6 +20,7 @@ import org.junit.Assert.* import org.junit.Test import org.matrix.androidsdk.crypto.data.MXOlmSession import org.matrix.androidsdk.data.cryptostore.IMXCryptoStore +import org.matrix.olm.OlmAccount import org.matrix.olm.OlmManager import org.matrix.olm.OlmSession @@ -64,7 +65,14 @@ class CryptoStoreTest { assertNull(cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + val firstOlmAccount = OlmAccount().apply { + generateOneTimeKeys(1) + } + val firstOlmSession = OlmSession() + firstOlmSession.initOutboundSession(firstOlmAccount, + firstOlmAccount.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY], + firstOlmAccount.oneTimeKeys()[OlmAccount.JSON_KEY_ONE_TIME_KEY]?.values?.first()) val firstSessionId = firstOlmSession.sessionIdentifier() val firstMxOlmSession = MXOlmSession(firstOlmSession) @@ -72,15 +80,21 @@ class CryptoStoreTest { assertEquals(firstSessionId, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + val secondOlmAccount = OlmAccount().apply { + generateOneTimeKeys(1) + } + val secondOlmSession = OlmSession() + secondOlmSession.initOutboundSession(secondOlmAccount, + secondOlmAccount.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY], + secondOlmAccount.oneTimeKeys()[OlmAccount.JSON_KEY_ONE_TIME_KEY]?.values?.first()) val secondSessionId = secondOlmSession.sessionIdentifier() val secondMxOlmSession = MXOlmSession(secondOlmSession) cryptoStore.storeSession(secondMxOlmSession, DUMMY_DEVICE_KEY) // Ensure sessionIds are distinct - // TODO: this test fails, so the whole test is a bit useless... - // assertNotEquals(firstSessionId, secondSessionId) + assertNotEquals(firstSessionId, secondSessionId) // Note: we cannot be sure what will be the result of getLastUsedSessionId() here diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXCrypto.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXCrypto.java index 5c88cd49e..0abdf1d4e 100755 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXCrypto.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXCrypto.java @@ -59,6 +59,7 @@ import org.matrix.androidsdk.rest.model.sync.SyncResponse; import org.matrix.androidsdk.util.JsonUtils; import org.matrix.androidsdk.util.Log; +import org.matrix.olm.OlmAccount; import java.lang.reflect.Constructor; import java.util.ArrayList; @@ -2148,7 +2149,7 @@ private void uploadOneTimeKeys(final ApiCallback callback) { final Map> oneTimeKeys = mOlmDevice.getOneTimeKeys(); Map oneTimeJson = new HashMap<>(); - Map curve25519Map = oneTimeKeys.get("curve25519"); + Map curve25519Map = oneTimeKeys.get(OlmAccount.JSON_KEY_ONE_TIME_KEY); if (null != curve25519Map) { for (String key_id : curve25519Map.keySet()) { From c356a4a399b89f32610fe51585ded7e508bf0bd7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Dec 2018 17:01:24 +0100 Subject: [PATCH 09/11] Better Kotlin code --- .../androidsdk/crypto/CryptoStoreTest.kt | 65 ++++++++++--------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt index d41744960..5696be251 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoStoreTest.kt @@ -65,56 +65,63 @@ class CryptoStoreTest { assertNull(cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) - val firstOlmAccount = OlmAccount().apply { + val olmAccount1 = OlmAccount().apply { generateOneTimeKeys(1) } - val firstOlmSession = OlmSession() - firstOlmSession.initOutboundSession(firstOlmAccount, - firstOlmAccount.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY], - firstOlmAccount.oneTimeKeys()[OlmAccount.JSON_KEY_ONE_TIME_KEY]?.values?.first()) - val firstSessionId = firstOlmSession.sessionIdentifier() - val firstMxOlmSession = MXOlmSession(firstOlmSession) + val olmSession1 = OlmSession().apply { + initOutboundSession(olmAccount1, + olmAccount1.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY], + olmAccount1.oneTimeKeys()[OlmAccount.JSON_KEY_ONE_TIME_KEY]?.values?.first()) + } + + val sessionId1 = olmSession1.sessionIdentifier() + val mxOlmSession1 = MXOlmSession(olmSession1) - cryptoStore.storeSession(firstMxOlmSession, DUMMY_DEVICE_KEY) + cryptoStore.storeSession(mxOlmSession1, DUMMY_DEVICE_KEY) - assertEquals(firstSessionId, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + assertEquals(sessionId1, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) - val secondOlmAccount = OlmAccount().apply { + val olmAccount2 = OlmAccount().apply { generateOneTimeKeys(1) } - val secondOlmSession = OlmSession() - secondOlmSession.initOutboundSession(secondOlmAccount, - secondOlmAccount.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY], - secondOlmAccount.oneTimeKeys()[OlmAccount.JSON_KEY_ONE_TIME_KEY]?.values?.first()) - val secondSessionId = secondOlmSession.sessionIdentifier() - val secondMxOlmSession = MXOlmSession(secondOlmSession) + val olmSession2 = OlmSession().apply { + initOutboundSession(olmAccount2, + olmAccount2.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY], + olmAccount2.oneTimeKeys()[OlmAccount.JSON_KEY_ONE_TIME_KEY]?.values?.first()) + } - cryptoStore.storeSession(secondMxOlmSession, DUMMY_DEVICE_KEY) + val sessionId2 = olmSession2.sessionIdentifier() + val mxOlmSession2 = MXOlmSession(olmSession2) + + cryptoStore.storeSession(mxOlmSession2, DUMMY_DEVICE_KEY) // Ensure sessionIds are distinct - assertNotEquals(firstSessionId, secondSessionId) + assertNotEquals(sessionId1, sessionId2) // Note: we cannot be sure what will be the result of getLastUsedSessionId() here - secondMxOlmSession.onMessageReceived() - cryptoStore.storeSession(secondMxOlmSession, DUMMY_DEVICE_KEY) + mxOlmSession2.onMessageReceived() + cryptoStore.storeSession(mxOlmSession2, DUMMY_DEVICE_KEY) - // Second Id is returned now - assertEquals(secondSessionId, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + // sessionId2 is returned now + assertEquals(sessionId2, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) - Thread.sleep(200) + Thread.sleep(2) - firstMxOlmSession.onMessageReceived() - cryptoStore.storeSession(firstMxOlmSession, DUMMY_DEVICE_KEY) + mxOlmSession1.onMessageReceived() + cryptoStore.storeSession(mxOlmSession1, DUMMY_DEVICE_KEY) - // First Id is returned now - assertEquals(firstSessionId, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) + // sessionId1 is returned now + assertEquals(sessionId1, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY)) // Cleanup - firstOlmSession.releaseSession() - secondOlmSession.releaseSession() + olmSession1.releaseSession() + olmSession2.releaseSession() + + olmAccount1.releaseAccount() + olmAccount2.releaseAccount() } companion object { From dcfac814e7ff39626028bcd1e939ac6f899bc9f5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Dec 2018 17:21:35 +0100 Subject: [PATCH 10/11] Add @Nullable to prevent bug --- .../matrix/androidsdk/rest/model/message/FileMessage.java | 2 ++ .../matrix/androidsdk/rest/model/message/ImageMessage.java | 4 ++++ .../matrix/androidsdk/rest/model/message/MediaMessage.java | 5 ++++- .../matrix/androidsdk/rest/model/message/VideoMessage.java | 5 +++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/FileMessage.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/FileMessage.java index d5a76e2c8..10c209f55 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/FileMessage.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/FileMessage.java @@ -16,6 +16,7 @@ package org.matrix.androidsdk.rest.model.message; import android.content.ClipDescription; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.webkit.MimeTypeMap; @@ -81,6 +82,7 @@ public FileMessage deepCopy() { return copy; } + @Nullable @Override public String getMimeType() { if (null != info) { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/ImageMessage.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/ImageMessage.java index e0f68e1d3..c2c213869 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/ImageMessage.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/ImageMessage.java @@ -17,6 +17,7 @@ package org.matrix.androidsdk.rest.model.message; import android.media.ExifInterface; +import android.support.annotation.Nullable; import org.matrix.androidsdk.crypto.MXEncryptedAttachments; import org.matrix.androidsdk.rest.model.crypto.EncryptedFileInfo; @@ -51,6 +52,7 @@ public ImageMessage deepCopy() { return copy; } + @Nullable @Override public String getUrl() { if (null != url) { @@ -73,6 +75,7 @@ public void setUrl(MXEncryptedAttachments.EncryptionResult encryptionResult, Str } } + @Nullable @Override public String getThumbnailUrl() { if (null != info) { @@ -96,6 +99,7 @@ public void setThumbnailUrl(MXEncryptedAttachments.EncryptionResult encryptionRe } } + @Nullable @Override public String getMimeType() { if (null != file) { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/MediaMessage.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/MediaMessage.java index ba1c7bfe6..5edfb822b 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/MediaMessage.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/MediaMessage.java @@ -16,6 +16,7 @@ package org.matrix.androidsdk.rest.model.message; import android.net.Uri; +import android.support.annotation.Nullable; import org.matrix.androidsdk.crypto.MXEncryptedAttachments; import org.matrix.androidsdk.util.Log; @@ -28,17 +29,18 @@ public class MediaMessage extends Message { /** * @return the media URL */ + @Nullable public String getUrl() { return null; } - public void setUrl(MXEncryptedAttachments.EncryptionResult encryptionResult, String url) { } /** * @return the thumbnail url */ + @Nullable public String getThumbnailUrl() { return null; } @@ -65,6 +67,7 @@ public boolean isLocalContent() { /** * @return The image mimetype. null is not defined. */ + @Nullable public String getMimeType() { return null; } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/VideoMessage.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/VideoMessage.java index a69e6747d..02d3242b6 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/VideoMessage.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/message/VideoMessage.java @@ -15,6 +15,8 @@ */ package org.matrix.androidsdk.rest.model.message; +import android.support.annotation.Nullable; + import org.matrix.androidsdk.crypto.MXEncryptedAttachments; import org.matrix.androidsdk.rest.model.crypto.EncryptedFileInfo; @@ -31,6 +33,7 @@ public VideoMessage() { msgtype = MSGTYPE_VIDEO; } + @Nullable @Override public String getUrl() { if (null != url) { @@ -53,6 +56,7 @@ public void setUrl(MXEncryptedAttachments.EncryptionResult encryptionResult, Str } } + @Nullable @Override public String getThumbnailUrl() { if ((null != info) && (null != info.thumbnail_url)) { @@ -97,6 +101,7 @@ public VideoMessage deepCopy() { return copy; } + @Nullable @Override public String getMimeType() { if (null != info) { From 312debfab7e7202e8aade5df2513f16ff5d32b09 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Jan 2019 12:39:29 +0100 Subject: [PATCH 11/11] Prepare delivery --- CHANGES.rst | 17 +---------------- matrix-sdk/build.gradle | 2 +- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2cb886f05..b57b9b9be 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,9 +1,6 @@ -Changes to Matrix Android SDK in 0.9.15 (2018-XX-XX) +Changes to Matrix Android SDK in 0.9.15 (2018-01-02) ======================================================= -Features: - - - Improvements: - isValidRecoveryKey() ignores now all whitespace characters, not only spaces @@ -11,18 +8,6 @@ Bugfix: - MXCrypto: Use the last olm session that got a message (vector-im/riot-android#2772). - Ensure there is no ghost device in the Realm crypto store (vector-im/riot-android#2784) -API Change: - - - -Translations: - - - -Others: - - - -Build: - - - Test: - New test for recovery key with newlines in it diff --git a/matrix-sdk/build.gradle b/matrix-sdk/build.gradle index d80bcf29e..8e881f9c2 100644 --- a/matrix-sdk/build.gradle +++ b/matrix-sdk/build.gradle @@ -27,7 +27,7 @@ android { minSdkVersion 16 targetSdkVersion 26 versionCode 915 - versionName "0.9.15-dev" + versionName "0.9.15" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" }