mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-12-26 10:58:34 +03:00
Merge pull request #10 from poljar/feature/fga/backup_recovery_key
Feature/fga/backup recovery key
This commit is contained in:
commit
022057dd6f
21 changed files with 210 additions and 113 deletions
|
@ -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
|
||||||
|
@ -322,7 +323,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")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,7 +519,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,
|
||||||
|
|
|
@ -597,6 +597,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
// for the test we just accept?
|
// for the test we just accept?
|
||||||
oldCode = sasTx.getDecimalCodeRepresentation()
|
oldCode = sasTx.getDecimalCodeRepresentation()
|
||||||
testHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
|
delay(500)
|
||||||
sasTx.userHasVerifiedShortCode()
|
sasTx.userHasVerifiedShortCode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,11 +624,12 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
val sasTx = tx as SasVerificationTransaction
|
val sasTx = tx as SasVerificationTransaction
|
||||||
when (sasTx.state) {
|
when (sasTx.state) {
|
||||||
VerificationTxState.ShortCodeReady -> {
|
VerificationTxState.ShortCodeReady -> {
|
||||||
|
newCode = sasTx.getDecimalCodeRepresentation()
|
||||||
if (matchOnce) {
|
if (matchOnce) {
|
||||||
testHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
|
delay(500)
|
||||||
sasTx.userHasVerifiedShortCode()
|
sasTx.userHasVerifiedShortCode()
|
||||||
}
|
}
|
||||||
newCode = sasTx.getDecimalCodeRepresentation()
|
|
||||||
matchOnce = false
|
matchOnce = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -669,6 +671,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
aliceNewSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo() != null
|
aliceNewSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo() != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"MSK Private parts should be the same",
|
"MSK Private parts should be the same",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.verification
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertFalse
|
import org.junit.Assert.assertFalse
|
||||||
|
@ -40,10 +41,9 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationServic
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@ -322,8 +322,6 @@ class SASTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// any two devices may only have at most one key verification in flight at a time.
|
// any two devices may only have at most one key verification in flight at a time.
|
||||||
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
|
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
|
||||||
@Test
|
@Test
|
||||||
|
@ -449,7 +447,8 @@ class SASTest : InstrumentedTest {
|
||||||
aliceSession.cryptoService().verificationService().beginKeyVerification(VerificationMethod.SAS, bobSession.myUserId, transactionId)
|
aliceSession.cryptoService().verificationService().beginKeyVerification(VerificationMethod.SAS, bobSession.myUserId, transactionId)
|
||||||
}
|
}
|
||||||
testHelper.await(latch)
|
testHelper.await(latch)
|
||||||
val aliceTx = aliceSession.cryptoService().verificationService().getExistingTransaction(bobSession.myUserId, transactionId) as SasVerificationTransaction
|
val aliceTx =
|
||||||
|
aliceSession.cryptoService().verificationService().getExistingTransaction(bobSession.myUserId, transactionId) as SasVerificationTransaction
|
||||||
val bobTx = bobSession.cryptoService().verificationService().getExistingTransaction(aliceSession.myUserId, transactionId) as SasVerificationTransaction
|
val bobTx = bobSession.cryptoService().verificationService().getExistingTransaction(aliceSession.myUserId, transactionId) as SasVerificationTransaction
|
||||||
|
|
||||||
assertEquals("Should have same SAS", aliceTx.getDecimalCodeRepresentation(), bobTx.getDecimalCodeRepresentation())
|
assertEquals("Should have same SAS", aliceTx.getDecimalCodeRepresentation(), bobTx.getDecimalCodeRepresentation())
|
||||||
|
@ -470,62 +469,71 @@ class SASTest : InstrumentedTest {
|
||||||
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
||||||
val bobVerificationService = bobSession!!.cryptoService().verificationService()
|
val bobVerificationService = bobSession!!.cryptoService().verificationService()
|
||||||
|
|
||||||
val aliceSASLatch = CountDownLatch(1)
|
val verifiedLatch = CountDownLatch(2)
|
||||||
val aliceListener = object : VerificationService.Listener {
|
val aliceListener = object : VerificationService.Listener {
|
||||||
|
|
||||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
||||||
Timber.v("RequestUpdated pr=$pr")
|
Timber.v("RequestUpdated pr=$pr")
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchOnce = true
|
var matched = false
|
||||||
|
var verified = false
|
||||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||||
Timber.v("Alice transactionUpdated: ${tx.state}")
|
Timber.v("Alice transactionUpdated: ${tx.state} on thread:${Thread.currentThread()}")
|
||||||
if (tx !is SasVerificationTransaction) return
|
if (tx !is SasVerificationTransaction) return
|
||||||
when (tx.state) {
|
when (tx.state) {
|
||||||
VerificationTxState.ShortCodeReady -> testHelper.runBlockingTest {
|
VerificationTxState.ShortCodeReady -> testHelper.runBlockingTest {
|
||||||
tx.userHasVerifiedShortCode()
|
if (!matched) {
|
||||||
}
|
matched = true
|
||||||
VerificationTxState.Verified -> {
|
delay(500)
|
||||||
if (matchOnce) {
|
tx.userHasVerifiedShortCode()
|
||||||
matchOnce = false
|
|
||||||
aliceSASLatch.countDown()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> Unit
|
VerificationTxState.Verified -> {
|
||||||
|
if (!verified) {
|
||||||
|
verified = true
|
||||||
|
verifiedLatch.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
aliceVerificationService.addListener(aliceListener)
|
aliceVerificationService.addListener(aliceListener)
|
||||||
|
|
||||||
val bobSASLatch = CountDownLatch(1)
|
|
||||||
val bobListener = object : VerificationService.Listener {
|
val bobListener = object : VerificationService.Listener {
|
||||||
var acceptOnce = true
|
var accepted = false
|
||||||
var matchOnce = true
|
var matched = false
|
||||||
|
var verified = false
|
||||||
|
|
||||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
||||||
Timber.v("RequestUpdated: pr=$pr")
|
Timber.v("RequestUpdated: pr=$pr")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||||
Timber.v("Bob transactionUpdated: ${tx.state}")
|
Timber.v("Bob transactionUpdated: ${tx.state} on thread: ${Thread.currentThread()}")
|
||||||
if (tx !is SasVerificationTransaction) return
|
if (tx !is SasVerificationTransaction) return
|
||||||
when (tx.state) {
|
when (tx.state) {
|
||||||
VerificationTxState.OnStarted -> testHelper.runBlockingTest {
|
VerificationTxState.OnStarted -> testHelper.runBlockingTest {
|
||||||
if (acceptOnce) {
|
if (!accepted) {
|
||||||
acceptOnce = false
|
accepted = true
|
||||||
tx.acceptVerification()
|
tx.acceptVerification()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VerificationTxState.ShortCodeReady -> testHelper.runBlockingTest {
|
VerificationTxState.ShortCodeReady -> testHelper.runBlockingTest {
|
||||||
if (matchOnce) {
|
if (!matched) {
|
||||||
matchOnce = false
|
matched = true
|
||||||
|
delay(500)
|
||||||
tx.userHasVerifiedShortCode()
|
tx.userHasVerifiedShortCode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VerificationTxState.ShortCodeAccepted -> {
|
VerificationTxState.Verified -> {
|
||||||
bobSASLatch.countDown()
|
if (!verified) {
|
||||||
|
verified = true
|
||||||
|
verifiedLatch.countDown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -538,8 +546,8 @@ class SASTest : InstrumentedTest {
|
||||||
testHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, transactionId)
|
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, transactionId)
|
||||||
}
|
}
|
||||||
testHelper.await(aliceSASLatch)
|
Timber.v("Await after beginKey ${Thread.currentThread()}")
|
||||||
testHelper.await(bobSASLatch)
|
testHelper.await(verifiedLatch)
|
||||||
|
|
||||||
// Assert that devices are verified
|
// Assert that devices are verified
|
||||||
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = testHelper.runBlockingTest {
|
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = testHelper.runBlockingTest {
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.api.session.crypto.keysbackup
|
||||||
|
|
||||||
|
import uniffi.olm.BackupRecoveryKey as InnerBackupRecoveryKey
|
||||||
|
|
||||||
|
class BackupRecoveryKey internal constructor(internal val inner: InnerBackupRecoveryKey) {
|
||||||
|
|
||||||
|
constructor() : this(InnerBackupRecoveryKey())
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun fromBase58(key: String): BackupRecoveryKey {
|
||||||
|
val inner = InnerBackupRecoveryKey.fromBase58(key)
|
||||||
|
return BackupRecoveryKey(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromBase64(key: String): BackupRecoveryKey {
|
||||||
|
val inner = InnerBackupRecoveryKey.fromBase64(key)
|
||||||
|
return BackupRecoveryKey(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromPassphrase(passphrase: String, salt: String, rounds: Int): BackupRecoveryKey {
|
||||||
|
val inner = InnerBackupRecoveryKey.fromPassphrase(passphrase, salt, rounds)
|
||||||
|
return BackupRecoveryKey(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newFromPassphrase(passphrase: String): BackupRecoveryKey {
|
||||||
|
val inner = InnerBackupRecoveryKey.newFromPassphrase(passphrase)
|
||||||
|
return BackupRecoveryKey(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is BackupRecoveryKey) return false
|
||||||
|
return this.toBase58() == other.toBase58()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toBase58() = inner.toBase58()
|
||||||
|
|
||||||
|
fun toBase64() = inner.toBase64()
|
||||||
|
|
||||||
|
fun decryptV1(ephemeralKey: String, mac: String, ciphertext: String) = inner.decryptV1(ephemeralKey, mac, ciphertext)
|
||||||
|
|
||||||
|
fun megolmV1PublicKey() = inner.megolmV1PublicKey()
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -65,7 +65,7 @@ import org.matrix.android.sdk.api.session.sync.model.DeviceListResponse
|
||||||
import org.matrix.android.sdk.api.session.sync.model.DeviceOneTimeKeysCountSyncResponse
|
import org.matrix.android.sdk.api.session.sync.model.DeviceOneTimeKeysCountSyncResponse
|
||||||
import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse
|
import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse
|
||||||
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.network.NetworkRequestsProcessor
|
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
|
||||||
import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
||||||
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
|
@ -131,7 +131,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
|
|
||||||
private val olmMachine by lazy { olmMachineProvider.olmMachine }
|
private val olmMachine by lazy { olmMachineProvider.olmMachine }
|
||||||
|
|
||||||
private val outgoingRequestsProcessor = NetworkRequestsProcessor(
|
private val outgoingRequestsProcessor = OutgoingRequestsProcessor(
|
||||||
requestSender = requestSender,
|
requestSender = requestSender,
|
||||||
coroutineScope = cryptoCoroutineScope,
|
coroutineScope = cryptoCoroutineScope,
|
||||||
cryptoSessionInfoProvider = cryptoSessionInfoProvider,
|
cryptoSessionInfoProvider = cryptoSessionInfoProvider,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,10 +34,10 @@ import uniffi.olm.RequestType
|
||||||
|
|
||||||
private val loggerTag = LoggerTag("OutgoingRequestsProcessor", LoggerTag.CRYPTO)
|
private val loggerTag = LoggerTag("OutgoingRequestsProcessor", LoggerTag.CRYPTO)
|
||||||
|
|
||||||
internal class NetworkRequestsProcessor(private val requestSender: RequestSender,
|
internal class OutgoingRequestsProcessor(private val requestSender: RequestSender,
|
||||||
private val coroutineScope: CoroutineScope,
|
private val coroutineScope: CoroutineScope,
|
||||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
||||||
private val shieldComputer: ShieldComputer,) {
|
private val shieldComputer: ShieldComputer,) {
|
||||||
|
|
||||||
fun interface ShieldComputer {
|
fun interface ShieldComputer {
|
||||||
suspend fun compute(userIds: List<String>): RoomEncryptionTrustLevel
|
suspend fun compute(userIds: List<String>): RoomEncryptionTrustLevel
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue