Use BackupRecoveryKey instead of plain string

This commit is contained in:
ganfra 2022-05-25 14:45:31 +02:00
parent 7c76ba8184
commit b0aae84727
16 changed files with 101 additions and 75 deletions

View file

@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAuthData import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAuthData
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo
import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromRecoveryKey import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromRecoveryKey
@ -250,7 +251,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
return MegolmBackupCreationInfo( return MegolmBackupCreationInfo(
algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP, algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP,
authData = createFakeMegolmBackupAuthData(), authData = createFakeMegolmBackupAuthData(),
recoveryKey = "fake" recoveryKey = BackupRecoveryKey.fromBase58("3cnTdW")
) )
} }
@ -445,7 +446,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
// Save it for gossiping // Save it for gossiping
session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version) session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
extractCurveKeyFromRecoveryKey(creationInfo.recoveryKey)?.toBase64NoPadding()?.let { secret -> extractCurveKeyFromRecoveryKey(creationInfo.recoveryKey.toBase58())?.toBase64NoPadding()?.let { secret ->
ssssService.storeSecret( ssssService.storeSecret(
KEYBACKUP_SECRET_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME,
secret, secret,

View file

@ -34,6 +34,7 @@ import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import org.matrix.android.sdk.api.listeners.StepProgressListener import org.matrix.android.sdk.api.listeners.StepProgressListener
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
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.api.session.crypto.keysbackup.toKeysVersionResult import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
@ -505,7 +506,7 @@ class KeysBackupTest : InstrumentedTest {
try { try {
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey( testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey(
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
"Bad recovery key" BackupRecoveryKey.fromBase58("Bad recovery key")
) )
fail("Should have failed to trust") fail("Should have failed to trust")
} catch (failure: Throwable) { } catch (failure: Throwable) {
@ -645,7 +646,7 @@ class KeysBackupTest : InstrumentedTest {
var importRoomKeysResult: ImportRoomKeysResult? = null var importRoomKeysResult: ImportRoomKeysResult? = null
testHelper.runBlockingTest { testHelper.runBlockingTest {
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d", BackupRecoveryKey.fromBase58("EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d"),
null, null,
null, null,
null null

View file

@ -142,8 +142,7 @@ interface KeysBackupService {
* @param keysBackupVersion the backup version to check. * @param keysBackupVersion the backup version to check.
* @param recoveryKey the recovery key to challenge with the key backup public key. * @param recoveryKey the recovery key to challenge with the key backup public key.
*/ */
suspend fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult, suspend fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult, recoveryKey: BackupRecoveryKey)
recoveryKey: String)
/** /**
* Set trust on a keys backup version. * Set trust on a keys backup version.
@ -167,7 +166,7 @@ interface KeysBackupService {
* @param callback Callback. It provides the number of found keys and the number of successfully imported keys. * @param callback Callback. It provides the number of found keys and the number of successfully imported keys.
*/ */
suspend fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult, suspend fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult,
recoveryKey: String, roomId: String?, recoveryKey: BackupRecoveryKey, roomId: String?,
sessionId: String?, sessionId: String?,
stepProgressListener: StepProgressListener?): ImportRoomKeysResult stepProgressListener: StepProgressListener?): ImportRoomKeysResult
@ -194,10 +193,10 @@ interface KeysBackupService {
val state: KeysBackupState val state: KeysBackupState
// For gossiping // For gossiping
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) fun saveBackupRecoveryKey(recoveryKey: BackupRecoveryKey?, version: String?)
suspend fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? suspend fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo?
suspend fun isValidRecoveryKeyForCurrentVersion(recoveryKey: String): Boolean suspend fun isValidRecoveryKeyForCurrentVersion(recoveryKey: BackupRecoveryKey): Boolean
fun computePrivateKey(passphrase: String, fun computePrivateKey(passphrase: String,
privateKeySalt: String, privateKeySalt: String,

View file

@ -31,7 +31,7 @@ data class MegolmBackupCreationInfo(
val authData: MegolmBackupAuthData, val authData: MegolmBackupAuthData,
/** /**
* The Base58 recovery key. * The recovery key.
*/ */
val recoveryKey: String val recoveryKey: BackupRecoveryKey
) )

View file

@ -17,6 +17,6 @@
package org.matrix.android.sdk.api.session.crypto.keysbackup package org.matrix.android.sdk.api.session.crypto.keysbackup
data class SavedKeyBackupKeyInfo( data class SavedKeyBackupKeyInfo(
val recoveryKey: String, val recoveryKey: BackupRecoveryKey,
val version: String val version: String
) )

View file

@ -20,10 +20,9 @@ import dagger.Lazy
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.logger.LoggerTag
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult
import org.matrix.android.sdk.api.session.crypto.keysbackup.SavedKeyBackupKeyInfo import org.matrix.android.sdk.api.session.crypto.keysbackup.SavedKeyBackupKeyInfo
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.api.util.awaitCallback
import org.matrix.android.sdk.internal.crypto.keysbackup.RustKeyBackupService import org.matrix.android.sdk.internal.crypto.keysbackup.RustKeyBackupService
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
@ -101,12 +100,12 @@ internal class PerSessionBackupQueryRateLimiter @Inject constructor(
(now - lastTry.timestamp) > MIN_TRY_BACKUP_PERIOD_MILLIS (now - lastTry.timestamp) > MIN_TRY_BACKUP_PERIOD_MILLIS
if (!shouldQuery) return false if (!shouldQuery) return false
val recoveryKey = savedKeyBackupKeyInfo?.recoveryKey ?: return false
val successfullyImported = withContext(coroutineDispatchers.io) { val successfullyImported = withContext(coroutineDispatchers.io) {
try { try {
keysBackupService.get().restoreKeysWithRecoveryKey( keysBackupService.get().restoreKeysWithRecoveryKey(
currentVersion, currentVersion,
savedKeyBackupKeyInfo?.recoveryKey ?: "", recoveryKey,
roomId, roomId,
sessionId, sessionId,
null, null,

View file

@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError 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.listeners.StepProgressListener import org.matrix.android.sdk.api.listeners.StepProgressListener
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupLastVersionResult import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupLastVersionResult
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService 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
@ -60,7 +61,6 @@ import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.util.JsonCanonicalizer import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.olm.OlmException import org.matrix.olm.OlmException
import timber.log.Timber import timber.log.Timber
import uniffi.olm.BackupRecoveryKey
import uniffi.olm.Request import uniffi.olm.Request
import uniffi.olm.RequestType import uniffi.olm.RequestType
import java.security.InvalidParameterException import java.security.InvalidParameterException
@ -150,7 +150,7 @@ internal class RustKeyBackupService @Inject constructor(
MegolmBackupCreationInfo( MegolmBackupCreationInfo(
algorithm = publicKey.backupAlgorithm, algorithm = publicKey.backupAlgorithm,
authData = signedMegolmBackupAuthData, authData = signedMegolmBackupAuthData,
recoveryKey = key.toBase58() recoveryKey = key
) )
} }
} }
@ -189,9 +189,11 @@ internal class RustKeyBackupService @Inject constructor(
} }
} }
override fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) { override fun saveBackupRecoveryKey(recoveryKey: BackupRecoveryKey?, version: String?) {
cryptoCoroutineScope.launch { cryptoCoroutineScope.launch {
olmMachine.saveRecoveryKey(recoveryKey, version) val recoveryKeyStr = recoveryKey?.toBase64()
// TODO : change rust API to use BackupRecoveryKey
olmMachine.saveRecoveryKey(recoveryKeyStr, version)
} }
} }
@ -312,7 +314,8 @@ internal class RustKeyBackupService @Inject constructor(
val body = UpdateKeysBackupVersionBody( val body = UpdateKeysBackupVersionBody(
algorithm = keysBackupVersion.algorithm, algorithm = keysBackupVersion.algorithm,
authData = newAuthData.copy(signatures = newSignatures).toJsonDict(), authData = newAuthData.copy(signatures = newSignatures).toJsonDict(),
version = keysBackupVersion.version) version = keysBackupVersion.version
)
withContext(coroutineDispatchers.io) { withContext(coroutineDispatchers.io) {
sender.updateBackup(keysBackupVersion, body) sender.updateBackup(keysBackupVersion, body)
@ -353,14 +356,13 @@ internal class RustKeyBackupService @Inject constructor(
} }
} }
override suspend fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult, recoveryKey: String) { override suspend fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult, recoveryKey: BackupRecoveryKey) {
Timber.v("trustKeysBackupVersionWithRecoveryKey: version ${keysBackupVersion.version}") Timber.v("trustKeysBackupVersionWithRecoveryKey: version ${keysBackupVersion.version}")
withContext(coroutineDispatchers.crypto) { withContext(coroutineDispatchers.crypto) {
// This is ~nowhere mentioned, the string here is actually a base58 encoded key. // 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 // 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. // base58 encoding and the same method seems to be used here.
val key = BackupRecoveryKey.fromBase58(recoveryKey) checkRecoveryKey(recoveryKey, keysBackupVersion)
checkRecoveryKey(key, keysBackupVersion)
trustKeysBackupVersion(keysBackupVersion, true) trustKeysBackupVersion(keysBackupVersion, true)
} }
} }
@ -378,7 +380,7 @@ internal class RustKeyBackupService @Inject constructor(
withContext(coroutineDispatchers.crypto) { withContext(coroutineDispatchers.crypto) {
try { try {
val version = sender.getKeyBackupLastVersion()?.toKeysVersionResult() val version = sender.getKeyBackupLastVersion()?.toKeysVersionResult()
Timber.v("Keybackup version: $version")
if (version != null) { if (version != null) {
val key = BackupRecoveryKey.fromBase64(secret) val key = BackupRecoveryKey.fromBase64(secret)
if (isValidRecoveryKey(key, version)) { if (isValidRecoveryKey(key, version)) {
@ -395,7 +397,9 @@ internal class RustKeyBackupService @Inject constructor(
} }
} }
// we can save, it's valid // we can save, it's valid
saveBackupRecoveryKey(key.toBase64(), version.version) saveBackupRecoveryKey(key, version.version)
} else {
Timber.d("Invalid recovery key")
} }
} else { } else {
Timber.e("onSecretKeyGossip: Failed to import backup recovery key, no backup version was found on the server") Timber.e("onSecretKeyGossip: Failed to import backup recovery key, no backup version was found on the server")
@ -480,7 +484,7 @@ internal class RustKeyBackupService @Inject constructor(
} }
// Save for next time and for gossiping // Save for next time and for gossiping
saveBackupRecoveryKey(recoveryKey.toBase64(), keysVersionResult.version) saveBackupRecoveryKey(recoveryKey, keysVersionResult.version)
} }
withContext(coroutineDispatchers.main) { withContext(coroutineDispatchers.main) {
@ -519,14 +523,18 @@ internal class RustKeyBackupService @Inject constructor(
stepProgressListener?.onStepProgress(stepProgress) stepProgressListener?.onStepProgress(stepProgress)
} }
Timber.v("restoreKeysWithRecoveryKey: Decrypted ${sessionsData.size} keys out" + Timber.v(
" of ${data.roomIdToRoomKeysBackupData.size} rooms from the backup store on the homeserver") "restoreKeysWithRecoveryKey: Decrypted ${sessionsData.size} keys out" +
" of ${data.roomIdToRoomKeysBackupData.size} rooms from the backup store on the homeserver"
)
// Do not trigger a backup for them if they come from the backup version we are using // Do not trigger a backup for them if they come from the backup version we are using
val backUp = keysVersionResult.version != keysBackupVersion?.version val backUp = keysVersionResult.version != keysBackupVersion?.version
if (backUp) { if (backUp) {
Timber.v("restoreKeysWithRecoveryKey: Those keys will be backed up" + Timber.v(
" to backup version: ${keysBackupVersion?.version}") "restoreKeysWithRecoveryKey: Those keys will be backed up" +
" to backup version: ${keysBackupVersion?.version}"
)
} }
// Import them into the crypto store // Import them into the crypto store
@ -557,15 +565,12 @@ internal class RustKeyBackupService @Inject constructor(
} }
override suspend fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult, override suspend fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult,
recoveryKey: String, recoveryKey: BackupRecoveryKey,
roomId: String?, roomId: String?,
sessionId: String?, sessionId: String?,
stepProgressListener: StepProgressListener?): ImportRoomKeysResult { stepProgressListener: StepProgressListener?): ImportRoomKeysResult {
Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}") Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}")
return restoreBackup(keysVersionResult, recoveryKey, roomId, sessionId, stepProgressListener)
val key = BackupRecoveryKey.fromBase58(recoveryKey)
return restoreBackup(keysVersionResult, key, roomId, sessionId, stepProgressListener)
} }
override suspend fun restoreKeyBackupWithPassword(keysBackupVersion: KeysVersionResult, override suspend fun restoreKeyBackupWithPassword(keysBackupVersion: KeysVersionResult,
@ -577,7 +582,6 @@ internal class RustKeyBackupService @Inject constructor(
val recoveryKey = withContext(coroutineDispatchers.crypto) { val recoveryKey = withContext(coroutineDispatchers.crypto) {
recoveryKeyFromPassword(password, keysBackupVersion) recoveryKeyFromPassword(password, keysBackupVersion)
} }
return restoreBackup(keysBackupVersion, recoveryKey, roomId, sessionId, stepProgressListener) return restoreBackup(keysBackupVersion, recoveryKey, roomId, sessionId, stepProgressListener)
} }
@ -703,16 +707,15 @@ internal class RustKeyBackupService @Inject constructor(
private fun isValidRecoveryKey(recoveryKey: BackupRecoveryKey, version: KeysVersionResult): Boolean { private fun isValidRecoveryKey(recoveryKey: BackupRecoveryKey, version: KeysVersionResult): Boolean {
val publicKey = recoveryKey.megolmV1PublicKey().publicKey val publicKey = recoveryKey.megolmV1PublicKey().publicKey
val authData = getMegolmBackupAuthData(version) ?: return false val authData = getMegolmBackupAuthData(version) ?: return false
Timber.v("recoveryKey.megolmV1PublicKey().publicKey $publicKey == getMegolmBackupAuthData(version).publicKey ${authData.publicKey}")
return authData.publicKey == publicKey return authData.publicKey == publicKey
} }
override suspend fun isValidRecoveryKeyForCurrentVersion(recoveryKey: String): Boolean { override suspend fun isValidRecoveryKeyForCurrentVersion(recoveryKey: BackupRecoveryKey): Boolean {
return withContext(coroutineDispatchers.crypto) { return withContext(coroutineDispatchers.crypto) {
val keysBackupVersion = keysBackupVersion ?: return@withContext false val keysBackupVersion = keysBackupVersion ?: return@withContext false
val key = BackupRecoveryKey.fromBase64(recoveryKey)
try { try {
isValidRecoveryKey(key, keysBackupVersion) isValidRecoveryKey(recoveryKey, keysBackupVersion)
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.i("isValidRecoveryKeyForCurrentVersion: Invalid recovery key") Timber.i("isValidRecoveryKeyForCurrentVersion: Invalid recovery key")
false false
@ -726,7 +729,9 @@ internal class RustKeyBackupService @Inject constructor(
override suspend fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? { override suspend fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? {
val info = olmMachine.getBackupKeys() ?: return null val info = olmMachine.getBackupKeys() ?: return null
return SavedKeyBackupKeyInfo(info.recoveryKey, info.backupVersion) // TODO change rust ffi to return BackupRecoveryKey instead of base64 string
val backupRecoveryKey = BackupRecoveryKey.fromBase64(info.recoveryKey)
return SavedKeyBackupKeyInfo(backupRecoveryKey, info.backupVersion)
} }
/** /**

View file

@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.crypto.OutgoingRoomKeyRequestState
import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.session.crypto.crosssigning.PrivateKeysInfo import org.matrix.android.sdk.api.session.crypto.crosssigning.PrivateKeysInfo
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.SavedKeyBackupKeyInfo import org.matrix.android.sdk.api.session.crypto.keysbackup.SavedKeyBackupKeyInfo
import org.matrix.android.sdk.api.session.crypto.model.AuditTrail import org.matrix.android.sdk.api.session.crypto.model.AuditTrail
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
@ -469,7 +470,8 @@ internal class RealmCryptoStore @Inject constructor(
val key = it.keyBackupRecoveryKey val key = it.keyBackupRecoveryKey
val version = it.keyBackupRecoveryKeyVersion val version = it.keyBackupRecoveryKeyVersion
if (!key.isNullOrBlank() && !version.isNullOrBlank()) { if (!key.isNullOrBlank() && !version.isNullOrBlank()) {
SavedKeyBackupKeyInfo(recoveryKey = key, version = version) val backupRecoveryKey = BackupRecoveryKey.fromBase58(key)
SavedKeyBackupKeyInfo(recoveryKey = backupRecoveryKey, version = version)
} else { } else {
null null
} }

View file

@ -520,10 +520,9 @@ internal class RoomSyncHandler @Inject constructor(
private fun decryptIfNeeded(event: Event, roomId: String) { private fun decryptIfNeeded(event: Event, roomId: String) {
try { try {
// Event from sync does not have roomId, so add it to the event first
// note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching // note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching
val result = runBlocking { val result = runBlocking {
cryptoService.decryptEvent(event.copy(roomId = roomId), "") cryptoService.decryptEvent(event, "")
} }
event.mxDecryptionResult = OlmDecryptionResult( event.mxDecryptionResult = OlmDecryptionResult(
payload = result.clearEvent, payload = result.clearEvent,

View file

@ -23,6 +23,7 @@ import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import javax.inject.Inject import javax.inject.Inject
class KeysBackupRestoreFromKeyViewModel @Inject constructor( class KeysBackupRestoreFromKeyViewModel @Inject constructor(
@ -42,7 +43,7 @@ class KeysBackupRestoreFromKeyViewModel @Inject constructor(
sharedViewModel.loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.loading))) sharedViewModel.loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.loading)))
recoveryCodeErrorText.value = null recoveryCodeErrorText.value = null
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val recoveryKey = recoveryCode.value!! val recoveryKey = BackupRecoveryKey.fromBase58(recoveryCode.value!!)
try { try {
sharedViewModel.recoverUsingBackupRecoveryKey(recoveryKey) sharedViewModel.recoverUsingBackupRecoveryKey(recoveryKey)
} catch (failure: Throwable) { } catch (failure: Throwable) {

View file

@ -31,6 +31,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.listeners.StepProgressListener import org.matrix.android.sdk.api.listeners.StepProgressListener
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult
import org.matrix.android.sdk.api.session.crypto.keysbackup.computeRecoveryKey import org.matrix.android.sdk.api.session.crypto.keysbackup.computeRecoveryKey
@ -88,7 +89,7 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
private val progressObserver = object : StepProgressListener { private val progressObserver = object : StepProgressListener {
override fun onStepProgress(step: StepProgressListener.Step) { override fun onStepProgress(step: StepProgressListener.Step) {
when (step) { when (step) {
is StepProgressListener.Step.ComputingKey -> { is StepProgressListener.Step.ComputingKey -> {
loadingEvent.postValue( loadingEvent.postValue(
WaitingViewData( WaitingViewData(
stringProvider.getString(R.string.keys_backup_restoring_waiting_message) + stringProvider.getString(R.string.keys_backup_restoring_waiting_message) +
@ -107,7 +108,7 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
) )
) )
} }
is StepProgressListener.Step.ImportingKey -> { is StepProgressListener.Step.ImportingKey -> {
Timber.d("backupKeys.ImportingKey.progress: ${step.progress}") Timber.d("backupKeys.ImportingKey.progress: ${step.progress}")
// Progress 0 can take a while, display an indeterminate progress in this case // Progress 0 can take a while, display an indeterminate progress in this case
if (step.progress == 0) { if (step.progress == 0) {
@ -131,14 +132,22 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
} }
is StepProgressListener.Step.DecryptingKey -> { is StepProgressListener.Step.DecryptingKey -> {
if (step.progress == 0) { if (step.progress == 0) {
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) + loadingEvent.postValue(
"\n" + stringProvider.getString(R.string.keys_backup_restoring_decrypting_keys_waiting_message), WaitingViewData(
isIndeterminate = true)) stringProvider.getString(R.string.keys_backup_restoring_waiting_message) +
"\n" + stringProvider.getString(R.string.keys_backup_restoring_decrypting_keys_waiting_message),
isIndeterminate = true
)
)
} else { } else {
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) + loadingEvent.postValue(
"\n" + stringProvider.getString(R.string.keys_backup_restoring_decrypting_keys_waiting_message), WaitingViewData(
step.progress, stringProvider.getString(R.string.keys_backup_restoring_waiting_message) +
step.total)) "\n" + stringProvider.getString(R.string.keys_backup_restoring_decrypting_keys_waiting_message),
step.progress,
step.total
)
)
} }
} }
} }
@ -170,7 +179,7 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
) )
// Go and use it!! // Go and use it!!
try { try {
recoverUsingBackupRecoveryKey(computeRecoveryKey(savedSecret.recoveryKey.fromBase64()), version) recoverUsingBackupRecoveryKey(savedSecret.recoveryKey, version)
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.e(failure, "## recoverUsingBackupRecoveryKey FAILED") Timber.e(failure, "## recoverUsingBackupRecoveryKey FAILED")
keySourceModel.postValue( keySourceModel.postValue(
@ -212,7 +221,9 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
recoverUsingBackupRecoveryKey(computeRecoveryKey(secret.fromBase64())) val computedRecoveryKey = computeRecoveryKey(secret.fromBase64())
val backupRecoveryKey = BackupRecoveryKey.fromBase58(computedRecoveryKey)
recoverUsingBackupRecoveryKey(backupRecoveryKey)
} catch (failure: Throwable) { } catch (failure: Throwable) {
_navigateEvent.postValue( _navigateEvent.postValue(
LiveEvent(NAVIGATE_FAILED_TO_LOAD_4S) LiveEvent(NAVIGATE_FAILED_TO_LOAD_4S)
@ -234,7 +245,8 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.loading))) loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.loading)))
try { try {
val result = keysBackup.restoreKeyBackupWithPassword(keyVersion, val result = keysBackup.restoreKeyBackupWithPassword(
keyVersion,
passphrase, passphrase,
null, null,
session.myUserId, session.myUserId,
@ -249,7 +261,7 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
} }
} }
suspend fun recoverUsingBackupRecoveryKey(recoveryKey: String, keyVersion: KeysVersionResult? = null) { suspend fun recoverUsingBackupRecoveryKey(recoveryKey: BackupRecoveryKey, keyVersion: KeysVersionResult? = null) {
val keysBackup = session.cryptoService().keysBackupService() val keysBackup = session.cryptoService().keysBackupService()
// This is badddddd // This is badddddd
val version = keyVersion ?: keyVersionResult.value ?: return val version = keyVersion ?: keyVersionResult.value ?: return
@ -257,7 +269,8 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.loading))) loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.loading)))
try { try {
val result = keysBackup.restoreKeysWithRecoveryKey(version, val result = keysBackup.restoreKeysWithRecoveryKey(
version,
recoveryKey, recoveryKey,
null, null,
session.myUserId, session.myUserId,

View file

@ -27,6 +27,7 @@ import im.vector.app.core.time.Clock
import im.vector.app.core.utils.LiveEvent import im.vector.app.core.utils.LiveEvent
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersion import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersion
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo
@ -71,7 +72,7 @@ class KeysBackupSetupSharedViewModel @Inject constructor(
// Step 3 // Step 3
// Var to ignore events from previous request(s) to generate a recovery key // Var to ignore events from previous request(s) to generate a recovery key
private var currentRequestId: MutableLiveData<Long> = MutableLiveData() private var currentRequestId: MutableLiveData<Long> = MutableLiveData()
var recoveryKey: MutableLiveData<String?> = MutableLiveData(null) var recoveryKey: MutableLiveData<BackupRecoveryKey?> = MutableLiveData(null)
var prepareRecoverFailError: MutableLiveData<Throwable?> = MutableLiveData(null) var prepareRecoverFailError: MutableLiveData<Throwable?> = MutableLiveData(null)
var megolmBackupCreationInfo: MegolmBackupCreationInfo? = null var megolmBackupCreationInfo: MegolmBackupCreationInfo? = null
var copyHasBeenMade = false var copyHasBeenMade = false

View file

@ -66,7 +66,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
views.keysBackupSetupStep3Label2.text = getString(R.string.keys_backup_setup_step3_text_line2_no_passphrase) views.keysBackupSetupStep3Label2.text = getString(R.string.keys_backup_setup_step3_text_line2_no_passphrase)
views.keysBackupSetupStep3FinishButton.text = getString(R.string.keys_backup_setup_step3_button_title_no_passphrase) views.keysBackupSetupStep3FinishButton.text = getString(R.string.keys_backup_setup_step3_button_title_no_passphrase)
views.keysBackupSetupStep3RecoveryKeyText.text = viewModel.recoveryKey.value!! views.keysBackupSetupStep3RecoveryKeyText.text = viewModel.recoveryKey.value!!.toBase58()
.replace(" ", "") .replace(" ", "")
.chunked(16) .chunked(16)
.joinToString("\n") { .joinToString("\n") {
@ -114,7 +114,8 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
} else { } else {
dialog.findViewById<TextView>(R.id.keys_backup_recovery_key_text)?.let { dialog.findViewById<TextView>(R.id.keys_backup_recovery_key_text)?.let {
it.isVisible = true it.isVisible = true
it.text = recoveryKey.replace(" ", "") it.text = recoveryKey.toBase58()
.replace(" ", "")
.chunked(16) .chunked(16)
.joinToString("\n") { .joinToString("\n") {
it it
@ -123,7 +124,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
} }
it.debouncedClicks { it.debouncedClicks {
copyToClipboard(requireActivity(), recoveryKey) copyToClipboard(requireActivity(), recoveryKey.toBase58())
} }
} }
} }
@ -145,7 +146,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
fragment = this, fragment = this,
activityResultLauncher = null, activityResultLauncher = null,
chooserTitle = context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title), chooserTitle = context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title),
text = recoveryKey, text = recoveryKey.toBase58(),
subject = context?.getString(R.string.recovery_key) subject = context?.getString(R.string.recovery_key)
) )
viewModel.copyHasBeenMade = true viewModel.copyHasBeenMade = true
@ -159,7 +160,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
viewModel.recoveryKey.value?.let { viewModel.recoveryKey.value?.let {
viewModel.copyHasBeenMade = true viewModel.copyHasBeenMade = true
copyToClipboard(requireActivity(), it) copyToClipboard(requireActivity(), it.toBase58())
} }
} }
@ -202,7 +203,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
val uri = activityRessult.data?.data ?: return@registerStartForActivityResult val uri = activityRessult.data?.data ?: return@registerStartForActivityResult
if (activityRessult.resultCode == Activity.RESULT_OK) { if (activityRessult.resultCode == Activity.RESULT_OK) {
viewModel.recoveryKey.value?.let { viewModel.recoveryKey.value?.let {
exportRecoveryKeyToFile(uri, it) exportRecoveryKeyToFile(uri, it.toBase58())
} }
} }
} }

View file

@ -23,6 +23,7 @@ import im.vector.app.core.resources.StringProvider
import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.listeners.ProgressListener
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.computeRecoveryKey import org.matrix.android.sdk.api.session.crypto.keysbackup.computeRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromRecoveryKey import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromRecoveryKey
import org.matrix.android.sdk.api.session.securestorage.EmptyKeySigner import org.matrix.android.sdk.api.session.securestorage.EmptyKeySigner
@ -91,8 +92,8 @@ class BackupToQuadSMigrationTask @Inject constructor(
reportProgress(params, R.string.bootstrap_progress_compute_curve_key) reportProgress(params, R.string.bootstrap_progress_compute_curve_key)
val recoveryKey = computeRecoveryKey(curveKey) val recoveryKey = computeRecoveryKey(curveKey)
val backupRecoveryKey = BackupRecoveryKey.fromBase58(recoveryKey)
val isValid = keysBackupService.isValidRecoveryKeyForCurrentVersion(recoveryKey) val isValid = keysBackupService.isValidRecoveryKeyForCurrentVersion(backupRecoveryKey)
if (!isValid) return Result.InvalidRecoverySecret if (!isValid) return Result.InvalidRecoverySecret
@ -143,7 +144,7 @@ class BackupToQuadSMigrationTask @Inject constructor(
) )
// save for gossiping // save for gossiping
keysBackupService.saveBackupRecoveryKey(recoveryKey, version.version) keysBackupService.saveBackupRecoveryKey(backupRecoveryKey, version.version)
// It's not a good idea to download the full backup, it might take very long // It's not a good idea to download the full backup, it might take very long
// and use a lot of resources // and use a lot of resources
return Result.Success return Result.Success

View file

@ -236,7 +236,7 @@ class BootstrapCrossSigningTask @Inject constructor(
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Save megolm backup key for gossiping") Timber.d("## BootstrapCrossSigningTask: Creating 4S - Save megolm backup key for gossiping")
session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version) session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
extractCurveKeyFromRecoveryKey(creationInfo.recoveryKey)?.toBase64NoPadding()?.let { secret -> extractCurveKeyFromRecoveryKey(creationInfo.recoveryKey.toBase58())?.toBase64NoPadding()?.let { secret ->
ssssService.storeSecret( ssssService.storeSecret(
KEYBACKUP_SECRET_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME,
secret, secret,
@ -251,7 +251,7 @@ class BootstrapCrossSigningTask @Inject constructor(
val isValid = session.cryptoService().keysBackupService().isValidRecoveryKeyForCurrentVersion(knownMegolmSecret!!.recoveryKey) val isValid = session.cryptoService().keysBackupService().isValidRecoveryKeyForCurrentVersion(knownMegolmSecret!!.recoveryKey)
if (isValid) { if (isValid) {
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Megolm key valid and known") Timber.d("## BootstrapCrossSigningTask: Creating 4S - Megolm key valid and known")
extractCurveKeyFromRecoveryKey(knownMegolmSecret.recoveryKey)?.toBase64NoPadding()?.let { secret -> extractCurveKeyFromRecoveryKey(knownMegolmSecret.recoveryKey.toBase58())?.toBase64NoPadding()?.let { secret ->
ssssService.storeSecret( ssssService.storeSecret(
KEYBACKUP_SECRET_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME,
secret, secret,

View file

@ -37,6 +37,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NA
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.computeRecoveryKey import org.matrix.android.sdk.api.session.crypto.keysbackup.computeRecoveryKey
import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
@ -431,9 +432,10 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
val version = session.cryptoService().keysBackupService().getCurrentVersion()?.toKeysVersionResult() ?: return@launch val version = session.cryptoService().keysBackupService().getCurrentVersion()?.toKeysVersionResult() ?: return@launch
val recoveryKey = computeRecoveryKey(secret.fromBase64()) val recoveryKey = computeRecoveryKey(secret.fromBase64())
val isValid = session.cryptoService().keysBackupService().isValidRecoveryKeyForCurrentVersion(recoveryKey) val backupRecoveryKey = BackupRecoveryKey.fromBase58(recoveryKey)
val isValid = session.cryptoService().keysBackupService().isValidRecoveryKeyForCurrentVersion(backupRecoveryKey)
if (isValid) { if (isValid) {
session.cryptoService().keysBackupService().saveBackupRecoveryKey(recoveryKey, version.version) session.cryptoService().keysBackupService().saveBackupRecoveryKey(backupRecoveryKey, version.version)
} }
session.cryptoService().keysBackupService().trustKeysBackupVersion(version, true) session.cryptoService().keysBackupService().trustKeysBackupVersion(version, true)
} catch (failure: Throwable) { } catch (failure: Throwable) {
@ -502,6 +504,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
} }
override fun transactionUpdated(tx: VerificationTransaction) = withState { state -> override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
Timber.v("transactionUpdated: $tx")
handleTransactionUpdate(state, tx) handleTransactionUpdate(state, tx)
} }
@ -510,7 +513,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
} }
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state -> override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
Timber.v("VerificationRequestUpdated: $pr")
if (state.selfVerificationMode && state.pendingRequest.invoke() == null && state.transactionId == null) { if (state.selfVerificationMode && state.pendingRequest.invoke() == null && state.transactionId == null) {
// is this an incoming with that user // is this an incoming with that user
if (pr.isIncoming && pr.otherUserId == state.otherUserMxItem?.id) { if (pr.isIncoming && pr.otherUserId == state.otherUserMxItem?.id) {