mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Refactoring / deprecation of MXDeviceInfo
introduced TrustLevels
This commit is contained in:
parent
98ba2d39a8
commit
6ab540045b
68 changed files with 1117 additions and 560 deletions
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -94,7 +94,6 @@ class CommonTestHelper(context: Context) {
|
|||
syncLiveData.removeObserver(this)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<SignatureUploadResponse> {
|
||||
|
@ -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<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
bobSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
fail("Failed to get device")
|
||||
}
|
||||
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
||||
override fun onSuccess(data: MXUsersDevicesMap<CryptoDeviceInfo>) {
|
||||
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<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
aliceSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
fail("Failed to get device")
|
||||
}
|
||||
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
||||
//check that the device is seen
|
||||
override fun onSuccess(data: MXUsersDevicesMap<CryptoDeviceInfo>) {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = " ")
|
||||
|
||||
|
|
|
@ -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<MXDeviceInfo>
|
||||
fun getUserDevices(userId: String): MutableList<CryptoDeviceInfo>
|
||||
|
||||
fun setDevicesKnown(devices: List<MXDeviceInfo>, callback: MatrixCallback<Unit>?)
|
||||
|
||||
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<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>)
|
||||
fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>>)
|
||||
|
||||
fun addNewSessionListener(newSessionListener: NewSessionListener)
|
||||
|
||||
|
|
|
@ -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<MXDeviceInfo>) : MXCryptoError()
|
||||
data class UnknownDevice(val deviceList: MXUsersDevicesMap<CryptoDeviceInfo>) : MXCryptoError()
|
||||
|
||||
enum class ErrorType {
|
||||
ENCRYPTING_NOT_ENABLED,
|
||||
|
|
|
@ -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<Boolean>? = 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<SignatureUploadResponse>)
|
||||
|
||||
fun checkDeviceTrust(userId: String, deviceId: String) : DeviceTrustResult
|
||||
fun checkDeviceTrust(userId: String, deviceId: String, locallyTrusted: Boolean?) : DeviceTrustResult
|
||||
}
|
||||
|
|
|
@ -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<CrossSigningKeyInfo> = ArrayList(),
|
||||
var crossSigningKeys: List<CryptoCrossSigningKey> = 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 }
|
||||
}
|
||||
|
|
|
@ -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<String, Map<String, String>>? = null
|
||||
//
|
||||
//) {
|
||||
// ) {
|
||||
//
|
||||
// data class Builder(
|
||||
// private val publicKeyBase64: String,
|
||||
|
@ -76,5 +76,5 @@
|
|||
// )
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
// }
|
||||
//
|
||||
|
|
|
@ -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<MXDeviceInfo> {
|
||||
val map = cryptoStore.getUserDevices(userId)
|
||||
return if (null != map) ArrayList(map.values) else ArrayList()
|
||||
override fun getUserDevices(userId: String): MutableList<CryptoDeviceInfo> {
|
||||
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<MXDeviceInfo>): MXUsersDevicesMap<MXDeviceInfo> {
|
||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
private fun getUnknownDevices(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXUsersDevicesMap<CryptoDeviceInfo> {
|
||||
val unknownDevices = MXUsersDevicesMap<CryptoDeviceInfo>()
|
||||
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<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>) {
|
||||
override fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>>) {
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
runCatching {
|
||||
deviceListManager.downloadKeys(userIds, forceDownload)
|
||||
|
|
|
@ -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<String>, failures: Map<String, Map<String, Any>>?): MXUsersDevicesMap<MXDeviceInfo> {
|
||||
private fun onKeysDownloadSucceed(userIds: List<String>, failures: Map<String, Map<String, Any>>?): MXUsersDevicesMap<CryptoDeviceInfo> {
|
||||
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<MXDeviceInfo>()
|
||||
val usersDevicesInfoMap = MXUsersDevicesMap<CryptoDeviceInfo>()
|
||||
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<String>?, forceDownload: Boolean): MXUsersDevicesMap<MXDeviceInfo> {
|
||||
suspend fun downloadKeys(userIds: List<String>?, forceDownload: Boolean): MXUsersDevicesMap<CryptoDeviceInfo> {
|
||||
Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds")
|
||||
// Map from userId -> deviceId -> MXDeviceInfo
|
||||
val stored = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
val stored = MXUsersDevicesMap<CryptoDeviceInfo>()
|
||||
|
||||
// List of user ids we need to download keys for
|
||||
val downloadUsers = ArrayList<String>()
|
||||
|
@ -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<String>): MXUsersDevicesMap<MXDeviceInfo> {
|
||||
private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList<String>): MXUsersDevicesMap<CryptoDeviceInfo> {
|
||||
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")
|
||||
|
|
|
@ -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<String, String>()
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -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<String, List<MXDeviceInfo>>): MXUsersDevicesMap<MXOlmSessionResult> {
|
||||
val devicesWithoutSession = ArrayList<MXDeviceInfo>()
|
||||
suspend fun handle(devicesByUser: Map<String, List<CryptoDeviceInfo>>): MXUsersDevicesMap<MXOlmSessionResult> {
|
||||
val devicesWithoutSession = ArrayList<CryptoDeviceInfo>()
|
||||
|
||||
val results = MXUsersDevicesMap<MXOlmSessionResult>()
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<String, Any>, deviceInfos: List<MXDeviceInfo>): EncryptedMessage {
|
||||
fun encryptMessage(payloadFields: Map<String, Any>, deviceInfos: List<CryptoDeviceInfo>): EncryptedMessage {
|
||||
val deviceInfoParticipantKey = deviceInfos.associateBy { it.identityKey()!! }
|
||||
|
||||
val payloadJson = payloadFields.toMutableMap()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MXDeviceInfo>): MXOutboundSessionInfo {
|
||||
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): 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<String, MutableList<MXDeviceInfo>>()/* userId */
|
||||
val shareMap = HashMap<String, MutableList<CryptoDeviceInfo>>()/* 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<String, List<MXDeviceInfo>>) {
|
||||
devicesByUsers: Map<String, List<CryptoDeviceInfo>>) {
|
||||
// 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<String, List<MXDeviceInfo>>()
|
||||
val subMap = HashMap<String, List<CryptoDeviceInfo>>()
|
||||
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<String, List<MXDeviceInfo>>) {
|
||||
devicesByUser: Map<String, List<CryptoDeviceInfo>>) {
|
||||
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<String>): MXUsersDevicesMap<MXDeviceInfo> {
|
||||
private suspend fun getDevicesInRoom(userIds: List<String>): MXUsersDevicesMap<CryptoDeviceInfo> {
|
||||
// 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<MXDeviceInfo>()
|
||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
val devicesInRoom = MXUsersDevicesMap<CryptoDeviceInfo>()
|
||||
val unknownDevices = MXUsersDevicesMap<CryptoDeviceInfo>()
|
||||
|
||||
for (userId in keys.userIds) {
|
||||
val deviceIds = keys.getUserDeviceIds(userId) ?: continue
|
||||
|
|
|
@ -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<MXDeviceInfo>): Boolean {
|
||||
fun sharedWithTooManyDevices(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): Boolean {
|
||||
val userIds = sharedWithDevices.userIds
|
||||
|
||||
for (userId in userIds) {
|
||||
|
|
|
@ -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<MXDeviceInfo>()
|
||||
val deviceInfos = ArrayList<CryptoDeviceInfo>()
|
||||
for (userId in userIds) {
|
||||
val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()
|
||||
for (device in devices) {
|
||||
|
|
|
@ -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<KeysQueryResponse>
|
||||
|
||||
|
||||
/**
|
||||
* 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<KeysQueryResponse>
|
||||
|
||||
|
||||
/**
|
||||
* 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<String, @JvmSuppressWildcards Any>?): Call<SignatureUploadResponse>
|
||||
|
||||
|
||||
/**
|
||||
* Claim one-time keys.
|
||||
* Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-keys-claim
|
||||
|
|
|
@ -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<Boolean>?) {
|
||||
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<SignatureUploadResponse>) {
|
||||
//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<SignatureUploadResponse>) {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<String>?,
|
||||
|
||||
override val keys: Map<String, String>,
|
||||
|
||||
override val signatures: Map<String, Map<String, String>>?,
|
||||
|
||||
var trustLevel: DeviceTrustLevel? = null
|
||||
) : CryptoInfo {
|
||||
|
||||
override fun signalableJSONDictionary(): Map<String, Any> {
|
||||
val map = HashMap<String, Any>()
|
||||
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<String, String>().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<Triple<String, String, String>> = 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<String, HashMap<String, String>>()
|
||||
signatures.forEach { info ->
|
||||
val uMap = signMap[info.first]
|
||||
?: HashMap<String, String>().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)
|
||||
}
|
|
@ -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<String>? = null,
|
||||
override val keys: Map<String, String>? = null,
|
||||
override val signatures: Map<String, Map<String, String>>? = 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<String, Any> {
|
||||
val map = HashMap<String, Any>()
|
||||
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)
|
||||
}
|
|
@ -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<String, Map<String, String>>?
|
||||
|
||||
// fun signalableJSONDictionary(): Map<String, Any>
|
||||
fun signalableJSONDictionary(): Map<String, Any>
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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<String, String>().also { updated[userId] = it }
|
||||
userMap["ed25519:${signedWithNoPrefix}"] = signature
|
||||
signatures = updated
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a dictionary of the parameters
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<String>?,
|
||||
|
||||
/**
|
||||
* 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<String, String>?,
|
||||
|
||||
/**
|
||||
* 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<String, Map<String, String>>? = 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<String, Any> {
|
||||
val map = HashMap<String, Any>()
|
||||
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<String, String>().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<Triple<String, String, String>> = 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<String, HashMap<String, String>>()
|
||||
signatures.forEach { info ->
|
||||
val uMap = signMap[info.first]
|
||||
?: HashMap<String, String>().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")
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String>,
|
||||
val algorithms: List<String>?,
|
||||
|
||||
@Json(name = "keys")
|
||||
override val keys: Map<String, String>,
|
||||
val keys: Map<String, String>?,
|
||||
|
||||
@Json(name = "signatures")
|
||||
override val signatures: Map<String, Map<String, String>>?
|
||||
) : MXKeysObject
|
||||
val signatures: Map<String, Map<String, String>>?,
|
||||
|
||||
@Json(name = "usage")
|
||||
val usage: List<String>? = null
|
||||
|
||||
)
|
||||
|
|
|
@ -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<String, Map<String, MXDeviceInfo>>? = null,
|
||||
var deviceKeys: Map<String, Map<String, RestDeviceInfo>>? = null,
|
||||
|
||||
/**
|
||||
* The failures sorted by homeservers. TODO Bad comment ?
|
||||
|
@ -45,13 +44,12 @@ data class KeysQueryResponse(
|
|||
var failures: Map<String, Map<String, Any>>? = null,
|
||||
|
||||
@Json(name = "master_keys")
|
||||
var masterKeys: Map<String, CrossSigningKeyInfo?>? = null,
|
||||
var masterKeys: Map<String, RestKeyInfo?>? = null,
|
||||
|
||||
@Json(name = "self_signing_keys")
|
||||
var selfSigningKeys: Map<String, CrossSigningKeyInfo?>? = null,
|
||||
var selfSigningKeys: Map<String, RestKeyInfo?>? = null,
|
||||
|
||||
@Json(name = "user_signing_keys")
|
||||
var userSigningKeys: Map<String, CrossSigningKeyInfo?>? = null
|
||||
var userSigningKeys: Map<String, RestKeyInfo?>? = null
|
||||
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<String>? = null,
|
||||
|
||||
/**
|
||||
* A map from "<key type>:<deviceId>" to "<base64-encoded key>".
|
||||
*/
|
||||
@Json(name = "keys")
|
||||
var keys: Map<String, String>? = null,
|
||||
|
||||
/**
|
||||
* The signature of this MXDeviceInfo.
|
||||
* A map from "<userId>" to a map from "<key type>:<deviceId>" to "<signature>"
|
||||
*/
|
||||
@Json(name = "signatures")
|
||||
var signatures: Map<String, Map<String, String>>? = null,
|
||||
|
||||
/*
|
||||
* Additional data from the home server.
|
||||
*/
|
||||
@Json(name = "unsigned")
|
||||
var unsigned: JsonDict? = null)
|
|
@ -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<String>?,
|
||||
|
||||
/**
|
||||
* 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<String, String>?,
|
||||
|
||||
/**
|
||||
* 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<String, Map<String, String>>? = null
|
||||
) {
|
||||
fun toCryptoModel(): CryptoCrossSigningKey {
|
||||
return CryptoInfoMapper.map(this)
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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<MXDeviceInfo> = ArrayList(),
|
||||
private val signingKeyInfoList: ArrayList<CrossSigningKeyInfo> = ArrayList()
|
||||
private val deviceInfoList: ArrayList<CryptoDeviceInfo> = ArrayList(),
|
||||
private val signingKeyInfoList: ArrayList<CryptoCrossSigningKey> = 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<String, Map<String, MXKeysObject>> {
|
||||
val map = HashMap<String, HashMap<String, MXKeysObject>>()
|
||||
fun build(): Map<String, Map<String, @JvmSuppressWildcards Any>> {
|
||||
val map = HashMap<String, HashMap<String, Any>>()
|
||||
|
||||
val usersList = (deviceInfoList.map { it.userId } + signingKeyInfoList.mapNotNull { it.userId }).distinct()
|
||||
|
||||
usersList.forEach { userID ->
|
||||
val userMap = HashMap<String, MXKeysObject>()
|
||||
val userMap = HashMap<String, Any>()
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<String>,
|
||||
|
||||
@Json(name = "keys")
|
||||
override val keys: Map<String, String>,
|
||||
|
||||
@Json(name = "signatures")
|
||||
override val signatures: Map<String, Map<String, String>>?
|
||||
) : MXKeysObject
|
|
@ -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<String, MXDeviceInfo>?)
|
||||
fun storeUserDevices(userId: String, devices: Map<String, CryptoDeviceInfo>?)
|
||||
|
||||
|
||||
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<String, MXDeviceInfo>?
|
||||
fun getUserDevices(userId: String): Map<String, CryptoDeviceInfo>?
|
||||
|
||||
/**
|
||||
* 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?
|
||||
|
||||
|
|
|
@ -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<DeviceInfoEntity>()
|
||||
.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<DeviceInfoEntity>()
|
||||
.equalTo(DeviceInfoEntityFields.IDENTITY_KEY, identityKey)
|
||||
.findFirst()
|
||||
}
|
||||
?.getDeviceInfo()
|
||||
?.let {
|
||||
CryptoMapper.mapToModel(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun storeUserDevices(userId: String, devices: Map<String, MXDeviceInfo>?) {
|
||||
override fun storeUserDevices(userId: String, devices: Map<String, CryptoDeviceInfo>?) {
|
||||
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<CryptoMetadataEntity>().findFirst()?.apply {
|
||||
xSignMasterPrivateKey = msk
|
||||
xSignSelfSignedPrivateKey = ssk
|
||||
|
@ -313,15 +332,14 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getUserDevices(userId: String): Map<String, MXDeviceInfo>? {
|
||||
override fun getUserDevices(userId: String): Map<String, CryptoDeviceInfo>? {
|
||||
return doRealmQueryAndCopy(realmConfiguration) {
|
||||
it.where<UserEntity>()
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<List<String>>(Types.newParameterizedType(
|
||||
List::class.java,
|
||||
String::class.java,
|
||||
Any::class.java
|
||||
))
|
||||
val mapMigrationAdapter = moshi.adapter<JsonDict>(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<MXDeviceInfo>(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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import io.realm.annotations.RealmModule
|
|||
OutgoingRoomKeyRequestEntity::class,
|
||||
UserEntity::class,
|
||||
KeyInfoEntity::class,
|
||||
CrossSigningInfoEntity::class
|
||||
CrossSigningInfoEntity::class,
|
||||
TrustLevelEntity::class
|
||||
])
|
||||
internal class RealmCryptoStoreModule
|
||||
|
|
|
@ -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<KeyInfoEntity> = 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) }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<List<String>>(Types.newParameterizedType(
|
||||
List::class.java,
|
||||
String::class.java,
|
||||
Any::class.java
|
||||
))
|
||||
private val mapMigrationAdapter = moshi.adapter<JsonDict>(Types.newParameterizedType(
|
||||
Map::class.java,
|
||||
String::class.java,
|
||||
Any::class.java
|
||||
))
|
||||
private val mapOfStringMigrationAdapter = moshi.adapter<Map<String, Map<String, String>>>(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<Map<String, String>>(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
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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<UserEntity>? = null
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -34,4 +34,3 @@ internal fun CrossSigningInfoEntity.Companion.get(realm: Realm, userId: String):
|
|||
.equalTo(UserEntityFields.USER_ID, userId)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
|
|
|
@ -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<UploadKeysTask.Params, KeysUploadResponse> {
|
||||
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).
|
||||
|
|
|
@ -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<UploadSignaturesTask.Params, SignatureUploadResponse> {
|
||||
data class Params(
|
||||
val signatures: Map<String, Map<String, MXKeysObject>>
|
||||
val signatures: Map<String, Map<String, Any>>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<UploadSigningKeysTask.Params, KeysQueryResponse> {
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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<MXDeviceInfo>? {
|
||||
fromDevice: String): MXUsersDevicesMap<CryptoDeviceInfo>? {
|
||||
return try {
|
||||
var keys = deviceListManager.downloadKeys(listOf(otherUserId), false)
|
||||
if (keys.getUserDeviceIds(otherUserId)?.contains(fromDevice) == true) {
|
||||
|
|
|
@ -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<SignatureUploadResponse> {
|
||||
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<SignatureUploadResponse> {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -77,7 +77,6 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
|||
}
|
||||
|
||||
crypto.downloadKeys(listOf("@testxsigningvfe:matrix.org"), true, object : MatrixCallback<Any> {
|
||||
|
||||
})
|
||||
|
||||
var error: Throwable? = null
|
||||
|
|
|
@ -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<Map<String, Any>>(Types.newParameterizedType(Map::class.java, List::class.java, String::class.java))
|
||||
|
||||
val map = adapter.fromJson(sample)
|
||||
print(map)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Unit> {
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
if (failure is Failure.OtherServerError
|
||||
&& failure.httpCode == 401
|
||||
|
|
|
@ -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<IncomingRoomKeyRequest>().apply { this.add(request) }
|
||||
|
||||
// Add a notification for every incoming request
|
||||
session?.downloadKeys(listOf(userId), false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
||||
session?.downloadKeys(listOf(userId), false, object : MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||
override fun onSuccess(data: MXUsersDevicesMap<CryptoDeviceInfo>) {
|
||||
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<DevicesListResponse> {
|
||||
|
@ -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?
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue