Merge pull request #1297 from vector-im/feature/xsigning_trust_optimization

Feature/xsigning trust optimization
This commit is contained in:
Benoit Marty 2020-04-29 10:32:29 +02:00 committed by GitHub
commit 67f07bd1bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 292 additions and 268 deletions

View File

@ -20,6 +20,8 @@ import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
import im.vector.matrix.android.internal.crypto.store.db.mapper.CrossSigningKeysMapper
import im.vector.matrix.android.internal.di.MoshiProvider
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlin.random.Random import kotlin.random.Random
@ -31,6 +33,7 @@ internal class CryptoStoreHelper {
.name("test.realm") .name("test.realm")
.modules(RealmCryptoStoreModule()) .modules(RealmCryptoStoreModule())
.build(), .build(),
crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()),
credentials = createCredential()) credentials = createCredential())
} }

View File

@ -49,7 +49,6 @@ data class RoomSummary constructor(
val inviterId: String? = null, val inviterId: String? = null,
val typingRoomMemberIds: List<String> = emptyList(), val typingRoomMemberIds: List<String> = emptyList(),
val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS, val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
// TODO Plug it
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null val roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
) { ) {

View File

@ -112,6 +112,7 @@ internal abstract class CryptoModule {
@SessionScope @SessionScope
fun providesRealmConfiguration(@SessionFilesDirectory directory: File, fun providesRealmConfiguration(@SessionFilesDirectory directory: File,
@UserMd5 userMd5: String, @UserMd5 userMd5: String,
realmCryptoStoreMigration: RealmCryptoStoreMigration,
realmKeysUtils: RealmKeysUtils): RealmConfiguration { realmKeysUtils: RealmKeysUtils): RealmConfiguration {
return RealmConfiguration.Builder() return RealmConfiguration.Builder()
.directory(directory) .directory(directory)
@ -121,7 +122,7 @@ internal abstract class CryptoModule {
.name("crypto_store.realm") .name("crypto_store.realm")
.modules(RealmCryptoStoreModule()) .modules(RealmCryptoStoreModule())
.schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION) .schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION)
.migration(RealmCryptoStoreMigration) .migration(realmCryptoStoreMigration)
.build() .build()
} }

View File

@ -15,18 +15,20 @@
*/ */
package im.vector.matrix.android.internal.crypto.crosssigning package im.vector.matrix.android.internal.crypto.crosssigning
import im.vector.matrix.android.api.extensions.orFalse
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.util.createBackgroundHandler import im.vector.matrix.android.internal.util.createBackgroundHandler
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import timber.log.Timber import timber.log.Timber
@ -38,13 +40,13 @@ internal class ShieldTrustUpdater @Inject constructor(
private val eventBus: EventBus, private val eventBus: EventBus,
private val computeTrustTask: ComputeTrustTask, private val computeTrustTask: ComputeTrustTask,
private val taskExecutor: TaskExecutor, private val taskExecutor: TaskExecutor,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
@SessionDatabase private val sessionRealmConfiguration: RealmConfiguration, @SessionDatabase private val sessionRealmConfiguration: RealmConfiguration,
private val roomSummaryUpdater: RoomSummaryUpdater private val roomSummaryUpdater: RoomSummaryUpdater
) { ) {
companion object { companion object {
private val BACKGROUND_HANDLER = createBackgroundHandler("SHIELD_CRYPTO_DB_THREAD") private val BACKGROUND_HANDLER = createBackgroundHandler("SHIELD_CRYPTO_DB_THREAD")
private val BACKGROUND_HANDLER_DISPATCHER = BACKGROUND_HANDLER.asCoroutineDispatcher()
} }
private val backgroundSessionRealm = AtomicReference<Realm>() private val backgroundSessionRealm = AtomicReference<Realm>()
@ -76,14 +78,11 @@ internal class ShieldTrustUpdater @Inject constructor(
if (!isStarted.get()) { if (!isStarted.get()) {
return return
} }
taskExecutor.executorScope.launch(coroutineDispatchers.crypto) { taskExecutor.executorScope.launch(BACKGROUND_HANDLER_DISPATCHER) {
val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(update.userIds)) val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(update.userIds))
// We need to send that back to session base // We need to send that back to session base
backgroundSessionRealm.get()?.executeTransaction { realm ->
BACKGROUND_HANDLER.post { roomSummaryUpdater.updateShieldTrust(realm, update.roomId, updatedTrust)
backgroundSessionRealm.get()?.executeTransaction { realm ->
roomSummaryUpdater.updateShieldTrust(realm, update.roomId, updatedTrust)
}
} }
} }
} }
@ -93,45 +92,30 @@ internal class ShieldTrustUpdater @Inject constructor(
if (!isStarted.get()) { if (!isStarted.get()) {
return return
} }
onCryptoDevicesChange(update.userIds) onCryptoDevicesChange(update.userIds)
} }
private fun onCryptoDevicesChange(users: List<String>) { private fun onCryptoDevicesChange(users: List<String>) {
BACKGROUND_HANDLER.post { taskExecutor.executorScope.launch(BACKGROUND_HANDLER_DISPATCHER) {
val impactedRoomsId = backgroundSessionRealm.get()?.where(RoomMemberSummaryEntity::class.java) val realm = backgroundSessionRealm.get() ?: return@launch
?.`in`(RoomMemberSummaryEntityFields.USER_ID, users.toTypedArray()) val distinctRoomIds = realm.where(RoomMemberSummaryEntity::class.java)
?.findAll() .`in`(RoomMemberSummaryEntityFields.USER_ID, users.toTypedArray())
?.map { it.roomId } .distinct(RoomMemberSummaryEntityFields.ROOM_ID)
?.distinct() .findAll()
.map { it.roomId }
val map = HashMap<String, List<String>>() distinctRoomIds.forEach { roomId ->
impactedRoomsId?.forEach { roomId -> val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
backgroundSessionRealm.get()?.let { realm -> if (roomSummary?.isEncrypted.orFalse()) {
RoomMemberSummaryEntity.where(realm, roomId) val allActiveRoomMembers = RoomMemberHelper(realm, roomId).getActiveRoomMemberIds()
.findAll() try {
.let { results -> // Can throw if the crypto database has been closed in between, in this case log and ignore?
map[roomId] = results.map { it.userId } val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(allActiveRoomMembers))
} realm.executeTransaction {
} roomSummaryUpdater.updateShieldTrust(it, roomId, updatedTrust)
}
map.forEach { entry ->
val roomId = entry.key
val userList = entry.value
taskExecutor.executorScope.launch {
withContext(coroutineDispatchers.crypto) {
try {
// Can throw if the crypto database has been closed in between, in this case log and ignore?
val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(userList))
BACKGROUND_HANDLER.post {
backgroundSessionRealm.get()?.executeTransaction { realm ->
roomSummaryUpdater.updateShieldTrust(realm, roomId, updatedTrust)
}
}
} catch (failure: Throwable) {
Timber.e(failure)
} }
} catch (failure: Throwable) {
Timber.e(failure)
} }
} }
} }

View File

@ -62,6 +62,7 @@ fun doRealmTransaction(realmConfiguration: RealmConfiguration, action: (Realm) -
realm.executeTransaction { action.invoke(it) } realm.executeTransaction { action.invoke(it) }
} }
} }
fun doRealmTransactionAsync(realmConfiguration: RealmConfiguration, action: (Realm) -> Unit) { fun doRealmTransactionAsync(realmConfiguration: RealmConfiguration, action: (Realm) -> Unit) {
Realm.getInstance(realmConfiguration).use { realm -> Realm.getInstance(realmConfiguration).use { realm ->
realm.executeTransactionAsync { action.invoke(it) } realm.executeTransactionAsync { action.invoke(it) }
@ -79,31 +80,26 @@ fun serializeForRealm(o: Any?): String? {
val baos = ByteArrayOutputStream() val baos = ByteArrayOutputStream()
val gzis = CompatUtil.createGzipOutputStream(baos) val gzis = CompatUtil.createGzipOutputStream(baos)
val out = ObjectOutputStream(gzis) val out = ObjectOutputStream(gzis)
out.use {
out.writeObject(o) it.writeObject(o)
out.close() }
return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT) return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)
} }
/** /**
* Do the opposite of serializeForRealm. * Do the opposite of serializeForRealm.
*/ */
@Suppress("UNCHECKED_CAST")
fun <T> deserializeFromRealm(string: String?): T? { fun <T> deserializeFromRealm(string: String?): T? {
if (string == null) { if (string == null) {
return null return null
} }
val decodedB64 = Base64.decode(string.toByteArray(), Base64.DEFAULT) val decodedB64 = Base64.decode(string.toByteArray(), Base64.DEFAULT)
val bais = ByteArrayInputStream(decodedB64) val bais = ByteArrayInputStream(decodedB64)
val gzis = GZIPInputStream(bais) val gzis = GZIPInputStream(bais)
val ois = ObjectInputStream(gzis) val ois = ObjectInputStream(gzis)
return ois.use {
@Suppress("UNCHECKED_CAST") it.readObject() as T
val result = ois.readObject() as T }
ois.close()
return result
} }

View File

@ -36,7 +36,6 @@ import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestState
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey 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.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
@ -46,6 +45,7 @@ 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.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo
import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo
import im.vector.matrix.android.internal.crypto.store.db.mapper.CrossSigningKeysMapper
import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntity 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.CrossSigningInfoEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMapper import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMapper
@ -91,6 +91,7 @@ import kotlin.collections.set
@SessionScope @SessionScope
internal class RealmCryptoStore @Inject constructor( internal class RealmCryptoStore @Inject constructor(
@CryptoDatabase private val realmConfiguration: RealmConfiguration, @CryptoDatabase private val realmConfiguration: RealmConfiguration,
private val crossSigningKeysMapper: CrossSigningKeysMapper,
private val credentials: Credentials) : IMXCryptoStore { private val credentials: Credentials) : IMXCryptoStore {
/* ========================================================================================== /* ==========================================================================================
@ -200,9 +201,9 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getDeviceId(): String { override fun getDeviceId(): String {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<CryptoMetadataEntity>().findFirst() it.where<CryptoMetadataEntity>().findFirst()?.deviceId
}?.deviceId ?: "" } ?: ""
} }
override fun saveOlmAccount() { override fun saveOlmAccount() {
@ -256,24 +257,25 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getUserDevice(userId: String, deviceId: String): CryptoDeviceInfo? { override fun getUserDevice(userId: String, deviceId: String): CryptoDeviceInfo? {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<DeviceInfoEntity>() it.where<DeviceInfoEntity>()
.equalTo(DeviceInfoEntityFields.PRIMARY_KEY, DeviceInfoEntity.createPrimaryKey(userId, deviceId)) .equalTo(DeviceInfoEntityFields.PRIMARY_KEY, DeviceInfoEntity.createPrimaryKey(userId, deviceId))
.findFirst() .findFirst()
}?.let { ?.let { deviceInfo ->
CryptoMapper.mapToModel(it) CryptoMapper.mapToModel(deviceInfo)
}
} }
} }
override fun deviceWithIdentityKey(identityKey: String): CryptoDeviceInfo? { override fun deviceWithIdentityKey(identityKey: String): CryptoDeviceInfo? {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<DeviceInfoEntity>() it.where<DeviceInfoEntity>()
.equalTo(DeviceInfoEntityFields.IDENTITY_KEY, identityKey) .equalTo(DeviceInfoEntityFields.IDENTITY_KEY, identityKey)
.findFirst() .findFirst()
?.let { deviceInfo ->
CryptoMapper.mapToModel(deviceInfo)
}
} }
?.let {
CryptoMapper.mapToModel(it)
}
} }
override fun storeUserDevices(userId: String, devices: Map<String, CryptoDeviceInfo>?) { override fun storeUserDevices(userId: String, devices: Map<String, CryptoDeviceInfo>?) {
@ -309,36 +311,19 @@ internal class RealmCryptoStore @Inject constructor(
} else { } else {
CrossSigningInfoEntity.getOrCreate(realm, userId).let { signingInfo -> CrossSigningInfoEntity.getOrCreate(realm, userId).let { signingInfo ->
// What should we do if we detect a change of the keys? // What should we do if we detect a change of the keys?
val existingMaster = signingInfo.getMasterKey() val existingMaster = signingInfo.getMasterKey()
if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) { if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) {
// update signatures? crossSigningKeysMapper.update(existingMaster, masterKey)
existingMaster.putSignatures(masterKey.signatures)
existingMaster.usages = masterKey.usages?.toTypedArray()?.let { RealmList(*it) }
?: RealmList()
} else { } else {
val keyEntity = realm.createObject(KeyInfoEntity::class.java).apply { val keyEntity = crossSigningKeysMapper.map(masterKey)
this.publicKeyBase64 = masterKey.unpaddedBase64PublicKey
this.usages = masterKey.usages?.toTypedArray()?.let { RealmList(*it) }
?: RealmList()
this.putSignatures(masterKey.signatures)
}
signingInfo.setMasterKey(keyEntity) signingInfo.setMasterKey(keyEntity)
} }
val existingSelfSigned = signingInfo.getSelfSignedKey() val existingSelfSigned = signingInfo.getSelfSignedKey()
if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) { if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) {
// update signatures? crossSigningKeysMapper.update(existingSelfSigned, selfSigningKey)
existingSelfSigned.putSignatures(selfSigningKey.signatures)
existingSelfSigned.usages = selfSigningKey.usages?.toTypedArray()?.let { RealmList(*it) }
?: RealmList()
} else { } else {
val keyEntity = realm.createObject(KeyInfoEntity::class.java).apply { val keyEntity = crossSigningKeysMapper.map(selfSigningKey)
this.publicKeyBase64 = selfSigningKey.unpaddedBase64PublicKey
this.usages = selfSigningKey.usages?.toTypedArray()?.let { RealmList(*it) }
?: RealmList()
this.putSignatures(selfSigningKey.signatures)
}
signingInfo.setSelfSignedKey(keyEntity) signingInfo.setSelfSignedKey(keyEntity)
} }
@ -346,21 +331,12 @@ internal class RealmCryptoStore @Inject constructor(
if (userSigningKey != null) { if (userSigningKey != null) {
val existingUSK = signingInfo.getUserSigningKey() val existingUSK = signingInfo.getUserSigningKey()
if (existingUSK != null && existingUSK.publicKeyBase64 == userSigningKey.unpaddedBase64PublicKey) { if (existingUSK != null && existingUSK.publicKeyBase64 == userSigningKey.unpaddedBase64PublicKey) {
// update signatures? crossSigningKeysMapper.update(existingUSK, userSigningKey)
existingUSK.putSignatures(userSigningKey.signatures)
existingUSK.usages = userSigningKey.usages?.toTypedArray()?.let { RealmList(*it) }
?: RealmList()
} else { } else {
val keyEntity = realm.createObject(KeyInfoEntity::class.java).apply { val keyEntity = crossSigningKeysMapper.map(userSigningKey)
this.publicKeyBase64 = userSigningKey.unpaddedBase64PublicKey
this.usages = userSigningKey.usages?.toTypedArray()?.let { RealmList(*it) }
?: RealmList()
this.putSignatures(userSigningKey.signatures)
}
signingInfo.setUserSignedKey(keyEntity) signingInfo.setUserSignedKey(keyEntity)
} }
} }
userEntity.crossSigningInfoEntity = signingInfo userEntity.crossSigningInfoEntity = signingInfo
} }
} }
@ -369,14 +345,16 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getCrossSigningPrivateKeys(): PrivateKeysInfo? { override fun getCrossSigningPrivateKeys(): PrivateKeysInfo? {
return doRealmQueryAndCopy(realmConfiguration) { realm -> return doWithRealm(realmConfiguration) { realm ->
realm.where<CryptoMetadataEntity>().findFirst() realm.where<CryptoMetadataEntity>()
}?.let { .findFirst()
PrivateKeysInfo( ?.let {
master = it.xSignMasterPrivateKey, PrivateKeysInfo(
selfSigned = it.xSignSelfSignedPrivateKey, master = it.xSignMasterPrivateKey,
user = it.xSignUserPrivateKey selfSigned = it.xSignSelfSignedPrivateKey,
) user = it.xSignUserPrivateKey
)
}
} }
} }
@ -400,16 +378,18 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? { override fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? {
return doRealmQueryAndCopy(realmConfiguration) { realm -> return doWithRealm(realmConfiguration) { realm ->
realm.where<CryptoMetadataEntity>().findFirst() realm.where<CryptoMetadataEntity>()
}?.let { .findFirst()
val key = it.keyBackupRecoveryKey ?.let {
val version = it.keyBackupRecoveryKeyVersion val key = it.keyBackupRecoveryKey
if (!key.isNullOrBlank() && !version.isNullOrBlank()) { val version = it.keyBackupRecoveryKeyVersion
SavedKeyBackupKeyInfo(recoveryKey = key, version = version) if (!key.isNullOrBlank() && !version.isNullOrBlank()) {
} else { SavedKeyBackupKeyInfo(recoveryKey = key, version = version)
null } else {
} null
}
}
} }
} }
@ -430,24 +410,30 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getUserDevices(userId: String): Map<String, CryptoDeviceInfo>? { override fun getUserDevices(userId: String): Map<String, CryptoDeviceInfo>? {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<UserEntity>() it.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, userId) .equalTo(UserEntityFields.USER_ID, userId)
.findFirst() .findFirst()
?.devices
?.map { deviceInfo ->
CryptoMapper.mapToModel(deviceInfo)
}
?.associateBy { cryptoDevice ->
cryptoDevice.deviceId
}
} }
?.devices
?.map { CryptoMapper.mapToModel(it) }
?.associateBy { it.deviceId }
} }
override fun getUserDeviceList(userId: String): List<CryptoDeviceInfo>? { override fun getUserDeviceList(userId: String): List<CryptoDeviceInfo>? {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<UserEntity>() it.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, userId) .equalTo(UserEntityFields.USER_ID, userId)
.findFirst() .findFirst()
?.devices
?.map { deviceInfo ->
CryptoMapper.mapToModel(deviceInfo)
}
} }
?.devices
?.map { CryptoMapper.mapToModel(it) }
} }
override fun getLiveDeviceList(userId: String): LiveData<List<CryptoDeviceInfo>> { override fun getLiveDeviceList(userId: String): LiveData<List<CryptoDeviceInfo>> {
@ -503,17 +489,16 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getRoomAlgorithm(roomId: String): String? { override fun getRoomAlgorithm(roomId: String): String? {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
CryptoRoomEntity.getById(it, roomId) CryptoRoomEntity.getById(it, roomId)?.algorithm
} }
?.algorithm
} }
override fun shouldEncryptForInvitedMembers(roomId: String): Boolean { override fun shouldEncryptForInvitedMembers(roomId: String): Boolean {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
CryptoRoomEntity.getById(it, roomId) CryptoRoomEntity.getById(it, roomId)?.shouldEncryptForInvitedMembers
} }
?.shouldEncryptForInvitedMembers ?: false ?: false
} }
override fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean) { override fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean) {
@ -577,24 +562,24 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getLastUsedSessionId(deviceKey: String): String? { override fun getLastUsedSessionId(deviceKey: String): String? {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<OlmSessionEntity>() it.where<OlmSessionEntity>()
.equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey) .equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey)
.sort(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, Sort.DESCENDING) .sort(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, Sort.DESCENDING)
.findFirst() .findFirst()
?.sessionId
} }
?.sessionId
} }
override fun getDeviceSessionIds(deviceKey: String): MutableSet<String> { override fun getDeviceSessionIds(deviceKey: String): MutableSet<String> {
return doRealmQueryAndCopyList(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<OlmSessionEntity>() it.where<OlmSessionEntity>()
.equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey) .equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey)
.findAll() .findAll()
.mapNotNull { sessionEntity ->
sessionEntity.sessionId
}
} }
.mapNotNull {
it.sessionId
}
.toMutableSet() .toMutableSet()
} }
@ -641,12 +626,12 @@ internal class RealmCryptoStore @Inject constructor(
// If not in cache (or not found), try to read it from realm // If not in cache (or not found), try to read it from realm
if (inboundGroupSessionToRelease[key] == null) { if (inboundGroupSessionToRelease[key] == null) {
doRealmQueryAndCopy(realmConfiguration) { doWithRealm(realmConfiguration) {
it.where<OlmInboundGroupSessionEntity>() it.where<OlmInboundGroupSessionEntity>()
.equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key) .equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key)
.findFirst() .findFirst()
?.getInboundGroupSession()
} }
?.getInboundGroupSession()
?.let { ?.let {
inboundGroupSessionToRelease[key] = it inboundGroupSessionToRelease[key] = it
} }
@ -660,13 +645,13 @@ internal class RealmCryptoStore @Inject constructor(
* so there is no need to use or update `inboundGroupSessionToRelease` for native memory management * so there is no need to use or update `inboundGroupSessionToRelease` for native memory management
*/ */
override fun getInboundGroupSessions(): MutableList<OlmInboundGroupSessionWrapper> { override fun getInboundGroupSessions(): MutableList<OlmInboundGroupSessionWrapper> {
return doRealmQueryAndCopyList(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<OlmInboundGroupSessionEntity>() it.where<OlmInboundGroupSessionEntity>()
.findAll() .findAll()
.mapNotNull { inboundGroupSessionEntity ->
inboundGroupSessionEntity.getInboundGroupSession()
}
} }
.mapNotNull {
it.getInboundGroupSession()
}
.toMutableList() .toMutableList()
} }
@ -755,13 +740,14 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun inboundGroupSessionsToBackup(limit: Int): List<OlmInboundGroupSessionWrapper> { override fun inboundGroupSessionsToBackup(limit: Int): List<OlmInboundGroupSessionWrapper> {
return doRealmQueryAndCopyList(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<OlmInboundGroupSessionEntity>() it.where<OlmInboundGroupSessionEntity>()
.equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, false) .equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, false)
.limit(limit.toLong()) .limit(limit.toLong())
.findAll() .findAll()
}.mapNotNull { inboundGroupSession -> .mapNotNull { inboundGroupSession ->
inboundGroupSession.getInboundGroupSession() inboundGroupSession.getInboundGroupSession()
}
} }
} }
@ -785,10 +771,9 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getGlobalBlacklistUnverifiedDevices(): Boolean { override fun getGlobalBlacklistUnverifiedDevices(): Boolean {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<CryptoMetadataEntity>().findFirst() it.where<CryptoMetadataEntity>().findFirst()?.globalBlacklistUnverifiedDevices
}?.globalBlacklistUnverifiedDevices } ?: false
?: false
} }
override fun setRoomsListBlacklistUnverifiedDevices(roomIds: List<String>) { override fun setRoomsListBlacklistUnverifiedDevices(roomIds: List<String>) {
@ -811,28 +796,28 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getRoomsListBlacklistUnverifiedDevices(): MutableList<String> { override fun getRoomsListBlacklistUnverifiedDevices(): MutableList<String> {
return doRealmQueryAndCopyList(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<CryptoRoomEntity>() it.where<CryptoRoomEntity>()
.equalTo(CryptoRoomEntityFields.BLACKLIST_UNVERIFIED_DEVICES, true) .equalTo(CryptoRoomEntityFields.BLACKLIST_UNVERIFIED_DEVICES, true)
.findAll() .findAll()
.mapNotNull { cryptoRoom ->
cryptoRoom.roomId
}
} }
.mapNotNull {
it.roomId
}
.toMutableList() .toMutableList()
} }
override fun getDeviceTrackingStatuses(): MutableMap<String, Int> { override fun getDeviceTrackingStatuses(): MutableMap<String, Int> {
return doRealmQueryAndCopyList(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<UserEntity>() it.where<UserEntity>()
.findAll() .findAll()
.associateBy { user ->
user.userId!!
}
.mapValues { entry ->
entry.value.deviceTrackingStatus
}
} }
.associateBy {
it.userId!!
}
.mapValues {
it.value.deviceTrackingStatus
}
.toMutableMap() .toMutableMap()
} }
@ -847,12 +832,12 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getDeviceTrackingStatus(userId: String, defaultValue: Int): Int { override fun getDeviceTrackingStatus(userId: String, defaultValue: Int): Int {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<UserEntity>() it.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, userId) .equalTo(UserEntityFields.USER_ID, userId)
.findFirst() .findFirst()
?.deviceTrackingStatus
} }
?.deviceTrackingStatus
?: defaultValue ?: defaultValue
} }
@ -1089,63 +1074,65 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getIncomingRoomKeyRequest(userId: String, deviceId: String, requestId: String): IncomingRoomKeyRequest? { override fun getIncomingRoomKeyRequest(userId: String, deviceId: String, requestId: String): IncomingRoomKeyRequest? {
return doRealmQueryAndCopyList(realmConfiguration) { realm -> return doWithRealm(realmConfiguration) { realm ->
realm.where<IncomingGossipingRequestEntity>() realm.where<IncomingGossipingRequestEntity>()
.equalTo(IncomingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name) .equalTo(IncomingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
.equalTo(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, deviceId) .equalTo(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, deviceId)
.equalTo(IncomingGossipingRequestEntityFields.OTHER_USER_ID, userId) .equalTo(IncomingGossipingRequestEntityFields.OTHER_USER_ID, userId)
.findAll() .findAll()
}.mapNotNull { entity -> .mapNotNull { entity ->
entity.toIncomingGossipingRequest() as? IncomingRoomKeyRequest entity.toIncomingGossipingRequest() as? IncomingRoomKeyRequest
}.firstOrNull() }
.firstOrNull()
}
} }
override fun getPendingIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest> { override fun getPendingIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest> {
return doRealmQueryAndCopyList(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<IncomingGossipingRequestEntity>() it.where<IncomingGossipingRequestEntity>()
.equalTo(IncomingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name) .equalTo(IncomingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
.equalTo(IncomingGossipingRequestEntityFields.REQUEST_STATE_STR, GossipingRequestState.PENDING.name) .equalTo(IncomingGossipingRequestEntityFields.REQUEST_STATE_STR, GossipingRequestState.PENDING.name)
.findAll() .findAll()
.map { entity ->
IncomingRoomKeyRequest(
userId = entity.otherUserId,
deviceId = entity.otherDeviceId,
requestId = entity.requestId,
requestBody = entity.getRequestedKeyInfo(),
localCreationTimestamp = entity.localCreationTimestamp
)
}
} }
.map { entity ->
IncomingRoomKeyRequest(
userId = entity.otherUserId,
deviceId = entity.otherDeviceId,
requestId = entity.requestId,
requestBody = entity.getRequestedKeyInfo(),
localCreationTimestamp = entity.localCreationTimestamp
)
}
} }
override fun getPendingIncomingGossipingRequests(): List<IncomingShareRequestCommon> { override fun getPendingIncomingGossipingRequests(): List<IncomingShareRequestCommon> {
return doRealmQueryAndCopyList(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<IncomingGossipingRequestEntity>() it.where<IncomingGossipingRequestEntity>()
.equalTo(IncomingGossipingRequestEntityFields.REQUEST_STATE_STR, GossipingRequestState.PENDING.name) .equalTo(IncomingGossipingRequestEntityFields.REQUEST_STATE_STR, GossipingRequestState.PENDING.name)
.findAll() .findAll()
} .mapNotNull { entity ->
.mapNotNull { entity -> when (entity.type) {
when (entity.type) { GossipRequestType.KEY -> {
GossipRequestType.KEY -> { IncomingRoomKeyRequest(
IncomingRoomKeyRequest( userId = entity.otherUserId,
userId = entity.otherUserId, deviceId = entity.otherDeviceId,
deviceId = entity.otherDeviceId, requestId = entity.requestId,
requestId = entity.requestId, requestBody = entity.getRequestedKeyInfo(),
requestBody = entity.getRequestedKeyInfo(), localCreationTimestamp = entity.localCreationTimestamp
localCreationTimestamp = entity.localCreationTimestamp )
) }
} GossipRequestType.SECRET -> {
GossipRequestType.SECRET -> { IncomingSecretShareRequest(
IncomingSecretShareRequest( userId = entity.otherUserId,
userId = entity.otherUserId, deviceId = entity.otherDeviceId,
deviceId = entity.otherDeviceId, requestId = entity.requestId,
requestId = entity.requestId, secretName = entity.getRequestedSecretName(),
secretName = entity.getRequestedSecretName(), localCreationTimestamp = entity.localCreationTimestamp
localCreationTimestamp = entity.localCreationTimestamp )
) }
} }
} }
} }
} }
override fun storeIncomingGossipingRequest(request: IncomingShareRequestCommon, ageLocalTS: Long?) { override fun storeIncomingGossipingRequest(request: IncomingShareRequestCommon, ageLocalTS: Long?) {
@ -1183,9 +1170,9 @@ internal class RealmCryptoStore @Inject constructor(
* Cross Signing * Cross Signing
* ========================================================================================== */ * ========================================================================================== */
override fun getMyCrossSigningInfo(): MXCrossSigningInfo? { override fun getMyCrossSigningInfo(): MXCrossSigningInfo? {
return doRealmQueryAndCopy(realmConfiguration) { return doWithRealm(realmConfiguration) {
it.where<CryptoMetadataEntity>().findFirst() it.where<CryptoMetadataEntity>().findFirst()?.userId
}?.userId?.let { }?.let {
getCrossSigningInfo(it) getCrossSigningInfo(it)
} }
} }
@ -1304,33 +1291,24 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun getCrossSigningInfo(userId: String): MXCrossSigningInfo? { override fun getCrossSigningInfo(userId: String): MXCrossSigningInfo? {
return doRealmQueryAndCopy(realmConfiguration) { realm -> return doWithRealm(realmConfiguration) { realm ->
realm.where(CrossSigningInfoEntity::class.java) val crossSigningInfo = realm.where(CrossSigningInfoEntity::class.java)
.equalTo(CrossSigningInfoEntityFields.USER_ID, userId) .equalTo(CrossSigningInfoEntityFields.USER_ID, userId)
.findFirst() .findFirst()
}?.let { xsignInfo -> if (crossSigningInfo == null) {
mapCrossSigningInfoEntity(xsignInfo) null
} else {
mapCrossSigningInfoEntity(crossSigningInfo)
}
} }
} }
private fun mapCrossSigningInfoEntity(xsignInfo: CrossSigningInfoEntity): MXCrossSigningInfo { private fun mapCrossSigningInfoEntity(xsignInfo: CrossSigningInfoEntity): MXCrossSigningInfo {
val userId = xsignInfo.userId ?: ""
return MXCrossSigningInfo( return MXCrossSigningInfo(
userId = xsignInfo.userId ?: "", userId = userId,
crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull { crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull {
val pubKey = it.publicKeyBase64 ?: return@mapNotNull null crossSigningKeysMapper.map(userId, it)
CryptoCrossSigningKey(
userId = xsignInfo.userId ?: "",
keys = mapOf("ed25519:$pubKey" to pubKey),
usages = it.usages.map { it },
signatures = it.getSignatures(),
trustLevel = it.trustLevelEntity?.let {
DeviceTrustLevel(
crossSigningVerified = it.crossSignedVerified ?: false,
locallyVerified = it.locallyVerified ?: false
)
}
)
} }
) )
} }
@ -1341,26 +1319,7 @@ internal class RealmCryptoStore @Inject constructor(
realm.where<CrossSigningInfoEntity>() realm.where<CrossSigningInfoEntity>()
.equalTo(UserEntityFields.USER_ID, userId) .equalTo(UserEntityFields.USER_ID, userId)
}, },
{ entity -> { mapCrossSigningInfoEntity(it) }
MXCrossSigningInfo(
userId = userId,
crossSigningKeys = entity.crossSigningKeys.mapNotNull {
val pubKey = it.publicKeyBase64 ?: return@mapNotNull null
CryptoCrossSigningKey(
userId = userId,
keys = mapOf("ed25519:$pubKey" to pubKey),
usages = it.usages.map { it },
signatures = it.getSignatures(),
trustLevel = it.trustLevelEntity?.let {
DeviceTrustLevel(
crossSigningVerified = it.crossSignedVerified ?: false,
locallyVerified = it.locallyVerified ?: false
)
}
)
}
)
}
) )
return Transformations.map(liveData) { return Transformations.map(liveData) {
it.firstOrNull().toOptional() it.firstOrNull().toOptional()
@ -1402,17 +1361,8 @@ internal class RealmCryptoStore @Inject constructor(
// existing.crossSigningKeys.forEach { it.deleteFromRealm() } // existing.crossSigningKeys.forEach { it.deleteFromRealm() }
val xkeys = RealmList<KeyInfoEntity>() val xkeys = RealmList<KeyInfoEntity>()
info.crossSigningKeys.forEach { cryptoCrossSigningKey -> info.crossSigningKeys.forEach { cryptoCrossSigningKey ->
xkeys.add( val keyEntity = crossSigningKeysMapper.map(cryptoCrossSigningKey)
realm.createObject(KeyInfoEntity::class.java).also { keyInfoEntity -> xkeys.add(keyEntity)
keyInfoEntity.publicKeyBase64 = cryptoCrossSigningKey.unpaddedBase64PublicKey
keyInfoEntity.usages = cryptoCrossSigningKey.usages?.let { RealmList(*it.toTypedArray()) }
?: RealmList()
keyInfoEntity.putSignatures(cryptoCrossSigningKey.signatures)
// TODO how to handle better, check if same keys?
// reset trust
keyInfoEntity.trustLevelEntity = null
}
)
} }
existing.crossSigningKeys = xkeys existing.crossSigningKeys = xkeys
} }

View File

@ -20,6 +20,7 @@ import com.squareup.moshi.Moshi
import com.squareup.moshi.Types import com.squareup.moshi.Types
import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.store.db.mapper.CrossSigningKeysMapper
import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntityFields 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.CryptoMetadataEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields
@ -33,11 +34,14 @@ import im.vector.matrix.android.internal.di.SerializeNulls
import io.realm.DynamicRealm import io.realm.DynamicRealm
import io.realm.RealmMigration import io.realm.RealmMigration
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject
internal object RealmCryptoStoreMigration : RealmMigration { internal class RealmCryptoStoreMigration @Inject constructor(private val crossSigningKeysMapper: CrossSigningKeysMapper) : RealmMigration {
// Version 1L added Cross Signing info persistence // Version 1L added Cross Signing info persistence
const val CRYPTO_STORE_SCHEMA_VERSION = 3L companion object {
const val CRYPTO_STORE_SCHEMA_VERSION = 4L
}
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
Timber.v("Migrating Realm Crypto from $oldVersion to $newVersion") Timber.v("Migrating Realm Crypto from $oldVersion to $newVersion")
@ -45,6 +49,7 @@ internal object RealmCryptoStoreMigration : RealmMigration {
if (oldVersion <= 0) migrateTo1(realm) if (oldVersion <= 0) migrateTo1(realm)
if (oldVersion <= 1) migrateTo2(realm) if (oldVersion <= 1) migrateTo2(realm)
if (oldVersion <= 2) migrateTo3(realm) if (oldVersion <= 2) migrateTo3(realm)
if (oldVersion <= 3) migrateTo4(realm)
} }
private fun migrateTo1(realm: DynamicRealm) { private fun migrateTo1(realm: DynamicRealm) {
@ -193,4 +198,18 @@ internal object RealmCryptoStoreMigration : RealmMigration {
?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY, String::class.java) ?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY, String::class.java)
?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY_VERSION, String::class.java) ?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY_VERSION, String::class.java)
} }
private fun migrateTo4(realm: DynamicRealm) {
Timber.d("Updating KeyInfoEntity table")
val keyInfoEntities = realm.where("KeyInfoEntity").findAll()
try {
keyInfoEntities.forEach {
val stringSignatures = it.getString(KeyInfoEntityFields.SIGNATURES)
val objectSignatures: Map<String, Map<String, String>>? = deserializeFromRealm(stringSignatures)
val jsonSignatures = crossSigningKeysMapper.serializeSignatures(objectSignatures)
it.setString(KeyInfoEntityFields.SIGNATURES, jsonSignatures)
}
} catch (failure: Throwable) {
}
}
} }

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 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.mapper
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntity
import io.realm.RealmList
import timber.log.Timber
import javax.inject.Inject
internal class CrossSigningKeysMapper @Inject constructor(moshi: Moshi) {
private val signaturesAdapter = moshi.adapter<Map<String, Map<String, String>>>(Types.newParameterizedType(
Map::class.java,
String::class.java,
Any::class.java
))
fun update(keyInfo: KeyInfoEntity, cryptoCrossSigningKey: CryptoCrossSigningKey) {
// update signatures?
keyInfo.signatures = serializeSignatures(cryptoCrossSigningKey.signatures)
keyInfo.usages = cryptoCrossSigningKey.usages?.toTypedArray()?.let { RealmList(*it) }
?: RealmList()
}
fun map(userId: String?, keyInfo: KeyInfoEntity?): CryptoCrossSigningKey? {
val pubKey = keyInfo?.publicKeyBase64 ?: return null
return CryptoCrossSigningKey(
userId = userId ?: "",
keys = mapOf("ed25519:$pubKey" to pubKey),
usages = keyInfo.usages.map { it },
signatures = deserializeSignatures(keyInfo.signatures),
trustLevel = keyInfo.trustLevelEntity?.let {
DeviceTrustLevel(
crossSigningVerified = it.crossSignedVerified ?: false,
locallyVerified = it.locallyVerified ?: false
)
}
)
}
fun map(keyInfo: CryptoCrossSigningKey): KeyInfoEntity {
return KeyInfoEntity().apply {
publicKeyBase64 = keyInfo.unpaddedBase64PublicKey
usages = keyInfo.usages?.let { RealmList(*it.toTypedArray()) } ?: RealmList()
signatures = serializeSignatures(keyInfo.signatures)
// TODO how to handle better, check if same keys?
// reset trust
trustLevelEntity = null
}
}
fun serializeSignatures(signatures: Map<String, Map<String, String>>?): String {
return signaturesAdapter.toJson(signatures)
}
fun deserializeSignatures(signatures: String?): Map<String, Map<String, String>>? {
if (signatures == null) {
return null
}
return try {
signaturesAdapter.fromJson(signatures)
} catch (failure: Throwable) {
Timber.e(failure)
null
}
}
}

View File

@ -16,8 +16,6 @@
package im.vector.matrix.android.internal.crypto.store.db.model 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.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
@ -31,15 +29,4 @@ internal open class KeyInfoEntity(
*/ */
var signatures: String? = null, var signatures: String? = null,
var trustLevelEntity: TrustLevelEntity? = null var trustLevelEntity: TrustLevelEntity? = null
) : RealmObject() { ) : RealmObject()
// Deserialize data
fun getSignatures(): Map<String, Map<String, String>>? {
return deserializeFromRealm(signatures)
}
// Serialize data
fun putSignatures(deviceInfo: Map<String, Map<String, String>>?) {
signatures = serializeForRealm(deviceInfo)
}
}

View File

@ -152,7 +152,7 @@ internal class RoomSummaryUpdater @Inject constructor(
if (updateMembers) { if (updateMembers) {
val otherRoomMembers = RoomMemberHelper(realm, roomId) val otherRoomMembers = RoomMemberHelper(realm, roomId)
.queryRoomMembersEvent() .queryActiveRoomMembersEvent()
.notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) .notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId)
.findAll() .findAll()
.asSequence() .asSequence()