From 4e6bed87e4cf592af8c77750b3139482a51f9f2d Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 6 May 2022 17:51:35 +0200 Subject: [PATCH] Introduce ExtractMigrationDataUseCase --- .../store/ExtractMigrationDataUseCase.kt | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/ExtractMigrationDataUseCase.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/ExtractMigrationDataUseCase.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/ExtractMigrationDataUseCase.kt new file mode 100644 index 0000000000..0b0b5a6346 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/ExtractMigrationDataUseCase.kt @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.crypto.store + +import io.realm.Realm +import io.realm.kotlin.where +import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity +import org.matrix.olm.OlmUtility +import uniffi.olm.CrossSigningKeyExport +import uniffi.olm.MigrationData +import uniffi.olm.PickledAccount +import uniffi.olm.PickledInboundGroupSession +import uniffi.olm.PickledSession +import java.nio.charset.Charset + +private val charset = Charset.forName("UTF-8") + +internal class ExtractMigrationDataUseCase() { + + operator fun invoke(realm: Realm): MigrationData? { + val pickleKey = OlmUtility.getRandomKey() + + val olmSessionEntities = realm.where().findAll() + val pickledSessions = olmSessionEntities.map { it.toPickledSession(pickleKey) } + + val inboundGroupSessionEntities = realm.where().findAll() + val pickledInboundGroupSessions = inboundGroupSessionEntities.map { it.toPickledInboundGroupSession(pickleKey) } + + val metadataEntity = realm.where().findFirst() ?: return null + val masterKey = metadataEntity.xSignMasterPrivateKey + val userKey = metadataEntity.xSignUserPrivateKey + val selfSignedKey = metadataEntity.xSignSelfSignedPrivateKey + + val userId = metadataEntity.userId!! + val deviceId = metadataEntity.deviceId!! + val backupVersion = metadataEntity.backupVersion + val backupRecoveryKey = metadataEntity.keyBackupRecoveryKey + + val trackedUserEntities = realm.where().findAll() + val trackedUserIds = trackedUserEntities.mapNotNull { + it.userId + } + val isOlmAccountShared = metadataEntity.deviceKeysSentToServer + + val olmAccount = metadataEntity.getOlmAccount()!! + val pickledOlmAccount = olmAccount.pickle(pickleKey) + val pickledAccount = PickledAccount( + userId = userId, + deviceId = deviceId, + pickle = pickledOlmAccount, + shared = isOlmAccountShared, + uploadedSignedKeyCount = 50 + ) + return MigrationData( + account = pickledAccount, + sessions = pickledSessions, + inboundGroupSessions = pickledInboundGroupSessions, + pickleKey = String(pickleKey, charset), + backupVersion = backupVersion, + backupRecoveryKey = backupRecoveryKey, + crossSigning = CrossSigningKeyExport( + masterKey = masterKey, + selfSigningKey = selfSignedKey, + userSigningKey = userKey + ), + trackedUsers = trackedUserIds + ) + } + + private fun OlmInboundGroupSessionEntity.toPickledInboundGroupSession(pickleKey: ByteArray): PickledInboundGroupSession { + val senderKey = this.senderKey ?: "" + val olmInboundGroupSession = getInboundGroupSession()!! + val pickledInboundGroupSession = olmInboundGroupSession.olmInboundGroupSession!!.pickle(pickleKey) + return PickledInboundGroupSession( + pickle = pickledInboundGroupSession, + senderKey = senderKey, + signingKey = olmInboundGroupSession.keysClaimed.orEmpty(), + roomId = olmInboundGroupSession.roomId!!, + forwardingChains = olmInboundGroupSession.forwardingCurve25519KeyChain.orEmpty(), + imported = true, + backedUp = backedUp + ) + } + + private fun OlmSessionEntity.toPickledSession(pickleKey: ByteArray): PickledSession { + val deviceKey = this.deviceKey ?: "" + val lastReceivedMessageTs = this.lastReceivedMessageTs + val olmSession = getOlmSession()!! + val pickledOlmSession = olmSession.pickle(pickleKey) + return PickledSession( + pickle = pickledOlmSession, + senderKey = deviceKey, + createdUsingFallbackKey = false, + creationTime = lastReceivedMessageTs.toString(), + lastUseTime = lastReceivedMessageTs.toString() + ) + } + + private fun Any.pickle(pickleKey: ByteArray): String { + return try { + val pickleMethod = this.javaClass.getDeclaredMethod("serialize", ByteArray::class.java, StringBuffer::class.java) + pickleMethod.isAccessible = true + val pickled = pickleMethod.invoke(this, pickleKey, StringBuffer())!! + String(pickled as ByteArray, charset) + } catch (throwable: Throwable) { + "" + } + } +}