crypto: Fill out all the methods to support backups

This commit is contained in:
Damir Jelić 2021-10-22 10:44:07 +02:00
parent 021041fc2e
commit 3b93d6b08c
12 changed files with 221 additions and 86 deletions

View File

@ -20,6 +20,7 @@ import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.paging.PagedList
import com.squareup.moshi.Types
import dagger.Lazy
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
@ -57,17 +58,20 @@ import org.matrix.android.sdk.api.util.JsonDict
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.keysbackup.RustKeyBackupService
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData
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.model.rest.RoomKeysBackupData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody
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.keysbackup.tasks.StoreSessionsDataTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask
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.MXEncryptEventContentResult
@ -137,6 +141,7 @@ internal class RequestSender @Inject constructor(
private val deleteBackupTask: DeleteBackupTask,
private val createKeysBackupVersionTask: CreateKeysBackupVersionTask,
private val backupRoomKeysTask: StoreSessionsDataTask,
private val updateKeysBackupVersionTask: UpdateKeysBackupVersionTask,
) {
companion object {
const val REQUEST_RETRY_COUNT = 3
@ -297,44 +302,25 @@ internal class RequestSender @Inject constructor(
val adapter = MoshiProvider
.providesMoshi()
.newBuilder()
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
.build()
.adapter<MutableMap<String, RoomKeysBackupData>>(MutableMap::class.java)
.adapter<MutableMap<String, RoomKeysBackupData>>(
Types.newParameterizedType(
Map::class.java,
String::class.java,
RoomKeysBackupData::class.java
))
val keys = adapter.fromJson(request.rooms)!!
Timber.d("BACKUP: CONVERTED KEYS TO HASHMAP $keys")
/*
val keyAdapter = MoshiProvider.providesMoshi().adapter(KeyBackupData::class.java)
val keysBackupData = KeysBackupData()
for (room in keys) {
val sessions = room.value.getOrDefault("sessions", mapOf())
for (session in sessions) {
Timber.d("BACKUP: HEEELOO CONVERTING KEY ${session.value}")
val key = keyAdapter.fromJson(session.value)!!
Timber.d("BACKUP: HEEELOO CONVERTED KEY $key")
keysBackupData
.roomIdToRoomKeysBackupData
.getOrPut(room.key, { RoomKeysBackupData() })
.sessionIdToKeyBackupData[session.key] = key
}
}
*/
/*
for ((roomId, backupData) in keys) {
val roomData = backup.roomIdToRoomKeysBackupData.getOrPut(roomId, { RoomKeysBackupData() })
for ((sessionId, key) in backupData.sessionIdToKeyBackupData) {
Timber.d("BACKUP INSERTING KEY $key")
roomData.sessionIdToKeyBackupData[sessionId] = key
}
}
*/
val params = StoreSessionsDataTask.Params(request.version, KeysBackupData())
val response = backupRoomKeysTask.execute(params)
val params = StoreSessionsDataTask.Params(request.version, KeysBackupData(keys))
val response = backupRoomKeysTask.executeRetry(params, REQUEST_RETRY_COUNT)
val responseAdapter = MoshiProvider.providesMoshi().adapter(BackupKeysResult::class.java)
return responseAdapter.toJson(response)!!
}
suspend fun updateBackup(keysBackupVersion: KeysVersionResult, body: UpdateKeysBackupVersionBody) {
val params = UpdateKeysBackupVersionTask.Params(keysBackupVersion.version, body)
updateKeysBackupVersionTask.executeRetry(params, REQUEST_RETRY_COUNT)
}
}
/**
@ -566,6 +552,8 @@ internal class DefaultCryptoService @Inject constructor(
Timber.v("Failed create an Olm machine: $throwable")
}
// We try to enable key backups, if the backup version on the server is trusted,
// we're gonna continue backing up.
tryOrNull {
keysBackupService!!.checkAndStartKeysBackup()
}
@ -1075,7 +1063,8 @@ internal class DefaultCryptoService @Inject constructor(
}
is Request.KeysBackup -> {
async {
TODO()
// The rust-sdk won't ever produce KeysBackup requests here,
// those only get explicitly created.
}
}
}

View File

@ -24,10 +24,10 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.UnsignedDeviceInfo
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
import uniffi.olm.CryptoStoreErrorException
import uniffi.olm.CryptoStoreException
import uniffi.olm.Device as InnerDevice
import uniffi.olm.OlmMachine
import uniffi.olm.SignatureErrorException
import uniffi.olm.SignatureException
import uniffi.olm.VerificationRequest
/** Class representing a device that supports E2EE in the Matrix world
@ -41,7 +41,7 @@ internal class Device(
private val sender: RequestSender,
private val listeners: ArrayList<VerificationService.Listener>,
) {
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
private suspend fun refreshData() {
val device = withContext(Dispatchers.IO) {
machine.getDevice(inner.userId, inner.deviceId)
@ -63,7 +63,7 @@ internal class Device(
* [org.matrix.android.sdk.internal.crypto.OwnUserIdentity.requestVerification]
* method can be used instead.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun requestVerification(methods: List<VerificationMethod>): VerificationRequest? {
val stringMethods = prepareMethods(methods)
val result = withContext(Dispatchers.IO) {
@ -87,7 +87,7 @@ internal class Device(
* The [requestVerification] method should be used instead.
*
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun startVerification(): SasVerification? {
val result = withContext(Dispatchers.IO) {
machine.startSasWithDevice(inner.userId, inner.deviceId)
@ -109,7 +109,7 @@ internal class Device(
* This won't upload any signatures, it will only mark the device as trusted
* in the local database.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun markAsTrusted() {
withContext(Dispatchers.IO) {
machine.markDeviceAsTrusted(inner.userId, inner.deviceId)
@ -125,7 +125,7 @@ internal class Device(
* This will fail if the device doesn't belong to use or if we don't have the
* private part of our self-signing key.
*/
@Throws(SignatureErrorException::class)
@Throws(SignatureException::class)
suspend fun verify(): Boolean {
val request = withContext(Dispatchers.IO) {
machine.verifyDevice(inner.userId, inner.deviceId)
@ -139,7 +139,7 @@ internal class Device(
/**
* Get the DeviceTrustLevel of this device
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun trustLevel(): DeviceTrustLevel {
refreshData()
return DeviceTrustLevel(crossSigningVerified = inner.crossSigningTrusted, locallyVerified = inner.locallyTrusted)

View File

@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.squareup.moshi.Types
import java.io.File
import java.nio.charset.Charset
import java.util.concurrent.ConcurrentHashMap
@ -37,6 +38,8 @@ import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.crosssigning.UserTrustResult
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
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.MXUsersDevicesMap
@ -44,16 +47,18 @@ import org.matrix.android.sdk.internal.crypto.model.rest.RestKeyInfo
import org.matrix.android.sdk.internal.crypto.model.rest.UnsignedDeviceInfo
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
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.ToDeviceSyncResponse
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import timber.log.Timber
import uniffi.olm.BackupKey
import uniffi.olm.BackupKeys
import uniffi.olm.CrossSigningKeyExport
import uniffi.olm.CrossSigningStatus
import uniffi.olm.CryptoStoreErrorException
import uniffi.olm.DecryptionErrorException
import uniffi.olm.CryptoStoreException
import uniffi.olm.DecryptionException
import uniffi.olm.DeviceLists
import uniffi.olm.KeyRequestPair
import uniffi.olm.Logger
@ -262,7 +267,7 @@ internal class OlmMachine(
*
* @param responseBody The body of the response that was received.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun markRequestAsSent(
requestId: String,
requestType: RequestType,
@ -290,7 +295,7 @@ internal class OlmMachine(
*
* @param keyCounts The map of uploaded one-time key types and counts.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun receiveSyncChanges(
toDevice: ToDeviceSyncResponse?,
deviceChanges: DeviceListResponse?,
@ -342,7 +347,7 @@ internal class OlmMachine(
*
* @return A [Request.KeysClaim] request that needs to be sent out to the server.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun getMissingSessions(users: List<String>): Request? =
withContext(Dispatchers.IO) { inner.getMissingSessions(users) }
@ -364,7 +369,7 @@ internal class OlmMachine(
*
* @return The list of [Request.ToDevice] that need to be sent out.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun shareRoomKey(roomId: String, users: List<String>): List<Request> =
withContext(Dispatchers.IO) { inner.shareRoomKey(roomId, users) }
@ -398,7 +403,7 @@ internal class OlmMachine(
*
* @return The encrypted version of the [Content]
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun encrypt(roomId: String, eventType: String, content: Content): Content =
withContext(Dispatchers.IO) {
val adapter = MoshiProvider.providesMoshi().adapter<Content>(Map::class.java)
@ -453,7 +458,7 @@ internal class OlmMachine(
* request itself. The cancellation *must* be sent out before the request, otherwise devices
* will ignore the key request.
*/
@Throws(DecryptionErrorException::class)
@Throws(DecryptionException::class)
suspend fun requestRoomKey(event: Event): KeyRequestPair =
withContext(Dispatchers.IO) {
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
@ -472,7 +477,7 @@ internal class OlmMachine(
*
* @return the encrypted key export as a bytearray.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun exportKeys(passphrase: String, rounds: Int): ByteArray =
withContext(Dispatchers.IO) { inner.exportKeys(passphrase, rounds).toByteArray() }
@ -485,7 +490,7 @@ internal class OlmMachine(
*
* @param listener A callback that can be used to introspect the progress of the key import.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun importKeys(
keys: ByteArray,
passphrase: String,
@ -501,7 +506,7 @@ internal class OlmMachine(
ImportRoomKeysResult(result.total, result.imported)
}
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun getIdentity(userId: String): UserIdentities? {
val identity = withContext(Dispatchers.IO) {
inner.getIdentity(userId)
@ -545,7 +550,7 @@ internal class OlmMachine(
*
* @return The Device if it found one.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun getCryptoDeviceInfo(userId: String, deviceId: String): CryptoDeviceInfo? {
return if (userId == userId() && deviceId == deviceId()) {
// Our own device isn't part of our store on the Rust side, return it
@ -556,7 +561,7 @@ internal class OlmMachine(
}
}
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun getDevice(userId: String, deviceId: String): Device? {
val device = withContext(Dispatchers.IO) {
inner.getDevice(userId, deviceId)
@ -578,7 +583,7 @@ internal class OlmMachine(
*
* @return The list of Devices or an empty list if there aren't any.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
val devices = this.getUserDevices(userId).map { it.toCryptoDeviceInfo() }.toMutableList()
@ -658,7 +663,7 @@ internal class OlmMachine(
}
/** Discard the currently active room key for the given room if there is one. */
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
fun discardRoomKey(roomId: String) {
runBlocking { inner.discardRoomKey(roomId) }
}
@ -770,7 +775,7 @@ internal class OlmMachine(
}
}
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun enableBackup(key: String, version: String) {
return withContext(Dispatchers.Default) {
val backupKey = BackupKey(key, mapOf(), null)
@ -797,7 +802,7 @@ internal class OlmMachine(
inner.saveRecoveryKey(key, version)
}
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun backupRoomKeys(): Request? {
return withContext(Dispatchers.Default) {
Timber.d("BACKUP CREATING REQUEST")
@ -806,4 +811,18 @@ internal class OlmMachine(
request
}
}
@Throws(CryptoStoreException::class)
suspend fun checkAuthDataSignature(authData: MegolmBackupAuthData): Boolean {
return withContext(Dispatchers.Default) {
val adapter = MoshiProvider
.providesMoshi()
.newBuilder()
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
.build()
.adapter(MegolmBackupAuthData::class.java)
val serializedAuthData = adapter.toJson(authData)
inner.verifyBackup(serializedAuthData)
}
}
}

View File

@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.verification.UpdateDispatcher
import uniffi.olm.CryptoStoreErrorException
import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
import uniffi.olm.QrCode
import uniffi.olm.Verification
@ -172,7 +172,7 @@ internal class QrCodeVerification(
* The method turns into a noop if we're not yet ready to confirm the scanning,
* i.e. we didn't yet receive a m.key.verification.start event from the other side.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
private suspend fun confirm() {
val result = withContext(Dispatchers.IO)
{

View File

@ -27,7 +27,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.internal.crypto.verification.UpdateDispatcher
import org.matrix.android.sdk.internal.crypto.verification.getEmojiForCode
import uniffi.olm.CryptoStoreErrorException
import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
import uniffi.olm.Sas
import uniffi.olm.Verification
@ -202,7 +202,7 @@ internal class SasVerification(
}
}
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
private suspend fun confirm() {
val result = withContext(Dispatchers.IO) {
machine.confirmVerification(inner.otherUserId, inner.flowId)

View File

@ -25,8 +25,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.model.CryptoCrossSigningKey
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
import uniffi.olm.CryptoStoreErrorException
import uniffi.olm.SignatureErrorException
import uniffi.olm.CryptoStoreException
import uniffi.olm.SignatureException
/**
* A sealed class representing user identities.
@ -46,7 +46,7 @@ sealed class UserIdentities {
*
* @return True if the identity is considered to be verified and trusted, false otherwise.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
abstract suspend fun verified(): Boolean
/**
@ -59,7 +59,7 @@ sealed class UserIdentities {
* Throws a SignatureErrorException if we can't sign the identity,
* if for example we don't have access to our user-signing key.
*/
@Throws(SignatureErrorException::class)
@Throws(SignatureException::class)
abstract suspend fun verify()
/**
@ -93,7 +93,7 @@ internal class OwnUserIdentity(
*
* To perform an interactive verification user the [requestVerification] method instead.
*/
@Throws(SignatureErrorException::class)
@Throws(SignatureException::class)
override suspend fun verify() {
val request = withContext(Dispatchers.Default) { olmMachine.inner().verifyIdentity(userId) }
this.requestSender.sendSignatureUpload(request)
@ -104,7 +104,7 @@ internal class OwnUserIdentity(
*
* @return True if the identity is considered to be verified and trusted, false otherwise.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
override suspend fun verified(): Boolean {
return withContext(Dispatchers.IO) { olmMachine.inner().isIdentityVerified(userId) }
}
@ -130,7 +130,7 @@ internal class OwnUserIdentity(
* @param methods The list of [VerificationMethod] that we wish to advertise to the other
* side as being supported.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun requestVerification(methods: List<VerificationMethod>): VerificationRequest {
val stringMethods = prepareMethods(methods)
val result = this.olmMachine.inner().requestSelfVerification(stringMethods)
@ -187,7 +187,7 @@ internal class UserIdentity(
*
* To perform an interactive verification user the [requestVerification] method instead.
*/
@Throws(SignatureErrorException::class)
@Throws(SignatureException::class)
override suspend fun verify() {
val request = withContext(Dispatchers.Default) { olmMachine.inner().verifyIdentity(userId) }
this.requestSender.sendSignatureUpload(request)
@ -225,7 +225,7 @@ internal class UserIdentity(
* @param transactionId The transaction id that should be used for the request that sends
* the `m.key.verification.request` to the room.
*/
@Throws(CryptoStoreErrorException::class)
@Throws(CryptoStoreException::class)
suspend fun requestVerification(
methods: List<VerificationMethod>,
roomId: String,

View File

@ -84,6 +84,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.internal.wait
import org.matrix.android.sdk.internal.crypto.keysbackup.model.SignalableMegolmBackupAuthData
import org.matrix.olm.OlmException
import org.matrix.olm.OlmPkDecryption

View File

@ -42,6 +42,7 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.SignalableMegolmB
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.model.rest.UpdateKeysBackupVersionBody
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
@ -266,34 +267,148 @@ internal class RustKeyBackupService @Inject constructor(
override fun backupAllGroupSessions(progressListener: ProgressListener?,
callback: MatrixCallback<Unit>?) {
// This is only used in tests?
TODO()
}
private suspend fun checkBackupTrust(authData: MegolmBackupAuthData?): KeysBackupVersionTrust {
return if (authData == null || authData.publicKey.isEmpty() || authData.signatures.isEmpty()) {
Timber.v("getKeysBackupTrust: Key backup is absent or missing required data")
KeysBackupVersionTrust()
} else {
KeysBackupVersionTrust(olmMachine.checkAuthDataSignature(authData))
}
}
override fun getKeysBackupTrust(keysBackupVersion: KeysVersionResult,
callback: MatrixCallback<KeysBackupVersionTrust>) {
Timber.d("BACKUP: HELLOO TRYING TO CHECK THE TRUST")
// TODO
callback.onSuccess(KeysBackupVersionTrust(false))
val authData = keysBackupVersion.getAuthDataAsMegolmBackupAuthData()
cryptoCoroutineScope.launch {
try {
callback.onSuccess(checkBackupTrust(authData))
} catch (exception: Throwable) {
callback.onFailure(exception)
}
}
}
override fun trustKeysBackupVersion(keysBackupVersion: KeysVersionResult,
trust: Boolean,
callback: MatrixCallback<Unit>) {
Timber.v("trustKeyBackupVersion: $trust, version ${keysBackupVersion.version}")
TODO()
// Get auth data to update it
val authData = getMegolmBackupAuthData(keysBackupVersion)
if (authData == null) {
Timber.w("trustKeyBackupVersion:trust: Key backup is missing required data")
callback.onFailure(IllegalArgumentException("Missing element"))
} else {
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
val body = withContext(coroutineDispatchers.crypto) {
// Get current signatures, or create an empty set
val userId = olmMachine.userId()
val signatures = authData.signatures[userId].orEmpty().toMutableMap()
if (trust) {
// Add current device signature
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, authData.signalableJSONDictionary())
val deviceSignature = olmMachine.sign(canonicalJson)
deviceSignature[userId]?.forEach { entry ->
signatures[entry.key] = entry.value
}
} else {
signatures.remove("ed25519:${olmMachine.deviceId()}")
}
val newAuthData = authData.copy()
val newSignatures = newAuthData.signatures.toMutableMap()
newSignatures[userId] = signatures
@Suppress("UNCHECKED_CAST")
UpdateKeysBackupVersionBody(
algorithm = keysBackupVersion.algorithm,
authData = newAuthData.copy(signatures = newSignatures).toJsonDict(),
version = keysBackupVersion.version)
}
try {
sender.updateBackup(keysBackupVersion, body)
val newKeysBackupVersion = KeysVersionResult(
algorithm = keysBackupVersion.algorithm,
authData = body.authData,
version = keysBackupVersion.version,
hash = keysBackupVersion.hash,
count = keysBackupVersion.count
)
checkAndStartWithKeysBackupVersion(newKeysBackupVersion)
callback.onSuccess(Unit)
} catch (exception: Throwable) {
callback.onFailure(exception)
}
}
}
}
// Check that the recovery key matches to the public key that we downloaded from the server.
// If they match, we can trust the public key and enable backups since we have the private key.
private fun checkRecoveryKey(recoveryKey: BackupRecoveryKey, keysBackupData: KeysVersionResult) {
val backupKey = recoveryKey.publicKey()
val authData = getMegolmBackupAuthData(keysBackupData)
when {
authData == null -> {
Timber.w("isValidRecoveryKeyForKeysBackupVersion: Key backup is missing required data")
throw IllegalArgumentException("Missing element")
}
backupKey.publicKey != authData.publicKey -> {
Timber.w("isValidRecoveryKeyForKeysBackupVersion: Public keys mismatch")
throw IllegalArgumentException("Invalid recovery key or password")
}
else -> {
// This case is fine, the public key on the server matches the public key the
// recovery key produced.
}
}
}
override fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult,
recoveryKey: String,
callback: MatrixCallback<Unit>) {
Timber.v("trustKeysBackupVersionWithRecoveryKey: version ${keysBackupVersion.version}")
TODO()
cryptoCoroutineScope.launch {
try {
// This is ~nowhere mentioned, the string here is actually a base58 encoded key.
// This not really supported by the spec for the backup key, the 4S key supports
// base58 encoding and the same method seems to be used here.
val key = BackupRecoveryKey.fromBase58(recoveryKey)
checkRecoveryKey(key, keysBackupVersion)
trustKeysBackupVersion(keysBackupVersion, true, callback)
} catch (exception: Throwable) {
callback.onFailure(exception)
}
}
}
override fun trustKeysBackupVersionWithPassphrase(keysBackupVersion: KeysVersionResult,
password: String,
callback: MatrixCallback<Unit>) {
TODO()
cryptoCoroutineScope.launch {
try {
val key = BackupRecoveryKey.fromPassphrase(password)
checkRecoveryKey(key, keysBackupVersion)
trustKeysBackupVersion(keysBackupVersion, true, callback)
} catch (exception: Throwable) {
callback.onFailure(exception)
}
}
}
override fun onSecretKeyGossip(secret: String) {
@ -437,8 +552,9 @@ internal class RustKeyBackupService @Inject constructor(
}
Timber.v(" -> enabling key backups")
// TODO
// enableKeysBackup(keyBackupVersion)
cryptoCoroutineScope.launch {
enableKeysBackup(keyBackupVersion)
}
} else {
Timber.v("checkAndStartWithKeysBackupVersion: No usable key backup. version: ${keyBackupVersion.version}")
if (versionInStore != null) {
@ -595,7 +711,7 @@ internal class RustKeyBackupService @Inject constructor(
olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_BACKUP, response)
Timber.d("BACKUP MARKED REQUEST AS SENT")
// TODO again is this correct?
// TODO, again is this correct?
withContext(Dispatchers.Main) {
backupKeys()
}

View File

@ -17,7 +17,7 @@ base64 = "0.13.0"
thiserror = "1.0.25"
tracing = "0.1.26"
tracing-subscriber = "0.2.18"
uniffi = "0.12.0"
uniffi = "0.14.0"
pbkdf2 = "0.8.0"
sha2 = "0.9.5"
rand = "0.8.4"
@ -28,14 +28,14 @@ version = "0.2.1"
features = ["lax_deserialize"]
[dependencies.matrix-sdk-common]
path = "/home/poljar/werk/priv/nio-rust/crates/matrix-sdk-common/"
path = "/home/poljar/werk/matrix/nio-rust/crates/matrix-sdk-common/"
# git = "https://github.com/matrix-org/matrix-rust-sdk/"
# rev = "9bae87b0ac213f9d37c033e76ea3a336e164cf02"
[dependencies.matrix-sdk-crypto]
# git = "https://github.com/matrix-org/matrix-rust-sdk/"
# rev = "9bae87b0ac213f9d37c033e76ea3a336e164cf02"
path = "/home/poljar/werk/priv/nio-rust/crates/matrix-sdk-crypto/"
path = "/home/poljar/werk/matrix/nio-rust/crates/matrix-sdk-crypto/"
features = ["sled_cryptostore", "qrcode", "backups_v1"]
[dependencies.tokio]

View File

@ -1,11 +1,9 @@
#![deny(
dead_code,
missing_docs,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications
)]
//! TODO

View File

@ -1321,4 +1321,12 @@ impl OlmMachine {
})
.collect()
}
/// TODO
pub fn verify_backup(&self, auth_data: &str) -> Result<bool, CryptoStoreError> {
let auth_data = serde_json::from_str(auth_data)?;
Ok(self
.runtime
.block_on(self.inner.backup_machine().verify_backup(auth_data))?)
}
}

View File

@ -360,6 +360,8 @@ interface OlmMachine {
[Throws=CryptoStoreError]
BackupKeys? get_backup_keys();
boolean backup_enabled();
[Throws=CryptoStoreError]
boolean verify_backup([ByRef] string auth_data);
};
dictionary PassphraseInfo {
@ -389,6 +391,8 @@ interface BackupRecoveryKey {
constructor(string key);
[Name=from_passphrase]
constructor(string key);
[Name=from_base58]
constructor(string key);
string to_base58();
BackupKey public_key();
};