From 6ab540045b552061db80df51e232bed693d00fcd Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 20 Jan 2020 18:13:32 +0100 Subject: [PATCH] Refactoring / deprecation of MXDeviceInfo introduced TrustLevels --- .../vector/matrix/android/InstrumentedTest.kt | 5 +- .../matrix/android/common/CommonTestHelper.kt | 1 - .../matrix/android/common/CryptoTestHelper.kt | 3 - .../crypto/crosssigning/XSigningTest.kt | 55 ++++---- .../crypto/keysbackup/KeysBackupTest.kt | 4 +- .../internal/crypto/verification/SASTest.kt | 8 +- .../api/extensions/MatrixSdkExtensions.kt | 4 +- .../api/session/crypto/CryptoService.kt | 16 ++- .../api/session/crypto/MXCryptoError.kt | 4 +- .../crosssigning/CrossSigningService.kt | 9 +- .../crypto/crosssigning/MXCrossSigningInfo.kt | 26 ++-- .../session/crypto/crosssigning/MXKeyInfo.kt | 18 +-- .../internal/crypto/DefaultCryptoService.kt | 48 ++++--- .../internal/crypto/DeviceListManager.kt | 55 ++++---- .../internal/crypto/MyDeviceInfoHolder.kt | 25 +++- .../EnsureOlmSessionsForDevicesAction.kt | 8 +- .../EnsureOlmSessionsForUsersAction.kt | 2 +- .../crypto/actions/MessageEncrypter.kt | 4 +- .../actions/SetDeviceVerificationAction.kt | 13 +- .../algorithms/megolm/MXMegolmEncryption.kt | 18 +-- .../megolm/MXOutboundSessionInfo.kt | 4 +- .../crypto/algorithms/olm/MXOlmEncryption.kt | 4 +- .../android/internal/crypto/api/CryptoApi.kt | 4 - .../DefaultCrossSigningService.kt | 117 ++++++++-------- .../DeviceTrustLevel.kt} | 22 +-- .../crypto/crosssigning/DeviceTrustResult.kt | 4 +- .../crypto/crosssigning/Extensions.kt | 9 +- .../crypto/crosssigning/UserTrustResult.kt | 33 +++++ .../model/KeyBackupVersionTrustSignature.kt | 4 +- .../model/KeysBackupVersionTrustSignature.kt | 4 +- .../crypto/model/CryptoCrossSigningKey.kt | 86 ++++++++++++ .../internal/crypto/model/CryptoDeviceInfo.kt | 102 ++++++++++++++ .../model/{MXKeysObject.kt => CryptoInfo.kt} | 11 +- .../internal/crypto/model/CryptoInfoMapper.kt | 84 ++++++++++++ .../internal/crypto/model/MXDeviceInfo.kt | 8 -- .../crypto/model/MXOlmSessionResult.kt | 2 +- .../crypto/model/rest/CrossSigningKeyInfo.kt | 126 ------------------ .../internal/crypto/model/rest/DeviceKeys.kt | 17 ++- .../crypto/model/rest/KeysQueryResponse.kt | 10 +- .../crypto/model/rest/KeysUploadBody.kt | 2 +- .../crypto/model/rest/RestDeviceInfo.kt | 59 ++++++++ .../internal/crypto/model/rest/RestKeyInfo.kt | 57 ++++++++ .../model/rest/SignatureUploadResponse.kt | 2 - .../model/rest/UploadSignatureQueryBuilder.kt | 24 ++-- .../model/rest/UploadSigningKeysBody.kt | 7 +- .../crypto/model/rest/XSigningKeys.kt | 20 --- .../internal/crypto/store/IMXCryptoStore.kt | 28 ++-- .../crypto/store/db/RealmCryptoStore.kt | 116 ++++++++++------ .../store/db/RealmCryptoStoreMigration.kt | 80 ++++++++++- .../crypto/store/db/RealmCryptoStoreModule.kt | 3 +- .../store/db/model/CrossSigningInfoEntity.kt | 13 +- .../crypto/store/db/model/CryptoMapper.kt | 110 +++++++++++++++ .../store/db/model/CryptoMetadataEntity.kt | 1 - .../crypto/store/db/model/DeviceInfoEntity.kt | 31 +++-- .../crypto/store/db/model/KeyInfoEntity.kt | 1 - .../crypto/store/db/model/TrustLevelEntity.kt | 13 ++ .../db/query/CrossSigningInfoEntityQueries.kt | 1 - .../internal/crypto/tasks/UploadKeysTask.kt | 4 +- .../crypto/tasks/UploadSignaturesTask.kt | 7 +- .../crypto/tasks/UploadSigningKeysTask.kt | 21 ++- .../DefaultSasVerificationService.kt | 7 +- .../SASVerificationTransaction.kt | 28 ++-- .../session/room/send/EncryptEventWorker.kt | 1 - .../internal/task/CoroutineSequencersTest.kt | 46 +++++++ .../riotx/features/debug/DebugMenuActivity.kt | 2 - .../crypto/keysrequest/KeyRequestHandler.kt | 13 +- .../verification/VerificationBottomSheet.kt | 2 +- .../home/room/detail/RoomDetailViewModel.kt | 1 - 68 files changed, 1117 insertions(+), 560 deletions(-) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/{model/rest/UploadSignaturesBody.kt => crosssigning/DeviceTrustLevel.kt} (51%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/UserTrustResult.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoCrossSigningKey.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoDeviceInfo.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/{MXKeysObject.kt => CryptoInfo.kt} (74%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoInfoMapper.kt delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/CrossSigningKeyInfo.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RestDeviceInfo.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RestKeyInfo.kt delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/XSigningKeys.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMapper.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/TrustLevelEntity.kt diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/InstrumentedTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/InstrumentedTest.kt index 7c29ec520c..56b358c69f 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/InstrumentedTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/InstrumentedTest.kt @@ -19,12 +19,9 @@ package im.vector.matrix.android import android.content.Context import androidx.test.core.app.ApplicationProvider import java.io.File -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import org.junit.Rule - interface InstrumentedTest { - + fun context(): Context { return ApplicationProvider.getApplicationContext() } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt index 104cf94280..97ed8ca6c5 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt @@ -94,7 +94,6 @@ class CommonTestHelper(context: Context) { syncLiveData.removeObserver(this) } } - } GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt index afc2d3a749..25cfff5c16 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt @@ -92,7 +92,6 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { val lock1 = CountDownLatch(2) - val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) } @@ -140,8 +139,6 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { bobRoomSummariesLive.observeForever(roomJoinedObserver) } - - bobSession.joinRoom(aliceRoomId, callback = TestMatrixCallback(lock2)) mTestHelper.await(lock2) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt index 3f03e687fe..01374bd1c3 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt @@ -4,11 +4,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import im.vector.matrix.android.InstrumentedTest import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.common.* -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth -import org.junit.Assert import org.junit.Assert.* import org.junit.FixMethodOrder import org.junit.Test @@ -16,7 +15,6 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import java.util.concurrent.CountDownLatch - @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class XSigningTest : InstrumentedTest { @@ -24,12 +22,10 @@ class XSigningTest : InstrumentedTest { private val mTestHelper = CommonTestHelper(context()) private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) - @Test fun test_InitializeAndStoreKeys() { val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - val aliceLatch = CountDownLatch(1) aliceSession.getCrossSigningService() .initializeCrossSigning(UserPasswordAuth( @@ -41,14 +37,15 @@ class XSigningTest : InstrumentedTest { val myCrossSigningKeys = aliceSession.getCrossSigningService().getMyCrossSigningKeys() val masterPubKey = myCrossSigningKeys?.masterKey() - Assert.assertNotNull("Master key should be stored", masterPubKey?.unpaddedBase64PublicKey) + assertNotNull("Master key should be stored", masterPubKey?.unpaddedBase64PublicKey) val selfSigningKey = myCrossSigningKeys?.selfSigningKey() - Assert.assertNotNull("SelfSigned key should be stored", selfSigningKey?.unpaddedBase64PublicKey) + assertNotNull("SelfSigned key should be stored", selfSigningKey?.unpaddedBase64PublicKey) val userKey = myCrossSigningKeys?.userKey() - Assert.assertNotNull("User key should be stored", userKey?.unpaddedBase64PublicKey) + assertNotNull("User key should be stored", userKey?.unpaddedBase64PublicKey) + assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted == true) - Assert.assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted == true) + mTestHelper.signout(aliceSession) } @Test @@ -76,21 +73,23 @@ class XSigningTest : InstrumentedTest { mTestHelper.await(aliceLatch) mTestHelper.await(bobLatch) - //Check that alice can see bob keys + // Check that alice can see bob keys val downloadLatch = CountDownLatch(1) aliceSession.downloadKeys(listOf(bobSession.myUserId), true, TestMatrixCallback(downloadLatch)) mTestHelper.await(downloadLatch) val bobKeysFromAlicePOV = aliceSession.getCrossSigningService().getUserCrossSigningKeys(bobSession.myUserId) - Assert.assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV?.masterKey()) - Assert.assertNull("Alice should not see bob User key", bobKeysFromAlicePOV?.userKey()) - Assert.assertNotNull("Alice can see bob SelfSigned key", bobKeysFromAlicePOV?.selfSigningKey()) + assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV?.masterKey()) + assertNull("Alice should not see bob User key", bobKeysFromAlicePOV?.userKey()) + assertNotNull("Alice can see bob SelfSigned key", bobKeysFromAlicePOV?.selfSigningKey()) - Assert.assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV?.masterKey()?.unpaddedBase64PublicKey, bobSession.getCrossSigningService().getMyCrossSigningKeys()?.masterKey()?.unpaddedBase64PublicKey) - Assert.assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV?.selfSigningKey()?.unpaddedBase64PublicKey, bobSession.getCrossSigningService().getMyCrossSigningKeys()?.selfSigningKey()?.unpaddedBase64PublicKey) + assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV?.masterKey()?.unpaddedBase64PublicKey, bobSession.getCrossSigningService().getMyCrossSigningKeys()?.masterKey()?.unpaddedBase64PublicKey) + assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV?.selfSigningKey()?.unpaddedBase64PublicKey, bobSession.getCrossSigningService().getMyCrossSigningKeys()?.selfSigningKey()?.unpaddedBase64PublicKey) - Assert.assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted == false) + assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted == false) + mTestHelper.signout(aliceSession) + mTestHelper.signout(bobSession) } @Test @@ -118,15 +117,14 @@ class XSigningTest : InstrumentedTest { mTestHelper.await(aliceLatch) mTestHelper.await(bobLatch) - //Check that alice can see bob keys + // Check that alice can see bob keys val downloadLatch = CountDownLatch(1) val bobUserId = bobSession.myUserId aliceSession.downloadKeys(listOf(bobUserId), true, TestMatrixCallback(downloadLatch)) mTestHelper.await(downloadLatch) val bobKeysFromAlicePOV = aliceSession.getCrossSigningService().getUserCrossSigningKeys(bobUserId) - Assert.assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted == false) - + assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted == false) val trustLatch = CountDownLatch(1) aliceSession.getCrossSigningService().trustUser(bobUserId, object : MatrixCallback { @@ -146,15 +144,14 @@ class XSigningTest : InstrumentedTest { val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true)) val bobSecondDeviceId = bobSession2.sessionParams.credentials.deviceId - // Check that bob first session sees the new login val bobKeysLatch = CountDownLatch(1) - bobSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback> { + bobSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback> { override fun onFailure(failure: Throwable) { fail("Failed to get device") } - override fun onSuccess(data: MXUsersDevicesMap) { + override fun onSuccess(data: MXUsersDevicesMap) { if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId!!) == false) { fail("Bob should see the new device") } @@ -181,13 +178,13 @@ class XSigningTest : InstrumentedTest { // Now alice should cross trust bob's second device val aliceKeysLatch = CountDownLatch(1) - aliceSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback> { + aliceSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback> { override fun onFailure(failure: Throwable) { fail("Failed to get device") } - override fun onSuccess(data: MXUsersDevicesMap) { - //check that the device is seen + override fun onSuccess(data: MXUsersDevicesMap) { + // check that the device is seen if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) { fail("Alice should see the new device") } @@ -196,9 +193,11 @@ class XSigningTest : InstrumentedTest { }) mTestHelper.await(aliceKeysLatch) + val result = aliceSession.getCrossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) + assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified()) - val result = aliceSession.getCrossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId) - assertTrue("Bob second device should be trusted from alice POV", result.isSuccess()) - + mTestHelper.signout(aliceSession) + mTestHelper.signout(bobSession) + mTestHelper.signout(bobSession2) } } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt index 15deebdab1..3c77661b8b 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt @@ -29,12 +29,12 @@ import im.vector.matrix.android.common.* import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import im.vector.matrix.android.internal.crypto.MegolmSessionData import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper import org.junit.Assert.* import org.junit.FixMethodOrder @@ -1161,7 +1161,7 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup2.isEnabled) // - Validate the old device from the new one - aliceSession2.setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, oldDeviceId, aliceSession2.myUserId) + aliceSession2.setDeviceVerification(DeviceTrustLevel(false, true), oldDeviceId, aliceSession2.myUserId) // -> Backup should automatically enable on the new device val latch4 = CountDownLatch(1) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt index 1887a013dc..f566c82d16 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt @@ -24,7 +24,7 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.common.CommonTestHelper import im.vector.matrix.android.common.CryptoTestHelper -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationCancel @@ -38,7 +38,6 @@ import org.junit.runners.MethodSorters import java.util.* import java.util.concurrent.CountDownLatch - @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class SASTest : InstrumentedTest { @@ -140,7 +139,6 @@ class SASTest : InstrumentedTest { var cancelReason: String? = null val cancelLatch = CountDownLatch(1) - val bobListener = object : SasVerificationService.SasVerificationListener { override fun transactionCreated(tx: SasVerificationTransaction) {} @@ -530,8 +528,8 @@ class SASTest : InstrumentedTest { mTestHelper.await(bobSASLatch) // Assert that devices are verified - val bobDeviceInfoFromAlicePOV: MXDeviceInfo? = aliceSession.getDeviceInfo(bobUserId, bobDeviceId) - val aliceDeviceInfoFromBobPOV: MXDeviceInfo? = bobSession.getDeviceInfo(aliceSession.myUserId, aliceSession.getMyDevice().deviceId) + val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.getDeviceInfo(bobUserId, bobDeviceId) + val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.getDeviceInfo(aliceSession.myUserId, aliceSession.getMyDevice().deviceId) // latch wait a bit again Thread.sleep(1000) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt index bada3f86a1..23e8c70386 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt @@ -17,14 +17,14 @@ package im.vector.matrix.android.api.extensions import im.vector.matrix.android.api.comparators.DatedObjectComparators -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo /* ========================================================================================== * MXDeviceInfo * ========================================================================================== */ -fun MXDeviceInfo.getFingerprintHumanReadable() = fingerprint() +fun CryptoDeviceInfo.getFingerprintHumanReadable() = fingerprint() ?.chunked(4) ?.joinToString(separator = " ") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt index 9dbc679ba2..de657e6abb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt @@ -27,6 +27,8 @@ import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.NewSessionListener +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult @@ -48,7 +50,7 @@ interface CryptoService { fun isCryptoEnabled(): Boolean fun getSasVerificationService(): SasVerificationService - + fun getCrossSigningService(): CrossSigningService fun getKeysBackupService(): KeysBackupService @@ -57,15 +59,15 @@ interface CryptoService { fun setWarnOnUnknownDevices(warn: Boolean) - fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String) + fun setDeviceVerification(trustLevel: DeviceTrustLevel, deviceId: String, userId: String) - fun getUserDevices(userId: String): MutableList + fun getUserDevices(userId: String): MutableList fun setDevicesKnown(devices: List, callback: MatrixCallback?) - fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo? + fun deviceWithIdentityKey(senderKey: String, algorithm: String): CryptoDeviceInfo? - fun getMyDevice(): MXDeviceInfo + fun getMyDevice(): CryptoDeviceInfo fun getGlobalBlacklistUnverifiedDevices(): Boolean @@ -81,7 +83,7 @@ interface CryptoService { fun setRoomBlacklistUnverifiedDevices(roomId: String) - fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? + fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? fun reRequestRoomKeyForEvent(event: Event) @@ -113,7 +115,7 @@ interface CryptoService { fun shouldEncryptForInvitedMembers(roomId: String): Boolean - fun downloadKeys(userIds: List, forceDownload: Boolean, callback: MatrixCallback>) + fun downloadKeys(userIds: List, forceDownload: Boolean, callback: MatrixCallback>) fun addNewSessionListener(newSessionListener: NewSessionListener) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt index fe41b6c074..be817c70cb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt @@ -18,7 +18,7 @@ package im.vector.matrix.android.api.session.crypto -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import org.matrix.olm.OlmException @@ -36,7 +36,7 @@ sealed class MXCryptoError : Throwable() { data class OlmError(val olmException: OlmException) : MXCryptoError() - data class UnknownDevice(val deviceList: MXUsersDevicesMap) : MXCryptoError() + data class UnknownDevice(val deviceList: MXUsersDevicesMap) : MXCryptoError() enum class ErrorType { ENCRYPTING_NOT_ENABLED, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt index 7e43847849..a45c39ab50 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.crypto.crosssigning import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustResult +import im.vector.matrix.android.internal.crypto.crosssigning.UserTrustResult import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth @@ -25,7 +26,11 @@ interface CrossSigningService { fun isUserTrusted(userId: String) : Boolean - fun checkUserTrust(userId: String, callback: MatrixCallback? = null) + /** + * Will not force a download of the key, but will verify signatures trust chain. + * Checks that my trusted user key has signed the other user UserKey + */ + fun checkUserTrust(userId: String) : UserTrustResult /** * Initialize cross signing for this user. @@ -44,5 +49,5 @@ interface CrossSigningService { */ fun signDevice(deviceId: String, callback: MatrixCallback) - fun checkDeviceTrust(userId: String, deviceId: String) : DeviceTrustResult + fun checkDeviceTrust(userId: String, deviceId: String, locallyTrusted: Boolean?) : DeviceTrustResult } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/MXCrossSigningInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/MXCrossSigningInfo.kt index 9c559998a3..f778c99f2f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/MXCrossSigningInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/MXCrossSigningInfo.kt @@ -16,32 +16,26 @@ package im.vector.matrix.android.api.session.crypto.crosssigning -import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey +import im.vector.matrix.android.internal.crypto.model.KeyUsage data class MXCrossSigningInfo( - /** - * the user id - */ -// @Json(name = "user_id") var userId: String, -// @Json(name = "user_keys") - var crossSigningKeys: List = ArrayList(), + var crossSigningKeys: List = ArrayList(), + // TODO this should at the key level no? val isTrusted: Boolean = false ) { - fun masterKey(): CrossSigningKeyInfo? = crossSigningKeys - .firstOrNull { it.usages?.contains(CrossSigningKeyInfo.KeyUsage.MASTER.value) == true } + fun masterKey(): CryptoCrossSigningKey? = crossSigningKeys + .firstOrNull { it.usages?.contains(KeyUsage.MASTER.value) == true } + fun userKey(): CryptoCrossSigningKey? = crossSigningKeys + .firstOrNull { it.usages?.contains(KeyUsage.USER_SIGNING.value) == true } - fun userKey(): CrossSigningKeyInfo? = crossSigningKeys - .firstOrNull { it.usages?.contains(CrossSigningKeyInfo.KeyUsage.USER_SIGNING.value) == true } - - - fun selfSigningKey(): CrossSigningKeyInfo? = crossSigningKeys - .firstOrNull { it.usages?.contains(CrossSigningKeyInfo.KeyUsage.SELF_SIGNING.value) == true } - + fun selfSigningKey(): CryptoCrossSigningKey? = crossSigningKeys + .firstOrNull { it.usages?.contains(KeyUsage.SELF_SIGNING.value) == true } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/MXKeyInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/MXKeyInfo.kt index 4302438a01..5beb4ea893 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/MXKeyInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/MXKeyInfo.kt @@ -1,4 +1,4 @@ -///* +// /* // * Copyright 2020 New Vector Ltd // * // * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,15 +14,15 @@ // * limitations under the License. // */ // -//package im.vector.matrix.android.api.session.crypto.crosssigning +// package im.vector.matrix.android.api.session.crypto.crosssigning // -//import com.squareup.moshi.Json -//import com.squareup.moshi.JsonClass -//import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo +// import com.squareup.moshi.Json +// import com.squareup.moshi.JsonClass +// import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo // // -//@JsonClass(generateAdapter = true) -//data class MXKeyInfo( +// @JsonClass(generateAdapter = true) +// data class MXKeyInfo( // // @Json(name = "public_key") // val publicKeyBase64: String, @@ -42,7 +42,7 @@ // @Json(name = "signatures") // var signatures: Map>? = null // -//) { +// ) { // // data class Builder( // private val publicKeyBase64: String, @@ -76,5 +76,5 @@ // ) // } // } -//} +// } // diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt index 4eaf428b95..53b2442625 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt @@ -45,7 +45,9 @@ import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory import im.vector.matrix.android.internal.crypto.crosssigning.DefaultCrossSigningService +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult @@ -55,9 +57,15 @@ import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.model.toRest import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore -import im.vector.matrix.android.internal.crypto.tasks.* +import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceTask +import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceWithUserPasswordTask +import im.vector.matrix.android.internal.crypto.tasks.GetDeviceInfoTask +import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask +import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask +import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where @@ -72,7 +80,12 @@ import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.fetchCopied -import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import org.matrix.olm.OlmManager import timber.log.Timber import java.util.concurrent.atomic.AtomicBoolean @@ -196,7 +209,7 @@ internal class DefaultCryptoService @Inject constructor( return if (longFormat) olmManager.getDetailedVersion(context) else olmManager.version } - override fun getMyDevice(): MXDeviceInfo { + override fun getMyDevice(): CryptoDeviceInfo { return myDeviceInfoHolder.get().myDevice } @@ -354,7 +367,7 @@ internal class DefaultCryptoService @Inject constructor( * @param algorithm the encryption algorithm. * @return the device info, or null if not found / unsupported algorithm / crypto released */ - override fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo? { + override fun deviceWithIdentityKey(senderKey: String, algorithm: String): CryptoDeviceInfo? { return if (algorithm != MXCRYPTO_ALGORITHM_MEGOLM && algorithm != MXCRYPTO_ALGORITHM_OLM) { // We only deal in olm keys null @@ -367,7 +380,7 @@ internal class DefaultCryptoService @Inject constructor( * @param userId the user id * @param deviceId the device id */ - override fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? { + override fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? { return if (userId.isNotEmpty() && !deviceId.isNullOrEmpty()) { cryptoStore.getUserDevice(userId, deviceId) } else { @@ -398,7 +411,7 @@ internal class DefaultCryptoService @Inject constructor( // assume if the device is either verified or blocked // it means that the device is known if (device?.isUnknown == true) { - device.verified = MXDeviceInfo.DEVICE_VERIFICATION_UNVERIFIED + device.trustLevel = DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false) isUpdated = true } } @@ -419,8 +432,8 @@ internal class DefaultCryptoService @Inject constructor( * @param deviceId the unique identifier for the device. * @param userId the owner of the device */ - override fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String) { - setDeviceVerificationAction.handle(verificationStatus, deviceId, userId) + override fun setDeviceVerification(trustLevel: DeviceTrustLevel, deviceId: String, userId: String) { + setDeviceVerificationAction.handle(trustLevel, deviceId, userId) } /** @@ -499,9 +512,8 @@ internal class DefaultCryptoService @Inject constructor( /** * @return the stored device keys for a user. */ - override fun getUserDevices(userId: String): MutableList { - val map = cryptoStore.getUserDevices(userId) - return if (null != map) ArrayList(map.values) else ArrayList() + override fun getUserDevices(userId: String): MutableList { + return cryptoStore.getUserDevices(userId)?.values?.toMutableList() ?: ArrayList() } fun isEncryptionEnabledForInvitedUser(): Boolean { @@ -763,11 +775,15 @@ internal class DefaultCryptoService @Inject constructor( // Prepare the device keys data to send // Sign it val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary()) - getMyDevice().signatures = objectSigner.signObject(canonicalJson) + var rest = getMyDevice().toRest() + + rest = rest.copy( + signatures = objectSigner.signObject(canonicalJson) + ) // For now, we set the device id explicitly, as we may not be using the // same one as used in login. - val uploadDeviceKeysParams = UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId) + val uploadDeviceKeysParams = UploadKeysTask.Params(rest, null, getMyDevice().deviceId) return uploadKeysTask.execute(uploadDeviceKeysParams) } @@ -1007,8 +1023,8 @@ internal class DefaultCryptoService @Inject constructor( * @param devicesInRoom the devices map * @return the unknown devices map */ - private fun getUnknownDevices(devicesInRoom: MXUsersDevicesMap): MXUsersDevicesMap { - val unknownDevices = MXUsersDevicesMap() + private fun getUnknownDevices(devicesInRoom: MXUsersDevicesMap): MXUsersDevicesMap { + val unknownDevices = MXUsersDevicesMap() val userIds = devicesInRoom.userIds for (userId in userIds) { devicesInRoom.getUserDeviceIds(userId)?.forEach { deviceId -> @@ -1023,7 +1039,7 @@ internal class DefaultCryptoService @Inject constructor( return unknownDevices } - override fun downloadKeys(userIds: List, forceDownload: Boolean, callback: MatrixCallback>) { + override fun downloadKeys(userIds: List, forceDownload: Boolean, callback: MatrixCallback>) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { runCatching { deviceListManager.downloadKeys(userIds, forceDownload) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt index 526c584576..c2633cc5b6 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt @@ -19,7 +19,9 @@ package im.vector.matrix.android.internal.crypto import im.vector.matrix.android.api.MatrixPatterns import im.vector.matrix.android.api.auth.data.Credentials -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoInfoMapper import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask @@ -166,7 +168,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * @param userIds the userIds list * @param failures the failure map. */ - private fun onKeysDownloadSucceed(userIds: List, failures: Map>?): MXUsersDevicesMap { + private fun onKeysDownloadSucceed(userIds: List, failures: Map>?): MXUsersDevicesMap { if (failures != null) { for ((k, value) in failures) { val statusCode = when (val status = value["status"]) { @@ -182,7 +184,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } } val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap() - val usersDevicesInfoMap = MXUsersDevicesMap() + val usersDevicesInfoMap = MXUsersDevicesMap() for (userId in userIds) { val devices = cryptoStore.getUserDevices(userId) if (null == devices) { @@ -207,6 +209,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } } cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) + return usersDevicesInfoMap } @@ -217,10 +220,10 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * @param userIds The users to fetch. * @param forceDownload Always download the keys even if cached. */ - suspend fun downloadKeys(userIds: List?, forceDownload: Boolean): MXUsersDevicesMap { + suspend fun downloadKeys(userIds: List?, forceDownload: Boolean): MXUsersDevicesMap { Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds") // Map from userId -> deviceId -> MXDeviceInfo - val stored = MXUsersDevicesMap() + val stored = MXUsersDevicesMap() // List of user ids we need to download keys for val downloadUsers = ArrayList() @@ -265,7 +268,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * * @param downloadUsers the user ids list */ - private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList): MXUsersDevicesMap { + private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList): MXUsersDevicesMap { Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers") // get the user ids which did not already trigger a keys download val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) } @@ -283,49 +286,49 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users") for (userId in filteredUsers) { - val devices = response.deviceKeys?.get(userId) - Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices") - if (devices != null) { - val mutableDevices = devices.toMutableMap() - for ((deviceId, deviceInfo) in devices) { + // al devices = + val models = response.deviceKeys?.get(userId)?.mapValues { entry -> CryptoInfoMapper.map(entry.value) } + ?.toMutableMap() + + Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $models") + if (!models.isNullOrEmpty()) { + for ((deviceId, deviceInfo) in models) { // Get the potential previously store device keys for this device val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(userId, deviceId) // in some race conditions (like unit tests) // the self device must be seen as verified if (deviceInfo.deviceId == credentials.deviceId && userId == credentials.userId) { - deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED + deviceInfo.trustLevel = DeviceTrustLevel(previouslyStoredDeviceKeys?.trustLevel?.crossSigningVerified ?: false, true) } // Validate received keys if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) { // New device keys are not valid. Do not store them - mutableDevices.remove(deviceId) + models.remove(deviceId) if (null != previouslyStoredDeviceKeys) { // But keep old validated ones if any - mutableDevices[deviceId] = previouslyStoredDeviceKeys + models[deviceId] = previouslyStoredDeviceKeys } } else if (null != previouslyStoredDeviceKeys) { // The verified status is not sync'ed with hs. // This is a client side information, valid only for this client. // So, transfer its previous value - mutableDevices[deviceId]!!.verified = previouslyStoredDeviceKeys.verified + models[deviceId]!!.trustLevel = previouslyStoredDeviceKeys.trustLevel } } // Update the store // Note that devices which aren't in the response will be removed from the stores - cryptoStore.storeUserDevices(userId, mutableDevices) + cryptoStore.storeUserDevices(userId, models) } - - - //Handle cross signing keys update - val masterKey = response.masterKeys?.get(userId)?.also { - Timber.d("## CrossSigning : Got keys for $userId : MSK ${it.unpaddedBase64PublicKey}") + // Handle cross signing keys update + val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also { + Timber.d("## CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}") } - val selfSigningKey = response.selfSigningKeys?.get(userId)?.also { + val selfSigningKey = response.selfSigningKeys?.get(userId)?.toCryptoModel()?.also { Timber.d("## CrossSigning : Got keys for $userId : SSK ${it.unpaddedBase64PublicKey}") } - val userSigningKey = response.userSigningKeys?.get(userId)?.also { + val userSigningKey = response.userSigningKeys?.get(userId)?.toCryptoModel()?.also { Timber.d("## CrossSigning : Got keys for $userId : USK ${it.unpaddedBase64PublicKey}") } cryptoStore.storeUserCrossSigningKeys( @@ -348,7 +351,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * @param previouslyStoredDeviceKeys the device keys we received before for this device * @return true if succeeds */ - private fun validateDeviceKeys(deviceKeys: MXDeviceInfo?, userId: String, deviceId: String, previouslyStoredDeviceKeys: MXDeviceInfo?): Boolean { + private fun validateDeviceKeys(deviceKeys: CryptoDeviceInfo?, userId: String, deviceId: String, previouslyStoredDeviceKeys: CryptoDeviceInfo?): Boolean { if (null == deviceKeys) { Timber.e("## validateDeviceKeys() : deviceKeys is null from $userId:$deviceId") return false @@ -376,14 +379,14 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } val signKeyId = "ed25519:" + deviceKeys.deviceId - val signKey = deviceKeys.keys?.get(signKeyId) + val signKey = deviceKeys.keys[signKeyId] if (null == signKey) { Timber.e("## validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no ed25519 key") return false } - val signatureMap = deviceKeys.signatures?.get(userId) + val signatureMap = deviceKeys.signatures[userId] if (null == signatureMap) { Timber.e("## validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no map for $userId") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt index f93245de12..e50faf6f76 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt @@ -17,7 +17,8 @@ package im.vector.matrix.android.internal.crypto import im.vector.matrix.android.api.auth.data.Credentials -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.session.SessionScope import javax.inject.Inject @@ -35,11 +36,13 @@ internal class MyDeviceInfoHolder @Inject constructor( /** * my device info */ - val myDevice: MXDeviceInfo = MXDeviceInfo(credentials.deviceId!!, credentials.userId) + val myDevice: CryptoDeviceInfo init { + val keys = HashMap() +// TODO it's a bit strange, why not load from DB? if (!olmDevice.deviceEd25519Key.isNullOrEmpty()) { keys["ed25519:" + credentials.deviceId] = olmDevice.deviceEd25519Key!! } @@ -48,10 +51,22 @@ internal class MyDeviceInfoHolder @Inject constructor( keys["curve25519:" + credentials.deviceId] = olmDevice.deviceCurve25519Key!! } - myDevice.keys = keys +// myDevice.keys = keys +// +// myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms() - myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms() - myDevice.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED + // TODO hwo to really check cross signed status? + // + val crossSigned = cryptoStore.getMyCrossSigningInfo()?.masterKey()?.trustLevel?.locallyVerified ?: false +// myDevice.trustLevel = DeviceTrustLevel(crossSigned, true) + + myDevice = CryptoDeviceInfo( + credentials.deviceId!!, + credentials.userId, + keys = keys, + algorithms = MXCryptoAlgorithms.supportedAlgorithms(), + trustLevel = DeviceTrustLevel(crossSigned, true) + ) // Add our own deviceinfo to the store val endToEndDevicesForUser = cryptoStore.getUserDevices(credentials.userId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index 0283d3c85b..e1cac0d75f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -17,7 +17,7 @@ package im.vector.matrix.android.internal.crypto.actions import im.vector.matrix.android.internal.crypto.MXOlmDevice -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap @@ -28,8 +28,8 @@ import javax.inject.Inject internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val olmDevice: MXOlmDevice, private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) { - suspend fun handle(devicesByUser: Map>): MXUsersDevicesMap { - val devicesWithoutSession = ArrayList() + suspend fun handle(devicesByUser: Map>): MXUsersDevicesMap { + val devicesWithoutSession = ArrayList() val results = MXUsersDevicesMap() @@ -102,7 +102,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val return results } - private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? { + private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: CryptoDeviceInfo): String? { var sessionId: String? = null val deviceId = deviceInfo.deviceId diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt index 0c649cce89..5766ee9980 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt @@ -40,7 +40,7 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o // Don't bother setting up session to ourself it.identityKey() != olmDevice.deviceCurve25519Key // Don't bother setting up sessions with blocked users - && !it.isVerified + && !(it.trustLevel?.isVerified() ?: false) } } return ensureOlmSessionsForDevicesAction.handle(devicesByUser) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt index ebe219600d..fae205e581 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt @@ -19,7 +19,7 @@ package im.vector.matrix.android.internal.crypto.actions import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_OLM import im.vector.matrix.android.internal.crypto.MXOlmDevice -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.EncryptedMessage import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.convertToUTF8 @@ -37,7 +37,7 @@ internal class MessageEncrypter @Inject constructor(private val credentials: Cre * @param deviceInfos list of device infos to encrypt for. * @return the content for an m.room.encrypted event. */ - fun encryptMessage(payloadFields: Map, deviceInfos: List): EncryptedMessage { + fun encryptMessage(payloadFields: Map, deviceInfos: List): EncryptedMessage { val deviceInfoParticipantKey = deviceInfos.associateBy { it.identityKey()!! } val payloadJson = payloadFields.toMutableMap() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt index cc22c9830d..6d5c6687d4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt @@ -16,6 +16,7 @@ package im.vector.matrix.android.internal.crypto.actions +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.di.UserId @@ -27,7 +28,7 @@ internal class SetDeviceVerificationAction @Inject constructor( @UserId private val userId: String, private val keysBackup: KeysBackup) { - fun handle(verificationStatus: Int, deviceId: String, userId: String) { + fun handle(trustLevel: DeviceTrustLevel, deviceId: String, userId: String) { val device = cryptoStore.getUserDevice(userId, deviceId) // Sanity check @@ -36,10 +37,7 @@ internal class SetDeviceVerificationAction @Inject constructor( return } - if (device.verified != verificationStatus) { - device.verified = verificationStatus - cryptoStore.storeUserDevice(userId, device) - + if (device.isVerified != trustLevel.isVerified()) { if (userId == this.userId) { // If one of the user's own devices is being marked as verified / unverified, // check the key backup status, since whether or not we use this depends on @@ -47,5 +45,10 @@ internal class SetDeviceVerificationAction @Inject constructor( keysBackup.checkAndStartKeysBackup() } } + + if (device.trustLevel != trustLevel) { + device.trustLevel = trustLevel + cryptoStore.storeUserDevice(userId, device) + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 897a1f0a5d..ee35810763 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -29,7 +29,7 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevi import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore @@ -95,7 +95,7 @@ internal class MXMegolmEncryption( * * @param devicesInRoom the devices list */ - private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): MXOutboundSessionInfo { + private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): MXOutboundSessionInfo { var session = outboundSession if (session == null // Need to make a brand new session? @@ -106,7 +106,7 @@ internal class MXMegolmEncryption( outboundSession = session } val safeSession = session - val shareMap = HashMap>()/* userId */ + val shareMap = HashMap>()/* userId */ val userIds = devicesInRoom.userIds for (userId in userIds) { val deviceIds = devicesInRoom.getUserDeviceIds(userId) @@ -129,14 +129,14 @@ internal class MXMegolmEncryption( * @param devicesByUsers the devices map */ private suspend fun shareKey(session: MXOutboundSessionInfo, - devicesByUsers: Map>) { + devicesByUsers: Map>) { // nothing to send, the task is done if (devicesByUsers.isEmpty()) { Timber.v("## shareKey() : nothing more to do") return } // reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user) - val subMap = HashMap>() + val subMap = HashMap>() var devicesCount = 0 for ((userId, devices) in devicesByUsers) { subMap[userId] = devices @@ -158,7 +158,7 @@ internal class MXMegolmEncryption( * @param devicesByUser the devices map */ private suspend fun shareUserDevicesKey(session: MXOutboundSessionInfo, - devicesByUser: Map>) { + devicesByUser: Map>) { val sessionKey = olmDevice.getSessionKey(session.sessionId) val chainIndex = olmDevice.getMessageIndex(session.sessionId) @@ -262,7 +262,7 @@ internal class MXMegolmEncryption( * * @param userIds the user ids whose devices must be checked. */ - private suspend fun getDevicesInRoom(userIds: List): MXUsersDevicesMap { + private suspend fun getDevicesInRoom(userIds: List): MXUsersDevicesMap { // We are happy to use a cached version here: we assume that if we already // have a list of the user's devices, then we already share an e2e room // with them, which means that they will have announced any new devices via @@ -271,8 +271,8 @@ internal class MXMegolmEncryption( val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) - val devicesInRoom = MXUsersDevicesMap() - val unknownDevices = MXUsersDevicesMap() + val devicesInRoom = MXUsersDevicesMap() + val unknownDevices = MXUsersDevicesMap() for (userId in keys.userIds) { val deviceIds = keys.getUserDeviceIds(userId) ?: continue diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt index 5ab272d4e2..47ec98e5c9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt @@ -17,7 +17,7 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import timber.log.Timber @@ -52,7 +52,7 @@ internal class MXOutboundSessionInfo( * @param devicesInRoom the devices map * @return true if we have shared the session with devices which aren't in devicesInRoom. */ - fun sharedWithTooManyDevices(devicesInRoom: MXUsersDevicesMap): Boolean { + fun sharedWithTooManyDevices(devicesInRoom: MXUsersDevicesMap): Boolean { val userIds = sharedWithDevices.userIds for (userId in userIds) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt index 1c275940af..899e884e0d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -25,7 +25,7 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore internal class MXOlmEncryption( @@ -42,7 +42,7 @@ internal class MXOlmEncryption( // // TODO: there is a race condition here! What if a new user turns up ensureSession(userIds) - val deviceInfos = ArrayList() + val deviceInfos = ArrayList() for (userId in userIds) { val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList() for (device in devices) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt index a4747b1932..4953d53ae0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt @@ -16,7 +16,6 @@ */ package im.vector.matrix.android.internal.crypto.api -import im.vector.matrix.android.internal.crypto.model.MXKeysObject import im.vector.matrix.android.internal.crypto.model.rest.* import im.vector.matrix.android.internal.network.NetworkConstants import retrofit2.Call @@ -66,7 +65,6 @@ internal interface CryptoApi { @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "keys/query") fun downloadKeysForUsers(@Body params: KeysQueryBody): Call - /** * CrossSigning - Uploading signing keys * Public keys for the cross-signing keys are uploaded to the servers using /keys/device_signing/upload. @@ -75,7 +73,6 @@ internal interface CryptoApi { @POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "keys/device_signing/upload") fun uploadSigningKeys(@Body params: UploadSigningKeysBody): Call - /** * CrossSigning - Uploading signatures * Signatures of device keys can be up @@ -95,7 +92,6 @@ internal interface CryptoApi { @POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "keys/signatures/upload") fun uploadSignatures(@Body params: Map?): Call - /** * Claim one-time keys. * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-keys-claim diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt index 36c270fdfa..fe63681357 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt @@ -26,7 +26,13 @@ import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningIn import im.vector.matrix.android.internal.crypto.DeviceListManager import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder -import im.vector.matrix.android.internal.crypto.model.rest.* +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey +import im.vector.matrix.android.internal.crypto.model.KeyUsage +import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse +import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse +import im.vector.matrix.android.internal.crypto.model.rest.UploadResponseFailure +import im.vector.matrix.android.internal.crypto.model.rest.UploadSignatureQueryBuilder +import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.UploadSignaturesTask import im.vector.matrix.android.internal.crypto.tasks.UploadSigningKeysTask @@ -42,7 +48,6 @@ import org.matrix.olm.OlmUtility import timber.log.Timber import javax.inject.Inject - @SessionScope internal class DefaultCrossSigningService @Inject constructor( @UserId private val userId: String, @@ -55,7 +60,6 @@ internal class DefaultCrossSigningService @Inject constructor( private val uploadSignaturesTask: UploadSignaturesTask, private val taskExecutor: TaskExecutor) : CrossSigningService { - private var olmUtility: OlmUtility? = null private var crossSigningState: CrossSigningState = CrossSigningState.Unknown @@ -68,7 +72,7 @@ internal class DefaultCrossSigningService @Inject constructor( try { olmUtility = OlmUtility() - //Try to get stored keys if they exist + // Try to get stored keys if they exist cryptoStore.getMyCrossSigningInfo()?.let { mxCrossSigningInfo -> Timber.i("## CrossSigning - Found Existing self signed keys") Timber.i("## CrossSigning - Checking if private keys are known") @@ -108,7 +112,6 @@ internal class DefaultCrossSigningService @Inject constructor( } } } - } } catch (e: Throwable) { // Mmm this kind of a big issue @@ -116,13 +119,11 @@ internal class DefaultCrossSigningService @Inject constructor( } } - fun release() { olmUtility?.releaseUtility() listOf(masterPkSigning, userPkSigning, selfSigningPkSigning).forEach { it?.releaseSigning() } } - /** * - Make 3 key pairs (MSK, USK, SSK) * - Save the private keys with proper security @@ -136,18 +137,18 @@ internal class DefaultCrossSigningService @Inject constructor( val myUserID = credentials.userId - //================= + // ================= // MASTER KEY - //================= + // ================= val masterPkOlm = OlmPkSigning() val masterKeyPrivateKey = OlmPkSigning.generateSeed() val masterPublicKey = masterPkOlm.initWithSeed(masterKeyPrivateKey) Timber.v("## CrossSigning - masterPublicKey:$masterPublicKey") - //================= + // ================= // USER KEY - //================= + // ================= val userSigningPkOlm = OlmPkSigning() val uskPrivateKey = OlmPkSigning.generateSeed() val uskPublicKey = userSigningPkOlm.initWithSeed(uskPrivateKey) @@ -155,39 +156,37 @@ internal class DefaultCrossSigningService @Inject constructor( Timber.v("## CrossSigning - uskPublicKey:$uskPublicKey") // Sign userSigningKey with master - val signedUSK = CrossSigningKeyInfo.Builder(myUserID, CrossSigningKeyInfo.KeyUsage.USER_SIGNING) + val signedUSK = CryptoCrossSigningKey.Builder(myUserID, KeyUsage.USER_SIGNING) .key(uskPublicKey) .build() .canonicalSignable() .let { masterPkOlm.sign(it) } - //================= + // ================= // SELF SIGNING KEY - //================= + // ================= val selfSigningPkOlm = OlmPkSigning() val sskPrivateKey = OlmPkSigning.generateSeed() val sskPublicKey = selfSigningPkOlm.initWithSeed(sskPrivateKey) Timber.v("## CrossSigning - sskPublicKey:$sskPublicKey") - // Sign userSigningKey with master - val signedSSK = JsonCanonicalizer.getCanonicalJson(Map::class.java, CrossSigningKeyInfo.Builder(myUserID, CrossSigningKeyInfo.KeyUsage.SELF_SIGNING) + val signedSSK = JsonCanonicalizer.getCanonicalJson(Map::class.java, CryptoCrossSigningKey.Builder(myUserID, KeyUsage.SELF_SIGNING) .key(sskPublicKey) .build().signalableJSONDictionary()).let { masterPkOlm.sign(it) } - // I need to upload the keys - val mskCrossSigningKeyInfo = CrossSigningKeyInfo.Builder(myUserID, CrossSigningKeyInfo.KeyUsage.MASTER) + val mskCrossSigningKeyInfo = CryptoCrossSigningKey.Builder(myUserID, KeyUsage.MASTER) .key(masterPublicKey) .build() val params = UploadSigningKeysTask.Params( masterKey = mskCrossSigningKeyInfo, - userKey = CrossSigningKeyInfo.Builder(myUserID, CrossSigningKeyInfo.KeyUsage.USER_SIGNING) + userKey = CryptoCrossSigningKey.Builder(myUserID, KeyUsage.USER_SIGNING) .key(uskPublicKey) .signature(myUserID, masterPublicKey, signedUSK) .build(), - selfSignedKey = CrossSigningKeyInfo.Builder(myUserID, CrossSigningKeyInfo.KeyUsage.SELF_SIGNING) + selfSignedKey = CryptoCrossSigningKey.Builder(myUserID, KeyUsage.SELF_SIGNING) .key(sskPublicKey) .signature(myUserID, masterPublicKey, signedSSK) .build(), @@ -253,9 +252,8 @@ internal class DefaultCrossSigningService @Inject constructor( } }.executeBy(taskExecutor) - - callback?.onSuccess(Unit) crossSigningState = CrossSigningState.Trusted + callback?.onSuccess(Unit) } override fun onFailure(failure: Throwable) { @@ -264,8 +262,6 @@ internal class DefaultCrossSigningService @Inject constructor( } } }.executeBy(taskExecutor) - - } /** @@ -293,40 +289,43 @@ internal class DefaultCrossSigningService @Inject constructor( /** * Will not force a download of the key, but will verify signatures trust chain */ - override fun checkUserTrust(userId: String, callback: MatrixCallback?) { + override fun checkUserTrust(userId: String): UserTrustResult { Timber.d("## CrossSigning checkUserTrust for $userId") // I trust a user if I trust his master key // I can trust the master key if it is signed by my user key // TODO what if the master key is signed by a device key that i have verified // First let's get my user key - val myUserKey = cryptoStore.getCrossSigningInfo(credentials.userId)?.userKey() - if (myUserKey == null) { - Timber.d("## CrossSigning checkUserTrust false, CrossSigning is not enabled (userKey not defined)") - callback?.onSuccess(false) - return + val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(credentials.userId) + + val myUserKey = myCrossSigningInfo?.userKey() + ?: return UserTrustResult.CrossSigningNotConfigured(credentials.userId) + + if (!myCrossSigningInfo.isTrusted) { + return UserTrustResult.KeysNotTrusted(myCrossSigningInfo) } // Let's get the other user master key - val masterKey = cryptoStore.getCrossSigningInfo(userId)?.masterKey() - if (masterKey == null) { - Timber.d("## CrossSigning checkUserTrust false for $userId, ") - callback?.onSuccess(false) - return - } + val otherMasterKey = cryptoStore.getCrossSigningInfo(userId)?.masterKey() + ?: return UserTrustResult.UnknownCrossSignatureInfo(userId) - val masterKeySignaturesMadeByMyUserKey = masterKey.signatures + val masterKeySignaturesMadeByMyUserKey = otherMasterKey.signatures ?.get(credentials.userId) // Signatures made by me ?.get("ed25519:${myUserKey.unpaddedBase64PublicKey}") - if (masterKeySignaturesMadeByMyUserKey.isNullOrBlank()) { Timber.d("## CrossSigning checkUserTrust false for $userId, not signed by my UserSigningKey") - callback?.onSuccess(false) - return + return UserTrustResult.KeyNotSigned(otherMasterKey) } + // Check that Alice USK signature of Bob MSK is valid + try { + olmUtility!!.verifyEd25519Signature(masterKeySignaturesMadeByMyUserKey, myUserKey.unpaddedBase64PublicKey, otherMasterKey.canonicalSignable()) + } catch (failure: Throwable) { + return UserTrustResult.InvalidSignature(myUserKey, masterKeySignaturesMadeByMyUserKey) + } + return UserTrustResult.Success } override fun getUserCrossSigningKeys(userId: String): MXCrossSigningInfo? { @@ -338,7 +337,7 @@ internal class DefaultCrossSigningService @Inject constructor( } override fun trustUser(userId: String, callback: MatrixCallback) { - //We should have this user keys + // We should have this user keys val otherMasterKeys = getUserCrossSigningKeys(userId)?.masterKey() if (otherMasterKeys == null) { callback.onFailure(Throwable("Other master signing key is not known")) @@ -365,20 +364,17 @@ internal class DefaultCrossSigningService @Inject constructor( return } - otherMasterKeys.addSignature(credentials.userId, userPubKey, newSignature) cryptoStore.setUserKeysAsTrusted(userId, true) // TODO update local copy with new signature directly here? kind of local echo of trust? val uploadQuery = UploadSignatureQueryBuilder() - .withSigningKeyInfo(otherMasterKeys) + .withSigningKeyInfo(otherMasterKeys.addSignatureAndCopy(credentials.userId, userPubKey, newSignature)) .build() uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) { this.callback = callback }.executeBy(taskExecutor) - } - override fun signDevice(deviceId: String, callback: MatrixCallback) { // This device should be yours val device = cryptoStore.getUserDevice(credentials.userId, deviceId) @@ -412,7 +408,7 @@ internal class DefaultCrossSigningService @Inject constructor( credentials.userId to mapOf( - "ed25519:${ssPubKey}" to newSignature + "ed25519:$ssPubKey" to newSignature ) ) ) @@ -440,21 +436,20 @@ internal class DefaultCrossSigningService @Inject constructor( }.executeBy(taskExecutor) } - override fun checkDeviceTrust(userId: String, deviceId: String): DeviceTrustResult { + override fun checkDeviceTrust(userId: String, deviceId: String, locallyTrusted: Boolean?): DeviceTrustResult { val otherDevice = cryptoStore.getUserDevice(userId, deviceId) ?: return DeviceTrustResult.UnknownDevice(deviceId) val myKeys = getUserCrossSigningKeys(credentials.userId) - ?: return DeviceTrustResult.CrossSigningNotConfigured(credentials.userId) + ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(credentials.userId)) - if (!myKeys.isTrusted) return DeviceTrustResult.KeysNotTrusted(myKeys) + if (!myKeys.isTrusted) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys)) val otherKeys = getUserCrossSigningKeys(userId) - ?: return DeviceTrustResult.CrossSigningNotConfigured(userId) - + ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId)) // TODO should we force verification ? - if (!otherKeys.isTrusted) return DeviceTrustResult.KeysNotTrusted(otherKeys) + if (!otherKeys.isTrusted) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(otherKeys)) // Check if the trust chain is valid /* @@ -476,18 +471,24 @@ internal class DefaultCrossSigningService @Inject constructor( */ val otherSSKSignature = otherDevice.signatures?.get(userId)?.get("ed25519:${otherKeys.selfSigningKey()?.unpaddedBase64PublicKey}") - ?: return DeviceTrustResult.MissingDeviceSignature(deviceId, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey - ?: "") + ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.MissingDeviceSignature(deviceId, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey + ?: "")) // Check bob's device is signed by bob's SSK try { - olmUtility?.verifyEd25519Signature(otherSSKSignature, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey, otherDevice.canonicalSignable()) + olmUtility!!.verifyEd25519Signature(otherSSKSignature, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey, otherDevice.canonicalSignable()) } catch (e: Throwable) { - return DeviceTrustResult.InvalidDeviceSignature(deviceId, otherSSKSignature, e) + return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.InvalidDeviceSignature(deviceId, otherSSKSignature, e)) } - return DeviceTrustResult.Success(deviceId, true) + return DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = true, locallyVerified = locallyTrusted)) + } + private fun legacyFallbackTrust(locallyTrusted: Boolean?, crossSignTrustFail: DeviceTrustResult): DeviceTrustResult { + return if (locallyTrusted == true) { + DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true)) + } else { + crossSignTrustFail + } } } - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSignaturesBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DeviceTrustLevel.kt similarity index 51% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSignaturesBody.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DeviceTrustLevel.kt index 113a26aff9..4b08caf375 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSignaturesBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DeviceTrustLevel.kt @@ -5,7 +5,7 @@ * 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 + * 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, @@ -13,19 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package im.vector.matrix.android.internal.crypto.model.rest +package im.vector.matrix.android.internal.crypto.crosssigning -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass +data class DeviceTrustLevel(val crossSigningVerified: Boolean, val locallyVerified: Boolean?) { -@JsonClass(generateAdapter = true) -data class UploadSignaturesBody( - @Json(name = "master_key") - val masterKey: CrossSigningKeyInfo? = null, - - @Json(name = "self_signing_key") - val selfSigningKey: CrossSigningKeyInfo? = null, - - @Json(name = "user_signing_key") - val userSigningKey: CrossSigningKeyInfo? = null -) + fun isVerified() = crossSigningVerified || locallyVerified == true + fun isCrossSigningVerified() = crossSigningVerified + fun isLocallyVerified() = locallyVerified +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DeviceTrustResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DeviceTrustResult.kt index e84b263af9..52821ec684 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DeviceTrustResult.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DeviceTrustResult.kt @@ -19,13 +19,13 @@ import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningIn sealed class DeviceTrustResult { - data class Success(val deviceID: String, val crossSigned: Boolean) : DeviceTrustResult() + data class Success(val level: DeviceTrustLevel) : DeviceTrustResult() data class UnknownDevice(val deviceID: String) : DeviceTrustResult() data class CrossSigningNotConfigured(val userID: String) : DeviceTrustResult() data class KeysNotTrusted(val key: MXCrossSigningInfo) : DeviceTrustResult() data class MissingDeviceSignature(val deviceId: String, val signingKey: String) : DeviceTrustResult() data class InvalidDeviceSignature(val deviceId: String, val signingKey: String, val throwable: Throwable?) : DeviceTrustResult() - } fun DeviceTrustResult.isSuccess(): Boolean = this is DeviceTrustResult.Success +fun DeviceTrustResult.isCrossSignedVerified(): Boolean = (this as? DeviceTrustResult.Success)?.level?.isCrossSigningVerified() == true diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/Extensions.kt index 4083732d53..a8369e5c04 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/Extensions.kt @@ -15,15 +15,14 @@ */ package im.vector.matrix.android.internal.crypto.crosssigning -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo -import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.util.JsonCanonicalizer - -fun MXDeviceInfo.canonicalSignable(): String { +fun CryptoDeviceInfo.canonicalSignable(): String { return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary()) } -fun CrossSigningKeyInfo.canonicalSignable(): String { +fun CryptoCrossSigningKey.canonicalSignable(): String { return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/UserTrustResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/UserTrustResult.kt new file mode 100644 index 0000000000..cf6964c051 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/UserTrustResult.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.crosssigning + +import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey + +sealed class UserTrustResult { + + object Success : UserTrustResult() + +// data class Success(val deviceID: String, val crossSigned: Boolean) : UserTrustResult() +// +// data class UnknownDevice(val deviceID: String) : UserTrustResult() + data class CrossSigningNotConfigured(val userID: String) : UserTrustResult() + data class UnknownCrossSignatureInfo(val userID: String) : UserTrustResult() + data class KeysNotTrusted(val key: MXCrossSigningInfo) : UserTrustResult() + data class KeyNotSigned(val key: CryptoCrossSigningKey) : UserTrustResult() + data class InvalidSignature(val key: CryptoCrossSigningKey, val signature: String) : UserTrustResult() +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrustSignature.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrustSignature.kt index caf4aaad0b..e85e1cc433 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrustSignature.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrustSignature.kt @@ -16,7 +16,7 @@ package im.vector.matrix.android.internal.crypto.keysbackup.model -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo /** * A signature in a the `KeyBackupVersionTrust` object. @@ -26,7 +26,7 @@ class KeyBackupVersionTrustSignature { /** * The device that signed the backup version. */ - var device: MXDeviceInfo? = null + var device: CryptoDeviceInfo? = null /** *Flag to indicate the signature from this device is valid. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrustSignature.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrustSignature.kt index d91189a4bf..b860afd36d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrustSignature.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrustSignature.kt @@ -16,7 +16,7 @@ package im.vector.matrix.android.internal.crypto.keysbackup.model -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo /** * A signature in a `KeysBackupVersionTrust` object. @@ -32,7 +32,7 @@ class KeysBackupVersionTrustSignature { * The device that signed the backup version. * Can be null if the device is not known. */ - var device: MXDeviceInfo? = null + var device: CryptoDeviceInfo? = null /** * Flag to indicate the signature from this device is valid. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoCrossSigningKey.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoCrossSigningKey.kt new file mode 100644 index 0000000000..a9babae935 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoCrossSigningKey.kt @@ -0,0 +1,86 @@ +package im.vector.matrix.android.internal.crypto.model + +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.model.rest.RestKeyInfo + +data class CryptoCrossSigningKey( + override val userId: String, + + val usages: List?, + + override val keys: Map, + + override val signatures: Map>?, + + var trustLevel: DeviceTrustLevel? = null +) : CryptoInfo { + + override fun signalableJSONDictionary(): Map { + val map = HashMap() + userId.let { map["user_id"] = it } + usages?.let { map["usage"] = it } + keys.let { map["keys"] = it } + + return map + } + + val unpaddedBase64PublicKey: String? = keys.values.firstOrNull() + + val isMasterKey = usages?.contains(KeyUsage.MASTER.value) ?: false + val isSelfSigningKey = usages?.contains(KeyUsage.SELF_SIGNING.value) ?: false + val isUserKey = usages?.contains(KeyUsage.USER_SIGNING.value) ?: false + + fun addSignatureAndCopy(userId: String, signedWithNoPrefix: String, signature: String): CryptoCrossSigningKey { + val updated = (signatures?.toMutableMap() ?: HashMap()) + val userMap = updated[userId]?.toMutableMap() + ?: HashMap().also { updated[userId] = it } + userMap["ed25519:$signedWithNoPrefix"] = signature + + return this.copy( + signatures = updated + ) + } + + data class Builder( + val userId: String, + val usage: KeyUsage, + private var base64Pkey: String? = null, + private val signatures: ArrayList> = ArrayList() + ) { + + fun key(publicKeyBase64: String) = apply { + base64Pkey = publicKeyBase64 + } + + fun signature(userId: String, keySignedBase64: String, base64Signature: String) = apply { + signatures.add(Triple(userId, keySignedBase64, base64Signature)) + } + + fun build(): CryptoCrossSigningKey { + val b64key = base64Pkey ?: throw IllegalArgumentException("") + + val signMap = HashMap>() + signatures.forEach { info -> + val uMap = signMap[info.first] + ?: HashMap().also { signMap[info.first] = it } + uMap["ed25519:${info.second}"] = info.third + } + + return CryptoCrossSigningKey( + userId = userId, + usages = listOf(usage.value), + keys = mapOf("ed25519:$b64key" to b64key), + signatures = signMap) + } + } +} + +enum class KeyUsage(val value: String) { + MASTER("master"), + SELF_SIGNING("self_signing"), + USER_SIGNING("user_signing") +} + +fun CryptoCrossSigningKey.toRest(): RestKeyInfo { + return CryptoInfoMapper.map(this) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoDeviceInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoDeviceInfo.kt new file mode 100644 index 0000000000..cede23f16a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoDeviceInfo.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model + +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.model.rest.RestDeviceInfo +import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMapper +import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity + +data class CryptoDeviceInfo( + val deviceId: String, + override val userId: String, + var algorithms: List? = null, + override val keys: Map? = null, + override val signatures: Map>? = null, + val unsigned: JsonDict? = null, + +// TODO how to store if this device is verified by a user SSK, or is legacy trusted? +// I need to know if it is trusted via cross signing (Trusted because bob verified it) + + var trustLevel: DeviceTrustLevel? = null, + var isBlocked: Boolean = false + +) + + : CryptoInfo { + + val isVerified: Boolean + get() = trustLevel?.isVerified() ?: false + + val isUnknown: Boolean + get() = trustLevel == null + + /** + * @return the fingerprint + */ + fun fingerprint(): String? { + return keys + ?.takeIf { !deviceId.isBlank() } + ?.get("ed25519:$deviceId") + } + + /** + * @return the identity key + */ + fun identityKey(): String? { + return keys + ?.takeIf { !deviceId.isBlank() } + ?.get("curve25519:$deviceId") + } + + /** + * @return the display name + */ + fun displayName(): String? { + return unsigned?.get("device_display_name") as? String + } + + override fun signalableJSONDictionary(): Map { + val map = HashMap() + map["device_id"] = deviceId + map["user_id"] = userId + algorithms?.let { map["algorithms"] = it } + keys?.let { map["keys"] = it } + return map + } +// +// /** +// * @return a dictionary of the parameters +// */ +// fun toDeviceKeys(): DeviceKeys { +// return DeviceKeys( +// userId = userId, +// deviceId = deviceId, +// algorithms = algorithms!!, +// keys = keys!!, +// signatures = signatures!! +// ) +// } +} + +fun CryptoDeviceInfo.toRest(): RestDeviceInfo { + return CryptoInfoMapper.map(this) +} + +internal fun CryptoDeviceInfo.toEntity(): DeviceInfoEntity { + return CryptoMapper.mapToEntity(this) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXKeysObject.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoInfo.kt similarity index 74% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXKeysObject.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoInfo.kt index e3b17d5513..c9d49bede0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXKeysObject.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,11 @@ package im.vector.matrix.android.internal.crypto.model -interface MXKeysObject { +/** + * Generic crypto info. + * Can be a device (CryptoDeviceInfo), as well as a CryptoCrossSigningInfo (can be seen as a kind of virtual device) + */ +interface CryptoInfo { val userId: String @@ -24,6 +28,5 @@ interface MXKeysObject { val signatures: Map>? -// fun signalableJSONDictionary(): Map + fun signalableJSONDictionary(): Map } - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoInfoMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoInfoMapper.kt new file mode 100644 index 0000000000..8d216f3160 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoInfoMapper.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model + +import com.squareup.moshi.Moshi +import im.vector.matrix.android.internal.crypto.model.rest.RestDeviceInfo +import im.vector.matrix.android.internal.crypto.model.rest.RestKeyInfo +import im.vector.matrix.android.internal.di.SerializeNulls + +object CryptoInfoMapper { + + private val moshi = Moshi.Builder().add(SerializeNulls.JSON_ADAPTER_FACTORY).build() + + fun map(restDeviceInfo: RestDeviceInfo): CryptoDeviceInfo { + return CryptoDeviceInfo( + deviceId = restDeviceInfo.deviceId, + userId = restDeviceInfo.userId, + algorithms = restDeviceInfo.algorithms, + keys = restDeviceInfo.keys, + signatures = restDeviceInfo.signatures, + unsigned = restDeviceInfo.unsigned, + trustLevel = null + ) + } + + fun map(cryptoDeviceInfo: CryptoDeviceInfo): RestDeviceInfo { + return RestDeviceInfo( + deviceId = cryptoDeviceInfo.deviceId, + algorithms = cryptoDeviceInfo.algorithms, + keys = cryptoDeviceInfo.keys, + signatures = cryptoDeviceInfo.signatures, + unsigned = cryptoDeviceInfo.unsigned, + userId = cryptoDeviceInfo.userId + ) + } + + fun map(keyInfo: RestKeyInfo): CryptoCrossSigningKey { + return CryptoCrossSigningKey( + userId = keyInfo.userId, + usages = keyInfo.usages, + keys = keyInfo.keys ?: emptyMap(), + signatures = keyInfo.signatures, + trustLevel = null + ) + } + + fun map(keyInfo: CryptoCrossSigningKey): RestKeyInfo { + return RestKeyInfo( + userId = keyInfo.userId, + usages = keyInfo.usages, + keys = keyInfo.keys, + signatures = keyInfo.signatures + ) + } + + fun RestDeviceInfo.toCryptoModel(): CryptoDeviceInfo { + return map(this) + } + + fun CryptoDeviceInfo.toRest(): RestDeviceInfo { + return map(this) + } + +// fun RestKeyInfo.toCryptoModel(): CryptoCrossSigningKey { +// return map(this) +// } + + fun CryptoCrossSigningKey.toRest(): RestKeyInfo { + return map(this) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt index e0e6e6e4ce..cc9b3bff74 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt @@ -147,14 +147,6 @@ data class MXDeviceInfo( return map } - fun addSignature(userId: String, signedWithNoPrefix: String, signature: String) = apply { - val updated = (signatures?.toMutableMap() ?: HashMap()) - val userMap = updated[userId]?.toMutableMap() - ?: HashMap().also { updated[userId] = it } - userMap["ed25519:${signedWithNoPrefix}"] = signature - signatures = updated - } - /** * @return a dictionary of the parameters */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSessionResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSessionResult.kt index a92034873f..3f0dfe8b57 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSessionResult.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSessionResult.kt @@ -22,7 +22,7 @@ data class MXOlmSessionResult( /** * the device */ - val deviceInfo: MXDeviceInfo, + val deviceInfo: CryptoDeviceInfo, /** * Base64 olm session id. * null if no session could be established. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/CrossSigningKeyInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/CrossSigningKeyInfo.kt deleted file mode 100644 index 67f4c79fb2..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/CrossSigningKeyInfo.kt +++ /dev/null @@ -1,126 +0,0 @@ -package im.vector.matrix.android.internal.crypto.model.rest - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import im.vector.matrix.android.internal.crypto.model.MXKeysObject - -/** - * "self_signing_key": { - * "user_id": "@alice:example.com", - * "usage": ["self_signing"], - * "keys": { - * "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key" - * }, - * "signatures": { - * "@alice:example.com": { - * "ed25519:base64+master+public+key": "base64+signature" - * } - * } - * } - */ -@JsonClass(generateAdapter = true) -data class CrossSigningKeyInfo( - /** - * The user who owns the key - */ - @Json(name = "user_id") - override var userId: String, - /** - * Allowed uses for the key. - * Must contain "master" for master keys, "self_signing" for self-signing keys, and "user_signing" for user-signing keys. - * See CrossSigningKeyInfo#KEY_USAGE_* constants - */ - @Json(name = "usage") - val usages: List?, - - /** - * An object that must have one entry, - * whose name is "ed25519:" followed by the unpadded base64 encoding of the public key, - * and whose value is the unpadded base64 encoding of the public key. - */ - @Json(name = "keys") - override var keys: Map?, - - /** - * Signatures of the key. - * A self-signing or user-signing key must be signed by the master key. - * A master key may be signed by a device. - */ - @Json(name = "signatures") - override var signatures: Map>? = null -) : MXKeysObject { - // Shortcut to get key as "keys" is an object that must have one entry - val unpaddedBase64PublicKey: String? = keys?.values?.firstOrNull() - - val isMasterKey = usages?.contains(KeyUsage.MASTER.value) ?: false - val isSelfSigningKey = usages?.contains(KeyUsage.SELF_SIGNING.value) ?: false - val isUserKey = usages?.contains(KeyUsage.USER_SIGNING.value) ?: false - - fun signalableJSONDictionary(): Map { - val map = HashMap() - userId.let { map["user_id"] = it } - usages?.let { map["usage"] = it } - keys?.let { map["keys"] = it } - - return map - } - - fun addSignature(userId: String, signedWithNoPrefix: String, signature: String) = apply { - val updated = (signatures?.toMutableMap() ?: HashMap()) - val userMap = updated[userId]?.toMutableMap() - ?: HashMap().also { updated[userId] = it } - userMap["ed25519:${signedWithNoPrefix}"] = signature - signatures = updated - } - -// fun toXSigningKeys(): XSigningKeys { -// return XSigningKeys( -// userId = userId, -// usage = usages ?: emptyList(), -// keys = keys ?: emptyMap(), -// signatures = signatures -// -// ) -// } - - data class Builder( - val userId: String, - val usage: KeyUsage, - private var base64Pkey: String? = null, - private val signatures: ArrayList> = ArrayList() - ) { - - fun key(publicKeyBase64: String) = apply { - base64Pkey = publicKeyBase64 - } - - fun signature(userId: String, keySignedBase64: String, base64Signature: String) = apply { - signatures.add(Triple(userId, keySignedBase64, base64Signature)) - } - - fun build(): CrossSigningKeyInfo { - val b64key = base64Pkey ?: throw IllegalArgumentException("") - - val signMap = HashMap>() - signatures.forEach { info -> - val uMap = signMap[info.first] - ?: HashMap().also { signMap[info.first] = it } - uMap["ed25519:${info.second}"] = info.third - } - - return CrossSigningKeyInfo( - userId = userId, - usages = listOf(usage.value), - keys = mapOf("ed25519:$b64key" to b64key), - signatures = signMap - ) - } - } - - enum class KeyUsage(val value: String) { - MASTER("master"), - SELF_SIGNING("self_signing"), - USER_SIGNING("user_signing") - } - -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceKeys.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceKeys.kt index 72dc2daf88..27a3480d0e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceKeys.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceKeys.kt @@ -18,22 +18,25 @@ package im.vector.matrix.android.internal.crypto.model.rest import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import im.vector.matrix.android.internal.crypto.model.MXKeysObject @JsonClass(generateAdapter = true) data class DeviceKeys( @Json(name = "user_id") - override val userId: String, + val userId: String?, @Json(name = "device_id") - val deviceId: String, + val deviceId: String?, @Json(name = "algorithms") - val algorithms: List, + val algorithms: List?, @Json(name = "keys") - override val keys: Map, + val keys: Map?, @Json(name = "signatures") - override val signatures: Map>? -) : MXKeysObject + val signatures: Map>?, + + @Json(name = "usage") + val usage: List? = null + +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryResponse.kt index 0527b5ae3f..b2b62706a7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryResponse.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.model.rest import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo /** * This class represents the response to /keys/query request made by downloadKeysForUsers @@ -36,7 +35,7 @@ data class KeysQueryResponse( * TODO Use MXUsersDevicesMap? */ @Json(name = "device_keys") - var deviceKeys: Map>? = null, + var deviceKeys: Map>? = null, /** * The failures sorted by homeservers. TODO Bad comment ? @@ -45,13 +44,12 @@ data class KeysQueryResponse( var failures: Map>? = null, @Json(name = "master_keys") - var masterKeys: Map? = null, + var masterKeys: Map? = null, @Json(name = "self_signing_keys") - var selfSigningKeys: Map? = null, + var selfSigningKeys: Map? = null, @Json(name = "user_signing_keys") - var userSigningKeys: Map? = null + var userSigningKeys: Map? = null ) - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadBody.kt index f67cbd4766..961125767e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadBody.kt @@ -23,7 +23,7 @@ import im.vector.matrix.android.api.util.JsonDict @JsonClass(generateAdapter = true) data class KeysUploadBody( @Json(name = "device_keys") - var deviceKeys: DeviceKeys? = null, + var deviceKeys: RestDeviceInfo? = null, @Json(name = "one_time_keys") var oneTimeKeys: JsonDict? = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RestDeviceInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RestDeviceInfo.kt new file mode 100644 index 0000000000..e057780093 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RestDeviceInfo.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict + +@JsonClass(generateAdapter = true) +data class RestDeviceInfo( + /** + * The id of this device. + */ + @Json(name = "device_id") + var deviceId: String, + + /** + * the user id + */ + @Json(name = "user_id") + var userId: String, + + /** + * The list of algorithms supported by this device. + */ + @Json(name = "algorithms") + var algorithms: List? = null, + + /** + * A map from ":" to "". + */ + @Json(name = "keys") + var keys: Map? = null, + + /** + * The signature of this MXDeviceInfo. + * A map from "" to a map from ":" to "" + */ + @Json(name = "signatures") + var signatures: Map>? = null, + + /* + * Additional data from the home server. + */ + @Json(name = "unsigned") + var unsigned: JsonDict? = null) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RestKeyInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RestKeyInfo.kt new file mode 100644 index 0000000000..ffd1817402 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RestKeyInfo.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey +import im.vector.matrix.android.internal.crypto.model.CryptoInfoMapper + +@JsonClass(generateAdapter = true) +data class RestKeyInfo( + /** + * The user who owns the key + */ + @Json(name = "user_id") + val userId: String, + /** + * Allowed uses for the key. + * Must contain "master" for master keys, "self_signing" for self-signing keys, and "user_signing" for user-signing keys. + * See CrossSigningKeyInfo#KEY_USAGE_* constants + */ + @Json(name = "usage") + val usages: List?, + + /** + * An object that must have one entry, + * whose name is "ed25519:" followed by the unpadded base64 encoding of the public key, + * and whose value is the unpadded base64 encoding of the public key. + */ + @Json(name = "keys") + val keys: Map?, + + /** + * Signatures of the key. + * A self-signing or user-signing key must be signed by the master key. + * A master key may be signed by a device. + */ + @Json(name = "signatures") + val signatures: Map>? = null +) { + fun toCryptoModel(): CryptoCrossSigningKey { + return CryptoInfoMapper.map(this) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SignatureUploadResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SignatureUploadResponse.kt index 4cfb608c46..d8186ad2cb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SignatureUploadResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SignatureUploadResponse.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto.model.rest import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo /** * Upload Signature response @@ -38,7 +37,6 @@ data class SignatureUploadResponse( ) - @JsonClass(generateAdapter = true) data class UploadResponseFailure( @Json(name = "status") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSignatureQueryBuilder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSignatureQueryBuilder.kt index 0f10ba18b7..ba877afedd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSignatureQueryBuilder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSignatureQueryBuilder.kt @@ -15,38 +15,39 @@ */ package im.vector.matrix.android.internal.crypto.model.rest -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo -import im.vector.matrix.android.internal.crypto.model.MXKeysObject +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo +import im.vector.matrix.android.internal.crypto.model.toRest /** * Helper class to build CryptoApi#uploadSignatures params */ data class UploadSignatureQueryBuilder( - private val deviceInfoList: ArrayList = ArrayList(), - private val signingKeyInfoList: ArrayList = ArrayList() + private val deviceInfoList: ArrayList = ArrayList(), + private val signingKeyInfoList: ArrayList = ArrayList() ) { - fun withDeviceInfo(deviceInfo: MXDeviceInfo) = apply { + fun withDeviceInfo(deviceInfo: CryptoDeviceInfo) = apply { deviceInfoList.add(deviceInfo) } - fun withSigningKeyInfo(info: CrossSigningKeyInfo) = apply { + fun withSigningKeyInfo(info: CryptoCrossSigningKey) = apply { signingKeyInfoList.add(info) } - fun build(): Map> { - val map = HashMap>() + fun build(): Map> { + val map = HashMap>() val usersList = (deviceInfoList.map { it.userId } + signingKeyInfoList.mapNotNull { it.userId }).distinct() usersList.forEach { userID -> - val userMap = HashMap() + val userMap = HashMap() deviceInfoList.filter { it.userId == userID }.forEach { deviceInfo -> - userMap[deviceInfo.deviceId] = deviceInfo.toDeviceKeys() + userMap[deviceInfo.deviceId] = deviceInfo.toRest() } signingKeyInfoList.filter { it.userId == userID }.forEach { keyInfo -> keyInfo.unpaddedBase64PublicKey?.let { base64Key -> - userMap[base64Key] = keyInfo + userMap[base64Key] = keyInfo.toRest() } } map[userID] = userMap @@ -54,5 +55,4 @@ data class UploadSignatureQueryBuilder( return map } - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSigningKeysBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSigningKeysBody.kt index ea7934dabc..2b485ea4b1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSigningKeysBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UploadSigningKeysBody.kt @@ -17,18 +17,17 @@ package im.vector.matrix.android.internal.crypto.model.rest import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import im.vector.matrix.android.internal.auth.registration.AuthParams @JsonClass(generateAdapter = true) internal data class UploadSigningKeysBody( @Json(name = "master_key") - val masterKey: CrossSigningKeyInfo? = null, + val masterKey: RestKeyInfo? = null, @Json(name = "self_signing_key") - val selfSigningKey: CrossSigningKeyInfo? = null, + val selfSigningKey: RestKeyInfo? = null, @Json(name = "user_signing_key") - val userSigningKey: CrossSigningKeyInfo? = null, + val userSigningKey: RestKeyInfo? = null, @Json(name = "auth") val auth: UserPasswordAuth? = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/XSigningKeys.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/XSigningKeys.kt deleted file mode 100644 index 56043ca521..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/XSigningKeys.kt +++ /dev/null @@ -1,20 +0,0 @@ -package im.vector.matrix.android.internal.crypto.model.rest - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import im.vector.matrix.android.internal.crypto.model.MXKeysObject - -@JsonClass(generateAdapter = true) -data class XSigningKeys( - @Json(name = "user_id") - override val userId: String, - - @Json(name = "usage") - val usage: List, - - @Json(name = "keys") - override val keys: Map, - - @Json(name = "signatures") - override val signatures: Map>? -) : MXKeysObject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt index 1857075fe7..7236881954 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt @@ -21,10 +21,10 @@ import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningIn import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.NewSessionListener import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper -import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity import org.matrix.olm.OlmAccount @@ -156,7 +156,7 @@ internal interface IMXCryptoStore { * @param userId the user's id. * @param device the device to store. */ - fun storeUserDevice(userId: String?, deviceInfo: MXDeviceInfo?) + fun storeUserDevice(userId: String?, deviceInfo: CryptoDeviceInfo?) /** * Retrieve a device for a user. @@ -165,7 +165,7 @@ internal interface IMXCryptoStore { * @param userId the user's id. * @return the device */ - fun getUserDevice(userId: String, deviceId: String): MXDeviceInfo? + fun getUserDevice(userId: String, deviceId: String): CryptoDeviceInfo? /** * Retrieve a device by its identity key. @@ -173,7 +173,7 @@ internal interface IMXCryptoStore { * @param identityKey the device identity key (`MXDeviceInfo.identityKey`) * @return the device or null if not found */ - fun deviceWithIdentityKey(identityKey: String): MXDeviceInfo? + fun deviceWithIdentityKey(identityKey: String): CryptoDeviceInfo? /** * Store the known devices for a user. @@ -181,12 +181,11 @@ internal interface IMXCryptoStore { * @param userId The user's id. * @param devices A map from device id to 'MXDevice' object for the device. */ - fun storeUserDevices(userId: String, devices: Map?) + fun storeUserDevices(userId: String, devices: Map?) - - fun storeUserCrossSigningKeys(userId: String, masterKey: CrossSigningKeyInfo?, - selfSigningKey: CrossSigningKeyInfo?, - userSigningKey: CrossSigningKeyInfo?) + fun storeUserCrossSigningKeys(userId: String, masterKey: CryptoCrossSigningKey?, + selfSigningKey: CryptoCrossSigningKey?, + userSigningKey: CryptoCrossSigningKey?) /** * Retrieve the known devices for a user. @@ -194,7 +193,7 @@ internal interface IMXCryptoStore { * @param userId The user's id. * @return The devices map if some devices are known, else null */ - fun getUserDevices(userId: String): Map? + fun getUserDevices(userId: String): Map? /** * Store the crypto algorithm for a room. @@ -389,10 +388,9 @@ internal interface IMXCryptoStore { fun removeSessionListener(listener: NewSessionListener) - - //============================================= + // ============================================= // CROSS SIGNING - //============================================= + // ============================================= /** * Gets the current crosssigning info @@ -400,11 +398,9 @@ internal interface IMXCryptoStore { fun getMyCrossSigningInfo() : MXCrossSigningInfo? fun setMyCrossSigningInfo(info: MXCrossSigningInfo?) - fun getCrossSigningInfo(userId: String) : MXCrossSigningInfo? fun setCrossSigningInfo(userId: String, info: MXCrossSigningInfo?) - fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?) fun getCrossSigningPrivateKeys() : PrivateKeysInfo? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index b83bbc4760..ca44a18fa4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -21,14 +21,36 @@ import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningIn import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.NewSessionListener import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper -import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.model.toEntity import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo -import im.vector.matrix.android.internal.crypto.store.db.model.* +import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntity +import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMapper +import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMetadataEntity +import im.vector.matrix.android.internal.crypto.store.db.model.CryptoRoomEntity +import im.vector.matrix.android.internal.crypto.store.db.model.CryptoRoomEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity +import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.IncomingRoomKeyRequestEntity +import im.vector.matrix.android.internal.crypto.store.db.model.IncomingRoomKeyRequestEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntity +import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity +import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntity +import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntity +import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingRoomKeyRequestEntity +import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingRoomKeyRequestEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntity +import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity +import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.createPrimaryKey import im.vector.matrix.android.internal.crypto.store.db.query.delete import im.vector.matrix.android.internal.crypto.store.db.query.get import im.vector.matrix.android.internal.crypto.store.db.query.getById @@ -171,20 +193,22 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati return olmAccount } - override fun storeUserDevice(userId: String?, deviceInfo: MXDeviceInfo?) { + override fun storeUserDevice(userId: String?, deviceInfo: CryptoDeviceInfo?) { if (userId == null || deviceInfo == null) { return } - doRealmTransaction(realmConfiguration) { - val user = UserEntity.getOrCreate(it, userId) + doRealmTransaction(realmConfiguration) { realm -> + val user = UserEntity.getOrCreate(realm, userId) // Create device info - val deviceInfoEntity = DeviceInfoEntity.getOrCreate(it, userId, deviceInfo.deviceId).apply { - deviceId = deviceInfo.deviceId - identityKey = deviceInfo.identityKey() - putDeviceInfo(deviceInfo) - } + val deviceInfoEntity = CryptoMapper.mapToEntity(deviceInfo) + realm.insertOrUpdate(deviceInfoEntity) +// val deviceInfoEntity = DeviceInfoEntity.getOrCreate(it, userId, deviceInfo.deviceId).apply { +// deviceId = deviceInfo.deviceId +// identityKey = deviceInfo.identityKey() +// putDeviceInfo(deviceInfo) +// } if (!user.devices.contains(deviceInfoEntity)) { user.devices.add(deviceInfoEntity) @@ -192,25 +216,28 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati } } - override fun getUserDevice(userId: String, deviceId: String): MXDeviceInfo? { + override fun getUserDevice(userId: String, deviceId: String): CryptoDeviceInfo? { return doRealmQueryAndCopy(realmConfiguration) { it.where() .equalTo(DeviceInfoEntityFields.PRIMARY_KEY, DeviceInfoEntity.createPrimaryKey(userId, deviceId)) .findFirst() + }?.let { + CryptoMapper.mapToModel(it) } - ?.getDeviceInfo() } - override fun deviceWithIdentityKey(identityKey: String): MXDeviceInfo? { + override fun deviceWithIdentityKey(identityKey: String): CryptoDeviceInfo? { return doRealmQueryAndCopy(realmConfiguration) { it.where() .equalTo(DeviceInfoEntityFields.IDENTITY_KEY, identityKey) .findFirst() } - ?.getDeviceInfo() + ?.let { + CryptoMapper.mapToModel(it) + } } - override fun storeUserDevices(userId: String, devices: Map?) { + override fun storeUserDevices(userId: String, devices: Map?) { doRealmTransaction(realmConfiguration) { realm -> if (devices == null) { // Remove the user @@ -221,25 +248,18 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati // Add the devices // Ensure all other devices are deleted u.devices.deleteAllFromRealm() - - u.devices.addAll( - devices.map { - DeviceInfoEntity.getOrCreate(realm, userId, it.value.deviceId).apply { - deviceId = it.value.deviceId - identityKey = it.value.identityKey() - putDeviceInfo(it.value) - } - } - ) + val new = devices.map { entry -> entry.value.toEntity() } + new.forEach { realm.insertOrUpdate(it) } + u.devices.addAll(new) } } } } override fun storeUserCrossSigningKeys(userId: String, - masterKey: CrossSigningKeyInfo?, - selfSigningKey: CrossSigningKeyInfo?, - userSigningKey: CrossSigningKeyInfo?) { + masterKey: CryptoCrossSigningKey?, + selfSigningKey: CryptoCrossSigningKey?, + userSigningKey: CryptoCrossSigningKey?) { doRealmTransaction(realmConfiguration) { realm -> UserEntity.getOrCreate(realm, userId) .let { userEntity -> @@ -253,7 +273,7 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati val existingMaster = signingInfo.getMasterKey() if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) { - //update signatures? + // update signatures? existingMaster.putSignatures(masterKey.signatures) existingMaster.usages = masterKey.usages?.toTypedArray()?.let { RealmList(*it) } ?: RealmList() @@ -269,7 +289,7 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati val existingSelfSigned = signingInfo.getSelfSignedKey() if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) { - //update signatures? + // update signatures? existingSelfSigned.putSignatures(selfSigningKey.signatures) existingSelfSigned.usages = selfSigningKey.usages?.toTypedArray()?.let { RealmList(*it) } ?: RealmList() @@ -288,7 +308,6 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati } } } - } override fun getCrossSigningPrivateKeys(): PrivateKeysInfo? { @@ -304,7 +323,7 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati } override fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?) { - doRealmTransaction(realmConfiguration) {realm -> + doRealmTransaction(realmConfiguration) { realm -> realm.where().findFirst()?.apply { xSignMasterPrivateKey = msk xSignSelfSignedPrivateKey = ssk @@ -313,15 +332,14 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati } } - - override fun getUserDevices(userId: String): Map? { + override fun getUserDevices(userId: String): Map? { return doRealmQueryAndCopy(realmConfiguration) { it.where() .equalTo(UserEntityFields.USER_ID, userId) .findFirst() } ?.devices - ?.mapNotNull { it.getDeviceInfo() } + ?.map { CryptoMapper.mapToModel(it) } ?.associateBy { it.deviceId } } @@ -836,10 +854,19 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati override fun setUserKeysAsTrusted(userId: String, trusted: Boolean) { doRealmTransaction(realmConfiguration) { realm -> - realm.where(CrossSigningInfoEntity::class.java) + val info = realm.where(CrossSigningInfoEntity::class.java) .equalTo(CrossSigningInfoEntityFields.USER_ID, userId) .findFirst() - ?.isTrusted = trusted + if (info != null) { + val level = info.trustLevelEntity + if (level == null) { + val newLevel = realm.createObject(TrustLevelEntity::class.java) + newLevel.locallyVerified = true + info.trustLevelEntity = newLevel + } else { + level.locallyVerified = true + } + } } } @@ -853,7 +880,7 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati userId = userId, crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull { val pubKey = it.publicKeyBase64 ?: return@mapNotNull null - CrossSigningKeyInfo( + CryptoCrossSigningKey( userId = userId, keys = mapOf("ed25519:$pubKey" to pubKey), usages = it.usages.map { it }, @@ -861,7 +888,7 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati ) }, - isTrusted = xsignInfo.isTrusted + isTrusted = xsignInfo.trustLevelEntity?.isVerified() ?: false // TODO ) } } @@ -889,6 +916,15 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati ) } existing.crossSigningKeys = xkeys + val existingTrust = existing.trustLevelEntity + if (existingTrust != null) { + existingTrust.locallyVerified = true + } else { + realm.createObject(TrustLevelEntity::class.java).let { + it.locallyVerified = info.isTrusted + existing.trustLevelEntity = it + } + } } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 52473a48e2..fa69cdab63 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -16,8 +16,17 @@ package im.vector.matrix.android.internal.crypto.store.db -import im.vector.matrix.android.internal.crypto.store.db.model.* -import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntity +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMetadataEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields +import im.vector.matrix.android.internal.di.SerializeNulls import io.realm.DynamicRealm import io.realm.RealmMigration import timber.log.Timber @@ -39,27 +48,88 @@ internal object RealmCryptoStoreMigration : RealmMigration { .addField(KeyInfoEntityFields.SIGNATURES, String::class.java) .addRealmListField(KeyInfoEntityFields.USAGES.`$`, String::class.java) + val trustLevelentityEntitySchema = realm.schema.create("TrustLevelEntity") + .addField(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED, Boolean::class.java) + .setNullable(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED,true) + .addField(TrustLevelEntityFields.LOCALLY_VERIFIED, Boolean::class.java) + .setNullable(TrustLevelEntityFields.LOCALLY_VERIFIED,true) Timber.d("Create CrossSigningInfoEntity") val crossSigningInfoSchema = realm.schema.create("CrossSigningInfoEntity") .addField(CrossSigningInfoEntityFields.USER_ID, String::class.java) - .addField(CrossSigningInfoEntityFields.IS_TRUSTED, Boolean::class.java) + .addRealmObjectField(CrossSigningInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevelentityEntitySchema) .addPrimaryKey(CrossSigningInfoEntityFields.USER_ID) .addRealmListField(CrossSigningInfoEntityFields.CROSS_SIGNING_KEYS.`$`, keyInfoEntitySchema) - Timber.d("Updating UserEntity table") realm.schema.get("UserEntity") ?.addRealmObjectField(UserEntityFields.CROSS_SIGNING_INFO_ENTITY.`$`, crossSigningInfoSchema) - Timber.d("Updating CryptoMetadataEntity table") realm.schema.get("CryptoMetadataEntity") ?.addField(CryptoMetadataEntityFields.X_SIGN_MASTER_PRIVATE_KEY, String::class.java) ?.addField(CryptoMetadataEntityFields.X_SIGN_USER_PRIVATE_KEY, String::class.java) ?.addField(CryptoMetadataEntityFields.X_SIGN_SELF_SIGNED_PRIVATE_KEY, String::class.java) + val moshi = Moshi.Builder().add(SerializeNulls.JSON_ADAPTER_FACTORY).build() + val listMigrationAdapter = moshi.adapter>(Types.newParameterizedType( + List::class.java, + String::class.java, + Any::class.java + )) + val mapMigrationAdapter = moshi.adapter(Types.newParameterizedType( + Map::class.java, + String::class.java, + Any::class.java + )) + + realm.schema.get("DeviceInfoEntity") + ?.addField(DeviceInfoEntityFields.USER_ID, String::class.java) + ?.addField(DeviceInfoEntityFields.ALGORITHM_LIST_JSON, String::class.java) + ?.addField(DeviceInfoEntityFields.KEYS_MAP_JSON, String::class.java) + ?.addField(DeviceInfoEntityFields.SIGNATURE_MAP_JSON, String::class.java) + ?.addField(DeviceInfoEntityFields.UNSIGNED_MAP_JSON, String::class.java) + ?.addField(DeviceInfoEntityFields.IS_BLOCKED, Boolean::class.java) + ?.setNullable(DeviceInfoEntityFields.IS_BLOCKED,true) + ?.addRealmObjectField(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`,trustLevelentityEntitySchema) + ?.transform { obj -> + + val oldSerializedData = obj.getString("deviceInfoData") + deserializeFromRealm(oldSerializedData)?.let { oldDevice -> + + val trustLevel = realm.createObject("TrustLevelEntity") + when (oldDevice.verified) { + MXDeviceInfo.DEVICE_VERIFICATION_UNKNOWN -> { + obj.setNull(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`) + } + MXDeviceInfo.DEVICE_VERIFICATION_BLOCKED -> { + trustLevel.setNull(TrustLevelEntityFields.LOCALLY_VERIFIED) + trustLevel.setNull(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED) + obj.setBoolean(DeviceInfoEntityFields.IS_BLOCKED, oldDevice.isBlocked) + obj.setObject(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevel) + } + MXDeviceInfo.DEVICE_VERIFICATION_UNVERIFIED -> { + trustLevel.setBoolean(TrustLevelEntityFields.LOCALLY_VERIFIED, false) + trustLevel.setBoolean(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED, false) + obj.setObject(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevel) + } + MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED -> { + trustLevel.setBoolean(TrustLevelEntityFields.LOCALLY_VERIFIED, true) + trustLevel.setBoolean(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED, false) + obj.setObject(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevel) + } + } + + obj.setString(DeviceInfoEntityFields.USER_ID, oldDevice.userId) + obj.setString(DeviceInfoEntityFields.IDENTITY_KEY, oldDevice.identityKey()) + obj.setString(DeviceInfoEntityFields.ALGORITHM_LIST_JSON, listMigrationAdapter.toJson(oldDevice.algorithms)) + obj.setString(DeviceInfoEntityFields.KEYS_MAP_JSON, mapMigrationAdapter.toJson(oldDevice.keys)) + obj.setString(DeviceInfoEntityFields.SIGNATURE_MAP_JSON, mapMigrationAdapter.toJson(oldDevice.signatures)) + obj.setString(DeviceInfoEntityFields.UNSIGNED_MAP_JSON, mapMigrationAdapter.toJson(oldDevice.unsigned)) + } + } + ?.removeField("deviceInfoData") } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt index 692a404d99..9d7b823efb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt @@ -34,6 +34,7 @@ import io.realm.annotations.RealmModule OutgoingRoomKeyRequestEntity::class, UserEntity::class, KeyInfoEntity::class, - CrossSigningInfoEntity::class + CrossSigningInfoEntity::class, + TrustLevelEntity::class ]) internal class RealmCryptoStoreModule diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CrossSigningInfoEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CrossSigningInfoEntity.kt index 8d0daf66fc..b3a654d2a4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CrossSigningInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CrossSigningInfoEntity.kt @@ -16,7 +16,7 @@ package im.vector.matrix.android.internal.crypto.store.db.model -import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo +import im.vector.matrix.android.internal.crypto.model.KeyUsage import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey @@ -25,27 +25,26 @@ internal open class CrossSigningInfoEntity( @PrimaryKey var userId: String? = null, var crossSigningKeys: RealmList = RealmList(), - var isTrusted: Boolean = false + var trustLevelEntity: TrustLevelEntity? = null ) : RealmObject() { companion object - fun getMasterKey() = crossSigningKeys.firstOrNull { it.usages.contains(CrossSigningKeyInfo.KeyUsage.MASTER.value) } + fun getMasterKey() = crossSigningKeys.firstOrNull { it.usages.contains(KeyUsage.MASTER.value) } fun setMasterKey(info: KeyInfoEntity?) { crossSigningKeys - .filter { it.usages.contains(CrossSigningKeyInfo.KeyUsage.MASTER.value) } + .filter { it.usages.contains(KeyUsage.MASTER.value) } .forEach { crossSigningKeys.remove(it) } info?.let { crossSigningKeys.add(it) } } - fun getSelfSignedKey() = crossSigningKeys.firstOrNull { it.usages.contains(CrossSigningKeyInfo.KeyUsage.SELF_SIGNING.value) } + fun getSelfSignedKey() = crossSigningKeys.firstOrNull { it.usages.contains(KeyUsage.SELF_SIGNING.value) } fun setSelfSignedKey(info: KeyInfoEntity?) { crossSigningKeys - .filter { it.usages.contains(CrossSigningKeyInfo.KeyUsage.SELF_SIGNING.value) } + .filter { it.usages.contains(KeyUsage.SELF_SIGNING.value) } .forEach { crossSigningKeys.remove(it) } info?.let { crossSigningKeys.add(it) } } - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMapper.kt new file mode 100644 index 0000000000..5a4938d1fe --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMapper.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.store.db.model + +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo +import im.vector.matrix.android.internal.di.SerializeNulls +import timber.log.Timber + +object CryptoMapper { + + private val moshi = Moshi.Builder().add(SerializeNulls.JSON_ADAPTER_FACTORY).build() + private val listMigrationAdapter = moshi.adapter>(Types.newParameterizedType( + List::class.java, + String::class.java, + Any::class.java + )) + private val mapMigrationAdapter = moshi.adapter(Types.newParameterizedType( + Map::class.java, + String::class.java, + Any::class.java + )) + private val mapOfStringMigrationAdapter = moshi.adapter>>(Types.newParameterizedType( + Map::class.java, + String::class.java, + Any::class.java + )) + + internal fun mapToEntity(deviceInfo: CryptoDeviceInfo): DeviceInfoEntity { + return DeviceInfoEntity( + primaryKey = DeviceInfoEntity.createPrimaryKey(deviceInfo.userId, deviceInfo.deviceId), + userId = deviceInfo.userId, + deviceId = deviceInfo.deviceId, + algorithmListJson = listMigrationAdapter.toJson(deviceInfo.algorithms), + keysMapJson = mapMigrationAdapter.toJson(deviceInfo.keys), + signatureMapJson = mapMigrationAdapter.toJson(deviceInfo.signatures), + isBlocked = deviceInfo.isBlocked, + trustLevelEntity = deviceInfo.trustLevel?.let { + TrustLevelEntity( + crossSignedVerified = it.crossSigningVerified, + locallyVerified = it.locallyVerified + ) + }, + unsignedMapJson = mapMigrationAdapter.toJson(deviceInfo.unsigned) + ) + } + + internal fun mapToModel(deviceInfoEntity: DeviceInfoEntity): CryptoDeviceInfo { + return CryptoDeviceInfo( + userId = deviceInfoEntity.userId ?: "", + deviceId = deviceInfoEntity.deviceId ?: "", + isBlocked = deviceInfoEntity.isBlocked ?: false, + trustLevel = deviceInfoEntity.trustLevelEntity?.let { + DeviceTrustLevel(it.crossSignedVerified ?: false, it.locallyVerified) + }, + unsigned = deviceInfoEntity.unsignedMapJson?.let { + try { + mapMigrationAdapter.fromJson(it) + } catch (failure: Throwable) { + Timber.e(failure) + null + } + }, + signatures = deviceInfoEntity.signatureMapJson?.let { + try { + mapOfStringMigrationAdapter.fromJson(it) + } catch (failure: Throwable) { + Timber.e(failure) + null + } + }, + keys = deviceInfoEntity.keysMapJson?.let { + try { + moshi.adapter>(Types.newParameterizedType( + Map::class.java, + String::class.java, + Any::class.java + )).fromJson(it) + } catch (failure: Throwable) { + Timber.e(failure) + null + } + }, + algorithms = deviceInfoEntity.algorithmListJson?.let { + try { + listMigrationAdapter.fromJson(it) + } catch (failure: Throwable) { + Timber.e(failure) + null + } + } + ) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt index beb7cfd797..8a2c2914da 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.store.db.model import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm -import io.realm.Realm import io.realm.RealmObject import io.realm.annotations.PrimaryKey import org.matrix.olm.OlmAccount diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt index 2c321cc50e..dceb77ddd9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt @@ -16,9 +16,6 @@ package im.vector.matrix.android.internal.crypto.store.db.model -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo -import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm -import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.LinkingObjects @@ -30,18 +27,26 @@ internal fun DeviceInfoEntity.Companion.createPrimaryKey(userId: String, deviceI internal open class DeviceInfoEntity(@PrimaryKey var primaryKey: String = "", var deviceId: String? = null, var identityKey: String? = null, - var deviceInfoData: String? = null) + // var deviceInfoData: String? = null, + var userId: String? = null, + var isBlocked: Boolean? = null, + var algorithmListJson: String? = null, + var keysMapJson: String? = null, + var signatureMapJson: String? = null, + var unsignedMapJson: String? = null, + var trustLevelEntity: TrustLevelEntity? = null + ) : RealmObject() { - // Deserialize data - fun getDeviceInfo(): MXDeviceInfo? { - return deserializeFromRealm(deviceInfoData) - } - - // Serialize data - fun putDeviceInfo(deviceInfo: MXDeviceInfo?) { - deviceInfoData = serializeForRealm(deviceInfo) - } +// // Deserialize data +// fun getDeviceInfo(): MXDeviceInfo? { +// return deserializeFromRealm(deviceInfoData) +// } +// +// // Serialize data +// fun putDeviceInfo(deviceInfo: MXDeviceInfo?) { +// deviceInfoData = serializeForRealm(deviceInfo) +// } @LinkingObjects("devices") val users: RealmResults? = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/KeyInfoEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/KeyInfoEntity.kt index 6e2418f37c..61dfad63af 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/KeyInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/KeyInfoEntity.kt @@ -21,7 +21,6 @@ import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm import io.realm.RealmList import io.realm.RealmObject - internal open class KeyInfoEntity( var publicKeyBase64: String? = null, // var isTrusted: Boolean = false, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/TrustLevelEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/TrustLevelEntity.kt new file mode 100644 index 0000000000..c35282b392 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/TrustLevelEntity.kt @@ -0,0 +1,13 @@ +package im.vector.matrix.android.internal.crypto.store.db.model + +import io.realm.RealmObject + +internal open class TrustLevelEntity( + var crossSignedVerified: Boolean? = null, + var locallyVerified: Boolean? = null +) : RealmObject() { + + companion object + + fun isVerified() : Boolean = crossSignedVerified == true || locallyVerified == true +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/CrossSigningInfoEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/CrossSigningInfoEntityQueries.kt index 0128611fce..4c81cb46fa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/CrossSigningInfoEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/CrossSigningInfoEntityQueries.kt @@ -34,4 +34,3 @@ internal fun CrossSigningInfoEntity.Companion.get(realm: Realm, userId: String): .equalTo(UserEntityFields.USER_ID, userId) .findFirst() } - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt index d8bfe73eda..4ea60a7bad 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt @@ -18,9 +18,9 @@ package im.vector.matrix.android.internal.crypto.tasks import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.internal.crypto.api.CryptoApi -import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse +import im.vector.matrix.android.internal.crypto.model.rest.RestDeviceInfo import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.convertToUTF8 @@ -30,7 +30,7 @@ import javax.inject.Inject internal interface UploadKeysTask : Task { data class Params( // the device keys to send. - val deviceKeys: DeviceKeys?, + val deviceKeys: RestDeviceInfo?, // the one-time keys to send. val oneTimeKeys: JsonDict?, // the explicit device_id to use for upload (default is to use the same as that used during auth). diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadSignaturesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadSignaturesTask.kt index 861863e9a3..8d4c8a9033 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadSignaturesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadSignaturesTask.kt @@ -17,17 +17,15 @@ package im.vector.matrix.android.internal.crypto.tasks import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.internal.crypto.api.CryptoApi -import im.vector.matrix.android.internal.crypto.model.MXKeysObject import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task import org.greenrobot.eventbus.EventBus import javax.inject.Inject - internal interface UploadSignaturesTask : Task { data class Params( - val signatures: Map> + val signatures: Map> ) } @@ -41,10 +39,9 @@ internal class DefaultUploadSignaturesTask @Inject constructor( apiCall = cryptoApi.uploadSignatures(params.signatures) } if (executeRequest.failures?.isNotEmpty() == true) { - //TODO better + // TODO better throw Failure.OtherServerError(executeRequest.toString(), 400) } return executeRequest } - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadSigningKeysTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadSigningKeysTask.kt index bf454ac903..6aec375878 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadSigningKeysTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadSigningKeysTask.kt @@ -19,25 +19,25 @@ package im.vector.matrix.android.internal.crypto.tasks import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse import im.vector.matrix.android.internal.crypto.api.CryptoApi -import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo +import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse import im.vector.matrix.android.internal.crypto.model.rest.UploadSigningKeysBody import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth +import im.vector.matrix.android.internal.crypto.model.toRest import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task import org.greenrobot.eventbus.EventBus import javax.inject.Inject - internal interface UploadSigningKeysTask : Task { data class Params( // the device keys to send. - val masterKey: CrossSigningKeyInfo, + val masterKey: CryptoCrossSigningKey, // the one-time keys to send. - val userKey: CrossSigningKeyInfo, + val userKey: CryptoCrossSigningKey, // the explicit device_id to use for upload (default is to use the same as that used during auth). - val selfSignedKey: CrossSigningKeyInfo, + val selfSignedKey: CryptoCrossSigningKey, val userPasswordAuth: UserPasswordAuth? ) } @@ -47,11 +47,10 @@ internal class DefaultUploadSigningKeysTask @Inject constructor( private val eventBus: EventBus ) : UploadSigningKeysTask { override suspend fun execute(params: UploadSigningKeysTask.Params): KeysQueryResponse { - val uploadQuery = UploadSigningKeysBody( - masterKey = params.masterKey, - userSigningKey = params.userKey, - selfSigningKey = params.selfSignedKey, + masterKey = params.masterKey.toRest(), + userSigningKey = params.userKey.toRest(), + selfSigningKey = params.selfSignedKey.toRest(), auth = params.userPasswordAuth.takeIf { params.userPasswordAuth?.session != null } ) try { @@ -83,10 +82,6 @@ internal class DefaultUploadSigningKeysTask @Inject constructor( } // Other error throw throwable - } - } - - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt index cc96a8154e..faa21f9356 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt @@ -32,7 +32,8 @@ import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.internal.crypto.DeviceListManager import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.* import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore @@ -202,7 +203,7 @@ internal class DefaultSasVerificationService @Inject constructor( } override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { - setDeviceVerificationAction.handle(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, + setDeviceVerificationAction.handle(DeviceTrustLevel(false, true), deviceID, userId) @@ -398,7 +399,7 @@ internal class DefaultSasVerificationService @Inject constructor( } private suspend fun checkKeysAreDownloaded(otherUserId: String, - fromDevice: String): MXUsersDevicesMap? { + fromDevice: String): MXUsersDevicesMap? { return try { var keys = deviceListManager.downloadKeys(listOf(otherUserId), false) if (keys.getUserDeviceIds(otherUserId)?.contains(fromDevice) == true) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt index 7f2e109188..8a6b6f654d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt @@ -25,7 +25,7 @@ import im.vector.matrix.android.api.session.crypto.sas.SasMode import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore @@ -168,7 +168,7 @@ internal abstract class SASVerificationTransaction( ?.masterKey() ?.unpaddedBase64PublicKey ?.let { masterPublicKey -> - val crossSigningKeyId = "ed25519:${masterPublicKey}" + val crossSigningKeyId = "ed25519:$masterPublicKey" macUsingAgreedMethod(masterPublicKey, baseInfo + crossSigningKeyId)?.let { MSKMacString -> keyMap[crossSigningKeyId] = MSKMacString } @@ -179,12 +179,10 @@ internal abstract class SASVerificationTransaction( if (macString.isNullOrBlank() || keyStrings.isNullOrBlank()) { // Should not happen Timber.e("## SAS verification [$transactionId] failed to send KeyMac, empty key hashes.") - Timber.e("## SAS verification [$transactionId] failed to send KeyMac, empty key hashes.") cancel(CancelCode.UnexpectedMessage) return } - val macMsg = transport.createMac(transactionId, keyMap, keyStrings) myMac = macMsg state = SasVerificationTxState.SendingMac @@ -277,11 +275,11 @@ internal abstract class SASVerificationTransaction( val otherMasterKey = cryptoStore.getCrossSigningInfo(otherUserId)?.masterKey() val otherCrossSigningMasterKeyPublic = otherMasterKey?.unpaddedBase64PublicKey if (otherCrossSigningMasterKeyPublic != null) { - //Did the user signed his master key + // Did the user signed his master key theirMac!!.mac!!.keys.forEach { val keyIDNoPrefix = if (it.startsWith("ed25519:")) it.substring("ed25519:".length) else it if (keyIDNoPrefix == otherCrossSigningMasterKeyPublic) { - //Check the signature + // Check the signature val mac = macUsingAgreedMethod(otherCrossSigningMasterKeyPublic, baseInfo + it) if (mac != theirMac?.mac?.get(it)) { // WRONG! @@ -303,12 +301,23 @@ internal abstract class SASVerificationTransaction( return } - if (otherMasterKeyIsVerified) { + // If not me sign his MSK and upload the signature + if (otherMasterKeyIsVerified && otherUserId != credentials.userId) { // we should trust this master key // And check verification MSK -> SSK? crossSigningService.trustUser(otherUserId, object : MatrixCallback { override fun onFailure(failure: Throwable) { - Timber.e(failure,"## SAS Verification: Failed to trust User $otherUserId") + Timber.e(failure, "## SAS Verification: Failed to trust User $otherUserId") + } + }) + } + + if (otherUserId == credentials.userId) { + // If me it's reasonable to sign and upload the device signature + // Notice that i might not have the private keys, so may ot be able to do it + crossSigningService.signDevice(otherDeviceId!!, object : MatrixCallback { + override fun onFailure(failure: Throwable) { + Timber.w(failure, "## SAS Verification: Failed to sign new device $otherDeviceId") } }) } @@ -322,7 +331,8 @@ internal abstract class SASVerificationTransaction( } private fun setDeviceVerified(deviceId: String, userId: String) { - setDeviceVerificationAction.handle(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, + // TODO should not override cross sign status + setDeviceVerificationAction.handle(DeviceTrustLevel(false, true), deviceId, userId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt index 758634c391..201e0b2322 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt @@ -77,7 +77,6 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) } crypto.downloadKeys(listOf("@testxsigningvfe:matrix.org"), true, object : MatrixCallback { - }) var error: Throwable? = null diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/task/CoroutineSequencersTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/task/CoroutineSequencersTest.kt index 9591feaa32..4b6ae8b38d 100644 --- a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/task/CoroutineSequencersTest.kt +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/task/CoroutineSequencersTest.kt @@ -16,6 +16,8 @@ package im.vector.matrix.android.internal.task +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.delay @@ -126,4 +128,48 @@ class CoroutineSequencersTest { println("BLOCKING METHOD $name ENDS on ${Thread.currentThread().name}") return name } + + @Test + fun test_MoshiMap() { + val moshi = Moshi.Builder().build() + val sample = """ + { + "master_key": { + "user_id": "@alice:example.com", + "usage": ["master"], + "keys": { + "ed25519:base64+master+public+key": "base64+self+master+key", + } + }, + "self_signing_key": { + "user_id": "@alice:example.com", + "usage": ["self_signing"], + "keys": { + "ed25519:base64+self+signing+public+key": "base64+self+signing+public+key", + }, + "signatures": { + "@alice:example.com": { + "ed25519:base64+master+public+key": "base64+signature" + } + } + }, + "user_signing_key": { + "user_id": "@alice:example.com", + "keys": { + "ed25519:base64+device+signing+public+key": "base64+device+signing+public+key", + }, + "usage": ["user_signing"], + "signatures": { + "@alice:example.com": { + "ed25519:base64+master+public+key": "base64+signature" + } + } + } + """.trimIndent() + + val adapter = moshi.adapter>(Types.newParameterizedType(Map::class.java, List::class.java, String::class.java)) + + val map = adapter.fromJson(sample) + print(map) + } } diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt index ef680e800a..db3c91d441 100644 --- a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt @@ -44,7 +44,6 @@ class DebugMenuActivity : VectorBaseActivity() { override fun getLayoutRes() = R.layout.activity_debug_menu - @Inject lateinit var activeSessionHolder: ActiveSessionHolder @@ -164,7 +163,6 @@ class DebugMenuActivity : VectorBaseActivity() { @OnClick(R.id.debug_initialise_xsigning) fun testXSigning() { activeSessionHolder.getActiveSession().getCrossSigningService().initializeCrossSigning(null, object : MatrixCallback { - override fun onFailure(failure: Throwable) { if (failure is Failure.OtherServerError && failure.httpCode == 401 diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt index 1af7d2fbd1..0f4b651d43 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt @@ -28,7 +28,8 @@ import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransactio import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCancellation -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse @@ -98,8 +99,8 @@ class KeyRequestHandler @Inject constructor(private val context: Context) alertsToRequests[mappingKey] = ArrayList().apply { this.add(request) } // Add a notification for every incoming request - session?.downloadKeys(listOf(userId), false, object : MatrixCallback> { - override fun onSuccess(data: MXUsersDevicesMap) { + session?.downloadKeys(listOf(userId), false, object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { val deviceInfo = data.getObject(userId, deviceId) if (null == deviceInfo) { @@ -109,9 +110,9 @@ class KeyRequestHandler @Inject constructor(private val context: Context) } if (deviceInfo.isUnknown) { - session?.setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_UNVERIFIED, deviceId, userId) + session?.setDeviceVerification(DeviceTrustLevel(false, false), deviceId, userId) - deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_UNVERIFIED + deviceInfo.trustLevel = DeviceTrustLevel(false, false) // can we get more info on this device? session?.getDevicesList(object : MatrixCallback { @@ -143,7 +144,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context) userId: String, deviceId: String, wasNewDevice: Boolean, - deviceInfo: MXDeviceInfo?, + deviceInfo: CryptoDeviceInfo?, moreInfo: DeviceInfo? = null) { val deviceName = if (deviceInfo!!.displayName().isNullOrEmpty()) deviceInfo.deviceId else deviceInfo.displayName() val dialogText: String? diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt index 22369f37b5..0cb61faf1f 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt @@ -101,7 +101,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { it.otherUserMxItem?.let { matrixItem -> avatarRenderer.render(matrixItem, otherUserAvatarImageView) - if(it.sasTransactionState == SasVerificationTxState.Verified) { + if (it.sasTransactionState == SasVerificationTxState.Verified) { otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName()) otherUserShield.isVisible = true } else { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 4701eecb7e..0f97d19135 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -50,7 +50,6 @@ import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap -import im.vector.riotx.BuildConfig import im.vector.riotx.R import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.platform.VectorViewModel