mirror of
https://github.com/element-hq/element-android
synced 2024-12-22 01:04:56 +03:00
crypto: Initial support for server-side backups of room keys
This commit is contained in:
parent
d3a761a73a
commit
406fd0d8d5
10 changed files with 898 additions and 50 deletions
|
@ -37,10 +37,12 @@ import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
|
import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService
|
||||||
import org.matrix.android.sdk.api.session.crypto.keyshare.GossipingRequestListener
|
import org.matrix.android.sdk.api.session.crypto.keyshare.GossipingRequestListener
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
|
@ -54,7 +56,14 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.internal.auth.registration.handleUIA
|
import org.matrix.android.sdk.internal.auth.registration.handleUIA
|
||||||
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
|
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
|
import org.matrix.android.sdk.internal.crypto.keysbackup.RustKeyBackupService
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.CreateKeysBackupVersionTask
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.DeleteBackupTask
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetKeysBackupLastVersionTask
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetKeysBackupVersionTask
|
||||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
|
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult
|
import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult
|
||||||
|
@ -119,6 +128,10 @@ internal class RequestSender @Inject constructor(
|
||||||
private val signaturesUploadTask: UploadSignaturesTask,
|
private val signaturesUploadTask: UploadSignaturesTask,
|
||||||
private val sendVerificationMessageTask: Lazy<DefaultSendVerificationMessageTask>,
|
private val sendVerificationMessageTask: Lazy<DefaultSendVerificationMessageTask>,
|
||||||
private val uploadSigningKeysTask: UploadSigningKeysTask,
|
private val uploadSigningKeysTask: UploadSigningKeysTask,
|
||||||
|
private val getKeysBackupLastVersionTask: GetKeysBackupLastVersionTask,
|
||||||
|
private val getKeysBackupVersionTask: GetKeysBackupVersionTask,
|
||||||
|
private val deleteBackupTask: DeleteBackupTask,
|
||||||
|
private val createKeysBackupVersionTask: CreateKeysBackupVersionTask,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val REQUEST_RETRY_COUNT = 3
|
const val REQUEST_RETRY_COUNT = 3
|
||||||
|
@ -192,7 +205,7 @@ internal class RequestSender @Inject constructor(
|
||||||
request: UploadSigningKeysRequest,
|
request: UploadSigningKeysRequest,
|
||||||
interactiveAuthInterceptor: UserInteractiveAuthInterceptor?
|
interactiveAuthInterceptor: UserInteractiveAuthInterceptor?
|
||||||
) {
|
) {
|
||||||
val adapter = MoshiProvider.providesMoshi().adapter<RestKeyInfo>(RestKeyInfo::class.java)
|
val adapter = MoshiProvider.providesMoshi().adapter(RestKeyInfo::class.java)
|
||||||
val masterKey = adapter.fromJson(request.masterKey)!!.toCryptoModel()
|
val masterKey = adapter.fromJson(request.masterKey)!!.toCryptoModel()
|
||||||
val selfSigningKey = adapter.fromJson(request.selfSigningKey)!!.toCryptoModel()
|
val selfSigningKey = adapter.fromJson(request.selfSigningKey)!!.toCryptoModel()
|
||||||
val userSigningKey = adapter.fromJson(request.userSigningKey)!!.toCryptoModel()
|
val userSigningKey = adapter.fromJson(request.userSigningKey)!!.toCryptoModel()
|
||||||
|
@ -248,6 +261,32 @@ internal class RequestSender @Inject constructor(
|
||||||
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap, transactionId)
|
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap, transactionId)
|
||||||
sendToDeviceTask.executeRetry(sendToDeviceParams, REQUEST_RETRY_COUNT)
|
sendToDeviceTask.executeRetry(sendToDeviceParams, REQUEST_RETRY_COUNT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getKeyBackupVersion(version: String? = null): KeysVersionResult? {
|
||||||
|
return try {
|
||||||
|
if (version != null) {
|
||||||
|
getKeysBackupVersionTask.execute(version)
|
||||||
|
} else {
|
||||||
|
getKeysBackupLastVersionTask.execute(Unit)
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
if (failure is Failure.ServerError
|
||||||
|
&& failure.error.code == MatrixError.M_NOT_FOUND) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
throw failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun createKeyBackup(body: CreateKeysBackupVersionBody): KeysVersion {
|
||||||
|
return createKeysBackupVersionTask.execute(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteKeyBackup(version: String) {
|
||||||
|
val params = DeleteBackupTask.Params(version)
|
||||||
|
deleteBackupTask.execute(params)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -272,8 +311,6 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
private val cryptoStore: IMXCryptoStore,
|
private val cryptoStore: IMXCryptoStore,
|
||||||
// Set of parameters used to configure/customize the end-to-end crypto.
|
// Set of parameters used to configure/customize the end-to-end crypto.
|
||||||
private val mxCryptoConfig: MXCryptoConfig,
|
private val mxCryptoConfig: MXCryptoConfig,
|
||||||
// The key backup service.
|
|
||||||
private val keysBackupService: DefaultKeysBackupService,
|
|
||||||
// Actions
|
// Actions
|
||||||
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
||||||
// Tasks
|
// Tasks
|
||||||
|
@ -283,6 +320,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
private val setDeviceNameTask: SetDeviceNameTask,
|
private val setDeviceNameTask: SetDeviceNameTask,
|
||||||
private val loadRoomMembersTask: LoadRoomMembersTask,
|
private val loadRoomMembersTask: LoadRoomMembersTask,
|
||||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
||||||
|
private val createKeysBackupVersionTask: CreateKeysBackupVersionTask,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
private val cryptoCoroutineScope: CoroutineScope,
|
private val cryptoCoroutineScope: CoroutineScope,
|
||||||
|
@ -300,6 +338,9 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
// The cross signing service.
|
// The cross signing service.
|
||||||
private var crossSigningService: RustCrossSigningService? = null
|
private var crossSigningService: RustCrossSigningService? = null
|
||||||
|
|
||||||
|
// The key backup service.
|
||||||
|
private var keysBackupService: RustKeyBackupService? = null
|
||||||
|
|
||||||
private val deviceObserver: DeviceUpdateObserver = DeviceUpdateObserver()
|
private val deviceObserver: DeviceUpdateObserver = DeviceUpdateObserver()
|
||||||
|
|
||||||
// Locks for some of our operations
|
// Locks for some of our operations
|
||||||
|
@ -448,9 +489,12 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
cryptoStore.open()
|
cryptoStore.open()
|
||||||
|
|
||||||
// this can throw if no backup
|
// this can throw if no backup
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
tryOrNull {
|
tryOrNull {
|
||||||
keysBackupService.checkAndStartKeysBackup()
|
keysBackupService.checkAndStartKeysBackup()
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,6 +510,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
olmMachine = machine
|
olmMachine = machine
|
||||||
verificationService = RustVerificationService(machine)
|
verificationService = RustVerificationService(machine)
|
||||||
crossSigningService = RustCrossSigningService(machine)
|
crossSigningService = RustCrossSigningService(machine)
|
||||||
|
keysBackupService = RustKeyBackupService(machine, sender, coroutineDispatchers, cryptoCoroutineScope)
|
||||||
Timber.v(
|
Timber.v(
|
||||||
"## CRYPTO | Successfully started up an Olm machine for " +
|
"## CRYPTO | Successfully started up an Olm machine for " +
|
||||||
"${userId}, ${deviceId}, identity keys: ${this.olmMachine?.identityKeys()}")
|
"${userId}, ${deviceId}, identity keys: ${this.olmMachine?.identityKeys()}")
|
||||||
|
@ -473,6 +518,10 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
Timber.v("Failed create an Olm machine: $throwable")
|
Timber.v("Failed create an Olm machine: $throwable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tryOrNull {
|
||||||
|
keysBackupService!!.checkAndStartKeysBackup()
|
||||||
|
}
|
||||||
|
|
||||||
// Open the store
|
// Open the store
|
||||||
cryptoStore.open()
|
cryptoStore.open()
|
||||||
|
|
||||||
|
@ -494,7 +543,12 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
/**
|
/**
|
||||||
* @return the Keys backup Service
|
* @return the Keys backup Service
|
||||||
*/
|
*/
|
||||||
override fun keysBackupService() = keysBackupService
|
override fun keysBackupService(): KeysBackupService {
|
||||||
|
if (keysBackupService == null) {
|
||||||
|
internalStart()
|
||||||
|
}
|
||||||
|
return keysBackupService!!
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the VerificationService
|
* @return the VerificationService
|
||||||
|
@ -693,7 +747,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
eventType: String,
|
eventType: String,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
callback: MatrixCallback<MXEncryptEventContentResult>) {
|
callback: MatrixCallback<MXEncryptEventContentResult>) {
|
||||||
// moved to crypto scope to have uptodate values
|
// moved to crypto scope to have up to date values
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||||
val algorithm = getEncryptionAlgorithm(roomId)
|
val algorithm = getEncryptionAlgorithm(roomId)
|
||||||
|
|
||||||
|
@ -971,6 +1025,11 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
signatureUpload(it)
|
signatureUpload(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is Request.KeysBackup -> {
|
||||||
|
async {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.joinAll()
|
}.joinAll()
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,8 @@ import org.matrix.android.sdk.internal.session.sync.model.DeviceListResponse
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.DeviceOneTimeKeysCountSyncResponse
|
import org.matrix.android.sdk.internal.session.sync.model.DeviceOneTimeKeysCountSyncResponse
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse
|
import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import uniffi.olm.BackupKey
|
||||||
|
import uniffi.olm.BackupKeys
|
||||||
import uniffi.olm.CrossSigningKeyExport
|
import uniffi.olm.CrossSigningKeyExport
|
||||||
import uniffi.olm.CrossSigningStatus
|
import uniffi.olm.CrossSigningStatus
|
||||||
import uniffi.olm.CryptoStoreErrorException
|
import uniffi.olm.CryptoStoreErrorException
|
||||||
|
@ -59,6 +61,7 @@ import uniffi.olm.OlmMachine as InnerMachine
|
||||||
import uniffi.olm.ProgressListener as RustProgressListener
|
import uniffi.olm.ProgressListener as RustProgressListener
|
||||||
import uniffi.olm.Request
|
import uniffi.olm.Request
|
||||||
import uniffi.olm.RequestType
|
import uniffi.olm.RequestType
|
||||||
|
import uniffi.olm.RoomKeyCounts
|
||||||
import uniffi.olm.UserIdentity as RustUserIdentity
|
import uniffi.olm.UserIdentity as RustUserIdentity
|
||||||
import uniffi.olm.setLogger
|
import uniffi.olm.setLogger
|
||||||
|
|
||||||
|
@ -760,4 +763,33 @@ internal class OlmMachine(
|
||||||
// TODO map the errors from importCrossSigningKeys to the UserTrustResult
|
// TODO map the errors from importCrossSigningKeys to the UserTrustResult
|
||||||
return UserTrustResult.Success
|
return UserTrustResult.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun sign(message: String): Map<String, Map<String, String>> {
|
||||||
|
return withContext(Dispatchers.Default) {
|
||||||
|
inner.sign(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(CryptoStoreErrorException::class)
|
||||||
|
suspend fun enableBackup(key: String, version: String) {
|
||||||
|
return withContext(Dispatchers.Default) {
|
||||||
|
val backupKey = BackupKey(key, mapOf(), null)
|
||||||
|
inner.enableBackup(backupKey, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun roomKeyCounts(): RoomKeyCounts {
|
||||||
|
// TODO convert this to a suspendable method
|
||||||
|
return inner.roomKeyCounts()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBackupKeys(): BackupKeys? {
|
||||||
|
// TODO this needs to be suspendable
|
||||||
|
return inner.getBackupKeys()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveRecoveryKey(key: String?, version: String?) {
|
||||||
|
// TODO convert this to a suspendable method
|
||||||
|
inner.saveRecoveryKey(key, version)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,466 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.keysbackup
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||||
|
import org.matrix.android.sdk.api.listeners.StepProgressListener
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||||
|
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
|
||||||
|
import org.matrix.android.sdk.internal.crypto.OlmMachine
|
||||||
|
import org.matrix.android.sdk.internal.crypto.RequestSender
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.SignalableMegolmBackupAuthData
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||||
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.SavedKeyBackupKeyInfo
|
||||||
|
import org.matrix.android.sdk.internal.extensions.foldToCallback
|
||||||
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||||
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
|
import timber.log.Timber
|
||||||
|
import uniffi.olm.BackupRecoveryKey
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DefaultKeysBackupService class instance manage incremental backup of e2e keys (megolm keys)
|
||||||
|
* to the user's homeserver.
|
||||||
|
*/
|
||||||
|
@SessionScope
|
||||||
|
internal class RustKeyBackupService @Inject constructor(
|
||||||
|
private val olmMachine: OlmMachine,
|
||||||
|
private val sender: RequestSender,
|
||||||
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
private val cryptoCoroutineScope: CoroutineScope,
|
||||||
|
) : KeysBackupService {
|
||||||
|
|
||||||
|
private val uiHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
private val keysBackupStateManager = KeysBackupStateManager(uiHandler)
|
||||||
|
|
||||||
|
// The backup version
|
||||||
|
override var keysBackupVersion: KeysVersionResult? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
private var backupAllGroupSessionsCallback: MatrixCallback<Unit>? = null
|
||||||
|
|
||||||
|
private var keysBackupStateListener: KeysBackupStateListener? = null
|
||||||
|
|
||||||
|
override val isEnabled: Boolean
|
||||||
|
get() = keysBackupStateManager.isEnabled
|
||||||
|
|
||||||
|
override val isStucked: Boolean
|
||||||
|
get() = keysBackupStateManager.isStucked
|
||||||
|
|
||||||
|
override val state: KeysBackupState
|
||||||
|
get() = keysBackupStateManager.state
|
||||||
|
|
||||||
|
override val currentBackupVersion: String?
|
||||||
|
get() = keysBackupVersion?.version
|
||||||
|
|
||||||
|
override fun addListener(listener: KeysBackupStateListener) {
|
||||||
|
keysBackupStateManager.addListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeListener(listener: KeysBackupStateListener) {
|
||||||
|
keysBackupStateManager.removeListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun prepareKeysBackupVersion(password: String?,
|
||||||
|
progressListener: ProgressListener?,
|
||||||
|
callback: MatrixCallback<MegolmBackupCreationInfo>) {
|
||||||
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
|
runCatching {
|
||||||
|
withContext(coroutineDispatchers.crypto) {
|
||||||
|
val key = if (password != null) {
|
||||||
|
BackupRecoveryKey.fromPassphrase(password)
|
||||||
|
} else {
|
||||||
|
BackupRecoveryKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
val publicKey = key.publicKey()
|
||||||
|
val backupAuthData = SignalableMegolmBackupAuthData(
|
||||||
|
publicKey = publicKey.publicKey,
|
||||||
|
privateKeySalt = publicKey.passphraseInfo?.privateKeySalt,
|
||||||
|
privateKeyIterations = publicKey.passphraseInfo?.privateKeyIterations
|
||||||
|
)
|
||||||
|
val canonicalJson = JsonCanonicalizer.getCanonicalJson(
|
||||||
|
Map::class.java,
|
||||||
|
backupAuthData.signalableJSONDictionary()
|
||||||
|
)
|
||||||
|
|
||||||
|
val signedMegolmBackupAuthData = MegolmBackupAuthData(
|
||||||
|
publicKey = backupAuthData.publicKey,
|
||||||
|
privateKeySalt = backupAuthData.privateKeySalt,
|
||||||
|
privateKeyIterations = backupAuthData.privateKeyIterations,
|
||||||
|
signatures = olmMachine.sign(canonicalJson)
|
||||||
|
)
|
||||||
|
|
||||||
|
MegolmBackupCreationInfo(
|
||||||
|
algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP,
|
||||||
|
authData = signedMegolmBackupAuthData,
|
||||||
|
recoveryKey = key.toBase58()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.foldToCallback(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createKeysBackupVersion(keysBackupCreationInfo: MegolmBackupCreationInfo,
|
||||||
|
callback: MatrixCallback<KeysVersion>) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val createKeysBackupVersionBody = CreateKeysBackupVersionBody(
|
||||||
|
algorithm = keysBackupCreationInfo.algorithm,
|
||||||
|
authData = keysBackupCreationInfo.authData.toJsonDict()
|
||||||
|
)
|
||||||
|
|
||||||
|
keysBackupStateManager.state = KeysBackupState.Enabling
|
||||||
|
|
||||||
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
|
try {
|
||||||
|
val data = sender.createKeyBackup(createKeysBackupVersionBody)
|
||||||
|
// Reset backup markers.
|
||||||
|
// Don't we need to join the task here? Isn't this a race condition?
|
||||||
|
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||||
|
// TODO reset our backup state here, i.e. the `backed_up` flag on inbound group sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
olmMachine.enableBackup(keysBackupCreationInfo.authData.publicKey, data.version)
|
||||||
|
|
||||||
|
callback.onSuccess(data)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||||
|
callback.onFailure(failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) {
|
||||||
|
cryptoCoroutineScope.launch {
|
||||||
|
olmMachine.saveRecoveryKey(recoveryKey, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetBackupAllGroupSessionsListeners() {
|
||||||
|
backupAllGroupSessionsCallback = null
|
||||||
|
|
||||||
|
keysBackupStateListener?.let {
|
||||||
|
keysBackupStateManager.removeListener(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
keysBackupStateListener = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all local key backup data.
|
||||||
|
*
|
||||||
|
* Note: This method does not update the state
|
||||||
|
*/
|
||||||
|
private fun resetKeysBackupData() {
|
||||||
|
resetBackupAllGroupSessionsListeners()
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
TODO reset data on the rust side
|
||||||
|
cryptoStore.setKeyBackupVersion(null)
|
||||||
|
cryptoStore.setKeysBackupData(null)
|
||||||
|
backupOlmPkEncryption?.releaseEncryption()
|
||||||
|
backupOlmPkEncryption = null
|
||||||
|
|
||||||
|
// Reset backup markers
|
||||||
|
cryptoStore.resetBackupMarkers()
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteBackup(version: String, callback: MatrixCallback<Unit>?) {
|
||||||
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
|
withContext(coroutineDispatchers.crypto) {
|
||||||
|
if (keysBackupVersion != null && version == keysBackupVersion?.version) {
|
||||||
|
resetKeysBackupData()
|
||||||
|
keysBackupVersion = null
|
||||||
|
keysBackupStateManager.state = KeysBackupState.Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
fun eventuallyRestartBackup() {
|
||||||
|
// Do not stay in KeysBackupState.Unknown but check what is available on the homeserver
|
||||||
|
if (state == KeysBackupState.Unknown) {
|
||||||
|
checkAndStartKeysBackup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
sender.deleteKeyBackup(version)
|
||||||
|
eventuallyRestartBackup()
|
||||||
|
uiHandler.post { callback?.onSuccess(Unit) }
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
eventuallyRestartBackup()
|
||||||
|
uiHandler.post { callback?.onFailure(failure) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canRestoreKeys(): Boolean {
|
||||||
|
// TODO
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTotalNumbersOfKeys(): Int {
|
||||||
|
return olmMachine.roomKeyCounts().total.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTotalNumbersOfBackedUpKeys(): Int {
|
||||||
|
return olmMachine.roomKeyCounts().backedUp.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun backupAllGroupSessions(progressListener: ProgressListener?,
|
||||||
|
callback: MatrixCallback<Unit>?) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getKeysBackupTrust(keysBackupVersion: KeysVersionResult,
|
||||||
|
callback: MatrixCallback<KeysBackupVersionTrust>) {
|
||||||
|
Timber.d("BACKUP: HELLOO TRYING TO CHECK THE TRUST")
|
||||||
|
// TODO
|
||||||
|
callback.onSuccess(KeysBackupVersionTrust(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun trustKeysBackupVersion(keysBackupVersion: KeysVersionResult,
|
||||||
|
trust: Boolean,
|
||||||
|
callback: MatrixCallback<Unit>) {
|
||||||
|
Timber.v("trustKeyBackupVersion: $trust, version ${keysBackupVersion.version}")
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult,
|
||||||
|
recoveryKey: String,
|
||||||
|
callback: MatrixCallback<Unit>) {
|
||||||
|
Timber.v("trustKeysBackupVersionWithRecoveryKey: version ${keysBackupVersion.version}")
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun trustKeysBackupVersionWithPassphrase(keysBackupVersion: KeysVersionResult,
|
||||||
|
password: String,
|
||||||
|
callback: MatrixCallback<Unit>) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSecretKeyGossip(secret: String) {
|
||||||
|
Timber.i("## CrossSigning - onSecretKeyGossip")
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBackupProgress(progressListener: ProgressListener) {
|
||||||
|
val backedUpKeys = getTotalNumbersOfBackedUpKeys()
|
||||||
|
val total = getTotalNumbersOfKeys()
|
||||||
|
|
||||||
|
progressListener.onProgress(backedUpKeys, total)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult,
|
||||||
|
recoveryKey: String,
|
||||||
|
roomId: String?,
|
||||||
|
sessionId: String?,
|
||||||
|
stepProgressListener: StepProgressListener?,
|
||||||
|
callback: MatrixCallback<ImportRoomKeysResult>) {
|
||||||
|
// TODO
|
||||||
|
Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun restoreKeyBackupWithPassword(keysBackupVersion: KeysVersionResult,
|
||||||
|
password: String,
|
||||||
|
roomId: String?,
|
||||||
|
sessionId: String?,
|
||||||
|
stepProgressListener: StepProgressListener?,
|
||||||
|
callback: MatrixCallback<ImportRoomKeysResult>) {
|
||||||
|
// TODO
|
||||||
|
Timber.v("[MXKeyBackup] restoreKeyBackup with password: From backup version: ${keysBackupVersion.version}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getVersion(version: String, callback: MatrixCallback<KeysVersionResult?>) {
|
||||||
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
|
runCatching {
|
||||||
|
sender.getKeyBackupVersion(version)
|
||||||
|
}.foldToCallback(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCurrentVersion(callback: MatrixCallback<KeysVersionResult?>) {
|
||||||
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
|
runCatching {
|
||||||
|
sender.getKeyBackupVersion()
|
||||||
|
}.foldToCallback(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun forceUsingLastVersionHelper(): Boolean {
|
||||||
|
val response = sender.getKeyBackupVersion()
|
||||||
|
val serverBackupVersion = response?.version
|
||||||
|
val localBackupVersion = keysBackupVersion?.version
|
||||||
|
|
||||||
|
Timber.d("BACKUP: $serverBackupVersion")
|
||||||
|
|
||||||
|
return if (serverBackupVersion == null) {
|
||||||
|
if (localBackupVersion == null) {
|
||||||
|
// No backup on the server, and backup is not active
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// No backup on the server, and we are currently backing up, so stop backing up
|
||||||
|
resetKeysBackupData()
|
||||||
|
keysBackupVersion = null
|
||||||
|
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (localBackupVersion == null) {
|
||||||
|
// Do a check
|
||||||
|
checkAndStartWithKeysBackupVersion(response)
|
||||||
|
// backup on the server, and backup is not active
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
// Backup on the server, and we are currently backing up, compare version
|
||||||
|
if (localBackupVersion == serverBackupVersion) {
|
||||||
|
// We are already using the last version of the backup
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// This will automatically check for the last version then
|
||||||
|
deleteBackup(localBackupVersion, null)
|
||||||
|
// We are not using the last version, so delete the current version we are using on the server
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun forceUsingLastVersion(callback: MatrixCallback<Boolean>) {
|
||||||
|
cryptoCoroutineScope.launch {
|
||||||
|
runCatching {
|
||||||
|
forceUsingLastVersionHelper()
|
||||||
|
}.foldToCallback(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkAndStartKeysBackup() {
|
||||||
|
if (!isStucked) {
|
||||||
|
// Try to start or restart the backup only if it is in unknown or bad state
|
||||||
|
Timber.w("checkAndStartKeysBackup: invalid state: $state")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keysBackupVersion = null
|
||||||
|
keysBackupStateManager.state = KeysBackupState.CheckingBackUpOnHomeserver
|
||||||
|
|
||||||
|
getCurrentVersion(object : MatrixCallback<KeysVersionResult?> {
|
||||||
|
override fun onSuccess(data: KeysVersionResult?) {
|
||||||
|
checkAndStartWithKeysBackupVersion(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
Timber.e(failure, "checkAndStartKeysBackup: Failed to get current version")
|
||||||
|
keysBackupStateManager.state = KeysBackupState.Unknown
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkAndStartWithKeysBackupVersion(keyBackupVersion: KeysVersionResult?) {
|
||||||
|
Timber.v("checkAndStartWithKeyBackupVersion: ${keyBackupVersion?.version}")
|
||||||
|
|
||||||
|
keysBackupVersion = keyBackupVersion
|
||||||
|
|
||||||
|
if (keyBackupVersion == null) {
|
||||||
|
Timber.v("checkAndStartWithKeysBackupVersion: Found no key backup version on the homeserver")
|
||||||
|
resetKeysBackupData()
|
||||||
|
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||||
|
} else {
|
||||||
|
getKeysBackupTrust(keyBackupVersion, object : MatrixCallback<KeysBackupVersionTrust> {
|
||||||
|
override fun onSuccess(data: KeysBackupVersionTrust) {
|
||||||
|
val versionInStore = getKeyBackupRecoveryKeyInfo()?.version
|
||||||
|
|
||||||
|
if (data.usable) {
|
||||||
|
Timber.v("checkAndStartWithKeysBackupVersion: Found usable key backup. version: ${keyBackupVersion.version}")
|
||||||
|
// Check the version we used at the previous app run
|
||||||
|
if (versionInStore != null && versionInStore != keyBackupVersion.version) {
|
||||||
|
Timber.v(" -> clean the previously used version $versionInStore")
|
||||||
|
resetKeysBackupData()
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.v(" -> enabling key backups")
|
||||||
|
// TODO
|
||||||
|
// enableKeysBackup(keyBackupVersion)
|
||||||
|
} else {
|
||||||
|
Timber.v("checkAndStartWithKeysBackupVersion: No usable key backup. version: ${keyBackupVersion.version}")
|
||||||
|
if (versionInStore != null) {
|
||||||
|
Timber.v(" -> disabling key backup")
|
||||||
|
resetKeysBackupData()
|
||||||
|
}
|
||||||
|
|
||||||
|
keysBackupStateManager.state = KeysBackupState.NotTrusted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
// Cannot happen
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isValidRecoveryKeyForCurrentVersion(recoveryKey: String, callback: MatrixCallback<Boolean>) {
|
||||||
|
val keysBackupVersion = keysBackupVersion ?: return Unit.also { callback.onSuccess(false) }
|
||||||
|
|
||||||
|
try {
|
||||||
|
val key = BackupRecoveryKey.fromBase64(recoveryKey)
|
||||||
|
val publicKey = key.publicKey().publicKey
|
||||||
|
val authData = getMegolmBackupAuthData(keysBackupVersion) ?: return Unit.also { callback.onSuccess(false) }
|
||||||
|
|
||||||
|
callback.onSuccess(authData.publicKey == publicKey)
|
||||||
|
} catch (error: Throwable) {
|
||||||
|
callback.onFailure(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? {
|
||||||
|
val info = olmMachine.getBackupKeys() ?: return null
|
||||||
|
return SavedKeyBackupKeyInfo(info.recoveryKey, info.backupVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract MegolmBackupAuthData data from a backup version.
|
||||||
|
*
|
||||||
|
* @param keysBackupData the key backup data
|
||||||
|
*
|
||||||
|
* @return the authentication if found and valid, null in other case
|
||||||
|
*/
|
||||||
|
private fun getMegolmBackupAuthData(keysBackupData: KeysVersionResult): MegolmBackupAuthData? {
|
||||||
|
return keysBackupData
|
||||||
|
.takeIf { it.version.isNotEmpty() && it.algorithm == MXCRYPTO_ALGORITHM_MEGOLM_BACKUP }
|
||||||
|
?.getAuthDataAsMegolmBackupAuthData()
|
||||||
|
?.takeIf { it.publicKey.isNotEmpty() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,19 +18,25 @@ thiserror = "1.0.25"
|
||||||
tracing = "0.1.26"
|
tracing = "0.1.26"
|
||||||
tracing-subscriber = "0.2.18"
|
tracing-subscriber = "0.2.18"
|
||||||
uniffi = "0.12.0"
|
uniffi = "0.12.0"
|
||||||
|
pbkdf2 = "0.8.0"
|
||||||
|
sha2 = "0.9.5"
|
||||||
|
rand = "0.8.4"
|
||||||
|
hmac = "0.11.0"
|
||||||
|
|
||||||
[dependencies.js_int]
|
[dependencies.js_int]
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
features = ["lax_deserialize"]
|
features = ["lax_deserialize"]
|
||||||
|
|
||||||
[dependencies.matrix-sdk-common]
|
[dependencies.matrix-sdk-common]
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
path = "/home/poljar/werk/priv/nio-rust/crates/matrix-sdk-common/"
|
||||||
rev = "9bae87b0ac213f9d37c033e76ea3a336e164cf02"
|
# git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
||||||
|
# rev = "9bae87b0ac213f9d37c033e76ea3a336e164cf02"
|
||||||
|
|
||||||
[dependencies.matrix-sdk-crypto]
|
[dependencies.matrix-sdk-crypto]
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
# git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
||||||
rev = "9bae87b0ac213f9d37c033e76ea3a336e164cf02"
|
# rev = "9bae87b0ac213f9d37c033e76ea3a336e164cf02"
|
||||||
features = ["sled_cryptostore"]
|
path = "/home/poljar/werk/priv/nio-rust/crates/matrix-sdk-crypto/"
|
||||||
|
features = ["sled_cryptostore", "qrcode", "backups_v1"]
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
version = "1.7.1"
|
version = "1.7.1"
|
||||||
|
@ -38,8 +44,9 @@ default_features = false
|
||||||
features = ["rt-multi-thread"]
|
features = ["rt-multi-thread"]
|
||||||
|
|
||||||
[dependencies.ruma]
|
[dependencies.ruma]
|
||||||
version = "0.3.0"
|
git = "https://github.com/ruma/ruma"
|
||||||
features = ["client-api"]
|
rev = "0101e110f"
|
||||||
|
features = ["client-api-c"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
uniffi_build = "0.12.0"
|
uniffi_build = "0.12.0"
|
||||||
|
|
117
rust-sdk/src/backup_recovery_key.rs
Normal file
117
rust-sdk/src/backup_recovery_key.rs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
use hmac::Hmac;
|
||||||
|
use pbkdf2::pbkdf2;
|
||||||
|
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
|
use sha2::Sha512;
|
||||||
|
use std::{collections::HashMap, iter};
|
||||||
|
|
||||||
|
use matrix_sdk_crypto::backups::RecoveryKey;
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub struct BackupRecoveryKey {
|
||||||
|
pub(crate) inner: RecoveryKey,
|
||||||
|
passphrase_info: Option<PassphraseInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PassphraseInfo {
|
||||||
|
/// TODO
|
||||||
|
pub private_key_salt: String,
|
||||||
|
/// TODO
|
||||||
|
pub private_key_iterations: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub struct BackupKey {
|
||||||
|
/// TODO
|
||||||
|
pub public_key: String,
|
||||||
|
/// TODO
|
||||||
|
pub signatures: HashMap<String, HashMap<String, String>>,
|
||||||
|
/// TODO
|
||||||
|
pub passphrase_info: Option<PassphraseInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackupRecoveryKey {
|
||||||
|
const KEY_SIZE: usize = 32;
|
||||||
|
const SALT_SIZE: usize = 32;
|
||||||
|
const PBKDF_ROUNDS: u32 = 500_000;
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: RecoveryKey::new()
|
||||||
|
.expect("Can't gather enough randomness to create a recovery key"),
|
||||||
|
passphrase_info: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn from_base64(key: String) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: RecoveryKey::from_base64(key).unwrap(),
|
||||||
|
passphrase_info: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn from_base58(key: String) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: RecoveryKey::from_base58(&key).unwrap(),
|
||||||
|
passphrase_info: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn from_passphrase(passphrase: String) -> Self {
|
||||||
|
let mut key = [0u8; Self::KEY_SIZE];
|
||||||
|
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let salt: String = iter::repeat(())
|
||||||
|
.map(|()| rng.sample(Alphanumeric))
|
||||||
|
.map(char::from)
|
||||||
|
.take(Self::SALT_SIZE)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
pbkdf2::<Hmac<Sha512>>(
|
||||||
|
passphrase.as_bytes(),
|
||||||
|
salt.as_bytes(),
|
||||||
|
Self::PBKDF_ROUNDS,
|
||||||
|
&mut key,
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner: RecoveryKey::from_bytes(key),
|
||||||
|
passphrase_info: Some(PassphraseInfo {
|
||||||
|
private_key_salt: salt,
|
||||||
|
private_key_iterations: Self::PBKDF_ROUNDS as i32,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn public_key(&self) -> BackupKey {
|
||||||
|
let public_key = self.inner.public_key();
|
||||||
|
|
||||||
|
let signatures: HashMap<String, HashMap<String, String>> = public_key
|
||||||
|
.signatures()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
(
|
||||||
|
k.to_string(),
|
||||||
|
v.into_iter().map(|(k, v)| (k.to_string(), v)).collect(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
BackupKey {
|
||||||
|
public_key: public_key.encoded_key(),
|
||||||
|
signatures,
|
||||||
|
passphrase_info: self.passphrase_info.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn to_base58(&self) -> String {
|
||||||
|
self.inner.to_base58()
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
//! TODO
|
//! TODO
|
||||||
|
|
||||||
|
mod backup_recovery_key;
|
||||||
mod device;
|
mod device;
|
||||||
mod error;
|
mod error;
|
||||||
mod logger;
|
mod logger;
|
||||||
|
@ -18,18 +19,21 @@ mod responses;
|
||||||
mod users;
|
mod users;
|
||||||
mod verification;
|
mod verification;
|
||||||
|
|
||||||
|
pub use backup_recovery_key::{BackupKey, BackupRecoveryKey, PassphraseInfo};
|
||||||
pub use device::Device;
|
pub use device::Device;
|
||||||
pub use error::{CryptoStoreError, DecryptionError, KeyImportError, SignatureError, SecretImportError};
|
pub use error::{
|
||||||
|
CryptoStoreError, DecryptionError, KeyImportError, SecretImportError, SignatureError,
|
||||||
|
};
|
||||||
pub use logger::{set_logger, Logger};
|
pub use logger::{set_logger, Logger};
|
||||||
pub use machine::{KeyRequestPair, OlmMachine};
|
pub use machine::{KeyRequestPair, OlmMachine};
|
||||||
pub use responses::{
|
pub use responses::{
|
||||||
DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType, SignatureUploadRequest,
|
BootstrapCrossSigningResult, DeviceLists, KeysImportResult, OutgoingVerificationRequest,
|
||||||
BootstrapCrossSigningResult, UploadSigningKeysRequest,
|
Request, RequestType, SignatureUploadRequest, UploadSigningKeysRequest,
|
||||||
};
|
};
|
||||||
pub use users::UserIdentity;
|
pub use users::UserIdentity;
|
||||||
pub use verification::{
|
pub use verification::{
|
||||||
CancelInfo, QrCode, RequestVerificationResult, Sas, ScanResult, StartSasResult, Verification,
|
CancelInfo, ConfirmVerificationResult, QrCode, RequestVerificationResult, Sas, ScanResult,
|
||||||
VerificationRequest, ConfirmVerificationResult,
|
StartSasResult, Verification, VerificationRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Callback that will be passed over the FFI to report progress
|
/// Callback that will be passed over the FFI to report progress
|
||||||
|
@ -83,6 +87,42 @@ pub struct CrossSigningKeyExport {
|
||||||
pub user_signing_key: Option<String>,
|
pub user_signing_key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub struct RoomKeyCounts {
|
||||||
|
/// TODO
|
||||||
|
pub total: i64,
|
||||||
|
/// TODO
|
||||||
|
pub backed_up: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub struct BackupKeys {
|
||||||
|
/// TODO
|
||||||
|
pub recovery_key: String,
|
||||||
|
/// TODO
|
||||||
|
pub backup_version: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::TryFrom<matrix_sdk_crypto::store::BackupKeys> for BackupKeys {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(keys: matrix_sdk_crypto::store::BackupKeys) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
recovery_key: keys.recovery_key.ok_or(())?.to_base64(),
|
||||||
|
backup_version: keys.backup_version.ok_or(())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<matrix_sdk_crypto::store::RoomKeyCounts> for RoomKeyCounts {
|
||||||
|
fn from(count: matrix_sdk_crypto::store::RoomKeyCounts) -> Self {
|
||||||
|
Self {
|
||||||
|
total: count.total as i64,
|
||||||
|
backed_up: count.backed_up as i64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<matrix_sdk_crypto::CrossSigningKeyExport> for CrossSigningKeyExport {
|
impl From<matrix_sdk_crypto::CrossSigningKeyExport> for CrossSigningKeyExport {
|
||||||
fn from(e: matrix_sdk_crypto::CrossSigningKeyExport) -> Self {
|
fn from(e: matrix_sdk_crypto::CrossSigningKeyExport) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -9,6 +9,7 @@ use js_int::UInt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::r0::{
|
client::r0::{
|
||||||
|
backup::add_backup_keys::Response as KeysBackupResponse,
|
||||||
keys::{
|
keys::{
|
||||||
claim_keys::Response as KeysClaimResponse, get_keys::Response as KeysQueryResponse,
|
claim_keys::Response as KeysClaimResponse, get_keys::Response as KeysQueryResponse,
|
||||||
upload_keys::Response as KeysUploadResponse,
|
upload_keys::Response as KeysUploadResponse,
|
||||||
|
@ -31,17 +32,21 @@ use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use matrix_sdk_common::{deserialized_responses::AlgorithmInfo, uuid::Uuid};
|
use matrix_sdk_common::{deserialized_responses::AlgorithmInfo, uuid::Uuid};
|
||||||
use matrix_sdk_crypto::{
|
use matrix_sdk_crypto::{
|
||||||
decrypt_key_export, encrypt_key_export, matrix_qrcode::QrVerificationData, EncryptionSettings,
|
backups::{MegolmV1BackupKey, RecoveryKey},
|
||||||
LocalTrust, OlmMachine as InnerMachine, UserIdentities, Verification as RustVerification,
|
decrypt_key_export, encrypt_key_export,
|
||||||
|
matrix_qrcode::QrVerificationData,
|
||||||
|
EncryptionSettings, LocalTrust, OlmMachine as InnerMachine, UserIdentities,
|
||||||
|
Verification as RustVerification,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{CryptoStoreError, DecryptionError, SecretImportError, SignatureError},
|
error::{CryptoStoreError, DecryptionError, SecretImportError, SignatureError},
|
||||||
responses::{response_from_string, OutgoingVerificationRequest, OwnedResponse},
|
responses::{response_from_string, OutgoingVerificationRequest, OwnedResponse},
|
||||||
BootstrapCrossSigningResult, ConfirmVerificationResult, CrossSigningKeyExport,
|
BackupKey, BackupKeys, BootstrapCrossSigningResult, ConfirmVerificationResult,
|
||||||
CrossSigningStatus, DecryptedEvent, Device, DeviceLists, KeyImportError, KeysImportResult,
|
CrossSigningKeyExport, CrossSigningStatus, DecryptedEvent, Device, DeviceLists, KeyImportError,
|
||||||
ProgressListener, QrCode, Request, RequestType, RequestVerificationResult, ScanResult,
|
KeysImportResult, ProgressListener, QrCode, Request, RequestType, RequestVerificationResult,
|
||||||
SignatureUploadRequest, StartSasResult, UserIdentity, Verification, VerificationRequest,
|
RoomKeyCounts, ScanResult, SignatureUploadRequest, StartSasResult, UserIdentity, Verification,
|
||||||
|
VerificationRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A high level state machine that handles E2EE for Matrix.
|
/// A high level state machine that handles E2EE for Matrix.
|
||||||
|
@ -95,7 +100,7 @@ impl OlmMachine {
|
||||||
|
|
||||||
/// Get the display name of our own device.
|
/// Get the display name of our own device.
|
||||||
pub fn display_name(&self) -> Result<Option<String>, CryptoStoreError> {
|
pub fn display_name(&self) -> Result<Option<String>, CryptoStoreError> {
|
||||||
Ok(self.runtime.block_on(self.inner.dislpay_name())?)
|
Ok(self.runtime.block_on(self.inner.display_name())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a cross signing user identity for the given user ID.
|
/// Get a cross signing user identity for the given user ID.
|
||||||
|
@ -305,6 +310,9 @@ impl OlmMachine {
|
||||||
RequestType::SignatureUpload => {
|
RequestType::SignatureUpload => {
|
||||||
SignatureUploadResponse::try_from_http_response(response).map(Into::into)
|
SignatureUploadResponse::try_from_http_response(response).map(Into::into)
|
||||||
}
|
}
|
||||||
|
RequestType::KeysBackup => {
|
||||||
|
KeysBackupResponse::try_from_http_response(response).map(Into::into)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.expect("Can't convert json string to response");
|
.expect("Can't convert json string to response");
|
||||||
|
|
||||||
|
@ -701,10 +709,7 @@ impl OlmMachine {
|
||||||
methods: Vec<String>,
|
methods: Vec<String>,
|
||||||
) -> Option<OutgoingVerificationRequest> {
|
) -> Option<OutgoingVerificationRequest> {
|
||||||
let user_id = UserId::try_from(user_id).ok()?;
|
let user_id = UserId::try_from(user_id).ok()?;
|
||||||
let methods = methods
|
let methods = methods.into_iter().map(VerificationMethod::from).collect();
|
||||||
.into_iter()
|
|
||||||
.map(VerificationMethod::from)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if let Some(verification) = self.inner.get_verification_request(&user_id, flow_id) {
|
if let Some(verification) = self.inner.get_verification_request(&user_id, flow_id) {
|
||||||
verification.accept_with_methods(methods).map(|r| r.into())
|
verification.accept_with_methods(methods).map(|r| r.into())
|
||||||
|
@ -731,10 +736,7 @@ impl OlmMachine {
|
||||||
|
|
||||||
let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?;
|
let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?;
|
||||||
|
|
||||||
let methods = methods
|
let methods = methods.into_iter().map(VerificationMethod::from).collect();
|
||||||
.into_iter()
|
|
||||||
.map(VerificationMethod::from)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(if let Some(identity) = identity.and_then(|i| i.other()) {
|
Ok(if let Some(identity) = identity.and_then(|i| i.other()) {
|
||||||
let content = self
|
let content = self
|
||||||
|
@ -779,10 +781,7 @@ impl OlmMachine {
|
||||||
|
|
||||||
let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?;
|
let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?;
|
||||||
|
|
||||||
let methods = methods
|
let methods = methods.into_iter().map(VerificationMethod::from).collect();
|
||||||
.into_iter()
|
|
||||||
.map(VerificationMethod::from)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(if let Some(identity) = identity.and_then(|i| i.other()) {
|
Ok(if let Some(identity) = identity.and_then(|i| i.other()) {
|
||||||
let request = self.runtime.block_on(identity.request_verification(
|
let request = self.runtime.block_on(identity.request_verification(
|
||||||
|
@ -816,10 +815,7 @@ impl OlmMachine {
|
||||||
) -> Result<Option<RequestVerificationResult>, CryptoStoreError> {
|
) -> Result<Option<RequestVerificationResult>, CryptoStoreError> {
|
||||||
let user_id = UserId::try_from(user_id)?;
|
let user_id = UserId::try_from(user_id)?;
|
||||||
|
|
||||||
let methods = methods
|
let methods = methods.into_iter().map(VerificationMethod::from).collect();
|
||||||
.into_iter()
|
|
||||||
.map(VerificationMethod::from)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
if let Some(device) = self
|
if let Some(device) = self
|
||||||
|
@ -854,10 +850,7 @@ impl OlmMachine {
|
||||||
.runtime
|
.runtime
|
||||||
.block_on(self.inner.get_identity(self.inner.user_id()))?;
|
.block_on(self.inner.get_identity(self.inner.user_id()))?;
|
||||||
|
|
||||||
let methods = methods
|
let methods = methods.into_iter().map(VerificationMethod::from).collect();
|
||||||
.into_iter()
|
|
||||||
.map(VerificationMethod::from)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(if let Some(identity) = identity.and_then(|i| i.own()) {
|
Ok(if let Some(identity) = identity.and_then(|i| i.own()) {
|
||||||
let (verification, request) = self
|
let (verification, request) = self
|
||||||
|
@ -1023,10 +1016,7 @@ impl OlmMachine {
|
||||||
let user_id = UserId::try_from(user_id).ok()?;
|
let user_id = UserId::try_from(user_id).ok()?;
|
||||||
self.inner
|
self.inner
|
||||||
.get_verification(&user_id, flow_id)
|
.get_verification(&user_id, flow_id)
|
||||||
.and_then(|v| {
|
.and_then(|v| v.qr_v1().and_then(|qr| qr.to_bytes().map(encode).ok()))
|
||||||
v.qr_v1()
|
|
||||||
.and_then(|qr| qr.to_bytes().map(encode).ok())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pass data from a scanned QR code to an active verification request and
|
/// Pass data from a scanned QR code to an active verification request and
|
||||||
|
@ -1254,4 +1244,74 @@ impl OlmMachine {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn enable_backup(&self, key: BackupKey, version: String) -> Result<(), CryptoStoreError> {
|
||||||
|
let backup_key = MegolmV1BackupKey::from_base64(&key.public_key).unwrap();
|
||||||
|
backup_key.set_version(version);
|
||||||
|
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.inner.backup_machine().enable_backup(backup_key))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn disable_backup(&self) -> Result<(), CryptoStoreError> {
|
||||||
|
Ok(self
|
||||||
|
.runtime
|
||||||
|
.block_on(self.inner.backup_machine().disable_backup())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn backup_room_keys(&self) -> Result<Option<Request>, CryptoStoreError> {
|
||||||
|
let request = self
|
||||||
|
.runtime
|
||||||
|
.block_on(self.inner.backup_machine().backup())?;
|
||||||
|
|
||||||
|
Ok(request.map(|r| r.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn room_key_counts(&self) -> Result<RoomKeyCounts, CryptoStoreError> {
|
||||||
|
Ok(self
|
||||||
|
.runtime
|
||||||
|
.block_on(self.inner.backup_machine().room_key_counts())?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn save_recovery_key(
|
||||||
|
&self,
|
||||||
|
key: Option<String>,
|
||||||
|
version: Option<String>,
|
||||||
|
) -> Result<(), CryptoStoreError> {
|
||||||
|
let key = key.map(RecoveryKey::from_base64).transpose().ok().flatten();
|
||||||
|
Ok(self
|
||||||
|
.runtime
|
||||||
|
.block_on(self.inner.backup_machine().save_recovery_key(key, version))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn get_backup_keys(&self) -> Result<Option<BackupKeys>, CryptoStoreError> {
|
||||||
|
Ok(self
|
||||||
|
.runtime
|
||||||
|
.block_on(self.inner.backup_machine().get_backup_keys())?
|
||||||
|
.try_into()
|
||||||
|
.ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn sign(&self, message: &str) -> HashMap<String, HashMap<String, String>> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.inner.sign(message))
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
(
|
||||||
|
k.to_string(),
|
||||||
|
v.into_iter().map(|(k, v)| (k.to_string(), v)).collect(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,6 +208,7 @@ interface Request {
|
||||||
KeysUpload(string request_id, string body);
|
KeysUpload(string request_id, string body);
|
||||||
KeysQuery(string request_id, sequence<string> users);
|
KeysQuery(string request_id, sequence<string> users);
|
||||||
KeysClaim(string request_id, record<DOMString, record<DOMString, string>> one_time_keys);
|
KeysClaim(string request_id, record<DOMString, record<DOMString, string>> one_time_keys);
|
||||||
|
KeysBackup(string request_id, record<DOMString, record<DOMString, string>> rooms);
|
||||||
RoomMessage(string request_id, string room_id, string event_type, string content);
|
RoomMessage(string request_id, string room_id, string event_type, string content);
|
||||||
SignatureUpload(string request_id, string body);
|
SignatureUpload(string request_id, string body);
|
||||||
};
|
};
|
||||||
|
@ -222,6 +223,7 @@ enum RequestType {
|
||||||
"KeysUpload",
|
"KeysUpload",
|
||||||
"ToDevice",
|
"ToDevice",
|
||||||
"SignatureUpload",
|
"SignatureUpload",
|
||||||
|
"KeysBackup",
|
||||||
};
|
};
|
||||||
|
|
||||||
interface OlmMachine {
|
interface OlmMachine {
|
||||||
|
@ -343,4 +345,49 @@ interface OlmMachine {
|
||||||
void import_cross_signing_keys(CrossSigningKeyExport export);
|
void import_cross_signing_keys(CrossSigningKeyExport export);
|
||||||
[Throws=CryptoStoreError]
|
[Throws=CryptoStoreError]
|
||||||
boolean is_identity_verified([ByRef] string user_id);
|
boolean is_identity_verified([ByRef] string user_id);
|
||||||
|
|
||||||
|
record<DOMString, record<DOMString, string>> sign([ByRef] string message);
|
||||||
|
[Throws=CryptoStoreError]
|
||||||
|
void enable_backup(BackupKey key, string version);
|
||||||
|
[Throws=CryptoStoreError]
|
||||||
|
void disable_backup();
|
||||||
|
[Throws=CryptoStoreError]
|
||||||
|
Request? backup_room_keys();
|
||||||
|
[Throws=CryptoStoreError]
|
||||||
|
void save_recovery_key(string? key, string? version);
|
||||||
|
[Throws=CryptoStoreError]
|
||||||
|
RoomKeyCounts room_key_counts();
|
||||||
|
[Throws=CryptoStoreError]
|
||||||
|
BackupKeys? get_backup_keys();
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary PassphraseInfo {
|
||||||
|
string private_key_salt;
|
||||||
|
i32 private_key_iterations;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary BackupKey {
|
||||||
|
string public_key;
|
||||||
|
record<DOMString, record<DOMString, string>> signatures;
|
||||||
|
PassphraseInfo? passphrase_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary BackupKeys {
|
||||||
|
string recovery_key;
|
||||||
|
string backup_version;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary RoomKeyCounts {
|
||||||
|
i64 total;
|
||||||
|
i64 backed_up;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface BackupRecoveryKey {
|
||||||
|
constructor();
|
||||||
|
[Name=from_base64]
|
||||||
|
constructor(string key);
|
||||||
|
[Name=from_passphrase]
|
||||||
|
constructor(string key);
|
||||||
|
string to_base58();
|
||||||
|
BackupKey public_key();
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@ use serde_json::json;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::r0::{
|
api::client::r0::{
|
||||||
|
backup::add_backup_keys::Response as KeysBackupResponse,
|
||||||
keys::{
|
keys::{
|
||||||
claim_keys::{Request as KeysClaimRequest, Response as KeysClaimResponse},
|
claim_keys::{Request as KeysClaimRequest, Response as KeysClaimResponse},
|
||||||
get_keys::Response as KeysQueryResponse,
|
get_keys::Response as KeysQueryResponse,
|
||||||
|
@ -152,6 +153,10 @@ pub enum Request {
|
||||||
request_id: String,
|
request_id: String,
|
||||||
body: String,
|
body: String,
|
||||||
},
|
},
|
||||||
|
KeysBackup {
|
||||||
|
request_id: String,
|
||||||
|
rooms: HashMap<String, HashMap<String, String>>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OutgoingRequest> for Request {
|
impl From<OutgoingRequest> for Request {
|
||||||
|
@ -186,6 +191,7 @@ impl From<OutgoingRequest> for Request {
|
||||||
},
|
},
|
||||||
RoomMessage(r) => Request::from(r),
|
RoomMessage(r) => Request::from(r),
|
||||||
KeysClaim(c) => (*r.request_id(), c.clone()).into(),
|
KeysClaim(c) => (*r.request_id(), c.clone()).into(),
|
||||||
|
KeysBackup(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,6 +262,7 @@ pub enum RequestType {
|
||||||
KeysUpload,
|
KeysUpload,
|
||||||
ToDevice,
|
ToDevice,
|
||||||
SignatureUpload,
|
SignatureUpload,
|
||||||
|
KeysBackup,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DeviceLists {
|
pub struct DeviceLists {
|
||||||
|
@ -291,6 +298,7 @@ pub(crate) enum OwnedResponse {
|
||||||
KeysQuery(KeysQueryResponse),
|
KeysQuery(KeysQueryResponse),
|
||||||
ToDevice(ToDeviceResponse),
|
ToDevice(ToDeviceResponse),
|
||||||
SignatureUpload(SignatureUploadResponse),
|
SignatureUpload(SignatureUploadResponse),
|
||||||
|
KeysBackup(KeysBackupResponse),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<KeysClaimResponse> for OwnedResponse {
|
impl From<KeysClaimResponse> for OwnedResponse {
|
||||||
|
@ -323,6 +331,12 @@ impl From<SignatureUploadResponse> for OwnedResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<KeysBackupResponse> for OwnedResponse {
|
||||||
|
fn from(r: KeysBackupResponse) -> Self {
|
||||||
|
Self::KeysBackup(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a OwnedResponse> for IncomingResponse<'a> {
|
impl<'a> From<&'a OwnedResponse> for IncomingResponse<'a> {
|
||||||
fn from(r: &'a OwnedResponse) -> Self {
|
fn from(r: &'a OwnedResponse) -> Self {
|
||||||
match r {
|
match r {
|
||||||
|
@ -331,6 +345,7 @@ impl<'a> From<&'a OwnedResponse> for IncomingResponse<'a> {
|
||||||
OwnedResponse::KeysUpload(r) => IncomingResponse::KeysUpload(r),
|
OwnedResponse::KeysUpload(r) => IncomingResponse::KeysUpload(r),
|
||||||
OwnedResponse::ToDevice(r) => IncomingResponse::ToDevice(r),
|
OwnedResponse::ToDevice(r) => IncomingResponse::ToDevice(r),
|
||||||
OwnedResponse::SignatureUpload(r) => IncomingResponse::SignatureUpload(r),
|
OwnedResponse::SignatureUpload(r) => IncomingResponse::SignatureUpload(r),
|
||||||
|
OwnedResponse::KeysBackup(r) => IncomingResponse::KeysBackup(r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState,
|
class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState,
|
||||||
session: Session
|
session: Session
|
||||||
|
@ -81,6 +82,7 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
|
||||||
|
|
||||||
private fun getKeysBackupTrust() = withState { state ->
|
private fun getKeysBackupTrust() = withState { state ->
|
||||||
val versionResult = keysBackupService.keysBackupVersion
|
val versionResult = keysBackupService.keysBackupVersion
|
||||||
|
Timber.d("BACKUP: HEEEEEEE $versionResult ${state.keysBackupVersionTrust}")
|
||||||
|
|
||||||
if (state.keysBackupVersionTrust is Uninitialized && versionResult != null) {
|
if (state.keysBackupVersionTrust is Uninitialized && versionResult != null) {
|
||||||
setState {
|
setState {
|
||||||
|
@ -89,10 +91,12 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
|
||||||
deleteBackupRequest = Uninitialized
|
deleteBackupRequest = Uninitialized
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Timber.d("BACKUP: HEEEEEEE TWO")
|
||||||
|
|
||||||
keysBackupService
|
keysBackupService
|
||||||
.getKeysBackupTrust(versionResult, object : MatrixCallback<KeysBackupVersionTrust> {
|
.getKeysBackupTrust(versionResult, object : MatrixCallback<KeysBackupVersionTrust> {
|
||||||
override fun onSuccess(data: KeysBackupVersionTrust) {
|
override fun onSuccess(data: KeysBackupVersionTrust) {
|
||||||
|
Timber.d("BACKUP: HEEEE suceeeded $data")
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
keysBackupVersionTrust = Success(data)
|
keysBackupVersionTrust = Success(data)
|
||||||
|
@ -101,6 +105,7 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
|
Timber.d("BACKUP: HEEEE FAILED $failure")
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
keysBackupVersionTrust = Fail(failure)
|
keysBackupVersionTrust = Fail(failure)
|
||||||
|
|
Loading…
Reference in a new issue